@te-river/opencode-team-mode 1.7.2 → 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.
- package/README.md +1009 -849
- package/README.zh-CN.md +906 -756
- package/dist/host/v2-browser-gate.d.ts +4 -1
- package/dist/host/v2-browser-gate.d.ts.map +1 -1
- package/dist/host/v2-browser-gate.js +1 -1
- package/dist/host/v2-browser-gate.js.map +1 -1
- package/dist/host/v2-capabilities.js +1 -1
- package/dist/host/v2-capabilities.js.map +1 -1
- package/dist/host/v2-guard.d.ts +3 -1
- package/dist/host/v2-guard.d.ts.map +1 -1
- package/dist/host/v2-guard.js +2 -2
- package/dist/host/v2-guard.js.map +1 -1
- package/dist/host/v2-offload.d.ts +12 -2
- package/dist/host/v2-offload.d.ts.map +1 -1
- package/dist/host/v2-offload.js +9 -9
- package/dist/host/v2-offload.js.map +1 -1
- package/dist/host/v2-probe-chain.d.ts +5 -1
- package/dist/host/v2-probe-chain.d.ts.map +1 -1
- package/dist/host/v2-probe-chain.js +4 -6
- package/dist/host/v2-probe-chain.js.map +1 -1
- package/dist/host/v2-probe.d.ts.map +1 -1
- package/dist/host/v2-probe.js +2 -1
- package/dist/host/v2-probe.js.map +1 -1
- package/dist/host/v2-prune.d.ts +5 -1
- package/dist/host/v2-prune.d.ts.map +1 -1
- package/dist/host/v2-prune.js +8 -14
- package/dist/host/v2-prune.js.map +1 -1
- package/dist/host/v2-retry.d.ts +9 -2
- package/dist/host/v2-retry.d.ts.map +1 -1
- package/dist/host/v2-retry.js +13 -15
- package/dist/host/v2-retry.js.map +1 -1
- package/dist/host/v2-session.d.ts +6 -2
- package/dist/host/v2-session.d.ts.map +1 -1
- package/dist/host/v2-session.js +7 -13
- package/dist/host/v2-session.js.map +1 -1
- package/dist/host/v2.d.ts +7 -2
- package/dist/host/v2.d.ts.map +1 -1
- package/dist/host/v2.js +38 -35
- package/dist/host/v2.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 +23 -7
- package/dist/prompts/lead.js.map +1 -1
- package/dist/prompts/specialists.d.ts +1 -1
- package/dist/prompts/specialists.d.ts.map +1 -1
- package/dist/prompts/specialists.js +1 -1
- package/dist/tm/args-schema.d.ts +1 -1
- package/dist/tm/args-schema.js +6 -6
- package/dist/tm/args-schema.js.map +1 -1
- package/dist/tm/board.js +2 -2
- package/dist/tm/board.js.map +1 -1
- package/dist/tm/config-files.d.ts +41 -17
- package/dist/tm/config-files.d.ts.map +1 -1
- package/dist/tm/config-files.js +68 -73
- package/dist/tm/config-files.js.map +1 -1
- package/dist/tm/config-layers.d.ts +65 -33
- package/dist/tm/config-layers.d.ts.map +1 -1
- package/dist/tm/config-layers.js +165 -81
- package/dist/tm/config-layers.js.map +1 -1
- package/dist/tm/config-template.d.ts +32 -0
- package/dist/tm/config-template.d.ts.map +1 -0
- package/dist/tm/config-template.js +74 -0
- package/dist/tm/config-template.js.map +1 -0
- package/dist/tm/config.d.ts +115 -140
- package/dist/tm/config.d.ts.map +1 -1
- package/dist/tm/config.js +100 -200
- package/dist/tm/config.js.map +1 -1
- package/dist/tm/index.d.ts +11 -3
- package/dist/tm/index.d.ts.map +1 -1
- package/dist/tm/index.js +46 -16
- package/dist/tm/index.js.map +1 -1
- package/dist/tm/ledger.d.ts +7 -3
- package/dist/tm/ledger.d.ts.map +1 -1
- package/dist/tm/ledger.js +5 -4
- package/dist/tm/ledger.js.map +1 -1
- package/dist/tm/memory.js +3 -3
- package/dist/tm/pipelines.js +1 -1
- package/dist/tm/search.js +6 -6
- package/dist/tm/search.js.map +1 -1
- package/dist/tm/stats.js +2 -2
- package/dist/tm/stats.js.map +1 -1
- package/dist/tm/tools.js +2 -2
- package/dist/tm/webfetch.d.ts +5 -2
- package/dist/tm/webfetch.d.ts.map +1 -1
- package/dist/tm/webfetch.js +24 -18
- package/dist/tm/webfetch.js.map +1 -1
- package/docs/installation-v2.md +59 -38
- package/docs/installation.md +4 -4
- package/package.json +1 -1
- package/dist/approval-gate.d.ts +0 -205
- package/dist/approval-gate.d.ts.map +0 -1
- package/dist/approval-gate.js +0 -627
- package/dist/approval-gate.js.map +0 -1
- package/dist/host/v1.d.ts +0 -14
- package/dist/host/v1.d.ts.map +0 -1
- package/dist/host/v1.js +0 -340
- package/dist/host/v1.js.map +0 -1
- package/dist/host/v2-client.d.ts +0 -53
- package/dist/host/v2-client.d.ts.map +0 -1
- package/dist/host/v2-client.js +0 -193
- package/dist/host/v2-client.js.map +0 -1
- package/dist/host/v2-transcript.d.ts +0 -73
- package/dist/host/v2-transcript.d.ts.map +0 -1
- package/dist/host/v2-transcript.js +0 -151
- package/dist/host/v2-transcript.js.map +0 -1
- package/dist/host-hooks.d.ts +0 -126
- package/dist/host-hooks.d.ts.map +0 -1
- package/dist/host-hooks.js +0 -284
- package/dist/host-hooks.js.map +0 -1
- package/dist/tm/bash-timeout.d.ts +0 -72
- package/dist/tm/bash-timeout.d.ts.map +0 -1
- package/dist/tm/bash-timeout.js +0 -100
- package/dist/tm/bash-timeout.js.map +0 -1
- package/dist/tm/browser.d.ts +0 -642
- package/dist/tm/browser.d.ts.map +0 -1
- package/dist/tm/browser.js +0 -2836
- package/dist/tm/browser.js.map +0 -1
- package/dist/tm/ptc/budgets.d.ts +0 -59
- package/dist/tm/ptc/budgets.d.ts.map +0 -1
- package/dist/tm/ptc/budgets.js +0 -138
- package/dist/tm/ptc/budgets.js.map +0 -1
- package/dist/tm/ptc/contract.d.ts +0 -171
- package/dist/tm/ptc/contract.d.ts.map +0 -1
- package/dist/tm/ptc/contract.js +0 -85
- package/dist/tm/ptc/contract.js.map +0 -1
- package/dist/tm/ptc/driver.d.ts +0 -40
- package/dist/tm/ptc/driver.d.ts.map +0 -1
- package/dist/tm/ptc/driver.js +0 -213
- package/dist/tm/ptc/driver.js.map +0 -1
- package/dist/tm/ptc/engines.d.ts +0 -43
- package/dist/tm/ptc/engines.d.ts.map +0 -1
- package/dist/tm/ptc/engines.js +0 -454
- package/dist/tm/ptc/engines.js.map +0 -1
- package/dist/tm/ptc/gate.d.ts +0 -28
- package/dist/tm/ptc/gate.d.ts.map +0 -1
- package/dist/tm/ptc/gate.js +0 -130
- package/dist/tm/ptc/gate.js.map +0 -1
- package/dist/tm/ptc/index.d.ts +0 -19
- package/dist/tm/ptc/index.d.ts.map +0 -1
- package/dist/tm/ptc/index.js +0 -15
- package/dist/tm/ptc/index.js.map +0 -1
- package/dist/tm/ptc/pscan.d.ts +0 -30
- package/dist/tm/ptc/pscan.d.ts.map +0 -1
- package/dist/tm/ptc/pscan.js +0 -341
- package/dist/tm/ptc/pscan.js.map +0 -1
- package/dist/tm/ptc/summary.d.ts +0 -17
- package/dist/tm/ptc/summary.d.ts.map +0 -1
- package/dist/tm/ptc/summary.js +0 -134
- package/dist/tm/ptc/summary.js.map +0 -1
- package/dist/tm/ptc/tool.d.ts +0 -66
- package/dist/tm/ptc/tool.d.ts.map +0 -1
- package/dist/tm/ptc/tool.js +0 -231
- package/dist/tm/ptc/tool.js.map +0 -1
- package/dist/tm/ptc.d.ts +0 -298
- package/dist/tm/ptc.d.ts.map +0 -1
- package/dist/tm/ptc.js +0 -865
- package/dist/tm/ptc.js.map +0 -1
- package/dist/tm/pty.d.ts +0 -70
- package/dist/tm/pty.d.ts.map +0 -1
- package/dist/tm/pty.js +0 -245
- package/dist/tm/pty.js.map +0 -1
- package/dist/tool-coerce.d.ts +0 -24
- package/dist/tool-coerce.d.ts.map +0 -1
- package/dist/tool-coerce.js +0 -63
- 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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"}
|
package/dist/host-hooks.d.ts
DELETED
|
@@ -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
|
package/dist/host-hooks.d.ts.map
DELETED
|
@@ -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"}
|
package/dist/host-hooks.js
DELETED
|
@@ -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
|