oasis_test_v2 2.2.19 → 2.2.21
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 +1005 -258
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -243,7 +243,7 @@ function rankMemories(items, terms) {
|
|
|
243
243
|
(a, b2) => b2.score - a.score || b2.accessedAt.localeCompare(a.accessedAt) || a.memId.localeCompare(b2.memId)
|
|
244
244
|
);
|
|
245
245
|
}
|
|
246
|
-
var ActorMemoryNotFoundError, ActorMemoryVersionConflictError, MEMORY_CONTENT_MAX_CHARS, MEMORY_KEYWORDS_MAX, MEMORY_KEYWORD_MAX_CHARS, MEMORY_PER_ACTOR_MAX, MEMORY_DIGEST_CHARS, MEMORY_INJECT_MAX, MEMORY_KEYWORD_WEIGHT, MEMORY_QUERY_TERMS_MAX, MEMORY_READ_MAX_IDS, MEMORY_NGRAM_SCAN_MAX_CHARS, CJK_RE, ASCII_WORD_RE;
|
|
246
|
+
var ActorMemoryNotFoundError, ActorMemoryVersionConflictError, MEMORY_CONTENT_MAX_CHARS, MEMORY_KEYWORDS_MAX, MEMORY_KEYWORD_MAX_CHARS, MEMORY_PER_ACTOR_MAX, MEMORY_DIGEST_CHARS, MEMORY_INJECT_DIGEST_CHARS, MEMORY_INJECT_MAX, MEMORY_KEYWORD_WEIGHT, MEMORY_QUERY_TERMS_MAX, MEMORY_READ_MAX_IDS, MEMORY_NGRAM_SCAN_MAX_CHARS, CJK_RE, ASCII_WORD_RE;
|
|
247
247
|
var init_actor_memory = __esm({
|
|
248
248
|
"../contract/src/actor-memory.ts"() {
|
|
249
249
|
"use strict";
|
|
@@ -268,7 +268,8 @@ var init_actor_memory = __esm({
|
|
|
268
268
|
MEMORY_KEYWORDS_MAX = 16;
|
|
269
269
|
MEMORY_KEYWORD_MAX_CHARS = 32;
|
|
270
270
|
MEMORY_PER_ACTOR_MAX = 500;
|
|
271
|
-
MEMORY_DIGEST_CHARS =
|
|
271
|
+
MEMORY_DIGEST_CHARS = 140;
|
|
272
|
+
MEMORY_INJECT_DIGEST_CHARS = 80;
|
|
272
273
|
MEMORY_INJECT_MAX = 30;
|
|
273
274
|
MEMORY_KEYWORD_WEIGHT = 3;
|
|
274
275
|
MEMORY_QUERY_TERMS_MAX = 32;
|
|
@@ -1219,6 +1220,35 @@ var init_chat = __esm({
|
|
|
1219
1220
|
}
|
|
1220
1221
|
});
|
|
1221
1222
|
|
|
1223
|
+
// ../contract/src/session-outputs.ts
|
|
1224
|
+
function diffSessionOutputs(scanned, previous3) {
|
|
1225
|
+
const prev = previous3 ?? {};
|
|
1226
|
+
const changed = [];
|
|
1227
|
+
const fingerprints = {};
|
|
1228
|
+
for (const entry of [...scanned].sort((a, b2) => a.path.localeCompare(b2.path))) {
|
|
1229
|
+
const { path: path41, size, mtime } = entry;
|
|
1230
|
+
if (!path41) continue;
|
|
1231
|
+
const usable = typeof size === "number" && Number.isFinite(size) && typeof mtime === "string" && mtime !== "";
|
|
1232
|
+
if (usable) fingerprints[path41] = { size, mtime };
|
|
1233
|
+
const before = prev[path41];
|
|
1234
|
+
const unchanged = usable && before !== void 0 && before.size === size && before.mtime === mtime;
|
|
1235
|
+
if (unchanged) continue;
|
|
1236
|
+
changed.push({ path: path41, name: basenameOf(path41), size: size ?? null, mtime: mtime ?? null });
|
|
1237
|
+
}
|
|
1238
|
+
return { changed, fingerprints };
|
|
1239
|
+
}
|
|
1240
|
+
function basenameOf(path41) {
|
|
1241
|
+
const i = path41.lastIndexOf("/");
|
|
1242
|
+
return i >= 0 ? path41.slice(i + 1) : path41;
|
|
1243
|
+
}
|
|
1244
|
+
var SESSION_OUTPUTS_DIR;
|
|
1245
|
+
var init_session_outputs = __esm({
|
|
1246
|
+
"../contract/src/session-outputs.ts"() {
|
|
1247
|
+
"use strict";
|
|
1248
|
+
SESSION_OUTPUTS_DIR = "outputs";
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1222
1252
|
// ../contract/src/chat-session.ts
|
|
1223
1253
|
function supportsChatModelOverride(runtimeKind) {
|
|
1224
1254
|
return !!runtimeKind && CHAT_MODEL_OVERRIDE_RUNTIMES.includes(runtimeKind);
|
|
@@ -1409,10 +1439,11 @@ function touchAnchorMessageId(messageId) {
|
|
|
1409
1439
|
const hash2 = messageId.indexOf("#");
|
|
1410
1440
|
return hash2 >= 0 ? messageId.slice(0, hash2) : messageId;
|
|
1411
1441
|
}
|
|
1412
|
-
var DelegationLabelConflictError, DELEGATION_FILE_LIMITS, DELEGATION_INBOUND_DIR, DELEGATION_OUTBOUND_DIR,
|
|
1442
|
+
var DelegationLabelConflictError, DELEGATION_FILE_LIMITS, DELEGATION_INBOUND_DIR, DELEGATION_OUTBOUND_DIR, DELEGATION_RETURN_RECENT_ROUNDS;
|
|
1413
1443
|
var init_delegation = __esm({
|
|
1414
1444
|
"../contract/src/delegation.ts"() {
|
|
1415
1445
|
"use strict";
|
|
1446
|
+
init_session_outputs();
|
|
1416
1447
|
DelegationLabelConflictError = class extends Error {
|
|
1417
1448
|
constructor(parentSessionId, label) {
|
|
1418
1449
|
super(`delegation label conflict in parent session: parent=${parentSessionId} label=${label ?? "<null>"}`);
|
|
@@ -1431,7 +1462,6 @@ var init_delegation = __esm({
|
|
|
1431
1462
|
};
|
|
1432
1463
|
DELEGATION_INBOUND_DIR = "inputs/attachments";
|
|
1433
1464
|
DELEGATION_OUTBOUND_DIR = "delegations";
|
|
1434
|
-
DELEGATION_OUTPUTS_DIR = "outputs";
|
|
1435
1465
|
DELEGATION_RETURN_RECENT_ROUNDS = 3;
|
|
1436
1466
|
}
|
|
1437
1467
|
});
|
|
@@ -1452,6 +1482,8 @@ var init_delegation_prompts = __esm({
|
|
|
1452
1482
|
"## \u5DE5\u4F5C\u533A\u7EA6\u5B9A",
|
|
1453
1483
|
"",
|
|
1454
1484
|
"- **\u4EA4\u4ED8\u7269\u653E `outputs/`**\uFF1A\u4F60\u8981\u4EA4\u56DE\u53BB\u7684\u6587\u4EF6\u653E\u5728\u8FD9\u4E2A\u76EE\u5F55\u4E0B\uFF0C\u4FDD\u7559\u4F60\u81EA\u5DF1\u7684\u5B50\u76EE\u5F55\u7ED3\u6784\u3002",
|
|
1485
|
+
" \u8FD9\u91CC\u7684\u6587\u4EF6\u65E2\u4F1A\u4EA4\u56DE\u534F\u8C03\u8005\uFF0C\u4E5F\u4F1A\u5728**\u4F60\u8FD9\u6761\u5B50\u4F1A\u8BDD\u7684\u9875\u9762\u4E0A**\u663E\u793A\u6210\u53EF\u70B9\u5F00\u7684\u6587\u4EF6\u5361\u7247\uFF1B",
|
|
1486
|
+
" \u4E2D\u95F4\u4EA7\u7269\u548C\u8349\u7A3F\u522B\u653E\u8FD9\u513F\u3002",
|
|
1455
1487
|
`- \`${PARENT_CONVERSATION_PATH}\` \u5982\u679C\u5B58\u5728\uFF0C\u662F\u4E3B\u4F1A\u8BDD\u7684\u5BF9\u8BDD\u5FEB\u7167\uFF0C\u53EF\u4EE5\u67E5\u9605\u3002`,
|
|
1456
1488
|
" **\u5B83\u4E0D\u4FDD\u8BC1\u5B58\u5728**\uFF08\u4E3B\u4F1A\u8BDD\u6CA1\u6709\u53EF\u8F6C\u50A8\u7684\u5386\u53F2\u65F6\u5C31\u6CA1\u6709\u8FD9\u4E2A\u6587\u4EF6\uFF09\uFF0C\u522B\u628A\u5B83\u5F53\u524D\u63D0\u3002",
|
|
1457
1489
|
"- \u534F\u8C03\u8005\u968F\u59D4\u6D3E\u5E26\u8FC7\u6765\u7684\u6587\u4EF6\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u4EFB\u52A1\u4E66\u91CC\u4F1A\u70B9\u540D\u3002",
|
|
@@ -1658,6 +1690,7 @@ var init_src = __esm({
|
|
|
1658
1690
|
init_playbook();
|
|
1659
1691
|
init_experiments();
|
|
1660
1692
|
init_chat();
|
|
1693
|
+
init_session_outputs();
|
|
1661
1694
|
init_chat_session();
|
|
1662
1695
|
init_chat_session();
|
|
1663
1696
|
init_live_protocol();
|
|
@@ -4049,7 +4082,8 @@ var init_model = __esm({
|
|
|
4049
4082
|
BUSINESS_HANDOFF_POLICY_FIELD = "businessHandoffPolicy";
|
|
4050
4083
|
BUSINESS_HANDOFF_POLICY_EXEMPT = "exempt";
|
|
4051
4084
|
DEFAULT_CONFIG = {
|
|
4052
|
-
maxRetries: 3
|
|
4085
|
+
maxRetries: 3,
|
|
4086
|
+
maxStagnantCheckpoints: 3
|
|
4053
4087
|
};
|
|
4054
4088
|
deriveWorkId = (nodeId, version2) => `wk:${nodeId}:${version2}`;
|
|
4055
4089
|
deriveReviewId = (workId, reviewer, group) => `rv:${workId}:${reviewer}:${group}`;
|
|
@@ -4057,6 +4091,101 @@ var init_model = __esm({
|
|
|
4057
4091
|
}
|
|
4058
4092
|
});
|
|
4059
4093
|
|
|
4094
|
+
// ../engine/src/closure-gate.ts
|
|
4095
|
+
function closureSlotOfFacts(facts) {
|
|
4096
|
+
const report = facts.liveClosureReport();
|
|
4097
|
+
if (report) return { kind: "closure-report", id: report.id };
|
|
4098
|
+
const business = facts.liveBusinessNodes();
|
|
4099
|
+
if (business.length === 1) return { kind: "sole-artifact", id: business[0].id };
|
|
4100
|
+
if (business.length === 0) return { kind: "empty" };
|
|
4101
|
+
return { kind: "missing", businessCount: business.length };
|
|
4102
|
+
}
|
|
4103
|
+
function humanGateActorsOfFacts(facts, artifactId) {
|
|
4104
|
+
const slot = closureSlotOfFacts(facts);
|
|
4105
|
+
if (slot.kind !== "closure-report" && slot.kind !== "sole-artifact") return [];
|
|
4106
|
+
if (slot.id !== artifactId) return [];
|
|
4107
|
+
const owner = facts.humanOwner();
|
|
4108
|
+
return owner === null ? [] : [owner];
|
|
4109
|
+
}
|
|
4110
|
+
function closureReviewGroup(actor) {
|
|
4111
|
+
return `closure:${actor}`;
|
|
4112
|
+
}
|
|
4113
|
+
function closureReviewerSpecs(closureActors) {
|
|
4114
|
+
return closureActors.map((actor) => ({
|
|
4115
|
+
reviewerActorId: actor,
|
|
4116
|
+
reviewGroup: closureReviewGroup(actor),
|
|
4117
|
+
source: "closure"
|
|
4118
|
+
}));
|
|
4119
|
+
}
|
|
4120
|
+
function engineClosureFacts(state) {
|
|
4121
|
+
return {
|
|
4122
|
+
liveBusinessNodes: () => state.liveNodes().filter((n) => n.type !== BRIEF_TYPE && n.type !== DELIVERY_SUMMARY_TYPE).map((n) => ({ id: n.id, type: n.type })),
|
|
4123
|
+
liveClosureReport: () => {
|
|
4124
|
+
const r = state.liveNodes().find((n) => n.type === DELIVERY_SUMMARY_TYPE);
|
|
4125
|
+
return r ? { id: r.id, type: r.type } : null;
|
|
4126
|
+
},
|
|
4127
|
+
humanOwner: () => {
|
|
4128
|
+
const brief = state.liveNodes().find((n) => n.type === BRIEF_TYPE);
|
|
4129
|
+
const owner = brief?.assigneeActorId;
|
|
4130
|
+
if (!owner || !owner.startsWith(HUMAN_ACTOR_PREFIX)) return null;
|
|
4131
|
+
return owner;
|
|
4132
|
+
}
|
|
4133
|
+
};
|
|
4134
|
+
}
|
|
4135
|
+
function engineWorkorderHumanOwner(state) {
|
|
4136
|
+
return engineClosureFacts(state).humanOwner();
|
|
4137
|
+
}
|
|
4138
|
+
function engineHumanGateActors(state, nodeId) {
|
|
4139
|
+
return humanGateActorsOfFacts(engineClosureFacts(state), nodeId);
|
|
4140
|
+
}
|
|
4141
|
+
var DELIVERY_SUMMARY_TYPE, BRIEF_TYPE, HUMAN_ACTOR_PREFIX;
|
|
4142
|
+
var init_closure_gate = __esm({
|
|
4143
|
+
"../engine/src/closure-gate.ts"() {
|
|
4144
|
+
"use strict";
|
|
4145
|
+
DELIVERY_SUMMARY_TYPE = "delivery-summary";
|
|
4146
|
+
BRIEF_TYPE = "brief";
|
|
4147
|
+
HUMAN_ACTOR_PREFIX = "actor:human:";
|
|
4148
|
+
}
|
|
4149
|
+
});
|
|
4150
|
+
|
|
4151
|
+
// ../engine/src/continuation.ts
|
|
4152
|
+
function continuationApprover(state) {
|
|
4153
|
+
const owner = engineWorkorderHumanOwner(state);
|
|
4154
|
+
if (owner || state.liveNodes().some((node2) => node2.type === "brief")) return owner;
|
|
4155
|
+
return state.workorder.ownerActorId.startsWith("actor:human:") ? state.workorder.ownerActorId : null;
|
|
4156
|
+
}
|
|
4157
|
+
function continuationUsedRounds(state, nodeId) {
|
|
4158
|
+
const withOutput = new Set(state.artifactsOfNode(nodeId).map((a) => a.workId));
|
|
4159
|
+
return state.mainWorksOf(nodeId).filter((w2) => w2.assigneeActorId.startsWith("actor:agent:") && (w2.startedAt || w2.sessionRef || w2.lastActivityAt || withOutput.has(w2.id))).length;
|
|
4160
|
+
}
|
|
4161
|
+
function continuationAwaitingStart(state, work) {
|
|
4162
|
+
return work.status === "running" && !work.endedAt && !work.deadAt && !work.cancelledAt && !work.startedAt && !work.sessionRef && !work.lastActivityAt && state.artifactsOf(work.id).length === 0;
|
|
4163
|
+
}
|
|
4164
|
+
function continuationExhausted(state, node2) {
|
|
4165
|
+
const budget = node2.continuationBudget;
|
|
4166
|
+
return !!budget && (budget.request?.decision === "denied" || budget.request?.decision === "pending" || continuationUsedRounds(state, node2.id) >= budget.authorizedRounds);
|
|
4167
|
+
}
|
|
4168
|
+
function continuationRequestId(node2) {
|
|
4169
|
+
return `continuation:${node2.id}:${node2.continuationBudget.requestGeneration + 1}`;
|
|
4170
|
+
}
|
|
4171
|
+
function continuationSummary(state, node2, rounds) {
|
|
4172
|
+
const used = continuationUsedRounds(state, node2.id);
|
|
4173
|
+
const latest = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
4174
|
+
const output = node2.latestProposedWorkId ? state.work(node2.latestProposedWorkId) : latest;
|
|
4175
|
+
const files = output ? state.artifactsOf(output.id) : [];
|
|
4176
|
+
return `\u300A${node2.title}\u300B\u5C1A\u672A\u5B8C\u6210\uFF0C\u5DF2\u8FBE\u5230\u81EA\u52A8\u6D3E\u53D1\u4E0A\u9650\u3002\u7D2F\u8BA1\u5DF2\u8FD0\u884C ${used} \u8F6E\uFF0C\u5DF2\u6388\u6743 ${node2.continuationBudget.authorizedRounds} \u8F6E\u3002
|
|
4177
|
+
\u9636\u6BB5\u6210\u679C\uFF1A${files.length ? files.map((a) => a.logicalPath).join("\u3001") : "\u6682\u65E0\u5DF2\u63D0\u4EA4\u7684\u4EA4\u4ED8\u5185\u5BB9"}\u3002
|
|
4178
|
+
\u6700\u8FD1 checkpoint \u8BF4\u660E\uFF08\u6267\u884C\u4EBA\u539F\u6587\uFF09\uFF1A${output?.proposalReason || "\u672A\u77E5\uFF0C\u672A\u63D0\u4F9B"}
|
|
4179
|
+
\u5269\u4F59\u4E8B\u9879\u53CA\u4E0B\u4E00\u6279\u8BA1\u5212\uFF1A\u4EE5\u4EE5\u4E0A\u8BF4\u660E\u4E3A\u4F9D\u636E\uFF1B\u672A\u660E\u786E\u63D0\u4F9B\u7684\u90E8\u5206\u672A\u77E5\uFF0C\u9700\u4EBA\u5DE5\u786E\u8BA4\u3002
|
|
4180
|
+
\u540C\u610F\u540E\u6700\u591A\u518D\u8FD0\u884C ${rounds} \u8F6E\uFF1B\u62D2\u7EDD\u6216\u4E0D\u56DE\u590D\u5C06\u4FDD\u6301\u6682\u505C\u5E76\u4FDD\u7559\u6210\u679C\uFF0C\u4E0D\u81EA\u52A8\u5BA3\u544A\u5B8C\u6210\u3002`;
|
|
4181
|
+
}
|
|
4182
|
+
var init_continuation = __esm({
|
|
4183
|
+
"../engine/src/continuation.ts"() {
|
|
4184
|
+
"use strict";
|
|
4185
|
+
init_closure_gate();
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
|
|
4060
4189
|
// ../engine/src/events.ts
|
|
4061
4190
|
function eventAnchors(e) {
|
|
4062
4191
|
switch (e.kind) {
|
|
@@ -4301,6 +4430,10 @@ var init_state = __esm({
|
|
|
4301
4430
|
artifactsOf(workId) {
|
|
4302
4431
|
return this.artifactList.filter((a) => a.workId === workId);
|
|
4303
4432
|
}
|
|
4433
|
+
/** 多批次分析一次读取本节点全部快照,避免每个 work 再扫整单产物。 */
|
|
4434
|
+
artifactsOfNode(nodeId) {
|
|
4435
|
+
return this.artifactList.filter((a) => a.nodeId === nodeId);
|
|
4436
|
+
}
|
|
4304
4437
|
workInputsOf(workId) {
|
|
4305
4438
|
return this.workInputList.filter((i) => i.workId === workId);
|
|
4306
4439
|
}
|
|
@@ -4458,60 +4591,6 @@ var init_state = __esm({
|
|
|
4458
4591
|
}
|
|
4459
4592
|
});
|
|
4460
4593
|
|
|
4461
|
-
// ../engine/src/closure-gate.ts
|
|
4462
|
-
function closureSlotOfFacts(facts) {
|
|
4463
|
-
const report = facts.liveClosureReport();
|
|
4464
|
-
if (report) return { kind: "closure-report", id: report.id };
|
|
4465
|
-
const business = facts.liveBusinessNodes();
|
|
4466
|
-
if (business.length === 1) return { kind: "sole-artifact", id: business[0].id };
|
|
4467
|
-
if (business.length === 0) return { kind: "empty" };
|
|
4468
|
-
return { kind: "missing", businessCount: business.length };
|
|
4469
|
-
}
|
|
4470
|
-
function humanGateActorsOfFacts(facts, artifactId) {
|
|
4471
|
-
const slot = closureSlotOfFacts(facts);
|
|
4472
|
-
if (slot.kind !== "closure-report" && slot.kind !== "sole-artifact") return [];
|
|
4473
|
-
if (slot.id !== artifactId) return [];
|
|
4474
|
-
const owner = facts.humanOwner();
|
|
4475
|
-
return owner === null ? [] : [owner];
|
|
4476
|
-
}
|
|
4477
|
-
function closureReviewGroup(actor) {
|
|
4478
|
-
return `closure:${actor}`;
|
|
4479
|
-
}
|
|
4480
|
-
function closureReviewerSpecs(closureActors) {
|
|
4481
|
-
return closureActors.map((actor) => ({
|
|
4482
|
-
reviewerActorId: actor,
|
|
4483
|
-
reviewGroup: closureReviewGroup(actor),
|
|
4484
|
-
source: "closure"
|
|
4485
|
-
}));
|
|
4486
|
-
}
|
|
4487
|
-
function engineClosureFacts(state) {
|
|
4488
|
-
return {
|
|
4489
|
-
liveBusinessNodes: () => state.liveNodes().filter((n) => n.type !== BRIEF_TYPE && n.type !== DELIVERY_SUMMARY_TYPE).map((n) => ({ id: n.id, type: n.type })),
|
|
4490
|
-
liveClosureReport: () => {
|
|
4491
|
-
const r = state.liveNodes().find((n) => n.type === DELIVERY_SUMMARY_TYPE);
|
|
4492
|
-
return r ? { id: r.id, type: r.type } : null;
|
|
4493
|
-
},
|
|
4494
|
-
humanOwner: () => {
|
|
4495
|
-
const brief = state.liveNodes().find((n) => n.type === BRIEF_TYPE);
|
|
4496
|
-
const owner = brief?.assigneeActorId;
|
|
4497
|
-
if (!owner || !owner.startsWith(HUMAN_ACTOR_PREFIX)) return null;
|
|
4498
|
-
return owner;
|
|
4499
|
-
}
|
|
4500
|
-
};
|
|
4501
|
-
}
|
|
4502
|
-
function engineHumanGateActors(state, nodeId) {
|
|
4503
|
-
return humanGateActorsOfFacts(engineClosureFacts(state), nodeId);
|
|
4504
|
-
}
|
|
4505
|
-
var DELIVERY_SUMMARY_TYPE, BRIEF_TYPE, HUMAN_ACTOR_PREFIX;
|
|
4506
|
-
var init_closure_gate = __esm({
|
|
4507
|
-
"../engine/src/closure-gate.ts"() {
|
|
4508
|
-
"use strict";
|
|
4509
|
-
DELIVERY_SUMMARY_TYPE = "delivery-summary";
|
|
4510
|
-
BRIEF_TYPE = "brief";
|
|
4511
|
-
HUMAN_ACTOR_PREFIX = "actor:human:";
|
|
4512
|
-
}
|
|
4513
|
-
});
|
|
4514
|
-
|
|
4515
4594
|
// ../engine/src/letters.ts
|
|
4516
4595
|
function repliesSorted(state, issueId) {
|
|
4517
4596
|
return [...state.repliesOf(issueId)].sort((a, b2) => a.createdAt.localeCompare(b2.createdAt));
|
|
@@ -4668,6 +4747,26 @@ function scan(state, ctx) {
|
|
|
4668
4747
|
const events = [];
|
|
4669
4748
|
for (const node2 of state.liveNodes()) {
|
|
4670
4749
|
const lw = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
4750
|
+
const continuationPolicy = node2.assigneeActorId ? ctx.continuationPolicies?.get(node2.assigneeActorId) : null;
|
|
4751
|
+
if (!node2.continuationBudget && continuationPolicy) {
|
|
4752
|
+
events.push({ kind: "continuation.initialized", workorderId: state.workorder.id, nodeId: node2.id, rounds: continuationPolicy.maxRounds });
|
|
4753
|
+
continue;
|
|
4754
|
+
}
|
|
4755
|
+
const delivered = node2.latestProposedWorkId ? state.work(node2.latestProposedWorkId) : null;
|
|
4756
|
+
const hasCurrentDelivery = delivered?.status === "success" && !delivered.deadAt && (delivered.conclusion != null || node2.latestAcceptId === delivered.id) && delivered.nodeVersion === node2.version && inputsMatch(state, node2, delivered);
|
|
4757
|
+
if (node2.continuationBudget && continuationExhausted(state, node2) && !hasCurrentDelivery && (!lw || lw.endedAt || lw.deadAt || continuationAwaitingStart(state, lw))) {
|
|
4758
|
+
const requestId = continuationRequestId(node2);
|
|
4759
|
+
if (node2.continuationBudget.request?.decision !== "pending" && node2.continuationBudget.request?.decision !== "denied") {
|
|
4760
|
+
events.push({
|
|
4761
|
+
kind: "continuation.requested",
|
|
4762
|
+
workorderId: state.workorder.id,
|
|
4763
|
+
nodeId: node2.id,
|
|
4764
|
+
requestId,
|
|
4765
|
+
rounds: continuationPolicy?.maxRounds ?? node2.continuationBudget.roundsPerGrant
|
|
4766
|
+
});
|
|
4767
|
+
}
|
|
4768
|
+
continue;
|
|
4769
|
+
}
|
|
4671
4770
|
if (!lw) {
|
|
4672
4771
|
tryToRun(state, node2.id, ctx, events);
|
|
4673
4772
|
continue;
|
|
@@ -4961,6 +5060,20 @@ function scanBlockersForNode(state, node2, latest) {
|
|
|
4961
5060
|
function tryToRun(state, nodeId, ctx, events) {
|
|
4962
5061
|
const node2 = state.node(nodeId);
|
|
4963
5062
|
if (!node2) return;
|
|
5063
|
+
if (continuationExhausted(state, node2)) {
|
|
5064
|
+
const latest2 = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
5065
|
+
const request2 = node2.continuationBudget?.request;
|
|
5066
|
+
if ((!latest2 || latest2.endedAt || latest2.deadAt) && request2?.decision !== "pending" && request2?.decision !== "denied") {
|
|
5067
|
+
events.push({
|
|
5068
|
+
kind: "continuation.requested",
|
|
5069
|
+
workorderId: state.workorder.id,
|
|
5070
|
+
nodeId: node2.id,
|
|
5071
|
+
requestId: continuationRequestId(node2),
|
|
5072
|
+
rounds: (node2.assigneeActorId ? ctx.continuationPolicies?.get(node2.assigneeActorId)?.maxRounds : null) ?? node2.continuationBudget.roundsPerGrant
|
|
5073
|
+
});
|
|
5074
|
+
} else if (latest2 && !latest2.endedAt && !latest2.deadAt) checkRunningWorkTimeout(state, node2, latest2, ctx, events);
|
|
5075
|
+
return;
|
|
5076
|
+
}
|
|
4964
5077
|
if (!node2.assigneeActorId) return;
|
|
4965
5078
|
const latest = (node2.latestWorkId ? state.work(node2.latestWorkId) : null) ?? null;
|
|
4966
5079
|
if (unexemptedVetoBlockers(scanBlockersForNode(state, node2, latest), "dispatch").length > 0) {
|
|
@@ -5059,19 +5172,34 @@ function findHumanRecoveryPost(state, node2, deadAt) {
|
|
|
5059
5172
|
const first = candidates[0];
|
|
5060
5173
|
return first ? { actorId: first.actorId, at: first.at } : null;
|
|
5061
5174
|
}
|
|
5062
|
-
function
|
|
5063
|
-
const works = state.mainWorksOf(nodeId).sort((a, b2) =>
|
|
5175
|
+
function countStagnantCheckpoints(state, nodeId) {
|
|
5176
|
+
const works = state.mainWorksOf(nodeId).sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id, "en", { numeric: true }));
|
|
5177
|
+
const contents = /* @__PURE__ */ new Map();
|
|
5178
|
+
for (const a of state.artifactsOfNode(nodeId)) {
|
|
5179
|
+
const parts = contents.get(a.workId) ?? [];
|
|
5180
|
+
parts.push(JSON.stringify([a.logicalPath, a.contentKind, a.contentRef]));
|
|
5181
|
+
contents.set(a.workId, parts);
|
|
5182
|
+
}
|
|
5183
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5064
5184
|
let count2 = 0;
|
|
5185
|
+
let version2;
|
|
5065
5186
|
for (const w2 of works) {
|
|
5066
|
-
if (w2.retryAt)
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5187
|
+
if (w2.retryAt || w2.status === "success" && w2.conclusion != null) {
|
|
5188
|
+
seen.clear();
|
|
5189
|
+
count2 = 0;
|
|
5190
|
+
version2 = void 0;
|
|
5191
|
+
}
|
|
5192
|
+
if (w2.status !== "success") continue;
|
|
5193
|
+
const parts = contents.get(w2.id);
|
|
5194
|
+
if (!parts?.length) continue;
|
|
5195
|
+
if (version2 !== void 0 && version2 !== w2.nodeVersion) {
|
|
5196
|
+
seen.clear();
|
|
5197
|
+
count2 = 0;
|
|
5198
|
+
}
|
|
5199
|
+
version2 = w2.nodeVersion;
|
|
5200
|
+
const signature = JSON.stringify(parts.sort());
|
|
5201
|
+
count2 = !w2.retryAt && w2.conclusion == null && seen.has(signature) ? count2 + 1 : 0;
|
|
5202
|
+
seen.add(signature);
|
|
5075
5203
|
}
|
|
5076
5204
|
return count2;
|
|
5077
5205
|
}
|
|
@@ -5090,8 +5218,8 @@ function countConsecutiveRejections(state, nodeId) {
|
|
|
5090
5218
|
return count2;
|
|
5091
5219
|
}
|
|
5092
5220
|
function unconcludedCheckpointsExhausted(state, nodeId, ctx) {
|
|
5093
|
-
const limit = ctx.config?.
|
|
5094
|
-
const rounds =
|
|
5221
|
+
const limit = ctx.config?.maxStagnantCheckpoints ?? DEFAULT_CONFIG.maxStagnantCheckpoints;
|
|
5222
|
+
const rounds = countStagnantCheckpoints(state, nodeId);
|
|
5095
5223
|
return rounds > limit ? { rounds, limit } : null;
|
|
5096
5224
|
}
|
|
5097
5225
|
function gateRejectionsExhausted(state, nodeId, ctx) {
|
|
@@ -5445,6 +5573,7 @@ function assessConvergence(state, ctx) {
|
|
|
5445
5573
|
var init_activate = __esm({
|
|
5446
5574
|
"../engine/src/activate.ts"() {
|
|
5447
5575
|
"use strict";
|
|
5576
|
+
init_continuation();
|
|
5448
5577
|
init_src3();
|
|
5449
5578
|
init_src2();
|
|
5450
5579
|
init_model();
|
|
@@ -5499,6 +5628,7 @@ CREATE TABLE IF NOT EXISTS ${q2}.workflow_nodes (
|
|
|
5499
5628
|
latest_accept_id text,
|
|
5500
5629
|
is_final_output boolean NOT NULL DEFAULT false,
|
|
5501
5630
|
fields jsonb,
|
|
5631
|
+
continuation_budget jsonb,
|
|
5502
5632
|
cancelled_at timestamptz,
|
|
5503
5633
|
-- \u5C01\u5B58\u539F\u56E0\uFF08accepted/cancelled/frozen\uFF09\u3002NULL = \u5B58\u91CF\u884C\uFF0C\u8BFB\u6A21\u578B\u6309 cancelled \u515C\u5E95\u3002
|
|
5504
5634
|
seal_reason text,
|
|
@@ -5506,6 +5636,7 @@ CREATE TABLE IF NOT EXISTS ${q2}.workflow_nodes (
|
|
|
5506
5636
|
created_at timestamptz NOT NULL,
|
|
5507
5637
|
updated_at timestamptz NOT NULL
|
|
5508
5638
|
);
|
|
5639
|
+
ALTER TABLE ${q2}.workflow_nodes ADD COLUMN IF NOT EXISTS continuation_budget jsonb;
|
|
5509
5640
|
CREATE INDEX IF NOT EXISTS nodes_workorder_idx ON ${q2}.workflow_nodes (workorder_id);
|
|
5510
5641
|
|
|
5511
5642
|
CREATE TABLE IF NOT EXISTS ${q2}.workflow_edges (
|
|
@@ -11411,6 +11542,7 @@ var init_store_postgres = __esm({
|
|
|
11411
11542
|
// NULL = 存量行(ADR 0137 迁移批量置的 cancelled_at):按 cancelled 读,不追溯改写历史。
|
|
11412
11543
|
sealReason: n.seal_reason ?? null,
|
|
11413
11544
|
fields: n.fields ?? void 0,
|
|
11545
|
+
continuationBudget: n.continuation_budget ?? null,
|
|
11414
11546
|
rowVersion: n.row_version,
|
|
11415
11547
|
createdAt: iso(n.created_at),
|
|
11416
11548
|
updatedAt: iso(n.updated_at)
|
|
@@ -11592,8 +11724,8 @@ var init_store_postgres = __esm({
|
|
|
11592
11724
|
`INSERT INTO ${this.s}.workflow_nodes
|
|
11593
11725
|
(id,workorder_id,type,title,spec,version,assignee_actor_id,assignee_role,
|
|
11594
11726
|
latest_work_id,latest_proposed_work_id,latest_accept_id,is_final_output,
|
|
11595
|
-
fields,cancelled_at,seal_reason,row_version,created_at,updated_at)
|
|
11596
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)
|
|
11727
|
+
fields,cancelled_at,seal_reason,row_version,created_at,updated_at,continuation_budget)
|
|
11728
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
|
|
11597
11729
|
ON CONFLICT (id) DO NOTHING`,
|
|
11598
11730
|
[
|
|
11599
11731
|
n.id,
|
|
@@ -11613,13 +11745,14 @@ var init_store_postgres = __esm({
|
|
|
11613
11745
|
n.sealReason ?? null,
|
|
11614
11746
|
n.rowVersion,
|
|
11615
11747
|
n.createdAt,
|
|
11616
|
-
n.updatedAt
|
|
11748
|
+
n.updatedAt,
|
|
11749
|
+
n.continuationBudget ? JSON.stringify(n.continuationBudget) : null
|
|
11617
11750
|
]
|
|
11618
11751
|
);
|
|
11619
11752
|
break;
|
|
11620
11753
|
}
|
|
11621
11754
|
case "node.update":
|
|
11622
|
-
await this.patch("workflow_nodes", "id", m2.id, m2.patch, { fields: true });
|
|
11755
|
+
await this.patch("workflow_nodes", "id", m2.id, m2.patch, { fields: true, continuationBudget: true });
|
|
11623
11756
|
break;
|
|
11624
11757
|
case "edge.insert": {
|
|
11625
11758
|
const e = m2.row;
|
|
@@ -12043,7 +12176,7 @@ function newNode(id, workorderId, at, version2) {
|
|
|
12043
12176
|
updatedAt: at
|
|
12044
12177
|
};
|
|
12045
12178
|
}
|
|
12046
|
-
function markNodeForRetry(state, nodeId, at, resetRejectedBudget = false) {
|
|
12179
|
+
function markNodeForRetry(state, nodeId, at, resetRejectedBudget = false, resetCheckpointBudget = false) {
|
|
12047
12180
|
const node2 = state.node(nodeId);
|
|
12048
12181
|
if (!node2) return;
|
|
12049
12182
|
const lw = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
@@ -12052,6 +12185,9 @@ function markNodeForRetry(state, nodeId, at, resetRejectedBudget = false) {
|
|
|
12052
12185
|
}
|
|
12053
12186
|
if (lw && !lw.deadAt && workState(lw, hasOutput(lw, state.artifactsOf(lw.id).length)) === "success") {
|
|
12054
12187
|
if (resetRejectedBudget && lw.acceptanceState === "rejected") state.updateWork(lw.id, { retryAt: at });
|
|
12188
|
+
if (resetCheckpointBudget && lw.conclusion == null && state.artifactsOf(lw.id).length > 0) {
|
|
12189
|
+
state.updateWork(lw.id, { retryAt: at });
|
|
12190
|
+
}
|
|
12055
12191
|
for (const req of state.requirementsOf(node2.id)) {
|
|
12056
12192
|
if (!req.latestReviewId) continue;
|
|
12057
12193
|
const r = state.review(req.latestReviewId);
|
|
@@ -12183,7 +12319,7 @@ var init_plan = __esm({
|
|
|
12183
12319
|
const changed = node2.assigneeActorId !== e.assigneeActorId;
|
|
12184
12320
|
const lw = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
12185
12321
|
const settled = lw !== null && lw !== void 0 && workState(lw, hasOutput(lw, state.artifactsOf(lw.id).length)) === "success";
|
|
12186
|
-
if (changed && e.resetRejectedBudget === true && lw &&
|
|
12322
|
+
if (changed && lw && !lw.deadAt && settled && (e.resetRejectedBudget === true && lw.acceptanceState === "rejected" || e.resetCheckpointBudget === true && lw.conclusion == null && state.artifactsOf(lw.id).length > 0)) {
|
|
12187
12323
|
state.updateWork(lw.id, { retryAt: ctx.at });
|
|
12188
12324
|
}
|
|
12189
12325
|
state.updateNode(e.nodeId, {
|
|
@@ -12258,7 +12394,7 @@ var init_plan = __esm({
|
|
|
12258
12394
|
name: "plan/node-retry",
|
|
12259
12395
|
kind: "plan.node_retry",
|
|
12260
12396
|
apply(e, state, ctx) {
|
|
12261
|
-
markNodeForRetry(state, e.nodeId, ctx.at, e.resetRejectedBudget === true);
|
|
12397
|
+
markNodeForRetry(state, e.nodeId, ctx.at, e.resetRejectedBudget === true, e.resetCheckpointBudget === true);
|
|
12262
12398
|
}
|
|
12263
12399
|
};
|
|
12264
12400
|
planUpdateFields = {
|
|
@@ -12396,7 +12532,8 @@ var init_workorder = __esm({
|
|
|
12396
12532
|
workorderId: e.workorderId,
|
|
12397
12533
|
nodeId: node2.id,
|
|
12398
12534
|
by: e.by,
|
|
12399
|
-
resetRejectedBudget: e.resetRejectedBudget
|
|
12535
|
+
resetRejectedBudget: e.resetRejectedBudget,
|
|
12536
|
+
resetCheckpointBudget: e.resetCheckpointBudget
|
|
12400
12537
|
}, state, ctx);
|
|
12401
12538
|
}
|
|
12402
12539
|
}
|
|
@@ -12419,6 +12556,7 @@ var workCreate, workRedispatch, workSnapshotRecorded, workKill, workResponse, wo
|
|
|
12419
12556
|
var init_work = __esm({
|
|
12420
12557
|
"../engine/src/handlers/work.ts"() {
|
|
12421
12558
|
"use strict";
|
|
12559
|
+
init_continuation();
|
|
12422
12560
|
init_views2();
|
|
12423
12561
|
init_kill();
|
|
12424
12562
|
init_letters();
|
|
@@ -12467,6 +12605,7 @@ var init_work = __esm({
|
|
|
12467
12605
|
});
|
|
12468
12606
|
return;
|
|
12469
12607
|
}
|
|
12608
|
+
if (e.continuationGateVersion === 1 && continuationExhausted(state, node2)) return;
|
|
12470
12609
|
supersedeLatestWork(state, e.nodeId, ctx.at);
|
|
12471
12610
|
killNodeReviews(state, e.nodeId, ctx.at);
|
|
12472
12611
|
for (const w2 of state.replyWorksOf(e.nodeId)) {
|
|
@@ -12947,7 +13086,7 @@ var init_issue = __esm({
|
|
|
12947
13086
|
}
|
|
12948
13087
|
if (issue2.kind === "gap" || issue2.kind === "escalation") {
|
|
12949
13088
|
const nodeId = issue2.raisedByNodeId ?? issue2.aboutNodeId;
|
|
12950
|
-
if (nodeId) markNodeForRetry(state, nodeId, ctx.at, e.resetRejectedBudget === true);
|
|
13089
|
+
if (nodeId) markNodeForRetry(state, nodeId, ctx.at, e.resetRejectedBudget === true, e.resetCheckpointBudget === true);
|
|
12951
13090
|
}
|
|
12952
13091
|
}
|
|
12953
13092
|
};
|
|
@@ -13148,6 +13287,10 @@ var init_review = __esm({
|
|
|
13148
13287
|
}
|
|
13149
13288
|
return;
|
|
13150
13289
|
}
|
|
13290
|
+
const budget = state.node(w2.nodeId)?.continuationBudget;
|
|
13291
|
+
if (budget?.request && (budget.request.decision === "pending" || budget.request.decision === "denied")) {
|
|
13292
|
+
state.updateNode(w2.nodeId, { continuationBudget: { ...budget, request: { ...budget.request, decision: "completed" } } });
|
|
13293
|
+
}
|
|
13151
13294
|
state.updateNode(w2.nodeId, { latestAcceptId: e.workId, updatedAt: ctx.at });
|
|
13152
13295
|
state.updateWork(e.workId, {
|
|
13153
13296
|
acceptanceState: "accepted",
|
|
@@ -13267,6 +13410,67 @@ var init_review = __esm({
|
|
|
13267
13410
|
}
|
|
13268
13411
|
});
|
|
13269
13412
|
|
|
13413
|
+
// ../engine/src/handlers/continuation.ts
|
|
13414
|
+
var validRounds, continuationInitialize, continuationRequest, continuationDecide;
|
|
13415
|
+
var init_continuation2 = __esm({
|
|
13416
|
+
"../engine/src/handlers/continuation.ts"() {
|
|
13417
|
+
"use strict";
|
|
13418
|
+
init_continuation();
|
|
13419
|
+
validRounds = (n) => Number.isSafeInteger(n) && n >= 1 && n <= 100;
|
|
13420
|
+
continuationInitialize = {
|
|
13421
|
+
name: "continuation/initialize",
|
|
13422
|
+
kind: "continuation.initialized",
|
|
13423
|
+
apply(e, state) {
|
|
13424
|
+
const node2 = state.node(e.nodeId);
|
|
13425
|
+
if (!node2 || node2.continuationBudget || !validRounds(e.rounds)) return;
|
|
13426
|
+
state.updateNode(node2.id, { continuationBudget: { authorizedRounds: e.rounds, requestGeneration: 0, roundsPerGrant: e.rounds, request: null } });
|
|
13427
|
+
}
|
|
13428
|
+
};
|
|
13429
|
+
continuationRequest = {
|
|
13430
|
+
name: "continuation/request",
|
|
13431
|
+
kind: "continuation.requested",
|
|
13432
|
+
apply(e, state, ctx) {
|
|
13433
|
+
const node2 = state.node(e.nodeId);
|
|
13434
|
+
if (!node2 || node2.cancelledAt || !node2.continuationBudget || !validRounds(e.rounds) || continuationRequestId(node2) !== e.requestId || !continuationExhausted(state, node2) || node2.continuationBudget.request?.decision === "pending" || node2.continuationBudget.request?.decision === "denied") return;
|
|
13435
|
+
const latest = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
13436
|
+
if (latest && !latest.endedAt && !latest.deadAt && !continuationAwaitingStart(state, latest)) return;
|
|
13437
|
+
state.updateNode(node2.id, { continuationBudget: {
|
|
13438
|
+
...node2.continuationBudget,
|
|
13439
|
+
requestGeneration: node2.continuationBudget.requestGeneration + 1,
|
|
13440
|
+
request: {
|
|
13441
|
+
id: e.requestId,
|
|
13442
|
+
at: ctx.at,
|
|
13443
|
+
rounds: e.rounds,
|
|
13444
|
+
usedRounds: continuationUsedRounds(state, node2.id),
|
|
13445
|
+
summary: continuationSummary(state, node2, e.rounds),
|
|
13446
|
+
decision: "pending"
|
|
13447
|
+
}
|
|
13448
|
+
} });
|
|
13449
|
+
}
|
|
13450
|
+
};
|
|
13451
|
+
continuationDecide = {
|
|
13452
|
+
name: "continuation/decide",
|
|
13453
|
+
kind: "continuation.decided",
|
|
13454
|
+
apply(e, state, ctx) {
|
|
13455
|
+
const node2 = state.node(e.nodeId);
|
|
13456
|
+
const budget = node2?.continuationBudget;
|
|
13457
|
+
const request2 = budget?.request;
|
|
13458
|
+
if (!node2 || node2.cancelledAt || !budget || !request2 || request2.id !== e.requestId || request2.decision !== "pending" || !ctx.actorId.startsWith("actor:human:") || ctx.actorId !== continuationApprover(state)) return;
|
|
13459
|
+
state.updateNode(node2.id, { continuationBudget: {
|
|
13460
|
+
...budget,
|
|
13461
|
+
authorizedRounds: e.approve ? Math.max(budget.authorizedRounds, continuationUsedRounds(state, node2.id)) + request2.rounds : budget.authorizedRounds,
|
|
13462
|
+
roundsPerGrant: request2.rounds,
|
|
13463
|
+
request: { ...request2, decision: e.approve ? "approved" : "denied", decidedBy: ctx.actorId, decidedAt: ctx.at, reason: e.reason }
|
|
13464
|
+
} });
|
|
13465
|
+
const latest = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
|
|
13466
|
+
if (e.approve && latest && continuationAwaitingStart(state, latest)) {
|
|
13467
|
+
state.emit({ kind: "work.redispatch", workorderId: e.workorderId, workId: latest.id });
|
|
13468
|
+
}
|
|
13469
|
+
}
|
|
13470
|
+
};
|
|
13471
|
+
}
|
|
13472
|
+
});
|
|
13473
|
+
|
|
13270
13474
|
// ../engine/src/handlers/index.ts
|
|
13271
13475
|
var HANDLERS;
|
|
13272
13476
|
var init_handlers = __esm({
|
|
@@ -13278,6 +13482,7 @@ var init_handlers = __esm({
|
|
|
13278
13482
|
init_work();
|
|
13279
13483
|
init_review();
|
|
13280
13484
|
init_issue();
|
|
13485
|
+
init_continuation2();
|
|
13281
13486
|
init_types();
|
|
13282
13487
|
HANDLERS = [
|
|
13283
13488
|
/* ── 工作区级(5)── */
|
|
@@ -13298,6 +13503,9 @@ var init_handlers = __esm({
|
|
|
13298
13503
|
erase(planUpdateReviewRequirements),
|
|
13299
13504
|
erase(planNodeRetry),
|
|
13300
13505
|
erase(planUpdateFields),
|
|
13506
|
+
erase(continuationInitialize),
|
|
13507
|
+
erase(continuationRequest),
|
|
13508
|
+
erase(continuationDecide),
|
|
13301
13509
|
/* ── 节点级 work(7)── */
|
|
13302
13510
|
erase(workCreate),
|
|
13303
13511
|
erase(workRedispatch),
|
|
@@ -13332,7 +13540,7 @@ var init_handlers = __esm({
|
|
|
13332
13540
|
// ../engine/src/bus.ts
|
|
13333
13541
|
function versionCommandEvent(event) {
|
|
13334
13542
|
if (event.kind === "work.create" || event.kind === "review.create") {
|
|
13335
|
-
return { ...event, pauseGateVersion: 1 };
|
|
13543
|
+
return { ...event, pauseGateVersion: 1, ...event.kind === "work.create" ? { continuationGateVersion: 1 } : {} };
|
|
13336
13544
|
}
|
|
13337
13545
|
if (event.kind === "issue.reply") return { ...event, handlingVersion: 1 };
|
|
13338
13546
|
if (event.kind === "work.response" && event.semanticsVersion === 2) {
|
|
@@ -13346,11 +13554,11 @@ function versionCommandEvent(event) {
|
|
|
13346
13554
|
case "work.handoff_recorded":
|
|
13347
13555
|
return { ...event, semanticsVersion: 2 };
|
|
13348
13556
|
case "issue.resolve":
|
|
13349
|
-
return { ...event, semanticsVersion: 2, resetRejectedBudget: true };
|
|
13557
|
+
return { ...event, semanticsVersion: 2, resetRejectedBudget: true, resetCheckpointBudget: true };
|
|
13350
13558
|
case "plan.node_retry":
|
|
13351
13559
|
case "workorder.resumed":
|
|
13352
13560
|
case "plan.assign_actor":
|
|
13353
|
-
return { ...event, resetRejectedBudget: true };
|
|
13561
|
+
return { ...event, resetRejectedBudget: true, resetCheckpointBudget: true };
|
|
13354
13562
|
default:
|
|
13355
13563
|
return event;
|
|
13356
13564
|
}
|
|
@@ -13396,6 +13604,7 @@ var init_bus = __esm({
|
|
|
13396
13604
|
init_model();
|
|
13397
13605
|
init_handlers();
|
|
13398
13606
|
init_state();
|
|
13607
|
+
init_continuation();
|
|
13399
13608
|
SYSTEM_ACTOR = "actor:system:engine";
|
|
13400
13609
|
NOOP_IO = {
|
|
13401
13610
|
async dispatchWork() {
|
|
@@ -13882,6 +14091,21 @@ var init_bus = __esm({
|
|
|
13882
14091
|
const snap = await this.store.transaction((tx) => tx.loadWorkorder(workorderId));
|
|
13883
14092
|
if (!snap || snap.workorder.dispatchPaused) return false;
|
|
13884
14093
|
const work = snap.works.find((row) => row.id === workId);
|
|
14094
|
+
if (work && work.lane === "main" && !work.startedAt && !work.sessionRef && !work.lastActivityAt) {
|
|
14095
|
+
const node2 = snap.nodes.find((row) => row.id === work.nodeId);
|
|
14096
|
+
if (node2 && !node2.continuationBudget && this.io.continuationPolicy) {
|
|
14097
|
+
const policy = await this.io.continuationPolicy(work.assigneeActorId);
|
|
14098
|
+
if (policy) {
|
|
14099
|
+
if (!Number.isSafeInteger(policy.maxRounds) || policy.maxRounds < 1 || policy.maxRounds > 100) {
|
|
14100
|
+
throw new Error(`invalid continuation round limit for ${work.assigneeActorId}`);
|
|
14101
|
+
}
|
|
14102
|
+
buffered.push({ event: { kind: "continuation.initialized", workorderId, nodeId: node2.id, rounds: policy.maxRounds } });
|
|
14103
|
+
buffered.push({ event: { kind: "work.redispatch", workorderId, workId } });
|
|
14104
|
+
return false;
|
|
14105
|
+
}
|
|
14106
|
+
}
|
|
14107
|
+
if (node2 && continuationExhausted(new WorkorderState(snap), node2)) return false;
|
|
14108
|
+
}
|
|
13885
14109
|
if (opts?.neverStarted && work) {
|
|
13886
14110
|
const node2 = snap.nodes.find((row) => row.id === work.nodeId);
|
|
13887
14111
|
if (work.startedAt || work.sessionRef || !node2 || node2.cancelledAt || node2.latestWorkId !== workId) return false;
|
|
@@ -13991,7 +14215,21 @@ var init_bus = __esm({
|
|
|
13991
14215
|
if (!snapshot) return { emitted: 0, terminated: false };
|
|
13992
14216
|
if (snapshot.workorder.lifecycle !== "active") return { emitted: 0, terminated: false };
|
|
13993
14217
|
const state = new WorkorderState(snapshot);
|
|
13994
|
-
const
|
|
14218
|
+
const continuationPolicies = /* @__PURE__ */ new Map();
|
|
14219
|
+
if (this.io.continuationPolicy) {
|
|
14220
|
+
for (const node2 of state.liveNodes()) {
|
|
14221
|
+
const budget = node2.continuationBudget;
|
|
14222
|
+
if (budget && (!continuationExhausted(state, node2) || budget.request?.decision === "pending" || budget.request?.decision === "denied")) continue;
|
|
14223
|
+
if (node2.assigneeActorId && !continuationPolicies.has(node2.assigneeActorId)) {
|
|
14224
|
+
const policy = await this.io.continuationPolicy(node2.assigneeActorId);
|
|
14225
|
+
if (policy && (!Number.isSafeInteger(policy.maxRounds) || policy.maxRounds < 1 || policy.maxRounds > 100)) {
|
|
14226
|
+
throw new Error(`invalid continuation round limit for ${node2.assigneeActorId}`);
|
|
14227
|
+
}
|
|
14228
|
+
continuationPolicies.set(node2.assigneeActorId, policy);
|
|
14229
|
+
}
|
|
14230
|
+
}
|
|
14231
|
+
}
|
|
14232
|
+
const scanCtx = { continuationPolicies, at: this.clock.now(), actorId: SYSTEM_ACTOR, config: this.config, policy: this.policyOf() };
|
|
13995
14233
|
const { events, terminated } = scan(state, scanCtx);
|
|
13996
14234
|
await tx.applyMutations(workorderId, state.mutations);
|
|
13997
14235
|
const allEvents = [...state.emitted, ...events];
|
|
@@ -14126,6 +14364,7 @@ var init_src4 = __esm({
|
|
|
14126
14364
|
"../engine/src/index.ts"() {
|
|
14127
14365
|
"use strict";
|
|
14128
14366
|
init_model();
|
|
14367
|
+
init_continuation();
|
|
14129
14368
|
init_events();
|
|
14130
14369
|
init_views2();
|
|
14131
14370
|
init_state();
|
|
@@ -14320,7 +14559,8 @@ function memoryIndexLines(entries) {
|
|
|
14320
14559
|
const rows = entries.slice(0, MEMORY_INJECT_MAX).map((e) => {
|
|
14321
14560
|
const tag = e.projectId === null ? "[actor]" : "[project]";
|
|
14322
14561
|
const kw = e.keywords.length > 0 ? ` {${e.keywords.join(",")}}` : "";
|
|
14323
|
-
|
|
14562
|
+
const preview2 = e.digest.length > MEMORY_INJECT_DIGEST_CHARS ? `${e.digest.slice(0, MEMORY_INJECT_DIGEST_CHARS)}\u2026` : e.digest;
|
|
14563
|
+
return `- \`${e.memId}\` ${tag}${kw} ${preview2}`;
|
|
14324
14564
|
});
|
|
14325
14565
|
return [
|
|
14326
14566
|
``,
|
|
@@ -15262,6 +15502,7 @@ ${manifestDiff(baseC, newC)}
|
|
|
15262
15502
|
let skillsLines = [];
|
|
15263
15503
|
let identityNote = null;
|
|
15264
15504
|
let batchedContinuation = false;
|
|
15505
|
+
let continuationMaxRounds = 3;
|
|
15265
15506
|
let memoryEnabled = true;
|
|
15266
15507
|
let executorRole = null;
|
|
15267
15508
|
const executionActor = args.actorId ?? artifact.owner;
|
|
@@ -15269,6 +15510,7 @@ ${manifestDiff(baseC, newC)}
|
|
|
15269
15510
|
const actorCtx = await args.resolveActorContext(executionActor).catch(() => null);
|
|
15270
15511
|
if (actorCtx) {
|
|
15271
15512
|
batchedContinuation = actorCtx.config?.batchedContinuation === true;
|
|
15513
|
+
continuationMaxRounds = actorCtx.config?.continuationMaxRounds ?? 3;
|
|
15272
15514
|
memoryEnabled = actorCtx.memoryEnabled !== false;
|
|
15273
15515
|
executorRole = actorCtx.roles?.[0] ?? null;
|
|
15274
15516
|
if (actorCtx.config?.prompt) {
|
|
@@ -15640,7 +15882,7 @@ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
|
|
|
15640
15882
|
`- \u7F3A\u4E00\u4E2A**\u8FD8\u4E0D\u5B58\u5728\u7684\u524D\u7F6E** \u2192 \`oasis gap\` \u4EA4\u4E0A\u53BB\uFF0C**\u522B\u786C\u7F16**\uFF1B\u5DF2\u505A\u5B8C\u7684\u90E8\u5206\u7167\u5E38 \`propose\`\uFF0C\u4F46\u8FD9\u4E00\u8F6E \`conclude\` \u4F1A\u88AB\u62D2\uFF0C\u63D0\u4EA4\u5B8C\u5373\u6536\u5DE5\u2014\u2014\u524D\u7F6E\u5C31\u7EEA\u540E\u7CFB\u7EDF\u4F1A\u5E26\u7740\u89E3\u6CD5\u8BF4\u660E\u91CD\u65B0\u5524\u8D77\u4F60\u3002`,
|
|
15641
15883
|
`- \u5BF9**\u4E0A\u6E38**\u6709\u610F\u89C1\u2014\u2014\u6709\u7591\u95EE\u3001\u8981\u5B83\u6539\u3001\u5224\u5B83\u6CA1\u8FBE\u6807 \u2192 \`oasis annotate <\u4E0A\u6E38id> --body "<\u8BF4\u6E05\u695A>"\` \u540E**\u6536\u5DE5**\u3002\u5199\u7ED9\u4E0A\u6E38\u7684\u8BC4\u8BBA\u81EA\u52A8\u5E26\u963B\u585E\u529B\uFF1A**\u5B83\u7B54\u590D\u524D\u4E0D\u80FD\u6536\u5C3E**\uFF0C\u7B54\u590D\u540E\u7CFB\u7EDF\u91CD\u6D3E\u4F60\u3002`,
|
|
15642
15884
|
...args.part === void 0 ? [`- \u6D3B\u592A\u5927 / \u53EF\u5E76\u884C \u2192 \`oasis add-revision\` \u62C6\u6210\u5B50\u90E8\u4EF6\u6D3E\u51FA\u53BB\uFF0C\u522B\u4E00\u4EBA\u4E00\u4F1A\u8BDD\u786C\u625B\u3002`] : [],
|
|
15643
|
-
...batchedContinuation && args.part === void 0 ? [`- \u957F\u4EFB\u52A1\u5DF2\u5141\u8BB8\u5206\u6279\u7EED\u8DD1\uFF1A\u4E00\u6279\u6709\u8FDB\u5C55\u4F46\u6574\u4F53\u672A\u5B8C\u6210\u65F6\uFF0C\u6309\u300C\u600E\u4E48\u63D0\u4EA4\u300D\u91CC\u7684 propose \u547D\u4EE4\u8FFD\u52A0 \`--checkpoint --continue\`\uFF0Creason \u5199\u6E05\u672C\u6279\u5B8C\u6210\u4E86\u4EC0\u4E48\u3001\u5269\u4EC0\u4E48\u3001\u4E0B\u6279\u4ECE\u54EA\u7EE7\u7EED\uFF0C\u7136\u540E\u6536\u5DE5\uFF1B\u7CFB\u7EDF\
|
|
15885
|
+
...batchedContinuation && args.part === void 0 ? [`- \u957F\u4EFB\u52A1\u5DF2\u5141\u8BB8\u5206\u6279\u7EED\u8DD1\uFF1A\u4E00\u6279\u6709\u8FDB\u5C55\u4F46\u6574\u4F53\u672A\u5B8C\u6210\u65F6\uFF0C\u6309\u300C\u600E\u4E48\u63D0\u4EA4\u300D\u91CC\u7684 propose \u547D\u4EE4\u8FFD\u52A0 \`--checkpoint --continue\`\uFF0Creason \u5199\u6E05\u672C\u6279\u5B8C\u6210\u4E86\u4EC0\u4E48\u3001\u5269\u4EC0\u4E48\u3001\u4E0B\u6279\u4ECE\u54EA\u7EE7\u7EED\uFF0C\u7136\u540E\u6536\u5DE5\uFF1B\u7CFB\u7EDF\u5728\u6BCF\u6B21\u6388\u6743\u7684 ${continuationMaxRounds} \u8F6E\u989D\u5EA6\u5185\u6D3E\u53D1\u4E0B\u4E00\u8F6E\u6B63\u5F0F session\uFF08\u9996\u6B21\u6267\u884C\u4E5F\u8BA1\u4E00\u8F6E\uFF09\uFF0C\u5230\u9650\u5C1A\u672A conclude \u65F6\u6682\u505C\u5E76\u7B49\u5F85\u4EBA\u5DE5\u5BA1\u6279\uFF0C\u540C\u610F\u540E\u8FFD\u52A0\u540C\u6837\u989D\u5EA6\u3002\u7A7A\u4EA7\u51FA\u6216\u8FDE\u7EED\u91CD\u590D\u5DF2\u6709\u5FEB\u7167\u4ECD\u4F1A\u63D0\u524D\u6682\u505C\uFF0C\u9047\u5230\u963B\u585E\u5E94\u5982\u5B9E\u4E0A\u62A5\u3002\u5168\u90E8\u5B8C\u6210\u65F6\u7167\u5E38 propose \u540E \`oasis conclude ${artifactId} --note "<\u4EA4\u4ED8\u7ED3\u8BBA>"\`\u3002`] : [],
|
|
15644
15886
|
`- \u5199\u5165\u53EA\u8D70 \`oasis propose\`\uFF0C\u771F\u6B63\u5B8C\u6574\u4E86\u624D \`oasis conclude\`\uFF1B\u5361\u6CA1\u5361\u4F4F\u3001\u8FDB\u5EA6\u90FD\u662F\u7CFB\u7EDF\u7B97\u7684\uFF0C\u522B\u81EA\u5DF1\u7EF4\u62A4\u3002`,
|
|
15645
15887
|
`- \u4F46**\u7B80\u5355\u3001\u81EA\u8DB3\u3001\u4E0D\u4F9D\u8D56\u672A\u5B9A\u51B3\u7B56\u7684\u4EFB\u52A1**\uFF08\u5982\u6539\u4E2A\u80CC\u666F\u8272\uFF09\u2192 \u76F4\u63A5\u505A\uFF0C\u5224\u65AD\u6743\u5728\u4F60\u3002`,
|
|
15646
15888
|
...typeDef?.recommendedInputs && typeDef.recommendedInputs.length > 0 ? [`- \u672C\u7C7B\u578B\u5E38\u89C1\u4E0A\u6E38\u4F9D\u8D56\uFF08\u63A8\u8350\uFF0C\u975E\u5F3A\u5236\uFF09\uFF1A${typeDef.recommendedInputs.join("\u3001")}\u2014\u2014\u4F9D\u8D56\u5176\u4E2D\u67D0\u4E2A\u800C\u5B83\u8FD8\u6CA1\u6709\uFF0C\u6309\u4E0A\u9762 gap \u89C4\u5219\u62A5\u7F3A\u53E3\u3002`] : []
|
|
@@ -18759,6 +19001,36 @@ function bridgeLoadReadModel(input) {
|
|
|
18759
19001
|
pinMeta.set(edge.toNodeId, record8);
|
|
18760
19002
|
}
|
|
18761
19003
|
const pendingReviews = /* @__PURE__ */ new Map();
|
|
19004
|
+
for (const snap of snapshots2) {
|
|
19005
|
+
let approver;
|
|
19006
|
+
for (const node2 of snap.nodes) {
|
|
19007
|
+
const request2 = node2.continuationBudget?.request;
|
|
19008
|
+
const latest = node2.latestWorkId ? workById.get(node2.latestWorkId) : void 0;
|
|
19009
|
+
if (!request2 || node2.cancelledAt || latest && !latest.endedAt && !latest.deadAt && (latest.startedAt || latest.sessionRef || latest.lastActivityAt || snap.artifacts.some((a) => a.workId === latest.id))) continue;
|
|
19010
|
+
if (request2.decision === "pending" || request2.decision === "denied") {
|
|
19011
|
+
const artifact = artifacts.get(node2.id);
|
|
19012
|
+
if (artifact) artifact.continuationPause = { decision: request2.decision, detail: request2.summary, at: request2.at };
|
|
19013
|
+
}
|
|
19014
|
+
if (request2.decision !== "pending") continue;
|
|
19015
|
+
if (approver === void 0) approver = continuationApprover(new WorkorderState(snap));
|
|
19016
|
+
pendingReviews.set(request2.id, {
|
|
19017
|
+
draftId: request2.id,
|
|
19018
|
+
continuation: { nodeId: node2.id, requestId: request2.id, approverActorId: approver },
|
|
19019
|
+
plan: { workspace: snap.workorder.id, reason: request2.summary, ops: [] },
|
|
19020
|
+
changeClass: "C",
|
|
19021
|
+
nonMonotonicOps: [],
|
|
19022
|
+
proposedBy: node2.assigneeActorId ?? snap.workorder.ownerActorId,
|
|
19023
|
+
origin: null,
|
|
19024
|
+
workspace: snap.workorder.id,
|
|
19025
|
+
advisory: [],
|
|
19026
|
+
downstreamClosure: [],
|
|
19027
|
+
at: request2.at,
|
|
19028
|
+
anchor: node2.id,
|
|
19029
|
+
kind: "command",
|
|
19030
|
+
command: { command: "continueBatch", args: { artifactId: node2.id, rounds: request2.rounds }, effectLabel: request2.summary }
|
|
19031
|
+
});
|
|
19032
|
+
}
|
|
19033
|
+
}
|
|
18762
19034
|
const pointedReviewIds = new Set(
|
|
18763
19035
|
allRequirements.map((r) => r.latestReviewId).filter((x2) => x2 != null)
|
|
18764
19036
|
);
|
|
@@ -19358,6 +19630,7 @@ var import_node_crypto3, PENDING_ROLE_PREFIX2, PENDING_UNASSIGNED2, EngineKernel
|
|
|
19358
19630
|
var init_kernel_bridge = __esm({
|
|
19359
19631
|
"../core/src/kernel-bridge.ts"() {
|
|
19360
19632
|
"use strict";
|
|
19633
|
+
init_src4();
|
|
19361
19634
|
import_node_crypto3 = require("node:crypto");
|
|
19362
19635
|
init_read_model();
|
|
19363
19636
|
init_closure_gate2();
|
|
@@ -21149,7 +21422,35 @@ var init_kernel_bridge = __esm({
|
|
|
21149
21422
|
await this.refreshModel();
|
|
21150
21423
|
return { draftId };
|
|
21151
21424
|
}
|
|
21425
|
+
async decideContinuation(draftId, actor, approve, reason) {
|
|
21426
|
+
const review = this.model.pendingReviews.get(draftId);
|
|
21427
|
+
if (!review?.continuation || !review.workspace) throw new Error("\u7EED\u8DD1\u5BA1\u6279\u4E0D\u5B58\u5728\u6216\u5DF2\u5904\u7406");
|
|
21428
|
+
const snapshot = await this.store.transaction((tx) => tx.loadWorkorder(review.workspace));
|
|
21429
|
+
const node2 = snapshot?.nodes.find((n) => n.id === review.continuation.nodeId);
|
|
21430
|
+
const request2 = node2?.continuationBudget?.request;
|
|
21431
|
+
if (!actor.startsWith("actor:human:") || !snapshot || continuationApprover(new WorkorderState(snapshot)) !== actor) throw new Error("\u4EC5\u5DE5\u5355\u4EBA\u7C7B\u8D1F\u8D23\u4EBA\u53EF\u4EE5\u5BA1\u6279\u7EED\u8DD1\u989D\u5EA6");
|
|
21432
|
+
if (!request2 || request2.id !== draftId || request2.decision !== "pending" || node2?.cancelledAt) throw new Error("\u7EED\u8DD1\u5BA1\u6279\u5DF2\u5904\u7406\u6216\u5931\u6548");
|
|
21433
|
+
const expectedAuthorized = Math.max(
|
|
21434
|
+
node2.continuationBudget.authorizedRounds,
|
|
21435
|
+
continuationUsedRounds(new WorkorderState(snapshot), node2.id)
|
|
21436
|
+
) + request2.rounds;
|
|
21437
|
+
await this.commit({
|
|
21438
|
+
companyId: snapshot.workorder.companyId,
|
|
21439
|
+
workorderId: snapshot.workorder.id,
|
|
21440
|
+
actorId: actor,
|
|
21441
|
+
event: { kind: "continuation.decided", workorderId: snapshot.workorder.id, nodeId: node2.id, requestId: draftId, approve, reason }
|
|
21442
|
+
});
|
|
21443
|
+
const after = await this.store.transaction((tx) => tx.loadWorkorder(snapshot.workorder.id));
|
|
21444
|
+
const afterBudget = after?.nodes.find((n) => n.id === node2.id)?.continuationBudget;
|
|
21445
|
+
const decided = afterBudget?.request;
|
|
21446
|
+
const applied = approve ? (afterBudget?.authorizedRounds ?? 0) >= expectedAuthorized : decided?.id === draftId && (decided.decision === "denied" || decided.decision === "completed");
|
|
21447
|
+
if (!applied) throw new Error("\u7EED\u8DD1\u5BA1\u6279\u53D1\u751F\u51B2\u7A81\uFF0C\u8BF7\u5237\u65B0\u540E\u67E5\u770B\u7ED3\u679C");
|
|
21448
|
+
return node2.id;
|
|
21449
|
+
}
|
|
21152
21450
|
async applyAuthorizedCommand(args) {
|
|
21451
|
+
if (this.model.pendingReviews.get(args.draftId)?.continuation) {
|
|
21452
|
+
return { command: "continueBatch", artifactId: await this.decideContinuation(args.draftId, args.actor, true) };
|
|
21453
|
+
}
|
|
21153
21454
|
const card2 = this.draftCards.get(args.draftId);
|
|
21154
21455
|
if (!card2 || card2.kind !== "command" || !card2.command) {
|
|
21155
21456
|
throw new Error(`\u4E0D\u662F\u547D\u4EE4\u6388\u6743\u8BF7\u6C42\u6216\u5DF2\u5904\u7406\uFF1A${args.draftId}`);
|
|
@@ -21179,6 +21480,10 @@ var init_kernel_bridge = __esm({
|
|
|
21179
21480
|
return { command: card2.command, artifactId };
|
|
21180
21481
|
}
|
|
21181
21482
|
async rejectIntervention(args) {
|
|
21483
|
+
if (this.model.pendingReviews.get(args.draftId)?.continuation) {
|
|
21484
|
+
await this.decideContinuation(args.draftId, args.actor, false, args.reason);
|
|
21485
|
+
return;
|
|
21486
|
+
}
|
|
21182
21487
|
this.draftCards.delete(args.draftId);
|
|
21183
21488
|
await this.forgetDraftCard(args.draftId);
|
|
21184
21489
|
await this.refreshModel();
|
|
@@ -24897,6 +25202,8 @@ async function runProtocolSession(job, cfg) {
|
|
|
24897
25202
|
const transcript = openTranscript(transcriptRef);
|
|
24898
25203
|
const outputCbs = [];
|
|
24899
25204
|
const telemetryCbs = [];
|
|
25205
|
+
const liveCbs = [];
|
|
25206
|
+
const appendByKey = /* @__PURE__ */ new Map();
|
|
24900
25207
|
const exitCbs = [];
|
|
24901
25208
|
const state = cfg.createState();
|
|
24902
25209
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -24932,7 +25239,7 @@ async function runProtocolSession(job, cfg) {
|
|
|
24932
25239
|
pending.clear();
|
|
24933
25240
|
};
|
|
24934
25241
|
const send = (command) => {
|
|
24935
|
-
if (closing || child.stdin.destroyed || !child.stdin.writable) return Promise.reject(new
|
|
25242
|
+
if (closing || child.stdin.destroyed || !child.stdin.writable) return Promise.reject(new ProtocolSessionClosing("session stdin is not writable"));
|
|
24936
25243
|
const idValue = command.id ?? `oasis_${++nextRequestId}`;
|
|
24937
25244
|
const message = { ...command, id: idValue };
|
|
24938
25245
|
const key = commandId(idValue);
|
|
@@ -24952,11 +25259,12 @@ async function runProtocolSession(job, cfg) {
|
|
|
24952
25259
|
if (isResponse(message) && responseId !== void 0 && pending.has(responseId)) {
|
|
24953
25260
|
const waiter = pending.get(responseId);
|
|
24954
25261
|
pending.delete(responseId);
|
|
24955
|
-
if (message.type === "response" && message.success === false) waiter.reject(new
|
|
24956
|
-
else if (Object.prototype.hasOwnProperty.call(message, "error")) waiter.reject(new
|
|
25262
|
+
if (message.type === "response" && message.success === false) waiter.reject(new ProtocolCommandRejected(String(message.error ?? "runtime rejected command")));
|
|
25263
|
+
else if (Object.prototype.hasOwnProperty.call(message, "error")) waiter.reject(new ProtocolCommandRejected(String(message.error?.message ?? message.error)));
|
|
24957
25264
|
else waiter.resolve(message.result ?? message);
|
|
24958
25265
|
return;
|
|
24959
25266
|
}
|
|
25267
|
+
for (const event of cfg.liveEvents?.(message, state) ?? []) for (const cb of liveCbs) cb(event);
|
|
24960
25268
|
for (const line of cfg.normalize(message, state)) {
|
|
24961
25269
|
if (line.outputText) for (const cb of outputCbs) cb(line.outputText);
|
|
24962
25270
|
const event = { kind: line.kind, payload: line.payload, seq: ++eventSeq, ts: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -25048,26 +25356,34 @@ async function runProtocolSession(job, cfg) {
|
|
|
25048
25356
|
onTelemetry(cb) {
|
|
25049
25357
|
telemetryCbs.push(cb);
|
|
25050
25358
|
},
|
|
25359
|
+
onLiveEvent(cb) {
|
|
25360
|
+
liveCbs.push(cb);
|
|
25361
|
+
},
|
|
25051
25362
|
get canAppendInput() {
|
|
25052
25363
|
return !closing && started && !exited;
|
|
25053
25364
|
},
|
|
25054
25365
|
appendInput(input) {
|
|
25055
|
-
|
|
25366
|
+
const key = input.clientMessageKey;
|
|
25367
|
+
if (key && appendByKey.has(key)) return appendByKey.get(key);
|
|
25368
|
+
if (input.requireConsumptionReceipt && !cfg.supportsInputConsumptionReceipts) return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
|
|
25056
25369
|
appendChain = appendChain.then(async () => {
|
|
25057
25370
|
if (exited || closing) return { accepted: false, reason: "session-closing" };
|
|
25058
25371
|
const files = materializeFiles(dir, input.files, input.binaryFiles);
|
|
25059
25372
|
try {
|
|
25060
|
-
const
|
|
25061
|
-
return { ...
|
|
25062
|
-
} catch {
|
|
25063
|
-
|
|
25373
|
+
const result2 = await cfg.append(input, state, send);
|
|
25374
|
+
return { ...result2, ...files.written.length || files.failed.length ? { files } : {} };
|
|
25375
|
+
} catch (error2) {
|
|
25376
|
+
const reason = error2 instanceof ProtocolSessionClosing ? "session-closing" : error2 instanceof ProtocolCommandRejected ? "rejected" : "unconfirmed";
|
|
25377
|
+
return { accepted: false, reason, ...files.written.length || files.failed.length ? { files } : {} };
|
|
25064
25378
|
}
|
|
25065
25379
|
});
|
|
25066
|
-
|
|
25380
|
+
const result = appendChain;
|
|
25381
|
+
if (key) appendByKey.set(key, result);
|
|
25382
|
+
return result;
|
|
25067
25383
|
}
|
|
25068
25384
|
};
|
|
25069
25385
|
}
|
|
25070
|
-
var import_node_child_process6, import_node_crypto10, path9;
|
|
25386
|
+
var import_node_child_process6, import_node_crypto10, path9, ProtocolCommandRejected, ProtocolSessionClosing;
|
|
25071
25387
|
var init_protocol_session = __esm({
|
|
25072
25388
|
"../adapters/src/_core/protocol-session.ts"() {
|
|
25073
25389
|
"use strict";
|
|
@@ -25083,6 +25399,10 @@ var init_protocol_session = __esm({
|
|
|
25083
25399
|
init_transcript();
|
|
25084
25400
|
init_filter_extra_args();
|
|
25085
25401
|
init_root_bypass();
|
|
25402
|
+
ProtocolCommandRejected = class extends Error {
|
|
25403
|
+
};
|
|
25404
|
+
ProtocolSessionClosing = class extends Error {
|
|
25405
|
+
};
|
|
25086
25406
|
}
|
|
25087
25407
|
});
|
|
25088
25408
|
|
|
@@ -25595,11 +25915,13 @@ var init_codex = __esm({
|
|
|
25595
25915
|
workRoot: this.opts.workRoot,
|
|
25596
25916
|
env: this.opts.env,
|
|
25597
25917
|
runtimeKind: "codex",
|
|
25918
|
+
supportsInputConsumptionReceipts: true,
|
|
25598
25919
|
syncSkills: this.opts.syncSkills,
|
|
25599
25920
|
createState: () => ({
|
|
25600
25921
|
threadId: void 0,
|
|
25601
25922
|
turnId: void 0,
|
|
25602
25923
|
active: false,
|
|
25924
|
+
pendingInputs: /* @__PURE__ */ new Set(),
|
|
25603
25925
|
codex: createCodexNormalizeState(),
|
|
25604
25926
|
// 本次 run 的用量:逐轮累加 `thread/tokenUsage/updated` 的 `last`(见该解析函数注释)。
|
|
25605
25927
|
usage: { input: 0, cacheRead: 0, cacheWrite: 0, output: 0 },
|
|
@@ -25643,6 +25965,34 @@ ${task}` : task;
|
|
|
25643
25965
|
if (typeof turnId === "string" && turnId) state.turnId = turnId;
|
|
25644
25966
|
if (!state.turnId) throw new Error("codex app-server returned no turn id");
|
|
25645
25967
|
},
|
|
25968
|
+
liveEvents(message, state) {
|
|
25969
|
+
const params = message.params;
|
|
25970
|
+
const method = message.method;
|
|
25971
|
+
const turnId = typeof params?.turnId === "string" ? params.turnId : state.turnId ?? "codex-pending";
|
|
25972
|
+
const base = { protocolVersion: 2, streamId: `codex:${state.threadId}`, turnId };
|
|
25973
|
+
if (method === "item/agentMessage/delta" && typeof params?.itemId === "string" && typeof params.delta === "string") {
|
|
25974
|
+
return [{ ...base, itemId: params.itemId, itemType: "message", operation: "append_text", payload: { text: params.delta } }];
|
|
25975
|
+
}
|
|
25976
|
+
if (method === "item/started" || method === "item/completed") {
|
|
25977
|
+
const item = params?.item;
|
|
25978
|
+
if (item?.type === "userMessage" && typeof item.clientId === "string" && state.pendingInputs.delete(item.clientId)) {
|
|
25979
|
+
return [{
|
|
25980
|
+
...base,
|
|
25981
|
+
itemId: `input:${item.clientId}`,
|
|
25982
|
+
itemType: "control",
|
|
25983
|
+
operation: "completed",
|
|
25984
|
+
payload: { code: "input_consumed", clientMessageKey: item.clientId }
|
|
25985
|
+
}];
|
|
25986
|
+
}
|
|
25987
|
+
if (method === "item/completed" && item?.type === "agentMessage" && typeof item.id === "string") {
|
|
25988
|
+
return [{ ...base, itemId: item.id, itemType: "message", operation: "completed", payload: { text: typeof item.text === "string" ? item.text : "" } }];
|
|
25989
|
+
}
|
|
25990
|
+
}
|
|
25991
|
+
if (method === "turn/completed") {
|
|
25992
|
+
return [{ ...base, itemId: "turn-end", itemType: "control", operation: "completed", payload: { code: "output_block_completed" } }];
|
|
25993
|
+
}
|
|
25994
|
+
return [];
|
|
25995
|
+
},
|
|
25646
25996
|
normalize(message, state) {
|
|
25647
25997
|
const params = message.params;
|
|
25648
25998
|
const method = typeof message.method === "string" ? message.method : "";
|
|
@@ -25701,7 +26051,7 @@ ${task}` : task;
|
|
|
25701
26051
|
};
|
|
25702
26052
|
},
|
|
25703
26053
|
async append(input, state, send) {
|
|
25704
|
-
if (!state.threadId || !state.turnId) return { accepted: false, reason: "
|
|
26054
|
+
if (!state.threadId || !state.turnId || !state.active) return { accepted: false, reason: "session-closing" };
|
|
25705
26055
|
if (input.interrupt) {
|
|
25706
26056
|
await send({ jsonrpc: "2.0", method: "turn/interrupt", params: { threadId: state.threadId, turnId: state.turnId } });
|
|
25707
26057
|
const next = await send({ jsonrpc: "2.0", method: "turn/start", params: { threadId: state.threadId, input: [{ type: "text", text: input.text }] } });
|
|
@@ -25710,13 +26060,22 @@ ${task}` : task;
|
|
|
25710
26060
|
state.active = true;
|
|
25711
26061
|
return { accepted: true, interrupt: "unknown" };
|
|
25712
26062
|
}
|
|
25713
|
-
|
|
25714
|
-
|
|
25715
|
-
|
|
25716
|
-
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
|
|
26063
|
+
if (input.clientMessageKey) state.pendingInputs.add(input.clientMessageKey);
|
|
26064
|
+
try {
|
|
26065
|
+
await send({ jsonrpc: "2.0", method: "turn/steer", params: {
|
|
26066
|
+
threadId: state.threadId,
|
|
26067
|
+
expectedTurnId: state.turnId,
|
|
26068
|
+
...input.clientMessageKey ? { clientUserMessageId: input.clientMessageKey } : {},
|
|
26069
|
+
input: [{ type: "text", text: input.text }]
|
|
26070
|
+
} });
|
|
26071
|
+
} catch (error2) {
|
|
26072
|
+
if (input.clientMessageKey) state.pendingInputs.delete(input.clientMessageKey);
|
|
26073
|
+
if (error2 instanceof ProtocolCommandRejected && /no active turn|not active|expected.*turn|turn.*mismatch/i.test(error2.message)) {
|
|
26074
|
+
return { accepted: false, reason: "session-closing" };
|
|
26075
|
+
}
|
|
26076
|
+
throw error2;
|
|
26077
|
+
}
|
|
26078
|
+
return { accepted: true, consumption: "pending" };
|
|
25720
26079
|
}
|
|
25721
26080
|
});
|
|
25722
26081
|
}
|
|
@@ -26592,6 +26951,7 @@ async function runACPSession(job, cfg) {
|
|
|
26592
26951
|
const transcriptOut = openTranscript(transcriptRef);
|
|
26593
26952
|
const outputCbs = [];
|
|
26594
26953
|
const telemetryCbs = [];
|
|
26954
|
+
const liveCbs = [];
|
|
26595
26955
|
const exitCbs = [];
|
|
26596
26956
|
let exited;
|
|
26597
26957
|
let killReason = null;
|
|
@@ -26670,6 +27030,34 @@ ${note}` : note };
|
|
|
26670
27030
|
});
|
|
26671
27031
|
let lastRpcError;
|
|
26672
27032
|
let streamingCurrentTurn = false;
|
|
27033
|
+
const emitBoundary = () => {
|
|
27034
|
+
for (const cb of liveCbs) cb({
|
|
27035
|
+
protocolVersion: 2,
|
|
27036
|
+
streamId: id,
|
|
27037
|
+
turnId: id,
|
|
27038
|
+
itemId: "prompt-boundary",
|
|
27039
|
+
itemType: "control",
|
|
27040
|
+
operation: "completed",
|
|
27041
|
+
payload: { code: "output_block_completed" }
|
|
27042
|
+
});
|
|
27043
|
+
};
|
|
27044
|
+
const confirmedInputs = /* @__PURE__ */ new Set();
|
|
27045
|
+
const confirmAppendBatch = () => {
|
|
27046
|
+
for (const input of inflightAppendBatch) {
|
|
27047
|
+
const key = input.clientMessageKey;
|
|
27048
|
+
if (!key || confirmedInputs.has(key)) continue;
|
|
27049
|
+
confirmedInputs.add(key);
|
|
27050
|
+
for (const cb of liveCbs) cb({
|
|
27051
|
+
protocolVersion: 2,
|
|
27052
|
+
streamId: id,
|
|
27053
|
+
turnId: id,
|
|
27054
|
+
itemId: `input:${key}`,
|
|
27055
|
+
itemType: "control",
|
|
27056
|
+
operation: "completed",
|
|
27057
|
+
payload: { code: "input_consumed", clientMessageKey: key }
|
|
27058
|
+
});
|
|
27059
|
+
}
|
|
27060
|
+
};
|
|
26673
27061
|
const client = new ACPClient(
|
|
26674
27062
|
(line) => {
|
|
26675
27063
|
try {
|
|
@@ -26678,9 +27066,11 @@ ${note}` : note };
|
|
|
26678
27066
|
}
|
|
26679
27067
|
},
|
|
26680
27068
|
(text5) => {
|
|
27069
|
+
confirmAppendBatch();
|
|
26681
27070
|
for (const cb of outputCbs) cb(text5);
|
|
26682
27071
|
},
|
|
26683
27072
|
(e) => {
|
|
27073
|
+
if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) confirmAppendBatch();
|
|
26684
27074
|
for (const cb of telemetryCbs) cb(e);
|
|
26685
27075
|
},
|
|
26686
27076
|
// runtime 自己用 stopReason=cancelled 收尾时也保留取消语义;此前误记成 error,
|
|
@@ -26814,9 +27204,11 @@ ${task}` : task;
|
|
|
26814
27204
|
promptResult = await sendPrompt(userText);
|
|
26815
27205
|
}
|
|
26816
27206
|
while (!killReason && appendQueue.length > 0) {
|
|
27207
|
+
emitBoundary();
|
|
26817
27208
|
inflightAppendBatch = appendQueue.splice(0);
|
|
26818
27209
|
const merged = inflightAppendBatch.map((i) => i.text).filter(Boolean).join("\n\n");
|
|
26819
27210
|
promptResult = await sendPrompt(merged);
|
|
27211
|
+
confirmAppendBatch();
|
|
26820
27212
|
inflightAppendBatch = [];
|
|
26821
27213
|
}
|
|
26822
27214
|
closingForExit = true;
|
|
@@ -26855,6 +27247,9 @@ ${task}` : task;
|
|
|
26855
27247
|
onTelemetry(cb) {
|
|
26856
27248
|
telemetryCbs.push(cb);
|
|
26857
27249
|
},
|
|
27250
|
+
onLiveEvent(cb) {
|
|
27251
|
+
liveCbs.push(cb);
|
|
27252
|
+
},
|
|
26858
27253
|
// ADR-0093 对话追加:入队一条 user 输入,drain 循环会把它发成同 session 的下一回合。
|
|
26859
27254
|
// 已收尾/已退出则拒(session-closing),上层据此降级 kill+resume。
|
|
26860
27255
|
get canAppendInput() {
|
|
@@ -26878,12 +27273,15 @@ ${task}` : task;
|
|
|
26878
27273
|
}
|
|
26879
27274
|
};
|
|
26880
27275
|
function doAppend(input) {
|
|
26881
|
-
if (input.requireConsumptionReceipt) return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
|
|
26882
27276
|
if (exited || closingForExit) return Promise.resolve({ accepted: false, reason: "session-closing" });
|
|
26883
27277
|
const report = materializeFiles(dir, input.files, input.binaryFiles);
|
|
26884
27278
|
const queued = report.failed.length ? { ...input, text: appendMaterializeWarning(input.text, report) } : input;
|
|
26885
27279
|
appendQueue.push(queued);
|
|
26886
|
-
const withFiles = (r) =>
|
|
27280
|
+
const withFiles = (r) => ({
|
|
27281
|
+
...r,
|
|
27282
|
+
...input.requireConsumptionReceipt && r.accepted ? { consumption: "pending" } : {},
|
|
27283
|
+
...report.written.length || report.failed.length ? { files: report } : {}
|
|
27284
|
+
});
|
|
26887
27285
|
if (!input.interrupt) return Promise.resolve(withFiles({ accepted: true, interrupt: "queued_at_boundary" }));
|
|
26888
27286
|
const run = appendChain.then(async () => {
|
|
26889
27287
|
const pending = inflightStopReason;
|
|
@@ -27637,12 +28035,13 @@ var init_pi = __esm({
|
|
|
27637
28035
|
return ["--mode", "rpc", "--session", sessionFile, ...model ? ["--model", model] : []];
|
|
27638
28036
|
},
|
|
27639
28037
|
runtimeKind: "pi",
|
|
28038
|
+
supportsInputConsumptionReceipts: true,
|
|
27640
28039
|
workRoot: opts.workRoot,
|
|
27641
28040
|
cleanupWorkdir: opts.cleanupWorkdir,
|
|
27642
28041
|
env: opts.env,
|
|
27643
28042
|
extraArgs: opts.extraArgs,
|
|
27644
28043
|
blockedFlags: PI_BLOCKED_FLAGS,
|
|
27645
|
-
createState: () => ({}),
|
|
28044
|
+
createState: () => ({ pendingInputs: [] }),
|
|
27646
28045
|
initialCommand(job2, _dir, task) {
|
|
27647
28046
|
const prompt2 = job2.systemPrompt ? `${job2.systemPrompt}
|
|
27648
28047
|
|
|
@@ -27659,6 +28058,28 @@ ${task}` : task;
|
|
|
27659
28058
|
} catch {
|
|
27660
28059
|
}
|
|
27661
28060
|
},
|
|
28061
|
+
liveEvents(message, state) {
|
|
28062
|
+
const base = { protocolVersion: 2, streamId: "pi-rpc", turnId: "pi-rpc" };
|
|
28063
|
+
const raw = message.message;
|
|
28064
|
+
if (message.type === "message_end" && raw?.role === "assistant") {
|
|
28065
|
+
return [{ ...base, itemId: "message-end", itemType: "control", operation: "completed", payload: { code: "output_block_completed" } }];
|
|
28066
|
+
}
|
|
28067
|
+
if (message.type === "message_start" && raw?.role === "user") {
|
|
28068
|
+
const text5 = typeof raw.content === "string" ? raw.content : Array.isArray(raw.content) ? raw.content.filter((p2) => p2.type === "text").map((p2) => p2.text).join("") : void 0;
|
|
28069
|
+
const pending = state.pendingInputs[0];
|
|
28070
|
+
if (pending && text5 === pending.text) {
|
|
28071
|
+
state.pendingInputs.shift();
|
|
28072
|
+
return [{
|
|
28073
|
+
...base,
|
|
28074
|
+
itemId: `input:${pending.key}`,
|
|
28075
|
+
itemType: "control",
|
|
28076
|
+
operation: "completed",
|
|
28077
|
+
payload: { code: "input_consumed", clientMessageKey: pending.key }
|
|
28078
|
+
}];
|
|
28079
|
+
}
|
|
28080
|
+
}
|
|
28081
|
+
return [];
|
|
28082
|
+
},
|
|
27662
28083
|
normalize(message) {
|
|
27663
28084
|
return normalizePi(JSON.stringify(message));
|
|
27664
28085
|
},
|
|
@@ -27668,14 +28089,21 @@ ${task}` : task;
|
|
|
27668
28089
|
runtimeSessionId(state) {
|
|
27669
28090
|
return state.sessionId;
|
|
27670
28091
|
},
|
|
27671
|
-
async append(input,
|
|
27672
|
-
|
|
27673
|
-
|
|
28092
|
+
async append(input, state, send) {
|
|
28093
|
+
const pending = input.clientMessageKey ? { key: input.clientMessageKey, text: input.text } : void 0;
|
|
28094
|
+
if (pending) state.pendingInputs.push(pending);
|
|
28095
|
+
try {
|
|
28096
|
+
if (input.interrupt) {
|
|
28097
|
+
await send({ type: "abort" });
|
|
28098
|
+
await send({ type: "steer", message: input.text });
|
|
28099
|
+
return { accepted: true, interrupt: "unknown" };
|
|
28100
|
+
}
|
|
27674
28101
|
await send({ type: "steer", message: input.text });
|
|
27675
|
-
return { accepted: true,
|
|
28102
|
+
return { accepted: true, consumption: "pending" };
|
|
28103
|
+
} catch (error2) {
|
|
28104
|
+
if (pending) state.pendingInputs = state.pendingInputs.filter((p2) => p2 !== pending);
|
|
28105
|
+
throw error2;
|
|
27676
28106
|
}
|
|
27677
|
-
await send({ type: "steer", message: input.text });
|
|
27678
|
-
return { accepted: true };
|
|
27679
28107
|
}
|
|
27680
28108
|
});
|
|
27681
28109
|
}
|
|
@@ -28485,10 +28913,13 @@ var init_normalizer = __esm({
|
|
|
28485
28913
|
proseClaim = /* @__PURE__ */ new Map();
|
|
28486
28914
|
/** 本 turn 内已经见过的 item 键——认领闸只拦「新起一段」,不拦已知 item 的后续 op。 */
|
|
28487
28915
|
claimedKeys = /* @__PURE__ */ new Set();
|
|
28916
|
+
closedKeys = /* @__PURE__ */ new Set();
|
|
28488
28917
|
/** 见过 stdout 文本没有——见过就压掉 telemetry `message`(与 server.ts 原口径同款去重)。 */
|
|
28489
28918
|
sawTextOutput = false;
|
|
28490
28919
|
/** 本 turn 产出过 item op 没有。收尾时用来决定要不要发 turn 信号(见 turnFinished)。 */
|
|
28491
28920
|
producedAny = false;
|
|
28921
|
+
/** Receipts wait for a whole output block; they never split its bytes. */
|
|
28922
|
+
pendingInputs = /* @__PURE__ */ new Map();
|
|
28492
28923
|
constructor(opts) {
|
|
28493
28924
|
this.providerName = opts.providerName;
|
|
28494
28925
|
this.hasLiveProtocol = opts.hasLiveProtocol === true;
|
|
@@ -28532,20 +28963,17 @@ var init_normalizer = __esm({
|
|
|
28532
28963
|
}
|
|
28533
28964
|
/** 结构化实时事件(原生 Live Protocol v2)。 */
|
|
28534
28965
|
onLiveEvent(event) {
|
|
28535
|
-
this.turnId = event.turnId || this.turnId;
|
|
28536
28966
|
const payload = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload : {};
|
|
28537
28967
|
if (event.itemType === "control" && payload.code === "input_consumed" && typeof payload.clientMessageKey === "string") {
|
|
28968
|
+
this.pendingInputs.set(payload.clientMessageKey, event.itemId);
|
|
28969
|
+
if (!this.open) this.flushInputs();
|
|
28970
|
+
return;
|
|
28971
|
+
}
|
|
28972
|
+
if (event.itemType === "control" && payload.code === "output_block_completed") {
|
|
28538
28973
|
this.closeOpen("completed");
|
|
28539
|
-
this.emitItem({
|
|
28540
|
-
key: providerItemKey(this.providerName, event.itemId),
|
|
28541
|
-
kind: "control",
|
|
28542
|
-
role: "system",
|
|
28543
|
-
origin: "live",
|
|
28544
|
-
opShape: { op: "set_status", status: "completed" },
|
|
28545
|
-
attrs: { code: "input_consumed", clientMessageKey: payload.clientMessageKey }
|
|
28546
|
-
});
|
|
28547
28974
|
return;
|
|
28548
28975
|
}
|
|
28976
|
+
this.turnId = event.turnId || this.turnId;
|
|
28549
28977
|
if (event.operation === "turn_completed" || event.operation === "turn_failed") {
|
|
28550
28978
|
this.turnFinished(event.operation === "turn_failed" ? "failed" : "completed");
|
|
28551
28979
|
return;
|
|
@@ -28561,13 +28989,12 @@ var init_normalizer = __esm({
|
|
|
28561
28989
|
}
|
|
28562
28990
|
return;
|
|
28563
28991
|
}
|
|
28564
|
-
if (event.operation === "completed" && (event.itemType === "message" || event.itemType === "thinking")
|
|
28565
|
-
this.
|
|
28566
|
-
|
|
28567
|
-
payload.text,
|
|
28568
|
-
|
|
28569
|
-
|
|
28570
|
-
);
|
|
28992
|
+
if (event.operation === "completed" && (event.itemType === "message" || event.itemType === "thinking")) {
|
|
28993
|
+
const key = providerItemKey(this.providerName, event.itemId);
|
|
28994
|
+
if (typeof payload.text === "string") {
|
|
28995
|
+
this.snapshotProse(event.itemType === "message" ? "text" : "thinking", payload.text, "live", key);
|
|
28996
|
+
}
|
|
28997
|
+
if (this.open?.key === key) this.closeOpen("completed");
|
|
28571
28998
|
return;
|
|
28572
28999
|
}
|
|
28573
29000
|
if (event.operation === "started" && event.itemType === "tool") {
|
|
@@ -28592,6 +29019,7 @@ var init_normalizer = __esm({
|
|
|
28592
29019
|
this.fallbackIndex.clear();
|
|
28593
29020
|
this.proseClaim.clear();
|
|
28594
29021
|
this.claimedKeys.clear();
|
|
29022
|
+
this.closedKeys.clear();
|
|
28595
29023
|
this.sawTextOutput = false;
|
|
28596
29024
|
this.producedAny = false;
|
|
28597
29025
|
if (!produced) return;
|
|
@@ -28615,6 +29043,10 @@ var init_normalizer = __esm({
|
|
|
28615
29043
|
if (!known && !this.claim(kind, origin)) return;
|
|
28616
29044
|
const key = explicitKey ?? (this.open && this.open.kind === kind ? this.open.key : this.nextFallbackKey(kind));
|
|
28617
29045
|
this.claimedKeys.add(key);
|
|
29046
|
+
if (known && this.closedKeys.has(key)) {
|
|
29047
|
+
this.emitItem({ key, kind, role: "assistant", origin, opShape: { op: "append_text", text: text5 } });
|
|
29048
|
+
return;
|
|
29049
|
+
}
|
|
28618
29050
|
if (!this.open || this.open.key !== key) {
|
|
28619
29051
|
this.closeOpen("completed");
|
|
28620
29052
|
this.open = { key, kind, origin };
|
|
@@ -28631,6 +29063,10 @@ var init_normalizer = __esm({
|
|
|
28631
29063
|
if (!known && !this.claim(kind, origin)) return;
|
|
28632
29064
|
const key = explicitKey ?? (this.open && this.open.kind === kind ? this.open.key : this.nextFallbackKey(kind));
|
|
28633
29065
|
this.claimedKeys.add(key);
|
|
29066
|
+
if (known && this.closedKeys.has(key)) {
|
|
29067
|
+
this.emitItem({ key, kind, role: "assistant", origin, opShape: { op: "set_text", text: text5 } });
|
|
29068
|
+
return;
|
|
29069
|
+
}
|
|
28634
29070
|
if (!this.open || this.open.key !== key) {
|
|
28635
29071
|
this.closeOpen("completed");
|
|
28636
29072
|
this.open = { key, kind, origin };
|
|
@@ -28649,8 +29085,23 @@ var init_normalizer = __esm({
|
|
|
28649
29085
|
closeOpen(status) {
|
|
28650
29086
|
const open3 = this.open;
|
|
28651
29087
|
this.open = null;
|
|
28652
|
-
if (
|
|
28653
|
-
this.emitItem({ key: open3.key, kind: open3.kind, role: "assistant", origin: open3.origin, opShape: { op: "set_status", status } });
|
|
29088
|
+
if (open3) this.closedKeys.add(open3.key);
|
|
29089
|
+
if (open3) this.emitItem({ key: open3.key, kind: open3.kind, role: "assistant", origin: open3.origin, opShape: { op: "set_status", status } });
|
|
29090
|
+
this.flushInputs();
|
|
29091
|
+
}
|
|
29092
|
+
flushInputs() {
|
|
29093
|
+
const inputs = [...this.pendingInputs];
|
|
29094
|
+
this.pendingInputs.clear();
|
|
29095
|
+
for (const [clientMessageKey, id] of inputs) {
|
|
29096
|
+
this.emitItem({
|
|
29097
|
+
key: providerItemKey(this.providerName, id),
|
|
29098
|
+
kind: "control",
|
|
29099
|
+
role: "system",
|
|
29100
|
+
origin: "live",
|
|
29101
|
+
opShape: { op: "set_status", status: "completed" },
|
|
29102
|
+
attrs: { code: "input_consumed", clientMessageKey }
|
|
29103
|
+
});
|
|
29104
|
+
}
|
|
28654
29105
|
}
|
|
28655
29106
|
toolCall(origin, id, name, input) {
|
|
28656
29107
|
this.closeOpen("completed");
|
|
@@ -28870,7 +29321,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28870
29321
|
telemetryTextInContent;
|
|
28871
29322
|
/** 本段的「无输出」注解已经提前落过(`closeSegmentBeforeUserInput`),`beginSegment` 别再落一条。 */
|
|
28872
29323
|
segmentEndEmitted = false;
|
|
28873
|
-
/** **不变量 1**:`providerItemKey → itemId
|
|
29324
|
+
/** **不变量 1**:`providerItemKey → itemId`,本账本唯一持有。仅 runtime turn 结束/收尾清空,用户插入保留。 */
|
|
28874
29325
|
itemKeyMap = /* @__PURE__ */ new Map();
|
|
28875
29326
|
/** 内存账本:itemId → 行。插入顺序即 seq 顺序。 */
|
|
28876
29327
|
rows = /* @__PURE__ */ new Map();
|
|
@@ -29049,7 +29500,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29049
29500
|
}
|
|
29050
29501
|
});
|
|
29051
29502
|
const row = this.rows.get(id);
|
|
29052
|
-
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
29503
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord, messageId: row.messageId };
|
|
29053
29504
|
}
|
|
29054
29505
|
/**
|
|
29055
29506
|
* 回灌重启前已经落库的那段正文(`chat-recovery` 重挂 / 迟到对账用)。
|
|
@@ -29084,7 +29535,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29084
29535
|
// ── 段与归属 ──────────────────────────────────────────────────────────────
|
|
29085
29536
|
/**
|
|
29086
29537
|
* 切段(人中途插入):此刻起建的 item 属于**下一条** assistant 行。
|
|
29087
|
-
*
|
|
29538
|
+
* 已有 provider item 保留身份与位置;新 item 才归下一段,迟到快照不能复制旧块。
|
|
29088
29539
|
*/
|
|
29089
29540
|
beginSegment() {
|
|
29090
29541
|
if (this.segmentEndEmitted) {
|
|
@@ -29093,8 +29544,6 @@ var init_chat_item_ledger = __esm({
|
|
|
29093
29544
|
this.closeStreamingRows("completed");
|
|
29094
29545
|
if (this.segmentItemCount === 0 && this.messageId) this.insertSegmentEnd();
|
|
29095
29546
|
}
|
|
29096
|
-
this.itemKeyMap.clear();
|
|
29097
|
-
this.lastTurnId = null;
|
|
29098
29547
|
this.messageId = null;
|
|
29099
29548
|
this.segment += 1;
|
|
29100
29549
|
this.segmentItemCount = 0;
|
|
@@ -29118,7 +29567,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29118
29567
|
const id = this.insertSegmentEnd();
|
|
29119
29568
|
this.segmentEndEmitted = true;
|
|
29120
29569
|
const row = this.rows.get(id);
|
|
29121
|
-
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
29570
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord, messageId: row.messageId };
|
|
29122
29571
|
}
|
|
29123
29572
|
/** 零输出段兜底(S1 评审 B1):那条 assistant 行库里已经在了,一条 item 都没有会被对账抓成假红。 */
|
|
29124
29573
|
insertSegmentEnd() {
|
|
@@ -29137,6 +29586,9 @@ var init_chat_item_ledger = __esm({
|
|
|
29137
29586
|
/** 行落库了,把本段还挂着 NULL 的 assistant item 认领回去,并让后续 item 直接带上它。 */
|
|
29138
29587
|
bindMessage(messageId) {
|
|
29139
29588
|
this.messageId = messageId;
|
|
29589
|
+
for (const row of this.rows.values()) {
|
|
29590
|
+
if (row.role === "assistant" && row.messageId === null) row.messageId = messageId;
|
|
29591
|
+
}
|
|
29140
29592
|
if (!this.persistEnabled) return;
|
|
29141
29593
|
this.enqueue(async () => {
|
|
29142
29594
|
await this.deps.items.bindMessage(this.deps.sessionId, this.deps.turnId, messageId);
|
|
@@ -29196,7 +29648,8 @@ var init_chat_item_ledger = __esm({
|
|
|
29196
29648
|
itemId: id,
|
|
29197
29649
|
version: row.wireVersion,
|
|
29198
29650
|
startedVersion: row.startedVersion,
|
|
29199
|
-
ord: row.ord
|
|
29651
|
+
ord: row.ord,
|
|
29652
|
+
messageId: row.messageId
|
|
29200
29653
|
};
|
|
29201
29654
|
if (event.opShape.op === "set_status") {
|
|
29202
29655
|
const followUp = this.applyOpTo(row, event);
|
|
@@ -29232,7 +29685,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29232
29685
|
}
|
|
29233
29686
|
/** 这一次写的回执。`persist` 已经在里面走过钟了,这里只是把读数带出去给 wire。 */
|
|
29234
29687
|
outcomeOf(row) {
|
|
29235
|
-
return { itemId: row.id, version: row.wireVersion, ord: row.ord };
|
|
29688
|
+
return { itemId: row.id, version: row.wireVersion, ord: row.ord, messageId: row.messageId };
|
|
29236
29689
|
}
|
|
29237
29690
|
/** 关掉所有还 streaming 的行(收尾/切段)。 */
|
|
29238
29691
|
closeStreamingRows(status) {
|
|
@@ -29284,13 +29737,16 @@ var init_chat_item_ledger = __esm({
|
|
|
29284
29737
|
origin: input.origin,
|
|
29285
29738
|
inContent: input.inContent,
|
|
29286
29739
|
persist: input.persist,
|
|
29287
|
-
segment: this.segment
|
|
29740
|
+
segment: this.segment,
|
|
29741
|
+
/* 归属在**建行那一刻**定死:链是异步的,等它执行时 this.messageId 可能已被切段改了。
|
|
29742
|
+
记在行上而不是只记在入库那一步——wire 帧也要带它(2026-09-11),两边必须同一个值。 */
|
|
29743
|
+
messageId: input.forceNullMessage ? null : this.messageId
|
|
29288
29744
|
};
|
|
29289
29745
|
this.rows.set(id, row);
|
|
29290
29746
|
this.createdCount += 1;
|
|
29291
29747
|
if (!input.forceNullMessage) this.segmentItemCount += 1;
|
|
29292
29748
|
if (!input.persist || !this.persistEnabled) return id;
|
|
29293
|
-
const messageId =
|
|
29749
|
+
const messageId = row.messageId;
|
|
29294
29750
|
const createdAt = this.now();
|
|
29295
29751
|
const payload = input.payload ?? this.payloadOf(row);
|
|
29296
29752
|
const metadata = input.metadata ?? { [CHAT_ITEM_META_LEGACY_CONTENT]: input.inContent };
|
|
@@ -29473,6 +29929,7 @@ async function openAssistantRunningRow(deps) {
|
|
|
29473
29929
|
async function finalizeAssistantRow(deps) {
|
|
29474
29930
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
29475
29931
|
const finishedAt = now();
|
|
29932
|
+
const parts = await appendOutputsPart(deps.parts, deps.outputsPart);
|
|
29476
29933
|
if (deps.assistantMsgId) {
|
|
29477
29934
|
if (typeof deps.chatStore.updateMessage !== "function") return;
|
|
29478
29935
|
try {
|
|
@@ -29480,7 +29937,7 @@ async function finalizeAssistantRow(deps) {
|
|
|
29480
29937
|
content: deps.content,
|
|
29481
29938
|
status: deps.status,
|
|
29482
29939
|
completedAt: finishedAt,
|
|
29483
|
-
...
|
|
29940
|
+
...parts.length ? { parts } : {},
|
|
29484
29941
|
...deps.runId ? { runId: deps.runId } : {},
|
|
29485
29942
|
...deps.turnId ? { turnId: deps.turnId } : {}
|
|
29486
29943
|
});
|
|
@@ -29488,7 +29945,7 @@ async function finalizeAssistantRow(deps) {
|
|
|
29488
29945
|
}
|
|
29489
29946
|
return;
|
|
29490
29947
|
}
|
|
29491
|
-
if (!deps.content && !deps.runId &&
|
|
29948
|
+
if (!deps.content && !deps.runId && parts.length === 0) return;
|
|
29492
29949
|
if (typeof deps.chatStore.appendMessage !== "function") return;
|
|
29493
29950
|
try {
|
|
29494
29951
|
await deps.chatStore.appendMessage({
|
|
@@ -29501,11 +29958,18 @@ async function finalizeAssistantRow(deps) {
|
|
|
29501
29958
|
status: deps.status,
|
|
29502
29959
|
...deps.runId ? { runId: deps.runId } : {},
|
|
29503
29960
|
...deps.turnId ? { turnId: deps.turnId } : {},
|
|
29504
|
-
...
|
|
29961
|
+
...parts.length ? { parts } : {}
|
|
29505
29962
|
});
|
|
29506
29963
|
} catch {
|
|
29507
29964
|
}
|
|
29508
29965
|
}
|
|
29966
|
+
async function appendOutputsPart(parts, provider) {
|
|
29967
|
+
if (!provider) return parts;
|
|
29968
|
+
const part = await provider().catch(() => null);
|
|
29969
|
+
if (!part || !part.outputs?.length) return parts;
|
|
29970
|
+
const maxSeq = parts.reduce((m2, p2) => typeof p2.seq === "number" && p2.seq > m2 ? p2.seq : m2, 0);
|
|
29971
|
+
return [...parts, { ...part, seq: maxSeq + 1 }];
|
|
29972
|
+
}
|
|
29509
29973
|
function runBackgroundAssistantChatTurn(deps) {
|
|
29510
29974
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
29511
29975
|
const log3 = deps.log ?? ((m2) => console.warn(m2));
|
|
@@ -29674,6 +30138,7 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
29674
30138
|
parts,
|
|
29675
30139
|
status,
|
|
29676
30140
|
...runId ? { runId } : {},
|
|
30141
|
+
...deps.outputsPart ? { outputsPart: deps.outputsPart } : {},
|
|
29677
30142
|
now
|
|
29678
30143
|
});
|
|
29679
30144
|
if (typeof deps.chatStore.updateSession === "function") {
|
|
@@ -51546,7 +52011,7 @@ var require_websocket = __commonJS({
|
|
|
51546
52011
|
var http2 = require("http");
|
|
51547
52012
|
var net = require("net");
|
|
51548
52013
|
var tls = require("tls");
|
|
51549
|
-
var { randomBytes: randomBytes13, createHash:
|
|
52014
|
+
var { randomBytes: randomBytes13, createHash: createHash39 } = require("crypto");
|
|
51550
52015
|
var { Duplex, Readable } = require("stream");
|
|
51551
52016
|
var { URL: URL2 } = require("url");
|
|
51552
52017
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -52214,7 +52679,7 @@ var require_websocket = __commonJS({
|
|
|
52214
52679
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
52215
52680
|
return;
|
|
52216
52681
|
}
|
|
52217
|
-
const digest =
|
|
52682
|
+
const digest = createHash39("sha1").update(key + GUID).digest("base64");
|
|
52218
52683
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
52219
52684
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
52220
52685
|
return;
|
|
@@ -52583,7 +53048,7 @@ var require_websocket_server = __commonJS({
|
|
|
52583
53048
|
var EventEmitter = require("events");
|
|
52584
53049
|
var http2 = require("http");
|
|
52585
53050
|
var { Duplex } = require("stream");
|
|
52586
|
-
var { createHash:
|
|
53051
|
+
var { createHash: createHash39 } = require("crypto");
|
|
52587
53052
|
var extension3 = require_extension();
|
|
52588
53053
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
52589
53054
|
var subprotocol2 = require_subprotocol();
|
|
@@ -52890,7 +53355,7 @@ var require_websocket_server = __commonJS({
|
|
|
52890
53355
|
);
|
|
52891
53356
|
}
|
|
52892
53357
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
52893
|
-
const digest =
|
|
53358
|
+
const digest = createHash39("sha1").update(key + GUID).digest("base64");
|
|
52894
53359
|
const headers = [
|
|
52895
53360
|
"HTTP/1.1 101 Switching Protocols",
|
|
52896
53361
|
"Upgrade: websocket",
|
|
@@ -142723,6 +143188,7 @@ function shortId2(id) {
|
|
|
142723
143188
|
function stallOf(model, id, runtime, dispatcherSignals) {
|
|
142724
143189
|
const a = model.artifacts.get(id);
|
|
142725
143190
|
if (!a) return null;
|
|
143191
|
+
if (a.continuationPause) return a.continuationPause.decision === "pending" ? { kind: "waiting_human", action: "discuss", ref: id, detail: a.continuationPause.detail, since: a.continuationPause.at } : { kind: "held", detail: "\u5DF2\u62D2\u7EDD\u8FFD\u52A0\u81EA\u52A8\u7EED\u8DD1\u989D\u5EA6\uFF0C\u8282\u70B9\u4FDD\u6301\u6682\u505C\uFF1B\u6210\u679C\u5DF2\u4FDD\u7559\u3002", since: a.continuationPause.at };
|
|
142726
143192
|
const ownerIsAgent = a.owner.startsWith("actor:agent:");
|
|
142727
143193
|
const escs = unresolvedEscalationsOf(model, id);
|
|
142728
143194
|
if (escs.length > 0) {
|
|
@@ -142878,7 +143344,7 @@ function nodeInfo(model, a) {
|
|
|
142878
143344
|
const hasUnproducedParts = (a.parts ?? []).some((p2) => !producedParts.has(p2.name));
|
|
142879
143345
|
const blockedByIssue = unresolvedGapsOf(model, a.id).length > 0 || unresolvedEscalationsOf(model, a.id).length > 0;
|
|
142880
143346
|
let mark;
|
|
142881
|
-
if (!sealed && blockedByIssue) {
|
|
143347
|
+
if (!sealed && (blockedByIssue || a.continuationPause)) {
|
|
142882
143348
|
mark = "blocked";
|
|
142883
143349
|
} else if (a.latestWorkState !== void 0) {
|
|
142884
143350
|
const st = a.latestWorkState;
|
|
@@ -142935,6 +143401,7 @@ function deriveStage(members, opts) {
|
|
|
142935
143401
|
}
|
|
142936
143402
|
function nodeRunState(model, artifact, info, runtime, opts) {
|
|
142937
143403
|
if (info.sealed) return void 0;
|
|
143404
|
+
if (artifact.continuationPause) return artifact.continuationPause.decision === "pending" ? "pending" : "blocked";
|
|
142938
143405
|
const id = artifact.id;
|
|
142939
143406
|
const now = opts?.now ?? Date.now();
|
|
142940
143407
|
const rt = runtime ?? {};
|
|
@@ -160267,6 +160734,7 @@ var init_live_chat = __esm({
|
|
|
160267
160734
|
operation: "started",
|
|
160268
160735
|
itemVersion: outcome.startedVersion,
|
|
160269
160736
|
...ord !== void 0 ? { ord } : {},
|
|
160737
|
+
...outcome.messageId !== null ? { messageId: outcome.messageId } : {},
|
|
160270
160738
|
offset: 0,
|
|
160271
160739
|
payload: v3StartedPayload(event.role, event.attrs, itemType, event.kind)
|
|
160272
160740
|
};
|
|
@@ -160300,6 +160768,11 @@ var init_live_chat = __esm({
|
|
|
160300
160768
|
// 账本这一次写用掉的那一格钟——**库里那一行的 version 就是它**,快照与增量流同一套版本空间。
|
|
160301
160769
|
itemVersion: outcome.version,
|
|
160302
160770
|
...ord !== void 0 ? { ord } : {},
|
|
160771
|
+
/* 归属:账本写进 `chat_items.message_id` 的那个值,原样带出去(2026-09-11)。
|
|
160772
|
+
此前只有快照带它,于是「面板挂载之后才生出来的 item」在客户端永远没有归属——
|
|
160773
|
+
右侧子会话面板靠它把 DB 详情与实时桶合成一条,对不上号就把同一轮画两遍。
|
|
160774
|
+
`null` 不上 wire(省字节):客户端合并层本就按「帧不带 ⇒ 保留已有」处理。 */
|
|
160775
|
+
...outcome.messageId !== null ? { messageId: outcome.messageId } : {},
|
|
160303
160776
|
...offset !== void 0 ? { offset } : {},
|
|
160304
160777
|
...payload !== void 0 ? { payload } : {}
|
|
160305
160778
|
};
|
|
@@ -161065,15 +161538,29 @@ var init_chat_attachment_files = __esm({
|
|
|
161065
161538
|
async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, attachments, retainLocallyOnFailure = false) {
|
|
161066
161539
|
const newId = deps.newId ?? import_node_crypto20.randomUUID;
|
|
161067
161540
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
161068
|
-
|
|
161069
|
-
|
|
161541
|
+
if (!deps.store.claimPendingInsert) {
|
|
161542
|
+
return { accepted: false, reason: "queue-unavailable", queued: false, delivered: "", batch: [] };
|
|
161543
|
+
}
|
|
161544
|
+
const { submitted, batch } = await deps.store.claimPendingInsert({
|
|
161545
|
+
id: clientKey ? `append-request:${(0, import_node_crypto20.createHash)("sha256").update(JSON.stringify([chatSessionId, clientKey])).digest("hex")}` : newId(),
|
|
161070
161546
|
chatSessionId,
|
|
161071
161547
|
text: text5,
|
|
161072
161548
|
...attachments?.length ? { attachments } : {},
|
|
161073
161549
|
...clientKey ? { clientKey } : {},
|
|
161074
161550
|
createdAt: now()
|
|
161075
161551
|
});
|
|
161076
|
-
|
|
161552
|
+
if (!batch.length) {
|
|
161553
|
+
const state = submitted.state ?? "queued";
|
|
161554
|
+
const returned = state === "cancelled" && submitted.stateReason?.startsWith("client-retained:");
|
|
161555
|
+
const reason = returned ? submitted.stateReason.slice("client-retained:".length) : "unconfirmed";
|
|
161556
|
+
return {
|
|
161557
|
+
accepted: state === "delivered",
|
|
161558
|
+
...state !== "delivered" ? { reason } : {},
|
|
161559
|
+
queued: !returned,
|
|
161560
|
+
delivered: "",
|
|
161561
|
+
batch: [submitted]
|
|
161562
|
+
};
|
|
161563
|
+
}
|
|
161077
161564
|
const prepared = await buildChatAttachmentFiles(
|
|
161078
161565
|
batch.flatMap((m2) => m2.attachments ?? []),
|
|
161079
161566
|
deps.readBlob ?? (async (ref2) => {
|
|
@@ -161089,7 +161576,6 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
161089
161576
|
})));
|
|
161090
161577
|
const batchKey = `append:${chatSessionId}:${batch.map((m2) => m2.id).join(",")}`;
|
|
161091
161578
|
const ids2 = batch.map((m2) => m2.id);
|
|
161092
|
-
await deps.store.markPendingMessages?.(chatSessionId, ids2, "delivering").catch(() => void 0);
|
|
161093
161579
|
const clientKeys = batch.map((m2) => m2.clientKey).filter((k2) => !!k2);
|
|
161094
161580
|
const res = await deps.deliver(merged, batchKey, {
|
|
161095
161581
|
...clientKeys.length ? { clientKeys } : {},
|
|
@@ -161102,7 +161588,7 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
161102
161588
|
const nextState = res.accepted ? "delivered" : DEFINITELY_NOT_DELIVERED.has(res.reason ?? "") ? "queued" : "unconfirmed";
|
|
161103
161589
|
const returnToClient = retainLocallyOnFailure && !!clientKey && nextState === "queued";
|
|
161104
161590
|
if (returnToClient && deps.store.markPendingMessages) {
|
|
161105
|
-
await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled",
|
|
161591
|
+
await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled", `client-retained:${res.reason}`);
|
|
161106
161592
|
await deps.store.markPendingMessages(chatSessionId, ids2.filter((id) => id !== submitted.id), nextState, res.reason);
|
|
161107
161593
|
} else {
|
|
161108
161594
|
const transition = deps.store.markPendingMessages?.(chatSessionId, ids2, nextState, res.reason);
|
|
@@ -161323,6 +161809,17 @@ var init_workorder_drafts = __esm({
|
|
|
161323
161809
|
async forWorkspace(workspace) {
|
|
161324
161810
|
return [...this.drafts.values()].reverse().find((d) => d.workspace === workspace);
|
|
161325
161811
|
}
|
|
161812
|
+
/**
|
|
161813
|
+
* 按 workspace + **版本号**取某一版(2026-09-11:对话流里一版一张卡,右侧面板也按版本定位,
|
|
161814
|
+
* 于是「人在面板里改负责人/审核人再保存」这件事必须写回**他正在看的那一版**)。
|
|
161815
|
+
*
|
|
161816
|
+
* ⚠ 别用 `forWorkspace` 顶替它:那个取的是链上**最新**一版。看着 V1 改了一笔、却写进 V3,
|
|
161817
|
+
* 等于用旧图把新图整个盖掉——V2/V3 的内容会无声消失(2026-09-11 发起人报的「节点图要隔离」)。
|
|
161818
|
+
* 找不到那一版时回 undefined,由调用方判 404,不许静默回落到最新版。
|
|
161819
|
+
*/
|
|
161820
|
+
async forWorkspaceVersion(workspace, version2) {
|
|
161821
|
+
return [...this.drafts.values()].find((d) => d.workspace === workspace && d.version === version2);
|
|
161822
|
+
}
|
|
161326
161823
|
/** 按 chat 会话取最新一份:agent 不自带 ws(后端生成)时,friday 据本会话 id 取回它刚建的草案。 */
|
|
161327
161824
|
async forSession(chatSessionId) {
|
|
161328
161825
|
return [...this.drafts.values()].reverse().find((d) => d.chatSessionId === chatSessionId);
|
|
@@ -162369,8 +162866,8 @@ async function buildDynamicWorkorderPlan(input) {
|
|
|
162369
162866
|
const explicit = node2.owner ? await validateExplicitOwner(input.registry, node2.owner, node2.type) : null;
|
|
162370
162867
|
if (explicit?.issue) issues.push(explicit.issue);
|
|
162371
162868
|
const owner = explicit?.actor ? { role: def.ownerRole ?? "", actor: explicit.actor } : def.ownerRole ? await resolveRole(def.ownerRole, node2.type) : null;
|
|
162372
|
-
const
|
|
162373
|
-
if (enforceStaffing && def.ownerRole && !
|
|
162869
|
+
const staffable = staffableTypes.has(node2.type);
|
|
162870
|
+
if (enforceStaffing && def.ownerRole && !staffable && !input.retainUnstaffedNodes) continue;
|
|
162374
162871
|
const ownerActor = owner?.actor ?? (def.ownerRole ? `pending:role:${def.ownerRole}` : PENDING_UNASSIGNED);
|
|
162375
162872
|
availableNodes.push({
|
|
162376
162873
|
type: node2.type,
|
|
@@ -162569,7 +163066,18 @@ async function validateExplicitOwner(registry2, owner, artifactType) {
|
|
|
162569
163066
|
return { actor: owner };
|
|
162570
163067
|
}
|
|
162571
163068
|
function pickPreferred(actors) {
|
|
162572
|
-
return
|
|
163069
|
+
return actors.filter((a) => a.kind === "agent").sort((a, b2) => a.id.localeCompare(b2.id))[0];
|
|
163070
|
+
}
|
|
163071
|
+
async function autoAssignableRoles(registry2) {
|
|
163072
|
+
const out = /* @__PURE__ */ new Set();
|
|
163073
|
+
if (!registry2) return out;
|
|
163074
|
+
for (const actor of await registry2.listActors({ status: "active" })) {
|
|
163075
|
+
if (actor.kind !== "agent") continue;
|
|
163076
|
+
const binding = await registry2.getBinding(actor.id);
|
|
163077
|
+
if (!binding || binding.status !== "active") continue;
|
|
163078
|
+
for (const role of actor.roles) out.add(role);
|
|
163079
|
+
}
|
|
163080
|
+
return out;
|
|
162573
163081
|
}
|
|
162574
163082
|
async function resolveActiveRole(registry2, role, artifactType, roles) {
|
|
162575
163083
|
if (!registry2) {
|
|
@@ -162605,7 +163113,10 @@ async function resolveActiveRole(registry2, role, artifactType, roles) {
|
|
|
162605
163113
|
};
|
|
162606
163114
|
}
|
|
162607
163115
|
const preferred = pickPreferred(actors);
|
|
163116
|
+
const nodeWord = artifactType ? `${artifactType} \u8282\u70B9` : "\u5BF9\u5E94\u8282\u70B9";
|
|
162608
163117
|
if (!preferred) {
|
|
163118
|
+
const humans = actors.filter((a) => a.kind === "human");
|
|
163119
|
+
const message = humans.length > 0 ? `${role} \u5C97\u4F4D\u5F53\u524D\u53EA\u6709\u771F\u4EBA\uFF08${humans.map((h) => h.name).join("\u3001")}\uFF09\u3001\u6CA1\u6709\u53EF\u81EA\u52A8\u63A5\u5355\u7684 agent\u2014\u2014${nodeWord}\u843D\u5F85\u5C97\uFF0C\u8BF7\u5728\u56FE\u4E0A\u6307\u5B9A\u8D1F\u8D23\u4EBA\uFF0C\u6216\u7ED9\u8BE5\u5C97\u4F4D\u914D\u4E00\u4E2A agent\u3002` : `\u7F3A\u5C11 active \u7684 ${role} \u5458\u5DE5\uFF0C${nodeWord}\u4E0D\u4F1A\u81EA\u52A8\u6D3E\u53D1\u3002`;
|
|
162609
163120
|
return {
|
|
162610
163121
|
role,
|
|
162611
163122
|
actor: null,
|
|
@@ -162614,27 +163125,25 @@ async function resolveActiveRole(registry2, role, artifactType, roles) {
|
|
|
162614
163125
|
severity: "blocking",
|
|
162615
163126
|
role,
|
|
162616
163127
|
...artifactType ? { artifactType } : {},
|
|
162617
|
-
message
|
|
163128
|
+
message
|
|
162618
163129
|
}
|
|
162619
163130
|
};
|
|
162620
163131
|
}
|
|
162621
|
-
|
|
162622
|
-
|
|
162623
|
-
|
|
162624
|
-
|
|
163132
|
+
const binding = await registry2.getBinding(preferred.id);
|
|
163133
|
+
if (!binding || binding.status !== "active") {
|
|
163134
|
+
return {
|
|
163135
|
+
role,
|
|
163136
|
+
actor: null,
|
|
163137
|
+
issue: {
|
|
163138
|
+
code: "missing_binding",
|
|
163139
|
+
severity: "blocking",
|
|
162625
163140
|
role,
|
|
162626
|
-
|
|
162627
|
-
|
|
162628
|
-
|
|
162629
|
-
|
|
162630
|
-
|
|
162631
|
-
|
|
162632
|
-
// 措辞对两条路都成立:规则路会丢节点、agent/剧本路保留为待岗——**都是「暂不自动派发」**,
|
|
162633
|
-
// 而非「暂不创建」(后者在保留待岗后就是假话)。
|
|
162634
|
-
message: `${preferred.name}\uFF08${preferred.id}\uFF09\u7F3A\u5C11 active runtime \u7ED1\u5B9A\uFF0C${artifactType ? `${artifactType} \u8282\u70B9` : "\u5BF9\u5E94\u8282\u70B9"}\u6682\u4E0D\u81EA\u52A8\u6D3E\u53D1\uFF08\u5F85\u8865\u9F50\u540E\u81EA\u52A8\u63A5\u624B\uFF09\u3002`
|
|
162635
|
-
}
|
|
162636
|
-
};
|
|
162637
|
-
}
|
|
163141
|
+
...artifactType ? { artifactType } : {},
|
|
163142
|
+
// 措辞对两条路都成立:规则路会丢节点、agent/剧本路保留为待岗——**都是「暂不自动派发」**,
|
|
163143
|
+
// 而非「暂不创建」(后者在保留待岗后就是假话)。
|
|
163144
|
+
message: `${preferred.name}\uFF08${preferred.id}\uFF09\u7F3A\u5C11 active runtime \u7ED1\u5B9A\uFF0C${nodeWord}\u6682\u4E0D\u81EA\u52A8\u6D3E\u53D1\uFF08\u5F85\u8865\u9F50\u540E\u81EA\u52A8\u63A5\u624B\uFF09\u3002`
|
|
163145
|
+
}
|
|
163146
|
+
};
|
|
162638
163147
|
}
|
|
162639
163148
|
return { role, actor: preferred.id };
|
|
162640
163149
|
}
|
|
@@ -163061,7 +163570,12 @@ function planningStatusOf(_model, arts, resolveRoleExecutor) {
|
|
|
163061
163570
|
const role = a.owner.slice(PENDING_ROLE_PREFIX.length);
|
|
163062
163571
|
if (seenRoles.has(role)) continue;
|
|
163063
163572
|
seenRoles.add(role);
|
|
163064
|
-
issues.push({
|
|
163573
|
+
issues.push({
|
|
163574
|
+
code: "missing_role",
|
|
163575
|
+
severity: "warning",
|
|
163576
|
+
role,
|
|
163577
|
+
message: `${role} \u5C97\u4F4D\u6CA1\u6709\u53EF\u81EA\u52A8\u63A5\u5355\u7684 agent\uFF0C\u76F8\u5173\u8282\u70B9\u5F85\u6D3E\u2014\u2014\u8BF7 assign \u6307\u5B9A\u8D1F\u8D23\u4EBA\uFF0C\u6216\u7ED9\u8BE5\u5C97\u4F4D\u914D\u4E00\u4E2A agent\u3002`
|
|
163578
|
+
});
|
|
163065
163579
|
}
|
|
163066
163580
|
if (resolveRoleExecutor) {
|
|
163067
163581
|
for (const a of arts) {
|
|
@@ -163109,7 +163623,12 @@ function draftPlanningStatusOf(spawns, isRoleStaffed, extraBlockingIssues = [])
|
|
|
163109
163623
|
if (isRoleStaffed(role)) continue;
|
|
163110
163624
|
if (seenRoles.has(role)) continue;
|
|
163111
163625
|
seenRoles.add(role);
|
|
163112
|
-
issues.push({
|
|
163626
|
+
issues.push({
|
|
163627
|
+
code: "missing_role",
|
|
163628
|
+
severity: "warning",
|
|
163629
|
+
role,
|
|
163630
|
+
message: `${role} \u5C97\u4F4D\u6CA1\u6709\u53EF\u81EA\u52A8\u63A5\u5355\u7684 agent\uFF0C\u76F8\u5173\u8282\u70B9\u5F85\u6D3E\u2014\u2014\u8BF7\u5728\u56FE\u4E0A\u6307\u5B9A\u8D1F\u8D23\u4EBA\uFF0C\u6216\u7ED9\u8BE5\u5C97\u4F4D\u914D\u4E00\u4E2A agent\u3002`
|
|
163631
|
+
});
|
|
163113
163632
|
}
|
|
163114
163633
|
}
|
|
163115
163634
|
for (const extra of extraBlockingIssues) {
|
|
@@ -164181,7 +164700,7 @@ async function planChatContinuation(input) {
|
|
|
164181
164700
|
};
|
|
164182
164701
|
}
|
|
164183
164702
|
var UNFINISHED_OPEN, UNFINISHED_EMPTY;
|
|
164184
|
-
var
|
|
164703
|
+
var init_continuation3 = __esm({
|
|
164185
164704
|
"../server/src/domains/chat-sessions/continuation.ts"() {
|
|
164186
164705
|
"use strict";
|
|
164187
164706
|
init_injected_context();
|
|
@@ -194775,6 +195294,7 @@ var init_curator_skill = __esm({
|
|
|
194775
195294
|
skills,
|
|
194776
195295
|
connectorIds,
|
|
194777
195296
|
maxConcurrent: 4,
|
|
195297
|
+
...currentConfig?.continuationMaxRounds !== void 0 ? { continuationMaxRounds: currentConfig.continuationMaxRounds } : {},
|
|
194778
195298
|
...currentConfig?.batchedContinuation !== void 0 ? { batchedContinuation: currentConfig.batchedContinuation } : {}
|
|
194779
195299
|
}, by);
|
|
194780
195300
|
}
|
|
@@ -195758,7 +196278,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195758
196278
|
out.listDirCode = located.code;
|
|
195759
196279
|
if (located.location) out.location = located.location;
|
|
195760
196280
|
out.skipped.push({
|
|
195761
|
-
source: `${
|
|
196281
|
+
source: `${SESSION_OUTPUTS_DIR}/`,
|
|
195762
196282
|
code: located.code,
|
|
195763
196283
|
message: located.message ?? locationFailureMessage(located.code)
|
|
195764
196284
|
});
|
|
@@ -195766,7 +196286,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195766
196286
|
}
|
|
195767
196287
|
out.location = located.location;
|
|
195768
196288
|
}
|
|
195769
|
-
const listing = await port.listDir(child,
|
|
196289
|
+
const listing = await port.listDir(child, SESSION_OUTPUTS_DIR).catch((err) => ({
|
|
195770
196290
|
ok: false,
|
|
195771
196291
|
code: "INTERNAL",
|
|
195772
196292
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -195776,11 +196296,11 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195776
196296
|
if (listing.code === "NOT_FOUND") return out;
|
|
195777
196297
|
const code2 = listing.code;
|
|
195778
196298
|
const message = isLocationFailureCode(code2) ? listing.message ?? locationFailureMessage(code2) : listing.message ?? code2;
|
|
195779
|
-
out.skipped.push({ source: `${
|
|
196299
|
+
out.skipped.push({ source: `${SESSION_OUTPUTS_DIR}/`, code: code2, message });
|
|
195780
196300
|
return out;
|
|
195781
196301
|
}
|
|
195782
196302
|
out.listDirCode = "OK";
|
|
195783
|
-
const queue = [
|
|
196303
|
+
const queue = [SESSION_OUTPUTS_DIR];
|
|
195784
196304
|
const files = [];
|
|
195785
196305
|
let guard = 0;
|
|
195786
196306
|
let hitFileCap = false;
|
|
@@ -195791,7 +196311,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195791
196311
|
}
|
|
195792
196312
|
guard += 1;
|
|
195793
196313
|
const dir = queue.shift();
|
|
195794
|
-
const level = dir ===
|
|
196314
|
+
const level = dir === SESSION_OUTPUTS_DIR ? listing : await port.listDir(child, dir).catch(() => ({ ok: false, code: "INTERNAL" }));
|
|
195795
196315
|
if (!level.ok) {
|
|
195796
196316
|
out.skipped.push({ source: `${dir}/`, code: level.code, message: level.code });
|
|
195797
196317
|
continue;
|
|
@@ -195817,7 +196337,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195817
196337
|
});
|
|
195818
196338
|
}
|
|
195819
196339
|
for (const file of files) {
|
|
195820
|
-
const relative5 = normalizeRelPath(file.rel.slice(
|
|
196340
|
+
const relative5 = normalizeRelPath(file.rel.slice(SESSION_OUTPUTS_DIR.length + 1));
|
|
195821
196341
|
if (!relative5) {
|
|
195822
196342
|
out.skipped.push({ source: file.rel, code: "PATH_REJECTED", message: "\u8DEF\u5F84\u4E0D\u5408\u6CD5\uFF0C\u6CA1\u642C" });
|
|
195823
196343
|
continue;
|
|
@@ -202567,33 +203087,13 @@ async function draftActorResolver(registry2) {
|
|
|
202567
203087
|
}
|
|
202568
203088
|
async function draftRoleStaffingResolver(registry2) {
|
|
202569
203089
|
if (!registry2) return () => true;
|
|
202570
|
-
const
|
|
202571
|
-
|
|
202572
|
-
for (const a of actors) if (a.status === "active") for (const r of a.roles) staffedRoles.add(r);
|
|
202573
|
-
return (role) => staffedRoles.has(role);
|
|
203090
|
+
const assignable = await autoAssignableRoles(registry2);
|
|
203091
|
+
return (role) => assignable.has(role);
|
|
202574
203092
|
}
|
|
202575
203093
|
async function draftRolePlannableResolver(registry2) {
|
|
202576
203094
|
if (!registry2) return async () => true;
|
|
202577
|
-
const
|
|
202578
|
-
|
|
202579
|
-
for (const a of actors) {
|
|
202580
|
-
if (a.status !== "active") continue;
|
|
202581
|
-
for (const r of a.roles) {
|
|
202582
|
-
const list2 = activeActorsByRole.get(r) ?? [];
|
|
202583
|
-
list2.push(a);
|
|
202584
|
-
activeActorsByRole.set(r, list2);
|
|
202585
|
-
}
|
|
202586
|
-
}
|
|
202587
|
-
return async (role) => {
|
|
202588
|
-
const list2 = activeActorsByRole.get(role);
|
|
202589
|
-
if (!list2 || list2.length === 0) return false;
|
|
202590
|
-
for (const a of list2) {
|
|
202591
|
-
if (a.kind !== "agent") return true;
|
|
202592
|
-
const binding = await registry2.getBinding(a.id);
|
|
202593
|
-
if (binding && binding.status === "active") return true;
|
|
202594
|
-
}
|
|
202595
|
-
return false;
|
|
202596
|
-
};
|
|
203095
|
+
const assignable = await autoAssignableRoles(registry2);
|
|
203096
|
+
return async (role) => assignable.has(role);
|
|
202597
203097
|
}
|
|
202598
203098
|
async function draftTypeReviewerResolver(schema, registry2) {
|
|
202599
203099
|
if (!schema || !registry2) return void 0;
|
|
@@ -202905,6 +203405,8 @@ async function resolveProposeContent(kernel, blobs, args, artifactId, ctx) {
|
|
|
202905
203405
|
}
|
|
202906
203406
|
function authorizedCommandReceipt(command, args, actor) {
|
|
202907
203407
|
switch (command) {
|
|
203408
|
+
case "continueBatch":
|
|
203409
|
+
return { message: `\u5DF2\u6388\u6743\u518D\u8FD0\u884C ${args.rounds} \u8F6E\uFF1B\u5176\u4ED6\u963B\u585E\u89E3\u9664\u540E\u7EE7\u7EED\u3002` };
|
|
202908
203410
|
case "forceConclude":
|
|
202909
203411
|
return { message: `force_conclude ${str(args, "artifactId")}\u2014\u2014\u91CC\u7A0B\u7891\u5E26 forced(by=${actor}) \u6807\u8BB0\uFF0C\u6D88\u8D39\u8005\u53EF\u89C1` };
|
|
202910
203412
|
case "promote":
|
|
@@ -204058,7 +204560,7 @@ async function startOasisServer(opts) {
|
|
|
204058
204560
|
const leftWaiterIds = /* @__PURE__ */ new Map();
|
|
204059
204561
|
const LEFT_WAITER_IDS_MAX_DRAFTS = 512;
|
|
204060
204562
|
const terminalReviews = /* @__PURE__ */ new Map();
|
|
204061
|
-
const notifyReviewResolved = (draftId, r, resume, storeFor) => {
|
|
204563
|
+
const notifyReviewResolved = (draftId, r, resume, storeFor, companyId) => {
|
|
204062
204564
|
if (terminalReviews.has(draftId)) return;
|
|
204063
204565
|
terminalReviews.set(draftId, r);
|
|
204064
204566
|
if (terminalReviews.size > 512) {
|
|
@@ -204078,7 +204580,7 @@ async function startOasisServer(opts) {
|
|
|
204078
204580
|
leftWaiterIds.delete(draftId);
|
|
204079
204581
|
if (resume?.origin) {
|
|
204080
204582
|
const needsAgentDelivery = resume.kind === "command" && !hadWaiters;
|
|
204081
|
-
void settleAuthorizationStatusLine(resume.origin, r, resume, draftId, needsAgentDelivery, storeFor);
|
|
204583
|
+
void settleAuthorizationStatusLine(resume.origin, r, resume, draftId, needsAgentDelivery, storeFor, companyId);
|
|
204082
204584
|
}
|
|
204083
204585
|
};
|
|
204084
204586
|
const resolveEngine = opts.resolveEngine ?? (async () => ({ kernel: opts.kernel, oplog: opts.oplog, blobs: opts.blobs, ...opts.registry ? { registry: opts.registry } : {} }));
|
|
@@ -204156,6 +204658,14 @@ async function startOasisServer(opts) {
|
|
|
204156
204658
|
}
|
|
204157
204659
|
return null;
|
|
204158
204660
|
};
|
|
204661
|
+
const outputsPartFor = (chatSessionId, companyId) => {
|
|
204662
|
+
const scan2 = opts.sessionOutputsScan;
|
|
204663
|
+
if (!scan2) return void 0;
|
|
204664
|
+
return async () => {
|
|
204665
|
+
const files = await scan2({ chatSessionId, ...companyId !== void 0 ? { companyId } : {} });
|
|
204666
|
+
return files && files.length > 0 ? { type: "outputs", seq: 0, outputs: files } : null;
|
|
204667
|
+
};
|
|
204668
|
+
};
|
|
204159
204669
|
async function anchorMessageIdFor(origin, storeFor) {
|
|
204160
204670
|
if (!origin) return void 0;
|
|
204161
204671
|
const live = liveChat.messageFor(origin);
|
|
@@ -204220,7 +204730,7 @@ async function startOasisServer(opts) {
|
|
|
204220
204730
|
const label = ctx.effectLabel || ctx.command || "";
|
|
204221
204731
|
return approved ? `\u547D\u4EE4\u5DF2\u7531 ${by} \u6279\u51C6\u5E76\u4EE5\u5176\u540D\u4E49\u6267\u884C${label ? `\uFF1A${label}` : ""}\u3002` : `\u547D\u4EE4\u5DF2\u88AB ${by} \u9A73\u56DE${reason ? `\uFF1A${reason}` : ""}\u3002`;
|
|
204222
204732
|
};
|
|
204223
|
-
const settleAuthorizationStatusLine = async (origin, resolution, ctx, draftId, needsAgentDelivery, storeFor) => {
|
|
204733
|
+
const settleAuthorizationStatusLine = async (origin, resolution, ctx, draftId, needsAgentDelivery, storeFor, companyId) => {
|
|
204224
204734
|
const store = storeFor ? await storeFor().catch(() => null) : opts.chatSession;
|
|
204225
204735
|
if (!store) return;
|
|
204226
204736
|
const session = await store.getSession(origin).catch(() => null);
|
|
@@ -204293,10 +204803,12 @@ async function startOasisServer(opts) {
|
|
|
204293
204803
|
...dispatched.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: dispatched.supportsLiveProtocol } : {},
|
|
204294
204804
|
done: dispatched.done
|
|
204295
204805
|
};
|
|
204806
|
+
const resumeOutputsPart = outputsPartFor(origin, companyId);
|
|
204296
204807
|
runBackgroundAssistantChatTurn({
|
|
204297
204808
|
chatStore: store,
|
|
204298
204809
|
chatSessionId: origin,
|
|
204299
|
-
session: bgSession
|
|
204810
|
+
session: bgSession,
|
|
204811
|
+
...resumeOutputsPart ? { outputsPart: resumeOutputsPart } : {}
|
|
204300
204812
|
});
|
|
204301
204813
|
await markDelivered();
|
|
204302
204814
|
};
|
|
@@ -204552,10 +205064,12 @@ async function startOasisServer(opts) {
|
|
|
204552
205064
|
...session.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: session.supportsLiveProtocol } : {},
|
|
204553
205065
|
...session.done ? { done: session.done } : {}
|
|
204554
205066
|
};
|
|
205067
|
+
const broadcastOutputsPart = outputsPartFor(sessionId, companyId);
|
|
204555
205068
|
runBackgroundAssistantChatTurn({
|
|
204556
205069
|
chatStore: store,
|
|
204557
205070
|
chatSessionId: sessionId,
|
|
204558
205071
|
session: bgSession,
|
|
205072
|
+
...broadcastOutputsPart ? { outputsPart: broadcastOutputsPart } : {},
|
|
204559
205073
|
liveChat,
|
|
204560
205074
|
liveHandle: {
|
|
204561
205075
|
runtimeSessionId: session.id,
|
|
@@ -205383,11 +205897,7 @@ async function startOasisServer(opts) {
|
|
|
205383
205897
|
return;
|
|
205384
205898
|
}
|
|
205385
205899
|
}
|
|
205386
|
-
const staffedRoles =
|
|
205387
|
-
if (principalRegistry) {
|
|
205388
|
-
const actorsForStaffing = await principalRegistry.listActors();
|
|
205389
|
-
for (const a of actorsForStaffing) if (a.status === "active") for (const r of a.roles) staffedRoles.add(r);
|
|
205390
|
-
}
|
|
205900
|
+
const staffedRoles = await autoAssignableRoles(principalRegistry);
|
|
205391
205901
|
const spawnedForPlanning = body2.plan.ops.filter((op) => op.action === "spawn").filter((op) => op.type !== DELIVERY_SUMMARY_ARTIFACT_TYPE).map((op) => {
|
|
205392
205902
|
const title = op.title ?? op.description;
|
|
205393
205903
|
return {
|
|
@@ -205533,7 +206043,8 @@ async function startOasisServer(opts) {
|
|
|
205533
206043
|
draftId,
|
|
205534
206044
|
{ status: "approved", command, effectLabel: review.command.effectLabel, authorizedBy: actor, receipt },
|
|
205535
206045
|
{ origin: review.origin, kind: "command", authorizedBy: actor, effectLabel: review.command.effectLabel, command },
|
|
205536
|
-
chatStoreNowOrNull
|
|
206046
|
+
chatStoreNowOrNull,
|
|
206047
|
+
currentCompanyId
|
|
205537
206048
|
);
|
|
205538
206049
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
205539
206050
|
JSON.stringify({ applied: 1, command, artifactId, draftId, draftedBy: review.proposedBy, authorizedBy: actor })
|
|
@@ -205795,7 +206306,7 @@ async function startOasisServer(opts) {
|
|
|
205795
206306
|
}
|
|
205796
206307
|
if (url.pathname === "/api/workorder/draft/plan" && req.method === "POST" && wodrafts) {
|
|
205797
206308
|
try {
|
|
205798
|
-
const { workspace, plan, holder } = JSON.parse(rawBody);
|
|
206309
|
+
const { workspace, plan, holder, version: version2 } = JSON.parse(rawBody);
|
|
205799
206310
|
if (!workspace || !plan || !Array.isArray(plan.ops)) throw new Error("\u9700\u8981 { workspace, plan:{ops:[]} }");
|
|
205800
206311
|
const planLockedBy = wodrafts.editLockHolder(workspace);
|
|
205801
206312
|
if (planLockedBy && planLockedBy !== holder) {
|
|
@@ -205804,9 +206315,10 @@ async function startOasisServer(opts) {
|
|
|
205804
206315
|
);
|
|
205805
206316
|
return;
|
|
205806
206317
|
}
|
|
205807
|
-
const draft = await wodrafts.forWorkspace(workspace);
|
|
206318
|
+
const draft = version2 === void 0 ? await wodrafts.forWorkspace(workspace) : await wodrafts.forWorkspaceVersion(workspace, version2);
|
|
205808
206319
|
if (!draft) {
|
|
205809
|
-
|
|
206320
|
+
const what = version2 === void 0 ? workspace : `${workspace} v${version2}`;
|
|
206321
|
+
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: `no staged workorder draft for ${what}` }));
|
|
205810
206322
|
return;
|
|
205811
206323
|
}
|
|
205812
206324
|
const pv = await engine.kernel.previewIntervention({ ...plan, reason: plan.reason || "(draft edit)" });
|
|
@@ -206076,7 +206588,7 @@ async function startOasisServer(opts) {
|
|
|
206076
206588
|
if (viewName === "intervention-drafts") {
|
|
206077
206589
|
const origin = url.searchParams.get("origin");
|
|
206078
206590
|
const workspace = url.searchParams.get("workspace") ?? void 0;
|
|
206079
|
-
const reviews = listPendingReviews(engine.kernel.model, origin === null ? workspace : void 0).filter((r) => origin === null ? true : r.origin === origin);
|
|
206591
|
+
const reviews = listPendingReviews(engine.kernel.model, origin === null ? workspace : void 0).filter((r) => origin === null ? true : r.origin === origin).filter((r) => !r.continuation || r.continuation.approverActorId === actor);
|
|
206080
206592
|
const data2 = reviews.map((r) => ({
|
|
206081
206593
|
id: r.draftId,
|
|
206082
206594
|
plan: r.plan,
|
|
@@ -206094,7 +206606,8 @@ async function startOasisServer(opts) {
|
|
|
206094
206606
|
anchor: r.anchor,
|
|
206095
206607
|
// 提案 coordinator-command-authorization:命令授权卡的种类 + 命令本体(前端据此渲成命令卡)。
|
|
206096
206608
|
...r.kind !== void 0 ? { kind: r.kind } : {},
|
|
206097
|
-
...r.command !== void 0 ? { command: r.command } : {}
|
|
206609
|
+
...r.command !== void 0 ? { command: r.command } : {},
|
|
206610
|
+
...r.continuation ? { continuation: r.continuation } : {}
|
|
206098
206611
|
}));
|
|
206099
206612
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ data: data2 }));
|
|
206100
206613
|
return;
|
|
@@ -206721,6 +207234,7 @@ ${composed}`;
|
|
|
206721
207234
|
await checkpointChain.catch(() => void 0);
|
|
206722
207235
|
await itemLedger.drain();
|
|
206723
207236
|
const projectedContent = itemLedger.projectContent();
|
|
207237
|
+
const chatOutputsPart = outputsPartFor(persistTarget.id, currentCompanyId);
|
|
206724
207238
|
await finalizeAssistantRow({
|
|
206725
207239
|
chatStore,
|
|
206726
207240
|
chatSessionId: persistTarget.id,
|
|
@@ -206730,6 +207244,7 @@ ${composed}`;
|
|
|
206730
207244
|
status,
|
|
206731
207245
|
...session.runId ? { runId: session.runId } : {},
|
|
206732
207246
|
...heldTurn ? { turnId: heldTurn.id } : {},
|
|
207247
|
+
...chatOutputsPart ? { outputsPart: chatOutputsPart } : {},
|
|
206733
207248
|
now: () => finishedAt
|
|
206734
207249
|
});
|
|
206735
207250
|
await heldTurn?.settle({
|
|
@@ -208228,7 +208743,7 @@ var init_server3 = __esm({
|
|
|
208228
208743
|
init_planner();
|
|
208229
208744
|
init_chat_parts();
|
|
208230
208745
|
init_injected_context();
|
|
208231
|
-
|
|
208746
|
+
init_continuation3();
|
|
208232
208747
|
init_chat_turn_gate();
|
|
208233
208748
|
init_chat_workorder_files();
|
|
208234
208749
|
init_chat_item_ledger();
|
|
@@ -208241,7 +208756,7 @@ var init_server3 = __esm({
|
|
|
208241
208756
|
init_assembly();
|
|
208242
208757
|
init_child_live_turn();
|
|
208243
208758
|
init_src8();
|
|
208244
|
-
|
|
208759
|
+
init_continuation3();
|
|
208245
208760
|
githubAppPending = new PendingAppCreations();
|
|
208246
208761
|
connectorAuthSnapshots = /* @__PURE__ */ new Map();
|
|
208247
208762
|
CONNECTOR_AUTH_SNAPSHOT_TTL_MS = 30 * 60 * 1e3;
|
|
@@ -213729,6 +214244,17 @@ var init_dev_store = __esm({
|
|
|
213729
214244
|
/** ADR-0166 D6 待发队列(每会话一条,按 seq 升序 = 入队序)。 */
|
|
213730
214245
|
pending = /* @__PURE__ */ new Map();
|
|
213731
214246
|
pendingSeq = 0;
|
|
214247
|
+
async claimPendingInsert(m2) {
|
|
214248
|
+
const list2 = this.pending.get(m2.chatSessionId) ?? [];
|
|
214249
|
+
const prior = list2.find((x2) => x2.id === m2.id) ?? list2.find((x2) => m2.clientKey && x2.clientKey === m2.clientKey && (x2.state ?? "queued") === "queued");
|
|
214250
|
+
if (prior) return { submitted: prior, batch: [] };
|
|
214251
|
+
const submitted = { ...m2, state: "queued", stateAt: m2.createdAt, seq: ++this.pendingSeq };
|
|
214252
|
+
const all2 = [...list2, submitted];
|
|
214253
|
+
const batch = all2.filter((x2) => (x2.state ?? "queued") === "queued");
|
|
214254
|
+
this.pending.set(m2.chatSessionId, all2);
|
|
214255
|
+
this.setState(m2.chatSessionId, new Set(batch.map((x2) => x2.id)), "delivering", "append-now");
|
|
214256
|
+
return { submitted, batch };
|
|
214257
|
+
}
|
|
213732
214258
|
async enqueuePendingMessage(m2) {
|
|
213733
214259
|
const list2 = this.pending.get(m2.chatSessionId) ?? [];
|
|
213734
214260
|
if (m2.clientKey) {
|
|
@@ -214096,6 +214622,11 @@ var init_dev_store = __esm({
|
|
|
214096
214622
|
return r;
|
|
214097
214623
|
}
|
|
214098
214624
|
// 待发队列也要落盘——它存在的全部理由就是"别只活在一处内存里"(ADR-0166 D6)。
|
|
214625
|
+
async claimPendingInsert(m2) {
|
|
214626
|
+
const result = await super.claimPendingInsert(m2);
|
|
214627
|
+
this.save();
|
|
214628
|
+
return result;
|
|
214629
|
+
}
|
|
214099
214630
|
async enqueuePendingMessage(m2) {
|
|
214100
214631
|
const record8 = await super.enqueuePendingMessage(m2);
|
|
214101
214632
|
this.save();
|
|
@@ -214943,6 +215474,74 @@ var init_company_scoped_registry = __esm({
|
|
|
214943
215474
|
}
|
|
214944
215475
|
});
|
|
214945
215476
|
|
|
215477
|
+
// ../server/src/domains/chat-sessions/outputs-scan.ts
|
|
215478
|
+
function joinRel(dir, name) {
|
|
215479
|
+
return dir ? `${dir}/${name}` : name;
|
|
215480
|
+
}
|
|
215481
|
+
async function scanSessionOutputs(bridge, loc, now = Date.now) {
|
|
215482
|
+
const deadline = now() + BUDGET_MS;
|
|
215483
|
+
const out = [];
|
|
215484
|
+
let listCalls = 0;
|
|
215485
|
+
const listDir = (path41) => {
|
|
215486
|
+
listCalls += 1;
|
|
215487
|
+
return bridge.list(loc.nodeId, {
|
|
215488
|
+
workdirKey: loc.workdirKey,
|
|
215489
|
+
runtimeKind: loc.runtimeKind,
|
|
215490
|
+
rtId: loc.rtId,
|
|
215491
|
+
path: path41
|
|
215492
|
+
});
|
|
215493
|
+
};
|
|
215494
|
+
const root = await listDir(SESSION_OUTPUTS_DIR).catch(() => null);
|
|
215495
|
+
if (!root) return null;
|
|
215496
|
+
if (!root.ok) {
|
|
215497
|
+
return root.code === "NOT_FOUND" ? [] : null;
|
|
215498
|
+
}
|
|
215499
|
+
let level = [{ path: SESSION_OUTPUTS_DIR, outcome: root }];
|
|
215500
|
+
for (let depth = 0; depth <= MAX_DEPTH; depth += 1) {
|
|
215501
|
+
const nextDirs = [];
|
|
215502
|
+
for (const { path: dir, outcome } of level) {
|
|
215503
|
+
if (!outcome.ok) continue;
|
|
215504
|
+
for (const entry of outcome.entries) {
|
|
215505
|
+
if (entry.type === "dir") {
|
|
215506
|
+
nextDirs.push(joinRel(dir, entry.name));
|
|
215507
|
+
continue;
|
|
215508
|
+
}
|
|
215509
|
+
if (out.length >= MAX_ENTRIES) continue;
|
|
215510
|
+
out.push({ path: joinRel(dir, entry.name), size: entry.size, mtime: entry.mtime });
|
|
215511
|
+
}
|
|
215512
|
+
}
|
|
215513
|
+
if (nextDirs.length === 0 || depth === MAX_DEPTH) break;
|
|
215514
|
+
if (out.length >= MAX_ENTRIES || listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
|
|
215515
|
+
const batch = [];
|
|
215516
|
+
for (const dir of nextDirs) {
|
|
215517
|
+
if (listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
|
|
215518
|
+
const outcome = await listDir(dir).catch(() => null);
|
|
215519
|
+
if (outcome) batch.push({ path: dir, outcome });
|
|
215520
|
+
}
|
|
215521
|
+
level = batch;
|
|
215522
|
+
}
|
|
215523
|
+
return out;
|
|
215524
|
+
}
|
|
215525
|
+
async function settleSessionOutputs(deps) {
|
|
215526
|
+
const scanned = await scanSessionOutputs(deps.bridge, deps.loc, deps.now);
|
|
215527
|
+
if (scanned === null) return null;
|
|
215528
|
+
const previous3 = await deps.readFingerprints(deps.sessionId).catch(() => null);
|
|
215529
|
+
const diff = diffSessionOutputs(scanned, previous3 ?? void 0);
|
|
215530
|
+
await deps.writeFingerprints(deps.sessionId, diff.fingerprints).catch(() => void 0);
|
|
215531
|
+
return diff;
|
|
215532
|
+
}
|
|
215533
|
+
var MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS;
|
|
215534
|
+
var init_outputs_scan = __esm({
|
|
215535
|
+
"../server/src/domains/chat-sessions/outputs-scan.ts"() {
|
|
215536
|
+
"use strict";
|
|
215537
|
+
init_src();
|
|
215538
|
+
MAX_DEPTH = 3;
|
|
215539
|
+
MAX_ENTRIES = 100;
|
|
215540
|
+
MAX_LIST_CALLS = 12;
|
|
215541
|
+
BUDGET_MS = 5e3;
|
|
215542
|
+
}
|
|
215543
|
+
});
|
|
215544
|
+
|
|
214946
215545
|
// ../server/src/org-model-backfill.ts
|
|
214947
215546
|
function resolveDisplayName(existing, humanActor, accountName) {
|
|
214948
215547
|
const pick3 = existing?.trim() || humanActor?.name?.trim() || accountName?.trim();
|
|
@@ -219694,6 +220293,7 @@ var init_service5 = __esm({
|
|
|
219694
220293
|
connectorIds: patch.connectorIds ?? latest?.connectorIds ?? [],
|
|
219695
220294
|
maxConcurrent: patch.maxConcurrent ?? latest?.maxConcurrent ?? 4,
|
|
219696
220295
|
batchedContinuation: patch.batchedContinuation ?? latest?.batchedContinuation ?? false,
|
|
220296
|
+
continuationMaxRounds: patch.continuationMaxRounds ?? latest?.continuationMaxRounds ?? 3,
|
|
219697
220297
|
extraArgs: patch.extraArgs ?? latest?.extraArgs ?? [],
|
|
219698
220298
|
createdBy: by,
|
|
219699
220299
|
createdAt: this.now(),
|
|
@@ -219721,6 +220321,7 @@ var init_service5 = __esm({
|
|
|
219721
220321
|
connectorIds: [...target.connectorIds],
|
|
219722
220322
|
maxConcurrent: target.maxConcurrent,
|
|
219723
220323
|
batchedContinuation: target.batchedContinuation ?? false,
|
|
220324
|
+
continuationMaxRounds: target.continuationMaxRounds ?? 3,
|
|
219724
220325
|
extraArgs: [...target.extraArgs ?? []],
|
|
219725
220326
|
createdBy: by,
|
|
219726
220327
|
createdAt: this.now(),
|
|
@@ -220609,12 +221210,13 @@ ${input.description}
|
|
|
220609
221210
|
};
|
|
220610
221211
|
mask = (plain) => plain.length <= 4 ? "\u2022\u2022\u2022\u2022" : `${"\u2022".repeat(4)}${plain.slice(-4)}`;
|
|
220611
221212
|
changedKeys = (prev, next) => {
|
|
220612
|
-
if (!prev) return ["prompt", "model", "skills", "connectorIds", "maxConcurrent", "batchedContinuation", "extraArgs"];
|
|
221213
|
+
if (!prev) return ["prompt", "model", "skills", "connectorIds", "maxConcurrent", "batchedContinuation", "continuationMaxRounds", "extraArgs"];
|
|
220613
221214
|
const keys = [];
|
|
220614
221215
|
if (prev.prompt !== next.prompt) keys.push("prompt");
|
|
220615
221216
|
if (prev.model !== next.model) keys.push("model");
|
|
220616
221217
|
if (JSON.stringify(prev.skills) !== JSON.stringify(next.skills)) keys.push("skills");
|
|
220617
221218
|
if (JSON.stringify(prev.connectorIds) !== JSON.stringify(next.connectorIds)) keys.push("connectorIds");
|
|
221219
|
+
if ((prev.continuationMaxRounds ?? 3) !== (next.continuationMaxRounds ?? 3)) keys.push("continuationMaxRounds");
|
|
220618
221220
|
if ((prev.batchedContinuation ?? false) !== (next.batchedContinuation ?? false)) keys.push("batchedContinuation");
|
|
220619
221221
|
if (prev.maxConcurrent !== next.maxConcurrent) keys.push("maxConcurrent");
|
|
220620
221222
|
if (JSON.stringify(prev.extraArgs ?? []) !== JSON.stringify(next.extraArgs ?? [])) keys.push("extraArgs");
|
|
@@ -221367,11 +221969,11 @@ function aggregateUsageByActor(runs) {
|
|
|
221367
221969
|
function weekWindow(now = /* @__PURE__ */ new Date()) {
|
|
221368
221970
|
return { from: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString(), to: now.toISOString() };
|
|
221369
221971
|
}
|
|
221370
|
-
async function listWeeklyUsageByActor(trace) {
|
|
221972
|
+
async function listWeeklyUsageByActor(trace, artifactIds) {
|
|
221371
221973
|
if (!trace?.listUsageRuns) return null;
|
|
221372
221974
|
const { from, to } = weekWindow();
|
|
221373
221975
|
try {
|
|
221374
|
-
return aggregateUsageByActor(await trace.listUsageRuns({ from, to }));
|
|
221976
|
+
return aggregateUsageByActor(await trace.listUsageRuns({ from, to, ...artifactIds ? { artifactIds: [...artifactIds] } : {} }));
|
|
221375
221977
|
} catch {
|
|
221376
221978
|
return null;
|
|
221377
221979
|
}
|
|
@@ -221973,9 +222575,15 @@ function actorsDomain(opts) {
|
|
|
221973
222575
|
const revealLimiter = new RevealRateLimiter(revealLimits.perSession, revealLimits.perSessionPerKey);
|
|
221974
222576
|
const consoleRevealLimits = opts.credentialRevealLimits?.console ?? { perSession: 60, perSessionPerKey: 10 };
|
|
221975
222577
|
const consoleRevealLimiter = new RevealRateLimiter(consoleRevealLimits.perSession, consoleRevealLimits.perSessionPerKey);
|
|
222578
|
+
async function usageFor(companyId, ctx) {
|
|
222579
|
+
const source = opts.resolveTrace ? await opts.resolveTrace(companyId) : { store: opts.trace?.(), companyScoped: false };
|
|
222580
|
+
const artifactIds = companyId && !source?.companyScoped ? new Set(ctx.getModel?.().artifacts.keys() ?? []) : void 0;
|
|
222581
|
+
return { usage: source?.store, ...artifactIds ? { artifactIds } : {} };
|
|
222582
|
+
}
|
|
221976
222583
|
return (router) => {
|
|
221977
222584
|
router.get("/api/actors", async (req) => {
|
|
221978
|
-
const
|
|
222585
|
+
const ctx = await resolveCtx(req.auth.companyId);
|
|
222586
|
+
const { service } = ctx;
|
|
221979
222587
|
const filter = {};
|
|
221980
222588
|
const kind = req.query.get("kind");
|
|
221981
222589
|
const teamId = req.query.get("team_id");
|
|
@@ -221987,9 +222595,10 @@ function actorsDomain(opts) {
|
|
|
221987
222595
|
const includeDeleted = req.query.get("include_deleted") === "1";
|
|
221988
222596
|
const items = status || includeDeleted ? all2 : all2.filter((a) => a.kind !== "agent" || !isDeletedActor(a));
|
|
221989
222597
|
const agentIds = items.filter((a) => a.kind === "agent").map((a) => a.id);
|
|
222598
|
+
const usageSource = await usageFor(req.auth.companyId, ctx).catch(() => ({ usage: void 0, artifactIds: void 0 }));
|
|
221990
222599
|
const [configs, usageByActor] = await Promise.all([
|
|
221991
222600
|
service.latestConfigs(agentIds),
|
|
221992
|
-
listWeeklyUsageByActor(
|
|
222601
|
+
listWeeklyUsageByActor(usageSource.usage, usageSource.artifactIds)
|
|
221993
222602
|
]);
|
|
221994
222603
|
return {
|
|
221995
222604
|
status: 200,
|
|
@@ -222131,6 +222740,9 @@ function actorsDomain(opts) {
|
|
|
222131
222740
|
checkStringArray("skills");
|
|
222132
222741
|
checkStringArray("connectorIds");
|
|
222133
222742
|
checkBoolean("batchedContinuation");
|
|
222743
|
+
if (b2.continuationMaxRounds !== void 0 && (!Number.isSafeInteger(b2.continuationMaxRounds) || b2.continuationMaxRounds < 1 || b2.continuationMaxRounds > 100)) {
|
|
222744
|
+
throw new ApiError(400, "BAD_REQUEST", "continuationMaxRounds \u5FC5\u987B\u662F 1\u2013100 \u7684\u6574\u6570");
|
|
222745
|
+
}
|
|
222134
222746
|
const rawExtra = b2.extraArgs;
|
|
222135
222747
|
if (rawExtra !== void 0 && typeof rawExtra !== "string" && !(Array.isArray(rawExtra) && rawExtra.every((x2) => typeof x2 === "string"))) {
|
|
222136
222748
|
throw new ApiError(400, "BAD_REQUEST", "extraArgs \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4");
|
|
@@ -222152,6 +222764,7 @@ function actorsDomain(opts) {
|
|
|
222152
222764
|
...b2.skills !== void 0 ? { skills: b2.skills } : {},
|
|
222153
222765
|
...b2.connectorIds !== void 0 ? { connectorIds: b2.connectorIds } : {},
|
|
222154
222766
|
...typeof b2.maxConcurrent === "number" ? { maxConcurrent: b2.maxConcurrent } : {},
|
|
222767
|
+
...b2.continuationMaxRounds !== void 0 ? { continuationMaxRounds: b2.continuationMaxRounds } : {},
|
|
222155
222768
|
...b2.batchedContinuation !== void 0 ? { batchedContinuation: b2.batchedContinuation } : {},
|
|
222156
222769
|
...extraArgs !== void 0 ? { extraArgs } : {}
|
|
222157
222770
|
},
|
|
@@ -222665,9 +223278,8 @@ function actorsDomain(opts) {
|
|
|
222665
223278
|
return {
|
|
222666
223279
|
status: 200,
|
|
222667
223280
|
body: await actorStats(req.params.id, {
|
|
222668
|
-
|
|
222669
|
-
model
|
|
222670
|
-
...req.auth.companyId ? { artifactIds: new Set(model?.artifacts.keys() ?? []) } : {}
|
|
223281
|
+
...await usageFor(req.auth.companyId, ctx),
|
|
223282
|
+
model
|
|
222671
223283
|
})
|
|
222672
223284
|
};
|
|
222673
223285
|
});
|
|
@@ -223251,7 +223863,7 @@ function createActorsDomain(opts) {
|
|
|
223251
223863
|
return {
|
|
223252
223864
|
service: defaultCtx.service,
|
|
223253
223865
|
resolveCtx,
|
|
223254
|
-
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
223866
|
+
register: actorsDomain({ resolveCtx, ...opts.resolveTrace ? { resolveTrace: opts.resolveTrace } : {}, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
223255
223867
|
};
|
|
223256
223868
|
}
|
|
223257
223869
|
var import_node_crypto49;
|
|
@@ -225716,6 +226328,7 @@ var init_activity_trace = __esm({
|
|
|
225716
226328
|
|
|
225717
226329
|
// ../server/src/domains/collab/inbox.ts
|
|
225718
226330
|
function changeConfirmSummary(r) {
|
|
226331
|
+
if (r.continuation) return `\u81EA\u52A8\u7EED\u8DD1\u5DF2\u5230\u9650\uFF0C\u7B49\u5F85\u4F60\u5BA1\u6838\uFF1A${r.plan.reason}`;
|
|
225719
226332
|
const why = r.plan.reason.slice(0, 100);
|
|
225720
226333
|
if (r.kind === "command" && r.command) {
|
|
225721
226334
|
return `\u7BA1\u7406\u8005\u8BF7\u6C42\u6267\u884C\u4E00\u4E2A\u9AD8\u5371\u64CD\u4F5C\uFF0C\u7B49\u4F60\u6388\u6743\uFF1A${r.command.effectLabel}`;
|
|
@@ -225876,7 +226489,7 @@ function buildInbox(model, me, leadOf, resolveActor, markers = [], slaMs, nowIso
|
|
|
225876
226489
|
const anchorOwner = model.artifacts.get(r.anchor)?.owner;
|
|
225877
226490
|
const isOwnerRecipient = creatorOf.get(r.workspace) === me;
|
|
225878
226491
|
const isLeadRecipient = !!anchorOwner && !!leadOf && leadOf(anchorOwner) === me;
|
|
225879
|
-
if (!isOwnerRecipient && !isLeadRecipient) continue;
|
|
226492
|
+
if (r.continuation ? r.continuation.approverActorId !== me : !isOwnerRecipient && !isLeadRecipient) continue;
|
|
225880
226493
|
const anchorArt = model.artifacts.get(r.anchor);
|
|
225881
226494
|
const opCount = r.plan.ops?.length ?? 0;
|
|
225882
226495
|
out.push({
|
|
@@ -228494,7 +229107,7 @@ function reasonFromWorkdirCode(code2) {
|
|
|
228494
229107
|
return "ERROR";
|
|
228495
229108
|
}
|
|
228496
229109
|
}
|
|
228497
|
-
function
|
|
229110
|
+
function joinRel2(dir, name) {
|
|
228498
229111
|
return dir ? `${dir}/${name}` : name;
|
|
228499
229112
|
}
|
|
228500
229113
|
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
@@ -228527,14 +229140,14 @@ async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
|
228527
229140
|
if (outcome.truncated) truncated = true;
|
|
228528
229141
|
for (const entry of outcome.entries) {
|
|
228529
229142
|
if (entry.type === "dir") {
|
|
228530
|
-
nextDirs.push(
|
|
229143
|
+
nextDirs.push(joinRel2(dir, entry.name));
|
|
228531
229144
|
continue;
|
|
228532
229145
|
}
|
|
228533
229146
|
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
228534
229147
|
truncated = true;
|
|
228535
229148
|
continue;
|
|
228536
229149
|
}
|
|
228537
|
-
const rel =
|
|
229150
|
+
const rel = joinRel2(dir, entry.name);
|
|
228538
229151
|
items.push({
|
|
228539
229152
|
id: `workdir:${rel}`,
|
|
228540
229153
|
name: entry.name,
|
|
@@ -231227,6 +231840,9 @@ function nodesDomain(deps) {
|
|
|
231227
231840
|
const rts = runtimesByNode.get(n.id) ?? [];
|
|
231228
231841
|
const daemonVersion = daemonVersionById.get(n.id);
|
|
231229
231842
|
const connection = connectionById.get(n.id);
|
|
231843
|
+
const nodeOffline = !onlineIds.has(n.id);
|
|
231844
|
+
const lastDisconnectedAt = connection?.lastDisconnectedAt !== void 0 ? new Date(connection.lastDisconnectedAt).toISOString() : nodeOffline ? n.lastSeenAt : void 0;
|
|
231845
|
+
const lastReconnectedAt = connection?.lastReconnectedAt !== void 0 ? new Date(connection.lastReconnectedAt).toISOString() : void 0;
|
|
231230
231846
|
const activeRunCount = deps.activeRunCountOf?.(n.id) ?? 0;
|
|
231231
231847
|
const updateStatus = reconcileNodeUpdate(n.id, daemonVersion, activeRunCount, healthNow);
|
|
231232
231848
|
return {
|
|
@@ -231240,10 +231856,10 @@ function nodesDomain(deps) {
|
|
|
231240
231856
|
...sourceIpById.get(n.id) ? { sourceIp: sourceIpById.get(n.id) } : {},
|
|
231241
231857
|
...n.name ? { name: n.name } : {},
|
|
231242
231858
|
...updateStatus ? { updateStatus } : {},
|
|
231243
|
-
...
|
|
231859
|
+
...lastDisconnectedAt || lastReconnectedAt ? {
|
|
231244
231860
|
connection: {
|
|
231245
|
-
...
|
|
231246
|
-
...
|
|
231861
|
+
...lastDisconnectedAt ? { lastDisconnectedAt } : {},
|
|
231862
|
+
...lastReconnectedAt ? { lastReconnectedAt } : {}
|
|
231247
231863
|
}
|
|
231248
231864
|
} : {},
|
|
231249
231865
|
...deps.nodeHealth ? {
|
|
@@ -241578,6 +242194,7 @@ var init_postgres_registry = __esm({
|
|
|
241578
242194
|
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS max_concurrent integer NOT NULL DEFAULT 4`);
|
|
241579
242195
|
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb`);
|
|
241580
242196
|
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS batched_continuation boolean NOT NULL DEFAULT false`);
|
|
242197
|
+
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS continuation_max_rounds integer NOT NULL DEFAULT 3 CHECK (continuation_max_rounds BETWEEN 1 AND 100)`);
|
|
241581
242198
|
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS extra_args jsonb NOT NULL DEFAULT '[]'::jsonb`);
|
|
241582
242199
|
await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS memory_enabled boolean NOT NULL DEFAULT true`);
|
|
241583
242200
|
await pool.query(`
|
|
@@ -241829,8 +242446,8 @@ var init_postgres_registry = __esm({
|
|
|
241829
242446
|
const r = await this.pool.query(
|
|
241830
242447
|
// reviewer_ids 列保留(历史数据)但不再写入——决策 0029 删了 actor 级 reviewer override;列有 DEFAULT '[]' 自动填。
|
|
241831
242448
|
`INSERT INTO ${this.s}.actor_configs
|
|
241832
|
-
(actor_id, version, prompt, model, skills, connector_ids, max_concurrent, batched_continuation, extra_args, created_by, created_at, source, rolled_back_from)
|
|
241833
|
-
SELECT $1,$2::int,$3,$4,$5::jsonb,$6::jsonb,$7::int,$8::boolean,$9::jsonb,$10,$11,$12,$13
|
|
242449
|
+
(actor_id, version, prompt, model, skills, connector_ids, max_concurrent, batched_continuation, extra_args, created_by, created_at, source, rolled_back_from, continuation_max_rounds)
|
|
242450
|
+
SELECT $1,$2::int,$3,$4,$5::jsonb,$6::jsonb,$7::int,$8::boolean,$9::jsonb,$10,$11,$12,$13,$14::int
|
|
241834
242451
|
WHERE (SELECT COALESCE(MAX(version), 0) + 1 FROM ${this.s}.actor_configs WHERE actor_id = $1) = $2::int`,
|
|
241835
242452
|
[
|
|
241836
242453
|
c.actorId,
|
|
@@ -241845,7 +242462,8 @@ var init_postgres_registry = __esm({
|
|
|
241845
242462
|
c.createdBy,
|
|
241846
242463
|
c.createdAt,
|
|
241847
242464
|
c.source,
|
|
241848
|
-
c.rolledBackFrom ?? null
|
|
242465
|
+
c.rolledBackFrom ?? null,
|
|
242466
|
+
c.continuationMaxRounds ?? 3
|
|
241849
242467
|
]
|
|
241850
242468
|
);
|
|
241851
242469
|
if (r.rowCount !== 1) throw new Error(`config version conflict for ${c.actorId}: got ${c.version}`);
|
|
@@ -242297,6 +242915,7 @@ var init_postgres_registry = __esm({
|
|
|
242297
242915
|
connectorIds: row.connector_ids,
|
|
242298
242916
|
maxConcurrent: row.max_concurrent ?? 4,
|
|
242299
242917
|
batchedContinuation: row.batched_continuation ?? false,
|
|
242918
|
+
continuationMaxRounds: row.continuation_max_rounds ?? 3,
|
|
242300
242919
|
extraArgs: row.extra_args ?? [],
|
|
242301
242920
|
// 注意:memory_enabled 是死列,不映射进 ActorConfig——记忆开关在 agent_prefs。
|
|
242302
242921
|
createdBy: row.created_by,
|
|
@@ -247647,6 +248266,7 @@ var init_postgres_chat_sessions = __esm({
|
|
|
247647
248266
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS escalation_id text`);
|
|
247648
248267
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS model text`);
|
|
247649
248268
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS visibility text`);
|
|
248269
|
+
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS outputs_fingerprints jsonb`);
|
|
247650
248270
|
await pool.query(`
|
|
247651
248271
|
WITH ranked AS (
|
|
247652
248272
|
SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
|
|
@@ -247877,6 +248497,23 @@ var init_postgres_chat_sessions = __esm({
|
|
|
247877
248497
|
args
|
|
247878
248498
|
);
|
|
247879
248499
|
}
|
|
248500
|
+
/**
|
|
248501
|
+
* `outputs/` 跨轮指纹(2026-09-11 产出卡)。整表覆盖,不做增量合并——判据的真源永远是
|
|
248502
|
+
* 最近一次扫描看到的现场;合并会让「文件被删掉」这件事永远留在表里。
|
|
248503
|
+
*/
|
|
248504
|
+
async readOutputFingerprints(sessionId) {
|
|
248505
|
+
const r = await this.pool.query(
|
|
248506
|
+
`SELECT outputs_fingerprints FROM ${this.s}.chat_sessions WHERE id = $1 AND company_id = $2`,
|
|
248507
|
+
[sessionId, this.companyId]
|
|
248508
|
+
);
|
|
248509
|
+
return r.rows[0]?.outputs_fingerprints ?? null;
|
|
248510
|
+
}
|
|
248511
|
+
async writeOutputFingerprints(sessionId, fingerprints) {
|
|
248512
|
+
await this.pool.query(
|
|
248513
|
+
`UPDATE ${this.s}.chat_sessions SET outputs_fingerprints = $1::jsonb WHERE id = $2 AND company_id = $3`,
|
|
248514
|
+
[JSON.stringify(fingerprints), sessionId, this.companyId]
|
|
248515
|
+
);
|
|
248516
|
+
}
|
|
247880
248517
|
/** 删项目时把本公司里绑到它的会话回落成「不指定项目」。会话一条都不删,只清 project_id。 */
|
|
247881
248518
|
async clearProjectBinding(projectId2) {
|
|
247882
248519
|
if (!projectId2) return 0;
|
|
@@ -248279,6 +248916,42 @@ var init_postgres_chat_sessions = __esm({
|
|
|
248279
248916
|
return r.rows.map(rowToMessage);
|
|
248280
248917
|
}
|
|
248281
248918
|
// ── ADR-0166 D6 待发队列 ─────────────────────────────────────────────────
|
|
248919
|
+
async claimPendingInsert(m2) {
|
|
248920
|
+
const client = await this.pool.connect();
|
|
248921
|
+
try {
|
|
248922
|
+
await client.query("BEGIN");
|
|
248923
|
+
const scope = await client.query(`SELECT id FROM ${this.s}.chat_sessions WHERE id=$1 AND company_id=$2 FOR UPDATE`, [m2.chatSessionId, this.companyId]);
|
|
248924
|
+
if (!scope.rows.length) throw new ChatCompanyScopeError(this.companyId, m2.chatSessionId);
|
|
248925
|
+
const ins = await client.query(
|
|
248926
|
+
`INSERT INTO ${this.s}.chat_pending_messages (id, chat_session_id, text, attachments, client_key, created_at, flavour)
|
|
248927
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7) ON CONFLICT DO NOTHING RETURNING *`,
|
|
248928
|
+
[m2.id, m2.chatSessionId, m2.text, m2.attachments ? JSON.stringify(m2.attachments) : null, m2.clientKey ?? null, m2.createdAt, m2.flavour ?? "user"]
|
|
248929
|
+
);
|
|
248930
|
+
if (!ins.rows.length) {
|
|
248931
|
+
const prior = await client.query(
|
|
248932
|
+
`SELECT * FROM ${this.s}.chat_pending_messages WHERE chat_session_id=$1
|
|
248933
|
+
AND (id=$2 OR (client_key=$3 AND state='queued')) ORDER BY (id=$2) DESC LIMIT 1`,
|
|
248934
|
+
[m2.chatSessionId, m2.id, m2.clientKey ?? null]
|
|
248935
|
+
);
|
|
248936
|
+
if (!prior.rows[0]) throw new Error("claimPendingInsert: conflicting submission unavailable");
|
|
248937
|
+
await client.query("COMMIT");
|
|
248938
|
+
return { submitted: rowToPending(prior.rows[0]), batch: [] };
|
|
248939
|
+
}
|
|
248940
|
+
const claimed = await client.query(
|
|
248941
|
+
`UPDATE ${this.s}.chat_pending_messages SET state='delivering', state_at=now(), state_reason='append-now'
|
|
248942
|
+
WHERE chat_session_id=$1 AND state='queued' RETURNING *`,
|
|
248943
|
+
[m2.chatSessionId]
|
|
248944
|
+
);
|
|
248945
|
+
await client.query("COMMIT");
|
|
248946
|
+
return { submitted: rowToPending(ins.rows[0]), batch: claimed.rows.map(rowToPending).sort((a, b2) => a.seq - b2.seq) };
|
|
248947
|
+
} catch (error2) {
|
|
248948
|
+
await client.query("ROLLBACK").catch(() => {
|
|
248949
|
+
});
|
|
248950
|
+
throw error2;
|
|
248951
|
+
} finally {
|
|
248952
|
+
client.release();
|
|
248953
|
+
}
|
|
248954
|
+
}
|
|
248282
248955
|
async enqueuePendingMessage(m2) {
|
|
248283
248956
|
await this.assertSessionInScope(m2.chatSessionId);
|
|
248284
248957
|
const initialState = m2.state;
|
|
@@ -252201,6 +252874,7 @@ async function provisionCompanyDataPlaneSchema(pool, schema) {
|
|
|
252201
252874
|
reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
252202
252875
|
max_concurrent integer NOT NULL DEFAULT 4,
|
|
252203
252876
|
batched_continuation boolean NOT NULL DEFAULT false,
|
|
252877
|
+
continuation_max_rounds integer NOT NULL DEFAULT 3 CHECK (continuation_max_rounds BETWEEN 1 AND 100),
|
|
252204
252878
|
extra_args jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
252205
252879
|
memory_enabled boolean NOT NULL DEFAULT true,
|
|
252206
252880
|
created_by text NOT NULL REFERENCES ${s2}.actors(id) ON DELETE RESTRICT,
|
|
@@ -252209,6 +252883,7 @@ async function provisionCompanyDataPlaneSchema(pool, schema) {
|
|
|
252209
252883
|
rolled_back_from integer,
|
|
252210
252884
|
PRIMARY KEY (actor_id, version)
|
|
252211
252885
|
)`);
|
|
252886
|
+
await pool.query(`ALTER TABLE ${s2}.actor_configs ADD COLUMN IF NOT EXISTS continuation_max_rounds integer NOT NULL DEFAULT 3 CHECK (continuation_max_rounds BETWEEN 1 AND 100)`);
|
|
252212
252887
|
await pool.query(`
|
|
252213
252888
|
CREATE TABLE IF NOT EXISTS ${s2}.agent_prefs (
|
|
252214
252889
|
actor_id text PRIMARY KEY REFERENCES ${s2}.actors(id) ON DELETE CASCADE,
|
|
@@ -252332,6 +253007,7 @@ function rowToConfig2(row) {
|
|
|
252332
253007
|
connectorIds: row.connector_ids,
|
|
252333
253008
|
maxConcurrent: row.max_concurrent,
|
|
252334
253009
|
batchedContinuation: row.batched_continuation,
|
|
253010
|
+
continuationMaxRounds: row.continuation_max_rounds ?? 3,
|
|
252335
253011
|
extraArgs: row.extra_args ?? [],
|
|
252336
253012
|
createdBy: row.created_by,
|
|
252337
253013
|
createdAt: new Date(row.created_at).toISOString(),
|
|
@@ -252614,8 +253290,8 @@ var init_company_owned_registry_store = __esm({
|
|
|
252614
253290
|
async appendConfig(c) {
|
|
252615
253291
|
const r = await this.pool.query(
|
|
252616
253292
|
`INSERT INTO ${this.s}.actor_configs
|
|
252617
|
-
(actor_id, version, prompt, model, skills, connector_ids, max_concurrent, batched_continuation, extra_args, created_by, created_at, source, rolled_back_from)
|
|
252618
|
-
SELECT $1,$2::int,$3,$4,$5::jsonb,$6::jsonb,$7::int,$8::boolean,$9::jsonb,$10,$11,$12,$13
|
|
253293
|
+
(actor_id, version, prompt, model, skills, connector_ids, max_concurrent, batched_continuation, extra_args, created_by, created_at, source, rolled_back_from, continuation_max_rounds)
|
|
253294
|
+
SELECT $1,$2::int,$3,$4,$5::jsonb,$6::jsonb,$7::int,$8::boolean,$9::jsonb,$10,$11,$12,$13,$14::int
|
|
252619
253295
|
WHERE (SELECT COALESCE(MAX(version), 0) + 1 FROM ${this.s}.actor_configs WHERE actor_id = $1) = $2::int`,
|
|
252620
253296
|
[
|
|
252621
253297
|
c.actorId,
|
|
@@ -252630,7 +253306,8 @@ var init_company_owned_registry_store = __esm({
|
|
|
252630
253306
|
c.createdBy,
|
|
252631
253307
|
c.createdAt,
|
|
252632
253308
|
c.source,
|
|
252633
|
-
c.rolledBackFrom ?? null
|
|
253309
|
+
c.rolledBackFrom ?? null,
|
|
253310
|
+
c.continuationMaxRounds ?? 3
|
|
252634
253311
|
]
|
|
252635
253312
|
);
|
|
252636
253313
|
if (r.rowCount !== 1) throw new Error(`config version conflict for ${c.actorId}: got ${c.version}`);
|
|
@@ -253695,9 +254372,19 @@ var init_cutover_aware_trace = __esm({
|
|
|
253695
254372
|
constructor(o) {
|
|
253696
254373
|
this.o = o;
|
|
253697
254374
|
}
|
|
253698
|
-
|
|
254375
|
+
/** Resolve store and isolation together, from one cutover snapshot. Read-only consumers
|
|
254376
|
+
* must not infer company isolation merely from having a companyId. */
|
|
254377
|
+
async readTarget() {
|
|
253699
254378
|
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
253700
|
-
|
|
254379
|
+
const companyScoped = this.o.cutover.readsCompanySchema(row);
|
|
254380
|
+
return { store: companyScoped ? this.o.owned : this.o.legacy, companyScoped };
|
|
254381
|
+
}
|
|
254382
|
+
/** Expose only usage reads: callers cannot bypass the frozen-write fence. */
|
|
254383
|
+
async resolveUsageReadTarget() {
|
|
254384
|
+
return this.readTarget();
|
|
254385
|
+
}
|
|
254386
|
+
async pick() {
|
|
254387
|
+
return (await this.readTarget()).store;
|
|
253701
254388
|
}
|
|
253702
254389
|
async pickWritable() {
|
|
253703
254390
|
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
@@ -254676,8 +255363,8 @@ async function runIdentityMigration(input) {
|
|
|
254676
255363
|
const ins = await client.query(
|
|
254677
255364
|
`INSERT INTO ${cs}.actor_configs
|
|
254678
255365
|
(actor_id,version,prompt,model,skills,connector_ids,reviewer_ids,max_concurrent,
|
|
254679
|
-
batched_continuation,extra_args,memory_enabled,created_by,created_at,source,rolled_back_from)
|
|
254680
|
-
VALUES ($1,$2,$3,$4,$5::jsonb,$6::jsonb,$7::jsonb,$8,$9,$10::jsonb,$11,$12,$13,$14,$15)
|
|
255366
|
+
batched_continuation,extra_args,memory_enabled,created_by,created_at,source,rolled_back_from,continuation_max_rounds)
|
|
255367
|
+
VALUES ($1,$2,$3,$4,$5::jsonb,$6::jsonb,$7::jsonb,$8,$9,$10::jsonb,$11,$12,$13,$14,$15,$16)
|
|
254681
255368
|
ON CONFLICT (actor_id, version) DO NOTHING`,
|
|
254682
255369
|
[
|
|
254683
255370
|
r.actor_id,
|
|
@@ -254694,7 +255381,8 @@ async function runIdentityMigration(input) {
|
|
|
254694
255381
|
r.created_by,
|
|
254695
255382
|
r.created_at,
|
|
254696
255383
|
r.source,
|
|
254697
|
-
r.rolled_back_from ?? null
|
|
255384
|
+
r.rolled_back_from ?? null,
|
|
255385
|
+
r.continuation_max_rounds ?? 3
|
|
254698
255386
|
]
|
|
254699
255387
|
);
|
|
254700
255388
|
plan.configRows += ins.rowCount ?? 0;
|
|
@@ -257034,6 +257722,9 @@ var init_cutover_aware_workorder_drafts = __esm({
|
|
|
257034
257722
|
async forWorkspace(w2) {
|
|
257035
257723
|
return (await this.pick()).forWorkspace(w2);
|
|
257036
257724
|
}
|
|
257725
|
+
async forWorkspaceVersion(w2, version2) {
|
|
257726
|
+
return (await this.pick()).forWorkspaceVersion(w2, version2);
|
|
257727
|
+
}
|
|
257037
257728
|
async forSession(s2) {
|
|
257038
257729
|
return (await this.pick()).forSession(s2);
|
|
257039
257730
|
}
|
|
@@ -257981,6 +258672,7 @@ var init_src11 = __esm({
|
|
|
257981
258672
|
init_tenancy_move_verify();
|
|
257982
258673
|
init_company_scoped_registry();
|
|
257983
258674
|
init_session_workdir();
|
|
258675
|
+
init_outputs_scan();
|
|
257984
258676
|
init_org_model_backfill();
|
|
257985
258677
|
init_types_admin();
|
|
257986
258678
|
init_roles_admin();
|
|
@@ -258045,7 +258737,7 @@ var init_src11 = __esm({
|
|
|
258045
258737
|
init_execution_continuity2();
|
|
258046
258738
|
init_chat_sessions();
|
|
258047
258739
|
init_dev_store();
|
|
258048
|
-
|
|
258740
|
+
init_continuation3();
|
|
258049
258741
|
init_knowledge2();
|
|
258050
258742
|
init_delegations();
|
|
258051
258743
|
init_automations();
|
|
@@ -258390,6 +259082,13 @@ init_src11();
|
|
|
258390
259082
|
init_src();
|
|
258391
259083
|
var BRAINSTORM_LEGACY_LINE = "\u8BF7\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u95EE\u9898\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002";
|
|
258392
259084
|
var BRAINSTORM_ARTIFACT_GUARD = "\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002";
|
|
259085
|
+
var OUTPUTS_CONVENTION = [
|
|
259086
|
+
"## \u4EA7\u51FA\u653E\u54EA",
|
|
259087
|
+
"",
|
|
259088
|
+
"\u8981\u7ED9\u7528\u6237\u770B\u7684\u6210\u54C1\u6587\u4EF6\uFF0C**\u653E\u8FDB\u5DE5\u4F5C\u76EE\u5F55\u4E0B\u7684 `outputs/`**\uFF08\u53EF\u4EE5\u6709\u5B50\u76EE\u5F55\uFF0C\u7ED3\u6784\u81EA\u5DF1\u5B9A\uFF09\u3002",
|
|
259089
|
+
"\u53EA\u6709\u8FD9\u4E2A\u76EE\u5F55\u91CC\u7684\u6587\u4EF6\u4F1A\u5728\u5BF9\u8BDD\u91CC\u663E\u793A\u6210\u53EF\u70B9\u5F00\u7684\u6587\u4EF6\u5361\u7247\uFF1B\u5199\u5728\u522B\u5904\u7684\u7528\u6237\u770B\u4E0D\u89C1\u3002",
|
|
259090
|
+
"\u4E2D\u95F4\u4EA7\u7269\u3001\u4E34\u65F6\u811A\u672C\u3001\u8349\u7A3F\u4E0D\u8981\u653E\u8FD9\u91CC\u2014\u2014\u653E\u8FDB\u53BB\u5C31\u4F1A\u6446\u5230\u7528\u6237\u9762\u524D\u3002"
|
|
259091
|
+
].join("\n");
|
|
258393
259092
|
function buildChatTaskHeader(input) {
|
|
258394
259093
|
if (input.kind === "isolated-knowledge") {
|
|
258395
259094
|
return {
|
|
@@ -258410,7 +259109,9 @@ function buildChatTaskHeader(input) {
|
|
|
258410
259109
|
`your_actor_id: ${input.actorId}`,
|
|
258411
259110
|
`workspace: ${input.workspace}`,
|
|
258412
259111
|
"",
|
|
258413
|
-
CONVERSATIONAL_MANAGER_BODY
|
|
259112
|
+
CONVERSATIONAL_MANAGER_BODY,
|
|
259113
|
+
"",
|
|
259114
|
+
OUTPUTS_CONVENTION
|
|
258414
259115
|
]
|
|
258415
259116
|
};
|
|
258416
259117
|
}
|
|
@@ -258422,7 +259123,9 @@ function buildChatTaskHeader(input) {
|
|
|
258422
259123
|
input.sessionLine,
|
|
258423
259124
|
`your_actor_id: ${input.actorId}`,
|
|
258424
259125
|
"",
|
|
258425
|
-
routed ? BRAINSTORM_ARTIFACT_GUARD : BRAINSTORM_LEGACY_LINE
|
|
259126
|
+
routed ? BRAINSTORM_ARTIFACT_GUARD : BRAINSTORM_LEGACY_LINE,
|
|
259127
|
+
// 子会话那一段由 EXPERT_DELEGATION_SYSTEM_PROMPT 讲,这里不重复(见 OUTPUTS_CONVENTION 注释)。
|
|
259128
|
+
...input.delegatedChildTurn ? [] : ["", OUTPUTS_CONVENTION]
|
|
258426
259129
|
],
|
|
258427
259130
|
...routed ? { routingSystemPrompt: EXECUTION_ROUTING_BODY } : {}
|
|
258428
259131
|
};
|
|
@@ -258571,9 +259274,9 @@ function render(reason, facts) {
|
|
|
258571
259274
|
switch (reason.kind) {
|
|
258572
259275
|
case "checkpoints-exhausted":
|
|
258573
259276
|
return {
|
|
258574
|
-
headline: `\u8FD9\u4E00\u6B65**\
|
|
259277
|
+
headline: `\u8FD9\u4E00\u6B65**\u8FDE\u7EED ${reason.rounds} \u8F6E\u6CA1\u6709\u65B0\u589E\u4EA4\u4ED8\u5185\u5BB9**\uFF0C\u91CD\u590D\u63D0\u4EA4\u5DF2\u6709\u5FEB\u7167\u4E14\u672A\u6536\u53E3\uFF08\u65E0\u8FDB\u5C55\u4E0A\u9650 ${reason.limit}\uFF09\u2014\u2014\u5F15\u64CE\u5DF2\u6682\u505C\u81EA\u52A8\u7EED\u8DD1\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406\u3002`,
|
|
258575
259278
|
detail: facts,
|
|
258576
|
-
action: "\u9700\u8981\u4F60\u5904\u7406\uFF1A\
|
|
259279
|
+
action: "\u9700\u8981\u4F60\u5904\u7406\uFF1A\u68C0\u67E5\u6700\u8FD1\u5FEB\u7167\u548C\u5269\u4F59\u5DE5\u4F5C\uFF0C\u8865\u5145\u6267\u884C\u65B9\u5411\u540E\u91CD\u8DD1\u672C\u8282\u70B9\uFF08\u91CD\u7F6E\u65E0\u8FDB\u5C55\u8BA1\u6570\uFF09\uFF1B\u5982\u679C\u5DF2\u7ECF\u5B8C\u6210\uFF0C\u8BA9\u6267\u884C\u4EBA\u63D0\u4EA4\u5B8C\u6574\u5FEB\u7167\u5E76 conclude\u3002"
|
|
258577
259280
|
};
|
|
258578
259281
|
case "gate-rejections-exhausted":
|
|
258579
259282
|
return {
|
|
@@ -259597,6 +260300,12 @@ async function startServe(opts) {
|
|
|
259597
260300
|
kernel: defaultCompanyKernel,
|
|
259598
260301
|
blobs: assets,
|
|
259599
260302
|
trace: () => traceStoreForActors,
|
|
260303
|
+
// Same current-company ledger as chat/dispatch writes (PR #1223). Resolve the
|
|
260304
|
+
// physical read target and its isolation together; never treat legacy as owned.
|
|
260305
|
+
resolveTrace: async (cid) => {
|
|
260306
|
+
const store = await traceStoreFor(cid);
|
|
260307
|
+
return store instanceof CutoverAwareTraceStore ? store.resolveUsageReadTarget() : { store, companyScoped: !pgPool };
|
|
260308
|
+
},
|
|
259600
260309
|
audit: registryAudit,
|
|
259601
260310
|
// ADR 凭据保险库 §4.5/§4.7 / ADR 0125:reveal 判权的项目解析 + 凭据读取审计读写。
|
|
259602
260311
|
resolveDispatchScope: resolveDispatchScope2,
|
|
@@ -261615,6 +262324,39 @@ async function startServe(opts) {
|
|
|
261615
262324
|
// 三个方法的组合规则住在 `createDelegationWorkdirPort`(`@oasis/server`)——评审
|
|
261616
262325
|
// wo:acfee725 blocker ② 回归防线:**serve.ts 与单测走同一处**,那样「装配层退回全局 binding」
|
|
261617
262326
|
// 这类事故在单测里就能被复现。绝不许在这里再写一份。
|
|
262327
|
+
/**
|
|
262328
|
+
* 会话产出扫描(2026-09-11 产出卡):轮次收尾扫这条会话的 `outputs/`,交出「这一轮变了哪些」。
|
|
262329
|
+
*
|
|
262330
|
+
* 复用 `delegationWorkdirLocator`(与派发、回流、chat 域**同一个 binding resolver 实例**)——
|
|
262331
|
+
* 委派回程读的就是子会话的 `outputs/`,主会话这条只是把同一套读口用到了自己身上。
|
|
262332
|
+
*
|
|
262333
|
+
* 三处 fail-closed,都返回 `null`(= 这一轮没有产出卡),一处都不猜:
|
|
262334
|
+
* · 桥还没建好 / 会话没绑定 / 节点离线 → 拿不到工作区;
|
|
262335
|
+
* · store 没有指纹方法(老 store / 文件态装配)→ 没地方记「上一轮长什么样」,
|
|
262336
|
+
* 硬扫会把整个 `outputs/` 每轮都当成新增,一口气刷几十张卡;
|
|
262337
|
+
* · 解析不出公司 → **绝不回落默认公司**(否则会去别家公司的库里读写指纹)。
|
|
262338
|
+
*/
|
|
262339
|
+
sessionOutputsScan: async ({ chatSessionId, companyId }) => {
|
|
262340
|
+
const bridge = workdirBridge;
|
|
262341
|
+
if (!bridge) return null;
|
|
262342
|
+
const store = chatStoreRouter ? await chatStoreFor(companyId).catch(() => null) : chatSessionStore;
|
|
262343
|
+
const read = store?.readOutputFingerprints?.bind(store);
|
|
262344
|
+
const write = store?.writeOutputFingerprints?.bind(store);
|
|
262345
|
+
if (!read || !write) return null;
|
|
262346
|
+
const located = await locateSessionWorkdir(delegationWorkdirLocator, {
|
|
262347
|
+
chatSessionId,
|
|
262348
|
+
...companyId !== void 0 ? { companyId } : {}
|
|
262349
|
+
});
|
|
262350
|
+
if (!located.ok) return null;
|
|
262351
|
+
const diff = await settleSessionOutputs({
|
|
262352
|
+
bridge,
|
|
262353
|
+
loc: located.loc,
|
|
262354
|
+
sessionId: chatSessionId,
|
|
262355
|
+
readFingerprints: read,
|
|
262356
|
+
writeFingerprints: write
|
|
262357
|
+
}).catch(() => null);
|
|
262358
|
+
return diff?.changed ?? null;
|
|
262359
|
+
},
|
|
261618
262360
|
delegationWorkdir: createDelegationWorkdirPort({
|
|
261619
262361
|
locator: delegationWorkdirLocator,
|
|
261620
262362
|
// 桥在 hub 建好后(本函数尾部)才赋值 → getter 每次调用时读最新值;早于此处的构造时 `workdirBridge`
|
|
@@ -263235,6 +263977,10 @@ async function startServe(opts) {
|
|
|
263235
263977
|
return false;
|
|
263236
263978
|
});
|
|
263237
263979
|
const engineIO = {
|
|
263980
|
+
async continuationPolicy(actorId) {
|
|
263981
|
+
const config2 = await (await companyRegistryFor(companyId)).latestConfig(actorId);
|
|
263982
|
+
return config2?.batchedContinuation ? { maxRounds: config2.continuationMaxRounds ?? 3 } : null;
|
|
263983
|
+
},
|
|
263238
263984
|
async dispatchWork(workId, opts2) {
|
|
263239
263985
|
if (engineDispatchBridges.size === 0) {
|
|
263240
263986
|
console.warn(`[new-engine] dispatchWork ${workId} \u8DF3\u8FC7\uFF1A\u672C\u8FDB\u7A0B\u4E00\u5BB6\u516C\u53F8\u7684\u6D3E\u53D1\u6865\u90FD\u6CA1\u6709\uFF08--dispatch \u672A\u5F00\uFF1F\uFF09`);
|
|
@@ -265300,6 +266046,7 @@ async function runSession(dispatchId, job, deps) {
|
|
|
265300
266046
|
const cancelledBeforeStart = deps.wasCancelled?.() ?? false;
|
|
265301
266047
|
const normalizer = new ProviderStreamNormalizer({
|
|
265302
266048
|
providerName: job.binding?.runtimeKind ?? "runtime",
|
|
266049
|
+
hasLiveProtocol: handle.supportsLiveProtocol === true,
|
|
265303
266050
|
fallbackTurnId: `oasis-turn:${handle.id}`,
|
|
265304
266051
|
emit: (event) => deps.sink.stream({ type: "session_normalized_event", dispatchId, event })
|
|
265305
266052
|
});
|
|
@@ -272392,7 +273139,7 @@ function shimScript() {
|
|
|
272392
273139
|
}
|
|
272393
273140
|
|
|
272394
273141
|
// src/index.ts
|
|
272395
|
-
var PKG_VERSION = true ? "2.2.
|
|
273142
|
+
var PKG_VERSION = true ? "2.2.21" : "dev";
|
|
272396
273143
|
var LOCAL_BIN = localBin();
|
|
272397
273144
|
var NPM_PREFIX = npmPrefix();
|
|
272398
273145
|
var INSTANCE = DEFAULT_INSTANCE;
|