@te-river/opencode-team-mode 1.6.2 → 1.7.0
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/README.md +117 -118
- package/README.zh-CN.md +98 -100
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +89 -79
- package/dist/agents.js.map +1 -1
- package/dist/approval-gate.d.ts +8 -9
- package/dist/approval-gate.d.ts.map +1 -1
- package/dist/approval-gate.js +13 -14
- package/dist/approval-gate.js.map +1 -1
- package/dist/capabilities.d.ts +11 -37
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +11 -141
- package/dist/capabilities.js.map +1 -1
- package/dist/commands.js +1 -1
- package/dist/envprotect/hook.d.ts +12 -31
- package/dist/envprotect/hook.d.ts.map +1 -1
- package/dist/envprotect/hook.js +13 -91
- package/dist/envprotect/hook.js.map +1 -1
- package/dist/envprotect/patterns.d.ts +6 -4
- package/dist/envprotect/patterns.d.ts.map +1 -1
- package/dist/envprotect/patterns.js +6 -4
- package/dist/envprotect/patterns.js.map +1 -1
- package/dist/envprotect.d.ts +1 -1
- package/dist/envprotect.d.ts.map +1 -1
- package/dist/envprotect.js +1 -1
- package/dist/envprotect.js.map +1 -1
- package/dist/host/v2-browser-gate.d.ts +19 -1
- package/dist/host/v2-browser-gate.d.ts.map +1 -1
- package/dist/host/v2-browser-gate.js +47 -6
- package/dist/host/v2-browser-gate.js.map +1 -1
- package/dist/host/v2-capabilities.d.ts +33 -0
- package/dist/host/v2-capabilities.d.ts.map +1 -1
- package/dist/host/v2-capabilities.js +50 -1
- package/dist/host/v2-capabilities.js.map +1 -1
- package/dist/host/v2-compaction.d.ts +129 -0
- package/dist/host/v2-compaction.d.ts.map +1 -0
- package/dist/host/v2-compaction.js +346 -0
- package/dist/host/v2-compaction.js.map +1 -0
- package/dist/host/v2-default-agent.d.ts +37 -0
- package/dist/host/v2-default-agent.d.ts.map +1 -0
- package/dist/host/v2-default-agent.js +128 -0
- package/dist/host/v2-default-agent.js.map +1 -0
- package/dist/host/v2-events.d.ts +2 -0
- package/dist/host/v2-events.d.ts.map +1 -1
- package/dist/host/v2-events.js +27 -0
- package/dist/host/v2-events.js.map +1 -1
- package/dist/host/v2-guard.d.ts +178 -0
- package/dist/host/v2-guard.d.ts.map +1 -1
- package/dist/host/v2-guard.js +324 -8
- package/dist/host/v2-guard.js.map +1 -1
- package/dist/host/v2-offload.d.ts +1 -1
- package/dist/host/v2-offload.js +2 -2
- package/dist/host/v2-permissions.d.ts +45 -44
- package/dist/host/v2-permissions.d.ts.map +1 -1
- package/dist/host/v2-permissions.js +48 -49
- package/dist/host/v2-permissions.js.map +1 -1
- package/dist/host/v2-probe-chain.d.ts +93 -0
- package/dist/host/v2-probe-chain.d.ts.map +1 -0
- package/dist/host/v2-probe-chain.js +146 -0
- package/dist/host/v2-probe-chain.js.map +1 -0
- package/dist/host/v2-prune.d.ts +157 -0
- package/dist/host/v2-prune.d.ts.map +1 -0
- package/dist/host/v2-prune.js +507 -0
- package/dist/host/v2-prune.js.map +1 -0
- package/dist/host/v2-retry.d.ts +143 -0
- package/dist/host/v2-retry.d.ts.map +1 -0
- package/dist/host/v2-retry.js +315 -0
- package/dist/host/v2-retry.js.map +1 -0
- package/dist/host/v2-scope.d.ts.map +1 -1
- package/dist/host/v2-scope.js +14 -7
- package/dist/host/v2-scope.js.map +1 -1
- package/dist/host/v2-session-client.d.ts +24 -0
- package/dist/host/v2-session-client.d.ts.map +1 -1
- package/dist/host/v2-session-client.js +243 -3
- package/dist/host/v2-session-client.js.map +1 -1
- package/dist/host/v2-session.d.ts +72 -1
- package/dist/host/v2-session.d.ts.map +1 -1
- package/dist/host/v2-session.js +154 -10
- package/dist/host/v2-session.js.map +1 -1
- package/dist/host/v2-tool.d.ts +2 -3
- package/dist/host/v2-tool.d.ts.map +1 -1
- package/dist/host/v2-tool.js +2 -3
- package/dist/host/v2-tool.js.map +1 -1
- package/dist/host/v2-types.d.ts +25 -0
- package/dist/host/v2-types.d.ts.map +1 -1
- package/dist/host/v2.d.ts +14 -12
- package/dist/host/v2.d.ts.map +1 -1
- package/dist/host/v2.js +500 -73
- package/dist/host/v2.js.map +1 -1
- package/dist/identity.d.ts +27 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +34 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +9 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -27
- package/dist/index.js.map +1 -1
- package/dist/prompts/lead.d.ts +1 -1
- package/dist/prompts/lead.d.ts.map +1 -1
- package/dist/prompts/lead.js +38 -25
- package/dist/prompts/lead.js.map +1 -1
- package/dist/prompts/shared.d.ts +2 -2
- package/dist/prompts/shared.d.ts.map +1 -1
- package/dist/prompts/shared.js +66 -56
- package/dist/prompts/shared.js.map +1 -1
- package/dist/prompts/specialists.d.ts +2 -2
- package/dist/prompts/specialists.d.ts.map +1 -1
- package/dist/prompts/specialists.js +53 -83
- package/dist/prompts/specialists.js.map +1 -1
- package/dist/tm/args-schema.d.ts +7 -32
- package/dist/tm/args-schema.d.ts.map +1 -1
- package/dist/tm/args-schema.js +12 -153
- package/dist/tm/args-schema.js.map +1 -1
- package/dist/tm/board.d.ts +10 -9
- package/dist/tm/board.d.ts.map +1 -1
- package/dist/tm/board.js +11 -10
- package/dist/tm/board.js.map +1 -1
- package/dist/tm/config-files.d.ts +81 -0
- package/dist/tm/config-files.d.ts.map +1 -0
- package/dist/tm/config-files.js +209 -0
- package/dist/tm/config-files.js.map +1 -0
- package/dist/tm/config-layers.d.ts +117 -0
- package/dist/tm/config-layers.d.ts.map +1 -0
- package/dist/tm/config-layers.js +320 -0
- package/dist/tm/config-layers.js.map +1 -0
- package/dist/tm/config.d.ts +10 -101
- package/dist/tm/config.d.ts.map +1 -1
- package/dist/tm/config.js +17 -71
- package/dist/tm/config.js.map +1 -1
- package/dist/tm/dispatch.d.ts +115 -21
- package/dist/tm/dispatch.d.ts.map +1 -1
- package/dist/tm/dispatch.js +424 -67
- package/dist/tm/dispatch.js.map +1 -1
- package/dist/tm/egress.d.ts +19 -7
- package/dist/tm/egress.d.ts.map +1 -1
- package/dist/tm/egress.js +21 -9
- package/dist/tm/egress.js.map +1 -1
- package/dist/tm/index.d.ts +23 -20
- package/dist/tm/index.d.ts.map +1 -1
- package/dist/tm/index.js +117 -116
- package/dist/tm/index.js.map +1 -1
- package/dist/tm/ledger.d.ts +9 -2
- package/dist/tm/ledger.d.ts.map +1 -1
- package/dist/tm/ledger.js +4 -1
- package/dist/tm/ledger.js.map +1 -1
- package/dist/tm/perm-ask.d.ts +3 -3
- package/dist/tm/perm-ask.d.ts.map +1 -1
- package/dist/tm/perm-ask.js +3 -3
- package/dist/tm/perm-ask.js.map +1 -1
- package/dist/tm/pipelines.d.ts +12 -11
- package/dist/tm/pipelines.d.ts.map +1 -1
- package/dist/tm/pipelines.js +40 -207
- package/dist/tm/pipelines.js.map +1 -1
- package/dist/tm/preview.d.ts +1 -1
- package/dist/tm/preview.js +3 -3
- package/dist/tm/preview.js.map +1 -1
- package/dist/tm/refs.d.ts +31 -4
- package/dist/tm/refs.d.ts.map +1 -1
- package/dist/tm/refs.js +48 -4
- package/dist/tm/refs.js.map +1 -1
- package/dist/tm/result.d.ts +1 -1
- package/dist/tm/result.d.ts.map +1 -1
- package/dist/tm/result.js +4 -3
- package/dist/tm/result.js.map +1 -1
- package/dist/tm/search.d.ts +2 -2
- package/dist/tm/search.js +7 -7
- package/dist/tm/search.js.map +1 -1
- package/dist/tm/serp-loop.d.ts +19 -7
- package/dist/tm/serp-loop.d.ts.map +1 -1
- package/dist/tm/serp-loop.js +24 -9
- package/dist/tm/serp-loop.js.map +1 -1
- package/dist/tm/stats.d.ts +12 -27
- package/dist/tm/stats.d.ts.map +1 -1
- package/dist/tm/stats.js +43 -45
- package/dist/tm/stats.js.map +1 -1
- package/dist/tm/store.d.ts +18 -5
- package/dist/tm/store.d.ts.map +1 -1
- package/dist/tm/store.js +39 -9
- package/dist/tm/store.js.map +1 -1
- package/dist/tm/tools.d.ts +7 -5
- package/dist/tm/tools.d.ts.map +1 -1
- package/dist/tm/tools.js +10 -41
- package/dist/tm/tools.js.map +1 -1
- package/dist/tm/webfetch.d.ts +1 -1
- package/dist/tm/webfetch.d.ts.map +1 -1
- package/dist/tm/webfetch.js +38 -23
- package/dist/tm/webfetch.js.map +1 -1
- package/dist/types.d.ts +40 -123
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -21
- package/dist/types.js.map +1 -1
- package/docs/installation-v2.md +109 -33
- package/docs/installation.md +9 -0
- package/docs/research/browser-pane.md +5 -0
- package/docs/v2-port-notes.md +87 -0
- package/package.json +1 -1
- package/scripts/gen-v2-config.mjs +246 -94
- package/scripts/lib/fake-ctx.mjs +75 -0
|
@@ -108,7 +108,7 @@ interface OffloadDeps {
|
|
|
108
108
|
* take a `ref` out of the snapshot, so a preview that drops the ref tokens does not
|
|
109
109
|
* save the model a round-trip — it makes it click the wrong element, or spend a call
|
|
110
110
|
* re-snapshotting. For these two the text stays in context, capped, with the handle
|
|
111
|
-
* kept for the tail. 1 200 tokens is the budget
|
|
111
|
+
* kept for the tail. 1 200 tokens is the budget a native snapshot already
|
|
112
112
|
* runs at, so the governed door and the native one cost the same window space.
|
|
113
113
|
*/
|
|
114
114
|
export declare const ADDRESSING_NATIVE_TOOLS: ReadonlySet<string>;
|
package/dist/host/v2-offload.js
CHANGED
|
@@ -70,7 +70,7 @@ export const NATIVE_GOVERNED_TOOLS = new Set([
|
|
|
70
70
|
* `session.hook("context")` carrying a single message and no
|
|
71
71
|
* `<task id=… state="completed">` envelope at all -- so v1's chat.message offload
|
|
72
72
|
* has no anchor on v2, and rewriting the message list against a guessed shape is
|
|
73
|
-
* how evidence gets deleted silently (the same reason
|
|
73
|
+
* how evidence gets deleted silently (the same reason we decline
|
|
74
74
|
* `experimental.chat.messages.transform`). Consequence stated plainly: a
|
|
75
75
|
* BACKGROUND child's reply, which the host injects later, is NOT governed here --
|
|
76
76
|
* closing that needs the ctx.event/ctx.session rebuild, which is an open decision.
|
|
@@ -115,7 +115,7 @@ function locatableText(result) {
|
|
|
115
115
|
* take a `ref` out of the snapshot, so a preview that drops the ref tokens does not
|
|
116
116
|
* save the model a round-trip — it makes it click the wrong element, or spend a call
|
|
117
117
|
* re-snapshotting. For these two the text stays in context, capped, with the handle
|
|
118
|
-
* kept for the tail. 1 200 tokens is the budget
|
|
118
|
+
* kept for the tail. 1 200 tokens is the budget a native snapshot already
|
|
119
119
|
* runs at, so the governed door and the native one cost the same window space.
|
|
120
120
|
*/
|
|
121
121
|
export const ADDRESSING_NATIVE_TOOLS = new Set(["browser_snapshot", "browser_find"]);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The agent permission matrix -> v2's ordered `{action, resource, effect}` rules.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The matrix expresses a whitelist as an object keyed by tool name, with two
|
|
5
5
|
* tricks the v2 schema cannot hold: a value may be an OBJECT of
|
|
6
6
|
* pattern->effect (the bash escalation that made the host's official dialog
|
|
7
|
-
* fire for the R6/R2 faces), and `"tm_*"`
|
|
8
|
-
*
|
|
7
|
+
* fire for the R6/R2 faces), and `"tm_*"` is a literal wildcard KEY that
|
|
8
|
+
* matches our whole tool family. v2 takes only flat triples, so:
|
|
9
9
|
*
|
|
10
10
|
* - a plain `tool: "allow"|"deny"|"ask"` becomes `{action: tool, resource:"*", effect}`;
|
|
11
11
|
* - an escalation object becomes ONE `ask` triple for that action — the object
|
|
@@ -25,9 +25,10 @@ export interface PermissionTriple {
|
|
|
25
25
|
effect: PermissionEffect;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* surface (probed `session.context.tools`:
|
|
30
|
-
* skill subagent webfetch websearch write,
|
|
28
|
+
* The matrix names three built-in tools differently from the host's v2 tool
|
|
29
|
+
* ids. Measured against the live v2 surface (probed `session.context.tools`:
|
|
30
|
+
* edit glob grep question read shell skill subagent webfetch websearch write,
|
|
31
|
+
* plus patch/browser_*):
|
|
31
32
|
*
|
|
32
33
|
* bash → shell · task → subagent · apply_patch → patch
|
|
33
34
|
*
|
|
@@ -38,41 +39,41 @@ export interface PermissionTriple {
|
|
|
38
39
|
*/
|
|
39
40
|
export declare const V2_ACTION_NAMES: Readonly<Record<string, string>>;
|
|
40
41
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* RETIRED TOOLS — names the v1 personality used to register and this package
|
|
43
|
+
* no longer ships. v1 support was removed in 1.7.0, so these are historical
|
|
44
|
+
* names; the set survives because it still has three live jobs:
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* `
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
46
|
+
* - `v2.ts` skips them when registering, so a stale build or a future re-add
|
|
47
|
+
* cannot slip one back onto the tool surface;
|
|
48
|
+
* - `triplesFromAgentPermission` drops any permission rule naming one, and
|
|
49
|
+
* `mergeTriples` reclaims such a rule from an upgraded config — an `allow`
|
|
50
|
+
* for an action the host has never heard of claims a capability that does
|
|
51
|
+
* not exist;
|
|
52
|
+
* - `scripts/gen-v2-config.mjs` drops their triples from the generated
|
|
53
|
+
* markdown for the same reason.
|
|
52
54
|
*
|
|
53
|
-
* `tm_ptc_run`
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* results, so deleting them there would delete offload itself.
|
|
55
|
+
* Why each left: `tm_ptc_run` because the host's own Code Mode does the job
|
|
56
|
+
* (one program, N governed calls, zero round-trips, proved against a live
|
|
57
|
+
* session: parallel governed calls, results still offloaded through handles).
|
|
58
|
+
* `tm_read`/`tm_grep`/`tm_bash` because the NATIVE read/grep/shell are the
|
|
59
|
+
* better tools (paged reads, image/PDF attachment, background shell, and —
|
|
60
|
+
* since the offload layer — they are governed too, measured at 12,902 tokens
|
|
61
|
+
* arriving as a 78-token preview). Retiring them was only safe in that order,
|
|
62
|
+
* which is why the governance moved to `execute.after` FIRST and this list is
|
|
63
|
+
* the step that follows it, not the one that precedes it.
|
|
63
64
|
*/
|
|
64
65
|
export declare const V1_ONLY_TOOLS: ReadonlySet<string>;
|
|
65
66
|
/**
|
|
66
|
-
* The
|
|
67
|
+
* The file/shell ladder runs through the host's own tools, so the triples that
|
|
67
68
|
* DENY them must not be projected: v2-session.ts deletes every literal-`deny`
|
|
68
69
|
* action from the request, and denying `read` while `tm_read` is unregistered
|
|
69
70
|
* would leave a role with no way to open a file at all. This is a translation
|
|
70
|
-
* decision, not an edit to src/agents.ts —
|
|
71
|
-
*
|
|
71
|
+
* decision, not an edit to src/agents.ts — the matrix keeps its denies and the
|
|
72
|
+
* ladder is applied at translation time.
|
|
72
73
|
*
|
|
73
74
|
* Both halves have to agree, so the SAME set is consulted by `toolsToRemove()` in
|
|
74
75
|
* v2-session.ts; this constant is the one source. Letting the native file tools
|
|
75
|
-
* back in does not lower a red line:
|
|
76
|
+
* back in does not lower a red line: the P2 scope ("stay inside the project +
|
|
76
77
|
* the store dirs") is the host's own `external_directory` permission action on v2,
|
|
77
78
|
* observed live answering `effect:"ask"` with a `permission.asked` behind it — a
|
|
78
79
|
* real dialog where our code used to hard-throw. The two red lines the host does
|
|
@@ -83,25 +84,25 @@ export declare const V1_ONLY_TOOLS: ReadonlySet<string>;
|
|
|
83
84
|
export declare const V2_LADDER_ACTIONS: ReadonlySet<string>;
|
|
84
85
|
export interface TranslateOptions {
|
|
85
86
|
/**
|
|
86
|
-
* With R6 on,
|
|
87
|
-
* dialog
|
|
88
|
-
* does honor an `ask` EFFECT it evaluates itself, so escalating the
|
|
89
|
-
* `shell` action to `ask` is the closest honest equivalent: the human
|
|
90
|
-
* asked, just per command rather than per pattern. #94 refines the
|
|
87
|
+
* With R6 on, the matrix escalates `bash` to a pattern object so the host's
|
|
88
|
+
* OFFICIAL dialog fires. A plugin cannot raise a dialog on v2 (probed) — but
|
|
89
|
+
* the host does honor an `ask` EFFECT it evaluates itself, so escalating the
|
|
90
|
+
* mapped `shell` action to `ask` is the closest honest equivalent: the human
|
|
91
|
+
* is asked, just per command rather than per pattern. #94 refines the
|
|
91
92
|
* per-pattern half through `permission.hook("evaluate")`.
|
|
92
93
|
*/
|
|
93
94
|
escalateShellAsk?: boolean;
|
|
94
95
|
/**
|
|
95
|
-
* Which role these triples are for. Needed for the ONE tool
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
96
|
+
* Which role these triples are for. Needed for the ONE tool the matrix does
|
|
97
|
+
* not name: `tm_ledger` is not in the permission map, so nothing would ever
|
|
98
|
+
* name it, and an unruled action is the host's default rather than our stated
|
|
99
|
+
* rule. The lead's list is the lead's: `allow` for it, `deny` for the five
|
|
100
|
+
* specialists, which also makes the runtime gate (`onlyAgent: "Team"`)
|
|
101
|
+
* visible in the config the user can read.
|
|
101
102
|
*/
|
|
102
103
|
agentName?: string;
|
|
103
104
|
}
|
|
104
|
-
/** The
|
|
105
|
+
/** The agent `permission` block, as loosely as the config hands it to us. */
|
|
105
106
|
export declare function triplesFromAgentPermission(permission: Record<string, unknown> | undefined | null, options?: TranslateOptions): {
|
|
106
107
|
triples: PermissionTriple[];
|
|
107
108
|
unmapped: string[];
|
|
@@ -113,7 +114,7 @@ export declare function triplesFromAgentPermission(permission: Record<string, un
|
|
|
113
114
|
* it). Idempotent: running it twice changes nothing.
|
|
114
115
|
*
|
|
115
116
|
* `drop` is the reclaim path for an upgrade: a rule naming an action this
|
|
116
|
-
*
|
|
117
|
+
* package never registers (a retired tool) can only have come from us, and
|
|
117
118
|
* leaving it behind would keep claiming a capability the host does not have.
|
|
118
119
|
*/
|
|
119
120
|
export declare function mergeTriples(existing: ReadonlyArray<PermissionTriple> | undefined | null, ours: ReadonlyArray<PermissionTriple>, drop?: ReadonlySet<string>): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2-permissions.d.ts","sourceRoot":"","sources":["../../src/host/v2-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"v2-permissions.d.ts","sourceRoot":"","sources":["../../src/host/v2-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,gBAAgB,CAAA;CACzB;AAID;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI5D,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAe5C,CAAA;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAqC,CAAA;AAQvF,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,6EAA6E;AAC7E,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,EACtD,OAAO,GAAE,gBAAqB,GAC7B;IAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAuDrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,CAAC,gBAAgB,CAAC,GAAG,SAAS,GAAG,IAAI,EAC5D,IAAI,EAAE,aAAa,CAAC,gBAAgB,CAAC,EACrC,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB;IAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAcnD"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The agent permission matrix -> v2's ordered `{action, resource, effect}` rules.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The matrix expresses a whitelist as an object keyed by tool name, with two
|
|
5
5
|
* tricks the v2 schema cannot hold: a value may be an OBJECT of
|
|
6
6
|
* pattern->effect (the bash escalation that made the host's official dialog
|
|
7
|
-
* fire for the R6/R2 faces), and `"tm_*"`
|
|
8
|
-
*
|
|
7
|
+
* fire for the R6/R2 faces), and `"tm_*"` is a literal wildcard KEY that
|
|
8
|
+
* matches our whole tool family. v2 takes only flat triples, so:
|
|
9
9
|
*
|
|
10
10
|
* - a plain `tool: "allow"|"deny"|"ask"` becomes `{action: tool, resource:"*", effect}`;
|
|
11
11
|
* - an escalation object becomes ONE `ask` triple for that action — the object
|
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
* rather than guessed at: a rule the host cannot parse is worse than no rule,
|
|
19
19
|
* because it may fail closed on the whole agent.
|
|
20
20
|
*/
|
|
21
|
+
import { isLeadAgent } from "../identity.js";
|
|
21
22
|
const EFFECTS = new Set(["allow", "deny", "ask"]);
|
|
22
23
|
/**
|
|
23
|
-
*
|
|
24
|
-
* surface (probed `session.context.tools`:
|
|
25
|
-
* skill subagent webfetch websearch write,
|
|
24
|
+
* The matrix names three built-in tools differently from the host's v2 tool
|
|
25
|
+
* ids. Measured against the live v2 surface (probed `session.context.tools`:
|
|
26
|
+
* edit glob grep question read shell skill subagent webfetch websearch write,
|
|
27
|
+
* plus patch/browser_*):
|
|
26
28
|
*
|
|
27
29
|
* bash → shell · task → subagent · apply_patch → patch
|
|
28
30
|
*
|
|
@@ -37,28 +39,28 @@ export const V2_ACTION_NAMES = {
|
|
|
37
39
|
apply_patch: "patch",
|
|
38
40
|
};
|
|
39
41
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
42
|
+
* RETIRED TOOLS — names the v1 personality used to register and this package
|
|
43
|
+
* no longer ships. v1 support was removed in 1.7.0, so these are historical
|
|
44
|
+
* names; the set survives because it still has three live jobs:
|
|
43
45
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* `
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*
|
|
46
|
+
* - `v2.ts` skips them when registering, so a stale build or a future re-add
|
|
47
|
+
* cannot slip one back onto the tool surface;
|
|
48
|
+
* - `triplesFromAgentPermission` drops any permission rule naming one, and
|
|
49
|
+
* `mergeTriples` reclaims such a rule from an upgraded config — an `allow`
|
|
50
|
+
* for an action the host has never heard of claims a capability that does
|
|
51
|
+
* not exist;
|
|
52
|
+
* - `scripts/gen-v2-config.mjs` drops their triples from the generated
|
|
53
|
+
* markdown for the same reason.
|
|
51
54
|
*
|
|
52
|
-
* `tm_ptc_run`
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* results, so deleting them there would delete offload itself.
|
|
55
|
+
* Why each left: `tm_ptc_run` because the host's own Code Mode does the job
|
|
56
|
+
* (one program, N governed calls, zero round-trips, proved against a live
|
|
57
|
+
* session: parallel governed calls, results still offloaded through handles).
|
|
58
|
+
* `tm_read`/`tm_grep`/`tm_bash` because the NATIVE read/grep/shell are the
|
|
59
|
+
* better tools (paged reads, image/PDF attachment, background shell, and —
|
|
60
|
+
* since the offload layer — they are governed too, measured at 12,902 tokens
|
|
61
|
+
* arriving as a 78-token preview). Retiring them was only safe in that order,
|
|
62
|
+
* which is why the governance moved to `execute.after` FIRST and this list is
|
|
63
|
+
* the step that follows it, not the one that precedes it.
|
|
62
64
|
*/
|
|
63
65
|
export const V1_ONLY_TOOLS = new Set([
|
|
64
66
|
"tm_ptc_run",
|
|
@@ -69,7 +71,7 @@ export const V1_ONLY_TOOLS = new Set([
|
|
|
69
71
|
* `tm_pty` executes on the host's OWN terminal sessions through `client.pty.*`,
|
|
70
72
|
* and the v2 plugin context has no pty domain at all — so registering it meant
|
|
71
73
|
* shipping a tool whose every start answers "宿主 pty 接口不可用". A tool that
|
|
72
|
-
* cannot work is the overstated claim, not the fallback.
|
|
74
|
+
* cannot work is the overstated claim, not the fallback.
|
|
73
75
|
* Revisit only when #28 measures the native `shell` background round-trip
|
|
74
76
|
(what returns, how output is retrieved, how it is cancelled); if the host's own
|
|
75
77
|
* background shell covers it, this stays retired and the prompt says `shell`.
|
|
@@ -77,16 +79,16 @@ export const V1_ONLY_TOOLS = new Set([
|
|
|
77
79
|
"tm_pty",
|
|
78
80
|
]);
|
|
79
81
|
/**
|
|
80
|
-
* The
|
|
82
|
+
* The file/shell ladder runs through the host's own tools, so the triples that
|
|
81
83
|
* DENY them must not be projected: v2-session.ts deletes every literal-`deny`
|
|
82
84
|
* action from the request, and denying `read` while `tm_read` is unregistered
|
|
83
85
|
* would leave a role with no way to open a file at all. This is a translation
|
|
84
|
-
* decision, not an edit to src/agents.ts —
|
|
85
|
-
*
|
|
86
|
+
* decision, not an edit to src/agents.ts — the matrix keeps its denies and the
|
|
87
|
+
* ladder is applied at translation time.
|
|
86
88
|
*
|
|
87
89
|
* Both halves have to agree, so the SAME set is consulted by `toolsToRemove()` in
|
|
88
90
|
* v2-session.ts; this constant is the one source. Letting the native file tools
|
|
89
|
-
* back in does not lower a red line:
|
|
91
|
+
* back in does not lower a red line: the P2 scope ("stay inside the project +
|
|
90
92
|
* the store dirs") is the host's own `external_directory` permission action on v2,
|
|
91
93
|
* observed live answering `effect:"ask"` with a `permission.asked` behind it — a
|
|
92
94
|
* real dialog where our code used to hard-throw. The two red lines the host does
|
|
@@ -99,20 +101,20 @@ const V2_ONLY_ACTIONS = new Set(["list", "todowrite", "lsp"]);
|
|
|
99
101
|
function normalizeEffect(value) {
|
|
100
102
|
return typeof value === "string" && EFFECTS.has(value) ? value : null;
|
|
101
103
|
}
|
|
102
|
-
/** The
|
|
104
|
+
/** The agent `permission` block, as loosely as the config hands it to us. */
|
|
103
105
|
export function triplesFromAgentPermission(permission, options = {}) {
|
|
104
106
|
const triples = [];
|
|
105
107
|
const unmapped = [];
|
|
106
108
|
if (!permission || typeof permission !== "object")
|
|
107
109
|
return { triples, unmapped };
|
|
108
|
-
for (const [
|
|
109
|
-
if (!
|
|
110
|
+
for (const [matrixAction, value] of Object.entries(permission)) {
|
|
111
|
+
if (!matrixAction)
|
|
110
112
|
continue;
|
|
111
|
-
if (V2_ONLY_ACTIONS.has(
|
|
112
|
-
unmapped.push(
|
|
113
|
+
if (V2_ONLY_ACTIONS.has(matrixAction)) {
|
|
114
|
+
unmapped.push(matrixAction);
|
|
113
115
|
continue;
|
|
114
116
|
}
|
|
115
|
-
const action = V2_ACTION_NAMES[
|
|
117
|
+
const action = V2_ACTION_NAMES[matrixAction] ?? matrixAction;
|
|
116
118
|
// A rule for an action v2 never registers claims a capability that does not
|
|
117
119
|
// exist, and `permission.evaluate` cannot even see it to enforce it. One
|
|
118
120
|
// source for the decision: this is the same set v2.ts skips when registering
|
|
@@ -128,16 +130,13 @@ export function triplesFromAgentPermission(permission, options = {}) {
|
|
|
128
130
|
if (V2_LADDER_ACTIONS.has(action) && direct === "deny")
|
|
129
131
|
continue;
|
|
130
132
|
const effect = options.escalateShellAsk && action === "shell" && direct === "allow" ? "ask" : direct;
|
|
133
|
+
// The host's 45 `browser_*` tools share ONE permission action named
|
|
134
|
+
// `browser` (read from the binary), and they are NOT in the direct tool
|
|
135
|
+
// surface — so deleting `browser_*` from `event.tools` at the request layer
|
|
136
|
+
// removes a catalog the assembled request never contained. The matrix now
|
|
137
|
+
// names that action directly (`browser`), so the deny reaches the host's
|
|
138
|
+
// own evaluator, which is the only lever that speaks their language.
|
|
131
139
|
triples.push({ action, resource: "*", effect });
|
|
132
|
-
// The host's 45 `browser_*` tools share ONE permission action named `browser`
|
|
133
|
-
// (read from the binary), and they are NOT in the direct tool surface — so
|
|
134
|
-
// deleting `browser_*` from `event.tools` at the request layer removes a
|
|
135
|
-
// catalog the assembled request never contained, while a role that is denied
|
|
136
|
-
// `tm_browser` can still reach every one of them from inside `execute`.
|
|
137
|
-
// Projecting the deny is the only lever that speaks their language.
|
|
138
|
-
if (action === "tm_browser" && direct === "deny") {
|
|
139
|
-
triples.push({ action: "browser", resource: "*", effect: "deny" });
|
|
140
|
-
}
|
|
141
140
|
continue;
|
|
142
141
|
}
|
|
143
142
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
@@ -155,7 +154,7 @@ export function triplesFromAgentPermission(permission, options = {}) {
|
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
156
|
if (options.agentName) {
|
|
158
|
-
const ledgerEffect = options.agentName
|
|
157
|
+
const ledgerEffect = isLeadAgent(options.agentName) ? "allow" : "deny";
|
|
159
158
|
if (!triples.some((x) => x.action === "tm_ledger")) {
|
|
160
159
|
triples.push({ action: "tm_ledger", resource: "*", effect: ledgerEffect });
|
|
161
160
|
}
|
|
@@ -169,7 +168,7 @@ export function triplesFromAgentPermission(permission, options = {}) {
|
|
|
169
168
|
* it). Idempotent: running it twice changes nothing.
|
|
170
169
|
*
|
|
171
170
|
* `drop` is the reclaim path for an upgrade: a rule naming an action this
|
|
172
|
-
*
|
|
171
|
+
* package never registers (a retired tool) can only have come from us, and
|
|
173
172
|
* leaving it behind would keep claiming a capability the host does not have.
|
|
174
173
|
*/
|
|
175
174
|
export function mergeTriples(existing, ours, drop) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2-permissions.js","sourceRoot":"","sources":["../../src/host/v2-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"v2-permissions.js","sourceRoot":"","sources":["../../src/host/v2-permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Mode adoption guard (#35, 2026-10-06).
|
|
3
|
+
*
|
|
4
|
+
* The v2 retirement moved the file/command ladder onto the HOST's native
|
|
5
|
+
* `read` / `grep` / `glob` / `shell`, and the host's `execute` (Code Mode) is the
|
|
6
|
+
* one program that folds N governed calls into a single round-trip. A model that
|
|
7
|
+
* keeps reaching for the native tools one at a time pays N rounds for what one
|
|
8
|
+
* `execute` program answers — the exact inefficiency the retirement was supposed
|
|
9
|
+
* to remove, wearing the opposite face.
|
|
10
|
+
*
|
|
11
|
+
* This is an ADOPTION HINT, not a gate: after a run of consecutive native
|
|
12
|
+
* file/command calls it APPENDS one line to the next result naming `execute`.
|
|
13
|
+
* It never rewrites the body, never touches args, never refuses a call — the
|
|
14
|
+
* same shape as `src/tm/dupe-guard.ts` (append a directive + count + trajectory
|
|
15
|
+
* event), minus the escalation, because a hint that blocks would cost the user
|
|
16
|
+
* the very round it is trying to save.
|
|
17
|
+
*
|
|
18
|
+
* Discipline, copied from the layers beside it:
|
|
19
|
+
* - Team-scoped through the shared `scope.decide` gate — a foreign session's
|
|
20
|
+
* result is nobody's to annotate;
|
|
21
|
+
* - it may NEVER throw into somebody's tool call: a failure is swallowed and
|
|
22
|
+
* COUNTED (`threw`), and the host's verbatim result passes through;
|
|
23
|
+
* - the append is IDEMPOTENT (a marker check), because the host reloads plugins
|
|
24
|
+
* in-process and a replayed hook must not stack two lines.
|
|
25
|
+
*/
|
|
26
|
+
import type { V2Registration } from "./v2-types.js";
|
|
27
|
+
import type { TeamScope } from "./v2-scope.js";
|
|
28
|
+
/** The native tools whose repeated use is what `execute` folds into one program.
|
|
29
|
+
* A closed list on purpose: `question`, `edit`, `subagent` … are not "one more
|
|
30
|
+
* read", and counting them would make the streak mean nothing. */
|
|
31
|
+
export declare const NATIVE_CHAIN_TOOLS: ReadonlySet<string>;
|
|
32
|
+
/** The tool that RESETS the streak: it is the alternative the hint points at, so
|
|
33
|
+
* using it is the behaviour the guard exists to encourage. */
|
|
34
|
+
export declare const CHAIN_RESET_TOOL = "execute";
|
|
35
|
+
/** Stable marker so a replayed hook cannot append the line twice. */
|
|
36
|
+
export declare const PROBE_CHAIN_MARKER = "\uFF08Code Mode \u91C7\u7528\u7387\u63D0\u793A\uFF09";
|
|
37
|
+
export declare const DEFAULT_PROBE_CHAIN_AFTER = 3;
|
|
38
|
+
export interface ProbeChainReport {
|
|
39
|
+
/** chain-tool calls observed in a Team session */
|
|
40
|
+
seen: number;
|
|
41
|
+
/** results that actually got the appended line */
|
|
42
|
+
advised: number;
|
|
43
|
+
/** `execute` calls that reset the streak */
|
|
44
|
+
reset: number;
|
|
45
|
+
/** non-Team sessions left completely alone */
|
|
46
|
+
foreignSkipped: number;
|
|
47
|
+
/** a throw inside the hook, swallowed and counted (never a reason to fail a call) */
|
|
48
|
+
threw: number;
|
|
49
|
+
}
|
|
50
|
+
export interface ProbeChainVerdict {
|
|
51
|
+
/** the current run of consecutive native file/command calls */
|
|
52
|
+
count: number;
|
|
53
|
+
/** should the line be appended to THIS result? */
|
|
54
|
+
advise: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The pure ledger. `threshold` is the number of consecutive native calls after
|
|
58
|
+
* which the NEXT result carries the hint: with the default 3, calls 1-3 are
|
|
59
|
+
* silent and the 4th (and every one after it, until a reset) is advised. A
|
|
60
|
+
* non-chain tool is transparent — it neither counts nor resets — and `execute`
|
|
61
|
+
* resets, because it is the alternative.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createProbeChain(threshold: number): {
|
|
64
|
+
observe: (tool: string) => ProbeChainVerdict;
|
|
65
|
+
streak: () => number;
|
|
66
|
+
};
|
|
67
|
+
/** The appended line, in the tool-output language the tm_* strings use. */
|
|
68
|
+
export declare function renderProbeChainNote(count: number): string;
|
|
69
|
+
/**
|
|
70
|
+
* Append the line to the result's first text part, byte-exact otherwise. Returns
|
|
71
|
+
* true only when a line was actually added — an unknown result shape, a missing
|
|
72
|
+
* text part, or an already-marked body all return false (the caller counts the
|
|
73
|
+
* difference). The body is never rewritten: the original text is preserved
|
|
74
|
+
* verbatim and the line is appended after it.
|
|
75
|
+
*/
|
|
76
|
+
export declare function appendProbeChainNote(result: unknown, line: string): boolean;
|
|
77
|
+
export interface ProbeChainDeps {
|
|
78
|
+
/** Team-scope isolation: a foreign or unattributable session is left alone. */
|
|
79
|
+
scope?: TeamScope;
|
|
80
|
+
env?: Record<string, string | undefined>;
|
|
81
|
+
/** the trajectory sink — wired in v2.ts, same shape as the browser gate's `onSerp` */
|
|
82
|
+
onAdvise?: (info: {
|
|
83
|
+
tool: string;
|
|
84
|
+
count: number;
|
|
85
|
+
sessionID?: unknown;
|
|
86
|
+
}) => void;
|
|
87
|
+
}
|
|
88
|
+
export declare function applyV2ProbeChain(ctx: unknown, deps: ProbeChainDeps): {
|
|
89
|
+
registrations: Promise<V2Registration>[];
|
|
90
|
+
report: ProbeChainReport;
|
|
91
|
+
active: boolean;
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=v2-probe-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v2-probe-chain.d.ts","sourceRoot":"","sources":["../../src/host/v2-probe-chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C;;mEAEmE;AACnE,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAA8C,CAAA;AAEjG;+DAC+D;AAC/D,eAAO,MAAM,gBAAgB,YAAY,CAAA;AAEzC,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,yDAAsB,CAAA;AAErD,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAA;IACf,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAA;IACtB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG;IACnD,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,CAAA;IAC5C,MAAM,EAAE,MAAM,MAAM,CAAA;CACrB,CAcA;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAY3E;AAED,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACxC,sFAAsF;IACtF,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;CAChF;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,cAAc,GACnB;IAAE,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAkDzF"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Mode adoption guard (#35, 2026-10-06).
|
|
3
|
+
*
|
|
4
|
+
* The v2 retirement moved the file/command ladder onto the HOST's native
|
|
5
|
+
* `read` / `grep` / `glob` / `shell`, and the host's `execute` (Code Mode) is the
|
|
6
|
+
* one program that folds N governed calls into a single round-trip. A model that
|
|
7
|
+
* keeps reaching for the native tools one at a time pays N rounds for what one
|
|
8
|
+
* `execute` program answers — the exact inefficiency the retirement was supposed
|
|
9
|
+
* to remove, wearing the opposite face.
|
|
10
|
+
*
|
|
11
|
+
* This is an ADOPTION HINT, not a gate: after a run of consecutive native
|
|
12
|
+
* file/command calls it APPENDS one line to the next result naming `execute`.
|
|
13
|
+
* It never rewrites the body, never touches args, never refuses a call — the
|
|
14
|
+
* same shape as `src/tm/dupe-guard.ts` (append a directive + count + trajectory
|
|
15
|
+
* event), minus the escalation, because a hint that blocks would cost the user
|
|
16
|
+
* the very round it is trying to save.
|
|
17
|
+
*
|
|
18
|
+
* Discipline, copied from the layers beside it:
|
|
19
|
+
* - Team-scoped through the shared `scope.decide` gate — a foreign session's
|
|
20
|
+
* result is nobody's to annotate;
|
|
21
|
+
* - it may NEVER throw into somebody's tool call: a failure is swallowed and
|
|
22
|
+
* COUNTED (`threw`), and the host's verbatim result passes through;
|
|
23
|
+
* - the append is IDEMPOTENT (a marker check), because the host reloads plugins
|
|
24
|
+
* in-process and a replayed hook must not stack two lines.
|
|
25
|
+
*/
|
|
26
|
+
/** The native tools whose repeated use is what `execute` folds into one program.
|
|
27
|
+
* A closed list on purpose: `question`, `edit`, `subagent` … are not "one more
|
|
28
|
+
* read", and counting them would make the streak mean nothing. */
|
|
29
|
+
export const NATIVE_CHAIN_TOOLS = new Set(["read", "grep", "glob", "shell"]);
|
|
30
|
+
/** The tool that RESETS the streak: it is the alternative the hint points at, so
|
|
31
|
+
* using it is the behaviour the guard exists to encourage. */
|
|
32
|
+
export const CHAIN_RESET_TOOL = "execute";
|
|
33
|
+
/** Stable marker so a replayed hook cannot append the line twice. */
|
|
34
|
+
export const PROBE_CHAIN_MARKER = "(Code Mode 采用率提示)";
|
|
35
|
+
export const DEFAULT_PROBE_CHAIN_AFTER = 3;
|
|
36
|
+
/**
|
|
37
|
+
* The pure ledger. `threshold` is the number of consecutive native calls after
|
|
38
|
+
* which the NEXT result carries the hint: with the default 3, calls 1-3 are
|
|
39
|
+
* silent and the 4th (and every one after it, until a reset) is advised. A
|
|
40
|
+
* non-chain tool is transparent — it neither counts nor resets — and `execute`
|
|
41
|
+
* resets, because it is the alternative.
|
|
42
|
+
*/
|
|
43
|
+
export function createProbeChain(threshold) {
|
|
44
|
+
let count = 0;
|
|
45
|
+
return {
|
|
46
|
+
observe(tool) {
|
|
47
|
+
if (tool === CHAIN_RESET_TOOL) {
|
|
48
|
+
count = 0;
|
|
49
|
+
return { count, advise: false };
|
|
50
|
+
}
|
|
51
|
+
if (!NATIVE_CHAIN_TOOLS.has(tool))
|
|
52
|
+
return { count, advise: false };
|
|
53
|
+
count++;
|
|
54
|
+
return { count, advise: count > threshold };
|
|
55
|
+
},
|
|
56
|
+
streak: () => count,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** The appended line, in the tool-output language the tm_* strings use. */
|
|
60
|
+
export function renderProbeChainNote(count) {
|
|
61
|
+
return (`${PROBE_CHAIN_MARKER} 你已经连续 ${count} 次用原生 read/grep/glob/shell 逐个取数——` +
|
|
62
|
+
`这些调用可以折叠成一个 \`execute\`(Code Mode)程序:一次跑完多个受治理调用,只回一份汇总,省下 ${count - 1} 轮。` +
|
|
63
|
+
`下一步:把接下来的多次读取/搜索写进一个 execute 程序。`);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Append the line to the result's first text part, byte-exact otherwise. Returns
|
|
67
|
+
* true only when a line was actually added — an unknown result shape, a missing
|
|
68
|
+
* text part, or an already-marked body all return false (the caller counts the
|
|
69
|
+
* difference). The body is never rewritten: the original text is preserved
|
|
70
|
+
* verbatim and the line is appended after it.
|
|
71
|
+
*/
|
|
72
|
+
export function appendProbeChainNote(result, line) {
|
|
73
|
+
const r = result;
|
|
74
|
+
if (!r || !Array.isArray(r.content))
|
|
75
|
+
return false;
|
|
76
|
+
for (const p of r.content) {
|
|
77
|
+
if (!p || typeof p !== "object")
|
|
78
|
+
continue;
|
|
79
|
+
const part = p;
|
|
80
|
+
if (part.type !== "text" || typeof part.text !== "string")
|
|
81
|
+
continue;
|
|
82
|
+
if (part.text.includes(PROBE_CHAIN_MARKER))
|
|
83
|
+
return false;
|
|
84
|
+
part.text = `${part.text}\n\n${line}`;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
export function applyV2ProbeChain(ctx, deps) {
|
|
90
|
+
const env = deps.env ?? process.env;
|
|
91
|
+
const report = { seen: 0, advised: 0, reset: 0, foreignSkipped: 0, threw: 0 };
|
|
92
|
+
const off = /^(0|false|no|off)$/i.test(String(env.TM_PROBE_CHAIN ?? "").trim());
|
|
93
|
+
const raw = String(env.TM_PROBE_CHAIN_AFTER ?? "").trim();
|
|
94
|
+
const parsed = raw === "" ? DEFAULT_PROBE_CHAIN_AFTER : Number(raw);
|
|
95
|
+
const threshold = Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed)) : DEFAULT_PROBE_CHAIN_AFTER;
|
|
96
|
+
const disabled = off || threshold <= 0;
|
|
97
|
+
const hook = ctx?.tool?.hook;
|
|
98
|
+
if (typeof hook !== "function") {
|
|
99
|
+
// No seam at all: report it rather than pretending the hint is live.
|
|
100
|
+
return { registrations: [], report, active: false };
|
|
101
|
+
}
|
|
102
|
+
const chain = createProbeChain(threshold);
|
|
103
|
+
const reg = hook("execute.after", (rawEvent) => {
|
|
104
|
+
try {
|
|
105
|
+
const event = rawEvent;
|
|
106
|
+
const tool = String(event?.tool ?? "");
|
|
107
|
+
// The owner question comes FIRST: a foreign or unattributable session is
|
|
108
|
+
// left alone no matter which tool it called.
|
|
109
|
+
if (deps.scope) {
|
|
110
|
+
if (deps.scope.count(deps.scope.decide(event)) !== "ours") {
|
|
111
|
+
report.foreignSkipped++;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
deps.scope.learn(event.agent, event.sessionID);
|
|
115
|
+
}
|
|
116
|
+
if (disabled)
|
|
117
|
+
return;
|
|
118
|
+
if (tool === CHAIN_RESET_TOOL) {
|
|
119
|
+
report.reset++;
|
|
120
|
+
chain.observe(tool);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!NATIVE_CHAIN_TOOLS.has(tool))
|
|
124
|
+
return;
|
|
125
|
+
report.seen++;
|
|
126
|
+
const verdict = chain.observe(tool);
|
|
127
|
+
if (!verdict.advise)
|
|
128
|
+
return;
|
|
129
|
+
if (!appendProbeChainNote(event.result, renderProbeChainNote(verdict.count)))
|
|
130
|
+
return;
|
|
131
|
+
report.advised++;
|
|
132
|
+
try {
|
|
133
|
+
deps.onAdvise?.({ tool, count: verdict.count, sessionID: event.sessionID });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
/* the trajectory is an extra, never a reason to fail the call */
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// A governance failure must never break somebody's tool call.
|
|
141
|
+
report.threw++;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return { registrations: [reg], report, active: !disabled };
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=v2-probe-chain.js.map
|