oasis_test_v2 2.2.5 → 2.2.7
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 +713 -147
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -366,12 +366,16 @@ var init_annotation = __esm({
|
|
|
366
366
|
function commentThreadSessionId(a, b2) {
|
|
367
367
|
return `${COMMENT_THREAD_SESSION_PREFIX}${[a, b2].sort().join("|")}`;
|
|
368
368
|
}
|
|
369
|
-
|
|
369
|
+
function reviewThreadSessionId(a, b2) {
|
|
370
|
+
return `${REVIEW_THREAD_SESSION_PREFIX}${[a, b2].sort().join("|")}`;
|
|
371
|
+
}
|
|
372
|
+
var GAP_THREAD_SESSION_PREFIX, COMMENT_THREAD_SESSION_PREFIX, REVIEW_THREAD_SESSION_PREFIX;
|
|
370
373
|
var init_collab_view = __esm({
|
|
371
374
|
"../contract/src/collab-view.ts"() {
|
|
372
375
|
"use strict";
|
|
373
376
|
GAP_THREAD_SESSION_PREFIX = "gapline:";
|
|
374
377
|
COMMENT_THREAD_SESSION_PREFIX = "commentline:";
|
|
378
|
+
REVIEW_THREAD_SESSION_PREFIX = "reviewline:";
|
|
375
379
|
}
|
|
376
380
|
});
|
|
377
381
|
|
|
@@ -1340,27 +1344,21 @@ function trimRoundSummariesForReturn(summaries, recentRounds = DELEGATION_RETURN
|
|
|
1340
1344
|
});
|
|
1341
1345
|
}
|
|
1342
1346
|
function collectDelegationArtifacts(roundSummaries) {
|
|
1343
|
-
const
|
|
1347
|
+
const out = [];
|
|
1344
1348
|
for (const round of roundSummaries) {
|
|
1345
1349
|
for (const file of round.files ?? []) {
|
|
1346
|
-
|
|
1347
|
-
if (seen) {
|
|
1348
|
-
if (!seen.blobRef && file.blobRef) seen.blobRef = file.blobRef;
|
|
1349
|
-
continue;
|
|
1350
|
-
}
|
|
1351
|
-
artifacts.set(file.name, {
|
|
1350
|
+
out.push({
|
|
1352
1351
|
name: file.name,
|
|
1353
1352
|
...file.blobRef ? { blobRef: file.blobRef } : {},
|
|
1354
1353
|
round: round.round,
|
|
1355
|
-
// settledSeq
|
|
1356
|
-
//
|
|
1357
|
-
//
|
|
1358
|
-
// 无 → 该委派整条退回「单委派单锚点」降级(`ann:36bfa04b`)。
|
|
1354
|
+
// 这一轮收口时的 settledSeq。未收口的轮次(pending 续跑那一支)与本字段落地前的存量行
|
|
1355
|
+
// 都没有值——前端凭它是否缺失走两条路径:有 → 按 settledSeq 各自定位对应
|
|
1356
|
+
// `chat-msg:delegation-done|<id>:<seq>`;无 → 该委派整条退回单锚点降级(`ann:36bfa04b`)。
|
|
1359
1357
|
...typeof round.settledSeq === "number" ? { settledSeq: round.settledSeq } : {}
|
|
1360
1358
|
});
|
|
1361
1359
|
}
|
|
1362
1360
|
}
|
|
1363
|
-
return
|
|
1361
|
+
return out;
|
|
1364
1362
|
}
|
|
1365
1363
|
function delegationOutboxFingerprints(roundSummaries) {
|
|
1366
1364
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -12271,7 +12269,7 @@ function latestProposedOf(state, nodeId, deliveriesOnly) {
|
|
|
12271
12269
|
const successes = state.mainWorksOf(nodeId).filter((w2) => w2.status === "success" && (!deliveriesOnly || state.artifactsOf(w2.id).length > 0 || w2.conclusion != null)).sort((a, b2) => a.createdAt.localeCompare(b2.createdAt));
|
|
12272
12270
|
return successes.length > 0 ? successes[successes.length - 1].id : null;
|
|
12273
12271
|
}
|
|
12274
|
-
var workCreate, workKill, workResponse, workHandoffRecorded, workStart, workSubmitOutput, workContentEdited, workConclude, nodeLatestProposedRebased, workTimeout;
|
|
12272
|
+
var workCreate, workRedispatch, workSnapshotRecorded, workKill, workResponse, workHandoffRecorded, workStart, workSubmitOutput, workContentEdited, workConclude, nodeLatestProposedRebased, workTimeout;
|
|
12275
12273
|
var init_work = __esm({
|
|
12276
12274
|
"../engine/src/handlers/work.ts"() {
|
|
12277
12275
|
"use strict";
|
|
@@ -12384,11 +12382,78 @@ var init_work = __esm({
|
|
|
12384
12382
|
// 会话侧也要杀(旧 agent 可能还挂着)。cancelPreviousWork 找不到/会话已退则 no-op。
|
|
12385
12383
|
// 回信 work 不杀前会话(apply 不 kill 主链,节点已完结、无在跑会话),只派发。
|
|
12386
12384
|
async effect(e, ctx) {
|
|
12387
|
-
if (
|
|
12385
|
+
if (!await ctx.canDispatchWork(e.workorderId, e.workId)) return;
|
|
12388
12386
|
if (!e.replyToIssueId) await ctx.cancelPreviousWork(e.nodeId, e.workId);
|
|
12389
12387
|
await ctx.dispatchWork(e.workId, e.resumeEligible !== void 0 ? { resumeEligible: e.resumeEligible } : void 0);
|
|
12390
12388
|
}
|
|
12391
12389
|
};
|
|
12390
|
+
workRedispatch = {
|
|
12391
|
+
name: "work/redispatch",
|
|
12392
|
+
kind: "work.redispatch",
|
|
12393
|
+
apply() {
|
|
12394
|
+
},
|
|
12395
|
+
async effect(e, ctx) {
|
|
12396
|
+
if (!await ctx.canDispatchWork(e.workorderId, e.workId, { neverStarted: true })) return;
|
|
12397
|
+
await ctx.dispatchWork(e.workId, { resumeEligible: false });
|
|
12398
|
+
}
|
|
12399
|
+
};
|
|
12400
|
+
workSnapshotRecorded = {
|
|
12401
|
+
name: "work/snapshot-recorded",
|
|
12402
|
+
kind: "work.snapshot_recorded",
|
|
12403
|
+
apply(e, state, ctx) {
|
|
12404
|
+
const node2 = state.node(e.nodeId);
|
|
12405
|
+
if (!ctx.actorId.startsWith("actor:human:") || !state.workorder.dispatchPaused || !node2 || node2.cancelledAt || node2.latestWorkId !== e.expectedLatestWorkId || state.work(e.workId) || !e.contentRef || !e.conclusion.trim()) return;
|
|
12406
|
+
if (state.allWorksIncludingReplies(e.nodeId).some((w2) => !w2.endedAt && !w2.deadAt) || state.allReviews().some((r) => r.nodeId === e.nodeId && !r.endedAt && !r.cancelledAt)) return;
|
|
12407
|
+
state.insertWork({
|
|
12408
|
+
id: e.workId,
|
|
12409
|
+
workorderId: e.workorderId,
|
|
12410
|
+
nodeId: e.nodeId,
|
|
12411
|
+
assigneeActorId: ctx.actorId,
|
|
12412
|
+
createdAt: ctx.at,
|
|
12413
|
+
startedAt: null,
|
|
12414
|
+
endedAt: ctx.at,
|
|
12415
|
+
deadAt: null,
|
|
12416
|
+
cancelledAt: null,
|
|
12417
|
+
status: "success",
|
|
12418
|
+
lane: "main",
|
|
12419
|
+
outcome: "completed",
|
|
12420
|
+
outcomeDetail: null,
|
|
12421
|
+
sessionRef: null,
|
|
12422
|
+
continuesWorkId: node2.latestAcceptId,
|
|
12423
|
+
parentWorkId: node2.latestProposedWorkId,
|
|
12424
|
+
outputVersionNo: null,
|
|
12425
|
+
conclusion: e.conclusion,
|
|
12426
|
+
proposalReason: e.conclusion,
|
|
12427
|
+
lastActivityAt: ctx.at,
|
|
12428
|
+
handledIssueIds: null,
|
|
12429
|
+
agentHandoffAttemptId: null,
|
|
12430
|
+
businessHandoffStatus: null,
|
|
12431
|
+
businessHandoffRecordedAt: null,
|
|
12432
|
+
acceptanceState: null,
|
|
12433
|
+
acceptedAt: null,
|
|
12434
|
+
acceptedBy: null,
|
|
12435
|
+
rejectedReason: null,
|
|
12436
|
+
overrideBy: null,
|
|
12437
|
+
overrideReason: null,
|
|
12438
|
+
nodeVersion: node2.version
|
|
12439
|
+
});
|
|
12440
|
+
state.replaceArtifacts(e.workId, [{
|
|
12441
|
+
id: `${e.workId}:out`,
|
|
12442
|
+
workId: e.workId,
|
|
12443
|
+
nodeId: e.nodeId,
|
|
12444
|
+
type: null,
|
|
12445
|
+
name: "output",
|
|
12446
|
+
logicalPath: "/",
|
|
12447
|
+
contentRef: e.contentRef,
|
|
12448
|
+
contentKind: "inline",
|
|
12449
|
+
ordinal: 0,
|
|
12450
|
+
createdAt: ctx.at
|
|
12451
|
+
}]);
|
|
12452
|
+
state.replaceWorkInputs(e.workId, state.edgesInto(e.nodeId).filter((edge) => edge.kind === "data" && edge.pinnedWorkId).map((edge) => ({ workId: e.workId, upstreamNodeId: edge.fromNodeId, upstreamWorkId: edge.pinnedWorkId })));
|
|
12453
|
+
state.replaceRequirements(e.nodeId, state.requirementsOf(e.nodeId).map((r) => ({ ...r, latestReviewId: null })));
|
|
12454
|
+
state.updateNode(e.nodeId, { latestWorkId: e.workId, latestProposedWorkId: e.workId, updatedAt: ctx.at });
|
|
12455
|
+
}
|
|
12456
|
+
};
|
|
12392
12457
|
workKill = {
|
|
12393
12458
|
name: "work/kill",
|
|
12394
12459
|
kind: "work.kill",
|
|
@@ -12810,7 +12875,7 @@ var init_review = __esm({
|
|
|
12810
12875
|
}
|
|
12811
12876
|
},
|
|
12812
12877
|
async effect(e, ctx) {
|
|
12813
|
-
if (
|
|
12878
|
+
if (!await ctx.canDispatchReview(e.workorderId, e.reviewId)) return;
|
|
12814
12879
|
if (e.reviewerIsAgent) await ctx.dispatchReview(e.reviewId, e.nodeId, e.reviewerActorId);
|
|
12815
12880
|
else await ctx.notify([e.reviewerActorId], { kind: "review-requested", reviewId: e.reviewId });
|
|
12816
12881
|
}
|
|
@@ -13089,6 +13154,8 @@ var init_handlers = __esm({
|
|
|
13089
13154
|
erase(planUpdateFields),
|
|
13090
13155
|
/* ── 节点级 work(7)── */
|
|
13091
13156
|
erase(workCreate),
|
|
13157
|
+
erase(workRedispatch),
|
|
13158
|
+
erase(workSnapshotRecorded),
|
|
13092
13159
|
erase(workStart),
|
|
13093
13160
|
erase(workSubmitOutput),
|
|
13094
13161
|
erase(workContentEdited),
|
|
@@ -13663,11 +13730,15 @@ var init_bus = __esm({
|
|
|
13663
13730
|
// ★ 注入 workorderId:派发侧读模型没追上时,据它指名重读那张工单(见 EngineIO.dispatchWork 注释)。
|
|
13664
13731
|
dispatchWork: (id, opts) => this.io.dispatchWork(id, { ...opts, workorderId: record8.workorderId }),
|
|
13665
13732
|
dispatchReview: (id, nodeId, reviewerActorId) => this.io.dispatchReview(id, nodeId, reviewerActorId),
|
|
13666
|
-
canDispatchWork: async (workorderId, workId) => {
|
|
13733
|
+
canDispatchWork: async (workorderId, workId, opts) => {
|
|
13667
13734
|
const snap = await this.store.transaction((tx) => tx.loadWorkorder(workorderId));
|
|
13668
13735
|
if (!snap || snap.workorder.dispatchPaused) return false;
|
|
13669
13736
|
const work = snap.works.find((row) => row.id === workId);
|
|
13670
|
-
|
|
13737
|
+
if (opts?.neverStarted && work) {
|
|
13738
|
+
const node2 = snap.nodes.find((row) => row.id === work.nodeId);
|
|
13739
|
+
if (work.startedAt || work.sessionRef || !node2 || node2.cancelledAt || node2.latestWorkId !== workId) return false;
|
|
13740
|
+
}
|
|
13741
|
+
return work?.status === "running" && !work.deadAt && !work.cancelledAt && !work.endedAt;
|
|
13671
13742
|
},
|
|
13672
13743
|
canDispatchReview: async (workorderId, reviewId) => {
|
|
13673
13744
|
const snap = await this.store.transaction((tx) => tx.loadWorkorder(workorderId));
|
|
@@ -19708,6 +19779,39 @@ var init_kernel_bridge = __esm({
|
|
|
19708
19779
|
throw new KernelError("\u5185\u5BB9\u5DF2\u88AB\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u540E\u518D\u4FDD\u5B58", "content-conflict");
|
|
19709
19780
|
}
|
|
19710
19781
|
}
|
|
19782
|
+
/** 暂停时保存人工完整稿:不借 work.create 开运行,不改暂停位,验收/下游调度留待恢复。 */
|
|
19783
|
+
async recordPausedRevision(args) {
|
|
19784
|
+
if (!args.actor.startsWith("actor:human:")) {
|
|
19785
|
+
throw new KernelError("\u6682\u505C\u671F\u95F4\u5F55\u5165\u5B8C\u6574\u7A3F\u4EC5\u9650\u4EBA\u7C7B\u64CD\u4F5C", "forbidden");
|
|
19786
|
+
}
|
|
19787
|
+
await this.refreshTracked();
|
|
19788
|
+
const wid = this.wo(args.artifactId);
|
|
19789
|
+
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
19790
|
+
const node2 = snap?.nodes.find((n) => n.id === args.artifactId);
|
|
19791
|
+
if (!snap?.workorder.dispatchPaused || !node2 || node2.cancelledAt) {
|
|
19792
|
+
throw new KernelError("\u5DE5\u5355\u6216\u8282\u70B9\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u4FDD\u5B58", "revision-conflict");
|
|
19793
|
+
}
|
|
19794
|
+
const workId = deriveWorkId(node2.id, snap.works.filter((w2) => w2.nodeId === node2.id).length + 1);
|
|
19795
|
+
await this.commit({
|
|
19796
|
+
companyId: "",
|
|
19797
|
+
workorderId: wid,
|
|
19798
|
+
actorId: args.actor,
|
|
19799
|
+
event: {
|
|
19800
|
+
kind: "work.snapshot_recorded",
|
|
19801
|
+
workorderId: wid,
|
|
19802
|
+
nodeId: node2.id,
|
|
19803
|
+
workId,
|
|
19804
|
+
expectedLatestWorkId: node2.latestWorkId,
|
|
19805
|
+
contentRef: args.contentRef,
|
|
19806
|
+
conclusion: args.note
|
|
19807
|
+
}
|
|
19808
|
+
});
|
|
19809
|
+
const after = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
19810
|
+
if (!after?.artifacts.some((a) => a.workId === workId && a.contentRef === args.contentRef)) {
|
|
19811
|
+
throw new KernelError("\u4FDD\u5B58\u672A\u843D\u8D26\uFF1A\u8282\u70B9\u5DF2\u88AB\u4FEE\u6539\u6216\u5DE5\u5355\u5DF2\u6062\u590D\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5", "revision-conflict");
|
|
19812
|
+
}
|
|
19813
|
+
return this.model.revisions.get(workId);
|
|
19814
|
+
}
|
|
19711
19815
|
async proposeRevision(args) {
|
|
19712
19816
|
await this.refreshTracked();
|
|
19713
19817
|
const wid = this.wo(args.artifactId);
|
|
@@ -29417,11 +29521,22 @@ async function sweepStaleChatTurns(deps) {
|
|
|
29417
29521
|
deps.log?.(
|
|
29418
29522
|
`[chat-turn-sweep] \u8F6E\u6B21 ${turn.id}\uFF08\u4F1A\u8BDD ${turn.chatSessionId}\uFF09\u6536\u53E3\u4E3A ${outcome.status}\uFF08${outcome.reason}\uFF09\uFF0C\u4F1A\u8BDD\u69FD\u5DF2\u91CA\u653E`
|
|
29419
29523
|
);
|
|
29524
|
+
const settledStatus = outcome.settled ? outcome.status : void 0;
|
|
29525
|
+
if (deps.onTurnSettled && settledStatus && settledStatus !== "succeeded") {
|
|
29526
|
+
await deps.onTurnSettled({
|
|
29527
|
+
chatSessionId: turn.chatSessionId,
|
|
29528
|
+
turnId: turn.id,
|
|
29529
|
+
status: settledStatus,
|
|
29530
|
+
reason: outcome.reason ?? `\u8F6E\u6B21\u6536\u53E3\u4E3A ${settledStatus}`
|
|
29531
|
+
}).catch((e) => {
|
|
29532
|
+
deps.log?.(`[chat-turn-sweep] \u8F6E\u6B21 ${turn.id} \u6536\u53E3\u540E\u7684\u56DE\u8C03\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u62CD\uFF09: ${String(e)}`);
|
|
29533
|
+
});
|
|
29534
|
+
}
|
|
29420
29535
|
}
|
|
29421
29536
|
return settled;
|
|
29422
29537
|
}
|
|
29423
29538
|
async function sweepStaleChatTurnsAcrossCompanies(deps) {
|
|
29424
|
-
const { listCompanies, storesFor, onCompanyError, log: log3, ...rest } = deps;
|
|
29539
|
+
const { listCompanies, storesFor, onCompanyError, log: log3, onTurnSettled, ...rest } = deps;
|
|
29425
29540
|
const settled = [];
|
|
29426
29541
|
for (const companyId of await listCompanies()) {
|
|
29427
29542
|
try {
|
|
@@ -29431,7 +29546,9 @@ async function sweepStaleChatTurnsAcrossCompanies(deps) {
|
|
|
29431
29546
|
turns: stores.turns,
|
|
29432
29547
|
...stores.assistantRowStatus ? { assistantRowStatus: stores.assistantRowStatus } : {},
|
|
29433
29548
|
...stores.settleAssistantRow ? { settleAssistantRow: stores.settleAssistantRow } : {},
|
|
29434
|
-
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {}
|
|
29549
|
+
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
|
|
29550
|
+
// 把这一家的 companyId 补进去再往外报——下游(委派台账)按公司分 store,少了它就查错库。
|
|
29551
|
+
...onTurnSettled ? { onTurnSettled: (info) => onTurnSettled({ ...info, ...companyId ? { companyId } : {} }) } : {}
|
|
29435
29552
|
});
|
|
29436
29553
|
settled.push(...outcomes);
|
|
29437
29554
|
} catch (err) {
|
|
@@ -159619,7 +159736,7 @@ function normalizedOpToV3Op(op, kind) {
|
|
|
159619
159736
|
if (op === "set_text") return "set_text";
|
|
159620
159737
|
if (op === "patch_input") return "patch_input";
|
|
159621
159738
|
if (op === "set_status") {
|
|
159622
|
-
return
|
|
159739
|
+
return "completed";
|
|
159623
159740
|
}
|
|
159624
159741
|
return void 0;
|
|
159625
159742
|
}
|
|
@@ -159803,7 +159920,7 @@ var init_live_chat = __esm({
|
|
|
159803
159920
|
if (!turn.items) return;
|
|
159804
159921
|
const itemId = outcome.itemId;
|
|
159805
159922
|
const itemType = normalizedKindToItemType(event.kind);
|
|
159806
|
-
|
|
159923
|
+
let operation = normalizedOpToV3Op(event.opShape.op, event.kind);
|
|
159807
159924
|
if (!operation) return;
|
|
159808
159925
|
const perItem = turn.v3Items.get(itemId) ?? { textOffset: 0, started: false };
|
|
159809
159926
|
const isTextual2 = event.kind === "text" || event.kind === "thinking";
|
|
@@ -159839,6 +159956,7 @@ var init_live_chat = __esm({
|
|
|
159839
159956
|
payload = { input: opShape.input };
|
|
159840
159957
|
} else if (opShape.op === "set_status") {
|
|
159841
159958
|
if (opShape.status === "streaming") return;
|
|
159959
|
+
operation = opShape.status === "completed" ? "completed" : "failed";
|
|
159842
159960
|
}
|
|
159843
159961
|
turn.v3Items.set(itemId, perItem);
|
|
159844
159962
|
const frame = {
|
|
@@ -159969,6 +160087,24 @@ var init_live_chat = __esm({
|
|
|
159969
160087
|
this.publishV3(turn, this.assignV3Seq(turn, frame));
|
|
159970
160088
|
return true;
|
|
159971
160089
|
}
|
|
160090
|
+
/** 恢复路在 DB 收口旧 item 后,将同一身份/版本的终态发给已挂上的观众。 */
|
|
160091
|
+
publishItemStatus(chatSessionId, item) {
|
|
160092
|
+
const turn = this.turns.get(chatSessionId);
|
|
160093
|
+
if (!turn || turn.status !== "running" || item.sessionId !== chatSessionId || item.status === "streaming") return false;
|
|
160094
|
+
if (turn.items?.turnId !== item.turnId) return false;
|
|
160095
|
+
this.flushPendingV3(turn);
|
|
160096
|
+
this.publishV3(turn, this.assignV3Seq(turn, {
|
|
160097
|
+
protocolVersion: 3,
|
|
160098
|
+
streamId: turn.liveStreamId,
|
|
160099
|
+
turnId: item.turnId,
|
|
160100
|
+
itemId: item.id,
|
|
160101
|
+
itemType: normalizedKindToItemType(item.kind),
|
|
160102
|
+
operation: item.status === "completed" ? "completed" : "failed",
|
|
160103
|
+
itemVersion: item.version,
|
|
160104
|
+
...item.ord !== null ? { ord: item.ord } : {}
|
|
160105
|
+
}));
|
|
160106
|
+
return true;
|
|
160107
|
+
}
|
|
159972
160108
|
emitV3TurnTerminal(turn, status) {
|
|
159973
160109
|
if (status !== "done" && status !== "error") return;
|
|
159974
160110
|
const operation = status === "done" ? "turn_completed" : "turn_failed";
|
|
@@ -195686,6 +195822,7 @@ var init_service3 = __esm({
|
|
|
195686
195822
|
const store = await this.options.resolveStore(companyId).catch(() => null);
|
|
195687
195823
|
await store?.patch(record8.id, { childRunId: anchor }).catch(() => null);
|
|
195688
195824
|
}
|
|
195825
|
+
await this.bindChildRunToPlaceholder(record8, companyId, claim.turn.assistantRunId ?? void 0);
|
|
195689
195826
|
await notDispatched("resumed-existing-run");
|
|
195690
195827
|
return;
|
|
195691
195828
|
}
|
|
@@ -195719,6 +195856,7 @@ var init_service3 = __esm({
|
|
|
195719
195856
|
const store = await this.options.resolveStore(companyId);
|
|
195720
195857
|
await store.patch(record8.id, { childRunId: anchor }).catch(() => null);
|
|
195721
195858
|
}
|
|
195859
|
+
await this.bindChildRunToPlaceholder(record8, companyId, dispatched.runId);
|
|
195722
195860
|
this.track(this.settleRound(record8, dispatched, companyId, () => output, turnId, turns));
|
|
195723
195861
|
} catch (error2) {
|
|
195724
195862
|
const failedAt = this.now();
|
|
@@ -195835,6 +195973,29 @@ var init_service3 = __esm({
|
|
|
195835
195973
|
* 委派台账是同一类账,只是多一层——它还得回流。所以这里只做两件:把行拍成 `failed/interrupted`、
|
|
195836
195974
|
* 然后走同一个 {@link notifySettled}。已经是终态的行原样返回(幂等,重复扫不重复回流)。
|
|
195837
195975
|
*/
|
|
195976
|
+
/**
|
|
195977
|
+
* 把这一轮的 run 锚点写到子会话那条**回答占位行**上。
|
|
195978
|
+
*
|
|
195979
|
+
* 为什么非它不可:serve 重启后,daemon 的 hello 对账与会话进程自报身份**共用**
|
|
195980
|
+
* `planChatTurnRecovery`,而那个函数第一行就是 `if (!row.runId) continue` —— 占位行没有 runId,
|
|
195981
|
+
* 这条子会话就永远配不出恢复计划,答 `unknown`,然后被 hub evict(杀掉)。
|
|
195982
|
+
* 轮次是第几轮按 `roundSummaries.length + 1` 算,与 `seedChildRoundRows` 那两处口径逐字一致。
|
|
195983
|
+
*
|
|
195984
|
+
* 失败只记日志:这是加固,不是这一轮派发成立的前提。
|
|
195985
|
+
*/
|
|
195986
|
+
async bindChildRunToPlaceholder(record8, companyId, runId) {
|
|
195987
|
+
if (!runId) return;
|
|
195988
|
+
const round = record8.roundSummaries.length + 1;
|
|
195989
|
+
const { placeholderId } = childRoundMessageIds(record8.childSessionId, round);
|
|
195990
|
+
try {
|
|
195991
|
+
const chatStore = await this.options.resolveChatSessions(companyId);
|
|
195992
|
+
await chatStore.updateMessage(placeholderId, { runId });
|
|
195993
|
+
} catch (err) {
|
|
195994
|
+
console.warn(
|
|
195995
|
+
`[delegation] \u5B50\u4F1A\u8BDD\u5360\u4F4D\u884C\u5199 runId \u5931\u8D25\uFF08${record8.id} child=${record8.childSessionId} round=${round} run=${runId}\uFF09: ${String(err)}\u2014\u2014\u91CD\u542F\u540E\u8FD9\u6761\u5B50\u4F1A\u8BDD\u4F1A\u88AB\u5224 unknown \u5E76 evict`
|
|
195996
|
+
);
|
|
195997
|
+
}
|
|
195998
|
+
}
|
|
195838
195999
|
async settleFromSweep(childSessionId, reason, companyId) {
|
|
195839
196000
|
const store = await this.options.resolveStore(companyId).catch(() => null);
|
|
195840
196001
|
if (!store) return null;
|
|
@@ -196957,12 +197118,15 @@ var init_dispatch_adapter = __esm({
|
|
|
196957
197118
|
// ../server/src/domains/delegations/sweep-hook.ts
|
|
196958
197119
|
function delegationSweepHook(deps) {
|
|
196959
197120
|
const log3 = deps.log ?? ((m2) => console.warn(m2));
|
|
196960
|
-
return async (chatSessionId, reason) => {
|
|
196961
|
-
|
|
196962
|
-
|
|
196963
|
-
|
|
196964
|
-
|
|
196965
|
-
|
|
197121
|
+
return async (chatSessionId, reason, _messageId, knownCompanyId) => {
|
|
197122
|
+
let companyId = knownCompanyId;
|
|
197123
|
+
if (companyId === void 0) {
|
|
197124
|
+
const session = await deps.sessions.getSession(chatSessionId).catch((err) => {
|
|
197125
|
+
log3(`[chat-sweep] \u4F1A\u8BDD ${chatSessionId} \u7684\u516C\u53F8\u5F52\u5C5E\u8BFB\u4E0D\u5230\uFF08${String(err)}\uFF09\u2014\u2014\u6309\u9ED8\u8BA4\u516C\u53F8\u8BD5\u4E00\u6B21`);
|
|
197126
|
+
return null;
|
|
197127
|
+
});
|
|
197128
|
+
companyId = session?.companyId ?? void 0;
|
|
197129
|
+
}
|
|
196966
197130
|
await deps.settle(chatSessionId, reason, companyId).catch((err) => {
|
|
196967
197131
|
log3(`[chat-sweep] \u59D4\u6D3E\u53F0\u8D26\u6536\u655B\u5931\u8D25\uFF08\u4F1A\u8BDD ${chatSessionId} \u516C\u53F8 ${companyId ?? "\u9ED8\u8BA4"}\uFF09: ${String(err)}`);
|
|
196968
197132
|
});
|
|
@@ -214524,7 +214688,7 @@ async function prepareDroppedDispatch(work, deps) {
|
|
|
214524
214688
|
const node2 = kernel.model.artifacts.get(work.nodeId);
|
|
214525
214689
|
const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(work.workorderId));
|
|
214526
214690
|
const current = snap?.works.find((w2) => w2.id === work.workId);
|
|
214527
|
-
if (!snap || !current || current.status !== "running" || current.startedAt || current.sessionRef || current.endedAt || !snap.nodes.some((n) => n.id === work.nodeId && n.latestWorkId === current.id && !n.cancelledAt) || !node2 || node2.workspace !== work.workorderId || lifecycleOf(kernel.model, node2.id) !== "active" ||
|
|
214691
|
+
if (!snap || snap.workorder.dispatchPaused || !current || current.status !== "running" || current.deadAt || current.cancelledAt || current.startedAt || current.sessionRef || current.endedAt || !snap.nodes.some((n) => n.id === work.nodeId && n.latestWorkId === current.id && !n.cancelledAt) || !node2 || node2.workspace !== work.workorderId || lifecycleOf(kernel.model, node2.id) !== "active" || isHeld(kernel.model, node2.id)) return false;
|
|
214528
214692
|
if (snap.issues.some((i) => i.kind === "escalation" && i.aboutNodeId === node2.id && !i.resolvedAt && i.intent !== "advisory")) return false;
|
|
214529
214693
|
if (!work.assigneeActorId) return false;
|
|
214530
214694
|
const binding = await deps.bindingFor(work.assigneeActorId);
|
|
@@ -216135,13 +216299,14 @@ async function planChatTurnRecovery(deps) {
|
|
|
216135
216299
|
const plans = [];
|
|
216136
216300
|
for (const row of rows) {
|
|
216137
216301
|
if (!row.runId) continue;
|
|
216138
|
-
const run = await deps.getRun(row.runId)
|
|
216302
|
+
const run = await deps.getRun(row.runId);
|
|
216139
216303
|
if (!run || run.status !== "running" || run.action !== "chat" || !run.artifactId) continue;
|
|
216140
216304
|
const active = byArtifact.get(run.artifactId);
|
|
216141
216305
|
if (!active || deps.isWired(run.artifactId)) continue;
|
|
216142
216306
|
const meta = asObj3(run.metadata);
|
|
216143
216307
|
const runtimeSessionId = typeof meta?.runtimeSessionId === "string" ? meta.runtimeSessionId : run.jobKey ?? void 0;
|
|
216144
|
-
|
|
216308
|
+
const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
|
|
216309
|
+
const plan = {
|
|
216145
216310
|
dispatchId: active.dispatchId,
|
|
216146
216311
|
chatSessionId: row.sessionId,
|
|
216147
216312
|
assistantMsgId: row.id,
|
|
@@ -216152,18 +216317,42 @@ async function planChatTurnRecovery(deps) {
|
|
|
216152
216317
|
...runtimeSessionId ? { runtimeSessionId } : {},
|
|
216153
216318
|
startedAt: run.startedAt,
|
|
216154
216319
|
seedContent: row.content,
|
|
216155
|
-
seedParts: partsOf(row.parts)
|
|
216156
|
-
|
|
216320
|
+
seedParts: partsOf(row.parts),
|
|
216321
|
+
...turn ? { turnId: turn.id } : {}
|
|
216322
|
+
};
|
|
216323
|
+
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
216324
|
+
deps.onSettled?.({ plan, turn });
|
|
216325
|
+
continue;
|
|
216326
|
+
}
|
|
216327
|
+
plans.push(plan);
|
|
216157
216328
|
}
|
|
216158
216329
|
return plans;
|
|
216159
216330
|
}
|
|
216331
|
+
async function finishPersistedRecoveryItems(items, sessionId, turnId, status, log3) {
|
|
216332
|
+
if (!items || !chatItemsDoubleWriteEnabled()) return [];
|
|
216333
|
+
try {
|
|
216334
|
+
const rows = await items.listItemsByTurn(sessionId, turnId);
|
|
216335
|
+
let version2 = await items.sessionVersionCursor(sessionId);
|
|
216336
|
+
const closed = [];
|
|
216337
|
+
for (const row of rows) {
|
|
216338
|
+
if (row.status !== "streaming") continue;
|
|
216339
|
+
const updated = await items.applyOp(row.id, { op: "set_status", status }, { version: ++version2 });
|
|
216340
|
+
if (!updated) throw new Error(`item ${row.id} disappeared`);
|
|
216341
|
+
closed.push(updated);
|
|
216342
|
+
}
|
|
216343
|
+
return closed;
|
|
216344
|
+
} catch (err) {
|
|
216345
|
+
log3(`[chat-recovery] items \u6536\u5C3E\u5931\u8D25\uFF1Asession=${sessionId} turn=${turnId} ${String(err)}`);
|
|
216346
|
+
return [];
|
|
216347
|
+
}
|
|
216348
|
+
}
|
|
216160
216349
|
function wireRecoveredChatTurn(deps) {
|
|
216161
216350
|
const { plan, handle, liveChat, chatStore, trace } = deps;
|
|
216162
216351
|
const log3 = deps.log ?? (() => void 0);
|
|
216163
216352
|
const itemLedger = new ChatItemLedger({
|
|
216164
216353
|
...deps.items ? { items: deps.items } : {},
|
|
216165
216354
|
sessionId: plan.chatSessionId,
|
|
216166
|
-
turnId: deps.itemsTurnId ?? fallbackTurnId(plan.runId),
|
|
216355
|
+
turnId: deps.itemsTurnId ?? plan.turnId ?? fallbackTurnId(plan.runId),
|
|
216167
216356
|
runId: plan.runId,
|
|
216168
216357
|
messageId: plan.assistantMsgId,
|
|
216169
216358
|
log: log3
|
|
@@ -216329,6 +216518,14 @@ function wireRecoveredChatTurn(deps) {
|
|
|
216329
216518
|
normalizedSource.finishTurn(failed ? "failed" : "completed");
|
|
216330
216519
|
itemLedger.finish(failed ? "failed" : "completed");
|
|
216331
216520
|
await itemLedger.drain();
|
|
216521
|
+
const closedItems = await finishPersistedRecoveryItems(
|
|
216522
|
+
deps.items,
|
|
216523
|
+
plan.chatSessionId,
|
|
216524
|
+
itemLedger.turnId,
|
|
216525
|
+
failed ? "failed" : "completed",
|
|
216526
|
+
log3
|
|
216527
|
+
);
|
|
216528
|
+
for (const item of closedItems) liveChat.publishItemStatus(plan.chatSessionId, item);
|
|
216332
216529
|
await chatStore.updateMessage(currentMsgId, {
|
|
216333
216530
|
content: itemLedger.projectContent(),
|
|
216334
216531
|
status: failed ? "error" : "done",
|
|
@@ -216426,6 +216623,29 @@ async function reconcileChatExitFrame(deps) {
|
|
|
216426
216623
|
const meta = asObj3(run.metadata);
|
|
216427
216624
|
if (meta?.dispatchId !== deps.dispatchId) continue;
|
|
216428
216625
|
if (deps.ownedRunId?.(row.sessionId) === row.runId) return false;
|
|
216626
|
+
const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
|
|
216627
|
+
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
216628
|
+
await reconcileSettledChatTurn({
|
|
216629
|
+
plan: {
|
|
216630
|
+
dispatchId: deps.dispatchId,
|
|
216631
|
+
chatSessionId: row.sessionId,
|
|
216632
|
+
assistantMsgId: row.id,
|
|
216633
|
+
runId: run.id,
|
|
216634
|
+
artifactId: run.artifactId,
|
|
216635
|
+
actor: run.actorId,
|
|
216636
|
+
startedAt: run.startedAt,
|
|
216637
|
+
seedContent: row.content,
|
|
216638
|
+
seedParts: partsOf(row.parts),
|
|
216639
|
+
turnId: turn.id
|
|
216640
|
+
},
|
|
216641
|
+
turn,
|
|
216642
|
+
chatStore: deps.chatStore,
|
|
216643
|
+
trace: deps.trace,
|
|
216644
|
+
...deps.items ? { items: deps.items } : {},
|
|
216645
|
+
...deps.log ? { log: deps.log } : {}
|
|
216646
|
+
});
|
|
216647
|
+
return true;
|
|
216648
|
+
}
|
|
216429
216649
|
const { info } = deps;
|
|
216430
216650
|
const failed = exitFailed(info);
|
|
216431
216651
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -216467,6 +216687,13 @@ async function reconcileChatExitFrame(deps) {
|
|
|
216467
216687
|
normalizer.turnFinished(failed ? "failed" : "completed");
|
|
216468
216688
|
itemLedger.finish(failed ? "failed" : "completed");
|
|
216469
216689
|
await itemLedger.drain();
|
|
216690
|
+
await finishPersistedRecoveryItems(
|
|
216691
|
+
deps.items,
|
|
216692
|
+
row.sessionId,
|
|
216693
|
+
itemLedger.turnId,
|
|
216694
|
+
failed ? "failed" : "completed",
|
|
216695
|
+
deps.log ?? (() => void 0)
|
|
216696
|
+
);
|
|
216470
216697
|
await deps.chatStore.updateMessage(row.id, {
|
|
216471
216698
|
content: itemLedger.projectContent(),
|
|
216472
216699
|
status: failed ? "error" : "done",
|
|
@@ -216505,6 +216732,156 @@ async function reconcileChatExitFrame(deps) {
|
|
|
216505
216732
|
}
|
|
216506
216733
|
return false;
|
|
216507
216734
|
}
|
|
216735
|
+
async function planChatTurnRecoveryAcrossCompanies(deps) {
|
|
216736
|
+
const groups = [];
|
|
216737
|
+
const settled = [];
|
|
216738
|
+
const skipped = [];
|
|
216739
|
+
let scanned = 0;
|
|
216740
|
+
for (const companyId of await deps.listCompanies()) {
|
|
216741
|
+
try {
|
|
216742
|
+
const stores = await deps.storesFor(companyId);
|
|
216743
|
+
const companySettled = [];
|
|
216744
|
+
const plans = await planChatTurnRecovery({
|
|
216745
|
+
activeSessions: deps.activeSessions,
|
|
216746
|
+
listRunningAssistantMessages: stores.listRunningAssistantMessages,
|
|
216747
|
+
getRun: deps.getRun,
|
|
216748
|
+
isWired: deps.isWired,
|
|
216749
|
+
...stores.getTurnForRun ? { getTurnForRun: stores.getTurnForRun } : {},
|
|
216750
|
+
onSettled: (hit) => companySettled.push(hit)
|
|
216751
|
+
});
|
|
216752
|
+
scanned += 1;
|
|
216753
|
+
settled.push(...companySettled.map((hit) => ({ ...hit, companyId, stores })));
|
|
216754
|
+
if (plans.length) groups.push({ companyId, stores, plans });
|
|
216755
|
+
if (deps.stopOnFirstHit && (plans.length || companySettled.length)) break;
|
|
216756
|
+
} catch (err) {
|
|
216757
|
+
skipped.push({ companyId, error: String(err) });
|
|
216758
|
+
deps.onCompanyError?.(companyId, err);
|
|
216759
|
+
}
|
|
216760
|
+
}
|
|
216761
|
+
return { groups, settled, scanned, skipped };
|
|
216762
|
+
}
|
|
216763
|
+
async function sweepDanglingChatTurnsAcrossCompanies(deps) {
|
|
216764
|
+
const { listCompanies, storesFor, onCompanyError, onSessionClosed, log: log3, ...rest } = deps;
|
|
216765
|
+
const closed = [];
|
|
216766
|
+
for (const companyId of await listCompanies()) {
|
|
216767
|
+
try {
|
|
216768
|
+
const stores = await storesFor(companyId);
|
|
216769
|
+
const ids2 = await sweepDanglingChatTurns({
|
|
216770
|
+
...rest,
|
|
216771
|
+
listRunningAssistantMessages: stores.listRunningAssistantMessages,
|
|
216772
|
+
listMessages: (sid) => stores.chatStore.listMessages(sid),
|
|
216773
|
+
updateMessage: (id, patch) => stores.chatStore.updateMessage(id, patch),
|
|
216774
|
+
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
|
|
216775
|
+
...onSessionClosed ? { onSessionClosed: (sid, reason, messageId) => onSessionClosed(sid, reason, messageId, companyId) } : {}
|
|
216776
|
+
});
|
|
216777
|
+
closed.push(...ids2);
|
|
216778
|
+
} catch (err) {
|
|
216779
|
+
onCompanyError?.(companyId, err);
|
|
216780
|
+
}
|
|
216781
|
+
}
|
|
216782
|
+
return closed;
|
|
216783
|
+
}
|
|
216784
|
+
async function reconcileChatExitFrameAcrossCompanies(deps) {
|
|
216785
|
+
const { listCompanies, storesFor, onCompanyError, log: log3, ...rest } = deps;
|
|
216786
|
+
for (const companyId of await listCompanies()) {
|
|
216787
|
+
try {
|
|
216788
|
+
const stores = await storesFor(companyId);
|
|
216789
|
+
const handled2 = await reconcileChatExitFrame({
|
|
216790
|
+
...rest,
|
|
216791
|
+
listRunningAssistantMessages: stores.listRunningAssistantMessages,
|
|
216792
|
+
chatStore: stores.chatStore,
|
|
216793
|
+
...stores.getTurnForRun ? { getTurnForRun: stores.getTurnForRun } : {},
|
|
216794
|
+
...stores.items ? { items: stores.items } : {},
|
|
216795
|
+
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {}
|
|
216796
|
+
});
|
|
216797
|
+
if (handled2) return true;
|
|
216798
|
+
} catch (err) {
|
|
216799
|
+
onCompanyError?.(companyId, err);
|
|
216800
|
+
}
|
|
216801
|
+
}
|
|
216802
|
+
return false;
|
|
216803
|
+
}
|
|
216804
|
+
async function reconcileSettledChatTurn(deps) {
|
|
216805
|
+
const { plan, turn } = deps;
|
|
216806
|
+
if (turn.status === "reserved" || turn.status === "running") return;
|
|
216807
|
+
const succeeded = turn.status === "succeeded";
|
|
216808
|
+
const completedAt = turn.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
216809
|
+
const turnId = deps.items ? await resolveChatItemsTurnId(deps.items, plan.assistantMsgId, turn.id) : turn.id;
|
|
216810
|
+
await finishPersistedRecoveryItems(
|
|
216811
|
+
deps.items,
|
|
216812
|
+
plan.chatSessionId,
|
|
216813
|
+
turnId,
|
|
216814
|
+
succeeded ? "completed" : "failed",
|
|
216815
|
+
deps.log ?? (() => void 0)
|
|
216816
|
+
);
|
|
216817
|
+
await deps.chatStore.updateMessage(plan.assistantMsgId, {
|
|
216818
|
+
status: succeeded ? "done" : "error",
|
|
216819
|
+
completedAt
|
|
216820
|
+
});
|
|
216821
|
+
const run = await deps.trace.getRun(plan.runId);
|
|
216822
|
+
if (run?.status === "running") {
|
|
216823
|
+
await deps.trace.updateRun(plan.runId, {
|
|
216824
|
+
status: turn.status,
|
|
216825
|
+
endedAt: completedAt
|
|
216826
|
+
});
|
|
216827
|
+
}
|
|
216828
|
+
}
|
|
216829
|
+
async function recoverChatTurnsAcrossCompanies(deps) {
|
|
216830
|
+
const result = await planChatTurnRecoveryAcrossCompanies(deps);
|
|
216831
|
+
const recovered = [];
|
|
216832
|
+
const log3 = deps.log ?? (() => void 0);
|
|
216833
|
+
const retire = async (hit) => {
|
|
216834
|
+
const { plan, turn, companyId, stores } = hit;
|
|
216835
|
+
log3(`[chat-claim] company=${companyId ?? "<default>"} session=${plan.chatSessionId} run=${plan.runId} dispatch=${plan.dispatchId} \u7ED3\u8BBA=settled\uFF08\u4F9D\u636E\uFF1A\u540C\u516C\u53F8\u8F6E\u6B21 ${turn.id} \u5DF2 ${turn.status}\uFF0CcompletedAt=${turn.completedAt ?? "-"}\uFF09`);
|
|
216836
|
+
deps.evict(plan.dispatchId);
|
|
216837
|
+
try {
|
|
216838
|
+
await reconcileSettledChatTurn({
|
|
216839
|
+
plan,
|
|
216840
|
+
turn,
|
|
216841
|
+
chatStore: stores.chatStore,
|
|
216842
|
+
trace: deps.trace,
|
|
216843
|
+
...stores.items ? { items: stores.items } : {},
|
|
216844
|
+
log: log3
|
|
216845
|
+
});
|
|
216846
|
+
} catch (err) {
|
|
216847
|
+
deps.onCompanyError?.(companyId, err);
|
|
216848
|
+
}
|
|
216849
|
+
};
|
|
216850
|
+
for (const hit of result.settled) await retire(hit);
|
|
216851
|
+
for (const { companyId, stores, plans } of result.groups) {
|
|
216852
|
+
for (const plan of plans) {
|
|
216853
|
+
try {
|
|
216854
|
+
const itemsTurnId = stores.items ? await resolveChatItemsTurnId(stores.items, plan.assistantMsgId, plan.turnId ?? fallbackTurnId(plan.runId)) : plan.turnId ?? fallbackTurnId(plan.runId);
|
|
216855
|
+
const turn = await stores.getTurnForRun?.(plan.chatSessionId, plan.runId);
|
|
216856
|
+
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
216857
|
+
const hit = { companyId, stores, plan, turn };
|
|
216858
|
+
result.settled.push(hit);
|
|
216859
|
+
await retire(hit);
|
|
216860
|
+
continue;
|
|
216861
|
+
}
|
|
216862
|
+
const handle = deps.recover(plan);
|
|
216863
|
+
const { done } = wireRecoveredChatTurn({
|
|
216864
|
+
plan,
|
|
216865
|
+
handle,
|
|
216866
|
+
liveChat: deps.liveChat,
|
|
216867
|
+
chatStore: stores.chatStore,
|
|
216868
|
+
trace: deps.trace,
|
|
216869
|
+
register: () => deps.register(plan, handle),
|
|
216870
|
+
unregister: () => deps.unregister(plan),
|
|
216871
|
+
...deps.traceHealth ? { traceHealth: deps.traceHealth } : {},
|
|
216872
|
+
...stores.items ? { items: stores.items, itemsTurnId } : {},
|
|
216873
|
+
log: log3
|
|
216874
|
+
});
|
|
216875
|
+
recovered.push({ companyId, plan, done });
|
|
216876
|
+
log3(`[chat-recovery] \u91CD\u6302 chat \u8F6E\uFF1Acompany=${companyId ?? "<default>"} session=${plan.chatSessionId} run=${plan.runId} dispatch=${plan.dispatchId}`);
|
|
216877
|
+
} catch (err) {
|
|
216878
|
+
result.skipped.push({ companyId, error: String(err) });
|
|
216879
|
+
deps.onCompanyError?.(companyId, err);
|
|
216880
|
+
}
|
|
216881
|
+
}
|
|
216882
|
+
}
|
|
216883
|
+
return { ...result, recovered };
|
|
216884
|
+
}
|
|
216508
216885
|
var import_node_crypto45, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
|
|
216509
216886
|
var init_chat_recovery = __esm({
|
|
216510
216887
|
"../server/src/chat-recovery.ts"() {
|
|
@@ -225719,7 +226096,7 @@ function reviewSummary(snap, reviews, events) {
|
|
|
225719
226096
|
return { phase: "done", status: `\u300A${name}\u300B\u5BA1\u6838\u901A\u8FC7\u3002`, latest, pending: [] };
|
|
225720
226097
|
}
|
|
225721
226098
|
if (work?.acceptanceState === "rejected" || judgement === "rejected") {
|
|
225722
|
-
return { phase: "done", status:
|
|
226099
|
+
return { phase: "done", status: `\u300A${name}\u300B\u5BA1\u6838\u4E0D\u901A\u8FC7\u3002`, latest, pending: [] };
|
|
225723
226100
|
}
|
|
225724
226101
|
const endedWork = !!(work?.cancelledAt || work?.deadAt);
|
|
225725
226102
|
if (endedWork && latest.every((r) => !!r.cancelledAt && reviewState(r) !== "retry")) {
|
|
@@ -225772,6 +226149,9 @@ var init_workorder_manager = __esm({
|
|
|
225772
226149
|
function isPendingActor(id) {
|
|
225773
226150
|
return typeof id === "string" && id.startsWith("pending:");
|
|
225774
226151
|
}
|
|
226152
|
+
function isAgentActor(id) {
|
|
226153
|
+
return typeof id === "string" && id.startsWith("actor:agent:");
|
|
226154
|
+
}
|
|
225775
226155
|
function isDispatchLayerFailure(detail) {
|
|
225776
226156
|
const reason = detail?.reason;
|
|
225777
226157
|
return typeof reason === "string" && reason.startsWith("dispatch ");
|
|
@@ -226307,6 +226687,22 @@ function buildWorkorderActivity(input) {
|
|
|
226307
226687
|
const created = events.filter((r) => r.event.kind === "review.create" && ids2.has(r.event.reviewId));
|
|
226308
226688
|
const dates = summary.latest.flatMap((r) => [r.createdAt, r.startedAt, r.endedAt, r.decidedAt, r.cancelledAt, r.retryAt].filter((v2) => !!v2));
|
|
226309
226689
|
const pending = summary.pending.filter((r) => r.reviewerActorId.startsWith("actor:human:") && (!input.viewerActorId || r.reviewerActorId === input.viewerActorId));
|
|
226690
|
+
const groups = [...new Set(summary.latest.map((r) => r.reviewGroup))];
|
|
226691
|
+
const reviewNotes = summary.latest.flatMap((r) => {
|
|
226692
|
+
const text5 = r.note?.trim();
|
|
226693
|
+
if (!text5) return [];
|
|
226694
|
+
const rejected = r.verdict === "request_changes";
|
|
226695
|
+
const label = ACTIVITY_COPY.reviewNoteLabel(rejected);
|
|
226696
|
+
return [{
|
|
226697
|
+
id: r.id,
|
|
226698
|
+
// 同一个人担多个评审组时才标组号——单组标了纯属噪声(卡上本来就只有这一个人)。
|
|
226699
|
+
label: groups.length > 1 ? `\u8BC4\u5BA1\u7EC4 ${groups.indexOf(r.reviewGroup) + 1} \xB7 ${label}` : label,
|
|
226700
|
+
text: text5,
|
|
226701
|
+
tone: rejected ? "reject" : "note"
|
|
226702
|
+
}];
|
|
226703
|
+
});
|
|
226704
|
+
const revieweeId = workById.get(workId)?.assigneeActorId;
|
|
226705
|
+
const reviewLine = reviewNotes.length && revieweeId && revieweeId !== reviewerId ? reviewThreadSessionId(revieweeId, reviewerId) : void 0;
|
|
226310
226706
|
visibleReviews.set(cardId, {
|
|
226311
226707
|
id: cardId,
|
|
226312
226708
|
reviewWorkId: workId,
|
|
@@ -226320,8 +226716,13 @@ function buildWorkorderActivity(input) {
|
|
|
226320
226716
|
executorId: reviewerId,
|
|
226321
226717
|
phase: summary.phase,
|
|
226322
226718
|
status: summary.status,
|
|
226323
|
-
|
|
226324
|
-
|
|
226719
|
+
/* 同一人担多个责任组时,展开区留一份**逐组结论**(只有判定词、没有意见正文)——小状态只有
|
|
226720
|
+
一句总判定,说不清哪个组过了哪个组没过。意见正文一律走下面的意见条。 */
|
|
226721
|
+
...summary.latest.length > 1 ? {
|
|
226722
|
+
detail: summary.latest.map((r) => `${nameOf(r.reviewerActorId)}\uFF08\u8BC4\u5BA1\u7EC4 ${groups.indexOf(r.reviewGroup) + 1}\uFF09\uFF1A${reviewLabel(r)}`).join("\n")
|
|
226723
|
+
} : {},
|
|
226724
|
+
...reviewNotes.length ? { reviewNotes } : {},
|
|
226725
|
+
...reviewLine ? { reviewThreadSessionId: reviewLine } : {},
|
|
226325
226726
|
artifacts: artifactsByWork.get(workId) ?? [],
|
|
226326
226727
|
actions: pending.length ? [
|
|
226327
226728
|
{ kind: "review", label: BUTTON.approve, target: first.nodeId, revisionId: workId, verdict: "approve" },
|
|
@@ -226361,7 +226762,7 @@ ${r.verdict === "request_changes" ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u
|
|
|
226361
226762
|
if (issueById.get(issueId)?.kind === "gap") {
|
|
226362
226763
|
d.traceAvailable = true;
|
|
226363
226764
|
d.traceWorkIds = gapOrigins.get(issueId);
|
|
226364
|
-
} else {
|
|
226765
|
+
} else if (!issueId.startsWith(REWORK_ANNOTATION_PREFIX)) {
|
|
226365
226766
|
d.traceWorkIds = issueWorks.get(issueId);
|
|
226366
226767
|
}
|
|
226367
226768
|
}
|
|
@@ -226382,24 +226783,29 @@ ${r.verdict === "request_changes" ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u
|
|
|
226382
226783
|
}
|
|
226383
226784
|
return void 0;
|
|
226384
226785
|
};
|
|
226385
|
-
const cards = drafts.map((d) =>
|
|
226386
|
-
|
|
226387
|
-
|
|
226388
|
-
|
|
226389
|
-
|
|
226390
|
-
|
|
226391
|
-
|
|
226392
|
-
|
|
226393
|
-
|
|
226394
|
-
|
|
226395
|
-
|
|
226396
|
-
|
|
226397
|
-
|
|
226398
|
-
|
|
226399
|
-
|
|
226400
|
-
|
|
226401
|
-
|
|
226402
|
-
|
|
226786
|
+
const cards = drafts.map((d) => {
|
|
226787
|
+
const traced = isAgentActor(d.executorId);
|
|
226788
|
+
return {
|
|
226789
|
+
id: d.id,
|
|
226790
|
+
seq: d.seq,
|
|
226791
|
+
at: d.at,
|
|
226792
|
+
updatedAt: d.updatedAt,
|
|
226793
|
+
...d.nodeId ? { nodeId: d.nodeId, nodeTitle: nodeName(d.nodeId) } : {},
|
|
226794
|
+
executor: ref2(d.executorId),
|
|
226795
|
+
...d.reviewWorkId ? { reviewWorkId: d.reviewWorkId, reviewers: d.reviewerIds?.map(ref2) } : {},
|
|
226796
|
+
...d.handActorId ? { handActor: ref2(d.handActorId) } : {},
|
|
226797
|
+
phase: d.phase,
|
|
226798
|
+
status: d.status,
|
|
226799
|
+
...d.detail ? { detail: d.detail } : {},
|
|
226800
|
+
...d.reviewNotes?.length ? { reviewNotes: d.reviewNotes } : {},
|
|
226801
|
+
...d.reviewThreadSessionId ? { reviewThreadSessionId: d.reviewThreadSessionId } : {},
|
|
226802
|
+
artifacts: d.artifacts,
|
|
226803
|
+
actions: d.actions,
|
|
226804
|
+
...traced && d.traceAvailable ? { traceAvailable: true } : {},
|
|
226805
|
+
...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, ...traced ? { traceAvailable: true } : {} } : {},
|
|
226806
|
+
.../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(traced ? runIdOfCard(d) : void 0)
|
|
226807
|
+
};
|
|
226808
|
+
});
|
|
226403
226809
|
return { workorderId, cards, truncated };
|
|
226404
226810
|
}
|
|
226405
226811
|
var ACTIVITY_EVENT_KINDS, ACTIVITY_EVENT_LIMIT, ACTIVITY_COPY, REWORK_ANNOTATION_PREFIX, ENGINE_RETRY_EXHAUSTED, BUTTON;
|
|
@@ -226465,15 +226871,13 @@ var init_activity2 = __esm({
|
|
|
226465
226871
|
doneAwaitingReview: (name) => `\u300A${name}\u300B\u5DF2\u5B8C\u6210\uFF0C\u7B49\u5F85\u5BA1\u6838\u3002`,
|
|
226466
226872
|
done: (name) => `\u300A${name}\u300B\u5DF2\u5B8C\u6210\u3002`,
|
|
226467
226873
|
finalDelivered: (acceptor) => `\u6700\u7EC8\u4EA4\u4ED8\u5DF2\u4E0A\u4F20\uFF0C\u7B49\u5F85 ${acceptor} \u9A8C\u6536\u3002`,
|
|
226468
|
-
|
|
226469
|
-
|
|
226470
|
-
|
|
226471
|
-
|
|
226472
|
-
|
|
226473
|
-
|
|
226474
|
-
|
|
226475
|
-
此前与 {@link reviewTimedOut} 共用一句,把被熔断的评审说成「未在时限内完成」。 */
|
|
226476
|
-
reviewKilled: (name) => `\u300A${name}\u300B\u7684\u8BC4\u5BA1\u8FDE\u7EED\u5931\u8D25\uFF0C\u5DF2\u505C\u6B62\u3002`,
|
|
226874
|
+
/* 🚨 审核卡那一族文案(待审 / 正在审 / 通过 / 不通过 / 超时 / 熔断)**不在这里**:
|
|
226875
|
+
审核卡改成按「被审交付 × 评审人」合卡之后,它的小状态由 `review-activity.ts` 的
|
|
226876
|
+
`reviewSummary()` 按该评审人的**有效判定**整句给出,不是逐事件套模板。这里此前留着一份
|
|
226877
|
+
同名同义的副本,六个 key 全是零引用——我 2026-09-09 改「审核不通过」文案时先改到了这份
|
|
226878
|
+
死副本上,页面纹丝不动。删掉,别再留第二个事实源。
|
|
226879
|
+
留在这里的只有下面这一句——它不是小状态,是**审核意见条**上那个判定词,由本文件拼卡时用。 */
|
|
226880
|
+
reviewNoteLabel: (rejected) => rejected ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u6838\u610F\u89C1",
|
|
226477
226881
|
acceptPending: "\u8BF7\u9A8C\u6536\u6700\u7EC8\u4EA4\u4ED8\u3002",
|
|
226478
226882
|
accepted: "\u9A8C\u6536\u6700\u7EC8\u4EA4\u4ED8\u5DF2\u5B8C\u6210\u9A8C\u6536\u3002",
|
|
226479
226883
|
acceptedNode: (name) => `\u300A${name}\u300B\u5DF2\u9A8C\u6536\u3002`,
|
|
@@ -229337,7 +229741,15 @@ function collabDomain(opts) {
|
|
|
229337
229741
|
}
|
|
229338
229742
|
const text5 = raw;
|
|
229339
229743
|
try {
|
|
229744
|
+
const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(ws));
|
|
229745
|
+
if (snap?.workorder.dispatchPaused && !isHumanActor(req.auth.actor)) {
|
|
229746
|
+
return { status: 403, body: { error: { code: "forbidden", message: "\u6682\u505C\u671F\u95F4\u5F55\u5165\u5DE5\u5355\u6B63\u6587\u4EC5\u9650\u4EBA\u7C7B\u64CD\u4F5C" } } };
|
|
229747
|
+
}
|
|
229340
229748
|
const contentRef = await blobs.put(new TextEncoder().encode(text5));
|
|
229749
|
+
if (snap?.workorder.dispatchPaused) {
|
|
229750
|
+
await kernel.recordPausedRevision({ artifactId: rootId, actor: req.auth.actor, contentRef, note: "\u7F16\u8F91\u5DE5\u5355\u9875\u5934\u6B63\u6587" });
|
|
229751
|
+
return { status: 200, body: { overview: text5, concluded: false } };
|
|
229752
|
+
}
|
|
229341
229753
|
await kernel.proposeRevision({ artifactId: rootId, actor: req.auth.actor, contentRef, reason: "\u7F16\u8F91\u5DE5\u5355\u9875\u5934\u6B63\u6587" });
|
|
229342
229754
|
await kernel.advanceQueue(rootId, req.auth.actor);
|
|
229343
229755
|
const result = await kernel.conclude({ artifactId: rootId, actor: req.auth.actor, note: "\u7F16\u8F91\u5DE5\u5355\u9875\u5934\u6B63\u6587" });
|
|
@@ -229345,7 +229757,7 @@ function collabDomain(opts) {
|
|
|
229345
229757
|
} catch (err) {
|
|
229346
229758
|
if (err instanceof KernelError) {
|
|
229347
229759
|
return {
|
|
229348
|
-
status: 400,
|
|
229760
|
+
status: err.code === "revision-conflict" ? 409 : 400,
|
|
229349
229761
|
body: { error: { code: err.code ?? "kernel_error", message: err.message } }
|
|
229350
229762
|
};
|
|
229351
229763
|
}
|
|
@@ -232404,12 +232816,83 @@ function buildCommentThread(input) {
|
|
|
232404
232816
|
bubbles
|
|
232405
232817
|
};
|
|
232406
232818
|
}
|
|
232819
|
+
function isReviewThreadSessionId(sessionId) {
|
|
232820
|
+
return typeof sessionId === "string" && sessionId.startsWith(REVIEW_THREAD_SESSION_PREFIX);
|
|
232821
|
+
}
|
|
232822
|
+
function partiesOfReviewThreadSessionId(sessionId) {
|
|
232823
|
+
if (!isReviewThreadSessionId(sessionId)) return null;
|
|
232824
|
+
const parts = sessionId.slice(REVIEW_THREAD_SESSION_PREFIX.length).split("|");
|
|
232825
|
+
return parts.length === 2 && parts[0] && parts[1] ? [parts[0], parts[1]] : null;
|
|
232826
|
+
}
|
|
232827
|
+
function buildReviewThread(input) {
|
|
232828
|
+
const parties = partiesOfReviewThreadSessionId(input.sessionId);
|
|
232829
|
+
if (!parties || !input.snap) return null;
|
|
232830
|
+
const [a, b2] = parties;
|
|
232831
|
+
const works = new Map(input.snap.works.map((w2) => [w2.id, w2]));
|
|
232832
|
+
const rows = input.snap.reviews.filter((r) => {
|
|
232833
|
+
const reviewee = works.get(r.targetWorkId)?.assigneeActorId;
|
|
232834
|
+
if (!reviewee || reviewee === r.reviewerActorId) return false;
|
|
232835
|
+
const pair = [r.reviewerActorId, reviewee].sort();
|
|
232836
|
+
return pair[0] === a && pair[1] === b2;
|
|
232837
|
+
});
|
|
232838
|
+
if (rows.length === 0) return null;
|
|
232839
|
+
const { ref: ref2 } = input;
|
|
232840
|
+
const nameOf = (id) => ref2(id).name?.trim() || "\u2014";
|
|
232841
|
+
const mention = (author) => nameOf(author === a ? b2 : a);
|
|
232842
|
+
const bubble = (o) => ({
|
|
232843
|
+
id: o.id,
|
|
232844
|
+
at: o.at,
|
|
232845
|
+
author: ref2(o.author),
|
|
232846
|
+
text: `@${mention(o.author)} ${o.body}`,
|
|
232847
|
+
issueId: o.issueId,
|
|
232848
|
+
kind: o.kind
|
|
232849
|
+
});
|
|
232850
|
+
const bubbles = [];
|
|
232851
|
+
for (const workId of new Set(rows.map((r) => r.targetWorkId))) {
|
|
232852
|
+
const w2 = works.get(workId);
|
|
232853
|
+
const body2 = w2?.conclusion?.trim();
|
|
232854
|
+
if (!w2 || !body2) continue;
|
|
232855
|
+
bubbles.push(bubble({
|
|
232856
|
+
id: `${workId}:delivered`,
|
|
232857
|
+
at: w2.endedAt || w2.createdAt,
|
|
232858
|
+
author: w2.assigneeActorId,
|
|
232859
|
+
body: body2,
|
|
232860
|
+
issueId: workId,
|
|
232861
|
+
kind: "raised"
|
|
232862
|
+
}));
|
|
232863
|
+
}
|
|
232864
|
+
for (const r of rows) {
|
|
232865
|
+
const note = r.note?.trim();
|
|
232866
|
+
if (!note) continue;
|
|
232867
|
+
const label = r.verdict === "request_changes" ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u6838\u610F\u89C1";
|
|
232868
|
+
bubbles.push(bubble({
|
|
232869
|
+
id: `${r.id}:verdict`,
|
|
232870
|
+
at: r.decidedAt || r.endedAt || r.createdAt,
|
|
232871
|
+
author: r.reviewerActorId,
|
|
232872
|
+
body: `${label}\uFF1A${note}`,
|
|
232873
|
+
issueId: r.targetWorkId,
|
|
232874
|
+
kind: "reply"
|
|
232875
|
+
}));
|
|
232876
|
+
}
|
|
232877
|
+
if (bubbles.length === 0) return null;
|
|
232878
|
+
bubbles.sort((x2, y) => x2.at.localeCompare(y.at) || x2.id.localeCompare(y.id));
|
|
232879
|
+
const first = rows[0];
|
|
232880
|
+
const revieweeId = works.get(first.targetWorkId).assigneeActorId;
|
|
232881
|
+
return {
|
|
232882
|
+
sessionId: input.sessionId,
|
|
232883
|
+
workorderId: input.workorderId,
|
|
232884
|
+
executor: ref2(revieweeId),
|
|
232885
|
+
coordinator: ref2(first.reviewerActorId),
|
|
232886
|
+
bubbles
|
|
232887
|
+
};
|
|
232888
|
+
}
|
|
232407
232889
|
var init_gap_thread = __esm({
|
|
232408
232890
|
"../server/src/domains/collab/gap-thread.ts"() {
|
|
232409
232891
|
"use strict";
|
|
232410
232892
|
init_src();
|
|
232411
232893
|
init_workorder_manager();
|
|
232412
232894
|
init_src();
|
|
232895
|
+
init_src();
|
|
232413
232896
|
}
|
|
232414
232897
|
});
|
|
232415
232898
|
|
|
@@ -233047,12 +233530,12 @@ function createChatSessionsDomain(opts) {
|
|
|
233047
233530
|
router.get("/api/workorders/:workorderId/gap-thread", async (req) => {
|
|
233048
233531
|
const workorderId = req.params["workorderId"];
|
|
233049
233532
|
const sessionId = req.query.get("session") ?? "";
|
|
233050
|
-
if (!isGapThreadSessionId(sessionId) && !isCommentThreadSessionId(sessionId)) {
|
|
233533
|
+
if (!isGapThreadSessionId(sessionId) && !isCommentThreadSessionId(sessionId) && !isReviewThreadSessionId(sessionId)) {
|
|
233051
233534
|
throw new ApiError(404, "NOT_FOUND", "thread not found");
|
|
233052
233535
|
}
|
|
233053
233536
|
const { snap, ref: ref2 } = await gapThreadInputsFor(req, workorderId);
|
|
233054
233537
|
const nodeOwner = (nodeId) => snap?.nodes?.find((n) => String(n.id) === nodeId)?.assigneeActorId ?? snap?.workorder.ownerActorId;
|
|
233055
|
-
const view = !snap ? null : isCommentThreadSessionId(sessionId) ? buildCommentThread({ workorderId, snap, ref: ref2, sessionId, nodeOwner }) : buildGapThread({ workorderId, snap, ref: ref2, sessionId });
|
|
233538
|
+
const view = !snap ? null : isReviewThreadSessionId(sessionId) ? buildReviewThread({ workorderId, snap, ref: ref2, sessionId }) : isCommentThreadSessionId(sessionId) ? buildCommentThread({ workorderId, snap, ref: ref2, sessionId, nodeOwner }) : buildGapThread({ workorderId, snap, ref: ref2, sessionId });
|
|
233056
233539
|
if (!view) throw new ApiError(404, "NOT_FOUND", "gap thread not found");
|
|
233057
233540
|
return { status: 200, body: view };
|
|
233058
233541
|
});
|
|
@@ -246157,6 +246640,21 @@ var init_postgres_chat_sessions = __esm({
|
|
|
246157
246640
|
);
|
|
246158
246641
|
return r.rows.map(rowToMessage);
|
|
246159
246642
|
}
|
|
246643
|
+
/** 按本 schema 中的会话 + run 精确找轮,不能用 activeTurn:已取消的轮已不占 active slot。 */
|
|
246644
|
+
async getTurnForRun(sessionId, runId) {
|
|
246645
|
+
const r = await this.pool.query(
|
|
246646
|
+
`SELECT id, status, completed_at, last_error FROM ${this.s}.chat_session_turns
|
|
246647
|
+
WHERE chat_session_id = $1 AND assistant_run_id = $2 ORDER BY reserved_at DESC LIMIT 1`,
|
|
246648
|
+
[sessionId, runId]
|
|
246649
|
+
);
|
|
246650
|
+
const row = r.rows[0];
|
|
246651
|
+
return row ? {
|
|
246652
|
+
id: row.id,
|
|
246653
|
+
status: row.status,
|
|
246654
|
+
completedAt: row.completed_at == null ? null : new Date(row.completed_at).toISOString(),
|
|
246655
|
+
lastError: row.last_error ?? null
|
|
246656
|
+
} : null;
|
|
246657
|
+
}
|
|
246160
246658
|
async getSession(id) {
|
|
246161
246659
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.chat_sessions WHERE id = $1`, [id]);
|
|
246162
246660
|
return r.rows[0] ? rowToSession(r.rows[0]) : null;
|
|
@@ -256992,6 +257490,7 @@ async function startServe(opts) {
|
|
|
256992
257490
|
if (!cid || cid === defaultCompanyId) return "";
|
|
256993
257491
|
return ` [${cid}]`;
|
|
256994
257492
|
};
|
|
257493
|
+
const engineSchemaFor = (companyId) => companyId === defaultCompanyId ? defaultCompanySchema : companyEngineDirName(companyId);
|
|
256995
257494
|
let getEngine = async () => {
|
|
256996
257495
|
throw new Error("company engine router not initialized");
|
|
256997
257496
|
};
|
|
@@ -257097,7 +257596,7 @@ async function startServe(opts) {
|
|
|
257097
257596
|
if (seeded > 0) console.log(`[serve] \u6536\u4EF6\u7BB1\u5DF2\u8BFB\u6C34\u4F4D\uFF1A\u64AD\u79CD ${seeded} \u6761\u5386\u53F2\u4F1A\u8BDD\uFF08ADR-0086 \u9996\u6B21\u4E0A\u7EBF\uFF09`);
|
|
257098
257597
|
nodeStore = await PostgresNodeStore.open(pgPool, sharedSchema);
|
|
257099
257598
|
nodeTokens = await PostgresNodeTokenStore.open(pgPool, sharedSchema);
|
|
257100
|
-
console.log(`[serve] \u4E8B\u5B9E/\u72B6\u6001\u4F53\u7CFB\uFF1APostgres \u9ED8\u8BA4\u516C\u53F8 schema=${defaultCompanySchema}\uFF08oplog / registry / blobs / artifacts / chat\uFF09\uFF1B\u5171\u4EAB\u63A7\u5236\u9762 schema=${sharedSchema}\uFF08companies / nodes / model_prices / tenant_storage_*\uFF09`);
|
|
257599
|
+
console.log(`[serve] \u4E8B\u5B9E/\u72B6\u6001\u4F53\u7CFB\uFF1APostgres \u9ED8\u8BA4\u516C\u53F8 schema=${defaultCompanySchema}\uFF08oplog / registry / blobs / artifacts / chat\uFF09\uFF1B\u5171\u4EAB\u63A7\u5236\u9762 schema=${sharedSchema}\uFF08companies / nodes / model_prices / tenant_storage_*\uFF09\uFF1Bchat \u6062\u590D/\u8BA4\u9886/\u6E05\u626B\uFF1A\u6309 active \u516C\u53F8\u9010\u5BB6\u6247\u51FA\uFF08\u9ED8\u8BA4\u516C\u53F8\u4F18\u5148\uFF09\uFF0C\u6BCF\u6B21\u5224\u5B9A\u7684\u516C\u53F8\u4E0E\u4F9D\u636E\u89C1 [chat-claim] / [chat-recovery] \u884C`);
|
|
257101
257600
|
} else {
|
|
257102
257601
|
projectStateStore = await FileProjectStateStore.open(path29.join(opts.dir, "artifact-document-projects.json"));
|
|
257103
257602
|
artifactStateStore = await FileArtifactStateStore.open(path29.join(opts.dir, "artifact-document-state.json"));
|
|
@@ -257108,7 +257607,6 @@ async function startServe(opts) {
|
|
|
257108
257607
|
console.log("[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
257109
257608
|
}
|
|
257110
257609
|
const chatRecoveryStore = platformChatRecovery ?? chatSessionStore;
|
|
257111
|
-
const chatRecoveryItems = pgPool ? await PostgresChatItemStore.open(pgPool, defaultCompanySchema) : void 0;
|
|
257112
257610
|
const knowledgeConfigStore = await FileKnowledgeConfigStore.open(path29.join(opts.dir, "knowledge.json"));
|
|
257113
257611
|
const knowledgeRunStore = await FileKnowledgeRunStore.open(path29.join(opts.dir, "knowledge-runs.json"));
|
|
257114
257612
|
const knowledgeDeploymentId = await loadOrCreateKnowledgeDeploymentId(path29.join(opts.dir, "knowledge-deployment-id"));
|
|
@@ -257813,7 +258311,7 @@ async function startServe(opts) {
|
|
|
257813
258311
|
id: c.id,
|
|
257814
258312
|
// 默认公司的引擎表仍在共享 schema(ADR 0164 §D5 的 RP0,搬迁另立单)——这条映射
|
|
257815
258313
|
// 搞错,默认公司的工单会一张都对不上,回填会把它们全判成「无证据」。
|
|
257816
|
-
engineSchema:
|
|
258314
|
+
engineSchema: engineSchemaFor(c.id)
|
|
257817
258315
|
}));
|
|
257818
258316
|
const res = await backfillTenancyColumns(tenancyPool, { schema: defaultCompanySchema, companies: companies2, apply: true });
|
|
257819
258317
|
const wrote = res.rows.reduce((n, r) => n + r.updated, 0);
|
|
@@ -257988,28 +258486,28 @@ async function startServe(opts) {
|
|
|
257988
258486
|
const assistants2 = await PostgresAssistantBindStore.open(pgPool, loc.schemaName, { companyId });
|
|
257989
258487
|
const humanPrefs2 = await PostgresHumanPrefsStore.open(pgPool, loc.schemaName);
|
|
257990
258488
|
const ownedReadMarkers2 = await PostgresReadMarkerStore.open(pgPool, loc.schemaName, {
|
|
257991
|
-
chatSessionsSchema:
|
|
258489
|
+
chatSessionsSchema: engineSchemaFor(companyId)
|
|
257992
258490
|
});
|
|
257993
258491
|
const ownedMemory2 = await PostgresActorMemoryStore.open(pgPool, loc.schemaName);
|
|
257994
258492
|
const ownedDrafts2 = await PostgresWorkorderDraftStore.open(
|
|
257995
258493
|
pgPool,
|
|
257996
|
-
|
|
258494
|
+
engineSchemaFor(companyId)
|
|
257997
258495
|
);
|
|
257998
258496
|
const ownedPlannerIssues2 = await PostgresWorkorderDraftPlannerIssuesStore.open(
|
|
257999
258497
|
pgPool,
|
|
258000
|
-
|
|
258498
|
+
engineSchemaFor(companyId)
|
|
258001
258499
|
);
|
|
258002
258500
|
const ownedLedger2 = await PostgresDispatchStore.open(
|
|
258003
258501
|
pgPool,
|
|
258004
|
-
|
|
258502
|
+
engineSchemaFor(companyId)
|
|
258005
258503
|
);
|
|
258006
258504
|
const ownedBlobs2 = await PostgresBlobStore.open(
|
|
258007
258505
|
pgPool,
|
|
258008
|
-
|
|
258506
|
+
engineSchemaFor(companyId)
|
|
258009
258507
|
);
|
|
258010
258508
|
const ownedTrace2 = await PostgresTraceStore.open(
|
|
258011
258509
|
tracePool,
|
|
258012
|
-
|
|
258510
|
+
engineSchemaFor(companyId)
|
|
258013
258511
|
);
|
|
258014
258512
|
return { owned: owned2, ownedOrgRegistry: ownedOrgRegistry2, types: types3, assistants: assistants2, humanPrefs: humanPrefs2, ownedReadMarkers: ownedReadMarkers2, ownedMemory: ownedMemory2, ownedDrafts: ownedDrafts2, ownedPlannerIssues: ownedPlannerIssues2, ownedLedger: ownedLedger2, ownedBlobs: ownedBlobs2, ownedTrace: ownedTrace2 };
|
|
258015
258513
|
}
|
|
@@ -258265,7 +258763,7 @@ async function startServe(opts) {
|
|
|
258265
258763
|
const id = companyId ?? defaultCompanyId;
|
|
258266
258764
|
const hit = delegationStores.get(id);
|
|
258267
258765
|
if (hit) return hit;
|
|
258268
|
-
const schemaFor =
|
|
258766
|
+
const schemaFor = engineSchemaFor(id);
|
|
258269
258767
|
const store = pgPool ? await PostgresDelegationStore.open(pgPool, schemaFor) : await FileDelegationStore.open(path29.join(opts.dir, `delegations-${companyEngineDirName(id)}.json`));
|
|
258270
258768
|
delegationStores.set(id, store);
|
|
258271
258769
|
return store;
|
|
@@ -258516,7 +259014,7 @@ async function startServe(opts) {
|
|
|
258516
259014
|
const knowledgeGovernanceStoreFor = (companyId) => {
|
|
258517
259015
|
const cached3 = knowledgeGovernanceStores.get(companyId);
|
|
258518
259016
|
if (cached3) return cached3;
|
|
258519
|
-
const knowledgeSchema =
|
|
259017
|
+
const knowledgeSchema = engineSchemaFor(companyId);
|
|
258520
259018
|
const opened = pgDsn && pgPool ? withSchemaDdlLock(pgPool, knowledgeSchema, () => PostgresKnowledgeGovernanceStore.open(pgPool, knowledgeSchema, knowledgeSnapshotKey)) : FileKnowledgeGovernanceStore.open(path29.join(
|
|
258521
259019
|
opts.dir,
|
|
258522
259020
|
companyId === defaultCompanyId ? "knowledge-governance.json" : path29.join("companies", companyEngineDirName(companyId), "knowledge-governance.json")
|
|
@@ -259991,60 +260489,101 @@ async function startServe(opts) {
|
|
|
259991
260489
|
});
|
|
259992
260490
|
};
|
|
259993
260491
|
{
|
|
259994
|
-
const
|
|
259995
|
-
if (
|
|
260492
|
+
const defaultListRunningChatRows = chatRecoveryStore.listRunningAssistantMessages?.bind(chatRecoveryStore);
|
|
260493
|
+
if (defaultListRunningChatRows) {
|
|
260494
|
+
const chatRecoveryCompanies = async () => {
|
|
260495
|
+
if (!chatStoreRouter) return [void 0];
|
|
260496
|
+
const active = (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id);
|
|
260497
|
+
return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
|
|
260498
|
+
};
|
|
260499
|
+
const chatRecoveryPlanes = /* @__PURE__ */ new Map();
|
|
260500
|
+
const chatRecoveryFor = async (companyId) => {
|
|
260501
|
+
if (!pgPool || !platformChatRecovery) {
|
|
260502
|
+
return { listRunningAssistantMessages: defaultListRunningChatRows, chatStore: chatSessionStore };
|
|
260503
|
+
}
|
|
260504
|
+
const id = companyId ?? defaultCompanyId;
|
|
260505
|
+
const hit = chatRecoveryPlanes.get(id);
|
|
260506
|
+
if (hit) return hit;
|
|
260507
|
+
const sessions = id === defaultCompanyId ? platformChatRecovery : PlatformChatRecoveryStore.open(pgPool, engineSchemaFor(id));
|
|
260508
|
+
const items = await chatItemsFor(id);
|
|
260509
|
+
const plane = {
|
|
260510
|
+
listRunningAssistantMessages: () => sessions.listRunningAssistantMessages(),
|
|
260511
|
+
chatStore: sessions,
|
|
260512
|
+
getTurnForRun: (sid, rid) => sessions.getTurnForRun(sid, rid),
|
|
260513
|
+
...items ? { items } : {}
|
|
260514
|
+
};
|
|
260515
|
+
chatRecoveryPlanes.set(id, plane);
|
|
260516
|
+
return plane;
|
|
260517
|
+
};
|
|
260518
|
+
const chatRecoveryLastError = /* @__PURE__ */ new Map();
|
|
260519
|
+
const chatRecoveryCompanyWarn = (label) => (companyId, err) => {
|
|
260520
|
+
const key = `${label}::${companyId ?? "<default>"}`;
|
|
260521
|
+
const msg = String(err);
|
|
260522
|
+
if (chatRecoveryLastError.get(key) === msg) return;
|
|
260523
|
+
chatRecoveryLastError.set(key, msg);
|
|
260524
|
+
console.warn(`[chat-recovery] ${label}\uFF1A\u516C\u53F8 ${companyId ?? "<default>"} \u8FD9\u4E00\u8F6E\u5931\u8D25\uFF08\u5176\u4F59\u516C\u53F8\u7EE7\u7EED\uFF09\uFF1A${msg}`);
|
|
260525
|
+
};
|
|
259996
260526
|
let chatRecoveryChain = Promise.resolve();
|
|
259997
260527
|
const enqueueChatRecovery = (label, step) => {
|
|
259998
260528
|
chatRecoveryChain = chatRecoveryChain.then(step).catch((err) => console.error(`[chat-recovery] ${label}\u5931\u8D25: ${String(err)}`));
|
|
259999
260529
|
};
|
|
260000
|
-
const recoverChatTurns = async (daemonId, activeSessions) => {
|
|
260001
|
-
|
|
260002
|
-
|
|
260003
|
-
|
|
260004
|
-
|
|
260005
|
-
|
|
260006
|
-
|
|
260007
|
-
|
|
260008
|
-
|
|
260009
|
-
|
|
260010
|
-
|
|
260011
|
-
|
|
260012
|
-
|
|
260013
|
-
|
|
260014
|
-
|
|
260015
|
-
|
|
260016
|
-
|
|
260017
|
-
|
|
260018
|
-
|
|
260019
|
-
|
|
260020
|
-
|
|
260021
|
-
|
|
260022
|
-
|
|
260023
|
-
|
|
260024
|
-
|
|
260025
|
-
|
|
260026
|
-
},
|
|
260027
|
-
traceHealth,
|
|
260028
|
-
...chatRecoveryItems ? { items: chatRecoveryItems, itemsTurnId } : {},
|
|
260029
|
-
log: (m2) => console.log(m2)
|
|
260030
|
-
});
|
|
260031
|
-
console.log(`[chat-recovery] \u91CD\u6302 chat \u8F6E\uFF1Asession=${plan.chatSessionId} run=${plan.runId} node=${daemonId}`);
|
|
260032
|
-
}
|
|
260033
|
-
};
|
|
260530
|
+
const recoverChatTurns = async (daemonId, activeSessions, opts2) => recoverChatTurnsAcrossCompanies({
|
|
260531
|
+
activeSessions,
|
|
260532
|
+
listCompanies: chatRecoveryCompanies,
|
|
260533
|
+
storesFor: chatRecoveryFor,
|
|
260534
|
+
getRun: (id) => fanOutTrace.getRun(id),
|
|
260535
|
+
isWired: (artifactId) => chatLiveSessions.has(`chat::${artifactId}`),
|
|
260536
|
+
...opts2.stopOnFirstHit ? { stopOnFirstHit: true } : {},
|
|
260537
|
+
onCompanyError: chatRecoveryCompanyWarn(opts2.label),
|
|
260538
|
+
liveChat,
|
|
260539
|
+
trace: runRoutedTrace,
|
|
260540
|
+
traceHealth,
|
|
260541
|
+
recover: (plan) => chatRemoteAdapter.recover(plan.dispatchId, daemonId, plan.runtimeKind, plan.actor),
|
|
260542
|
+
register: (plan, handle) => chatLiveSessions.set(`chat::${plan.artifactId}`, {
|
|
260543
|
+
artifactId: plan.artifactId,
|
|
260544
|
+
sessionId: plan.runId,
|
|
260545
|
+
dispatchId: plan.dispatchId,
|
|
260546
|
+
kill: () => handle.kill()
|
|
260547
|
+
}),
|
|
260548
|
+
unregister: (plan) => {
|
|
260549
|
+
chatLiveSessions.delete(`chat::${plan.artifactId}`);
|
|
260550
|
+
},
|
|
260551
|
+
evict: (dispatchId) => {
|
|
260552
|
+
if (!hub.evictSession(dispatchId, "reaped")) hub.dispatch(daemonId, { type: "kill", dispatchId });
|
|
260553
|
+
},
|
|
260554
|
+
log: (message) => console.log(message)
|
|
260555
|
+
});
|
|
260034
260556
|
hub.registerSessionClaimResolver?.(async (id) => {
|
|
260035
260557
|
if (!id.jobArtifactId || !chatRemoteAdapter) return "unknown";
|
|
260036
260558
|
const jobKey = `chat::${id.jobArtifactId}`;
|
|
260037
|
-
if (chatLiveSessions.has(jobKey))
|
|
260559
|
+
if (chatLiveSessions.has(jobKey)) {
|
|
260560
|
+
console.log(
|
|
260561
|
+
`[chat-claim] dispatch=${id.dispatchId} artifact=${id.jobArtifactId} \u7ED3\u8BBA=live\uFF08\u4F9D\u636E\uFF1A\u672C\u8FDB\u7A0B\u5728\u9014\u8868\u91CC\u5DF2\u6709\u8FD9\u4E00\u8F6E\uFF0C\u65E0\u9700\u91CD\u6302\uFF09`
|
|
260562
|
+
);
|
|
260563
|
+
return "live";
|
|
260564
|
+
}
|
|
260565
|
+
let outcome;
|
|
260038
260566
|
await new Promise((resolve10) => {
|
|
260039
|
-
chatRecoveryChain = chatRecoveryChain.then(() =>
|
|
260040
|
-
|
|
260041
|
-
|
|
260567
|
+
chatRecoveryChain = chatRecoveryChain.then(async () => {
|
|
260568
|
+
outcome = await recoverChatTurns(
|
|
260569
|
+
id.nodeId,
|
|
260570
|
+
[{ dispatchId: id.dispatchId, sessionId: id.sessionId, jobArtifactId: id.jobArtifactId }],
|
|
260571
|
+
{ label: "\u8BA4\u9886", stopOnFirstHit: true }
|
|
260572
|
+
);
|
|
260573
|
+
}).catch((err) => {
|
|
260042
260574
|
console.error(`[chat-recovery] session_hello \u8BA4\u9886\u5931\u8D25: ${String(err)}`);
|
|
260043
260575
|
}).then(() => {
|
|
260044
260576
|
resolve10();
|
|
260045
260577
|
});
|
|
260046
260578
|
});
|
|
260047
|
-
|
|
260579
|
+
if (outcome?.settled.some((hit2) => hit2.plan.dispatchId === id.dispatchId)) return "settled";
|
|
260580
|
+
const live = chatLiveSessions.has(jobKey);
|
|
260581
|
+
const hit = outcome?.recovered.find((r) => r.plan.artifactId === id.jobArtifactId);
|
|
260582
|
+
const skipped = outcome?.skipped ?? [];
|
|
260583
|
+
console.log(
|
|
260584
|
+
`[chat-claim] dispatch=${id.dispatchId} artifact=${id.jobArtifactId} company=${hit ? hit.companyId ?? "<default>" : "-"} session=${hit?.plan.chatSessionId ?? "-"} run=${hit?.plan.runId ?? "-"} \u7ED3\u8BBA=${live ? "live" : "unknown"}\uFF08\u4F9D\u636E\uFF1A${live ? `\u5728\u8BE5\u516C\u53F8 schema \u91CC\u5BF9\u4E0A\u4E00\u6761 running assistant \u884C\u3001\u5176 run \u4ECD running\uFF0C\u5DF2\u91CD\u6302` : `\u626B\u8FC7 ${outcome?.scanned ?? 0} \u5BB6\u516C\u53F8\uFF0C\u65E0 running \u884C\u4E0E\u5B83\u5BF9\u4E0A` + (skipped.length ? `\uFF1B\u53E6\u6709 ${skipped.length} \u5BB6\u6CA1\u67E5\u6210\uFF08${skipped.map((x2) => x2.companyId ?? "<default>").join("\u3001")}\uFF09` : "")}\uFF09` + (live ? "" : "\u3002unknown \u53EA\u662F\u300C\u6211\u4E0D\u77E5\u9053\u300D\uFF0C\u5904\u7F6E\u4EA4\u51B7\u542F\u52A8\u5BBD\u9650\u90A3\u4E00\u7EA7")
|
|
260585
|
+
);
|
|
260586
|
+
return live ? "live" : "unknown";
|
|
260048
260587
|
});
|
|
260049
260588
|
hub.addMessageListener((daemonId, msg) => {
|
|
260050
260589
|
if (msg.type !== "runtime_models") return;
|
|
@@ -260089,7 +260628,14 @@ async function startServe(opts) {
|
|
|
260089
260628
|
});
|
|
260090
260629
|
hub.addMessageListener((daemonId, msg) => {
|
|
260091
260630
|
if (msg.type === "hello" && msg.meta.activeSessions?.length) {
|
|
260092
|
-
enqueueChatRecovery("hello \u5BF9\u8D26", () =>
|
|
260631
|
+
enqueueChatRecovery("hello \u5BF9\u8D26", async () => {
|
|
260632
|
+
const outcome = await recoverChatTurns(daemonId, msg.meta.activeSessions, { label: "hello \u5BF9\u8D26" });
|
|
260633
|
+
if (outcome.recovered.length === 0 && outcome.skipped.length === 0) return;
|
|
260634
|
+
const byCompany = outcome.recovered.map((r) => `${r.companyId ?? "<default>"}:${r.plan.chatSessionId}`).join("\u3001");
|
|
260635
|
+
console.log(
|
|
260636
|
+
`[chat-recovery] hello \u5BF9\u8D26\uFF1Anode=${daemonId} \u626B\u8FC7 ${outcome.scanned} \u5BB6\u516C\u53F8\uFF0C\u91CD\u6302 ${outcome.recovered.length} \u8F6E${byCompany ? `\uFF08${byCompany}\uFF09` : ""}` + (outcome.skipped.length ? `\uFF1B${outcome.skipped.length} \u5BB6\u6CA1\u67E5\u6210\uFF08${outcome.skipped.map((x2) => x2.companyId ?? "<default>").join("\u3001")}\uFF09` : "")
|
|
260637
|
+
);
|
|
260638
|
+
});
|
|
260093
260639
|
} else if (msg.type === "session_started") {
|
|
260094
260640
|
void ledgerForDispatch(msg.dispatchId).then((l) => l.markStarted(msg.dispatchId, { sessionRef: msg.sessionId, daemonId })).catch(ledgerWarn("markStarted"));
|
|
260095
260641
|
const prodMapping = dispatchToWork.get(msg.dispatchId);
|
|
@@ -260135,16 +260681,16 @@ async function startServe(opts) {
|
|
|
260135
260681
|
})).catch(ledgerWarn("close(exit)"));
|
|
260136
260682
|
const dispatchOwnedAtReceipt = allDispatchers().some((d) => d.inFlightSessions().some((x2) => x2.sessionId === msg.dispatchId));
|
|
260137
260683
|
enqueueChatRecovery("exit \u5BF9\u8D26", async () => {
|
|
260138
|
-
const chatHandled = await
|
|
260684
|
+
const chatHandled = await reconcileChatExitFrameAcrossCompanies({
|
|
260139
260685
|
dispatchId: msg.dispatchId,
|
|
260140
260686
|
info: msg.info,
|
|
260141
260687
|
isOwned: (id) => [...chatLiveSessions.values()].some((c) => c.dispatchId === id),
|
|
260142
260688
|
ownedRunId: (sid) => liveChat.runFor(sid),
|
|
260143
|
-
listRunningAssistantMessages: listRunningChatRows,
|
|
260144
260689
|
trace: runRoutedTrace,
|
|
260145
|
-
|
|
260690
|
+
listCompanies: chatRecoveryCompanies,
|
|
260691
|
+
storesFor: chatRecoveryFor,
|
|
260692
|
+
onCompanyError: chatRecoveryCompanyWarn("exit \u5BF9\u8D26"),
|
|
260146
260693
|
drainStash: (id) => chatRemoteAdapter?.drainStash(id) ?? { frames: [] },
|
|
260147
|
-
...chatRecoveryItems ? { items: chatRecoveryItems } : {},
|
|
260148
260694
|
log: (m2) => console.log(m2)
|
|
260149
260695
|
});
|
|
260150
260696
|
if (!chatHandled) {
|
|
@@ -260208,21 +260754,22 @@ async function startServe(opts) {
|
|
|
260208
260754
|
}
|
|
260209
260755
|
});
|
|
260210
260756
|
const chatSweepTimer = setInterval(() => {
|
|
260211
|
-
void
|
|
260212
|
-
|
|
260757
|
+
void sweepDanglingChatTurnsAcrossCompanies({
|
|
260758
|
+
listCompanies: chatRecoveryCompanies,
|
|
260759
|
+
storesFor: chatRecoveryFor,
|
|
260760
|
+
onCompanyError: chatRecoveryCompanyWarn("chat-sweep"),
|
|
260213
260761
|
getRun: (id) => fanOutTrace.getRun(id),
|
|
260214
260762
|
ownedRunId: (sid) => liveChat.runFor(sid),
|
|
260215
|
-
listMessages: (sid) => chatRecoveryStore.listMessages(sid),
|
|
260216
|
-
updateMessage: chatRecoveryStore.updateMessage.bind(chatRecoveryStore),
|
|
260217
260763
|
now: Date.now(),
|
|
260218
260764
|
// 提案 §6.2.9:委派台账**接进这一拍,不新写一套扫描**。
|
|
260219
260765
|
// 这一拍闭合的是「run 已终态、assistant 行却还挂 running」的行;委派台账是同一类账,
|
|
260220
260766
|
// 只是多一层——它还得回流。不接的话那条委派会永远显示「进行中」(刷新也不变),
|
|
260221
260767
|
// 且「队列非空 ⇒ 不算完成」变成死锁:既看不到结果、也等不到失败。
|
|
260222
|
-
// ⚠️
|
|
260223
|
-
//
|
|
260224
|
-
//
|
|
260225
|
-
//
|
|
260768
|
+
// ⚠️ **必须带公司**:委派台账按公司分 store,不带的话非默认公司那条委派会被拿去默认公司的
|
|
260769
|
+
// 库里查、查不到、于是永远收敛不掉——症状是「刷新也不变的进行中」,且只在非默认公司出现。
|
|
260770
|
+
// 扇出之后**公司是已知量**(这一行就是从那家的 schema 读出来的),由扇出层作为第四个参数
|
|
260771
|
+
// 直接传给钩子,不再按会话反查一次——少一次点查,也少一条「读不到就按默认公司试一次」的
|
|
260772
|
+
// 错误回落。回落那一跳仍收在 `delegationSweepHook` 里(dev 文件模式还需要它,有自己的测试)。
|
|
260226
260773
|
onSessionClosed: delegationSweepHook({
|
|
260227
260774
|
sessions: chatRecoveryStore,
|
|
260228
260775
|
settle: (sid, reason, companyId) => server.settleDelegationFromSweep(sid, reason, companyId),
|
|
@@ -260251,6 +260798,20 @@ async function startServe(opts) {
|
|
|
260251
260798
|
isOwned: (turn) => Boolean(turn.assistantRunId && liveChat.runFor(turn.chatSessionId) === turn.assistantRunId),
|
|
260252
260799
|
now: Date.now(),
|
|
260253
260800
|
noRunGraceMs: chatTurnOrphanGraceMs,
|
|
260801
|
+
/* 轮次被判死之后把委派台账也收敛掉(2026-09-09 生产事故的第二道口子)。
|
|
260802
|
+
此前只有 `sweepDanglingChatTurns` 那一拍挂了委派钩子,而它要先拿 assistant 行的 `run_id`
|
|
260803
|
+
去查 run —— 子会话占位行的 `run_id` 一直是空的,那一拍整条跳过、钩子一次没调用:
|
|
260804
|
+
三条委派的轮次 12:27–12:35 就 failed 了,台账到 13:10 还是 running,父会话永远"处理中"。
|
|
260805
|
+
本拍是从轮次账本走的(`assistant_run_id` 一直有值),是"这一轮死没死"的权威来源。
|
|
260806
|
+
`companyId` 由扇出那层填好 —— 台账按公司分 store,少了它会查到默认公司的库。
|
|
260807
|
+
非委派会话调进去是空转:`settleFromSweep` 按 childSessionId 查不到记录就原样返回。 */
|
|
260808
|
+
onTurnSettled: async ({ chatSessionId, status, reason, companyId }) => {
|
|
260809
|
+
await server.settleDelegationFromSweep(
|
|
260810
|
+
chatSessionId,
|
|
260811
|
+
`\u5B50\u4F1A\u8BDD\u8FD9\u4E00\u8F6E\u88AB\u6062\u590D\u5668\u6536\u53E3\u4E3A ${status}\uFF08${reason}\uFF09`,
|
|
260812
|
+
companyId
|
|
260813
|
+
);
|
|
260814
|
+
},
|
|
260254
260815
|
log: (m2) => console.log(m2),
|
|
260255
260816
|
onCompanyError: (companyId, err) => {
|
|
260256
260817
|
const key = companyId ?? "<default>";
|
|
@@ -260724,14 +261285,22 @@ async function startServe(opts) {
|
|
|
260724
261285
|
//(activate.ts:`latestAcceptId == null && !lastProposedRejected`),本模块拿不到,
|
|
260725
261286
|
// 而两个方向的代价不对称——该续没续只是少一点上下文,不该续却续了就把返工要隔离的
|
|
260726
261287
|
// 旧会话又接了回来。何况这条 work 从来没跑过,本就没有属于它的会话可续。
|
|
260727
|
-
|
|
261288
|
+
// 补派进入同一工单事件队列:与 pause 的 apply/effect 串行,不能在检查后绕到 IO 直派。
|
|
261289
|
+
redispatch: async (workId, workorderId) => {
|
|
261290
|
+
await newKernel.getBus().submit({
|
|
261291
|
+
companyId,
|
|
261292
|
+
workorderId,
|
|
261293
|
+
actorId: SYSTEM_ACTOR2,
|
|
261294
|
+
event: { kind: "work.redispatch", workorderId, workId }
|
|
261295
|
+
});
|
|
261296
|
+
},
|
|
260728
261297
|
now: Date.now(),
|
|
260729
261298
|
graceMs: DISPATCH_DROP_GRACE_MS,
|
|
260730
261299
|
backoffMs: DISPATCH_DROP_BACKOFF_MS,
|
|
260731
261300
|
maxPerTick: DISPATCH_DROP_MAX_PER_TICK
|
|
260732
261301
|
}).then((r) => {
|
|
260733
261302
|
if (r.redispatched.length > 0) {
|
|
260734
|
-
console.warn(`[dispatch-drop] \u8865\u6D3E ${r.redispatched.length} \u6761\u4ECE\u672A\u5F00\u5DE5\u7684 work\uFF1A${r.redispatched.join(", ")}`);
|
|
261303
|
+
console.warn(`[dispatch-drop] \u5DF2\u8BF7\u6C42\u8865\u6D3E ${r.redispatched.length} \u6761\u4ECE\u672A\u5F00\u5DE5\u7684 work\uFF1A${r.redispatched.join(", ")}`);
|
|
260735
261304
|
}
|
|
260736
261305
|
}).catch((err) => console.error(`[dispatch-drop] tick \u5931\u8D25: ${String(err)}`));
|
|
260737
261306
|
}, DISPATCH_DROP_SWEEP_TICK_MS);
|
|
@@ -262832,17 +263401,19 @@ var SessionProcessState = class {
|
|
|
262832
263401
|
}
|
|
262833
263402
|
let done = false;
|
|
262834
263403
|
const finish = () => {
|
|
262835
|
-
if (
|
|
262836
|
-
|
|
262837
|
-
|
|
262838
|
-
|
|
263404
|
+
if (done) return;
|
|
263405
|
+
done = true;
|
|
263406
|
+
clearTimeout(timer);
|
|
263407
|
+
this.exitAckResolve = null;
|
|
263408
|
+
resolve10();
|
|
262839
263409
|
};
|
|
263410
|
+
const timer = setTimeout(finish, EXIT_ACK_WAIT_MS);
|
|
262840
263411
|
this.exitAckResolve = finish;
|
|
262841
|
-
setTimeout(finish, EXIT_ACK_WAIT_MS).unref?.();
|
|
262842
263412
|
});
|
|
262843
263413
|
}
|
|
262844
263414
|
teardown() {
|
|
262845
263415
|
this.stopping = true;
|
|
263416
|
+
this.exitAckResolve?.();
|
|
262846
263417
|
if (this.watchdog) {
|
|
262847
263418
|
clearInterval(this.watchdog);
|
|
262848
263419
|
this.watchdog = null;
|
|
@@ -264279,10 +264850,6 @@ var COMMAND_DECLS = {
|
|
|
264279
264850
|
{ name: "as", desc: "\u7ED9\u8FD9\u6B21\u59D4\u6D3E\u8D77\u4E2A\u77ED\u540D\uFF0C\u4E4B\u540E\u7528\u5B83 --follow-up\uFF1B\u649E\u540D\u670D\u52A1\u7AEF\u81EA\u52A8\u52A0\u540E\u7F00\uFF0C**\u4EE5\u8F93\u51FA\u91CC\u90A3\u4E2A\u4E3A\u51C6**" },
|
|
264280
264851
|
{ name: "file", desc: "\u4E00\u8D77\u5E26\u8FC7\u53BB\u7684\u6587\u4EF6\uFF08\u4F60\u5DE5\u4F5C\u76EE\u5F55\u4E0B\u7684\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u53EF\u7ED9\u591A\u6B21\uFF1B\u5355\u4E2A \u22642MB\u3001\u4E00\u6B21 \u226420 \u4E2A / 20MB\uFF09" },
|
|
264281
264852
|
{ name: "follow-up", desc: "\u5F80\u5DF2\u6709\u59D4\u6D3E\u91CC\u518D\u8FFD\u95EE/\u8F6C\u8FBE\u4E00\u53E5\uFF1A\u7ED9 label / \u4E13\u5BB6\u540D / delegationId \u524D\u7F00" },
|
|
264282
|
-
// 旧名别名。**登记在这里是必须的**:`command-declarations.test.ts` 的参数对拍要求
|
|
264283
|
-
// 「代码读的 flag」与「登记的 flag」逐条一致,代码里为兼容还在读 `continue`。
|
|
264284
|
-
// 别名为什么必须留、什么时候能删,见 cli.ts 里 delegate 第 ③ 支的注释。
|
|
264285
|
-
{ name: "continue", desc: "\u3010\u65E7\u540D\uFF0C\u5C06\u4E0B\u7EBF\u3011\u7B49\u540C --follow-up\uFF1B\u53EA\u4E3A\u8DE8\u7248\u672C\u517C\u5BB9\u4FDD\u7559\uFF0C\u65B0\u8C03\u7528\u4E00\u5F8B\u5199 --follow-up" },
|
|
264286
264853
|
{ name: "text", desc: "\u8FFD\u95EE\u5185\u5BB9\uFF08\u914D --follow-up\uFF09\u3002\u6539\u65B9\u5411\u5C31\u660E\u8BF4\u300C\u6539\u65B9\u5411\u300D\uFF0C\u4E13\u5BB6\u4F1A\u81EA\u5DF1\u5224\u65AD\u8981\u4E0D\u8981\u653E\u5F03\u624B\u4E0A\u90A3\u6BB5\u3002**\u770B\u56DE\u6267**\uFF1A\u63D2\u8FDB\u5728\u8DD1\u7684\u90A3\u4E00\u8F6E / \u8D77\u4E86\u65B0\u7684\u4E00\u8F6E / \u8FD8\u6392\u5728\u961F\u5217\u91CC\u2014\u2014\u6392\u961F\u65F6\u4E13\u5BB6\u8981\u7B49\u672C\u8F6E\u8DD1\u5B8C\u624D\u770B\u89C1\uFF0C\u8BE5\u8DDF\u7528\u6237\u8BF4\u4E00\u58F0" },
|
|
264287
264854
|
{ name: "status", desc: "\u770B\u4E00\u4E2A\u59D4\u6D3E\u7684\u8BE6\u60C5\uFF1A\u5168\u91CF\u5404\u8F6E\u5C0F\u7ED3 + \u4EA7\u7269\u6E05\u5355" },
|
|
264288
264855
|
{ name: "list", desc: "\u5217\u51FA\u8FD9\u6761\u5BF9\u8BDD\u6D3E\u51FA\u53BB\u7684\u6D3B\uFF1B\u9ED8\u8BA4\u53EA\u7ED9\u8FDB\u884C\u4E2D + \u6700\u8FD1 10 \u6761", boolean: true },
|
|
@@ -266763,9 +267330,8 @@ ${round.text}`);
|
|
|
266763
267330
|
}
|
|
266764
267331
|
break;
|
|
266765
267332
|
}
|
|
266766
|
-
const followUpRef = flags.get("follow-up")
|
|
267333
|
+
const followUpRef = flags.get("follow-up");
|
|
266767
267334
|
if (followUpRef !== void 0) {
|
|
266768
|
-
if (followUpRef === "true") throw new Error("--follow-up \u8981\u8DDF\u4E00\u4E2A\u5F15\u7528\uFF1Alabel / \u4E13\u5BB6\u540D / delegationId \u524D\u7F00");
|
|
266769
267335
|
const text5 = flags.get("text");
|
|
266770
267336
|
if (!text5) throw new Error('--follow-up \u8981\u914D --text "<\u8981\u8FFD\u95EE\u7684\u8BDD>"');
|
|
266771
267337
|
const target = await resolveRef2(followUpRef);
|
|
@@ -269584,7 +270150,7 @@ function shimScript() {
|
|
|
269584
270150
|
}
|
|
269585
270151
|
|
|
269586
270152
|
// src/index.ts
|
|
269587
|
-
var PKG_VERSION = true ? "2.2.
|
|
270153
|
+
var PKG_VERSION = true ? "2.2.7" : "dev";
|
|
269588
270154
|
var LOCAL_BIN = localBin();
|
|
269589
270155
|
var NPM_PREFIX = npmPrefix();
|
|
269590
270156
|
var INSTANCE = DEFAULT_INSTANCE;
|