oasis_test 0.1.102 → 0.1.103
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 +574 -97
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -387,7 +387,9 @@ function emptyReadModel() {
|
|
|
387
387
|
gaps: /* @__PURE__ */ new Map(),
|
|
388
388
|
escalations: /* @__PURE__ */ new Map(),
|
|
389
389
|
pendingReviews: /* @__PURE__ */ new Map(),
|
|
390
|
-
pausedWorkorders: /* @__PURE__ */ new Set()
|
|
390
|
+
pausedWorkorders: /* @__PURE__ */ new Set(),
|
|
391
|
+
acceptBlockers: /* @__PURE__ */ new Map()
|
|
392
|
+
// 默认空 = 无挡板 = 历史行为;只有 bridge 从引擎快照刷新时才填
|
|
391
393
|
};
|
|
392
394
|
}
|
|
393
395
|
function indexAdd(index, key, value) {
|
|
@@ -1513,6 +1515,15 @@ function stuckDiagnosis(model, id, opts) {
|
|
|
1513
1515
|
handler: "none"
|
|
1514
1516
|
});
|
|
1515
1517
|
}
|
|
1518
|
+
const blocker = model.acceptBlockers.get(id);
|
|
1519
|
+
if (blocker) {
|
|
1520
|
+
causes.push({
|
|
1521
|
+
category: "accept-barrier",
|
|
1522
|
+
source: "oplog",
|
|
1523
|
+
detail: `\u4EA7\u51FA\u5DF2\u5C31\u7EEA\u4F46\u6536\u53E3\u88AB\u62E6\uFF08${blocker.code}\uFF09\uFF1A${blocker.detail}${blocker.overridable ? "\u2014\u2014\u4EBA\u53EF force-conclude \u7B7E\u5B57\u7A7F\u900F\uFF08\u7559 override \u75D5\u8FF9\uFF09" : "\u2014\u2014\u4E0D\u53EF\u7A7F\u900F\uFF0C\u6309\u4E0A\u9762\u7684\u539F\u56E0\u5148\u4FEE\u6B63\u76EE\u6807"}`,
|
|
1524
|
+
handler: "human"
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1516
1527
|
const rt = opts.runtime;
|
|
1517
1528
|
if (rt.queued) {
|
|
1518
1529
|
causes.push({
|
|
@@ -4335,6 +4346,93 @@ var init_exit_classify = __esm({
|
|
|
4335
4346
|
}
|
|
4336
4347
|
});
|
|
4337
4348
|
|
|
4349
|
+
// ../core/src/produce-resume-fallback.ts
|
|
4350
|
+
function isSessionNotFound(info) {
|
|
4351
|
+
return info.reason === "session-not-found";
|
|
4352
|
+
}
|
|
4353
|
+
function makeProduceResumeFallback(first, spawnFallback) {
|
|
4354
|
+
const stableId2 = first.id;
|
|
4355
|
+
const exitCbs = [];
|
|
4356
|
+
const outCbs = [];
|
|
4357
|
+
const telCbs = [];
|
|
4358
|
+
const outBuf = [];
|
|
4359
|
+
const telBuf = [];
|
|
4360
|
+
let sawOutput = false;
|
|
4361
|
+
let active = first;
|
|
4362
|
+
let fellBack = false;
|
|
4363
|
+
let terminal;
|
|
4364
|
+
const fanOut = (chunk) => {
|
|
4365
|
+
if (outCbs.length) for (const cb of outCbs) cb(chunk);
|
|
4366
|
+
else outBuf.push(chunk);
|
|
4367
|
+
};
|
|
4368
|
+
const fanTel = (event) => {
|
|
4369
|
+
if (telCbs.length) for (const cb of telCbs) cb(event);
|
|
4370
|
+
else telBuf.push(event);
|
|
4371
|
+
};
|
|
4372
|
+
const wire = (s2) => {
|
|
4373
|
+
s2.onOutput?.((c) => {
|
|
4374
|
+
sawOutput = true;
|
|
4375
|
+
fanOut(c);
|
|
4376
|
+
});
|
|
4377
|
+
s2.onTelemetry?.((e) => {
|
|
4378
|
+
sawOutput = true;
|
|
4379
|
+
fanTel(e);
|
|
4380
|
+
});
|
|
4381
|
+
};
|
|
4382
|
+
const fireTerminal = (info) => {
|
|
4383
|
+
if (terminal) return;
|
|
4384
|
+
terminal = info;
|
|
4385
|
+
for (const cb of exitCbs.splice(0)) cb(info);
|
|
4386
|
+
};
|
|
4387
|
+
wire(first);
|
|
4388
|
+
first.onExit((info) => {
|
|
4389
|
+
if (fellBack || sawOutput || !isSessionNotFound(info)) {
|
|
4390
|
+
fireTerminal(info);
|
|
4391
|
+
return;
|
|
4392
|
+
}
|
|
4393
|
+
fellBack = true;
|
|
4394
|
+
void spawnFallback().then(
|
|
4395
|
+
(second) => {
|
|
4396
|
+
active = second;
|
|
4397
|
+
wire(second);
|
|
4398
|
+
second.onExit((info2) => fireTerminal(info2));
|
|
4399
|
+
},
|
|
4400
|
+
// 回落连 spawn 都起不来(节点掉线等)→ 把原始 session-not-found 如实上报,交常规重试兜底。
|
|
4401
|
+
() => fireTerminal(info)
|
|
4402
|
+
);
|
|
4403
|
+
});
|
|
4404
|
+
return {
|
|
4405
|
+
get id() {
|
|
4406
|
+
return stableId2;
|
|
4407
|
+
},
|
|
4408
|
+
kill: () => active.kill(),
|
|
4409
|
+
onExit(cb) {
|
|
4410
|
+
if (terminal) {
|
|
4411
|
+
cb(terminal);
|
|
4412
|
+
return;
|
|
4413
|
+
}
|
|
4414
|
+
exitCbs.push(cb);
|
|
4415
|
+
},
|
|
4416
|
+
onOutput(cb) {
|
|
4417
|
+
for (const c of outBuf.splice(0)) cb(c);
|
|
4418
|
+
outCbs.push(cb);
|
|
4419
|
+
},
|
|
4420
|
+
onTelemetry(cb) {
|
|
4421
|
+
for (const e of telBuf.splice(0)) cb(e);
|
|
4422
|
+
telCbs.push(cb);
|
|
4423
|
+
},
|
|
4424
|
+
appendInput: (input) => active.appendInput?.(input) ?? Promise.resolve({ accepted: false, reason: "unsupported" }),
|
|
4425
|
+
get canAppendInput() {
|
|
4426
|
+
return active.canAppendInput !== false && typeof active.appendInput === "function";
|
|
4427
|
+
}
|
|
4428
|
+
};
|
|
4429
|
+
}
|
|
4430
|
+
var init_produce_resume_fallback = __esm({
|
|
4431
|
+
"../core/src/produce-resume-fallback.ts"() {
|
|
4432
|
+
"use strict";
|
|
4433
|
+
}
|
|
4434
|
+
});
|
|
4435
|
+
|
|
4338
4436
|
// ../timeout-policy/src/policy.ts
|
|
4339
4437
|
function classify(actorId) {
|
|
4340
4438
|
if (!actorId) return "unknown";
|
|
@@ -4801,6 +4899,7 @@ var init_dispatcher = __esm({
|
|
|
4801
4899
|
"../core/src/dispatcher.ts"() {
|
|
4802
4900
|
"use strict";
|
|
4803
4901
|
import_node_crypto2 = require("node:crypto");
|
|
4902
|
+
init_produce_resume_fallback();
|
|
4804
4903
|
init_read_model();
|
|
4805
4904
|
init_assembler();
|
|
4806
4905
|
init_content_type_handler();
|
|
@@ -5574,7 +5673,10 @@ var init_dispatcher = __esm({
|
|
|
5574
5673
|
action: "produce",
|
|
5575
5674
|
...args.part !== void 0 ? { part: args.part } : {},
|
|
5576
5675
|
...args.engineWorkId !== void 0 ? { engineWorkId: args.engineWorkId } : {},
|
|
5577
|
-
...args.engineCompanyId !== void 0 ? { engineCompanyId: args.engineCompanyId } : {}
|
|
5676
|
+
...args.engineCompanyId !== void 0 ? { engineCompanyId: args.engineCompanyId } : {},
|
|
5677
|
+
// 工单 e4a43a42:只有短时重试(同一未收口任务)才接续旧会话;变更请求/gap/级联触发的返工
|
|
5678
|
+
// 一律新会话(`resumeEligible: false`)——否则接续会把过时上下文和旧任务书一起拖进来。缺省=可续。
|
|
5679
|
+
...args.resumeEligible !== void 0 ? { resumeEligible: args.resumeEligible } : {}
|
|
5578
5680
|
}, args.bypassScheduling === true, args.dispatchId);
|
|
5579
5681
|
}
|
|
5580
5682
|
/** 新引擎回信 work(设计-最终 §回信轮)的 effect 调到此——派发收容回信会话(唯一写动作 `oasis reply`)。
|
|
@@ -5820,7 +5922,8 @@ var init_dispatcher = __esm({
|
|
|
5820
5922
|
const priorConsistent = prior !== void 0 && prior.nodeId === binding?.nodeId && prior.runtimeKind === binding?.runtimeKind;
|
|
5821
5923
|
const retainedPartSession = spec.action === "produce" && spec.part !== void 0 && priorConsistent && !prior.sessionMissing ? prior.rtId : void 0;
|
|
5822
5924
|
const retainedWholeSession = spec.action === "produce" && spec.part === void 0 && priorConsistent && !prior.sessionMissing ? this.resumableWholeSession(spec.artifactId, seqNow, prior.rtId) : void 0;
|
|
5823
|
-
const
|
|
5925
|
+
const resumeEligible = spec.resumeEligible !== false;
|
|
5926
|
+
const retainedSession = resumeEligible ? retainedPartSession ?? retainedWholeSession : void 0;
|
|
5824
5927
|
const runtimeSessionId = retainedSession ?? (isProduceLike ? (0, import_node_crypto2.randomUUID)() : void 0);
|
|
5825
5928
|
const resumeSession = retainedSession !== void 0;
|
|
5826
5929
|
const retry = this.strikes.get(jobKey);
|
|
@@ -5938,7 +6041,8 @@ var init_dispatcher = __esm({
|
|
|
5938
6041
|
// `handle.id` = `agent_runs.id` = token 的 dispatchId claim,三处同值。
|
|
5939
6042
|
dispatchId: attempt
|
|
5940
6043
|
};
|
|
5941
|
-
const
|
|
6044
|
+
const first = await this.opts.adapter.spawn(job);
|
|
6045
|
+
const session = resumeSession ? makeProduceResumeFallback(first, () => this.opts.adapter.spawn({ ...job, resumeRuntimeSession: false })) : first;
|
|
5942
6046
|
if (!this.isSpawnAttemptCurrent(jobKey, attempt)) {
|
|
5943
6047
|
try {
|
|
5944
6048
|
await session.kill();
|
|
@@ -6995,7 +7099,7 @@ function scan(state, ctx) {
|
|
|
6995
7099
|
if (wstate === "success" && node.latestAcceptId !== lw.id) {
|
|
6996
7100
|
const reqs = state.requirementsOf(node.id);
|
|
6997
7101
|
if (reqs.length === 0) {
|
|
6998
|
-
if (
|
|
7102
|
+
if (acceptBlock(state, lw.id) !== null) continue;
|
|
6999
7103
|
events.push({
|
|
7000
7104
|
kind: "work.accept",
|
|
7001
7105
|
workorderId: state.workorder.id,
|
|
@@ -7036,7 +7140,11 @@ function tryToRun(state, nodeId, ctx, events) {
|
|
|
7036
7140
|
nodeId,
|
|
7037
7141
|
workId,
|
|
7038
7142
|
reasons: [],
|
|
7039
|
-
continuesWorkId: null
|
|
7143
|
+
continuesWorkId: null,
|
|
7144
|
+
// 工单 e4a43a42(接续只对短时重试成立):**已收口过的节点被重新激活 = 返工**(变更请求/驳回/上游
|
|
7145
|
+
// 变更把已 accept 的节点重开)→ 起全新会话,别接续那场过时的旧对话;**从未收口的节点** = 首跑或
|
|
7146
|
+
// 失败后重试,接续 WIP 是对的。判据 = 有没有 latest_accept。派发层据此决定 resumeSession。
|
|
7147
|
+
resumeEligible: node.latestAcceptId == null
|
|
7040
7148
|
});
|
|
7041
7149
|
}
|
|
7042
7150
|
function inputsMatch(state, node, latestWork2) {
|
|
@@ -7164,13 +7272,15 @@ function processCommentReplies(state, nodeId, ctx, events) {
|
|
|
7164
7272
|
if (latest) {
|
|
7165
7273
|
const s2 = workState(latest, hasOutput(latest, 0));
|
|
7166
7274
|
if (s2 === "success" && latest.acceptanceState !== "accepted") {
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7275
|
+
if (acceptBlock(state, latest.id) === null) {
|
|
7276
|
+
events.push({
|
|
7277
|
+
kind: "work.accept",
|
|
7278
|
+
workorderId: state.workorder.id,
|
|
7279
|
+
workId: latest.id,
|
|
7280
|
+
by: "actor:system:engine",
|
|
7281
|
+
override: null
|
|
7282
|
+
});
|
|
7283
|
+
}
|
|
7174
7284
|
return;
|
|
7175
7285
|
}
|
|
7176
7286
|
}
|
|
@@ -7267,12 +7377,54 @@ function isBusinessDependencyEdge(state, edge) {
|
|
|
7267
7377
|
return edge.kind === "data" && edge.required && downstream?.fields?.[BUSINESS_HANDOFF_POLICY_FIELD] !== BUSINESS_HANDOFF_POLICY_EXEMPT;
|
|
7268
7378
|
}
|
|
7269
7379
|
function businessHandoffBarrierSatisfied(state, nodeId, workId) {
|
|
7380
|
+
return handoffBlock(state, nodeId, workId) === null;
|
|
7381
|
+
}
|
|
7382
|
+
function acceptBlock(state, workId) {
|
|
7383
|
+
const w2 = state.work(workId);
|
|
7384
|
+
if (!w2) return { code: "work-missing", detail: `work \u4E0D\u5728\u518C\uFF1A${workId}`, overridable: false };
|
|
7385
|
+
if (w2.deadAt) {
|
|
7386
|
+
return {
|
|
7387
|
+
code: "work-dead",
|
|
7388
|
+
detail: `\u8FD9\u4E00\u7248\u5DF2\u4F5C\u5E9F\uFF08${w2.deadAt}${w2.rejectedReason ? `\uFF1A${w2.rejectedReason}` : ""}\uFF09\u2014\u2014\u4F5C\u5E9F\u7248\u4E0D\u80FD\u6536\u53E3\uFF0C\u6539\u5BF9\u6700\u65B0\u7248\u64CD\u4F5C\u6216\u5148\u91CD\u8DD1\u672C\u8282\u70B9`,
|
|
7389
|
+
overridable: false
|
|
7390
|
+
};
|
|
7391
|
+
}
|
|
7392
|
+
if (w2.acceptanceState === "accepted") {
|
|
7393
|
+
return { code: "already-accepted", detail: `\u5DF2\u662F\u9A8C\u6536\u6001\uFF08${w2.acceptedAt ?? "?"}\uFF09\u2014\u2014\u65E0\u9700\u91CD\u590D\u6536\u53E3`, overridable: false };
|
|
7394
|
+
}
|
|
7395
|
+
const node = state.node(w2.nodeId);
|
|
7396
|
+
if (!node) return { code: "node-missing", detail: `\u8282\u70B9\u4E0D\u5728\u518C\uFF1A${w2.nodeId}`, overridable: false };
|
|
7397
|
+
if (!w2.endedAt && !hasOutput(w2, state.artifactsOf(w2.id).length)) {
|
|
7398
|
+
return {
|
|
7399
|
+
code: "work-in-flight",
|
|
7400
|
+
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`,
|
|
7401
|
+
overridable: false
|
|
7402
|
+
};
|
|
7403
|
+
}
|
|
7404
|
+
return handoffBlock(state, w2.nodeId, workId);
|
|
7405
|
+
}
|
|
7406
|
+
function handoffBlock(state, nodeId, workId) {
|
|
7270
7407
|
const work = state.work(workId);
|
|
7271
|
-
if (!requiresBusinessHandoff(state, nodeId, work)) return
|
|
7408
|
+
if (!requiresBusinessHandoff(state, nodeId, work)) return null;
|
|
7409
|
+
if (work?.acceptanceState === "accepted" && work.overrideBy) return null;
|
|
7272
7410
|
const node = state.node(nodeId);
|
|
7273
|
-
|
|
7411
|
+
if (node?.latestWorkId !== workId) {
|
|
7412
|
+
return {
|
|
7413
|
+
code: "not-latest-work",
|
|
7414
|
+
detail: `\u4E0D\u662F\u672C\u8282\u70B9\u6700\u65B0\u4E00\u7248\uFF08\u6700\u65B0\uFF1A${node?.latestWorkId ?? "\u65E0"}\uFF09\u2014\u2014\u6536\u53E3\u53EA\u8BA4\u6700\u65B0\u7248`,
|
|
7415
|
+
overridable: false
|
|
7416
|
+
};
|
|
7417
|
+
}
|
|
7418
|
+
const delivered = work?.businessHandoffStatus === "delivered" || work?.status === "success" && work.agentHandoffAttemptId === null && work.businessHandoffStatus === null;
|
|
7419
|
+
if (delivered) return null;
|
|
7420
|
+
return {
|
|
7421
|
+
code: "handoff-undeclared",
|
|
7422
|
+
detail: `\u4E1A\u52A1\u4EA4\u63A5\u672A\u5B8C\u6210\uFF1Awork.status=${work?.status ?? "?"}\u3001handoff=${work?.businessHandoffStatus ?? "\u672A\u58F0\u660E"}${work?.agentHandoffAttemptId ? `\uFF08attempt ${work.agentHandoffAttemptId}\uFF09` : ""}\u2014\u2014\u7B49\u6267\u884C\u4EBA\u8865\u4EA4\u63A5\uFF0C\u6216\u7531\u4EBA force-conclude \u7B7E\u5B57\u7A7F\u900F`,
|
|
7423
|
+
overridable: true
|
|
7424
|
+
};
|
|
7274
7425
|
}
|
|
7275
7426
|
function requiresBusinessHandoff(state, nodeId, work) {
|
|
7427
|
+
if (work?.replyToIssueId) return false;
|
|
7276
7428
|
if (!state.edgesFrom(nodeId).some((edge) => isBusinessDependencyEdge(state, edge))) return false;
|
|
7277
7429
|
if (typeof work?.assigneeActorId === "string" && work.assigneeActorId.startsWith("actor:human:")) return false;
|
|
7278
7430
|
return true;
|
|
@@ -7465,6 +7617,9 @@ CREATE TABLE IF NOT EXISTS ${schema}.issues (
|
|
|
7465
7617
|
author_actor_id text NOT NULL,
|
|
7466
7618
|
hand_actor_id text,
|
|
7467
7619
|
raised_by_node_id text,
|
|
7620
|
+
-- \u2605 escalation \u66FF\u54EA\u6761 gap \u53D1\u8D77\uFF08\u88AB\u5F15\u7528 gap \u7684 issue id\uFF09\uFF1B\u4F9B issue.resolve \u7CBE\u786E\u8FDE\u5E26\u5173\u95ED
|
|
7621
|
+
-- \uFF08ADR-0030 D1\uFF1Bwo-05dfbd6e \u8865\u9F50\u6865\u63A5\u8FC1\u79FB\u9057\u6F0F\uFF09\u3002\u975E escalation / \u672A\u5E26 --gap \u65F6\u4E3A NULL\u3002
|
|
7622
|
+
gap_id text,
|
|
7468
7623
|
-- \u2605 ADR 0144\uFF1Ablocking / state \u5217\u5DF2\u5220\u9664\u2014\u2014\u5B58\u6D3B\u5224\u636E\u552F\u4E00\u5316\u4E3A resolved_at \u5224\u7A7A\uFF1B
|
|
7469
7624
|
-- resolution \u627F\u63A5\u65E7\u4E09\u503C state \u7684 closed\uFF08=wontfix\uFF09\u8BED\u4E49\u3002\u7EA6\u675F\u7531\u8FC1\u79FB\u6BB5\u7EDF\u4E00\u52A0\uFF08\u540D\u5B57\u5316\u3001\u5E42\u7B49\uFF09\u3002
|
|
7470
7625
|
proposed_resolution text,
|
|
@@ -7589,6 +7744,8 @@ ALTER TABLE ${schema}.works ADD COLUMN IF NOT EXISTS reply_to_issue_id text;
|
|
|
7589
7744
|
-- \u968F\u540E state / blocking \u5217\u5220\u9664\uFF08\u4F9D\u8D56 state \u7684 CHECK \u4E0E\u4E09\u4E2A\u65E7\u90E8\u5206\u7D22\u5F15\u968F\u5217\u81EA\u52A8\u5220\u9664\uFF09\u3002
|
|
7590
7745
|
-- \u56DE\u586B\u5FC5\u987B\u5728\u5220\u5217\u524D\u3001\u4E14\u53EA\u5728 state \u5217\u8FD8\u5B58\u5728\u65F6\u6267\u884C\uFF08DO \u5757\u5224 information_schema\uFF0C\u91CD\u590D\u542F\u52A8\u5E42\u7B49\uFF09\u3002
|
|
7591
7746
|
ALTER TABLE ${schema}.issues ADD COLUMN IF NOT EXISTS resolution text;
|
|
7747
|
+
-- escalation\u2192gap \u7CBE\u786E\u5173\u8054\uFF08wo-05dfbd6e\uFF09\uFF1A\u4F9B issue.resolve \u8FDE\u5E26\u5173\u95ED\u5339\u914D escalation\uFF08ADR-0030 D1\uFF09\u3002
|
|
7748
|
+
ALTER TABLE ${schema}.issues ADD COLUMN IF NOT EXISTS gap_id text;
|
|
7592
7749
|
DO $do$ BEGIN
|
|
7593
7750
|
IF EXISTS (SELECT 1 FROM information_schema.columns
|
|
7594
7751
|
WHERE table_schema = '${schema}' AND table_name = 'issues' AND column_name = 'state') THEN
|
|
@@ -13054,6 +13211,7 @@ var init_store_postgres = __esm({
|
|
|
13054
13211
|
authorActorId: i.author_actor_id,
|
|
13055
13212
|
handActorId: i.hand_actor_id,
|
|
13056
13213
|
raisedByNodeId: i.raised_by_node_id,
|
|
13214
|
+
gapId: i.gap_id ?? null,
|
|
13057
13215
|
proposedResolution: i.proposed_resolution,
|
|
13058
13216
|
proposedBy: i.proposed_by,
|
|
13059
13217
|
proposedAt: iso(i.proposed_at),
|
|
@@ -13288,9 +13446,9 @@ var init_store_postgres = __esm({
|
|
|
13288
13446
|
await this.c.query(
|
|
13289
13447
|
`INSERT INTO ${this.s}.issues
|
|
13290
13448
|
(id,workorder_id,kind,body,about_node_id,about_work_id,author_actor_id,hand_actor_id,
|
|
13291
|
-
raised_by_node_id,proposed_resolution,proposed_by,proposed_at,
|
|
13449
|
+
raised_by_node_id,gap_id,proposed_resolution,proposed_by,proposed_at,
|
|
13292
13450
|
resolved_at,resolution,resolved_by,resolved_note,resolved_via_work_id,created_at)
|
|
13293
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)
|
|
13451
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
|
|
13294
13452
|
ON CONFLICT (id) DO NOTHING`,
|
|
13295
13453
|
[
|
|
13296
13454
|
i.id,
|
|
@@ -13302,6 +13460,7 @@ var init_store_postgres = __esm({
|
|
|
13302
13460
|
i.authorActorId,
|
|
13303
13461
|
i.handActorId,
|
|
13304
13462
|
i.raisedByNodeId,
|
|
13463
|
+
i.gapId,
|
|
13305
13464
|
i.proposedResolution,
|
|
13306
13465
|
i.proposedBy,
|
|
13307
13466
|
i.proposedAt,
|
|
@@ -13513,7 +13672,25 @@ var init_store_postgres = __esm({
|
|
|
13513
13672
|
async lockWorkorder(workorderId) {
|
|
13514
13673
|
await this.c.query(`SELECT 1 FROM ${this.s}.workorders WHERE id = $1 FOR UPDATE`, [workorderId]);
|
|
13515
13674
|
}
|
|
13516
|
-
async listEvents(workorderId, fromSeq = 0) {
|
|
13675
|
+
async listEvents(workorderId, fromSeq = 0, limit, kinds) {
|
|
13676
|
+
if (kinds && kinds.length > 0) {
|
|
13677
|
+
const res2 = workorderId ? await this.c.query(
|
|
13678
|
+
`SELECT * FROM ${this.s}.events WHERE workorder_id = $1 AND seq > $2 AND kind = ANY($3) ORDER BY seq${limit !== void 0 ? ` DESC LIMIT ${Number(limit)}` : ""}`,
|
|
13679
|
+
[workorderId, fromSeq, [...kinds]]
|
|
13680
|
+
) : await this.c.query(
|
|
13681
|
+
`SELECT * FROM ${this.s}.events WHERE seq > $1 AND kind = ANY($2) ORDER BY seq${limit !== void 0 ? ` DESC LIMIT ${Number(limit)}` : ""}`,
|
|
13682
|
+
[fromSeq, [...kinds]]
|
|
13683
|
+
);
|
|
13684
|
+
const rows = res2.rows.map(toRecord);
|
|
13685
|
+
return limit !== void 0 ? rows.reverse() : rows;
|
|
13686
|
+
}
|
|
13687
|
+
if (limit !== void 0) {
|
|
13688
|
+
const res2 = workorderId ? await this.c.query(
|
|
13689
|
+
`SELECT * FROM ${this.s}.events WHERE workorder_id = $1 AND seq > $2 ORDER BY seq DESC LIMIT $3`,
|
|
13690
|
+
[workorderId, fromSeq, limit]
|
|
13691
|
+
) : await this.c.query(`SELECT * FROM ${this.s}.events WHERE seq > $1 ORDER BY seq DESC LIMIT $2`, [fromSeq, limit]);
|
|
13692
|
+
return res2.rows.map(toRecord).reverse();
|
|
13693
|
+
}
|
|
13517
13694
|
const res = workorderId ? await this.c.query(
|
|
13518
13695
|
`SELECT * FROM ${this.s}.events WHERE workorder_id = $1 AND seq > $2 ORDER BY seq`,
|
|
13519
13696
|
[workorderId, fromSeq]
|
|
@@ -14064,7 +14241,7 @@ var init_work = __esm({
|
|
|
14064
14241
|
// 回信 work 不杀前会话(apply 不 kill 主链,节点已完结、无在跑会话),只派发。
|
|
14065
14242
|
async effect(e, ctx) {
|
|
14066
14243
|
if (!e.replyToIssueId) await ctx.cancelPreviousWork(e.nodeId, e.workId);
|
|
14067
|
-
await ctx.dispatchWork(e.workId);
|
|
14244
|
+
await ctx.dispatchWork(e.workId, e.resumeEligible !== void 0 ? { resumeEligible: e.resumeEligible } : void 0);
|
|
14068
14245
|
}
|
|
14069
14246
|
};
|
|
14070
14247
|
workKill = {
|
|
@@ -14226,6 +14403,7 @@ var init_issue = __esm({
|
|
|
14226
14403
|
authorActorId: e.authorActorId,
|
|
14227
14404
|
handActorId: e.handActorId,
|
|
14228
14405
|
raisedByNodeId: e.raisedByNodeId,
|
|
14406
|
+
gapId: e.gapId ?? null,
|
|
14229
14407
|
proposedResolution: null,
|
|
14230
14408
|
proposedBy: null,
|
|
14231
14409
|
proposedAt: null,
|
|
@@ -14291,6 +14469,22 @@ var init_issue = __esm({
|
|
|
14291
14469
|
resolvedNote: e.note,
|
|
14292
14470
|
resolvedViaWorkId: e.viaWorkId
|
|
14293
14471
|
});
|
|
14472
|
+
if (issue2.kind === "gap") {
|
|
14473
|
+
for (const other of state.allIssues()) {
|
|
14474
|
+
if (other.kind === "escalation" && other.resolvedAt === null && other.workorderId === issue2.workorderId && other.gapId === issue2.id) {
|
|
14475
|
+
state.updateIssue(other.id, {
|
|
14476
|
+
proposedResolution: null,
|
|
14477
|
+
proposedBy: null,
|
|
14478
|
+
proposedAt: null,
|
|
14479
|
+
resolvedAt: ctx.at,
|
|
14480
|
+
resolution: "resolved",
|
|
14481
|
+
resolvedBy: "actor:system:engine",
|
|
14482
|
+
resolvedNote: e.note,
|
|
14483
|
+
resolvedViaWorkId: null
|
|
14484
|
+
});
|
|
14485
|
+
}
|
|
14486
|
+
}
|
|
14487
|
+
}
|
|
14294
14488
|
if (issue2.kind === "gap" || issue2.kind === "escalation") {
|
|
14295
14489
|
const nodeId = issue2.raisedByNodeId ?? issue2.aboutNodeId;
|
|
14296
14490
|
if (nodeId) markNodeForRetry(state, nodeId, ctx.at);
|
|
@@ -14461,11 +14655,9 @@ var init_review = __esm({
|
|
|
14461
14655
|
name: "work/accept",
|
|
14462
14656
|
kind: "work.accept",
|
|
14463
14657
|
apply(e, state, ctx) {
|
|
14658
|
+
const block = acceptBlock(state, e.workId);
|
|
14659
|
+
if (block && !(block.overridable && e.override)) return;
|
|
14464
14660
|
const w2 = state.work(e.workId);
|
|
14465
|
-
if (!w2 || w2.deadAt || w2.acceptanceState === "accepted") return;
|
|
14466
|
-
const node = state.node(w2.nodeId);
|
|
14467
|
-
if (!node) return;
|
|
14468
|
-
if (!businessHandoffBarrierSatisfied(state, w2.nodeId, w2.id)) return;
|
|
14469
14661
|
if (w2.replyToIssueId) {
|
|
14470
14662
|
state.updateWork(e.workId, {
|
|
14471
14663
|
acceptanceState: "accepted",
|
|
@@ -14742,8 +14934,10 @@ var init_bus = __esm({
|
|
|
14742
14934
|
};
|
|
14743
14935
|
}
|
|
14744
14936
|
/** 读最近事件(/api/events 首次连接回放 / 调试视图),替代 oplog.read。 */
|
|
14937
|
+
/** 最近 N 条事件(时间线/详情页用)。★ 2026-08-16:limit 以前**声明了但没传下去**——
|
|
14938
|
+
* 百万级事件的病态工单一次性全量拉回内存,直接把 serve 打崩(ws:wo-18c2bc4b 实证)。 */
|
|
14745
14939
|
recentEvents(workorderId, fromSeq, limit = 200) {
|
|
14746
|
-
return this.store.transaction((tx) => tx.listEvents(workorderId, fromSeq));
|
|
14940
|
+
return this.store.transaction((tx) => tx.listEvents(workorderId, fromSeq, limit));
|
|
14747
14941
|
}
|
|
14748
14942
|
/** 取存储引用——供 serve 层创建 IO 适配器时使用。 */
|
|
14749
14943
|
getStore() {
|
|
@@ -15021,7 +15215,7 @@ var init_bus = __esm({
|
|
|
15021
15215
|
eventSeq: record8.seq,
|
|
15022
15216
|
actorId: record8.actorId,
|
|
15023
15217
|
emit: (event, opts) => void buffered.push({ event, ...opts }),
|
|
15024
|
-
dispatchWork: (id) => this.io.dispatchWork(id),
|
|
15218
|
+
dispatchWork: (id, opts) => this.io.dispatchWork(id, opts),
|
|
15025
15219
|
dispatchReview: (id, nodeId, reviewerActorId) => this.io.dispatchReview(id, nodeId, reviewerActorId),
|
|
15026
15220
|
cancelSession: (ref2) => this.io.cancelSession(ref2),
|
|
15027
15221
|
// ★ work.create 的「kill 旧会话」:apply 已把该节点上一个 latest_work 置 deadAt(会话可能还挂着),
|
|
@@ -15491,6 +15685,18 @@ function bridgeLoadReadModel(input) {
|
|
|
15491
15685
|
name: a.name
|
|
15492
15686
|
})));
|
|
15493
15687
|
}
|
|
15688
|
+
const acceptBlockers = /* @__PURE__ */ new Map();
|
|
15689
|
+
for (const snap of snapshots2) {
|
|
15690
|
+
let state = null;
|
|
15691
|
+
for (const node of snap.nodes) {
|
|
15692
|
+
if (!node.latestWorkId || node.latestAcceptId === node.latestWorkId) continue;
|
|
15693
|
+
const lw = snap.works.find((w2) => w2.id === node.latestWorkId);
|
|
15694
|
+
if (!lw || !lw.endedAt || lw.deadAt) continue;
|
|
15695
|
+
state ??= new WorkorderState(snap);
|
|
15696
|
+
const block = acceptBlock(state, lw.id);
|
|
15697
|
+
if (block) acceptBlockers.set(node.id, block);
|
|
15698
|
+
}
|
|
15699
|
+
}
|
|
15494
15700
|
const artifactFiles = /* @__PURE__ */ new Map();
|
|
15495
15701
|
for (const a of allArtifacts) {
|
|
15496
15702
|
const list = artifactFiles.get(a.workId) ?? [];
|
|
@@ -15685,7 +15891,8 @@ function bridgeLoadReadModel(input) {
|
|
|
15685
15891
|
pinMeta,
|
|
15686
15892
|
gaps,
|
|
15687
15893
|
escalations,
|
|
15688
|
-
pendingReviews
|
|
15894
|
+
pendingReviews,
|
|
15895
|
+
acceptBlockers
|
|
15689
15896
|
};
|
|
15690
15897
|
}
|
|
15691
15898
|
var init_bridge_readmodel = __esm({
|
|
@@ -16677,8 +16884,8 @@ var init_kernel_bridge = __esm({
|
|
|
16677
16884
|
const pendingHead = pendingConcludeAttempt(this.model, args.artifactId)?.head;
|
|
16678
16885
|
const targetWorkId = args.revisionId ?? (currentRev && currentRev.state === "proposed" ? currentRev.id : null) ?? pendingHead ?? concludedHead(this.model, args.artifactId);
|
|
16679
16886
|
if (!targetWorkId) throw new Error("no concluded head to review");
|
|
16680
|
-
|
|
16681
|
-
let
|
|
16887
|
+
const verdict = args.verdict === "approve" ? "approve" : "request_changes";
|
|
16888
|
+
let targets = [];
|
|
16682
16889
|
if (args.reviewId !== void 0) {
|
|
16683
16890
|
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
16684
16891
|
const row = snap?.reviews.find((r) => r.id === args.reviewId) ?? null;
|
|
@@ -16698,32 +16905,67 @@ var init_kernel_bridge = __esm({
|
|
|
16698
16905
|
if (row.verdict === null && (row.cancelledAt || row.endedAt)) {
|
|
16699
16906
|
throw new Error(`review ${args.reviewId} is no longer open\uFF08\u8BE5\u8BC4\u5BA1\u884C\u5DF2\u8D85\u65F6\u5931\u8D25/\u88AB\u53D6\u6D88\uFF0C\u8FDF\u5230\u7968\u88AB\u62D2\uFF09`);
|
|
16700
16907
|
}
|
|
16701
|
-
|
|
16702
|
-
|
|
16703
|
-
} else if (!args.revisionId) {
|
|
16908
|
+
targets = [{ id: row.id, group: row.reviewGroup }];
|
|
16909
|
+
} else {
|
|
16704
16910
|
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
16705
|
-
const
|
|
16706
|
-
|
|
16707
|
-
|
|
16911
|
+
const rows = snap?.reviews.filter(
|
|
16912
|
+
(r) => r.targetWorkId === targetWorkId && r.reviewerActorId === args.actor
|
|
16913
|
+
) ?? [];
|
|
16914
|
+
const open2 = rows.filter((r) => isReviewOpen(r));
|
|
16915
|
+
if (open2.length === 0) {
|
|
16916
|
+
const decided = rows.filter((r) => r.verdict !== null);
|
|
16917
|
+
if (decided.length > 0) {
|
|
16918
|
+
if (decided.every((r) => r.verdict === verdict)) {
|
|
16919
|
+
return { status: verdict === "approve" ? "satisfied" : "failed" };
|
|
16920
|
+
}
|
|
16921
|
+
throw new KernelError(
|
|
16922
|
+
`\u4F60\u5DF2\u5BF9\u8FD9\u4E00\u7248\u6295\u8FC7\u300C${decided[0].verdict}\u300D\uFF08${args.artifactId} @ ${targetWorkId}\uFF09\u2014\u2014\u540C\u4E00\u7248\u4E0D\u63A5\u53D7\u6539\u5224\uFF0C\u8981\u91CD\u6295\u8BF7\u5148\u91CD\u65B0\u53D1\u8D77\u8BC4\u5BA1\uFF08oasis rereview / \u63A7\u5236\u53F0\u300C\u91CD\u65B0\u8BC4\u5BA1\u300D\uFF09\u3002`,
|
|
16923
|
+
"review-already-decided"
|
|
16924
|
+
);
|
|
16925
|
+
}
|
|
16926
|
+
const reqs = snap?.requirements.filter((r) => r.nodeId === args.artifactId) ?? [];
|
|
16927
|
+
const mine = reqs.filter((r) => r.reviewerActorId === args.actor);
|
|
16928
|
+
if (mine.length > 0) {
|
|
16929
|
+
targets = [...new Set(mine.map((r) => r.reviewGroup))].map((group) => ({ id: null, group }));
|
|
16930
|
+
} else {
|
|
16931
|
+
const stale = (snap?.reviews ?? []).filter(
|
|
16932
|
+
(r) => r.nodeId === args.artifactId && r.reviewerActorId === args.actor && r.targetWorkId !== targetWorkId
|
|
16933
|
+
);
|
|
16934
|
+
if (reqs.length === 0) {
|
|
16935
|
+
throw new KernelError(
|
|
16936
|
+
`${args.artifactId} \u6CA1\u6709\u8BC4\u5BA1\u8981\u6C42\u2014\u2014\u8FD9\u4E2A\u8282\u70B9\u7684\u6536\u53E3\u4E0D\u9700\u8981\u6295\u7968\uFF0C\u4F60\u7684\u7968\u6CA1\u6709\u53EF\u843D\u7684\u884C\uFF08\u8981\u8D8A\u8FC7\u6D41\u7A0B\u76F4\u63A5\u6536\u53E3\u7528 force-conclude\uFF09\u3002`,
|
|
16937
|
+
"no-review-requirement"
|
|
16938
|
+
);
|
|
16939
|
+
}
|
|
16940
|
+
throw new KernelError(
|
|
16941
|
+
`\u8FD9\u4E00\u7248\u4E0A\u6CA1\u6709\u6D3E\u7ED9\u4F60\u7684\u8BC4\u5BA1\u884C\uFF08${args.artifactId} @ ${targetWorkId}\uFF09\uFF1A\u672C\u7248\u8BC4\u5BA1\u4EBA = [${[...new Set(reqs.map((r) => r.reviewerActorId))].join("\u3001")}]${stale.length > 0 ? `\uFF1B\u4F60\u5728\u65E7\u7248 ${stale[0].targetWorkId} \u4E0A\u6709\u4E00\u884C\uFF0C\u90A3\u4E00\u7248\u5DF2\u7ECF\u4E0D\u662F\u5F53\u524D\u5F85\u5BA1\u7248\u672C` : ""}\u2014\u2014\u7968\u6CA1\u6709\u843D\u8D26\uFF08\u4EE5\u524D\u8FD9\u91CC\u4F1A\u6084\u6084\u65B0\u5EFA\u4E00\u884C\u628A\u7968\u541E\u6389\uFF09\u3002`,
|
|
16942
|
+
"review-row-not-found"
|
|
16943
|
+
);
|
|
16944
|
+
}
|
|
16945
|
+
} else {
|
|
16946
|
+
targets = open2.map((r) => ({ id: r.id, group: r.reviewGroup }));
|
|
16947
|
+
}
|
|
16708
16948
|
}
|
|
16709
|
-
|
|
16710
|
-
|
|
16711
|
-
|
|
16712
|
-
actorId: args.actor,
|
|
16713
|
-
handActorId: args.hand ?? null,
|
|
16714
|
-
event: {
|
|
16715
|
-
kind: "review.response",
|
|
16949
|
+
for (const target of targets) {
|
|
16950
|
+
await this.commit({
|
|
16951
|
+
companyId: "",
|
|
16716
16952
|
workorderId: wid,
|
|
16717
|
-
|
|
16718
|
-
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
|
|
16722
|
-
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16726
|
-
|
|
16953
|
+
actorId: args.actor,
|
|
16954
|
+
handActorId: args.hand ?? null,
|
|
16955
|
+
event: {
|
|
16956
|
+
kind: "review.response",
|
|
16957
|
+
workorderId: wid,
|
|
16958
|
+
reviewId: target.id,
|
|
16959
|
+
// 有行投行;无行时带真实桶名建本人的行(不再是没人指的 default 幽灵行)
|
|
16960
|
+
targetWorkId,
|
|
16961
|
+
reviewerActorId: args.actor,
|
|
16962
|
+
reviewGroup: target.group,
|
|
16963
|
+
verdict,
|
|
16964
|
+
note: args.note ?? null,
|
|
16965
|
+
handActorId: args.hand ?? null
|
|
16966
|
+
}
|
|
16967
|
+
});
|
|
16968
|
+
}
|
|
16727
16969
|
const attempt = this.model.concludeAttempts.get(args.artifactId)?.at(-1);
|
|
16728
16970
|
return attempt ? { status: args.verdict === "approve" ? "satisfied" : "failed" } : { status: "pending" };
|
|
16729
16971
|
}
|
|
@@ -16731,8 +16973,17 @@ var init_kernel_bridge = __esm({
|
|
|
16731
16973
|
const wid = this.wo(args.artifactId);
|
|
16732
16974
|
const artifact = this.model.artifacts.get(args.artifactId);
|
|
16733
16975
|
const currentRev = artifact?.currentRev ? this.model.revisions.get(artifact.currentRev) : void 0;
|
|
16734
|
-
const
|
|
16735
|
-
|
|
16976
|
+
const snap = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
16977
|
+
const head = (currentRev && currentRev.state === "proposed" ? currentRev.id : null) ?? concludedHead(this.model, args.artifactId) ?? (snap?.nodes.find((n) => n.id === args.artifactId)?.latestWorkId ?? null);
|
|
16978
|
+
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
|
+
const block = snap ? acceptBlock(new WorkorderState(snap), head) : null;
|
|
16980
|
+
if (block?.code === "already-accepted") return;
|
|
16981
|
+
if (block && !block.overridable) {
|
|
16982
|
+
throw new KernelError(
|
|
16983
|
+
`force-conclude \u672A\u6267\u884C\uFF08${args.artifactId} @ ${head}\uFF09\uFF1A${block.detail}`,
|
|
16984
|
+
`accept-blocked:${block.code}`
|
|
16985
|
+
);
|
|
16986
|
+
}
|
|
16736
16987
|
await this.commit({
|
|
16737
16988
|
companyId: "",
|
|
16738
16989
|
workorderId: wid,
|
|
@@ -16745,6 +16996,14 @@ var init_kernel_bridge = __esm({
|
|
|
16745
16996
|
override: { by: args.actor, reason: args.reason ?? args.note ?? "force concluded" }
|
|
16746
16997
|
}
|
|
16747
16998
|
});
|
|
16999
|
+
const after = await this.store.transaction((tx) => tx.loadWorkorder(wid));
|
|
17000
|
+
const accepted = after?.works.find((w2) => w2.id === head)?.acceptanceState === "accepted";
|
|
17001
|
+
if (!accepted) {
|
|
17002
|
+
throw new KernelError(
|
|
17003
|
+
`force-conclude \u672A\u751F\u6548\uFF08${args.artifactId} @ ${head}\uFF09\uFF1Awork.accept \u4E8B\u4EF6\u5DF2\u5165 oplog\uFF0C\u4F46 apply \u672A\u6539\u53D8\u9A8C\u6536\u6001${block ? `\u2014\u2014\u4E8B\u524D\u5224\u636E\uFF1A${block.detail}` : "\u2014\u2014\u539F\u56E0\u672A\u77E5\uFF0C\u8BF7\u67E5 work/node \u73B0\u72B6"}`,
|
|
17004
|
+
"accept-no-effect"
|
|
17005
|
+
);
|
|
17006
|
+
}
|
|
16748
17007
|
}
|
|
16749
17008
|
async groupCommit(args) {
|
|
16750
17009
|
await this.merge({ artifactId: args.artifactId, actor: args.actor });
|
|
@@ -16953,6 +17212,7 @@ var init_kernel_bridge = __esm({
|
|
|
16953
17212
|
issueId,
|
|
16954
17213
|
issueKind: "escalation",
|
|
16955
17214
|
body,
|
|
17215
|
+
gapId: args.gapId,
|
|
16956
17216
|
aboutNodeId: args.artifactId,
|
|
16957
17217
|
aboutWorkId: null,
|
|
16958
17218
|
// ★ 修复(实证 ws:wo-a558f8cc):raisedByNodeId 只在 **agent/人自提**(/api/cmd escalate)时填节点——
|
|
@@ -17234,7 +17494,10 @@ var init_kernel_bridge = __esm({
|
|
|
17234
17494
|
});
|
|
17235
17495
|
}
|
|
17236
17496
|
async bumpPin(args) {
|
|
17237
|
-
|
|
17497
|
+
throw new KernelError(
|
|
17498
|
+
`pin \u5DF2\u5E9F\u5F03\uFF08${args.artifactId} \u2192 ${args.to}@${args.pinned} \u672A\u6267\u884C\uFF09\uFF1A\u65B0\u5F15\u64CE\u7684\u4E0B\u6E38\u7248\u672C\u56FA\u5B9A\u7531 work.accept \u81EA\u52A8\u63A8\u8FDB\u2014\u2014\u8981\u8BA9\u4E0B\u6E38\u6362\u7248\uFF0C\u53BB\u9A8C\u6536\u4E0A\u6E38\u5BF9\u5E94\u7684\u90A3\u4E00\u7248\uFF08conclude / force-conclude\uFF09\u3002`,
|
|
17499
|
+
"deprecated-op"
|
|
17500
|
+
);
|
|
17238
17501
|
}
|
|
17239
17502
|
async recordToolCall(_args) {
|
|
17240
17503
|
}
|
|
@@ -17331,6 +17594,7 @@ var init_kernel_bridge = __esm({
|
|
|
17331
17594
|
}
|
|
17332
17595
|
const wid = plan.workspace ?? plan.ops.find((o) => o.action === "spawn" && o.workspace)?.workspace ?? "ws:default";
|
|
17333
17596
|
let applied = 0;
|
|
17597
|
+
const unsupported = [];
|
|
17334
17598
|
await this.ensureWorkorder(wid, "", _actor);
|
|
17335
17599
|
for (const op of plan.ops) {
|
|
17336
17600
|
switch (op.action) {
|
|
@@ -17469,6 +17733,7 @@ var init_kernel_bridge = __esm({
|
|
|
17469
17733
|
break;
|
|
17470
17734
|
}
|
|
17471
17735
|
default:
|
|
17736
|
+
unsupported.push(`${op.action}\uFF08${op.artifactId ?? "?"}\uFF09`);
|
|
17472
17737
|
break;
|
|
17473
17738
|
}
|
|
17474
17739
|
}
|
|
@@ -17492,6 +17757,12 @@ var init_kernel_bridge = __esm({
|
|
|
17492
17757
|
this.draftCards.delete(plan.resolvesProposal);
|
|
17493
17758
|
await this.refreshModel();
|
|
17494
17759
|
}
|
|
17760
|
+
if (unsupported.length > 0) {
|
|
17761
|
+
throw new KernelError(
|
|
17762
|
+
`\u5DF2\u843D\u5730 ${applied} \u6761 op\uFF1B\u4EE5\u4E0B op \u672C\u5F15\u64CE\u4E0D\u652F\u6301\u3001\u672A\u6267\u884C\uFF1A${unsupported.join("\u3001")}\uFF08pin \u5DF2\u5E9F\u5F03\u2014\u2014\u4E0B\u6E38\u7248\u672C\u7531\u9A8C\u6536\u4E0A\u6E38\u81EA\u52A8\u63A8\u8FDB\uFF09`,
|
|
17763
|
+
"unsupported-op"
|
|
17764
|
+
);
|
|
17765
|
+
}
|
|
17495
17766
|
return { interventionId: `intervention:${(0, import_node_crypto3.randomUUID)()}`, applied };
|
|
17496
17767
|
}
|
|
17497
17768
|
};
|
|
@@ -17516,6 +17787,7 @@ var init_src4 = __esm({
|
|
|
17516
17787
|
init_workorder_ledger();
|
|
17517
17788
|
init_closure_gate();
|
|
17518
17789
|
init_exit_classify();
|
|
17790
|
+
init_produce_resume_fallback();
|
|
17519
17791
|
init_dispatcher();
|
|
17520
17792
|
init_session_limits();
|
|
17521
17793
|
init_kernel_bridge();
|
|
@@ -160686,6 +160958,9 @@ ${task}` : task;
|
|
|
160686
160958
|
});
|
|
160687
160959
|
|
|
160688
160960
|
// ../adapters/src/_core/workdir-gc.ts
|
|
160961
|
+
function reclaimableBytes(c) {
|
|
160962
|
+
return c.sizeBytes + (c.runtimeStateBytes ?? 0);
|
|
160963
|
+
}
|
|
160689
160964
|
function idleMs(c, now) {
|
|
160690
160965
|
const last = c.meta?.lastUsedAt ? Date.parse(c.meta.lastUsedAt) : NaN;
|
|
160691
160966
|
return now - (Number.isFinite(last) ? last : c.mtimeMs);
|
|
@@ -160706,19 +160981,58 @@ function decideOne(c, policy, now) {
|
|
|
160706
160981
|
}
|
|
160707
160982
|
if (c.artifactState === void 0) return mk("keep", "artifact-unknown");
|
|
160708
160983
|
if (c.artifactState === "active") {
|
|
160709
|
-
|
|
160710
|
-
return c.meta.lastExit && idle > policy.abnormalGraceMs ? mk("delete", "one-shot-ttl") : mk("keep", "artifact-active");
|
|
160984
|
+
return mk("keep", "artifact-active");
|
|
160711
160985
|
}
|
|
160712
|
-
if (c.meta.lastExit?.reason === "clean") return mk("delete", "settled-clean");
|
|
160713
160986
|
return idle > policy.ttlMs ? mk("delete", "settled-ttl") : mk("keep", "within-ttl");
|
|
160714
160987
|
}
|
|
160715
160988
|
function planGc(candidates, policy, now) {
|
|
160716
160989
|
const decisions = candidates.map((c) => decideOne(c, policy, now));
|
|
160717
|
-
const { highWaterBytes, lowWaterBytes } = policy;
|
|
160718
|
-
if (highWaterBytes === void 0) return decisions;
|
|
160719
160990
|
const byDir = new Map(candidates.map((c) => [c.dir, c]));
|
|
160991
|
+
applyRetentionBudget(decisions, byDir, policy, now);
|
|
160992
|
+
applyHighWaterLru(decisions, byDir, policy, now);
|
|
160993
|
+
return decisions;
|
|
160994
|
+
}
|
|
160995
|
+
function applyRetentionBudget(decisions, byDir, policy, now) {
|
|
160996
|
+
const maxRounds = policy.maxRoundsPerArtifact;
|
|
160997
|
+
const capBytes = policy.sessionStoreCapBytes;
|
|
160998
|
+
const evictable = () => decisions.filter((d) => d.action === "keep").map((d) => ({ d, c: byDir.get(d.dir) })).filter(({ c }) => !c.live && (c.artifactState === "active" || c.artifactState === "settled"));
|
|
160999
|
+
if (maxRounds !== void 0 && maxRounds >= 0) {
|
|
161000
|
+
const byArtifact = /* @__PURE__ */ new Map();
|
|
161001
|
+
for (const item of evictable()) {
|
|
161002
|
+
const id = item.c.meta?.artifactId;
|
|
161003
|
+
if (!id) continue;
|
|
161004
|
+
(byArtifact.get(id) ?? byArtifact.set(id, []).get(id)).push(item);
|
|
161005
|
+
}
|
|
161006
|
+
for (const group of byArtifact.values()) {
|
|
161007
|
+
if (group.length <= maxRounds) continue;
|
|
161008
|
+
group.sort((a, b2) => idleMs(a.c, now) - idleMs(b2.c, now));
|
|
161009
|
+
for (const { d } of group.slice(maxRounds)) {
|
|
161010
|
+
d.action = "delete";
|
|
161011
|
+
d.reason = "budget-rounds";
|
|
161012
|
+
}
|
|
161013
|
+
}
|
|
161014
|
+
}
|
|
161015
|
+
if (capBytes !== void 0) {
|
|
161016
|
+
let remaining = decisions.filter((d) => d.action === "keep").reduce((sum, d) => sum + reclaimableBytes(byDir.get(d.dir)), 0);
|
|
161017
|
+
if (remaining > capBytes) {
|
|
161018
|
+
const tier = (c) => c.artifactState === "settled" ? 0 : 1;
|
|
161019
|
+
const order = evictable().sort(
|
|
161020
|
+
(a, b2) => tier(a.c) - tier(b2.c) || idleMs(b2.c, now) - idleMs(a.c, now)
|
|
161021
|
+
);
|
|
161022
|
+
for (const { d, c } of order) {
|
|
161023
|
+
if (remaining <= capBytes) break;
|
|
161024
|
+
d.action = "delete";
|
|
161025
|
+
d.reason = "budget-cap";
|
|
161026
|
+
remaining -= reclaimableBytes(c);
|
|
161027
|
+
}
|
|
161028
|
+
}
|
|
161029
|
+
}
|
|
161030
|
+
}
|
|
161031
|
+
function applyHighWaterLru(decisions, byDir, policy, now) {
|
|
161032
|
+
const { highWaterBytes, lowWaterBytes } = policy;
|
|
161033
|
+
if (highWaterBytes === void 0) return;
|
|
160720
161034
|
let remaining = decisions.filter((d) => d.action === "keep").reduce((sum, d) => sum + d.sizeBytes, 0);
|
|
160721
|
-
if (remaining <= highWaterBytes) return
|
|
161035
|
+
if (remaining <= highWaterBytes) return;
|
|
160722
161036
|
const target = lowWaterBytes ?? highWaterBytes;
|
|
160723
161037
|
const lru = decisions.filter((d) => d.action === "keep").map((d) => ({ d, c: byDir.get(d.dir) })).filter(({ c }) => !c.live && c.artifactState === "settled").sort((a, b2) => idleMs(b2.c, now) - idleMs(a.c, now));
|
|
160724
161038
|
for (const { d } of lru) {
|
|
@@ -160727,17 +161041,23 @@ function planGc(candidates, policy, now) {
|
|
|
160727
161041
|
d.reason = "high-water-lru";
|
|
160728
161042
|
remaining -= d.sizeBytes;
|
|
160729
161043
|
}
|
|
160730
|
-
return decisions;
|
|
160731
161044
|
}
|
|
160732
|
-
function gcAccounting(decisions, policy) {
|
|
160733
|
-
const
|
|
160734
|
-
const
|
|
161045
|
+
function gcAccounting(decisions, policy, candidates) {
|
|
161046
|
+
const byDir = candidates ? new Map(candidates.map((c) => [c.dir, c])) : void 0;
|
|
161047
|
+
const sizeOf = (d) => {
|
|
161048
|
+
const c = byDir?.get(d.dir);
|
|
161049
|
+
return c ? reclaimableBytes(c) : d.sizeBytes;
|
|
161050
|
+
};
|
|
161051
|
+
const totalBytes = decisions.reduce((s2, d) => s2 + sizeOf(d), 0);
|
|
161052
|
+
const freedBytes = decisions.filter((d) => d.action === "delete").reduce((s2, d) => s2 + sizeOf(d), 0);
|
|
160735
161053
|
const keptBytes = totalBytes - freedBytes;
|
|
161054
|
+
const cap = policy.sessionStoreCapBytes;
|
|
160736
161055
|
return {
|
|
160737
161056
|
totalBytes,
|
|
160738
161057
|
keptBytes,
|
|
160739
161058
|
freedBytes,
|
|
160740
|
-
|
|
161059
|
+
// 高水位报警:旧 highWaterBytes 或新总量上限任一被突破都报(新上限缺省即开)。
|
|
161060
|
+
overHighWater: policy.highWaterBytes !== void 0 && keptBytes > policy.highWaterBytes || cap !== void 0 && keptBytes > cap
|
|
160741
161061
|
};
|
|
160742
161062
|
}
|
|
160743
161063
|
var DEFAULT_GC_POLICY;
|
|
@@ -160748,7 +161068,9 @@ var init_workdir_gc = __esm({
|
|
|
160748
161068
|
orphanTtlMs: 24 * 36e5,
|
|
160749
161069
|
ttlMs: 7 * 24 * 36e5,
|
|
160750
161070
|
chatTtlMs: 14 * 24 * 36e5,
|
|
160751
|
-
abnormalGraceMs: 24 * 36e5
|
|
161071
|
+
abnormalGraceMs: 24 * 36e5,
|
|
161072
|
+
maxRoundsPerArtifact: 3,
|
|
161073
|
+
sessionStoreCapBytes: 2 * 1024 ** 3
|
|
160752
161074
|
};
|
|
160753
161075
|
}
|
|
160754
161076
|
});
|
|
@@ -160819,14 +161141,20 @@ function kindFromPath(workRoot, dir) {
|
|
|
160819
161141
|
if (parts[0] === "tmp" && parts[1]) return parts[1].replace(/-[^-]*$/, "");
|
|
160820
161142
|
return void 0;
|
|
160821
161143
|
}
|
|
160822
|
-
function observe(dir) {
|
|
161144
|
+
function observe(dir, workRoot, measureRuntimeState) {
|
|
160823
161145
|
const meta = readMeta(dir);
|
|
160824
161146
|
let mtimeMs = 0;
|
|
160825
161147
|
try {
|
|
160826
161148
|
mtimeMs = import_node_fs11.default.statSync(dir).mtimeMs;
|
|
160827
161149
|
} catch {
|
|
160828
161150
|
}
|
|
160829
|
-
|
|
161151
|
+
const runtimeKind = meta?.runtimeKind ?? kindFromPath(workRoot, dir);
|
|
161152
|
+
let runtimeStateBytes = 0;
|
|
161153
|
+
try {
|
|
161154
|
+
runtimeStateBytes = measureRuntimeState?.(dir, runtimeKind) ?? 0;
|
|
161155
|
+
} catch {
|
|
161156
|
+
}
|
|
161157
|
+
return { dir, meta, sizeBytes: dirSizeBytes(dir), runtimeStateBytes, mtimeMs, live: isWorkdirLive(dir) };
|
|
160830
161158
|
}
|
|
160831
161159
|
async function runGcSweep(deps) {
|
|
160832
161160
|
const policy = { ...DEFAULT_GC_POLICY, ...deps.policy };
|
|
@@ -160834,7 +161162,7 @@ async function runGcSweep(deps) {
|
|
|
160834
161162
|
const log3 = deps.log ?? (() => {
|
|
160835
161163
|
});
|
|
160836
161164
|
const dirs = listWorkdirs(deps.workRoot, deps.legacyRoot);
|
|
160837
|
-
const candidates = dirs.map((d) => observe(d));
|
|
161165
|
+
const candidates = dirs.map((d) => observe(d, deps.workRoot, deps.measureRuntimeState));
|
|
160838
161166
|
const needStates = candidates.filter(
|
|
160839
161167
|
(c) => c.meta?.artifactId && c.meta.kind !== "chat" && c.meta.kind !== "one-shot"
|
|
160840
161168
|
);
|
|
@@ -160852,7 +161180,7 @@ async function runGcSweep(deps) {
|
|
|
160852
161180
|
if (st) c.artifactState = st;
|
|
160853
161181
|
}
|
|
160854
161182
|
const decisions = planGc(candidates, policy, now);
|
|
160855
|
-
const acct = gcAccounting(decisions, policy);
|
|
161183
|
+
const acct = gcAccounting(decisions, policy, candidates);
|
|
160856
161184
|
let deleted = 0;
|
|
160857
161185
|
for (const d of decisions) {
|
|
160858
161186
|
if (d.action !== "delete") continue;
|
|
@@ -164661,7 +164989,13 @@ async function startOasisServer(opts) {
|
|
|
164661
164989
|
try {
|
|
164662
164990
|
let companyId;
|
|
164663
164991
|
if (companyCtx) {
|
|
164664
|
-
if (companyCtx.kind === "not-found")
|
|
164992
|
+
if (companyCtx.kind === "not-found") {
|
|
164993
|
+
throw companyCtx.selector ? new ApiError(404, "COMPANY_NOT_FOUND", `\u8BF7\u6C42\u6307\u5B9A\u7684\u516C\u53F8\u4E0D\u5B58\u5728\uFF1A${companyCtx.selector}`) : new ApiError(
|
|
164994
|
+
404,
|
|
164995
|
+
"COMPANY_UNRESOLVED",
|
|
164996
|
+
`\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u516C\u53F8\uFF1A${actor} \u672A\u6307\u5B9A\u516C\u53F8\uFF08x-oasis-company\uFF09\uFF0C\u81EA\u8EAB\u4E5F\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u516C\u53F8\u3002agent \u4F1A\u8BDD\u5C5E\u4E8E\u5176\u88AB\u6D3E\u53D1\u7684\u90A3\u5BB6\u516C\u53F8\uFF0C\u5E94\u7531\u6D3E\u53D1\u65B9\u5728\u4F1A\u8BDD token \u91CC\u7B7E companyId\u2014\u2014\u6536\u5230\u672C\u9519\u8BEF\u901A\u5E38\u662F\u7B7E\u53D1\u4FA7\u6F0F\u7B7E\uFF0C\u800C\u975E\u516C\u53F8\u8BB0\u5F55\u6709\u95EE\u9898\u3002`
|
|
164997
|
+
);
|
|
164998
|
+
}
|
|
164665
164999
|
if (companyCtx.kind === "forbidden") throw new ApiError(403, "NOT_A_MEMBER", "\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF0C\u8BBF\u95EE\u88AB\u62D2");
|
|
164666
165000
|
if (companyCtx.kind === "ok") companyId = companyCtx.companyId;
|
|
164667
165001
|
}
|
|
@@ -168146,6 +168480,7 @@ var init_service3 = __esm({
|
|
|
168146
168480
|
* 队首若 stale(base ≠ 当前 head,文档层乱序 approve 会撞上)→ 内核拒绝并指 rebase/group-commit。
|
|
168147
168481
|
*/
|
|
168148
168482
|
async mergeUpTo(artifactId, revisionId, actor) {
|
|
168483
|
+
let voted = false;
|
|
168149
168484
|
for (let guard = 0; guard < 1e3; guard++) {
|
|
168150
168485
|
const target = this.kernel.model.revisions.get(revisionId);
|
|
168151
168486
|
if (!target) throw new Error(`revision not found in kernel: ${revisionId}`);
|
|
@@ -168153,7 +168488,14 @@ var init_service3 = __esm({
|
|
|
168153
168488
|
if (target.state === "working") {
|
|
168154
168489
|
await this.kernel.merge({ artifactId, actor });
|
|
168155
168490
|
} else if (target.state === "proposed") {
|
|
168491
|
+
if (voted) {
|
|
168492
|
+
const gate = pendingConcludeAttempt(this.kernel.model, artifactId)?.gate;
|
|
168493
|
+
throw new Error(
|
|
168494
|
+
`\u5DF2\u8BB0\u5F55\u4F60\u7684\u901A\u8FC7\u7968\uFF0C\u4F46\u8FD9\u4E00\u7248\u8FD8\u6CA1\u6536\u9F50\u8BC4\u5BA1${gate ? `\uFF08\u9700 ${gate.quorum} \u7968\uFF0C\u8BC4\u5BA1\u4EBA [${gate.eligible.join("\u3001")}]\uFF09` : ""}\u2014\u2014\u7B49\u5176\u4F59\u8BC4\u5BA1\u4EBA\u8868\u6001\u540E\u81EA\u52A8\u843D\u5730\u3002`
|
|
168495
|
+
);
|
|
168496
|
+
}
|
|
168156
168497
|
await this.kernel.submitReview({ artifactId, actor, verdict: "approve", revisionId });
|
|
168498
|
+
voted = true;
|
|
168157
168499
|
} else {
|
|
168158
168500
|
throw new Error(`revision not approvable (state=${target.state}): ${revisionId}`);
|
|
168159
168501
|
}
|
|
@@ -180387,7 +180729,7 @@ function collabDomain(opts) {
|
|
|
180387
180729
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
180388
180730
|
const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
|
|
180389
180731
|
const wsArts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === req.params.id);
|
|
180390
|
-
const coordinatorEvents = engineStore ? await engineStore.transaction((tx) => tx.listEvents(req.params.id)) : void 0;
|
|
180732
|
+
const coordinatorEvents = engineStore ? await engineStore.transaction((tx) => tx.listEvents(req.params.id, 0, void 0, COORDINATOR_ACTIVITY_EVENT_KINDS)) : void 0;
|
|
180391
180733
|
const signalsForThisCompany = opts.dispatcherSignalsOf ? (id) => opts.dispatcherSignalsOf(req.auth.companyId, id) : void 0;
|
|
180392
180734
|
const roleDefsByName = opts.roles ? new Map((await opts.roles.list()).map((r) => [r.name, { label: r.label, defaultOwner: r.defaultOwner }])) : void 0;
|
|
180393
180735
|
const roleDefaultOwnerOf = roleDefsByName ? (role) => {
|
|
@@ -180739,6 +181081,7 @@ function collabDomain(opts) {
|
|
|
180739
181081
|
});
|
|
180740
181082
|
};
|
|
180741
181083
|
}
|
|
181084
|
+
var COORDINATOR_ACTIVITY_EVENT_KINDS;
|
|
180742
181085
|
var init_collab = __esm({
|
|
180743
181086
|
"../server/src/domains/collab/index.ts"() {
|
|
180744
181087
|
"use strict";
|
|
@@ -180764,6 +181107,7 @@ var init_collab = __esm({
|
|
|
180764
181107
|
init_organization_usage();
|
|
180765
181108
|
init_planner();
|
|
180766
181109
|
init_closure_report();
|
|
181110
|
+
COORDINATOR_ACTIVITY_EVENT_KINDS = ["plan.changed", "issue.create", "issue.resolve"];
|
|
180767
181111
|
}
|
|
180768
181112
|
});
|
|
180769
181113
|
|
|
@@ -182041,6 +182385,7 @@ var init_service7 = __esm({
|
|
|
182041
182385
|
onCheckpointState;
|
|
182042
182386
|
readBundleFile;
|
|
182043
182387
|
onAgentHandoffPersisted;
|
|
182388
|
+
resolveNodeConclusions;
|
|
182044
182389
|
constructor(opts) {
|
|
182045
182390
|
this.store = opts.store;
|
|
182046
182391
|
this.mode = opts.mode;
|
|
@@ -182051,6 +182396,7 @@ var init_service7 = __esm({
|
|
|
182051
182396
|
this.onCheckpointState = opts.onCheckpointState;
|
|
182052
182397
|
this.readBundleFile = opts.readBundleFile;
|
|
182053
182398
|
this.onAgentHandoffPersisted = opts.onAgentHandoffPersisted;
|
|
182399
|
+
this.resolveNodeConclusions = opts.resolveNodeConclusions;
|
|
182054
182400
|
}
|
|
182055
182401
|
assertWritable() {
|
|
182056
182402
|
if (this.mode === "off") throw new Error("execution continuity is disabled");
|
|
@@ -182246,6 +182592,12 @@ var init_service7 = __esm({
|
|
|
182246
182592
|
async coordinatorView(workOrderId, companyId) {
|
|
182247
182593
|
const graph = await this.resolveGraph(workOrderId, companyId);
|
|
182248
182594
|
if (graph.length === 0) return null;
|
|
182595
|
+
let conclusions = {};
|
|
182596
|
+
try {
|
|
182597
|
+
conclusions = await this.resolveNodeConclusions?.(workOrderId, companyId) ?? {};
|
|
182598
|
+
} catch (error2) {
|
|
182599
|
+
console.warn(`[execution-continuity] conclusion \u8BFB\u53D6\u5931\u8D25\uFF08\u534F\u8C03\u8005\u89C6\u56FE\u7167\u5E38\u8FD4\u56DE\uFF09: ${String(error2)}`);
|
|
182600
|
+
}
|
|
182249
182601
|
const attempts = await this.store.listAttempts(workOrderId);
|
|
182250
182602
|
const latest = latestAttemptByNode(attempts);
|
|
182251
182603
|
const handoffs = /* @__PURE__ */ new Map();
|
|
@@ -182309,7 +182661,10 @@ var init_service7 = __esm({
|
|
|
182309
182661
|
recoverability: checkpoint.recoverability,
|
|
182310
182662
|
lastCheckpointAt: checkpoint.createdAt
|
|
182311
182663
|
} } : {},
|
|
182312
|
-
blockedBy: nodeState === "waiting" ? node.dependsOn.filter((id) => !DONE_STATES.has(stateByNode.get(id) ?? "waiting")) : []
|
|
182664
|
+
blockedBy: nodeState === "waiting" ? node.dependsOn.filter((id) => !DONE_STATES.has(stateByNode.get(id) ?? "waiting")) : [],
|
|
182665
|
+
// 纯透传(展示层兜底料源,见 CoordinatorNodeView.conclusion)。不参与本方法里任何
|
|
182666
|
+
// nodeState / todo / dependencyBlocks / collaborationStatus 的派生。
|
|
182667
|
+
...conclusions[node.nodeId]?.trim() ? { conclusion: conclusions[node.nodeId].trim() } : {}
|
|
182313
182668
|
};
|
|
182314
182669
|
});
|
|
182315
182670
|
const reverse = reverseEdges(graph);
|
|
@@ -182457,6 +182812,23 @@ var init_upstream_handoff_input = __esm({
|
|
|
182457
182812
|
}
|
|
182458
182813
|
});
|
|
182459
182814
|
|
|
182815
|
+
// ../server/src/domains/execution-continuity/node-conclusions.ts
|
|
182816
|
+
function nodeConclusionsFromSnapshot(snapshot) {
|
|
182817
|
+
const workById = new Map(snapshot.works.map((work) => [work.id, work]));
|
|
182818
|
+
const out = {};
|
|
182819
|
+
for (const node of snapshot.nodes) {
|
|
182820
|
+
const accepted = node.latestAcceptId ? workById.get(node.latestAcceptId) : void 0;
|
|
182821
|
+
const conclusion = accepted?.conclusion?.trim() || snapshot.works.filter((work) => work.nodeId === node.id && work.status === "success" && work.conclusion?.trim()).sort((a, b2) => a.createdAt.localeCompare(b2.createdAt)).at(-1)?.conclusion?.trim();
|
|
182822
|
+
if (conclusion) out[node.id] = conclusion;
|
|
182823
|
+
}
|
|
182824
|
+
return out;
|
|
182825
|
+
}
|
|
182826
|
+
var init_node_conclusions = __esm({
|
|
182827
|
+
"../server/src/domains/execution-continuity/node-conclusions.ts"() {
|
|
182828
|
+
"use strict";
|
|
182829
|
+
}
|
|
182830
|
+
});
|
|
182831
|
+
|
|
182460
182832
|
// ../server/src/domains/execution-continuity/index.ts
|
|
182461
182833
|
function createExecutionContinuityDomain(opts) {
|
|
182462
182834
|
const service = new ExecutionContinuityService(opts);
|
|
@@ -182471,6 +182843,7 @@ var init_execution_continuity2 = __esm({
|
|
|
182471
182843
|
init_routes8();
|
|
182472
182844
|
init_handoff_work_bridge();
|
|
182473
182845
|
init_upstream_handoff_input();
|
|
182846
|
+
init_node_conclusions();
|
|
182474
182847
|
}
|
|
182475
182848
|
});
|
|
182476
182849
|
|
|
@@ -186141,15 +186514,16 @@ var init_builtin_skills = __esm({
|
|
|
186141
186514
|
|
|
186142
186515
|
// ../server/src/design/board-watch.ts
|
|
186143
186516
|
function decideBoardLetter(c, nowMs, quietMs) {
|
|
186517
|
+
if (c.hasOpenWatchLetter) return false;
|
|
186518
|
+
if (c.freshTerminalRunId !== null) return true;
|
|
186144
186519
|
if (c.boardMtime === null) return false;
|
|
186145
186520
|
if (!c.hasRevision) return false;
|
|
186146
186521
|
if (c.lastOpAtMs === null || c.boardMtime <= c.lastOpAtMs) return false;
|
|
186147
186522
|
if (nowMs - c.boardMtime < quietMs) return false;
|
|
186148
|
-
if (c.hasOpenWatchLetter) return false;
|
|
186149
186523
|
if (c.lastWatchMtime !== null && c.boardMtime <= c.lastWatchMtime) return false;
|
|
186150
186524
|
return true;
|
|
186151
186525
|
}
|
|
186152
|
-
var MARKER_PREFIX, boardWatchMarker, parseMarker, BoardWatcher;
|
|
186526
|
+
var MARKER_PREFIX, boardWatchMarker, parseMarker, RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, TERMINAL_RUN_STATUS, BoardWatcher;
|
|
186153
186527
|
var init_board_watch = __esm({
|
|
186154
186528
|
"../server/src/design/board-watch.ts"() {
|
|
186155
186529
|
"use strict";
|
|
@@ -186162,6 +186536,17 @@ var init_board_watch = __esm({
|
|
|
186162
186536
|
const n = Number.parseInt(body.slice(i + MARKER_PREFIX.length), 10);
|
|
186163
186537
|
return Number.isFinite(n) ? n : null;
|
|
186164
186538
|
};
|
|
186539
|
+
RUN_MARKER_PREFIX = "[od-board-watch run:";
|
|
186540
|
+
boardWatchRunMarker = (runId) => `${RUN_MARKER_PREFIX}${runId}]`;
|
|
186541
|
+
parseRunMarker = (body) => {
|
|
186542
|
+
const i = body.indexOf(RUN_MARKER_PREFIX);
|
|
186543
|
+
if (i < 0) return null;
|
|
186544
|
+
const j = body.indexOf("]", i + RUN_MARKER_PREFIX.length);
|
|
186545
|
+
if (j < 0) return null;
|
|
186546
|
+
const id = body.slice(i + RUN_MARKER_PREFIX.length, j);
|
|
186547
|
+
return id.length > 0 ? id : null;
|
|
186548
|
+
};
|
|
186549
|
+
TERMINAL_RUN_STATUS = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled", "cancelled"]);
|
|
186165
186550
|
BoardWatcher = class {
|
|
186166
186551
|
constructor(deps) {
|
|
186167
186552
|
this.deps = deps;
|
|
@@ -186178,6 +186563,22 @@ var init_board_watch = __esm({
|
|
|
186178
186563
|
return null;
|
|
186179
186564
|
}
|
|
186180
186565
|
}
|
|
186566
|
+
/**
|
|
186567
|
+
* 查一块板的 run 列表(`GET /api/runs?projectId=`,与 CLI `design status` 同源)。
|
|
186568
|
+
* run 注册表是 od 的**内存态**——重启即空,查不到不代表没跑过;所以这里失败一律降级成空数组,
|
|
186569
|
+
* 由板文件 mtime 那条慢路径兜底,绝不因为查不到 run 就压掉写信。
|
|
186570
|
+
*/
|
|
186571
|
+
async boardRuns(boardKey) {
|
|
186572
|
+
const fetchImpl = this.deps.fetchImpl ?? fetch;
|
|
186573
|
+
try {
|
|
186574
|
+
const res = await fetchImpl(`${this.deps.odBase}/api/runs?projectId=${encodeURIComponent(boardKey)}`);
|
|
186575
|
+
if (!res.ok) return [];
|
|
186576
|
+
const body = await res.json();
|
|
186577
|
+
return (body.runs ?? []).map((r) => ({ runId: String(r["runId"] ?? r["id"] ?? ""), status: String(r["status"] ?? "unknown") })).filter((r) => r.runId.length > 0);
|
|
186578
|
+
} catch {
|
|
186579
|
+
return [];
|
|
186580
|
+
}
|
|
186581
|
+
}
|
|
186181
186582
|
async tick(now = Date.now()) {
|
|
186182
186583
|
const { kernel, boardKeyOf, log: log3 } = this.deps;
|
|
186183
186584
|
const quietMs = this.deps.quietMs ?? 10 * 6e4;
|
|
@@ -186194,9 +186595,17 @@ var init_board_watch = __esm({
|
|
|
186194
186595
|
}
|
|
186195
186596
|
const boardKey = boardKeyOf(a.id);
|
|
186196
186597
|
const files = await this.boardFiles(boardKey);
|
|
186197
|
-
if (
|
|
186598
|
+
if (files === null) continue;
|
|
186599
|
+
const runs = await this.boardRuns(boardKey);
|
|
186600
|
+
if (files.length === 0 && runs.length === 0) continue;
|
|
186198
186601
|
const boardMtime = files.reduce((mx, f2) => f2.mtime !== null && (mx === null || f2.mtime > mx) ? f2.mtime : mx, null);
|
|
186199
|
-
const watchLetters = annotationsOf(model, a.id).filter(
|
|
186602
|
+
const watchLetters = annotationsOf(model, a.id).filter(
|
|
186603
|
+
(x2) => x2.author === actor && (parseMarker(x2.body) !== null || parseRunMarker(x2.body) !== null)
|
|
186604
|
+
);
|
|
186605
|
+
const letteredRuns = new Set(
|
|
186606
|
+
watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null)
|
|
186607
|
+
);
|
|
186608
|
+
const newestTerminalRun = runs.find((r) => TERMINAL_RUN_STATUS.has(r.status)) ?? null;
|
|
186200
186609
|
const lastOpAt = model.lastOpAt.get(a.id);
|
|
186201
186610
|
const candidate = {
|
|
186202
186611
|
boardMtime,
|
|
@@ -186206,21 +186615,41 @@ var init_board_watch = __esm({
|
|
|
186206
186615
|
lastWatchMtime: watchLetters.reduce((mx, x2) => {
|
|
186207
186616
|
const m2 = parseMarker(x2.body);
|
|
186208
186617
|
return m2 !== null && (mx === null || m2 > mx) ? m2 : mx;
|
|
186209
|
-
}, null)
|
|
186618
|
+
}, null),
|
|
186619
|
+
freshTerminalRunId: newestTerminalRun !== null && !letteredRuns.has(newestTerminalRun.runId) ? newestTerminalRun.runId : null
|
|
186210
186620
|
};
|
|
186211
186621
|
if (!decideBoardLetter(candidate, now, quietMs)) continue;
|
|
186212
|
-
const
|
|
186213
|
-
|
|
186214
|
-
|
|
186215
|
-
|
|
186216
|
-
|
|
186217
|
-
|
|
186218
|
-
|
|
186219
|
-
|
|
186220
|
-
|
|
186622
|
+
const fmt = (fs34) => fs34.slice(0, 5).map((f2) => `- ${f2.path}\uFF08${f2.kind ?? "?"}\uFF09\xB7 ${f2.mtime ? new Date(f2.mtime).toISOString() : "?"}`).join("\n");
|
|
186623
|
+
let body;
|
|
186624
|
+
let logLine;
|
|
186625
|
+
if (candidate.freshTerminalRunId !== null) {
|
|
186626
|
+
const status = newestTerminalRun.status;
|
|
186627
|
+
const ok = status === "succeeded";
|
|
186628
|
+
const listed = files.length > 0 ? fmt([...files].sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0))) : "\uFF08\u677F\u4E0A\u6682\u65E0\u4EA4\u4ED8\u7269\uFF09";
|
|
186629
|
+
body = [
|
|
186630
|
+
// 两个 marker 都带:run marker 管快路径去重,mtime marker 顺手把慢路径也盖住——
|
|
186631
|
+
// 否则这封信被 resolve 后,慢路径会就**同一批板内容**再写一封(两路去重各管各的)。
|
|
186632
|
+
boardMtime !== null ? `${boardWatchRunMarker(candidate.freshTerminalRunId)} ${boardWatchMarker(boardMtime)}` : boardWatchRunMarker(candidate.freshTerminalRunId),
|
|
186633
|
+
ok ? `\u753B\u677F ${boardKey} \u7684\u5F15\u64CE\u8DD1\u5B8C\u4E86\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} \u7EC8\u6001=${status}\uFF09\uFF0C\u8D27\u5728\u677F\u4E0A\u7B49\u4F60\u6536\uFF1A` : `\u753B\u677F ${boardKey} \u7684\u5F15\u64CE\u8FD9\u4E00\u8F6E**\u6CA1\u8DD1\u6210**\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} \u7EC8\u6001=${status}\uFF09\u3002\u677F\u4E0A\u5F53\u524D\u5185\u5BB9\uFF1A`,
|
|
186634
|
+
listed,
|
|
186635
|
+
``,
|
|
186636
|
+
ok ? `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${a.id} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002` : `\u5148 \`oasis design status ${a.id}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\uFF1B\u6CA1\u6709\u5C31\u628A\u9700\u6C42\u4E0E\u771F\u5B9E\u6570\u636E\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E \`oasis design chat ${a.id} --message "<\u2026>" --async\`\uFF0C\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${a.id} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
|
|
186637
|
+
].join("\n");
|
|
186638
|
+
logLine = `[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u5F15\u64CE\u6536\u5C3E\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} ${status}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`;
|
|
186639
|
+
} else {
|
|
186640
|
+
const newest = files.filter((f2) => f2.mtime !== null && candidate.lastOpAtMs !== null && f2.mtime > candidate.lastOpAtMs).sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0));
|
|
186641
|
+
body = [
|
|
186642
|
+
boardWatchMarker(boardMtime),
|
|
186643
|
+
`\u753B\u677F ${boardKey} \u4E0A\u6709 Oasis \u4E4B\u5916\u7684\u65B0\u52A8\u9759\uFF08\u4EBA\u5728\u677F\u4E0A\u5BF9\u8BDD/\u6539\u7A3F\uFF0C\u6216\u5F15\u64CE\u4EA7\u4E86\u6CA1\u4EBA\u6536\u7684\u65B0\u7248\uFF09\uFF0C\u6BD4\u672C\u4EA7\u7269\u6700\u8FD1\u4E00\u6B21\u64CD\u4F5C\uFF08${lastOpAt ?? "?"}\uFF09\u65B0\u3001\u4E14\u5DF2\u9759\u9ED8 ${Math.round(quietMs / 6e4)} \u5206\u949F\uFF1A`,
|
|
186644
|
+
fmt(newest.length > 0 ? newest : files),
|
|
186645
|
+
``,
|
|
186646
|
+
`\u8BF7\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5185\u5BB9\u5408\u610F\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u6536\u5F55\u677F\u4E0A\u4EBA\u5DE5\u8FED\u4EE3>"\` \u6536\u5F55\u4E3A\u65B0\u7248\u672C\uFF1B\u4E0D\u5408\u610F/\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u7248\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
|
|
186647
|
+
].join("\n");
|
|
186648
|
+
logLine = `[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u6709\u65B0\u5185\u5BB9\uFF08mtime=${new Date(boardMtime).toISOString()}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`;
|
|
186649
|
+
}
|
|
186221
186650
|
await kernel.annotate({ artifactId: a.id, actor, body, kind: "comment" });
|
|
186222
186651
|
written++;
|
|
186223
|
-
log3?.(
|
|
186652
|
+
log3?.(logLine);
|
|
186224
186653
|
}
|
|
186225
186654
|
}
|
|
186226
186655
|
};
|
|
@@ -192761,12 +193190,12 @@ function makeResumeRetryProxy(first, spawnFallback) {
|
|
|
192761
193190
|
s2.onTelemetry?.((e) => fanTel(e));
|
|
192762
193191
|
};
|
|
192763
193192
|
wire(first);
|
|
192764
|
-
const
|
|
193193
|
+
const isSessionNotFound2 = (err) => {
|
|
192765
193194
|
const chatExit = err instanceof Error ? err.chatExit : void 0;
|
|
192766
193195
|
return chatExit?.reason === "session-not-found";
|
|
192767
193196
|
};
|
|
192768
193197
|
const done = first.done.catch(async (err) => {
|
|
192769
|
-
if (sawOutput || !
|
|
193198
|
+
if (sawOutput || !isSessionNotFound2(err)) throw err;
|
|
192770
193199
|
const second = await spawnFallback();
|
|
192771
193200
|
active = second;
|
|
192772
193201
|
wire(second);
|
|
@@ -193440,6 +193869,16 @@ async function startServe(opts) {
|
|
|
193440
193869
|
...liveSchemaMap.get(artifact.type)?.ownerRole ? { assignedRole: liveSchemaMap.get(artifact.type).ownerRole } : {},
|
|
193441
193870
|
dependsOn: artifact.inputs.map((edge) => edge.to)
|
|
193442
193871
|
}));
|
|
193872
|
+
},
|
|
193873
|
+
// 业务交接的展示料源(见 CoordinatorNodeView.conclusion):每个节点最近一版**已收口** Work 的
|
|
193874
|
+
// conclusion。纯读、纯可选——读不到(无引擎 store / 工单不在库 / 任何异常)就返回空,
|
|
193875
|
+
// 协调者视图照常工作。
|
|
193876
|
+
resolveNodeConclusions: async (workOrderId, companyId) => {
|
|
193877
|
+
const companyEngine = await getEngine(companyId ?? defaultCompanyId);
|
|
193878
|
+
const store = companyEngine.kernel.getStore?.();
|
|
193879
|
+
if (!store) return {};
|
|
193880
|
+
const snap = await store.transaction((tx) => tx.loadWorkorder(workOrderId));
|
|
193881
|
+
return snap ? nodeConclusionsFromSnapshot(snap) : {};
|
|
193443
193882
|
}
|
|
193444
193883
|
});
|
|
193445
193884
|
console.log(`[serve] execution-continuity mode=${continuityMode}\uFF08record=\u5BF9\u7167\u7EC4\uFF0Cresume=\u6062\u590D\u5305\u5B9E\u9A8C\u7EC4\uFF09`);
|
|
@@ -193585,7 +194024,7 @@ async function startServe(opts) {
|
|
|
193585
194024
|
const selector = typeof raw === "string" && raw ? raw : void 0;
|
|
193586
194025
|
const me = await companies.currentUser({ actor }, headers);
|
|
193587
194026
|
const ctx = await companies.service.resolveCurrentCompany(me.accountId, selector);
|
|
193588
|
-
if (!ctx) return { kind: "not-found" };
|
|
194027
|
+
if (!ctx) return { kind: "not-found", ...selector !== void 0 ? { selector } : {} };
|
|
193589
194028
|
return ctx.isMember ? { kind: "ok", companyId: ctx.company.id } : { kind: "forbidden" };
|
|
193590
194029
|
};
|
|
193591
194030
|
const resolveActor = (token) => issuer.resolve(token) ?? (opts.allowSimpleTokens && token.startsWith("token:") ? token.slice("token:".length) : null);
|
|
@@ -193913,7 +194352,8 @@ async function startServe(opts) {
|
|
|
193913
194352
|
const artifactId = `artifact:planner:${(0, import_node_crypto41.randomUUID)()}`;
|
|
193914
194353
|
const handle = await chatRemoteAdapter.spawn({
|
|
193915
194354
|
actor: planner.id,
|
|
193916
|
-
|
|
194355
|
+
// companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
|
|
194356
|
+
actorToken: issueSessionToken(planner.id, { artifactId, action: "plan-workorder", limits, binding, companyId: defaultCompanyId }),
|
|
193917
194357
|
artifactId,
|
|
193918
194358
|
bundle: { files },
|
|
193919
194359
|
...actorCtx?.config?.prompt ? { systemPrompt: actorCtx.config.prompt } : {},
|
|
@@ -194926,7 +195366,7 @@ async function startServe(opts) {
|
|
|
194926
195366
|
if (engStore) void rebuildDispatchMappings(engStore);
|
|
194927
195367
|
}
|
|
194928
195368
|
newKernel.setEngineIO({
|
|
194929
|
-
async dispatchWork(workId) {
|
|
195369
|
+
async dispatchWork(workId, opts2) {
|
|
194930
195370
|
if (!newEngineProduce) return;
|
|
194931
195371
|
if (dispatchedWorks.has(workId)) return;
|
|
194932
195372
|
const kernel2 = newKernel;
|
|
@@ -194955,7 +195395,7 @@ async function startServe(opts) {
|
|
|
194955
195395
|
}
|
|
194956
195396
|
let result;
|
|
194957
195397
|
try {
|
|
194958
|
-
result = replyIssueId && newEngineReplyWork ? await newEngineReplyWork(rev.artifactId, rev.author, replyIssueId, ledgerId) : await newEngineProduce(rev.artifactId, rev.author, workId, ledgerId);
|
|
195398
|
+
result = replyIssueId && newEngineReplyWork ? await newEngineReplyWork(rev.artifactId, rev.author, replyIssueId, ledgerId) : await newEngineProduce(rev.artifactId, rev.author, workId, ledgerId, opts2?.resumeEligible);
|
|
194959
195399
|
} catch (err) {
|
|
194960
195400
|
dispatchLedger.close(ledgerId, { outcome: "failed", reason: `dispatch error: ${err instanceof Error ? err.message : String(err)}` }).catch(ledgerWarn("close(error)"));
|
|
194961
195401
|
console.log(`[new-engine] dispatchWork ${workId} \u6D3E\u53D1\u5F02\u5E38\uFF1A${String(err)} \u2192 \u7F6E work failed`);
|
|
@@ -195375,14 +195815,16 @@ async function startServe(opts) {
|
|
|
195375
195815
|
});
|
|
195376
195816
|
const slot = { dispatcher: d, logged: 0, ticking: false };
|
|
195377
195817
|
dispatchers.set(companyId, slot);
|
|
195378
|
-
newEngineProduce = async (artifactId, actorId, workId, dispatchId) => {
|
|
195818
|
+
newEngineProduce = async (artifactId, actorId, workId, dispatchId, resumeEligible) => {
|
|
195379
195819
|
const result = await d.requestProduce({
|
|
195380
195820
|
artifactId,
|
|
195381
195821
|
actor: actorId,
|
|
195382
195822
|
bypassScheduling: true,
|
|
195383
195823
|
engineWorkId: workId,
|
|
195384
195824
|
engineCompanyId: companyId,
|
|
195385
|
-
...dispatchId !== void 0 ? { dispatchId } : {}
|
|
195825
|
+
...dispatchId !== void 0 ? { dispatchId } : {},
|
|
195826
|
+
// 工单 e4a43a42:返工(已收口节点被重激活)→ resumeEligible=false → 派发层起全新会话。
|
|
195827
|
+
...resumeEligible !== void 0 ? { resumeEligible } : {}
|
|
195386
195828
|
});
|
|
195387
195829
|
return { kind: result.kind, dispatchId: result.kind === "dispatched" ? result.dispatchId : void 0 };
|
|
195388
195830
|
};
|
|
@@ -195876,6 +196318,12 @@ ${st.detail}
|
|
|
195876
196318
|
artifactId: ctx?.artifactId ?? "artifact:coordinator",
|
|
195877
196319
|
action: ctx?.action ?? "coordinate",
|
|
195878
196320
|
limits: ctx?.limits ?? { wallClockMs: wallClockForKind("coordinate") },
|
|
196321
|
+
// CO-101 门禁要这个 claim 才认得出「这次请求属于哪家公司」。漏签的后果不是报错这么简单:
|
|
196322
|
+
// 管理者的 roles / actors / workorders / inbox / session-log 会**全部** 404,而 status / ls /
|
|
196323
|
+
// graph / content 照常(热路径回退默认公司),于是它只瞎一半——够它误诊成「平台名册服务挂了」,
|
|
196324
|
+
// 再上报给人;未关的上报又会关掉自动重唤,把本可自愈的节点永久悬挂。2026-08-14 至少 3 张单
|
|
196325
|
+
// 栽在这上面。协调者与 apply 同用默认公司 kernel(见下方 structuralBaselines 注释),故取默认公司。
|
|
196326
|
+
companyId: defaultCompanyId,
|
|
195879
196327
|
...ctx?.binding !== void 0 ? { binding: ctx.binding } : {}
|
|
195880
196328
|
});
|
|
195881
196329
|
const ws = ctx?.artifactId ? kernel.model.artifacts.get(ctx.artifactId)?.workspace : void 0;
|
|
@@ -196846,6 +197294,33 @@ function reapClaudeProjects(workdir, runtimeKind) {
|
|
|
196846
197294
|
} catch {
|
|
196847
197295
|
}
|
|
196848
197296
|
}
|
|
197297
|
+
function measureClaudeProjects(workdir, runtimeKind) {
|
|
197298
|
+
if (runtimeKind !== "claude" && runtimeKind !== "claude-code") return 0;
|
|
197299
|
+
const dir = import_node_path24.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
|
|
197300
|
+
return dirSizeBytes2(dir);
|
|
197301
|
+
}
|
|
197302
|
+
function dirSizeBytes2(dir) {
|
|
197303
|
+
let total = 0;
|
|
197304
|
+
let entries;
|
|
197305
|
+
try {
|
|
197306
|
+
entries = import_node_fs20.default.readdirSync(dir, { withFileTypes: true });
|
|
197307
|
+
} catch {
|
|
197308
|
+
return 0;
|
|
197309
|
+
}
|
|
197310
|
+
for (const e of entries) {
|
|
197311
|
+
const p2 = import_node_path24.default.join(dir, e.name);
|
|
197312
|
+
if (e.isSymbolicLink()) continue;
|
|
197313
|
+
if (e.isDirectory()) {
|
|
197314
|
+
total += dirSizeBytes2(p2);
|
|
197315
|
+
continue;
|
|
197316
|
+
}
|
|
197317
|
+
try {
|
|
197318
|
+
total += import_node_fs20.default.statSync(p2).size;
|
|
197319
|
+
} catch {
|
|
197320
|
+
}
|
|
197321
|
+
}
|
|
197322
|
+
return total;
|
|
197323
|
+
}
|
|
196849
197324
|
|
|
196850
197325
|
// ../cli/src/node.ts
|
|
196851
197326
|
init_src6();
|
|
@@ -196928,6 +197403,8 @@ async function startNode(opts) {
|
|
|
196928
197403
|
...process.env["OASIS_GC_INTERVAL_MS"] ? { intervalMs: Number(process.env["OASIS_GC_INTERVAL_MS"]) } : {},
|
|
196929
197404
|
queryArtifactStates: (ids2) => client.queryArtifactStates(ids2),
|
|
196930
197405
|
reapRuntimeState: reapClaudeProjects,
|
|
197406
|
+
// 工单 e4a43a42:把连带的 claude 会话记录占用一并量进保留预算(比工作目录还大的那一坨)。
|
|
197407
|
+
measureRuntimeState: measureClaudeProjects,
|
|
196931
197408
|
log: (m2) => console.log(`[oasis node] ${m2}`)
|
|
196932
197409
|
});
|
|
196933
197410
|
if (opts.gcEnabled !== false) console.log(`[oasis node] \u5DE5\u4F5C\u533A\u56DE\u6536\u5668\uFF1A\u5DF2\u542F\u7528\uFF08\u6839 ${workRoot}\uFF09`);
|
|
@@ -201302,7 +201779,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
|
|
|
201302
201779
|
}
|
|
201303
201780
|
|
|
201304
201781
|
// src/index.ts
|
|
201305
|
-
var PKG_VERSION = true ? "0.1.
|
|
201782
|
+
var PKG_VERSION = true ? "0.1.103" : "dev";
|
|
201306
201783
|
var OASIS_DIR = path26.join(os10.homedir(), ".oasis");
|
|
201307
201784
|
var CONFIG_FILE = path26.join(OASIS_DIR, "node-config.json");
|
|
201308
201785
|
var PID_FILE = path26.join(OASIS_DIR, "node.pid");
|