oasis_test_v2 2.2.6 → 2.2.8
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 +1228 -256
- 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));
|
|
@@ -17335,6 +17406,7 @@ ${supplemental.taskAppend.trim()}
|
|
|
17335
17406
|
...Object.keys(jobEnv).length > 0 ? { env: jobEnv } : {},
|
|
17336
17407
|
...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {},
|
|
17337
17408
|
...provisioned?.requiredTools && provisioned.requiredTools.length > 0 ? { requiredTools: provisioned.requiredTools } : {},
|
|
17409
|
+
...provisioned?.requiredToolVersions && Object.keys(provisioned.requiredToolVersions).length > 0 ? { requiredToolVersions: provisioned.requiredToolVersions } : {},
|
|
17338
17410
|
// 凭据本体随 job 下发,供**节点侧本机注入**(见上方 provision 注释)。
|
|
17339
17411
|
...provisioned?.connectorCreds && provisioned.connectorCreds.length > 0 ? { connectorCreds: provisioned.connectorCreds } : {},
|
|
17340
17412
|
...executionEnv !== void 0 ? { executionEnv } : {},
|
|
@@ -19708,6 +19780,39 @@ var init_kernel_bridge = __esm({
|
|
|
19708
19780
|
throw new KernelError("\u5185\u5BB9\u5DF2\u88AB\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6587\u4EF6\u540E\u518D\u4FDD\u5B58", "content-conflict");
|
|
19709
19781
|
}
|
|
19710
19782
|
}
|
|
19783
|
+
/** 暂停时保存人工完整稿:不借 work.create 开运行,不改暂停位,验收/下游调度留待恢复。 */
|
|
19784
|
+
async recordPausedRevision(args) {
|
|
19785
|
+
if (!args.actor.startsWith("actor:human:")) {
|
|
19786
|
+
throw new KernelError("\u6682\u505C\u671F\u95F4\u5F55\u5165\u5B8C\u6574\u7A3F\u4EC5\u9650\u4EBA\u7C7B\u64CD\u4F5C", "forbidden");
|
|
19787
|
+
}
|
|
19788
|
+
await this.refreshTracked();
|
|
19789
|
+
const wid = this.wo(args.artifactId);
|
|
19790
|
+
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
19791
|
+
const node2 = snap?.nodes.find((n) => n.id === args.artifactId);
|
|
19792
|
+
if (!snap?.workorder.dispatchPaused || !node2 || node2.cancelledAt) {
|
|
19793
|
+
throw new KernelError("\u5DE5\u5355\u6216\u8282\u70B9\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u4FDD\u5B58", "revision-conflict");
|
|
19794
|
+
}
|
|
19795
|
+
const workId = deriveWorkId(node2.id, snap.works.filter((w2) => w2.nodeId === node2.id).length + 1);
|
|
19796
|
+
await this.commit({
|
|
19797
|
+
companyId: "",
|
|
19798
|
+
workorderId: wid,
|
|
19799
|
+
actorId: args.actor,
|
|
19800
|
+
event: {
|
|
19801
|
+
kind: "work.snapshot_recorded",
|
|
19802
|
+
workorderId: wid,
|
|
19803
|
+
nodeId: node2.id,
|
|
19804
|
+
workId,
|
|
19805
|
+
expectedLatestWorkId: node2.latestWorkId,
|
|
19806
|
+
contentRef: args.contentRef,
|
|
19807
|
+
conclusion: args.note
|
|
19808
|
+
}
|
|
19809
|
+
});
|
|
19810
|
+
const after = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
19811
|
+
if (!after?.artifacts.some((a) => a.workId === workId && a.contentRef === args.contentRef)) {
|
|
19812
|
+
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");
|
|
19813
|
+
}
|
|
19814
|
+
return this.model.revisions.get(workId);
|
|
19815
|
+
}
|
|
19711
19816
|
async proposeRevision(args) {
|
|
19712
19817
|
await this.refreshTracked();
|
|
19713
19818
|
const wid = this.wo(args.artifactId);
|
|
@@ -29417,11 +29522,22 @@ async function sweepStaleChatTurns(deps) {
|
|
|
29417
29522
|
deps.log?.(
|
|
29418
29523
|
`[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
29524
|
);
|
|
29525
|
+
const settledStatus = outcome.settled ? outcome.status : void 0;
|
|
29526
|
+
if (deps.onTurnSettled && settledStatus && settledStatus !== "succeeded") {
|
|
29527
|
+
await deps.onTurnSettled({
|
|
29528
|
+
chatSessionId: turn.chatSessionId,
|
|
29529
|
+
turnId: turn.id,
|
|
29530
|
+
status: settledStatus,
|
|
29531
|
+
reason: outcome.reason ?? `\u8F6E\u6B21\u6536\u53E3\u4E3A ${settledStatus}`
|
|
29532
|
+
}).catch((e) => {
|
|
29533
|
+
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)}`);
|
|
29534
|
+
});
|
|
29535
|
+
}
|
|
29420
29536
|
}
|
|
29421
29537
|
return settled;
|
|
29422
29538
|
}
|
|
29423
29539
|
async function sweepStaleChatTurnsAcrossCompanies(deps) {
|
|
29424
|
-
const { listCompanies, storesFor, onCompanyError, log: log3, ...rest } = deps;
|
|
29540
|
+
const { listCompanies, storesFor, onCompanyError, log: log3, onTurnSettled, ...rest } = deps;
|
|
29425
29541
|
const settled = [];
|
|
29426
29542
|
for (const companyId of await listCompanies()) {
|
|
29427
29543
|
try {
|
|
@@ -29431,7 +29547,9 @@ async function sweepStaleChatTurnsAcrossCompanies(deps) {
|
|
|
29431
29547
|
turns: stores.turns,
|
|
29432
29548
|
...stores.assistantRowStatus ? { assistantRowStatus: stores.assistantRowStatus } : {},
|
|
29433
29549
|
...stores.settleAssistantRow ? { settleAssistantRow: stores.settleAssistantRow } : {},
|
|
29434
|
-
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {}
|
|
29550
|
+
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
|
|
29551
|
+
// 把这一家的 companyId 补进去再往外报——下游(委派台账)按公司分 store,少了它就查错库。
|
|
29552
|
+
...onTurnSettled ? { onTurnSettled: (info) => onTurnSettled({ ...info, ...companyId ? { companyId } : {} }) } : {}
|
|
29435
29553
|
});
|
|
29436
29554
|
settled.push(...outcomes);
|
|
29437
29555
|
} catch (err) {
|
|
@@ -159619,7 +159737,7 @@ function normalizedOpToV3Op(op, kind) {
|
|
|
159619
159737
|
if (op === "set_text") return "set_text";
|
|
159620
159738
|
if (op === "patch_input") return "patch_input";
|
|
159621
159739
|
if (op === "set_status") {
|
|
159622
|
-
return
|
|
159740
|
+
return "completed";
|
|
159623
159741
|
}
|
|
159624
159742
|
return void 0;
|
|
159625
159743
|
}
|
|
@@ -159803,7 +159921,7 @@ var init_live_chat = __esm({
|
|
|
159803
159921
|
if (!turn.items) return;
|
|
159804
159922
|
const itemId = outcome.itemId;
|
|
159805
159923
|
const itemType = normalizedKindToItemType(event.kind);
|
|
159806
|
-
|
|
159924
|
+
let operation = normalizedOpToV3Op(event.opShape.op, event.kind);
|
|
159807
159925
|
if (!operation) return;
|
|
159808
159926
|
const perItem = turn.v3Items.get(itemId) ?? { textOffset: 0, started: false };
|
|
159809
159927
|
const isTextual2 = event.kind === "text" || event.kind === "thinking";
|
|
@@ -159839,6 +159957,7 @@ var init_live_chat = __esm({
|
|
|
159839
159957
|
payload = { input: opShape.input };
|
|
159840
159958
|
} else if (opShape.op === "set_status") {
|
|
159841
159959
|
if (opShape.status === "streaming") return;
|
|
159960
|
+
operation = opShape.status === "completed" ? "completed" : "failed";
|
|
159842
159961
|
}
|
|
159843
159962
|
turn.v3Items.set(itemId, perItem);
|
|
159844
159963
|
const frame = {
|
|
@@ -159969,6 +160088,24 @@ var init_live_chat = __esm({
|
|
|
159969
160088
|
this.publishV3(turn, this.assignV3Seq(turn, frame));
|
|
159970
160089
|
return true;
|
|
159971
160090
|
}
|
|
160091
|
+
/** 恢复路在 DB 收口旧 item 后,将同一身份/版本的终态发给已挂上的观众。 */
|
|
160092
|
+
publishItemStatus(chatSessionId, item) {
|
|
160093
|
+
const turn = this.turns.get(chatSessionId);
|
|
160094
|
+
if (!turn || turn.status !== "running" || item.sessionId !== chatSessionId || item.status === "streaming") return false;
|
|
160095
|
+
if (turn.items?.turnId !== item.turnId) return false;
|
|
160096
|
+
this.flushPendingV3(turn);
|
|
160097
|
+
this.publishV3(turn, this.assignV3Seq(turn, {
|
|
160098
|
+
protocolVersion: 3,
|
|
160099
|
+
streamId: turn.liveStreamId,
|
|
160100
|
+
turnId: item.turnId,
|
|
160101
|
+
itemId: item.id,
|
|
160102
|
+
itemType: normalizedKindToItemType(item.kind),
|
|
160103
|
+
operation: item.status === "completed" ? "completed" : "failed",
|
|
160104
|
+
itemVersion: item.version,
|
|
160105
|
+
...item.ord !== null ? { ord: item.ord } : {}
|
|
160106
|
+
}));
|
|
160107
|
+
return true;
|
|
160108
|
+
}
|
|
159972
160109
|
emitV3TurnTerminal(turn, status) {
|
|
159973
160110
|
if (status !== "done" && status !== "error") return;
|
|
159974
160111
|
const operation = status === "done" ? "turn_completed" : "turn_failed";
|
|
@@ -195686,6 +195823,7 @@ var init_service3 = __esm({
|
|
|
195686
195823
|
const store = await this.options.resolveStore(companyId).catch(() => null);
|
|
195687
195824
|
await store?.patch(record8.id, { childRunId: anchor }).catch(() => null);
|
|
195688
195825
|
}
|
|
195826
|
+
await this.bindChildRunToPlaceholder(record8, companyId, claim.turn.assistantRunId ?? void 0);
|
|
195689
195827
|
await notDispatched("resumed-existing-run");
|
|
195690
195828
|
return;
|
|
195691
195829
|
}
|
|
@@ -195700,7 +195838,15 @@ var init_service3 = __esm({
|
|
|
195700
195838
|
...companyId ? { companyId } : {},
|
|
195701
195839
|
...snapshot ? { parentConversationSnapshot: snapshot } : {},
|
|
195702
195840
|
extraSystemPrompt: EXPERT_DELEGATION_SYSTEM_PROMPT,
|
|
195703
|
-
..._DelegationService.inboundBundle(inbound) ? { workspaceBundle: _DelegationService.inboundBundle(inbound) } : {}
|
|
195841
|
+
..._DelegationService.inboundBundle(inbound) ? { workspaceBundle: _DelegationService.inboundBundle(inbound) } : {},
|
|
195842
|
+
/* 派发这一侧要拿它们建 `chat_items` 账本(见本文件 `DelegationDispatchRequest` 的注释)。
|
|
195843
|
+
轮次口径与 `bindChildRunToPlaceholder` / `seedChildRoundRows` **逐字一致**:
|
|
195844
|
+
`record.roundSummaries.length + 1`。三处一旦不同源,item 就会挂到上一轮那条回答行上。 */
|
|
195845
|
+
assistantMessageId: childRoundMessageIds(
|
|
195846
|
+
record8.childSessionId,
|
|
195847
|
+
record8.roundSummaries.length + 1
|
|
195848
|
+
).placeholderId,
|
|
195849
|
+
chatTurnId: turnId
|
|
195704
195850
|
});
|
|
195705
195851
|
outcomeSettled = true;
|
|
195706
195852
|
await opts?.onDispatched?.().catch((err) => console.warn(`[delegation] \u5F85\u8F6C\u8FBE\u6807\u8BB0\u5DF2\u6D88\u8D39\u5931\u8D25\uFF08${record8.id}\uFF09: ${String(err)}`));
|
|
@@ -195719,6 +195865,7 @@ var init_service3 = __esm({
|
|
|
195719
195865
|
const store = await this.options.resolveStore(companyId);
|
|
195720
195866
|
await store.patch(record8.id, { childRunId: anchor }).catch(() => null);
|
|
195721
195867
|
}
|
|
195868
|
+
await this.bindChildRunToPlaceholder(record8, companyId, dispatched.runId);
|
|
195722
195869
|
this.track(this.settleRound(record8, dispatched, companyId, () => output, turnId, turns));
|
|
195723
195870
|
} catch (error2) {
|
|
195724
195871
|
const failedAt = this.now();
|
|
@@ -195835,6 +195982,29 @@ var init_service3 = __esm({
|
|
|
195835
195982
|
* 委派台账是同一类账,只是多一层——它还得回流。所以这里只做两件:把行拍成 `failed/interrupted`、
|
|
195836
195983
|
* 然后走同一个 {@link notifySettled}。已经是终态的行原样返回(幂等,重复扫不重复回流)。
|
|
195837
195984
|
*/
|
|
195985
|
+
/**
|
|
195986
|
+
* 把这一轮的 run 锚点写到子会话那条**回答占位行**上。
|
|
195987
|
+
*
|
|
195988
|
+
* 为什么非它不可:serve 重启后,daemon 的 hello 对账与会话进程自报身份**共用**
|
|
195989
|
+
* `planChatTurnRecovery`,而那个函数第一行就是 `if (!row.runId) continue` —— 占位行没有 runId,
|
|
195990
|
+
* 这条子会话就永远配不出恢复计划,答 `unknown`,然后被 hub evict(杀掉)。
|
|
195991
|
+
* 轮次是第几轮按 `roundSummaries.length + 1` 算,与 `seedChildRoundRows` 那两处口径逐字一致。
|
|
195992
|
+
*
|
|
195993
|
+
* 失败只记日志:这是加固,不是这一轮派发成立的前提。
|
|
195994
|
+
*/
|
|
195995
|
+
async bindChildRunToPlaceholder(record8, companyId, runId) {
|
|
195996
|
+
if (!runId) return;
|
|
195997
|
+
const round = record8.roundSummaries.length + 1;
|
|
195998
|
+
const { placeholderId } = childRoundMessageIds(record8.childSessionId, round);
|
|
195999
|
+
try {
|
|
196000
|
+
const chatStore = await this.options.resolveChatSessions(companyId);
|
|
196001
|
+
await chatStore.updateMessage(placeholderId, { runId });
|
|
196002
|
+
} catch (err) {
|
|
196003
|
+
console.warn(
|
|
196004
|
+
`[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`
|
|
196005
|
+
);
|
|
196006
|
+
}
|
|
196007
|
+
}
|
|
195838
196008
|
async settleFromSweep(childSessionId, reason, companyId) {
|
|
195839
196009
|
const store = await this.options.resolveStore(companyId).catch(() => null);
|
|
195840
196010
|
if (!store) return null;
|
|
@@ -196940,7 +197110,11 @@ function toChatDispatchRequest(request2) {
|
|
|
196940
197110
|
delegatedChildTurn: true,
|
|
196941
197111
|
// §6.1.3 去程:`--file` 带来的字节就在这一格。**丢了它命令照样回 202**,
|
|
196942
197112
|
// 而专家的工作区里什么都没有——这正是上一版真实发生过的形态。
|
|
196943
|
-
...request2.workspaceBundle ? { workspaceBundle: request2.workspaceBundle } : {}
|
|
197113
|
+
...request2.workspaceBundle ? { workspaceBundle: request2.workspaceBundle } : {},
|
|
197114
|
+
// 这两格漏了同样**不报错**:子会话照跑、正文照回,只是 `chat_items` 的行没有 message_id、
|
|
197115
|
+
// turnId 回落成 `chat-run-turn:<runId>`,右栏面板的增量流对不上轮——正是本文件存在的理由。
|
|
197116
|
+
...request2.assistantMessageId ? { assistantMessageId: request2.assistantMessageId } : {},
|
|
197117
|
+
...request2.chatTurnId ? { chatTurnId: request2.chatTurnId } : {}
|
|
196944
197118
|
};
|
|
196945
197119
|
}
|
|
196946
197120
|
function applyWorkspaceBundle(target, bundle) {
|
|
@@ -196957,12 +197131,15 @@ var init_dispatch_adapter = __esm({
|
|
|
196957
197131
|
// ../server/src/domains/delegations/sweep-hook.ts
|
|
196958
197132
|
function delegationSweepHook(deps) {
|
|
196959
197133
|
const log3 = deps.log ?? ((m2) => console.warn(m2));
|
|
196960
|
-
return async (chatSessionId, reason) => {
|
|
196961
|
-
|
|
196962
|
-
|
|
196963
|
-
|
|
196964
|
-
|
|
196965
|
-
|
|
197134
|
+
return async (chatSessionId, reason, _messageId, knownCompanyId) => {
|
|
197135
|
+
let companyId = knownCompanyId;
|
|
197136
|
+
if (companyId === void 0) {
|
|
197137
|
+
const session = await deps.sessions.getSession(chatSessionId).catch((err) => {
|
|
197138
|
+
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`);
|
|
197139
|
+
return null;
|
|
197140
|
+
});
|
|
197141
|
+
companyId = session?.companyId ?? void 0;
|
|
197142
|
+
}
|
|
196966
197143
|
await deps.settle(chatSessionId, reason, companyId).catch((err) => {
|
|
196967
197144
|
log3(`[chat-sweep] \u59D4\u6D3E\u53F0\u8D26\u6536\u655B\u5931\u8D25\uFF08\u4F1A\u8BDD ${chatSessionId} \u516C\u53F8 ${companyId ?? "\u9ED8\u8BA4"}\uFF09: ${String(err)}`);
|
|
196968
197145
|
});
|
|
@@ -197157,27 +197334,77 @@ var init_assembly = __esm({
|
|
|
197157
197334
|
});
|
|
197158
197335
|
|
|
197159
197336
|
// ../server/src/domains/delegations/child-live-turn.ts
|
|
197160
|
-
function registerDelegatedChildTurn(liveChat, childSessionId, session) {
|
|
197337
|
+
function registerDelegatedChildTurn(liveChat, childSessionId, session, deps = {}) {
|
|
197338
|
+
const log3 = deps.log ?? ((m2) => console.warn(m2));
|
|
197161
197339
|
const appendInput = session.appendInput;
|
|
197162
|
-
|
|
197340
|
+
const canAppend = typeof appendInput === "function" && session.canAppendInput !== false;
|
|
197341
|
+
const ledger = new ChatItemLedger({
|
|
197342
|
+
...deps.items ? { items: deps.items } : {},
|
|
197343
|
+
sessionId: childSessionId,
|
|
197344
|
+
turnId: deps.turnId ?? fallbackTurnId(session.runId),
|
|
197345
|
+
...session.runId ? { runId: session.runId } : {},
|
|
197346
|
+
...deps.assistantMessageId ? { messageId: deps.assistantMessageId } : {},
|
|
197347
|
+
...deps.versionSeed !== void 0 ? { versionSeed: deps.versionSeed } : {},
|
|
197348
|
+
log: log3
|
|
197349
|
+
});
|
|
197163
197350
|
const ctrl = liveChat.start(childSessionId, {
|
|
197164
197351
|
runtimeSessionId: session.id,
|
|
197165
197352
|
...session.runId ? { runId: session.runId } : {},
|
|
197166
197353
|
kill: () => {
|
|
197167
197354
|
void session.kill?.();
|
|
197168
197355
|
},
|
|
197169
|
-
|
|
197356
|
+
// 纪律 ①:插不进去的 runtime 也登记,只是这两格照实报。
|
|
197357
|
+
...canAppend && appendInput ? { appendInput: (input) => appendInput.call(session, input) } : {},
|
|
197170
197358
|
// **每次现读**,不快照:一轮跑到收尾时 stdin 会先关,那之后 runtime 自己会回
|
|
197171
197359
|
// `session-closing`,判断权本就该留在它那儿(同 `/api/chat` 那条路的写法)。
|
|
197172
197360
|
get canAppendInput() {
|
|
197173
|
-
return session.canAppendInput !== false;
|
|
197361
|
+
return typeof session.appendInput === "function" && session.canAppendInput !== false;
|
|
197362
|
+
}
|
|
197363
|
+
}, { items: ledger });
|
|
197364
|
+
if (deps.assistantMessageId) ctrl.setAssistantMessageId(deps.assistantMessageId);
|
|
197365
|
+
const hasChannels = typeof session.onOutput === "function" || typeof session.onTelemetry === "function" || typeof session.onLiveEvent === "function";
|
|
197366
|
+
const normalized4 = typeof session.onNormalizedProviderEvent === "function" ? {
|
|
197367
|
+
onNormalizedProviderEvent: session.onNormalizedProviderEvent.bind(session),
|
|
197368
|
+
finishTurn: (signal) => session.finishNormalizedTurn?.(signal)
|
|
197369
|
+
} : hasChannels ? attachStreamNormalizer(
|
|
197370
|
+
{
|
|
197371
|
+
id: session.id,
|
|
197372
|
+
onOutput: (cb) => session.onOutput?.(cb),
|
|
197373
|
+
onTelemetry: (cb) => session.onTelemetry?.(cb),
|
|
197374
|
+
onLiveEvent: (cb) => session.onLiveEvent?.(cb),
|
|
197375
|
+
...session.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: session.supportsLiveProtocol } : {}
|
|
197376
|
+
},
|
|
197377
|
+
{
|
|
197378
|
+
providerName: deps.runtimeKind ?? "runtime",
|
|
197379
|
+
fallbackTurnId: `oasis-turn:${session.runId ?? session.id}`
|
|
197380
|
+
}
|
|
197381
|
+
) : null;
|
|
197382
|
+
normalized4?.onNormalizedProviderEvent((event) => {
|
|
197383
|
+
try {
|
|
197384
|
+
ctrl.applyNormalizedEvent(event);
|
|
197385
|
+
} catch {
|
|
197174
197386
|
}
|
|
197175
197387
|
});
|
|
197176
|
-
|
|
197388
|
+
const settle = async (signal) => {
|
|
197389
|
+
try {
|
|
197390
|
+
normalized4?.finishTurn(signal);
|
|
197391
|
+
} catch {
|
|
197392
|
+
}
|
|
197393
|
+
try {
|
|
197394
|
+
ledger.finish(signal);
|
|
197395
|
+
await ledger.drain();
|
|
197396
|
+
} catch (err) {
|
|
197397
|
+
log3(`[delegation] \u5B50\u4F1A\u8BDD ${childSessionId} \u8D26\u672C\u6536\u5C3E\u5931\u8D25: ${String(err)}`);
|
|
197398
|
+
}
|
|
197399
|
+
ctrl.finish(signal === "completed" ? "done" : "error");
|
|
197400
|
+
};
|
|
197401
|
+
void session.done.then(() => settle("completed"), () => settle("failed"));
|
|
197177
197402
|
}
|
|
197178
197403
|
var init_child_live_turn = __esm({
|
|
197179
197404
|
"../server/src/domains/delegations/child-live-turn.ts"() {
|
|
197180
197405
|
"use strict";
|
|
197406
|
+
init_src6();
|
|
197407
|
+
init_chat_item_ledger();
|
|
197181
197408
|
}
|
|
197182
197409
|
});
|
|
197183
197410
|
|
|
@@ -197291,10 +197518,12 @@ var init_tool_home = __esm({
|
|
|
197291
197518
|
});
|
|
197292
197519
|
|
|
197293
197520
|
// ../connectors/src/_base/install.ts
|
|
197294
|
-
function installPlan(cmd, platform2, toolsPrefix) {
|
|
197295
|
-
|
|
197521
|
+
function installPlan(cmd, platform2, toolsPrefix, version2) {
|
|
197522
|
+
const npmPkg = NPM_TOOL_PACKAGES[cmd];
|
|
197523
|
+
if (npmPkg) {
|
|
197296
197524
|
const npm = platform2 === "win32" ? "npm.cmd" : "npm";
|
|
197297
|
-
|
|
197525
|
+
const spec = version2 ? `${npmPkg}@${version2}` : npmPkg;
|
|
197526
|
+
return [{ file: npm, args: ["install", "-g", "--prefix", toolsPrefix, spec], ensureDir: toolsPrefix }];
|
|
197298
197527
|
}
|
|
197299
197528
|
const pkg = cmd === "gh" ? { brew: "gh", apt: "gh", dnf: "gh", pacman: "github-cli", apk: "github-cli", winget: "GitHub.cli", choco: "gh", scoop: "gh" } : { brew: "git", apt: "git", dnf: "git", pacman: "git", apk: "git", winget: "Git.Git", choco: "git", scoop: "git" };
|
|
197300
197529
|
if (platform2 === "darwin") {
|
|
@@ -197339,17 +197568,54 @@ function defaultDeps() {
|
|
|
197339
197568
|
mkdirp: (dir) => {
|
|
197340
197569
|
(0, import_node_fs9.mkdirSync)(dir, { recursive: true });
|
|
197341
197570
|
},
|
|
197342
|
-
log: (msg) => console.warn(msg)
|
|
197571
|
+
log: (msg) => console.warn(msg),
|
|
197572
|
+
installedVersion: async (pkg, prefix) => readInstalledVersion(pkg, prefix)
|
|
197343
197573
|
};
|
|
197344
197574
|
}
|
|
197345
|
-
|
|
197575
|
+
function readInstalledVersion(pkg, prefix) {
|
|
197576
|
+
const parts = pkg.split("/");
|
|
197577
|
+
for (const mid of [["lib", "node_modules"], ["node_modules"]]) {
|
|
197578
|
+
const file = (0, import_node_path11.join)(prefix, ...mid, ...parts, "package.json");
|
|
197579
|
+
if (!(0, import_node_fs9.existsSync)(file)) continue;
|
|
197580
|
+
try {
|
|
197581
|
+
const v2 = JSON.parse((0, import_node_fs9.readFileSync)(file, "utf8")).version;
|
|
197582
|
+
if (typeof v2 === "string" && v2) return v2;
|
|
197583
|
+
} catch {
|
|
197584
|
+
}
|
|
197585
|
+
}
|
|
197586
|
+
return null;
|
|
197587
|
+
}
|
|
197588
|
+
async function alignToWantedVersion(cmd, wanted, d) {
|
|
197589
|
+
if (alignedVersion.get(cmd) === wanted) return;
|
|
197590
|
+
const pkg = NPM_TOOL_PACKAGES[cmd];
|
|
197591
|
+
if (!pkg) return;
|
|
197592
|
+
const installed = await d.installedVersion(pkg, d.toolsPrefix);
|
|
197593
|
+
if (!installed) return;
|
|
197594
|
+
if (installed === wanted) {
|
|
197595
|
+
alignedVersion.set(cmd, wanted);
|
|
197596
|
+
return;
|
|
197597
|
+
}
|
|
197598
|
+
const npm = d.platform === "win32" ? "npm.cmd" : "npm";
|
|
197599
|
+
d.log(`[connector-install] ${cmd} ${installed} \u2192 ${wanted}\uFF08\u670D\u52A1\u7AEF\u767B\u8BB0\u7248\u672C\uFF09\uFF0C\u5B89\u88C5\u4E2D\u2026`);
|
|
197600
|
+
try {
|
|
197601
|
+
d.mkdirp(d.toolsPrefix);
|
|
197602
|
+
await d.run(npm, ["install", "-g", "--prefix", d.toolsPrefix, `${pkg}@${wanted}`]);
|
|
197603
|
+
alignedVersion.set(cmd, wanted);
|
|
197604
|
+
d.log(`[connector-install] ${cmd} \u5DF2\u5BF9\u9F50\u5230 ${wanted}`);
|
|
197605
|
+
} catch (err) {
|
|
197606
|
+
d.log(`[connector-install] ${cmd} \u5BF9\u9F50\u5230 ${wanted} \u5931\u8D25\uFF0C\u7EE7\u7EED\u7528 ${installed}\uFF1A${String(err)}`);
|
|
197607
|
+
}
|
|
197608
|
+
}
|
|
197609
|
+
async function ensureToolInstalled(cmd, opts = {}) {
|
|
197610
|
+
const { versions, ...deps } = opts;
|
|
197346
197611
|
const d = { ...defaultDeps(), ...deps };
|
|
197347
|
-
|
|
197348
|
-
if (await d.isPresent(cmd)) {
|
|
197612
|
+
const wanted = versions?.[cmd];
|
|
197613
|
+
if (confirmed.has(cmd) || await d.isPresent(cmd)) {
|
|
197349
197614
|
confirmed.add(cmd);
|
|
197615
|
+
if (wanted) await alignToWantedVersion(cmd, wanted, d);
|
|
197350
197616
|
return true;
|
|
197351
197617
|
}
|
|
197352
|
-
const steps = installPlan(cmd, d.platform, d.toolsPrefix);
|
|
197618
|
+
const steps = installPlan(cmd, d.platform, d.toolsPrefix, wanted);
|
|
197353
197619
|
if (steps.length === 0) {
|
|
197354
197620
|
d.log(`[connector-install] ${cmd} \u7F3A\u5931\uFF0C\u4E14 ${d.platform} \u4E0B\u65E0\u5DF2\u77E5\u81EA\u52A8\u5B89\u88C5\u6CD5\u2014\u2014\u8BF7\u624B\u52A8\u5B89\u88C5`);
|
|
197355
197621
|
return false;
|
|
@@ -197362,6 +197628,7 @@ async function ensureToolInstalled(cmd, deps = {}) {
|
|
|
197362
197628
|
await d.run(step.file, step.args);
|
|
197363
197629
|
if (await d.isPresent(cmd)) {
|
|
197364
197630
|
confirmed.add(cmd);
|
|
197631
|
+
if (wanted) alignedVersion.set(cmd, wanted);
|
|
197365
197632
|
d.log(`[connector-install] ${cmd} \u5B89\u88C5\u6210\u529F\uFF08${step.file} ${step.args.join(" ")}\uFF09`);
|
|
197366
197633
|
return true;
|
|
197367
197634
|
}
|
|
@@ -197372,15 +197639,15 @@ async function ensureToolInstalled(cmd, deps = {}) {
|
|
|
197372
197639
|
d.log(`[connector-install] ${cmd} \u81EA\u52A8\u5B89\u88C5\u672A\u6210\u529F\u2014\u2014\u5C06\u56DE\u9000\u5230 wrapper/\u547D\u4EE4\u81EA\u8EAB\u62A5\u9519`);
|
|
197373
197640
|
return false;
|
|
197374
197641
|
}
|
|
197375
|
-
async function ensureConnectorTools(tools,
|
|
197642
|
+
async function ensureConnectorTools(tools, opts = {}) {
|
|
197376
197643
|
if (!tools || tools.length === 0) return [];
|
|
197377
197644
|
const missing = [];
|
|
197378
197645
|
for (const cmd of tools) {
|
|
197379
|
-
if (!await ensureToolInstalled(cmd,
|
|
197646
|
+
if (!await ensureToolInstalled(cmd, opts)) missing.push(cmd);
|
|
197380
197647
|
}
|
|
197381
197648
|
return missing;
|
|
197382
197649
|
}
|
|
197383
|
-
var import_node_child_process12, import_node_util2, import_node_fs9, import_node_path11, execFileAsync, confirmed;
|
|
197650
|
+
var import_node_child_process12, import_node_util2, import_node_fs9, import_node_path11, execFileAsync, NPM_TOOL_PACKAGES, confirmed, alignedVersion;
|
|
197384
197651
|
var init_install = __esm({
|
|
197385
197652
|
"../connectors/src/_base/install.ts"() {
|
|
197386
197653
|
"use strict";
|
|
@@ -197390,7 +197657,9 @@ var init_install = __esm({
|
|
|
197390
197657
|
import_node_path11 = require("node:path");
|
|
197391
197658
|
init_tool_home();
|
|
197392
197659
|
execFileAsync = (0, import_node_util2.promisify)(import_node_child_process12.execFile);
|
|
197660
|
+
NPM_TOOL_PACKAGES = { "lark-cli": "@larksuite/cli" };
|
|
197393
197661
|
confirmed = /* @__PURE__ */ new Set();
|
|
197662
|
+
alignedVersion = /* @__PURE__ */ new Map();
|
|
197394
197663
|
}
|
|
197395
197664
|
});
|
|
197396
197665
|
|
|
@@ -201461,7 +201730,7 @@ var init_connector_adapter = __esm({
|
|
|
201461
201730
|
async spawn(job) {
|
|
201462
201731
|
const t0 = Date.now();
|
|
201463
201732
|
if (job.requiredTools?.length) {
|
|
201464
|
-
const missing = await ensureConnectorTools(job.requiredTools);
|
|
201733
|
+
const missing = await ensureConnectorTools(job.requiredTools, { versions: job.requiredToolVersions });
|
|
201465
201734
|
if (missing.length) log("[adapter]", ` connector tools still missing (agent may fail): ${missing.join(", ")}`);
|
|
201466
201735
|
}
|
|
201467
201736
|
const prepared = await prepareConnectorsForJob(job);
|
|
@@ -203125,13 +203394,22 @@ async function startOasisServer(opts) {
|
|
|
203125
203394
|
resolveStore: opts.delegationStoreFor,
|
|
203126
203395
|
resolveChatSessions: resolveChatSessionsForDelegation,
|
|
203127
203396
|
resolveActors: async (companyId) => (await actorsDomain2.resolveCtx(companyId)).service,
|
|
203128
|
-
|
|
203129
|
-
|
|
203130
|
-
|
|
203131
|
-
|
|
203397
|
+
/* **派发完顺手把子会话这一轮登记进 live 注册表,并给它接上 `chat_items` 账本。**
|
|
203398
|
+
两件事的成因不同,都在 `child-live-turn.ts` 的文件头:
|
|
203399
|
+
· 不登记 → 下面那行 `appendToChild` 恒回 `no-live-turn`,每一条 `--follow-up` 转达
|
|
203400
|
+
都落队列,专家要等本轮跑完才看见「手上这段作废」(v2 bug 0088);
|
|
203401
|
+
· 不接账本 → 子会话在 `chat_items` 里只有 storage 层镜像来的一问一答两行纯文字,
|
|
203402
|
+
右栏面板只能靠详情接口拿 run_id 去轨迹表**现折**,跑的过程中一片空白。 */
|
|
203132
203403
|
dispatchChat: async (request2) => {
|
|
203133
203404
|
const session = await dispatchChat(request2);
|
|
203134
|
-
|
|
203405
|
+
const itemStore = opts.resolveChatItems ? await opts.resolveChatItems(request2.companyId).catch(() => void 0) : opts.chatItems;
|
|
203406
|
+
const versionSeed = itemStore ? await itemStore.sessionVersionCursor(request2.chatSessionId).catch(() => 0) : 0;
|
|
203407
|
+
registerDelegatedChildTurn(liveChat, request2.chatSessionId, session, {
|
|
203408
|
+
...itemStore ? { items: itemStore } : {},
|
|
203409
|
+
...request2.chatTurnId ? { turnId: request2.chatTurnId } : {},
|
|
203410
|
+
...request2.assistantMessageId ? { assistantMessageId: request2.assistantMessageId } : {},
|
|
203411
|
+
versionSeed
|
|
203412
|
+
});
|
|
203135
203413
|
return session;
|
|
203136
203414
|
},
|
|
203137
203415
|
appendToChild: (childSessionId, text5, extra) => liveChat.append(childSessionId, text5, extra),
|
|
@@ -203906,6 +204184,7 @@ async function startOasisServer(opts) {
|
|
|
203906
204184
|
const rawBody = Buffer.concat(chunks).toString("utf8");
|
|
203907
204185
|
const companyCtx = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
|
|
203908
204186
|
const currentCompanyId = companyCtx?.kind === "ok" ? companyCtx.companyId : void 0;
|
|
204187
|
+
const connectorActorsService = async (companyId) => opts.actors ? (await opts.actors.resolveCtx(companyId ?? currentCompanyId)).service : void 0;
|
|
203909
204188
|
const wodrafts = opts.resolveWorkorderDrafts ? await opts.resolveWorkorderDrafts(currentCompanyId) : opts.workorderDrafts;
|
|
203910
204189
|
const wodraftPlannerIssues = (opts.resolveWorkorderDraftPlannerIssues && await opts.resolveWorkorderDraftPlannerIssues(currentCompanyId)) ?? opts.workorderDraftPlannerIssues ?? defaultDraftPlannerIssuesStore;
|
|
203911
204190
|
const engine = await resolveEngine(currentCompanyId);
|
|
@@ -205969,12 +206248,12 @@ ${composed}`;
|
|
|
205969
206248
|
const actorCtx = await opts.resolveActorContext(body2.actorId).catch(() => null);
|
|
205970
206249
|
if (actorCtx) {
|
|
205971
206250
|
const { mkdtempSync: mkdtempSync6, mkdirSync: mkdirSync25, writeFileSync: writeFileSync18 } = await import("node:fs");
|
|
205972
|
-
const { join:
|
|
206251
|
+
const { join: join40, dirname: dirname36 } = await import("node:path");
|
|
205973
206252
|
const { tmpdir: tmpdir12 } = await import("node:os");
|
|
205974
|
-
const dir = mkdtempSync6(
|
|
206253
|
+
const dir = mkdtempSync6(join40(tmpdir12(), "oasis-chat-"));
|
|
205975
206254
|
if (actorCtx.config?.prompt) {
|
|
205976
206255
|
for (const [rel, content3] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
|
|
205977
|
-
const file =
|
|
206256
|
+
const file = join40(dir, rel);
|
|
205978
206257
|
mkdirSync25(dirname36(file), { recursive: true });
|
|
205979
206258
|
writeFileSync18(file, content3);
|
|
205980
206259
|
}
|
|
@@ -205986,12 +206265,12 @@ ${composed}`;
|
|
|
205986
206265
|
const s2 = byId.get(id);
|
|
205987
206266
|
return s2 ? `- **${s2.name}** (\`${s2.id}\`): ${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
|
|
205988
206267
|
});
|
|
205989
|
-
writeFileSync18(
|
|
206268
|
+
writeFileSync18(join40(dir, "SKILLS.md"), ["# \u53EF\u7528\u6280\u80FD", "", "\u4EE5\u4E0B\u6280\u80FD\u5DF2\u4E3A\u4F60\u542F\u7528\uFF0C\u53EF\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u76F4\u63A5\u4F7F\u7528\uFF1A", "", ...lines].join("\n"));
|
|
205990
206269
|
}
|
|
205991
206270
|
if (opts.materializeSkills) {
|
|
205992
206271
|
const skillFiles = await opts.materializeSkills(body2.actorId, "claude").catch(() => ({}));
|
|
205993
206272
|
for (const [rel, content3] of Object.entries(skillFiles)) {
|
|
205994
|
-
const file =
|
|
206273
|
+
const file = join40(dir, rel);
|
|
205995
206274
|
mkdirSync25(dirname36(file), { recursive: true });
|
|
205996
206275
|
writeFileSync18(file, content3);
|
|
205997
206276
|
}
|
|
@@ -206005,7 +206284,7 @@ ${composed}`;
|
|
|
206005
206284
|
const modeNote = c.mode === "oauth" ? "OAuth \xB7 \u51ED\u8BC1\u7531\u5E73\u53F0\u7BA1\u7406\uFF0C\u901A\u8FC7\u5BF9\u5E94 CLI wrapper \u8C03\u7528" : "\u76F4\u63A5\u5199\u5165 \xB7 \u51ED\u8BC1\u5DF2\u6CE8\u5165\u73AF\u5883\u53D8\u91CF";
|
|
206006
206285
|
return `- **${c.name}** (\`${c.id}\`): ${statusNote} \xB7 ${modeNote}`;
|
|
206007
206286
|
});
|
|
206008
|
-
writeFileSync18(
|
|
206287
|
+
writeFileSync18(join40(dir, "CONNECTORS.md"), ["# \u53EF\u7528\u8FDE\u63A5\u5668", "", "\u4EE5\u4E0B\u8FDE\u63A5\u5668\u5DF2\u4E3A\u672C\u6B21\u4F1A\u8BDD\u914D\u7F6E\uFF0C\u51ED\u8BC1\u5DF2\u901A\u8FC7\u73AF\u5883\u53D8\u91CF\u6216 CLI wrapper \u6CE8\u5165\uFF0C\u65E0\u9700\u624B\u52A8\u914D\u7F6E\uFF1A", "", ...lines].join("\n"));
|
|
206009
206288
|
}
|
|
206010
206289
|
spawnCwd = dir;
|
|
206011
206290
|
}
|
|
@@ -206090,7 +206369,7 @@ ${composed}`;
|
|
|
206090
206369
|
}
|
|
206091
206370
|
if (req.method === "POST" && /^\/api\/connectors\/[^/]+\/disconnect$/.test(url.pathname)) {
|
|
206092
206371
|
const connId = decodeURIComponent(url.pathname.split("/")[3] ?? "");
|
|
206093
|
-
const svc =
|
|
206372
|
+
const svc = await connectorActorsService();
|
|
206094
206373
|
if (!svc) {
|
|
206095
206374
|
res.writeHead(501).end(JSON.stringify({ error: "actors service disabled" }));
|
|
206096
206375
|
return;
|
|
@@ -206116,7 +206395,7 @@ ${composed}`;
|
|
|
206116
206395
|
}
|
|
206117
206396
|
if (req.method === "DELETE" && /^\/api\/connectors\/[^/]+$/.test(url.pathname)) {
|
|
206118
206397
|
const connId = decodeURIComponent(url.pathname.slice("/api/connectors/".length));
|
|
206119
|
-
const svc =
|
|
206398
|
+
const svc = await connectorActorsService();
|
|
206120
206399
|
if (!svc) {
|
|
206121
206400
|
res.writeHead(501).end(JSON.stringify({ error: "actors service disabled" }));
|
|
206122
206401
|
return;
|
|
@@ -206135,6 +206414,31 @@ ${composed}`;
|
|
|
206135
206414
|
}
|
|
206136
206415
|
return;
|
|
206137
206416
|
}
|
|
206417
|
+
if (req.method === "DELETE" && /^\/api\/actors\/[^/]+\/connectors\/[^/]+$/.test(url.pathname)) {
|
|
206418
|
+
const parts = url.pathname.split("/");
|
|
206419
|
+
const actorId = decodeURIComponent(parts[3] ?? "");
|
|
206420
|
+
const connId = decodeURIComponent(parts[5] ?? "");
|
|
206421
|
+
const svc = await connectorActorsService();
|
|
206422
|
+
if (!svc) {
|
|
206423
|
+
res.writeHead(501).end(JSON.stringify({ error: "actors service disabled" }));
|
|
206424
|
+
return;
|
|
206425
|
+
}
|
|
206426
|
+
try {
|
|
206427
|
+
const { variablesDeleted } = await svc.deleteActorConnector(actorId, connId);
|
|
206428
|
+
let channelDisabled = false;
|
|
206429
|
+
if (connId === "feishu" && channelService) {
|
|
206430
|
+
const b2 = await channelService.getBindingForActor(actorId);
|
|
206431
|
+
if (b2 && b2.status !== "revoked") {
|
|
206432
|
+
await channelService.disable(actorId);
|
|
206433
|
+
channelDisabled = true;
|
|
206434
|
+
}
|
|
206435
|
+
}
|
|
206436
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, variablesDeleted, channelDisabled }));
|
|
206437
|
+
} catch (e) {
|
|
206438
|
+
res.writeHead(500).end(JSON.stringify({ error: String(e) }));
|
|
206439
|
+
}
|
|
206440
|
+
return;
|
|
206441
|
+
}
|
|
206138
206442
|
if (url.pathname === "/api/connectors/feishu/setup" && req.method === "GET") {
|
|
206139
206443
|
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
206140
206444
|
const send = (obj2) => res.write(`data: ${JSON.stringify(obj2)}
|
|
@@ -206184,8 +206488,8 @@ ${composed}`;
|
|
|
206184
206488
|
if (!errStr && pollData["client_id"]) {
|
|
206185
206489
|
const clientId = String(pollData["client_id"]);
|
|
206186
206490
|
const clientSecret = pollData["client_secret"] ? String(pollData["client_secret"]) : void 0;
|
|
206187
|
-
|
|
206188
|
-
|
|
206491
|
+
const svc = clientSecret ? await connectorActorsService() : void 0;
|
|
206492
|
+
if (svc && clientSecret) {
|
|
206189
206493
|
const credScope = actorIdParam ? { scope: "personal", actorId: actorIdParam } : { scope: "global" };
|
|
206190
206494
|
await svc.putVariable({ key: "FEISHU_APP_ID", value: clientId, ...credScope, connectorId: "feishu", encrypted: true });
|
|
206191
206495
|
await svc.putVariable({ key: "FEISHU_APP_SECRET", value: clientSecret, ...credScope, connectorId: "feishu", encrypted: true });
|
|
@@ -206248,6 +206552,8 @@ ${composed}`;
|
|
|
206248
206552
|
});
|
|
206249
206553
|
githubAppPending.put(state, {
|
|
206250
206554
|
...actorId ? { actorId } : {},
|
|
206555
|
+
// 回调那一步没有鉴权头,解析不出公司——只能在这里记下来带过去。
|
|
206556
|
+
...currentCompanyId ? { companyId: currentCompanyId } : {},
|
|
206251
206557
|
employeeSlug: nameSlug,
|
|
206252
206558
|
createdAtMs: Date.now(),
|
|
206253
206559
|
// 存下这次选的归属——回调时要拿它跟 GitHub 返回的 owner 比对。
|
|
@@ -206268,7 +206574,7 @@ ${composed}`;
|
|
|
206268
206574
|
if (url.pathname === "/api/connectors/github/app/orgs" && req.method === "POST") {
|
|
206269
206575
|
try {
|
|
206270
206576
|
const { actorId } = JSON.parse(rawBody || "{}");
|
|
206271
|
-
const svc =
|
|
206577
|
+
const svc = await connectorActorsService();
|
|
206272
206578
|
if (!svc) throw new Error("actors service unavailable");
|
|
206273
206579
|
const token = await svc.revealResolvedVariable("GITHUB_TOKEN", actorId);
|
|
206274
206580
|
if (!token) {
|
|
@@ -206301,7 +206607,7 @@ ${composed}`;
|
|
|
206301
206607
|
}
|
|
206302
206608
|
if (url.pathname === "/api/connectors/github/app/available" && req.method === "POST") {
|
|
206303
206609
|
try {
|
|
206304
|
-
const svc =
|
|
206610
|
+
const svc = await connectorActorsService();
|
|
206305
206611
|
if (!svc) throw new Error("actors service unavailable");
|
|
206306
206612
|
const rows = await svc.listVariables();
|
|
206307
206613
|
const plain = (key, actorId) => rows.find((r) => r.key === key && r.actorId === actorId && !r.encrypted)?.maskedValue ?? "";
|
|
@@ -206364,7 +206670,7 @@ ${composed}`;
|
|
|
206364
206670
|
res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u7F3A\u5C11 appId" }));
|
|
206365
206671
|
return;
|
|
206366
206672
|
}
|
|
206367
|
-
const svc =
|
|
206673
|
+
const svc = await connectorActorsService();
|
|
206368
206674
|
if (!svc) throw new Error("actors service unavailable");
|
|
206369
206675
|
const rows = await svc.listVariables();
|
|
206370
206676
|
const owners = rows.filter((r) => r.key === "GITHUB_APP_ID" && !r.encrypted && r.maskedValue === appId);
|
|
@@ -206411,7 +206717,7 @@ ${composed}`;
|
|
|
206411
206717
|
bad(400, "\u8FD9\u4E0D\u50CF\u4E00\u4E2A\u79C1\u94A5\u6587\u4EF6\u2014\u2014\u8BF7\u4E0A\u4F20 GitHub \u4E0B\u8F7D\u7684 .pem\uFF08\u5185\u5BB9\u4EE5 -----BEGIN ... PRIVATE KEY----- \u5F00\u5934\uFF09");
|
|
206412
206718
|
return;
|
|
206413
206719
|
}
|
|
206414
|
-
const svc =
|
|
206720
|
+
const svc = await connectorActorsService();
|
|
206415
206721
|
if (!svc) throw new Error("actors service unavailable");
|
|
206416
206722
|
let meta;
|
|
206417
206723
|
try {
|
|
@@ -206469,7 +206775,7 @@ ${composed}`;
|
|
|
206469
206775
|
if (url.pathname === "/api/connectors/github/app/permissions" && req.method === "POST") {
|
|
206470
206776
|
try {
|
|
206471
206777
|
const { actorId } = JSON.parse(rawBody || "{}");
|
|
206472
|
-
const svc =
|
|
206778
|
+
const svc = await connectorActorsService();
|
|
206473
206779
|
if (!svc) throw new Error("actors service unavailable");
|
|
206474
206780
|
const appId = await svc.revealResolvedVariable("GITHUB_APP_ID", actorId);
|
|
206475
206781
|
const privateKeyPem = await svc.revealResolvedVariable("GITHUB_APP_PRIVATE_KEY", actorId);
|
|
@@ -206523,7 +206829,7 @@ ${composed}`;
|
|
|
206523
206829
|
fail(problem.message);
|
|
206524
206830
|
return;
|
|
206525
206831
|
}
|
|
206526
|
-
const svc =
|
|
206832
|
+
const svc = await connectorActorsService(pending.companyId);
|
|
206527
206833
|
if (!svc) throw new Error("actors service unavailable");
|
|
206528
206834
|
const scope = pending.actorId ? { scope: "personal", actorId: pending.actorId } : { scope: "global" };
|
|
206529
206835
|
const put3 = (key, value2, encrypted) => svc.putVariable({ key, value: value2, ...scope, connectorId: "github", encrypted });
|
|
@@ -206592,7 +206898,7 @@ ${composed}`;
|
|
|
206592
206898
|
reply({ ok: false, error: "pending" });
|
|
206593
206899
|
return;
|
|
206594
206900
|
}
|
|
206595
|
-
const svc =
|
|
206901
|
+
const svc = await connectorActorsService();
|
|
206596
206902
|
if (!svc) throw new Error("actors service unavailable");
|
|
206597
206903
|
const scope = pending.actorId ? { scope: "personal", actorId: pending.actorId } : { scope: "global" };
|
|
206598
206904
|
const put3 = (key, value2, encrypted) => svc.putVariable({ key, value: value2, ...scope, connectorId: "github", encrypted });
|
|
@@ -206681,8 +206987,8 @@ ${composed}`;
|
|
|
206681
206987
|
throw new Error(String(data["error_description"] ?? errStr ?? "login failed"));
|
|
206682
206988
|
}
|
|
206683
206989
|
if (!data["access_token"]) throw new Error("no access_token in response");
|
|
206684
|
-
|
|
206685
|
-
|
|
206990
|
+
const svc = await connectorActorsService();
|
|
206991
|
+
if (svc) {
|
|
206686
206992
|
if (actorId) {
|
|
206687
206993
|
await svc.putVariable({ key: "FEISHU_APP_ID", value: appId, scope: "personal", actorId, connectorId: "feishu", encrypted: true });
|
|
206688
206994
|
} else {
|
|
@@ -206783,8 +207089,8 @@ ${composed}`;
|
|
|
206783
207089
|
});
|
|
206784
207090
|
const p2 = await pr.json();
|
|
206785
207091
|
if (p2.access_token) {
|
|
206786
|
-
|
|
206787
|
-
|
|
207092
|
+
const svc = await connectorActorsService();
|
|
207093
|
+
if (svc) {
|
|
206788
207094
|
const connId = "github";
|
|
206789
207095
|
await svc.upsertConnector({ id: connId, name: "GitHub", mode: "oauth", status: "connected", account: "github" });
|
|
206790
207096
|
for (const [k2, v2] of [["GIT_AUTHOR_NAME", actorNameParam], ["GIT_AUTHOR_EMAIL", ""], ["GIT_COMMITTER_NAME", actorNameParam], ["GIT_COMMITTER_EMAIL", ""], ["EMAIL", ""]])
|
|
@@ -214524,7 +214830,7 @@ async function prepareDroppedDispatch(work, deps) {
|
|
|
214524
214830
|
const node2 = kernel.model.artifacts.get(work.nodeId);
|
|
214525
214831
|
const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(work.workorderId));
|
|
214526
214832
|
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" ||
|
|
214833
|
+
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
214834
|
if (snap.issues.some((i) => i.kind === "escalation" && i.aboutNodeId === node2.id && !i.resolvedAt && i.intent !== "advisory")) return false;
|
|
214529
214835
|
if (!work.assigneeActorId) return false;
|
|
214530
214836
|
const binding = await deps.bindingFor(work.assigneeActorId);
|
|
@@ -216135,13 +216441,14 @@ async function planChatTurnRecovery(deps) {
|
|
|
216135
216441
|
const plans = [];
|
|
216136
216442
|
for (const row of rows) {
|
|
216137
216443
|
if (!row.runId) continue;
|
|
216138
|
-
const run = await deps.getRun(row.runId)
|
|
216444
|
+
const run = await deps.getRun(row.runId);
|
|
216139
216445
|
if (!run || run.status !== "running" || run.action !== "chat" || !run.artifactId) continue;
|
|
216140
216446
|
const active = byArtifact.get(run.artifactId);
|
|
216141
216447
|
if (!active || deps.isWired(run.artifactId)) continue;
|
|
216142
216448
|
const meta = asObj3(run.metadata);
|
|
216143
216449
|
const runtimeSessionId = typeof meta?.runtimeSessionId === "string" ? meta.runtimeSessionId : run.jobKey ?? void 0;
|
|
216144
|
-
|
|
216450
|
+
const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
|
|
216451
|
+
const plan = {
|
|
216145
216452
|
dispatchId: active.dispatchId,
|
|
216146
216453
|
chatSessionId: row.sessionId,
|
|
216147
216454
|
assistantMsgId: row.id,
|
|
@@ -216152,18 +216459,42 @@ async function planChatTurnRecovery(deps) {
|
|
|
216152
216459
|
...runtimeSessionId ? { runtimeSessionId } : {},
|
|
216153
216460
|
startedAt: run.startedAt,
|
|
216154
216461
|
seedContent: row.content,
|
|
216155
|
-
seedParts: partsOf(row.parts)
|
|
216156
|
-
|
|
216462
|
+
seedParts: partsOf(row.parts),
|
|
216463
|
+
...turn ? { turnId: turn.id } : {}
|
|
216464
|
+
};
|
|
216465
|
+
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
216466
|
+
deps.onSettled?.({ plan, turn });
|
|
216467
|
+
continue;
|
|
216468
|
+
}
|
|
216469
|
+
plans.push(plan);
|
|
216157
216470
|
}
|
|
216158
216471
|
return plans;
|
|
216159
216472
|
}
|
|
216473
|
+
async function finishPersistedRecoveryItems(items, sessionId, turnId, status, log3) {
|
|
216474
|
+
if (!items || !chatItemsDoubleWriteEnabled()) return [];
|
|
216475
|
+
try {
|
|
216476
|
+
const rows = await items.listItemsByTurn(sessionId, turnId);
|
|
216477
|
+
let version2 = await items.sessionVersionCursor(sessionId);
|
|
216478
|
+
const closed = [];
|
|
216479
|
+
for (const row of rows) {
|
|
216480
|
+
if (row.status !== "streaming") continue;
|
|
216481
|
+
const updated = await items.applyOp(row.id, { op: "set_status", status }, { version: ++version2 });
|
|
216482
|
+
if (!updated) throw new Error(`item ${row.id} disappeared`);
|
|
216483
|
+
closed.push(updated);
|
|
216484
|
+
}
|
|
216485
|
+
return closed;
|
|
216486
|
+
} catch (err) {
|
|
216487
|
+
log3(`[chat-recovery] items \u6536\u5C3E\u5931\u8D25\uFF1Asession=${sessionId} turn=${turnId} ${String(err)}`);
|
|
216488
|
+
return [];
|
|
216489
|
+
}
|
|
216490
|
+
}
|
|
216160
216491
|
function wireRecoveredChatTurn(deps) {
|
|
216161
216492
|
const { plan, handle, liveChat, chatStore, trace } = deps;
|
|
216162
216493
|
const log3 = deps.log ?? (() => void 0);
|
|
216163
216494
|
const itemLedger = new ChatItemLedger({
|
|
216164
216495
|
...deps.items ? { items: deps.items } : {},
|
|
216165
216496
|
sessionId: plan.chatSessionId,
|
|
216166
|
-
turnId: deps.itemsTurnId ?? fallbackTurnId(plan.runId),
|
|
216497
|
+
turnId: deps.itemsTurnId ?? plan.turnId ?? fallbackTurnId(plan.runId),
|
|
216167
216498
|
runId: plan.runId,
|
|
216168
216499
|
messageId: plan.assistantMsgId,
|
|
216169
216500
|
log: log3
|
|
@@ -216329,6 +216660,14 @@ function wireRecoveredChatTurn(deps) {
|
|
|
216329
216660
|
normalizedSource.finishTurn(failed ? "failed" : "completed");
|
|
216330
216661
|
itemLedger.finish(failed ? "failed" : "completed");
|
|
216331
216662
|
await itemLedger.drain();
|
|
216663
|
+
const closedItems = await finishPersistedRecoveryItems(
|
|
216664
|
+
deps.items,
|
|
216665
|
+
plan.chatSessionId,
|
|
216666
|
+
itemLedger.turnId,
|
|
216667
|
+
failed ? "failed" : "completed",
|
|
216668
|
+
log3
|
|
216669
|
+
);
|
|
216670
|
+
for (const item of closedItems) liveChat.publishItemStatus(plan.chatSessionId, item);
|
|
216332
216671
|
await chatStore.updateMessage(currentMsgId, {
|
|
216333
216672
|
content: itemLedger.projectContent(),
|
|
216334
216673
|
status: failed ? "error" : "done",
|
|
@@ -216426,6 +216765,29 @@ async function reconcileChatExitFrame(deps) {
|
|
|
216426
216765
|
const meta = asObj3(run.metadata);
|
|
216427
216766
|
if (meta?.dispatchId !== deps.dispatchId) continue;
|
|
216428
216767
|
if (deps.ownedRunId?.(row.sessionId) === row.runId) return false;
|
|
216768
|
+
const turn = await deps.getTurnForRun?.(row.sessionId, run.id);
|
|
216769
|
+
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
216770
|
+
await reconcileSettledChatTurn({
|
|
216771
|
+
plan: {
|
|
216772
|
+
dispatchId: deps.dispatchId,
|
|
216773
|
+
chatSessionId: row.sessionId,
|
|
216774
|
+
assistantMsgId: row.id,
|
|
216775
|
+
runId: run.id,
|
|
216776
|
+
artifactId: run.artifactId,
|
|
216777
|
+
actor: run.actorId,
|
|
216778
|
+
startedAt: run.startedAt,
|
|
216779
|
+
seedContent: row.content,
|
|
216780
|
+
seedParts: partsOf(row.parts),
|
|
216781
|
+
turnId: turn.id
|
|
216782
|
+
},
|
|
216783
|
+
turn,
|
|
216784
|
+
chatStore: deps.chatStore,
|
|
216785
|
+
trace: deps.trace,
|
|
216786
|
+
...deps.items ? { items: deps.items } : {},
|
|
216787
|
+
...deps.log ? { log: deps.log } : {}
|
|
216788
|
+
});
|
|
216789
|
+
return true;
|
|
216790
|
+
}
|
|
216429
216791
|
const { info } = deps;
|
|
216430
216792
|
const failed = exitFailed(info);
|
|
216431
216793
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -216467,6 +216829,13 @@ async function reconcileChatExitFrame(deps) {
|
|
|
216467
216829
|
normalizer.turnFinished(failed ? "failed" : "completed");
|
|
216468
216830
|
itemLedger.finish(failed ? "failed" : "completed");
|
|
216469
216831
|
await itemLedger.drain();
|
|
216832
|
+
await finishPersistedRecoveryItems(
|
|
216833
|
+
deps.items,
|
|
216834
|
+
row.sessionId,
|
|
216835
|
+
itemLedger.turnId,
|
|
216836
|
+
failed ? "failed" : "completed",
|
|
216837
|
+
deps.log ?? (() => void 0)
|
|
216838
|
+
);
|
|
216470
216839
|
await deps.chatStore.updateMessage(row.id, {
|
|
216471
216840
|
content: itemLedger.projectContent(),
|
|
216472
216841
|
status: failed ? "error" : "done",
|
|
@@ -216505,6 +216874,156 @@ async function reconcileChatExitFrame(deps) {
|
|
|
216505
216874
|
}
|
|
216506
216875
|
return false;
|
|
216507
216876
|
}
|
|
216877
|
+
async function planChatTurnRecoveryAcrossCompanies(deps) {
|
|
216878
|
+
const groups = [];
|
|
216879
|
+
const settled = [];
|
|
216880
|
+
const skipped = [];
|
|
216881
|
+
let scanned = 0;
|
|
216882
|
+
for (const companyId of await deps.listCompanies()) {
|
|
216883
|
+
try {
|
|
216884
|
+
const stores = await deps.storesFor(companyId);
|
|
216885
|
+
const companySettled = [];
|
|
216886
|
+
const plans = await planChatTurnRecovery({
|
|
216887
|
+
activeSessions: deps.activeSessions,
|
|
216888
|
+
listRunningAssistantMessages: stores.listRunningAssistantMessages,
|
|
216889
|
+
getRun: deps.getRun,
|
|
216890
|
+
isWired: deps.isWired,
|
|
216891
|
+
...stores.getTurnForRun ? { getTurnForRun: stores.getTurnForRun } : {},
|
|
216892
|
+
onSettled: (hit) => companySettled.push(hit)
|
|
216893
|
+
});
|
|
216894
|
+
scanned += 1;
|
|
216895
|
+
settled.push(...companySettled.map((hit) => ({ ...hit, companyId, stores })));
|
|
216896
|
+
if (plans.length) groups.push({ companyId, stores, plans });
|
|
216897
|
+
if (deps.stopOnFirstHit && (plans.length || companySettled.length)) break;
|
|
216898
|
+
} catch (err) {
|
|
216899
|
+
skipped.push({ companyId, error: String(err) });
|
|
216900
|
+
deps.onCompanyError?.(companyId, err);
|
|
216901
|
+
}
|
|
216902
|
+
}
|
|
216903
|
+
return { groups, settled, scanned, skipped };
|
|
216904
|
+
}
|
|
216905
|
+
async function sweepDanglingChatTurnsAcrossCompanies(deps) {
|
|
216906
|
+
const { listCompanies, storesFor, onCompanyError, onSessionClosed, log: log3, ...rest } = deps;
|
|
216907
|
+
const closed = [];
|
|
216908
|
+
for (const companyId of await listCompanies()) {
|
|
216909
|
+
try {
|
|
216910
|
+
const stores = await storesFor(companyId);
|
|
216911
|
+
const ids2 = await sweepDanglingChatTurns({
|
|
216912
|
+
...rest,
|
|
216913
|
+
listRunningAssistantMessages: stores.listRunningAssistantMessages,
|
|
216914
|
+
listMessages: (sid) => stores.chatStore.listMessages(sid),
|
|
216915
|
+
updateMessage: (id, patch) => stores.chatStore.updateMessage(id, patch),
|
|
216916
|
+
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {},
|
|
216917
|
+
...onSessionClosed ? { onSessionClosed: (sid, reason, messageId) => onSessionClosed(sid, reason, messageId, companyId) } : {}
|
|
216918
|
+
});
|
|
216919
|
+
closed.push(...ids2);
|
|
216920
|
+
} catch (err) {
|
|
216921
|
+
onCompanyError?.(companyId, err);
|
|
216922
|
+
}
|
|
216923
|
+
}
|
|
216924
|
+
return closed;
|
|
216925
|
+
}
|
|
216926
|
+
async function reconcileChatExitFrameAcrossCompanies(deps) {
|
|
216927
|
+
const { listCompanies, storesFor, onCompanyError, log: log3, ...rest } = deps;
|
|
216928
|
+
for (const companyId of await listCompanies()) {
|
|
216929
|
+
try {
|
|
216930
|
+
const stores = await storesFor(companyId);
|
|
216931
|
+
const handled2 = await reconcileChatExitFrame({
|
|
216932
|
+
...rest,
|
|
216933
|
+
listRunningAssistantMessages: stores.listRunningAssistantMessages,
|
|
216934
|
+
chatStore: stores.chatStore,
|
|
216935
|
+
...stores.getTurnForRun ? { getTurnForRun: stores.getTurnForRun } : {},
|
|
216936
|
+
...stores.items ? { items: stores.items } : {},
|
|
216937
|
+
...log3 ? { log: (m2) => log3(companyId ? `${m2}\uFF3B${companyId}\uFF3D` : m2) } : {}
|
|
216938
|
+
});
|
|
216939
|
+
if (handled2) return true;
|
|
216940
|
+
} catch (err) {
|
|
216941
|
+
onCompanyError?.(companyId, err);
|
|
216942
|
+
}
|
|
216943
|
+
}
|
|
216944
|
+
return false;
|
|
216945
|
+
}
|
|
216946
|
+
async function reconcileSettledChatTurn(deps) {
|
|
216947
|
+
const { plan, turn } = deps;
|
|
216948
|
+
if (turn.status === "reserved" || turn.status === "running") return;
|
|
216949
|
+
const succeeded = turn.status === "succeeded";
|
|
216950
|
+
const completedAt = turn.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
216951
|
+
const turnId = deps.items ? await resolveChatItemsTurnId(deps.items, plan.assistantMsgId, turn.id) : turn.id;
|
|
216952
|
+
await finishPersistedRecoveryItems(
|
|
216953
|
+
deps.items,
|
|
216954
|
+
plan.chatSessionId,
|
|
216955
|
+
turnId,
|
|
216956
|
+
succeeded ? "completed" : "failed",
|
|
216957
|
+
deps.log ?? (() => void 0)
|
|
216958
|
+
);
|
|
216959
|
+
await deps.chatStore.updateMessage(plan.assistantMsgId, {
|
|
216960
|
+
status: succeeded ? "done" : "error",
|
|
216961
|
+
completedAt
|
|
216962
|
+
});
|
|
216963
|
+
const run = await deps.trace.getRun(plan.runId);
|
|
216964
|
+
if (run?.status === "running") {
|
|
216965
|
+
await deps.trace.updateRun(plan.runId, {
|
|
216966
|
+
status: turn.status,
|
|
216967
|
+
endedAt: completedAt
|
|
216968
|
+
});
|
|
216969
|
+
}
|
|
216970
|
+
}
|
|
216971
|
+
async function recoverChatTurnsAcrossCompanies(deps) {
|
|
216972
|
+
const result = await planChatTurnRecoveryAcrossCompanies(deps);
|
|
216973
|
+
const recovered = [];
|
|
216974
|
+
const log3 = deps.log ?? (() => void 0);
|
|
216975
|
+
const retire = async (hit) => {
|
|
216976
|
+
const { plan, turn, companyId, stores } = hit;
|
|
216977
|
+
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`);
|
|
216978
|
+
deps.evict(plan.dispatchId);
|
|
216979
|
+
try {
|
|
216980
|
+
await reconcileSettledChatTurn({
|
|
216981
|
+
plan,
|
|
216982
|
+
turn,
|
|
216983
|
+
chatStore: stores.chatStore,
|
|
216984
|
+
trace: deps.trace,
|
|
216985
|
+
...stores.items ? { items: stores.items } : {},
|
|
216986
|
+
log: log3
|
|
216987
|
+
});
|
|
216988
|
+
} catch (err) {
|
|
216989
|
+
deps.onCompanyError?.(companyId, err);
|
|
216990
|
+
}
|
|
216991
|
+
};
|
|
216992
|
+
for (const hit of result.settled) await retire(hit);
|
|
216993
|
+
for (const { companyId, stores, plans } of result.groups) {
|
|
216994
|
+
for (const plan of plans) {
|
|
216995
|
+
try {
|
|
216996
|
+
const itemsTurnId = stores.items ? await resolveChatItemsTurnId(stores.items, plan.assistantMsgId, plan.turnId ?? fallbackTurnId(plan.runId)) : plan.turnId ?? fallbackTurnId(plan.runId);
|
|
216997
|
+
const turn = await stores.getTurnForRun?.(plan.chatSessionId, plan.runId);
|
|
216998
|
+
if (turn && turn.status !== "reserved" && turn.status !== "running") {
|
|
216999
|
+
const hit = { companyId, stores, plan, turn };
|
|
217000
|
+
result.settled.push(hit);
|
|
217001
|
+
await retire(hit);
|
|
217002
|
+
continue;
|
|
217003
|
+
}
|
|
217004
|
+
const handle = deps.recover(plan);
|
|
217005
|
+
const { done } = wireRecoveredChatTurn({
|
|
217006
|
+
plan,
|
|
217007
|
+
handle,
|
|
217008
|
+
liveChat: deps.liveChat,
|
|
217009
|
+
chatStore: stores.chatStore,
|
|
217010
|
+
trace: deps.trace,
|
|
217011
|
+
register: () => deps.register(plan, handle),
|
|
217012
|
+
unregister: () => deps.unregister(plan),
|
|
217013
|
+
...deps.traceHealth ? { traceHealth: deps.traceHealth } : {},
|
|
217014
|
+
...stores.items ? { items: stores.items, itemsTurnId } : {},
|
|
217015
|
+
log: log3
|
|
217016
|
+
});
|
|
217017
|
+
recovered.push({ companyId, plan, done });
|
|
217018
|
+
log3(`[chat-recovery] \u91CD\u6302 chat \u8F6E\uFF1Acompany=${companyId ?? "<default>"} session=${plan.chatSessionId} run=${plan.runId} dispatch=${plan.dispatchId}`);
|
|
217019
|
+
} catch (err) {
|
|
217020
|
+
result.skipped.push({ companyId, error: String(err) });
|
|
217021
|
+
deps.onCompanyError?.(companyId, err);
|
|
217022
|
+
}
|
|
217023
|
+
}
|
|
217024
|
+
}
|
|
217025
|
+
return { ...result, recovered };
|
|
217026
|
+
}
|
|
216508
217027
|
var import_node_crypto45, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
|
|
216509
217028
|
var init_chat_recovery = __esm({
|
|
216510
217029
|
"../server/src/chat-recovery.ts"() {
|
|
@@ -217856,9 +218375,32 @@ var init_service5 = __esm({
|
|
|
217856
218375
|
const a = await this.opts.store.getActor(id);
|
|
217857
218376
|
if (!a) throw new Error(`actor not found: ${id}`);
|
|
217858
218377
|
await this.upsertActor({ ...a, status: "disabled" }, by);
|
|
218378
|
+
await this.dropRuntimeBindingOfDeletedAgent(a.id, a.kind, by);
|
|
217859
218379
|
if (a.status === "active") await this.opts.onActorDisabled?.(id, by);
|
|
217860
218380
|
return a;
|
|
217861
218381
|
}
|
|
218382
|
+
/**
|
|
218383
|
+
* 删掉 Agent 时把它跟 runtime 的绑定一起撤掉。
|
|
218384
|
+
*
|
|
218385
|
+
* 为什么必须做(2026-09-09 线上实测):删除只写 `status='disabled'`,`actor_bindings` 那行
|
|
218386
|
+
* 原样留着且仍是 `active`。而 `GET /api/actors` 会把已删 Agent 过滤掉、`GET /api/bindings` 不会——
|
|
218387
|
+
* 运行时管理页拿两份数据对着算「运行 Agent」,于是已删的 Agent 继续占着一格,
|
|
218388
|
+
* 名字还查不回来,页面上直接露出 `asst-xxx-cbf34f` 这种裸 id。didi 组织实测:yx_claude 上
|
|
218389
|
+
* 12 条 active 绑定里 7 条的 Agent 早被删了(其中 5 条是助理)。
|
|
218390
|
+
*
|
|
218391
|
+
* 顺带修好另一处:`pickAssistantRuntime` 用「(nodeId,kind) 上的 active 绑定数」当负载做均衡,
|
|
218392
|
+
* 已删 Agent 的残留绑定会把负载算高,把新助理往别的机器上赶。
|
|
218393
|
+
*
|
|
218394
|
+
* 只对 agent 生效:真人被移出组织是「离岗」不是删除,PRD 明确其助理照常在岗、账号回来还能接上。
|
|
218395
|
+
* 幂等:removeBinding 对没有绑定的 actor 是 no-op,所以重复删也安全(存量脏数据也能靠再删一次修好)。
|
|
218396
|
+
*/
|
|
218397
|
+
async dropRuntimeBindingOfDeletedAgent(id, kind, by) {
|
|
218398
|
+
if (kind !== "agent") return;
|
|
218399
|
+
const existing = await this.opts.store.getBinding(id);
|
|
218400
|
+
if (!existing) return;
|
|
218401
|
+
await this.opts.store.removeBinding(id);
|
|
218402
|
+
await this.audit({ kind: "binding_change", actorId: id, by, at: this.now(), detail: { removed: true, reason: "agent_deleted" } });
|
|
218403
|
+
}
|
|
217862
218404
|
/**
|
|
217863
218405
|
* 原子「若当前 active 则停用」(QA R10):与 disableActor 的区别在于——
|
|
217864
218406
|
* 只在 actor 当前**确为 active** 时才写,返回 `ok:true + before + seq`;已被并发者/别的路径先 disable
|
|
@@ -217882,6 +218424,7 @@ var init_service5 = __esm({
|
|
|
217882
218424
|
if (!res.ok) return res;
|
|
217883
218425
|
this.opts.onRolesChanged?.(id, []);
|
|
217884
218426
|
await this.audit({ kind: "actor_upsert", actorId: id, by, at: this.now(), detail: { status: "disabled", roles: res.before.roles } });
|
|
218427
|
+
await this.dropRuntimeBindingOfDeletedAgent(id, res.before.kind, by);
|
|
217885
218428
|
await this.opts.onActorDisabled?.(id, by);
|
|
217886
218429
|
return { ok: true, before: res.before, seq };
|
|
217887
218430
|
}
|
|
@@ -218482,39 +219025,42 @@ ${input.description}
|
|
|
218482
219025
|
const c = cfg ?? await this.opts.store.latestConfig(actorId);
|
|
218483
219026
|
return new Set(c?.connectorIds ?? []);
|
|
218484
219027
|
}
|
|
218485
|
-
/**
|
|
218486
|
-
|
|
218487
|
-
|
|
219028
|
+
/**
|
|
219029
|
+
* 设置某员工×连接器的连接记录(启用/停用)。无则创建,有则翻转 enabled。
|
|
219030
|
+
*
|
|
219031
|
+
* `configuredBy` 只在**授权流程走完**时传(「谁为这名员工把它接上的」)。普通开关不传,
|
|
219032
|
+
* 此时把已有值原样带回——**只增不抹**:翻一次开关不该把授权时记下的人擦掉。
|
|
219033
|
+
* pg 侧另有 `COALESCE` 兜同一条,两层都做是因为内存 store 是整行替换。
|
|
219034
|
+
*/
|
|
219035
|
+
async setActorConnectorEnabled(actorId, connectorId, enabled, configuredBy) {
|
|
219036
|
+
const keep = configuredBy ?? (await this.opts.store.listActorConnectorConnections(actorId)).find((c) => c.connectorId === connectorId)?.configuredBy;
|
|
219037
|
+
const conn = {
|
|
219038
|
+
actorId,
|
|
219039
|
+
connectorId,
|
|
219040
|
+
enabled,
|
|
219041
|
+
updatedAt: this.now(),
|
|
219042
|
+
...keep ? { configuredBy: keep } : {}
|
|
219043
|
+
};
|
|
218488
219044
|
await this.opts.store.upsertActorConnectorConnection(conn);
|
|
218489
219045
|
return conn;
|
|
218490
219046
|
}
|
|
218491
|
-
listActorConnectorConnections(actorId) {
|
|
218492
|
-
return this.opts.store.listActorConnectorConnections(actorId);
|
|
218493
|
-
}
|
|
218494
219047
|
/**
|
|
218495
|
-
*
|
|
219048
|
+
* 删除**一条**「员工 × 连接器」连接:连接记录 + 该员工这个连接器的**个人凭据变量**。
|
|
218496
219049
|
*
|
|
218497
|
-
*
|
|
218498
|
-
*
|
|
218499
|
-
* 不删的话,「移除」之后 `actorConnected` 里还有它,卡片照样在,读作「删不掉」;
|
|
218500
|
-
* ② 「员工×连接器」的连接记录——不删的话 `effective` 里还有它(enabled=false),
|
|
218501
|
-
* 卡片变成一张「已停用」的僵尸卡,而人要的是它消失。
|
|
219050
|
+
* 与开关的区别(发起人 2026-09-09 定的,两颗按钮并存):开关是「暂时不用」,凭据留着、
|
|
219051
|
+
* 打开就能接着用;删除是「不要了」,凭据清掉、再要用得重走一遍授权。
|
|
218502
219052
|
*
|
|
218503
|
-
*
|
|
218504
|
-
*
|
|
218505
|
-
* 于是:组织已连接时,移除个人连接后这名员工会**回落到组织默认**——卡片仍在,但身份那一行
|
|
218506
|
-
* 变回组织账号。这是对的,不是没删干净。
|
|
218507
|
-
*
|
|
218508
|
-
* 幂等:没有个人凭据、没有记录时照样返回成功(`variablesDeleted: 0`)——重复点、并发点都不该报错。
|
|
219053
|
+
* **只动这名员工自己的东西**:组织级变量(scope=global)一个不碰——那是别人也在用的。
|
|
219054
|
+
* 飞书对话通道的下线不在这里做(本服务够不到 channelService),由路由层在删完凭据后补一刀。
|
|
218509
219055
|
*/
|
|
218510
|
-
async
|
|
218511
|
-
const
|
|
218512
|
-
const
|
|
218513
|
-
(v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId === connectorId
|
|
218514
|
-
);
|
|
218515
|
-
for (const v2 of mine) await this.opts.store.deleteVariable(v2.key, actorId);
|
|
219056
|
+
async deleteActorConnector(actorId, connectorId) {
|
|
219057
|
+
const personal = (await this.opts.store.listVariables(actorId)).filter((v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId === connectorId);
|
|
219058
|
+
for (const v2 of personal) await this.deleteVariable(v2.key, actorId);
|
|
218516
219059
|
await this.opts.store.deleteActorConnectorConnection(actorId, connectorId);
|
|
218517
|
-
return {
|
|
219060
|
+
return { variablesDeleted: personal.length };
|
|
219061
|
+
}
|
|
219062
|
+
listActorConnectorConnections(actorId) {
|
|
219063
|
+
return this.opts.store.listActorConnectorConnections(actorId);
|
|
218518
219064
|
}
|
|
218519
219065
|
/**
|
|
218520
219066
|
* 员工连接器面板所需状态:连接记录 + 组织级已连接集合 + 该员工已授权(有个人 connector 变量)集合。
|
|
@@ -220483,14 +221029,9 @@ function actorsDomain(opts) {
|
|
|
220483
221029
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
220484
221030
|
const b2 = req.body;
|
|
220485
221031
|
if (typeof b2?.enabled !== "boolean") throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 enabled (boolean)");
|
|
220486
|
-
const conn = await service.setActorConnectorEnabled(req.params.id, req.params.connectorId,
|
|
221032
|
+
const conn = b2.enabled ? await service.setActorConnectorEnabled(req.params.id, req.params.connectorId, true, req.auth.actor) : await service.setActorConnectorEnabled(req.params.id, req.params.connectorId, false);
|
|
220487
221033
|
return { status: 200, body: conn };
|
|
220488
221034
|
});
|
|
220489
|
-
router.delete("/api/actors/:id/connectors/:connectorId", async (req) => {
|
|
220490
|
-
const { service } = await resolveCtx(req.auth.companyId);
|
|
220491
|
-
const r = await service.removeActorConnector(req.params.id, req.params.connectorId);
|
|
220492
|
-
return { status: 200, body: r };
|
|
220493
|
-
});
|
|
220494
221035
|
const requireVariableManager = (req) => {
|
|
220495
221036
|
if (!isHumanActor(req.auth.actor)) {
|
|
220496
221037
|
throw new ApiError(
|
|
@@ -225713,13 +226254,19 @@ function reviewSummary(snap, reviews, events) {
|
|
|
225713
226254
|
const name = node2?.title ?? first.nodeId;
|
|
225714
226255
|
const latest = latestActivityReviews(reviews, events);
|
|
225715
226256
|
const requirements = snap.requirements.filter((r) => r.nodeId === first.nodeId && r.reviewerActorId === first.reviewerActorId);
|
|
225716
|
-
|
|
226257
|
+
for (const requirement of requirements) {
|
|
226258
|
+
const current = reviews.find((r) => r.id === requirement.latestReviewId);
|
|
226259
|
+
if (!current) continue;
|
|
226260
|
+
const index2 = latest.findIndex((r) => r.reviewGroup === requirement.reviewGroup);
|
|
226261
|
+
if (index2 >= 0) latest[index2] = current;
|
|
226262
|
+
}
|
|
226263
|
+
const judgement = judgeWork({ requirements: requirements.length ? requirements : latest, reviews: latest });
|
|
225717
226264
|
const pending = latest.filter((r) => !r.cancelledAt && !r.verdict && reviewState(r) === "running");
|
|
225718
226265
|
if (work?.acceptanceState === "accepted" || work?.acceptedAt || work?.acceptanceState !== "rejected" && judgement === "passed") {
|
|
225719
226266
|
return { phase: "done", status: `\u300A${name}\u300B\u5BA1\u6838\u901A\u8FC7\u3002`, latest, pending: [] };
|
|
225720
226267
|
}
|
|
225721
226268
|
if (work?.acceptanceState === "rejected" || judgement === "rejected") {
|
|
225722
|
-
return { phase: "done", status:
|
|
226269
|
+
return { phase: "done", status: `\u300A${name}\u300B\u5BA1\u6838\u4E0D\u901A\u8FC7\u3002`, latest, pending: [] };
|
|
225723
226270
|
}
|
|
225724
226271
|
const endedWork = !!(work?.cancelledAt || work?.deadAt);
|
|
225725
226272
|
if (endedWork && latest.every((r) => !!r.cancelledAt && reviewState(r) !== "retry")) {
|
|
@@ -225749,6 +226296,66 @@ var init_review_activity = __esm({
|
|
|
225749
226296
|
}
|
|
225750
226297
|
});
|
|
225751
226298
|
|
|
226299
|
+
// ../server/src/domains/collab/review-requirement-activity.ts
|
|
226300
|
+
function reviewRequirementActivity(snap, events, ref2) {
|
|
226301
|
+
const previous3 = /* @__PURE__ */ new Map();
|
|
226302
|
+
const titles = new Map(snap.nodes.map((n) => [n.id, n.title]));
|
|
226303
|
+
const cards = [];
|
|
226304
|
+
const label = (r) => `${ref2(r.reviewerActorId).name || "\u672A\u547D\u540D\u5BA1\u6838\u4EBA"}${r.source === "closure" ? "\uFF08\u7ED3\u6848\u5BA1\u6838\uFF09" : ""}`;
|
|
226305
|
+
const names = (rows) => [...new Set(rows.map(label))].join("\u3001") || "\u65E0";
|
|
226306
|
+
for (const rec of [...events].sort((a, b2) => a.seq - b2.seq)) {
|
|
226307
|
+
if (rec.status === "pending" || rec.status === "failed") continue;
|
|
226308
|
+
const e = rec.event;
|
|
226309
|
+
if (e.kind === "plan.changed") {
|
|
226310
|
+
for (const node2 of e.addNodes ?? []) previous3.set(node2.id, node2.reviewers ?? []);
|
|
226311
|
+
for (const id of e.removeNodes ?? []) previous3.delete(id);
|
|
226312
|
+
continue;
|
|
226313
|
+
}
|
|
226314
|
+
if (e.kind === "plan.add_node") {
|
|
226315
|
+
previous3.set(e.nodeId, e.reviewers ?? []);
|
|
226316
|
+
continue;
|
|
226317
|
+
}
|
|
226318
|
+
if (e.kind !== "plan.update_review_requirements") continue;
|
|
226319
|
+
const before = previous3.get(e.nodeId);
|
|
226320
|
+
const after = e.reviewers;
|
|
226321
|
+
const beforeKeys = new Set(before?.map(requirementKey));
|
|
226322
|
+
const afterKeys = new Set(after.map(requirementKey));
|
|
226323
|
+
const added = before ? after.filter((r) => !beforeKeys.has(requirementKey(r))) : [];
|
|
226324
|
+
const removed = before?.filter((r) => !afterKeys.has(requirementKey(r))) ?? [];
|
|
226325
|
+
const nodeTitle = titles.get(e.nodeId) || "\u5DF2\u79FB\u9664\u7684\u8282\u70B9";
|
|
226326
|
+
const system = rec.actorId.startsWith("actor:system:");
|
|
226327
|
+
const closureAdded = system && added.length > 0 && !removed.length && added.every((r) => r.source === "closure");
|
|
226328
|
+
cards.push({
|
|
226329
|
+
id: `wo:${rec.seq}`,
|
|
226330
|
+
seq: rec.seq,
|
|
226331
|
+
at: rec.createdAt,
|
|
226332
|
+
updatedAt: rec.createdAt,
|
|
226333
|
+
nodeId: e.nodeId,
|
|
226334
|
+
nodeTitle,
|
|
226335
|
+
executor: system ? { ...ref2(rec.actorId), name: "\u7CFB\u7EDF" } : ref2(rec.actorId),
|
|
226336
|
+
...rec.handActorId ? { handActor: ref2(rec.handActorId) } : {},
|
|
226337
|
+
phase: "done",
|
|
226338
|
+
status: closureAdded ? `\u4E3A\u300A${nodeTitle}\u300B\u8865\u5145\u4E86\u7ED3\u6848\u5BA1\u6838\u8981\u6C42\u3002` : `\u66F4\u65B0\u4E86\u300A${nodeTitle}\u300B\u7684\u5BA1\u6838\u5217\u8868\u3002`,
|
|
226339
|
+
detail: [
|
|
226340
|
+
...added.length ? [`\u65B0\u589E\uFF1A${names(added)}`] : [],
|
|
226341
|
+
...removed.length ? [`\u79FB\u9664\uFF1A${names(removed)}`] : [],
|
|
226342
|
+
`\u66F4\u65B0\u540E\uFF1A${names(after)}`
|
|
226343
|
+
].join("\n"),
|
|
226344
|
+
artifacts: [],
|
|
226345
|
+
actions: []
|
|
226346
|
+
});
|
|
226347
|
+
previous3.set(e.nodeId, after);
|
|
226348
|
+
}
|
|
226349
|
+
return cards;
|
|
226350
|
+
}
|
|
226351
|
+
var requirementKey;
|
|
226352
|
+
var init_review_requirement_activity = __esm({
|
|
226353
|
+
"../server/src/domains/collab/review-requirement-activity.ts"() {
|
|
226354
|
+
"use strict";
|
|
226355
|
+
requirementKey = (r) => JSON.stringify([r.reviewerActorId, r.reviewGroup, r.source]);
|
|
226356
|
+
}
|
|
226357
|
+
});
|
|
226358
|
+
|
|
225752
226359
|
// ../server/src/domains/collab/workorder-manager.ts
|
|
225753
226360
|
function resolveWorkorderManager(snap) {
|
|
225754
226361
|
const nodes = snap.nodes;
|
|
@@ -225772,6 +226379,9 @@ var init_workorder_manager = __esm({
|
|
|
225772
226379
|
function isPendingActor(id) {
|
|
225773
226380
|
return typeof id === "string" && id.startsWith("pending:");
|
|
225774
226381
|
}
|
|
226382
|
+
function isAgentActor(id) {
|
|
226383
|
+
return typeof id === "string" && id.startsWith("actor:agent:");
|
|
226384
|
+
}
|
|
225775
226385
|
function isDispatchLayerFailure(detail) {
|
|
225776
226386
|
const reason = detail?.reason;
|
|
225777
226387
|
return typeof reason === "string" && reason.startsWith("dispatch ");
|
|
@@ -226307,6 +226917,22 @@ function buildWorkorderActivity(input) {
|
|
|
226307
226917
|
const created = events.filter((r) => r.event.kind === "review.create" && ids2.has(r.event.reviewId));
|
|
226308
226918
|
const dates = summary.latest.flatMap((r) => [r.createdAt, r.startedAt, r.endedAt, r.decidedAt, r.cancelledAt, r.retryAt].filter((v2) => !!v2));
|
|
226309
226919
|
const pending = summary.pending.filter((r) => r.reviewerActorId.startsWith("actor:human:") && (!input.viewerActorId || r.reviewerActorId === input.viewerActorId));
|
|
226920
|
+
const groups = [...new Set(summary.latest.map((r) => r.reviewGroup))];
|
|
226921
|
+
const reviewNotes = summary.latest.flatMap((r) => {
|
|
226922
|
+
const text5 = r.note?.trim();
|
|
226923
|
+
if (!text5) return [];
|
|
226924
|
+
const rejected = r.verdict === "request_changes";
|
|
226925
|
+
const label = ACTIVITY_COPY.reviewNoteLabel(rejected);
|
|
226926
|
+
return [{
|
|
226927
|
+
id: r.id,
|
|
226928
|
+
// 同一个人担多个评审组时才标组号——单组标了纯属噪声(卡上本来就只有这一个人)。
|
|
226929
|
+
label: groups.length > 1 ? `\u8BC4\u5BA1\u7EC4 ${groups.indexOf(r.reviewGroup) + 1} \xB7 ${label}` : label,
|
|
226930
|
+
text: text5,
|
|
226931
|
+
tone: rejected ? "reject" : "note"
|
|
226932
|
+
}];
|
|
226933
|
+
});
|
|
226934
|
+
const revieweeId = workById.get(workId)?.assigneeActorId;
|
|
226935
|
+
const reviewLine = reviewNotes.length && revieweeId && revieweeId !== reviewerId ? reviewThreadSessionId(revieweeId, reviewerId) : void 0;
|
|
226310
226936
|
visibleReviews.set(cardId, {
|
|
226311
226937
|
id: cardId,
|
|
226312
226938
|
reviewWorkId: workId,
|
|
@@ -226320,8 +226946,13 @@ function buildWorkorderActivity(input) {
|
|
|
226320
226946
|
executorId: reviewerId,
|
|
226321
226947
|
phase: summary.phase,
|
|
226322
226948
|
status: summary.status,
|
|
226323
|
-
|
|
226324
|
-
|
|
226949
|
+
/* 同一人担多个责任组时,展开区留一份**逐组结论**(只有判定词、没有意见正文)——小状态只有
|
|
226950
|
+
一句总判定,说不清哪个组过了哪个组没过。意见正文一律走下面的意见条。 */
|
|
226951
|
+
...summary.latest.length > 1 ? {
|
|
226952
|
+
detail: summary.latest.map((r) => `${nameOf(r.reviewerActorId)}\uFF08\u8BC4\u5BA1\u7EC4 ${groups.indexOf(r.reviewGroup) + 1}\uFF09\uFF1A${reviewLabel(r)}`).join("\n")
|
|
226953
|
+
} : {},
|
|
226954
|
+
...reviewNotes.length ? { reviewNotes } : {},
|
|
226955
|
+
...reviewLine ? { reviewThreadSessionId: reviewLine } : {},
|
|
226325
226956
|
artifacts: artifactsByWork.get(workId) ?? [],
|
|
226326
226957
|
actions: pending.length ? [
|
|
226327
226958
|
{ kind: "review", label: BUTTON.approve, target: first.nodeId, revisionId: workId, verdict: "approve" },
|
|
@@ -226361,7 +226992,7 @@ ${r.verdict === "request_changes" ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u
|
|
|
226361
226992
|
if (issueById.get(issueId)?.kind === "gap") {
|
|
226362
226993
|
d.traceAvailable = true;
|
|
226363
226994
|
d.traceWorkIds = gapOrigins.get(issueId);
|
|
226364
|
-
} else {
|
|
226995
|
+
} else if (!issueId.startsWith(REWORK_ANNOTATION_PREFIX)) {
|
|
226365
226996
|
d.traceWorkIds = issueWorks.get(issueId);
|
|
226366
226997
|
}
|
|
226367
226998
|
}
|
|
@@ -226382,24 +227013,31 @@ ${r.verdict === "request_changes" ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u
|
|
|
226382
227013
|
}
|
|
226383
227014
|
return void 0;
|
|
226384
227015
|
};
|
|
226385
|
-
const cards = drafts.map((d) =>
|
|
226386
|
-
|
|
226387
|
-
|
|
226388
|
-
|
|
226389
|
-
|
|
226390
|
-
|
|
226391
|
-
|
|
226392
|
-
|
|
226393
|
-
|
|
226394
|
-
|
|
226395
|
-
|
|
226396
|
-
|
|
226397
|
-
|
|
226398
|
-
|
|
226399
|
-
|
|
226400
|
-
|
|
226401
|
-
|
|
226402
|
-
|
|
227016
|
+
const cards = drafts.map((d) => {
|
|
227017
|
+
const traced = isAgentActor(d.executorId);
|
|
227018
|
+
return {
|
|
227019
|
+
id: d.id,
|
|
227020
|
+
seq: d.seq,
|
|
227021
|
+
at: d.at,
|
|
227022
|
+
updatedAt: d.updatedAt,
|
|
227023
|
+
...d.nodeId ? { nodeId: d.nodeId, nodeTitle: nodeName(d.nodeId) } : {},
|
|
227024
|
+
executor: ref2(d.executorId),
|
|
227025
|
+
...d.reviewWorkId ? { reviewWorkId: d.reviewWorkId, reviewers: d.reviewerIds?.map(ref2) } : {},
|
|
227026
|
+
...d.handActorId ? { handActor: ref2(d.handActorId) } : {},
|
|
227027
|
+
phase: d.phase,
|
|
227028
|
+
status: d.status,
|
|
227029
|
+
...d.detail ? { detail: d.detail } : {},
|
|
227030
|
+
...d.reviewNotes?.length ? { reviewNotes: d.reviewNotes } : {},
|
|
227031
|
+
...d.reviewThreadSessionId ? { reviewThreadSessionId: d.reviewThreadSessionId } : {},
|
|
227032
|
+
artifacts: d.artifacts,
|
|
227033
|
+
actions: d.actions,
|
|
227034
|
+
...traced && d.traceAvailable ? { traceAvailable: true } : {},
|
|
227035
|
+
...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, ...traced ? { traceAvailable: true } : {} } : {},
|
|
227036
|
+
.../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(traced ? runIdOfCard(d) : void 0)
|
|
227037
|
+
};
|
|
227038
|
+
});
|
|
227039
|
+
cards.push(...reviewRequirementActivity(snap, events, ref2));
|
|
227040
|
+
cards.sort((a, b2) => a.updatedAt.localeCompare(b2.updatedAt) || a.seq - b2.seq || a.id.localeCompare(b2.id));
|
|
226403
227041
|
return { workorderId, cards, truncated };
|
|
226404
227042
|
}
|
|
226405
227043
|
var ACTIVITY_EVENT_KINDS, ACTIVITY_EVENT_LIMIT, ACTIVITY_COPY, REWORK_ANNOTATION_PREFIX, ENGINE_RETRY_EXHAUSTED, BUTTON;
|
|
@@ -226409,9 +227047,12 @@ var init_activity2 = __esm({
|
|
|
226409
227047
|
init_src();
|
|
226410
227048
|
init_src4();
|
|
226411
227049
|
init_review_activity();
|
|
227050
|
+
init_review_requirement_activity();
|
|
226412
227051
|
init_workorder_manager();
|
|
226413
227052
|
ACTIVITY_EVENT_KINDS = [
|
|
226414
227053
|
"plan.changed",
|
|
227054
|
+
"plan.add_node",
|
|
227055
|
+
"plan.update_review_requirements",
|
|
226415
227056
|
"plan.update_spec",
|
|
226416
227057
|
"plan.node_retry",
|
|
226417
227058
|
"work.create",
|
|
@@ -226465,15 +227106,13 @@ var init_activity2 = __esm({
|
|
|
226465
227106
|
doneAwaitingReview: (name) => `\u300A${name}\u300B\u5DF2\u5B8C\u6210\uFF0C\u7B49\u5F85\u5BA1\u6838\u3002`,
|
|
226466
227107
|
done: (name) => `\u300A${name}\u300B\u5DF2\u5B8C\u6210\u3002`,
|
|
226467
227108
|
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`,
|
|
227109
|
+
/* 🚨 审核卡那一族文案(待审 / 正在审 / 通过 / 不通过 / 超时 / 熔断)**不在这里**:
|
|
227110
|
+
审核卡改成按「被审交付 × 评审人」合卡之后,它的小状态由 `review-activity.ts` 的
|
|
227111
|
+
`reviewSummary()` 按该评审人的**有效判定**整句给出,不是逐事件套模板。这里此前留着一份
|
|
227112
|
+
同名同义的副本,六个 key 全是零引用——我 2026-09-09 改「审核不通过」文案时先改到了这份
|
|
227113
|
+
死副本上,页面纹丝不动。删掉,别再留第二个事实源。
|
|
227114
|
+
留在这里的只有下面这一句——它不是小状态,是**审核意见条**上那个判定词,由本文件拼卡时用。 */
|
|
227115
|
+
reviewNoteLabel: (rejected) => rejected ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u6838\u610F\u89C1",
|
|
226477
227116
|
acceptPending: "\u8BF7\u9A8C\u6536\u6700\u7EC8\u4EA4\u4ED8\u3002",
|
|
226478
227117
|
accepted: "\u9A8C\u6536\u6700\u7EC8\u4EA4\u4ED8\u5DF2\u5B8C\u6210\u9A8C\u6536\u3002",
|
|
226479
227118
|
acceptedNode: (name) => `\u300A${name}\u300B\u5DF2\u9A8C\u6536\u3002`,
|
|
@@ -227641,9 +228280,18 @@ function toAuditEntry(op) {
|
|
|
227641
228280
|
summary: `${KIND_LABEL2[op.kind] ?? op.kind} \xB7 ${op.artifactId}`
|
|
227642
228281
|
};
|
|
227643
228282
|
}
|
|
228283
|
+
function nodeIdOfWorkId(workId) {
|
|
228284
|
+
if (!workId || !workId.startsWith("wk:")) return null;
|
|
228285
|
+
const body2 = workId.slice(3);
|
|
228286
|
+
const cut = body2.lastIndexOf(":");
|
|
228287
|
+
if (cut <= 0) return null;
|
|
228288
|
+
if (!/^\d+$/.test(body2.slice(cut + 1))) return null;
|
|
228289
|
+
const nodeId = body2.slice(0, cut);
|
|
228290
|
+
return nodeId.startsWith("artifact:") ? nodeId : null;
|
|
228291
|
+
}
|
|
227644
228292
|
function eventToAuditEntry(rec) {
|
|
227645
228293
|
const ev = rec.event;
|
|
227646
|
-
const artifactId = rec.nodeId ?? rec.workId ?? "";
|
|
228294
|
+
const artifactId = rec.nodeId ?? nodeIdOfWorkId(rec.workId) ?? "";
|
|
227647
228295
|
return {
|
|
227648
228296
|
seq: rec.seq,
|
|
227649
228297
|
at: rec.createdAt,
|
|
@@ -227653,7 +228301,11 @@ function eventToAuditEntry(rec) {
|
|
|
227653
228301
|
summary: `${KIND_LABEL2[ev.kind] ?? ev.kind}${artifactId ? ` \xB7 ${artifactId}` : ""}`
|
|
227654
228302
|
};
|
|
227655
228303
|
}
|
|
227656
|
-
function enrichAudit(entry, model, titleOf) {
|
|
228304
|
+
function enrichAudit(entry, model, titleOf, workorderId) {
|
|
228305
|
+
if (!entry.artifactId) {
|
|
228306
|
+
const workspaceTitle2 = workorderId ? titleOf.get(workorderId) : void 0;
|
|
228307
|
+
return { ...entry, ...workspaceTitle2 ? { workspaceTitle: workspaceTitle2 } : {}, visibility: "ok" };
|
|
228308
|
+
}
|
|
227657
228309
|
const a = model.artifacts.get(entry.artifactId);
|
|
227658
228310
|
if (!a) return { ...entry, visibility: "deleted" };
|
|
227659
228311
|
const workspaceTitle = titleOf.get(a.workspace);
|
|
@@ -227671,8 +228323,8 @@ async function buildAudit(source, q2 = {}, model) {
|
|
|
227671
228323
|
const items = [];
|
|
227672
228324
|
let lastCursor = null;
|
|
227673
228325
|
let truncated = false;
|
|
227674
|
-
const push2 = (entry, cursor) => {
|
|
227675
|
-
items.push(model && titleOf ? enrichAudit(entry, model, titleOf) : entry);
|
|
228326
|
+
const push2 = (entry, cursor, workorderId) => {
|
|
228327
|
+
items.push(model && titleOf ? enrichAudit(entry, model, titleOf, workorderId) : entry);
|
|
227676
228328
|
lastCursor = cursor;
|
|
227677
228329
|
};
|
|
227678
228330
|
const match = (actor, kind, at) => {
|
|
@@ -227683,30 +228335,33 @@ async function buildAudit(source, q2 = {}, model) {
|
|
|
227683
228335
|
return true;
|
|
227684
228336
|
};
|
|
227685
228337
|
if (q2.latest) {
|
|
227686
|
-
|
|
228338
|
+
const collected = [];
|
|
227687
228339
|
if (source.engineStore) {
|
|
227688
228340
|
const records = await source.engineStore.transaction((tx) => tx.listEvents(void 0, 0));
|
|
227689
228341
|
for (const rec of records) {
|
|
227690
228342
|
const ev = rec.event;
|
|
227691
228343
|
if (!match(rec.actorId, ev.kind, rec.createdAt)) continue;
|
|
227692
|
-
collected.push(eventToAuditEntry(rec));
|
|
228344
|
+
collected.push({ entry: eventToAuditEntry(rec), workorderId: rec.workorderId });
|
|
227693
228345
|
}
|
|
227694
228346
|
} else if (source.oplog) {
|
|
227695
228347
|
const src = source.oplog.readFilteredOps ? source.oplog.readFilteredOps({ ...q2.actor ? { actor: q2.actor } : {}, ...q2.kind ? { kind: q2.kind } : {} }, void 0, { latest: true }) : source.oplog.readAll();
|
|
227696
228348
|
for await (const { op } of src) {
|
|
227697
228349
|
if (!match(op.actor, op.kind, op.timestamp)) continue;
|
|
227698
|
-
collected.push(toAuditEntry(op));
|
|
228350
|
+
collected.push({ entry: toAuditEntry(op) });
|
|
227699
228351
|
}
|
|
227700
228352
|
}
|
|
227701
228353
|
const tail = collected.slice(-limit).reverse();
|
|
227702
|
-
return {
|
|
228354
|
+
return {
|
|
228355
|
+
items: model && titleOf ? tail.map(({ entry, workorderId }) => enrichAudit(entry, model, titleOf, workorderId)) : tail.map(({ entry }) => entry),
|
|
228356
|
+
cursor: null
|
|
228357
|
+
};
|
|
227703
228358
|
}
|
|
227704
228359
|
if (source.engineStore) {
|
|
227705
228360
|
const records = await source.engineStore.transaction((tx) => tx.listEvents(void 0, fromSeq));
|
|
227706
228361
|
for (const rec of records) {
|
|
227707
228362
|
const ev = rec.event;
|
|
227708
228363
|
if (!match(rec.actorId, ev.kind, rec.createdAt)) continue;
|
|
227709
|
-
push2(eventToAuditEntry(rec), String(rec.seq));
|
|
228364
|
+
push2(eventToAuditEntry(rec), String(rec.seq), rec.workorderId);
|
|
227710
228365
|
if (items.length >= limit) {
|
|
227711
228366
|
truncated = true;
|
|
227712
228367
|
break;
|
|
@@ -227752,6 +228407,30 @@ var init_audit = __esm({
|
|
|
227752
228407
|
"workorder.paused": "\u6682\u505C\u6D3E\u53D1",
|
|
227753
228408
|
"workorder.resumed": "\u6062\u590D\u6D3E\u53D1",
|
|
227754
228409
|
"workorder.sealed": "\u5C01\u5B58",
|
|
228410
|
+
// 其余新引擎事件 kind——缺标签时 summary 直接露英文 kind(生产实测:`plan.changed` 是量最大的
|
|
228411
|
+
// 一类审计条目,界面上就写着「plan.changed」)。这张表按 EngineEvent 的 kind 全集补齐。
|
|
228412
|
+
"plan.changed": "\u6539\u4EFB\u52A1\u56FE",
|
|
228413
|
+
"plan.add_edge": "\u63A5\u4F9D\u8D56",
|
|
228414
|
+
"plan.delete_edge": "\u65AD\u4F9D\u8D56",
|
|
228415
|
+
"plan.update_review_requirements": "\u6539\u8BC4\u5BA1\u8981\u6C42",
|
|
228416
|
+
"plan.update_fields": "\u6539\u5B57\u6BB5",
|
|
228417
|
+
"work.started": "\u5F00\u5DE5",
|
|
228418
|
+
"work.redispatch": "\u91CD\u6D3E",
|
|
228419
|
+
"work.snapshot_recorded": "\u5B58\u5FEB\u7167",
|
|
228420
|
+
"work.handoff_recorded": "\u8BB0\u4EA4\u63A5",
|
|
228421
|
+
"work.reject": "\u6253\u56DE",
|
|
228422
|
+
"work.content_edited": "\u6539\u5185\u5BB9",
|
|
228423
|
+
"work.conclude": "\u5B9A\u7A3F",
|
|
228424
|
+
"node.latest_proposed.rebased": "\u6362\u57FA\u7EBF",
|
|
228425
|
+
"review.started": "\u5F00\u59CB\u8BC4\u5BA1",
|
|
228426
|
+
"review.kill": "\u64A4\u8BC4\u5BA1",
|
|
228427
|
+
"review.timeout": "\u8BC4\u5BA1\u8D85\u65F6",
|
|
228428
|
+
"issue.resolution_proposed": "\u63D0\u89E3\u51B3\u65B9\u6848",
|
|
228429
|
+
"issue.resolution_rejected": "\u9A73\u56DE\u89E3\u51B3\u65B9\u6848",
|
|
228430
|
+
"workorder.created": "\u5EFA\u5DE5\u5355",
|
|
228431
|
+
"workorder.root_set": "\u5B9A\u6839\u8282\u70B9",
|
|
228432
|
+
"workorder.meta_changed": "\u6539\u5DE5\u5355\u4FE1\u606F",
|
|
228433
|
+
"workorder.ping": "\u5524\u9192",
|
|
227755
228434
|
// 旧 op kind(oplog 回退路径)
|
|
227756
228435
|
spawn_artifact: "\u5EFA\u4EA7\u7269",
|
|
227757
228436
|
propose_revision: "\u63D0\u4FEE\u8BA2",
|
|
@@ -229337,7 +230016,15 @@ function collabDomain(opts) {
|
|
|
229337
230016
|
}
|
|
229338
230017
|
const text5 = raw;
|
|
229339
230018
|
try {
|
|
230019
|
+
const snap = await kernel.getStore().transaction((tx) => tx.loadWorkorder(ws));
|
|
230020
|
+
if (snap?.workorder.dispatchPaused && !isHumanActor(req.auth.actor)) {
|
|
230021
|
+
return { status: 403, body: { error: { code: "forbidden", message: "\u6682\u505C\u671F\u95F4\u5F55\u5165\u5DE5\u5355\u6B63\u6587\u4EC5\u9650\u4EBA\u7C7B\u64CD\u4F5C" } } };
|
|
230022
|
+
}
|
|
229340
230023
|
const contentRef = await blobs.put(new TextEncoder().encode(text5));
|
|
230024
|
+
if (snap?.workorder.dispatchPaused) {
|
|
230025
|
+
await kernel.recordPausedRevision({ artifactId: rootId, actor: req.auth.actor, contentRef, note: "\u7F16\u8F91\u5DE5\u5355\u9875\u5934\u6B63\u6587" });
|
|
230026
|
+
return { status: 200, body: { overview: text5, concluded: false } };
|
|
230027
|
+
}
|
|
229341
230028
|
await kernel.proposeRevision({ artifactId: rootId, actor: req.auth.actor, contentRef, reason: "\u7F16\u8F91\u5DE5\u5355\u9875\u5934\u6B63\u6587" });
|
|
229342
230029
|
await kernel.advanceQueue(rootId, req.auth.actor);
|
|
229343
230030
|
const result = await kernel.conclude({ artifactId: rootId, actor: req.auth.actor, note: "\u7F16\u8F91\u5DE5\u5355\u9875\u5934\u6B63\u6587" });
|
|
@@ -229345,7 +230032,7 @@ function collabDomain(opts) {
|
|
|
229345
230032
|
} catch (err) {
|
|
229346
230033
|
if (err instanceof KernelError) {
|
|
229347
230034
|
return {
|
|
229348
|
-
status: 400,
|
|
230035
|
+
status: err.code === "revision-conflict" ? 409 : 400,
|
|
229349
230036
|
body: { error: { code: err.code ?? "kernel_error", message: err.message } }
|
|
229350
230037
|
};
|
|
229351
230038
|
}
|
|
@@ -232404,12 +233091,83 @@ function buildCommentThread(input) {
|
|
|
232404
233091
|
bubbles
|
|
232405
233092
|
};
|
|
232406
233093
|
}
|
|
233094
|
+
function isReviewThreadSessionId(sessionId) {
|
|
233095
|
+
return typeof sessionId === "string" && sessionId.startsWith(REVIEW_THREAD_SESSION_PREFIX);
|
|
233096
|
+
}
|
|
233097
|
+
function partiesOfReviewThreadSessionId(sessionId) {
|
|
233098
|
+
if (!isReviewThreadSessionId(sessionId)) return null;
|
|
233099
|
+
const parts = sessionId.slice(REVIEW_THREAD_SESSION_PREFIX.length).split("|");
|
|
233100
|
+
return parts.length === 2 && parts[0] && parts[1] ? [parts[0], parts[1]] : null;
|
|
233101
|
+
}
|
|
233102
|
+
function buildReviewThread(input) {
|
|
233103
|
+
const parties = partiesOfReviewThreadSessionId(input.sessionId);
|
|
233104
|
+
if (!parties || !input.snap) return null;
|
|
233105
|
+
const [a, b2] = parties;
|
|
233106
|
+
const works = new Map(input.snap.works.map((w2) => [w2.id, w2]));
|
|
233107
|
+
const rows = input.snap.reviews.filter((r) => {
|
|
233108
|
+
const reviewee = works.get(r.targetWorkId)?.assigneeActorId;
|
|
233109
|
+
if (!reviewee || reviewee === r.reviewerActorId) return false;
|
|
233110
|
+
const pair = [r.reviewerActorId, reviewee].sort();
|
|
233111
|
+
return pair[0] === a && pair[1] === b2;
|
|
233112
|
+
});
|
|
233113
|
+
if (rows.length === 0) return null;
|
|
233114
|
+
const { ref: ref2 } = input;
|
|
233115
|
+
const nameOf = (id) => ref2(id).name?.trim() || "\u2014";
|
|
233116
|
+
const mention = (author) => nameOf(author === a ? b2 : a);
|
|
233117
|
+
const bubble = (o) => ({
|
|
233118
|
+
id: o.id,
|
|
233119
|
+
at: o.at,
|
|
233120
|
+
author: ref2(o.author),
|
|
233121
|
+
text: `@${mention(o.author)} ${o.body}`,
|
|
233122
|
+
issueId: o.issueId,
|
|
233123
|
+
kind: o.kind
|
|
233124
|
+
});
|
|
233125
|
+
const bubbles = [];
|
|
233126
|
+
for (const workId of new Set(rows.map((r) => r.targetWorkId))) {
|
|
233127
|
+
const w2 = works.get(workId);
|
|
233128
|
+
const body2 = w2?.conclusion?.trim();
|
|
233129
|
+
if (!w2 || !body2) continue;
|
|
233130
|
+
bubbles.push(bubble({
|
|
233131
|
+
id: `${workId}:delivered`,
|
|
233132
|
+
at: w2.endedAt || w2.createdAt,
|
|
233133
|
+
author: w2.assigneeActorId,
|
|
233134
|
+
body: body2,
|
|
233135
|
+
issueId: workId,
|
|
233136
|
+
kind: "raised"
|
|
233137
|
+
}));
|
|
233138
|
+
}
|
|
233139
|
+
for (const r of rows) {
|
|
233140
|
+
const note = r.note?.trim();
|
|
233141
|
+
if (!note) continue;
|
|
233142
|
+
const label = r.verdict === "request_changes" ? "\u5BA1\u6838\u4E0D\u901A\u8FC7" : "\u5BA1\u6838\u610F\u89C1";
|
|
233143
|
+
bubbles.push(bubble({
|
|
233144
|
+
id: `${r.id}:verdict`,
|
|
233145
|
+
at: r.decidedAt || r.endedAt || r.createdAt,
|
|
233146
|
+
author: r.reviewerActorId,
|
|
233147
|
+
body: `${label}\uFF1A${note}`,
|
|
233148
|
+
issueId: r.targetWorkId,
|
|
233149
|
+
kind: "reply"
|
|
233150
|
+
}));
|
|
233151
|
+
}
|
|
233152
|
+
if (bubbles.length === 0) return null;
|
|
233153
|
+
bubbles.sort((x2, y) => x2.at.localeCompare(y.at) || x2.id.localeCompare(y.id));
|
|
233154
|
+
const first = rows[0];
|
|
233155
|
+
const revieweeId = works.get(first.targetWorkId).assigneeActorId;
|
|
233156
|
+
return {
|
|
233157
|
+
sessionId: input.sessionId,
|
|
233158
|
+
workorderId: input.workorderId,
|
|
233159
|
+
executor: ref2(revieweeId),
|
|
233160
|
+
coordinator: ref2(first.reviewerActorId),
|
|
233161
|
+
bubbles
|
|
233162
|
+
};
|
|
233163
|
+
}
|
|
232407
233164
|
var init_gap_thread = __esm({
|
|
232408
233165
|
"../server/src/domains/collab/gap-thread.ts"() {
|
|
232409
233166
|
"use strict";
|
|
232410
233167
|
init_src();
|
|
232411
233168
|
init_workorder_manager();
|
|
232412
233169
|
init_src();
|
|
233170
|
+
init_src();
|
|
232413
233171
|
}
|
|
232414
233172
|
});
|
|
232415
233173
|
|
|
@@ -233047,12 +233805,12 @@ function createChatSessionsDomain(opts) {
|
|
|
233047
233805
|
router.get("/api/workorders/:workorderId/gap-thread", async (req) => {
|
|
233048
233806
|
const workorderId = req.params["workorderId"];
|
|
233049
233807
|
const sessionId = req.query.get("session") ?? "";
|
|
233050
|
-
if (!isGapThreadSessionId(sessionId) && !isCommentThreadSessionId(sessionId)) {
|
|
233808
|
+
if (!isGapThreadSessionId(sessionId) && !isCommentThreadSessionId(sessionId) && !isReviewThreadSessionId(sessionId)) {
|
|
233051
233809
|
throw new ApiError(404, "NOT_FOUND", "thread not found");
|
|
233052
233810
|
}
|
|
233053
233811
|
const { snap, ref: ref2 } = await gapThreadInputsFor(req, workorderId);
|
|
233054
233812
|
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 });
|
|
233813
|
+
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
233814
|
if (!view) throw new ApiError(404, "NOT_FOUND", "gap thread not found");
|
|
233057
233815
|
return { status: 200, body: view };
|
|
233058
233816
|
});
|
|
@@ -237267,6 +238025,11 @@ var init_daemon_adapter = __esm({
|
|
|
237267
238025
|
clearTimeout(entry.ackTimer);
|
|
237268
238026
|
entry.ackTimer = void 0;
|
|
237269
238027
|
}
|
|
238028
|
+
const reapTimer = this.sessionReapTimers.get(dispatchId);
|
|
238029
|
+
if (reapTimer) {
|
|
238030
|
+
clearTimeout(reapTimer);
|
|
238031
|
+
this.sessionReapTimers.delete(dispatchId);
|
|
238032
|
+
}
|
|
237270
238033
|
entry.exited = info;
|
|
237271
238034
|
for (const w2 of entry.appendWaiters.splice(0)) {
|
|
237272
238035
|
clearTimeout(w2.timer);
|
|
@@ -237362,6 +238125,10 @@ var init_daemon_adapter = __esm({
|
|
|
237362
238125
|
for (const [dispatchId, entry] of this.pending) {
|
|
237363
238126
|
if (entry.nodeId !== daemonId || entry.exited || present.has(dispatchId)) continue;
|
|
237364
238127
|
if (this.hub.hasSession?.(dispatchId)) continue;
|
|
238128
|
+
if (this.sessionReapTimers.has(dispatchId)) {
|
|
238129
|
+
this.log(`[dispatch-delivery] ${dispatchId}\uFF1A\u8282\u70B9 ${daemonId} \u7A7A\u5E93\u5B58\u4E0D\u8986\u76D6\u72EC\u7ACB\u4F1A\u8BDD\u65AD\u7EBF\u5BBD\u9650\uFF0C\u7ED3\u8BBA=unknown\uFF08\u4F9D\u636E\uFF1A\u6570\u636E\u9762\u66FE\u8FDE\u63A5\uFF0C\u7B49\u5F85\u91CD\u8FDE\u6216\u539F ${this.sessionReapGraceMs}ms \u671F\u9650\uFF09`);
|
|
238130
|
+
continue;
|
|
238131
|
+
}
|
|
237365
238132
|
absent.push(dispatchId);
|
|
237366
238133
|
}
|
|
237367
238134
|
for (const dispatchId of absent) {
|
|
@@ -237394,8 +238161,7 @@ var init_daemon_adapter = __esm({
|
|
|
237394
238161
|
*/
|
|
237395
238162
|
onSessionDown(dispatchId) {
|
|
237396
238163
|
if (this.sessionReapTimers.has(dispatchId)) return;
|
|
237397
|
-
|
|
237398
|
-
if (!entry || entry.exited) return;
|
|
238164
|
+
if (this.settledDispatchIds.has(dispatchId)) return;
|
|
237399
238165
|
const timer = setTimeout(() => {
|
|
237400
238166
|
this.sessionReapTimers.delete(dispatchId);
|
|
237401
238167
|
const e = this.pending.get(dispatchId);
|
|
@@ -238731,6 +239497,7 @@ ${ctx.nodeFault}
|
|
|
238731
239497
|
env: { ...prov?.env ?? {}, OASIS_STAGE: "1", ...workspace ? { OASIS_WORKSPACE: workspace } : {} },
|
|
238732
239498
|
...prov?.wrapperPaths && prov.wrapperPaths.length > 0 ? { wrapperPaths: prov.wrapperPaths } : {},
|
|
238733
239499
|
...prov?.requiredTools && prov.requiredTools.length > 0 ? { requiredTools: prov.requiredTools } : {},
|
|
239500
|
+
...prov?.requiredToolVersions && Object.keys(prov.requiredToolVersions).length > 0 ? { requiredToolVersions: prov.requiredToolVersions } : {},
|
|
238734
239501
|
...prov?.connectorCreds && prov.connectorCreds.length > 0 ? { connectorCreds: prov.connectorCreds } : {}
|
|
238735
239502
|
};
|
|
238736
239503
|
this.deps.log?.(`[coordinator] ${logMsg}`);
|
|
@@ -239291,6 +240058,7 @@ var init_postgres_registry = __esm({
|
|
|
239291
240058
|
updated_at timestamptz NOT NULL,
|
|
239292
240059
|
PRIMARY KEY (actor_id, connector_id)
|
|
239293
240060
|
)`);
|
|
240061
|
+
await pool.query(`ALTER TABLE "${s2}".actor_connector_connections ADD COLUMN IF NOT EXISTS configured_by text`);
|
|
239294
240062
|
await pool.query(`
|
|
239295
240063
|
CREATE TABLE IF NOT EXISTS "${s2}".skill_catalog (
|
|
239296
240064
|
id text PRIMARY KEY, -- slug
|
|
@@ -239678,10 +240446,11 @@ var init_postgres_registry = __esm({
|
|
|
239678
240446
|
}
|
|
239679
240447
|
async upsertActorConnectorConnection(c) {
|
|
239680
240448
|
await this.pool.query(
|
|
239681
|
-
`INSERT INTO ${this.s}.actor_connector_connections (actor_id, connector_id, enabled, updated_at)
|
|
239682
|
-
VALUES ($1, $2, $3, $4)
|
|
239683
|
-
ON CONFLICT (actor_id, connector_id) DO UPDATE SET enabled = $3, updated_at = $4
|
|
239684
|
-
|
|
240449
|
+
`INSERT INTO ${this.s}.actor_connector_connections (actor_id, connector_id, enabled, updated_at, configured_by)
|
|
240450
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
240451
|
+
ON CONFLICT (actor_id, connector_id) DO UPDATE SET enabled = $3, updated_at = $4,
|
|
240452
|
+
configured_by = COALESCE($5, ${this.s}.actor_connector_connections.configured_by)`,
|
|
240453
|
+
[c.actorId, c.connectorId, c.enabled, c.updatedAt, c.configuredBy ?? null]
|
|
239685
240454
|
);
|
|
239686
240455
|
}
|
|
239687
240456
|
async listActorConnectorConnections(actorId) {
|
|
@@ -239693,7 +240462,8 @@ var init_postgres_registry = __esm({
|
|
|
239693
240462
|
actorId: row.actor_id,
|
|
239694
240463
|
connectorId: row.connector_id,
|
|
239695
240464
|
enabled: row.enabled,
|
|
239696
|
-
updatedAt: new Date(row.updated_at).toISOString()
|
|
240465
|
+
updatedAt: new Date(row.updated_at).toISOString(),
|
|
240466
|
+
...row.configured_by ? { configuredBy: row.configured_by } : {}
|
|
239697
240467
|
}));
|
|
239698
240468
|
}
|
|
239699
240469
|
async listAllActorConnectorConnections() {
|
|
@@ -239704,7 +240474,8 @@ var init_postgres_registry = __esm({
|
|
|
239704
240474
|
actorId: row.actor_id,
|
|
239705
240475
|
connectorId: row.connector_id,
|
|
239706
240476
|
enabled: row.enabled,
|
|
239707
|
-
updatedAt: new Date(row.updated_at).toISOString()
|
|
240477
|
+
updatedAt: new Date(row.updated_at).toISOString(),
|
|
240478
|
+
...row.configured_by ? { configuredBy: row.configured_by } : {}
|
|
239708
240479
|
}));
|
|
239709
240480
|
}
|
|
239710
240481
|
async deleteActorConnectorConnection(actorId, connectorId) {
|
|
@@ -246157,6 +246928,21 @@ var init_postgres_chat_sessions = __esm({
|
|
|
246157
246928
|
);
|
|
246158
246929
|
return r.rows.map(rowToMessage);
|
|
246159
246930
|
}
|
|
246931
|
+
/** 按本 schema 中的会话 + run 精确找轮,不能用 activeTurn:已取消的轮已不占 active slot。 */
|
|
246932
|
+
async getTurnForRun(sessionId, runId) {
|
|
246933
|
+
const r = await this.pool.query(
|
|
246934
|
+
`SELECT id, status, completed_at, last_error FROM ${this.s}.chat_session_turns
|
|
246935
|
+
WHERE chat_session_id = $1 AND assistant_run_id = $2 ORDER BY reserved_at DESC LIMIT 1`,
|
|
246936
|
+
[sessionId, runId]
|
|
246937
|
+
);
|
|
246938
|
+
const row = r.rows[0];
|
|
246939
|
+
return row ? {
|
|
246940
|
+
id: row.id,
|
|
246941
|
+
status: row.status,
|
|
246942
|
+
completedAt: row.completed_at == null ? null : new Date(row.completed_at).toISOString(),
|
|
246943
|
+
lastError: row.last_error ?? null
|
|
246944
|
+
} : null;
|
|
246945
|
+
}
|
|
246160
246946
|
async getSession(id) {
|
|
246161
246947
|
const r = await this.pool.query(`SELECT * FROM ${this.s}.chat_sessions WHERE id = $1`, [id]);
|
|
246162
246948
|
return r.rows[0] ? rowToSession(r.rows[0]) : null;
|
|
@@ -254703,6 +255489,98 @@ var init_chat_builtin_skills = __esm({
|
|
|
254703
255489
|
}
|
|
254704
255490
|
});
|
|
254705
255491
|
|
|
255492
|
+
// ../server/src/governance/connector-tool-versions.ts
|
|
255493
|
+
function connectorToolVersionsFile(dataDir) {
|
|
255494
|
+
return (0, import_node_path25.join)(dataDir, "connector-tools", "versions.json");
|
|
255495
|
+
}
|
|
255496
|
+
function registryBase() {
|
|
255497
|
+
const raw = process.env["npm_config_registry"] || process.env["NPM_CONFIG_REGISTRY"] || "https://registry.npmjs.org";
|
|
255498
|
+
return raw.replace(/\/+$/, "");
|
|
255499
|
+
}
|
|
255500
|
+
async function fetchLatestNpmVersion(pkg) {
|
|
255501
|
+
try {
|
|
255502
|
+
const url = `${registryBase()}/${pkg.split("/").map(encodeURIComponent).join("%2F")}/latest`;
|
|
255503
|
+
const res = await fetch(url, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(NPM_PROBE_TIMEOUT_MS) });
|
|
255504
|
+
if (!res.ok) return null;
|
|
255505
|
+
const v2 = (await res.json()).version;
|
|
255506
|
+
return typeof v2 === "string" && v2 ? v2 : null;
|
|
255507
|
+
} catch {
|
|
255508
|
+
return null;
|
|
255509
|
+
}
|
|
255510
|
+
}
|
|
255511
|
+
async function readRegisteredToolVersions(dataDir) {
|
|
255512
|
+
try {
|
|
255513
|
+
const raw = JSON.parse(await (0, import_promises14.readFile)(connectorToolVersionsFile(dataDir), "utf8"));
|
|
255514
|
+
if (!raw || typeof raw !== "object") return {};
|
|
255515
|
+
const out = {};
|
|
255516
|
+
for (const [k2, v2] of Object.entries(raw)) {
|
|
255517
|
+
if (typeof v2 === "string" && v2) out[k2] = v2;
|
|
255518
|
+
}
|
|
255519
|
+
return out;
|
|
255520
|
+
} catch {
|
|
255521
|
+
return {};
|
|
255522
|
+
}
|
|
255523
|
+
}
|
|
255524
|
+
async function writeRegisteredToolVersions(dataDir, versions) {
|
|
255525
|
+
const file = connectorToolVersionsFile(dataDir);
|
|
255526
|
+
await (0, import_promises14.mkdir)((0, import_node_path25.join)(dataDir, "connector-tools"), { recursive: true });
|
|
255527
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
255528
|
+
await (0, import_promises14.writeFile)(tmp, `${JSON.stringify(versions, null, 2)}
|
|
255529
|
+
`, "utf8");
|
|
255530
|
+
await (0, import_promises14.rename)(tmp, file);
|
|
255531
|
+
}
|
|
255532
|
+
async function refreshConnectorToolVersions(opts) {
|
|
255533
|
+
const fetchLatest = opts.fetchLatest ?? fetchLatestNpmVersion;
|
|
255534
|
+
const log3 = opts.log ?? (() => {
|
|
255535
|
+
});
|
|
255536
|
+
const versions = await readRegisteredToolVersions(opts.dataDir);
|
|
255537
|
+
const results = [];
|
|
255538
|
+
let changed = false;
|
|
255539
|
+
for (const [tool, pkg] of Object.entries(NPM_TOOL_PACKAGES)) {
|
|
255540
|
+
const before = versions[tool];
|
|
255541
|
+
const latest = await fetchLatest(pkg);
|
|
255542
|
+
if (!latest) {
|
|
255543
|
+
results.push({ tool, pkg, ...before ? { from: before, to: before } : {}, outcome: "unreachable" });
|
|
255544
|
+
log3(`[connector-tools] ${pkg} \u95EE\u4E0D\u5230\u6700\u65B0\u7248\uFF0C\u6CBF\u7528\u767B\u8BB0\u7248\u672C ${before ?? "\uFF08\u65E0\uFF09"}`);
|
|
255545
|
+
continue;
|
|
255546
|
+
}
|
|
255547
|
+
if (before === latest) {
|
|
255548
|
+
results.push({ tool, pkg, from: before, to: latest, outcome: "unchanged" });
|
|
255549
|
+
continue;
|
|
255550
|
+
}
|
|
255551
|
+
versions[tool] = latest;
|
|
255552
|
+
changed = true;
|
|
255553
|
+
results.push({ tool, pkg, ...before ? { from: before } : {}, to: latest, outcome: "updated" });
|
|
255554
|
+
log3(`[connector-tools] ${pkg} \u767B\u8BB0\u7248\u672C ${before ?? "\uFF08\u65E0\uFF09"} \u2192 ${latest}`);
|
|
255555
|
+
}
|
|
255556
|
+
if (changed) {
|
|
255557
|
+
try {
|
|
255558
|
+
await writeRegisteredToolVersions(opts.dataDir, versions);
|
|
255559
|
+
} catch (err) {
|
|
255560
|
+
log3(`[connector-tools] \u767B\u8BB0\u7248\u672C\u843D\u76D8\u5931\u8D25\uFF08\u672C\u8FDB\u7A0B\u5185\u4ECD\u751F\u6548\uFF09\uFF1A${String(err)}`);
|
|
255561
|
+
}
|
|
255562
|
+
}
|
|
255563
|
+
return { versions, results };
|
|
255564
|
+
}
|
|
255565
|
+
function pickToolVersions(registered, tools) {
|
|
255566
|
+
const out = {};
|
|
255567
|
+
for (const t of tools) {
|
|
255568
|
+
const v2 = registered[t];
|
|
255569
|
+
if (v2) out[t] = v2;
|
|
255570
|
+
}
|
|
255571
|
+
return out;
|
|
255572
|
+
}
|
|
255573
|
+
var import_promises14, import_node_path25, NPM_PROBE_TIMEOUT_MS;
|
|
255574
|
+
var init_connector_tool_versions = __esm({
|
|
255575
|
+
"../server/src/governance/connector-tool-versions.ts"() {
|
|
255576
|
+
"use strict";
|
|
255577
|
+
import_promises14 = require("node:fs/promises");
|
|
255578
|
+
import_node_path25 = require("node:path");
|
|
255579
|
+
init_src8();
|
|
255580
|
+
NPM_PROBE_TIMEOUT_MS = 3e3;
|
|
255581
|
+
}
|
|
255582
|
+
});
|
|
255583
|
+
|
|
254706
255584
|
// ../server/src/design/board-watch.ts
|
|
254707
255585
|
function decideBoardLetter(c, nowMs, quietMs) {
|
|
254708
255586
|
if (c.hasOpenWatchLetter) return false;
|
|
@@ -255498,6 +256376,7 @@ var init_src11 = __esm({
|
|
|
255498
256376
|
init_builtin_skills();
|
|
255499
256377
|
init_chat_builtin_skills();
|
|
255500
256378
|
init_connector_skills();
|
|
256379
|
+
init_connector_tool_versions();
|
|
255501
256380
|
init_board_watch();
|
|
255502
256381
|
init_ephemeral_project();
|
|
255503
256382
|
init_run_settlement();
|
|
@@ -256360,6 +257239,7 @@ function materializeBuiltins(builtins, runtimeKind, opts) {
|
|
|
256360
257239
|
var CONNECTOR_SKILL_SOURCES = [
|
|
256361
257240
|
{ connectorId: "feishu", baseUrl: FEISHU_WELL_KNOWN_SKILLS_BASE }
|
|
256362
257241
|
];
|
|
257242
|
+
var registeredToolVersions = {};
|
|
256363
257243
|
async function readAllConnectorSkills(dataDir) {
|
|
256364
257244
|
const out = [];
|
|
256365
257245
|
for (const src of CONNECTOR_SKILL_SOURCES) {
|
|
@@ -256391,11 +257271,18 @@ async function refreshConnectorSkillsNow(dataDir, apply) {
|
|
|
256391
257271
|
}
|
|
256392
257272
|
}
|
|
256393
257273
|
if (out.length > 0) apply(out);
|
|
257274
|
+
try {
|
|
257275
|
+
const { versions } = await refreshConnectorToolVersions({ dataDir, log: (m2) => console.log(m2) });
|
|
257276
|
+
registeredToolVersions = versions;
|
|
257277
|
+
} catch (err) {
|
|
257278
|
+
console.warn(`[serve] connector CLI \u767B\u8BB0\u7248\u672C\u5237\u65B0\u5931\u8D25\uFF0C\u6CBF\u7528\u4E0A\u4E00\u4EFD\uFF1A${String(err)}`);
|
|
257279
|
+
}
|
|
256394
257280
|
return results;
|
|
256395
257281
|
}
|
|
256396
257282
|
function refreshConnectorSkillsInBackground(dataDir, apply) {
|
|
256397
257283
|
void refreshConnectorSkillsNow(dataDir, apply);
|
|
256398
257284
|
}
|
|
257285
|
+
var CONNECTOR_SKILL_REFRESH_MS = 24 * 60 * 6e4;
|
|
256399
257286
|
var SESSION_TOKEN_GRACE_MS = 15 * 6e4;
|
|
256400
257287
|
function makeResumeRetryProxy(first, spawnFallback) {
|
|
256401
257288
|
const relay = () => {
|
|
@@ -256589,6 +257476,8 @@ async function buildActorProvision(service, actorId, scope, onBrokerRefused) {
|
|
|
256589
257476
|
env,
|
|
256590
257477
|
wrapperPaths: [oasisWrapperScript(), ...provision.wrapperPaths],
|
|
256591
257478
|
requiredTools: provision.requiredTools,
|
|
257479
|
+
// 只挑本次真要用的那几个:job 里不塞无关工具的版本号。
|
|
257480
|
+
requiredToolVersions: pickToolVersions(registeredToolVersions, provision.requiredTools),
|
|
256592
257481
|
connectorCreds: provision.connectorCreds,
|
|
256593
257482
|
cleanup: provision.cleanup
|
|
256594
257483
|
};
|
|
@@ -256992,6 +257881,7 @@ async function startServe(opts) {
|
|
|
256992
257881
|
if (!cid || cid === defaultCompanyId) return "";
|
|
256993
257882
|
return ` [${cid}]`;
|
|
256994
257883
|
};
|
|
257884
|
+
const engineSchemaFor = (companyId) => companyId === defaultCompanyId ? defaultCompanySchema : companyEngineDirName(companyId);
|
|
256995
257885
|
let getEngine = async () => {
|
|
256996
257886
|
throw new Error("company engine router not initialized");
|
|
256997
257887
|
};
|
|
@@ -257097,7 +257987,7 @@ async function startServe(opts) {
|
|
|
257097
257987
|
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
257988
|
nodeStore = await PostgresNodeStore.open(pgPool, sharedSchema);
|
|
257099
257989
|
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`);
|
|
257990
|
+
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
257991
|
} else {
|
|
257102
257992
|
projectStateStore = await FileProjectStateStore.open(path29.join(opts.dir, "artifact-document-projects.json"));
|
|
257103
257993
|
artifactStateStore = await FileArtifactStateStore.open(path29.join(opts.dir, "artifact-document-state.json"));
|
|
@@ -257108,7 +257998,6 @@ async function startServe(opts) {
|
|
|
257108
257998
|
console.log("[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
257109
257999
|
}
|
|
257110
258000
|
const chatRecoveryStore = platformChatRecovery ?? chatSessionStore;
|
|
257111
|
-
const chatRecoveryItems = pgPool ? await PostgresChatItemStore.open(pgPool, defaultCompanySchema) : void 0;
|
|
257112
258001
|
const knowledgeConfigStore = await FileKnowledgeConfigStore.open(path29.join(opts.dir, "knowledge.json"));
|
|
257113
258002
|
const knowledgeRunStore = await FileKnowledgeRunStore.open(path29.join(opts.dir, "knowledge-runs.json"));
|
|
257114
258003
|
const knowledgeDeploymentId = await loadOrCreateKnowledgeDeploymentId(path29.join(opts.dir, "knowledge-deployment-id"));
|
|
@@ -257777,10 +258666,17 @@ async function startServe(opts) {
|
|
|
257777
258666
|
});
|
|
257778
258667
|
console.log(`[serve] loaded ${builtinSkills.length} builtin skill(s): ${builtinSkills.map((s2) => s2.id).join(", ") || "(none)"}`);
|
|
257779
258668
|
connectorSkills = await readAllConnectorSkills(opts.dir);
|
|
258669
|
+
registeredToolVersions = await readRegisteredToolVersions(opts.dir);
|
|
257780
258670
|
console.log(`[serve] connector skills\uFF08\u843D\u76D8\u526F\u672C\uFF09\uFF1A${connectorSkills.length} \u4E2A`);
|
|
257781
258671
|
refreshConnectorSkillsInBackground(opts.dir, (s2) => {
|
|
257782
258672
|
connectorSkills = s2;
|
|
257783
258673
|
});
|
|
258674
|
+
const connectorSkillTimer = setInterval(() => {
|
|
258675
|
+
refreshConnectorSkillsInBackground(opts.dir, (s2) => {
|
|
258676
|
+
connectorSkills = s2;
|
|
258677
|
+
});
|
|
258678
|
+
}, CONNECTOR_SKILL_REFRESH_MS);
|
|
258679
|
+
connectorSkillTimer.unref?.();
|
|
257784
258680
|
if (defaultCompanyMigration.createdCompany) {
|
|
257785
258681
|
const seededAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
257786
258682
|
for (const devActor of ["actor:human:yx"]) {
|
|
@@ -257813,7 +258709,7 @@ async function startServe(opts) {
|
|
|
257813
258709
|
id: c.id,
|
|
257814
258710
|
// 默认公司的引擎表仍在共享 schema(ADR 0164 §D5 的 RP0,搬迁另立单)——这条映射
|
|
257815
258711
|
// 搞错,默认公司的工单会一张都对不上,回填会把它们全判成「无证据」。
|
|
257816
|
-
engineSchema:
|
|
258712
|
+
engineSchema: engineSchemaFor(c.id)
|
|
257817
258713
|
}));
|
|
257818
258714
|
const res = await backfillTenancyColumns(tenancyPool, { schema: defaultCompanySchema, companies: companies2, apply: true });
|
|
257819
258715
|
const wrote = res.rows.reduce((n, r) => n + r.updated, 0);
|
|
@@ -257988,28 +258884,28 @@ async function startServe(opts) {
|
|
|
257988
258884
|
const assistants2 = await PostgresAssistantBindStore.open(pgPool, loc.schemaName, { companyId });
|
|
257989
258885
|
const humanPrefs2 = await PostgresHumanPrefsStore.open(pgPool, loc.schemaName);
|
|
257990
258886
|
const ownedReadMarkers2 = await PostgresReadMarkerStore.open(pgPool, loc.schemaName, {
|
|
257991
|
-
chatSessionsSchema:
|
|
258887
|
+
chatSessionsSchema: engineSchemaFor(companyId)
|
|
257992
258888
|
});
|
|
257993
258889
|
const ownedMemory2 = await PostgresActorMemoryStore.open(pgPool, loc.schemaName);
|
|
257994
258890
|
const ownedDrafts2 = await PostgresWorkorderDraftStore.open(
|
|
257995
258891
|
pgPool,
|
|
257996
|
-
|
|
258892
|
+
engineSchemaFor(companyId)
|
|
257997
258893
|
);
|
|
257998
258894
|
const ownedPlannerIssues2 = await PostgresWorkorderDraftPlannerIssuesStore.open(
|
|
257999
258895
|
pgPool,
|
|
258000
|
-
|
|
258896
|
+
engineSchemaFor(companyId)
|
|
258001
258897
|
);
|
|
258002
258898
|
const ownedLedger2 = await PostgresDispatchStore.open(
|
|
258003
258899
|
pgPool,
|
|
258004
|
-
|
|
258900
|
+
engineSchemaFor(companyId)
|
|
258005
258901
|
);
|
|
258006
258902
|
const ownedBlobs2 = await PostgresBlobStore.open(
|
|
258007
258903
|
pgPool,
|
|
258008
|
-
|
|
258904
|
+
engineSchemaFor(companyId)
|
|
258009
258905
|
);
|
|
258010
258906
|
const ownedTrace2 = await PostgresTraceStore.open(
|
|
258011
258907
|
tracePool,
|
|
258012
|
-
|
|
258908
|
+
engineSchemaFor(companyId)
|
|
258013
258909
|
);
|
|
258014
258910
|
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
258911
|
}
|
|
@@ -258265,7 +259161,7 @@ async function startServe(opts) {
|
|
|
258265
259161
|
const id = companyId ?? defaultCompanyId;
|
|
258266
259162
|
const hit = delegationStores.get(id);
|
|
258267
259163
|
if (hit) return hit;
|
|
258268
|
-
const schemaFor =
|
|
259164
|
+
const schemaFor = engineSchemaFor(id);
|
|
258269
259165
|
const store = pgPool ? await PostgresDelegationStore.open(pgPool, schemaFor) : await FileDelegationStore.open(path29.join(opts.dir, `delegations-${companyEngineDirName(id)}.json`));
|
|
258270
259166
|
delegationStores.set(id, store);
|
|
258271
259167
|
return store;
|
|
@@ -258516,7 +259412,7 @@ async function startServe(opts) {
|
|
|
258516
259412
|
const knowledgeGovernanceStoreFor = (companyId) => {
|
|
258517
259413
|
const cached3 = knowledgeGovernanceStores.get(companyId);
|
|
258518
259414
|
if (cached3) return cached3;
|
|
258519
|
-
const knowledgeSchema =
|
|
259415
|
+
const knowledgeSchema = engineSchemaFor(companyId);
|
|
258520
259416
|
const opened = pgDsn && pgPool ? withSchemaDdlLock(pgPool, knowledgeSchema, () => PostgresKnowledgeGovernanceStore.open(pgPool, knowledgeSchema, knowledgeSnapshotKey)) : FileKnowledgeGovernanceStore.open(path29.join(
|
|
258521
259417
|
opts.dir,
|
|
258522
259418
|
companyId === defaultCompanyId ? "knowledge-governance.json" : path29.join("companies", companyEngineDirName(companyId), "knowledge-governance.json")
|
|
@@ -258650,6 +259546,7 @@ async function startServe(opts) {
|
|
|
258650
259546
|
env: provision.env,
|
|
258651
259547
|
wrapperPaths: provision.wrapperPaths,
|
|
258652
259548
|
...provision.requiredTools.length > 0 ? { requiredTools: provision.requiredTools } : {},
|
|
259549
|
+
...Object.keys(provision.requiredToolVersions).length > 0 ? { requiredToolVersions: provision.requiredToolVersions } : {},
|
|
258653
259550
|
...provision.connectorCreds.length > 0 ? { connectorCreds: provision.connectorCreds } : {}
|
|
258654
259551
|
});
|
|
258655
259552
|
const chunks = [];
|
|
@@ -259635,7 +260532,7 @@ async function startServe(opts) {
|
|
|
259635
260532
|
console.warn(`[trace] chat run ${traceRunId} \u7EED\u8D26\u5F02\u5E38: ${String(err)}`);
|
|
259636
260533
|
});
|
|
259637
260534
|
};
|
|
259638
|
-
const provision = isolatedKnowledgeTurn ? { env: {}, wrapperPaths: [], requiredTools: [], connectorCreds: [], cleanup: async () => void 0 } : await buildActorProvision(actorService, actorId);
|
|
260535
|
+
const provision = isolatedKnowledgeTurn ? { env: {}, wrapperPaths: [], requiredTools: [], requiredToolVersions: {}, connectorCreds: [], cleanup: async () => void 0 } : await buildActorProvision(actorService, actorId);
|
|
259639
260536
|
const actorCtx = await buildActorContext(actorService, actorId).catch(() => null);
|
|
259640
260537
|
const chatResolvedModel = sessionModelOverride ?? actorCtx?.config?.model ?? void 0;
|
|
259641
260538
|
const chatModel = chatResolvedModel ?? await registryStore.getRuntimeConfig(`runtime:${binding.nodeId}:${binding.runtimeKind}`).then((c) => c?.model).catch(() => void 0) ?? void 0;
|
|
@@ -259775,6 +260672,7 @@ async function startServe(opts) {
|
|
|
259775
260672
|
},
|
|
259776
260673
|
wrapperPaths: provision.wrapperPaths,
|
|
259777
260674
|
...provision.requiredTools.length > 0 ? { requiredTools: provision.requiredTools } : {},
|
|
260675
|
+
...Object.keys(provision.requiredToolVersions).length > 0 ? { requiredToolVersions: provision.requiredToolVersions } : {},
|
|
259778
260676
|
...provision.connectorCreds.length > 0 ? { connectorCreds: provision.connectorCreds } : {},
|
|
259779
260677
|
...requiredConnectors.length > 0 ? { requiredConnectorSlugs: requiredConnectors } : {}
|
|
259780
260678
|
});
|
|
@@ -259991,60 +260889,101 @@ async function startServe(opts) {
|
|
|
259991
260889
|
});
|
|
259992
260890
|
};
|
|
259993
260891
|
{
|
|
259994
|
-
const
|
|
259995
|
-
if (
|
|
260892
|
+
const defaultListRunningChatRows = chatRecoveryStore.listRunningAssistantMessages?.bind(chatRecoveryStore);
|
|
260893
|
+
if (defaultListRunningChatRows) {
|
|
260894
|
+
const chatRecoveryCompanies = async () => {
|
|
260895
|
+
if (!chatStoreRouter) return [void 0];
|
|
260896
|
+
const active = (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id);
|
|
260897
|
+
return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
|
|
260898
|
+
};
|
|
260899
|
+
const chatRecoveryPlanes = /* @__PURE__ */ new Map();
|
|
260900
|
+
const chatRecoveryFor = async (companyId) => {
|
|
260901
|
+
if (!pgPool || !platformChatRecovery) {
|
|
260902
|
+
return { listRunningAssistantMessages: defaultListRunningChatRows, chatStore: chatSessionStore };
|
|
260903
|
+
}
|
|
260904
|
+
const id = companyId ?? defaultCompanyId;
|
|
260905
|
+
const hit = chatRecoveryPlanes.get(id);
|
|
260906
|
+
if (hit) return hit;
|
|
260907
|
+
const sessions = id === defaultCompanyId ? platformChatRecovery : PlatformChatRecoveryStore.open(pgPool, engineSchemaFor(id));
|
|
260908
|
+
const items = await chatItemsFor(id);
|
|
260909
|
+
const plane = {
|
|
260910
|
+
listRunningAssistantMessages: () => sessions.listRunningAssistantMessages(),
|
|
260911
|
+
chatStore: sessions,
|
|
260912
|
+
getTurnForRun: (sid, rid) => sessions.getTurnForRun(sid, rid),
|
|
260913
|
+
...items ? { items } : {}
|
|
260914
|
+
};
|
|
260915
|
+
chatRecoveryPlanes.set(id, plane);
|
|
260916
|
+
return plane;
|
|
260917
|
+
};
|
|
260918
|
+
const chatRecoveryLastError = /* @__PURE__ */ new Map();
|
|
260919
|
+
const chatRecoveryCompanyWarn = (label) => (companyId, err) => {
|
|
260920
|
+
const key = `${label}::${companyId ?? "<default>"}`;
|
|
260921
|
+
const msg = String(err);
|
|
260922
|
+
if (chatRecoveryLastError.get(key) === msg) return;
|
|
260923
|
+
chatRecoveryLastError.set(key, msg);
|
|
260924
|
+
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}`);
|
|
260925
|
+
};
|
|
259996
260926
|
let chatRecoveryChain = Promise.resolve();
|
|
259997
260927
|
const enqueueChatRecovery = (label, step) => {
|
|
259998
260928
|
chatRecoveryChain = chatRecoveryChain.then(step).catch((err) => console.error(`[chat-recovery] ${label}\u5931\u8D25: ${String(err)}`));
|
|
259999
260929
|
};
|
|
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
|
-
};
|
|
260930
|
+
const recoverChatTurns = async (daemonId, activeSessions, opts2) => recoverChatTurnsAcrossCompanies({
|
|
260931
|
+
activeSessions,
|
|
260932
|
+
listCompanies: chatRecoveryCompanies,
|
|
260933
|
+
storesFor: chatRecoveryFor,
|
|
260934
|
+
getRun: (id) => fanOutTrace.getRun(id),
|
|
260935
|
+
isWired: (artifactId) => chatLiveSessions.has(`chat::${artifactId}`),
|
|
260936
|
+
...opts2.stopOnFirstHit ? { stopOnFirstHit: true } : {},
|
|
260937
|
+
onCompanyError: chatRecoveryCompanyWarn(opts2.label),
|
|
260938
|
+
liveChat,
|
|
260939
|
+
trace: runRoutedTrace,
|
|
260940
|
+
traceHealth,
|
|
260941
|
+
recover: (plan) => chatRemoteAdapter.recover(plan.dispatchId, daemonId, plan.runtimeKind, plan.actor),
|
|
260942
|
+
register: (plan, handle) => chatLiveSessions.set(`chat::${plan.artifactId}`, {
|
|
260943
|
+
artifactId: plan.artifactId,
|
|
260944
|
+
sessionId: plan.runId,
|
|
260945
|
+
dispatchId: plan.dispatchId,
|
|
260946
|
+
kill: () => handle.kill()
|
|
260947
|
+
}),
|
|
260948
|
+
unregister: (plan) => {
|
|
260949
|
+
chatLiveSessions.delete(`chat::${plan.artifactId}`);
|
|
260950
|
+
},
|
|
260951
|
+
evict: (dispatchId) => {
|
|
260952
|
+
if (!hub.evictSession(dispatchId, "reaped")) hub.dispatch(daemonId, { type: "kill", dispatchId });
|
|
260953
|
+
},
|
|
260954
|
+
log: (message) => console.log(message)
|
|
260955
|
+
});
|
|
260034
260956
|
hub.registerSessionClaimResolver?.(async (id) => {
|
|
260035
260957
|
if (!id.jobArtifactId || !chatRemoteAdapter) return "unknown";
|
|
260036
260958
|
const jobKey = `chat::${id.jobArtifactId}`;
|
|
260037
|
-
if (chatLiveSessions.has(jobKey))
|
|
260959
|
+
if (chatLiveSessions.has(jobKey)) {
|
|
260960
|
+
console.log(
|
|
260961
|
+
`[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`
|
|
260962
|
+
);
|
|
260963
|
+
return "live";
|
|
260964
|
+
}
|
|
260965
|
+
let outcome;
|
|
260038
260966
|
await new Promise((resolve10) => {
|
|
260039
|
-
chatRecoveryChain = chatRecoveryChain.then(() =>
|
|
260040
|
-
|
|
260041
|
-
|
|
260967
|
+
chatRecoveryChain = chatRecoveryChain.then(async () => {
|
|
260968
|
+
outcome = await recoverChatTurns(
|
|
260969
|
+
id.nodeId,
|
|
260970
|
+
[{ dispatchId: id.dispatchId, sessionId: id.sessionId, jobArtifactId: id.jobArtifactId }],
|
|
260971
|
+
{ label: "\u8BA4\u9886", stopOnFirstHit: true }
|
|
260972
|
+
);
|
|
260973
|
+
}).catch((err) => {
|
|
260042
260974
|
console.error(`[chat-recovery] session_hello \u8BA4\u9886\u5931\u8D25: ${String(err)}`);
|
|
260043
260975
|
}).then(() => {
|
|
260044
260976
|
resolve10();
|
|
260045
260977
|
});
|
|
260046
260978
|
});
|
|
260047
|
-
|
|
260979
|
+
if (outcome?.settled.some((hit2) => hit2.plan.dispatchId === id.dispatchId)) return "settled";
|
|
260980
|
+
const live = chatLiveSessions.has(jobKey);
|
|
260981
|
+
const hit = outcome?.recovered.find((r) => r.plan.artifactId === id.jobArtifactId);
|
|
260982
|
+
const skipped = outcome?.skipped ?? [];
|
|
260983
|
+
console.log(
|
|
260984
|
+
`[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")
|
|
260985
|
+
);
|
|
260986
|
+
return live ? "live" : "unknown";
|
|
260048
260987
|
});
|
|
260049
260988
|
hub.addMessageListener((daemonId, msg) => {
|
|
260050
260989
|
if (msg.type !== "runtime_models") return;
|
|
@@ -260089,7 +261028,14 @@ async function startServe(opts) {
|
|
|
260089
261028
|
});
|
|
260090
261029
|
hub.addMessageListener((daemonId, msg) => {
|
|
260091
261030
|
if (msg.type === "hello" && msg.meta.activeSessions?.length) {
|
|
260092
|
-
enqueueChatRecovery("hello \u5BF9\u8D26", () =>
|
|
261031
|
+
enqueueChatRecovery("hello \u5BF9\u8D26", async () => {
|
|
261032
|
+
const outcome = await recoverChatTurns(daemonId, msg.meta.activeSessions, { label: "hello \u5BF9\u8D26" });
|
|
261033
|
+
if (outcome.recovered.length === 0 && outcome.skipped.length === 0) return;
|
|
261034
|
+
const byCompany = outcome.recovered.map((r) => `${r.companyId ?? "<default>"}:${r.plan.chatSessionId}`).join("\u3001");
|
|
261035
|
+
console.log(
|
|
261036
|
+
`[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` : "")
|
|
261037
|
+
);
|
|
261038
|
+
});
|
|
260093
261039
|
} else if (msg.type === "session_started") {
|
|
260094
261040
|
void ledgerForDispatch(msg.dispatchId).then((l) => l.markStarted(msg.dispatchId, { sessionRef: msg.sessionId, daemonId })).catch(ledgerWarn("markStarted"));
|
|
260095
261041
|
const prodMapping = dispatchToWork.get(msg.dispatchId);
|
|
@@ -260135,16 +261081,16 @@ async function startServe(opts) {
|
|
|
260135
261081
|
})).catch(ledgerWarn("close(exit)"));
|
|
260136
261082
|
const dispatchOwnedAtReceipt = allDispatchers().some((d) => d.inFlightSessions().some((x2) => x2.sessionId === msg.dispatchId));
|
|
260137
261083
|
enqueueChatRecovery("exit \u5BF9\u8D26", async () => {
|
|
260138
|
-
const chatHandled = await
|
|
261084
|
+
const chatHandled = await reconcileChatExitFrameAcrossCompanies({
|
|
260139
261085
|
dispatchId: msg.dispatchId,
|
|
260140
261086
|
info: msg.info,
|
|
260141
261087
|
isOwned: (id) => [...chatLiveSessions.values()].some((c) => c.dispatchId === id),
|
|
260142
261088
|
ownedRunId: (sid) => liveChat.runFor(sid),
|
|
260143
|
-
listRunningAssistantMessages: listRunningChatRows,
|
|
260144
261089
|
trace: runRoutedTrace,
|
|
260145
|
-
|
|
261090
|
+
listCompanies: chatRecoveryCompanies,
|
|
261091
|
+
storesFor: chatRecoveryFor,
|
|
261092
|
+
onCompanyError: chatRecoveryCompanyWarn("exit \u5BF9\u8D26"),
|
|
260146
261093
|
drainStash: (id) => chatRemoteAdapter?.drainStash(id) ?? { frames: [] },
|
|
260147
|
-
...chatRecoveryItems ? { items: chatRecoveryItems } : {},
|
|
260148
261094
|
log: (m2) => console.log(m2)
|
|
260149
261095
|
});
|
|
260150
261096
|
if (!chatHandled) {
|
|
@@ -260208,21 +261154,22 @@ async function startServe(opts) {
|
|
|
260208
261154
|
}
|
|
260209
261155
|
});
|
|
260210
261156
|
const chatSweepTimer = setInterval(() => {
|
|
260211
|
-
void
|
|
260212
|
-
|
|
261157
|
+
void sweepDanglingChatTurnsAcrossCompanies({
|
|
261158
|
+
listCompanies: chatRecoveryCompanies,
|
|
261159
|
+
storesFor: chatRecoveryFor,
|
|
261160
|
+
onCompanyError: chatRecoveryCompanyWarn("chat-sweep"),
|
|
260213
261161
|
getRun: (id) => fanOutTrace.getRun(id),
|
|
260214
261162
|
ownedRunId: (sid) => liveChat.runFor(sid),
|
|
260215
|
-
listMessages: (sid) => chatRecoveryStore.listMessages(sid),
|
|
260216
|
-
updateMessage: chatRecoveryStore.updateMessage.bind(chatRecoveryStore),
|
|
260217
261163
|
now: Date.now(),
|
|
260218
261164
|
// 提案 §6.2.9:委派台账**接进这一拍,不新写一套扫描**。
|
|
260219
261165
|
// 这一拍闭合的是「run 已终态、assistant 行却还挂 running」的行;委派台账是同一类账,
|
|
260220
261166
|
// 只是多一层——它还得回流。不接的话那条委派会永远显示「进行中」(刷新也不变),
|
|
260221
261167
|
// 且「队列非空 ⇒ 不算完成」变成死锁:既看不到结果、也等不到失败。
|
|
260222
|
-
// ⚠️
|
|
260223
|
-
//
|
|
260224
|
-
//
|
|
260225
|
-
//
|
|
261168
|
+
// ⚠️ **必须带公司**:委派台账按公司分 store,不带的话非默认公司那条委派会被拿去默认公司的
|
|
261169
|
+
// 库里查、查不到、于是永远收敛不掉——症状是「刷新也不变的进行中」,且只在非默认公司出现。
|
|
261170
|
+
// 扇出之后**公司是已知量**(这一行就是从那家的 schema 读出来的),由扇出层作为第四个参数
|
|
261171
|
+
// 直接传给钩子,不再按会话反查一次——少一次点查,也少一条「读不到就按默认公司试一次」的
|
|
261172
|
+
// 错误回落。回落那一跳仍收在 `delegationSweepHook` 里(dev 文件模式还需要它,有自己的测试)。
|
|
260226
261173
|
onSessionClosed: delegationSweepHook({
|
|
260227
261174
|
sessions: chatRecoveryStore,
|
|
260228
261175
|
settle: (sid, reason, companyId) => server.settleDelegationFromSweep(sid, reason, companyId),
|
|
@@ -260251,6 +261198,20 @@ async function startServe(opts) {
|
|
|
260251
261198
|
isOwned: (turn) => Boolean(turn.assistantRunId && liveChat.runFor(turn.chatSessionId) === turn.assistantRunId),
|
|
260252
261199
|
now: Date.now(),
|
|
260253
261200
|
noRunGraceMs: chatTurnOrphanGraceMs,
|
|
261201
|
+
/* 轮次被判死之后把委派台账也收敛掉(2026-09-09 生产事故的第二道口子)。
|
|
261202
|
+
此前只有 `sweepDanglingChatTurns` 那一拍挂了委派钩子,而它要先拿 assistant 行的 `run_id`
|
|
261203
|
+
去查 run —— 子会话占位行的 `run_id` 一直是空的,那一拍整条跳过、钩子一次没调用:
|
|
261204
|
+
三条委派的轮次 12:27–12:35 就 failed 了,台账到 13:10 还是 running,父会话永远"处理中"。
|
|
261205
|
+
本拍是从轮次账本走的(`assistant_run_id` 一直有值),是"这一轮死没死"的权威来源。
|
|
261206
|
+
`companyId` 由扇出那层填好 —— 台账按公司分 store,少了它会查到默认公司的库。
|
|
261207
|
+
非委派会话调进去是空转:`settleFromSweep` 按 childSessionId 查不到记录就原样返回。 */
|
|
261208
|
+
onTurnSettled: async ({ chatSessionId, status, reason, companyId }) => {
|
|
261209
|
+
await server.settleDelegationFromSweep(
|
|
261210
|
+
chatSessionId,
|
|
261211
|
+
`\u5B50\u4F1A\u8BDD\u8FD9\u4E00\u8F6E\u88AB\u6062\u590D\u5668\u6536\u53E3\u4E3A ${status}\uFF08${reason}\uFF09`,
|
|
261212
|
+
companyId
|
|
261213
|
+
);
|
|
261214
|
+
},
|
|
260254
261215
|
log: (m2) => console.log(m2),
|
|
260255
261216
|
onCompanyError: (companyId, err) => {
|
|
260256
261217
|
const key = companyId ?? "<default>";
|
|
@@ -260724,14 +261685,22 @@ async function startServe(opts) {
|
|
|
260724
261685
|
//(activate.ts:`latestAcceptId == null && !lastProposedRejected`),本模块拿不到,
|
|
260725
261686
|
// 而两个方向的代价不对称——该续没续只是少一点上下文,不该续却续了就把返工要隔离的
|
|
260726
261687
|
// 旧会话又接了回来。何况这条 work 从来没跑过,本就没有属于它的会话可续。
|
|
260727
|
-
|
|
261688
|
+
// 补派进入同一工单事件队列:与 pause 的 apply/effect 串行,不能在检查后绕到 IO 直派。
|
|
261689
|
+
redispatch: async (workId, workorderId) => {
|
|
261690
|
+
await newKernel.getBus().submit({
|
|
261691
|
+
companyId,
|
|
261692
|
+
workorderId,
|
|
261693
|
+
actorId: SYSTEM_ACTOR2,
|
|
261694
|
+
event: { kind: "work.redispatch", workorderId, workId }
|
|
261695
|
+
});
|
|
261696
|
+
},
|
|
260728
261697
|
now: Date.now(),
|
|
260729
261698
|
graceMs: DISPATCH_DROP_GRACE_MS,
|
|
260730
261699
|
backoffMs: DISPATCH_DROP_BACKOFF_MS,
|
|
260731
261700
|
maxPerTick: DISPATCH_DROP_MAX_PER_TICK
|
|
260732
261701
|
}).then((r) => {
|
|
260733
261702
|
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(", ")}`);
|
|
261703
|
+
console.warn(`[dispatch-drop] \u5DF2\u8BF7\u6C42\u8865\u6D3E ${r.redispatched.length} \u6761\u4ECE\u672A\u5F00\u5DE5\u7684 work\uFF1A${r.redispatched.join(", ")}`);
|
|
260735
261704
|
}
|
|
260736
261705
|
}).catch((err) => console.error(`[dispatch-drop] tick \u5931\u8D25: ${String(err)}`));
|
|
260737
261706
|
}, DISPATCH_DROP_SWEEP_TICK_MS);
|
|
@@ -262325,6 +263294,7 @@ ${nodeFault}` : "");
|
|
|
262325
263294
|
if (gitTimer) clearInterval(gitTimer);
|
|
262326
263295
|
if (coordTimer) clearInterval(coordTimer);
|
|
262327
263296
|
if (livenessTimer) clearInterval(livenessTimer);
|
|
263297
|
+
clearInterval(connectorSkillTimer);
|
|
262328
263298
|
if (orphanSweepTimer) clearInterval(orphanSweepTimer);
|
|
262329
263299
|
for (const t of dropSweepTimers) clearInterval(t);
|
|
262330
263300
|
if (deadlineClockTimer) clearInterval(deadlineClockTimer);
|
|
@@ -262523,7 +263493,7 @@ async function runSession(dispatchId, job, deps) {
|
|
|
262523
263493
|
}
|
|
262524
263494
|
}
|
|
262525
263495
|
if (job.requiredTools?.length) {
|
|
262526
|
-
const missing = await ensureConnectorTools(job.requiredTools);
|
|
263496
|
+
const missing = await ensureConnectorTools(job.requiredTools, { versions: job.requiredToolVersions });
|
|
262527
263497
|
if (missing.length) log2("[node-cli]", ` connector tools still missing (agent may fail): ${missing.join(", ")}`);
|
|
262528
263498
|
}
|
|
262529
263499
|
const prepared = await prepareConnectorsForJob(job);
|
|
@@ -262832,17 +263802,19 @@ var SessionProcessState = class {
|
|
|
262832
263802
|
}
|
|
262833
263803
|
let done = false;
|
|
262834
263804
|
const finish = () => {
|
|
262835
|
-
if (
|
|
262836
|
-
|
|
262837
|
-
|
|
262838
|
-
|
|
263805
|
+
if (done) return;
|
|
263806
|
+
done = true;
|
|
263807
|
+
clearTimeout(timer);
|
|
263808
|
+
this.exitAckResolve = null;
|
|
263809
|
+
resolve10();
|
|
262839
263810
|
};
|
|
263811
|
+
const timer = setTimeout(finish, EXIT_ACK_WAIT_MS);
|
|
262840
263812
|
this.exitAckResolve = finish;
|
|
262841
|
-
setTimeout(finish, EXIT_ACK_WAIT_MS).unref?.();
|
|
262842
263813
|
});
|
|
262843
263814
|
}
|
|
262844
263815
|
teardown() {
|
|
262845
263816
|
this.stopping = true;
|
|
263817
|
+
this.exitAckResolve?.();
|
|
262846
263818
|
if (this.watchdog) {
|
|
262847
263819
|
clearInterval(this.watchdog);
|
|
262848
263820
|
this.watchdog = null;
|
|
@@ -262924,7 +263896,7 @@ function detectRuntimes() {
|
|
|
262924
263896
|
|
|
262925
263897
|
// ../cli/src/daemon/workdir-handler.ts
|
|
262926
263898
|
var import_node_fs19 = __toESM(require("node:fs"), 1);
|
|
262927
|
-
var
|
|
263899
|
+
var import_node_path26 = __toESM(require("node:path"), 1);
|
|
262928
263900
|
init_src6();
|
|
262929
263901
|
init_src();
|
|
262930
263902
|
var WORKDIR_READ_MAX_BYTES2 = 2 * 1024 * 1024;
|
|
@@ -262943,10 +263915,10 @@ function isSensitiveSegment(segment) {
|
|
|
262943
263915
|
}
|
|
262944
263916
|
function relPathIsSensitive(rel) {
|
|
262945
263917
|
if (!rel) return false;
|
|
262946
|
-
return rel.split(
|
|
263918
|
+
return rel.split(import_node_path26.default.sep).some((seg) => seg.length > 0 && isSensitiveSegment(seg));
|
|
262947
263919
|
}
|
|
262948
263920
|
function withinBase(p2, base) {
|
|
262949
|
-
return p2 === base || p2.startsWith(base +
|
|
263921
|
+
return p2 === base || p2.startsWith(base + import_node_path26.default.sep);
|
|
262950
263922
|
}
|
|
262951
263923
|
function normalizeRel(raw) {
|
|
262952
263924
|
const trimmed = (raw ?? "").trim();
|
|
@@ -262963,7 +263935,7 @@ async function trustedCanonicalContainer(req, logicalBase, dirKind) {
|
|
|
262963
263935
|
} catch {
|
|
262964
263936
|
return null;
|
|
262965
263937
|
}
|
|
262966
|
-
return isLegacy ?
|
|
263938
|
+
return isLegacy ? import_node_path26.default.join(trustedRootReal, "oasis-chat-sessions") : import_node_path26.default.join(trustedRootReal, "sessions", dirKind);
|
|
262967
263939
|
}
|
|
262968
263940
|
async function resolveWithinWorkdir(req) {
|
|
262969
263941
|
const dirKind = sessionDirKind(req.runtimeKind);
|
|
@@ -262981,11 +263953,11 @@ async function resolveWithinWorkdir(req) {
|
|
|
262981
263953
|
} catch {
|
|
262982
263954
|
return { ok: false, code: "NOT_FOUND" };
|
|
262983
263955
|
}
|
|
262984
|
-
if (
|
|
263956
|
+
if (import_node_path26.default.dirname(base) !== canonicalContainer) return { ok: false, code: "PATH_ESCAPE" };
|
|
262985
263957
|
const rel = normalizeRel(req.path);
|
|
262986
|
-
const requested =
|
|
263958
|
+
const requested = import_node_path26.default.resolve(base, rel);
|
|
262987
263959
|
if (!withinBase(requested, base)) return { ok: false, code: "PATH_ESCAPE" };
|
|
262988
|
-
const cleanRel = base === requested ? "" :
|
|
263960
|
+
const cleanRel = base === requested ? "" : import_node_path26.default.relative(base, requested);
|
|
262989
263961
|
if (relPathIsSensitive(cleanRel)) return { ok: false, code: "SENSITIVE" };
|
|
262990
263962
|
let real;
|
|
262991
263963
|
try {
|
|
@@ -262995,7 +263967,7 @@ async function resolveWithinWorkdir(req) {
|
|
|
262995
263967
|
return { ok: false, code: "PATH_ESCAPE" };
|
|
262996
263968
|
}
|
|
262997
263969
|
if (!withinBase(real, base)) return { ok: false, code: "PATH_ESCAPE" };
|
|
262998
|
-
const realRel = base === real ? "" :
|
|
263970
|
+
const realRel = base === real ? "" : import_node_path26.default.relative(base, real);
|
|
262999
263971
|
if (relPathIsSensitive(realRel)) return { ok: false, code: "SENSITIVE" };
|
|
263000
263972
|
return { ok: true, base, real };
|
|
263001
263973
|
}
|
|
@@ -263031,7 +264003,7 @@ async function handleWorkdirList(req) {
|
|
|
263031
264003
|
const slice = truncated ? visible.slice(0, WORKDIR_LIST_MAX_ENTRIES) : visible;
|
|
263032
264004
|
const entries = [];
|
|
263033
264005
|
for (const d of slice) {
|
|
263034
|
-
const abs =
|
|
264006
|
+
const abs = import_node_path26.default.join(anchor, d.name);
|
|
263035
264007
|
try {
|
|
263036
264008
|
const st = await import_node_fs19.default.promises.lstat(abs);
|
|
263037
264009
|
if (st.isSymbolicLink()) continue;
|
|
@@ -263063,7 +264035,7 @@ async function verifyOpenedFd(fh, base, fallback) {
|
|
|
263063
264035
|
const fdReal = await fdCanonicalPath(fh);
|
|
263064
264036
|
if (fdReal === null) return { anchor: fallback };
|
|
263065
264037
|
if (!withinBase(fdReal, base)) return { error: { ok: false, code: "PATH_ESCAPE" } };
|
|
263066
|
-
const fdRel = base === fdReal ? "" :
|
|
264038
|
+
const fdRel = base === fdReal ? "" : import_node_path26.default.relative(base, fdReal);
|
|
263067
264039
|
if (relPathIsSensitive(fdRel)) return { error: { ok: false, code: "SENSITIVE" } };
|
|
263068
264040
|
return { anchor: `/proc/self/fd/${fh.fd}` };
|
|
263069
264041
|
}
|
|
@@ -263152,7 +264124,7 @@ function looksBinary(bytes2) {
|
|
|
263152
264124
|
function contentTypeFor(absPath, bytes2) {
|
|
263153
264125
|
const sniffed = sniffContentType(bytes2);
|
|
263154
264126
|
if (sniffed) return sniffed;
|
|
263155
|
-
const ext =
|
|
264127
|
+
const ext = import_node_path26.default.extname(absPath).toLowerCase();
|
|
263156
264128
|
if (EXT_CONTENT_TYPE[ext]) return EXT_CONTENT_TYPE[ext];
|
|
263157
264129
|
return looksBinary(bytes2) ? "application/octet-stream" : "text/plain; charset=utf-8";
|
|
263158
264130
|
}
|
|
@@ -263748,17 +264720,17 @@ var RuntimeRouterAdapter = class {
|
|
|
263748
264720
|
// ../cli/src/daemon/reap-claude-projects.ts
|
|
263749
264721
|
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
263750
264722
|
var import_node_os10 = __toESM(require("node:os"), 1);
|
|
263751
|
-
var
|
|
264723
|
+
var import_node_path27 = __toESM(require("node:path"), 1);
|
|
263752
264724
|
function claudeProjectSlug(cwd) {
|
|
263753
264725
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
263754
264726
|
}
|
|
263755
264727
|
function claudeProjectsRoot() {
|
|
263756
|
-
const configDir = process.env["CLAUDE_CONFIG_DIR"] ||
|
|
263757
|
-
return
|
|
264728
|
+
const configDir = process.env["CLAUDE_CONFIG_DIR"] || import_node_path27.default.join(import_node_os10.default.homedir(), ".claude");
|
|
264729
|
+
return import_node_path27.default.join(configDir, "projects");
|
|
263758
264730
|
}
|
|
263759
264731
|
function reapClaudeProjects(workdir, runtimeKind) {
|
|
263760
264732
|
if (runtimeKind !== "claude" && runtimeKind !== "claude-code") return;
|
|
263761
|
-
const dir =
|
|
264733
|
+
const dir = import_node_path27.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
|
|
263762
264734
|
if (!import_node_fs20.default.existsSync(dir)) return;
|
|
263763
264735
|
try {
|
|
263764
264736
|
import_node_fs20.default.rmSync(dir, { recursive: true, force: true });
|
|
@@ -263767,7 +264739,7 @@ function reapClaudeProjects(workdir, runtimeKind) {
|
|
|
263767
264739
|
}
|
|
263768
264740
|
function measureClaudeProjects(workdir, runtimeKind) {
|
|
263769
264741
|
if (runtimeKind !== "claude" && runtimeKind !== "claude-code") return 0;
|
|
263770
|
-
const dir =
|
|
264742
|
+
const dir = import_node_path27.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
|
|
263771
264743
|
return dirSizeBytes2(dir);
|
|
263772
264744
|
}
|
|
263773
264745
|
function dirSizeBytes2(dir) {
|
|
@@ -263779,7 +264751,7 @@ function dirSizeBytes2(dir) {
|
|
|
263779
264751
|
return 0;
|
|
263780
264752
|
}
|
|
263781
264753
|
for (const e of entries) {
|
|
263782
|
-
const p2 =
|
|
264754
|
+
const p2 = import_node_path27.default.join(dir, e.name);
|
|
263783
264755
|
if (e.isSymbolicLink()) continue;
|
|
263784
264756
|
if (e.isDirectory()) {
|
|
263785
264757
|
total += dirSizeBytes2(p2);
|
|
@@ -266190,7 +267162,7 @@ function fieldsFromFlags(flags, ownFlags) {
|
|
|
266190
267162
|
}
|
|
266191
267163
|
return Object.keys(fields).length > 0 ? fields : void 0;
|
|
266192
267164
|
}
|
|
266193
|
-
function buildStageOp(cmd, flags, positional,
|
|
267165
|
+
function buildStageOp(cmd, flags, positional, readFile9 = (file) => fs39.readFileSync(file, "utf8")) {
|
|
266194
267166
|
switch (cmd) {
|
|
266195
267167
|
case "link":
|
|
266196
267168
|
return {
|
|
@@ -266212,7 +267184,7 @@ function buildStageOp(cmd, flags, positional, readFile8 = (file) => fs39.readFil
|
|
|
266212
267184
|
...flags.get("title") !== void 0 ? { title: flags.get("title") } : {},
|
|
266213
267185
|
...(flags.get("brief") ?? flags.get("description")) !== void 0 ? { description: flags.get("brief") ?? flags.get("description") } : {},
|
|
266214
267186
|
...flags.get("input") !== void 0 ? { inputs: flags.get("input").split(",").map((to) => ({ to: to.trim() })) } : {},
|
|
266215
|
-
...partsFile !== void 0 ? { parts: JSON.parse(
|
|
267187
|
+
...partsFile !== void 0 ? { parts: JSON.parse(readFile9(partsFile)) } : {},
|
|
266216
267188
|
...fields !== void 0 ? { fields } : {}
|
|
266217
267189
|
};
|
|
266218
267190
|
}
|
|
@@ -269579,7 +270551,7 @@ function shimScript() {
|
|
|
269579
270551
|
}
|
|
269580
270552
|
|
|
269581
270553
|
// src/index.ts
|
|
269582
|
-
var PKG_VERSION = true ? "2.2.
|
|
270554
|
+
var PKG_VERSION = true ? "2.2.8" : "dev";
|
|
269583
270555
|
var LOCAL_BIN = localBin();
|
|
269584
270556
|
var NPM_PREFIX = npmPrefix();
|
|
269585
270557
|
var INSTANCE = DEFAULT_INSTANCE;
|