oasis_test 0.1.103 → 0.1.105
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 +403 -107
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4440,6 +4440,14 @@ function classify(actorId) {
|
|
|
4440
4440
|
if (actorId.startsWith("actor:human:")) return "human";
|
|
4441
4441
|
return "unknown";
|
|
4442
4442
|
}
|
|
4443
|
+
function pickReminderStep(cadence2, elapsedMs) {
|
|
4444
|
+
let hit = null;
|
|
4445
|
+
for (let i = 0; i < cadence2.length; i++) {
|
|
4446
|
+
const s2 = cadence2[i];
|
|
4447
|
+
if (elapsedMs >= s2.atMs) hit = { step: s2, index: i };
|
|
4448
|
+
}
|
|
4449
|
+
return hit;
|
|
4450
|
+
}
|
|
4443
4451
|
function mergeConfig(cfg) {
|
|
4444
4452
|
return {
|
|
4445
4453
|
version: cfg?.version ?? "code-default-v1",
|
|
@@ -7364,9 +7372,9 @@ function checkTerminated(state) {
|
|
|
7364
7372
|
if (!isBusinessDependencyEdge(state, edge)) continue;
|
|
7365
7373
|
if (isEdgeInputUnobtainable(state, edge)) continue;
|
|
7366
7374
|
const upstream = state.node(edge.fromNodeId);
|
|
7367
|
-
if (!upstream?.
|
|
7375
|
+
if (!upstream?.latestAcceptId || !businessHandoffBarrierSatisfied(state, upstream.id, upstream.latestAcceptId)) return false;
|
|
7368
7376
|
}
|
|
7369
|
-
return live.every((n) => n.
|
|
7377
|
+
return live.every((n) => n.latestAcceptId !== null && (n.latestAcceptId === n.latestWorkId || n.latestAcceptId === n.latestSuccessWorkId));
|
|
7370
7378
|
}
|
|
7371
7379
|
function isEdgeInputUnobtainable(state, edge) {
|
|
7372
7380
|
const upstream = state.node(edge.fromNodeId);
|
|
@@ -7394,10 +7402,17 @@ function acceptBlock(state, workId) {
|
|
|
7394
7402
|
}
|
|
7395
7403
|
const node = state.node(w2.nodeId);
|
|
7396
7404
|
if (!node) return { code: "node-missing", detail: `\u8282\u70B9\u4E0D\u5728\u518C\uFF1A${w2.nodeId}`, overridable: false };
|
|
7397
|
-
if (!
|
|
7405
|
+
if (!hasOutput(w2, state.artifactsOf(w2.id).length)) {
|
|
7406
|
+
if (!w2.endedAt) {
|
|
7407
|
+
return {
|
|
7408
|
+
code: "work-in-flight",
|
|
7409
|
+
detail: `\u8FD9\u4E00\u7248\u8FD8\u5728\u8DD1\u3001\u4E14\u8FD8\u6CA1\u6709\u4EFB\u4F55\u4EA7\u51FA\u2014\u2014\u7B49\u5B83\u4EA4\u7A3F\u540E\u518D\u6536\u53E3\uFF0C\u6216\u5148\u505C\u6389\uFF08hold\uFF09/ \u91CD\u8DD1`,
|
|
7410
|
+
overridable: false
|
|
7411
|
+
};
|
|
7412
|
+
}
|
|
7398
7413
|
return {
|
|
7399
|
-
code: "
|
|
7400
|
-
detail: `\u8FD9\u4E00\u7248\
|
|
7414
|
+
code: "no-output",
|
|
7415
|
+
detail: `\u8FD9\u4E00\u7248\u5DF2\u7ED3\u675F\u4F46\u6CA1\u6709\u4EFB\u4F55\u4EA7\u51FA\uFF08\u7A7A\u58F3\uFF09\u2014\u2014\u4E0D\u80FD\u628A\u6CA1\u6709\u5185\u5BB9\u7684\u7248\u672C\u6536\u53E3\u6210\u4EA4\u4ED8\u7248\u672C\uFF1B\u6539\u5BF9\u6700\u8FD1\u4E00\u7248\u6709\u4EA7\u51FA\u7684\u7248\u672C\u64CD\u4F5C\uFF0C\u6216\u91CD\u8DD1\u672C\u8282\u70B9`,
|
|
7401
7416
|
overridable: false
|
|
7402
7417
|
};
|
|
7403
7418
|
}
|
|
@@ -7408,10 +7423,10 @@ function handoffBlock(state, nodeId, workId) {
|
|
|
7408
7423
|
if (!requiresBusinessHandoff(state, nodeId, work)) return null;
|
|
7409
7424
|
if (work?.acceptanceState === "accepted" && work.overrideBy) return null;
|
|
7410
7425
|
const node = state.node(nodeId);
|
|
7411
|
-
if (node?.latestWorkId !==
|
|
7426
|
+
if (workId !== node?.latestWorkId && workId !== node?.latestSuccessWorkId) {
|
|
7412
7427
|
return {
|
|
7413
7428
|
code: "not-latest-work",
|
|
7414
|
-
detail: `\u4E0D\u662F\u672C\u8282\u70B9\u6700\u65B0\
|
|
7429
|
+
detail: `\u4E0D\u662F\u672C\u8282\u70B9\u6700\u65B0\u7248\uFF08\u6700\u65B0\u5C1D\u8BD5\uFF1A${node?.latestWorkId ?? "\u65E0"}\uFF1B\u6700\u65B0\u6709\u6548\u4EA7\u51FA\uFF1A${node?.latestSuccessWorkId ?? "\u65E0"}\uFF09\u2014\u2014\u6536\u53E3\u53EA\u8BA4\u6700\u65B0\u4E00\u7248\u6216\u6700\u65B0\u6709\u6548\u4EA7\u51FA`,
|
|
7415
7430
|
overridable: false
|
|
7416
7431
|
};
|
|
7417
7432
|
}
|
|
@@ -7504,6 +7519,7 @@ CREATE TABLE IF NOT EXISTS ${schema}.workflow_nodes (
|
|
|
7504
7519
|
assignee_actor_id text,
|
|
7505
7520
|
assignee_role text,
|
|
7506
7521
|
latest_work_id text,
|
|
7522
|
+
latest_success_work_id text,
|
|
7507
7523
|
latest_accept_id text,
|
|
7508
7524
|
is_final_output boolean NOT NULL DEFAULT false,
|
|
7509
7525
|
fields jsonb,
|
|
@@ -7765,6 +7781,9 @@ CREATE INDEX IF NOT EXISTS issues_workorder_alive_idx ON ${schema}.issues (worko
|
|
|
7765
7781
|
CREATE INDEX IF NOT EXISTS issues_about_alive_idx ON ${schema}.issues (about_node_id) WHERE resolved_at IS NULL;
|
|
7766
7782
|
CREATE INDEX IF NOT EXISTS issues_raised_alive_idx ON ${schema}.issues (raised_by_node_id) WHERE resolved_at IS NULL;
|
|
7767
7783
|
ALTER TABLE ${schema}.workflow_nodes ADD COLUMN IF NOT EXISTS fields jsonb;
|
|
7784
|
+
-- \u2605 \u6709\u6548\u4EA7\u51FA\u6307\u9488\uFF08ADR 0148\uFF09\uFF1Anode \u4E0A\u300C\u6700\u8FD1\u4E00\u6B21\u5C1D\u8BD5\u300D\uFF08latest_work_id\uFF09\u4E4B\u5916\u518D\u5B58\u300C\u6700\u8FD1\u4E00\u6B21\u6709\u6548\u4EA7\u51FA\u300D\uFF0C
|
|
7785
|
+
-- \u5931\u8D25\u7A7A\u6D3E\u53D1\u4E0D\u518D\u9876\u6389\u6210\u54C1\u3002\u56DE\u586B\u5728\u4E0B\u65B9 works.status \u56DE\u586B\u4E4B\u540E\u6267\u884C\uFF08\u4F9D\u8D56 status \u5DF2\u843D\u5E93\uFF09\u3002
|
|
7786
|
+
ALTER TABLE ${schema}.workflow_nodes ADD COLUMN IF NOT EXISTS latest_success_work_id text;
|
|
7768
7787
|
-- \u2605 \u5148\u5220\u89C6\u56FE\u518D\u5220\u5217\uFF1Awork_view/node_view/workorder_view \u7528 SELECT *\uFF0C\u5BF9\u57FA\u8868\u5217\u6709\u4F9D\u8D56\u2014\u2014\u5B58\u91CF\u5E93\u4E0A
|
|
7769
7788
|
-- DROP COLUMN \u4F1A\u62A5 "cannot drop column ... because other objects depend on it"\uFF0C\u4E14\u672C\u6BB5\u662F\u5355\u6761
|
|
7770
7789
|
-- \u591A\u8BED\u53E5 Query\uFF08\u4E00\u4E2A\u9690\u5F0F\u4E8B\u52A1\uFF09\uFF0C\u4E00\u5904\u5931\u8D25\u5168\u6BB5\u56DE\u6EDA\u3001migrate \u629B\u9519 \u2192 serve \u8D77\u4E0D\u6765\u3002\u89C6\u56FE\u968F\u540E\u7531
|
|
@@ -7820,6 +7839,16 @@ UPDATE ${schema}.reviews SET status = CASE
|
|
|
7820
7839
|
WHEN ended_at IS NOT NULL THEN 'failed'
|
|
7821
7840
|
ELSE 'running' END
|
|
7822
7841
|
WHERE status IS NULL;
|
|
7842
|
+
-- \u2605 \u6709\u6548\u4EA7\u51FA\u6307\u9488\u56DE\u586B\uFF08ADR 0148\uFF09\uFF1Alatest_success_work_id = \u8BE5\u8282\u70B9\u6700\u8FD1\u4E00\u4E2A status=success \u7684**\u4E3B\u94FE** work
|
|
7843
|
+
-- \uFF08\u6392\u9664\u56DE\u4FE1 work reply_to_issue_id\uFF09\u3002\u5FC5\u987B\u5728\u4E0A\u9762 works.status \u56DE\u586B\u4E4B\u540E\uFF08\u4F9D\u8D56 status \u5DF2\u843D\u5E93\uFF09\u3002
|
|
7844
|
+
-- \u53EA\u586B\u5F53\u524D\u4E3A NULL \u7684\u884C \u2192 \u5E42\u7B49\uFF1A\u91CD\u542F\u65F6\u4E0D\u8986\u76D6 handler \u5DF2\u7EF4\u62A4\u7684\u503C\uFF1B\u4ECE\u672A\u6210\u529F\u8FC7\u7684\u8282\u70B9\u4FDD\u6301 NULL\u3002
|
|
7845
|
+
UPDATE ${schema}.workflow_nodes n SET latest_success_work_id = (
|
|
7846
|
+
SELECT w.id FROM ${schema}.works w
|
|
7847
|
+
WHERE w.node_id = n.id AND w.status = 'success' AND w.reply_to_issue_id IS NULL
|
|
7848
|
+
ORDER BY w.created_at DESC, w.id DESC
|
|
7849
|
+
LIMIT 1
|
|
7850
|
+
)
|
|
7851
|
+
WHERE n.latest_success_work_id IS NULL;
|
|
7823
7852
|
`;
|
|
7824
7853
|
}
|
|
7825
7854
|
function ENGINE_VIEWS(schema) {
|
|
@@ -7835,20 +7864,25 @@ SELECT r.*, r.status AS state
|
|
|
7835
7864
|
FROM ${schema}.reviews r;
|
|
7836
7865
|
|
|
7837
7866
|
DROP VIEW IF EXISTS ${schema}.node_view;
|
|
7867
|
+
-- \u2605 ADR 0148\uFF1A\u4E3B\u6001\u6309 latest_success\uFF08\u6700\u8FD1\u4E00\u4E2A\u6709\u6548\u4EA7\u51FA\uFF09\u8D70\u2014\u2014\u300C\u5DF2\u4EA4\u4ED8 v3\u3001\u672B\u6B21\u5C1D\u8BD5 v4 \u5D29\u4E86\u300D\u663E\u793A\u5DF2\u5B8C\u6210\uFF0C
|
|
7868
|
+
-- \u672B\u6B21\u5931\u8D25\u53EA\u4F5C\u526F\u6807\u8BB0\uFF08\u7248\u672C\u5217\u8868\u91CC\u90A3\u6761 failed \u4ECD\u5728\uFF09\uFF0C\u4E0D\u518D\u51FA\u73B0\u300C\u56FE\u4E0A\u7EA2\u3001\u4E0B\u6E38\u5728\u8DD1\u300D\u7684\u5272\u88C2\u3002
|
|
7869
|
+
-- \u4E0E views.ts nodeState \u540C\u6E90\uFF1A\u5728\u8DD1\uFF08latest_work running\uFF09\u4F18\u5148\uFF1B\u5426\u5219\u6709 latest_success \u5C31\u6309\u5176\u9A8C\u6536\u4E0E\u5426\u5206
|
|
7870
|
+
-- \u5DF2\u5B8C\u6210/\u8BC4\u5BA1\u4E2D\uFF1B\u518D\u5426\u5219\u672B\u6B21\u5C1D\u8BD5\u5931\u8D25/dead \u2192 \u5361\u4F4F\u3002
|
|
7838
7871
|
CREATE VIEW ${schema}.node_view AS
|
|
7839
7872
|
SELECT n.*,
|
|
7840
7873
|
CASE
|
|
7841
7874
|
WHEN n.cancelled_at IS NOT NULL THEN 'dead' -- \u5DF2\u4F5C\u5E9F\uFF08\u79FB\u51FA\u8BA1\u5212\uFF09
|
|
7842
7875
|
WHEN lw.id IS NULL THEN 'waiting' -- \u672A\u8FD0\u884C\uFF08latest_work \u4E3A\u7A7A\uFF09
|
|
7843
7876
|
WHEN lw.status = 'retry' THEN 'waiting' -- \u672A\u8FD0\u884C\uFF08\u7528\u6237\u4ECB\u5165\u91CD\u8BD5\uFF0C\u7B49 try_to_run\uFF09
|
|
7844
|
-
WHEN lw.status = 'running' THEN 'running' -- \u5728\u8FDB\u884C
|
|
7845
|
-
WHEN
|
|
7846
|
-
WHEN
|
|
7847
|
-
WHEN lw.status IN ('failed','dead') THEN 'dead' -- \u5361\u4F4F\u4E86
|
|
7877
|
+
WHEN lw.status = 'running' THEN 'running' -- \u5728\u8FDB\u884C\uFF08\u91CD\u8DD1\u8FDB\u884C\u4E2D\u4E5F\u4F18\u5148\u663E\u793A\u5728\u8FDB\u884C\uFF09
|
|
7878
|
+
WHEN ls.id IS NOT NULL AND n.latest_accept_id = ls.id THEN 'done' -- \u5DF2\u5B8C\u6210\uFF08\u6709\u6548\u4EA7\u51FA\u5DF2\u9A8C\u6536\uFF09
|
|
7879
|
+
WHEN ls.id IS NOT NULL AND n.latest_accept_id IS DISTINCT FROM ls.id THEN 'reviewing' -- \u8BC4\u5BA1\u4E2D\uFF08\u6709\u6548\u4EA7\u51FA\u672A\u9A8C\u6536\uFF09
|
|
7880
|
+
WHEN lw.status IN ('failed','dead') THEN 'dead' -- \u4ECE\u672A\u4EA7\u51FA\u4E14\u672B\u6B21\u5C1D\u8BD5\u5931\u8D25 \u2192 \u5361\u4F4F\u4E86
|
|
7848
7881
|
ELSE 'waiting'
|
|
7849
7882
|
END AS state
|
|
7850
7883
|
FROM ${schema}.workflow_nodes n
|
|
7851
|
-
LEFT JOIN ${schema}.works lw ON lw.id = n.latest_work_id
|
|
7884
|
+
LEFT JOIN ${schema}.works lw ON lw.id = n.latest_work_id
|
|
7885
|
+
LEFT JOIN ${schema}.works ls ON ls.id = n.latest_success_work_id;
|
|
7852
7886
|
|
|
7853
7887
|
DROP VIEW IF EXISTS ${schema}.workorder_view;
|
|
7854
7888
|
CREATE VIEW ${schema}.workorder_view AS
|
|
@@ -13128,6 +13162,7 @@ var init_store_postgres = __esm({
|
|
|
13128
13162
|
assigneeActorId: n.assignee_actor_id,
|
|
13129
13163
|
assigneeRole: n.assignee_role,
|
|
13130
13164
|
latestWorkId: n.latest_work_id,
|
|
13165
|
+
latestSuccessWorkId: n.latest_success_work_id ?? null,
|
|
13131
13166
|
latestAcceptId: n.latest_accept_id,
|
|
13132
13167
|
isFinalOutput: n.is_final_output,
|
|
13133
13168
|
cancelledAt: iso(n.cancelled_at),
|
|
@@ -13304,9 +13339,9 @@ var init_store_postgres = __esm({
|
|
|
13304
13339
|
await this.c.query(
|
|
13305
13340
|
`INSERT INTO ${this.s}.workflow_nodes
|
|
13306
13341
|
(id,workorder_id,type,title,spec,version,assignee_actor_id,assignee_role,
|
|
13307
|
-
latest_work_id,latest_accept_id,is_final_output,
|
|
13342
|
+
latest_work_id,latest_success_work_id,latest_accept_id,is_final_output,
|
|
13308
13343
|
fields,cancelled_at,row_version,created_at,updated_at)
|
|
13309
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
|
|
13344
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
|
|
13310
13345
|
ON CONFLICT (id) DO NOTHING`,
|
|
13311
13346
|
[
|
|
13312
13347
|
n.id,
|
|
@@ -13318,6 +13353,7 @@ var init_store_postgres = __esm({
|
|
|
13318
13353
|
n.assigneeActorId,
|
|
13319
13354
|
n.assigneeRole,
|
|
13320
13355
|
n.latestWorkId,
|
|
13356
|
+
n.latestSuccessWorkId,
|
|
13321
13357
|
n.latestAcceptId,
|
|
13322
13358
|
n.isFinalOutput,
|
|
13323
13359
|
n.fields ? JSON.stringify(n.fields) : null,
|
|
@@ -13586,11 +13622,14 @@ var init_store_postgres = __esm({
|
|
|
13586
13622
|
* `NOT EXISTS` 那一段是"我前面还有没跑完的兄弟就别动我";`SKIP LOCKED` 是
|
|
13587
13623
|
* "别的 worker 正在处理这张工单就跳过整张"。两者合起来 = 按工单分区的串行队列。
|
|
13588
13624
|
*/
|
|
13589
|
-
async claimNextEvent(now) {
|
|
13625
|
+
async claimNextEvent(now, recoverAppliedBefore) {
|
|
13590
13626
|
const res = await this.c.query(
|
|
13591
13627
|
`SELECT e.* FROM ${this.s}.events e
|
|
13592
|
-
WHERE
|
|
13593
|
-
|
|
13628
|
+
WHERE (
|
|
13629
|
+
(e.status = 'pending' AND (e.available_at IS NULL OR e.available_at <= $1))
|
|
13630
|
+
-- \u5D29\u6E83\u81EA\u6108\uFF1A\u5361\u5728 applied \u592A\u4E45\u7684\uFF08\u8FDB\u7A0B\u6B7B\u5728\u300C\u843D\u72B6\u6001\u300D\u4E0E\u300C\u6536\u5C3E\u300D\u4E4B\u95F4\uFF09\u4E5F\u56DE\u6536\u91CD\u8DD1
|
|
13631
|
+
OR ($2::timestamptz IS NOT NULL AND e.status = 'applied' AND e.applied_at < $2::timestamptz)
|
|
13632
|
+
)
|
|
13594
13633
|
AND NOT EXISTS (
|
|
13595
13634
|
SELECT 1 FROM ${this.s}.events p
|
|
13596
13635
|
WHERE p.workorder_id = e.workorder_id
|
|
@@ -13599,7 +13638,7 @@ var init_store_postgres = __esm({
|
|
|
13599
13638
|
ORDER BY e.seq
|
|
13600
13639
|
FOR UPDATE OF e SKIP LOCKED
|
|
13601
13640
|
LIMIT 1`,
|
|
13602
|
-
[now]
|
|
13641
|
+
[now, recoverAppliedBefore ?? null]
|
|
13603
13642
|
);
|
|
13604
13643
|
return res.rows[0] ? toRecord(res.rows[0]) : null;
|
|
13605
13644
|
}
|
|
@@ -13640,10 +13679,14 @@ var init_store_postgres = __esm({
|
|
|
13640
13679
|
);
|
|
13641
13680
|
return (res.rowCount ?? 0) > 0;
|
|
13642
13681
|
}
|
|
13643
|
-
async claimAllPending(now) {
|
|
13682
|
+
async claimAllPending(now, recoverAppliedBefore) {
|
|
13644
13683
|
const res = await this.c.query(
|
|
13645
13684
|
`SELECT e.* FROM ${this.s}.events e
|
|
13646
|
-
WHERE
|
|
13685
|
+
WHERE (
|
|
13686
|
+
(e.status = 'pending' AND (e.available_at IS NULL OR e.available_at <= $1))
|
|
13687
|
+
-- \u5D29\u6E83\u81EA\u6108\uFF1A\u89C1 claimNextEvent \u7684\u6CE8\u91CA
|
|
13688
|
+
OR ($2::timestamptz IS NOT NULL AND e.status = 'applied' AND e.applied_at < $2::timestamptz)
|
|
13689
|
+
)
|
|
13647
13690
|
AND NOT EXISTS (SELECT 1 FROM ${this.s}.events p WHERE p.workorder_id = e.workorder_id
|
|
13648
13691
|
AND p.status IN ('pending','applied') AND p.seq < e.seq)
|
|
13649
13692
|
-- \u2605 scan \u4E8B\u4EF6\u6392\u524D\u9762\uFF1Aorigin='apply' \u4E14\u65E0\u4E0A\u6E38\u4E8B\u4EF6\uFF08\u4E0D\u662F\u88AB\u5176\u4ED6\u4E8B\u4EF6\u89E6\u53D1\u7684\uFF09
|
|
@@ -13653,7 +13696,7 @@ var init_store_postgres = __esm({
|
|
|
13653
13696
|
CASE WHEN e.origin = 'apply' AND e.caused_by_seq IS NULL THEN 0 ELSE 1 END,
|
|
13654
13697
|
e.seq
|
|
13655
13698
|
FOR UPDATE OF e SKIP LOCKED`,
|
|
13656
|
-
[now]
|
|
13699
|
+
[now, recoverAppliedBefore ?? null]
|
|
13657
13700
|
);
|
|
13658
13701
|
return res.rows.map(toRecord);
|
|
13659
13702
|
}
|
|
@@ -13810,6 +13853,7 @@ function newNode(id, workorderId, at, version2) {
|
|
|
13810
13853
|
assigneeActorId: null,
|
|
13811
13854
|
assigneeRole: null,
|
|
13812
13855
|
latestWorkId: null,
|
|
13856
|
+
latestSuccessWorkId: null,
|
|
13813
13857
|
latestAcceptId: null,
|
|
13814
13858
|
isFinalOutput: false,
|
|
13815
13859
|
cancelledAt: null,
|
|
@@ -14138,6 +14182,10 @@ var init_workorder = __esm({
|
|
|
14138
14182
|
});
|
|
14139
14183
|
|
|
14140
14184
|
// ../engine/src/handlers/work.ts
|
|
14185
|
+
function latestSuccessOf(state, nodeId) {
|
|
14186
|
+
const successes = state.worksOf(nodeId).filter((w2) => !w2.replyToIssueId && w2.status === "success").sort((a, b2) => a.createdAt.localeCompare(b2.createdAt));
|
|
14187
|
+
return successes.length > 0 ? successes[successes.length - 1].id : null;
|
|
14188
|
+
}
|
|
14141
14189
|
var workCreate, workKill, workResponse, workHandoffRecorded, workStart, workSubmitOutput, workTimeout;
|
|
14142
14190
|
var init_work = __esm({
|
|
14143
14191
|
"../engine/src/handlers/work.ts"() {
|
|
@@ -14267,6 +14315,12 @@ var init_work = __esm({
|
|
|
14267
14315
|
...w2.status !== "dead" ? { status: "dead" } : {}
|
|
14268
14316
|
});
|
|
14269
14317
|
}
|
|
14318
|
+
if (node.latestSuccessWorkId) {
|
|
14319
|
+
const ls = state.work(node.latestSuccessWorkId);
|
|
14320
|
+
if (!ls || ls.status !== "success") {
|
|
14321
|
+
state.updateNode(e.nodeId, { latestSuccessWorkId: latestSuccessOf(state, e.nodeId) });
|
|
14322
|
+
}
|
|
14323
|
+
}
|
|
14270
14324
|
killNodeReviews(state, e.nodeId, ctx.at);
|
|
14271
14325
|
},
|
|
14272
14326
|
async effect(e, ctx) {
|
|
@@ -14300,6 +14354,9 @@ var init_work = __esm({
|
|
|
14300
14354
|
outcomeDetail: businessFailed ? { ...e.detail ?? {}, reason: "agent business handoff not delivered" } : e.detail,
|
|
14301
14355
|
status
|
|
14302
14356
|
});
|
|
14357
|
+
if (status === "success") {
|
|
14358
|
+
state.updateNode(w2.nodeId, { latestSuccessWorkId: e.workId });
|
|
14359
|
+
}
|
|
14303
14360
|
}
|
|
14304
14361
|
};
|
|
14305
14362
|
workHandoffRecorded = {
|
|
@@ -14309,17 +14366,20 @@ var init_work = __esm({
|
|
|
14309
14366
|
const w2 = state.work(e.workId);
|
|
14310
14367
|
if (!w2 || e.producedBy !== "agent") return;
|
|
14311
14368
|
if (w2.agentHandoffAttemptId && w2.agentHandoffAttemptId !== e.attemptId) return;
|
|
14369
|
+
const flipsToFailed = e.status === "not_delivered" && w2.endedAt && !w2.deadAt;
|
|
14312
14370
|
state.updateWork(e.workId, {
|
|
14313
14371
|
agentHandoffAttemptId: e.attemptId,
|
|
14314
14372
|
businessHandoffStatus: e.status,
|
|
14315
14373
|
businessHandoffRecordedAt: ctx.at,
|
|
14316
|
-
|
|
14317
|
-
...e.status === "not_delivered" && w2.endedAt && !w2.deadAt ? {
|
|
14374
|
+
...flipsToFailed ? {
|
|
14318
14375
|
status: "failed",
|
|
14319
14376
|
outcome: "failed",
|
|
14320
14377
|
outcomeDetail: { ...w2.outcomeDetail ?? {}, reason: "agent business handoff not delivered" }
|
|
14321
14378
|
} : {}
|
|
14322
14379
|
});
|
|
14380
|
+
if (flipsToFailed && state.node(w2.nodeId)?.latestSuccessWorkId === e.workId) {
|
|
14381
|
+
state.updateNode(w2.nodeId, { latestSuccessWorkId: latestSuccessOf(state, w2.nodeId) });
|
|
14382
|
+
}
|
|
14323
14383
|
}
|
|
14324
14384
|
};
|
|
14325
14385
|
workStart = {
|
|
@@ -14865,7 +14925,7 @@ function emptySnapshotPlaceholder(record8) {
|
|
|
14865
14925
|
reviews: []
|
|
14866
14926
|
};
|
|
14867
14927
|
}
|
|
14868
|
-
var SYSTEM_ACTOR2, NOOP_IO, SYSTEM_CLOCK, HandlerRegistry, EngineBus, ApplyFailure, sleep;
|
|
14928
|
+
var SYSTEM_ACTOR2, NOOP_IO, SYSTEM_CLOCK, HandlerRegistry, DEFAULT_STUCK_APPLIED_RECOVERY_MS, EngineBus, ApplyFailure, sleep;
|
|
14869
14929
|
var init_bus = __esm({
|
|
14870
14930
|
"../engine/src/bus.ts"() {
|
|
14871
14931
|
"use strict";
|
|
@@ -14903,6 +14963,7 @@ var init_bus = __esm({
|
|
|
14903
14963
|
return [...this.byKind.values()].flat();
|
|
14904
14964
|
}
|
|
14905
14965
|
};
|
|
14966
|
+
DEFAULT_STUCK_APPLIED_RECOVERY_MS = 5 * 6e4;
|
|
14906
14967
|
EngineBus = class {
|
|
14907
14968
|
store;
|
|
14908
14969
|
io;
|
|
@@ -14911,6 +14972,7 @@ var init_bus = __esm({
|
|
|
14911
14972
|
policy;
|
|
14912
14973
|
registry;
|
|
14913
14974
|
maxEffectAttempts;
|
|
14975
|
+
stuckAppliedRecoveryMs;
|
|
14914
14976
|
running = false;
|
|
14915
14977
|
/** 事件监听器(内存 pubsub):每批事件处理完调用,供 /api/events SSE 推送(替代旧 oplog.subscribe)。 */
|
|
14916
14978
|
eventListener = null;
|
|
@@ -14922,10 +14984,19 @@ var init_bus = __esm({
|
|
|
14922
14984
|
this.policy = opts.policy ?? CODE_DEFAULT_POLICY;
|
|
14923
14985
|
this.registry = new HandlerRegistry(opts.handlers);
|
|
14924
14986
|
this.maxEffectAttempts = opts.maxEffectAttempts ?? 3;
|
|
14987
|
+
this.stuckAppliedRecoveryMs = opts.stuckAppliedRecoveryMs ?? DEFAULT_STUCK_APPLIED_RECOVERY_MS;
|
|
14925
14988
|
}
|
|
14926
14989
|
setIO(io) {
|
|
14927
14990
|
this.io = io;
|
|
14928
14991
|
}
|
|
14992
|
+
/**
|
|
14993
|
+
* 崩溃自愈水位线:早于此刻仍停在 `applied` 的事件,认为是「进程死在落状态与收尾之间」的残留,
|
|
14994
|
+
* 回收重跑。`stuckAppliedRecoveryMs <= 0` 时返回 undefined(= 关闭自愈,维持旧行为)。
|
|
14995
|
+
*/
|
|
14996
|
+
recoverAppliedBefore() {
|
|
14997
|
+
if (this.stuckAppliedRecoveryMs <= 0) return void 0;
|
|
14998
|
+
return new Date(Date.parse(this.clock.now()) - this.stuckAppliedRecoveryMs).toISOString();
|
|
14999
|
+
}
|
|
14929
15000
|
/** 注册事件监听器——新事件处理完即回调(serve 桥接 SSE 实时推送,替代 oplog.subscribe)。返回退订。 */
|
|
14930
15001
|
onEvent(fn) {
|
|
14931
15002
|
this.eventListener = fn;
|
|
@@ -15014,7 +15085,7 @@ var init_bus = __esm({
|
|
|
15014
15085
|
let lastFullScan = 0;
|
|
15015
15086
|
while (true) {
|
|
15016
15087
|
try {
|
|
15017
|
-
const batch = await this.store.transaction((tx) => tx.claimAllPending(this.clock.now()));
|
|
15088
|
+
const batch = await this.store.transaction((tx) => tx.claimAllPending(this.clock.now(), this.recoverAppliedBefore()));
|
|
15018
15089
|
const touched = /* @__PURE__ */ new Set();
|
|
15019
15090
|
if (batch.length > 0) {
|
|
15020
15091
|
for (const record8 of batch) {
|
|
@@ -15247,6 +15318,10 @@ var init_bus = __esm({
|
|
|
15247
15318
|
};
|
|
15248
15319
|
let ok = true;
|
|
15249
15320
|
for (const h of handlers) {
|
|
15321
|
+
const effectKey = `${h.name}#effect`;
|
|
15322
|
+
if (record8.appliedAt && await this.store.transaction((tx) => tx.isHandlerDone(record8.seq, effectKey))) {
|
|
15323
|
+
continue;
|
|
15324
|
+
}
|
|
15250
15325
|
let lastErr = "";
|
|
15251
15326
|
for (let attempt = 1; attempt <= this.maxEffectAttempts; attempt++) {
|
|
15252
15327
|
try {
|
|
@@ -15259,6 +15334,8 @@ var init_bus = __esm({
|
|
|
15259
15334
|
}
|
|
15260
15335
|
if (lastErr) {
|
|
15261
15336
|
ok = false;
|
|
15337
|
+
} else {
|
|
15338
|
+
await this.store.transaction((tx) => tx.markHandlerDone(record8.seq, effectKey));
|
|
15262
15339
|
}
|
|
15263
15340
|
}
|
|
15264
15341
|
await this.store.transaction(async (tx) => {
|
|
@@ -15317,7 +15394,7 @@ var init_bus = __esm({
|
|
|
15317
15394
|
/* ═══════════════════════ 内部:旧 applyPhase(claim + apply 一体) ═══════════════════════ */
|
|
15318
15395
|
async applyPhase() {
|
|
15319
15396
|
return this.store.transaction(async (tx) => {
|
|
15320
|
-
const record8 = await tx.claimNextEvent(this.clock.now());
|
|
15397
|
+
const record8 = await tx.claimNextEvent(this.clock.now(), this.recoverAppliedBefore());
|
|
15321
15398
|
if (!record8) return null;
|
|
15322
15399
|
const snapshot = await tx.loadWorkorder(record8.workorderId);
|
|
15323
15400
|
if (snapshot?.workorder.doneAt) {
|
|
@@ -15442,6 +15519,10 @@ function mapWorkStateToRevisionState(w2) {
|
|
|
15442
15519
|
if (w2.acceptanceState === "rejected") return "rejected";
|
|
15443
15520
|
return "proposed";
|
|
15444
15521
|
// success but not yet judged
|
|
15522
|
+
// ★ ADR 0148:failed 尝试没有对应的 RevisionState(枚举只有 working/proposed/merged/rejected/
|
|
15523
|
+
// superseded,改枚举会牵动契约与前端,见红线 4)——仍投影成 superseded 保持生命周期语义,但**内容
|
|
15524
|
+
// 如实为 empty**(workToRevision 对无产出 work 给 contentKind="empty")。读面(CLI 版本列表)据此把
|
|
15525
|
+
// 「superseded + empty」如实标成「未产出」,不再让「这次失败了」被读成「被更好的版本取代」。
|
|
15445
15526
|
case "failed":
|
|
15446
15527
|
return "superseded";
|
|
15447
15528
|
case "retry":
|
|
@@ -15711,8 +15792,16 @@ function bridgeLoadReadModel(input) {
|
|
|
15711
15792
|
const lw = node.latestWorkId ? workById.get(node.latestWorkId) : void 0;
|
|
15712
15793
|
if (lw) {
|
|
15713
15794
|
const st = lw.status ?? (lw.retryAt ? "retry" : lw.deadAt ? "dead" : lw.endedAt ? lw.outcome === "failed" ? "failed" : "success" : "running");
|
|
15714
|
-
|
|
15715
|
-
|
|
15795
|
+
let uiSt;
|
|
15796
|
+
if (st === "retry") uiSt = null;
|
|
15797
|
+
else if (st === "running") uiSt = "running";
|
|
15798
|
+
else if (node.latestSuccessWorkId) uiSt = "success";
|
|
15799
|
+
else uiSt = st;
|
|
15800
|
+
art.latestWorkState = uiSt;
|
|
15801
|
+
art.latestAccepted = node.latestSuccessWorkId !== null && node.latestAcceptId === node.latestSuccessWorkId;
|
|
15802
|
+
if ((st === "failed" || st === "dead") && node.latestSuccessWorkId) {
|
|
15803
|
+
art.currentRev = node.latestSuccessWorkId;
|
|
15804
|
+
}
|
|
15716
15805
|
} else {
|
|
15717
15806
|
art.latestWorkState = null;
|
|
15718
15807
|
art.latestAccepted = false;
|
|
@@ -15902,6 +15991,26 @@ var init_bridge_readmodel = __esm({
|
|
|
15902
15991
|
}
|
|
15903
15992
|
});
|
|
15904
15993
|
|
|
15994
|
+
// ../core/src/propose-guard.ts
|
|
15995
|
+
function proposeBlock(head, headArtifactCount) {
|
|
15996
|
+
if (!head) return null;
|
|
15997
|
+
const state = workState(head, hasOutput(head, headArtifactCount));
|
|
15998
|
+
if (state === "dead") {
|
|
15999
|
+
const why = head.rejectedReason ? `\uFF1A${head.rejectedReason}` : "";
|
|
16000
|
+
return {
|
|
16001
|
+
code: "head-dead",
|
|
16002
|
+
detail: `\u8FD9\u4E00\u7248\uFF08${head.id}\uFF09\u5DF2\u88AB\u505C\u6389\uFF08${head.deadAt ?? "?"}${why}\uFF09\u2014\u2014\u4F5C\u5E9F\u7248\u4E4B\u4E0A\u4E0D\u80FD\u518D\u4EA4\u7A3F\u3002\u8981\u7EE7\u7EED\uFF0C\u5148\u91CD\u8DD1\u672C\u8282\u70B9\uFF08\u5728\u63A7\u5236\u53F0\u70B9\u300C\u91CD\u65B0\u8FD0\u884C\u300D\uFF0C\u6216\u8BA9\u7BA1\u7406\u8005\u91CD\u6D3E\uFF09\u62FF\u5230\u4E00\u4E2A\u53EF\u5199\u7684\u65B0\u7248\u672C\uFF0C\u518D propose\u3002`
|
|
16003
|
+
};
|
|
16004
|
+
}
|
|
16005
|
+
return null;
|
|
16006
|
+
}
|
|
16007
|
+
var init_propose_guard = __esm({
|
|
16008
|
+
"../core/src/propose-guard.ts"() {
|
|
16009
|
+
"use strict";
|
|
16010
|
+
init_src3();
|
|
16011
|
+
}
|
|
16012
|
+
});
|
|
16013
|
+
|
|
15905
16014
|
// ../core/src/preview-intervention.ts
|
|
15906
16015
|
function resolveOwner(type, typeDef, roleIndex, schemaSize) {
|
|
15907
16016
|
if (!typeDef) {
|
|
@@ -16206,7 +16315,11 @@ function previewIntervention(model, plan, config2) {
|
|
|
16206
16315
|
if (lifecycleOf(fork, a.id) !== "active") continue;
|
|
16207
16316
|
for (const e of a.inputs) {
|
|
16208
16317
|
if (lifecycleOf(fork, e.to) === "sealed:cancelled") {
|
|
16209
|
-
|
|
16318
|
+
const consumerWs = fork.artifacts.get(a.id)?.workspace;
|
|
16319
|
+
const cancelledWs = fork.artifacts.get(e.to)?.workspace;
|
|
16320
|
+
violations.push(
|
|
16321
|
+
`\u4F9D\u8D56\u5E9F\u5F03\u8282\u70B9: ${a.id}${consumerWs ? `\uFF08${consumerWs}\uFF09` : ""} \u2192 ${e.to}${cancelledWs ? `\uFF08${cancelledWs}\uFF09` : ""}\uFF08cancelled \u4E0D\u53EF\u88AB\u4F9D\u8D56\uFF09`
|
|
16322
|
+
);
|
|
16210
16323
|
}
|
|
16211
16324
|
}
|
|
16212
16325
|
}
|
|
@@ -16367,6 +16480,7 @@ var init_kernel_bridge = __esm({
|
|
|
16367
16480
|
init_closure_gate();
|
|
16368
16481
|
init_kernel();
|
|
16369
16482
|
init_bridge_readmodel();
|
|
16483
|
+
init_propose_guard();
|
|
16370
16484
|
init_src3();
|
|
16371
16485
|
init_src3();
|
|
16372
16486
|
init_preview_intervention();
|
|
@@ -16479,26 +16593,26 @@ var init_kernel_bridge = __esm({
|
|
|
16479
16593
|
convergenceUnified: this.model.convergenceUnified,
|
|
16480
16594
|
disabledActors: this.model.disabledActors
|
|
16481
16595
|
});
|
|
16482
|
-
for (const [draftId,
|
|
16483
|
-
const anchorFromPlan =
|
|
16484
|
-
const anchorId =
|
|
16485
|
-
const cardWs =
|
|
16596
|
+
for (const [draftId, card2] of this.draftCards) {
|
|
16597
|
+
const anchorFromPlan = card2.plan?.ops.find((o) => "artifactId" in o && o.artifactId) ?? card2.plan?.ops.find((o) => "id" in o && o.id);
|
|
16598
|
+
const anchorId = card2.anchor ?? (anchorFromPlan ? anchorFromPlan.artifactId ?? anchorFromPlan.id : void 0) ?? "";
|
|
16599
|
+
const cardWs = card2.plan?.workspace ?? this.model.artifacts.get(anchorId)?.workspace ?? card2.anchor ?? "";
|
|
16486
16600
|
fresh.pendingReviews.set(draftId, {
|
|
16487
16601
|
draftId,
|
|
16488
|
-
plan:
|
|
16489
|
-
changeClass:
|
|
16490
|
-
nonMonotonicOps:
|
|
16491
|
-
proposedBy:
|
|
16492
|
-
origin:
|
|
16602
|
+
plan: card2.plan ?? { workspace: cardWs, reason: "", ops: [] },
|
|
16603
|
+
changeClass: card2.changeClass ?? "B",
|
|
16604
|
+
nonMonotonicOps: card2.nonMonotonicOps ?? [],
|
|
16605
|
+
proposedBy: card2.proposedBy,
|
|
16606
|
+
origin: card2.origin ?? null,
|
|
16493
16607
|
workspace: cardWs,
|
|
16494
16608
|
advisory: [],
|
|
16495
16609
|
downstreamClosure: [],
|
|
16496
16610
|
at: "",
|
|
16497
16611
|
// ★ anchor 也兜底到 plan ops 的产物 id——inbox change-confirm 用 r.anchor 找「anchor owner 的
|
|
16498
16612
|
// 人类上级」(leadOf(r.anchor));graph-edit 卡之前恒空、boss 收不到卡(collab-inbox-audit 实测)。
|
|
16499
|
-
anchor: anchorId ||
|
|
16500
|
-
kind:
|
|
16501
|
-
...
|
|
16613
|
+
anchor: anchorId || card2.anchor || "",
|
|
16614
|
+
kind: card2.kind,
|
|
16615
|
+
...card2.command ? { command: { command: card2.command, args: card2.commandArgs ?? {}, effectLabel: card2.effectLabel ?? "" } } : {}
|
|
16502
16616
|
});
|
|
16503
16617
|
}
|
|
16504
16618
|
Object.assign(this.model, fresh);
|
|
@@ -16596,8 +16710,8 @@ var init_kernel_bridge = __esm({
|
|
|
16596
16710
|
typeName: a.type
|
|
16597
16711
|
};
|
|
16598
16712
|
}
|
|
16599
|
-
cancelCascadeEdges(
|
|
16600
|
-
return
|
|
16713
|
+
cancelCascadeEdges(id) {
|
|
16714
|
+
return edgesTouching(this.model, id).map((e) => ({ from: e.artifactId, to: e.to }));
|
|
16601
16715
|
}
|
|
16602
16716
|
/* ═══════════════════════ 写方法 ═══════════════════════ */
|
|
16603
16717
|
// ── 建节点 ──
|
|
@@ -16697,8 +16811,14 @@ var init_kernel_bridge = __esm({
|
|
|
16697
16811
|
}
|
|
16698
16812
|
}
|
|
16699
16813
|
if (!workId) {
|
|
16700
|
-
const
|
|
16701
|
-
|
|
16814
|
+
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
16815
|
+
const headId = snap?.nodes.find((n) => n.id === args.artifactId)?.latestWorkId ?? null;
|
|
16816
|
+
if (headId) {
|
|
16817
|
+
const headWork = snap?.works.find((w2) => w2.id === headId);
|
|
16818
|
+
const headOutputs = snap?.artifacts.filter((a) => a.workId === headId).length ?? 0;
|
|
16819
|
+
const block = proposeBlock(headWork, headOutputs);
|
|
16820
|
+
if (block) throw new KernelError(block.detail, block.code);
|
|
16821
|
+
}
|
|
16702
16822
|
const worksCount = [...this.model.revisions.values()].filter((r) => r.artifactId === args.artifactId).length;
|
|
16703
16823
|
const revisionId = `wk:${args.artifactId}:${worksCount + 1}`;
|
|
16704
16824
|
await this.commit({
|
|
@@ -16974,7 +17094,8 @@ var init_kernel_bridge = __esm({
|
|
|
16974
17094
|
const artifact = this.model.artifacts.get(args.artifactId);
|
|
16975
17095
|
const currentRev = artifact?.currentRev ? this.model.revisions.get(artifact.currentRev) : void 0;
|
|
16976
17096
|
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
16977
|
-
const
|
|
17097
|
+
const targetNode = snap?.nodes.find((n) => n.id === args.artifactId);
|
|
17098
|
+
const head = (currentRev && currentRev.state === "proposed" ? currentRev.id : null) ?? concludedHead(this.model, args.artifactId) ?? (targetNode?.latestSuccessWorkId ?? targetNode?.latestWorkId ?? null);
|
|
16978
17099
|
if (!head) throw new KernelError(`force-conclude \u65E0\u4ECE\u4E0B\u624B\uFF08${args.artifactId}\uFF09\uFF1A\u672C\u8282\u70B9\u8FD8\u6CA1\u6709\u4EFB\u4F55\u4E00\u7248\u4EA7\u51FA`, "no-head");
|
|
16979
17100
|
const block = snap ? acceptBlock(new WorkorderState(snap), head) : null;
|
|
16980
17101
|
if (block?.code === "already-accepted") return;
|
|
@@ -17326,6 +17447,13 @@ var init_kernel_bridge = __esm({
|
|
|
17326
17447
|
}
|
|
17327
17448
|
async seal(args) {
|
|
17328
17449
|
const wid = this.wo(args.artifactId);
|
|
17450
|
+
if (args.reason === "cancelled") {
|
|
17451
|
+
const preserve = args.preserveEdgesAmong;
|
|
17452
|
+
for (const e of edgesTouching(this.model, args.artifactId)) {
|
|
17453
|
+
if (preserve && preserve.has(e.artifactId) && preserve.has(e.to)) continue;
|
|
17454
|
+
await this.unlinkInput({ artifactId: e.artifactId, to: e.to, actor: args.actor });
|
|
17455
|
+
}
|
|
17456
|
+
}
|
|
17329
17457
|
await this.commit({
|
|
17330
17458
|
companyId: "",
|
|
17331
17459
|
workorderId: wid,
|
|
@@ -17521,10 +17649,10 @@ var init_kernel_bridge = __esm({
|
|
|
17521
17649
|
async proposeCommandAuthorization(args) {
|
|
17522
17650
|
const originNorm = args.origin ?? null;
|
|
17523
17651
|
const argsJson = JSON.stringify(args.args ?? {});
|
|
17524
|
-
for (const [id,
|
|
17525
|
-
if (
|
|
17526
|
-
if ((
|
|
17527
|
-
if (JSON.stringify(
|
|
17652
|
+
for (const [id, card2] of this.draftCards) {
|
|
17653
|
+
if (card2.kind !== "command" || card2.command !== args.command) continue;
|
|
17654
|
+
if ((card2.origin ?? null) !== originNorm) continue;
|
|
17655
|
+
if (JSON.stringify(card2.commandArgs ?? {}) !== argsJson) continue;
|
|
17528
17656
|
await this.refreshModel();
|
|
17529
17657
|
return { draftId: id };
|
|
17530
17658
|
}
|
|
@@ -17542,23 +17670,23 @@ var init_kernel_bridge = __esm({
|
|
|
17542
17670
|
return { draftId };
|
|
17543
17671
|
}
|
|
17544
17672
|
async applyAuthorizedCommand(args) {
|
|
17545
|
-
const
|
|
17546
|
-
if (!
|
|
17673
|
+
const card2 = this.draftCards.get(args.draftId);
|
|
17674
|
+
if (!card2 || card2.kind !== "command" || !card2.command) {
|
|
17547
17675
|
throw new Error(`\u4E0D\u662F\u547D\u4EE4\u6388\u6743\u8BF7\u6C42\u6216\u5DF2\u5904\u7406\uFF1A${args.draftId}`);
|
|
17548
17676
|
}
|
|
17549
|
-
const artifactId =
|
|
17550
|
-
if (
|
|
17677
|
+
const artifactId = card2.anchor;
|
|
17678
|
+
if (card2.command === "forceConclude") {
|
|
17551
17679
|
await this.forceConclude({
|
|
17552
17680
|
artifactId,
|
|
17553
17681
|
actor: args.actor,
|
|
17554
|
-
reason: typeof
|
|
17682
|
+
reason: typeof card2.commandArgs?.["reason"] === "string" ? card2.commandArgs["reason"] : "\u7BA1\u7406\u8005\u8BF7\u6C42\u3001\u4EBA\u5DF2\u6388\u6743"
|
|
17555
17683
|
});
|
|
17556
|
-
} else if (
|
|
17557
|
-
const escalationId = typeof
|
|
17684
|
+
} else if (card2.command === "resolveEscalation") {
|
|
17685
|
+
const escalationId = typeof card2.commandArgs?.["escalationId"] === "string" ? card2.commandArgs["escalationId"] : void 0;
|
|
17558
17686
|
if (!escalationId) {
|
|
17559
17687
|
throw new Error(`resolveEscalation \u6388\u6743\u5361\u7F3A escalationId\uFF1A${args.draftId}`);
|
|
17560
17688
|
}
|
|
17561
|
-
const reason = typeof
|
|
17689
|
+
const reason = typeof card2.commandArgs?.["reason"] === "string" ? card2.commandArgs["reason"] : void 0;
|
|
17562
17690
|
await this.resolveEscalation({
|
|
17563
17691
|
artifactId,
|
|
17564
17692
|
actor: args.actor,
|
|
@@ -17566,23 +17694,39 @@ var init_kernel_bridge = __esm({
|
|
|
17566
17694
|
...reason !== void 0 ? { reason } : {}
|
|
17567
17695
|
});
|
|
17568
17696
|
} else {
|
|
17569
|
-
throw new Error(`\u547D\u4EE4\u6388\u6743\uFF1A\u6682\u4E0D\u652F\u6301\u6267\u884C ${
|
|
17697
|
+
throw new Error(`\u547D\u4EE4\u6388\u6743\uFF1A\u6682\u4E0D\u652F\u6301\u6267\u884C ${card2.command}`);
|
|
17570
17698
|
}
|
|
17571
17699
|
this.draftCards.delete(args.draftId);
|
|
17572
17700
|
await this.refreshModel();
|
|
17573
|
-
return { command:
|
|
17701
|
+
return { command: card2.command, artifactId };
|
|
17574
17702
|
}
|
|
17575
17703
|
async rejectIntervention(args) {
|
|
17576
17704
|
this.draftCards.delete(args.draftId);
|
|
17577
17705
|
await this.refreshModel();
|
|
17578
17706
|
}
|
|
17707
|
+
/**
|
|
17708
|
+
* 有效 workspace:优先 plan.workspace,缺省时从首个带 workspace 的 spawn op 兜底(与
|
|
17709
|
+
* applyIntervention 里 wid 的兜底同源,见下方 line ~1453)。
|
|
17710
|
+
* ★ 诊断 9f589b81 §② B:建单方案(buildDynamicWorkorderPlan)曾漏传顶层 plan.workspace,导致
|
|
17711
|
+
* preview 的 workspace 过滤短路、拿全公司读模型跑终态校验——别家工单里一条僵尸边就能拒掉无关建单
|
|
17712
|
+
* (事故半径=全组织)。planner 已补 plan.workspace(根治),此兜底再保一层:只要 plan 里有带
|
|
17713
|
+
* workspace 的 spawn op,就绝不静默 fallback 到全公司。两者皆缺才回退全模型(如纯 seal/unlink 单节点
|
|
17714
|
+
* 改图,此时 wo() 反查也能定位,全局校验只是更宽、不误伤建单)。
|
|
17715
|
+
*/
|
|
17716
|
+
effectiveWorkspace(plan) {
|
|
17717
|
+
if (plan.workspace) return plan.workspace;
|
|
17718
|
+
const spawn8 = plan.ops.find(
|
|
17719
|
+
(o) => o.action === "spawn" && o.workspace
|
|
17720
|
+
);
|
|
17721
|
+
return spawn8?.workspace;
|
|
17722
|
+
}
|
|
17579
17723
|
async previewIntervention(plan) {
|
|
17580
|
-
const ws = plan
|
|
17724
|
+
const ws = this.effectiveWorkspace(plan);
|
|
17581
17725
|
const scopedModel = ws ? filterReadModelByWorkspace(this.model, ws) : this.model;
|
|
17582
17726
|
return previewIntervention(scopedModel, plan, { schema: this.schema, roleIndex: this.roleIndex });
|
|
17583
17727
|
}
|
|
17584
17728
|
async applyIntervention(plan, _baseSeqs, _actor) {
|
|
17585
|
-
const ws = plan
|
|
17729
|
+
const ws = this.effectiveWorkspace(plan);
|
|
17586
17730
|
const scopedModel = ws ? filterReadModelByWorkspace(this.model, ws) : this.model;
|
|
17587
17731
|
plan = resolveInterventionRefs(scopedModel, plan, this.schema);
|
|
17588
17732
|
const preview2 = await this.previewIntervention(plan);
|
|
@@ -18128,9 +18272,9 @@ var init_crypto = __esm({
|
|
|
18128
18272
|
function buildTextCard(text, opts) {
|
|
18129
18273
|
const elements = [{ tag: "div", text: { tag: "lark_md", content: text || " " } }];
|
|
18130
18274
|
if (opts?.footer) elements.push({ tag: "note", elements: [{ tag: "lark_md", content: opts.footer }] });
|
|
18131
|
-
const
|
|
18132
|
-
if (opts?.header)
|
|
18133
|
-
return
|
|
18275
|
+
const card2 = { config: { wide_screen_mode: true, update_multi: true }, elements };
|
|
18276
|
+
if (opts?.header) card2["header"] = { title: { tag: "plain_text", content: opts.header } };
|
|
18277
|
+
return card2;
|
|
18134
18278
|
}
|
|
18135
18279
|
var BASE, FeishuApiClient;
|
|
18136
18280
|
var init_api = __esm({
|
|
@@ -18209,26 +18353,26 @@ var init_api = __esm({
|
|
|
18209
18353
|
};
|
|
18210
18354
|
}
|
|
18211
18355
|
/** 发一张 interactive 卡片,返回 message_id(后续 patch 同一条消息实现流式更新)。 */
|
|
18212
|
-
async sendCard(chatId,
|
|
18356
|
+
async sendCard(chatId, card2) {
|
|
18213
18357
|
const token = await this.tenantAccessToken();
|
|
18214
18358
|
const res = await this.fetchImpl(`${this.base}/open-apis/im/v1/messages?receive_id_type=chat_id`, {
|
|
18215
18359
|
method: "POST",
|
|
18216
18360
|
headers: { "content-type": "application/json; charset=utf-8", authorization: `Bearer ${token}` },
|
|
18217
|
-
body: JSON.stringify({ receive_id: chatId, msg_type: "interactive", content: JSON.stringify(
|
|
18361
|
+
body: JSON.stringify({ receive_id: chatId, msg_type: "interactive", content: JSON.stringify(card2) })
|
|
18218
18362
|
});
|
|
18219
18363
|
const body = await res.json();
|
|
18220
18364
|
if (body.code !== 0) throw new Error(`feishu send card failed: code=${body.code} msg=${body.msg ?? ""}`);
|
|
18221
18365
|
return { messageId: body.data?.message_id ?? "" };
|
|
18222
18366
|
}
|
|
18223
18367
|
/** 就地更新一张已发出的 interactive 卡片(流式回复靠反复 patch 同一条消息)。 */
|
|
18224
|
-
async patchCard(messageId,
|
|
18368
|
+
async patchCard(messageId, card2) {
|
|
18225
18369
|
const token = await this.tenantAccessToken();
|
|
18226
18370
|
const res = await this.fetchImpl(
|
|
18227
18371
|
`${this.base}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}`,
|
|
18228
18372
|
{
|
|
18229
18373
|
method: "PATCH",
|
|
18230
18374
|
headers: { "content-type": "application/json; charset=utf-8", authorization: `Bearer ${token}` },
|
|
18231
|
-
body: JSON.stringify({ content: JSON.stringify(
|
|
18375
|
+
body: JSON.stringify({ content: JSON.stringify(card2) })
|
|
18232
18376
|
}
|
|
18233
18377
|
);
|
|
18234
18378
|
const body = await res.json();
|
|
@@ -18671,11 +18815,11 @@ var init_service = __esm({
|
|
|
18671
18815
|
this.dirty = false;
|
|
18672
18816
|
const snapshot = this.buf;
|
|
18673
18817
|
try {
|
|
18674
|
-
const
|
|
18818
|
+
const card2 = buildTextCard(snapshot, { footer: "\u25CF \u751F\u6210\u4E2D\u2026" });
|
|
18675
18819
|
if (!this.messageId) {
|
|
18676
|
-
this.messageId = (await this.client.sendCard(this.chatId,
|
|
18820
|
+
this.messageId = (await this.client.sendCard(this.chatId, card2)).messageId;
|
|
18677
18821
|
} else {
|
|
18678
|
-
await this.client.patchCard(this.messageId,
|
|
18822
|
+
await this.client.patchCard(this.messageId, card2);
|
|
18679
18823
|
}
|
|
18680
18824
|
} catch (e) {
|
|
18681
18825
|
this.log(`feishu card stream tick failed: ${errMsg(e)}`);
|
|
@@ -18689,10 +18833,10 @@ var init_service = __esm({
|
|
|
18689
18833
|
clearInterval(this.timer);
|
|
18690
18834
|
this.timer = null;
|
|
18691
18835
|
}
|
|
18692
|
-
const
|
|
18836
|
+
const card2 = buildTextCard(this.buf || "\uFF08\u65E0\u8F93\u51FA\uFF09");
|
|
18693
18837
|
try {
|
|
18694
|
-
if (!this.messageId) await this.client.sendCard(this.chatId,
|
|
18695
|
-
else await this.client.patchCard(this.messageId,
|
|
18838
|
+
if (!this.messageId) await this.client.sendCard(this.chatId, card2);
|
|
18839
|
+
else await this.client.patchCard(this.messageId, card2);
|
|
18696
18840
|
} catch (e) {
|
|
18697
18841
|
this.log(`feishu card finalize failed: ${errMsg(e)}`);
|
|
18698
18842
|
}
|
|
@@ -18703,10 +18847,10 @@ var init_service = __esm({
|
|
|
18703
18847
|
clearInterval(this.timer);
|
|
18704
18848
|
this.timer = null;
|
|
18705
18849
|
}
|
|
18706
|
-
const
|
|
18850
|
+
const card2 = buildTextCard(`${this.buf ? this.buf + "\n\n" : ""}\u26A0\uFE0F ${message}`);
|
|
18707
18851
|
try {
|
|
18708
|
-
if (!this.messageId) await this.client.sendCard(this.chatId,
|
|
18709
|
-
else await this.client.patchCard(this.messageId,
|
|
18852
|
+
if (!this.messageId) await this.client.sendCard(this.chatId, card2);
|
|
18853
|
+
else await this.client.patchCard(this.messageId, card2);
|
|
18710
18854
|
} catch (e) {
|
|
18711
18855
|
this.log(`feishu card error-finalize failed: ${errMsg(e)}`);
|
|
18712
18856
|
}
|
|
@@ -127492,14 +127636,14 @@ var require_lib4 = __commonJS({
|
|
|
127492
127636
|
});
|
|
127493
127637
|
}
|
|
127494
127638
|
};
|
|
127495
|
-
function appendErrorFooter(
|
|
127496
|
-
const cardObj =
|
|
127639
|
+
function appendErrorFooter(card2) {
|
|
127640
|
+
const cardObj = card2;
|
|
127497
127641
|
const elements = Array.isArray(cardObj === null || cardObj === void 0 ? void 0 : cardObj.elements) ? [...cardObj.elements] : [];
|
|
127498
127642
|
elements.push({
|
|
127499
127643
|
tag: "note",
|
|
127500
127644
|
elements: [{ tag: "plain_text", content: "\u26A0\uFE0F \u751F\u6210\u4E2D\u65AD" }]
|
|
127501
127645
|
});
|
|
127502
|
-
return Object.assign(Object.assign({},
|
|
127646
|
+
return Object.assign(Object.assign({}, card2), { elements });
|
|
127503
127647
|
}
|
|
127504
127648
|
var CardStreamController = class {
|
|
127505
127649
|
constructor(sender, to, idType, opts, initial) {
|
|
@@ -128268,13 +128412,13 @@ ${block}
|
|
|
128268
128412
|
});
|
|
128269
128413
|
}
|
|
128270
128414
|
// ─── card ─────────────────────────────────────────────
|
|
128271
|
-
sendCard(to, idType,
|
|
128415
|
+
sendCard(to, idType, card2, opts) {
|
|
128272
128416
|
return __awaiter(this, void 0, void 0, function* () {
|
|
128273
128417
|
const id = yield this.sendOneWithFallback({
|
|
128274
128418
|
to,
|
|
128275
128419
|
idType,
|
|
128276
128420
|
msgType: "interactive",
|
|
128277
|
-
content:
|
|
128421
|
+
content: card2,
|
|
128278
128422
|
replyTo: opts.replyTo,
|
|
128279
128423
|
replyInThread: opts.replyInThread
|
|
128280
128424
|
});
|
|
@@ -128386,11 +128530,11 @@ ${block}
|
|
|
128386
128530
|
* and the public `channel.updateCard()` low-level API. Not suitable for
|
|
128387
128531
|
* text streaming (use native cardkit streaming below for that).
|
|
128388
128532
|
*/
|
|
128389
|
-
patchCard(messageId,
|
|
128533
|
+
patchCard(messageId, card2) {
|
|
128390
128534
|
return __awaiter(this, void 0, void 0, function* () {
|
|
128391
128535
|
yield this.client.im.v1.message.patch({
|
|
128392
128536
|
path: { message_id: messageId },
|
|
128393
|
-
data: { content: JSON.stringify(
|
|
128537
|
+
data: { content: JSON.stringify(card2) }
|
|
128394
128538
|
});
|
|
128395
128539
|
});
|
|
128396
128540
|
}
|
|
@@ -129941,9 +130085,9 @@ ${lines.join("\n")}
|
|
|
129941
130085
|
});
|
|
129942
130086
|
}
|
|
129943
130087
|
// ─── low-level ─────────────────────────────────────────
|
|
129944
|
-
updateCard(messageId,
|
|
130088
|
+
updateCard(messageId, card2) {
|
|
129945
130089
|
return __awaiter(this, void 0, void 0, function* () {
|
|
129946
|
-
yield this.sender.patchCard(messageId,
|
|
130090
|
+
yield this.sender.patchCard(messageId, card2);
|
|
129947
130091
|
});
|
|
129948
130092
|
}
|
|
129949
130093
|
/**
|
|
@@ -149182,7 +149326,7 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
149182
149326
|
}
|
|
149183
149327
|
}
|
|
149184
149328
|
const plan = withClosureReport(
|
|
149185
|
-
{ reason: `\u52A8\u6001\u89C4\u5212\u5DE5\u5355\uFF1A${displayTitle}`, ops },
|
|
149329
|
+
{ reason: `\u52A8\u6001\u89C4\u5212\u5DE5\u5355\uFF1A${displayTitle}`, ops, workspace: input.workspace },
|
|
149186
149330
|
{ draftedBy: input.actor }
|
|
149187
149331
|
);
|
|
149188
149332
|
const closureOp = plan.ops.find(
|
|
@@ -179630,6 +179774,126 @@ var init_inbox_chat = __esm({
|
|
|
179630
179774
|
}
|
|
179631
179775
|
});
|
|
179632
179776
|
|
|
179777
|
+
// ../server/src/domains/collab/waiting-inbox.ts
|
|
179778
|
+
function card(kind, row, severity, since, summary) {
|
|
179779
|
+
return {
|
|
179780
|
+
kind,
|
|
179781
|
+
...row.artifactId ? { artifactId: row.artifactId } : {},
|
|
179782
|
+
workspace: row.workorderId,
|
|
179783
|
+
...row.title ? { title: row.title } : {},
|
|
179784
|
+
since,
|
|
179785
|
+
actionRequired: true,
|
|
179786
|
+
summary,
|
|
179787
|
+
// info 档只提示不算 overdue;warn/urgent 置 overdue(前端红点/排序)。
|
|
179788
|
+
...severity === "info" ? {} : { overdue: true }
|
|
179789
|
+
};
|
|
179790
|
+
}
|
|
179791
|
+
function buildWaitingInbox(reviews, works, policy, nowIso, me) {
|
|
179792
|
+
const now = Date.parse(nowIso);
|
|
179793
|
+
const out = [];
|
|
179794
|
+
const consider = (rows, scenario, kind) => {
|
|
179795
|
+
for (const r of rows) {
|
|
179796
|
+
if (r.actorId !== me) continue;
|
|
179797
|
+
const start = r.startedAt || r.createdAt;
|
|
179798
|
+
if (r.status === "dead") {
|
|
179799
|
+
out.push(card(kind, r, "urgent", start, `\u4F60\u7684${KIND_LABEL[kind]}\u5DF2\u4E2D\u6B62\uFF0C\u9700\u8981\u4F60\u4ECB\u5165\u91CD\u5F00`));
|
|
179800
|
+
continue;
|
|
179801
|
+
}
|
|
179802
|
+
if (r.status !== "running") continue;
|
|
179803
|
+
if (classify(me) !== "human") continue;
|
|
179804
|
+
const action = policy.resolve({ executor: "human", scenario }).timeoutAction;
|
|
179805
|
+
if (action.kind !== "remind") continue;
|
|
179806
|
+
const elapsed = Number.isFinite(now) ? now - Date.parse(start) : NaN;
|
|
179807
|
+
if (!Number.isFinite(elapsed)) continue;
|
|
179808
|
+
const picked = pickReminderStep(action.cadence, elapsed);
|
|
179809
|
+
if (!picked) continue;
|
|
179810
|
+
const mins = Math.round(elapsed / 6e4);
|
|
179811
|
+
out.push(card(kind, r, picked.step.severity, start, `\u6709\u4E00\u9879${KIND_LABEL[kind]}\u5728\u7B49\u4F60\uFF0C\u5DF2 ${mins} \u5206\u949F\u672A\u52A8`));
|
|
179812
|
+
}
|
|
179813
|
+
};
|
|
179814
|
+
consider(reviews, "review", "review-waiting");
|
|
179815
|
+
consider(works, "work", "work-waiting");
|
|
179816
|
+
return out;
|
|
179817
|
+
}
|
|
179818
|
+
function waitingWorkspacesFor(model, me) {
|
|
179819
|
+
const out = /* @__PURE__ */ new Set();
|
|
179820
|
+
for (const a of model.artifacts.values()) {
|
|
179821
|
+
if (lifecycleOf(model, a.id) !== "active") continue;
|
|
179822
|
+
if (a.owner === me) {
|
|
179823
|
+
out.add(a.workspace);
|
|
179824
|
+
continue;
|
|
179825
|
+
}
|
|
179826
|
+
if (pendingConcludeAttempt(model, a.id)?.gate?.eligible.includes(me)) out.add(a.workspace);
|
|
179827
|
+
}
|
|
179828
|
+
return out;
|
|
179829
|
+
}
|
|
179830
|
+
function collectWaitingRows(snapshots2, me) {
|
|
179831
|
+
const reviews = [];
|
|
179832
|
+
const works = [];
|
|
179833
|
+
const latestBy = (map, row) => {
|
|
179834
|
+
const prev = map.get(row.nodeId);
|
|
179835
|
+
if (!prev || row.createdAt > prev.createdAt) map.set(row.nodeId, row);
|
|
179836
|
+
};
|
|
179837
|
+
for (const snap of snapshots2) {
|
|
179838
|
+
const nodeTitle = new Map(snap.nodes.map((n) => [n.id, n.title]));
|
|
179839
|
+
const cancelled = new Set(snap.nodes.filter((n) => n.cancelledAt).map((n) => n.id));
|
|
179840
|
+
const outputCountOf = (workId) => snap.artifacts.reduce((n, a) => a.workId === workId ? n + 1 : n, 0);
|
|
179841
|
+
const latestReview = /* @__PURE__ */ new Map();
|
|
179842
|
+
for (const r of snap.reviews) {
|
|
179843
|
+
if (r.reviewerActorId !== me || cancelled.has(r.nodeId)) continue;
|
|
179844
|
+
latestBy(latestReview, r);
|
|
179845
|
+
}
|
|
179846
|
+
for (const r of latestReview.values()) {
|
|
179847
|
+
const st = reviewState(r);
|
|
179848
|
+
if (st !== "running" && st !== "dead") continue;
|
|
179849
|
+
reviews.push({
|
|
179850
|
+
id: r.id,
|
|
179851
|
+
workorderId: snap.workorder.id,
|
|
179852
|
+
artifactId: r.nodeId,
|
|
179853
|
+
actorId: r.reviewerActorId,
|
|
179854
|
+
...nodeTitle.get(r.nodeId) ? { title: nodeTitle.get(r.nodeId) } : {},
|
|
179855
|
+
createdAt: r.createdAt,
|
|
179856
|
+
startedAt: r.startedAt,
|
|
179857
|
+
status: st
|
|
179858
|
+
});
|
|
179859
|
+
}
|
|
179860
|
+
const latestMain = /* @__PURE__ */ new Map();
|
|
179861
|
+
const latestReply = /* @__PURE__ */ new Map();
|
|
179862
|
+
for (const w2 of snap.works) {
|
|
179863
|
+
if (w2.assigneeActorId !== me || cancelled.has(w2.nodeId)) continue;
|
|
179864
|
+
latestBy(w2.replyToIssueId ? latestReply : latestMain, w2);
|
|
179865
|
+
}
|
|
179866
|
+
for (const w2 of [...latestMain.values(), ...latestReply.values()]) {
|
|
179867
|
+
const st = workState(w2, hasOutput(w2, outputCountOf(w2.id)));
|
|
179868
|
+
if (st !== "running" && st !== "dead") continue;
|
|
179869
|
+
works.push({
|
|
179870
|
+
id: w2.id,
|
|
179871
|
+
workorderId: snap.workorder.id,
|
|
179872
|
+
artifactId: w2.nodeId,
|
|
179873
|
+
actorId: w2.assigneeActorId,
|
|
179874
|
+
...nodeTitle.get(w2.nodeId) ? { title: nodeTitle.get(w2.nodeId) } : {},
|
|
179875
|
+
createdAt: w2.createdAt,
|
|
179876
|
+
startedAt: w2.startedAt,
|
|
179877
|
+
status: st
|
|
179878
|
+
});
|
|
179879
|
+
}
|
|
179880
|
+
}
|
|
179881
|
+
return { reviews, works };
|
|
179882
|
+
}
|
|
179883
|
+
var KIND_LABEL;
|
|
179884
|
+
var init_waiting_inbox = __esm({
|
|
179885
|
+
"../server/src/domains/collab/waiting-inbox.ts"() {
|
|
179886
|
+
"use strict";
|
|
179887
|
+
init_src4();
|
|
179888
|
+
init_src3();
|
|
179889
|
+
init_src2();
|
|
179890
|
+
KIND_LABEL = {
|
|
179891
|
+
"review-waiting": "\u8BC4\u5BA1",
|
|
179892
|
+
"work-waiting": "\u5904\u7406"
|
|
179893
|
+
};
|
|
179894
|
+
}
|
|
179895
|
+
});
|
|
179896
|
+
|
|
179633
179897
|
// ../server/src/domains/collab/audit.ts
|
|
179634
179898
|
function toAuditEntry(op) {
|
|
179635
179899
|
return {
|
|
@@ -179638,7 +179902,7 @@ function toAuditEntry(op) {
|
|
|
179638
179902
|
actor: op.actor,
|
|
179639
179903
|
op: op.kind,
|
|
179640
179904
|
artifactId: op.artifactId,
|
|
179641
|
-
summary: `${
|
|
179905
|
+
summary: `${KIND_LABEL2[op.kind] ?? op.kind} \xB7 ${op.artifactId}`
|
|
179642
179906
|
};
|
|
179643
179907
|
}
|
|
179644
179908
|
function eventToAuditEntry(rec) {
|
|
@@ -179650,7 +179914,7 @@ function eventToAuditEntry(rec) {
|
|
|
179650
179914
|
actor: rec.actorId,
|
|
179651
179915
|
op: ev.kind,
|
|
179652
179916
|
artifactId,
|
|
179653
|
-
summary: `${
|
|
179917
|
+
summary: `${KIND_LABEL2[ev.kind] ?? ev.kind}${artifactId ? ` \xB7 ${artifactId}` : ""}`
|
|
179654
179918
|
};
|
|
179655
179919
|
}
|
|
179656
179920
|
function enrichAudit(entry, model, titleOf) {
|
|
@@ -179726,12 +179990,12 @@ async function buildAudit(source, q = {}, model) {
|
|
|
179726
179990
|
}
|
|
179727
179991
|
return { items, cursor: truncated ? lastCursor : null };
|
|
179728
179992
|
}
|
|
179729
|
-
var
|
|
179993
|
+
var KIND_LABEL2;
|
|
179730
179994
|
var init_audit = __esm({
|
|
179731
179995
|
"../server/src/domains/collab/audit.ts"() {
|
|
179732
179996
|
"use strict";
|
|
179733
179997
|
init_node_state();
|
|
179734
|
-
|
|
179998
|
+
KIND_LABEL2 = {
|
|
179735
179999
|
// 新引擎事件 kind
|
|
179736
180000
|
"plan.update_spec": "\u6539\u4EFB\u52A1",
|
|
179737
180001
|
"plan.add_node": "\u52A0\u8282\u70B9",
|
|
@@ -180838,7 +181102,7 @@ function collabDomain(opts) {
|
|
|
180838
181102
|
return { status: 200, body: { dispatched: !already } };
|
|
180839
181103
|
});
|
|
180840
181104
|
router.get("/api/inbox", async (req) => {
|
|
180841
|
-
const { kernel, registry: registry2 } = await resolveCtx(req.auth.companyId);
|
|
181105
|
+
const { kernel, registry: registry2, engineStore } = await resolveCtx(req.auth.companyId);
|
|
180842
181106
|
const me = req.auth.actor;
|
|
180843
181107
|
const [leadOf, resolveActor, markers] = await Promise.all([
|
|
180844
181108
|
buildLeadResolver(registry2),
|
|
@@ -180851,7 +181115,18 @@ function collabDomain(opts) {
|
|
|
180851
181115
|
const inboxSlaMs = opts.sla?.humanMs ?? 24 * 36e5;
|
|
180852
181116
|
const kernelItems = buildInbox(kernel.model, me, leadOf, resolveActor, markers, inboxSlaMs, (/* @__PURE__ */ new Date()).toISOString());
|
|
180853
181117
|
const chatItems = opts.chatSession && opts.readMarkers ? await buildChatInbox(opts.chatSession, markers, me, resolveActor) : [];
|
|
180854
|
-
|
|
181118
|
+
let waitingItems = [];
|
|
181119
|
+
if (engineStore) {
|
|
181120
|
+
const snaps = (await Promise.all(
|
|
181121
|
+
[...waitingWorkspacesFor(kernel.model, me)].map(
|
|
181122
|
+
(wid) => engineStore.transaction((tx) => tx.loadWorkorder(wid))
|
|
181123
|
+
)
|
|
181124
|
+
)).filter((s2) => s2 !== null);
|
|
181125
|
+
const { reviews, works } = collectWaitingRows(snaps, me);
|
|
181126
|
+
const policy = opts.policy?.() ?? CODE_DEFAULT_POLICY;
|
|
181127
|
+
waitingItems = buildWaitingInbox(reviews, works, policy, (/* @__PURE__ */ new Date()).toISOString(), me);
|
|
181128
|
+
}
|
|
181129
|
+
return { status: 200, body: { items: mergeInbox(kernelItems, [...chatItems, ...waitingItems]) } };
|
|
180855
181130
|
});
|
|
180856
181131
|
const companyOf = (req) => req.auth.companyId ?? "company:default";
|
|
180857
181132
|
const noTagStore = { status: 501, body: { error: { code: "not_implemented", message: "\u672C\u90E8\u7F72\u672A\u542F\u7528\u5DE5\u5355\u6807\u7B7E" } } };
|
|
@@ -181091,6 +181366,8 @@ var init_collab = __esm({
|
|
|
181091
181366
|
init_workorder_detail();
|
|
181092
181367
|
init_inbox();
|
|
181093
181368
|
init_inbox_chat();
|
|
181369
|
+
init_waiting_inbox();
|
|
181370
|
+
init_src2();
|
|
181094
181371
|
init_audit();
|
|
181095
181372
|
init_dashboard();
|
|
181096
181373
|
init_workorder_metrics();
|
|
@@ -194431,6 +194708,8 @@ async function startServe(opts) {
|
|
|
194431
194708
|
// 工单标签词表(组织级受控、旁账,与 playbook-overrides 同款侧存)。
|
|
194432
194709
|
tags: workorderTagStore,
|
|
194433
194710
|
...opts.sla ? { sla: opts.sla } : {},
|
|
194711
|
+
// ADR 0147:waiting 投影的 human 阶梯从策略中心取;getter 保证随 SIGHUP/reload 热载而变。
|
|
194712
|
+
policy: () => activeTimeoutPolicy,
|
|
194434
194713
|
resolveEngine: (cid) => getEngine(cid ?? defaultCompanyId)
|
|
194435
194714
|
}),
|
|
194436
194715
|
trace.register,
|
|
@@ -201490,7 +201769,8 @@ ${res.warning}`);
|
|
|
201490
201769
|
r.id === v2.artifact.currentRev ? "\u2190 current head" : "",
|
|
201491
201770
|
r.id === v2.concludedHead ? "\u2190 \u5DF2\u4EA4\u4ED8" : ""
|
|
201492
201771
|
].filter(Boolean).join(" ");
|
|
201493
|
-
|
|
201772
|
+
const stateLabel = r.state === "superseded" && r.contentKind === "empty" ? "\u672A\u4EA7\u51FA" : r.state;
|
|
201773
|
+
println(` ${r.id} ${stateLabel} by ${r.author} "${r.reason}"${marks ? ` ${marks}` : ""}`);
|
|
201494
201774
|
}
|
|
201495
201775
|
const NOTE_ABSTRACT = 150;
|
|
201496
201776
|
const voteRows = [];
|
|
@@ -201779,7 +202059,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
|
|
|
201779
202059
|
}
|
|
201780
202060
|
|
|
201781
202061
|
// src/index.ts
|
|
201782
|
-
var PKG_VERSION = true ? "0.1.
|
|
202062
|
+
var PKG_VERSION = true ? "0.1.105" : "dev";
|
|
201783
202063
|
var OASIS_DIR = path26.join(os10.homedir(), ".oasis");
|
|
201784
202064
|
var CONFIG_FILE = path26.join(OASIS_DIR, "node-config.json");
|
|
201785
202065
|
var PID_FILE = path26.join(OASIS_DIR, "node.pid");
|
|
@@ -201882,8 +202162,10 @@ function setupAutostart() {
|
|
|
201882
202162
|
].join("\n"));
|
|
201883
202163
|
try {
|
|
201884
202164
|
(0, import_node_child_process17.execSync)("systemctl --user daemon-reload && systemctl --user enable --now oasis-node", { stdio: "ignore" });
|
|
202165
|
+
return true;
|
|
201885
202166
|
} catch {
|
|
201886
202167
|
}
|
|
202168
|
+
return false;
|
|
201887
202169
|
} else if (process.platform === "darwin") {
|
|
201888
202170
|
const plist = path26.join(os10.homedir(), "Library", "LaunchAgents", "com.oasis.node.plist");
|
|
201889
202171
|
fs33.mkdirSync(path26.dirname(plist), { recursive: true });
|
|
@@ -201899,9 +202181,12 @@ function setupAutostart() {
|
|
|
201899
202181
|
</dict></plist>`);
|
|
201900
202182
|
try {
|
|
201901
202183
|
(0, import_node_child_process17.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
|
|
202184
|
+
return true;
|
|
201902
202185
|
} catch {
|
|
201903
202186
|
}
|
|
202187
|
+
return false;
|
|
201904
202188
|
}
|
|
202189
|
+
return false;
|
|
201905
202190
|
}
|
|
201906
202191
|
function spawnDaemon() {
|
|
201907
202192
|
const log3 = fs33.openSync(LOG_FILE, "a");
|
|
@@ -201910,9 +202195,7 @@ function spawnDaemon() {
|
|
|
201910
202195
|
stdio: ["ignore", log3, log3]
|
|
201911
202196
|
});
|
|
201912
202197
|
child.unref();
|
|
201913
|
-
|
|
201914
|
-
fs33.writeFileSync(PID_FILE, String(pid));
|
|
201915
|
-
return pid;
|
|
202198
|
+
return child.pid ?? 0;
|
|
201916
202199
|
}
|
|
201917
202200
|
function stopDaemon() {
|
|
201918
202201
|
const pid = readPid();
|
|
@@ -201984,12 +202267,21 @@ void (async () => {
|
|
|
201984
202267
|
console.error("[oasis] no config \u2014 run `oasis daemon start` first");
|
|
201985
202268
|
process.exit(1);
|
|
201986
202269
|
}
|
|
201987
|
-
|
|
201988
|
-
|
|
201989
|
-
|
|
201990
|
-
|
|
202270
|
+
try {
|
|
202271
|
+
const fd = fs33.openSync(PID_FILE, "wx");
|
|
202272
|
+
fs33.writeSync(fd, String(process.pid));
|
|
202273
|
+
fs33.closeSync(fd);
|
|
202274
|
+
} catch {
|
|
202275
|
+
const alivePid = readPid();
|
|
202276
|
+
if (alivePid && alivePid !== process.pid && isRunning2(alivePid)) {
|
|
202277
|
+
console.error(`[oasis] daemon already running (PID ${alivePid}) \u2014 exiting duplicate`);
|
|
202278
|
+
process.exit(0);
|
|
202279
|
+
}
|
|
202280
|
+
try {
|
|
202281
|
+
fs33.writeFileSync(PID_FILE, String(process.pid));
|
|
202282
|
+
} catch {
|
|
202283
|
+
}
|
|
201991
202284
|
}
|
|
201992
|
-
fs33.writeFileSync(PID_FILE, String(process.pid));
|
|
201993
202285
|
const cleanup = () => {
|
|
201994
202286
|
try {
|
|
201995
202287
|
fs33.unlinkSync(PID_FILE);
|
|
@@ -202083,9 +202375,13 @@ void (async () => {
|
|
|
202083
202375
|
saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {}, reportRuntimesOnNextStart: true });
|
|
202084
202376
|
installBinary();
|
|
202085
202377
|
stopDaemon();
|
|
202086
|
-
setupAutostart();
|
|
202087
|
-
|
|
202088
|
-
|
|
202378
|
+
const tookOver = setupAutostart();
|
|
202379
|
+
if (!tookOver) {
|
|
202380
|
+
const pid = spawnDaemon();
|
|
202381
|
+
console.log(`\u2713 daemon started (PID ${pid})${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
|
|
202382
|
+
} else {
|
|
202383
|
+
console.log(`\u2713 daemon started via systemd (oasis-node.service)${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
|
|
202384
|
+
}
|
|
202089
202385
|
console.log(` logs: tail -f ${LOG_FILE}`);
|
|
202090
202386
|
if (!process.env["PATH"]?.includes(path26.dirname(LOCAL_BIN)))
|
|
202091
202387
|
console.log(` add to PATH: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oasis_test",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.105",
|
|
4
4
|
"description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oasis": "./dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"oasisRelease": {
|
|
29
|
-
"sourceHead": "
|
|
29
|
+
"sourceHead": "60d88fd92311e481e7bbfe922d75db13a3483643"
|
|
30
30
|
}
|
|
31
31
|
}
|