@te-river/opencode-team-mode 1.8.5 → 1.8.6
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 +9 -8
- package/README.zh-CN.md +10 -9
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +14 -5
- package/dist/agents.js.map +1 -1
- package/dist/host/v2-types.d.ts +0 -1
- package/dist/host/v2-types.d.ts.map +1 -1
- package/dist/host/v2.d.ts +9 -5
- package/dist/host/v2.d.ts.map +1 -1
- package/dist/host/v2.js +84 -72
- package/dist/host/v2.js.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- 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 +7 -0
- package/dist/prompts/lead.js.map +1 -1
- package/dist/prompts/shared.d.ts +1 -1
- package/dist/prompts/shared.d.ts.map +1 -1
- package/dist/prompts/shared.js +10 -0
- package/dist/prompts/shared.js.map +1 -1
- package/dist/tm/config-files.d.ts +30 -6
- package/dist/tm/config-files.d.ts.map +1 -1
- package/dist/tm/config-files.js +47 -10
- package/dist/tm/config-files.js.map +1 -1
- package/dist/tm/config-layers.d.ts +20 -9
- package/dist/tm/config-layers.d.ts.map +1 -1
- package/dist/tm/config-layers.js +19 -8
- package/dist/tm/config-layers.js.map +1 -1
- package/dist/tm/config-template.js +2 -2
- package/dist/tm/config-template.js.map +1 -1
- package/dist/tm/dispatch.d.ts +67 -11
- package/dist/tm/dispatch.d.ts.map +1 -1
- package/dist/tm/dispatch.js +254 -27
- package/dist/tm/dispatch.js.map +1 -1
- package/dist/tm/pipelines.d.ts.map +1 -1
- package/dist/tm/pipelines.js +6 -3
- package/dist/tm/pipelines.js.map +1 -1
- package/dist/tm/result.d.ts +13 -1
- package/dist/tm/result.d.ts.map +1 -1
- package/dist/tm/result.js +14 -2
- package/dist/tm/result.js.map +1 -1
- package/docs/installation-v2.md +28 -24
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/tm/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAc,MAAM,aAAa,CAAA;AAMxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzE;6DAC6D;AAC7D,eAAO,MAAM,gBAAgB,2EAA4E,CAAA;AAEzG;;;4DAG4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;+CAE2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;8CAG0C;IAC1C,GAAG,CAAC,EAAE,gBAAgB,CAAA;IACtB
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/tm/dispatch.ts"],"names":[],"mappings":"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"}
|
package/dist/tm/dispatch.js
CHANGED
|
@@ -112,6 +112,34 @@ export function sessionApiOf(client) {
|
|
|
112
112
|
return null;
|
|
113
113
|
return api;
|
|
114
114
|
}
|
|
115
|
+
export async function readHostSessionState(api, sessionID) {
|
|
116
|
+
const id = String(sessionID ?? "").trim();
|
|
117
|
+
if (!api || typeof api.get !== "function" || !id)
|
|
118
|
+
return { answered: false };
|
|
119
|
+
try {
|
|
120
|
+
// The flat `{sessionID}` spelling is the measured one; the v2 bridge also retries the
|
|
121
|
+
// older `{path:{id}}` shape, so either call convention still lands.
|
|
122
|
+
const un = unwrapClientResult(await api.get({ sessionID: id }));
|
|
123
|
+
if (!un.ok)
|
|
124
|
+
return { answered: false };
|
|
125
|
+
const d = un.data;
|
|
126
|
+
if (!d || typeof d !== "object")
|
|
127
|
+
return { answered: false };
|
|
128
|
+
const createdAt = typeof d.time?.created === "number" && d.time.created > 0 ? d.time.created : undefined;
|
|
129
|
+
const idleAt = typeof d.time?.idle === "number" && d.time.idle > 0 ? d.time.idle : undefined;
|
|
130
|
+
const outcome = typeof d.outcome === "string" && d.outcome.trim() ? d.outcome.trim() : undefined;
|
|
131
|
+
return {
|
|
132
|
+
answered: true,
|
|
133
|
+
...(createdAt ? { createdAt } : {}),
|
|
134
|
+
...(idleAt ? { idleAt } : {}),
|
|
135
|
+
...(outcome ? { outcome } : {}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// A throwing seam is "we could not ask" — never "it settled".
|
|
140
|
+
return { answered: false };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
115
143
|
/** The host's OWN visible-and-non-blocking sub-agent path: its built-in `task`
|
|
116
144
|
* takes `background: true`, and because that card is rendered by the host's
|
|
117
145
|
* own `task` renderer it links to the live child session — which a plugin tool
|
|
@@ -365,10 +393,27 @@ export function describeHostError(err, max = 220) {
|
|
|
365
393
|
* that had already landed — the fix is to say it, not to withhold the text.
|
|
366
394
|
*/
|
|
367
395
|
export const INCOMPLETE_PRESUMED_BODY_WARNING = "⚠ 推定结算且正文未带完成时间:这可能是最后一次工具调用前的半截话,不要据此判定交付;稍后再次 tm_join 可复核(或让子代理续跑)。";
|
|
368
|
-
/** One child's status line — the lead reads these, so keep them terse.
|
|
396
|
+
/** One child's status line — the lead reads these, so keep them terse. The tag is chosen by
|
|
397
|
+
* the settle SOURCE, because the same word would otherwise cover a host-confirmed finish, an
|
|
398
|
+
* unreachable seam and a guess: only `session-get` may print the host's outcome, and nothing
|
|
399
|
+
* but a real settlement may print 完成. */
|
|
369
400
|
export function renderChildLine(c, elapsedMs) {
|
|
370
401
|
const secs = Math.round(elapsedMs / 1000);
|
|
371
|
-
const
|
|
402
|
+
const hostRunSecs = c.hostIdleAt !== undefined && c.hostCreatedAt !== undefined && c.hostIdleAt > c.hostCreatedAt
|
|
403
|
+
? Math.round((c.hostIdleAt - c.hostCreatedAt) / 1000)
|
|
404
|
+
: secs;
|
|
405
|
+
const tag = c.state === "running"
|
|
406
|
+
? `运行中 ${secs}s`
|
|
407
|
+
: c.state === "error"
|
|
408
|
+
? `失败:${shorten(c.error ?? "", 80)}`
|
|
409
|
+
: c.settleSource === "session-get"
|
|
410
|
+
? // The host's own record answered with a finished run: the only form allowed to
|
|
411
|
+
// print its outcome word, and it never says 推定.
|
|
412
|
+
`已结算(${c.outcome ?? "宿主未给 outcome"})· 运行 ${hostRunSecs}s`
|
|
413
|
+
: c.settleSource === "parent-idle"
|
|
414
|
+
? // An inference. 完成 is exactly the word this may not borrow.
|
|
415
|
+
`推定已结算(未确认完成)· 派发后 ${secs}s`
|
|
416
|
+
: `已完成 ${secs}s`;
|
|
372
417
|
// #33: a row the host reported IDLE for a stop attempt is not a delivery — the lead
|
|
373
418
|
// needs to see that the child was never running, not a bare 已完成.
|
|
374
419
|
const stop = c.state === "idle" && c.cancelNote ? ` ·${c.cancelNote}` : "";
|
|
@@ -378,6 +423,38 @@ export function renderChildLine(c, elapsedMs) {
|
|
|
378
423
|
? " ·(宿主 subagent 派发)"
|
|
379
424
|
: ""}`;
|
|
380
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* One line saying HOW the round's rows reached their state: `宿主确认` (the host's own record,
|
|
428
|
+
* event or envelope), `推定` (our inference) and `未结算` (still running). A lead skimming two
|
|
429
|
+
* rows that both read 已结算 otherwise cannot tell which one the host actually confirmed — and
|
|
430
|
+
* that difference is the whole point of asking `session.get` first.
|
|
431
|
+
*/
|
|
432
|
+
export function settleTallyLine(children) {
|
|
433
|
+
if (!children.length)
|
|
434
|
+
return "";
|
|
435
|
+
let confirmed = 0;
|
|
436
|
+
let presumed = 0;
|
|
437
|
+
let open = 0;
|
|
438
|
+
const outcomes = new Map();
|
|
439
|
+
for (const c of children) {
|
|
440
|
+
if (c.state === "running") {
|
|
441
|
+
open++;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (c.settleSource === "parent-idle") {
|
|
445
|
+
presumed++;
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
confirmed++;
|
|
449
|
+
if (c.outcome)
|
|
450
|
+
outcomes.set(c.outcome, (outcomes.get(c.outcome) ?? 0) + 1);
|
|
451
|
+
}
|
|
452
|
+
const named = [...outcomes.entries()].map(([o, n]) => `${o} ${n}`);
|
|
453
|
+
const withoutOutcome = confirmed - [...outcomes.values()].reduce((a, b) => a + b, 0);
|
|
454
|
+
if (withoutOutcome > 0)
|
|
455
|
+
named.push(`宿主未给 outcome ${withoutOutcome}`);
|
|
456
|
+
return `结算:宿主确认 ${confirmed}${named.length ? `(${named.join(" · ")})` : ""} / 推定 ${presumed} / 未结算 ${open}`;
|
|
457
|
+
}
|
|
381
458
|
/** Verdicts tm_join can actually observe (issue #7's "leader keeps control"). */
|
|
382
459
|
export function summarizeStates(children) {
|
|
383
460
|
let running = 0;
|
|
@@ -528,6 +605,51 @@ export function steerVerdictLine(outcome, opts) {
|
|
|
528
605
|
return `未插话${seam}:${shorten(opts?.note ?? "宿主没有回 inbox id", 120)}`;
|
|
529
606
|
}
|
|
530
607
|
}
|
|
608
|
+
/** What the lead reads after the delivery check. Every branch says out loud that delivery is
|
|
609
|
+
* not obedience — the composition must never promise what the child will do. */
|
|
610
|
+
export function steerDeliveryLine(v, inboxID, note) {
|
|
611
|
+
const inbox = inboxID ? `(inbox=${inboxID})` : "";
|
|
612
|
+
switch (v) {
|
|
613
|
+
case "delivered":
|
|
614
|
+
return `已送达${inbox}:该 inbox 项已经不在待投递清单里,宿主把它交给了子会话。投递不等于子代理照做 —— 它是否按这条改,仍要看下一次结算与正文。`;
|
|
615
|
+
case "queued":
|
|
616
|
+
return `已受理(未送达,宿主在步边界投递)${inbox}:该项仍在待投递清单里。投递≠执行,别把这一句当成子代理会照做。`;
|
|
617
|
+
case "unverified":
|
|
618
|
+
return `已受理${inbox}(未验证送达${note ? `:${shorten(note, 80)}` : ":宿主没有给出可核对的待投递清单"})。投递≠执行。`;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
/** Re-read the child's inbox once after a successful steer and answer the only question that
|
|
622
|
+
* can be answered: is the item still pending? `unverified` covers every shape we cannot
|
|
623
|
+
* read — no seam, a throwing call, a payload with items we could not identify — because
|
|
624
|
+
* "absent" must not be inferred from "unreadable". */
|
|
625
|
+
export async function verifySteerDelivery(api, sessionID, inboxID) {
|
|
626
|
+
if (!api || api.v2SteerSeam !== true || typeof api.inboxList !== "function") {
|
|
627
|
+
return { verdict: "unverified", note: "这个宿主没有 session.inbox.list 的缝" };
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
const un = unwrapClientResult(await api.inboxList({ sessionID }));
|
|
631
|
+
if (!un.ok)
|
|
632
|
+
return { verdict: "unverified", note: un.message };
|
|
633
|
+
const data = un.data;
|
|
634
|
+
const seamOut = String(data?.outcome ?? "");
|
|
635
|
+
if (seamOut === "threw" || seamOut === "no-seam") {
|
|
636
|
+
return {
|
|
637
|
+
verdict: "unverified",
|
|
638
|
+
note: seamOut === "no-seam" ? "宿主没给 session.inbox.list" : String(data?.message ?? "查询被宿主拒绝"),
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
const listed = inboxItemLines(data?.items);
|
|
642
|
+
// An item we could not parse is not an item that is gone: `skipped` covers unknown id
|
|
643
|
+
// shapes AND the overflow past the list cap, and both make "absent" unprovable.
|
|
644
|
+
if (listed.skipped > 0)
|
|
645
|
+
return { verdict: "unverified", note: `清单里有 ${listed.skipped} 项没被列出(id 形状或条数上限)` };
|
|
646
|
+
const stillQueued = listed.lines.some((l) => l.split(" · ")[0] === `id=${inboxID}`);
|
|
647
|
+
return stillQueued ? { verdict: "queued" } : { verdict: "delivered" };
|
|
648
|
+
}
|
|
649
|
+
catch (err) {
|
|
650
|
+
return { verdict: "unverified", note: describeHostError(err, 120) };
|
|
651
|
+
}
|
|
652
|
+
}
|
|
531
653
|
export function steerOutcomeLabel(o) {
|
|
532
654
|
return o === "steered" ? "已受理" : o === "not-steered" ? "未插话" : "无插话缝";
|
|
533
655
|
}
|
|
@@ -688,6 +810,15 @@ export function buildDispatchTools(deps) {
|
|
|
688
810
|
// comparable with this one. Renaming would split the history in two.
|
|
689
811
|
const log = (e) => store.appendTrajectory({ tool: "tm_dispatch", ...e });
|
|
690
812
|
const children = new Map();
|
|
813
|
+
/** Settle checks still in flight. `observeEvent` is a synchronous hook — the host does not
|
|
814
|
+
* await it — while asking `session.get` is async, so the read is tracked here and DRAINED by
|
|
815
|
+
* tm_join before it reports. Without that, a join issued right after a parent-idle event
|
|
816
|
+
* could answer from a row we were in the middle of updating. */
|
|
817
|
+
const settling = new Set();
|
|
818
|
+
const trackSettle = (p) => {
|
|
819
|
+
settling.add(p);
|
|
820
|
+
void p.finally(() => settling.delete(p));
|
|
821
|
+
};
|
|
691
822
|
/** Per-parent memory of the LAST join's outcome. A wait is not parallelism
|
|
692
823
|
* — while tm_join is in flight the lead's turn is blocked exactly like a
|
|
693
824
|
* synchronous `task` call — so a SECOND wait that inherited a first wait
|
|
@@ -820,7 +951,7 @@ export function buildDispatchTools(deps) {
|
|
|
820
951
|
// occurs on a v1-shaped client that lacks `session.messages`, and a sentence
|
|
821
952
|
// blaming "v2" there would be false in the opposite direction.
|
|
822
953
|
if (typeof api?.children !== "function")
|
|
823
|
-
return { adopted: 0, looked: false, why: "这个宿主客户端没有给出 session.children
|
|
954
|
+
return { adopted: 0, looked: false, why: "这个宿主客户端没有给出 session.children(缺的只是「列举会话树」这一条缝,不是我整个 session API 不可用),我没有看过会话树" };
|
|
824
955
|
if (!parent)
|
|
825
956
|
return { adopted: 0, looked: false, why: "调用方会话 id 缺失,无从查询子会话" };
|
|
826
957
|
let un;
|
|
@@ -953,7 +1084,7 @@ export function buildDispatchTools(deps) {
|
|
|
953
1084
|
- No args: status snapshot of every open child of THIS session — running / idle(done) / error, with elapsed seconds. Cheap and non-blocking: use it to decide whether to keep working or start merging.
|
|
954
1085
|
- { waitMs: 30000 }: bounded wait (capped at ${waitLabel(maxWaitMs)}) until every child settles. A wait BLOCKS YOU — your turn is parked in this tool call, so it is the synchronous \`task\` experience with none of its visibility. Wait once, briefly, and only when the very next step needs the answer; a second consecutive wait after nothing settled is cut to 10s and answered with what to do instead. Never wait for a child whose result you do not need — abort it instead ({ cancel: true }).
|
|
955
1086
|
- { ids: [...] }: restrict to those child sessions — a host \`task\` child is collectable when you NAME it (its parentage is verified against the host's own session tree, never assumed); { cancel: true }: abort still-running ones. With explicit ids the host is ALWAYS asked, even for a row this process believes settled — a settled-looking row can have been woken and be running for real.
|
|
956
|
-
- Steer a child while it runs — the same call, no separate tool: { steer: "先停手,验收改成 X", ids: ["<child>"] } admits the line into that child's inbox with delivery "steer" (or "queue") and the host wakes execution at the step boundary. 已受理
|
|
1087
|
+
- Steer a child while it runs — the same call, no separate tool: { steer: "先停手,验收改成 X", ids: ["<child>"] } admits the line into that child's inbox with delivery "steer" (or "queue") and the host wakes execution at the step boundary. 已受理 means the host took the line into that inbox — NOT that the child read or obeyed it. After a successful steer the tool re-reads that inbox ONCE: an item gone from the pending list is reported 已送达, still pending is 已受理(未送达,宿主在步边界投递), and an inbox it cannot read is 已受理(未验证送达)— delivery is never inferred from the write alone, and none of the three means the child followed it. Steer REFUSES a child whose registered state is not running (idle / error / unknown), because the host's doc sentence "Steering wakes session execution" means the line starts a second run that overlaps new dispatches — pass { wake: true } to do that deliberately. { unread: true, ids: ["<child>"] } lists that child's undelivered inbox items (ids + shape only, never the body); { unsend: {"sessionID":"ses_…","inboxID":"msg_…"} } withdraws one that has not been delivered — an already-delivered item is the documented no-op and is reported as one. All three ask the host for parentage FIRST and refuse a session that is not yours; a host without the seam answers no-seam instead of pretending.
|
|
957
1088
|
- A plugin/host restart does NOT orphan a child: those the host still lists under your session are re-adopted automatically (their rows are marked 接管), and one that answered while nobody was listening is reported 已完成, not lost.
|
|
958
1089
|
- Replies come back through the offload pipeline: a long sub-agent report arrives as a handle + ≤80-token preview (page it with tm_fetch), so a batch of parallel work does not multiply your context. STATUS: blocked/failed children are surfaced first, always. When a child's own session yields no assistant text, the host's injected \`<subagent sessionID="…">…</subagent>\` envelope in THIS session is read as the body and labelled 正文来源=宿主注入信封 (an id must match exactly).`,
|
|
959
1090
|
args: {
|
|
@@ -999,6 +1130,10 @@ export function buildDispatchTools(deps) {
|
|
|
999
1130
|
// wrote its governed result — so the token table read "0 调用 / 2 结果",
|
|
1000
1131
|
// which looks like a broken counter rather than a tool that ran.
|
|
1001
1132
|
store.appendTrajectory({ tool, step_id: "join", event: "call" });
|
|
1133
|
+
// A settle check may still be talking to the host (see `settling`). Drain it before
|
|
1134
|
+
// reading the registry so this reply never mixes a fresh table with a half-updated row.
|
|
1135
|
+
if (settling.size)
|
|
1136
|
+
await Promise.allSettled([...settling]);
|
|
1002
1137
|
const directory = typeof c.directory === "string" && c.directory ? c.directory : undefined;
|
|
1003
1138
|
const idList = parseIdList(args.ids);
|
|
1004
1139
|
const idFilter = idList && idList.length ? new Set(idList) : null;
|
|
@@ -1140,6 +1275,20 @@ export function buildDispatchTools(deps) {
|
|
|
1140
1275
|
// and a verdict read off whatever came back.
|
|
1141
1276
|
note = "这个客户端不是 v2 的 ctx.session 桥(v1 的 session.prompt 走 {path,body},没有 delivery 字段),未调用";
|
|
1142
1277
|
}
|
|
1278
|
+
// C — the write only proves ADMISSION. Ask the same inbox once more for the one
|
|
1279
|
+
// checkable fact: is the item still pending? Nothing is claimed when the seam
|
|
1280
|
+
// cannot answer (see steerDeliveryLine's unverified branch).
|
|
1281
|
+
const verified = outcome === "steered" && inboxID ? await verifySteerDelivery(api, target, inboxID) : null;
|
|
1282
|
+
if (verified) {
|
|
1283
|
+
log({
|
|
1284
|
+
step_id: "join",
|
|
1285
|
+
event: "steer_delivery",
|
|
1286
|
+
child: target,
|
|
1287
|
+
inboxID,
|
|
1288
|
+
verdict: verified.verdict,
|
|
1289
|
+
...(verified.note ? { reason: shorten(verified.note, 120) } : {}),
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1143
1292
|
log({
|
|
1144
1293
|
step_id: "join",
|
|
1145
1294
|
event: "steer",
|
|
@@ -1150,6 +1299,7 @@ export function buildDispatchTools(deps) {
|
|
|
1150
1299
|
ok: outcome === "steered",
|
|
1151
1300
|
wake: wakeRequested,
|
|
1152
1301
|
...(inboxID ? { inboxID } : {}),
|
|
1302
|
+
...(verified ? { delivered: verified.verdict } : {}),
|
|
1153
1303
|
...(seam ? { seam } : {}),
|
|
1154
1304
|
// the LENGTH, never the text — R6 binds the audit trail as much as the reply
|
|
1155
1305
|
chars: steerText.length,
|
|
@@ -1160,7 +1310,10 @@ export function buildDispatchTools(deps) {
|
|
|
1160
1310
|
const wake = outcome === "steered" && wakeRequested && row && row.state !== "running"
|
|
1161
1311
|
? `\n已按 wake:true 唤醒:它当时不是运行中(${row.state === "error" ? "失败" : "已结算"}),宿主把这条插话当成新的执行输入——它会和新派发的 worker 并发,留意这一点。`
|
|
1162
1312
|
: "";
|
|
1163
|
-
|
|
1313
|
+
const deliveryLine = verified
|
|
1314
|
+
? `\n投递核对:${steerDeliveryLine(verified.verdict, inboxID, verified.note)}`
|
|
1315
|
+
: "";
|
|
1316
|
+
return toToolResult(`插话目标:${rowLine}\n插话结果:${steerVerdictLine(outcome, { inboxID, note, seam })}\n插话计数:${steerOutcomeParts(tally).join(" · ")}${deliveryLine}${wake}`);
|
|
1164
1317
|
}
|
|
1165
1318
|
if (wantsUnread) {
|
|
1166
1319
|
if (!api || api.v2SteerSeam !== true || typeof api.inboxList !== "function") {
|
|
@@ -1223,18 +1376,29 @@ export function buildDispatchTools(deps) {
|
|
|
1223
1376
|
const saw = adoption.looked;
|
|
1224
1377
|
const inner = idFilter
|
|
1225
1378
|
? (() => {
|
|
1226
|
-
//
|
|
1227
|
-
//
|
|
1228
|
-
//
|
|
1229
|
-
//
|
|
1230
|
-
//
|
|
1379
|
+
// TWO independent facts live here, and they used to be fused into one
|
|
1380
|
+
// sentence: what the session READER can do, and whether the CHILDREN
|
|
1381
|
+
// endpoint exists. On 2.1.4 `ctx.session.get({sessionID})` answers while
|
|
1382
|
+
// `session.children` does not, so the old text ("可辨认的 session API 缺失"
|
|
1383
|
+
// plus the last error of a shape already abandoned) told the lead the host
|
|
1384
|
+
// had no session API at all — and it went back to assuming its id was
|
|
1385
|
+
// fine and the host broken. State them separately, and name the shapes
|
|
1386
|
+
// only when EVERY shape failed: a shape that worked makes the abandoned
|
|
1387
|
+
// ones irrelevant, not evidence.
|
|
1231
1388
|
const sr = deps.sessionReaderReport?.();
|
|
1232
1389
|
const bridge = sr && sr.attempted > 0
|
|
1233
|
-
?
|
|
1390
|
+
? sr.usedShape
|
|
1391
|
+
? `;ctx.session.get 可用(形状 ${sr.usedShape}):宿主没有 session.children,按 id 读单条会话可以,列举会话树不行`
|
|
1392
|
+
: `;按 id 读子会话也失败:试过的 ctx.session 形状 ${sr.failedShapes.length ? sr.failedShapes.join("→") : "?"}${sr.lastError ? `(宿主最后一次的错误:${shorten(sr.lastError, 90)})` : "(宿主没有报错,只是没给出 parentID)"}`
|
|
1234
1393
|
: "";
|
|
1394
|
+
// A named id that neither our registry nor the host's by-id read could
|
|
1395
|
+
// resolve is a miss about THAT id — saying it means the host has no
|
|
1396
|
+
// session API is the claim this branch exists to stop making.
|
|
1397
|
+
const named = [...idFilter].slice(0, 3).map((s) => shorten(s, 40)).join("、");
|
|
1398
|
+
const miss = `ids 未匹配到本会话的子代理:${named} 在本机不存在或无权限`;
|
|
1235
1399
|
return saw
|
|
1236
|
-
?
|
|
1237
|
-
:
|
|
1400
|
+
? `(${miss};宿主会话树里也没有可认领的子会话${bridge})`
|
|
1401
|
+
: `(${miss};而且我没能查看宿主会话树:${adoption.why}${bridge})`;
|
|
1238
1402
|
})()
|
|
1239
1403
|
: saw
|
|
1240
1404
|
? ""
|
|
@@ -1362,8 +1526,10 @@ export function buildDispatchTools(deps) {
|
|
|
1362
1526
|
});
|
|
1363
1527
|
}
|
|
1364
1528
|
const sums = summarizeStates(mine);
|
|
1529
|
+
const settleLine = settleTallyLine(mine);
|
|
1365
1530
|
const header = [
|
|
1366
1531
|
`派发汇总:${sums.idle} 完成 / ${sums.running} 运行中 / ${sums.error} 失败${settled ? "(全部已结算)" : `(未等到全部结算 · 本次已等 ${Math.round((now() - waitStart) / 1000)}s)`}`,
|
|
1532
|
+
...(settleLine ? [settleLine] : []),
|
|
1367
1533
|
...mine.map((r) => renderChildLine(r, (r.finishedAt ?? now()) - r.startedAt)),
|
|
1368
1534
|
];
|
|
1369
1535
|
// The `正文:` line belongs directly under the child rows it describes, but the
|
|
@@ -1538,6 +1704,66 @@ export function buildDispatchTools(deps) {
|
|
|
1538
1704
|
}
|
|
1539
1705
|
},
|
|
1540
1706
|
};
|
|
1707
|
+
/** Settle the still-running HOST children of a session that just went idle — by ASKING the
|
|
1708
|
+
* host, not by assuming. One `session.get` per child, three outcomes kept apart:
|
|
1709
|
+
* - `time.idle` present → settled, `settleSource="session-get"`, host `outcome` kept;
|
|
1710
|
+
* - answered WITHOUT idle → the child is still running: the row is left alone and the
|
|
1711
|
+
* attempt is counted (`settle_unchecked`), because a live child reported as settled is
|
|
1712
|
+
* the exact overstatement this tool was already fixed for;
|
|
1713
|
+
* - no seam / a throwing call → the old `parent-idle` presumption, printed as one.
|
|
1714
|
+
* `idle_settled` and `idle_presumed` are separate trajectory rows on purpose: tm_stats can
|
|
1715
|
+
* then show how often the host actually confirmed a finish. */
|
|
1716
|
+
async function reconcileHostChildren(rows) {
|
|
1717
|
+
for (const child of rows) {
|
|
1718
|
+
if (child.state !== "running")
|
|
1719
|
+
continue;
|
|
1720
|
+
const st = await readHostSessionState(api, child.sessionID);
|
|
1721
|
+
if (st.idleAt) {
|
|
1722
|
+
child.state = "idle";
|
|
1723
|
+
child.finishedAt = st.idleAt;
|
|
1724
|
+
child.hostIdleAt = st.idleAt;
|
|
1725
|
+
if (st.createdAt !== undefined)
|
|
1726
|
+
child.hostCreatedAt = st.createdAt;
|
|
1727
|
+
child.settleSource = "session-get";
|
|
1728
|
+
if (st.outcome)
|
|
1729
|
+
child.outcome = st.outcome;
|
|
1730
|
+
log({
|
|
1731
|
+
step_id: "events",
|
|
1732
|
+
event: "idle_settled",
|
|
1733
|
+
source: "session-get",
|
|
1734
|
+
child: child.sessionID,
|
|
1735
|
+
agent: child.agent,
|
|
1736
|
+
...(st.outcome ? { outcome: st.outcome } : {}),
|
|
1737
|
+
ms: st.idleAt - child.startedAt,
|
|
1738
|
+
});
|
|
1739
|
+
continue;
|
|
1740
|
+
}
|
|
1741
|
+
if (st.answered) {
|
|
1742
|
+
log({
|
|
1743
|
+
step_id: "events",
|
|
1744
|
+
event: "settle_unchecked",
|
|
1745
|
+
source: "session-get",
|
|
1746
|
+
reason: "still-running",
|
|
1747
|
+
child: child.sessionID,
|
|
1748
|
+
agent: child.agent,
|
|
1749
|
+
});
|
|
1750
|
+
continue;
|
|
1751
|
+
}
|
|
1752
|
+
presumeParentIdle([child]);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
/** The FALLBACK: a still-running host child of a session that just went idle, on a host we
|
|
1756
|
+
* could not ask. Recorded as `parent-idle` and printed as a presumption — never 完成. */
|
|
1757
|
+
function presumeParentIdle(rows) {
|
|
1758
|
+
for (const child of rows) {
|
|
1759
|
+
if (child.state !== "running")
|
|
1760
|
+
continue;
|
|
1761
|
+
child.state = "idle";
|
|
1762
|
+
child.finishedAt = now();
|
|
1763
|
+
child.settleSource = "parent-idle";
|
|
1764
|
+
log({ step_id: "events", event: "idle_presumed", child: child.sessionID, agent: child.agent, ms: child.finishedAt - child.startedAt });
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1541
1767
|
const observeEvent = (event) => {
|
|
1542
1768
|
const rawType = String(event?.type ?? "");
|
|
1543
1769
|
if (!rawType.startsWith("session."))
|
|
@@ -1560,21 +1786,22 @@ export function buildDispatchTools(deps) {
|
|
|
1560
1786
|
return;
|
|
1561
1787
|
const rec = children.get(sid);
|
|
1562
1788
|
if (!rec) {
|
|
1563
|
-
// The parent's own idle is the only completion signal
|
|
1564
|
-
// child
|
|
1565
|
-
//
|
|
1566
|
-
//
|
|
1567
|
-
//
|
|
1789
|
+
// The parent's own idle is the only completion signal the host gives us for a HOST
|
|
1790
|
+
// child — and it is not the strongest one available: `session.get({sessionID})` answers
|
|
1791
|
+
// the child's own `time.idle` (verified on 2.1.4), so we ASK first and only fall back to
|
|
1792
|
+
// the inference when the host cannot be asked. A row that stayed 运行中 forever would be
|
|
1793
|
+
// an overstatement in reverse; a row presumed settled when the host says it is still
|
|
1794
|
+
// executing is the overstatement this tool exists to refuse.
|
|
1568
1795
|
if (type === "session.idle") {
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1796
|
+
const pending = [...children.values()].filter((child) => child.via === "host-injection" && child.state === "running" && child.parentSessionID === sid);
|
|
1797
|
+
if (pending.length) {
|
|
1798
|
+
// No seam at all → the fallback needs no round-trip, so it lands HERE and the
|
|
1799
|
+
// synchronous hook stays synchronously readable. A seam that EXISTS is asked, and
|
|
1800
|
+
// that read is tracked so tm_join drains it before reporting.
|
|
1801
|
+
if (!api || typeof api.get !== "function")
|
|
1802
|
+
presumeParentIdle(pending);
|
|
1803
|
+
else
|
|
1804
|
+
trackSettle(reconcileHostChildren(pending));
|
|
1578
1805
|
}
|
|
1579
1806
|
}
|
|
1580
1807
|
return;
|