feat(FN-3734): add heartbeat readonly gating and action-gate reload endpoin
Merges reloadable exempt-tools action-gate reload endpoint into the engine alongside a new heartbeat readonly-tools gating mechanism, plus a service worker first-install blank page fix. The engine gains a reload endpoint for action gates, permanent-agent gating exports, and comprehensive test covera Fusion-Task-Id: FN-3734
This commit is contained in:
5
.changeset/fn-3734-heartbeat-readonly-tools.md
Normal file
5
.changeset/fn-3734-heartbeat-readonly-tools.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix permanent-agent tool gating so `fn_heartbeat_done`, `fn_send_message`, and `fn_read_messages` are treated as readonly/exempt and no longer require approval under permission-policy gating.
|
||||||
@@ -24,6 +24,9 @@ describe("permanent-agent-gating", () => {
|
|||||||
expect(classifyPermanentAgentToolCall("fn_research_run").category).toBe("network_api");
|
expect(classifyPermanentAgentToolCall("fn_research_run").category).toBe("network_api");
|
||||||
expect(classifyPermanentAgentToolCall("fn_task_show").category).toBe("none");
|
expect(classifyPermanentAgentToolCall("fn_task_show").category).toBe("none");
|
||||||
expect(classifyPermanentAgentToolCall("fn_research_get").category).toBe("none");
|
expect(classifyPermanentAgentToolCall("fn_research_get").category).toBe("none");
|
||||||
|
expect(classifyPermanentAgentToolCall("fn_heartbeat_done")).toEqual({ category: "none", recognized: true });
|
||||||
|
expect(classifyPermanentAgentToolCall("fn_send_message")).toEqual({ category: "none", recognized: true });
|
||||||
|
expect(classifyPermanentAgentToolCall("fn_read_messages")).toEqual({ category: "none", recognized: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses only canonical action-category names", () => {
|
it("uses only canonical action-category names", () => {
|
||||||
@@ -57,6 +60,20 @@ describe("permanent-agent-gating", () => {
|
|||||||
expect(decision.disposition).toBe("require-approval");
|
expect(decision.disposition).toBe("require-approval");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows recognized readonly heartbeat tools under permission policy", () => {
|
||||||
|
const decision = resolvePermanentAgentToolDecision({
|
||||||
|
toolName: "fn_heartbeat_done",
|
||||||
|
gating: {
|
||||||
|
permissionPolicy: {
|
||||||
|
presetId: "approval-required",
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(decision.disposition).toBe("allow");
|
||||||
|
});
|
||||||
|
|
||||||
it("resolves disposition from policy for sensitive categories", () => {
|
it("resolves disposition from policy for sensitive categories", () => {
|
||||||
const blockDecision = resolvePermanentAgentToolDecision({
|
const blockDecision = resolvePermanentAgentToolDecision({
|
||||||
toolName: "write",
|
toolName: "write",
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ const READONLY_FN_TOOLS = new Set([
|
|||||||
"fn_task_update",
|
"fn_task_update",
|
||||||
"fn_task_log",
|
"fn_task_log",
|
||||||
"fn_task_done",
|
"fn_task_done",
|
||||||
|
"fn_heartbeat_done",
|
||||||
|
"fn_send_message",
|
||||||
|
"fn_read_messages",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const MUTATING_GIT_SUBCOMMANDS = new Set([
|
const MUTATING_GIT_SUBCOMMANDS = new Set([
|
||||||
|
|||||||
Reference in New Issue
Block a user