@te-river/opencode-team-mode 1.7.1 → 1.7.3

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.
Files changed (167) hide show
  1. package/README.md +1009 -849
  2. package/README.zh-CN.md +906 -756
  3. package/dist/host/v2-browser-gate.d.ts +4 -1
  4. package/dist/host/v2-browser-gate.d.ts.map +1 -1
  5. package/dist/host/v2-browser-gate.js +1 -1
  6. package/dist/host/v2-browser-gate.js.map +1 -1
  7. package/dist/host/v2-capabilities.js +1 -1
  8. package/dist/host/v2-capabilities.js.map +1 -1
  9. package/dist/host/v2-guard.d.ts +3 -1
  10. package/dist/host/v2-guard.d.ts.map +1 -1
  11. package/dist/host/v2-guard.js +2 -2
  12. package/dist/host/v2-guard.js.map +1 -1
  13. package/dist/host/v2-offload.d.ts +12 -2
  14. package/dist/host/v2-offload.d.ts.map +1 -1
  15. package/dist/host/v2-offload.js +9 -9
  16. package/dist/host/v2-offload.js.map +1 -1
  17. package/dist/host/v2-probe-chain.d.ts +5 -1
  18. package/dist/host/v2-probe-chain.d.ts.map +1 -1
  19. package/dist/host/v2-probe-chain.js +4 -6
  20. package/dist/host/v2-probe-chain.js.map +1 -1
  21. package/dist/host/v2-probe.d.ts.map +1 -1
  22. package/dist/host/v2-probe.js +2 -1
  23. package/dist/host/v2-probe.js.map +1 -1
  24. package/dist/host/v2-prune.d.ts +5 -1
  25. package/dist/host/v2-prune.d.ts.map +1 -1
  26. package/dist/host/v2-prune.js +8 -14
  27. package/dist/host/v2-prune.js.map +1 -1
  28. package/dist/host/v2-retry.d.ts +9 -2
  29. package/dist/host/v2-retry.d.ts.map +1 -1
  30. package/dist/host/v2-retry.js +13 -15
  31. package/dist/host/v2-retry.js.map +1 -1
  32. package/dist/host/v2-session.d.ts +6 -2
  33. package/dist/host/v2-session.d.ts.map +1 -1
  34. package/dist/host/v2-session.js +7 -13
  35. package/dist/host/v2-session.js.map +1 -1
  36. package/dist/host/v2.d.ts +7 -2
  37. package/dist/host/v2.d.ts.map +1 -1
  38. package/dist/host/v2.js +38 -35
  39. package/dist/host/v2.js.map +1 -1
  40. package/dist/prompts/lead.d.ts +1 -1
  41. package/dist/prompts/lead.d.ts.map +1 -1
  42. package/dist/prompts/lead.js +23 -7
  43. package/dist/prompts/lead.js.map +1 -1
  44. package/dist/prompts/specialists.d.ts +1 -1
  45. package/dist/prompts/specialists.d.ts.map +1 -1
  46. package/dist/prompts/specialists.js +1 -1
  47. package/dist/tm/args-schema.d.ts +1 -1
  48. package/dist/tm/args-schema.js +6 -6
  49. package/dist/tm/args-schema.js.map +1 -1
  50. package/dist/tm/board.js +2 -2
  51. package/dist/tm/board.js.map +1 -1
  52. package/dist/tm/config-files.d.ts +41 -17
  53. package/dist/tm/config-files.d.ts.map +1 -1
  54. package/dist/tm/config-files.js +68 -73
  55. package/dist/tm/config-files.js.map +1 -1
  56. package/dist/tm/config-layers.d.ts +65 -33
  57. package/dist/tm/config-layers.d.ts.map +1 -1
  58. package/dist/tm/config-layers.js +165 -81
  59. package/dist/tm/config-layers.js.map +1 -1
  60. package/dist/tm/config-template.d.ts +32 -0
  61. package/dist/tm/config-template.d.ts.map +1 -0
  62. package/dist/tm/config-template.js +74 -0
  63. package/dist/tm/config-template.js.map +1 -0
  64. package/dist/tm/config.d.ts +115 -140
  65. package/dist/tm/config.d.ts.map +1 -1
  66. package/dist/tm/config.js +100 -200
  67. package/dist/tm/config.js.map +1 -1
  68. package/dist/tm/index.d.ts +11 -3
  69. package/dist/tm/index.d.ts.map +1 -1
  70. package/dist/tm/index.js +46 -16
  71. package/dist/tm/index.js.map +1 -1
  72. package/dist/tm/ledger.d.ts +7 -3
  73. package/dist/tm/ledger.d.ts.map +1 -1
  74. package/dist/tm/ledger.js +5 -4
  75. package/dist/tm/ledger.js.map +1 -1
  76. package/dist/tm/memory.js +3 -3
  77. package/dist/tm/pipelines.js +1 -1
  78. package/dist/tm/search.js +6 -6
  79. package/dist/tm/search.js.map +1 -1
  80. package/dist/tm/stats.js +2 -2
  81. package/dist/tm/stats.js.map +1 -1
  82. package/dist/tm/tools.js +2 -2
  83. package/dist/tm/webfetch.d.ts +5 -2
  84. package/dist/tm/webfetch.d.ts.map +1 -1
  85. package/dist/tm/webfetch.js +24 -18
  86. package/dist/tm/webfetch.js.map +1 -1
  87. package/docs/installation-v2.md +61 -38
  88. package/docs/installation.md +4 -4
  89. package/docs/v2-port-notes.md +31 -0
  90. package/package.json +1 -1
  91. package/scripts/gen-v2-config.mjs +40 -4
  92. package/dist/approval-gate.d.ts +0 -205
  93. package/dist/approval-gate.d.ts.map +0 -1
  94. package/dist/approval-gate.js +0 -627
  95. package/dist/approval-gate.js.map +0 -1
  96. package/dist/host/v1.d.ts +0 -14
  97. package/dist/host/v1.d.ts.map +0 -1
  98. package/dist/host/v1.js +0 -340
  99. package/dist/host/v1.js.map +0 -1
  100. package/dist/host/v2-client.d.ts +0 -53
  101. package/dist/host/v2-client.d.ts.map +0 -1
  102. package/dist/host/v2-client.js +0 -193
  103. package/dist/host/v2-client.js.map +0 -1
  104. package/dist/host/v2-transcript.d.ts +0 -73
  105. package/dist/host/v2-transcript.d.ts.map +0 -1
  106. package/dist/host/v2-transcript.js +0 -151
  107. package/dist/host/v2-transcript.js.map +0 -1
  108. package/dist/host-hooks.d.ts +0 -126
  109. package/dist/host-hooks.d.ts.map +0 -1
  110. package/dist/host-hooks.js +0 -284
  111. package/dist/host-hooks.js.map +0 -1
  112. package/dist/tm/bash-timeout.d.ts +0 -72
  113. package/dist/tm/bash-timeout.d.ts.map +0 -1
  114. package/dist/tm/bash-timeout.js +0 -100
  115. package/dist/tm/bash-timeout.js.map +0 -1
  116. package/dist/tm/browser.d.ts +0 -642
  117. package/dist/tm/browser.d.ts.map +0 -1
  118. package/dist/tm/browser.js +0 -2836
  119. package/dist/tm/browser.js.map +0 -1
  120. package/dist/tm/ptc/budgets.d.ts +0 -59
  121. package/dist/tm/ptc/budgets.d.ts.map +0 -1
  122. package/dist/tm/ptc/budgets.js +0 -138
  123. package/dist/tm/ptc/budgets.js.map +0 -1
  124. package/dist/tm/ptc/contract.d.ts +0 -171
  125. package/dist/tm/ptc/contract.d.ts.map +0 -1
  126. package/dist/tm/ptc/contract.js +0 -85
  127. package/dist/tm/ptc/contract.js.map +0 -1
  128. package/dist/tm/ptc/driver.d.ts +0 -40
  129. package/dist/tm/ptc/driver.d.ts.map +0 -1
  130. package/dist/tm/ptc/driver.js +0 -213
  131. package/dist/tm/ptc/driver.js.map +0 -1
  132. package/dist/tm/ptc/engines.d.ts +0 -43
  133. package/dist/tm/ptc/engines.d.ts.map +0 -1
  134. package/dist/tm/ptc/engines.js +0 -454
  135. package/dist/tm/ptc/engines.js.map +0 -1
  136. package/dist/tm/ptc/gate.d.ts +0 -28
  137. package/dist/tm/ptc/gate.d.ts.map +0 -1
  138. package/dist/tm/ptc/gate.js +0 -130
  139. package/dist/tm/ptc/gate.js.map +0 -1
  140. package/dist/tm/ptc/index.d.ts +0 -19
  141. package/dist/tm/ptc/index.d.ts.map +0 -1
  142. package/dist/tm/ptc/index.js +0 -15
  143. package/dist/tm/ptc/index.js.map +0 -1
  144. package/dist/tm/ptc/pscan.d.ts +0 -30
  145. package/dist/tm/ptc/pscan.d.ts.map +0 -1
  146. package/dist/tm/ptc/pscan.js +0 -341
  147. package/dist/tm/ptc/pscan.js.map +0 -1
  148. package/dist/tm/ptc/summary.d.ts +0 -17
  149. package/dist/tm/ptc/summary.d.ts.map +0 -1
  150. package/dist/tm/ptc/summary.js +0 -134
  151. package/dist/tm/ptc/summary.js.map +0 -1
  152. package/dist/tm/ptc/tool.d.ts +0 -66
  153. package/dist/tm/ptc/tool.d.ts.map +0 -1
  154. package/dist/tm/ptc/tool.js +0 -231
  155. package/dist/tm/ptc/tool.js.map +0 -1
  156. package/dist/tm/ptc.d.ts +0 -298
  157. package/dist/tm/ptc.d.ts.map +0 -1
  158. package/dist/tm/ptc.js +0 -865
  159. package/dist/tm/ptc.js.map +0 -1
  160. package/dist/tm/pty.d.ts +0 -70
  161. package/dist/tm/pty.d.ts.map +0 -1
  162. package/dist/tm/pty.js +0 -245
  163. package/dist/tm/pty.js.map +0 -1
  164. package/dist/tool-coerce.d.ts +0 -24
  165. package/dist/tool-coerce.d.ts.map +0 -1
  166. package/dist/tool-coerce.js +0 -63
  167. package/dist/tool-coerce.js.map +0 -1
@@ -1,73 +0,0 @@
1
- /**
2
- * Reading a CHILD session's own messages — in process, with no credential.
3
- *
4
- * This is the answer to the HTTP-API decision (task #32). The host's own API
5
- * (`docs/research/host-http-api.md`) does expose `GET /api/session/{id}/message`, and using
6
- * it would have meant reading the Basic-auth password out of
7
- * `<global-config>/service.json` — a secret that would then live in this plugin's memory,
8
- * in every error string the fetch layer can produce, and in any log line that quotes a
9
- * request. The user's condition for going there was "prove it cannot leak".
10
- *
11
- * Measured instead, on a live 2.0.18 with a plugin that only inspects its own ctx (zero
12
- * model tokens, `--model nope/nope`): the plugin ctx carries a full `session` domain —
13
- *
14
- * hook create get switchAgent switchModel prompt generate command synthetic interrupt
15
- * update move wait context
16
- *
17
- * and `ctx.session.context({ sessionID })` returns the session's message array. On the child
18
- * session of a real background dispatch that array had 5 items shaped
19
- * `{id, time:{created}, text, type}`. `ctx.session.get({sessionID})` returns
20
- * `{id, parentID, projectID, agent, model, outcome, tokens, title, location}`.
21
- *
22
- * So the capability was in front of us the whole time and the password was never needed:
23
- * nothing here opens a socket, reads a credential file, or can be redirected to a port that
24
- * is not our own host process. That is a stronger guarantee than "we will keep the secret
25
- * safe" — there is no secret.
26
- *
27
- * The one thing this MUST still earn is scope. `session.context` will answer for ANY session
28
- * id handed to it, including the user's unrelated `build`/`plan` sessions, which makes it a
29
- * private-content oracle if used carelessly. Therefore: parentage is verified with
30
- * `session.get` FIRST, and a session that is not the caller's own child is refused without
31
- * being read. The refusal is counted (`refused`) so "we did not look" stays distinguishable
32
- * from "we looked and were denied". Text bodies are never written to the trajectory — ids,
33
- * item counts and the chosen message's TYPE only, per R6.
34
- */
35
- export interface V2TranscriptReport {
36
- /** read() calls made */
37
- attempted: number;
38
- /** calls that returned a non-empty body */
39
- resolved: number;
40
- /** calls refused because session.get said the child is not the caller's */
41
- refused: number;
42
- /** calls where the parentage check itself could not answer (no seam, host error) */
43
- unverifiable: number;
44
- lastError?: string;
45
- }
46
- export interface V2TranscriptResult {
47
- text: string;
48
- /** how many messages the host handed back */
49
- items: number;
50
- /** the `type` of the message the body was taken from, when it had one */
51
- pickedType?: string;
52
- /** why there is no body — names the seam that failed, never a guess */
53
- error?: string;
54
- }
55
- export interface V2TranscriptReader {
56
- read: (sessionID: string, callerSessionID: string) => Promise<V2TranscriptResult>;
57
- report: V2TranscriptReport;
58
- /** Whether this host gives the seam at all — the caller says so out loud when not. */
59
- active: boolean;
60
- }
61
- /** The child's ANSWER is the last non-user message that carries text. Taking the last,
62
- * rather than concatenating the transcript, is the whole point: the lead asked for the
63
- * report, and the earlier turns are the work it does not want to pay tokens for. The type
64
- * of the chosen item is returned so the reply can say where the text came from instead of
65
- * implying it read the whole session. */
66
- export declare function pickChildReply(items: unknown[]): {
67
- text: string;
68
- type?: string;
69
- };
70
- export declare function createV2TranscriptReader(ctx: unknown): V2TranscriptReader;
71
- /** One line for tm_stats: what the reader was asked for and what it could say. */
72
- export declare function transcriptSummary(r: V2TranscriptReport): string;
73
- //# sourceMappingURL=v2-transcript.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v2-transcript.d.ts","sourceRoot":"","sources":["../../src/host/v2-transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAA;IACf,oFAAoF;IACpF,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACjF,MAAM,EAAE,kBAAkB,CAAA;IAC1B,sFAAsF;IACtF,MAAM,EAAE,OAAO,CAAA;CAChB;AAkCD;;;;0CAI0C;AAC1C,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAQhF;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CA2DzE;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAO/D"}
@@ -1,151 +0,0 @@
1
- /**
2
- * Reading a CHILD session's own messages — in process, with no credential.
3
- *
4
- * This is the answer to the HTTP-API decision (task #32). The host's own API
5
- * (`docs/research/host-http-api.md`) does expose `GET /api/session/{id}/message`, and using
6
- * it would have meant reading the Basic-auth password out of
7
- * `<global-config>/service.json` — a secret that would then live in this plugin's memory,
8
- * in every error string the fetch layer can produce, and in any log line that quotes a
9
- * request. The user's condition for going there was "prove it cannot leak".
10
- *
11
- * Measured instead, on a live 2.0.18 with a plugin that only inspects its own ctx (zero
12
- * model tokens, `--model nope/nope`): the plugin ctx carries a full `session` domain —
13
- *
14
- * hook create get switchAgent switchModel prompt generate command synthetic interrupt
15
- * update move wait context
16
- *
17
- * and `ctx.session.context({ sessionID })` returns the session's message array. On the child
18
- * session of a real background dispatch that array had 5 items shaped
19
- * `{id, time:{created}, text, type}`. `ctx.session.get({sessionID})` returns
20
- * `{id, parentID, projectID, agent, model, outcome, tokens, title, location}`.
21
- *
22
- * So the capability was in front of us the whole time and the password was never needed:
23
- * nothing here opens a socket, reads a credential file, or can be redirected to a port that
24
- * is not our own host process. That is a stronger guarantee than "we will keep the secret
25
- * safe" — there is no secret.
26
- *
27
- * The one thing this MUST still earn is scope. `session.context` will answer for ANY session
28
- * id handed to it, including the user's unrelated `build`/`plan` sessions, which makes it a
29
- * private-content oracle if used carelessly. Therefore: parentage is verified with
30
- * `session.get` FIRST, and a session that is not the caller's own child is refused without
31
- * being read. The refusal is counted (`refused`) so "we did not look" stays distinguishable
32
- * from "we looked and were denied". Text bodies are never written to the trajectory — ids,
33
- * item counts and the chosen message's TYPE only, per R6.
34
- */
35
- /** Unwrap whatever envelope the host puts on a result object. */
36
- function unwrap(raw) {
37
- if (!raw || typeof raw !== "object")
38
- return null;
39
- const rec = raw;
40
- for (const cand of [rec, rec.data, rec.result, rec.session]) {
41
- if (cand && typeof cand === "object")
42
- return cand;
43
- }
44
- return rec;
45
- }
46
- /** The text one message item carries, across the shapes the host has been seen to use:
47
- * a bare `text` (measured on 2.0.18), `parts[]` (the persisted message shape), and
48
- * `content[]` (the tool-result shape). Never descends into a tool payload. */
49
- function itemText(raw) {
50
- if (!raw || typeof raw !== "object")
51
- return null;
52
- const it = raw;
53
- const type = String(it.type ?? it.role ?? "");
54
- const direct = it.text;
55
- if (typeof direct === "string" && direct.trim())
56
- return { text: direct, type };
57
- for (const key of ["parts", "content"]) {
58
- const arr = it[key];
59
- if (!Array.isArray(arr))
60
- continue;
61
- const texts = arr
62
- .map((p) => (p && typeof p === "object" ? p.text : undefined))
63
- .filter((t) => typeof t === "string" && t.trim().length > 0);
64
- if (texts.length)
65
- return { text: texts.join(String.fromCharCode(10)), type };
66
- }
67
- return null;
68
- }
69
- /** The child's ANSWER is the last non-user message that carries text. Taking the last,
70
- * rather than concatenating the transcript, is the whole point: the lead asked for the
71
- * report, and the earlier turns are the work it does not want to pay tokens for. The type
72
- * of the chosen item is returned so the reply can say where the text came from instead of
73
- * implying it read the whole session. */
74
- export function pickChildReply(items) {
75
- for (let i = items.length - 1; i >= 0; i--) {
76
- const t = itemText(items[i]);
77
- if (!t)
78
- continue;
79
- if (/^user$/i.test(t.type))
80
- continue;
81
- return { text: t.text, type: t.type || undefined };
82
- }
83
- return { text: "" };
84
- }
85
- export function createV2TranscriptReader(ctx) {
86
- const report = { attempted: 0, resolved: 0, refused: 0, unverifiable: 0 };
87
- const session = ctx?.session;
88
- const getContext = session?.context;
89
- const get = session?.get;
90
- const active = typeof getContext === "function";
91
- const call = async (fn, arg) => await fn.call(session, arg);
92
- const read = async (sessionID, callerSessionID) => {
93
- report.attempted++;
94
- if (!active || typeof getContext !== "function") {
95
- return { text: "", items: 0, error: "这个宿主没给 ctx.session.context" };
96
- }
97
- if (!sessionID || !callerSessionID)
98
- return { text: "", items: 0, error: "缺少会话 id,无法核验归属" };
99
- // Scope FIRST. A reader that takes whatever id it is handed is a private-content oracle,
100
- // and "the user approved the API" does not extend to their unrelated sessions.
101
- if (typeof get !== "function") {
102
- report.unverifiable++;
103
- return { text: "", items: 0, error: "宿主没给 ctx.session.get,无法核验子会话归属,因此没有读取正文" };
104
- }
105
- let parentID;
106
- try {
107
- const rec = unwrap(await call(get, { sessionID }));
108
- const p = rec?.parentID ?? rec?.parent_id;
109
- parentID = typeof p === "string" && p ? p : undefined;
110
- }
111
- catch (err) {
112
- report.unverifiable++;
113
- report.lastError = String(err?.message ?? err).slice(0, 160);
114
- return { text: "", items: 0, error: `归属核验失败:${report.lastError ?? "宿主未回应"}` };
115
- }
116
- if (!parentID) {
117
- report.unverifiable++;
118
- return { text: "", items: 0, error: "宿主回答里没有 parentID,无法确认这个会话是你的子会话,因此没有读取正文" };
119
- }
120
- if (parentID !== callerSessionID) {
121
- report.refused++;
122
- return { text: "", items: 0, error: "这个会话不是你的子会话(宿主给的 parentID 不匹配),我没有读它" };
123
- }
124
- try {
125
- const raw = await call(getContext, { sessionID });
126
- const arr = Array.isArray(raw) ? raw : Array.isArray(raw?.data) ? raw.data : null;
127
- if (!arr) {
128
- report.lastError = "context 返回值不是数组";
129
- return { text: "", items: 0, error: report.lastError };
130
- }
131
- const picked = pickChildReply(arr);
132
- if (picked.text)
133
- report.resolved++;
134
- return { text: picked.text, items: arr.length, pickedType: picked.type };
135
- }
136
- catch (err) {
137
- report.lastError = String(err?.message ?? err).slice(0, 160);
138
- return { text: "", items: 0, error: `ctx.session.context 失败:${report.lastError}` };
139
- }
140
- };
141
- return { read, report, active };
142
- }
143
- /** One line for tm_stats: what the reader was asked for and what it could say. */
144
- export function transcriptSummary(r) {
145
- if (!r.attempted)
146
- return "子会话正文读取:没被调用过(缝在场,没数据)";
147
- return (`子会话正文读取:尝试 ${r.attempted} · 拿到正文 ${r.resolved} · ` +
148
- `归属不符拒绝 ${r.refused} · 无法核验 ${r.unverifiable}` +
149
- (r.lastError ? ` · 最后错误:${r.lastError}` : ""));
150
- }
151
- //# sourceMappingURL=v2-transcript.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v2-transcript.js","sourceRoot":"","sources":["../../src/host/v2-transcript.ts"],"names":[],"mappings":"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"}
@@ -1,126 +0,0 @@
1
- /**
2
- * Host-hook leverage beyond the tool surface (2026-09-18 audit follow-up).
3
- *
4
- * Everything here rides an ALREADY-PUBLISHED plugin hook — no host source is
5
- * touched — and each knob is independently switchable, because this host's
6
- * d.ts has promised surfaces the runtime never delivered (`permission.ask`
7
- * is declared and never fires on 1.18.29/30). So: pure decisions in one
8
- * place, adapters that never throw and never mutate unless the payload has
9
- * the exact shape we verified.
10
- *
11
- * The four hooks implemented here:
12
- * - `tool.definition` → append OUR discipline to a built-in
13
- * tool's OWN description, where the model decides (a bash timeout rule
14
- * buried in a system prompt loses to one printed next to the parameter);
15
- * - `chat.params` → per-agent sampling overrides
16
- * (off by default: "all agents at 0.2" is a documented project
17
- * invariant, so changing it is the user's call, not ours);
18
- * - `experimental.session.compacting`
19
- * + `experimental.compaction.autocontinue` → keep the reply skeleton and
20
- * the handle rules ALIVE across a compaction, instead of letting the
21
- * summarizer invent its own contract;
22
- * - `shell.env` → NO_COLOR/TERM=dumb for every child
23
- * shell (ANSI escapes in npm/cargo/pytest output are pure context
24
- * tokens), plus an explicit operator passthrough.
25
- *
26
- * Deliberately NOT here: `experimental.chat.messages.transform`. Rewriting
27
- * the outgoing message array means guessing the live shape of tool results
28
- * at that layer; a wrong guess silently deletes the evidence the EVIDENCE
29
- * line depends on. It needs a live host probe first, not a plausible patch.
30
- */
31
- export declare const HOOK_ENV_DEFAULTS: {
32
- readonly toolHints: "on";
33
- readonly agentTemperature: "off";
34
- readonly compactionContext: "on";
35
- readonly compactionAutoContinue: "on";
36
- readonly shellNoColor: "on";
37
- };
38
- type EnvLike = Record<string, string | undefined>;
39
- /** The on/off switches the plugin entry needs before building adapters. */
40
- export declare function hookSwitches(env?: EnvLike): {
41
- toolHints: boolean;
42
- compactionContext: boolean;
43
- };
44
- /**
45
- * Footers appended to a built-in tool's description, keyed by tool id.
46
- * Short, imperative, and about THIS plugin's guarantees — the point is that
47
- * the model reads it at the call site, not thousands of tokens earlier.
48
- */
49
- export declare const TOOL_HINTS: Record<string, string>;
50
- /**
51
- * The `task` footer when the operator has the host's background sub-agents
52
- * enabled (OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS). Without the flag the
53
- * hint above only explains the block; with it, `background:true` becomes the
54
- * route for parallel work the lead will keep following up on — the host's own
55
- * card links to the live child session and the host wakes the parent on
56
- * completion, neither of which a plugin tool can do. TeamMode keeps the
57
- * injected text out of the context window (see src/task-offload.ts), so
58
- * visibility no longer costs tokens.
59
- */
60
- export declare const TASK_HINT_BACKGROUND: Partial<typeof TOOL_HINTS>;
61
- /**
62
- * The `tool.definition` adapter. Returns true when it appended (so the
63
- * caller can log it). Idempotent: a re-issued description that already
64
- * carries the marker is left byte-exact, and the host description is never
65
- * replaced — only extended.
66
- */
67
- export declare function applyToolDefinition(input: unknown, output: unknown, enabled?: boolean, overrides?: Partial<typeof TOOL_HINTS>): boolean;
68
- /**
69
- * Sampling per role, ONLY when the operator opts in. architect is the one
70
- * role whose job (design alternatives) genuinely suffers at 0.2; the
71
- * mechanical roles stay cold — a specialist that hallucinates a file path
72
- * costs the whole team a round.
73
- */
74
- export declare const AGENT_TEMPERATURES: Record<string, number>;
75
- /**
76
- * `TM_AGENT_TEMPERATURE` = off | on | `agent=value;agent=value`.
77
- * Returns the temperature to force, or null to leave the host's value alone.
78
- * A malformed table resolves to null rather than half-applying garbage.
79
- */
80
- export declare function resolveAgentTemperature(agent: unknown, env?: EnvLike): number | null;
81
- /** The `chat.params` adapter: only `temperature` is touched, and only when
82
- * this agent has an override. */
83
- export declare function applyChatParams(input: unknown, output: unknown, env?: EnvLike): boolean;
84
- /**
85
- * Context lines handed to the host's compaction summarizer
86
- * (`experimental.session.compacting` → `output.context[]`). We ADD to the
87
- * default prompt and never replace it: setting `output.prompt` would void
88
- * whatever the host itself learns to preserve, and the summarizer would stop
89
- * being the host's.
90
- *
91
- * Each line corresponds to a thing that, once summarized away, cannot be
92
- * re-derived without spending a round to rediscover it:
93
- * - the reply skeleton — prose instead of STATUS:/CHANGES/… makes the
94
- * lead's machine check fail for the rest of the session;
95
- * - offload handles (ref/access_token/expire_at) — they are the ONLY
96
- * window onto a payload that never entered context;
97
- * - sub-agent child session ids (host `task`, leftovers of the old
98
- * running work, and after a summary it looks indistinguishably like done
99
- * work;
100
- * - provenance (file:line / URL + confidence) — without it a finding
101
- * degrades into model memory, the one thing this project refuses;
102
- * - the todo list and board paths — the state lives there, not in chat.
103
- */
104
- export declare const COMPACTION_CONTEXT: string[];
105
- /** The `experimental.session.compacting` adapter — additive, idempotent. */
106
- export declare function applySessionCompacting(output: unknown, enabled?: boolean): boolean;
107
- /**
108
- * `experimental.compaction.autocontinue`. Left ALONE by default: the host
109
- * resuming after a summary is what keeps a long pipeline moving, and
110
- * stopping mid-flight without being asked is a worse surprise than a
111
- * continued one. `TM_COMPACTION_AUTOCONTINUE=off` opts out — then the run
112
- * pauses for the user to re-read state before anything touches files again.
113
- */
114
- export declare function applyCompactionAutoContinue(output: unknown, env?: EnvLike): boolean;
115
- /**
116
- * Environment for every child shell. NO_COLOR/TERM=dumb is a token saving
117
- * with no functional cost (an agent reading a progress bar reads nothing),
118
- * and `TM_SHELL_ENV` is the explicit operator passthrough (`K=V;K2=V2`) —
119
- * deliberately allowlisted rather than forwarding process env, so this hook
120
- * can never become a side channel for secrets into a sub-agent's shell.
121
- */
122
- export declare function resolveShellEnv(env?: EnvLike): Record<string, string>;
123
- /** The `shell.env` adapter. Never deletes an existing key the host set. */
124
- export declare function applyShellEnv(output: unknown, env?: EnvLike): boolean;
125
- export {};
126
- //# sourceMappingURL=host-hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-hooks.d.ts","sourceRoot":"","sources":["../src/host-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,eAAO,MAAM,iBAAiB;;;;;;CAMpB,CAAA;AAEV,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAOjD,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,GAAG,GAAE,OAAqB,GAAG;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAA;CAAE,CAK3G;AAID;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK7C,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAAO,CAAC,OAAO,UAAU,CAG3D,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,OAAO,UAAO,EACd,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,UAAU,CAAC,GACrC,OAAO,CAeT;AAID;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOrD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,GAAE,OAAqB,GAAG,MAAM,GAAG,IAAI,CAkBjG;AAED;kCACkC;AAClC,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAE,OAAqB,GAAG,OAAO,CAWpG;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,kBAAkB,UAO9B,CAAA;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,UAAO,GAAG,OAAO,CAkB/E;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,GAAE,OAAqB,GAAG,OAAO,CAUhG;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,OAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBlF;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,GAAE,OAAqB,GAAG,OAAO,CAkBlF"}
@@ -1,284 +0,0 @@
1
- /**
2
- * Host-hook leverage beyond the tool surface (2026-09-18 audit follow-up).
3
- *
4
- * Everything here rides an ALREADY-PUBLISHED plugin hook — no host source is
5
- * touched — and each knob is independently switchable, because this host's
6
- * d.ts has promised surfaces the runtime never delivered (`permission.ask`
7
- * is declared and never fires on 1.18.29/30). So: pure decisions in one
8
- * place, adapters that never throw and never mutate unless the payload has
9
- * the exact shape we verified.
10
- *
11
- * The four hooks implemented here:
12
- * - `tool.definition` → append OUR discipline to a built-in
13
- * tool's OWN description, where the model decides (a bash timeout rule
14
- * buried in a system prompt loses to one printed next to the parameter);
15
- * - `chat.params` → per-agent sampling overrides
16
- * (off by default: "all agents at 0.2" is a documented project
17
- * invariant, so changing it is the user's call, not ours);
18
- * - `experimental.session.compacting`
19
- * + `experimental.compaction.autocontinue` → keep the reply skeleton and
20
- * the handle rules ALIVE across a compaction, instead of letting the
21
- * summarizer invent its own contract;
22
- * - `shell.env` → NO_COLOR/TERM=dumb for every child
23
- * shell (ANSI escapes in npm/cargo/pytest output are pure context
24
- * tokens), plus an explicit operator passthrough.
25
- *
26
- * Deliberately NOT here: `experimental.chat.messages.transform`. Rewriting
27
- * the outgoing message array means guessing the live shape of tool results
28
- * at that layer; a wrong guess silently deletes the evidence the EVIDENCE
29
- * line depends on. It needs a live host probe first, not a plausible patch.
30
- */
31
- export const HOOK_ENV_DEFAULTS = {
32
- toolHints: "on",
33
- agentTemperature: "off",
34
- compactionContext: "on",
35
- compactionAutoContinue: "on",
36
- shellNoColor: "on",
37
- };
38
- const isOn = (raw, def) => {
39
- const v = String(raw ?? "").trim().toLowerCase();
40
- if (v === "")
41
- return def === "on";
42
- return !(v === "off" || v === "0" || v === "false" || v === "no");
43
- };
44
- /** The on/off switches the plugin entry needs before building adapters. */
45
- export function hookSwitches(env = process.env) {
46
- return {
47
- toolHints: isOn(env.TM_TOOL_HINTS, HOOK_ENV_DEFAULTS.toolHints),
48
- compactionContext: isOn(env.TM_COMPACTION_CONTEXT, HOOK_ENV_DEFAULTS.compactionContext),
49
- };
50
- }
51
- /* ---------- tool.definition ---------- */
52
- /**
53
- * Footers appended to a built-in tool's description, keyed by tool id.
54
- * Short, imperative, and about THIS plugin's guarantees — the point is that
55
- * the model reads it at the call site, not thousands of tokens earlier.
56
- */
57
- export const TOOL_HINTS = {
58
- bash: "\n\n[OpenCode TeamMode] Time discipline: leave `timeout` out unless this step is genuinely slow — the default kill is 120 s and a bigger number only lengthens the silence, a read-only probe (ls/grep/rg/cat/Get-ChildItem) is never a 120-second command (values above the probe ceiling are clamped before the command runs). Independent steps do NOT belong chained with `;` into one call — separate calls, or one tm_ptc_run program.",
59
- task: "\n\n[OpenCode TeamMode] This blocks your session until the child returns — the right shape for one task whose answer you need next. For several independent tasks you will keep following up on, pass `background: true` (needs the operator flag OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS) so the user can watch the child and the host wakes you when it lands. Keep every brief self-contained: the child sees none of this conversation.",
60
- };
61
- /**
62
- * The `task` footer when the operator has the host's background sub-agents
63
- * enabled (OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS). Without the flag the
64
- * hint above only explains the block; with it, `background:true` becomes the
65
- * route for parallel work the lead will keep following up on — the host's own
66
- * card links to the live child session and the host wakes the parent on
67
- * completion, neither of which a plugin tool can do. TeamMode keeps the
68
- * injected text out of the context window (see src/task-offload.ts), so
69
- * visibility no longer costs tokens.
70
- */
71
- export const TASK_HINT_BACKGROUND = {
72
- task: "\n\n[OpenCode TeamMode] Delegating: `background:true` frees this session at once, the user can open the child's card to watch it, and the host wakes you with the result — TeamMode keeps that finished text OUT of your context (you get a preview). Use it when several children run at once and you will keep working meanwhile; a plain `task` call is right when your very next step needs the answer in hand. To pull a finished child's whole reply back deliberately: `tm_join { ids: [\"<child session>\"] }` — it comes through the offload pipeline as a handle + preview instead of kilotokens inline.",
73
- };
74
- /**
75
- * The `tool.definition` adapter. Returns true when it appended (so the
76
- * caller can log it). Idempotent: a re-issued description that already
77
- * carries the marker is left byte-exact, and the host description is never
78
- * replaced — only extended.
79
- */
80
- export function applyToolDefinition(input, output, enabled = true, overrides) {
81
- if (!enabled)
82
- return false;
83
- try {
84
- const toolID = String(input?.toolID ?? "").trim().toLowerCase();
85
- const key = Object.keys(TOOL_HINTS).find((k) => toolID === k || toolID.endsWith(`:${k}`) || toolID.endsWith(`/${k}`));
86
- if (!key)
87
- return false;
88
- const out = output;
89
- if (!out || typeof out.description !== "string")
90
- return false;
91
- const marker = "[OpenCode TeamMode]";
92
- if (out.description.includes(marker))
93
- return false;
94
- out.description = out.description + (overrides?.[key] ?? TOOL_HINTS[key]);
95
- return true;
96
- }
97
- catch {
98
- return false;
99
- }
100
- }
101
- /* ---------- chat.params ---------- */
102
- /**
103
- * Sampling per role, ONLY when the operator opts in. architect is the one
104
- * role whose job (design alternatives) genuinely suffers at 0.2; the
105
- * mechanical roles stay cold — a specialist that hallucinates a file path
106
- * costs the whole team a round.
107
- */
108
- export const AGENT_TEMPERATURES = {
109
- architect: 0.35,
110
- researcher: 0.3,
111
- team: 0.2,
112
- implementer: 0.2,
113
- reviewer: 0.1,
114
- tester: 0.2,
115
- };
116
- /**
117
- * `TM_AGENT_TEMPERATURE` = off | on | `agent=value;agent=value`.
118
- * Returns the temperature to force, or null to leave the host's value alone.
119
- * A malformed table resolves to null rather than half-applying garbage.
120
- */
121
- export function resolveAgentTemperature(agent, env = process.env) {
122
- const raw = String(env.TM_AGENT_TEMPERATURE ?? HOOK_ENV_DEFAULTS.agentTemperature).trim().toLowerCase();
123
- if (raw === "" || raw === HOOK_ENV_DEFAULTS.agentTemperature)
124
- return null;
125
- const name = String(agent ?? "").trim().toLowerCase();
126
- if (!name)
127
- return null;
128
- if (raw === "on" || raw === "true" || raw === "1") {
129
- const t = AGENT_TEMPERATURES[name];
130
- return typeof t === "number" ? t : null;
131
- }
132
- const table = {};
133
- for (const part of raw.split(/[;,]/)) {
134
- const m = /^\s*([a-z0-9_-]{1,24})\s*=\s*(\d+(?:\.\d+)?)\s*$/.exec(part);
135
- if (!m)
136
- return null;
137
- const value = Number(m[2]);
138
- if (!Number.isFinite(value) || value < 0 || value > 2)
139
- return null;
140
- table[m[1]] = value;
141
- }
142
- return typeof table[name] === "number" ? table[name] : null;
143
- }
144
- /** The `chat.params` adapter: only `temperature` is touched, and only when
145
- * this agent has an override. */
146
- export function applyChatParams(input, output, env = process.env) {
147
- try {
148
- const t = resolveAgentTemperature(input?.agent, env);
149
- if (t === null)
150
- return false;
151
- const out = output;
152
- if (!out || typeof out.temperature !== "number")
153
- return false;
154
- out.temperature = t;
155
- return true;
156
- }
157
- catch {
158
- return false;
159
- }
160
- }
161
- /* ---------- compaction ---------- */
162
- /**
163
- * Context lines handed to the host's compaction summarizer
164
- * (`experimental.session.compacting` → `output.context[]`). We ADD to the
165
- * default prompt and never replace it: setting `output.prompt` would void
166
- * whatever the host itself learns to preserve, and the summarizer would stop
167
- * being the host's.
168
- *
169
- * Each line corresponds to a thing that, once summarized away, cannot be
170
- * re-derived without spending a round to rediscover it:
171
- * - the reply skeleton — prose instead of STATUS:/CHANGES/… makes the
172
- * lead's machine check fail for the rest of the session;
173
- * - offload handles (ref/access_token/expire_at) — they are the ONLY
174
- * window onto a payload that never entered context;
175
- * - sub-agent child session ids (host `task`, leftovers of the old
176
- * running work, and after a summary it looks indistinguishably like done
177
- * work;
178
- * - provenance (file:line / URL + confidence) — without it a finding
179
- * degrades into model memory, the one thing this project refuses;
180
- * - the todo list and board paths — the state lives there, not in chat.
181
- */
182
- export const COMPACTION_CONTEXT = [
183
- "The GOAL directive survives compaction: the user's own ask (GOAL + its ACCEPTANCE criteria) is the contract for this run — carry it verbatim, keep working while a criterion lacks EVIDENCE, and never let a summarized transcript quietly redefine or shrink what they asked for.",
184
- "OpenCode TeamMode contract survives compaction: every specialist reply keeps the STATUS / CHANGES / FINDINGS / EVIDENCE / HANDOFF skeleton and the lead machine-checks it — never summarize a reply into prose without those keys.",
185
- "Offloaded payloads are addressed by handle (ref + access_token + expire_at) from tm_* results. Carry the handles forward VERBATIM; never re-run a tool to rediscover a payload a handle already names.",
186
- "Sub-agent children (the host's task tool, including background tasks) must survive with their session ids: an uncollected child is still-running work, not finished work — and a turn that ends with children open does NOT mean the task is done. After this summary, keep waiting/collecting before reporting anything as delivered.",
187
- "Every finding keeps its source (file:line or URL) and confidence tag after compaction, otherwise it is unverifiable memory.",
188
- "The todo list and the blackboard files are the state, not the transcript: keep task items and board paths, drop chit-chat and raw command echo.",
189
- ];
190
- /** The `experimental.session.compacting` adapter — additive, idempotent. */
191
- export function applySessionCompacting(output, enabled = true) {
192
- if (!enabled)
193
- return false;
194
- try {
195
- const out = output;
196
- if (!out)
197
- return false;
198
- if (!Array.isArray(out.context))
199
- out.context = [];
200
- const list = out.context;
201
- let added = 0;
202
- for (const line of COMPACTION_CONTEXT) {
203
- if (!list.includes(line)) {
204
- list.push(line);
205
- added++;
206
- }
207
- }
208
- return added > 0;
209
- }
210
- catch {
211
- return false;
212
- }
213
- }
214
- /**
215
- * `experimental.compaction.autocontinue`. Left ALONE by default: the host
216
- * resuming after a summary is what keeps a long pipeline moving, and
217
- * stopping mid-flight without being asked is a worse surprise than a
218
- * continued one. `TM_COMPACTION_AUTOCONTINUE=off` opts out — then the run
219
- * pauses for the user to re-read state before anything touches files again.
220
- */
221
- export function applyCompactionAutoContinue(output, env = process.env) {
222
- try {
223
- if (isOn(env.TM_COMPACTION_AUTOCONTINUE, HOOK_ENV_DEFAULTS.compactionAutoContinue))
224
- return false;
225
- const out = output;
226
- if (!out || typeof out.enabled !== "boolean")
227
- return false;
228
- out.enabled = false;
229
- return true;
230
- }
231
- catch {
232
- return false;
233
- }
234
- }
235
- /* ---------- shell.env ---------- */
236
- /**
237
- * Environment for every child shell. NO_COLOR/TERM=dumb is a token saving
238
- * with no functional cost (an agent reading a progress bar reads nothing),
239
- * and `TM_SHELL_ENV` is the explicit operator passthrough (`K=V;K2=V2`) —
240
- * deliberately allowlisted rather than forwarding process env, so this hook
241
- * can never become a side channel for secrets into a sub-agent's shell.
242
- */
243
- export function resolveShellEnv(env = process.env) {
244
- const out = {};
245
- if (isOn(env.TM_SHELL_NO_COLOR, HOOK_ENV_DEFAULTS.shellNoColor)) {
246
- out.NO_COLOR = "1";
247
- out.CLANG_COLOR_MODE = "never";
248
- out.TERM = "dumb";
249
- }
250
- const raw = String(env.TM_SHELL_ENV ?? "").trim();
251
- if (raw) {
252
- for (const part of raw.split(/[;]/)) {
253
- const m = /^\s*([A-Za-z_][A-Za-z0-9_]{0,40})\s*=\s*(.*)$/.exec(part);
254
- if (!m)
255
- continue;
256
- out[m[1]] = m[2].replace(/\s+$/, "");
257
- }
258
- }
259
- return out;
260
- }
261
- /** The `shell.env` adapter. Never deletes an existing key the host set. */
262
- export function applyShellEnv(output, env = process.env) {
263
- try {
264
- const patch = resolveShellEnv(env);
265
- const out = output;
266
- if (!out || Object.keys(patch).length === 0)
267
- return false;
268
- if (!out.env || typeof out.env !== "object")
269
- out.env = {};
270
- const target = out.env;
271
- let added = 0;
272
- for (const [k, v] of Object.entries(patch)) {
273
- if (target[k] === undefined) {
274
- target[k] = v;
275
- added++;
276
- }
277
- }
278
- return added > 0;
279
- }
280
- catch {
281
- return false;
282
- }
283
- }
284
- //# sourceMappingURL=host-hooks.js.map