@senpi/runtime 3.0.180 → 3.0.182
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/llm-decision/openclaw-llm-decision.d.ts +8 -0
- package/dist/actions/llm-decision/openclaw-llm-decision.d.ts.map +1 -1
- package/dist/actions/llm-decision/openclaw-llm-decision.js +56 -1
- package/dist/actions/llm-decision/openclaw-llm-decision.js.map +1 -1
- package/dist/approvals/approval-store.d.ts +37 -7
- package/dist/approvals/approval-store.d.ts.map +1 -1
- package/dist/approvals/approval-store.js +127 -23
- package/dist/approvals/approval-store.js.map +1 -1
- package/dist/approvals/telemetry.d.ts +27 -3
- package/dist/approvals/telemetry.d.ts.map +1 -1
- package/dist/approvals/telemetry.js +8 -4
- package/dist/approvals/telemetry.js.map +1 -1
- package/dist/approvals/tool-approval-gate.d.ts +79 -0
- package/dist/approvals/tool-approval-gate.d.ts.map +1 -1
- package/dist/approvals/tool-approval-gate.js +184 -15
- package/dist/approvals/tool-approval-gate.js.map +1 -1
- package/dist/approvals/turn-context.d.ts +271 -0
- package/dist/approvals/turn-context.d.ts.map +1 -0
- package/dist/approvals/turn-context.js +551 -0
- package/dist/approvals/turn-context.js.map +1 -0
- package/dist/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +17 -3
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +71 -4
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/deploy/job.d.ts +32 -0
- package/dist/deploy/job.d.ts.map +1 -1
- package/dist/deploy/job.js +72 -7
- package/dist/deploy/job.js.map +1 -1
- package/dist/deploy/register.d.ts +11 -0
- package/dist/deploy/register.d.ts.map +1 -1
- package/dist/deploy/register.js +84 -2
- package/dist/deploy/register.js.map +1 -1
- package/dist/host/openclaw-host.d.ts +138 -0
- package/dist/host/openclaw-host.d.ts.map +1 -0
- package/dist/host/openclaw-host.js +177 -0
- package/dist/host/openclaw-host.js.map +1 -0
- package/dist/index.d.ts +46 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +220 -12
- package/dist/index.js.map +1 -1
- package/dist/runtime/auto-update/coordinator.d.ts +203 -0
- package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
- package/dist/runtime/auto-update/coordinator.js +621 -12
- package/dist/runtime/auto-update/coordinator.js.map +1 -1
- package/dist/runtime/auto-update/gateway-restart.d.ts +60 -1
- package/dist/runtime/auto-update/gateway-restart.d.ts.map +1 -1
- package/dist/runtime/auto-update/gateway-restart.js +95 -21
- package/dist/runtime/auto-update/gateway-restart.js.map +1 -1
- package/dist/runtime/auto-update/idle-gate.d.ts +139 -0
- package/dist/runtime/auto-update/idle-gate.d.ts.map +1 -0
- package/dist/runtime/auto-update/idle-gate.js +217 -0
- package/dist/runtime/auto-update/idle-gate.js.map +1 -0
- package/dist/runtime/auto-update/install-results.d.ts +130 -0
- package/dist/runtime/auto-update/install-results.d.ts.map +1 -0
- package/dist/runtime/auto-update/install-results.js +264 -0
- package/dist/runtime/auto-update/install-results.js.map +1 -0
- package/dist/runtime/auto-update/money-call-tracker.d.ts +36 -0
- package/dist/runtime/auto-update/money-call-tracker.d.ts.map +1 -0
- package/dist/runtime/auto-update/money-call-tracker.js +129 -0
- package/dist/runtime/auto-update/money-call-tracker.js.map +1 -0
- package/dist/runtime/auto-update/register-idle-gate.d.ts +70 -0
- package/dist/runtime/auto-update/register-idle-gate.d.ts.map +1 -0
- package/dist/runtime/auto-update/register-idle-gate.js +123 -0
- package/dist/runtime/auto-update/register-idle-gate.js.map +1 -0
- package/dist/runtime/auto-update/run-update-command.d.ts +13 -0
- package/dist/runtime/auto-update/run-update-command.d.ts.map +1 -1
- package/dist/runtime/auto-update/run-update-command.js +18 -0
- package/dist/runtime/auto-update/run-update-command.js.map +1 -1
- package/dist/runtime/auto-update/state-store.d.ts +14 -1
- package/dist/runtime/auto-update/state-store.d.ts.map +1 -1
- package/dist/runtime/auto-update/state-store.js +37 -3
- package/dist/runtime/auto-update/state-store.js.map +1 -1
- package/dist/runtime/auto-update/types.d.ts +51 -0
- package/dist/runtime/auto-update/types.d.ts.map +1 -1
- package/dist/runtime/auto-update/types.js.map +1 -1
- package/dist/runtime/auto-update/update-marker.d.ts +95 -0
- package/dist/runtime/auto-update/update-marker.d.ts.map +1 -0
- package/dist/runtime/auto-update/update-marker.js +185 -0
- package/dist/runtime/auto-update/update-marker.js.map +1 -0
- package/dist/runtime/auto-update/update-schedule.d.ts +57 -0
- package/dist/runtime/auto-update/update-schedule.d.ts.map +1 -0
- package/dist/runtime/auto-update/update-schedule.js +75 -0
- package/dist/runtime/auto-update/update-schedule.js.map +1 -0
- package/dist/runtime/decision-errors.d.ts +7 -0
- package/dist/runtime/decision-errors.d.ts.map +1 -1
- package/dist/runtime/decision-errors.js +15 -0
- package/dist/runtime/decision-errors.js.map +1 -1
- package/dist/senpi/client.d.ts +25 -0
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +56 -0
- package/dist/senpi/client.js.map +1 -1
- package/dist/telemetry/metrics.d.ts +14 -0
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +24 -0
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/telemetry/otel-sdk.d.ts +12 -0
- package/dist/telemetry/otel-sdk.d.ts.map +1 -1
- package/dist/telemetry/otel-sdk.js +47 -1
- package/dist/telemetry/otel-sdk.js.map +1 -1
- package/dist/telemetry/spans/span-hooks.d.ts +18 -1
- package/dist/telemetry/spans/span-hooks.d.ts.map +1 -1
- package/dist/telemetry/spans/span-hooks.js +306 -96
- package/dist/telemetry/spans/span-hooks.js.map +1 -1
- package/dist/telemetry/tool-plane.d.ts +2 -0
- package/dist/telemetry/tool-plane.d.ts.map +1 -1
- package/dist/telemetry/tool-plane.js +1 -0
- package/dist/telemetry/tool-plane.js.map +1 -1
- package/dist/tool-surface/crib-cache.d.ts +21 -0
- package/dist/tool-surface/crib-cache.d.ts.map +1 -0
- package/dist/tool-surface/crib-cache.js +54 -0
- package/dist/tool-surface/crib-cache.js.map +1 -0
- package/dist/tool-surface/crib-hook.d.ts +78 -0
- package/dist/tool-surface/crib-hook.d.ts.map +1 -0
- package/dist/tool-surface/crib-hook.js +217 -0
- package/dist/tool-surface/crib-hook.js.map +1 -0
- package/dist/tool-surface/crib-sheet.d.ts +56 -0
- package/dist/tool-surface/crib-sheet.d.ts.map +1 -0
- package/dist/tool-surface/crib-sheet.js +129 -0
- package/dist/tool-surface/crib-sheet.js.map +1 -0
- package/dist/tool-surface/mcp-tool-source.d.ts +64 -0
- package/dist/tool-surface/mcp-tool-source.d.ts.map +1 -0
- package/dist/tool-surface/mcp-tool-source.js +189 -0
- package/dist/tool-surface/mcp-tool-source.js.map +1 -0
- package/dist/utils/event-catalog.d.ts +117 -5
- package/dist/utils/event-catalog.d.ts.map +1 -1
- package/dist/utils/event-catalog.js +19 -0
- package/dist/utils/event-catalog.js.map +1 -1
- package/dist/widgets/tool.d.ts +9 -0
- package/dist/widgets/tool.d.ts.map +1 -1
- package/dist/widgets/tool.js +1 -0
- package/dist/widgets/tool.js.map +1 -1
- package/openclaw.plugin.json +11 -1
- package/package.json +5 -2
|
@@ -34,6 +34,14 @@ export declare function splitProviderModel(raw: string | undefined): {
|
|
|
34
34
|
provider?: string;
|
|
35
35
|
model?: string;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Whether the host's llm-task plugin accepts a `provider`/`model` in the call:
|
|
39
|
+
* `plugins.entries["llm-task"].llm.allowModelOverride === true` in a whole
|
|
40
|
+
* OpenClaw config. OpenClaw 10.x refuses any override without it
|
|
41
|
+
* (`openclaw-worktrees/v2026.10.10-beta.3/src/plugins/runtime/runtime-llm.runtime.ts:414-463`,
|
|
42
|
+
* policy owner = the calling plugin, `llm-task`). senpi-agent sets it (A7).
|
|
43
|
+
*/
|
|
44
|
+
export declare function llmTaskModelOverrideAllowed(config: unknown): boolean;
|
|
37
45
|
export declare class OpenClawLlmDecision implements LlmDecision {
|
|
38
46
|
private readonly gatewayUrl;
|
|
39
47
|
private readonly gatewayToken?;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openclaw-llm-decision.d.ts","sourceRoot":"","sources":["../../../src/actions/llm-decision/openclaw-llm-decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"openclaw-llm-decision.d.ts","sourceRoot":"","sources":["../../../src/actions/llm-decision/openclaw-llm-decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAwBxF,MAAM,WAAW,0BAA0B;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CASjG;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAUpE;AAoCD,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAE3B,OAAO,CAAC,EAAE,0BAA0B;IAM1C,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;CA2F9E"}
|
|
@@ -5,6 +5,7 @@ import { HEADER_ADDRESS, HEADER_CORRELATION_ID, HEADER_RECIPE_NAME, HEADER_RUNTI
|
|
|
5
5
|
import { withRetry } from '../../utils/retry.js';
|
|
6
6
|
import { isRetryableDecisionError } from '../../runtime/decision-errors.js';
|
|
7
7
|
import { context as otelContext, propagation } from '@opentelemetry/api';
|
|
8
|
+
import { getOpenClawHost } from '../../host/openclaw-host.js';
|
|
8
9
|
/** Max retries (total attempts = MAX_DECISION_RETRIES + 1) for transient llm-task failures. */
|
|
9
10
|
const MAX_DECISION_RETRIES = 2;
|
|
10
11
|
/** Fixed delay between retry attempts (ms). Backoff is intentionally simple for now. */
|
|
@@ -47,6 +48,57 @@ export function splitProviderModel(raw) {
|
|
|
47
48
|
}
|
|
48
49
|
return { provider: trimmed.slice(0, idx), model: trimmed.slice(idx + 1) };
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Whether the host's llm-task plugin accepts a `provider`/`model` in the call:
|
|
53
|
+
* `plugins.entries["llm-task"].llm.allowModelOverride === true` in a whole
|
|
54
|
+
* OpenClaw config. OpenClaw 10.x refuses any override without it
|
|
55
|
+
* (`openclaw-worktrees/v2026.10.10-beta.3/src/plugins/runtime/runtime-llm.runtime.ts:414-463`,
|
|
56
|
+
* policy owner = the calling plugin, `llm-task`). senpi-agent sets it (A7).
|
|
57
|
+
*/
|
|
58
|
+
export function llmTaskModelOverrideAllowed(config) {
|
|
59
|
+
const rec = (v) => v && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
|
|
60
|
+
const entries = rec(rec(rec(config)?.plugins)?.entries);
|
|
61
|
+
if (!entries)
|
|
62
|
+
return false;
|
|
63
|
+
// The host trims and lowercases plugin entry keys (`normalizePluginPolicyId`,
|
|
64
|
+
// 10.10 `src/plugins/plugin-policy-id.ts:11-13`), so ` LLM-Task ` is the same entry.
|
|
65
|
+
return Object.entries(entries).some(([key, entry]) => key.trim().toLowerCase() === 'llm-task' && rec(rec(entry)?.llm)?.allowModelOverride === true);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The 10.x preflight: throws when a model override is about to be sent to a
|
|
69
|
+
* modern host whose live config does not allow one. 10.x masks the host's
|
|
70
|
+
* refusal as a generic retryable 500, so without this every decision of such
|
|
71
|
+
* a strategy would retry twice and then report a transient hiccup. The thrown
|
|
72
|
+
* text classifies as the non-retryable {@link DecisionErrorCode} MODEL_OVERRIDE_NOT_ALLOWED.
|
|
73
|
+
*
|
|
74
|
+
* No-op on a legacy host, with no host record, or when no config can be read
|
|
75
|
+
* (neither `runtime.config.current()` nor the registration snapshot); the host
|
|
76
|
+
* still enforces its own policy then.
|
|
77
|
+
*/
|
|
78
|
+
function assertModelOverrideAllowed(rawModel) {
|
|
79
|
+
const host = getOpenClawHost();
|
|
80
|
+
if (!host?.modern)
|
|
81
|
+
return;
|
|
82
|
+
let config;
|
|
83
|
+
try {
|
|
84
|
+
config = host.currentConfig();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Neither the live config nor the registration snapshot could be read.
|
|
90
|
+
if (!config || typeof config !== 'object')
|
|
91
|
+
return;
|
|
92
|
+
if (llmTaskModelOverrideAllowed(config))
|
|
93
|
+
return;
|
|
94
|
+
// Read by the agent and the user (raw cause in status and logs). It names the
|
|
95
|
+
// fix that works, a box update or Senpi support, and no config path: the
|
|
96
|
+
// flag is part of the box setup the Senpi agent image writes at boot, and a
|
|
97
|
+
// hand edit of the gateway config is not a fix the agent should make.
|
|
98
|
+
throw new Error(`llm-task model override not allowed: decision_model "${rawModel}" was not used because ` +
|
|
99
|
+
"this box's OpenClaw setup does not allow llm-task model overrides, a setting the Senpi agent image applies at boot. " +
|
|
100
|
+
'This is a box setup problem, not a strategy problem: a box update from Senpi support fixes it; it cannot be fixed from inside the agent.');
|
|
101
|
+
}
|
|
50
102
|
export class OpenClawLlmDecision {
|
|
51
103
|
gatewayUrl;
|
|
52
104
|
gatewayToken;
|
|
@@ -73,7 +125,10 @@ export class OpenClawLlmDecision {
|
|
|
73
125
|
// (extensions/llm-task/src/llm-task-tool.ts).
|
|
74
126
|
timeoutMs: 90_000,
|
|
75
127
|
};
|
|
76
|
-
const
|
|
128
|
+
const rawModel = request.model ?? this.defaultModel;
|
|
129
|
+
const { provider, model } = splitProviderModel(rawModel);
|
|
130
|
+
if (provider || model)
|
|
131
|
+
assertModelOverrideAllowed(rawModel?.trim() ?? '');
|
|
77
132
|
if (provider)
|
|
78
133
|
args.provider = provider;
|
|
79
134
|
if (model)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openclaw-llm-decision.js","sourceRoot":"","sources":["../../../src/actions/llm-decision/openclaw-llm-decision.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"openclaw-llm-decision.js","sourceRoot":"","sources":["../../../src/actions/llm-decision/openclaw-llm-decision.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,21 +14,51 @@ export declare const TOOL_APPROVALS_STATE_FILE = "tool-approvals.json";
|
|
|
14
14
|
* redeploys, and plugin auto-updates.
|
|
15
15
|
*
|
|
16
16
|
* To make a tool prompt again, delete its entry from `tool-approvals.json`
|
|
17
|
-
* (or the whole file) in the state dir.
|
|
17
|
+
* (or the whole file) in the state dir. That takes effect at the next gated
|
|
18
|
+
* call, with no restart.
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* The file is the source of truth, shared by every registry the host builds
|
|
21
|
+
* (chat, cron, …), each with its own store (RT566-02, RT566-10):
|
|
22
|
+
* - before every decision the store `stat`s the file and re-reads it only when
|
|
23
|
+
* its mtime, size or inode changed since the last read;
|
|
24
|
+
* - a missing file, or one that cannot be read or parsed, means no grants
|
|
25
|
+
* (every gated tool prompts); a bad file is logged once per version;
|
|
26
|
+
* - a write is read-modify-write: re-read the file, add this one grant, write
|
|
27
|
+
* atomically (temp → rename). It never drops a grant another registry wrote
|
|
28
|
+
* and never brings back one the user revoked.
|
|
21
29
|
*/
|
|
22
30
|
export declare class ToolApprovalStore {
|
|
23
31
|
private readonly logger?;
|
|
24
32
|
private readonly filePath;
|
|
25
|
-
private
|
|
33
|
+
private allowAlways;
|
|
34
|
+
/** Signature of the file version `allowAlways` was read from (undefined: never read). */
|
|
35
|
+
private loadedSignature;
|
|
36
|
+
/** File version a grant was last refused a write for (warn once per version). */
|
|
37
|
+
private unwritableWarnedSignature;
|
|
26
38
|
constructor(stateDir: string, logger?: LoggerLike | undefined);
|
|
27
|
-
private
|
|
39
|
+
private signature;
|
|
40
|
+
/** One `stat`; re-read only when the file changed since the last read. */
|
|
41
|
+
private refresh;
|
|
42
|
+
/**
|
|
43
|
+
* Read and parse the file; any failure is no grants. `ok` is false when the
|
|
44
|
+
* file exists but cannot be read or parsed (a missing file is ok and empty).
|
|
45
|
+
*/
|
|
46
|
+
private readFile;
|
|
28
47
|
isAllowedAlways(tool: string): boolean;
|
|
29
48
|
get size(): number;
|
|
30
|
-
/**
|
|
31
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Record a user's allow-always decision and persist it immediately.
|
|
51
|
+
*
|
|
52
|
+
* @returns true when the grant is in the state file, false when it is kept in
|
|
53
|
+
* memory only (the file could not be read or written; a warn names why).
|
|
54
|
+
*/
|
|
55
|
+
rememberAllowAlways(tool: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Write atomically. Returns the written file's signature, taken from the temp
|
|
58
|
+
* file before the rename (rename keeps inode, mtime and size), so a write by
|
|
59
|
+
* someone else right after the rename is seen at the next decision; undefined
|
|
60
|
+
* when the write failed.
|
|
61
|
+
*/
|
|
32
62
|
private persist;
|
|
33
63
|
}
|
|
34
64
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-store.d.ts","sourceRoot":"","sources":["../../src/approvals/approval-store.ts"],"names":[],"mappings":"AAGA,iFAAiF;AACjF,UAAU,UAAU;IAClB,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAChC;AAED,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"approval-store.d.ts","sourceRoot":"","sources":["../../src/approvals/approval-store.ts"],"names":[],"mappings":"AAGA,iFAAiF;AACjF,UAAU,UAAU;IAClB,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAChC;AAED,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;AAc/D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,iBAAiB;IAU1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAT1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,WAAW,CAAuC;IAC1D,yFAAyF;IACzF,OAAO,CAAC,eAAe,CAA4B;IACnD,iFAAiF;IACjF,OAAO,CAAC,yBAAyB,CAA4B;gBAG3D,QAAQ,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,UAAU,YAAA;IAMtC,OAAO,CAAC,SAAS;IAcjB,0EAA0E;IAC1E,OAAO,CAAC,OAAO;IAWf;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAuChB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKtC,IAAI,IAAI,IAAI,MAAM,CAGjB;IAED;;;;;OAKG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAuC1C;;;;;OAKG;IACH,OAAO,CAAC,OAAO;CAqBhB"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
/** File name inside the plugin state dir (volume-backed in prod). */
|
|
4
4
|
export const TOOL_APPROVALS_STATE_FILE = "tool-approvals.json";
|
|
5
|
+
const MISSING = "missing";
|
|
5
6
|
/**
|
|
6
7
|
* Persistent per-tool "always allow" decisions for the MCP tool approval gate.
|
|
7
8
|
*
|
|
@@ -12,31 +13,75 @@ export const TOOL_APPROVALS_STATE_FILE = "tool-approvals.json";
|
|
|
12
13
|
* redeploys, and plugin auto-updates.
|
|
13
14
|
*
|
|
14
15
|
* To make a tool prompt again, delete its entry from `tool-approvals.json`
|
|
15
|
-
* (or the whole file) in the state dir.
|
|
16
|
+
* (or the whole file) in the state dir. That takes effect at the next gated
|
|
17
|
+
* call, with no restart.
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
+
* The file is the source of truth, shared by every registry the host builds
|
|
20
|
+
* (chat, cron, …), each with its own store (RT566-02, RT566-10):
|
|
21
|
+
* - before every decision the store `stat`s the file and re-reads it only when
|
|
22
|
+
* its mtime, size or inode changed since the last read;
|
|
23
|
+
* - a missing file, or one that cannot be read or parsed, means no grants
|
|
24
|
+
* (every gated tool prompts); a bad file is logged once per version;
|
|
25
|
+
* - a write is read-modify-write: re-read the file, add this one grant, write
|
|
26
|
+
* atomically (temp → rename). It never drops a grant another registry wrote
|
|
27
|
+
* and never brings back one the user revoked.
|
|
19
28
|
*/
|
|
20
29
|
export class ToolApprovalStore {
|
|
21
30
|
logger;
|
|
22
31
|
filePath;
|
|
23
32
|
allowAlways = new Map();
|
|
33
|
+
/** Signature of the file version `allowAlways` was read from (undefined: never read). */
|
|
34
|
+
loadedSignature;
|
|
35
|
+
/** File version a grant was last refused a write for (warn once per version). */
|
|
36
|
+
unwritableWarnedSignature;
|
|
24
37
|
constructor(stateDir, logger) {
|
|
25
38
|
this.logger = logger;
|
|
26
39
|
this.filePath = join(stateDir, TOOL_APPROVALS_STATE_FILE);
|
|
27
|
-
this.
|
|
40
|
+
this.refresh();
|
|
28
41
|
}
|
|
29
|
-
|
|
42
|
+
signature(path = this.filePath) {
|
|
43
|
+
try {
|
|
44
|
+
const st = statSync(path);
|
|
45
|
+
return `${st.mtimeMs}:${st.size}:${st.ino}`;
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (isNotFound(err)) {
|
|
49
|
+
return MISSING;
|
|
50
|
+
}
|
|
51
|
+
// Unknown state: a fresh signature forces a read, which fails the same
|
|
52
|
+
// way and reads as no grants (fail closed: the tool prompts).
|
|
53
|
+
return `stat-error:${errMessage(err)}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** One `stat`; re-read only when the file changed since the last read. */
|
|
57
|
+
refresh() {
|
|
58
|
+
const sig = this.signature();
|
|
59
|
+
if (sig === this.loadedSignature) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Taken before the read: if the file changes in between, the next call's
|
|
63
|
+
// signature differs and it reads again.
|
|
64
|
+
this.loadedSignature = sig;
|
|
65
|
+
this.allowAlways = sig === MISSING ? new Map() : this.readFile(true).grants;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Read and parse the file; any failure is no grants. `ok` is false when the
|
|
69
|
+
* file exists but cannot be read or parsed (a missing file is ok and empty).
|
|
70
|
+
*/
|
|
71
|
+
readFile(warn) {
|
|
72
|
+
const grants = new Map();
|
|
30
73
|
let raw;
|
|
31
74
|
try {
|
|
32
75
|
raw = readFileSync(this.filePath, "utf8");
|
|
33
76
|
}
|
|
34
77
|
catch (err) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.logger?.warn(`[senpi-tool-approvals] failed to read state file, starting empty | path=${this.filePath} err=${err instanceof Error ? err.message : String(err)}`);
|
|
78
|
+
if (isNotFound(err)) {
|
|
79
|
+
return { grants, ok: true };
|
|
38
80
|
}
|
|
39
|
-
|
|
81
|
+
if (warn) {
|
|
82
|
+
this.logger?.warn(`[senpi-tool-approvals] failed to read state file, treating it as no grants (all gated tools will prompt) | path=${this.filePath} err=${errMessage(err)}`);
|
|
83
|
+
}
|
|
84
|
+
return { grants, ok: false };
|
|
40
85
|
}
|
|
41
86
|
try {
|
|
42
87
|
const parsed = JSON.parse(raw);
|
|
@@ -44,7 +89,7 @@ export class ToolApprovalStore {
|
|
|
44
89
|
if (entries && typeof entries === "object" && !Array.isArray(entries)) {
|
|
45
90
|
for (const [tool, entry] of Object.entries(entries)) {
|
|
46
91
|
if (typeof tool === "string" && tool.length > 0) {
|
|
47
|
-
|
|
92
|
+
grants.set(tool, {
|
|
48
93
|
decidedAt: typeof entry?.decidedAt === "string" ? entry.decidedAt : "",
|
|
49
94
|
});
|
|
50
95
|
}
|
|
@@ -52,36 +97,95 @@ export class ToolApprovalStore {
|
|
|
52
97
|
}
|
|
53
98
|
}
|
|
54
99
|
catch (err) {
|
|
55
|
-
|
|
100
|
+
if (warn) {
|
|
101
|
+
this.logger?.warn(`[senpi-tool-approvals] corrupt state file, treating it as no grants (all gated tools will prompt) | path=${this.filePath} err=${errMessage(err)}`);
|
|
102
|
+
}
|
|
103
|
+
return { grants: new Map(), ok: false };
|
|
56
104
|
}
|
|
105
|
+
return { grants, ok: true };
|
|
57
106
|
}
|
|
58
107
|
isAllowedAlways(tool) {
|
|
108
|
+
this.refresh();
|
|
59
109
|
return this.allowAlways.has(tool);
|
|
60
110
|
}
|
|
61
111
|
get size() {
|
|
112
|
+
this.refresh();
|
|
62
113
|
return this.allowAlways.size;
|
|
63
114
|
}
|
|
64
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Record a user's allow-always decision and persist it immediately.
|
|
117
|
+
*
|
|
118
|
+
* @returns true when the grant is in the state file, false when it is kept in
|
|
119
|
+
* memory only (the file could not be read or written; a warn names why).
|
|
120
|
+
*/
|
|
65
121
|
rememberAllowAlways(tool) {
|
|
66
|
-
if (!tool
|
|
67
|
-
return;
|
|
122
|
+
if (!tool) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (this.isAllowedAlways(tool)) {
|
|
126
|
+
// Already granted: in the file unless an earlier grant was kept in memory only.
|
|
127
|
+
const { grants, ok } = this.readFile(false);
|
|
128
|
+
return ok && grants.has(tool);
|
|
129
|
+
}
|
|
130
|
+
const entry = { decidedAt: new Date().toISOString() };
|
|
131
|
+
// Read-modify-write against the file as it is now, not this store's copy.
|
|
132
|
+
// The signature is taken before the read, so a write landing after it makes
|
|
133
|
+
// the next decision re-read.
|
|
134
|
+
const before = this.signature();
|
|
135
|
+
const { grants: merged, ok } = this.readFile(false);
|
|
136
|
+
if (!ok) {
|
|
137
|
+
// Writing now would replace the user's other grants with this one. Keep
|
|
138
|
+
// it in memory until the file next changes; the file stays as it is.
|
|
139
|
+
if (this.unwritableWarnedSignature !== before) {
|
|
140
|
+
this.unwritableWarnedSignature = before;
|
|
141
|
+
this.logger?.warn(`[senpi-tool-approvals] state file unreadable, allow-always not written (kept in memory until the file changes) | path=${this.filePath} tool=${tool}`);
|
|
142
|
+
}
|
|
143
|
+
// Add to what this store holds for this file version (earlier
|
|
144
|
+
// in-memory-only grants included), not to the empty failed read.
|
|
145
|
+
// loadedSignature stays the one from the refresh above, so a change since
|
|
146
|
+
// then is re-read at the next decision.
|
|
147
|
+
this.allowAlways.set(tool, entry);
|
|
148
|
+
return false;
|
|
68
149
|
}
|
|
69
|
-
|
|
70
|
-
|
|
150
|
+
merged.set(tool, entry);
|
|
151
|
+
// On a failed write the grant stays in memory until the file next changes.
|
|
152
|
+
this.allowAlways = merged;
|
|
153
|
+
const written = this.persist(merged);
|
|
154
|
+
this.loadedSignature = written ?? before;
|
|
155
|
+
return written !== undefined;
|
|
71
156
|
}
|
|
72
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Write atomically. Returns the written file's signature, taken from the temp
|
|
159
|
+
* file before the rename (rename keeps inode, mtime and size), so a write by
|
|
160
|
+
* someone else right after the rename is seen at the next decision; undefined
|
|
161
|
+
* when the write failed.
|
|
162
|
+
*/
|
|
163
|
+
persist(grants) {
|
|
164
|
+
const tmpPath = `${this.filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
73
165
|
try {
|
|
74
166
|
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
75
|
-
const shape = {
|
|
76
|
-
allowAlways: Object.fromEntries(this.allowAlways),
|
|
77
|
-
};
|
|
78
|
-
const tmpPath = `${this.filePath}.tmp`;
|
|
167
|
+
const shape = { allowAlways: Object.fromEntries(grants) };
|
|
79
168
|
writeFileSync(tmpPath, JSON.stringify(shape, null, 2), "utf8");
|
|
169
|
+
const written = this.signature(tmpPath);
|
|
80
170
|
renameSync(tmpPath, this.filePath);
|
|
171
|
+
return written;
|
|
81
172
|
}
|
|
82
173
|
catch (err) {
|
|
83
|
-
|
|
174
|
+
try {
|
|
175
|
+
unlinkSync(tmpPath);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Nothing to clean up.
|
|
179
|
+
}
|
|
180
|
+
this.logger?.warn(`[senpi-tool-approvals] failed to persist state (decision kept in memory only) | path=${this.filePath} err=${errMessage(err)}`);
|
|
181
|
+
return undefined;
|
|
84
182
|
}
|
|
85
183
|
}
|
|
86
184
|
}
|
|
185
|
+
function isNotFound(err) {
|
|
186
|
+
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
187
|
+
}
|
|
188
|
+
function errMessage(err) {
|
|
189
|
+
return err instanceof Error ? err.message : String(err);
|
|
190
|
+
}
|
|
87
191
|
//# sourceMappingURL=approval-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-store.js","sourceRoot":"","sources":["../../src/approvals/approval-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"approval-store.js","sourceRoot":"","sources":["../../src/approvals/approval-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { TurnKind } from "./turn-context.js";
|
|
2
|
+
/**
|
|
3
|
+
* How an approval ended: a user choice (`allow-once`, `allow-always`,
|
|
4
|
+
* `deny`), a missed prompt (`timeout`), or `cancelled`: the run was aborted
|
|
5
|
+
* while the prompt was open, the host had no route or surface to ask (on
|
|
6
|
+
* 10.x, before any tool runs), or the gate itself stopped a gated call in a
|
|
7
|
+
* scheduled turn (`cancelReason: "scheduled_turn"`) or in another run that is
|
|
8
|
+
* not a direct chat turn (`"not_chat_turn"`, D15). The wait time tells a user
|
|
9
|
+
* abort (long) from a no-route cancellation (milliseconds). `granted` is not a
|
|
10
|
+
* prompt outcome: the call ran under a stored "Always allow" grant with no
|
|
11
|
+
* prompt (D16), recorded with its run kind.
|
|
12
|
+
*/
|
|
13
|
+
export type ApprovalDecision = "allow-once" | "allow-always" | "deny" | "timeout" | "cancelled" | "granted";
|
|
14
|
+
/** Where an approval happened, for O7's per-turn-kind queries. Every field is optional. */
|
|
15
|
+
export interface ApprovalResolutionContext {
|
|
16
|
+
/** The turn's session key; stamped as `gen_ai.conversation.id` (the span convention). */
|
|
17
|
+
sessionKey?: string;
|
|
18
|
+
/** `chat` | `cron` | `heartbeat` | `other` | `unknown`; stamped as `senpi.turn.kind`. */
|
|
19
|
+
turnKind?: TurnKind;
|
|
20
|
+
/** Why the gate cancelled the call itself; stamped as `senpi.tool_approval.cancel_reason`. */
|
|
21
|
+
cancelReason?: "scheduled_turn" | "not_chat_turn";
|
|
22
|
+
}
|
|
3
23
|
/**
|
|
4
24
|
* Telemetry seam for the MCP tool approval gate.
|
|
5
25
|
*
|
|
@@ -17,8 +37,12 @@ export interface GateTelemetry {
|
|
|
17
37
|
* deny path, `deny` vs `timeout`). Emits the `tool_approval.resolved` event
|
|
18
38
|
* (info for allows, warn for deny/timeout) + `tool_approval.decisions` metric,
|
|
19
39
|
* so allow-always adoption and deny/timeout rates per tool are chartable.
|
|
40
|
+
*
|
|
41
|
+
* @param waitMs - From the gate's `requireApproval` to the resolution (0 when
|
|
42
|
+
* the gate cancelled the call itself); `senpi.tool_approval.wait_ms`.
|
|
43
|
+
* @param context - The turn's session key and kind (see {@link ApprovalResolutionContext}).
|
|
20
44
|
*/
|
|
21
|
-
resolved(toolName: string, decision: ApprovalDecision, isTrade: boolean): void;
|
|
45
|
+
resolved(toolName: string, decision: ApprovalDecision, isTrade: boolean, waitMs?: number, context?: ApprovalResolutionContext): void;
|
|
22
46
|
/**
|
|
23
47
|
* A user deny or a 10-minute timeout — a state-changing tool the user did NOT
|
|
24
48
|
* authorize, so it did not run. Recorded on the origin-uniform `tool.called`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/approvals/telemetry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/approvals/telemetry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAE5G,2FAA2F;AAC3F,MAAM,WAAW,yBAAyB;IACxC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAAC;CACnD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CACN,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,GAClC,IAAI,CAAC;IACR;;;;;;;;OAQG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,kBAAkB,GAAG,IAAI,CAAC;IAC3E;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;CAC7D;AAID,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,EAAE,aAgClC,CAAC"}
|
|
@@ -5,17 +5,21 @@ import { createLogger } from "../utils/logger.js";
|
|
|
5
5
|
const eventLogger = createLogger("[senpi-tool-approvals]");
|
|
6
6
|
/** Production wiring: real event + metric planes. */
|
|
7
7
|
export const defaultGateTelemetry = {
|
|
8
|
-
resolved(toolName, decision, isTrade) {
|
|
9
|
-
const
|
|
10
|
-
eventLogger.event(EVENT_NAME.toolApprovalResolved,
|
|
8
|
+
resolved(toolName, decision, isTrade, waitMs, context) {
|
|
9
|
+
const notRun = decision === "deny" || decision === "timeout" || decision === "cancelled";
|
|
10
|
+
eventLogger.event(EVENT_NAME.toolApprovalResolved, notRun ? "warn" : "info", {
|
|
11
11
|
body: `tool approval ${decision} for ${toolName}`,
|
|
12
12
|
attributes: eventAttrs({
|
|
13
13
|
"gen_ai.tool.name": toolName,
|
|
14
14
|
"senpi.tool_approval.decision": decision,
|
|
15
15
|
"senpi.tool.is_trade": String(isTrade),
|
|
16
|
+
"senpi.tool_approval.wait_ms": typeof waitMs === "number" && Number.isFinite(waitMs) ? Math.max(0, Math.round(waitMs)) : undefined,
|
|
17
|
+
"gen_ai.conversation.id": context?.sessionKey,
|
|
18
|
+
"senpi.turn.kind": context?.turnKind,
|
|
19
|
+
"senpi.tool_approval.cancel_reason": context?.cancelReason,
|
|
16
20
|
}),
|
|
17
21
|
});
|
|
18
|
-
recordApprovalDecision({ toolName, decision, isTrade });
|
|
22
|
+
recordApprovalDecision({ toolName, decision, isTrade, turnKind: context?.turnKind });
|
|
19
23
|
},
|
|
20
24
|
denied(toolName, reason) {
|
|
21
25
|
emitToolCalled({ name: toolName, origin: "agent", outcome: "denied", errorType: reason });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/approvals/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAiB,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/approvals/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAiB,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAyElD,MAAM,WAAW,GAAG,YAAY,CAAC,wBAAwB,CAAC,CAAC;AAE3D,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAkB;IACjD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;QACnD,MAAM,MAAM,GAAG,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,WAAW,CAAC;QACzF,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE;YAC3E,IAAI,EAAE,iBAAiB,QAAQ,QAAQ,QAAQ,EAAE;YACjD,UAAU,EAAE,UAAU,CAAmD;gBACvE,kBAAkB,EAAE,QAAQ;gBAC5B,8BAA8B,EAAE,QAAQ;gBACxC,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC;gBACtC,6BAA6B,EAC3B,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;gBACrG,wBAAwB,EAAE,OAAO,EAAE,UAAU;gBAC7C,iBAAiB,EAAE,OAAO,EAAE,QAAQ;gBACpC,mCAAmC,EAAE,OAAO,EAAE,YAAY;aAC3D,CAAC;SACH,CAAC,CAAC;QACH,sBAAsB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,CAAC,QAAQ,EAAE,MAAM;QACrB,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1F,cAAc,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,SAAS,CAAC,GAAG,EAAE,QAAQ;QACrB,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,qBAAqB,EAAE,OAAO,EAAE;YAC3D,IAAI,EAAE,iCAAiC,QAAQ,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YAC3E,UAAU,EAAE,UAAU,CAAoD;gBACxE,kBAAkB,EAAE,QAAQ;gBAC5B,YAAY,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;aAC1D,CAAC;YACF,MAAM,EAAE,EAAE,aAAa,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;SAC5E,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ToolApprovalStore } from "./approval-store.js";
|
|
2
2
|
import { type ToolApprovalsManifest, type ToolApprovalsMode } from "./manifest.js";
|
|
3
3
|
import { type GateTelemetry } from "./telemetry.js";
|
|
4
|
+
import type { TurnContextReader, TurnKind } from "./turn-context.js";
|
|
4
5
|
/**
|
|
5
6
|
* Tool approval gate: requires an interactive user approval before
|
|
6
7
|
* state-changing senpi MCP tools execute.
|
|
@@ -77,6 +78,37 @@ export declare function describeSenpiToolCall(tool: string, params: unknown): st
|
|
|
77
78
|
* gateway's description limit, which rejects (not truncates) anything longer.
|
|
78
79
|
*/
|
|
79
80
|
export declare function buildApprovalDescription(tool: string, params: unknown): string;
|
|
81
|
+
/**
|
|
82
|
+
* Counts approval prompts the gate has asked for and not yet seen resolved
|
|
83
|
+
* (trade tools, and in `all` mode every gated Senpi tool).
|
|
84
|
+
*
|
|
85
|
+
* Read by the self-update idle gate (`runtime/auto-update/idle-gate.ts`): a
|
|
86
|
+
* restart while a user is looking at an approval prompt would cancel it. An
|
|
87
|
+
* entry ends on any `onResolution` decision (including `cancelled`), or
|
|
88
|
+
* expires `timeoutMs + slackMs` after it began, so a prompt the gateway never
|
|
89
|
+
* resolved cannot keep the box busy forever.
|
|
90
|
+
*/
|
|
91
|
+
export declare class PendingApprovalTracker {
|
|
92
|
+
private readonly slackMs;
|
|
93
|
+
private readonly now;
|
|
94
|
+
private readonly open;
|
|
95
|
+
private nextId;
|
|
96
|
+
/**
|
|
97
|
+
* @param slackMs - Grace past the prompt's own timeout before an unresolved entry is dropped.
|
|
98
|
+
* @param now - Clock, for tests.
|
|
99
|
+
*/
|
|
100
|
+
constructor(slackMs?: number, now?: () => number);
|
|
101
|
+
/**
|
|
102
|
+
* Records a prompt that will time out after `timeoutMs`.
|
|
103
|
+
*
|
|
104
|
+
* @returns A handle for {@link end}.
|
|
105
|
+
*/
|
|
106
|
+
begin(timeoutMs: number): number;
|
|
107
|
+
/** Marks a prompt resolved. Unknown or repeated handles are ignored. */
|
|
108
|
+
end(id: number): void;
|
|
109
|
+
/** Prompts still waiting for an answer. */
|
|
110
|
+
count(): number;
|
|
111
|
+
}
|
|
80
112
|
export interface RegisterToolApprovalGateOptions {
|
|
81
113
|
api: ToolApprovalGatePluginApi;
|
|
82
114
|
mode: ToolApprovalsMode;
|
|
@@ -87,7 +119,54 @@ export interface RegisterToolApprovalGateOptions {
|
|
|
87
119
|
manifest?: ToolApprovalsManifest;
|
|
88
120
|
/** Injectable for tests; defaults to the real event/metric planes. */
|
|
89
121
|
telemetry?: GateTelemetry;
|
|
122
|
+
/** Counts open prompts for the self-update idle gate. Optional; nothing is counted without it. */
|
|
123
|
+
pendingApprovals?: PendingApprovalTracker;
|
|
124
|
+
/**
|
|
125
|
+
* The turn of each gated call (session key, chat / cron / heartbeat), for
|
|
126
|
+
* approval telemetry and the scheduled-turn block. Without it every call
|
|
127
|
+
* reads `unknown` and nothing is blocked.
|
|
128
|
+
*/
|
|
129
|
+
turnContext?: TurnContextReader;
|
|
130
|
+
/**
|
|
131
|
+
* The host lets the runtime classify runs (`turnClassificationAvailable`,
|
|
132
|
+
* one boot-time fact). Only then is a gated call outside a direct chat turn
|
|
133
|
+
* blocked with {@link scheduledTurnBlockReason}; without it every run reads
|
|
134
|
+
* `unknown` and keeps the prompt, as before this release, so a box missing
|
|
135
|
+
* the setting never blocks a chat trade.
|
|
136
|
+
*/
|
|
137
|
+
turnClassification?: boolean;
|
|
138
|
+
/** Clock for the approval wait time. Default `Date.now`. */
|
|
139
|
+
now?: () => number;
|
|
90
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* The text the model reads when the gate stops a gated call in a scheduled
|
|
143
|
+
* turn (D13). On 10.10-beta.3 the host's own outcome for such an approval is
|
|
144
|
+
* a sentence the plugin cannot change and the model cannot act on: for a tool
|
|
145
|
+
* whose hook context carries the run trigger it cancels with "Plugin approval
|
|
146
|
+
* unavailable: cron runs have no approval-capable initiating surface."
|
|
147
|
+
* (`openclaw-worktrees/v2026.10.10-beta.3/src/agents/agent-tools.before-tool-call.approval.ts:161-189,310-320`);
|
|
148
|
+
* the `senpi__` MCP tools are wrapped without the trigger
|
|
149
|
+
* (`src/agents/embedded-agent-runner/run/attempt-tool-prepare.ts:468-485`), so
|
|
150
|
+
* the host asks the gateway instead, which fails with "Plugin approval
|
|
151
|
+
* unavailable (no approval route)" (`:371`) when no approval client is
|
|
152
|
+
* connected. The gate blocks first, in every scheduled turn. This text says
|
|
153
|
+
* what happened and the one step that works: the user asks in chat, where the
|
|
154
|
+
* approval prompt can be shown. It names no other route.
|
|
155
|
+
*
|
|
156
|
+
* D14 (ruled 2026-10-24): the block is outright. When an approval client is
|
|
157
|
+
* connected, 10.10 would route a `senpi__` approval from a scheduled run to
|
|
158
|
+
* it; scheduled runs still never get a prompt, even with the user online.
|
|
159
|
+
* D15: the same block covers every run not positively classified as a direct
|
|
160
|
+
* chat turn (`other`, `unknown`, a call with no run id), with the
|
|
161
|
+
* not-a-chat-turn wording. D16: a stored "Always allow" grant is checked
|
|
162
|
+
* first and wins in every run kind; this block only stops prompts.
|
|
163
|
+
*/
|
|
164
|
+
export declare function scheduledTurnBlockReason(params: {
|
|
165
|
+
wireName: string;
|
|
166
|
+
kind: Exclude<TurnKind, "chat">;
|
|
167
|
+
isTrade: boolean;
|
|
168
|
+
summary?: string;
|
|
169
|
+
}): string;
|
|
91
170
|
/**
|
|
92
171
|
* Register the `before_tool_call` approval gate. No-op in `off` mode.
|
|
93
172
|
* Returns the store (for tests/introspection) when registered.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-approval-gate.d.ts","sourceRoot":"","sources":["../../src/approvals/tool-approval-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAC;AACvB,OAAO,
|
|
1
|
+
{"version":3,"file":"tool-approval-gate.d.ts","sourceRoot":"","sources":["../../src/approvals/tool-approval-gate.ts"],"names":[],"mappings":"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"}
|