oasis_test_v2 2.2.58 → 2.2.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +667 -136
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -835,9 +835,11 @@ var init_avatar = __esm({
|
|
|
835
835
|
});
|
|
836
836
|
|
|
837
837
|
// ../contract/src/runtime.ts
|
|
838
|
+
var SESSION_STARTUP_FAILED_PREFIX;
|
|
838
839
|
var init_runtime = __esm({
|
|
839
840
|
"../contract/src/runtime.ts"() {
|
|
840
841
|
"use strict";
|
|
842
|
+
SESSION_STARTUP_FAILED_PREFIX = "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5728\u8282\u70B9\u4E0A\u542F\u52A8\uFF1A";
|
|
841
843
|
}
|
|
842
844
|
});
|
|
843
845
|
|
|
@@ -10569,6 +10571,8 @@ function estimateUsageCostBreakdown(model, usage) {
|
|
|
10569
10571
|
cacheMicros: Math.round(
|
|
10570
10572
|
(cacheCreationTokens * price.inputPerMillion + cacheReadTokens * cacheReadPrice) / 1e6 * 1e6
|
|
10571
10573
|
),
|
|
10574
|
+
cacheReadMicros: Math.round(cacheReadTokens * cacheReadPrice / 1e6 * 1e6),
|
|
10575
|
+
cacheCreationMicros: Math.round(cacheCreationTokens * price.inputPerMillion / 1e6 * 1e6),
|
|
10572
10576
|
outputMicros: Math.round(outputTokens * price.outputPerMillion / 1e6 * 1e6)
|
|
10573
10577
|
};
|
|
10574
10578
|
}
|
|
@@ -11281,8 +11285,8 @@ var init_chat_item_ledger = __esm({
|
|
|
11281
11285
|
note: text6,
|
|
11282
11286
|
...extra?.reason ? { reason: extra.reason } : {},
|
|
11283
11287
|
...extra?.detail ? { detail: extra.detail } : {},
|
|
11284
|
-
/*
|
|
11285
|
-
刷新 /
|
|
11288
|
+
/* 「这一轮是谁停的」这一格(ADR 0600):**持久的那一份归因就在这里**——
|
|
11289
|
+
刷新 / 换设备读的是这一行,前端据它决定提示条上那句话;缺省 = 认不出 = 「对话已中断」。 */
|
|
11286
11290
|
...extra?.cancelledBy ? { cancelledBy: extra.cancelledBy } : {}
|
|
11287
11291
|
}
|
|
11288
11292
|
});
|
|
@@ -11876,15 +11880,39 @@ var init_chat_item_ledger = __esm({
|
|
|
11876
11880
|
});
|
|
11877
11881
|
|
|
11878
11882
|
// ../server/src/chat-cancel-attribution.ts
|
|
11879
|
-
function
|
|
11880
|
-
|
|
11881
|
-
return { text: USER_STOPPED_OUTPUT_NOTE, extra: { ...extra, cancelledBy: "user" } };
|
|
11883
|
+
function hasCancelNote(by) {
|
|
11884
|
+
return by !== void 0 && by in CHAT_CANCEL_NOTE;
|
|
11882
11885
|
}
|
|
11883
|
-
|
|
11886
|
+
function chatCancelNote(by) {
|
|
11887
|
+
return hasCancelNote(by) ? CHAT_CANCEL_NOTE[by] : CHAT_CANCELLED_NOTE;
|
|
11888
|
+
}
|
|
11889
|
+
function cancelCauseFromExitMessage(errorMessage2) {
|
|
11890
|
+
const line2 = errorMessage2?.trim();
|
|
11891
|
+
if (!line2) return void 0;
|
|
11892
|
+
return NODE_SHUTDOWN_EXIT_MARKERS.some((m2) => line2.includes(m2)) ? "system-restart" : void 0;
|
|
11893
|
+
}
|
|
11894
|
+
function attributeChatExit(text6, extra, cause) {
|
|
11895
|
+
if (extra.reason !== "cancelled") return { text: text6, extra };
|
|
11896
|
+
const by = cause ?? extra.cancelledBy;
|
|
11897
|
+
if (!by) return { text: text6, extra };
|
|
11898
|
+
return { text: chatCancelNote(by), extra: { ...extra, cancelledBy: by } };
|
|
11899
|
+
}
|
|
11900
|
+
var CHAT_CANCEL_NOTE, CHAT_CANCELLED_NOTE, USER_STOPPED_OUTPUT_NOTE, NODE_SHUTDOWN_EXIT_MARKERS;
|
|
11884
11901
|
var init_chat_cancel_attribution = __esm({
|
|
11885
11902
|
"../server/src/chat-cancel-attribution.ts"() {
|
|
11886
11903
|
"use strict";
|
|
11887
|
-
|
|
11904
|
+
CHAT_CANCEL_NOTE = {
|
|
11905
|
+
user: "\u7528\u6237\u505C\u6B62\u4E86\u8F93\u51FA",
|
|
11906
|
+
"system-restart": "\u5BF9\u8BDD\u5DF2\u4E2D\u65AD\uFF1A\u7CFB\u7EDF\u66F4\u65B0\u91CD\u542F",
|
|
11907
|
+
"node-disconnected": "\u5BF9\u8BDD\u5DF2\u4E2D\u65AD\uFF1A\u6267\u884C\u8FD9\u4E00\u8F6E\u7684\u673A\u5668\u65AD\u5F00\u4E86\u8FDE\u63A5",
|
|
11908
|
+
runtime: "\u5BF9\u8BDD\u5DF2\u4E2D\u65AD\uFF1A\u6267\u884C\u7A0B\u5E8F\u4E2D\u9014\u53D6\u6D88\u4E86\u8FD9\u4E00\u8F6E"
|
|
11909
|
+
};
|
|
11910
|
+
CHAT_CANCELLED_NOTE = "\u5BF9\u8BDD\u5DF2\u4E2D\u65AD";
|
|
11911
|
+
USER_STOPPED_OUTPUT_NOTE = CHAT_CANCEL_NOTE.user;
|
|
11912
|
+
NODE_SHUTDOWN_EXIT_MARKERS = [
|
|
11913
|
+
"\u8282\u70B9 daemon \u505C\u673A\uFF08",
|
|
11914
|
+
"\u8282\u70B9\u6B63\u5728\u505C\u673A\u6392\u7A7A"
|
|
11915
|
+
];
|
|
11888
11916
|
}
|
|
11889
11917
|
});
|
|
11890
11918
|
|
|
@@ -12322,9 +12350,10 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
12322
12350
|
chatExit?.summary ?? (err instanceof Error ? err.message : String(err)),
|
|
12323
12351
|
{
|
|
12324
12352
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
12325
|
-
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
12353
|
+
...chatExit?.detail ? { detail: chatExit.detail } : {},
|
|
12354
|
+
...chatExit?.cancelledBy ? { cancelledBy: chatExit.cancelledBy } : {}
|
|
12326
12355
|
},
|
|
12327
|
-
liveTurn?.
|
|
12356
|
+
liveTurn?.cancelCause()
|
|
12328
12357
|
);
|
|
12329
12358
|
const part = { type: "error", seq: ++localSeq, text: text6, ...extra };
|
|
12330
12359
|
localParts.push(part);
|
|
@@ -12385,6 +12414,73 @@ var init_stable_chat_dispatch_ids = __esm({
|
|
|
12385
12414
|
}
|
|
12386
12415
|
});
|
|
12387
12416
|
|
|
12417
|
+
// ../server/src/chat-interrupt-notice.ts
|
|
12418
|
+
function chatInterruptCauseOf(exitReason) {
|
|
12419
|
+
if (exitReason === "sweep-orphan" || exitReason === "sweep-absolute-cap") return exitReason;
|
|
12420
|
+
return "run-failed";
|
|
12421
|
+
}
|
|
12422
|
+
async function recordChatTurnInterruption(deps) {
|
|
12423
|
+
const { items, sessionId, turnId } = deps;
|
|
12424
|
+
if (!items || !chatItemsDoubleWriteEnabled()) return [];
|
|
12425
|
+
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
12426
|
+
const log3 = deps.log ?? (() => {
|
|
12427
|
+
});
|
|
12428
|
+
try {
|
|
12429
|
+
const rows = await items.listItemsByTurn(sessionId, turnId);
|
|
12430
|
+
const noticeId = chatInterruptItemId(turnId);
|
|
12431
|
+
const existing = rows.find((r) => r.id === noticeId);
|
|
12432
|
+
let version2 = await items.sessionVersionCursor(sessionId).catch(() => 0);
|
|
12433
|
+
const touched = [];
|
|
12434
|
+
const at = now();
|
|
12435
|
+
for (const row of rows) {
|
|
12436
|
+
if (row.status !== "streaming") continue;
|
|
12437
|
+
const updated = await items.applyOp(row.id, { op: "set_status", status: "interrupted" }, { at, version: ++version2, sessionId });
|
|
12438
|
+
if (updated) touched.push(updated);
|
|
12439
|
+
}
|
|
12440
|
+
const payload2 = deps.cause === "sweep-orphan" ? { code: "error", note: CHAT_INTERRUPT_NOTICE[deps.cause], reason: "cancelled", closedBy: deps.reason } : { code: "error", note: CHAT_INTERRUPT_NOTICE[deps.cause], reason: deps.reason };
|
|
12441
|
+
if (existing) {
|
|
12442
|
+
const updated = await items.applyOp(noticeId, { op: "set_payload", payload: payload2 }, { at, version: ++version2, sessionId });
|
|
12443
|
+
if (updated) touched.push(updated);
|
|
12444
|
+
return touched;
|
|
12445
|
+
}
|
|
12446
|
+
const created = await items.createItem({
|
|
12447
|
+
id: noticeId,
|
|
12448
|
+
sessionId,
|
|
12449
|
+
turnId,
|
|
12450
|
+
...deps.messageId ? { messageId: deps.messageId } : {},
|
|
12451
|
+
...deps.runId ? { runId: deps.runId } : {},
|
|
12452
|
+
kind: "control",
|
|
12453
|
+
role: "assistant",
|
|
12454
|
+
status: "failed",
|
|
12455
|
+
payload: payload2,
|
|
12456
|
+
// 这句话不进 `chat_messages.content`(那边由各自的收口路自己写),对账按这一位分流。
|
|
12457
|
+
metadata: { [CHAT_ITEM_META_LEGACY_CONTENT]: false },
|
|
12458
|
+
createdAt: at,
|
|
12459
|
+
version: ++version2
|
|
12460
|
+
});
|
|
12461
|
+
touched.push(created);
|
|
12462
|
+
return touched;
|
|
12463
|
+
} catch (err) {
|
|
12464
|
+
log3(`[chat-interrupt] items \u7ED3\u5C40\u9879\u5199\u5165\u5931\u8D25\uFF1Asession=${sessionId} turn=${turnId} ${String(err)}`);
|
|
12465
|
+
return [];
|
|
12466
|
+
}
|
|
12467
|
+
}
|
|
12468
|
+
var chatInterruptItemId, CHAT_INTERRUPT_NOTICE;
|
|
12469
|
+
var init_chat_interrupt_notice = __esm({
|
|
12470
|
+
"../server/src/chat-interrupt-notice.ts"() {
|
|
12471
|
+
"use strict";
|
|
12472
|
+
init_chat_item();
|
|
12473
|
+
init_chat_cancel_attribution();
|
|
12474
|
+
init_chat_item_ledger();
|
|
12475
|
+
chatInterruptItemId = (turnId) => `chat-interrupt:${turnId}`;
|
|
12476
|
+
CHAT_INTERRUPT_NOTICE = {
|
|
12477
|
+
"sweep-orphan": CHAT_CANCELLED_NOTE,
|
|
12478
|
+
"sweep-absolute-cap": "\u8FD9\u4E00\u8F6E\u8FD0\u884C\u65F6\u95F4\u8D85\u8FC7\u4E86\u4E0A\u9650\uFF0C\u5DF2\u88AB\u7CFB\u7EDF\u7EC8\u6B62\u3002\u4E0A\u9762\u662F\u7EC8\u6B62\u524D\u5DF2\u7ECF\u751F\u6210\u7684\u5185\u5BB9\uFF0C\u53EF\u4EE5\u628A\u4EFB\u52A1\u62C6\u5C0F\u4E00\u4E9B\u518D\u53D1\u4E00\u6B21\u3002",
|
|
12479
|
+
"run-failed": "\u8FD9\u4E00\u8F6E\u6CA1\u6709\u6B63\u5E38\u7ED3\u675F\uFF08\u8FD0\u884C\u88AB\u4E2D\u65AD\uFF09\uFF0C\u6CA1\u80FD\u8DD1\u5B8C\u3002\u4E0A\u9762\u662F\u4E2D\u65AD\u524D\u5DF2\u7ECF\u751F\u6210\u7684\u5185\u5BB9\uFF0C\u4F60\u53EF\u4EE5\u518D\u53D1\u4E00\u6B21\u3002"
|
|
12480
|
+
};
|
|
12481
|
+
}
|
|
12482
|
+
});
|
|
12483
|
+
|
|
12388
12484
|
// ../server/src/chat-turn-gate.ts
|
|
12389
12485
|
function localTurnSourceRunId(source, turnId) {
|
|
12390
12486
|
return `${source}:${turnId}`;
|
|
@@ -12540,6 +12636,19 @@ function assistantRowRecovery(chatStore, opts = {}) {
|
|
|
12540
12636
|
now: () => patch.completedAt,
|
|
12541
12637
|
...outputsPart ? { outputsPart } : {}
|
|
12542
12638
|
});
|
|
12639
|
+
if (patch.status !== "error") return;
|
|
12640
|
+
const items = opts.itemsFor?.(turn.chatSessionId);
|
|
12641
|
+
if (!items) return;
|
|
12642
|
+
await recordChatTurnInterruption({
|
|
12643
|
+
items,
|
|
12644
|
+
sessionId: turn.chatSessionId,
|
|
12645
|
+
turnId: await resolveChatItemsTurnId(items, row.id, fallbackTurnId(turn.assistantRunId ?? void 0)),
|
|
12646
|
+
runId: turn.assistantRunId ?? null,
|
|
12647
|
+
messageId: row.id,
|
|
12648
|
+
cause: patch.cause ?? "run-failed",
|
|
12649
|
+
reason: patch.cause ?? "run-failed",
|
|
12650
|
+
now: () => patch.completedAt
|
|
12651
|
+
});
|
|
12543
12652
|
}
|
|
12544
12653
|
};
|
|
12545
12654
|
}
|
|
@@ -12570,7 +12679,9 @@ async function reconcileChatTurn(turn, deps) {
|
|
|
12570
12679
|
if (deps.settleAssistantRow) {
|
|
12571
12680
|
await deps.settleAssistantRow(turn, {
|
|
12572
12681
|
status: patch.status === "succeeded" ? "done" : "error",
|
|
12573
|
-
completedAt: patch.completedAt
|
|
12682
|
+
completedAt: patch.completedAt,
|
|
12683
|
+
// 由头只在判死那一档有意义;`orphaned` 是本拍自己判的,其余按 run 的 exit_reason 走。
|
|
12684
|
+
...patch.status === "succeeded" ? {} : { cause: patch.cause ?? "run-failed" }
|
|
12574
12685
|
}).catch((e) => deps.log?.(`[chat-turn] \u8F6E\u6B21 ${turn.id} \u7684 assistant \u884C\u6536\u53E3\u5931\u8D25\uFF08\u8F6E\u6B21\u7167\u5E38\u6536\u53E3\uFF09: ${String(e)}`));
|
|
12575
12686
|
}
|
|
12576
12687
|
return writeTurn(patch);
|
|
@@ -12616,7 +12727,9 @@ async function reconcileChatTurn(turn, deps) {
|
|
|
12616
12727
|
const ok3 = await settleTurnAndRow({
|
|
12617
12728
|
status,
|
|
12618
12729
|
completedAt: run.endedAt ?? new Date(deps.now).toISOString(),
|
|
12619
|
-
...status === "succeeded" ? {} : { lastError: `run ${runId} \u7EC8\u6001 ${run.status}\uFF08\u7531\u6062\u590D\u5668\u6536\u53E3\uFF09` }
|
|
12730
|
+
...status === "succeeded" ? {} : { lastError: `run ${runId} \u7EC8\u6001 ${run.status}\uFF08\u7531\u6062\u590D\u5668\u6536\u53E3\uFF09` },
|
|
12731
|
+
// Bug #0185:孤儿扫描 / 绝对上限关掉的轮,对话里那句话要说得出是哪一种。
|
|
12732
|
+
...status === "succeeded" ? {} : { cause: chatInterruptCauseOf(run.exitReason) }
|
|
12620
12733
|
});
|
|
12621
12734
|
if (!ok3) return raced;
|
|
12622
12735
|
return { settled: true, turnId: turn.id, status, reason: `run \u7EC8\u6001 ${run.status}` };
|
|
@@ -12678,6 +12791,8 @@ var init_chat_turn_gate = __esm({
|
|
|
12678
12791
|
import_node_crypto16 = require("node:crypto");
|
|
12679
12792
|
init_chat_assistant_turn();
|
|
12680
12793
|
init_stable_chat_dispatch_ids();
|
|
12794
|
+
init_chat_item_ledger();
|
|
12795
|
+
init_chat_interrupt_notice();
|
|
12681
12796
|
DISPATCH_INTENT_PREFIX = "dispatch-intent:";
|
|
12682
12797
|
DISPATCH_CLAIMED_PREFIX = "dispatch-claimed:";
|
|
12683
12798
|
CHAT_TURN_ORPHAN_GRACE_MS = 10 * 6e4;
|
|
@@ -163715,7 +163830,8 @@ var init_live_chat = __esm({
|
|
|
163715
163830
|
turn.v3Subscribers.delete(sub);
|
|
163716
163831
|
};
|
|
163717
163832
|
},
|
|
163718
|
-
|
|
163833
|
+
cancelCause: () => turn.cancelCause,
|
|
163834
|
+
stoppedByUser: () => turn.cancelCause === "user",
|
|
163719
163835
|
finish: (status) => {
|
|
163720
163836
|
stopUserItems?.();
|
|
163721
163837
|
for (const receipt of turn.inputReceipts.values()) receipt.abandon();
|
|
@@ -164290,7 +164406,7 @@ var init_live_chat = __esm({
|
|
|
164290
164406
|
return { stopped: false, reason: "turn-changed" };
|
|
164291
164407
|
}
|
|
164292
164408
|
if (turn.status === "running") {
|
|
164293
|
-
turn.
|
|
164409
|
+
turn.cancelCause = "user";
|
|
164294
164410
|
try {
|
|
164295
164411
|
turn.handle.kill();
|
|
164296
164412
|
} catch {
|
|
@@ -164298,6 +164414,17 @@ var init_live_chat = __esm({
|
|
|
164298
164414
|
}
|
|
164299
164415
|
return { stopped: true };
|
|
164300
164416
|
}
|
|
164417
|
+
/**
|
|
164418
|
+
* 平台自己要停这一轮时的留痕(ADR 0600 扩档):liveness 静默保险丝、hub 驱逐会话都经这里。
|
|
164419
|
+
* 与 {@link stop} 同一条纪律——**写在真去 kill 之前**,且只写还在跑的那一轮;已经收尾的不改。
|
|
164420
|
+
* 先到先赢:`/stop` 已经写过 `user` 的轮不被后来的系统原因盖掉。
|
|
164421
|
+
*/
|
|
164422
|
+
markCancelCause(chatSessionId, cause) {
|
|
164423
|
+
const turn = this.turns.get(chatSessionId);
|
|
164424
|
+
if (!turn || turn.status !== "running" || turn.cancelCause) return false;
|
|
164425
|
+
turn.cancelCause = cause;
|
|
164426
|
+
return true;
|
|
164427
|
+
}
|
|
164301
164428
|
/** 该会话当前是否有一轮 running(供上层判断是否要挡并发发送)。 */
|
|
164302
164429
|
isRunning(chatSessionId) {
|
|
164303
164430
|
return this.turns.get(chatSessionId)?.status === "running";
|
|
@@ -166577,7 +166704,7 @@ async function validateExplicitOwner(registry2, owner, artifactType) {
|
|
|
166577
166704
|
if (!b2 || b2.status !== "active") {
|
|
166578
166705
|
return {
|
|
166579
166706
|
actor: null,
|
|
166580
|
-
issue: { code: "missing_binding", severity: "warning", artifactType, message: `\u6307\u5B9A\u7684\u8D1F\u8D23\u4EBA ${a.name}\uFF08${owner}\uFF09\u7F3A\u5C11
|
|
166707
|
+
issue: { code: "missing_binding", severity: "warning", artifactType, message: `\u6307\u5B9A\u7684\u8D1F\u8D23\u4EBA ${a.name}\uFF08${owner}\uFF09\u7F3A\u5C11\u53EF\u7528\u7684\u8FD0\u884C\u65F6\u7ED1\u5B9A\uFF0C${artifactType} \u8282\u70B9\u9000\u56DE\u6309\u5C97\u4F4D\u89E3\u6790\u3002` }
|
|
166581
166708
|
};
|
|
166582
166709
|
}
|
|
166583
166710
|
}
|
|
@@ -166626,7 +166753,7 @@ async function resolveActiveRole(registry2, role, artifactType, roles) {
|
|
|
166626
166753
|
severity: "warning",
|
|
166627
166754
|
role,
|
|
166628
166755
|
...artifactType ? { artifactType } : {},
|
|
166629
|
-
message: `\u5C97\u4F4D ${role} \u6307\u5B9A\u7684\u9ED8\u8BA4\u627F\u63A5\u4EBA ${declared} \u5F53\u524D\u4E0D\u53EF\u63A5\u5355\uFF08\u79BB\u5C97 / \u4E0D\u5728\u8BE5\u5C97\u4F4D / \u7F3A
|
|
166756
|
+
message: `\u5C97\u4F4D ${role} \u6307\u5B9A\u7684\u9ED8\u8BA4\u627F\u63A5\u4EBA ${declared} \u5F53\u524D\u4E0D\u53EF\u63A5\u5355\uFF08\u79BB\u5C97 / \u4E0D\u5728\u8BE5\u5C97\u4F4D / \u7F3A\u8FD0\u884C\u65F6\u7ED1\u5B9A\uFF09\uFF0C\u5DF2\u9000\u56DE\u6309\u89C4\u5219\u6311\u4EBA\u3002`
|
|
166630
166757
|
}
|
|
166631
166758
|
};
|
|
166632
166759
|
}
|
|
@@ -166659,7 +166786,7 @@ async function resolveActiveRole(registry2, role, artifactType, roles) {
|
|
|
166659
166786
|
...artifactType ? { artifactType } : {},
|
|
166660
166787
|
// 措辞对两条路都成立:规则路会丢节点、agent/剧本路保留为待岗——**都是「暂不自动派发」**,
|
|
166661
166788
|
// 而非「暂不创建」(后者在保留待岗后就是假话)。
|
|
166662
|
-
message: `${preferred.name}\uFF08${preferred.id}\uFF09\u7F3A\u5C11
|
|
166789
|
+
message: `${preferred.name}\uFF08${preferred.id}\uFF09\u7F3A\u5C11\u53EF\u7528\u7684\u8FD0\u884C\u65F6\u7ED1\u5B9A\uFF0C${nodeWord}\u6682\u4E0D\u81EA\u52A8\u6D3E\u53D1\uFF08\u5F85\u8865\u9F50\u540E\u81EA\u52A8\u63A5\u624B\uFF09\u3002`
|
|
166663
166790
|
}
|
|
166664
166791
|
};
|
|
166665
166792
|
}
|
|
@@ -168631,7 +168758,19 @@ async function planChatContinuation(input) {
|
|
|
168631
168758
|
fallbackInjectedMessageIds: fallbackMessage ? fallbackHistory.map((t) => t.id) : []
|
|
168632
168759
|
};
|
|
168633
168760
|
}
|
|
168634
|
-
|
|
168761
|
+
function placeholderRuntimeSessionFlag(isPlaceholder) {
|
|
168762
|
+
return isPlaceholder ? { [RUNTIME_SESSION_ID_PLACEHOLDER_KEY]: true } : {};
|
|
168763
|
+
}
|
|
168764
|
+
function nativeRuntimeSessionMetadata(metadata, reported) {
|
|
168765
|
+
if (!reported) return void 0;
|
|
168766
|
+
const base = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : void 0;
|
|
168767
|
+
if (!base) return void 0;
|
|
168768
|
+
if (base[RUNTIME_SESSION_ID_PLACEHOLDER_KEY] !== true) return void 0;
|
|
168769
|
+
if (typeof base["runtimeSessionId"] !== "string" || base["runtimeSessionId"] === reported) return void 0;
|
|
168770
|
+
const { [RUNTIME_SESSION_ID_PLACEHOLDER_KEY]: _wasPlaceholder, ...rest } = base;
|
|
168771
|
+
return { ...rest, runtimeSessionId: reported };
|
|
168772
|
+
}
|
|
168773
|
+
var HISTORY_FETCH_SLACK, UNFINISHED_OPEN, UNFINISHED_EMPTY, RUNTIME_SESSION_ID_PLACEHOLDER_KEY;
|
|
168635
168774
|
var init_continuation3 = __esm({
|
|
168636
168775
|
"../server/src/domains/chat-sessions/continuation.ts"() {
|
|
168637
168776
|
"use strict";
|
|
@@ -168641,6 +168780,7 @@ var init_continuation3 = __esm({
|
|
|
168641
168780
|
HISTORY_FETCH_SLACK = 2;
|
|
168642
168781
|
UNFINISHED_OPEN = "\u3010\u4E0A\u4E00\u8F6E\u672A\u5B8C\u6210\u2014\u2014\u4EE5\u4E0B\u662F\u4E2D\u65AD\u524D\u5DF2\u4EA7\u51FA\u7684\u90E8\u5206\uFF0C\u4E0D\u662F\u5B8C\u6574\u4EA4\u4ED8\u3011";
|
|
168643
168782
|
UNFINISHED_EMPTY = "\u3010\u4E0A\u4E00\u8F6E\u672A\u5B8C\u6210\u2014\u2014\u88AB\u4E2D\u65AD\u65F6\u8FD8\u6CA1\u6709\u4EA7\u51FA\u4EFB\u4F55\u6587\u5B57\u3011";
|
|
168783
|
+
RUNTIME_SESSION_ID_PLACEHOLDER_KEY = "runtimeSessionIdIsPlaceholder";
|
|
168644
168784
|
}
|
|
168645
168785
|
});
|
|
168646
168786
|
|
|
@@ -170890,7 +171030,7 @@ var init_service2 = __esm({
|
|
|
170890
171030
|
try {
|
|
170891
171031
|
await this.live.get(chatSessionId)?.kill?.();
|
|
170892
171032
|
} catch (error2) {
|
|
170893
|
-
const warning = `\u53D6\u6D88\u4FE1\u53F7\u5DF2\u8BB0\u5F55\uFF0C\u4F46
|
|
171033
|
+
const warning = `\u53D6\u6D88\u4FE1\u53F7\u5DF2\u8BB0\u5F55\uFF0C\u4F46\u8FD0\u884C\u65F6\u7EC8\u6B62\u8C03\u7528\u5931\u8D25\uFF1A${error2 instanceof Error ? error2.message : String(error2)}`.slice(0, 1e3);
|
|
170894
171034
|
await this.runStore.patchIfStatus(chatSessionId, ["interrupted"], {
|
|
170895
171035
|
warnings: [...transitioned.warnings ?? [], warning],
|
|
170896
171036
|
updatedAt: this.now()
|
|
@@ -171709,7 +171849,7 @@ var init_service2 = __esm({
|
|
|
171709
171849
|
const executionHealth = runtimeError ? "degraded" : "healthy";
|
|
171710
171850
|
const contentUsability = complete ? "usable" : "partial";
|
|
171711
171851
|
const warnings = [
|
|
171712
|
-
...runtimeMessage ? [
|
|
171852
|
+
...runtimeMessage ? [`\u8FD0\u884C\u65F6\u5F02\u5E38\u9000\u51FA\uFF0C\u4F46\u5DF2\u4FDD\u7559\u6709\u5F15\u7528\u7684\u56DE\u7B54\uFF1A${runtimeMessage}`] : [],
|
|
171713
171853
|
...!complete ? ["\u77E5\u8BC6\u7BA1\u7406\u5458\u56DE\u7B54\u7F3A\u5C11\u5B8C\u6574\u5B8C\u6210\u6807\u8BB0\uFF0C\u6309\u90E8\u5206\u5185\u5BB9\u8FD4\u56DE\u3002"] : []
|
|
171714
171854
|
];
|
|
171715
171855
|
if (answerAttempt) {
|
|
@@ -199493,7 +199633,7 @@ var init_curator_skill = __esm({
|
|
|
199493
199633
|
throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5FC5\u987B\u662F\u5DF2\u6FC0\u6D3B\u7684 AI \u5458\u5DE5\uFF1A${config2.curatorActorId}`);
|
|
199494
199634
|
}
|
|
199495
199635
|
const binding = (await this.actors.listBindings()).find((item) => item.actorId === config2.curatorActorId && item.status === "active");
|
|
199496
|
-
if (!binding) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5C1A\u672A\u7ED1\u5B9A\u53EF\u7528
|
|
199636
|
+
if (!binding) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5C1A\u672A\u7ED1\u5B9A\u53EF\u7528\u8FD0\u884C\u65F6\uFF1A${config2.curatorActorId}`);
|
|
199497
199637
|
const currentConfig = await this.actors.latestConfig(config2.curatorActorId);
|
|
199498
199638
|
const prompt2 = currentConfig?.prompt ?? "";
|
|
199499
199639
|
const model = currentConfig?.model ?? "";
|
|
@@ -202956,9 +203096,10 @@ function registerDelegatedChildTurn(liveChat, childSessionId, session, deps = {}
|
|
|
202956
203096
|
chatExit?.summary ?? (err instanceof Error ? err.message : String(err)),
|
|
202957
203097
|
{
|
|
202958
203098
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
202959
|
-
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
203099
|
+
...chatExit?.detail ? { detail: chatExit.detail } : {},
|
|
203100
|
+
...chatExit?.cancelledBy ? { cancelledBy: chatExit.cancelledBy } : {}
|
|
202960
203101
|
},
|
|
202961
|
-
ctrl.
|
|
203102
|
+
ctrl.cancelCause()
|
|
202962
203103
|
);
|
|
202963
203104
|
try {
|
|
202964
203105
|
ctrl.emit({ type: "error", text: text6, ...extra });
|
|
@@ -212311,9 +212452,10 @@ ${composed}`;
|
|
|
212311
212452
|
chatExit?.summary ?? (err instanceof Error ? err.message : String(err)),
|
|
212312
212453
|
{
|
|
212313
212454
|
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
212314
|
-
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
212455
|
+
...chatExit?.detail ? { detail: chatExit.detail } : {},
|
|
212456
|
+
...chatExit?.cancelledBy ? { cancelledBy: chatExit.cancelledBy } : {}
|
|
212315
212457
|
},
|
|
212316
|
-
live?.
|
|
212458
|
+
live?.cancelCause()
|
|
212317
212459
|
);
|
|
212318
212460
|
emit({ type: "error", text: errText2, ...errExtra });
|
|
212319
212461
|
const errorOutcome = itemLedger.recordError(errText2, errExtra);
|
|
@@ -212385,7 +212527,7 @@ ${composed}`;
|
|
|
212385
212527
|
return;
|
|
212386
212528
|
}
|
|
212387
212529
|
if (!opts.claudeBin) {
|
|
212388
|
-
throw new ApiError(501, "CHAT_RUNTIME_NOT_CONFIGURED", "\u5373\u65F6\u5BF9\u8BDD\u672A\u63A5\u5165 actor
|
|
212530
|
+
throw new ApiError(501, "CHAT_RUNTIME_NOT_CONFIGURED", "\u5373\u65F6\u5BF9\u8BDD\u672A\u63A5\u5165 actor \u8FD0\u884C\u65F6\u6D3E\u53D1\u94FE\u8DEF");
|
|
212389
212531
|
}
|
|
212390
212532
|
const env = body2.actorId && opts.resolveActorEnv ? await opts.resolveActorEnv(body2.actorId) : {};
|
|
212391
212533
|
let spawnCwd;
|
|
@@ -224483,13 +224625,21 @@ function wireRecoveredChatTurn(deps) {
|
|
|
224483
224625
|
log3(`[chat-recovery] \u4F1A\u8BDD ${plan.chatSessionId} \u91CD\u6302\uFF1A\u8865\u53D1\u6709 ${droppedFrames} \u5E27\u6CA1\u63A5\u4F4F\uFF08run ${plan.runId}\uFF09\uFF0C\u672C\u8F6E\u6309\u300C\u8F93\u51FA\u4E0D\u5B8C\u6574\u300D\u6536\u5C3E${exitIsFailure ? "\uFF08\u9000\u51FA\u5E27\u672C\u8EAB\u4E5F\u662F\u5931\u8D25\uFF09" : "\uFF0C\u4E0D\u8BB0\u6210\u6210\u529F"}`);
|
|
224484
224626
|
}
|
|
224485
224627
|
if (failed) {
|
|
224628
|
+
const exitAttribution = exitIsFailure ? attributeChatExit(exitErrorText(info), {
|
|
224629
|
+
...info.reason ? { reason: info.reason } : {},
|
|
224630
|
+
...cancelCauseFromExitMessage(info.errorMessage) ? { cancelledBy: cancelCauseFromExitMessage(info.errorMessage) } : {}
|
|
224631
|
+
}, live.cancelCause()) : void 0;
|
|
224486
224632
|
const errText2 = [
|
|
224487
|
-
|
|
224633
|
+
exitAttribution?.text,
|
|
224488
224634
|
droppedFrames > 0 ? outputGapNotice(droppedFrames) : void 0
|
|
224489
224635
|
].filter((t) => Boolean(t)).join("\n\n");
|
|
224490
|
-
live.emit({ type: "error", text: errText2 });
|
|
224636
|
+
live.emit({ type: "error", text: errText2, ...exitAttribution?.extra.cancelledBy ? { cancelledBy: exitAttribution.extra.cancelledBy } : {} });
|
|
224491
224637
|
const errReason = exitIsFailure ? info.reason : OUTPUT_GAP_ERROR_CODE;
|
|
224492
|
-
const
|
|
224638
|
+
const errExtra = {
|
|
224639
|
+
...errReason ? { reason: errReason } : {},
|
|
224640
|
+
...exitAttribution?.extra.cancelledBy ? { cancelledBy: exitAttribution.extra.cancelledBy } : {}
|
|
224641
|
+
};
|
|
224642
|
+
const errorOutcome = itemLedger.recordError(errText2, errExtra);
|
|
224493
224643
|
live.publishServerItem({
|
|
224494
224644
|
itemId: errorOutcome.itemId,
|
|
224495
224645
|
itemType: "control",
|
|
@@ -224497,7 +224647,7 @@ function wireRecoveredChatTurn(deps) {
|
|
|
224497
224647
|
version: errorOutcome.version,
|
|
224498
224648
|
ord: errorOutcome.ord,
|
|
224499
224649
|
status: "failed",
|
|
224500
|
-
payload: { code: "error", note: errText2, ...
|
|
224650
|
+
payload: { code: "error", note: errText2, ...errExtra }
|
|
224501
224651
|
});
|
|
224502
224652
|
}
|
|
224503
224653
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -224545,6 +224695,10 @@ function wireRecoveredChatTurn(deps) {
|
|
|
224545
224695
|
live.emit({ type: "done", ...plan.runtimeSessionId ? { sessionId: plan.runtimeSessionId } : {} });
|
|
224546
224696
|
live.finish(failed ? "error" : "done");
|
|
224547
224697
|
deps.unregister();
|
|
224698
|
+
const nativeSessionMetadata = info.runtimeSessionId && info.runtimeSessionId !== plan.runtimeSessionId ? nativeRuntimeSessionMetadata(
|
|
224699
|
+
(await trace.getRun(plan.runId).catch(() => null))?.metadata,
|
|
224700
|
+
info.runtimeSessionId
|
|
224701
|
+
) : void 0;
|
|
224548
224702
|
enqueueTrace(async () => {
|
|
224549
224703
|
await appender.append([{
|
|
224550
224704
|
eventType: "run.finished",
|
|
@@ -224556,7 +224710,9 @@ function wireRecoveredChatTurn(deps) {
|
|
|
224556
224710
|
...runTerminalPatch(info, plan.startedAt, finishedAt),
|
|
224557
224711
|
// 运行时自己退干净了,但平台没把输出接全——运行记录不许写「成功」(Bug 0123)。
|
|
224558
224712
|
// 退出帧本身已经是失败时保持它的归因,只补一句缺口说明。
|
|
224559
|
-
...outputGapRunPatch(droppedFrames, exitIsFailure, info)
|
|
224713
|
+
...outputGapRunPatch(droppedFrames, exitIsFailure, info),
|
|
224714
|
+
// 首轮占位会话号 → runtime 报回的原生号(Bug 0171)。
|
|
224715
|
+
...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
|
|
224560
224716
|
});
|
|
224561
224717
|
if (info.usage) await settleRunUsage(trace, plan.runId);
|
|
224562
224718
|
});
|
|
@@ -224573,12 +224729,14 @@ async function sweepDanglingChatTurns(deps) {
|
|
|
224573
224729
|
let status;
|
|
224574
224730
|
let completedAt;
|
|
224575
224731
|
let reason;
|
|
224732
|
+
let exitReason = null;
|
|
224576
224733
|
if (row.runId) {
|
|
224577
224734
|
const run = await deps.getRun(row.runId).catch(() => null);
|
|
224578
224735
|
if (run && run.status === "running") continue;
|
|
224579
224736
|
status = run?.status === "succeeded" ? "done" : "error";
|
|
224580
224737
|
completedAt = run?.endedAt ?? new Date(deps.now).toISOString();
|
|
224581
224738
|
reason = run ? `run \u7EC8\u6001 ${run.status}` : "run \u7F3A\u5931";
|
|
224739
|
+
exitReason = run?.exitReason ?? null;
|
|
224582
224740
|
} else {
|
|
224583
224741
|
if (deps.now - Date.parse(row.createdAt) < (deps.noRunGraceMs ?? 10 * 6e4)) continue;
|
|
224584
224742
|
status = "error";
|
|
@@ -224601,6 +224759,22 @@ async function sweepDanglingChatTurns(deps) {
|
|
|
224601
224759
|
completedAt,
|
|
224602
224760
|
...finalParts.length ? { parts: finalParts } : {}
|
|
224603
224761
|
});
|
|
224762
|
+
if (status === "error") {
|
|
224763
|
+
const items = deps.itemsFor?.(row.sessionId);
|
|
224764
|
+
if (items) {
|
|
224765
|
+
await recordChatTurnInterruption({
|
|
224766
|
+
items,
|
|
224767
|
+
sessionId: row.sessionId,
|
|
224768
|
+
turnId: await resolveChatItemsTurnId(items, row.id, fallbackTurnId(row.runId)),
|
|
224769
|
+
runId: row.runId,
|
|
224770
|
+
messageId: row.id,
|
|
224771
|
+
cause: chatInterruptCauseOf(exitReason),
|
|
224772
|
+
reason: exitReason ?? reason,
|
|
224773
|
+
now: () => completedAt,
|
|
224774
|
+
...deps.log ? { log: deps.log } : {}
|
|
224775
|
+
});
|
|
224776
|
+
}
|
|
224777
|
+
}
|
|
224604
224778
|
closed.push(row.id);
|
|
224605
224779
|
deps.log?.(`[chat-sweep] \u884C ${row.id}\uFF08\u4F1A\u8BDD ${row.sessionId}\uFF09\u6536\u655B\u4E3A ${status}\uFF08${reason}\uFF09`);
|
|
224606
224780
|
}
|
|
@@ -224617,6 +224791,8 @@ async function reconcileChatExitFrame(deps) {
|
|
|
224617
224791
|
const meta = asObj3(run.metadata);
|
|
224618
224792
|
if (meta?.dispatchId !== deps.dispatchId) continue;
|
|
224619
224793
|
if (deps.ownedRunId?.(row.sessionId) === row.runId) return false;
|
|
224794
|
+
const metaRsid = typeof meta?.runtimeSessionId === "string" ? meta.runtimeSessionId : void 0;
|
|
224795
|
+
const nativeSessionMetadata = deps.info.runtimeSessionId && deps.info.runtimeSessionId !== metaRsid ? nativeRuntimeSessionMetadata(run.metadata, deps.info.runtimeSessionId) : void 0;
|
|
224620
224796
|
const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
|
|
224621
224797
|
const outputsPart = deps.info.fileRestoreError ? void 0 : deps.outputsPartFor?.(row.sessionId);
|
|
224622
224798
|
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
@@ -224638,6 +224814,9 @@ async function reconcileChatExitFrame(deps) {
|
|
|
224638
224814
|
trace: deps.trace,
|
|
224639
224815
|
...deps.items ? { items: deps.items } : {},
|
|
224640
224816
|
...outputsPart ? { outputsPart } : {},
|
|
224817
|
+
// 轮已被清扫拍/用户 stop 收掉、退出帧才补发上来:run 若还挂 running,这就是把原生号
|
|
224818
|
+
// 写进运行记录的最后一次机会(这条路自己没有退出帧,所以由这里算好递进去)。
|
|
224819
|
+
...nativeSessionMetadata ? { runMetadata: nativeSessionMetadata } : {},
|
|
224641
224820
|
...deps.log ? { log: deps.log } : {}
|
|
224642
224821
|
});
|
|
224643
224822
|
return true;
|
|
@@ -224717,7 +224896,6 @@ async function reconcileChatExitFrame(deps) {
|
|
|
224717
224896
|
completedAt: finishedAt,
|
|
224718
224897
|
...finalParts.length ? { parts: finalParts } : {}
|
|
224719
224898
|
});
|
|
224720
|
-
const metaRsid = typeof meta?.runtimeSessionId === "string" ? meta.runtimeSessionId : void 0;
|
|
224721
224899
|
const nextRuntimeSessionId = info.runtimeSessionId ?? metaRsid;
|
|
224722
224900
|
await deps.chatStore.updateSession(row.sessionId, {
|
|
224723
224901
|
...nextRuntimeSessionId ? { runtimeSessionId: nextRuntimeSessionId } : {},
|
|
@@ -224738,7 +224916,9 @@ async function reconcileChatExitFrame(deps) {
|
|
|
224738
224916
|
await deps.trace.appendEvents(row.runId, seqless.map((e, i) => ({ ...e, seq: baseSeq + i + 1 })));
|
|
224739
224917
|
await deps.trace.updateRun(row.runId, {
|
|
224740
224918
|
...runTerminalPatch(info, run.startedAt, finishedAt),
|
|
224741
|
-
...outputGapRunPatch(droppedFrames, exitIsFailure, info)
|
|
224919
|
+
...outputGapRunPatch(droppedFrames, exitIsFailure, info),
|
|
224920
|
+
// 首轮占位会话号 → runtime 报回的原生号(Bug 0171,判据见本函数上方那一段)。
|
|
224921
|
+
...nativeSessionMetadata ? { metadata: nativeSessionMetadata } : {}
|
|
224742
224922
|
});
|
|
224743
224923
|
if (info.usage) await settleRunUsage(deps.trace, row.runId);
|
|
224744
224924
|
} catch (err) {
|
|
@@ -224808,7 +224988,9 @@ async function sweepDanglingChatTurnsAcrossCompanies(deps) {
|
|
|
224808
224988
|
listMessages: (sid) => stores.chatStore.listMessages(sid),
|
|
224809
224989
|
updateMessage: (id, patch) => stores.chatStore.updateMessage(id, patch),
|
|
224810
224990
|
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
|
|
224811
|
-
...sessionOutputsScan ? { outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId) } : {}
|
|
224991
|
+
...sessionOutputsScan ? { outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId) } : {},
|
|
224992
|
+
// Bug #0185:结局项要写进**这一家**的 `chat_items`(公司分 schema,写错家等于没写)。
|
|
224993
|
+
...stores.items ? { itemsFor: () => stores.items } : {}
|
|
224812
224994
|
});
|
|
224813
224995
|
closed.push(...ids2);
|
|
224814
224996
|
} catch (err) {
|
|
@@ -224863,7 +225045,8 @@ async function reconcileSettledChatTurn(deps) {
|
|
|
224863
225045
|
if (run?.status === "running") {
|
|
224864
225046
|
await deps.trace.updateRun(plan.runId, {
|
|
224865
225047
|
status: turn.status,
|
|
224866
|
-
endedAt: completedAt
|
|
225048
|
+
endedAt: completedAt,
|
|
225049
|
+
...deps.runMetadata ? { metadata: deps.runMetadata } : {}
|
|
224867
225050
|
});
|
|
224868
225051
|
}
|
|
224869
225052
|
}
|
|
@@ -224958,14 +225141,17 @@ var init_chat_recovery = __esm({
|
|
|
224958
225141
|
"../server/src/chat-recovery.ts"() {
|
|
224959
225142
|
"use strict";
|
|
224960
225143
|
init_file_restore();
|
|
225144
|
+
init_continuation3();
|
|
224961
225145
|
init_chat_parts();
|
|
224962
225146
|
init_sink();
|
|
224963
225147
|
init_resilient_appender();
|
|
224964
225148
|
init_usage_ledger();
|
|
224965
225149
|
init_chat_item_ledger();
|
|
225150
|
+
init_chat_interrupt_notice();
|
|
224966
225151
|
init_chat_assistant_turn();
|
|
224967
225152
|
init_src2();
|
|
224968
225153
|
init_session_exit_outcome();
|
|
225154
|
+
init_chat_cancel_attribution();
|
|
224969
225155
|
asObj3 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
|
|
224970
225156
|
partsOf = (parts) => Array.isArray(parts) ? parts.filter((p2) => p2 && typeof p2 === "object" && typeof p2.type === "string") : [];
|
|
224971
225157
|
maxPartSeq = (parts) => parts.reduce((n, p2) => Math.max(n, typeof p2.seq === "number" ? p2.seq : 0), 0);
|
|
@@ -225259,6 +225445,7 @@ var init_daemon_hub = __esm({
|
|
|
225259
225445
|
getNodeStatus;
|
|
225260
225446
|
/** 握手被拒审计钩子。 */
|
|
225261
225447
|
onHandshakeRejected;
|
|
225448
|
+
onSessionEvicted;
|
|
225262
225449
|
/** 握手失败限流器(改动 3)。 */
|
|
225263
225450
|
throttle;
|
|
225264
225451
|
/** 外部可订阅的事件回调 */
|
|
@@ -225291,6 +225478,7 @@ var init_daemon_hub = __esm({
|
|
|
225291
225478
|
this.resolveDataplane = opts.resolveDataplane;
|
|
225292
225479
|
this.coldStartGraceMs = opts.coldStartGraceMs ?? LINK_WINDOWS.hubColdStartGraceMs;
|
|
225293
225480
|
this.onHandshakeRejected = opts.onHandshakeRejected;
|
|
225481
|
+
this.onSessionEvicted = opts.onSessionEvicted;
|
|
225294
225482
|
this.throttle = new HandshakeThrottle(opts.throttle);
|
|
225295
225483
|
this.wss = new import_websocket_server.default({
|
|
225296
225484
|
server,
|
|
@@ -225666,6 +225854,31 @@ var init_daemon_hub = __esm({
|
|
|
225666
225854
|
}
|
|
225667
225855
|
return ids2;
|
|
225668
225856
|
}
|
|
225857
|
+
/**
|
|
225858
|
+
* 当前持有活数据面连接的会话**自报的 `jobArtifactId`**(Bug #0185)。
|
|
225859
|
+
*
|
|
225860
|
+
* 为什么 {@link liveSessionDispatchIds} 不够:那条线按 `dispatchId` 认领,而 **chat 道的运行账本
|
|
225861
|
+
* 以 artifactId 为键**——`run.id = chat-run:*` ≠ dispatchId(见本文件顶上那段与 `orphan-sweep.ts`
|
|
225862
|
+
* 的注释)。D2′ 之后 chat 会话搬进了独立会话进程:它不在 daemon 的 `activeSessions` 里
|
|
225863
|
+
* (那份只列 daemon 自己那张表),也不在 serve 的在途表里(重启即空),于是 serve 重启后
|
|
225864
|
+
* **四条认领线一条都不命中**,一条跑得好好的对话被 orphan-sweep 判孤儿闭合。
|
|
225865
|
+
*
|
|
225866
|
+
* 2026-09-21 生产(node-76fd271b454a,期间连续发版三次):三轮 chat run 全部
|
|
225867
|
+
* `status=orphaned / exitReason=sweep-orphan`,节点在线、会话进程还连着 hub。
|
|
225868
|
+
*
|
|
225869
|
+
* 会话在 `session_hello` 里已经把 `jobArtifactId` 报上来了(`session-process.ts` 那段注释写的
|
|
225870
|
+
* 就是这件事),这里只是把它取出来。与节点 hello 那条线同形、同语义,**只增不减**:
|
|
225871
|
+
* 命中只能阻止误闭合,永不导致误闭合。
|
|
225872
|
+
*/
|
|
225873
|
+
liveSessionArtifactIds() {
|
|
225874
|
+
const ids2 = /* @__PURE__ */ new Set();
|
|
225875
|
+
for (const [dispatchId, ws] of this.sessionSockets) {
|
|
225876
|
+
if (ws.readyState !== import_websocket.default.OPEN) continue;
|
|
225877
|
+
const artifactId = this.sessionMeta.get(dispatchId)?.jobArtifactId;
|
|
225878
|
+
if (artifactId) ids2.add(artifactId);
|
|
225879
|
+
}
|
|
225880
|
+
return ids2;
|
|
225881
|
+
}
|
|
225669
225882
|
/** 往会话进程直投一帧(kill / append_input / ping)。返回是否写进了 OPEN 的 socket。 */
|
|
225670
225883
|
sendToSession(dispatchId, msg) {
|
|
225671
225884
|
const ws = this.sessionSockets.get(dispatchId);
|
|
@@ -225680,7 +225893,13 @@ var init_daemon_hub = __esm({
|
|
|
225680
225893
|
evictSession(dispatchId, reason) {
|
|
225681
225894
|
const meta = this.sessionMeta.get(dispatchId);
|
|
225682
225895
|
const sent = this.sendToSession(dispatchId, { type: "evict", sessionId: meta?.sessionId ?? dispatchId, reason });
|
|
225683
|
-
if (sent)
|
|
225896
|
+
if (sent) {
|
|
225897
|
+
console.warn(`[hub] evict \u4F1A\u8BDD dispatch=${dispatchId} reason=${reason}`);
|
|
225898
|
+
try {
|
|
225899
|
+
this.onSessionEvicted?.(dispatchId, reason);
|
|
225900
|
+
} catch {
|
|
225901
|
+
}
|
|
225902
|
+
}
|
|
225684
225903
|
return sent;
|
|
225685
225904
|
}
|
|
225686
225905
|
/**
|
|
@@ -225829,16 +226048,60 @@ var init_daemon_hub = __esm({
|
|
|
225829
226048
|
});
|
|
225830
226049
|
|
|
225831
226050
|
// ../server/src/domains/actors/skill-fetcher.ts
|
|
226051
|
+
function indentWidth(line2) {
|
|
226052
|
+
return line2.length - line2.replace(/^[ \t]+/, "").length;
|
|
226053
|
+
}
|
|
226054
|
+
function scanFrontmatterEntries(fm) {
|
|
226055
|
+
const lines = fm.split("\n");
|
|
226056
|
+
const out = [];
|
|
226057
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
226058
|
+
const raw = lines[i];
|
|
226059
|
+
const text6 = raw.trim();
|
|
226060
|
+
if (!text6 || text6.startsWith("#")) continue;
|
|
226061
|
+
if (indentWidth(raw) > 0) continue;
|
|
226062
|
+
const colon = text6.indexOf(":");
|
|
226063
|
+
if (colon < 0) continue;
|
|
226064
|
+
const key2 = text6.slice(0, colon).trim();
|
|
226065
|
+
const rest = text6.slice(colon + 1).trim();
|
|
226066
|
+
if (BLOCK_SCALAR_HEAD.test(rest)) {
|
|
226067
|
+
const body2 = [];
|
|
226068
|
+
let j = i + 1;
|
|
226069
|
+
for (; j < lines.length; j += 1) {
|
|
226070
|
+
const next = lines[j];
|
|
226071
|
+
if (next.trim() === "") {
|
|
226072
|
+
body2.push("");
|
|
226073
|
+
continue;
|
|
226074
|
+
}
|
|
226075
|
+
if (indentWidth(next) === 0) break;
|
|
226076
|
+
body2.push(next);
|
|
226077
|
+
}
|
|
226078
|
+
i = j - 1;
|
|
226079
|
+
out.push({ key: key2, value: foldBlockScalar(body2), line: raw, block: true });
|
|
226080
|
+
continue;
|
|
226081
|
+
}
|
|
226082
|
+
out.push({ key: key2, value: unquoteScalar(rest), line: raw, block: false });
|
|
226083
|
+
}
|
|
226084
|
+
return out;
|
|
226085
|
+
}
|
|
226086
|
+
function unquoteScalar(value2) {
|
|
226087
|
+
const q2 = value2[0];
|
|
226088
|
+
if ((q2 === '"' || q2 === "'") && value2.length > 1 && value2.endsWith(q2)) return value2.slice(1, -1);
|
|
226089
|
+
return value2;
|
|
226090
|
+
}
|
|
226091
|
+
function foldBlockScalar(body2) {
|
|
226092
|
+
const widths = body2.filter((l) => l.trim()).map(indentWidth);
|
|
226093
|
+
const base = widths.length ? Math.min(...widths) : 0;
|
|
226094
|
+
return body2.map((l) => l.slice(base)).join(" ").replace(/[ \t\r\n]+/g, " ").trim();
|
|
226095
|
+
}
|
|
225832
226096
|
function parseSkillFrontmatter(content3) {
|
|
225833
226097
|
if (!content3.startsWith("---")) return { name: "", description: "" };
|
|
225834
226098
|
const end = content3.indexOf("---", 3);
|
|
225835
226099
|
if (end < 0) return { name: "", description: "" };
|
|
225836
226100
|
let name = "";
|
|
225837
226101
|
let description = "";
|
|
225838
|
-
for (const
|
|
225839
|
-
|
|
225840
|
-
if (
|
|
225841
|
-
else if (t.startsWith("description:")) description = t.slice(12).trim().replace(/^["']|["']$/g, "");
|
|
226102
|
+
for (const entry of scanFrontmatterEntries(content3.slice(3, end))) {
|
|
226103
|
+
if (entry.key === "name") name = entry.value;
|
|
226104
|
+
else if (entry.key === "description") description = entry.value;
|
|
225842
226105
|
}
|
|
225843
226106
|
return { name, description };
|
|
225844
226107
|
}
|
|
@@ -225851,15 +226114,13 @@ function validateSkillMd(content3) {
|
|
|
225851
226114
|
return "frontmatter \u7684\u67D0\u4E2A\u503C\u91CC\u542B `---`\uFF0C\u4F1A\u88AB\u89E3\u6790\u5668\u8BEF\u5F53\u6210\u7ED3\u675F\u6807\u8BB0\uFF08\u591A\u534A\u662F\u628A\u6574\u4EFD\u6587\u4EF6\u5185\u5BB9\u5F53\u6210\u4E86\u5B57\u6BB5\u503C\uFF09";
|
|
225852
226115
|
}
|
|
225853
226116
|
const fm = content3.slice(3, strictEnd);
|
|
225854
|
-
for (const
|
|
225855
|
-
|
|
225856
|
-
|
|
225857
|
-
const
|
|
225858
|
-
|
|
225859
|
-
|
|
225860
|
-
|
|
225861
|
-
if ((q2 === '"' || q2 === "'") && !(value2.length > 1 && value2.endsWith(q2))) {
|
|
225862
|
-
return `frontmatter \u7684 \`${line2.slice(0, colon).trim()}\` \u503C\u6709\u672A\u95ED\u5408\u7684\u5F15\u53F7\u2014\u2014\u591A\u534A\u662F\u628A\u6574\u4EFD\u6587\u4EF6\u5185\u5BB9\u5F53\u6210\u4E86\u5B57\u6BB5\u503C\uFF08description \u53EA\u5199\u4E00\u53E5\u8BDD\uFF0C\u6B63\u6587\u5199\u5728 frontmatter \u4E4B\u540E\uFF09`;
|
|
226117
|
+
for (const entry of scanFrontmatterEntries(fm)) {
|
|
226118
|
+
if (entry.block) continue;
|
|
226119
|
+
const text6 = entry.line.trim();
|
|
226120
|
+
const rawValue = text6.slice(text6.indexOf(":") + 1).trim();
|
|
226121
|
+
const q2 = rawValue[0];
|
|
226122
|
+
if ((q2 === '"' || q2 === "'") && !(rawValue.length > 1 && rawValue.endsWith(q2))) {
|
|
226123
|
+
return `frontmatter \u7684 \`${entry.key}\` \u503C\u6709\u672A\u95ED\u5408\u7684\u5F15\u53F7\u2014\u2014\u591A\u534A\u662F\u628A\u6574\u4EFD\u6587\u4EF6\u5185\u5BB9\u5F53\u6210\u4E86\u5B57\u6BB5\u503C\uFF08description \u53EA\u5199\u4E00\u53E5\u8BDD\uFF0C\u6B63\u6587\u5199\u5728 frontmatter \u4E4B\u540E\uFF09`;
|
|
225863
226124
|
}
|
|
225864
226125
|
}
|
|
225865
226126
|
const { name, description } = parseSkillFrontmatter(content3);
|
|
@@ -225876,7 +226137,7 @@ function toYamlDoubleQuoted(value2) {
|
|
|
225876
226137
|
}
|
|
225877
226138
|
async function fetchText(url, headers = {}) {
|
|
225878
226139
|
const res = await fetch(url, { headers });
|
|
225879
|
-
if (!res.ok) throw new
|
|
226140
|
+
if (!res.ok) throw new SkillFetchHttpError(res.status, url);
|
|
225880
226141
|
const buf = await res.arrayBuffer();
|
|
225881
226142
|
if (buf.byteLength > currentFileLimits().skillFileBytes) throw new Error(`File too large (>${currentFileLimits().skillFileBytes} bytes): ${url}`);
|
|
225882
226143
|
return new TextDecoder().decode(buf);
|
|
@@ -225931,6 +226192,21 @@ async function fetchFromClawHub(sourceUrl) {
|
|
|
225931
226192
|
files: bundle.files
|
|
225932
226193
|
};
|
|
225933
226194
|
}
|
|
226195
|
+
async function repoIsReadable(owner, repo) {
|
|
226196
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
|
|
226197
|
+
try {
|
|
226198
|
+
const res = await fetch(url, { headers: { ...githubHeaders(), Accept: "application/vnd.github+json" } });
|
|
226199
|
+
return res.ok;
|
|
226200
|
+
} catch {
|
|
226201
|
+
return true;
|
|
226202
|
+
}
|
|
226203
|
+
}
|
|
226204
|
+
async function skillMdMissingMessage(owner, repo, skillDir) {
|
|
226205
|
+
if (await repoIsReadable(owner, repo)) {
|
|
226206
|
+
return `SKILL.md not found in ${owner}/${repo} at ${skillDir || "root"}`;
|
|
226207
|
+
}
|
|
226208
|
+
return process.env["GITHUB_TOKEN"]?.trim() ? `\u65E0\u6743\u8BBF\u95EE\u4ED3\u5E93 ${owner}/${repo}\uFF1A\u670D\u52A1\u7AEF\u914D\u7F6E\u7684 GitHub \u51ED\u636E\u8BFB\u4E0D\u5230\u5B83\uFF08\u79C1\u6709\u4ED3\u5E93\u9700\u8981\u628A\u8BE5\u51ED\u636E\u52A0\u8FDB\u4ED3\u5E93\u534F\u4F5C\u8005\uFF0C\u6216\u4ED3\u5E93\u540D\u5199\u9519\u4E86\uFF09` : `\u65E0\u6743\u8BBF\u95EE\u4ED3\u5E93 ${owner}/${repo}\uFF1A\u5B83\u4E0D\u662F\u516C\u5F00\u4ED3\u5E93\uFF0C\u800C\u670D\u52A1\u7AEF\u6CA1\u6709\u914D\u7F6E GitHub \u8BBF\u95EE\u51ED\u636E\uFF08GITHUB_TOKEN\uFF09`;
|
|
226209
|
+
}
|
|
225934
226210
|
async function getDefaultBranch(owner, repo) {
|
|
225935
226211
|
const info = await fetchGitHubApi(`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`).catch(() => null);
|
|
225936
226212
|
return info?.default_branch ?? "main";
|
|
@@ -225951,7 +226227,7 @@ async function fetchSkillDir(owner, repo, dir, branch) {
|
|
|
225951
226227
|
const skillMdUrl = `${rawPrefix}/${dir ? dir + "/" : ""}SKILL.md`;
|
|
225952
226228
|
let skillMd;
|
|
225953
226229
|
try {
|
|
225954
|
-
skillMd = await fetchText(skillMdUrl);
|
|
226230
|
+
skillMd = await fetchText(skillMdUrl, githubHeaders());
|
|
225955
226231
|
} catch {
|
|
225956
226232
|
return null;
|
|
225957
226233
|
}
|
|
@@ -226033,7 +226309,7 @@ async function fetchFromGitHub(sourceUrl) {
|
|
|
226033
226309
|
skillDir = segs.slice(2).join("/");
|
|
226034
226310
|
}
|
|
226035
226311
|
const result = await fetchSkillDir(owner, repo, skillDir, branch);
|
|
226036
|
-
if (!result) throw new Error(
|
|
226312
|
+
if (!result) throw new Error(await skillMdMissingMessage(owner, repo, skillDir));
|
|
226037
226313
|
const fm = parseSkillFrontmatter(result.files["SKILL.md"] ?? "");
|
|
226038
226314
|
const slugBase = skillDir ? skillDir.split("/").pop().replace(/[^a-z0-9-]/g, "-") : repo.replace(/[^a-z0-9-]/g, "-");
|
|
226039
226315
|
return {
|
|
@@ -226055,12 +226331,20 @@ async function fetchSkillFromSource(sourceUrl, kind) {
|
|
|
226055
226331
|
throw new Error(`Unknown import kind: ${kind}`);
|
|
226056
226332
|
}
|
|
226057
226333
|
}
|
|
226058
|
-
var SKILL_DESCRIPTION_MAX, Bundle;
|
|
226334
|
+
var BLOCK_SCALAR_HEAD, SKILL_DESCRIPTION_MAX, SkillFetchHttpError, Bundle;
|
|
226059
226335
|
var init_skill_fetcher = __esm({
|
|
226060
226336
|
"../server/src/domains/actors/skill-fetcher.ts"() {
|
|
226061
226337
|
"use strict";
|
|
226062
226338
|
init_file_limits2();
|
|
226339
|
+
BLOCK_SCALAR_HEAD = /^[|>][0-9+-]{0,2}[ \t]*(?:#.*)?$/;
|
|
226063
226340
|
SKILL_DESCRIPTION_MAX = 600;
|
|
226341
|
+
SkillFetchHttpError = class extends Error {
|
|
226342
|
+
constructor(status, url) {
|
|
226343
|
+
super(`HTTP ${status} fetching ${url}`);
|
|
226344
|
+
this.status = status;
|
|
226345
|
+
this.name = "SkillFetchHttpError";
|
|
226346
|
+
}
|
|
226347
|
+
};
|
|
226064
226348
|
Bundle = class {
|
|
226065
226349
|
files = {};
|
|
226066
226350
|
add(path42, content3) {
|
|
@@ -226703,6 +226987,37 @@ var init_service5 = __esm({
|
|
|
226703
226987
|
await this.opts.store.removeBinding(actorId);
|
|
226704
226988
|
await this.audit({ kind: "binding_change", actorId, by, at: this.now(), detail: { removed: true } });
|
|
226705
226989
|
}
|
|
226990
|
+
/**
|
|
226991
|
+
* 运行时被删除时,把指向它的绑定一并撤掉(与 {@link dropRuntimeBindingOfDeletedAgent} 对称的另一头)。
|
|
226992
|
+
*
|
|
226993
|
+
* 为什么撤绑而不是把绑定留着标成失效(缺陷 0124,2026-09-21 R05 第 4 步实测):
|
|
226994
|
+
* 管理员删掉 `node-abe354908271` 上的 codex 后,绑在它上面的两个 Agent 的绑定行仍是
|
|
226995
|
+
* `codex / active`,指着一条已经不存在的运行时——运行时页要靠「这行还在不在」去过滤它,
|
|
226996
|
+
* 派单要靠 resolveBinding 去挡它,组织页要自己推「绑的那个还在不在」,**每个读者各挡一遍,
|
|
226997
|
+
* 漏一处就复发**。绑定表是「此刻该派到哪」的唯一事实源,不是历史账本:历史在 agent_runs /
|
|
226998
|
+
* traces / 审计里,撤绑一条都不动,所以 R05 要的「已有运行记录仍查得到」不受影响。
|
|
226999
|
+
* `BindingStatus` 只有 `active | paused` 两个值,拿 `paused`(人工暂停、可恢复)表示
|
|
227000
|
+
* 「目标没了」会让后来的读者误判,而加新值是 `packages/contract` 变更(红线 4)。
|
|
227001
|
+
*
|
|
227002
|
+
* 幂等:没有绑定就是空数组;`removeBinding` 对不存在的 actor 是 no-op。
|
|
227003
|
+
* @returns 被撤绑的 actorId 列表(调用方用来回给界面:删完影响了谁)。
|
|
227004
|
+
*/
|
|
227005
|
+
async releaseBindingsOnRuntime(target, by) {
|
|
227006
|
+
const affected = (await this.opts.store.listBindings()).filter(
|
|
227007
|
+
(b2) => b2.nodeId === target.nodeId && b2.runtimeKind === target.runtimeKind
|
|
227008
|
+
);
|
|
227009
|
+
for (const b2 of affected) {
|
|
227010
|
+
await this.opts.store.removeBinding(b2.actorId);
|
|
227011
|
+
await this.audit({
|
|
227012
|
+
kind: "binding_change",
|
|
227013
|
+
actorId: b2.actorId,
|
|
227014
|
+
by,
|
|
227015
|
+
at: this.now(),
|
|
227016
|
+
detail: { removed: true, reason: "runtime_deleted", nodeId: b2.nodeId, runtimeKind: b2.runtimeKind }
|
|
227017
|
+
});
|
|
227018
|
+
}
|
|
227019
|
+
return affected.map((b2) => b2.actorId);
|
|
227020
|
+
}
|
|
226706
227021
|
listBindings() {
|
|
226707
227022
|
return this.opts.store.listBindings();
|
|
226708
227023
|
}
|
|
@@ -227743,13 +228058,14 @@ ${input.description}
|
|
|
227743
228058
|
}
|
|
227744
228059
|
/**
|
|
227745
228060
|
* 按绑定选出这个执行上下文里每个变量名用哪一条(替代旧路径的三层 for,规则逐条对齐):
|
|
227746
|
-
* · 项目 >
|
|
228061
|
+
* · **这位 Agent > 项目 > 全员**(发起人 2026-09-21:项目是「小范围的组织默认值」,Agent 自己的配置最优先;
|
|
228062
|
+
* 2026-09-16 的 #1820 曾把项目排在最前,这次改回来,旧路径与这里一起改);
|
|
227747
228063
|
* · 连接器没启用的那一条不算,由下一层顶上(同旧路径 `put` 的早退);
|
|
227748
228064
|
* · 放不进进程环境的名字跳过;
|
|
227749
228065
|
* · 选出的那条到期就整个不提供,**不回退**到下一层(PRD §7,同旧路径)。
|
|
227750
228066
|
*/
|
|
227751
228067
|
async boundWinners(ctx, enabledConnectors) {
|
|
227752
|
-
const rank = { all: 1,
|
|
228068
|
+
const rank = { all: 1, project: 2, agent: 3 };
|
|
227753
228069
|
const best = /* @__PURE__ */ new Map();
|
|
227754
228070
|
for (const row of await this.opts.store.resolveBoundVariables(ctx)) {
|
|
227755
228071
|
if (row.connectorId && !enabledConnectors.has(row.connectorId)) continue;
|
|
@@ -227908,11 +228224,11 @@ ${input.description}
|
|
|
227908
228224
|
/**
|
|
227909
228225
|
* ADR 凭据保险库 §4.3 / ADR 0125 / **决策 0565 D4(2026-09-16 修订)**:派单/reveal 用的**合并后**
|
|
227910
228226
|
* 变量条目(明文 + deliveryMode)。
|
|
227911
|
-
* 优先级低→高:**global <
|
|
227912
|
-
* 即发起人 2026-09-
|
|
227913
|
-
*
|
|
227914
|
-
*
|
|
227915
|
-
*
|
|
228227
|
+
* 优先级低→高:**global < project < personal**(同 key 时高层覆盖低层,**Agent 自己的那份最优先**)。
|
|
228228
|
+
* 即发起人 2026-09-21 定的口径:**Agent 个人变量 > 当前项目变量 > 对该 Agent 生效的组织变量**
|
|
228229
|
+
* (项目视为「一个小范围的组织」的默认值;2026-09-16 的 #1820 曾改成项目最优先,2026-09-21 改回来)。
|
|
228230
|
+
* - 改的是这三行 for 的先后,不是 `put` 的条件——**显式空值照旧算「有值」**:高层写了空串就覆盖成空串,
|
|
228231
|
+
* 不回落低层(`put` 无条件 `merged.set`,别顺手加 `if (value)`,那会把「刻意置空」变成「读到下层值」)。
|
|
227916
228232
|
* - `opts.projectId` 给出时才叠加 project 层;缺省 = 只有 personal+global(存量行为)——
|
|
227917
228233
|
* 没有项目上下文时这次改动不改变任何取值。
|
|
227918
228234
|
* 连接器门禁与 resolveActorEnv 同源(未启用的连接器变量不注入)。
|
|
@@ -227960,12 +228276,12 @@ ${input.description}
|
|
|
227960
228276
|
for (const row of rows) {
|
|
227961
228277
|
if (row.scope === "global" && !row.actorId) put3(row);
|
|
227962
228278
|
}
|
|
227963
|
-
for (const row of rows) {
|
|
227964
|
-
if (row.scope === "personal" && (!row.actorId || row.actorId === actorId)) put3(row);
|
|
227965
|
-
}
|
|
227966
228279
|
for (const row of projectRows) {
|
|
227967
228280
|
if (row.scope === "project") put3(row);
|
|
227968
228281
|
}
|
|
228282
|
+
for (const row of rows) {
|
|
228283
|
+
if (row.scope === "personal" && (!row.actorId || row.actorId === actorId)) put3(row);
|
|
228284
|
+
}
|
|
227969
228285
|
return [...merged.values()].filter((e) => !expiredWinner.get(e.key));
|
|
227970
228286
|
}
|
|
227971
228287
|
/**
|
|
@@ -233378,7 +233694,7 @@ var init_activity2 = __esm({
|
|
|
233378
233694
|
也是引擎做的(`activate.ts` 的 `tryToRun`)——管理者根本不知道这两件事,此前却写成
|
|
233379
233695
|
「{管理者}已主动介入」「{管理者}正在重试」,把系统干的事指名道姓按到人头上。
|
|
233380
233696
|
「后来谁介入了」由 `plan.node_retry` 那张卡说,那里的主语是**真按下重试的人**,说的是事实。 */
|
|
233381
|
-
timedOut: (executor) => `${executor}
|
|
233697
|
+
timedOut: (executor) => `${executor} \u8FD0\u884C\u65F6\u8D85\u65F6\u3002`,
|
|
233382
233698
|
failed: (executor) => `${executor} \u6267\u884C\u5931\u8D25\u3002`,
|
|
233383
233699
|
/**
|
|
233384
233700
|
* **零产出轮**:会话正常退出,一件东西都没交(引擎判卷不是 success)。与 {@link failed} 分开说——
|
|
@@ -233386,7 +233702,7 @@ var init_activity2 = __esm({
|
|
|
233386
233702
|
*/
|
|
233387
233703
|
endedWithoutOutput: (executor, node2) => `${executor} \u672C\u8F6E\u7ED3\u675F\uFF0C\u6CA1\u6709\u4EA4\u51FA\u300A${node2}\u300B\u7684\u4EFB\u4F55\u6210\u679C\u3002`,
|
|
233388
233704
|
/** 人点了重试——只在 `plan.node_retry` 用,`by` 是真按下重试的那个人。 */
|
|
233389
|
-
retriedAfterTimeout: (executor, by) => `${executor}
|
|
233705
|
+
retriedAfterTimeout: (executor, by) => `${executor} \u8FD0\u884C\u65F6\u8D85\u65F6\uFF0C${by}\u5DF2\u4ECB\u5165\u91CD\u8BD5\u3002`,
|
|
233390
233706
|
retriedAfterFailure: (executor, by) => `${executor} \u6267\u884C\u5931\u8D25\uFF0C${by}\u5DF2\u4ECB\u5165\u91CD\u8BD5\u3002`,
|
|
233391
233707
|
resumed: (executor, node2) => `${executor} \u5DF2\u6062\u590D\uFF0C\u7EE7\u7EED\u5904\u7406\u300A${node2}\u300B\u3002`,
|
|
233392
233708
|
killed: (node2) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u5DF2\u7EC8\u6B62\u3002`,
|
|
@@ -235790,6 +236106,22 @@ function distribute(total, weights) {
|
|
|
235790
236106
|
return share;
|
|
235791
236107
|
});
|
|
235792
236108
|
}
|
|
236109
|
+
function splitSegmentCost(cost, model, d) {
|
|
236110
|
+
if (cost === null) return null;
|
|
236111
|
+
const zero = { input: 0, cacheRead: 0, cacheCreation: 0, output: 0, unsplit: 0 };
|
|
236112
|
+
if (cost === 0) return zero;
|
|
236113
|
+
const resolved = resolvedModel(model);
|
|
236114
|
+
const est = resolved ? estimateUsageCostBreakdown(resolved, {
|
|
236115
|
+
inputTokens: d.input,
|
|
236116
|
+
outputTokens: d.output,
|
|
236117
|
+
cacheReadTokens: d.cacheRead,
|
|
236118
|
+
cacheCreationTokens: d.cacheCreation
|
|
236119
|
+
}) : null;
|
|
236120
|
+
const weights = est ? [est.inputMicros, est.cacheReadMicros, est.cacheCreationMicros, est.outputMicros] : [0, 0, 0, 0];
|
|
236121
|
+
if (!weights.some((w2) => w2 > 0)) return { ...zero, unsplit: cost };
|
|
236122
|
+
const [input, cacheRead, cacheCreation, output] = distribute(cost, weights);
|
|
236123
|
+
return { input, cacheRead, cacheCreation, output, unsplit: 0 };
|
|
236124
|
+
}
|
|
235793
236125
|
function unpricedSegmentCost(model) {
|
|
235794
236126
|
return resolvedModel(model) === null ? 0 : null;
|
|
235795
236127
|
}
|
|
@@ -235845,7 +236177,8 @@ function deriveRunSegments(run) {
|
|
|
235845
236177
|
outputTokens: d.output,
|
|
235846
236178
|
cacheReadTokens: d.cacheRead,
|
|
235847
236179
|
cacheCreationTokens: d.cacheCreation,
|
|
235848
|
-
costUsdMicros: costs[i] ?? null
|
|
236180
|
+
costUsdMicros: costs[i] ?? null,
|
|
236181
|
+
costParts: splitSegmentCost(costs[i] ?? null, point3.model ?? run.effectiveModel, d)
|
|
235849
236182
|
}));
|
|
235850
236183
|
}
|
|
235851
236184
|
function deriveRounds(runs) {
|
|
@@ -236256,14 +236589,16 @@ function attributed(facts, relations) {
|
|
|
236256
236589
|
const draftOf = (key2) => {
|
|
236257
236590
|
let d = projectDrafts.get(key2);
|
|
236258
236591
|
if (!d) {
|
|
236259
|
-
d = {
|
|
236592
|
+
d = { convs: [], tasks: [], unlinked: new Cell(), group: new Cell() };
|
|
236260
236593
|
projectDrafts.set(key2, d);
|
|
236261
236594
|
}
|
|
236262
236595
|
return d;
|
|
236263
236596
|
};
|
|
236264
236597
|
for (const [rootId, bySession] of convSessionCells) {
|
|
236265
|
-
const
|
|
236266
|
-
for (const cell of bySession.values())
|
|
236598
|
+
const rootCell = new Cell();
|
|
236599
|
+
for (const cell of bySession.values()) rootCell.addCell(cell);
|
|
236600
|
+
if (!rootCell.active) continue;
|
|
236601
|
+
draftOf(projectKey(relations.sessions.get(rootId).projectId)).convs.push({ rootId, cell: rootCell });
|
|
236267
236602
|
}
|
|
236268
236603
|
for (const workorderId of taskAmount.keys()) {
|
|
236269
236604
|
draftOf(projectKey(relations.workorders.get(workorderId).projectId)).tasks.push(workorderId);
|
|
@@ -236272,15 +236607,26 @@ function attributed(facts, relations) {
|
|
|
236272
236607
|
const projects = [];
|
|
236273
236608
|
for (const [projectId2, d] of projectDrafts) {
|
|
236274
236609
|
const rows = [];
|
|
236275
|
-
|
|
236276
|
-
d.group.addCell(
|
|
236277
|
-
|
|
236278
|
-
|
|
236610
|
+
const convRows = d.convs.map(({ rootId, cell }) => {
|
|
236611
|
+
d.group.addCell(cell);
|
|
236612
|
+
const s2 = relations.sessions.get(rootId);
|
|
236613
|
+
return {
|
|
236614
|
+
kind: "conversation",
|
|
236615
|
+
sessionId: rootId,
|
|
236616
|
+
workorderId: null,
|
|
236617
|
+
title: s2.title || UNTITLED_CONVERSATION,
|
|
236618
|
+
createdAt: s2.createdAt,
|
|
236619
|
+
amount: cell.amount.toAmount(),
|
|
236620
|
+
flags: cell.flags.toFlags()
|
|
236621
|
+
};
|
|
236622
|
+
}).sort((a, b2) => byCreatedAt(a.createdAt, b2.createdAt) || (a.sessionId < b2.sessionId ? -1 : 1));
|
|
236623
|
+
rows.push(...convRows);
|
|
236279
236624
|
const taskRows = d.tasks.map((workorderId) => {
|
|
236280
236625
|
d.group.addCell(taskCells.get(workorderId));
|
|
236281
236626
|
const wo = relations.workorders.get(workorderId);
|
|
236282
236627
|
return {
|
|
236283
236628
|
kind: "task",
|
|
236629
|
+
sessionId: null,
|
|
236284
236630
|
workorderId,
|
|
236285
236631
|
title: wo.title,
|
|
236286
236632
|
createdAt: wo.createdAt,
|
|
@@ -236291,7 +236637,7 @@ function attributed(facts, relations) {
|
|
|
236291
236637
|
rows.push(...taskRows);
|
|
236292
236638
|
if (d.unlinked.active) {
|
|
236293
236639
|
d.group.addCell(d.unlinked);
|
|
236294
|
-
rows.push({ kind: "unlinked", workorderId: null, title: UNLINKED_ROW_TITLE, createdAt: null, amount: d.unlinked.amount.toAmount(), flags: d.unlinked.flags.toFlags() });
|
|
236640
|
+
rows.push({ kind: "unlinked", sessionId: null, workorderId: null, title: UNLINKED_ROW_TITLE, createdAt: null, amount: d.unlinked.amount.toAmount(), flags: d.unlinked.flags.toFlags() });
|
|
236295
236641
|
}
|
|
236296
236642
|
if (!d.group.active) continue;
|
|
236297
236643
|
projects.push({
|
|
@@ -236314,6 +236660,7 @@ var init_statistics = __esm({
|
|
|
236314
236660
|
"use strict";
|
|
236315
236661
|
init_src();
|
|
236316
236662
|
init_uncategorized_project();
|
|
236663
|
+
init_model_pricing();
|
|
236317
236664
|
init_accounting();
|
|
236318
236665
|
UNKNOWN_AGENT_NAME = "\u672A\u77E5 Agent";
|
|
236319
236666
|
UNREGISTERED_RUNTIME_NAME = "\u672A\u767B\u8BB0\u8FD0\u884C\u65F6";
|
|
@@ -236323,38 +236670,60 @@ var init_statistics = __esm({
|
|
|
236323
236670
|
BUCKET_KEYS = ["input", "output", "cacheRead", "cacheCreation"];
|
|
236324
236671
|
AmountAcc = class {
|
|
236325
236672
|
input = 0;
|
|
236326
|
-
|
|
236673
|
+
cacheRead = 0;
|
|
236674
|
+
cacheCreation = 0;
|
|
236327
236675
|
output = 0;
|
|
236328
236676
|
knownCost = 0;
|
|
236677
|
+
costParts = { input: 0, cacheRead: 0, cacheCreation: 0, output: 0, unsplit: 0 };
|
|
236329
236678
|
unknownTokens = 0;
|
|
236330
236679
|
knownSegments = 0;
|
|
236331
236680
|
unknownSegments = 0;
|
|
236332
236681
|
addSegment(s2) {
|
|
236333
|
-
const cache = s2.cacheReadTokens + s2.cacheCreationTokens;
|
|
236334
236682
|
this.input += s2.inputTokens;
|
|
236335
|
-
this.
|
|
236683
|
+
this.cacheRead += s2.cacheReadTokens;
|
|
236684
|
+
this.cacheCreation += s2.cacheCreationTokens;
|
|
236336
236685
|
this.output += s2.outputTokens;
|
|
236337
236686
|
if (s2.costUsdMicros === null) {
|
|
236338
|
-
this.unknownTokens += s2.inputTokens +
|
|
236687
|
+
this.unknownTokens += s2.inputTokens + s2.cacheReadTokens + s2.cacheCreationTokens + s2.outputTokens;
|
|
236339
236688
|
this.unknownSegments += 1;
|
|
236340
236689
|
} else {
|
|
236341
236690
|
this.knownCost += s2.costUsdMicros;
|
|
236691
|
+
if (s2.costParts) this.addParts(s2.costParts);
|
|
236692
|
+
else this.costParts.unsplit += s2.costUsdMicros;
|
|
236342
236693
|
this.knownSegments += 1;
|
|
236343
236694
|
}
|
|
236344
236695
|
}
|
|
236345
236696
|
addAcc(o) {
|
|
236346
236697
|
this.input += o.input;
|
|
236347
|
-
this.
|
|
236698
|
+
this.cacheRead += o.cacheRead;
|
|
236699
|
+
this.cacheCreation += o.cacheCreation;
|
|
236348
236700
|
this.output += o.output;
|
|
236349
236701
|
this.knownCost += o.knownCost;
|
|
236702
|
+
this.addParts(o.costParts);
|
|
236350
236703
|
this.unknownTokens += o.unknownTokens;
|
|
236351
236704
|
this.knownSegments += o.knownSegments;
|
|
236352
236705
|
this.unknownSegments += o.unknownSegments;
|
|
236353
236706
|
}
|
|
236707
|
+
addParts(p2) {
|
|
236708
|
+
this.costParts.input += p2.input;
|
|
236709
|
+
this.costParts.cacheRead += p2.cacheRead;
|
|
236710
|
+
this.costParts.cacheCreation += p2.cacheCreation;
|
|
236711
|
+
this.costParts.output += p2.output;
|
|
236712
|
+
this.costParts.unsplit += p2.unsplit;
|
|
236713
|
+
}
|
|
236354
236714
|
toAmount() {
|
|
236715
|
+
const cache = this.cacheRead + this.cacheCreation;
|
|
236355
236716
|
return {
|
|
236356
|
-
tokens: {
|
|
236717
|
+
tokens: {
|
|
236718
|
+
input: this.input,
|
|
236719
|
+
cache,
|
|
236720
|
+
cacheRead: this.cacheRead,
|
|
236721
|
+
cacheCreation: this.cacheCreation,
|
|
236722
|
+
output: this.output,
|
|
236723
|
+
total: this.input + cache + this.output
|
|
236724
|
+
},
|
|
236357
236725
|
costUsdMicros: this.knownCost,
|
|
236726
|
+
costParts: { ...this.costParts },
|
|
236358
236727
|
costKnown: this.unknownSegments === 0 ? "full" : this.knownSegments === 0 ? "none" : "partial",
|
|
236359
236728
|
costUnknownTokens: this.unknownTokens
|
|
236360
236729
|
};
|
|
@@ -241008,8 +241377,9 @@ function nodesDomain(deps) {
|
|
|
241008
241377
|
const runtime = (await deps.nodeStore.listRuntimes()).find((r) => r.id === runtimeId);
|
|
241009
241378
|
const { nodeDeleted } = await deps.nodeStore.deleteRuntime(runtimeId);
|
|
241010
241379
|
if (nodeDeleted && runtime) revokeNodeAccess(runtime.nodeId);
|
|
241380
|
+
const releasedActorIds = runtime ? await deps.releaseBindingsOnRuntime?.(req.auth.companyId, { nodeId: runtime.nodeId, runtimeKind: runtime.kind }, req.auth.actor) ?? [] : [];
|
|
241011
241381
|
deps.onStatusChange?.();
|
|
241012
|
-
return { status: 200, body: { ok: true } };
|
|
241382
|
+
return { status: 200, body: { ok: true, releasedActorIds } };
|
|
241013
241383
|
});
|
|
241014
241384
|
router.get("/api/runtimes/:runtimeId/models", async (req) => {
|
|
241015
241385
|
const runtimeId = req.params["runtimeId"];
|
|
@@ -245325,7 +245695,7 @@ function throwWorkdirError(code2) {
|
|
|
245325
245695
|
case "NODE_OFFLINE":
|
|
245326
245696
|
throw new ApiError(503, "NODE_OFFLINE", "\u5DE5\u4F5C\u533A\u6240\u5728\u8282\u70B9\u5F53\u524D\u79BB\u7EBF\uFF0C\u65E0\u6CD5\u67E5\u770B\u6587\u4EF6");
|
|
245327
245697
|
case "SESSION_NOT_BOUND":
|
|
245328
|
-
throw new ApiError(503, "SESSION_NOT_BOUND", "\u8BE5 agent \u5F53\u524D\u672A\u7ED1\u5B9A
|
|
245698
|
+
throw new ApiError(503, "SESSION_NOT_BOUND", "\u8BE5 agent \u5F53\u524D\u672A\u7ED1\u5B9A\u8FD0\u884C\u65F6\uFF0C\u65E0\u5DE5\u4F5C\u533A\u53EF\u8BFB");
|
|
245329
245699
|
case "TIMEOUT":
|
|
245330
245700
|
throw new ApiError(504, "TIMEOUT", "\u8BF7\u6C42\u8282\u70B9\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5");
|
|
245331
245701
|
case "NOT_FOUND":
|
|
@@ -245732,7 +246102,7 @@ function createChatSessionsDomain(opts) {
|
|
|
245732
246102
|
}
|
|
245733
246103
|
const startedAt = Date.parse(activeTurnRow.startedAt ?? activeTurnRow.reservedAt);
|
|
245734
246104
|
const stale = Number.isFinite(startedAt) && Date.now() - startedAt >= LINK_WINDOWS.chatTurnPhaseStaleAfterMs;
|
|
245735
|
-
return { turnId: activeTurnRow.id, phase: stale ? "
|
|
246105
|
+
return { turnId: activeTurnRow.id, phase: stale ? "recovering" : "running" };
|
|
245736
246106
|
})();
|
|
245737
246107
|
const snapshotEntries = [];
|
|
245738
246108
|
let nextSinceVersion = sinceVersion;
|
|
@@ -250901,7 +251271,7 @@ var init_daemon_adapter = __esm({
|
|
|
250901
251271
|
}
|
|
250902
251272
|
async spawn(job) {
|
|
250903
251273
|
if (job.binding?.nodeId) return this.opts.remote.spawn(job);
|
|
250904
|
-
const msg = `\u62D2\u7EDD\u6D3E\u53D1\uFF1A${job.actor} \u65E0
|
|
251274
|
+
const msg = `\u62D2\u7EDD\u6D3E\u53D1\uFF1A${job.actor} \u65E0\u53EF\u7528\u7684\u8FD0\u884C\u65F6\u7ED1\u5B9A\u2014\u2014\u4E0D\u56DE\u843D\u672C\u5730\uFF08\u5148\u5728\u5E73\u53F0\u4E3A\u8BE5\u5458\u5DE5\u914D\u7F6E\u8FD0\u884C\u65F6\uFF09`;
|
|
250905
251275
|
console.warn(`[dispatch] ${msg}\uFF08artifact=${job.artifactId}\uFF09`);
|
|
250906
251276
|
throw new Error(msg);
|
|
250907
251277
|
}
|
|
@@ -271144,6 +271514,7 @@ __export(src_exports2, {
|
|
|
271144
271514
|
BindingRouterAdapter: () => BindingRouterAdapter,
|
|
271145
271515
|
BoardWatcher: () => BoardWatcher,
|
|
271146
271516
|
CHAT_FOLDER_TASK_LABEL: () => CHAT_FOLDER_TASK_LABEL,
|
|
271517
|
+
CHAT_INTERRUPT_NOTICE: () => CHAT_INTERRUPT_NOTICE,
|
|
271147
271518
|
CHAT_ITEMS_DOUBLE_WRITE_ENV: () => CHAT_ITEMS_DOUBLE_WRITE_ENV,
|
|
271148
271519
|
CHAT_TURN_ORPHAN_GRACE_MS: () => CHAT_TURN_ORPHAN_GRACE_MS,
|
|
271149
271520
|
COMPANY_CROSS_TENANT_PREFIXES: () => COMPANY_CROSS_TENANT_PREFIXES,
|
|
@@ -271277,6 +271648,7 @@ __export(src_exports2, {
|
|
|
271277
271648
|
REJECTED_PROPOSALS_IN_TASK: () => REJECTED_PROPOSALS_IN_TASK,
|
|
271278
271649
|
RETIRED_CD_BOT_ACTOR: () => RETIRED_CD_BOT_ACTOR,
|
|
271279
271650
|
RETIRED_CD_ROLE: () => RETIRED_CD_ROLE,
|
|
271651
|
+
RUNTIME_SESSION_ID_PLACEHOLDER_KEY: () => RUNTIME_SESSION_ID_PLACEHOLDER_KEY,
|
|
271280
271652
|
RUNTIME_START_FRAME_TYPES: () => RUNTIME_START_FRAME_TYPES,
|
|
271281
271653
|
RUN_DEATH_PATHS: () => RUN_DEATH_PATHS,
|
|
271282
271654
|
RUN_SETTLEMENT_CONVERGENCE_BUDGET_MS: () => RUN_SETTLEMENT_CONVERGENCE_BUDGET_MS,
|
|
@@ -271290,6 +271662,7 @@ __export(src_exports2, {
|
|
|
271290
271662
|
SHARED_GRAPH_BODY: () => SHARED_GRAPH_BODY,
|
|
271291
271663
|
SKILL_DESCRIPTION_MAX: () => SKILL_DESCRIPTION_MAX,
|
|
271292
271664
|
STATUS_MARKER: () => STATUS_MARKER,
|
|
271665
|
+
SkillFetchHttpError: () => SkillFetchHttpError,
|
|
271293
271666
|
TenancyVisibilityInputError: () => TenancyVisibilityInputError,
|
|
271294
271667
|
TraceService: () => TraceService,
|
|
271295
271668
|
TraceStoreSink: () => TraceStoreSink,
|
|
@@ -271349,6 +271722,8 @@ __export(src_exports2, {
|
|
|
271349
271722
|
chatBroadcastEnabledFromEnv: () => chatBroadcastEnabledFromEnv,
|
|
271350
271723
|
chatDelegationEnabledFromEnv: () => chatDelegationEnabledFromEnv,
|
|
271351
271724
|
chatDispatchScope: () => chatDispatchScope,
|
|
271725
|
+
chatInterruptCauseOf: () => chatInterruptCauseOf,
|
|
271726
|
+
chatInterruptItemId: () => chatInterruptItemId,
|
|
271352
271727
|
chatItemsDoubleWriteEnabled: () => chatItemsDoubleWriteEnabled,
|
|
271353
271728
|
chatNodeChanged: () => chatNodeChanged,
|
|
271354
271729
|
chatRunLedgerFields: () => chatRunLedgerFields,
|
|
@@ -271539,6 +271914,7 @@ __export(src_exports2, {
|
|
|
271539
271914
|
memberNameResolver: () => memberNameResolver,
|
|
271540
271915
|
migrateDefaultCompany: () => migrateDefaultCompany,
|
|
271541
271916
|
mirrorDirFor: () => mirrorDirFor,
|
|
271917
|
+
nativeRuntimeSessionMetadata: () => nativeRuntimeSessionMetadata,
|
|
271542
271918
|
needsClosureReport: () => needsClosureReport,
|
|
271543
271919
|
nodeConclusionsFromSnapshot: () => nodeConclusionsFromSnapshot,
|
|
271544
271920
|
nodeCoordinatorFactsFromSnapshot: () => nodeCoordinatorFactsFromSnapshot,
|
|
@@ -271565,6 +271941,7 @@ __export(src_exports2, {
|
|
|
271565
271941
|
parseZhizengzengMarkdown: () => parseZhizengzengMarkdown,
|
|
271566
271942
|
participatingTaskCount: () => participatingTaskCount,
|
|
271567
271943
|
pickToolVersions: () => pickToolVersions,
|
|
271944
|
+
placeholderRuntimeSessionFlag: () => placeholderRuntimeSessionFlag,
|
|
271568
271945
|
planChatContinuation: () => planChatContinuation,
|
|
271569
271946
|
planChatTurnRecovery: () => planChatTurnRecovery,
|
|
271570
271947
|
planChatTurnRecoveryAcrossCompanies: () => planChatTurnRecoveryAcrossCompanies,
|
|
@@ -271601,6 +271978,7 @@ __export(src_exports2, {
|
|
|
271601
271978
|
reconcileTerminalRuns: () => reconcileTerminalRuns,
|
|
271602
271979
|
recordChatRuntimeIdentity: () => recordChatRuntimeIdentity,
|
|
271603
271980
|
recordChatSessionExit: () => recordChatSessionExit,
|
|
271981
|
+
recordChatTurnInterruption: () => recordChatTurnInterruption,
|
|
271604
271982
|
recordDeliveredOutputHandoff: () => recordDeliveredOutputHandoff,
|
|
271605
271983
|
recordFinalUsagePoint: () => recordFinalUsagePoint,
|
|
271606
271984
|
recordOtlpUsageCheckpoint: () => recordOtlpUsageCheckpoint,
|
|
@@ -271645,6 +272023,7 @@ __export(src_exports2, {
|
|
|
271645
272023
|
runtimeDriftBlock: () => runtimeDriftBlock,
|
|
271646
272024
|
safeAttachmentName: () => safeAttachmentName,
|
|
271647
272025
|
sanitizeSkillDir: () => sanitizeSkillDir,
|
|
272026
|
+
scanFrontmatterEntries: () => scanFrontmatterEntries,
|
|
271648
272027
|
scanSessionOutputs: () => scanSessionOutputs,
|
|
271649
272028
|
scanZeroOutputBreaker: () => scanZeroOutputBreaker,
|
|
271650
272029
|
scoreKnowledgeChunk: () => scoreKnowledgeChunk,
|
|
@@ -271741,6 +272120,7 @@ var init_src11 = __esm({
|
|
|
271741
272120
|
init_recovery_plan();
|
|
271742
272121
|
init_chat_attachment_files();
|
|
271743
272122
|
init_live_chat();
|
|
272123
|
+
init_chat_interrupt_notice();
|
|
271744
272124
|
init_chat_recovery();
|
|
271745
272125
|
init_chat_system_message();
|
|
271746
272126
|
init_stable_chat_dispatch_ids();
|
|
@@ -272263,12 +272643,14 @@ var DiscussionService = class {
|
|
|
272263
272643
|
decisions: []
|
|
272264
272644
|
}));
|
|
272265
272645
|
}
|
|
272266
|
-
/** Chat title is a retryable projection of committed content, never of a model draft.
|
|
272646
|
+
/** Chat title is a retryable projection of committed content, never of a model draft.
|
|
272647
|
+
* Projected once per conversation: later revisions (regeneration or page edits) keep the chat
|
|
272648
|
+
* title stable so users can find the conversation again, and never overwrite a manual rename. */
|
|
272267
272649
|
async syncDiscussionTitle(sessionId) {
|
|
272268
272650
|
await this.store.transaction(async (tx) => {
|
|
272269
272651
|
const discussion = await tx.get("discussion", sessionId);
|
|
272270
272652
|
const job = await tx.get("job", sessionId);
|
|
272271
|
-
if (!discussion || !job || job.value.titleSyncedRevision
|
|
272653
|
+
if (!discussion || !job || job.value.titleSyncedRevision !== void 0) return;
|
|
272272
272654
|
const session = await this.chat.getSession(sessionId);
|
|
272273
272655
|
if (!session || projectScope(session) !== discussion.value.record.project_id || session.humanActorId !== discussion.value.userId || session.aiActorId !== discussion.value.agentId) return;
|
|
272274
272656
|
if (session.title !== discussion.value.record.title) await this.chat.updateSession(sessionId, { title: discussion.value.record.title });
|
|
@@ -273401,7 +273783,9 @@ init_src4();
|
|
|
273401
273783
|
|
|
273402
273784
|
// ../cli/src/chat-exit.ts
|
|
273403
273785
|
var fs31 = __toESM(require("node:fs"), 1);
|
|
273786
|
+
init_src();
|
|
273404
273787
|
init_src7();
|
|
273788
|
+
init_chat_cancel_attribution();
|
|
273405
273789
|
function transcriptTail(ref2) {
|
|
273406
273790
|
try {
|
|
273407
273791
|
const stat4 = fs31.statSync(ref2);
|
|
@@ -273434,6 +273818,7 @@ var AUTH_RE2 = /\b401\b|\b403\b|unauthori[sz]ed|authenticat|oauth|invalid[_ -]?(
|
|
|
273434
273818
|
var MODEL_RE = /\bmodel\b[^\n]{0,120}?\b(?:not found|not exist|does not exist|not available|not supported|unavailable|unknown)\b|\b(?:unknown|invalid|unsupported) model\b|model_not_found/i;
|
|
273435
273819
|
var UNREACHABLE_RE2 = /不在线|socket 非 OPEN|帧未送达|失联/;
|
|
273436
273820
|
function failureKind(reason, line2) {
|
|
273821
|
+
if (line2.startsWith(SESSION_STARTUP_FAILED_PREFIX)) return "startup-failed";
|
|
273437
273822
|
switch (reason) {
|
|
273438
273823
|
case "auth-error":
|
|
273439
273824
|
return "auth";
|
|
@@ -273476,6 +273861,8 @@ function failureSummary(kind, ctx) {
|
|
|
273476
273861
|
return `\u5BF9\u8BDD\u5931\u8D25\uFF1A\u8FD9\u4E2A\u8FD0\u884C\u65F6\u7528\u4E0D\u4E86\u5F53\u524D\u914D\u7F6E\u7684\u6A21\u578B\u3002\u8BF7${chatRebindHint("\u6362\u4E00\u4E2A\u53EF\u7528\u7684\u6A21\u578B")}\u540E\u518D\u53D1\u4E00\u6B21\u3002`;
|
|
273477
273862
|
case "timeout":
|
|
273478
273863
|
return "\u5BF9\u8BDD\u5931\u8D25\uFF1A\u8FD9\u4E00\u8F6E\u8FD0\u884C\u8D85\u8FC7\u4E86\u65F6\u95F4\u4E0A\u9650\uFF0C\u5DF2\u88AB\u7EC8\u6B62\u3002\u53EF\u4EE5\u628A\u4EFB\u52A1\u62C6\u5C0F\u4E00\u4E9B\u518D\u53D1\u3002";
|
|
273864
|
+
case "startup-failed":
|
|
273865
|
+
return `\u5BF9\u8BDD\u5931\u8D25\uFF1A${ctx.readable || SESSION_STARTUP_FAILED_PREFIX}\u3002\u8FD9\u4E00\u8F6E\u6CA1\u6709\u5F00\u59CB\u8FD0\u884C\uFF0C\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u53CD\u590D\u51FA\u73B0\u7684\u8BDD\uFF0C\u591A\u534A\u662F\u90A3\u53F0\u8282\u70B9\u8D1F\u8F7D\u8FC7\u9AD8\uFF0C\u8BF7\u7BA1\u7406\u5458\u7ED9\u5B83\u51CF\u8D1F\u6216${chatRebindHint("\u6362\u4E00\u4E2A\u8FD0\u884C\u65F6")}\u3002`;
|
|
273479
273866
|
case "output-limit":
|
|
273480
273867
|
return "\u5BF9\u8BDD\u5931\u8D25\uFF1A\u6A21\u578B\u8F93\u51FA\u8D85\u51FA\u957F\u5EA6\u4E0A\u9650\uFF0C\u56DE\u590D\u88AB\u622A\u65AD\u4E86\u3002\u53EF\u4EE5\u8BA9\u5B83\u5206\u51E0\u6B21\u8F93\u51FA\uFF0C\u6216\u628A\u4EFB\u52A1\u62C6\u5C0F\u4E00\u4E9B\u518D\u53D1\u3002";
|
|
273481
273868
|
case "unknown":
|
|
@@ -273493,8 +273880,10 @@ function buildChatExitError(info) {
|
|
|
273493
273880
|
if (!detail && info.transcriptRef) detail = transcriptTail(info.transcriptRef);
|
|
273494
273881
|
}
|
|
273495
273882
|
let summary;
|
|
273883
|
+
let cancelledBy;
|
|
273496
273884
|
if (info.reason === "cancelled") {
|
|
273497
|
-
|
|
273885
|
+
cancelledBy = cancelCauseFromExitMessage(info.errorMessage);
|
|
273886
|
+
summary = chatCancelNote(cancelledBy);
|
|
273498
273887
|
} else {
|
|
273499
273888
|
const firstErrorLine = (info.errorMessage?.split("\n").map((line2) => line2.trim()).find(Boolean) ?? "").replace(INTERNAL_PREFIX_RE, "");
|
|
273500
273889
|
const readable = firstErrorLine ? clipLine(providerMessage(firstErrorLine) ?? firstErrorLine) : "";
|
|
@@ -273508,6 +273897,7 @@ function buildChatExitError(info) {
|
|
|
273508
273897
|
const chatExit = {
|
|
273509
273898
|
...info.reason ? { reason: info.reason } : {},
|
|
273510
273899
|
summary,
|
|
273900
|
+
...cancelledBy ? { cancelledBy } : {},
|
|
273511
273901
|
...detail ? { detail: `${genericLine}
|
|
273512
273902
|
|
|
273513
273903
|
${detail}`.slice(0, 4200) } : {}
|
|
@@ -275525,7 +275915,7 @@ async function startServe(opts) {
|
|
|
275525
275915
|
}
|
|
275526
275916
|
const binding = await registryStore.getBinding(agentId);
|
|
275527
275917
|
if (!binding || binding.status !== "active") {
|
|
275528
|
-
return { ok: false, reason: "agent_runtime_required", error: `\u6267\u884C\u4EBA\u672A\u7ED1\u5B9A
|
|
275918
|
+
return { ok: false, reason: "agent_runtime_required", error: `\u6267\u884C\u4EBA\u672A\u7ED1\u5B9A\u8FD0\u884C\u65F6\uFF1A${agentId}` };
|
|
275529
275919
|
}
|
|
275530
275920
|
return { ok: true };
|
|
275531
275921
|
}
|
|
@@ -276354,10 +276744,10 @@ async function startServe(opts) {
|
|
|
276354
276744
|
const preflightKnowledgeConnector = async (connectorSlug, actorId) => {
|
|
276355
276745
|
const label = connectorDisplayName(connectorSlug);
|
|
276356
276746
|
const binding = await actors.service.resolveBinding()(actorId);
|
|
276357
|
-
if (!binding) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5C1A\u672A\u7ED1\u5B9A\u53EF\u7528
|
|
276747
|
+
if (!binding) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5C1A\u672A\u7ED1\u5B9A\u53EF\u7528\u8FD0\u884C\u65F6\uFF1A${actorId}`);
|
|
276358
276748
|
const node2 = hub?.connectedDaemons().find((item) => item.daemonId === binding.nodeId);
|
|
276359
276749
|
if (!node2) throw new Error(`${label} Connector \u672A\u5C31\u7EEA\uFF1A\u77E5\u8BC6\u7BA1\u7406\u5458\u7ED1\u5B9A\u8282\u70B9 ${binding.nodeId} \u5F53\u524D\u4E0D\u5728\u7EBF`);
|
|
276360
|
-
if (!node2.meta.adapters.includes(binding.runtimeKind)) throw new Error(`${label} Connector \u672A\u5C31\u7EEA\uFF1A\u8282\u70B9\u672A\u4E0A\u62A5
|
|
276750
|
+
if (!node2.meta.adapters.includes(binding.runtimeKind)) throw new Error(`${label} Connector \u672A\u5C31\u7EEA\uFF1A\u8282\u70B9\u672A\u4E0A\u62A5\u8FD0\u884C\u65F6 ${binding.runtimeKind}`);
|
|
276361
276751
|
const readiness = node2.meta.connectorReadiness?.find((item) => item.slug === connectorSlug);
|
|
276362
276752
|
if (!readiness?.ready) {
|
|
276363
276753
|
throw new Error(`${label} Connector \u672A\u5C31\u7EEA\uFF1A${readiness?.error ?? "\u8282\u70B9\u672A\u4E0A\u62A5 wrapper/\u968F\u5305\u547D\u4EE4\u8D44\u4EA7\u81EA\u68C0\uFF0C\u8BF7\u5347\u7EA7\u5E76\u91CD\u542F node-daemon"}`);
|
|
@@ -276370,10 +276760,10 @@ async function startServe(opts) {
|
|
|
276370
276760
|
throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5FC5\u987B\u662F\u5DF2\u6FC0\u6D3B\u7684 AI \u5458\u5DE5\uFF1A${actorId}`);
|
|
276371
276761
|
}
|
|
276372
276762
|
const binding = await actors.service.resolveBinding()(actorId);
|
|
276373
|
-
if (!binding) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5C1A\u672A\u7ED1\u5B9A\u53EF\u7528
|
|
276763
|
+
if (!binding) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u5C1A\u672A\u7ED1\u5B9A\u53EF\u7528\u8FD0\u884C\u65F6\uFF1A${actorId}`);
|
|
276374
276764
|
const node2 = hub?.connectedDaemons().find((item) => item.daemonId === binding.nodeId);
|
|
276375
276765
|
if (!node2) throw new Error(`\u77E5\u8BC6\u7BA1\u7406\u5458\u7ED1\u5B9A\u8282\u70B9 ${binding.nodeId} \u5F53\u524D\u4E0D\u5728\u7EBF`);
|
|
276376
|
-
if (!node2.meta.adapters.includes(binding.runtimeKind)) throw new Error(`\u8282\u70B9\u672A\u4E0A\u62A5
|
|
276766
|
+
if (!node2.meta.adapters.includes(binding.runtimeKind)) throw new Error(`\u8282\u70B9\u672A\u4E0A\u62A5\u8FD0\u884C\u65F6 ${binding.runtimeKind}`);
|
|
276377
276767
|
if (!chatRemoteAdapter) throw new Error("node-gateway \u5C1A\u672A\u5C31\u7EEA");
|
|
276378
276768
|
};
|
|
276379
276769
|
const knowledgeProvisioner = new KnowledgeCuratorProvisioner(actors.service, (connectorSlug, skillId) => createConnectorBySlug(connectorSlug)?.embeddedSkills?.find((skill) => skill.id === skillId) ?? null);
|
|
@@ -276843,6 +277233,12 @@ async function startServe(opts) {
|
|
|
276843
277233
|
const [roster, bindings] = await Promise.all([service.listActors({ kind: "agent" }), service.listBindings()]);
|
|
276844
277234
|
return { actors: roster, bindings };
|
|
276845
277235
|
},
|
|
277236
|
+
// 缺陷 0124:删掉运行时后,绑在它上面的 Agent 不该继续挂在一条已经不存在的运行时上。
|
|
277237
|
+
// 与 actorBindings 同一条解析路径(按请求公司拿这家的员工域),写侧落审计。
|
|
277238
|
+
releaseBindingsOnRuntime: async (companyId, target, by) => {
|
|
277239
|
+
const { service } = await actors.resolveCtx(companyId ?? defaultCompanyId);
|
|
277240
|
+
return service.releaseBindingsOnRuntime(target, by);
|
|
277241
|
+
},
|
|
276846
277242
|
nodeTokens,
|
|
276847
277243
|
enrollTokens,
|
|
276848
277244
|
nodeStore,
|
|
@@ -277736,6 +278132,11 @@ async function startServe(opts) {
|
|
|
277736
278132
|
...chatRunLedgerFields({ executionProfile, chatSessionId, chatTurnId }),
|
|
277737
278133
|
metadata: {
|
|
277738
278134
|
runtimeSessionId,
|
|
278135
|
+
/* Bug 0102 / 0171:这个会话号是平台现编的占位 id 吗(首轮手里没有 runtime 的号)。
|
|
278136
|
+
收尾时据它决定要不要换成 runtime 报回来的原生号——**正常收尾路与恢复路读的是同一位**:
|
|
278137
|
+
恢复路由另一个进程收尾,`resumeRuntimeSession` 这个闭包变量随旧 serve 一起没了,
|
|
278138
|
+
判据只能落在持久事实上(见 `placeholderRuntimeSessionFlag`)。 */
|
|
278139
|
+
...placeholderRuntimeSessionFlag(!resumeRuntimeSession),
|
|
277739
278140
|
nodeId: target.nodeId,
|
|
277740
278141
|
runtimeKind: target.runtimeKind,
|
|
277741
278142
|
companyId: runCompanyId,
|
|
@@ -278001,7 +278402,15 @@ async function startServe(opts) {
|
|
|
278001
278402
|
...requiredConnectors.length > 0 ? { requiredConnectorSlugs: requiredConnectors } : {}
|
|
278002
278403
|
});
|
|
278003
278404
|
const chatJobKey = `chat::${artifactId}`;
|
|
278004
|
-
chatLiveSessions.set(chatJobKey, {
|
|
278405
|
+
chatLiveSessions.set(chatJobKey, {
|
|
278406
|
+
artifactId,
|
|
278407
|
+
sessionId: traceRunId,
|
|
278408
|
+
dispatchId: handle.id,
|
|
278409
|
+
kill: () => handle.kill(),
|
|
278410
|
+
...chatSessionId ? { markCancelCause: (cause) => {
|
|
278411
|
+
liveChat.markCancelCause(chatSessionId, cause);
|
|
278412
|
+
} } : {}
|
|
278413
|
+
});
|
|
278005
278414
|
chatRunByDispatch.set(handle.id, { runId: traceRunId, companyId: runCompanyId });
|
|
278006
278415
|
enqueueTrace(async () => {
|
|
278007
278416
|
const ts3 = await traceStoreFor(runCompanyId);
|
|
@@ -278067,12 +278476,9 @@ async function startServe(opts) {
|
|
|
278067
278476
|
const effectiveModel = info.model ?? chatModel ?? null;
|
|
278068
278477
|
const modelSource = info.model ? "runtime" : chatModelSource;
|
|
278069
278478
|
let nativeSessionMetadata;
|
|
278070
|
-
if (
|
|
278479
|
+
if (info.runtimeSessionId && info.runtimeSessionId !== runtimeSessionId) {
|
|
278071
278480
|
const run = await (await traceStoreFor(runCompanyId)).getRun(traceRunId).catch(() => null);
|
|
278072
|
-
|
|
278073
|
-
if (base?.["runtimeSessionId"] === runtimeSessionId) {
|
|
278074
|
-
nativeSessionMetadata = { ...base, runtimeSessionId: info.runtimeSessionId };
|
|
278075
|
-
}
|
|
278481
|
+
nativeSessionMetadata = nativeRuntimeSessionMetadata(run?.metadata, info.runtimeSessionId);
|
|
278076
278482
|
}
|
|
278077
278483
|
await traceAppender.update({
|
|
278078
278484
|
status: classifySessionExitStatus(info),
|
|
@@ -278226,6 +278632,18 @@ async function startServe(opts) {
|
|
|
278226
278632
|
// getNode 无 status 过滤,返回 deleted 行;查询失败时 verifyClient 侧默认拒绝(绝不放行)。
|
|
278227
278633
|
getNodeStatus: async (nodeId) => (await nodeStore.getNode(nodeId))?.status ?? null,
|
|
278228
278634
|
throttle: handshakeThrottleOpts,
|
|
278635
|
+
/* 驱逐留痕(ADR 0600 扩档):会话自尽后报回来的只是个光秃秃的 `cancelled`,
|
|
278636
|
+
「这台机器上的这个会话被换下了」只有这一帧知道。chat 轮据此写「执行这一轮的机器断开了连接」。 */
|
|
278637
|
+
onSessionEvicted: (dispatchId) => {
|
|
278638
|
+
for (const c of chatLiveSessions.values()) {
|
|
278639
|
+
if (c.dispatchId !== dispatchId) continue;
|
|
278640
|
+
try {
|
|
278641
|
+
c.markCancelCause?.("node-disconnected");
|
|
278642
|
+
} catch {
|
|
278643
|
+
}
|
|
278644
|
+
break;
|
|
278645
|
+
}
|
|
278646
|
+
},
|
|
278229
278647
|
// 改动 2/3 观测:deleted / unknown / status-error / rate-limited / invalid-token 分类可见,供事后归因。
|
|
278230
278648
|
onHandshakeRejected: (rej) => {
|
|
278231
278649
|
console.warn(
|
|
@@ -278551,10 +278969,12 @@ async function startServe(opts) {
|
|
|
278551
278969
|
storesFor: async (companyId) => {
|
|
278552
278970
|
const turns = await chatTurnsFor(companyId);
|
|
278553
278971
|
const messages = await chatStoreFor(companyId);
|
|
278972
|
+
const items = await chatItemsFor(companyId).catch(() => void 0);
|
|
278554
278973
|
return {
|
|
278555
278974
|
turns,
|
|
278556
278975
|
...assistantRowRecovery(messages, {
|
|
278557
|
-
outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId)
|
|
278976
|
+
outputsPartFor: (sid) => outputsPartProvider(sessionOutputsScan, sid, companyId),
|
|
278977
|
+
...items ? { itemsFor: () => items } : {}
|
|
278558
278978
|
})
|
|
278559
278979
|
};
|
|
278560
278980
|
},
|
|
@@ -279857,7 +280277,7 @@ async function startServe(opts) {
|
|
|
279857
280277
|
const quote = errorMessage2?.trim().slice(0, 300);
|
|
279858
280278
|
if (reason === "rate-limit") {
|
|
279859
280279
|
const hint = extractResetHint(errorMessage2);
|
|
279860
|
-
return `**\u5B83\u7684\u989D\u5EA6\u88AB\u6253\u6EE1\u4E86**${hint ? `\
|
|
280280
|
+
return `**\u5B83\u7684\u989D\u5EA6\u88AB\u6253\u6EE1\u4E86**${hint ? `\uFF08\u8FD0\u884C\u65F6\u539F\u8BDD\uFF1A\`${hint}\`\uFF09` : ""}\u2014\u2014\u989D\u5EA6\u5230\u70B9\u4F1A\u81EA\u5DF1\u6062\u590D\uFF0C**\u4E0D\u9700\u8981\u4F60\u4FEE\u4EFB\u4F55\u4E1C\u897F**\uFF1B\u4E0D\u60F3\u7B49\u5C31\u7ED9\u8FD9\u4E2A\u5458\u5DE5\u6362\u4E2A\u8FD0\u884C\u65F6\u3002` + (quote ? `
|
|
279861
280281
|
> ${quote}` : "");
|
|
279862
280282
|
}
|
|
279863
280283
|
const label = {
|
|
@@ -280226,7 +280646,7 @@ ${nodeFault}` : "");
|
|
|
280226
280646
|
if (horizon === "short" && s2.sinceMs < quotaStarvationMs * 2) continue;
|
|
280227
280647
|
const hours = Math.round(s2.sinceMs / 36e5);
|
|
280228
280648
|
const hint = extractResetHint(s2.lastErrorMessage);
|
|
280229
|
-
const when = hint ?
|
|
280649
|
+
const when = hint ? `\u8FD0\u884C\u65F6\u539F\u8BDD\uFF1A\`${hint}\`` : "\u8FD0\u884C\u65F6\u6CA1\u8BF4\u4EC0\u4E48\u65F6\u5019\u6062\u590D";
|
|
280230
280650
|
const longNote = horizon === "long" ? "**\u770B\u65F6\u95F4\u662F\u8DE8\u5929\u7684\u989D\u5EA6\u7A97\u53E3\uFF0C\u7B49\u4E0D\u8D77**\u2014\u2014" : "";
|
|
280231
280651
|
await escalateHold(
|
|
280232
280652
|
s2.artifactId,
|
|
@@ -280471,7 +280891,13 @@ ${nodeFault}` : "");
|
|
|
280471
280891
|
liveness: (sessionId) => probe.liveness(sessionId),
|
|
280472
280892
|
kill: (jobKey) => {
|
|
280473
280893
|
const c = chatLiveSessions.get(jobKey);
|
|
280474
|
-
if (c)
|
|
280894
|
+
if (c) {
|
|
280895
|
+
try {
|
|
280896
|
+
c.markCancelCause?.("no-output");
|
|
280897
|
+
} catch {
|
|
280898
|
+
}
|
|
280899
|
+
return Promise.resolve(c.kill()).then(() => true).catch(() => false);
|
|
280900
|
+
}
|
|
280475
280901
|
return dispatcherByJob.get(jobKey)?.killSession(jobKey) ?? Promise.resolve(false);
|
|
280476
280902
|
},
|
|
280477
280903
|
// trace 写不进去的 run:冻结的事件钟是"观测降级"不是"真静默",按"信息不足→不判"跳过、交 wallClock
|
|
@@ -280501,6 +280927,7 @@ ${nodeFault}` : "");
|
|
|
280501
280927
|
for (const d of hub?.connectedDaemons() ?? []) {
|
|
280502
280928
|
for (const a of d.meta.activeSessions ?? []) if (a.jobArtifactId) ids2.add(a.jobArtifactId);
|
|
280503
280929
|
}
|
|
280930
|
+
for (const a of hub?.liveSessionArtifactIds() ?? []) ids2.add(a);
|
|
280504
280931
|
return ids2;
|
|
280505
280932
|
};
|
|
280506
280933
|
const apiSilenceMs = opts.apiSilenceMs ?? 6e5;
|
|
@@ -280940,6 +281367,10 @@ function log2(tag, msg, extra) {
|
|
|
280940
281367
|
var import_node_child_process18 = require("node:child_process");
|
|
280941
281368
|
var fs35 = __toESM(require("node:fs"), 1);
|
|
280942
281369
|
var path29 = __toESM(require("node:path"), 1);
|
|
281370
|
+
var SESSION_LOCK_WAIT_MS = 1e4;
|
|
281371
|
+
var POLL_MS = 20;
|
|
281372
|
+
var PROBE_TIMES = 5;
|
|
281373
|
+
var PROBE_GAP_MS = 100;
|
|
280943
281374
|
var flockAvailable = null;
|
|
280944
281375
|
function hasFlock() {
|
|
280945
281376
|
if (flockAvailable !== null) return flockAvailable;
|
|
@@ -280952,48 +281383,88 @@ function hasFlock() {
|
|
|
280952
281383
|
}
|
|
280953
281384
|
return flockAvailable;
|
|
280954
281385
|
}
|
|
280955
|
-
function
|
|
280956
|
-
|
|
281386
|
+
function sleepSync(ms) {
|
|
281387
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
281388
|
+
}
|
|
281389
|
+
function acquireSessionLockAttempt(lockPath, opts = {}) {
|
|
281390
|
+
if (!hasFlock()) return { outcome: "no-flock" };
|
|
281391
|
+
const waitMs = opts.waitMs ?? SESSION_LOCK_WAIT_MS;
|
|
280957
281392
|
fs35.mkdirSync(path29.dirname(lockPath), { recursive: true });
|
|
280958
281393
|
const readyFile = `${lockPath}.ready.${process.pid}`;
|
|
280959
|
-
|
|
280960
|
-
|
|
280961
|
-
|
|
281394
|
+
const heldFile = `${lockPath}.held.${process.pid}`;
|
|
281395
|
+
for (const f2 of [readyFile, heldFile]) {
|
|
281396
|
+
try {
|
|
281397
|
+
fs35.unlinkSync(f2);
|
|
281398
|
+
} catch {
|
|
281399
|
+
}
|
|
280962
281400
|
}
|
|
280963
|
-
const child = (0, import_node_child_process18.spawn)(
|
|
280964
|
-
|
|
280965
|
-
|
|
280966
|
-
|
|
280967
|
-
|
|
281401
|
+
const child = (0, import_node_child_process18.spawn)(
|
|
281402
|
+
"sh",
|
|
281403
|
+
["-c", 'flock -n "$1" -c "printf ok > \\"$2\\"; exec cat" || printf held > "$3"', "sh", lockPath, readyFile, heldFile],
|
|
281404
|
+
{ stdio: ["pipe", "ignore", "ignore"], detached: true }
|
|
281405
|
+
);
|
|
281406
|
+
const startedAt = Date.now();
|
|
281407
|
+
const deadline = startedAt + waitMs;
|
|
280968
281408
|
let ready = false;
|
|
280969
|
-
|
|
281409
|
+
let held = false;
|
|
281410
|
+
for (; ; ) {
|
|
280970
281411
|
if (fs35.existsSync(readyFile)) {
|
|
280971
281412
|
ready = true;
|
|
280972
281413
|
break;
|
|
280973
281414
|
}
|
|
280974
|
-
(
|
|
281415
|
+
if (fs35.existsSync(heldFile)) {
|
|
281416
|
+
held = true;
|
|
281417
|
+
break;
|
|
281418
|
+
}
|
|
281419
|
+
if (Date.now() >= deadline) break;
|
|
281420
|
+
sleepSync(POLL_MS);
|
|
280975
281421
|
}
|
|
280976
|
-
|
|
280977
|
-
|
|
280978
|
-
|
|
281422
|
+
for (const f2 of [readyFile, heldFile]) {
|
|
281423
|
+
try {
|
|
281424
|
+
fs35.unlinkSync(f2);
|
|
281425
|
+
} catch {
|
|
281426
|
+
}
|
|
280979
281427
|
}
|
|
280980
|
-
|
|
281428
|
+
const killChild = () => {
|
|
281429
|
+
try {
|
|
281430
|
+
if (child.pid) process.kill(-child.pid, "SIGKILL");
|
|
281431
|
+
} catch {
|
|
281432
|
+
}
|
|
280981
281433
|
try {
|
|
280982
281434
|
child.kill("SIGKILL");
|
|
280983
281435
|
} catch {
|
|
280984
281436
|
}
|
|
280985
|
-
|
|
281437
|
+
};
|
|
281438
|
+
if (held) {
|
|
281439
|
+
killChild();
|
|
281440
|
+
return { outcome: "held" };
|
|
280986
281441
|
}
|
|
280987
|
-
|
|
280988
|
-
|
|
280989
|
-
|
|
280990
|
-
|
|
280991
|
-
|
|
280992
|
-
|
|
281442
|
+
if (!ready) {
|
|
281443
|
+
killChild();
|
|
281444
|
+
let heldEverywhere = true;
|
|
281445
|
+
for (let i = 0; i < PROBE_TIMES; i += 1) {
|
|
281446
|
+
if (!isSessionLocked(lockPath)) {
|
|
281447
|
+
heldEverywhere = false;
|
|
281448
|
+
break;
|
|
280993
281449
|
}
|
|
280994
|
-
|
|
280995
|
-
|
|
280996
|
-
|
|
281450
|
+
if (i + 1 < PROBE_TIMES) sleepSync(PROBE_GAP_MS);
|
|
281451
|
+
}
|
|
281452
|
+
if (heldEverywhere) return { outcome: "held" };
|
|
281453
|
+
return { outcome: "undetermined", waitedMs: Date.now() - startedAt };
|
|
281454
|
+
}
|
|
281455
|
+
return {
|
|
281456
|
+
outcome: "acquired",
|
|
281457
|
+
lock: {
|
|
281458
|
+
path: lockPath,
|
|
281459
|
+
release() {
|
|
281460
|
+
try {
|
|
281461
|
+
if (child.pid) process.kill(-child.pid, "SIGKILL");
|
|
281462
|
+
} catch {
|
|
281463
|
+
}
|
|
281464
|
+
try {
|
|
281465
|
+
child.stdin?.destroy();
|
|
281466
|
+
} catch {
|
|
281467
|
+
}
|
|
280997
281468
|
}
|
|
280998
281469
|
}
|
|
280999
281470
|
};
|
|
@@ -281322,14 +281793,21 @@ async function runSessionProcess(specFile, adapter) {
|
|
|
281322
281793
|
process.env["OASIS_BIND_RUNTIME_PDEATHSIG"] = "1";
|
|
281323
281794
|
const spec = await readSpec(specFile);
|
|
281324
281795
|
const { dispatchId, job, auth, sessionDir } = spec;
|
|
281325
|
-
const
|
|
281326
|
-
if (
|
|
281796
|
+
const attempt = acquireSessionLockAttempt(path31.join(sessionDir, "lock"));
|
|
281797
|
+
if (attempt.outcome === "held") {
|
|
281327
281798
|
log2("[session]", `${dispatchId}: \u4F1A\u8BDD\u9501\u5DF2\u88AB\u5360\u7528\uFF0C\u672C\u8FDB\u7A0B\u9000\u51FA\uFF08\u9632\u53CC\u8DD1\uFF09`);
|
|
281328
281799
|
return;
|
|
281329
281800
|
}
|
|
281330
|
-
if (
|
|
281801
|
+
if (attempt.outcome === "no-flock") {
|
|
281331
281802
|
log2("[session]", `${dispatchId}: \u672C\u673A\u65E0 flock(1)\uFF0C\u5B64\u513F\u5DE1\u68C0\u90A3\u9053\u9632\u7EBF\u4E0D\u53EF\u7528\uFF08\u53EA\u5269 evict + watchdog\uFF09`);
|
|
281332
281803
|
}
|
|
281804
|
+
const lock = attempt.outcome === "acquired" ? attempt.lock : null;
|
|
281805
|
+
if (attempt.outcome === "undetermined") {
|
|
281806
|
+
const detail = `\u4F1A\u8BDD\u9501\u5728 ${Math.round(attempt.waitedMs / 1e3)} \u79D2\u5185\u65E2\u6CA1\u62FF\u5230\u3001\u4E5F\u5224\u4E0D\u51FA\u88AB\u8C01\u5360\u7740\uFF08\u8282\u70B9\u8D1F\u8F7D\u8FC7\u9AD8\uFF0C\u7A97\u53E3 ${Math.round(SESSION_LOCK_WAIT_MS / 1e3)}s\uFF09`;
|
|
281807
|
+
log2("[session]", `${dispatchId}: ${detail} \u2192 \u4E0D\u5F00\u5DE5\uFF0C\u628A\u5931\u8D25\u62A5\u7ED9\u670D\u52A1\u7AEF`);
|
|
281808
|
+
await new SessionProcessState(spec, adapter, null).reportStartupFailure(`${SESSION_STARTUP_FAILED_PREFIX}${detail}`);
|
|
281809
|
+
return;
|
|
281810
|
+
}
|
|
281333
281811
|
const state = new SessionProcessState(spec, adapter, lock);
|
|
281334
281812
|
await state.run();
|
|
281335
281813
|
}
|
|
@@ -281438,6 +281916,27 @@ var SessionProcessState = class {
|
|
|
281438
281916
|
await done;
|
|
281439
281917
|
this.teardown();
|
|
281440
281918
|
}
|
|
281919
|
+
/**
|
|
281920
|
+
* 开工前就退出:连上服务端、把 `session_exited` 报出去、等一下 ack,然后收摊(Bug #0184 第 2 条)。
|
|
281921
|
+
*
|
|
281922
|
+
* 它和 {@link run} 的区别只有「不起 runtime」:帧走同一条必达路径(带 seq、进 outbox、重连重放),
|
|
281923
|
+
* 所以新节点 + 老服务端也不会出错——老服务端收到的就是一条普通的 `session_exited`。
|
|
281924
|
+
*
|
|
281925
|
+
* `reason` 用既有的 `error` 而不是新增枚举值:退出归因是 contract 上的封闭值域,
|
|
281926
|
+
* 加一个值会让**老服务端**读到不认识的 reason;真实原因放 `errorMessage`(那是自由文本,
|
|
281927
|
+
* 两侧都已按"给人看的一句话"对待)。
|
|
281928
|
+
*/
|
|
281929
|
+
async reportStartupFailure(errorMessage2) {
|
|
281930
|
+
this.connect();
|
|
281931
|
+
this.sendKeyFrame({
|
|
281932
|
+
type: "session_exited",
|
|
281933
|
+
dispatchId: this.spec.dispatchId,
|
|
281934
|
+
sessionId: this.spec.dispatchId,
|
|
281935
|
+
info: { code: null, reason: "error", errorMessage: errorMessage2 }
|
|
281936
|
+
});
|
|
281937
|
+
await this.waitExitAck();
|
|
281938
|
+
this.teardown();
|
|
281939
|
+
}
|
|
281441
281940
|
/** 会话已退出:等 exit 帧的 ack(等不到也走,服务端有时钟扫描兜底),然后放行主流程。 */
|
|
281442
281941
|
async afterSessionExit() {
|
|
281443
281942
|
await this.waitExitAck();
|
|
@@ -285224,8 +285723,9 @@ function safeFileName(raw, fallback) {
|
|
|
285224
285723
|
const cleaned = displayName2(raw).replace(/[\\/:*?"<>|-]+/g, "_").replace(/\s+/g, "_").replace(/^\.+/, "").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
285225
285724
|
return cleaned || fallback;
|
|
285226
285725
|
}
|
|
285227
|
-
function resolveProjectFileOutPath(rawOut, fileName) {
|
|
285228
|
-
const
|
|
285726
|
+
function resolveProjectFileOutPath(rawOut, fileName, contentType) {
|
|
285727
|
+
const ext = sourceExtension(fileName, contentType);
|
|
285728
|
+
const fallback = keepExtension(`${PROJECT_FILES_DIR}/${safeFileName(fileName, "file")}`, ext);
|
|
285229
285729
|
if (rawOut === void 0 || rawOut.trim() === "") return fallback;
|
|
285230
285730
|
const raw = rawOut.trim();
|
|
285231
285731
|
if (path35.isAbsolute(raw) || /^[a-zA-Z]:[\\/]/.test(raw)) {
|
|
@@ -285244,7 +285744,38 @@ function resolveProjectFileOutPath(rawOut, fileName) {
|
|
|
285244
285744
|
if (segments.length < 2 || segments[0] !== PROJECT_FILES_DIR) {
|
|
285245
285745
|
throw new Error(`--out \u5FC5\u987B\u843D\u5728 ${PROJECT_FILES_DIR}/ \u4E0B\uFF08\u4F8B\uFF1A--out ${PROJECT_FILES_DIR}/${safeFileName(fileName, "file")}\uFF09`);
|
|
285246
285746
|
}
|
|
285247
|
-
return segments.join("/");
|
|
285747
|
+
return keepExtension(segments.join("/"), ext);
|
|
285748
|
+
}
|
|
285749
|
+
var EXT_BY_MIME = {
|
|
285750
|
+
"text/markdown": ".md",
|
|
285751
|
+
"text/x-markdown": ".md",
|
|
285752
|
+
"text/plain": ".txt",
|
|
285753
|
+
"text/html": ".html",
|
|
285754
|
+
"text/csv": ".csv",
|
|
285755
|
+
"application/json": ".json",
|
|
285756
|
+
"application/pdf": ".pdf",
|
|
285757
|
+
"image/png": ".png",
|
|
285758
|
+
"image/jpeg": ".jpg",
|
|
285759
|
+
"image/gif": ".gif",
|
|
285760
|
+
"image/webp": ".webp",
|
|
285761
|
+
"image/svg+xml": ".svg",
|
|
285762
|
+
"application/zip": ".zip"
|
|
285763
|
+
};
|
|
285764
|
+
function realExtension(name) {
|
|
285765
|
+
const ext = path35.posix.extname(name);
|
|
285766
|
+
return /^\.[A-Za-z0-9]{1,8}$/.test(ext) ? ext : "";
|
|
285767
|
+
}
|
|
285768
|
+
function sourceExtension(fileName, contentType) {
|
|
285769
|
+
const own3 = realExtension(displayName2(fileName));
|
|
285770
|
+
if (own3) return own3;
|
|
285771
|
+
const mime = (contentType ?? "").split(";")[0].trim().toLowerCase();
|
|
285772
|
+
return EXT_BY_MIME[mime] ?? "";
|
|
285773
|
+
}
|
|
285774
|
+
function keepExtension(rel, ext) {
|
|
285775
|
+
if (!ext) return rel;
|
|
285776
|
+
const current = realExtension(rel);
|
|
285777
|
+
if (current.toLowerCase() === ext.toLowerCase()) return rel;
|
|
285778
|
+
return `${current ? rel.slice(0, rel.length - current.length) : rel}${ext}`;
|
|
285248
285779
|
}
|
|
285249
285780
|
function isTextLike(name, contentType) {
|
|
285250
285781
|
if (contentType) {
|
|
@@ -285532,7 +286063,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
|
|
|
285532
286063
|
}
|
|
285533
286064
|
}
|
|
285534
286065
|
}
|
|
285535
|
-
var CLI_VERSION = "2.2.
|
|
286066
|
+
var CLI_VERSION = "2.2.60" ? "2.2.60" : "dev";
|
|
285536
286067
|
function apiHeaders(token, company) {
|
|
285537
286068
|
const headers = {
|
|
285538
286069
|
authorization: `Bearer ${token}`,
|
|
@@ -286953,7 +287484,7 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
|
|
|
286953
287484
|
);
|
|
286954
287485
|
const row = findByBlobRef(view.items ?? [], ref2);
|
|
286955
287486
|
const name = displayName2(row.name);
|
|
286956
|
-
const rel = resolveProjectFileOutPath(flags.get("out"), name);
|
|
287487
|
+
const rel = resolveProjectFileOutPath(flags.get("out"), name, row.content_type);
|
|
286957
287488
|
const bytes2 = await api.bytes(`/api/blobs/${encodeURIComponent((row.blob_ref ?? "").replace(/^blob:/, ""))}`);
|
|
286958
287489
|
const abs = path36.resolve(process.cwd(), rel);
|
|
286959
287490
|
fs41.mkdirSync(path36.dirname(abs), { recursive: true });
|
|
@@ -289223,7 +289754,7 @@ function shimScript() {
|
|
|
289223
289754
|
}
|
|
289224
289755
|
|
|
289225
289756
|
// src/index.ts
|
|
289226
|
-
var PKG_VERSION = true ? "2.2.
|
|
289757
|
+
var PKG_VERSION = true ? "2.2.60" : "dev";
|
|
289227
289758
|
var LOCAL_BIN = localBin();
|
|
289228
289759
|
var NPM_PREFIX = npmPrefix();
|
|
289229
289760
|
var INSTANCE = DEFAULT_INSTANCE;
|