oasis_test_v2 2.2.19 → 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 +539 -102
- 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
|
``,
|
|
@@ -24897,6 +24931,8 @@ async function runProtocolSession(job, cfg) {
|
|
|
24897
24931
|
const transcript = openTranscript(transcriptRef);
|
|
24898
24932
|
const outputCbs = [];
|
|
24899
24933
|
const telemetryCbs = [];
|
|
24934
|
+
const liveCbs = [];
|
|
24935
|
+
const appendByKey = /* @__PURE__ */ new Map();
|
|
24900
24936
|
const exitCbs = [];
|
|
24901
24937
|
const state = cfg.createState();
|
|
24902
24938
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -24932,7 +24968,7 @@ async function runProtocolSession(job, cfg) {
|
|
|
24932
24968
|
pending.clear();
|
|
24933
24969
|
};
|
|
24934
24970
|
const send = (command) => {
|
|
24935
|
-
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"));
|
|
24936
24972
|
const idValue = command.id ?? `oasis_${++nextRequestId}`;
|
|
24937
24973
|
const message = { ...command, id: idValue };
|
|
24938
24974
|
const key = commandId(idValue);
|
|
@@ -24952,11 +24988,12 @@ async function runProtocolSession(job, cfg) {
|
|
|
24952
24988
|
if (isResponse(message) && responseId !== void 0 && pending.has(responseId)) {
|
|
24953
24989
|
const waiter = pending.get(responseId);
|
|
24954
24990
|
pending.delete(responseId);
|
|
24955
|
-
if (message.type === "response" && message.success === false) waiter.reject(new
|
|
24956
|
-
else if (Object.prototype.hasOwnProperty.call(message, "error")) waiter.reject(new
|
|
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)));
|
|
24957
24993
|
else waiter.resolve(message.result ?? message);
|
|
24958
24994
|
return;
|
|
24959
24995
|
}
|
|
24996
|
+
for (const event of cfg.liveEvents?.(message, state) ?? []) for (const cb of liveCbs) cb(event);
|
|
24960
24997
|
for (const line of cfg.normalize(message, state)) {
|
|
24961
24998
|
if (line.outputText) for (const cb of outputCbs) cb(line.outputText);
|
|
24962
24999
|
const event = { kind: line.kind, payload: line.payload, seq: ++eventSeq, ts: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -25048,26 +25085,34 @@ async function runProtocolSession(job, cfg) {
|
|
|
25048
25085
|
onTelemetry(cb) {
|
|
25049
25086
|
telemetryCbs.push(cb);
|
|
25050
25087
|
},
|
|
25088
|
+
onLiveEvent(cb) {
|
|
25089
|
+
liveCbs.push(cb);
|
|
25090
|
+
},
|
|
25051
25091
|
get canAppendInput() {
|
|
25052
25092
|
return !closing && started && !exited;
|
|
25053
25093
|
},
|
|
25054
25094
|
appendInput(input) {
|
|
25055
|
-
|
|
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" });
|
|
25056
25098
|
appendChain = appendChain.then(async () => {
|
|
25057
25099
|
if (exited || closing) return { accepted: false, reason: "session-closing" };
|
|
25058
25100
|
const files = materializeFiles(dir, input.files, input.binaryFiles);
|
|
25059
25101
|
try {
|
|
25060
|
-
const
|
|
25061
|
-
return { ...
|
|
25062
|
-
} catch {
|
|
25063
|
-
|
|
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 } : {} };
|
|
25064
25107
|
}
|
|
25065
25108
|
});
|
|
25066
|
-
|
|
25109
|
+
const result = appendChain;
|
|
25110
|
+
if (key) appendByKey.set(key, result);
|
|
25111
|
+
return result;
|
|
25067
25112
|
}
|
|
25068
25113
|
};
|
|
25069
25114
|
}
|
|
25070
|
-
var import_node_child_process6, import_node_crypto10, path9;
|
|
25115
|
+
var import_node_child_process6, import_node_crypto10, path9, ProtocolCommandRejected, ProtocolSessionClosing;
|
|
25071
25116
|
var init_protocol_session = __esm({
|
|
25072
25117
|
"../adapters/src/_core/protocol-session.ts"() {
|
|
25073
25118
|
"use strict";
|
|
@@ -25083,6 +25128,10 @@ var init_protocol_session = __esm({
|
|
|
25083
25128
|
init_transcript();
|
|
25084
25129
|
init_filter_extra_args();
|
|
25085
25130
|
init_root_bypass();
|
|
25131
|
+
ProtocolCommandRejected = class extends Error {
|
|
25132
|
+
};
|
|
25133
|
+
ProtocolSessionClosing = class extends Error {
|
|
25134
|
+
};
|
|
25086
25135
|
}
|
|
25087
25136
|
});
|
|
25088
25137
|
|
|
@@ -25595,11 +25644,13 @@ var init_codex = __esm({
|
|
|
25595
25644
|
workRoot: this.opts.workRoot,
|
|
25596
25645
|
env: this.opts.env,
|
|
25597
25646
|
runtimeKind: "codex",
|
|
25647
|
+
supportsInputConsumptionReceipts: true,
|
|
25598
25648
|
syncSkills: this.opts.syncSkills,
|
|
25599
25649
|
createState: () => ({
|
|
25600
25650
|
threadId: void 0,
|
|
25601
25651
|
turnId: void 0,
|
|
25602
25652
|
active: false,
|
|
25653
|
+
pendingInputs: /* @__PURE__ */ new Set(),
|
|
25603
25654
|
codex: createCodexNormalizeState(),
|
|
25604
25655
|
// 本次 run 的用量:逐轮累加 `thread/tokenUsage/updated` 的 `last`(见该解析函数注释)。
|
|
25605
25656
|
usage: { input: 0, cacheRead: 0, cacheWrite: 0, output: 0 },
|
|
@@ -25643,6 +25694,34 @@ ${task}` : task;
|
|
|
25643
25694
|
if (typeof turnId === "string" && turnId) state.turnId = turnId;
|
|
25644
25695
|
if (!state.turnId) throw new Error("codex app-server returned no turn id");
|
|
25645
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
|
+
},
|
|
25646
25725
|
normalize(message, state) {
|
|
25647
25726
|
const params = message.params;
|
|
25648
25727
|
const method = typeof message.method === "string" ? message.method : "";
|
|
@@ -25701,7 +25780,7 @@ ${task}` : task;
|
|
|
25701
25780
|
};
|
|
25702
25781
|
},
|
|
25703
25782
|
async append(input, state, send) {
|
|
25704
|
-
if (!state.threadId || !state.turnId) return { accepted: false, reason: "
|
|
25783
|
+
if (!state.threadId || !state.turnId || !state.active) return { accepted: false, reason: "session-closing" };
|
|
25705
25784
|
if (input.interrupt) {
|
|
25706
25785
|
await send({ jsonrpc: "2.0", method: "turn/interrupt", params: { threadId: state.threadId, turnId: state.turnId } });
|
|
25707
25786
|
const next = await send({ jsonrpc: "2.0", method: "turn/start", params: { threadId: state.threadId, input: [{ type: "text", text: input.text }] } });
|
|
@@ -25710,13 +25789,22 @@ ${task}` : task;
|
|
|
25710
25789
|
state.active = true;
|
|
25711
25790
|
return { accepted: true, interrupt: "unknown" };
|
|
25712
25791
|
}
|
|
25713
|
-
|
|
25714
|
-
|
|
25715
|
-
|
|
25716
|
-
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
|
|
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" };
|
|
25720
25808
|
}
|
|
25721
25809
|
});
|
|
25722
25810
|
}
|
|
@@ -26592,6 +26680,7 @@ async function runACPSession(job, cfg) {
|
|
|
26592
26680
|
const transcriptOut = openTranscript(transcriptRef);
|
|
26593
26681
|
const outputCbs = [];
|
|
26594
26682
|
const telemetryCbs = [];
|
|
26683
|
+
const liveCbs = [];
|
|
26595
26684
|
const exitCbs = [];
|
|
26596
26685
|
let exited;
|
|
26597
26686
|
let killReason = null;
|
|
@@ -26670,6 +26759,34 @@ ${note}` : note };
|
|
|
26670
26759
|
});
|
|
26671
26760
|
let lastRpcError;
|
|
26672
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
|
+
};
|
|
26673
26790
|
const client = new ACPClient(
|
|
26674
26791
|
(line) => {
|
|
26675
26792
|
try {
|
|
@@ -26678,9 +26795,11 @@ ${note}` : note };
|
|
|
26678
26795
|
}
|
|
26679
26796
|
},
|
|
26680
26797
|
(text5) => {
|
|
26798
|
+
confirmAppendBatch();
|
|
26681
26799
|
for (const cb of outputCbs) cb(text5);
|
|
26682
26800
|
},
|
|
26683
26801
|
(e) => {
|
|
26802
|
+
if (["message", "thought", "tool_use", "tool_result"].includes(e.kind)) confirmAppendBatch();
|
|
26684
26803
|
for (const cb of telemetryCbs) cb(e);
|
|
26685
26804
|
},
|
|
26686
26805
|
// runtime 自己用 stopReason=cancelled 收尾时也保留取消语义;此前误记成 error,
|
|
@@ -26814,9 +26933,11 @@ ${task}` : task;
|
|
|
26814
26933
|
promptResult = await sendPrompt(userText);
|
|
26815
26934
|
}
|
|
26816
26935
|
while (!killReason && appendQueue.length > 0) {
|
|
26936
|
+
emitBoundary();
|
|
26817
26937
|
inflightAppendBatch = appendQueue.splice(0);
|
|
26818
26938
|
const merged = inflightAppendBatch.map((i) => i.text).filter(Boolean).join("\n\n");
|
|
26819
26939
|
promptResult = await sendPrompt(merged);
|
|
26940
|
+
confirmAppendBatch();
|
|
26820
26941
|
inflightAppendBatch = [];
|
|
26821
26942
|
}
|
|
26822
26943
|
closingForExit = true;
|
|
@@ -26855,6 +26976,9 @@ ${task}` : task;
|
|
|
26855
26976
|
onTelemetry(cb) {
|
|
26856
26977
|
telemetryCbs.push(cb);
|
|
26857
26978
|
},
|
|
26979
|
+
onLiveEvent(cb) {
|
|
26980
|
+
liveCbs.push(cb);
|
|
26981
|
+
},
|
|
26858
26982
|
// ADR-0093 对话追加:入队一条 user 输入,drain 循环会把它发成同 session 的下一回合。
|
|
26859
26983
|
// 已收尾/已退出则拒(session-closing),上层据此降级 kill+resume。
|
|
26860
26984
|
get canAppendInput() {
|
|
@@ -26878,12 +27002,15 @@ ${task}` : task;
|
|
|
26878
27002
|
}
|
|
26879
27003
|
};
|
|
26880
27004
|
function doAppend(input) {
|
|
26881
|
-
if (input.requireConsumptionReceipt) return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
|
|
26882
27005
|
if (exited || closingForExit) return Promise.resolve({ accepted: false, reason: "session-closing" });
|
|
26883
27006
|
const report = materializeFiles(dir, input.files, input.binaryFiles);
|
|
26884
27007
|
const queued = report.failed.length ? { ...input, text: appendMaterializeWarning(input.text, report) } : input;
|
|
26885
27008
|
appendQueue.push(queued);
|
|
26886
|
-
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
|
+
});
|
|
26887
27014
|
if (!input.interrupt) return Promise.resolve(withFiles({ accepted: true, interrupt: "queued_at_boundary" }));
|
|
26888
27015
|
const run = appendChain.then(async () => {
|
|
26889
27016
|
const pending = inflightStopReason;
|
|
@@ -27637,12 +27764,13 @@ var init_pi = __esm({
|
|
|
27637
27764
|
return ["--mode", "rpc", "--session", sessionFile, ...model ? ["--model", model] : []];
|
|
27638
27765
|
},
|
|
27639
27766
|
runtimeKind: "pi",
|
|
27767
|
+
supportsInputConsumptionReceipts: true,
|
|
27640
27768
|
workRoot: opts.workRoot,
|
|
27641
27769
|
cleanupWorkdir: opts.cleanupWorkdir,
|
|
27642
27770
|
env: opts.env,
|
|
27643
27771
|
extraArgs: opts.extraArgs,
|
|
27644
27772
|
blockedFlags: PI_BLOCKED_FLAGS,
|
|
27645
|
-
createState: () => ({}),
|
|
27773
|
+
createState: () => ({ pendingInputs: [] }),
|
|
27646
27774
|
initialCommand(job2, _dir, task) {
|
|
27647
27775
|
const prompt2 = job2.systemPrompt ? `${job2.systemPrompt}
|
|
27648
27776
|
|
|
@@ -27659,6 +27787,28 @@ ${task}` : task;
|
|
|
27659
27787
|
} catch {
|
|
27660
27788
|
}
|
|
27661
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
|
+
},
|
|
27662
27812
|
normalize(message) {
|
|
27663
27813
|
return normalizePi(JSON.stringify(message));
|
|
27664
27814
|
},
|
|
@@ -27668,14 +27818,21 @@ ${task}` : task;
|
|
|
27668
27818
|
runtimeSessionId(state) {
|
|
27669
27819
|
return state.sessionId;
|
|
27670
27820
|
},
|
|
27671
|
-
async append(input,
|
|
27672
|
-
|
|
27673
|
-
|
|
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
|
+
}
|
|
27674
27830
|
await send({ type: "steer", message: input.text });
|
|
27675
|
-
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;
|
|
27676
27835
|
}
|
|
27677
|
-
await send({ type: "steer", message: input.text });
|
|
27678
|
-
return { accepted: true };
|
|
27679
27836
|
}
|
|
27680
27837
|
});
|
|
27681
27838
|
}
|
|
@@ -28485,10 +28642,13 @@ var init_normalizer = __esm({
|
|
|
28485
28642
|
proseClaim = /* @__PURE__ */ new Map();
|
|
28486
28643
|
/** 本 turn 内已经见过的 item 键——认领闸只拦「新起一段」,不拦已知 item 的后续 op。 */
|
|
28487
28644
|
claimedKeys = /* @__PURE__ */ new Set();
|
|
28645
|
+
closedKeys = /* @__PURE__ */ new Set();
|
|
28488
28646
|
/** 见过 stdout 文本没有——见过就压掉 telemetry `message`(与 server.ts 原口径同款去重)。 */
|
|
28489
28647
|
sawTextOutput = false;
|
|
28490
28648
|
/** 本 turn 产出过 item op 没有。收尾时用来决定要不要发 turn 信号(见 turnFinished)。 */
|
|
28491
28649
|
producedAny = false;
|
|
28650
|
+
/** Receipts wait for a whole output block; they never split its bytes. */
|
|
28651
|
+
pendingInputs = /* @__PURE__ */ new Map();
|
|
28492
28652
|
constructor(opts) {
|
|
28493
28653
|
this.providerName = opts.providerName;
|
|
28494
28654
|
this.hasLiveProtocol = opts.hasLiveProtocol === true;
|
|
@@ -28532,20 +28692,17 @@ var init_normalizer = __esm({
|
|
|
28532
28692
|
}
|
|
28533
28693
|
/** 结构化实时事件(原生 Live Protocol v2)。 */
|
|
28534
28694
|
onLiveEvent(event) {
|
|
28535
|
-
this.turnId = event.turnId || this.turnId;
|
|
28536
28695
|
const payload = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload : {};
|
|
28537
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") {
|
|
28538
28702
|
this.closeOpen("completed");
|
|
28539
|
-
this.emitItem({
|
|
28540
|
-
key: providerItemKey(this.providerName, event.itemId),
|
|
28541
|
-
kind: "control",
|
|
28542
|
-
role: "system",
|
|
28543
|
-
origin: "live",
|
|
28544
|
-
opShape: { op: "set_status", status: "completed" },
|
|
28545
|
-
attrs: { code: "input_consumed", clientMessageKey: payload.clientMessageKey }
|
|
28546
|
-
});
|
|
28547
28703
|
return;
|
|
28548
28704
|
}
|
|
28705
|
+
this.turnId = event.turnId || this.turnId;
|
|
28549
28706
|
if (event.operation === "turn_completed" || event.operation === "turn_failed") {
|
|
28550
28707
|
this.turnFinished(event.operation === "turn_failed" ? "failed" : "completed");
|
|
28551
28708
|
return;
|
|
@@ -28561,13 +28718,12 @@ var init_normalizer = __esm({
|
|
|
28561
28718
|
}
|
|
28562
28719
|
return;
|
|
28563
28720
|
}
|
|
28564
|
-
if (event.operation === "completed" && (event.itemType === "message" || event.itemType === "thinking")
|
|
28565
|
-
this.
|
|
28566
|
-
|
|
28567
|
-
payload.text,
|
|
28568
|
-
|
|
28569
|
-
|
|
28570
|
-
);
|
|
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");
|
|
28571
28727
|
return;
|
|
28572
28728
|
}
|
|
28573
28729
|
if (event.operation === "started" && event.itemType === "tool") {
|
|
@@ -28592,6 +28748,7 @@ var init_normalizer = __esm({
|
|
|
28592
28748
|
this.fallbackIndex.clear();
|
|
28593
28749
|
this.proseClaim.clear();
|
|
28594
28750
|
this.claimedKeys.clear();
|
|
28751
|
+
this.closedKeys.clear();
|
|
28595
28752
|
this.sawTextOutput = false;
|
|
28596
28753
|
this.producedAny = false;
|
|
28597
28754
|
if (!produced) return;
|
|
@@ -28615,6 +28772,10 @@ var init_normalizer = __esm({
|
|
|
28615
28772
|
if (!known && !this.claim(kind, origin)) return;
|
|
28616
28773
|
const key = explicitKey ?? (this.open && this.open.kind === kind ? this.open.key : this.nextFallbackKey(kind));
|
|
28617
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
|
+
}
|
|
28618
28779
|
if (!this.open || this.open.key !== key) {
|
|
28619
28780
|
this.closeOpen("completed");
|
|
28620
28781
|
this.open = { key, kind, origin };
|
|
@@ -28631,6 +28792,10 @@ var init_normalizer = __esm({
|
|
|
28631
28792
|
if (!known && !this.claim(kind, origin)) return;
|
|
28632
28793
|
const key = explicitKey ?? (this.open && this.open.kind === kind ? this.open.key : this.nextFallbackKey(kind));
|
|
28633
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
|
+
}
|
|
28634
28799
|
if (!this.open || this.open.key !== key) {
|
|
28635
28800
|
this.closeOpen("completed");
|
|
28636
28801
|
this.open = { key, kind, origin };
|
|
@@ -28649,8 +28814,23 @@ var init_normalizer = __esm({
|
|
|
28649
28814
|
closeOpen(status) {
|
|
28650
28815
|
const open3 = this.open;
|
|
28651
28816
|
this.open = null;
|
|
28652
|
-
if (
|
|
28653
|
-
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
|
+
}
|
|
28654
28834
|
}
|
|
28655
28835
|
toolCall(origin, id, name, input) {
|
|
28656
28836
|
this.closeOpen("completed");
|
|
@@ -28870,7 +29050,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28870
29050
|
telemetryTextInContent;
|
|
28871
29051
|
/** 本段的「无输出」注解已经提前落过(`closeSegmentBeforeUserInput`),`beginSegment` 别再落一条。 */
|
|
28872
29052
|
segmentEndEmitted = false;
|
|
28873
|
-
/** **不变量 1**:`providerItemKey → itemId
|
|
29053
|
+
/** **不变量 1**:`providerItemKey → itemId`,本账本唯一持有。仅 runtime turn 结束/收尾清空,用户插入保留。 */
|
|
28874
29054
|
itemKeyMap = /* @__PURE__ */ new Map();
|
|
28875
29055
|
/** 内存账本:itemId → 行。插入顺序即 seq 顺序。 */
|
|
28876
29056
|
rows = /* @__PURE__ */ new Map();
|
|
@@ -29049,7 +29229,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29049
29229
|
}
|
|
29050
29230
|
});
|
|
29051
29231
|
const row = this.rows.get(id);
|
|
29052
|
-
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 };
|
|
29053
29233
|
}
|
|
29054
29234
|
/**
|
|
29055
29235
|
* 回灌重启前已经落库的那段正文(`chat-recovery` 重挂 / 迟到对账用)。
|
|
@@ -29084,7 +29264,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29084
29264
|
// ── 段与归属 ──────────────────────────────────────────────────────────────
|
|
29085
29265
|
/**
|
|
29086
29266
|
* 切段(人中途插入):此刻起建的 item 属于**下一条** assistant 行。
|
|
29087
|
-
*
|
|
29267
|
+
* 已有 provider item 保留身份与位置;新 item 才归下一段,迟到快照不能复制旧块。
|
|
29088
29268
|
*/
|
|
29089
29269
|
beginSegment() {
|
|
29090
29270
|
if (this.segmentEndEmitted) {
|
|
@@ -29093,8 +29273,6 @@ var init_chat_item_ledger = __esm({
|
|
|
29093
29273
|
this.closeStreamingRows("completed");
|
|
29094
29274
|
if (this.segmentItemCount === 0 && this.messageId) this.insertSegmentEnd();
|
|
29095
29275
|
}
|
|
29096
|
-
this.itemKeyMap.clear();
|
|
29097
|
-
this.lastTurnId = null;
|
|
29098
29276
|
this.messageId = null;
|
|
29099
29277
|
this.segment += 1;
|
|
29100
29278
|
this.segmentItemCount = 0;
|
|
@@ -29118,7 +29296,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29118
29296
|
const id = this.insertSegmentEnd();
|
|
29119
29297
|
this.segmentEndEmitted = true;
|
|
29120
29298
|
const row = this.rows.get(id);
|
|
29121
|
-
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 };
|
|
29122
29300
|
}
|
|
29123
29301
|
/** 零输出段兜底(S1 评审 B1):那条 assistant 行库里已经在了,一条 item 都没有会被对账抓成假红。 */
|
|
29124
29302
|
insertSegmentEnd() {
|
|
@@ -29137,6 +29315,9 @@ var init_chat_item_ledger = __esm({
|
|
|
29137
29315
|
/** 行落库了,把本段还挂着 NULL 的 assistant item 认领回去,并让后续 item 直接带上它。 */
|
|
29138
29316
|
bindMessage(messageId) {
|
|
29139
29317
|
this.messageId = messageId;
|
|
29318
|
+
for (const row of this.rows.values()) {
|
|
29319
|
+
if (row.role === "assistant" && row.messageId === null) row.messageId = messageId;
|
|
29320
|
+
}
|
|
29140
29321
|
if (!this.persistEnabled) return;
|
|
29141
29322
|
this.enqueue(async () => {
|
|
29142
29323
|
await this.deps.items.bindMessage(this.deps.sessionId, this.deps.turnId, messageId);
|
|
@@ -29196,7 +29377,8 @@ var init_chat_item_ledger = __esm({
|
|
|
29196
29377
|
itemId: id,
|
|
29197
29378
|
version: row.wireVersion,
|
|
29198
29379
|
startedVersion: row.startedVersion,
|
|
29199
|
-
ord: row.ord
|
|
29380
|
+
ord: row.ord,
|
|
29381
|
+
messageId: row.messageId
|
|
29200
29382
|
};
|
|
29201
29383
|
if (event.opShape.op === "set_status") {
|
|
29202
29384
|
const followUp = this.applyOpTo(row, event);
|
|
@@ -29232,7 +29414,7 @@ var init_chat_item_ledger = __esm({
|
|
|
29232
29414
|
}
|
|
29233
29415
|
/** 这一次写的回执。`persist` 已经在里面走过钟了,这里只是把读数带出去给 wire。 */
|
|
29234
29416
|
outcomeOf(row) {
|
|
29235
|
-
return { itemId: row.id, version: row.wireVersion, ord: row.ord };
|
|
29417
|
+
return { itemId: row.id, version: row.wireVersion, ord: row.ord, messageId: row.messageId };
|
|
29236
29418
|
}
|
|
29237
29419
|
/** 关掉所有还 streaming 的行(收尾/切段)。 */
|
|
29238
29420
|
closeStreamingRows(status) {
|
|
@@ -29284,13 +29466,16 @@ var init_chat_item_ledger = __esm({
|
|
|
29284
29466
|
origin: input.origin,
|
|
29285
29467
|
inContent: input.inContent,
|
|
29286
29468
|
persist: input.persist,
|
|
29287
|
-
segment: this.segment
|
|
29469
|
+
segment: this.segment,
|
|
29470
|
+
/* 归属在**建行那一刻**定死:链是异步的,等它执行时 this.messageId 可能已被切段改了。
|
|
29471
|
+
记在行上而不是只记在入库那一步——wire 帧也要带它(2026-09-11),两边必须同一个值。 */
|
|
29472
|
+
messageId: input.forceNullMessage ? null : this.messageId
|
|
29288
29473
|
};
|
|
29289
29474
|
this.rows.set(id, row);
|
|
29290
29475
|
this.createdCount += 1;
|
|
29291
29476
|
if (!input.forceNullMessage) this.segmentItemCount += 1;
|
|
29292
29477
|
if (!input.persist || !this.persistEnabled) return id;
|
|
29293
|
-
const messageId =
|
|
29478
|
+
const messageId = row.messageId;
|
|
29294
29479
|
const createdAt = this.now();
|
|
29295
29480
|
const payload = input.payload ?? this.payloadOf(row);
|
|
29296
29481
|
const metadata = input.metadata ?? { [CHAT_ITEM_META_LEGACY_CONTENT]: input.inContent };
|
|
@@ -29473,6 +29658,7 @@ async function openAssistantRunningRow(deps) {
|
|
|
29473
29658
|
async function finalizeAssistantRow(deps) {
|
|
29474
29659
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
29475
29660
|
const finishedAt = now();
|
|
29661
|
+
const parts = await appendOutputsPart(deps.parts, deps.outputsPart);
|
|
29476
29662
|
if (deps.assistantMsgId) {
|
|
29477
29663
|
if (typeof deps.chatStore.updateMessage !== "function") return;
|
|
29478
29664
|
try {
|
|
@@ -29480,7 +29666,7 @@ async function finalizeAssistantRow(deps) {
|
|
|
29480
29666
|
content: deps.content,
|
|
29481
29667
|
status: deps.status,
|
|
29482
29668
|
completedAt: finishedAt,
|
|
29483
|
-
...
|
|
29669
|
+
...parts.length ? { parts } : {},
|
|
29484
29670
|
...deps.runId ? { runId: deps.runId } : {},
|
|
29485
29671
|
...deps.turnId ? { turnId: deps.turnId } : {}
|
|
29486
29672
|
});
|
|
@@ -29488,7 +29674,7 @@ async function finalizeAssistantRow(deps) {
|
|
|
29488
29674
|
}
|
|
29489
29675
|
return;
|
|
29490
29676
|
}
|
|
29491
|
-
if (!deps.content && !deps.runId &&
|
|
29677
|
+
if (!deps.content && !deps.runId && parts.length === 0) return;
|
|
29492
29678
|
if (typeof deps.chatStore.appendMessage !== "function") return;
|
|
29493
29679
|
try {
|
|
29494
29680
|
await deps.chatStore.appendMessage({
|
|
@@ -29501,11 +29687,18 @@ async function finalizeAssistantRow(deps) {
|
|
|
29501
29687
|
status: deps.status,
|
|
29502
29688
|
...deps.runId ? { runId: deps.runId } : {},
|
|
29503
29689
|
...deps.turnId ? { turnId: deps.turnId } : {},
|
|
29504
|
-
...
|
|
29690
|
+
...parts.length ? { parts } : {}
|
|
29505
29691
|
});
|
|
29506
29692
|
} catch {
|
|
29507
29693
|
}
|
|
29508
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
|
+
}
|
|
29509
29702
|
function runBackgroundAssistantChatTurn(deps) {
|
|
29510
29703
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
29511
29704
|
const log3 = deps.log ?? ((m2) => console.warn(m2));
|
|
@@ -29674,6 +29867,7 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
29674
29867
|
parts,
|
|
29675
29868
|
status,
|
|
29676
29869
|
...runId ? { runId } : {},
|
|
29870
|
+
...deps.outputsPart ? { outputsPart: deps.outputsPart } : {},
|
|
29677
29871
|
now
|
|
29678
29872
|
});
|
|
29679
29873
|
if (typeof deps.chatStore.updateSession === "function") {
|
|
@@ -51546,7 +51740,7 @@ var require_websocket = __commonJS({
|
|
|
51546
51740
|
var http2 = require("http");
|
|
51547
51741
|
var net = require("net");
|
|
51548
51742
|
var tls = require("tls");
|
|
51549
|
-
var { randomBytes: randomBytes13, createHash:
|
|
51743
|
+
var { randomBytes: randomBytes13, createHash: createHash39 } = require("crypto");
|
|
51550
51744
|
var { Duplex, Readable } = require("stream");
|
|
51551
51745
|
var { URL: URL2 } = require("url");
|
|
51552
51746
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -52214,7 +52408,7 @@ var require_websocket = __commonJS({
|
|
|
52214
52408
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
52215
52409
|
return;
|
|
52216
52410
|
}
|
|
52217
|
-
const digest =
|
|
52411
|
+
const digest = createHash39("sha1").update(key + GUID).digest("base64");
|
|
52218
52412
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
52219
52413
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
52220
52414
|
return;
|
|
@@ -52583,7 +52777,7 @@ var require_websocket_server = __commonJS({
|
|
|
52583
52777
|
var EventEmitter = require("events");
|
|
52584
52778
|
var http2 = require("http");
|
|
52585
52779
|
var { Duplex } = require("stream");
|
|
52586
|
-
var { createHash:
|
|
52780
|
+
var { createHash: createHash39 } = require("crypto");
|
|
52587
52781
|
var extension3 = require_extension();
|
|
52588
52782
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
52589
52783
|
var subprotocol2 = require_subprotocol();
|
|
@@ -52890,7 +53084,7 @@ var require_websocket_server = __commonJS({
|
|
|
52890
53084
|
);
|
|
52891
53085
|
}
|
|
52892
53086
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
52893
|
-
const digest =
|
|
53087
|
+
const digest = createHash39("sha1").update(key + GUID).digest("base64");
|
|
52894
53088
|
const headers = [
|
|
52895
53089
|
"HTTP/1.1 101 Switching Protocols",
|
|
52896
53090
|
"Upgrade: websocket",
|
|
@@ -160267,6 +160461,7 @@ var init_live_chat = __esm({
|
|
|
160267
160461
|
operation: "started",
|
|
160268
160462
|
itemVersion: outcome.startedVersion,
|
|
160269
160463
|
...ord !== void 0 ? { ord } : {},
|
|
160464
|
+
...outcome.messageId !== null ? { messageId: outcome.messageId } : {},
|
|
160270
160465
|
offset: 0,
|
|
160271
160466
|
payload: v3StartedPayload(event.role, event.attrs, itemType, event.kind)
|
|
160272
160467
|
};
|
|
@@ -160300,6 +160495,11 @@ var init_live_chat = __esm({
|
|
|
160300
160495
|
// 账本这一次写用掉的那一格钟——**库里那一行的 version 就是它**,快照与增量流同一套版本空间。
|
|
160301
160496
|
itemVersion: outcome.version,
|
|
160302
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 } : {},
|
|
160303
160503
|
...offset !== void 0 ? { offset } : {},
|
|
160304
160504
|
...payload !== void 0 ? { payload } : {}
|
|
160305
160505
|
};
|
|
@@ -161065,15 +161265,29 @@ var init_chat_attachment_files = __esm({
|
|
|
161065
161265
|
async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, attachments, retainLocallyOnFailure = false) {
|
|
161066
161266
|
const newId = deps.newId ?? import_node_crypto20.randomUUID;
|
|
161067
161267
|
const now = deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
161068
|
-
|
|
161069
|
-
|
|
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(),
|
|
161070
161273
|
chatSessionId,
|
|
161071
161274
|
text: text5,
|
|
161072
161275
|
...attachments?.length ? { attachments } : {},
|
|
161073
161276
|
...clientKey ? { clientKey } : {},
|
|
161074
161277
|
createdAt: now()
|
|
161075
161278
|
});
|
|
161076
|
-
|
|
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
|
+
}
|
|
161077
161291
|
const prepared = await buildChatAttachmentFiles(
|
|
161078
161292
|
batch.flatMap((m2) => m2.attachments ?? []),
|
|
161079
161293
|
deps.readBlob ?? (async (ref2) => {
|
|
@@ -161089,7 +161303,6 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
161089
161303
|
})));
|
|
161090
161304
|
const batchKey = `append:${chatSessionId}:${batch.map((m2) => m2.id).join(",")}`;
|
|
161091
161305
|
const ids2 = batch.map((m2) => m2.id);
|
|
161092
|
-
await deps.store.markPendingMessages?.(chatSessionId, ids2, "delivering").catch(() => void 0);
|
|
161093
161306
|
const clientKeys = batch.map((m2) => m2.clientKey).filter((k2) => !!k2);
|
|
161094
161307
|
const res = await deps.deliver(merged, batchKey, {
|
|
161095
161308
|
...clientKeys.length ? { clientKeys } : {},
|
|
@@ -161102,7 +161315,7 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
161102
161315
|
const nextState = res.accepted ? "delivered" : DEFINITELY_NOT_DELIVERED.has(res.reason ?? "") ? "queued" : "unconfirmed";
|
|
161103
161316
|
const returnToClient = retainLocallyOnFailure && !!clientKey && nextState === "queued";
|
|
161104
161317
|
if (returnToClient && deps.store.markPendingMessages) {
|
|
161105
|
-
await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled",
|
|
161318
|
+
await deps.store.markPendingMessages(chatSessionId, [submitted.id], "cancelled", `client-retained:${res.reason}`);
|
|
161106
161319
|
await deps.store.markPendingMessages(chatSessionId, ids2.filter((id) => id !== submitted.id), nextState, res.reason);
|
|
161107
161320
|
} else {
|
|
161108
161321
|
const transition = deps.store.markPendingMessages?.(chatSessionId, ids2, nextState, res.reason);
|
|
@@ -195758,7 +195971,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195758
195971
|
out.listDirCode = located.code;
|
|
195759
195972
|
if (located.location) out.location = located.location;
|
|
195760
195973
|
out.skipped.push({
|
|
195761
|
-
source: `${
|
|
195974
|
+
source: `${SESSION_OUTPUTS_DIR}/`,
|
|
195762
195975
|
code: located.code,
|
|
195763
195976
|
message: located.message ?? locationFailureMessage(located.code)
|
|
195764
195977
|
});
|
|
@@ -195766,7 +195979,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195766
195979
|
}
|
|
195767
195980
|
out.location = located.location;
|
|
195768
195981
|
}
|
|
195769
|
-
const listing = await port.listDir(child,
|
|
195982
|
+
const listing = await port.listDir(child, SESSION_OUTPUTS_DIR).catch((err) => ({
|
|
195770
195983
|
ok: false,
|
|
195771
195984
|
code: "INTERNAL",
|
|
195772
195985
|
message: err instanceof Error ? err.message : String(err)
|
|
@@ -195776,11 +195989,11 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195776
195989
|
if (listing.code === "NOT_FOUND") return out;
|
|
195777
195990
|
const code2 = listing.code;
|
|
195778
195991
|
const message = isLocationFailureCode(code2) ? listing.message ?? locationFailureMessage(code2) : listing.message ?? code2;
|
|
195779
|
-
out.skipped.push({ source: `${
|
|
195992
|
+
out.skipped.push({ source: `${SESSION_OUTPUTS_DIR}/`, code: code2, message });
|
|
195780
195993
|
return out;
|
|
195781
195994
|
}
|
|
195782
195995
|
out.listDirCode = "OK";
|
|
195783
|
-
const queue = [
|
|
195996
|
+
const queue = [SESSION_OUTPUTS_DIR];
|
|
195784
195997
|
const files = [];
|
|
195785
195998
|
let guard = 0;
|
|
195786
195999
|
let hitFileCap = false;
|
|
@@ -195791,7 +196004,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195791
196004
|
}
|
|
195792
196005
|
guard += 1;
|
|
195793
196006
|
const dir = queue.shift();
|
|
195794
|
-
const level = dir ===
|
|
196007
|
+
const level = dir === SESSION_OUTPUTS_DIR ? listing : await port.listDir(child, dir).catch(() => ({ ok: false, code: "INTERNAL" }));
|
|
195795
196008
|
if (!level.ok) {
|
|
195796
196009
|
out.skipped.push({ source: `${dir}/`, code: level.code, message: level.code });
|
|
195797
196010
|
continue;
|
|
@@ -195817,7 +196030,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
|
|
|
195817
196030
|
});
|
|
195818
196031
|
}
|
|
195819
196032
|
for (const file of files) {
|
|
195820
|
-
const relative5 = normalizeRelPath(file.rel.slice(
|
|
196033
|
+
const relative5 = normalizeRelPath(file.rel.slice(SESSION_OUTPUTS_DIR.length + 1));
|
|
195821
196034
|
if (!relative5) {
|
|
195822
196035
|
out.skipped.push({ source: file.rel, code: "PATH_REJECTED", message: "\u8DEF\u5F84\u4E0D\u5408\u6CD5\uFF0C\u6CA1\u642C" });
|
|
195823
196036
|
continue;
|
|
@@ -204058,7 +204271,7 @@ async function startOasisServer(opts) {
|
|
|
204058
204271
|
const leftWaiterIds = /* @__PURE__ */ new Map();
|
|
204059
204272
|
const LEFT_WAITER_IDS_MAX_DRAFTS = 512;
|
|
204060
204273
|
const terminalReviews = /* @__PURE__ */ new Map();
|
|
204061
|
-
const notifyReviewResolved = (draftId, r, resume, storeFor) => {
|
|
204274
|
+
const notifyReviewResolved = (draftId, r, resume, storeFor, companyId) => {
|
|
204062
204275
|
if (terminalReviews.has(draftId)) return;
|
|
204063
204276
|
terminalReviews.set(draftId, r);
|
|
204064
204277
|
if (terminalReviews.size > 512) {
|
|
@@ -204078,7 +204291,7 @@ async function startOasisServer(opts) {
|
|
|
204078
204291
|
leftWaiterIds.delete(draftId);
|
|
204079
204292
|
if (resume?.origin) {
|
|
204080
204293
|
const needsAgentDelivery = resume.kind === "command" && !hadWaiters;
|
|
204081
|
-
void settleAuthorizationStatusLine(resume.origin, r, resume, draftId, needsAgentDelivery, storeFor);
|
|
204294
|
+
void settleAuthorizationStatusLine(resume.origin, r, resume, draftId, needsAgentDelivery, storeFor, companyId);
|
|
204082
204295
|
}
|
|
204083
204296
|
};
|
|
204084
204297
|
const resolveEngine = opts.resolveEngine ?? (async () => ({ kernel: opts.kernel, oplog: opts.oplog, blobs: opts.blobs, ...opts.registry ? { registry: opts.registry } : {} }));
|
|
@@ -204156,6 +204369,14 @@ async function startOasisServer(opts) {
|
|
|
204156
204369
|
}
|
|
204157
204370
|
return null;
|
|
204158
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
|
+
};
|
|
204159
204380
|
async function anchorMessageIdFor(origin, storeFor) {
|
|
204160
204381
|
if (!origin) return void 0;
|
|
204161
204382
|
const live = liveChat.messageFor(origin);
|
|
@@ -204220,7 +204441,7 @@ async function startOasisServer(opts) {
|
|
|
204220
204441
|
const label = ctx.effectLabel || ctx.command || "";
|
|
204221
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`;
|
|
204222
204443
|
};
|
|
204223
|
-
const settleAuthorizationStatusLine = async (origin, resolution, ctx, draftId, needsAgentDelivery, storeFor) => {
|
|
204444
|
+
const settleAuthorizationStatusLine = async (origin, resolution, ctx, draftId, needsAgentDelivery, storeFor, companyId) => {
|
|
204224
204445
|
const store = storeFor ? await storeFor().catch(() => null) : opts.chatSession;
|
|
204225
204446
|
if (!store) return;
|
|
204226
204447
|
const session = await store.getSession(origin).catch(() => null);
|
|
@@ -204293,10 +204514,12 @@ async function startOasisServer(opts) {
|
|
|
204293
204514
|
...dispatched.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: dispatched.supportsLiveProtocol } : {},
|
|
204294
204515
|
done: dispatched.done
|
|
204295
204516
|
};
|
|
204517
|
+
const resumeOutputsPart = outputsPartFor(origin, companyId);
|
|
204296
204518
|
runBackgroundAssistantChatTurn({
|
|
204297
204519
|
chatStore: store,
|
|
204298
204520
|
chatSessionId: origin,
|
|
204299
|
-
session: bgSession
|
|
204521
|
+
session: bgSession,
|
|
204522
|
+
...resumeOutputsPart ? { outputsPart: resumeOutputsPart } : {}
|
|
204300
204523
|
});
|
|
204301
204524
|
await markDelivered();
|
|
204302
204525
|
};
|
|
@@ -204552,10 +204775,12 @@ async function startOasisServer(opts) {
|
|
|
204552
204775
|
...session.supportsLiveProtocol !== void 0 ? { supportsLiveProtocol: session.supportsLiveProtocol } : {},
|
|
204553
204776
|
...session.done ? { done: session.done } : {}
|
|
204554
204777
|
};
|
|
204778
|
+
const broadcastOutputsPart = outputsPartFor(sessionId, companyId);
|
|
204555
204779
|
runBackgroundAssistantChatTurn({
|
|
204556
204780
|
chatStore: store,
|
|
204557
204781
|
chatSessionId: sessionId,
|
|
204558
204782
|
session: bgSession,
|
|
204783
|
+
...broadcastOutputsPart ? { outputsPart: broadcastOutputsPart } : {},
|
|
204559
204784
|
liveChat,
|
|
204560
204785
|
liveHandle: {
|
|
204561
204786
|
runtimeSessionId: session.id,
|
|
@@ -205533,7 +205758,8 @@ async function startOasisServer(opts) {
|
|
|
205533
205758
|
draftId,
|
|
205534
205759
|
{ status: "approved", command, effectLabel: review.command.effectLabel, authorizedBy: actor, receipt },
|
|
205535
205760
|
{ origin: review.origin, kind: "command", authorizedBy: actor, effectLabel: review.command.effectLabel, command },
|
|
205536
|
-
chatStoreNowOrNull
|
|
205761
|
+
chatStoreNowOrNull,
|
|
205762
|
+
currentCompanyId
|
|
205537
205763
|
);
|
|
205538
205764
|
res.writeHead(200, { "content-type": "application/json" }).end(
|
|
205539
205765
|
JSON.stringify({ applied: 1, command, artifactId, draftId, draftedBy: review.proposedBy, authorizedBy: actor })
|
|
@@ -206721,6 +206947,7 @@ ${composed}`;
|
|
|
206721
206947
|
await checkpointChain.catch(() => void 0);
|
|
206722
206948
|
await itemLedger.drain();
|
|
206723
206949
|
const projectedContent = itemLedger.projectContent();
|
|
206950
|
+
const chatOutputsPart = outputsPartFor(persistTarget.id, currentCompanyId);
|
|
206724
206951
|
await finalizeAssistantRow({
|
|
206725
206952
|
chatStore,
|
|
206726
206953
|
chatSessionId: persistTarget.id,
|
|
@@ -206730,6 +206957,7 @@ ${composed}`;
|
|
|
206730
206957
|
status,
|
|
206731
206958
|
...session.runId ? { runId: session.runId } : {},
|
|
206732
206959
|
...heldTurn ? { turnId: heldTurn.id } : {},
|
|
206960
|
+
...chatOutputsPart ? { outputsPart: chatOutputsPart } : {},
|
|
206733
206961
|
now: () => finishedAt
|
|
206734
206962
|
});
|
|
206735
206963
|
await heldTurn?.settle({
|
|
@@ -213729,6 +213957,17 @@ var init_dev_store = __esm({
|
|
|
213729
213957
|
/** ADR-0166 D6 待发队列(每会话一条,按 seq 升序 = 入队序)。 */
|
|
213730
213958
|
pending = /* @__PURE__ */ new Map();
|
|
213731
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
|
+
}
|
|
213732
213971
|
async enqueuePendingMessage(m2) {
|
|
213733
213972
|
const list2 = this.pending.get(m2.chatSessionId) ?? [];
|
|
213734
213973
|
if (m2.clientKey) {
|
|
@@ -214096,6 +214335,11 @@ var init_dev_store = __esm({
|
|
|
214096
214335
|
return r;
|
|
214097
214336
|
}
|
|
214098
214337
|
// 待发队列也要落盘——它存在的全部理由就是"别只活在一处内存里"(ADR-0166 D6)。
|
|
214338
|
+
async claimPendingInsert(m2) {
|
|
214339
|
+
const result = await super.claimPendingInsert(m2);
|
|
214340
|
+
this.save();
|
|
214341
|
+
return result;
|
|
214342
|
+
}
|
|
214099
214343
|
async enqueuePendingMessage(m2) {
|
|
214100
214344
|
const record8 = await super.enqueuePendingMessage(m2);
|
|
214101
214345
|
this.save();
|
|
@@ -214943,6 +215187,74 @@ var init_company_scoped_registry = __esm({
|
|
|
214943
215187
|
}
|
|
214944
215188
|
});
|
|
214945
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
|
+
|
|
214946
215258
|
// ../server/src/org-model-backfill.ts
|
|
214947
215259
|
function resolveDisplayName(existing, humanActor, accountName) {
|
|
214948
215260
|
const pick3 = existing?.trim() || humanActor?.name?.trim() || accountName?.trim();
|
|
@@ -221367,11 +221679,11 @@ function aggregateUsageByActor(runs) {
|
|
|
221367
221679
|
function weekWindow(now = /* @__PURE__ */ new Date()) {
|
|
221368
221680
|
return { from: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3).toISOString(), to: now.toISOString() };
|
|
221369
221681
|
}
|
|
221370
|
-
async function listWeeklyUsageByActor(trace) {
|
|
221682
|
+
async function listWeeklyUsageByActor(trace, artifactIds) {
|
|
221371
221683
|
if (!trace?.listUsageRuns) return null;
|
|
221372
221684
|
const { from, to } = weekWindow();
|
|
221373
221685
|
try {
|
|
221374
|
-
return aggregateUsageByActor(await trace.listUsageRuns({ from, to }));
|
|
221686
|
+
return aggregateUsageByActor(await trace.listUsageRuns({ from, to, ...artifactIds ? { artifactIds: [...artifactIds] } : {} }));
|
|
221375
221687
|
} catch {
|
|
221376
221688
|
return null;
|
|
221377
221689
|
}
|
|
@@ -221973,9 +222285,15 @@ function actorsDomain(opts) {
|
|
|
221973
222285
|
const revealLimiter = new RevealRateLimiter(revealLimits.perSession, revealLimits.perSessionPerKey);
|
|
221974
222286
|
const consoleRevealLimits = opts.credentialRevealLimits?.console ?? { perSession: 60, perSessionPerKey: 10 };
|
|
221975
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
|
+
}
|
|
221976
222293
|
return (router) => {
|
|
221977
222294
|
router.get("/api/actors", async (req) => {
|
|
221978
|
-
const
|
|
222295
|
+
const ctx = await resolveCtx(req.auth.companyId);
|
|
222296
|
+
const { service } = ctx;
|
|
221979
222297
|
const filter = {};
|
|
221980
222298
|
const kind = req.query.get("kind");
|
|
221981
222299
|
const teamId = req.query.get("team_id");
|
|
@@ -221987,9 +222305,10 @@ function actorsDomain(opts) {
|
|
|
221987
222305
|
const includeDeleted = req.query.get("include_deleted") === "1";
|
|
221988
222306
|
const items = status || includeDeleted ? all2 : all2.filter((a) => a.kind !== "agent" || !isDeletedActor(a));
|
|
221989
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 }));
|
|
221990
222309
|
const [configs, usageByActor] = await Promise.all([
|
|
221991
222310
|
service.latestConfigs(agentIds),
|
|
221992
|
-
listWeeklyUsageByActor(
|
|
222311
|
+
listWeeklyUsageByActor(usageSource.usage, usageSource.artifactIds)
|
|
221993
222312
|
]);
|
|
221994
222313
|
return {
|
|
221995
222314
|
status: 200,
|
|
@@ -222665,9 +222984,8 @@ function actorsDomain(opts) {
|
|
|
222665
222984
|
return {
|
|
222666
222985
|
status: 200,
|
|
222667
222986
|
body: await actorStats(req.params.id, {
|
|
222668
|
-
|
|
222669
|
-
model
|
|
222670
|
-
...req.auth.companyId ? { artifactIds: new Set(model?.artifacts.keys() ?? []) } : {}
|
|
222987
|
+
...await usageFor(req.auth.companyId, ctx),
|
|
222988
|
+
model
|
|
222671
222989
|
})
|
|
222672
222990
|
};
|
|
222673
222991
|
});
|
|
@@ -223251,7 +223569,7 @@ function createActorsDomain(opts) {
|
|
|
223251
223569
|
return {
|
|
223252
223570
|
service: defaultCtx.service,
|
|
223253
223571
|
resolveCtx,
|
|
223254
|
-
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.isCompanyManager ? { isCompanyManager: opts.isCompanyManager } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {} })
|
|
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 } : {} })
|
|
223255
223573
|
};
|
|
223256
223574
|
}
|
|
223257
223575
|
var import_node_crypto49;
|
|
@@ -228494,7 +228812,7 @@ function reasonFromWorkdirCode(code2) {
|
|
|
228494
228812
|
return "ERROR";
|
|
228495
228813
|
}
|
|
228496
228814
|
}
|
|
228497
|
-
function
|
|
228815
|
+
function joinRel2(dir, name) {
|
|
228498
228816
|
return dir ? `${dir}/${name}` : name;
|
|
228499
228817
|
}
|
|
228500
228818
|
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
@@ -228527,14 +228845,14 @@ async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
|
228527
228845
|
if (outcome.truncated) truncated = true;
|
|
228528
228846
|
for (const entry of outcome.entries) {
|
|
228529
228847
|
if (entry.type === "dir") {
|
|
228530
|
-
nextDirs.push(
|
|
228848
|
+
nextDirs.push(joinRel2(dir, entry.name));
|
|
228531
228849
|
continue;
|
|
228532
228850
|
}
|
|
228533
228851
|
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
228534
228852
|
truncated = true;
|
|
228535
228853
|
continue;
|
|
228536
228854
|
}
|
|
228537
|
-
const rel =
|
|
228855
|
+
const rel = joinRel2(dir, entry.name);
|
|
228538
228856
|
items.push({
|
|
228539
228857
|
id: `workdir:${rel}`,
|
|
228540
228858
|
name: entry.name,
|
|
@@ -231227,6 +231545,9 @@ function nodesDomain(deps) {
|
|
|
231227
231545
|
const rts = runtimesByNode.get(n.id) ?? [];
|
|
231228
231546
|
const daemonVersion = daemonVersionById.get(n.id);
|
|
231229
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;
|
|
231230
231551
|
const activeRunCount = deps.activeRunCountOf?.(n.id) ?? 0;
|
|
231231
231552
|
const updateStatus = reconcileNodeUpdate(n.id, daemonVersion, activeRunCount, healthNow);
|
|
231232
231553
|
return {
|
|
@@ -231240,10 +231561,10 @@ function nodesDomain(deps) {
|
|
|
231240
231561
|
...sourceIpById.get(n.id) ? { sourceIp: sourceIpById.get(n.id) } : {},
|
|
231241
231562
|
...n.name ? { name: n.name } : {},
|
|
231242
231563
|
...updateStatus ? { updateStatus } : {},
|
|
231243
|
-
...
|
|
231564
|
+
...lastDisconnectedAt || lastReconnectedAt ? {
|
|
231244
231565
|
connection: {
|
|
231245
|
-
...
|
|
231246
|
-
...
|
|
231566
|
+
...lastDisconnectedAt ? { lastDisconnectedAt } : {},
|
|
231567
|
+
...lastReconnectedAt ? { lastReconnectedAt } : {}
|
|
231247
231568
|
}
|
|
231248
231569
|
} : {},
|
|
231249
231570
|
...deps.nodeHealth ? {
|
|
@@ -247647,6 +247968,7 @@ var init_postgres_chat_sessions = __esm({
|
|
|
247647
247968
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS escalation_id text`);
|
|
247648
247969
|
await pool.query(`ALTER TABLE ${q2}.chat_sessions ADD COLUMN IF NOT EXISTS model text`);
|
|
247649
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`);
|
|
247650
247972
|
await pool.query(`
|
|
247651
247973
|
WITH ranked AS (
|
|
247652
247974
|
SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
|
|
@@ -247877,6 +248199,23 @@ var init_postgres_chat_sessions = __esm({
|
|
|
247877
248199
|
args
|
|
247878
248200
|
);
|
|
247879
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
|
+
}
|
|
247880
248219
|
/** 删项目时把本公司里绑到它的会话回落成「不指定项目」。会话一条都不删,只清 project_id。 */
|
|
247881
248220
|
async clearProjectBinding(projectId2) {
|
|
247882
248221
|
if (!projectId2) return 0;
|
|
@@ -248279,6 +248618,42 @@ var init_postgres_chat_sessions = __esm({
|
|
|
248279
248618
|
return r.rows.map(rowToMessage);
|
|
248280
248619
|
}
|
|
248281
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
|
+
}
|
|
248282
248657
|
async enqueuePendingMessage(m2) {
|
|
248283
248658
|
await this.assertSessionInScope(m2.chatSessionId);
|
|
248284
248659
|
const initialState = m2.state;
|
|
@@ -253695,9 +254070,19 @@ var init_cutover_aware_trace = __esm({
|
|
|
253695
254070
|
constructor(o) {
|
|
253696
254071
|
this.o = o;
|
|
253697
254072
|
}
|
|
253698
|
-
|
|
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() {
|
|
253699
254076
|
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
253700
|
-
|
|
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;
|
|
253701
254086
|
}
|
|
253702
254087
|
async pickWritable() {
|
|
253703
254088
|
const row = await this.o.cutover.get(this.o.companyId, "trace");
|
|
@@ -257981,6 +258366,7 @@ var init_src11 = __esm({
|
|
|
257981
258366
|
init_tenancy_move_verify();
|
|
257982
258367
|
init_company_scoped_registry();
|
|
257983
258368
|
init_session_workdir();
|
|
258369
|
+
init_outputs_scan();
|
|
257984
258370
|
init_org_model_backfill();
|
|
257985
258371
|
init_types_admin();
|
|
257986
258372
|
init_roles_admin();
|
|
@@ -258390,6 +258776,13 @@ init_src11();
|
|
|
258390
258776
|
init_src();
|
|
258391
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";
|
|
258392
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");
|
|
258393
258786
|
function buildChatTaskHeader(input) {
|
|
258394
258787
|
if (input.kind === "isolated-knowledge") {
|
|
258395
258788
|
return {
|
|
@@ -258410,7 +258803,9 @@ function buildChatTaskHeader(input) {
|
|
|
258410
258803
|
`your_actor_id: ${input.actorId}`,
|
|
258411
258804
|
`workspace: ${input.workspace}`,
|
|
258412
258805
|
"",
|
|
258413
|
-
CONVERSATIONAL_MANAGER_BODY
|
|
258806
|
+
CONVERSATIONAL_MANAGER_BODY,
|
|
258807
|
+
"",
|
|
258808
|
+
OUTPUTS_CONVENTION
|
|
258414
258809
|
]
|
|
258415
258810
|
};
|
|
258416
258811
|
}
|
|
@@ -258422,7 +258817,9 @@ function buildChatTaskHeader(input) {
|
|
|
258422
258817
|
input.sessionLine,
|
|
258423
258818
|
`your_actor_id: ${input.actorId}`,
|
|
258424
258819
|
"",
|
|
258425
|
-
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]
|
|
258426
258823
|
],
|
|
258427
258824
|
...routed ? { routingSystemPrompt: EXECUTION_ROUTING_BODY } : {}
|
|
258428
258825
|
};
|
|
@@ -259597,6 +259994,12 @@ async function startServe(opts) {
|
|
|
259597
259994
|
kernel: defaultCompanyKernel,
|
|
259598
259995
|
blobs: assets,
|
|
259599
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
|
+
},
|
|
259600
260003
|
audit: registryAudit,
|
|
259601
260004
|
// ADR 凭据保险库 §4.5/§4.7 / ADR 0125:reveal 判权的项目解析 + 凭据读取审计读写。
|
|
259602
260005
|
resolveDispatchScope: resolveDispatchScope2,
|
|
@@ -261615,6 +262018,39 @@ async function startServe(opts) {
|
|
|
261615
262018
|
// 三个方法的组合规则住在 `createDelegationWorkdirPort`(`@oasis/server`)——评审
|
|
261616
262019
|
// wo:acfee725 blocker ② 回归防线:**serve.ts 与单测走同一处**,那样「装配层退回全局 binding」
|
|
261617
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
|
+
},
|
|
261618
262054
|
delegationWorkdir: createDelegationWorkdirPort({
|
|
261619
262055
|
locator: delegationWorkdirLocator,
|
|
261620
262056
|
// 桥在 hub 建好后(本函数尾部)才赋值 → getter 每次调用时读最新值;早于此处的构造时 `workdirBridge`
|
|
@@ -265300,6 +265736,7 @@ async function runSession(dispatchId, job, deps) {
|
|
|
265300
265736
|
const cancelledBeforeStart = deps.wasCancelled?.() ?? false;
|
|
265301
265737
|
const normalizer = new ProviderStreamNormalizer({
|
|
265302
265738
|
providerName: job.binding?.runtimeKind ?? "runtime",
|
|
265739
|
+
hasLiveProtocol: handle.supportsLiveProtocol === true,
|
|
265303
265740
|
fallbackTurnId: `oasis-turn:${handle.id}`,
|
|
265304
265741
|
emit: (event) => deps.sink.stream({ type: "session_normalized_event", dispatchId, event })
|
|
265305
265742
|
});
|
|
@@ -272392,7 +272829,7 @@ function shimScript() {
|
|
|
272392
272829
|
}
|
|
272393
272830
|
|
|
272394
272831
|
// src/index.ts
|
|
272395
|
-
var PKG_VERSION = true ? "2.2.
|
|
272832
|
+
var PKG_VERSION = true ? "2.2.20" : "dev";
|
|
272396
272833
|
var LOCAL_BIN = localBin();
|
|
272397
272834
|
var NPM_PREFIX = npmPrefix();
|
|
272398
272835
|
var INSTANCE = DEFAULT_INSTANCE;
|