oasis_test_v2 2.2.16 → 2.2.18
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/_base/wrapper-base.sh +27 -8
- package/dist/index.js +547 -153
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5737,6 +5737,26 @@ CREATE TABLE IF NOT EXISTS ${q2}.event_handler_log (
|
|
|
5737
5737
|
PRIMARY KEY (event_seq, handler_name)
|
|
5738
5738
|
);
|
|
5739
5739
|
|
|
5740
|
+
-- \u5F85\u4EBA\u786E\u8BA4\u7684 intervention \u8349\u6848\u5361\uFF08\u547D\u4EE4\u6388\u6743\u5361 / \u6539\u56FE\u5361\uFF09\u3002
|
|
5741
|
+
--
|
|
5742
|
+
-- \u4E3A\u4EC0\u4E48\u8981\u843D\u5E93\uFF082026-09-11 \u5B9E\u5F55\uFF09\uFF1A\u65E7\u5F15\u64CE\u628A\u8D77\u8349\u8BB0\u6210\u4E00\u6761 propose_intervention op \u8FDB oplog\uFF0C
|
|
5743
|
+
-- \u91CD\u542F replay \u5C31 fold \u56DE pendingReviews\uFF1B\u51B3\u7B56 0130 \u9000\u5F79 oplog \u65F6\u8FD9\u4E00\u6BB5\u53EA\u642C\u4E86\u5185\u5B58 Map\uFF0C
|
|
5744
|
+
-- \u6CE8\u91CA\u8FD8\u5199\u7740\u300C\u77ED\u751F\u547D\u5468\u671F\u3001\u91CD\u542F\u4E22\u53EF\u63A5\u53D7\u300D\u3002\u4F46\u8FD9\u5F20\u5361\u7684\u8BED\u4E49\u662F**\u7B49\u4E00\u4E2A\u771F\u4EBA\u6765\u70B9**\uFF0C\u5BFF\u547D\u7531\u4EBA\u51B3\u5B9A\uFF0C
|
|
5745
|
+
-- \u4E0D\u7531\u8FDB\u7A0B\u51B3\u5B9A\u3002v2 \u4E0A 31 \u5C0F\u65F6\u91CD\u542F\u4E86 158 \u6B21\uFF08\u22485 \u6B21/\u5C0F\u65F6\uFF09\uFF0C\u5B9E\u6D4B\u4E00\u5F20\u5361\u8D77\u8349 24 \u79D2\u540E\u5C31\u88AB\u91CD\u542F\u62B9\u6389\uFF0C
|
|
5746
|
+
-- \u534F\u8C03\u8005\u5374\u4ECD\u5728\u5BF9\u8BDD\u91CC\u8BA9\u4EBA\u300C\u53BB\u6536\u4EF6\u7BB1\u627E\u90A3\u5F20\u5361\u300D\u2014\u2014\u804A\u5929\u9875\u548C\u6536\u4EF6\u7BB1\u8BFB\u7684\u662F\u540C\u4E00\u4EFD\u5185\u5B58\uFF0C\u4E00\u8D77\u53D8\u7A7A\u3002
|
|
5747
|
+
--
|
|
5748
|
+
-- card \u5217\u7528 jsonb \u6574\u5B58\uFF1A\u5361\u4F53\u7684\u5F62\u72B6\u5F52 core\uFF08KernelBridge.draftCards\uFF09\u7BA1\uFF0C\u5F15\u64CE\u53EA\u8D1F\u8D23\u5B58\u53D6\uFF0C
|
|
5749
|
+
-- \u4E0D\u8BA4\u91CC\u9762\u7684\u4E1A\u52A1\u5B57\u6BB5\u2014\u2014\u5426\u5219\u6BCF\u52A0\u4E00\u4E2A\u5B57\u6BB5\u90FD\u8981\u52A8 DDL\uFF0C\u800C\u5F15\u64CE\u4E0D\u8BE5\u77E5\u9053 InterventionPlan \u662F\u4EC0\u4E48\u3002
|
|
5750
|
+
-- \u7D22\u5F15\u4F4D\u53EA\u7559\u6392\u969C\u8981\u7528\u7684\u90A3\u4E24\u5217\u3002\u4E0D\u52A0 workorder_id \u5916\u952E\uFF1A\u5361\u53EF\u4EE5\u6307\u5411\u4E00\u4E2A\u521A\u88AB\u4F5C\u5E9F\u7684\u8282\u70B9\uFF0C
|
|
5751
|
+
-- \u5916\u952E\u4F1A\u8BA9\u300C\u8D77\u8349\u300D\u8FD9\u4E00\u6B65\u76F4\u63A5\u5931\u8D25\uFF0C\u6BD4\u7559\u4E00\u6761\u5B64\u513F\u5361\u7CDF\u5F97\u591A\u3002
|
|
5752
|
+
CREATE TABLE IF NOT EXISTS ${q2}.intervention_drafts (
|
|
5753
|
+
draft_id text PRIMARY KEY,
|
|
5754
|
+
workorder_id text,
|
|
5755
|
+
created_at timestamptz NOT NULL,
|
|
5756
|
+
card jsonb NOT NULL
|
|
5757
|
+
);
|
|
5758
|
+
CREATE INDEX IF NOT EXISTS intervention_drafts_wo_idx ON ${q2}.intervention_drafts (workorder_id);
|
|
5759
|
+
|
|
5740
5760
|
-- \u2500\u2500 \u8FC1\u79FB\uFF1A\u65E2\u6709\u90E8\u7F72\u7684\u5217\u589E\u91CF\uFF08CREATE TABLE IF NOT EXISTS \u4E0D\u52A0\u5217\uFF0C\u8FD9\u91CC\u8865\uFF09\u2500\u2500
|
|
5741
5761
|
-- \u2605 review_requirements.source \u6269\u4E3A\u56DB\u503C\uFF08+'closure'\uFF09\uFF1A\u7ED3\u6848\u4EBA\u95F8\u7531 scan \u5728\u6536\u53E3\u90A3\u4E00\u523B\u5199\u5165\uFF0C\u4E0E manual \u5206\u5BB6\u3002
|
|
5742
5762
|
-- \u5199\u6CD5\u4E0E\u4E0B\u65B9 works_lane_check \u540C\u6B3E\uFF08\u5148\u5220\u540E\u52A0\uFF0C\u6210\u5BF9\u5E42\u7B49\uFF09\u3002
|
|
@@ -11278,6 +11298,38 @@ var init_store_postgres = __esm({
|
|
|
11278
11298
|
createdAt: iso(r.createdAt)
|
|
11279
11299
|
}));
|
|
11280
11300
|
}
|
|
11301
|
+
/* ── intervention 草案卡(见 store.ts 的接口注释)。与上面两口钟同类:直连 pool 的窄读写,
|
|
11302
|
+
标识符位一律经 quoteIdent——带连字符的公司 schema 裸拼会 syntax error。 ── */
|
|
11303
|
+
async listInterventionDrafts() {
|
|
11304
|
+
const res = await this.pool.query(
|
|
11305
|
+
`SELECT draft_id AS "draftId", workorder_id AS "workorderId", created_at AS "createdAt", card
|
|
11306
|
+
FROM ${quoteIdent(this.schema)}.intervention_drafts
|
|
11307
|
+
ORDER BY created_at`
|
|
11308
|
+
);
|
|
11309
|
+
return res.rows.map((r) => ({
|
|
11310
|
+
draftId: r.draftId,
|
|
11311
|
+
workorderId: r.workorderId ?? null,
|
|
11312
|
+
createdAt: iso(r.createdAt),
|
|
11313
|
+
card: r.card
|
|
11314
|
+
}));
|
|
11315
|
+
}
|
|
11316
|
+
async saveInterventionDraft(record8) {
|
|
11317
|
+
await this.pool.query(
|
|
11318
|
+
`INSERT INTO ${quoteIdent(this.schema)}.intervention_drafts (draft_id, workorder_id, created_at, card)
|
|
11319
|
+
VALUES ($1, $2, $3::timestamptz, $4::jsonb)
|
|
11320
|
+
ON CONFLICT (draft_id) DO UPDATE
|
|
11321
|
+
SET workorder_id = EXCLUDED.workorder_id,
|
|
11322
|
+
created_at = EXCLUDED.created_at,
|
|
11323
|
+
card = EXCLUDED.card`,
|
|
11324
|
+
[record8.draftId, record8.workorderId, record8.createdAt, JSON.stringify(record8.card ?? null)]
|
|
11325
|
+
);
|
|
11326
|
+
}
|
|
11327
|
+
async deleteInterventionDraft(draftId) {
|
|
11328
|
+
await this.pool.query(
|
|
11329
|
+
`DELETE FROM ${quoteIdent(this.schema)}.intervention_drafts WHERE draft_id = $1`,
|
|
11330
|
+
[draftId]
|
|
11331
|
+
);
|
|
11332
|
+
}
|
|
11281
11333
|
};
|
|
11282
11334
|
PgTx = class {
|
|
11283
11335
|
constructor(c, s2) {
|
|
@@ -15588,7 +15640,7 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
|
|
|
15588
15640
|
`- \u7F3A\u4E00\u4E2A**\u8FD8\u4E0D\u5B58\u5728\u7684\u524D\u7F6E** \u2192 \`oasis gap\` \u4EA4\u4E0A\u53BB\uFF0C**\u522B\u786C\u7F16**\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u2014\u2014\u524D\u7F6E\u5C31\u7EEA\u540E\u7CFB\u7EDF\u4F1A\u5E26\u7740\u89E3\u6CD5\u8BF4\u660E\u91CD\u65B0\u5524\u8D77\u4F60\u3002`,
|
|
15589
15641
|
`- \u5BF9**\u4E0A\u6E38**\u6709\u610F\u89C1\u2014\u2014\u6709\u7591\u95EE\u3001\u8981\u5B83\u6539\u3001\u5224\u5B83\u6CA1\u8FBE\u6807 \u2192 \`oasis annotate <\u4E0A\u6E38id> --body "<\u8BF4\u6E05\u695A>"\` \u540E**\u6536\u5DE5**\u3002\u5199\u7ED9\u4E0A\u6E38\u7684\u8BC4\u8BBA\u81EA\u52A8\u5E26\u963B\u585E\u529B\uFF1A**\u5B83\u7B54\u590D\u524D\u4E0D\u80FD\u6536\u5C3E**\uFF0C\u7B54\u590D\u540E\u7CFB\u7EDF\u91CD\u6D3E\u4F60\u3002`,
|
|
15590
15642
|
...args.part === void 0 ? [`- \u6D3B\u592A\u5927 / \u53EF\u5E76\u884C \u2192 \`oasis add-revision\` \u62C6\u6210\u5B50\u90E8\u4EF6\u6D3E\u51FA\u53BB\uFF0C\u522B\u4E00\u4EBA\u4E00\u4F1A\u8BDD\u786C\u625B\u3002`] : [],
|
|
15591
|
-
...batchedContinuation && args.part === void 0 ? [`- \u957F\u4EFB\u52A1\u5DF2\u5141\u8BB8\u5206\u6279\u7EED\u8DD1\uFF1A\u4E00\u6279\u6709\u8FDB\u5C55\u4F46\u6574\u4F53\u672A\u5B8C\u6210\u65F6\uFF0C\u6309\u300C\u600E\u4E48\u63D0\u4EA4\u300D\u91CC\u7684 propose \u547D\u4EE4\u8FFD\u52A0 \`--checkpoint --continue\`\uFF0Creason \u5199\u6E05\u672C\u6279\u5B8C\u6210\u4E86\u4EC0\u4E48\u3001\u5269\u4EC0\u4E48\u3001\u4E0B\u6279\u4ECE\u54EA\u7EE7\u7EED\uFF0C\u7136\u540E\u6536\u5DE5\uFF1B\u7CFB\u7EDF\u4F1A\u6D3E\u53D1\u4E0B\u4E00\u8F6E\u6B63\u5F0F session\u3002\u5168\u90E8\u5B8C\u6210\u65F6\u7167\u5E38 propose \u540E \`oasis conclude ${artifactId}\`\u3002`] : [],
|
|
15643
|
+
...batchedContinuation && args.part === void 0 ? [`- \u957F\u4EFB\u52A1\u5DF2\u5141\u8BB8\u5206\u6279\u7EED\u8DD1\uFF1A\u4E00\u6279\u6709\u8FDB\u5C55\u4F46\u6574\u4F53\u672A\u5B8C\u6210\u65F6\uFF0C\u6309\u300C\u600E\u4E48\u63D0\u4EA4\u300D\u91CC\u7684 propose \u547D\u4EE4\u8FFD\u52A0 \`--checkpoint --continue\`\uFF0Creason \u5199\u6E05\u672C\u6279\u5B8C\u6210\u4E86\u4EC0\u4E48\u3001\u5269\u4EC0\u4E48\u3001\u4E0B\u6279\u4ECE\u54EA\u7EE7\u7EED\uFF0C\u7136\u540E\u6536\u5DE5\uFF1B\u7CFB\u7EDF\u4F1A\u6D3E\u53D1\u4E0B\u4E00\u8F6E\u6B63\u5F0F session\u3002\u5168\u90E8\u5B8C\u6210\u65F6\u7167\u5E38 propose \u540E \`oasis conclude ${artifactId} --note "<\u4EA4\u4ED8\u7ED3\u8BBA>"\`\u3002`] : [],
|
|
15592
15644
|
`- \u5199\u5165\u53EA\u8D70 \`oasis propose\`\uFF0C\u771F\u6B63\u5B8C\u6574\u4E86\u624D \`oasis conclude\`\uFF1B\u5361\u6CA1\u5361\u4F4F\u3001\u8FDB\u5EA6\u90FD\u662F\u7CFB\u7EDF\u7B97\u7684\uFF0C\u522B\u81EA\u5DF1\u7EF4\u62A4\u3002`,
|
|
15593
15645
|
`- \u4F46**\u7B80\u5355\u3001\u81EA\u8DB3\u3001\u4E0D\u4F9D\u8D56\u672A\u5B9A\u51B3\u7B56\u7684\u4EFB\u52A1**\uFF08\u5982\u6539\u4E2A\u80CC\u666F\u8272\uFF09\u2192 \u76F4\u63A5\u505A\uFF0C\u5224\u65AD\u6743\u5728\u4F60\u3002`,
|
|
15594
15646
|
...typeDef?.recommendedInputs && typeDef.recommendedInputs.length > 0 ? [`- \u672C\u7C7B\u578B\u5E38\u89C1\u4E0A\u6E38\u4F9D\u8D56\uFF08\u63A8\u8350\uFF0C\u975E\u5F3A\u5236\uFF09\uFF1A${typeDef.recommendedInputs.join("\u3001")}\u2014\u2014\u4F9D\u8D56\u5176\u4E2D\u67D0\u4E2A\u800C\u5B83\u8FD8\u6CA1\u6709\uFF0C\u6309\u4E0A\u9762 gap \u89C4\u5219\u62A5\u7F3A\u53E3\u3002`] : []
|
|
@@ -19353,8 +19405,14 @@ var init_kernel_bridge = __esm({
|
|
|
19353
19405
|
/** 下一轮刷新是否顺带重扫一小片"不脏"的工单。只由 `refreshModel({ resync: true })`(读模型时钟)置位。 */
|
|
19354
19406
|
resyncRequested = false;
|
|
19355
19407
|
/**
|
|
19356
|
-
* intervention
|
|
19357
|
-
*
|
|
19408
|
+
* intervention 待授权卡。内存 map 是**读路径的缓存**,真源在库里(`intervention_drafts` 表,
|
|
19409
|
+
* 见 engine/schema.ts)——`open()` 启动时 rehydrate,propose 写库,apply/reject 删库。
|
|
19410
|
+
*
|
|
19411
|
+
* 旧引擎把起草记成一条 `propose_intervention` op 进 oplog,重启 replay 就 fold 回 pendingReviews;
|
|
19412
|
+
* 决策 0130 退役 oplog 时这一段只搬了内存 Map,注释写着「短生命周期、重启丢可接受」。
|
|
19413
|
+
* 2026-09-11 实录证伪了这句:v2 上 31 小时重启 158 次,一张卡起草 24 秒后就被抹掉,
|
|
19414
|
+
* 而协调者仍在对话里让人「去收件箱找那张卡」——聊天页和收件箱读的是同一份 `pendingReviews`,
|
|
19415
|
+
* 一起变空。这张卡的寿命由**等它的那个人**决定,不由进程决定,所以必须落库。
|
|
19358
19416
|
*/
|
|
19359
19417
|
draftCards = /* @__PURE__ */ new Map();
|
|
19360
19418
|
model;
|
|
@@ -19374,6 +19432,14 @@ var init_kernel_bridge = __esm({
|
|
|
19374
19432
|
for (const record8 of records) kernel.markWorkorderDirty(record8.workorderId);
|
|
19375
19433
|
});
|
|
19376
19434
|
bus.onApplied((wid) => kernel.markWorkorderDirty(wid));
|
|
19435
|
+
try {
|
|
19436
|
+
for (const rec of await store.listInterventionDrafts()) {
|
|
19437
|
+
const card2 = rec.card;
|
|
19438
|
+
kernel.draftCards.set(rec.draftId, { ...card2, at: card2?.at || rec.createdAt });
|
|
19439
|
+
}
|
|
19440
|
+
} catch (err) {
|
|
19441
|
+
console.warn(`[kernel-bridge] open \u6062\u590D\u5F85\u6388\u6743\u5361\u5931\u8D25\uFF08\u672C\u6B21\u91CD\u542F\u7684\u5B58\u91CF\u5361\u4F1A\u4E22\uFF09\uFF1A${String(err)}`);
|
|
19442
|
+
}
|
|
19377
19443
|
try {
|
|
19378
19444
|
const all2 = await store.transaction((tx) => tx.listAllWorkorders());
|
|
19379
19445
|
for (const id of all2) kernel.trackedWorkorders.add(id);
|
|
@@ -19384,6 +19450,39 @@ var init_kernel_bridge = __esm({
|
|
|
19384
19450
|
return kernel;
|
|
19385
19451
|
}
|
|
19386
19452
|
/* ─────────────────────── 内部工具 ─────────────────────── */
|
|
19453
|
+
/**
|
|
19454
|
+
* 起草落库。**写失败不阻断起草**——卡已经在内存里、已经显示给人了,这时候抛错会让
|
|
19455
|
+
* `oasis force-conclude` 整条命令失败,比"这张卡撑不过下次重启"糟得多。退化成本次改动之前的行为。
|
|
19456
|
+
*/
|
|
19457
|
+
async persistDraftCard(draftId, card2) {
|
|
19458
|
+
try {
|
|
19459
|
+
await this.store.saveInterventionDraft({
|
|
19460
|
+
draftId,
|
|
19461
|
+
workorderId: this.workorderOfCard(card2),
|
|
19462
|
+
createdAt: card2.at,
|
|
19463
|
+
card: card2
|
|
19464
|
+
});
|
|
19465
|
+
} catch (err) {
|
|
19466
|
+
console.warn(`[kernel-bridge] \u5F85\u6388\u6743\u5361\u843D\u5E93\u5931\u8D25 ${draftId}\uFF08\u91CD\u542F\u540E\u4F1A\u4E22\uFF09\uFF1A${String(err)}`);
|
|
19467
|
+
}
|
|
19468
|
+
}
|
|
19469
|
+
/** 卡已终局(人点了确认或驳回)→ 删库。失败只记日志:内存已出队,残留行会在下次 rehydrate 后
|
|
19470
|
+
* 被 apply/reject 的「卡不存在」分支挡掉,不会让已执行的命令被再执行一次。 */
|
|
19471
|
+
async forgetDraftCard(draftId) {
|
|
19472
|
+
try {
|
|
19473
|
+
await this.store.deleteInterventionDraft(draftId);
|
|
19474
|
+
} catch (err) {
|
|
19475
|
+
console.warn(`[kernel-bridge] \u5F85\u6388\u6743\u5361\u5220\u5E93\u5931\u8D25 ${draftId}\uFF1A${String(err)}`);
|
|
19476
|
+
}
|
|
19477
|
+
}
|
|
19478
|
+
/** 卡挂在哪张工单下(排障列用,允许为空)。锚点可能指向一个模型里已经没有的产物。 */
|
|
19479
|
+
workorderOfCard(card2) {
|
|
19480
|
+
const fromPlan = card2.plan?.workspace;
|
|
19481
|
+
if (fromPlan) return fromPlan;
|
|
19482
|
+
const anchor = card2.anchor;
|
|
19483
|
+
if (!anchor) return null;
|
|
19484
|
+
return this.model.artifacts.get(anchor)?.workspace ?? null;
|
|
19485
|
+
}
|
|
19387
19486
|
/** artifactId → 所属 workorderId(从 model 查)。 */
|
|
19388
19487
|
wo(artifactId) {
|
|
19389
19488
|
const a = this.model.artifacts.get(artifactId);
|
|
@@ -19554,7 +19653,8 @@ var init_kernel_bridge = __esm({
|
|
|
19554
19653
|
workspace: cardWs,
|
|
19555
19654
|
advisory: [],
|
|
19556
19655
|
downstreamClosure: [],
|
|
19557
|
-
|
|
19656
|
+
// 真实起草时刻。曾经恒为 "":收件箱那行因此时间列印 "-"、排序键空串最小、恒沉到列表最后一行。
|
|
19657
|
+
at: card2.at ?? "",
|
|
19558
19658
|
// ★ anchor 也兜底到 plan ops 的产物 id——inbox change-confirm 用 r.anchor 找「anchor owner 的
|
|
19559
19659
|
// 人类上级」(leadOf(r.anchor));graph-edit 卡之前恒空、boss 收不到卡(collab-inbox-audit 实测)。
|
|
19560
19660
|
anchor: anchorId || card2.anchor || "",
|
|
@@ -21007,30 +21107,33 @@ var init_kernel_bridge = __esm({
|
|
|
21007
21107
|
async proposeIntervention(plan, actor, opts) {
|
|
21008
21108
|
const preview2 = await previewIntervention(this.model, plan, { schema: this.schema, roleIndex: this.roleIndex });
|
|
21009
21109
|
const draftId = `draft:${(0, import_node_crypto3.randomUUID)()}`;
|
|
21010
|
-
|
|
21110
|
+
const card2 = {
|
|
21011
21111
|
kind: "graph-edit",
|
|
21012
21112
|
plan,
|
|
21013
21113
|
proposedBy: actor,
|
|
21014
21114
|
origin: opts?.origin,
|
|
21015
21115
|
parentMessageId: opts?.parentMessageId,
|
|
21016
21116
|
changeClass: preview2.changeClass,
|
|
21017
|
-
nonMonotonicOps: preview2.nonMonotonicOps
|
|
21018
|
-
|
|
21117
|
+
nonMonotonicOps: preview2.nonMonotonicOps,
|
|
21118
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
21119
|
+
};
|
|
21120
|
+
this.draftCards.set(draftId, card2);
|
|
21121
|
+
await this.persistDraftCard(draftId, card2);
|
|
21019
21122
|
await this.refreshModel();
|
|
21020
21123
|
return { draftId, changeClass: preview2.changeClass, nonMonotonicOps: preview2.nonMonotonicOps };
|
|
21021
21124
|
}
|
|
21022
21125
|
async proposeCommandAuthorization(args) {
|
|
21023
21126
|
const originNorm = args.origin ?? null;
|
|
21024
21127
|
const argsJson = JSON.stringify(args.args ?? {});
|
|
21025
|
-
for (const [id,
|
|
21026
|
-
if (
|
|
21027
|
-
if ((
|
|
21028
|
-
if (JSON.stringify(
|
|
21128
|
+
for (const [id, card3] of this.draftCards) {
|
|
21129
|
+
if (card3.kind !== "command" || card3.command !== args.command) continue;
|
|
21130
|
+
if ((card3.origin ?? null) !== originNorm) continue;
|
|
21131
|
+
if (JSON.stringify(card3.commandArgs ?? {}) !== argsJson) continue;
|
|
21029
21132
|
await this.refreshModel();
|
|
21030
21133
|
return { draftId: id };
|
|
21031
21134
|
}
|
|
21032
21135
|
const draftId = `draft:${(0, import_node_crypto3.randomUUID)()}`;
|
|
21033
|
-
|
|
21136
|
+
const card2 = {
|
|
21034
21137
|
kind: "command",
|
|
21035
21138
|
command: args.command,
|
|
21036
21139
|
commandArgs: args.args,
|
|
@@ -21038,8 +21141,11 @@ var init_kernel_bridge = __esm({
|
|
|
21038
21141
|
anchor: args.artifactId,
|
|
21039
21142
|
proposedBy: args.actor,
|
|
21040
21143
|
origin: args.origin,
|
|
21041
|
-
parentMessageId: args.parentMessageId
|
|
21042
|
-
|
|
21144
|
+
parentMessageId: args.parentMessageId,
|
|
21145
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
21146
|
+
};
|
|
21147
|
+
this.draftCards.set(draftId, card2);
|
|
21148
|
+
await this.persistDraftCard(draftId, card2);
|
|
21043
21149
|
await this.refreshModel();
|
|
21044
21150
|
return { draftId };
|
|
21045
21151
|
}
|
|
@@ -21068,11 +21174,13 @@ var init_kernel_bridge = __esm({
|
|
|
21068
21174
|
throw new Error(`\u547D\u4EE4\u6388\u6743\uFF1A\u6682\u4E0D\u652F\u6301\u6267\u884C ${card2.command}`);
|
|
21069
21175
|
}
|
|
21070
21176
|
this.draftCards.delete(args.draftId);
|
|
21177
|
+
await this.forgetDraftCard(args.draftId);
|
|
21071
21178
|
await this.refreshModel();
|
|
21072
21179
|
return { command: card2.command, artifactId };
|
|
21073
21180
|
}
|
|
21074
21181
|
async rejectIntervention(args) {
|
|
21075
21182
|
this.draftCards.delete(args.draftId);
|
|
21183
|
+
await this.forgetDraftCard(args.draftId);
|
|
21076
21184
|
await this.refreshModel();
|
|
21077
21185
|
}
|
|
21078
21186
|
/**
|
|
@@ -21383,6 +21491,7 @@ var init_kernel_bridge = __esm({
|
|
|
21383
21491
|
}
|
|
21384
21492
|
if (typeof plan.resolvesProposal === "string") {
|
|
21385
21493
|
this.draftCards.delete(plan.resolvesProposal);
|
|
21494
|
+
await this.forgetDraftCard(plan.resolvesProposal);
|
|
21386
21495
|
await this.refreshModel();
|
|
21387
21496
|
}
|
|
21388
21497
|
return {
|
|
@@ -25452,7 +25561,9 @@ var init_codex = __esm({
|
|
|
25452
25561
|
const reasoningEffort = this.opts.reasoningEffort;
|
|
25453
25562
|
return runProtocolSession(job, {
|
|
25454
25563
|
bin: this.opts.codexBin ?? "codex",
|
|
25455
|
-
|
|
25564
|
+
// 同 exec 那条路:关掉登录 shell,否则 `~/.profile` 会把 `$HOME/.local/bin` 前置到
|
|
25565
|
+
// PATH 最前,压过注入的连接器 wrapper(理由与实证见 exec 分支里那段长注释)。
|
|
25566
|
+
args: ["app-server", "--stdio", "-c", "allow_login_shell=false"],
|
|
25456
25567
|
extraArgs: this.opts.extraArgs,
|
|
25457
25568
|
blockedFlags: CODEX_APP_SERVER_BLOCKED_FLAGS,
|
|
25458
25569
|
workRoot: this.opts.workRoot,
|
|
@@ -25633,6 +25744,24 @@ ${task}` : task;
|
|
|
25633
25744
|
// 打开 reasoning 摘要通道:默认 auto,让 codex 产出 reasoning item(→ thought → 前端思考块)。
|
|
25634
25745
|
...this.opts.reasoningSummary !== "none" ? ["-c", `model_reasoning_summary=${this.opts.reasoningSummary ?? "auto"}`] : [],
|
|
25635
25746
|
...this.opts.reasoningEffort ? ["-c", `model_reasoning_effort=${this.opts.reasoningEffort}`] : [],
|
|
25747
|
+
/* ★ 关掉登录 shell(2026-09-11)。
|
|
25748
|
+
codex 的 shell 工具默认把每条命令丢进 `/bin/bash -lc`——本机 codex 自己的
|
|
25749
|
+
thread_history 里逐字记着 `"command":"/bin/bash -lc \"pwd && rg …"`。
|
|
25750
|
+
登录 shell 会重跑 `~/.profile`,而 Ubuntu 的 profile 里是
|
|
25751
|
+
`PATH="$HOME/.local/bin:$PATH"`(**前置**);于是任何装在 `~/.local/bin` 的同名 CLI
|
|
25752
|
+
都会抢到注入的连接器 wrapper 前面。实测后果:飞书连接器凭据、连接记录全对,
|
|
25753
|
+
dev-agent(codex)跑 `lark-cli whoami` 却回 `not_configured`——wrapper 被绕过、
|
|
25754
|
+
而隔离键 `LARKSUITE_CLI_CONFIG_DIR` 照旧生效指着空目录,两头落空。
|
|
25755
|
+
同机同用户的 claude-code agent 一切正常,差别只有这一个 `-l`。
|
|
25756
|
+
|
|
25757
|
+
`allow_login_shell` 是 codex 的正式配置键(本机实测:给它传整数会报
|
|
25758
|
+
`invalid type: integer, expected a boolean in \`allow_login_shell\``,
|
|
25759
|
+
而不存在的键是被静默忽略的),exec 与 app-server 两条路都吃 `-c` 覆盖。
|
|
25760
|
+
|
|
25761
|
+
⚠ 未验证的一格:这个键**是否真的**就是那个 `-l` 的开关,只能等下一次
|
|
25762
|
+
codex 派单后在节点上 `ps` 看是 `bash -lc` 还是 `bash -c`。设错了是 no-op,不会更坏。 */
|
|
25763
|
+
"-c",
|
|
25764
|
+
"allow_login_shell=false",
|
|
25636
25765
|
// ADR「agent CLI 启动参数员工级可配置化」:员工配置 job.extraArgs(在前)+ opts.extraArgs(静态默认,在后),
|
|
25637
25766
|
// 过 CODEX_BLOCKED_FLAGS 拦截。追加在**位置 prompt 之前**(codex exec 的 [PROMPT] 必须是末位参数)。
|
|
25638
25767
|
...prepareExtraArgs(
|
|
@@ -28663,7 +28792,7 @@ function projectsIntoContent(kind, role, origin, telemetryTextInContent) {
|
|
|
28663
28792
|
return true;
|
|
28664
28793
|
}
|
|
28665
28794
|
}
|
|
28666
|
-
var import_node_crypto15, CHAT_ITEMS_DOUBLE_WRITE_ENV, ChatItemLedger;
|
|
28795
|
+
var import_node_crypto15, CHAT_ITEMS_DOUBLE_WRITE_ENV, SEGMENT_END_NOTE, ChatItemLedger;
|
|
28667
28796
|
var init_chat_item_ledger = __esm({
|
|
28668
28797
|
"../server/src/chat-item-ledger.ts"() {
|
|
28669
28798
|
"use strict";
|
|
@@ -28671,6 +28800,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28671
28800
|
init_chat_item();
|
|
28672
28801
|
import_node_crypto15 = require("node:crypto");
|
|
28673
28802
|
CHAT_ITEMS_DOUBLE_WRITE_ENV = "OASIS_CHAT_ITEMS_DOUBLE_WRITE";
|
|
28803
|
+
SEGMENT_END_NOTE = "\u672C\u6BB5\u65E0\u8F93\u51FA\uFF08\u4EBA\u5728\u9996\u4E2A\u6B63\u6587\u4E4B\u524D\u63D2\u4E86\u8BDD\uFF09";
|
|
28674
28804
|
ChatItemLedger = class {
|
|
28675
28805
|
constructor(deps) {
|
|
28676
28806
|
this.deps = deps;
|
|
@@ -28699,6 +28829,8 @@ var init_chat_item_ledger = __esm({
|
|
|
28699
28829
|
log;
|
|
28700
28830
|
persistEnabled;
|
|
28701
28831
|
telemetryTextInContent;
|
|
28832
|
+
/** 本段的「无输出」注解已经提前落过(`closeSegmentBeforeUserInput`),`beginSegment` 别再落一条。 */
|
|
28833
|
+
segmentEndEmitted = false;
|
|
28702
28834
|
/** **不变量 1**:`providerItemKey → itemId`,本账本唯一持有。切段/收尾清空。 */
|
|
28703
28835
|
itemKeyMap = /* @__PURE__ */ new Map();
|
|
28704
28836
|
/** 内存账本:itemId → 行。插入顺序即 seq 顺序。 */
|
|
@@ -28916,19 +29048,11 @@ var init_chat_item_ledger = __esm({
|
|
|
28916
29048
|
* 映射一并清空——新段是新行,复用旧 itemId 会把新文本写进上一条消息。
|
|
28917
29049
|
*/
|
|
28918
29050
|
beginSegment() {
|
|
28919
|
-
this.
|
|
28920
|
-
|
|
28921
|
-
|
|
28922
|
-
|
|
28923
|
-
|
|
28924
|
-
status: "completed",
|
|
28925
|
-
origin: "server",
|
|
28926
|
-
text: "",
|
|
28927
|
-
attrs: {},
|
|
28928
|
-
inContent: false,
|
|
28929
|
-
persist: true,
|
|
28930
|
-
payload: { code: "segment_end", note: "\u672C\u6BB5\u65E0\u8F93\u51FA\uFF08\u4EBA\u5728\u9996\u4E2A\u6B63\u6587\u4E4B\u524D\u63D2\u4E86\u8BDD\uFF09" }
|
|
28931
|
-
});
|
|
29051
|
+
if (this.segmentEndEmitted) {
|
|
29052
|
+
this.segmentEndEmitted = false;
|
|
29053
|
+
} else {
|
|
29054
|
+
this.closeStreamingRows("completed");
|
|
29055
|
+
if (this.segmentItemCount === 0 && this.messageId) this.insertSegmentEnd();
|
|
28932
29056
|
}
|
|
28933
29057
|
this.itemKeyMap.clear();
|
|
28934
29058
|
this.lastTurnId = null;
|
|
@@ -28936,6 +29060,41 @@ var init_chat_item_ledger = __esm({
|
|
|
28936
29060
|
this.segment += 1;
|
|
28937
29061
|
this.segmentItemCount = 0;
|
|
28938
29062
|
}
|
|
29063
|
+
/**
|
|
29064
|
+
* 人插话时,先把「这一段到此为止」落定;**必须在那条 user 行之前调用**。
|
|
29065
|
+
*
|
|
29066
|
+
* 展示序(`ord`)由落库那一刻的表级序列派,所以**谁先落库谁在上面**。此前这条注解跟在
|
|
29067
|
+
* `beginSegment()` 里、而 `beginSegment` 由 `onUserInput` 回调在 user 行之后触发,于是
|
|
29068
|
+
* 「本段无输出」排到了人说的那句话**下面**——发起人 2026-09-11 的现场(会话 `eca44da4`:
|
|
29069
|
+
* user 行 ord 8503、注解 ord 8504)。他要的顺序是:开启这一轮的那条消息 → agent 头像块
|
|
29070
|
+
* (里面就是这条注解)→ 人插的那句话 → agent 接着说。
|
|
29071
|
+
*
|
|
29072
|
+
* 返回落下的那一行(`null` = 本段有输出、或还没有 assistant 行可归属,不需要注解),
|
|
29073
|
+
* 调用方据它把这条推上 v3 流——不推的话它只在下一次快照才出现。
|
|
29074
|
+
*/
|
|
29075
|
+
closeSegmentBeforeUserInput() {
|
|
29076
|
+
this.closeStreamingRows("completed");
|
|
29077
|
+
if (this.segmentEndEmitted) return null;
|
|
29078
|
+
if (this.segmentItemCount !== 0 || !this.messageId) return null;
|
|
29079
|
+
const id = this.insertSegmentEnd();
|
|
29080
|
+
this.segmentEndEmitted = true;
|
|
29081
|
+
const row = this.rows.get(id);
|
|
29082
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
29083
|
+
}
|
|
29084
|
+
/** 零输出段兜底(S1 评审 B1):那条 assistant 行库里已经在了,一条 item 都没有会被对账抓成假红。 */
|
|
29085
|
+
insertSegmentEnd() {
|
|
29086
|
+
return this.insert({
|
|
29087
|
+
kind: "control",
|
|
29088
|
+
role: "assistant",
|
|
29089
|
+
status: "completed",
|
|
29090
|
+
origin: "server",
|
|
29091
|
+
text: "",
|
|
29092
|
+
attrs: {},
|
|
29093
|
+
inContent: false,
|
|
29094
|
+
persist: true,
|
|
29095
|
+
payload: { code: "segment_end", note: SEGMENT_END_NOTE }
|
|
29096
|
+
});
|
|
29097
|
+
}
|
|
28939
29098
|
/** 行落库了,把本段还挂着 NULL 的 assistant item 认领回去,并让后续 item 直接带上它。 */
|
|
28940
29099
|
bindMessage(messageId) {
|
|
28941
29100
|
this.messageId = messageId;
|
|
@@ -159847,6 +160006,7 @@ var DEFAULT_BUFFER_MAX, DEFAULT_GRACE_MS, DEFAULT_LIVE_COALESCE_MS, DEFAULT_LIVE
|
|
|
159847
160006
|
var init_live_chat = __esm({
|
|
159848
160007
|
"../server/src/live-chat.ts"() {
|
|
159849
160008
|
"use strict";
|
|
160009
|
+
init_chat_item_ledger();
|
|
159850
160010
|
DEFAULT_BUFFER_MAX = 4e3;
|
|
159851
160011
|
DEFAULT_GRACE_MS = 6e4;
|
|
159852
160012
|
DEFAULT_LIVE_COALESCE_MS = 24;
|
|
@@ -160593,6 +160753,24 @@ var init_live_chat = __esm({
|
|
|
160593
160753
|
text: shown,
|
|
160594
160754
|
...opts?.attachments?.length ? { attachments: opts.attachments } : {}
|
|
160595
160755
|
};
|
|
160756
|
+
const segmentEnd = (() => {
|
|
160757
|
+
try {
|
|
160758
|
+
return turn.items?.closeSegmentBeforeUserInput() ?? null;
|
|
160759
|
+
} catch {
|
|
160760
|
+
return null;
|
|
160761
|
+
}
|
|
160762
|
+
})();
|
|
160763
|
+
if (segmentEnd) {
|
|
160764
|
+
this.publishServerItem(turn, {
|
|
160765
|
+
itemId: segmentEnd.itemId,
|
|
160766
|
+
itemType: "control",
|
|
160767
|
+
startedVersion: segmentEnd.startedVersion,
|
|
160768
|
+
version: segmentEnd.version,
|
|
160769
|
+
ord: segmentEnd.ord,
|
|
160770
|
+
status: "completed",
|
|
160771
|
+
payload: { code: "segment_end", note: SEGMENT_END_NOTE }
|
|
160772
|
+
});
|
|
160773
|
+
}
|
|
160596
160774
|
try {
|
|
160597
160775
|
turn.items?.recordUserInput(shown, opts?.attachments, opts?.clientKeys);
|
|
160598
160776
|
} catch {
|
|
@@ -212756,7 +212934,15 @@ var init_service4 = __esm({
|
|
|
212756
212934
|
* 亲手提交过至少一版」。这是仓库里唯一一条真实存在的「产物 ↔ 人」关系;
|
|
212757
212935
|
* 「谁 own 这个节点」不用(节点 owner 是**派给谁**,派了没干完的也会被算成产出,那是假数据)。
|
|
212758
212936
|
*
|
|
212759
|
-
*
|
|
212937
|
+
* **空轮不算产出**(ADR 0539 补记):内核把**每一轮执行**都投影成一版 revision,这一轮一个产出文件
|
|
212938
|
+
* 都没有时 bridge 如实给 `contentKind="empty"`(运行中占位 / 被限流打死的空跑 / 只留结论没产正文,
|
|
212939
|
+
* 见 `bridge-readmodel.workToRevision` 与 ADR-0134)。那种版本这里一律跳过——它不是「他交了什么」,
|
|
212940
|
+
* 展示层拿到只会得到一行没有大小、点开写着「还没有可预览的正文」的空行。
|
|
212941
|
+
* 项目页「相关文件」早就不列这类轮次(`collectEngineFilesForProject` 的
|
|
212942
|
+
* `latestAcceptId` / `cancelledAt` / `arts.length === 0` 三道闸),本栏此前漏了这条口径。
|
|
212943
|
+
*
|
|
212944
|
+
* 于是 `revisionCount` 记的是**他交付过几版**、`latestRevisionId`/`updatedAt` 取**他最近一次真的
|
|
212945
|
+
* 交了东西的那一版**;一版都没交过内容的产物整条不出现。
|
|
212760
212946
|
*
|
|
212761
212947
|
* 数据同 `artifactDetail`:从 `kernel.model` 现算投影(新引擎 oplog 已退役、派生 store 恒空),
|
|
212762
212948
|
* 不读 `artifact_revisions` 表——读那张表在新引擎上永远是空列表。
|
|
@@ -212774,6 +212960,7 @@ var init_service4 = __esm({
|
|
|
212774
212960
|
const byArtifact = /* @__PURE__ */ new Map();
|
|
212775
212961
|
for (const revision of projection.revisions) {
|
|
212776
212962
|
if (revision.actorId !== actorId) continue;
|
|
212963
|
+
if (isEmptyContent(this.kernel.model.revisions.get(revision.revisionId)?.contentKind)) continue;
|
|
212777
212964
|
const artifact = artifactById.get(revision.artifactId);
|
|
212778
212965
|
if (!artifact) continue;
|
|
212779
212966
|
const prev = byArtifact.get(revision.artifactId);
|
|
@@ -212816,8 +213003,10 @@ var init_service4 = __esm({
|
|
|
212816
213003
|
* 同一个 {@link contentByteSize}**,不另写一套判据):装箱单摊成每个物理文件一行,
|
|
212817
213004
|
* 其余(inline-blob / external-pin / 装箱单解析失败)整份产物一行。
|
|
212818
213005
|
*
|
|
212819
|
-
*
|
|
212820
|
-
*
|
|
213006
|
+
* 取哪一版:**他自己最近交付过内容的那一版**(`latestRevisionId`,空轮已在
|
|
213007
|
+
* {@link listActorArtifacts} 里滤掉),不是产物的 current head——这一栏回答的是「他交了什么」,
|
|
213008
|
+
* 拿别人后来覆盖的版本冒充他的产出就是假数据。该版本 id 随行发给前端(`revisionId`),
|
|
213009
|
+
* 预览栏据它定位同一版:行与预览必须指着同一个东西,否则会出现「行上有 20 KB、点开说没有正文」。
|
|
212821
213010
|
*/
|
|
212822
213011
|
async listActorFiles(actorId) {
|
|
212823
213012
|
const artifacts = await this.listActorArtifacts(actorId);
|
|
@@ -212829,6 +213018,7 @@ var init_service4 = __esm({
|
|
|
212829
213018
|
const base = {
|
|
212830
213019
|
artifactId: item.artifactId,
|
|
212831
213020
|
projectId: item.projectId,
|
|
213021
|
+
revisionId: item.latestRevisionId,
|
|
212832
213022
|
type: item.type,
|
|
212833
213023
|
updatedAt: item.updatedAt,
|
|
212834
213024
|
...contentKind ? { contentKind } : {},
|
|
@@ -220147,6 +220337,20 @@ ${input.description}
|
|
|
220147
220337
|
};
|
|
220148
220338
|
});
|
|
220149
220339
|
}
|
|
220340
|
+
/**
|
|
220341
|
+
* 这一行变量是谁建的(子 PRD《密钥管理》§1 的权限判据)。**只读元数据、不解密**——
|
|
220342
|
+
* `listVariables()` 会把每一行都解一遍出掩码,写路径上没必要付那个钱。
|
|
220343
|
+
*
|
|
220344
|
+
* 返回 undefined 有两种含义,调用方按「放行」处理:行不存在(新建),
|
|
220345
|
+
* 或行是 2026-09-10 之前落的(那时还没有 created_by 这一格)。
|
|
220346
|
+
*/
|
|
220347
|
+
async variableCreatedBy(key, selector = {}) {
|
|
220348
|
+
const rows = await this.opts.store.listVariables();
|
|
220349
|
+
const hit = rows.find(
|
|
220350
|
+
(r) => r.key === key && (r.actorId ?? "") === (selector.actorId ?? "") && (r.projectId ?? "") === (selector.projectId ?? "")
|
|
220351
|
+
);
|
|
220352
|
+
return hit?.createdBy;
|
|
220353
|
+
}
|
|
220150
220354
|
async revealVariable(key, actorId) {
|
|
220151
220355
|
const ct = await this.opts.store.getVariableCiphertext(key, actorId);
|
|
220152
220356
|
if (ct === null) return null;
|
|
@@ -220952,7 +221156,7 @@ async function actorStats(actorId, opts = {}) {
|
|
|
220952
221156
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
220953
221157
|
const from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
220954
221158
|
const to = now.toISOString();
|
|
220955
|
-
const weeklyUsage = opts.usage?.listRuns ? summarizeWeeklyRuns(await listWeeklyRuns(opts.usage, actorId, from, to)) : summarizeWeeklyUsage(opts.usage?.queryUsageStats ? await opts.usage.queryUsageStats({
|
|
221159
|
+
const weeklyUsage = opts.usage?.listRuns ? summarizeWeeklyRuns(await listWeeklyRuns(opts.usage, actorId, from, to, opts.artifactIds)) : summarizeWeeklyUsage(!opts.artifactIds && opts.usage?.queryUsageStats ? await opts.usage.queryUsageStats({
|
|
220956
221160
|
actorIds: [actorId],
|
|
220957
221161
|
from,
|
|
220958
221162
|
to,
|
|
@@ -220960,6 +221164,8 @@ async function actorStats(actorId, opts = {}) {
|
|
|
220960
221164
|
}) : []);
|
|
220961
221165
|
return {
|
|
220962
221166
|
actorId,
|
|
221167
|
+
usageAvailable: !!(opts.usage?.listRuns || !opts.artifactIds && opts.usage?.queryUsageStats),
|
|
221168
|
+
...opts.model ? { participatingTaskCount: participatingTaskCount(opts.model, actorId) } : {},
|
|
220963
221169
|
tokenWeek: weeklyUsage.tokenWeek,
|
|
220964
221170
|
tokenWeekBreakdown: weeklyUsage.tokenWeekBreakdown,
|
|
220965
221171
|
costWeekUsdMicros: weeklyUsage.costWeekUsdMicros,
|
|
@@ -220969,12 +221175,12 @@ async function actorStats(actorId, opts = {}) {
|
|
|
220969
221175
|
unpricedModels: weeklyUsage.unpricedModels
|
|
220970
221176
|
};
|
|
220971
221177
|
}
|
|
220972
|
-
async function listWeeklyRuns(trace, actorId, from, to) {
|
|
221178
|
+
async function listWeeklyRuns(trace, actorId, from, to, artifactIds) {
|
|
220973
221179
|
const out = [];
|
|
220974
221180
|
let cursor;
|
|
220975
221181
|
do {
|
|
220976
221182
|
const page = await trace.listRuns({ actorId, from, to, limit: 500, ...cursor ? { cursor } : {} });
|
|
220977
|
-
out.push(...page.items);
|
|
221183
|
+
out.push(...page.items.filter((run) => !artifactIds || run.artifactId != null && artifactIds.has(run.artifactId)));
|
|
220978
221184
|
cursor = page.nextCursor ?? void 0;
|
|
220979
221185
|
} while (cursor);
|
|
220980
221186
|
return out;
|
|
@@ -220985,6 +221191,13 @@ function summarizeWeeklyRuns(runs) {
|
|
|
220985
221191
|
function summarizeWeeklyUsage(rows) {
|
|
220986
221192
|
return summarize2(rows.map((row) => ({ model: row.effectiveModel, usage: row })));
|
|
220987
221193
|
}
|
|
221194
|
+
function participatingTaskCount(model, actorId) {
|
|
221195
|
+
const workorders = /* @__PURE__ */ new Set();
|
|
221196
|
+
for (const artifact of model.artifacts.values()) {
|
|
221197
|
+
if (artifact.owner === actorId) workorders.add(artifact.workspace);
|
|
221198
|
+
}
|
|
221199
|
+
return workorders.size;
|
|
221200
|
+
}
|
|
220988
221201
|
var init_stats = __esm({
|
|
220989
221202
|
"../server/src/domains/actors/stats.ts"() {
|
|
220990
221203
|
"use strict";
|
|
@@ -221932,6 +222145,17 @@ function actorsDomain(opts) {
|
|
|
221932
222145
|
);
|
|
221933
222146
|
}
|
|
221934
222147
|
};
|
|
222148
|
+
const requireSecretOwner = async (req, service, key, selector = {}) => {
|
|
222149
|
+
const owner = await service.variableCreatedBy(key, selector);
|
|
222150
|
+
if (!owner || owner === req.auth.actor) return;
|
|
222151
|
+
const manager = opts.isCompanyManager ? await opts.isCompanyManager(req.auth.companyId ?? "", req.auth.actor).catch(() => false) : false;
|
|
222152
|
+
if (manager) return;
|
|
222153
|
+
throw new ApiError(
|
|
222154
|
+
403,
|
|
222155
|
+
"SECRET_NOT_OWNED",
|
|
222156
|
+
"\u8FD9\u4EFD\u5BC6\u94A5\u7531\u522B\u4EBA\u521B\u5EFA\uFF0C\u53EA\u6709\u521B\u5EFA\u8005\u672C\u4EBA\u6216\u7EC4\u7EC7\u7BA1\u7406\u8005\u53EF\u4EE5\u4FEE\u6539 / \u5220\u9664"
|
|
222157
|
+
);
|
|
222158
|
+
};
|
|
221935
222159
|
const auditVariableChange = async (req, kind, variableKey, targetScope) => {
|
|
221936
222160
|
await opts.credentialAudit?.({
|
|
221937
222161
|
kind,
|
|
@@ -221986,6 +222210,7 @@ function actorsDomain(opts) {
|
|
|
221986
222210
|
);
|
|
221987
222211
|
}
|
|
221988
222212
|
if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
|
|
222213
|
+
await requireSecretOwner(req, service, b2.key, scope === "project" ? { projectId: b2.projectId } : {});
|
|
221989
222214
|
await service.putVariable({
|
|
221990
222215
|
key: b2.key,
|
|
221991
222216
|
value: b2.value,
|
|
@@ -222013,6 +222238,7 @@ function actorsDomain(opts) {
|
|
|
222013
222238
|
requireVariableManager(req);
|
|
222014
222239
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
222015
222240
|
const projectId2 = req.query.get("projectId") ?? void 0;
|
|
222241
|
+
await requireSecretOwner(req, service, req.params.key, projectId2 ? { projectId: projectId2 } : {});
|
|
222016
222242
|
await service.deleteVariable(
|
|
222017
222243
|
req.params.key,
|
|
222018
222244
|
void 0,
|
|
@@ -222060,6 +222286,7 @@ function actorsDomain(opts) {
|
|
|
222060
222286
|
);
|
|
222061
222287
|
}
|
|
222062
222288
|
}
|
|
222289
|
+
await requireSecretOwner(req, service, b2.key, { actorId });
|
|
222063
222290
|
await service.putVariable({
|
|
222064
222291
|
key: b2.key,
|
|
222065
222292
|
value: b2.value,
|
|
@@ -222080,6 +222307,7 @@ function actorsDomain(opts) {
|
|
|
222080
222307
|
router.delete("/api/actors/:id/variables/:key", async (req) => {
|
|
222081
222308
|
requireVariableManager(req);
|
|
222082
222309
|
const { service } = await resolveCtx(req.auth.companyId);
|
|
222310
|
+
await requireSecretOwner(req, service, req.params.key, { actorId: req.params.id });
|
|
222083
222311
|
await service.deleteVariable(req.params.key, req.params.id);
|
|
222084
222312
|
await auditVariableChange(req, "credential_delete", req.params.key, `personal:${req.params.id}`);
|
|
222085
222313
|
return { status: 200, body: { ok: true } };
|
|
@@ -222305,9 +222533,16 @@ function actorsDomain(opts) {
|
|
|
222305
222533
|
throw new ApiError(400, "BAD_REQUEST", "\u4F20 enabled\uFF08\u5F00\u5173\uFF09/ appliedToAll\uFF08\u5168\u5458\u751F\u6548\uFF09/ name\xB7description\uFF08\u66F4\u65B0\u5143\u6570\u636E\uFF09");
|
|
222306
222534
|
});
|
|
222307
222535
|
router.get("/api/actors/:id/stats", async (req) => {
|
|
222536
|
+
const ctx = await resolveCtx(req.auth.companyId);
|
|
222537
|
+
if (!await ctx.service.getActor(req.params.id)) throw new ApiError(404, "NOT_FOUND", "\u6CA1\u6709\u8FD9\u4E2A\u5458\u5DE5");
|
|
222538
|
+
const model = ctx.getModel?.();
|
|
222308
222539
|
return {
|
|
222309
222540
|
status: 200,
|
|
222310
|
-
body: await actorStats(req.params.id, {
|
|
222541
|
+
body: await actorStats(req.params.id, {
|
|
222542
|
+
usage: req.auth.companyId && !model ? void 0 : opts.trace?.(),
|
|
222543
|
+
model,
|
|
222544
|
+
...req.auth.companyId ? { artifactIds: new Set(model?.artifacts.keys() ?? []) } : {}
|
|
222545
|
+
})
|
|
222311
222546
|
};
|
|
222312
222547
|
});
|
|
222313
222548
|
router.get("/api/actors/:id/running-tasks", async (req) => {
|
|
@@ -222865,6 +223100,7 @@ function createActorsDomain(opts) {
|
|
|
222865
223100
|
const engineStoreOf = (k2) => k2?.getStore?.();
|
|
222866
223101
|
const defaultCtx = {
|
|
222867
223102
|
service: buildService(opts.store, opts.kernel, opts.blobs),
|
|
223103
|
+
...opts.kernel?.model ? { getModel: () => opts.kernel.model } : {},
|
|
222868
223104
|
...opts.oplog !== void 0 ? { oplog: opts.oplog } : {},
|
|
222869
223105
|
...engineStoreOf(opts.kernel) !== void 0 ? { engineStore: engineStoreOf(opts.kernel) } : {},
|
|
222870
223106
|
...opts.blobs !== void 0 ? { blobs: opts.blobs } : {}
|
|
@@ -222878,6 +223114,7 @@ function createActorsDomain(opts) {
|
|
|
222878
223114
|
const blobs = engine.assets ?? engine.blobs;
|
|
222879
223115
|
const ctx = {
|
|
222880
223116
|
service: buildService(engine.registry, engine.kernel, blobs),
|
|
223117
|
+
getModel: () => engine.kernel.model,
|
|
222881
223118
|
oplog: engine.oplog,
|
|
222882
223119
|
...engineStoreOf(engine.kernel) !== void 0 ? { engineStore: engineStoreOf(engine.kernel) } : {},
|
|
222883
223120
|
blobs
|
|
@@ -222888,7 +223125,7 @@ function createActorsDomain(opts) {
|
|
|
222888
223125
|
return {
|
|
222889
223126
|
service: defaultCtx.service,
|
|
222890
223127
|
resolveCtx,
|
|
222891
|
-
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
223128
|
+
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
222892
223129
|
};
|
|
222893
223130
|
}
|
|
222894
223131
|
var import_node_crypto49;
|
|
@@ -223245,6 +223482,7 @@ ${errs.join("\n")}`);
|
|
|
223245
223482
|
id: item.id,
|
|
223246
223483
|
artifact_id: item.artifactId,
|
|
223247
223484
|
project_id: item.projectId,
|
|
223485
|
+
revision_id: item.revisionId,
|
|
223248
223486
|
type: item.type,
|
|
223249
223487
|
name: item.name,
|
|
223250
223488
|
file_path: item.filePath,
|
|
@@ -223920,6 +224158,15 @@ var init_routes5 = __esm({
|
|
|
223920
224158
|
}
|
|
223921
224159
|
});
|
|
223922
224160
|
|
|
224161
|
+
// ../server/src/domains/collab/activity-notice.ts
|
|
224162
|
+
var NOTICE_TITLE;
|
|
224163
|
+
var init_activity_notice = __esm({
|
|
224164
|
+
"../server/src/domains/collab/activity-notice.ts"() {
|
|
224165
|
+
"use strict";
|
|
224166
|
+
NOTICE_TITLE = "\u53D8\u66F4\u901A\u77E5";
|
|
224167
|
+
}
|
|
224168
|
+
});
|
|
224169
|
+
|
|
223923
224170
|
// ../server/src/domains/collab/review-activity.ts
|
|
223924
224171
|
function reviewCardId(targetWorkId, reviewerActorId) {
|
|
223925
224172
|
return `${REVIEW_CARD_PREFIX}${encodeURIComponent(targetWorkId)}:${encodeURIComponent(reviewerActorId)}`;
|
|
@@ -224068,35 +224315,18 @@ var init_review_activity = __esm({
|
|
|
224068
224315
|
|
|
224069
224316
|
// ../server/src/domains/collab/review-requirement-activity.ts
|
|
224070
224317
|
function reviewRequirementActivity(snap, events, ref2) {
|
|
224071
|
-
const previous3 = /* @__PURE__ */ new Map();
|
|
224072
224318
|
const titles = new Map(snap.nodes.map((n) => [n.id, n.title]));
|
|
224073
224319
|
const cards = [];
|
|
224074
224320
|
const label = (r) => `${ref2(r.reviewerActorId).name || "\u672A\u547D\u540D\u5BA1\u6838\u4EBA"}${r.source === "closure" ? "\uFF08\u7ED3\u6848\u5BA1\u6838\uFF09" : ""}`;
|
|
224075
|
-
const names = (rows) => [...new Set(rows.map(label))].join("\u3001")
|
|
224321
|
+
const names = (rows) => [...new Set(rows.map(label))].join("\u3001");
|
|
224322
|
+
const submitter = (id) => ref2(id).name?.trim() || "\u672A\u547D\u540D\u6210\u5458";
|
|
224323
|
+
const sentence = (id, nodeTitle, rows) => rows.length ? `${submitter(id)} \u66F4\u6539\u300A${nodeTitle}\u300B\u8282\u70B9\u7684\u5BA1\u6838\u4EBA\u4E3A ${names(rows)}\u3002` : `${submitter(id)} \u6E05\u7A7A\u4E86\u300A${nodeTitle}\u300B\u8282\u70B9\u7684\u5BA1\u6838\u4EBA\u3002`;
|
|
224076
224324
|
for (const rec of [...events].sort((a, b2) => a.seq - b2.seq)) {
|
|
224077
224325
|
if (rec.status === "pending" || rec.status === "failed") continue;
|
|
224078
224326
|
const e = rec.event;
|
|
224079
|
-
if (e.kind === "plan.changed") {
|
|
224080
|
-
for (const node2 of e.addNodes ?? []) previous3.set(node2.id, node2.reviewers ?? []);
|
|
224081
|
-
for (const id of e.removeNodes ?? []) previous3.delete(id);
|
|
224082
|
-
continue;
|
|
224083
|
-
}
|
|
224084
|
-
if (e.kind === "plan.add_node") {
|
|
224085
|
-
previous3.set(e.nodeId, e.reviewers ?? []);
|
|
224086
|
-
continue;
|
|
224087
|
-
}
|
|
224088
224327
|
if (e.kind !== "plan.update_review_requirements") continue;
|
|
224089
|
-
|
|
224090
|
-
const after = e.reviewers;
|
|
224091
|
-
const beforeKeys = new Set(before?.map(requirementKey));
|
|
224092
|
-
const afterKeys = new Set(after.map(requirementKey));
|
|
224093
|
-
const added = before ? after.filter((r) => !beforeKeys.has(requirementKey(r))) : [];
|
|
224094
|
-
const removed = before?.filter((r) => !afterKeys.has(requirementKey(r))) ?? [];
|
|
224328
|
+
if (!isAuthoredChange(rec)) continue;
|
|
224095
224329
|
const nodeTitle = titles.get(e.nodeId) || "\u5DF2\u79FB\u9664\u7684\u8282\u70B9";
|
|
224096
|
-
if (!isAuthoredChange(rec)) {
|
|
224097
|
-
previous3.set(e.nodeId, after);
|
|
224098
|
-
continue;
|
|
224099
|
-
}
|
|
224100
224330
|
cards.push({
|
|
224101
224331
|
id: `wo:${rec.seq}`,
|
|
224102
224332
|
seq: rec.seq,
|
|
@@ -224107,24 +224337,19 @@ function reviewRequirementActivity(snap, events, ref2) {
|
|
|
224107
224337
|
executor: ref2(rec.actorId),
|
|
224108
224338
|
...rec.handActorId ? { handActor: ref2(rec.handActorId) } : {},
|
|
224109
224339
|
phase: "done",
|
|
224110
|
-
|
|
224111
|
-
|
|
224112
|
-
...added.length ? [`\u65B0\u589E\uFF1A${names(added)}`] : [],
|
|
224113
|
-
...removed.length ? [`\u79FB\u9664\uFF1A${names(removed)}`] : [],
|
|
224114
|
-
`\u66F4\u65B0\u540E\uFF1A${names(after)}`
|
|
224115
|
-
].join("\n"),
|
|
224340
|
+
noticeTitle: NOTICE_TITLE,
|
|
224341
|
+
status: sentence(rec.actorId, nodeTitle, e.reviewers),
|
|
224116
224342
|
artifacts: [],
|
|
224117
224343
|
actions: []
|
|
224118
224344
|
});
|
|
224119
|
-
previous3.set(e.nodeId, after);
|
|
224120
224345
|
}
|
|
224121
224346
|
return cards;
|
|
224122
224347
|
}
|
|
224123
|
-
var
|
|
224348
|
+
var isAuthoredChange;
|
|
224124
224349
|
var init_review_requirement_activity = __esm({
|
|
224125
224350
|
"../server/src/domains/collab/review-requirement-activity.ts"() {
|
|
224126
224351
|
"use strict";
|
|
224127
|
-
|
|
224352
|
+
init_activity_notice();
|
|
224128
224353
|
isAuthoredChange = (rec) => rec.origin !== "apply" && rec.origin !== "effect" && !rec.actorId.startsWith("actor:system:");
|
|
224129
224354
|
}
|
|
224130
224355
|
});
|
|
@@ -224220,7 +224445,6 @@ function buildWorkorderActivity(input) {
|
|
|
224220
224445
|
return tail || nodeId;
|
|
224221
224446
|
};
|
|
224222
224447
|
const cardOfWork = /* @__PURE__ */ new Map();
|
|
224223
|
-
const latestCardOfNode = /* @__PURE__ */ new Map();
|
|
224224
224448
|
const executionCards = [];
|
|
224225
224449
|
const offTimeline = (w2) => isDispatchLayerFailure(w2.outcomeDetail) || w2.outcome === "completed" && statusOf2(w2) !== "success" && !artifactsByWork.get(w2.id)?.length;
|
|
224226
224450
|
const statusOf2 = (w2) => w2.status ?? (w2.outcome === "failed" ? "failed" : w2.outcome === "completed" ? "success" : "running");
|
|
@@ -224249,35 +224473,45 @@ function buildWorkorderActivity(input) {
|
|
|
224249
224473
|
works.sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id));
|
|
224250
224474
|
const interrupts = [...interruptsOfNode.get(nodeId) ?? []].sort();
|
|
224251
224475
|
const node2 = nodeById.get(nodeId);
|
|
224476
|
+
const nodeCancelledAt = node2?.cancelledAt ?? null;
|
|
224252
224477
|
const name = nodeName(nodeId);
|
|
224253
|
-
let
|
|
224478
|
+
let fold2 = null;
|
|
224254
224479
|
for (const [idx, w2] of works.entries()) {
|
|
224255
|
-
|
|
224256
|
-
|
|
224257
|
-
|
|
224258
|
-
|
|
224259
|
-
|
|
224260
|
-
|
|
224261
|
-
|
|
224262
|
-
|
|
224263
|
-
|
|
224264
|
-
|
|
224265
|
-
|
|
224266
|
-
|
|
224267
|
-
|
|
224268
|
-
|
|
224269
|
-
|
|
224270
|
-
|
|
224271
|
-
executionCards.push(card2);
|
|
224272
|
-
latestCardOfNode.set(nodeId, card2);
|
|
224480
|
+
const next = works[idx + 1];
|
|
224481
|
+
const artifacts = artifactsByWork.get(w2.id) ?? [];
|
|
224482
|
+
const st = statusOf2(w2);
|
|
224483
|
+
const interrupt = interrupts.find((t) => t >= w2.createdAt && (!next || t < next.createdAt));
|
|
224484
|
+
const removedHere = !!nodeCancelledAt && nodeCancelledAt >= w2.createdAt && (!next || nodeCancelledAt < next.createdAt);
|
|
224485
|
+
const reassigned = !!next && next.assigneeActorId !== w2.assigneeActorId;
|
|
224486
|
+
const killedByPlan = !!(w2.deadAt || w2.cancelledAt) && !!next && (reassigned || (next.nodeVersion ?? 0) > (w2.nodeVersion ?? 0));
|
|
224487
|
+
const failedRound = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && (!!w2.deadAt || !!w2.cancelledAt || st === "failed" || st === "retry");
|
|
224488
|
+
if (failedRound && fold2) {
|
|
224489
|
+
fold2.traceWorkIds.push(w2.id);
|
|
224490
|
+
cardOfWork.set(w2.id, fold2);
|
|
224491
|
+
fold2.updatedAt = lastStampOf(w2);
|
|
224492
|
+
fold2.stuckReason = w2.outcomeDetail?.reason === "timeout" ? "timeout" : "failed";
|
|
224493
|
+
fold2.foldedRounds = (fold2.foldedRounds ?? 1) + 1;
|
|
224494
|
+
fold2.status = ACTIVITY_COPY.foldedFailures(nameOf(fold2.executorId), name, fold2.foldedRounds);
|
|
224495
|
+
continue;
|
|
224273
224496
|
}
|
|
224274
|
-
|
|
224497
|
+
fold2 = null;
|
|
224498
|
+
const card2 = {
|
|
224499
|
+
/* 卡 id = 这一轮的 workId(连败折叠时取第一轮,React key 与滚动锚点因此稳定)。 */
|
|
224500
|
+
id: `work:${w2.id}`,
|
|
224501
|
+
traceWorkIds: [w2.id],
|
|
224502
|
+
seq: 0,
|
|
224503
|
+
// 事件循环里按这一轮 work.create 的 seq 填
|
|
224504
|
+
at: w2.createdAt,
|
|
224505
|
+
updatedAt: lastStampOf(w2),
|
|
224506
|
+
nodeId,
|
|
224507
|
+
executorId: w2.assigneeActorId,
|
|
224508
|
+
phase: "running",
|
|
224509
|
+
status: ACTIVITY_COPY.working(name),
|
|
224510
|
+
artifacts,
|
|
224511
|
+
actions: []
|
|
224512
|
+
};
|
|
224513
|
+
executionCards.push(card2);
|
|
224275
224514
|
cardOfWork.set(w2.id, card2);
|
|
224276
|
-
card2.executorId = w2.assigneeActorId;
|
|
224277
|
-
card2.updatedAt = lastStampOf(w2);
|
|
224278
|
-
card2.artifacts = artifactsByWork.get(w2.id) ?? card2.artifacts;
|
|
224279
|
-
delete card2.openWorkId;
|
|
224280
|
-
delete card2.stuckReason;
|
|
224281
224515
|
if (w2.conclusion) {
|
|
224282
224516
|
card2.phase = "done";
|
|
224283
224517
|
if (rootBriefNodeIds.has(nodeId)) {
|
|
@@ -224293,32 +224527,21 @@ function buildWorkorderActivity(input) {
|
|
|
224293
224527
|
card2.status = awaits ? ACTIVITY_COPY.doneAwaitingReview(name) : ACTIVITY_COPY.done(name);
|
|
224294
224528
|
}
|
|
224295
224529
|
}
|
|
224296
|
-
|
|
224297
|
-
continue;
|
|
224298
|
-
}
|
|
224299
|
-
const next = works[idx + 1];
|
|
224300
|
-
const interrupt = interrupts.find((t) => t >= w2.createdAt && (!next || t < next.createdAt));
|
|
224301
|
-
if (interrupt) {
|
|
224530
|
+
} else if (interrupt) {
|
|
224302
224531
|
card2.phase = "stuck";
|
|
224303
224532
|
card2.status = ACTIVITY_COPY.interruptedByIssue(name);
|
|
224304
224533
|
if (interrupt > card2.updatedAt) card2.updatedAt = interrupt;
|
|
224305
|
-
|
|
224306
|
-
continue;
|
|
224307
|
-
}
|
|
224308
|
-
const stuckKind = w2.outcomeDetail?.reason === "timeout" ? "timeout" : "failed";
|
|
224309
|
-
const st = statusOf2(w2);
|
|
224310
|
-
if (w2.deadAt || w2.cancelledAt) {
|
|
224311
|
-
card2.phase = "stuck";
|
|
224312
|
-
card2.stuckReason = stuckKind;
|
|
224313
|
-
card2.status = ACTIVITY_COPY.killed(name);
|
|
224314
|
-
} else if (st === "failed") {
|
|
224534
|
+
} else if (removedHere || killedByPlan) {
|
|
224315
224535
|
card2.phase = "stuck";
|
|
224316
|
-
card2.
|
|
224317
|
-
|
|
224318
|
-
|
|
224536
|
+
card2.status = removedHere ? ACTIVITY_COPY.interruptedByNodeRemoved(name) : reassigned ? ACTIVITY_COPY.interruptedByReassign(name, nameOf(next.assigneeActorId)) : ACTIVITY_COPY.interruptedByPlanChange(name);
|
|
224537
|
+
if (!removedHere && !reassigned) card2.planChangeUnnamed = true;
|
|
224538
|
+
if (removedHere && nodeCancelledAt > card2.updatedAt) card2.updatedAt = nodeCancelledAt;
|
|
224539
|
+
} else if (failedRound) {
|
|
224540
|
+
const stuckKind = w2.outcomeDetail?.reason === "timeout" ? "timeout" : "failed";
|
|
224319
224541
|
card2.phase = "stuck";
|
|
224320
224542
|
card2.stuckReason = stuckKind;
|
|
224321
|
-
card2.status = ACTIVITY_COPY.awaitingRedispatch(nameOf(card2.executorId), name);
|
|
224543
|
+
card2.status = w2.deadAt || w2.cancelledAt ? ACTIVITY_COPY.killed(name) : st === "retry" ? ACTIVITY_COPY.awaitingRedispatch(nameOf(card2.executorId), name) : stuckKind === "timeout" ? ACTIVITY_COPY.timedOut(nameOf(card2.executorId)) : ACTIVITY_COPY.failed(nameOf(card2.executorId));
|
|
224544
|
+
fold2 = card2;
|
|
224322
224545
|
} else if (w2.endedAt) {
|
|
224323
224546
|
card2.phase = "running";
|
|
224324
224547
|
card2.status = ACTIVITY_COPY.awaitingConclude(name);
|
|
@@ -224339,17 +224562,28 @@ function buildWorkorderActivity(input) {
|
|
|
224339
224562
|
if (rec.handActorId) d.handActorId = rec.handActorId;
|
|
224340
224563
|
return d;
|
|
224341
224564
|
};
|
|
224342
|
-
const
|
|
224565
|
+
const cardOpenAt = (nodeId, at) => {
|
|
224566
|
+
const list2 = worksOfNode.get(nodeId);
|
|
224567
|
+
if (!list2) return void 0;
|
|
224568
|
+
let hit;
|
|
224569
|
+
for (const w2 of list2) {
|
|
224570
|
+
if (w2.createdAt > at) break;
|
|
224571
|
+
hit = w2;
|
|
224572
|
+
}
|
|
224573
|
+
return hit ? cardOfWork.get(hit.id) : void 0;
|
|
224574
|
+
};
|
|
224575
|
+
const woCard = (rec, status, opts = {}) => {
|
|
224343
224576
|
standalone.push({
|
|
224344
224577
|
id: `wo:${rec.seq}`,
|
|
224345
224578
|
seq: rec.seq,
|
|
224346
224579
|
at: rec.createdAt,
|
|
224347
224580
|
updatedAt: rec.createdAt,
|
|
224348
|
-
executorId,
|
|
224581
|
+
executorId: rec.actorId,
|
|
224349
224582
|
...rec.handActorId ? { handActorId: rec.handActorId } : {},
|
|
224350
|
-
phase,
|
|
224583
|
+
phase: "done",
|
|
224584
|
+
...opts.notice ? { noticeTitle: NOTICE_TITLE } : {},
|
|
224351
224585
|
status,
|
|
224352
|
-
...detail ? { detail } : {},
|
|
224586
|
+
...opts.detail ? { detail: opts.detail } : {},
|
|
224353
224587
|
artifacts: [],
|
|
224354
224588
|
actions: []
|
|
224355
224589
|
});
|
|
@@ -224362,20 +224596,29 @@ function buildWorkorderActivity(input) {
|
|
|
224362
224596
|
case "plan.changed": {
|
|
224363
224597
|
break;
|
|
224364
224598
|
}
|
|
224599
|
+
/* 暂停 / 恢复是**人对这单做的事**,不是某一轮执行——走「变更通知」那套卡面(帧 `2874:1189`)。
|
|
224600
|
+
此前它们沿用执行卡的标题模板,卡面第一行被拼成「{谁} 已完成」(`ACTIVITY_PHASE_LABEL.done`),
|
|
224601
|
+
第二行才是「{谁}已暂停任务。」——标题和正文说的不是同一件事,而「已完成」在这一拍根本
|
|
224602
|
+
没有所指(暂停不是完成)。 */
|
|
224365
224603
|
case "workorder.paused":
|
|
224366
|
-
woCard(rec,
|
|
224604
|
+
woCard(rec, ACTIVITY_COPY.paused(nameOf(rec.actorId)), { notice: true, detail: str4(ev.reason) });
|
|
224367
224605
|
break;
|
|
224368
224606
|
case "workorder.resumed":
|
|
224369
|
-
woCard(rec,
|
|
224607
|
+
woCard(rec, ACTIVITY_COPY.resumedWorkorder(nameOf(rec.actorId)), { notice: true });
|
|
224370
224608
|
break;
|
|
224371
224609
|
case "workorder.meta_changed": {
|
|
224372
224610
|
const patch = ev.patch ?? {};
|
|
224373
224611
|
if (!("goal" in patch) && !("description" in patch)) break;
|
|
224374
|
-
woCard(rec,
|
|
224612
|
+
woCard(rec, ACTIVITY_COPY.goalChanged(nameOf(rec.actorId), managerName()));
|
|
224375
224613
|
break;
|
|
224376
224614
|
}
|
|
224377
224615
|
case "plan.update_spec": {
|
|
224378
|
-
|
|
224616
|
+
const nodeId = String(ev.nodeId ?? "");
|
|
224617
|
+
const d = cardOpenAt(nodeId, rec.createdAt);
|
|
224618
|
+
if (d?.planChangeUnnamed) {
|
|
224619
|
+
d.status = ACTIVITY_COPY.interruptedBySpecChange(nameOf(rec.actorId), nodeName(nodeId));
|
|
224620
|
+
delete d.planChangeUnnamed;
|
|
224621
|
+
}
|
|
224379
224622
|
break;
|
|
224380
224623
|
}
|
|
224381
224624
|
/* ── 执行(work)──────────────────────────────────────────────
|
|
@@ -224655,10 +224898,10 @@ function buildWorkorderActivity(input) {
|
|
|
224655
224898
|
case "plan.node_retry": {
|
|
224656
224899
|
const nodeId = String(ev.nodeId ?? "");
|
|
224657
224900
|
const by = str4(ev.by) ?? rec.actorId;
|
|
224658
|
-
const target =
|
|
224659
|
-
if (!target || target.phase !== "stuck") break;
|
|
224901
|
+
const target = cardOpenAt(nodeId, rec.createdAt);
|
|
224902
|
+
if (!target || target.phase !== "stuck" || !target.stuckReason) break;
|
|
224660
224903
|
touch(target, rec);
|
|
224661
|
-
target.status = target.stuckReason === "timeout" ? ACTIVITY_COPY.retriedAfterTimeout(nameOf(target.executorId), nameOf(by)) : ACTIVITY_COPY.retriedAfterFailure(nameOf(target.executorId), nameOf(by));
|
|
224904
|
+
target.status = target.foldedRounds && target.foldedRounds > 1 ? ACTIVITY_COPY.foldedFailuresRetried(nameOf(target.executorId), nodeName(nodeId), target.foldedRounds, nameOf(by)) : target.stuckReason === "timeout" ? ACTIVITY_COPY.retriedAfterTimeout(nameOf(target.executorId), nameOf(by)) : ACTIVITY_COPY.retriedAfterFailure(nameOf(target.executorId), nameOf(by));
|
|
224662
224905
|
break;
|
|
224663
224906
|
}
|
|
224664
224907
|
default:
|
|
@@ -224757,8 +225000,10 @@ function buildWorkorderActivity(input) {
|
|
|
224757
225000
|
] : []
|
|
224758
225001
|
});
|
|
224759
225002
|
}
|
|
225003
|
+
const openedSeq = events.find((rec) => rec.event.kind === "work.create")?.seq;
|
|
225004
|
+
const beforeOpen = (seq) => openedSeq !== void 0 && seq < openedSeq;
|
|
224760
225005
|
const drafts = [
|
|
224761
|
-
...standalone,
|
|
225006
|
+
...standalone.filter((d) => !beforeOpen(d.seq)),
|
|
224762
225007
|
...executionCards,
|
|
224763
225008
|
...visibleReviews.values(),
|
|
224764
225009
|
...issueCards.values(),
|
|
@@ -224822,6 +225067,7 @@ function buildWorkorderActivity(input) {
|
|
|
224822
225067
|
...d.reviewWorkId ? { reviewWorkId: d.reviewWorkId, reviewers: d.reviewerIds?.map(ref2) } : {},
|
|
224823
225068
|
...d.handActorId ? { handActor: ref2(d.handActorId) } : {},
|
|
224824
225069
|
phase: d.phase,
|
|
225070
|
+
...d.noticeTitle ? { noticeTitle: d.noticeTitle } : {},
|
|
224825
225071
|
status: d.status,
|
|
224826
225072
|
...d.detail ? { detail: d.detail } : {},
|
|
224827
225073
|
...d.reviewNotes?.length ? { reviewNotes: d.reviewNotes } : {},
|
|
@@ -224833,7 +225079,7 @@ function buildWorkorderActivity(input) {
|
|
|
224833
225079
|
.../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(traced ? runIdOfCard(d) : void 0)
|
|
224834
225080
|
};
|
|
224835
225081
|
});
|
|
224836
|
-
cards.push(...reviewRequirementActivity(snap, events, ref2));
|
|
225082
|
+
cards.push(...reviewRequirementActivity(snap, events, ref2).filter((c) => !beforeOpen(c.seq)));
|
|
224837
225083
|
cards.sort((a, b2) => a.updatedAt.localeCompare(b2.updatedAt) || a.seq - b2.seq || a.id.localeCompare(b2.id));
|
|
224838
225084
|
return { workorderId, cards, truncated };
|
|
224839
225085
|
}
|
|
@@ -224843,13 +225089,13 @@ var init_activity2 = __esm({
|
|
|
224843
225089
|
"use strict";
|
|
224844
225090
|
init_src();
|
|
224845
225091
|
init_src4();
|
|
225092
|
+
init_activity_notice();
|
|
224846
225093
|
init_review_activity();
|
|
224847
225094
|
init_review_requirement_activity();
|
|
224848
225095
|
init_workorder_manager();
|
|
224849
225096
|
init_escalation_recipient();
|
|
224850
225097
|
ACTIVITY_EVENT_KINDS = [
|
|
224851
225098
|
"plan.changed",
|
|
224852
|
-
"plan.add_node",
|
|
224853
225099
|
"plan.update_review_requirements",
|
|
224854
225100
|
"plan.update_spec",
|
|
224855
225101
|
"plan.node_retry",
|
|
@@ -224923,7 +225169,39 @@ var init_activity2 = __esm({
|
|
|
224923
225169
|
*
|
|
224924
225170
|
* 不重复缺口卡那句「需要 @{协调者} 协助解决」:该找谁由缺口卡自己说,执行卡只说这一轮到此为止。
|
|
224925
225171
|
*/
|
|
225172
|
+
/**
|
|
225173
|
+
* **连败折叠**:同一节点上连续没跑成的 N 轮折成一张。不折叠的话 `ws:wo-6d588f8e` 的《隔离边界实现》
|
|
225174
|
+
* 会在时间线上连出 12 张,其中 9 张都是「执行失败。」——真正要给人看的是「这个节点一直没跑成」
|
|
225175
|
+
* 这一件事,逐轮的原因在轨迹抽屉里(`traceWorkIds` 收齐全部 N 轮,一轮不少)。
|
|
225176
|
+
*/
|
|
225177
|
+
foldedFailures: (executor, node2, n) => `${executor} \u8FDE\u7EED ${n} \u6B21\u6CA1\u8DD1\u6210\u300A${node2}\u300B\u3002`,
|
|
225178
|
+
/** 折叠卡上补「谁按的重试」——不把「连续 N 次」换成单轮那句。 */
|
|
225179
|
+
foldedFailuresRetried: (executor, node2, n, by) => `${executor} \u8FDE\u7EED ${n} \u6B21\u6CA1\u8DD1\u6210\u300A${node2}\u300B\uFF0C${by}\u5DF2\u4ECB\u5165\u91CD\u8BD5\u3002`,
|
|
224926
225180
|
interruptedByIssue: (node2) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u56E0\u5361\u70B9\u4E2D\u65AD\u3002`,
|
|
225181
|
+
/**
|
|
225182
|
+
* 这一轮**被计划变更打断**(P15)。与卡点中断同级:计划一动,这一轮就不再是「还在跑」,
|
|
225183
|
+
* 而是被引擎当场顶掉了——`plan.delete_node` 直接 `killLatestWork`;`plan.update_spec`(spec 真变)
|
|
225184
|
+
* 与 `plan.assign_actor`(换人)把 `node.version` 加一,scan 随即重派,`work.create` 自带
|
|
225185
|
+
* supersede 把在跑的这一轮翻成 dead。
|
|
225186
|
+
*
|
|
225187
|
+
* 此前这两条都不结束卡:删节点那档只落到 {@link killed}「本轮执行已终止」,不说是计划调整;
|
|
225188
|
+
* 改 spec / 改派那档**根本没有结局**——新一轮直接改写同一张卡,「中断」在页面上不存在,
|
|
225189
|
+
* 卡上署名还会换成新人(与 §2.1「一张卡只讲一个人的事」相抵)。
|
|
225190
|
+
*/
|
|
225191
|
+
interruptedByPlanChange: (node2) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u56E0\u8BA1\u5212\u8C03\u6574\u4E2D\u65AD\u3002`,
|
|
225192
|
+
/**
|
|
225193
|
+
* 计划变更里最常见的那一种:**有人改了任务说明**,在跑的这一轮因此被顶掉。
|
|
225194
|
+
*
|
|
225195
|
+
* 这句取代了原来那张独立的任务级卡「已确认任务说明更新,正在判断该修改的影响。」——两张卡讲的
|
|
225196
|
+
* 本来就是同一件事:计划动了、这一轮做不下去了。旧卡还有两个毛病:`phase` 写死 `running`,
|
|
225197
|
+
* 没有任何一拍能让它收口(`ws:wo-217a0909` 12:13 那两张就是这么永久停在「进行中」的);文案是
|
|
225198
|
+
* 一句没有主语也没有宾语的固定串,两次内容不同的修改渲染成两行一模一样的字。
|
|
225199
|
+
*/
|
|
225200
|
+
interruptedBySpecChange: (by, node2) => `${by} \u66F4\u65B0\u4E86\u4EFB\u52A1\u8BF4\u660E\uFF0C\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u5230\u6B64\u4E2D\u65AD\u3002`,
|
|
225201
|
+
/** 换人那一档点破接手的人——卡到此为止,下一轮是新执行人的新卡。 */
|
|
225202
|
+
interruptedByReassign: (node2, next) => `\u300A${node2}\u300B\u672C\u8F6E\u6267\u884C\u56E0\u8BA1\u5212\u8C03\u6574\u4E2D\u65AD\uFF0C\u5DF2\u6539\u7531 ${next} \u63A5\u624B\u3002`,
|
|
225203
|
+
/** 节点被移出计划:这一轮再没有下文,也不会有新卡。 */
|
|
225204
|
+
interruptedByNodeRemoved: (node2) => `\u300A${node2}\u300B\u5DF2\u4ECE\u8BA1\u5212\u4E2D\u79FB\u9664\uFF0C\u672C\u8F6E\u6267\u884C\u4E2D\u65AD\u3002`,
|
|
224927
225205
|
/**
|
|
224928
225206
|
* 引擎判卷 success、`endedAt` 也写了,却始终没有 `work.conclude`:**交了稿没收口**。
|
|
224929
225207
|
* 如实说这一档,不冒充「正在处理」——后者把一轮早就结束的执行画成还在跑。
|
|
@@ -224960,7 +225238,6 @@ var init_activity2 = __esm({
|
|
|
224960
225238
|
paused: (actor) => `${actor}\u5DF2\u6682\u505C\u4EFB\u52A1\u3002`,
|
|
224961
225239
|
resumedWorkorder: (actor) => `${actor}\u5DF2\u6062\u590D\u4EFB\u52A1\u3002`,
|
|
224962
225240
|
goalChanged: (actor, manager) => `${actor}\u4FEE\u6539\u4E86\u4EFB\u52A1\u76EE\u6807\uFF0C\u9700\u8981 ${manager}\u91CD\u65B0\u8BC4\u4F30\u3002`,
|
|
224963
|
-
specConfirmed: "\u5DF2\u786E\u8BA4\u4EFB\u52A1\u8BF4\u660E\u66F4\u65B0\uFF0C\u6B63\u5728\u5224\u65AD\u8BE5\u4FEE\u6539\u7684\u5F71\u54CD\u3002",
|
|
224964
225241
|
handleEscalation: (target) => `${target} \u7684\u6267\u884C\u5F02\u5E38\u6301\u7EED\u672A\u89E3\u51B3\u3002`,
|
|
224965
225242
|
handledEscalation: (target) => `\u5DF2\u5904\u7406 ${target} \u7684\u6267\u884C\u5F02\u5E38\u3002`,
|
|
224966
225243
|
/**
|
|
@@ -225472,6 +225749,12 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
|
|
|
225472
225749
|
actionRequired: true,
|
|
225473
225750
|
summary: changeConfirmSummary(r),
|
|
225474
225751
|
draftId: r.draftId,
|
|
225752
|
+
// ★ 落点(2026-09-11):带上起草它的那段对话,让前端把这一行跳回**卡真正渲染着的地方**。
|
|
225753
|
+
// 此前这一行恒跳「任务详情 ?tab=activity」,而全仓库渲染待审卡的只有 chat 页与任务页的协调者
|
|
225754
|
+
// 对话(都按 origin=会话 id 查),动态 tab 里一行渲染代码都没有——于是收件箱这条路
|
|
225755
|
+
// 「找得到那一行、却点不到确认键」。origin 缺省(自主会话起草、没人在场)时不带,
|
|
225756
|
+
// 仍回落工单详情。
|
|
225757
|
+
...r.origin ? { chatSessionId: r.origin } : {},
|
|
225475
225758
|
...anchorArt ? { title: nodeLabel(anchorArt) } : {},
|
|
225476
225759
|
detail: `${actorName(r.proposedBy) || "\u7BA1\u7406\u8005"}\uFF1A${r.command?.effectLabel ?? firstSentence(r.plan.reason)}`,
|
|
225477
225760
|
...r.kind !== "command" && opCount > 0 ? { impact: `\u6D89\u53CA ${opCount} \u5904\u6539\u52A8${r.nonMonotonicOps.length > 0 ? " \xB7 \u542B\u9500\u6BC1\u6027" : ""}` } : {},
|
|
@@ -225969,13 +226252,7 @@ function buildWaitingInbox(reviews, works, policy, nowIso, me, resolveActor) {
|
|
|
225969
226252
|
const isSelf = r.actorId === me;
|
|
225970
226253
|
const actorLabel = displayName(resolveActor, r.actorId);
|
|
225971
226254
|
if (r.status === "dead" || r.status === "exhausted") {
|
|
225972
|
-
out.push(isSelf ? card(kind, r, "urgent", start, `\u4F60\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF0C\u9700\u8981\u4F60\u4ECB\u5165\u91CD\u5F00`) : card(
|
|
225973
|
-
kind,
|
|
225974
|
-
r,
|
|
225975
|
-
"urgent",
|
|
225976
|
-
start,
|
|
225977
|
-
`\u8FD9\u4E00\u6B65\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF08${actorLabel} \u8FDE\u7EED\u5931\u8D25\u8D85\u9650\uFF09\u2014\u2014\u672C\u8F6E${KIND_LABEL[kind]}\u6536\u4E0D\u9F50\u3001\u8282\u70B9\u505C\u5728\u8FD9\u91CC\uFF0C\u9700\u8981\u4F60\u91CD\u5F00\u8FD9\u4E00\u8F6E`
|
|
225978
|
-
));
|
|
226255
|
+
out.push(isSelf ? card(kind, r, "urgent", start, `\u4F60\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF0C\u9700\u8981\u4F60\u4ECB\u5165\u91CD\u5F00`) : card(kind, r, "urgent", start, kind === "review-waiting" ? `\u8FD9\u4E00\u6B65\u7684\u8BC4\u5BA1\u5DF2\u4E2D\u6B62\uFF08${actorLabel} \u8FDE\u7EED\u5931\u8D25\u8D85\u9650\uFF09\u2014\u2014\u672C\u8F6E\u8BC4\u5BA1\u6536\u4E0D\u9F50\u3001\u8282\u70B9\u505C\u5728\u8FD9\u91CC\uFF0C\u9700\u8981\u4F60\u91CD\u5F00\u8FD9\u4E00\u8F6E` : `\u8FD9\u4E00\u6B65\u7684\u5904\u7406\u5DF2\u4E2D\u6B62\uFF08${actorLabel} \u8FDE\u7EED\u5931\u8D25\u8D85\u9650\uFF09\u2014\u2014\u8282\u70B9\u505C\u5728\u8FD9\u91CC\uFF0C\u9700\u8981\u6709\u4EBA\u91CD\u5F00`));
|
|
225979
226256
|
continue;
|
|
225980
226257
|
}
|
|
225981
226258
|
if (r.status !== "running") continue;
|
|
@@ -226027,14 +226304,20 @@ function collectWaitingRows(snapshots2, me, policy = CODE_DEFAULT_POLICY) {
|
|
|
226027
226304
|
const requirementHeads = new Map(snap.requirements.map((q2) => [reqKey(q2), q2.latestReviewId]));
|
|
226028
226305
|
const requirementHead = (r) => requirementHeads.get(reqKey(r));
|
|
226029
226306
|
const nodesWithRequirements = new Set(snap.requirements.map((q2) => q2.nodeId));
|
|
226030
|
-
const
|
|
226307
|
+
const briefNode = snap.nodes.find((n) => n.type === "brief");
|
|
226308
|
+
const managerOfWo = briefNode?.fields?.["manager"];
|
|
226031
226309
|
const isManager = typeof managerOfWo === "string" && managerOfWo === me;
|
|
226310
|
+
const launcher = briefNode?.assigneeActorId ?? null;
|
|
226311
|
+
const openIssueNodes = new Set(
|
|
226312
|
+
snap.issues.filter((i) => i.resolvedAt === null && i.aboutNodeId !== null).map((i) => i.aboutNodeId)
|
|
226313
|
+
);
|
|
226314
|
+
const nodeHasOpenIssue = (nodeId) => openIssueNodes.has(nodeId);
|
|
226032
226315
|
const latestReview = /* @__PURE__ */ new Map();
|
|
226033
226316
|
for (const r of snap.reviews) {
|
|
226034
226317
|
if (cancelled.has(r.nodeId)) continue;
|
|
226035
226318
|
if (acceptedWork.get(r.nodeId) === r.targetWorkId) continue;
|
|
226036
226319
|
const mine = r.reviewerActorId === me;
|
|
226037
|
-
const oversees = nodeOwner.get(r.nodeId) === me || isManager;
|
|
226320
|
+
const oversees = nodeOwner.get(r.nodeId) === me || isManager || launcher === me;
|
|
226038
226321
|
if (!mine && !oversees) continue;
|
|
226039
226322
|
latestBy(latestReview, r);
|
|
226040
226323
|
}
|
|
@@ -226053,10 +226336,12 @@ function collectWaitingRows(snapshots2, me, policy = CODE_DEFAULT_POLICY) {
|
|
|
226053
226336
|
const humanPending = !stopped && classify(r.reviewerActorId) === "human";
|
|
226054
226337
|
if (!mine && !stopped && !humanPending) continue;
|
|
226055
226338
|
const audience = [r.reviewerActorId];
|
|
226056
|
-
const
|
|
226057
|
-
if (
|
|
226058
|
-
|
|
226059
|
-
|
|
226339
|
+
const addBystanders = humanPending || stopped && !nodeHasOpenIssue(r.nodeId);
|
|
226340
|
+
if (addBystanders) {
|
|
226341
|
+
for (const candidate of [launcher, managerOfWo, nodeOwner.get(r.nodeId)]) {
|
|
226342
|
+
if (typeof candidate !== "string" || classify(candidate) !== "human") continue;
|
|
226343
|
+
if (!audience.includes(candidate)) audience.push(candidate);
|
|
226344
|
+
}
|
|
226060
226345
|
}
|
|
226061
226346
|
reviews.push({
|
|
226062
226347
|
id: r.id,
|
|
@@ -226073,17 +226358,25 @@ function collectWaitingRows(snapshots2, me, policy = CODE_DEFAULT_POLICY) {
|
|
|
226073
226358
|
const latestMain = /* @__PURE__ */ new Map();
|
|
226074
226359
|
const latestReply = /* @__PURE__ */ new Map();
|
|
226075
226360
|
for (const w2 of snap.works) {
|
|
226076
|
-
if (
|
|
226361
|
+
if (cancelled.has(w2.nodeId)) continue;
|
|
226077
226362
|
latestBy(isReplyWork(w2) ? latestReply : latestMain, w2);
|
|
226078
226363
|
}
|
|
226079
226364
|
for (const w2 of [...latestMain.values(), ...latestReply.values()]) {
|
|
226080
226365
|
const st = workState(w2, hasOutput(w2, outputCountOf(w2.id)));
|
|
226081
226366
|
if (st !== "running" && st !== "dead") continue;
|
|
226367
|
+
const audience = [w2.assigneeActorId];
|
|
226368
|
+
if (st === "dead" && !nodeHasOpenIssue(w2.nodeId)) {
|
|
226369
|
+
for (const candidate of [launcher, managerOfWo, nodeOwner.get(w2.nodeId)]) {
|
|
226370
|
+
if (typeof candidate !== "string" || classify(candidate) !== "human") continue;
|
|
226371
|
+
if (!audience.includes(candidate)) audience.push(candidate);
|
|
226372
|
+
}
|
|
226373
|
+
}
|
|
226082
226374
|
works.push({
|
|
226083
226375
|
id: w2.id,
|
|
226084
226376
|
workorderId: snap.workorder.id,
|
|
226085
226377
|
artifactId: w2.nodeId,
|
|
226086
226378
|
actorId: w2.assigneeActorId,
|
|
226379
|
+
notifyActorIds: audience,
|
|
226087
226380
|
...nodeTitle.get(w2.nodeId) ? { title: nodeTitle.get(w2.nodeId) } : {},
|
|
226088
226381
|
createdAt: w2.createdAt,
|
|
226089
226382
|
startedAt: w2.startedAt,
|
|
@@ -253221,6 +253514,17 @@ var init_cutover_aware_trace = __esm({
|
|
|
253221
253514
|
if (row.state === "frozen") throw new BundleFrozenError("trace");
|
|
253222
253515
|
return this.o.cutover.readsCompanySchema(row) ? this.o.owned : this.o.legacy;
|
|
253223
253516
|
}
|
|
253517
|
+
/**
|
|
253518
|
+
* 这家公司的 trace 当前**物理落在哪**:切走了就是它自己的引擎 schema,没切走就是那个
|
|
253519
|
+
* 所有未切公司共用的 legacy store。
|
|
253520
|
+
*
|
|
253521
|
+
* 给 {@link FanOutTraceStore} 去重用。判据**不在调用方重算**——就是 {@link pick} 那一条
|
|
253522
|
+
* (`cutover.get` + `readsCompanySchema`),照抄一遍就是又造一处会分叉的实现。
|
|
253523
|
+
*/
|
|
253524
|
+
async storageKey() {
|
|
253525
|
+
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
253526
|
+
return this.o.cutover.readsCompanySchema(row) ? `owned:${this.o.companyId}` : "legacy";
|
|
253527
|
+
}
|
|
253224
253528
|
// ── run ──────────────────────────────────────────────────────────────────
|
|
253225
253529
|
async createRun(input) {
|
|
253226
253530
|
return (await this.pickWritable()).createRun(input);
|
|
@@ -253338,10 +253642,43 @@ var init_cutover_aware_trace = __esm({
|
|
|
253338
253642
|
constructor(o) {
|
|
253339
253643
|
this.o = o;
|
|
253340
253644
|
}
|
|
253341
|
-
|
|
253645
|
+
/**
|
|
253646
|
+
* 本次要查的物理落点清单——**同一个 store 只出现一次**。
|
|
253647
|
+
*
|
|
253648
|
+
* 为什么必须去重:`trace` 是按 (公司, bundle) 逐个切的,没切走的公司**全部共用同一个
|
|
253649
|
+
* legacy store**(读同一张 `public.agent_runs`)。此前 `union` 直接按公司逐家拼接,于是
|
|
253650
|
+
* 一条 run 在库里只有一行,却被取回 N 份(N = 未切走的公司数)。
|
|
253651
|
+
*
|
|
253652
|
+
* 2026-09-11 v2 生产实证(`ws:wo-9f4652cd`):35 家公司、其中 33 家 trace 未切走,
|
|
253653
|
+
* 动态卡的「执行轨迹」抽屉里同一轮 attempt 出现 **33 次**;`listEvents` 更狠——那一轮
|
|
253654
|
+
* `agent_run_events` 有 1158 行,扇出后返回 ≈3.8 万行,还附带 33 条一模一样的 SQL。
|
|
253655
|
+
* 不是显示层重复,是响应体和查询量一起炸。
|
|
253656
|
+
*
|
|
253657
|
+
* 去重键由 store 自己报,不在这里按 cutover 状态重算(见 `storageKey` 的注释)。
|
|
253658
|
+
* 也不能按 `storeFor` 返回的对象做身份去重:`openCompanyPlane` 给每家各造了一个
|
|
253659
|
+
* `CutoverAwareTraceStore` 包装实例,33 个不同对象包着同一个 legacy store。
|
|
253660
|
+
*/
|
|
253661
|
+
async targets(op) {
|
|
253662
|
+
const out = [];
|
|
253663
|
+
const seen = /* @__PURE__ */ new Set();
|
|
253342
253664
|
for (const cid of await this.o.listCompanies()) {
|
|
253343
253665
|
try {
|
|
253344
|
-
|
|
253666
|
+
if (this.o.storageKeyFor) {
|
|
253667
|
+
const key = await this.o.storageKeyFor(cid);
|
|
253668
|
+
if (seen.has(key)) continue;
|
|
253669
|
+
seen.add(key);
|
|
253670
|
+
}
|
|
253671
|
+
out.push({ cid, store: await this.o.storeFor(cid) });
|
|
253672
|
+
} catch (err) {
|
|
253673
|
+
this.o.onError?.(cid, op, err);
|
|
253674
|
+
}
|
|
253675
|
+
}
|
|
253676
|
+
return out;
|
|
253677
|
+
}
|
|
253678
|
+
async first(op, fn) {
|
|
253679
|
+
for (const { cid, store } of await this.targets(op)) {
|
|
253680
|
+
try {
|
|
253681
|
+
const r = await fn(store);
|
|
253345
253682
|
if (r != null) return r;
|
|
253346
253683
|
} catch (err) {
|
|
253347
253684
|
this.o.onError?.(cid, op, err);
|
|
@@ -253351,20 +253688,26 @@ var init_cutover_aware_trace = __esm({
|
|
|
253351
253688
|
}
|
|
253352
253689
|
async union(op, fn) {
|
|
253353
253690
|
const out = [];
|
|
253354
|
-
for (const cid of await this.
|
|
253691
|
+
for (const { cid, store } of await this.targets(op)) {
|
|
253355
253692
|
try {
|
|
253356
|
-
out.push(...await fn(
|
|
253693
|
+
out.push(...await fn(store));
|
|
253357
253694
|
} catch (err) {
|
|
253358
253695
|
this.o.onError?.(cid, op, err);
|
|
253359
253696
|
}
|
|
253360
253697
|
}
|
|
253361
253698
|
return out;
|
|
253362
253699
|
}
|
|
253363
|
-
/**
|
|
253700
|
+
/**
|
|
253701
|
+
* 这个 run 属于哪一家。写侧要先拿它,别直接写。
|
|
253702
|
+
*
|
|
253703
|
+
* ⚠ 未切走的那批公司共用一张 legacy 表,这里**判不出真正的归属**——它们都「有」这条 run,
|
|
253704
|
+
* 返回的是名单里第一家。去重不改变这个答案(第一家仍是第一家),只是把 N 次重复查询收成一次。
|
|
253705
|
+
* 要真正判准归属,得等 `trace` bundle 切完、每家各有自己的 schema。
|
|
253706
|
+
*/
|
|
253364
253707
|
async companyOfRun(runId) {
|
|
253365
|
-
for (const cid of await this.
|
|
253708
|
+
for (const { cid, store } of await this.targets("companyOfRun")) {
|
|
253366
253709
|
try {
|
|
253367
|
-
if (await
|
|
253710
|
+
if (await store.getRun(runId)) return cid;
|
|
253368
253711
|
} catch (err) {
|
|
253369
253712
|
this.o.onError?.(cid, "companyOfRun", err);
|
|
253370
253713
|
}
|
|
@@ -256170,6 +256513,11 @@ var init_default_company_schema_move = __esm({
|
|
|
256170
256513
|
"chat_sessions",
|
|
256171
256514
|
"event_handler_log",
|
|
256172
256515
|
"events",
|
|
256516
|
+
// 待人确认的 intervention 草案卡(2026-09-11 落库)。与 events/workorders 同族:锚在本公司的
|
|
256517
|
+
// 工单与产物上、不跨公司读,所以**整表搬**。不进 ENGINE_SCHEMA_BUNDLE_TABLES——那份是
|
|
256518
|
+
// legacy 里混着所有公司行的表(dispatches/blobs/trace 一族),只能逐行按归属复制;
|
|
256519
|
+
// 这张表在默认公司 schema 里只有总部自己的卡。
|
|
256520
|
+
"intervention_drafts",
|
|
256173
256521
|
"issue_attachments",
|
|
256174
256522
|
"issue_recipients",
|
|
256175
256523
|
"issue_replies",
|
|
@@ -259067,6 +259415,16 @@ async function startServe(opts) {
|
|
|
259067
259415
|
// ADR 0125:写 scope=project 变量时校验 projectId 真实存在(getProject 直查,含临时项目)。
|
|
259068
259416
|
// projectStateStore 在下方 ~行 1023 才赋值——本箭头只在请求时跑,那时已就绪(同 resolveDispatchScope 惰性模式)。
|
|
259069
259417
|
projectExists: async (id) => await projectStateStore.getProject(id) !== null,
|
|
259418
|
+
/* 子 PRD《密钥管理》§1 的权限判据:密钥的写 / 删要认「创建者本人或组织管理者」。
|
|
259419
|
+
owner 与 admin 都算组织管理者(与组织页 `orgIdentityOf` 同一口径)。
|
|
259420
|
+
`controlPlaneStore` 在下方才赋值——本箭头只在请求时跑,那时已就绪(同 projectExists 的惰性模式)。
|
|
259421
|
+
成员表按 accountId 主键,但行上带 actorId,密钥那边手里只有 actorId,所以按 actorId 找。 */
|
|
259422
|
+
isCompanyManager: async (companyId, actorId) => {
|
|
259423
|
+
if (!companyId || !actorId) return false;
|
|
259424
|
+
const members = await controlPlaneStore.listMembers(companyId).catch(() => []);
|
|
259425
|
+
const me = members.find((m2) => m2.actorId === actorId);
|
|
259426
|
+
return me?.role === "owner" || me?.role === "admin";
|
|
259427
|
+
},
|
|
259070
259428
|
// CO-302 数据面隔离(actors 域试点):按当前公司取其引擎,用各自 registry 服务该域请求。
|
|
259071
259429
|
// engineRouter 在下方(行 ~411)以 const 声明;此箭头只在请求时调用,那时已初始化,闭包引用合法
|
|
259072
259430
|
// (TDZ 只在构造时访问才报错,这里不访问)。默认公司命中现有单实例(registry===registryStore),
|
|
@@ -260194,6 +260552,15 @@ async function startServe(opts) {
|
|
|
260194
260552
|
const fanOutTrace = new FanOutTraceStore({
|
|
260195
260553
|
listCompanies: async () => pgPool ? (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id) : [defaultCompanyId],
|
|
260196
260554
|
storeFor: (cid) => traceStoreFor(cid),
|
|
260555
|
+
/**
|
|
260556
|
+
* 去重键:`trace` 未切走的公司全部共用同一个 legacy store,扇出只该查它一次。
|
|
260557
|
+
* 由 store 自己报落点,这里不按 cutover 状态重算(见 FanOutTraceStore.targets 的注释)。
|
|
260558
|
+
* 非 PG / 非 cutover-aware 的路径没有这个方法 ⇒ 退回按公司逐家查,行为不变。
|
|
260559
|
+
*/
|
|
260560
|
+
storageKeyFor: async (cid) => {
|
|
260561
|
+
const s2 = await traceStoreFor(cid);
|
|
260562
|
+
return typeof s2.storageKey === "function" ? s2.storageKey() : cid;
|
|
260563
|
+
},
|
|
260197
260564
|
onError: (cid, op, err) => console.warn(`[trace] ${cid} \u7684 ${op} \u5931\u8D25\uFF08\u5176\u4F59\u516C\u53F8\u7EE7\u7EED\uFF09\uFF1A${String(err)}`)
|
|
260198
260565
|
});
|
|
260199
260566
|
const traceSinkCache = /* @__PURE__ */ new Map();
|
|
@@ -268202,6 +268569,28 @@ var ASYNC_AUTH_MESSAGE = "\u5DF2\u63D0\u4EA4\u4EBA\u5DE5\u5BA1\u6838\uFF1B\u5148
|
|
|
268202
268569
|
function asyncAuthorizationReturn(data) {
|
|
268203
268570
|
return data !== void 0 ? { message: ASYNC_AUTH_MESSAGE, data } : { message: ASYNC_AUTH_MESSAGE };
|
|
268204
268571
|
}
|
|
268572
|
+
var AUTH_CARD_GONE_MESSAGE = "\u8FD9\u5F20\u5F85\u6388\u6743\u5361\u5DF2\u7ECF\u4E0D\u5728\u5F85\u5904\u7406\u5217\u8868\u91CC\u4E86\u2014\u2014\u53EF\u80FD\u5DF2\u88AB\u4EBA\u5904\u7F6E\uFF0C\u4E5F\u53EF\u80FD\u5DF2\u5931\u6548\u3002**\u522B\u518D\u8BA9\u7528\u6237\u53BB\u6536\u4EF6\u7BB1\u627E\u8FD9\u5F20\u5361**\uFF1A\u5148\u7528\u53EA\u8BFB\u547D\u4EE4\u6838\u5BF9\u5F53\u524D\u72B6\u6001\uFF08\u5982 `oasis status <\u8282\u70B9>`\uFF09\uFF0C\u786E\u8BA4\u8FD9\u4EF6\u4E8B\u6CA1\u751F\u6548\uFF0C\u518D\u91CD\u65B0\u53D1\u8D77\u540C\u4E00\u6761\u547D\u4EE4\u3002";
|
|
268573
|
+
function authorizationGoneReturn(draftId) {
|
|
268574
|
+
return { message: AUTH_CARD_GONE_MESSAGE, data: { outcome: "authorization-gone", draftId } };
|
|
268575
|
+
}
|
|
268576
|
+
async function draftStillPending(base, headers, draftId) {
|
|
268577
|
+
try {
|
|
268578
|
+
const ctrl = new AbortController();
|
|
268579
|
+
const timer = setTimeout(() => ctrl.abort(), 5e3);
|
|
268580
|
+
timer.unref?.();
|
|
268581
|
+
try {
|
|
268582
|
+
const res = await fetch(`${base}/api/view?name=intervention-drafts`, { headers, signal: ctrl.signal });
|
|
268583
|
+
if (!res.ok) return null;
|
|
268584
|
+
const body2 = await res.json();
|
|
268585
|
+
if (!Array.isArray(body2.data)) return null;
|
|
268586
|
+
return body2.data.some((d) => d?.id === draftId);
|
|
268587
|
+
} finally {
|
|
268588
|
+
clearTimeout(timer);
|
|
268589
|
+
}
|
|
268590
|
+
} catch {
|
|
268591
|
+
return null;
|
|
268592
|
+
}
|
|
268593
|
+
}
|
|
268205
268594
|
function renderGraphLines(graph) {
|
|
268206
268595
|
const closureIds = new Set(graph.nodes.filter((n) => n.type === DELIVERY_SUMMARY_ARTIFACT_TYPE).map((n) => n.id));
|
|
268207
268596
|
const out = ["\n\u8282\u70B9\uFF08\u6539\u56FE\u5F15\u7528\u7528\u8FD9\u4E9B id\uFF09:"];
|
|
@@ -268223,6 +268612,10 @@ function resolveAuthWaitMs() {
|
|
|
268223
268612
|
}
|
|
268224
268613
|
async function waitForCommandAuthorization(base, headers, draftId, fallback) {
|
|
268225
268614
|
const asyncFallback = asyncAuthorizationReturn(fallback.data);
|
|
268615
|
+
const giveUp = async () => {
|
|
268616
|
+
const pending = await draftStillPending(base, headers, draftId);
|
|
268617
|
+
return pending === false ? authorizationGoneReturn(draftId) : asyncFallback;
|
|
268618
|
+
};
|
|
268226
268619
|
const timeoutMs = resolveAuthWaitMs();
|
|
268227
268620
|
const controller = new AbortController();
|
|
268228
268621
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -268280,12 +268673,13 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
|
|
|
268280
268673
|
data: { outcome: "denied", draftId }
|
|
268281
268674
|
};
|
|
268282
268675
|
}
|
|
268283
|
-
return
|
|
268676
|
+
if (p2.status === "gone") return authorizationGoneReturn(draftId);
|
|
268677
|
+
return await giveUp();
|
|
268284
268678
|
}
|
|
268285
268679
|
}
|
|
268286
|
-
return
|
|
268680
|
+
return await giveUp();
|
|
268287
268681
|
} catch {
|
|
268288
|
-
return
|
|
268682
|
+
return await giveUp();
|
|
268289
268683
|
} finally {
|
|
268290
268684
|
clearTimeout(timer);
|
|
268291
268685
|
clearInterval(progress);
|
|
@@ -271790,7 +272184,7 @@ function shimScript() {
|
|
|
271790
272184
|
}
|
|
271791
272185
|
|
|
271792
272186
|
// src/index.ts
|
|
271793
|
-
var PKG_VERSION = true ? "2.2.
|
|
272187
|
+
var PKG_VERSION = true ? "2.2.18" : "dev";
|
|
271794
272188
|
var LOCAL_BIN = localBin();
|
|
271795
272189
|
var NPM_PREFIX = npmPrefix();
|
|
271796
272190
|
var INSTANCE = DEFAULT_INSTANCE;
|