oasis_test_v2 2.2.18 → 2.2.20
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 +767 -122
- 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();
|
|
@@ -14320,7 +14353,8 @@ function memoryIndexLines(entries) {
|
|
|
14320
14353
|
const rows = entries.slice(0, MEMORY_INJECT_MAX).map((e) => {
|
|
14321
14354
|
const tag = e.projectId === null ? "[actor]" : "[project]";
|
|
14322
14355
|
const kw = e.keywords.length > 0 ? ` {${e.keywords.join(",")}}` : "";
|
|
14323
|
-
|
|
14356
|
+
const preview2 = e.digest.length > MEMORY_INJECT_DIGEST_CHARS ? `${e.digest.slice(0, MEMORY_INJECT_DIGEST_CHARS)}\u2026` : e.digest;
|
|
14357
|
+
return `- \`${e.memId}\` ${tag}${kw} ${preview2}`;
|
|
14324
14358
|
});
|
|
14325
14359
|
return [
|
|
14326
14360
|
``,
|
|
@@ -22923,7 +22957,7 @@ function normalizeClaudeStreamLine(line) {
|
|
|
22923
22957
|
for (const block of content3) {
|
|
22924
22958
|
switch (block["type"]) {
|
|
22925
22959
|
case "text":
|
|
22926
|
-
out.push({ kind: "message", payload: { text: block["text"] } });
|
|
22960
|
+
if (type === "assistant") out.push({ kind: "message", payload: { text: block["text"] } });
|
|
22927
22961
|
break;
|
|
22928
22962
|
case "thinking":
|
|
22929
22963
|
out.push({ kind: "thought", payload: { text: block["thinking"] } });
|
|
@@ -23123,6 +23157,8 @@ var init_claude_code = __esm({
|
|
|
23123
23157
|
// 同上
|
|
23124
23158
|
["--verbose", "boolean"],
|
|
23125
23159
|
// 与 --output-format stream-json 配对;单独无意义、成对是重复
|
|
23160
|
+
["--replay-user-messages", "boolean"],
|
|
23161
|
+
// Input consumption receipts are adapter-owned.
|
|
23126
23162
|
["--include-partial-messages", "boolean"]
|
|
23127
23163
|
// 同上
|
|
23128
23164
|
]);
|
|
@@ -23211,7 +23247,7 @@ var init_claude_code = __esm({
|
|
|
23211
23247
|
// 据此喂 onOutput 实现 chat 逐字流式(同时 telemetry 仍由整条 assistant 消息归一)。
|
|
23212
23248
|
...this.opts.streamJson ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : [],
|
|
23213
23249
|
// ADR-0093:stream-json 输入模式——让 claude 逐条从 stdin 读 user 消息(支持多轮追加)。
|
|
23214
|
-
...appendMode ? ["--input-format", "stream-json"] : [],
|
|
23250
|
+
...appendMode ? ["--input-format", "stream-json", "--replay-user-messages"] : [],
|
|
23215
23251
|
...job.model ?? this.opts.model ? ["--model", job.model ?? this.opts.model] : [],
|
|
23216
23252
|
// ADR「agent CLI 启动参数员工级可配置化」:员工配置 job.extraArgs(在前)+ opts.extraArgs(静态默认,在后),
|
|
23217
23253
|
// 过 CLAUDE_BLOCKED_FLAGS 拦截打破协议契约的 flag(剔除 + warn),追加到 argv 末尾。
|
|
@@ -23279,10 +23315,11 @@ var init_claude_code = __esm({
|
|
|
23279
23315
|
pendingClose = null;
|
|
23280
23316
|
}
|
|
23281
23317
|
};
|
|
23282
|
-
const
|
|
23318
|
+
const pendingInputReceipts = /* @__PURE__ */ new Map();
|
|
23319
|
+
const writeUserMessage = (text5, uuid2) => {
|
|
23283
23320
|
try {
|
|
23284
23321
|
child.stdin.write(
|
|
23285
|
-
JSON.stringify({ type: "user", message: { role: "user", content: [{ type: "text", text: text5 }] } }) + "\n"
|
|
23322
|
+
JSON.stringify({ type: "user", ...uuid2 ? { uuid: uuid2 } : {}, message: { role: "user", content: [{ type: "text", text: text5 }] } }) + "\n"
|
|
23286
23323
|
);
|
|
23287
23324
|
writes++;
|
|
23288
23325
|
cancelPendingClose();
|
|
@@ -23357,6 +23394,22 @@ var init_claude_code = __esm({
|
|
|
23357
23394
|
out.write(line + "\n");
|
|
23358
23395
|
try {
|
|
23359
23396
|
const p2 = JSON.parse(line);
|
|
23397
|
+
if (p2["type"] === "user" && p2["isReplay"] === true && !p2["parent_tool_use_id"] && typeof p2["uuid"] === "string") {
|
|
23398
|
+
const key = pendingInputReceipts.get(p2["uuid"]);
|
|
23399
|
+
if (key) {
|
|
23400
|
+
pendingInputReceipts.delete(p2["uuid"]);
|
|
23401
|
+
diag("input_consumed", { key, uuid: p2["uuid"] });
|
|
23402
|
+
for (const cb of liveEventCbs) cb({
|
|
23403
|
+
protocolVersion: 2,
|
|
23404
|
+
streamId: `claude-live:${id}`,
|
|
23405
|
+
turnId: id,
|
|
23406
|
+
itemId: `input:${p2["uuid"]}`,
|
|
23407
|
+
itemType: "control",
|
|
23408
|
+
operation: "completed",
|
|
23409
|
+
payload: { code: "input_consumed", clientMessageKey: key }
|
|
23410
|
+
});
|
|
23411
|
+
}
|
|
23412
|
+
}
|
|
23360
23413
|
if (p2["type"] === "result") {
|
|
23361
23414
|
lastResult = p2;
|
|
23362
23415
|
results++;
|
|
@@ -23532,6 +23585,9 @@ var init_claude_code = __esm({
|
|
|
23532
23585
|
}
|
|
23533
23586
|
};
|
|
23534
23587
|
function doAppend(input) {
|
|
23588
|
+
if (input.requireConsumptionReceipt && (!input.clientMessageKey || !appendMode)) {
|
|
23589
|
+
return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
|
|
23590
|
+
}
|
|
23535
23591
|
if (!appendMode) {
|
|
23536
23592
|
diag("append_rejected", { reason: "unsupported", writes, results });
|
|
23537
23593
|
return Promise.resolve({ accepted: false, reason: "unsupported", ...input.interrupt ? { interrupt: "unsupported" } : {} });
|
|
@@ -23545,9 +23601,12 @@ var init_claude_code = __esm({
|
|
|
23545
23601
|
const outgoing = report.failed.length ? appendMaterializeWarning(input.text, report) : input.text;
|
|
23546
23602
|
if (input.interrupt) writeInterrupt();
|
|
23547
23603
|
diag("append_accepted", { writes, results, interrupt: !!input.interrupt, chars: input.text.length });
|
|
23548
|
-
const
|
|
23604
|
+
const receiptId = input.requireConsumptionReceipt ? (0, import_node_crypto8.randomUUID)() : void 0;
|
|
23605
|
+
if (receiptId) pendingInputReceipts.set(receiptId, input.clientMessageKey);
|
|
23606
|
+
const ok3 = writeUserMessage(outgoing, receiptId);
|
|
23607
|
+
if (!ok3 && receiptId) pendingInputReceipts.delete(receiptId);
|
|
23549
23608
|
if (!ok3) return Promise.resolve({ accepted: false, reason: "rejected", ...hasFiles ? { files: report } : {}, ...input.interrupt ? { interrupt: "unknown" } : {} });
|
|
23550
|
-
return Promise.resolve({ accepted: true, ...hasFiles ? { files: report } : {}, interrupt: input.interrupt ? "interrupted" : "queued_at_boundary" });
|
|
23609
|
+
return Promise.resolve({ accepted: true, ...receiptId ? { consumption: "pending" } : {}, ...hasFiles ? { files: report } : {}, interrupt: input.interrupt ? "interrupted" : "queued_at_boundary" });
|
|
23551
23610
|
}
|
|
23552
23611
|
}
|
|
23553
23612
|
};
|
|
@@ -24872,6 +24931,8 @@ async function runProtocolSession(job, cfg) {
|
|
|
24872
24931
|
const transcript = openTranscript(transcriptRef);
|
|
24873
24932
|
const outputCbs = [];
|
|
24874
24933
|
const telemetryCbs = [];
|
|
24934
|
+
const liveCbs = [];
|
|
24935
|
+
const appendByKey = /* @__PURE__ */ new Map();
|
|
24875
24936
|
const exitCbs = [];
|
|
24876
24937
|
const state = cfg.createState();
|
|
24877
24938
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -24907,7 +24968,7 @@ async function runProtocolSession(job, cfg) {
|
|
|
24907
24968
|
pending.clear();
|
|
24908
24969
|
};
|
|
24909
24970
|
const send = (command) => {
|
|
24910
|
-
if (closing || child.stdin.destroyed || !child.stdin.writable) return Promise.reject(new
|
|
24971
|
+
if (closing || child.stdin.destroyed || !child.stdin.writable) return Promise.reject(new ProtocolSessionClosing("session stdin is not writable"));
|
|
24911
24972
|
const idValue = command.id ?? `oasis_${++nextRequestId}`;
|
|
24912
24973
|
const message = { ...command, id: idValue };
|
|
24913
24974
|
const key = commandId(idValue);
|
|
@@ -24927,11 +24988,12 @@ async function runProtocolSession(job, cfg) {
|
|
|
24927
24988
|
if (isResponse(message) && responseId !== void 0 && pending.has(responseId)) {
|
|
24928
24989
|
const waiter = pending.get(responseId);
|
|
24929
24990
|
pending.delete(responseId);
|
|
24930
|
-
if (message.type === "response" && message.success === false) waiter.reject(new
|
|
24931
|
-
else if (Object.prototype.hasOwnProperty.call(message, "error")) waiter.reject(new
|
|
24991
|
+
if (message.type === "response" && message.success === false) waiter.reject(new ProtocolCommandRejected(String(message.error ?? "runtime rejected command")));
|
|
24992
|
+
else if (Object.prototype.hasOwnProperty.call(message, "error")) waiter.reject(new ProtocolCommandRejected(String(message.error?.message ?? message.error)));
|
|
24932
24993
|
else waiter.resolve(message.result ?? message);
|
|
24933
24994
|
return;
|
|
24934
24995
|
}
|
|
24996
|
+
for (const event of cfg.liveEvents?.(message, state) ?? []) for (const cb of liveCbs) cb(event);
|
|
24935
24997
|
for (const line of cfg.normalize(message, state)) {
|
|
24936
24998
|
if (line.outputText) for (const cb of outputCbs) cb(line.outputText);
|
|
24937
24999
|
const event = { kind: line.kind, payload: line.payload, seq: ++eventSeq, ts: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -25023,25 +25085,34 @@ async function runProtocolSession(job, cfg) {
|
|
|
25023
25085
|
onTelemetry(cb) {
|
|
25024
25086
|
telemetryCbs.push(cb);
|
|
25025
25087
|
},
|
|
25088
|
+
onLiveEvent(cb) {
|
|
25089
|
+
liveCbs.push(cb);
|
|
25090
|
+
},
|
|
25026
25091
|
get canAppendInput() {
|
|
25027
25092
|
return !closing && started && !exited;
|
|
25028
25093
|
},
|
|
25029
25094
|
appendInput(input) {
|
|
25095
|
+
const key = input.clientMessageKey;
|
|
25096
|
+
if (key && appendByKey.has(key)) return appendByKey.get(key);
|
|
25097
|
+
if (input.requireConsumptionReceipt && !cfg.supportsInputConsumptionReceipts) return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
|
|
25030
25098
|
appendChain = appendChain.then(async () => {
|
|
25031
25099
|
if (exited || closing) return { accepted: false, reason: "session-closing" };
|
|
25032
25100
|
const files = materializeFiles(dir, input.files, input.binaryFiles);
|
|
25033
25101
|
try {
|
|
25034
|
-
const
|
|
25035
|
-
return { ...
|
|
25036
|
-
} catch {
|
|
25037
|
-
|
|
25102
|
+
const result2 = await cfg.append(input, state, send);
|
|
25103
|
+
return { ...result2, ...files.written.length || files.failed.length ? { files } : {} };
|
|
25104
|
+
} catch (error2) {
|
|
25105
|
+
const reason = error2 instanceof ProtocolSessionClosing ? "session-closing" : error2 instanceof ProtocolCommandRejected ? "rejected" : "unconfirmed";
|
|
25106
|
+
return { accepted: false, reason, ...files.written.length || files.failed.length ? { files } : {} };
|
|
25038
25107
|
}
|
|
25039
25108
|
});
|
|
25040
|
-
|
|
25109
|
+
const result = appendChain;
|
|
25110
|
+
if (key) appendByKey.set(key, result);
|
|
25111
|
+
return result;
|
|
25041
25112
|
}
|
|
25042
25113
|
};
|
|
25043
25114
|
}
|
|
25044
|
-
var import_node_child_process6, import_node_crypto10, path9;
|
|
25115
|
+
var import_node_child_process6, import_node_crypto10, path9, ProtocolCommandRejected, ProtocolSessionClosing;
|
|
25045
25116
|
var init_protocol_session = __esm({
|
|
25046
25117
|
"../adapters/src/_core/protocol-session.ts"() {
|
|
25047
25118
|
"use strict";
|
|
@@ -25057,6 +25128,10 @@ var init_protocol_session = __esm({
|
|
|
25057
25128
|
init_transcript();
|
|
25058
25129
|
init_filter_extra_args();
|
|
25059
25130
|
init_root_bypass();
|
|
25131
|
+
ProtocolCommandRejected = class extends Error {
|
|
25132
|
+
};
|
|
25133
|
+
ProtocolSessionClosing = class extends Error {
|
|
25134
|
+
};
|
|
25060
25135
|
}
|
|
25061
25136
|
});
|
|
25062
25137
|
|
|
@@ -25569,11 +25644,13 @@ var init_codex = __esm({
|
|
|
25569
25644
|
workRoot: this.opts.workRoot,
|
|
25570
25645
|
env: this.opts.env,
|
|
25571
25646
|
runtimeKind: "codex",
|
|
25647
|
+
supportsInputConsumptionReceipts: true,
|
|
25572
25648
|
syncSkills: this.opts.syncSkills,
|
|
25573
25649
|
createState: () => ({
|
|
25574
25650
|
threadId: void 0,
|
|
25575
25651
|
turnId: void 0,
|
|
25576
25652
|
active: false,
|
|
25653
|
+
pendingInputs: /* @__PURE__ */ new Set(),
|
|
25577
25654
|
codex: createCodexNormalizeState(),
|
|
25578
25655
|
// 本次 run 的用量:逐轮累加 `thread/tokenUsage/updated` 的 `last`(见该解析函数注释)。
|
|
25579
25656
|
usage: { input: 0, cacheRead: 0, cacheWrite: 0, output: 0 },
|
|
@@ -25617,6 +25694,34 @@ ${task}` : task;
|
|
|
25617
25694
|
if (typeof turnId === "string" && turnId) state.turnId = turnId;
|
|
25618
25695
|
if (!state.turnId) throw new Error("codex app-server returned no turn id");
|
|
25619
25696
|
},
|
|
25697
|
+
liveEvents(message, state) {
|
|
25698
|
+
const params = message.params;
|
|
25699
|
+
const method = message.method;
|
|
25700
|
+
const turnId = typeof params?.turnId === "string" ? params.turnId : state.turnId ?? "codex-pending";
|
|
25701
|
+
const base = { protocolVersion: 2, streamId: `codex:${state.threadId}`, turnId };
|
|
25702
|
+
if (method === "item/agentMessage/delta" && typeof params?.itemId === "string" && typeof params.delta === "string") {
|
|
25703
|
+
return [{ ...base, itemId: params.itemId, itemType: "message", operation: "append_text", payload: { text: params.delta } }];
|
|
25704
|
+
}
|
|
25705
|
+
if (method === "item/started" || method === "item/completed") {
|
|
25706
|
+
const item = params?.item;
|
|
25707
|
+
if (item?.type === "userMessage" && typeof item.clientId === "string" && state.pendingInputs.delete(item.clientId)) {
|
|
25708
|
+
return [{
|
|
25709
|
+
...base,
|
|
25710
|
+
itemId: `input:${item.clientId}`,
|
|
25711
|
+
itemType: "control",
|
|
25712
|
+
operation: "completed",
|
|
25713
|
+
payload: { code: "input_consumed", clientMessageKey: item.clientId }
|
|
25714
|
+
}];
|
|
25715
|
+
}
|
|
25716
|
+
if (method === "item/completed" && item?.type === "agentMessage" && typeof item.id === "string") {
|
|
25717
|
+
return [{ ...base, itemId: item.id, itemType: "message", operation: "completed", payload: { text: typeof item.text === "string" ? item.text : "" } }];
|
|
25718
|
+
}
|
|
25719
|
+
}
|
|
25720
|
+
if (method === "turn/completed") {
|
|
25721
|
+
return [{ ...base, itemId: "turn-end", itemType: "control", operation: "completed", payload: { code: "output_block_completed" } }];
|
|
25722
|
+
}
|
|
25723
|
+
return [];
|
|
25724
|
+
},
|
|
25620
25725
|
normalize(message, state) {
|
|
25621
25726
|
const params = message.params;
|
|
25622
25727
|
const method = typeof message.method === "string" ? message.method : "";
|
|
@@ -25675,7 +25780,7 @@ ${task}` : task;
|
|
|
25675
25780
|
};
|
|
25676
25781
|
},
|
|
25677
25782
|
async append(input, state, send) {
|
|
25678
|
-
if (!state.threadId || !state.turnId) return { accepted: false, reason: "
|
|
25783
|
+
if (!state.threadId || !state.turnId || !state.active) return { accepted: false, reason: "session-closing" };
|
|
25679
25784
|
if (input.interrupt) {
|
|
25680
25785
|
await send({ jsonrpc: "2.0", method: "turn/interrupt", params: { threadId: state.threadId, turnId: state.turnId } });
|
|
25681
25786
|
const next = await send({ jsonrpc: "2.0", method: "turn/start", params: { threadId: state.threadId, input: [{ type: "text", text: input.text }] } });
|
|
@@ -25684,13 +25789,22 @@ ${task}` : task;
|
|
|
25684
25789
|
state.active = true;
|
|
25685
25790
|
return { accepted: true, interrupt: "unknown" };
|
|
25686
25791
|
}
|
|
25687
|
-
|
|
25688
|
-
|
|
25689
|
-
|
|
25690
|
-
|
|
25691
|
-
|
|
25692
|
-
|
|
25693
|
-
|
|
25792
|
+
if (input.clientMessageKey) state.pendingInputs.add(input.clientMessageKey);
|
|
25793
|
+
try {
|
|
25794
|
+
await send({ jsonrpc: "2.0", method: "turn/steer", params: {
|
|
25795
|
+
threadId: state.threadId,
|
|
25796
|
+
expectedTurnId: state.turnId,
|
|
25797
|
+
...input.clientMessageKey ? { clientUserMessageId: input.clientMessageKey } : {},
|
|
25798
|
+
input: [{ type: "text", text: input.text }]
|
|
25799
|
+
} });
|
|
25800
|
+
} catch (error2) {
|
|
25801
|
+
if (input.clientMessageKey) state.pendingInputs.delete(input.clientMessageKey);
|
|
25802
|
+
if (error2 instanceof ProtocolCommandRejected && /no active turn|not active|expected.*turn|turn.*mismatch/i.test(error2.message)) {
|
|
25803
|
+
return { accepted: false, reason: "session-closing" };
|
|
25804
|
+
}
|
|
25805
|
+
throw error2;
|
|
25806
|
+
}
|
|
25807
|
+
return { accepted: true, consumption: "pending" };
|
|
25694
25808
|
}
|
|
25695
25809
|
});
|
|
25696
25810
|
}
|
|
@@ -26566,6 +26680,7 @@ async function runACPSession(job, cfg) {
|
|
|
26566
26680
|
const transcriptOut = openTranscript(transcriptRef);
|
|
26567
26681
|
const outputCbs = [];
|
|
26568
26682
|
const telemetryCbs = [];
|
|
26683
|
+
const liveCbs = [];
|
|
26569
26684
|
const exitCbs = [];
|
|
26570
26685
|
let exited;
|
|
26571
26686
|
let killReason = null;
|
|
@@ -26644,6 +26759,34 @@ ${note}` : note };
|
|
|
26644
26759
|
});
|
|
26645
26760
|
let lastRpcError;
|
|
26646
26761
|
let streamingCurrentTurn = false;
|
|
26762
|
+
const emitBoundary = () => {
|
|
26763
|
+
for (const cb of liveCbs) cb({
|
|
26764
|
+
protocolVersion: 2,
|
|
26765
|
+
streamId: id,
|
|
26766
|
+
turnId: id,
|
|
26767
|
+
itemId: "prompt-boundary",
|
|
26768
|
+
itemType: "control",
|
|
26769
|
+
operation: "completed",
|
|
26770
|
+
payload: { code: "output_block_completed" }
|
|
26771
|
+
});
|
|
26772
|
+
};
|
|
26773
|
+
const confirmedInputs = /* @__PURE__ */ new Set();
|
|
26774
|
+
const confirmAppendBatch = () => {
|
|
26775
|
+
for (const input of inflightAppendBatch) {
|
|
26776
|
+
const key = input.clientMessageKey;
|
|
26777
|
+
if (!key || confirmedInputs.has(key)) continue;
|
|
26778
|
+
confirmedInputs.add(key);
|
|
26779
|
+
for (const cb of liveCbs) cb({
|
|
26780
|
+
protocolVersion: 2,
|
|
26781
|
+
streamId: id,
|
|
26782
|
+
turnId: id,
|
|
26783
|
+
itemId: `input:${key}`,
|
|
26784
|
+
itemType: "control",
|
|
26785
|
+
operation: "completed",
|
|
26786
|
+
payload: { code: "input_consumed", clientMessageKey: key }
|
|
26787
|
+
});
|
|
26788
|
+
}
|
|
26789
|
+
};
|
|
26647
26790
|
const client = new ACPClient(
|
|
26648
26791
|
(line) => {
|
|
26649
26792
|
try {
|
|
@@ -26652,9 +26795,11 @@ ${note}` : note };
|
|
|
26652
26795
|
}
|
|
26653
26796
|
},
|
|
26654
26797
|
(text5) => {
|
|
26798
|
+
confirmAppendBatch();
|
|
26655
26799
|
for (const cb of outputCbs) cb(text5);
|
|
26656
26800
|
},
|
|
26657
26801
|
(e) => {
|
|
26802
|
+
if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) confirmAppendBatch();
|
|
26658
26803
|
for (const cb of telemetryCbs) cb(e);
|
|
26659
26804
|
},
|
|
26660
26805
|
// runtime 自己用 stopReason=cancelled 收尾时也保留取消语义;此前误记成 error,
|
|
@@ -26788,9 +26933,11 @@ ${task}` : task;
|
|
|
26788
26933
|
promptResult = await sendPrompt(userText);
|
|
26789
26934
|
}
|
|
26790
26935
|
while (!killReason && appendQueue.length > 0) {
|
|
26936
|
+
emitBoundary();
|
|
26791
26937
|
inflightAppendBatch = appendQueue.splice(0);
|
|
26792
26938
|
const merged = inflightAppendBatch.map((i) => i.text).filter(Boolean).join("\n\n");
|
|
26793
26939
|
promptResult = await sendPrompt(merged);
|
|
26940
|
+
confirmAppendBatch();
|
|
26794
26941
|
inflightAppendBatch = [];
|
|
26795
26942
|
}
|
|
26796
26943
|
closingForExit = true;
|
|
@@ -26829,6 +26976,9 @@ ${task}` : task;
|
|
|
26829
26976
|
onTelemetry(cb) {
|
|
26830
26977
|
telemetryCbs.push(cb);
|
|
26831
26978
|
},
|
|
26979
|
+
onLiveEvent(cb) {
|
|
26980
|
+
liveCbs.push(cb);
|
|
26981
|
+
},
|
|
26832
26982
|
// ADR-0093 对话追加:入队一条 user 输入,drain 循环会把它发成同 session 的下一回合。
|
|
26833
26983
|
// 已收尾/已退出则拒(session-closing),上层据此降级 kill+resume。
|
|
26834
26984
|
get canAppendInput() {
|
|
@@ -26856,7 +27006,11 @@ ${task}` : task;
|
|
|
26856
27006
|
const report = materializeFiles(dir, input.files, input.binaryFiles);
|
|
26857
27007
|
const queued = report.failed.length ? { ...input, text: appendMaterializeWarning(input.text, report) } : input;
|
|
26858
27008
|
appendQueue.push(queued);
|
|
26859
|
-
const withFiles = (r) =>
|
|
27009
|
+
const withFiles = (r) => ({
|
|
27010
|
+
...r,
|
|
27011
|
+
...input.requireConsumptionReceipt && r.accepted ? { consumption: "pending" } : {},
|
|
27012
|
+
...report.written.length || report.failed.length ? { files: report } : {}
|
|
27013
|
+
});
|
|
26860
27014
|
if (!input.interrupt) return Promise.resolve(withFiles({ accepted: true, interrupt: "queued_at_boundary" }));
|
|
26861
27015
|
const run = appendChain.then(async () => {
|
|
26862
27016
|
const pending = inflightStopReason;
|
|
@@ -27610,12 +27764,13 @@ var init_pi = __esm({
|
|
|
27610
27764
|
return ["--mode", "rpc", "--session", sessionFile, ...model ? ["--model", model] : []];
|
|
27611
27765
|
},
|
|
27612
27766
|
runtimeKind: "pi",
|
|
27767
|
+
supportsInputConsumptionReceipts: true,
|
|
27613
27768
|
workRoot: opts.workRoot,
|
|
27614
27769
|
cleanupWorkdir: opts.cleanupWorkdir,
|
|
27615
27770
|
env: opts.env,
|
|
27616
27771
|
extraArgs: opts.extraArgs,
|
|
27617
27772
|
blockedFlags: PI_BLOCKED_FLAGS,
|
|
27618
|
-
createState: () => ({}),
|
|
27773
|
+
createState: () => ({ pendingInputs: [] }),
|
|
27619
27774
|
initialCommand(job2, _dir, task) {
|
|
27620
27775
|
const prompt2 = job2.systemPrompt ? `${job2.systemPrompt}
|
|
27621
27776
|
|
|
@@ -27632,6 +27787,28 @@ ${task}` : task;
|
|
|
27632
27787
|
} catch {
|
|
27633
27788
|
}
|
|
27634
27789
|
},
|
|
27790
|
+
liveEvents(message, state) {
|
|
27791
|
+
const base = { protocolVersion: 2, streamId: "pi-rpc", turnId: "pi-rpc" };
|
|
27792
|
+
const raw = message.message;
|
|
27793
|
+
if (message.type === "message_end" && raw?.role === "assistant") {
|
|
27794
|
+
return [{ ...base, itemId: "message-end", itemType: "control", operation: "completed", payload: { code: "output_block_completed" } }];
|
|
27795
|
+
}
|
|
27796
|
+
if (message.type === "message_start" && raw?.role === "user") {
|
|
27797
|
+
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;
|
|
27798
|
+
const pending = state.pendingInputs[0];
|
|
27799
|
+
if (pending && text5 === pending.text) {
|
|
27800
|
+
state.pendingInputs.shift();
|
|
27801
|
+
return [{
|
|
27802
|
+
...base,
|
|
27803
|
+
itemId: `input:${pending.key}`,
|
|
27804
|
+
itemType: "control",
|
|
27805
|
+
operation: "completed",
|
|
27806
|
+
payload: { code: "input_consumed", clientMessageKey: pending.key }
|
|
27807
|
+
}];
|
|
27808
|
+
}
|
|
27809
|
+
}
|
|
27810
|
+
return [];
|
|
27811
|
+
},
|
|
27635
27812
|
normalize(message) {
|
|
27636
27813
|
return normalizePi(JSON.stringify(message));
|
|
27637
27814
|
},
|
|
@@ -27641,14 +27818,21 @@ ${task}` : task;
|
|
|
27641
27818
|
runtimeSessionId(state) {
|
|
27642
27819
|
return state.sessionId;
|
|
27643
27820
|
},
|
|
27644
|
-
async append(input,
|
|
27645
|
-
|
|
27646
|
-
|
|
27821
|
+
async append(input, state, send) {
|
|
27822
|
+
const pending = input.clientMessageKey ? { key: input.clientMessageKey, text: input.text } : void 0;
|
|
27823
|
+
if (pending) state.pendingInputs.push(pending);
|
|
27824
|
+
try {
|
|
27825
|
+
if (input.interrupt) {
|
|
27826
|
+
await send({ type: "abort" });
|
|
27827
|
+
await send({ type: "steer", message: input.text });
|
|
27828
|
+
return { accepted: true, interrupt: "unknown" };
|
|
27829
|
+
}
|
|
27647
27830
|
await send({ type: "steer", message: input.text });
|
|
27648
|
-
return { accepted: true,
|
|
27831
|
+
return { accepted: true, consumption: "pending" };
|
|
27832
|
+
} catch (error2) {
|
|
27833
|
+
if (pending) state.pendingInputs = state.pendingInputs.filter((p2) => p2 !== pending);
|
|
27834
|
+
throw error2;
|
|
27649
27835
|
}
|
|
27650
|
-
await send({ type: "steer", message: input.text });
|
|
27651
|
-
return { accepted: true };
|
|
27652
27836
|
}
|
|
27653
27837
|
});
|
|
27654
27838
|
}
|
|
@@ -28458,10 +28642,13 @@ var init_normalizer = __esm({
|
|
|
28458
28642
|
proseClaim = /* @__PURE__ */ new Map();
|
|
28459
28643
|
/** 本 turn 内已经见过的 item 键——认领闸只拦「新起一段」,不拦已知 item 的后续 op。 */
|
|
28460
28644
|
claimedKeys = /* @__PURE__ */ new Set();
|
|
28645
|
+
closedKeys = /* @__PURE__ */ new Set();
|
|
28461
28646
|
/** 见过 stdout 文本没有——见过就压掉 telemetry `message`(与 server.ts 原口径同款去重)。 */
|
|
28462
28647
|
sawTextOutput = false;
|
|
28463
28648
|
/** 本 turn 产出过 item op 没有。收尾时用来决定要不要发 turn 信号(见 turnFinished)。 */
|
|
28464
28649
|
producedAny = false;
|
|
28650
|
+
/** Receipts wait for a whole output block; they never split its bytes. */
|
|
28651
|
+
pendingInputs = /* @__PURE__ */ new Map();
|
|
28465
28652
|
constructor(opts) {
|
|
28466
28653
|
this.providerName = opts.providerName;
|
|
28467
28654
|
this.hasLiveProtocol = opts.hasLiveProtocol === true;
|
|
@@ -28505,8 +28692,17 @@ var init_normalizer = __esm({
|
|
|
28505
28692
|
}
|
|
28506
28693
|
/** 结构化实时事件(原生 Live Protocol v2)。 */
|
|
28507
28694
|
onLiveEvent(event) {
|
|
28508
|
-
this.turnId = event.turnId || this.turnId;
|
|
28509
28695
|
const payload = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload : {};
|
|
28696
|
+
if (event.itemType === "control" && payload.code === "input_consumed" && typeof payload.clientMessageKey === "string") {
|
|
28697
|
+
this.pendingInputs.set(payload.clientMessageKey, event.itemId);
|
|
28698
|
+
if (!this.open) this.flushInputs();
|
|
28699
|
+
return;
|
|
28700
|
+
}
|
|
28701
|
+
if (event.itemType === "control" && payload.code === "output_block_completed") {
|
|
28702
|
+
this.closeOpen("completed");
|
|
28703
|
+
return;
|
|
28704
|
+
}
|
|
28705
|
+
this.turnId = event.turnId || this.turnId;
|
|
28510
28706
|
if (event.operation === "turn_completed" || event.operation === "turn_failed") {
|
|
28511
28707
|
this.turnFinished(event.operation === "turn_failed" ? "failed" : "completed");
|
|
28512
28708
|
return;
|
|
@@ -28522,13 +28718,12 @@ var init_normalizer = __esm({
|
|
|
28522
28718
|
}
|
|
28523
28719
|
return;
|
|
28524
28720
|
}
|
|
28525
|
-
if (event.operation === "completed" && (event.itemType === "message" || event.itemType === "thinking")
|
|
28526
|
-
this.
|
|
28527
|
-
|
|
28528
|
-
payload.text,
|
|
28529
|
-
|
|
28530
|
-
|
|
28531
|
-
);
|
|
28721
|
+
if (event.operation === "completed" && (event.itemType === "message" || event.itemType === "thinking")) {
|
|
28722
|
+
const key = providerItemKey(this.providerName, event.itemId);
|
|
28723
|
+
if (typeof payload.text === "string") {
|
|
28724
|
+
this.snapshotProse(event.itemType === "message" ? "text" : "thinking", payload.text, "live", key);
|
|
28725
|
+
}
|
|
28726
|
+
if (this.open?.key === key) this.closeOpen("completed");
|
|
28532
28727
|
return;
|
|
28533
28728
|
}
|
|
28534
28729
|
if (event.operation === "started" && event.itemType === "tool") {
|
|
@@ -28553,6 +28748,7 @@ var init_normalizer = __esm({
|
|
|
28553
28748
|
this.fallbackIndex.clear();
|
|
28554
28749
|
this.proseClaim.clear();
|
|
28555
28750
|
this.claimedKeys.clear();
|
|
28751
|
+
this.closedKeys.clear();
|
|
28556
28752
|
this.sawTextOutput = false;
|
|
28557
28753
|
this.producedAny = false;
|
|
28558
28754
|
if (!produced) return;
|
|
@@ -28576,6 +28772,10 @@ var init_normalizer = __esm({
|
|
|
28576
28772
|
if (!known && !this.claim(kind, origin)) return;
|
|
28577
28773
|
const key = explicitKey ?? (this.open && this.open.kind === kind ? this.open.key : this.nextFallbackKey(kind));
|
|
28578
28774
|
this.claimedKeys.add(key);
|
|
28775
|
+
if (known && this.closedKeys.has(key)) {
|
|
28776
|
+
this.emitItem({ key, kind, role: "assistant", origin, opShape: { op: "append_text", text: text5 } });
|
|
28777
|
+
return;
|
|
28778
|
+
}
|
|
28579
28779
|
if (!this.open || this.open.key !== key) {
|
|
28580
28780
|
this.closeOpen("completed");
|
|
28581
28781
|
this.open = { key, kind, origin };
|
|
@@ -28592,6 +28792,10 @@ var init_normalizer = __esm({
|
|
|
28592
28792
|
if (!known && !this.claim(kind, origin)) return;
|
|
28593
28793
|
const key = explicitKey ?? (this.open && this.open.kind === kind ? this.open.key : this.nextFallbackKey(kind));
|
|
28594
28794
|
this.claimedKeys.add(key);
|
|
28795
|
+
if (known && this.closedKeys.has(key)) {
|
|
28796
|
+
this.emitItem({ key, kind, role: "assistant", origin, opShape: { op: "set_text", text: text5 } });
|
|
28797
|
+
return;
|
|
28798
|
+
}
|
|
28595
28799
|
if (!this.open || this.open.key !== key) {
|
|
28596
28800
|
this.closeOpen("completed");
|
|
28597
28801
|
this.open = { key, kind, origin };
|
|
@@ -28610,8 +28814,23 @@ var init_normalizer = __esm({
|
|
|
28610
28814
|
closeOpen(status) {
|
|
28611
28815
|
const open3 = this.open;
|
|
28612
28816
|
this.open = null;
|
|
28613
|
-
if (
|
|
28614
|
-
this.emitItem({ key: open3.key, kind: open3.kind, role: "assistant", origin: open3.origin, opShape: { op: "set_status", status } });
|
|
28817
|
+
if (open3) this.closedKeys.add(open3.key);
|
|
28818
|
+
if (open3) this.emitItem({ key: open3.key, kind: open3.kind, role: "assistant", origin: open3.origin, opShape: { op: "set_status", status } });
|
|
28819
|
+
this.flushInputs();
|
|
28820
|
+
}
|
|
28821
|
+
flushInputs() {
|
|
28822
|
+
const inputs = [...this.pendingInputs];
|
|
28823
|
+
this.pendingInputs.clear();
|
|
28824
|
+
for (const [clientMessageKey, id] of inputs) {
|
|
28825
|
+
this.emitItem({
|
|
28826
|
+
key: providerItemKey(this.providerName, id),
|
|
28827
|
+
kind: "control",
|
|
28828
|
+
role: "system",
|
|
28829
|
+
origin: "live",
|
|
28830
|
+
opShape: { op: "set_status", status: "completed" },
|
|
28831
|
+
attrs: { code: "input_consumed", clientMessageKey }
|
|
28832
|
+
});
|
|
28833
|
+
}
|
|
28615
28834
|
}
|
|
28616
28835
|
toolCall(origin, id, name, input) {
|
|
28617
28836
|
this.closeOpen("completed");
|
|
@@ -28831,7 +29050,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28831
29050
|
telemetryTextInContent;
|
|
28832
29051
|
/** 本段的「无输出」注解已经提前落过(`closeSegmentBeforeUserInput`),`beginSegment` 别再落一条。 */
|
|
28833
29052
|
segmentEndEmitted = false;
|
|
28834
|
-
/** **不变量 1**:`providerItemKey → itemId
|
|
29053
|
+
/** **不变量 1**:`providerItemKey → itemId`,本账本唯一持有。仅 runtime turn 结束/收尾清空,用户插入保留。 */
|
|
28835
29054
|
itemKeyMap = /* @__PURE__ */ new Map();
|
|
28836
29055
|
/** 内存账本:itemId → 行。插入顺序即 seq 顺序。 */
|
|
28837
29056
|
rows = /* @__PURE__ */ new Map();
|
|
@@ -28922,7 +29141,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28922
29141
|
const payload = { role: "user", text: text5 };
|
|
28923
29142
|
if (attachments?.length) payload.attachments = attachments;
|
|
28924
29143
|
if (clientKeys?.length) payload.clientSubmitIds = clientKeys;
|
|
28925
|
-
this.insert({
|
|
29144
|
+
return this.insert({
|
|
28926
29145
|
kind: "text",
|
|
28927
29146
|
role: "user",
|
|
28928
29147
|
status: "completed",
|
|
@@ -29010,7 +29229,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29010
29229
|
}
|
|
29011
29230
|
});
|
|
29012
29231
|
const row = this.rows.get(id);
|
|
29013
|
-
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
29232
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord, messageId: row.messageId };
|
|
29014
29233
|
}
|
|
29015
29234
|
/**
|
|
29016
29235
|
* 回灌重启前已经落库的那段正文(`chat-recovery` 重挂 / 迟到对账用)。
|
|
@@ -29045,7 +29264,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29045
29264
|
// ── 段与归属 ──────────────────────────────────────────────────────────────
|
|
29046
29265
|
/**
|
|
29047
29266
|
* 切段(人中途插入):此刻起建的 item 属于**下一条** assistant 行。
|
|
29048
|
-
*
|
|
29267
|
+
* 已有 provider item 保留身份与位置;新 item 才归下一段,迟到快照不能复制旧块。
|
|
29049
29268
|
*/
|
|
29050
29269
|
beginSegment() {
|
|
29051
29270
|
if (this.segmentEndEmitted) {
|
|
@@ -29054,8 +29273,6 @@ var init_chat_item_ledger = __esm({
|
|
|
29054
29273
|
this.closeStreamingRows("completed");
|
|
29055
29274
|
if (this.segmentItemCount === 0 && this.messageId) this.insertSegmentEnd();
|
|
29056
29275
|
}
|
|
29057
|
-
this.itemKeyMap.clear();
|
|
29058
|
-
this.lastTurnId = null;
|
|
29059
29276
|
this.messageId = null;
|
|
29060
29277
|
this.segment += 1;
|
|
29061
29278
|
this.segmentItemCount = 0;
|
|
@@ -29072,14 +29289,14 @@ var init_chat_item_ledger = __esm({
|
|
|
29072
29289
|
* 返回落下的那一行(`null` = 本段有输出、或还没有 assistant 行可归属,不需要注解),
|
|
29073
29290
|
* 调用方据它把这条推上 v3 流——不推的话它只在下一次快照才出现。
|
|
29074
29291
|
*/
|
|
29075
|
-
closeSegmentBeforeUserInput() {
|
|
29292
|
+
closeSegmentBeforeUserInput(allowUnboundSegment = false) {
|
|
29076
29293
|
this.closeStreamingRows("completed");
|
|
29077
29294
|
if (this.segmentEndEmitted) return null;
|
|
29078
|
-
if (this.segmentItemCount !== 0 || !this.messageId) return null;
|
|
29295
|
+
if (this.segmentItemCount !== 0 || !this.messageId && !allowUnboundSegment) return null;
|
|
29079
29296
|
const id = this.insertSegmentEnd();
|
|
29080
29297
|
this.segmentEndEmitted = true;
|
|
29081
29298
|
const row = this.rows.get(id);
|
|
29082
|
-
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord };
|
|
29299
|
+
return { itemId: id, version: row.wireVersion, startedVersion: row.startedVersion, ord: row.ord, messageId: row.messageId };
|
|
29083
29300
|
}
|
|
29084
29301
|
/** 零输出段兜底(S1 评审 B1):那条 assistant 行库里已经在了,一条 item 都没有会被对账抓成假红。 */
|
|
29085
29302
|
insertSegmentEnd() {
|
|
@@ -29098,6 +29315,9 @@ var init_chat_item_ledger = __esm({
|
|
|
29098
29315
|
/** 行落库了,把本段还挂着 NULL 的 assistant item 认领回去,并让后续 item 直接带上它。 */
|
|
29099
29316
|
bindMessage(messageId) {
|
|
29100
29317
|
this.messageId = messageId;
|
|
29318
|
+
for (const row of this.rows.values()) {
|
|
29319
|
+
if (row.role === "assistant" && row.messageId === null) row.messageId = messageId;
|
|
29320
|
+
}
|
|
29101
29321
|
if (!this.persistEnabled) return;
|
|
29102
29322
|
this.enqueue(async () => {
|
|
29103
29323
|
await this.deps.items.bindMessage(this.deps.sessionId, this.deps.turnId, messageId);
|
|
@@ -29157,7 +29377,8 @@ var init_chat_item_ledger = __esm({
|
|
|
29157
29377
|
itemId: id,
|
|
29158
29378
|
version: row.wireVersion,
|
|
29159
29379
|
startedVersion: row.startedVersion,
|
|
29160
|
-
ord: row.ord
|
|
29380
|
+
ord: row.ord,
|
|
29381
|
+
messageId: row.messageId
|
|
29161
29382
|
};
|
|
29162
29383
|
if (event.opShape.op === "set_status") {
|
|
29163
29384
|
const followUp = this.applyOpTo(row, event);
|
|
@@ -29193,7 +29414,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29193
29414
|
}
|
|
29194
29415
|
/** 这一次写的回执。`persist` 已经在里面走过钟了,这里只是把读数带出去给 wire。 */
|
|
29195
29416
|
outcomeOf(row) {
|
|
29196
|
-
return { itemId: row.id, version: row.wireVersion, ord: row.ord };
|
|
29417
|
+
return { itemId: row.id, version: row.wireVersion, ord: row.ord, messageId: row.messageId };
|
|
29197
29418
|
}
|
|
29198
29419
|
/** 关掉所有还 streaming 的行(收尾/切段)。 */
|
|
29199
29420
|
closeStreamingRows(status) {
|
|
@@ -29245,13 +29466,16 @@ var init_chat_item_ledger = __esm({
|
|
|
29245
29466
|
origin: input.origin,
|
|
29246
29467
|
inContent: input.inContent,
|
|
29247
29468
|
persist: input.persist,
|
|
29248
|
-
segment: this.segment
|
|
29469
|
+
segment: this.segment,
|
|
29470
|
+
/* 归属在**建行那一刻**定死:链是异步的,等它执行时 this.messageId 可能已被切段改了。
|
|
29471
|
+
记在行上而不是只记在入库那一步——wire 帧也要带它(2026-09-11),两边必须同一个值。 */
|
|
29472
|
+
messageId: input.forceNullMessage ? null : this.messageId
|
|
29249
29473
|
};
|
|
29250
29474
|
this.rows.set(id, row);
|
|
29251
29475
|
this.createdCount += 1;
|
|
29252
29476
|
if (!input.forceNullMessage) this.segmentItemCount += 1;
|
|
29253
29477
|
if (!input.persist || !this.persistEnabled) return id;
|
|
29254
|
-
const messageId =
|
|
29478
|
+
const messageId = row.messageId;
|
|
29255
29479
|
const createdAt = this.now();
|
|
29256
29480
|
const payload = input.payload ?? this.payloadOf(row);
|
|
29257
29481
|
const metadata = input.metadata ?? { [CHAT_ITEM_META_LEGACY_CONTENT]: input.inContent };
|
|
@@ -29434,6 +29658,7 @@ async function openAssistantRunningRow(deps) {
|
|
|
29434
29658
|
async function finalizeAssistantRow(deps) {
|
|
29435
29659
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
29436
29660
|
const finishedAt = now();
|
|
29661
|
+
const parts = await appendOutputsPart(deps.parts, deps.outputsPart);
|
|
29437
29662
|
if (deps.assistantMsgId) {
|
|
29438
29663
|
if (typeof deps.chatStore.updateMessage !== "function") return;
|
|
29439
29664
|
try {
|
|
@@ -29441,7 +29666,7 @@ async function finalizeAssistantRow(deps) {
|
|
|
29441
29666
|
content: deps.content,
|
|
29442
29667
|
status: deps.status,
|
|
29443
29668
|
completedAt: finishedAt,
|
|
29444
|
-
...
|
|
29669
|
+
...parts.length ? { parts } : {},
|
|
29445
29670
|
...deps.runId ? { runId: deps.runId } : {},
|
|
29446
29671
|
...deps.turnId ? { turnId: deps.turnId } : {}
|
|
29447
29672
|
});
|
|
@@ -29449,7 +29674,7 @@ async function finalizeAssistantRow(deps) {
|
|
|
29449
29674
|
}
|
|
29450
29675
|
return;
|
|
29451
29676
|
}
|
|
29452
|
-
if (!deps.content && !deps.runId &&
|
|
29677
|
+
if (!deps.content && !deps.runId && parts.length === 0) return;
|
|
29453
29678
|
if (typeof deps.chatStore.appendMessage !== "function") return;
|
|
29454
29679
|
try {
|
|
29455
29680
|
await deps.chatStore.appendMessage({
|
|
@@ -29462,11 +29687,18 @@ async function finalizeAssistantRow(deps) {
|
|
|
29462
29687
|
status: deps.status,
|
|
29463
29688
|
...deps.runId ? { runId: deps.runId } : {},
|
|
29464
29689
|
...deps.turnId ? { turnId: deps.turnId } : {},
|
|
29465
|
-
...
|
|
29690
|
+
...parts.length ? { parts } : {}
|
|
29466
29691
|
});
|
|
29467
29692
|
} catch {
|
|
29468
29693
|
}
|
|
29469
29694
|
}
|
|
29695
|
+
async function appendOutputsPart(parts, provider) {
|
|
29696
|
+
if (!provider) return parts;
|
|
29697
|
+
const part = await provider().catch(() => null);
|
|
29698
|
+
if (!part || !part.outputs?.length) return parts;
|
|
29699
|
+
const maxSeq = parts.reduce((m2, p2) => typeof p2.seq === "number" && p2.seq > m2 ? p2.seq : m2, 0);
|
|
29700
|
+
return [...parts, { ...part, seq: maxSeq + 1 }];
|
|
29701
|
+
}
|
|
29470
29702
|
function runBackgroundAssistantChatTurn(deps) {
|
|
29471
29703
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
29472
29704
|
const log3 = deps.log ?? ((m2) => console.warn(m2));
|
|
@@ -29635,6 +29867,7 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
29635
29867
|
parts,
|
|
29636
29868
|
status,
|
|
29637
29869
|
...runId ? { runId } : {},
|
|
29870
|
+
...deps.outputsPart ? { outputsPart: deps.outputsPart } : {},
|
|
29638
29871
|
now
|
|
29639
29872
|
});
|
|
29640
29873
|
if (typeof deps.chatStore.updateSession === "function") {
|
|
@@ -51507,7 +51740,7 @@ var require_websocket = __commonJS({
|
|
|
51507
51740
|
var http2 = require("http");
|
|
51508
51741
|
var net = require("net");
|
|
51509
51742
|
var tls = require("tls");
|
|
51510
|
-
var { randomBytes: randomBytes13, createHash:
|
|
51743
|
+
var { randomBytes: randomBytes13, createHash: createHash39 } = require("crypto");
|
|
51511
51744
|
var { Duplex, Readable } = require("stream");
|
|
51512
51745
|
var { URL: URL2 } = require("url");
|
|
51513
51746
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -52175,7 +52408,7 @@ var require_websocket = __commonJS({
|
|
|
52175
52408
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
52176
52409
|
return;
|
|
52177
52410
|
}
|
|
52178
|
-
const digest =
|
|
52411
|
+
const digest = createHash39("sha1").update(key + GUID).digest("base64");
|
|
52179
52412
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
52180
52413
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
52181
52414
|
return;
|
|
@@ -52544,7 +52777,7 @@ var require_websocket_server = __commonJS({
|
|
|
52544
52777
|
var EventEmitter = require("events");
|
|
52545
52778
|
var http2 = require("http");
|
|
52546
52779
|
var { Duplex } = require("stream");
|
|
52547
|
-
var { createHash:
|
|
52780
|
+
var { createHash: createHash39 } = require("crypto");
|
|
52548
52781
|
var extension3 = require_extension();
|
|
52549
52782
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
52550
52783
|
var subprotocol2 = require_subprotocol();
|
|
@@ -52851,7 +53084,7 @@ var require_websocket_server = __commonJS({
|
|
|
52851
53084
|
);
|
|
52852
53085
|
}
|
|
52853
53086
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
52854
|
-
const digest =
|
|
53087
|
+
const digest = createHash39("sha1").update(key + GUID).digest("base64");
|
|
52855
53088
|
const headers = [
|
|
52856
53089
|
"HTTP/1.1 101 Switching Protocols",
|
|
52857
53090
|
"Upgrade: websocket",
|
|
@@ -160057,7 +160290,8 @@ var init_live_chat = __esm({
|
|
|
160057
160290
|
v3Seq: 0,
|
|
160058
160291
|
v3Buffer: [],
|
|
160059
160292
|
v3Subscribers: prev?.recovering ? prev.v3Subscribers : /* @__PURE__ */ new Set(),
|
|
160060
|
-
v3Items: /* @__PURE__ */ new Map()
|
|
160293
|
+
v3Items: /* @__PURE__ */ new Map(),
|
|
160294
|
+
inputReceipts: /* @__PURE__ */ new Map()
|
|
160061
160295
|
};
|
|
160062
160296
|
this.turns.set(chatSessionId, turn);
|
|
160063
160297
|
this.flushPendingUserItems(chatSessionId);
|
|
@@ -160111,6 +160345,8 @@ var init_live_chat = __esm({
|
|
|
160111
160345
|
};
|
|
160112
160346
|
},
|
|
160113
160347
|
finish: (status) => {
|
|
160348
|
+
for (const receipt of turn.inputReceipts.values()) receipt.abandon();
|
|
160349
|
+
turn.inputReceipts.clear();
|
|
160114
160350
|
try {
|
|
160115
160351
|
turn.items?.finish(status === "error" ? "failed" : "completed");
|
|
160116
160352
|
} catch {
|
|
@@ -160172,6 +160408,11 @@ var init_live_chat = __esm({
|
|
|
160172
160408
|
* 账本坏掉不影响对话;v3 emit 抛异常也不影响 v2 通路与账本落盘——两条独立通路。
|
|
160173
160409
|
*/
|
|
160174
160410
|
applyNormalizedEvent(turn, event) {
|
|
160411
|
+
if (event.type === "item" && event.kind === "control" && event.attrs?.code === "input_consumed") {
|
|
160412
|
+
const key = event.attrs.clientMessageKey;
|
|
160413
|
+
if (key) turn.inputReceipts.get(key)?.consume();
|
|
160414
|
+
return;
|
|
160415
|
+
}
|
|
160175
160416
|
let outcome = null;
|
|
160176
160417
|
try {
|
|
160177
160418
|
outcome = turn.items?.apply(event) ?? null;
|
|
@@ -160220,6 +160461,7 @@ var init_live_chat = __esm({
|
|
|
160220
160461
|
operation: "started",
|
|
160221
160462
|
itemVersion: outcome.startedVersion,
|
|
160222
160463
|
...ord !== void 0 ? { ord } : {},
|
|
160464
|
+
...outcome.messageId !== null ? { messageId: outcome.messageId } : {},
|
|
160223
160465
|
offset: 0,
|
|
160224
160466
|
payload: v3StartedPayload(event.role, event.attrs, itemType, event.kind)
|
|
160225
160467
|
};
|
|
@@ -160253,6 +160495,11 @@ var init_live_chat = __esm({
|
|
|
160253
160495
|
// 账本这一次写用掉的那一格钟——**库里那一行的 version 就是它**,快照与增量流同一套版本空间。
|
|
160254
160496
|
itemVersion: outcome.version,
|
|
160255
160497
|
...ord !== void 0 ? { ord } : {},
|
|
160498
|
+
/* 归属:账本写进 `chat_items.message_id` 的那个值,原样带出去(2026-09-11)。
|
|
160499
|
+
此前只有快照带它,于是「面板挂载之后才生出来的 item」在客户端永远没有归属——
|
|
160500
|
+
右侧子会话面板靠它把 DB 详情与实时桶合成一条,对不上号就把同一轮画两遍。
|
|
160501
|
+
`null` 不上 wire(省字节):客户端合并层本就按「帧不带 ⇒ 保留已有」处理。 */
|
|
160502
|
+
...outcome.messageId !== null ? { messageId: outcome.messageId } : {},
|
|
160256
160503
|
...offset !== void 0 ? { offset } : {},
|
|
160257
160504
|
...payload !== void 0 ? { payload } : {}
|
|
160258
160505
|
};
|
|
@@ -160695,7 +160942,7 @@ var init_live_chat = __esm({
|
|
|
160695
160942
|
if (turn.recovering) return say({ accepted: false, reason: "recovering" }, { recovering: true });
|
|
160696
160943
|
const fn = turn.handle.appendInput;
|
|
160697
160944
|
if (typeof fn !== "function") return say({ accepted: false, reason: "unsupported" });
|
|
160698
|
-
const pendingItemId = !opts?.system && opts?.clientMessageKey ? `oasis-user:${opts.clientMessageKey}` : void 0;
|
|
160945
|
+
const pendingItemId = !opts?.requireConsumptionReceipt && !opts?.system && opts?.clientMessageKey ? `oasis-user:${opts.clientMessageKey}` : void 0;
|
|
160699
160946
|
const pendingAttachments = opts?.attachments?.map((a) => ({
|
|
160700
160947
|
name: a.name,
|
|
160701
160948
|
...a.blobRef ? { blobRef: a.blobRef } : {},
|
|
@@ -160726,17 +160973,75 @@ var init_live_chat = __esm({
|
|
|
160726
160973
|
payload: { role: "user" }
|
|
160727
160974
|
});
|
|
160728
160975
|
};
|
|
160976
|
+
let confirmed2 = false;
|
|
160977
|
+
let consumedItemId;
|
|
160978
|
+
let receiptPromise;
|
|
160979
|
+
const receiptKey = opts?.requireConsumptionReceipt && !opts.system ? opts.clientMessageKey : void 0;
|
|
160980
|
+
if (opts?.requireConsumptionReceipt && !opts.system && !receiptKey) {
|
|
160981
|
+
return say({ accepted: false, reason: "consumption-unsupported" });
|
|
160982
|
+
}
|
|
160983
|
+
if (receiptKey) {
|
|
160984
|
+
const existing = turn.inputReceipts.get(receiptKey);
|
|
160985
|
+
if (existing) {
|
|
160986
|
+
const consumed = await existing.result;
|
|
160987
|
+
await turn.items?.drain();
|
|
160988
|
+
return consumed && existing.itemId && turn.items?.ordFor(existing.itemId) !== void 0 ? { accepted: true } : { accepted: false, reason: "unconfirmed" };
|
|
160989
|
+
}
|
|
160990
|
+
let resolveReceipt;
|
|
160991
|
+
receiptPromise = new Promise((resolve10) => {
|
|
160992
|
+
resolveReceipt = resolve10;
|
|
160993
|
+
});
|
|
160994
|
+
turn.inputReceipts.set(receiptKey, {
|
|
160995
|
+
get itemId() {
|
|
160996
|
+
return consumedItemId;
|
|
160997
|
+
},
|
|
160998
|
+
result: receiptPromise,
|
|
160999
|
+
consume: () => {
|
|
161000
|
+
if (confirmed2 || turn.status !== "running") return;
|
|
161001
|
+
confirmed2 = true;
|
|
161002
|
+
consumedItemId = this.commitUserInput(turn, text5, opts);
|
|
161003
|
+
resolveReceipt(true);
|
|
161004
|
+
},
|
|
161005
|
+
abandon: () => {
|
|
161006
|
+
resolveReceipt(false);
|
|
161007
|
+
}
|
|
161008
|
+
});
|
|
161009
|
+
}
|
|
161010
|
+
const clearReceipt = () => {
|
|
161011
|
+
if (!receiptKey) return;
|
|
161012
|
+
turn.inputReceipts.get(receiptKey)?.abandon();
|
|
161013
|
+
turn.inputReceipts.delete(receiptKey);
|
|
161014
|
+
};
|
|
160729
161015
|
let res;
|
|
160730
161016
|
try {
|
|
160731
161017
|
res = await fn.call(turn.handle, {
|
|
160732
161018
|
text: text5,
|
|
161019
|
+
...receiptKey ? { requireConsumptionReceipt: true } : {},
|
|
160733
161020
|
...opts?.clientMessageKey ? { clientMessageKey: opts.clientMessageKey } : {},
|
|
160734
161021
|
...opts?.files && Object.keys(opts.files).length ? { files: opts.files } : {},
|
|
160735
161022
|
...opts?.binaryFiles && Object.keys(opts.binaryFiles).length ? { binaryFiles: opts.binaryFiles } : {}
|
|
160736
161023
|
});
|
|
160737
161024
|
} catch (e) {
|
|
161025
|
+
if (!receiptKey) clearReceipt();
|
|
160738
161026
|
failPending();
|
|
160739
|
-
|
|
161027
|
+
if (confirmed2) await turn.items?.drain();
|
|
161028
|
+
confirmed2 = confirmed2 && !!consumedItemId && turn.items?.ordFor(consumedItemId) !== void 0;
|
|
161029
|
+
return say({ accepted: confirmed2, ...!confirmed2 ? { reason: receiptKey ? "unconfirmed" : "rejected" } : {} }, { err: String(e) });
|
|
161030
|
+
}
|
|
161031
|
+
if (receiptPromise) {
|
|
161032
|
+
const notSent = ["unsupported", "consumption-unsupported", "session-closing", "no-live-turn", "recovering"];
|
|
161033
|
+
if (!confirmed2 && !res.accepted && notSent.includes(res.reason ?? "")) {
|
|
161034
|
+
clearReceipt();
|
|
161035
|
+
failPending();
|
|
161036
|
+
return say(res);
|
|
161037
|
+
}
|
|
161038
|
+
if (!await receiptPromise) {
|
|
161039
|
+
clearReceipt();
|
|
161040
|
+
failPending();
|
|
161041
|
+
return say({ accepted: false, reason: "unconfirmed" });
|
|
161042
|
+
}
|
|
161043
|
+
await turn.items?.drain();
|
|
161044
|
+
return say(!consumedItemId || turn.items?.ordFor(consumedItemId) === void 0 ? { accepted: false, reason: "unconfirmed" } : { accepted: true });
|
|
160740
161045
|
}
|
|
160741
161046
|
if (!res.accepted) {
|
|
160742
161047
|
failPending();
|
|
@@ -160747,6 +161052,10 @@ var init_live_chat = __esm({
|
|
|
160747
161052
|
failPending();
|
|
160748
161053
|
return say({ accepted: false, reason: "turn-finished" }, { turnStatus: turn.status, replaced: this.turns.get(chatSessionId) !== turn });
|
|
160749
161054
|
}
|
|
161055
|
+
this.commitUserInput(turn, text5, opts);
|
|
161056
|
+
return { accepted: true };
|
|
161057
|
+
}
|
|
161058
|
+
commitUserInput(turn, text5, opts) {
|
|
160750
161059
|
const shown = opts?.displayText ?? text5;
|
|
160751
161060
|
const userPart = {
|
|
160752
161061
|
type: "user",
|
|
@@ -160755,7 +161064,7 @@ var init_live_chat = __esm({
|
|
|
160755
161064
|
};
|
|
160756
161065
|
const segmentEnd = (() => {
|
|
160757
161066
|
try {
|
|
160758
|
-
return turn.items?.closeSegmentBeforeUserInput() ?? null;
|
|
161067
|
+
return turn.items?.closeSegmentBeforeUserInput(!!opts?.requireConsumptionReceipt && !!turn.onUserInput) ?? null;
|
|
160759
161068
|
} catch {
|
|
160760
161069
|
return null;
|
|
160761
161070
|
}
|
|
@@ -160771,8 +161080,9 @@ var init_live_chat = __esm({
|
|
|
160771
161080
|
payload: { code: "segment_end", note: SEGMENT_END_NOTE }
|
|
160772
161081
|
});
|
|
160773
161082
|
}
|
|
161083
|
+
let userItemId;
|
|
160774
161084
|
try {
|
|
160775
|
-
turn.items?.recordUserInput(shown, opts?.attachments, opts?.clientKeys);
|
|
161085
|
+
userItemId = turn.items?.recordUserInput(shown, opts?.attachments, opts?.clientKeys);
|
|
160776
161086
|
} catch {
|
|
160777
161087
|
}
|
|
160778
161088
|
const withSeq = { ...userPart, seq: ++turn.seq };
|
|
@@ -160806,7 +161116,7 @@ var init_live_chat = __esm({
|
|
|
160806
161116
|
turn.onUserInput?.(shown, opts?.attachments);
|
|
160807
161117
|
} catch {
|
|
160808
161118
|
}
|
|
160809
|
-
return
|
|
161119
|
+
return userItemId;
|
|
160810
161120
|
}
|
|
160811
161121
|
/** 反查该会话当前驻留那一轮的 runId(无进行中/grace 轮,或该轮无 runId 时 undefined)。
|
|
160812
161122
|
* 建单草案 submit 发生在这一轮内:用它给草案盖上「产出轮次」标记,供 friday 重进对话页把每版
|
|
@@ -160952,18 +161262,32 @@ var init_chat_attachment_files = __esm({
|
|
|
160952
161262
|
});
|
|
160953
161263
|
|
|
160954
161264
|
// ../server/src/domains/chat-sessions/append-now.ts
|
|
160955
|
-
async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, attachments) {
|
|
161265
|
+
async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, attachments, retainLocallyOnFailure = false) {
|
|
160956
161266
|
const newId = deps.newId ?? import_node_crypto20.randomUUID;
|
|
160957
161267
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
160958
|
-
|
|
160959
|
-
|
|
161268
|
+
if (!deps.store.claimPendingInsert) {
|
|
161269
|
+
return { accepted: false, reason: "queue-unavailable", queued: false, delivered: "", batch: [] };
|
|
161270
|
+
}
|
|
161271
|
+
const { submitted, batch } = await deps.store.claimPendingInsert({
|
|
161272
|
+
id: clientKey ? `append-request:${(0, import_node_crypto20.createHash)("sha256").update(JSON.stringify([chatSessionId, clientKey])).digest("hex")}` : newId(),
|
|
160960
161273
|
chatSessionId,
|
|
160961
161274
|
text: text5,
|
|
160962
161275
|
...attachments?.length ? { attachments } : {},
|
|
160963
161276
|
...clientKey ? { clientKey } : {},
|
|
160964
161277
|
createdAt: now()
|
|
160965
161278
|
});
|
|
160966
|
-
|
|
161279
|
+
if (!batch.length) {
|
|
161280
|
+
const state = submitted.state ?? "queued";
|
|
161281
|
+
const returned = state === "cancelled" && submitted.stateReason?.startsWith("client-retained:");
|
|
161282
|
+
const reason = returned ? submitted.stateReason.slice("client-retained:".length) : "unconfirmed";
|
|
161283
|
+
return {
|
|
161284
|
+
accepted: state === "delivered",
|
|
161285
|
+
...state !== "delivered" ? { reason } : {},
|
|
161286
|
+
queued: !returned,
|
|
161287
|
+
delivered: "",
|
|
161288
|
+
batch: [submitted]
|
|
161289
|
+
};
|
|
161290
|
+
}
|
|
160967
161291
|
const prepared = await buildChatAttachmentFiles(
|
|
160968
161292
|
batch.flatMap((m2) => m2.attachments ?? []),
|
|
160969
161293
|
deps.readBlob ?? (async (ref2) => {
|
|
@@ -160979,7 +161303,6 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
160979
161303
|
})));
|
|
160980
161304
|
const batchKey = `append:${chatSessionId}:${batch.map((m2) => m2.id).join(",")}`;
|
|
160981
161305
|
const ids2 = batch.map((m2) => m2.id);
|
|
160982
|
-
await deps.store.markPendingMessages?.(chatSessionId, ids2, "delivering").catch(() => void 0);
|
|
160983
161306
|
const clientKeys = batch.map((m2) => m2.clientKey).filter((k2) => !!k2);
|
|
160984
161307
|
const res = await deps.deliver(merged, batchKey, {
|
|
160985
161308
|
...clientKeys.length ? { clientKeys } : {},
|
|
@@ -160988,10 +161311,19 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
160988
161311
|
...displayText !== merged ? { displayText } : {},
|
|
160989
161312
|
...attachmentRefs.length ? { attachments: attachmentRefs } : {}
|
|
160990
161313
|
});
|
|
160991
|
-
const DEFINITELY_NOT_DELIVERED = /* @__PURE__ */ new Set(["unsupported", "no-live-turn", "session-closing", "turn-finished", "recovering"]);
|
|
161314
|
+
const DEFINITELY_NOT_DELIVERED = /* @__PURE__ */ new Set(["unsupported", "no-live-turn", "session-closing", "turn-finished", "recovering", "consumption-unsupported"]);
|
|
160992
161315
|
const nextState = res.accepted ? "delivered" : DEFINITELY_NOT_DELIVERED.has(res.reason ?? "") ? "queued" : "unconfirmed";
|
|
160993
|
-
|
|
160994
|
-
|
|
161316
|
+
const returnToClient = retainLocallyOnFailure && !!clientKey && nextState === "queued";
|
|
161317
|
+
if (returnToClient && deps.store.markPendingMessages) {
|
|
161318
|
+
await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled", `client-retained:${res.reason}`);
|
|
161319
|
+
await deps.store.markPendingMessages(chatSessionId, ids2.filter((id) => id !== submitted.id), nextState, res.reason);
|
|
161320
|
+
} else {
|
|
161321
|
+
const transition = deps.store.markPendingMessages?.(chatSessionId, ids2, nextState, res.reason);
|
|
161322
|
+
if (retainLocallyOnFailure) await transition;
|
|
161323
|
+
else await transition?.catch(() => void 0);
|
|
161324
|
+
if (returnToClient) await deps.store.removePendingMessage(chatSessionId, submitted.id);
|
|
161325
|
+
}
|
|
161326
|
+
return { ...res, queued: !returnToClient, delivered: merged, batch, ...prepared.paths.length ? { attachmentPaths: prepared.paths } : {} };
|
|
160995
161327
|
}
|
|
160996
161328
|
var import_node_crypto20;
|
|
160997
161329
|
var init_append_now = __esm({
|
|
@@ -195639,7 +195971,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195639
195971
|
out.listDirCode = located.code;
|
|
195640
195972
|
if (located.location) out.location = located.location;
|
|
195641
195973
|
out.skipped.push({
|
|
195642
|
-
source: `${
|
|
195974
|
+
source: `${SESSION_OUTPUTS_DIR}/`,
|
|
195643
195975
|
code: located.code,
|
|
195644
195976
|
message: located.message ?? locationFailureMessage(located.code)
|
|
195645
195977
|
});
|
|
@@ -195647,7 +195979,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195647
195979
|
}
|
|
195648
195980
|
out.location = located.location;
|
|
195649
195981
|
}
|
|
195650
|
-
const listing = await port.listDir(child,
|
|
195982
|
+
const listing = await port.listDir(child, SESSION_OUTPUTS_DIR).catch((err) => ({
|
|
195651
195983
|
ok: false,
|
|
195652
195984
|
code: "INTERNAL",
|
|
195653
195985
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -195657,11 +195989,11 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195657
195989
|
if (listing.code === "NOT_FOUND") return out;
|
|
195658
195990
|
const code2 = listing.code;
|
|
195659
195991
|
const message = isLocationFailureCode(code2) ? listing.message ?? locationFailureMessage(code2) : listing.message ?? code2;
|
|
195660
|
-
out.skipped.push({ source: `${
|
|
195992
|
+
out.skipped.push({ source: `${SESSION_OUTPUTS_DIR}/`, code: code2, message });
|
|
195661
195993
|
return out;
|
|
195662
195994
|
}
|
|
195663
195995
|
out.listDirCode = "OK";
|
|
195664
|
-
const queue = [
|
|
195996
|
+
const queue = [SESSION_OUTPUTS_DIR];
|
|
195665
195997
|
const files = [];
|
|
195666
195998
|
let guard = 0;
|
|
195667
195999
|
let hitFileCap = false;
|
|
@@ -195672,7 +196004,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195672
196004
|
}
|
|
195673
196005
|
guard += 1;
|
|
195674
196006
|
const dir = queue.shift();
|
|
195675
|
-
const level = dir ===
|
|
196007
|
+
const level = dir === SESSION_OUTPUTS_DIR ? listing : await port.listDir(child, dir).catch(() => ({ ok: false, code: "INTERNAL" }));
|
|
195676
196008
|
if (!level.ok) {
|
|
195677
196009
|
out.skipped.push({ source: `${dir}/`, code: level.code, message: level.code });
|
|
195678
196010
|
continue;
|
|
@@ -195698,7 +196030,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195698
196030
|
});
|
|
195699
196031
|
}
|
|
195700
196032
|
for (const file of files) {
|
|
195701
|
-
const relative5 = normalizeRelPath(file.rel.slice(
|
|
196033
|
+
const relative5 = normalizeRelPath(file.rel.slice(SESSION_OUTPUTS_DIR.length + 1));
|
|
195702
196034
|
if (!relative5) {
|
|
195703
196035
|
out.skipped.push({ source: file.rel, code: "PATH_REJECTED", message: "\u8DEF\u5F84\u4E0D\u5408\u6CD5\uFF0C\u6CA1\u642C" });
|
|
195704
196036
|
continue;
|
|
@@ -203939,7 +204271,7 @@ async function startOasisServer(opts) {
|
|
|
203939
204271
|
const leftWaiterIds = /* @__PURE__ */ new Map();
|
|
203940
204272
|
const LEFT_WAITER_IDS_MAX_DRAFTS = 512;
|
|
203941
204273
|
const terminalReviews = /* @__PURE__ */ new Map();
|
|
203942
|
-
const notifyReviewResolved = (draftId, r, resume, storeFor) => {
|
|
204274
|
+
const notifyReviewResolved = (draftId, r, resume, storeFor, companyId) => {
|
|
203943
204275
|
if (terminalReviews.has(draftId)) return;
|
|
203944
204276
|
terminalReviews.set(draftId, r);
|
|
203945
204277
|
if (terminalReviews.size > 512) {
|
|
@@ -203959,7 +204291,7 @@ async function startOasisServer(opts) {
|
|
|
203959
204291
|
leftWaiterIds.delete(draftId);
|
|
203960
204292
|
if (resume?.origin) {
|
|
203961
204293
|
const needsAgentDelivery = resume.kind === "command" && !hadWaiters;
|
|
203962
|
-
void settleAuthorizationStatusLine(resume.origin, r, resume, draftId, needsAgentDelivery, storeFor);
|
|
204294
|
+
void settleAuthorizationStatusLine(resume.origin, r, resume, draftId, needsAgentDelivery, storeFor, companyId);
|
|
203963
204295
|
}
|
|
203964
204296
|
};
|
|
203965
204297
|
const resolveEngine = opts.resolveEngine ?? (async () => ({ kernel: opts.kernel, oplog: opts.oplog, blobs: opts.blobs, ...opts.registry ? { registry: opts.registry } : {} }));
|
|
@@ -204037,6 +204369,14 @@ async function startOasisServer(opts) {
|
|
|
204037
204369
|
}
|
|
204038
204370
|
return null;
|
|
204039
204371
|
};
|
|
204372
|
+
const outputsPartFor = (chatSessionId, companyId) => {
|
|
204373
|
+
const scan2 = opts.sessionOutputsScan;
|
|
204374
|
+
if (!scan2) return void 0;
|
|
204375
|
+
return async () => {
|
|
204376
|
+
const files = await scan2({ chatSessionId, ...companyId !== void 0 ? { companyId } : {} });
|
|
204377
|
+
return files && files.length > 0 ? { type: "outputs", seq: 0, outputs: files } : null;
|
|
204378
|
+
};
|
|
204379
|
+
};
|
|
204040
204380
|
async function anchorMessageIdFor(origin, storeFor) {
|
|
204041
204381
|
if (!origin) return void 0;
|
|
204042
204382
|
const live = liveChat.messageFor(origin);
|
|
@@ -204053,7 +204393,7 @@ async function startOasisServer(opts) {
|
|
|
204053
204393
|
}
|
|
204054
204394
|
return void 0;
|
|
204055
204395
|
}
|
|
204056
|
-
const appendNowThroughQueue2 = async (sid, text5, clientKey, attachments, perCompanyStore) => {
|
|
204396
|
+
const appendNowThroughQueue2 = async (sid, text5, clientKey, attachments, perCompanyStore, retainLocallyOnFailure = false) => {
|
|
204057
204397
|
if (perCompanyStore === null) {
|
|
204058
204398
|
throw new Error(
|
|
204059
204399
|
`appendNowThroughQueue: per-company chat store unresolved (sid=${sid})`
|
|
@@ -204067,7 +204407,7 @@ async function startOasisServer(opts) {
|
|
|
204067
204407
|
store,
|
|
204068
204408
|
// ADR-0166 D9.1:附件与正文**同一次**送出——拆开就等于让 agent 拿着「见附件」而
|
|
204069
204409
|
// 附件不存在的描述去干活。`files` 空时这次调用与从前逐字相同。
|
|
204070
|
-
deliver: (merged, clientMessageKey, extra) => liveChat.append(sid, merged, { clientMessageKey, ...extra }),
|
|
204410
|
+
deliver: (merged, clientMessageKey, extra) => liveChat.append(sid, merged, { clientMessageKey, ...extra, requireConsumptionReceipt: true }),
|
|
204071
204411
|
// **先查资产柜再查事实柜**——chat 附件是 `uploadChatAttachment` 存进**资产柜**的
|
|
204072
204412
|
// (serve 那侧 `assets.put`),两个柜是**物理隔离**的(「资产柜与事实柜分开」)。
|
|
204073
204413
|
// 只查 `opts.blobs` 必然取不到,于是每张图都被报成「附件没能取回来」。
|
|
@@ -204082,8 +204422,8 @@ async function startOasisServer(opts) {
|
|
|
204082
204422
|
}
|
|
204083
204423
|
throw new Error(`blob ${blobRef} \u4E24\u4E2A\u67DC\u91CC\u90FD\u6CA1\u6709`);
|
|
204084
204424
|
}
|
|
204085
|
-
}, clientKey, attachments);
|
|
204086
|
-
return { accepted: r.accepted, ...r.reason ? { reason: r.reason } : {}, queued:
|
|
204425
|
+
}, clientKey, attachments, retainLocallyOnFailure);
|
|
204426
|
+
return { accepted: r.accepted, ...r.reason ? { reason: r.reason } : {}, queued: r.queued };
|
|
204087
204427
|
};
|
|
204088
204428
|
const GRAPH_EDIT_SUMMARY_MAX = 60;
|
|
204089
204429
|
const truncateSummary = (s2) => {
|
|
@@ -204101,7 +204441,7 @@ async function startOasisServer(opts) {
|
|
|
204101
204441
|
const label = ctx.effectLabel || ctx.command || "";
|
|
204102
204442
|
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`;
|
|
204103
204443
|
};
|
|
204104
|
-
const settleAuthorizationStatusLine = async (origin, resolution, ctx, draftId, needsAgentDelivery, storeFor) => {
|
|
204444
|
+
const settleAuthorizationStatusLine = async (origin, resolution, ctx, draftId, needsAgentDelivery, storeFor, companyId) => {
|
|
204105
204445
|
const store = storeFor ? await storeFor().catch(() => null) : opts.chatSession;
|
|
204106
204446
|
if (!store) return;
|
|
204107
204447
|
const session = await store.getSession(origin).catch(() => null);
|
|
@@ -204174,10 +204514,12 @@ async function startOasisServer(opts) {
|
|
|
204174
204514
|
...dispatched.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: dispatched.supportsLiveProtocol } : {},
|
|
204175
204515
|
done: dispatched.done
|
|
204176
204516
|
};
|
|
204517
|
+
const resumeOutputsPart = outputsPartFor(origin, companyId);
|
|
204177
204518
|
runBackgroundAssistantChatTurn({
|
|
204178
204519
|
chatStore: store,
|
|
204179
204520
|
chatSessionId: origin,
|
|
204180
|
-
session: bgSession
|
|
204521
|
+
session: bgSession,
|
|
204522
|
+
...resumeOutputsPart ? { outputsPart: resumeOutputsPart } : {}
|
|
204181
204523
|
});
|
|
204182
204524
|
await markDelivered();
|
|
204183
204525
|
};
|
|
@@ -204433,10 +204775,12 @@ async function startOasisServer(opts) {
|
|
|
204433
204775
|
...session.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: session.supportsLiveProtocol } : {},
|
|
204434
204776
|
...session.done ? { done: session.done } : {}
|
|
204435
204777
|
};
|
|
204778
|
+
const broadcastOutputsPart = outputsPartFor(sessionId, companyId);
|
|
204436
204779
|
runBackgroundAssistantChatTurn({
|
|
204437
204780
|
chatStore: store,
|
|
204438
204781
|
chatSessionId: sessionId,
|
|
204439
204782
|
session: bgSession,
|
|
204783
|
+
...broadcastOutputsPart ? { outputsPart: broadcastOutputsPart } : {},
|
|
204440
204784
|
liveChat,
|
|
204441
204785
|
liveHandle: {
|
|
204442
204786
|
runtimeSessionId: session.id,
|
|
@@ -205414,7 +205758,8 @@ async function startOasisServer(opts) {
|
|
|
205414
205758
|
draftId,
|
|
205415
205759
|
{ status: "approved", command, effectLabel: review.command.effectLabel, authorizedBy: actor, receipt },
|
|
205416
205760
|
{ origin: review.origin, kind: "command", authorizedBy: actor, effectLabel: review.command.effectLabel, command },
|
|
205417
|
-
chatStoreNowOrNull
|
|
205761
|
+
chatStoreNowOrNull,
|
|
205762
|
+
currentCompanyId
|
|
205418
205763
|
);
|
|
205419
205764
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
205420
205765
|
JSON.stringify({ applied: 1, command, artifactId, draftId, draftedBy: review.proposedBy, authorizedBy: actor })
|
|
@@ -206240,9 +206585,11 @@ async function startOasisServer(opts) {
|
|
|
206240
206585
|
let text5 = "";
|
|
206241
206586
|
let clientKey;
|
|
206242
206587
|
let insertAttachments = [];
|
|
206588
|
+
let retainLocallyOnFailure = false;
|
|
206243
206589
|
try {
|
|
206244
206590
|
const b2 = JSON.parse(rawBody);
|
|
206245
206591
|
text5 = String(b2.text ?? "").trim();
|
|
206592
|
+
retainLocallyOnFailure = b2.retainLocallyOnFailure === true;
|
|
206246
206593
|
if (typeof b2.clientKey === "string" && b2.clientKey) clientKey = b2.clientKey;
|
|
206247
206594
|
insertAttachments = (Array.isArray(b2.attachments) ? b2.attachments : []).filter((a) => !!a && typeof a === "object" && typeof a["name"] === "string" && (typeof a["text"] === "string" || typeof a["blobRef"] === "string")).map((a) => ({
|
|
206248
206595
|
name: a["name"],
|
|
@@ -206256,7 +206603,7 @@ async function startOasisServer(opts) {
|
|
|
206256
206603
|
res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "EMPTY_TEXT" }));
|
|
206257
206604
|
return;
|
|
206258
206605
|
}
|
|
206259
|
-
const result = await appendNowThroughQueue2(sid, text5, clientKey, insertAttachments, perCompanyStore);
|
|
206606
|
+
const result = await appendNowThroughQueue2(sid, text5, clientKey, insertAttachments, perCompanyStore, retainLocallyOnFailure);
|
|
206260
206607
|
res.writeHead(result.accepted ? 200 : 409, { "content-type": "application/json" }).end(JSON.stringify(result));
|
|
206261
206608
|
return;
|
|
206262
206609
|
}
|
|
@@ -206600,6 +206947,7 @@ ${composed}`;
|
|
|
206600
206947
|
await checkpointChain.catch(() => void 0);
|
|
206601
206948
|
await itemLedger.drain();
|
|
206602
206949
|
const projectedContent = itemLedger.projectContent();
|
|
206950
|
+
const chatOutputsPart = outputsPartFor(persistTarget.id, currentCompanyId);
|
|
206603
206951
|
await finalizeAssistantRow({
|
|
206604
206952
|
chatStore,
|
|
206605
206953
|
chatSessionId: persistTarget.id,
|
|
@@ -206609,6 +206957,7 @@ ${composed}`;
|
|
|
206609
206957
|
status,
|
|
206610
206958
|
...session.runId ? { runId: session.runId } : {},
|
|
206611
206959
|
...heldTurn ? { turnId: heldTurn.id } : {},
|
|
206960
|
+
...chatOutputsPart ? { outputsPart: chatOutputsPart } : {},
|
|
206612
206961
|
now: () => finishedAt
|
|
206613
206962
|
});
|
|
206614
206963
|
await heldTurn?.settle({
|
|
@@ -213608,6 +213957,17 @@ var init_dev_store = __esm({
|
|
|
213608
213957
|
/** ADR-0166 D6 待发队列(每会话一条,按 seq 升序 = 入队序)。 */
|
|
213609
213958
|
pending = /* @__PURE__ */ new Map();
|
|
213610
213959
|
pendingSeq = 0;
|
|
213960
|
+
async claimPendingInsert(m2) {
|
|
213961
|
+
const list2 = this.pending.get(m2.chatSessionId) ?? [];
|
|
213962
|
+
const prior = list2.find((x2) => x2.id === m2.id) ?? list2.find((x2) => m2.clientKey && x2.clientKey === m2.clientKey && (x2.state ?? "queued") === "queued");
|
|
213963
|
+
if (prior) return { submitted: prior, batch: [] };
|
|
213964
|
+
const submitted = { ...m2, state: "queued", stateAt: m2.createdAt, seq: ++this.pendingSeq };
|
|
213965
|
+
const all2 = [...list2, submitted];
|
|
213966
|
+
const batch = all2.filter((x2) => (x2.state ?? "queued") === "queued");
|
|
213967
|
+
this.pending.set(m2.chatSessionId, all2);
|
|
213968
|
+
this.setState(m2.chatSessionId, new Set(batch.map((x2) => x2.id)), "delivering", "append-now");
|
|
213969
|
+
return { submitted, batch };
|
|
213970
|
+
}
|
|
213611
213971
|
async enqueuePendingMessage(m2) {
|
|
213612
213972
|
const list2 = this.pending.get(m2.chatSessionId) ?? [];
|
|
213613
213973
|
if (m2.clientKey) {
|
|
@@ -213975,6 +214335,11 @@ var init_dev_store = __esm({
|
|
|
213975
214335
|
return r;
|
|
213976
214336
|
}
|
|
213977
214337
|
// 待发队列也要落盘——它存在的全部理由就是"别只活在一处内存里"(ADR-0166 D6)。
|
|
214338
|
+
async claimPendingInsert(m2) {
|
|
214339
|
+
const result = await super.claimPendingInsert(m2);
|
|
214340
|
+
this.save();
|
|
214341
|
+
return result;
|
|
214342
|
+
}
|
|
213978
214343
|
async enqueuePendingMessage(m2) {
|
|
213979
214344
|
const record8 = await super.enqueuePendingMessage(m2);
|
|
213980
214345
|
this.save();
|
|
@@ -214822,6 +215187,74 @@ var init_company_scoped_registry = __esm({
|
|
|
214822
215187
|
}
|
|
214823
215188
|
});
|
|
214824
215189
|
|
|
215190
|
+
// ../server/src/domains/chat-sessions/outputs-scan.ts
|
|
215191
|
+
function joinRel(dir, name) {
|
|
215192
|
+
return dir ? `${dir}/${name}` : name;
|
|
215193
|
+
}
|
|
215194
|
+
async function scanSessionOutputs(bridge, loc, now = Date.now) {
|
|
215195
|
+
const deadline = now() + BUDGET_MS;
|
|
215196
|
+
const out = [];
|
|
215197
|
+
let listCalls = 0;
|
|
215198
|
+
const listDir = (path41) => {
|
|
215199
|
+
listCalls += 1;
|
|
215200
|
+
return bridge.list(loc.nodeId, {
|
|
215201
|
+
workdirKey: loc.workdirKey,
|
|
215202
|
+
runtimeKind: loc.runtimeKind,
|
|
215203
|
+
rtId: loc.rtId,
|
|
215204
|
+
path: path41
|
|
215205
|
+
});
|
|
215206
|
+
};
|
|
215207
|
+
const root = await listDir(SESSION_OUTPUTS_DIR).catch(() => null);
|
|
215208
|
+
if (!root) return null;
|
|
215209
|
+
if (!root.ok) {
|
|
215210
|
+
return root.code === "NOT_FOUND" ? [] : null;
|
|
215211
|
+
}
|
|
215212
|
+
let level = [{ path: SESSION_OUTPUTS_DIR, outcome: root }];
|
|
215213
|
+
for (let depth = 0; depth <= MAX_DEPTH; depth += 1) {
|
|
215214
|
+
const nextDirs = [];
|
|
215215
|
+
for (const { path: dir, outcome } of level) {
|
|
215216
|
+
if (!outcome.ok) continue;
|
|
215217
|
+
for (const entry of outcome.entries) {
|
|
215218
|
+
if (entry.type === "dir") {
|
|
215219
|
+
nextDirs.push(joinRel(dir, entry.name));
|
|
215220
|
+
continue;
|
|
215221
|
+
}
|
|
215222
|
+
if (out.length >= MAX_ENTRIES) continue;
|
|
215223
|
+
out.push({ path: joinRel(dir, entry.name), size: entry.size, mtime: entry.mtime });
|
|
215224
|
+
}
|
|
215225
|
+
}
|
|
215226
|
+
if (nextDirs.length === 0 || depth === MAX_DEPTH) break;
|
|
215227
|
+
if (out.length >= MAX_ENTRIES || listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
|
|
215228
|
+
const batch = [];
|
|
215229
|
+
for (const dir of nextDirs) {
|
|
215230
|
+
if (listCalls >= MAX_LIST_CALLS || now() >= deadline) break;
|
|
215231
|
+
const outcome = await listDir(dir).catch(() => null);
|
|
215232
|
+
if (outcome) batch.push({ path: dir, outcome });
|
|
215233
|
+
}
|
|
215234
|
+
level = batch;
|
|
215235
|
+
}
|
|
215236
|
+
return out;
|
|
215237
|
+
}
|
|
215238
|
+
async function settleSessionOutputs(deps) {
|
|
215239
|
+
const scanned = await scanSessionOutputs(deps.bridge, deps.loc, deps.now);
|
|
215240
|
+
if (scanned === null) return null;
|
|
215241
|
+
const previous3 = await deps.readFingerprints(deps.sessionId).catch(() => null);
|
|
215242
|
+
const diff = diffSessionOutputs(scanned, previous3 ?? void 0);
|
|
215243
|
+
await deps.writeFingerprints(deps.sessionId, diff.fingerprints).catch(() => void 0);
|
|
215244
|
+
return diff;
|
|
215245
|
+
}
|
|
215246
|
+
var MAX_DEPTH, MAX_ENTRIES, MAX_LIST_CALLS, BUDGET_MS;
|
|
215247
|
+
var init_outputs_scan = __esm({
|
|
215248
|
+
"../server/src/domains/chat-sessions/outputs-scan.ts"() {
|
|
215249
|
+
"use strict";
|
|
215250
|
+
init_src();
|
|
215251
|
+
MAX_DEPTH = 3;
|
|
215252
|
+
MAX_ENTRIES = 100;
|
|
215253
|
+
MAX_LIST_CALLS = 12;
|
|
215254
|
+
BUDGET_MS = 5e3;
|
|
215255
|
+
}
|
|
215256
|
+
});
|
|
215257
|
+
|
|
214825
215258
|
// ../server/src/org-model-backfill.ts
|
|
214826
215259
|
function resolveDisplayName(existing, humanActor, accountName) {
|
|
214827
215260
|
const pick3 = existing?.trim() || humanActor?.name?.trim() || accountName?.trim();
|
|
@@ -218446,6 +218879,10 @@ var init_daemon_hub = __esm({
|
|
|
218446
218879
|
this.onHandshakeRejected?.(rejection);
|
|
218447
218880
|
cb(false, code2, message);
|
|
218448
218881
|
}
|
|
218882
|
+
/** Ask the running session: an upgrade leaves existing sessions alive. */
|
|
218883
|
+
supportsInputConsumptionReceipts(dispatchId) {
|
|
218884
|
+
return this.hasSession(dispatchId) && this.sessionMeta.get(dispatchId)?.supportsInputConsumptionReceipts === true;
|
|
218885
|
+
}
|
|
218449
218886
|
dispatch(daemonId, msg) {
|
|
218450
218887
|
const daemon = this.daemons.get(daemonId);
|
|
218451
218888
|
if (!daemon) return false;
|
|
@@ -218515,6 +218952,7 @@ var init_daemon_hub = __esm({
|
|
|
218515
218952
|
nodeId,
|
|
218516
218953
|
sessionId: msg.sessionId,
|
|
218517
218954
|
connectedAt: Date.now(),
|
|
218955
|
+
supportsInputConsumptionReceipts: msg.supportsInputConsumptionReceipts === true,
|
|
218518
218956
|
...msg.jobArtifactId ? { jobArtifactId: msg.jobArtifactId } : {}
|
|
218519
218957
|
});
|
|
218520
218958
|
for (const l of this.sessionConnectListeners) l({ dispatchId, sessionId: msg.sessionId, nodeId });
|
|
@@ -221241,11 +221679,11 @@ function aggregateUsageByActor(runs) {
|
|
|
221241
221679
|
function weekWindow(now = /* @__PURE__ */ new Date()) {
|
|
221242
221680
|
return { from: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString(), to: now.toISOString() };
|
|
221243
221681
|
}
|
|
221244
|
-
async function listWeeklyUsageByActor(trace) {
|
|
221682
|
+
async function listWeeklyUsageByActor(trace, artifactIds) {
|
|
221245
221683
|
if (!trace?.listUsageRuns) return null;
|
|
221246
221684
|
const { from, to } = weekWindow();
|
|
221247
221685
|
try {
|
|
221248
|
-
return aggregateUsageByActor(await trace.listUsageRuns({ from, to }));
|
|
221686
|
+
return aggregateUsageByActor(await trace.listUsageRuns({ from, to, ...artifactIds ? { artifactIds: [...artifactIds] } : {} }));
|
|
221249
221687
|
} catch {
|
|
221250
221688
|
return null;
|
|
221251
221689
|
}
|
|
@@ -221847,9 +222285,15 @@ function actorsDomain(opts) {
|
|
|
221847
222285
|
const revealLimiter = new RevealRateLimiter(revealLimits.perSession, revealLimits.perSessionPerKey);
|
|
221848
222286
|
const consoleRevealLimits = opts.credentialRevealLimits?.console ?? { perSession: 60, perSessionPerKey: 10 };
|
|
221849
222287
|
const consoleRevealLimiter = new RevealRateLimiter(consoleRevealLimits.perSession, consoleRevealLimits.perSessionPerKey);
|
|
222288
|
+
async function usageFor(companyId, ctx) {
|
|
222289
|
+
const source = opts.resolveTrace ? await opts.resolveTrace(companyId) : { store: opts.trace?.(), companyScoped: false };
|
|
222290
|
+
const artifactIds = companyId && !source?.companyScoped ? new Set(ctx.getModel?.().artifacts.keys() ?? []) : void 0;
|
|
222291
|
+
return { usage: source?.store, ...artifactIds ? { artifactIds } : {} };
|
|
222292
|
+
}
|
|
221850
222293
|
return (router) => {
|
|
221851
222294
|
router.get("/api/actors", async (req) => {
|
|
221852
|
-
const
|
|
222295
|
+
const ctx = await resolveCtx(req.auth.companyId);
|
|
222296
|
+
const { service } = ctx;
|
|
221853
222297
|
const filter = {};
|
|
221854
222298
|
const kind = req.query.get("kind");
|
|
221855
222299
|
const teamId = req.query.get("team_id");
|
|
@@ -221861,9 +222305,10 @@ function actorsDomain(opts) {
|
|
|
221861
222305
|
const includeDeleted = req.query.get("include_deleted") === "1";
|
|
221862
222306
|
const items = status || includeDeleted ? all2 : all2.filter((a) => a.kind !== "agent" || !isDeletedActor(a));
|
|
221863
222307
|
const agentIds = items.filter((a) => a.kind === "agent").map((a) => a.id);
|
|
222308
|
+
const usageSource = await usageFor(req.auth.companyId, ctx).catch(() => ({ usage: void 0, artifactIds: void 0 }));
|
|
221864
222309
|
const [configs, usageByActor] = await Promise.all([
|
|
221865
222310
|
service.latestConfigs(agentIds),
|
|
221866
|
-
listWeeklyUsageByActor(
|
|
222311
|
+
listWeeklyUsageByActor(usageSource.usage, usageSource.artifactIds)
|
|
221867
222312
|
]);
|
|
221868
222313
|
return {
|
|
221869
222314
|
status: 200,
|
|
@@ -222539,9 +222984,8 @@ function actorsDomain(opts) {
|
|
|
222539
222984
|
return {
|
|
222540
222985
|
status: 200,
|
|
222541
222986
|
body: await actorStats(req.params.id, {
|
|
222542
|
-
|
|
222543
|
-
model
|
|
222544
|
-
...req.auth.companyId ? { artifactIds: new Set(model?.artifacts.keys() ?? []) } : {}
|
|
222987
|
+
...await usageFor(req.auth.companyId, ctx),
|
|
222988
|
+
model
|
|
222545
222989
|
})
|
|
222546
222990
|
};
|
|
222547
222991
|
});
|
|
@@ -223125,7 +223569,7 @@ function createActorsDomain(opts) {
|
|
|
223125
223569
|
return {
|
|
223126
223570
|
service: defaultCtx.service,
|
|
223127
223571
|
resolveCtx,
|
|
223128
|
-
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
223572
|
+
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 } : {} })
|
|
223129
223573
|
};
|
|
223130
223574
|
}
|
|
223131
223575
|
var import_node_crypto49;
|
|
@@ -223300,6 +223744,16 @@ var init_assistant_tier_dispatch = __esm({
|
|
|
223300
223744
|
// ../server/src/domains/projects/routes.ts
|
|
223301
223745
|
function projectsDomain(opts) {
|
|
223302
223746
|
const svc = async (req) => opts.resolveService ? opts.resolveService(req.auth.companyId) : opts.service;
|
|
223747
|
+
const requireProjectManagePermission = async (req, projectId2, action) => {
|
|
223748
|
+
if (!opts.resolveMemberRole) return;
|
|
223749
|
+
if (isUncategorizedProjectId(projectId2)) return;
|
|
223750
|
+
const project = await (await svc(req)).getProject(projectId2);
|
|
223751
|
+
if (!project) throw new ApiError(404, "NOT_FOUND", `\u6CA1\u6709\u8FD9\u4E2A\u9879\u76EE\uFF1A${projectId2}`);
|
|
223752
|
+
if (project.createdBy && project.createdBy.id === req.auth.actor) return;
|
|
223753
|
+
const role = await opts.resolveMemberRole(req.auth.companyId, req.auth.actor);
|
|
223754
|
+
if (role === "owner" || role === "admin") return;
|
|
223755
|
+
throw new ApiError(403, action.code, `\u4F60\u6CA1\u6709${action.verb}\u8BE5\u9879\u76EE\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7EC4\u7EC7\u7BA1\u7406\u5458`);
|
|
223756
|
+
};
|
|
223303
223757
|
return (router) => {
|
|
223304
223758
|
router.post("/api/projects", async (req) => {
|
|
223305
223759
|
const body2 = req.body;
|
|
@@ -223341,6 +223795,7 @@ function projectsDomain(opts) {
|
|
|
223341
223795
|
return { status: 200, body: toApiProject(project) };
|
|
223342
223796
|
});
|
|
223343
223797
|
router.patch("/api/projects/:id", async (req) => {
|
|
223798
|
+
await requireProjectManagePermission(req, req.params.id, { verb: "\u7F16\u8F91", code: "PROJECT_EDIT_FORBIDDEN" });
|
|
223344
223799
|
const body2 = req.body ?? {};
|
|
223345
223800
|
for (const immutable of ["id", "project_id", "projectId", "createdBy", "created_by", "ownerId", "owner_id"]) {
|
|
223346
223801
|
if (Object.prototype.hasOwnProperty.call(body2, immutable)) {
|
|
@@ -223360,6 +223815,7 @@ function projectsDomain(opts) {
|
|
|
223360
223815
|
}
|
|
223361
223816
|
});
|
|
223362
223817
|
router.delete("/api/projects/:id", async (req) => {
|
|
223818
|
+
await requireProjectManagePermission(req, req.params.id, { verb: "\u5220\u9664", code: "PROJECT_DELETE_FORBIDDEN" });
|
|
223363
223819
|
try {
|
|
223364
223820
|
const result = await (await svc(req)).deleteProject(req.params.id);
|
|
223365
223821
|
return {
|
|
@@ -228356,7 +228812,7 @@ function reasonFromWorkdirCode(code2) {
|
|
|
228356
228812
|
return "ERROR";
|
|
228357
228813
|
}
|
|
228358
228814
|
}
|
|
228359
|
-
function
|
|
228815
|
+
function joinRel2(dir, name) {
|
|
228360
228816
|
return dir ? `${dir}/${name}` : name;
|
|
228361
228817
|
}
|
|
228362
228818
|
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
@@ -228389,14 +228845,14 @@ async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
|
228389
228845
|
if (outcome.truncated) truncated = true;
|
|
228390
228846
|
for (const entry of outcome.entries) {
|
|
228391
228847
|
if (entry.type === "dir") {
|
|
228392
|
-
nextDirs.push(
|
|
228848
|
+
nextDirs.push(joinRel2(dir, entry.name));
|
|
228393
228849
|
continue;
|
|
228394
228850
|
}
|
|
228395
228851
|
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
228396
228852
|
truncated = true;
|
|
228397
228853
|
continue;
|
|
228398
228854
|
}
|
|
228399
|
-
const rel =
|
|
228855
|
+
const rel = joinRel2(dir, entry.name);
|
|
228400
228856
|
items.push({
|
|
228401
228857
|
id: `workdir:${rel}`,
|
|
228402
228858
|
name: entry.name,
|
|
@@ -228641,7 +229097,8 @@ function createProjectsDomain(opts) {
|
|
|
228641
229097
|
register: projectsDomain({
|
|
228642
229098
|
service,
|
|
228643
229099
|
...opts.resolveCompany ? { resolveService } : {},
|
|
228644
|
-
...opts.defaultBranchResolver ? { defaultBranchResolver: opts.defaultBranchResolver } : {}
|
|
229100
|
+
...opts.defaultBranchResolver ? { defaultBranchResolver: opts.defaultBranchResolver } : {},
|
|
229101
|
+
...opts.resolveMemberRole ? { resolveMemberRole: opts.resolveMemberRole } : {}
|
|
228645
229102
|
}),
|
|
228646
229103
|
...opts.resolveCompany ? { resolveService } : {}
|
|
228647
229104
|
};
|
|
@@ -231088,6 +231545,9 @@ function nodesDomain(deps) {
|
|
|
231088
231545
|
const rts = runtimesByNode.get(n.id) ?? [];
|
|
231089
231546
|
const daemonVersion = daemonVersionById.get(n.id);
|
|
231090
231547
|
const connection = connectionById.get(n.id);
|
|
231548
|
+
const nodeOffline = !onlineIds.has(n.id);
|
|
231549
|
+
const lastDisconnectedAt = connection?.lastDisconnectedAt !== void 0 ? new Date(connection.lastDisconnectedAt).toISOString() : nodeOffline ? n.lastSeenAt : void 0;
|
|
231550
|
+
const lastReconnectedAt = connection?.lastReconnectedAt !== void 0 ? new Date(connection.lastReconnectedAt).toISOString() : void 0;
|
|
231091
231551
|
const activeRunCount = deps.activeRunCountOf?.(n.id) ?? 0;
|
|
231092
231552
|
const updateStatus = reconcileNodeUpdate(n.id, daemonVersion, activeRunCount, healthNow);
|
|
231093
231553
|
return {
|
|
@@ -231101,10 +231561,10 @@ function nodesDomain(deps) {
|
|
|
231101
231561
|
...sourceIpById.get(n.id) ? { sourceIp: sourceIpById.get(n.id) } : {},
|
|
231102
231562
|
...n.name ? { name: n.name } : {},
|
|
231103
231563
|
...updateStatus ? { updateStatus } : {},
|
|
231104
|
-
...
|
|
231564
|
+
...lastDisconnectedAt || lastReconnectedAt ? {
|
|
231105
231565
|
connection: {
|
|
231106
|
-
...
|
|
231107
|
-
...
|
|
231566
|
+
...lastDisconnectedAt ? { lastDisconnectedAt } : {},
|
|
231567
|
+
...lastReconnectedAt ? { lastReconnectedAt } : {}
|
|
231108
231568
|
}
|
|
231109
231569
|
} : {},
|
|
231110
231570
|
...deps.nodeHealth ? {
|
|
@@ -239294,6 +239754,9 @@ function routeToSession(hub, nodeId, dispatchId, msg) {
|
|
|
239294
239754
|
function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
|
|
239295
239755
|
const unknownIfInterrupting = input.interrupt ? { interrupt: "unknown" } : {};
|
|
239296
239756
|
if (entry.exited) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
|
|
239757
|
+
if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
|
|
239758
|
+
return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
|
|
239759
|
+
}
|
|
239297
239760
|
const requestId = (0, import_node_crypto65.randomUUID)();
|
|
239298
239761
|
const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
|
|
239299
239762
|
if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
|
|
@@ -239301,10 +239764,10 @@ function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
|
|
|
239301
239764
|
const timer = setTimeout(() => {
|
|
239302
239765
|
const i = entry.appendWaiters.findIndex((w2) => w2.timer === timer);
|
|
239303
239766
|
if (i >= 0) entry.appendWaiters.splice(i, 1);
|
|
239304
|
-
resolve10({ accepted: false, reason: "unsupported", ...unknownIfInterrupting });
|
|
239767
|
+
resolve10({ accepted: false, reason: input.requireConsumptionReceipt ? "unconfirmed" : "unsupported", ...unknownIfInterrupting });
|
|
239305
239768
|
}, timeoutMs);
|
|
239306
239769
|
timer.unref?.();
|
|
239307
|
-
entry.appendWaiters.push({ requestId, resolve: resolve10, timer });
|
|
239770
|
+
entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve10, timer });
|
|
239308
239771
|
});
|
|
239309
239772
|
}
|
|
239310
239773
|
var import_node_crypto65, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_IDS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
|
|
@@ -239451,7 +239914,7 @@ var init_daemon_adapter = __esm({
|
|
|
239451
239914
|
entry.exited = info;
|
|
239452
239915
|
for (const w2 of entry.appendWaiters.splice(0)) {
|
|
239453
239916
|
clearTimeout(w2.timer);
|
|
239454
|
-
w2.resolve({ accepted: false, reason: "session-closing" });
|
|
239917
|
+
w2.resolve({ accepted: false, reason: w2.requireConsumptionReceipt ? "unconfirmed" : "session-closing" });
|
|
239455
239918
|
}
|
|
239456
239919
|
if (info.reason === "server-unreachable") this.health?.recordUnreachable(entry.nodeId, info.reason);
|
|
239457
239920
|
else this.health?.recordReachable(entry.nodeId);
|
|
@@ -247505,6 +247968,7 @@ var init_postgres_chat_sessions = __esm({
|
|
|
247505
247968
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS escalation_id text`);
|
|
247506
247969
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS model text`);
|
|
247507
247970
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS visibility text`);
|
|
247971
|
+
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS outputs_fingerprints jsonb`);
|
|
247508
247972
|
await pool.query(`
|
|
247509
247973
|
WITH ranked AS (
|
|
247510
247974
|
SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
|
|
@@ -247735,6 +248199,23 @@ var init_postgres_chat_sessions = __esm({
|
|
|
247735
248199
|
args
|
|
247736
248200
|
);
|
|
247737
248201
|
}
|
|
248202
|
+
/**
|
|
248203
|
+
* `outputs/` 跨轮指纹(2026-09-11 产出卡)。整表覆盖,不做增量合并——判据的真源永远是
|
|
248204
|
+
* 最近一次扫描看到的现场;合并会让「文件被删掉」这件事永远留在表里。
|
|
248205
|
+
*/
|
|
248206
|
+
async readOutputFingerprints(sessionId) {
|
|
248207
|
+
const r = await this.pool.query(
|
|
248208
|
+
`SELECT outputs_fingerprints FROM ${this.s}.chat_sessions WHERE id = $1 AND company_id = $2`,
|
|
248209
|
+
[sessionId, this.companyId]
|
|
248210
|
+
);
|
|
248211
|
+
return r.rows[0]?.outputs_fingerprints ?? null;
|
|
248212
|
+
}
|
|
248213
|
+
async writeOutputFingerprints(sessionId, fingerprints) {
|
|
248214
|
+
await this.pool.query(
|
|
248215
|
+
`UPDATE ${this.s}.chat_sessions SET outputs_fingerprints = $1::jsonb WHERE id = $2 AND company_id = $3`,
|
|
248216
|
+
[JSON.stringify(fingerprints), sessionId, this.companyId]
|
|
248217
|
+
);
|
|
248218
|
+
}
|
|
247738
248219
|
/** 删项目时把本公司里绑到它的会话回落成「不指定项目」。会话一条都不删,只清 project_id。 */
|
|
247739
248220
|
async clearProjectBinding(projectId2) {
|
|
247740
248221
|
if (!projectId2) return 0;
|
|
@@ -248137,6 +248618,42 @@ var init_postgres_chat_sessions = __esm({
|
|
|
248137
248618
|
return r.rows.map(rowToMessage);
|
|
248138
248619
|
}
|
|
248139
248620
|
// ── ADR-0166 D6 待发队列 ─────────────────────────────────────────────────
|
|
248621
|
+
async claimPendingInsert(m2) {
|
|
248622
|
+
const client = await this.pool.connect();
|
|
248623
|
+
try {
|
|
248624
|
+
await client.query("BEGIN");
|
|
248625
|
+
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]);
|
|
248626
|
+
if (!scope.rows.length) throw new ChatCompanyScopeError(this.companyId, m2.chatSessionId);
|
|
248627
|
+
const ins = await client.query(
|
|
248628
|
+
`INSERT INTO ${this.s}.chat_pending_messages (id, chat_session_id, text, attachments, client_key, created_at, flavour)
|
|
248629
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7) ON CONFLICT DO NOTHING RETURNING *`,
|
|
248630
|
+
[m2.id, m2.chatSessionId, m2.text, m2.attachments ? JSON.stringify(m2.attachments) : null, m2.clientKey ?? null, m2.createdAt, m2.flavour ?? "user"]
|
|
248631
|
+
);
|
|
248632
|
+
if (!ins.rows.length) {
|
|
248633
|
+
const prior = await client.query(
|
|
248634
|
+
`SELECT * FROM ${this.s}.chat_pending_messages WHERE chat_session_id=$1
|
|
248635
|
+
AND (id=$2 OR (client_key=$3 AND state='queued')) ORDER BY (id=$2) DESC LIMIT 1`,
|
|
248636
|
+
[m2.chatSessionId, m2.id, m2.clientKey ?? null]
|
|
248637
|
+
);
|
|
248638
|
+
if (!prior.rows[0]) throw new Error("claimPendingInsert: conflicting submission unavailable");
|
|
248639
|
+
await client.query("COMMIT");
|
|
248640
|
+
return { submitted: rowToPending(prior.rows[0]), batch: [] };
|
|
248641
|
+
}
|
|
248642
|
+
const claimed = await client.query(
|
|
248643
|
+
`UPDATE ${this.s}.chat_pending_messages SET state='delivering', state_at=now(), state_reason='append-now'
|
|
248644
|
+
WHERE chat_session_id=$1 AND state='queued' RETURNING *`,
|
|
248645
|
+
[m2.chatSessionId]
|
|
248646
|
+
);
|
|
248647
|
+
await client.query("COMMIT");
|
|
248648
|
+
return { submitted: rowToPending(ins.rows[0]), batch: claimed.rows.map(rowToPending).sort((a, b2) => a.seq - b2.seq) };
|
|
248649
|
+
} catch (error2) {
|
|
248650
|
+
await client.query("ROLLBACK").catch(() => {
|
|
248651
|
+
});
|
|
248652
|
+
throw error2;
|
|
248653
|
+
} finally {
|
|
248654
|
+
client.release();
|
|
248655
|
+
}
|
|
248656
|
+
}
|
|
248140
248657
|
async enqueuePendingMessage(m2) {
|
|
248141
248658
|
await this.assertSessionInScope(m2.chatSessionId);
|
|
248142
248659
|
const initialState = m2.state;
|
|
@@ -253339,6 +253856,17 @@ var init_cutover_aware_dispatches = __esm({
|
|
|
253339
253856
|
if (row.state === "frozen") throw new BundleFrozenError("dispatch");
|
|
253340
253857
|
return this.o.cutover.readsCompanySchema(row) ? this.o.owned : this.o.legacy;
|
|
253341
253858
|
}
|
|
253859
|
+
/**
|
|
253860
|
+
* 这家公司的派发台账当前**物理落在哪**:切走了就是它自己的引擎 schema,没切走就是那个
|
|
253861
|
+
* 所有未切公司共用的 legacy 台账。
|
|
253862
|
+
*
|
|
253863
|
+
* 给 {@link FanOutDispatchLedger} 去重用。判据**不在调用方重算**——就是 {@link pick}
|
|
253864
|
+
* 那一条(`cutover.get` + `readsCompanySchema`)。与 `CutoverAwareTraceStore.storageKey` 同构。
|
|
253865
|
+
*/
|
|
253866
|
+
async storageKey() {
|
|
253867
|
+
const row = await this.o.cutover.get(this.o.companyId, "dispatch");
|
|
253868
|
+
return this.o.cutover.readsCompanySchema(row) ? `owned:${this.o.companyId}` : "legacy";
|
|
253869
|
+
}
|
|
253342
253870
|
async insertOpen(rec) {
|
|
253343
253871
|
return (await this.pickWritable()).insertOpen(rec);
|
|
253344
253872
|
}
|
|
@@ -253374,11 +253902,48 @@ var init_cutover_aware_dispatches = __esm({
|
|
|
253374
253902
|
constructor(o) {
|
|
253375
253903
|
this.o = o;
|
|
253376
253904
|
}
|
|
253377
|
-
|
|
253905
|
+
/**
|
|
253906
|
+
* 本次要作用的物理落点清单——**同一个台账只出现一次**。
|
|
253907
|
+
*
|
|
253908
|
+
* 为什么必须去重:`dispatch` 是按 (公司, bundle) 逐个切的,没切走的公司**全部共用同一个
|
|
253909
|
+
* legacy 台账**(读写同一张 `public.dispatches`)。此前 `each` 直接按公司逐家来,于是同一张
|
|
253910
|
+
* 表被访问 N 次(N = 未切走的公司数):
|
|
253911
|
+
*
|
|
253912
|
+
* - **纯读直接出重复行**(`listOpen` / `listByWorkorder` / `findOpenByTarget`)。
|
|
253913
|
+
* 2026-09-11 v2 生产实证(`ws:wo-9f4652cd`):35 家公司、其中 33 家 dispatch 未切走,
|
|
253914
|
+
* `listByWorkorder` 返回 33 份同一条派发,动态卡「执行轨迹」抽屉里同一轮 attempt
|
|
253915
|
+
* 就出现 33 次(`attempts` 由它喂,见 server 的 `activity-trace.ts`)。
|
|
253916
|
+
* - 写侧**没有被放大**,但原因是运气好而不是设计:`markStarted` / `close` 是同值 UPDATE;
|
|
253917
|
+
* `closeStaleNeverStarted` / `closeOpenByTarget` 的 WHERE 带 `ended_at IS NULL`,
|
|
253918
|
+
* 第一遍改完第二遍就匹配不上,返回 0,`reduce` 出来仍是真实值。
|
|
253919
|
+
* - 无论读写,同一条 SQL 都实打实打了 N 遍。
|
|
253920
|
+
*
|
|
253921
|
+
* 去重键由台账自己报,不在这里按 cutover 状态重算(见 `storageKey` 的注释)。也不能按
|
|
253922
|
+
* `ledgerFor` 返回的对象做身份去重:每家各有一个 `CutoverAwareDispatchStore` 包装实例,
|
|
253923
|
+
* N 个不同对象包着同一个 legacy 台账。
|
|
253924
|
+
*/
|
|
253925
|
+
async targets(op) {
|
|
253378
253926
|
const out = [];
|
|
253927
|
+
const seen = /* @__PURE__ */ new Set();
|
|
253379
253928
|
for (const cid of await this.o.listCompanies()) {
|
|
253380
253929
|
try {
|
|
253381
|
-
|
|
253930
|
+
if (this.o.storageKeyFor) {
|
|
253931
|
+
const key = await this.o.storageKeyFor(cid);
|
|
253932
|
+
if (seen.has(key)) continue;
|
|
253933
|
+
seen.add(key);
|
|
253934
|
+
}
|
|
253935
|
+
out.push({ cid, ledger: await this.o.ledgerFor(cid) });
|
|
253936
|
+
} catch (err) {
|
|
253937
|
+
this.o.onError?.(cid, op, err);
|
|
253938
|
+
}
|
|
253939
|
+
}
|
|
253940
|
+
return out;
|
|
253941
|
+
}
|
|
253942
|
+
async each(op, fn) {
|
|
253943
|
+
const out = [];
|
|
253944
|
+
for (const { cid, ledger } of await this.targets(op)) {
|
|
253945
|
+
try {
|
|
253946
|
+
out.push(await fn(ledger));
|
|
253382
253947
|
} catch (err) {
|
|
253383
253948
|
this.o.onError?.(cid, op, err);
|
|
253384
253949
|
}
|
|
@@ -253505,9 +254070,19 @@ var init_cutover_aware_trace = __esm({
|
|
|
253505
254070
|
constructor(o) {
|
|
253506
254071
|
this.o = o;
|
|
253507
254072
|
}
|
|
253508
|
-
|
|
254073
|
+
/** Resolve store and isolation together, from one cutover snapshot. Read-only consumers
|
|
254074
|
+
* must not infer company isolation merely from having a companyId. */
|
|
254075
|
+
async readTarget() {
|
|
253509
254076
|
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
253510
|
-
|
|
254077
|
+
const companyScoped = this.o.cutover.readsCompanySchema(row);
|
|
254078
|
+
return { store: companyScoped ? this.o.owned : this.o.legacy, companyScoped };
|
|
254079
|
+
}
|
|
254080
|
+
/** Expose only usage reads: callers cannot bypass the frozen-write fence. */
|
|
254081
|
+
async resolveUsageReadTarget() {
|
|
254082
|
+
return this.readTarget();
|
|
254083
|
+
}
|
|
254084
|
+
async pick() {
|
|
254085
|
+
return (await this.readTarget()).store;
|
|
253511
254086
|
}
|
|
253512
254087
|
async pickWritable() {
|
|
253513
254088
|
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
@@ -257791,6 +258366,7 @@ var init_src11 = __esm({
|
|
|
257791
258366
|
init_tenancy_move_verify();
|
|
257792
258367
|
init_company_scoped_registry();
|
|
257793
258368
|
init_session_workdir();
|
|
258369
|
+
init_outputs_scan();
|
|
257794
258370
|
init_org_model_backfill();
|
|
257795
258371
|
init_types_admin();
|
|
257796
258372
|
init_roles_admin();
|
|
@@ -258200,6 +258776,13 @@ init_src11();
|
|
|
258200
258776
|
init_src();
|
|
258201
258777
|
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";
|
|
258202
258778
|
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";
|
|
258779
|
+
var OUTPUTS_CONVENTION = [
|
|
258780
|
+
"## \u4EA7\u51FA\u653E\u54EA",
|
|
258781
|
+
"",
|
|
258782
|
+
"\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",
|
|
258783
|
+
"\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",
|
|
258784
|
+
"\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"
|
|
258785
|
+
].join("\n");
|
|
258203
258786
|
function buildChatTaskHeader(input) {
|
|
258204
258787
|
if (input.kind === "isolated-knowledge") {
|
|
258205
258788
|
return {
|
|
@@ -258220,7 +258803,9 @@ function buildChatTaskHeader(input) {
|
|
|
258220
258803
|
`your_actor_id: ${input.actorId}`,
|
|
258221
258804
|
`workspace: ${input.workspace}`,
|
|
258222
258805
|
"",
|
|
258223
|
-
CONVERSATIONAL_MANAGER_BODY
|
|
258806
|
+
CONVERSATIONAL_MANAGER_BODY,
|
|
258807
|
+
"",
|
|
258808
|
+
OUTPUTS_CONVENTION
|
|
258224
258809
|
]
|
|
258225
258810
|
};
|
|
258226
258811
|
}
|
|
@@ -258232,7 +258817,9 @@ function buildChatTaskHeader(input) {
|
|
|
258232
258817
|
input.sessionLine,
|
|
258233
258818
|
`your_actor_id: ${input.actorId}`,
|
|
258234
258819
|
"",
|
|
258235
|
-
routed ? BRAINSTORM_ARTIFACT_GUARD : BRAINSTORM_LEGACY_LINE
|
|
258820
|
+
routed ? BRAINSTORM_ARTIFACT_GUARD : BRAINSTORM_LEGACY_LINE,
|
|
258821
|
+
// 子会话那一段由 EXPERT_DELEGATION_SYSTEM_PROMPT 讲,这里不重复(见 OUTPUTS_CONVENTION 注释)。
|
|
258822
|
+
...input.delegatedChildTurn ? [] : ["", OUTPUTS_CONVENTION]
|
|
258236
258823
|
],
|
|
258237
258824
|
...routed ? { routingSystemPrompt: EXECUTION_ROUTING_BODY } : {}
|
|
258238
258825
|
};
|
|
@@ -259407,6 +259994,12 @@ async function startServe(opts) {
|
|
|
259407
259994
|
kernel: defaultCompanyKernel,
|
|
259408
259995
|
blobs: assets,
|
|
259409
259996
|
trace: () => traceStoreForActors,
|
|
259997
|
+
// Same current-company ledger as chat/dispatch writes (PR #1223). Resolve the
|
|
259998
|
+
// physical read target and its isolation together; never treat legacy as owned.
|
|
259999
|
+
resolveTrace: async (cid) => {
|
|
260000
|
+
const store = await traceStoreFor(cid);
|
|
260001
|
+
return store instanceof CutoverAwareTraceStore ? store.resolveUsageReadTarget() : { store, companyScoped: !pgPool };
|
|
260002
|
+
},
|
|
259410
260003
|
audit: registryAudit,
|
|
259411
260004
|
// ADR 凭据保险库 §4.5/§4.7 / ADR 0125:reveal 判权的项目解析 + 凭据读取审计读写。
|
|
259412
260005
|
resolveDispatchScope: resolveDispatchScope2,
|
|
@@ -259595,6 +260188,14 @@ async function startServe(opts) {
|
|
|
259595
260188
|
actorDirectory: registryStore,
|
|
259596
260189
|
// 与 collab 同源;闭包避开下方 listCompanyMembers 声明的 TDZ,实际请求时才读取。
|
|
259597
260190
|
listMembers: (companyId) => listCompanyMembers(companyId),
|
|
260191
|
+
// 改/删项目的门禁判据之一(另一半是项目创建人)——见 domains/projects/routes.ts 的
|
|
260192
|
+
// `requireProjectManagePermission`。同样用闭包避开 listCompanyMembers 的 TDZ。
|
|
260193
|
+
// 成员表按 company_id 有索引、量级几十行,与 `listMembers` 同一次读的成本量级;
|
|
260194
|
+
// 只在 PATCH/DELETE 这两个低频写端点上调,不在任何读热路径上。
|
|
260195
|
+
resolveMemberRole: async (companyId, actorId) => {
|
|
260196
|
+
const members = await listCompanyMembers(companyId);
|
|
260197
|
+
return members.find((m2) => m2.actorId === actorId)?.role;
|
|
260198
|
+
},
|
|
259598
260199
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
259599
260200
|
defaultBranchResolver,
|
|
259600
260201
|
// 展示:GET content-locations 给 git 仓补 resolvedDefaultBranch
|
|
@@ -260611,6 +261212,15 @@ async function startServe(opts) {
|
|
|
260611
261212
|
// 只扫 active 的公司:停用公司的台账不该再被兜底扫改写(与本文件其它按公司扇出的地方同口径)。
|
|
260612
261213
|
listCompanies: async () => (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id),
|
|
260613
261214
|
ledgerFor: (cid) => dispatchLedgerFor(cid),
|
|
261215
|
+
/**
|
|
261216
|
+
* 去重键:`dispatch` 未切走的公司全部共用同一张 legacy 台账,扇出只该作用一次。
|
|
261217
|
+
* 由台账自己报落点,这里不按 cutover 状态重算(见 FanOutDispatchLedger.targets 的注释)。
|
|
261218
|
+
* 非 PG / 非 cutover-aware 的路径没有这个方法 ⇒ 退回按公司逐家来,行为不变。
|
|
261219
|
+
*/
|
|
261220
|
+
storageKeyFor: async (cid) => {
|
|
261221
|
+
const l = await dispatchLedgerFor(cid);
|
|
261222
|
+
return typeof l.storageKey === "function" ? l.storageKey() : cid;
|
|
261223
|
+
},
|
|
260614
261224
|
onError: (cid, op, err) => console.warn(`[dispatch-ledger] ${cid} \u7684 ${op} \u5931\u8D25\uFF08\u5176\u4F59\u516C\u53F8\u7EE7\u7EED\uFF09\uFF1A${String(err)}`)
|
|
260615
261225
|
}) : dispatchLedger;
|
|
260616
261226
|
const companyRegistryFor = async (companyId) => pgPool ? (await openCompanyPlane(companyId)).overlay : scopedRegistryFor(companyId);
|
|
@@ -261408,6 +262018,39 @@ async function startServe(opts) {
|
|
|
261408
262018
|
// 三个方法的组合规则住在 `createDelegationWorkdirPort`(`@oasis/server`)——评审
|
|
261409
262019
|
// wo:acfee725 blocker ② 回归防线:**serve.ts 与单测走同一处**,那样「装配层退回全局 binding」
|
|
261410
262020
|
// 这类事故在单测里就能被复现。绝不许在这里再写一份。
|
|
262021
|
+
/**
|
|
262022
|
+
* 会话产出扫描(2026-09-11 产出卡):轮次收尾扫这条会话的 `outputs/`,交出「这一轮变了哪些」。
|
|
262023
|
+
*
|
|
262024
|
+
* 复用 `delegationWorkdirLocator`(与派发、回流、chat 域**同一个 binding resolver 实例**)——
|
|
262025
|
+
* 委派回程读的就是子会话的 `outputs/`,主会话这条只是把同一套读口用到了自己身上。
|
|
262026
|
+
*
|
|
262027
|
+
* 三处 fail-closed,都返回 `null`(= 这一轮没有产出卡),一处都不猜:
|
|
262028
|
+
* · 桥还没建好 / 会话没绑定 / 节点离线 → 拿不到工作区;
|
|
262029
|
+
* · store 没有指纹方法(老 store / 文件态装配)→ 没地方记「上一轮长什么样」,
|
|
262030
|
+
* 硬扫会把整个 `outputs/` 每轮都当成新增,一口气刷几十张卡;
|
|
262031
|
+
* · 解析不出公司 → **绝不回落默认公司**(否则会去别家公司的库里读写指纹)。
|
|
262032
|
+
*/
|
|
262033
|
+
sessionOutputsScan: async ({ chatSessionId, companyId }) => {
|
|
262034
|
+
const bridge = workdirBridge;
|
|
262035
|
+
if (!bridge) return null;
|
|
262036
|
+
const store = chatStoreRouter ? await chatStoreFor(companyId).catch(() => null) : chatSessionStore;
|
|
262037
|
+
const read = store?.readOutputFingerprints?.bind(store);
|
|
262038
|
+
const write = store?.writeOutputFingerprints?.bind(store);
|
|
262039
|
+
if (!read || !write) return null;
|
|
262040
|
+
const located = await locateSessionWorkdir(delegationWorkdirLocator, {
|
|
262041
|
+
chatSessionId,
|
|
262042
|
+
...companyId !== void 0 ? { companyId } : {}
|
|
262043
|
+
});
|
|
262044
|
+
if (!located.ok) return null;
|
|
262045
|
+
const diff = await settleSessionOutputs({
|
|
262046
|
+
bridge,
|
|
262047
|
+
loc: located.loc,
|
|
262048
|
+
sessionId: chatSessionId,
|
|
262049
|
+
readFingerprints: read,
|
|
262050
|
+
writeFingerprints: write
|
|
262051
|
+
}).catch(() => null);
|
|
262052
|
+
return diff?.changed ?? null;
|
|
262053
|
+
},
|
|
261411
262054
|
delegationWorkdir: createDelegationWorkdirPort({
|
|
261412
262055
|
locator: delegationWorkdirLocator,
|
|
261413
262056
|
// 桥在 hub 建好后(本函数尾部)才赋值 → getter 每次调用时读最新值;早于此处的构造时 `workdirBridge`
|
|
@@ -265093,6 +265736,7 @@ async function runSession(dispatchId, job, deps) {
|
|
|
265093
265736
|
const cancelledBeforeStart = deps.wasCancelled?.() ?? false;
|
|
265094
265737
|
const normalizer = new ProviderStreamNormalizer({
|
|
265095
265738
|
providerName: job.binding?.runtimeKind ?? "runtime",
|
|
265739
|
+
hasLiveProtocol: handle.supportsLiveProtocol === true,
|
|
265096
265740
|
fallbackTurnId: `oasis-turn:${handle.id}`,
|
|
265097
265741
|
emit: (event) => deps.sink.stream({ type: "session_normalized_event", dispatchId, event })
|
|
265098
265742
|
});
|
|
@@ -265262,7 +265906,8 @@ var SessionProcessState = class {
|
|
|
265262
265906
|
// 只报 dispatchId 的话 serve 重启后它认不出这是条活会话,会当成野进程 evict 掉。
|
|
265263
265907
|
...this.spec.job.artifactId ? { jobArtifactId: this.spec.job.artifactId } : {},
|
|
265264
265908
|
// ADR 0300 D2:自报支持帧号;新服务端据此回带 ackSeq 的 hello-ack,届时才把 serverSupportsDedup 置真。
|
|
265265
|
-
supportsFrameSeq: true
|
|
265909
|
+
supportsFrameSeq: true,
|
|
265910
|
+
supportsInputConsumptionReceipts: true
|
|
265266
265911
|
});
|
|
265267
265912
|
const toResend = this.serverSupportsDedup ? this.outbox : this.outbox.filter(isKeyFrame);
|
|
265268
265913
|
for (const m2 of toResend) this.rawSend(m2);
|
|
@@ -272184,7 +272829,7 @@ function shimScript() {
|
|
|
272184
272829
|
}
|
|
272185
272830
|
|
|
272186
272831
|
// src/index.ts
|
|
272187
|
-
var PKG_VERSION = true ? "2.2.
|
|
272832
|
+
var PKG_VERSION = true ? "2.2.20" : "dev";
|
|
272188
272833
|
var LOCAL_BIN = localBin();
|
|
272189
272834
|
var NPM_PREFIX = npmPrefix();
|
|
272190
272835
|
var INSTANCE = DEFAULT_INSTANCE;
|