oasis_test_v2 2.2.33 → 2.2.34
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 +1057 -505
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -370,10 +370,11 @@ function commentThreadSessionId(a, b2) {
|
|
|
370
370
|
function reviewThreadSessionId(a, b2) {
|
|
371
371
|
return `${REVIEW_THREAD_SESSION_PREFIX}${[a, b2].sort().join("|")}`;
|
|
372
372
|
}
|
|
373
|
-
var GAP_THREAD_SESSION_PREFIX, COMMENT_THREAD_SESSION_PREFIX, REVIEW_THREAD_SESSION_PREFIX;
|
|
373
|
+
var WORKORDER_HOLD_AT_CREATE_REASON, GAP_THREAD_SESSION_PREFIX, COMMENT_THREAD_SESSION_PREFIX, REVIEW_THREAD_SESSION_PREFIX;
|
|
374
374
|
var init_collab_view = __esm({
|
|
375
375
|
"../contract/src/collab-view.ts"() {
|
|
376
376
|
"use strict";
|
|
377
|
+
WORKORDER_HOLD_AT_CREATE_REASON = "\u5EFA\u5355\u65F6\u9009\u62E9\u7A0D\u540E\u6D3E\u53D1";
|
|
377
378
|
GAP_THREAD_SESSION_PREFIX = "gapline:";
|
|
378
379
|
COMMENT_THREAD_SESSION_PREFIX = "commentline:";
|
|
379
380
|
REVIEW_THREAD_SESSION_PREFIX = "reviewline:";
|
|
@@ -406,9 +407,22 @@ var init_inbox_read = __esm({
|
|
|
406
407
|
});
|
|
407
408
|
|
|
408
409
|
// ../contract/src/notifications.ts
|
|
410
|
+
var LEDGER_INBOX_KINDS;
|
|
409
411
|
var init_notifications = __esm({
|
|
410
412
|
"../contract/src/notifications.ts"() {
|
|
411
413
|
"use strict";
|
|
414
|
+
LEDGER_INBOX_KINDS = /* @__PURE__ */ new Set([
|
|
415
|
+
"review-request",
|
|
416
|
+
"rework",
|
|
417
|
+
"stale",
|
|
418
|
+
"pushback",
|
|
419
|
+
"assigned",
|
|
420
|
+
"needs-staffing",
|
|
421
|
+
"gap-escalation",
|
|
422
|
+
"change-confirm",
|
|
423
|
+
"review-waiting",
|
|
424
|
+
"work-waiting"
|
|
425
|
+
]);
|
|
412
426
|
}
|
|
413
427
|
});
|
|
414
428
|
|
|
@@ -1647,7 +1661,7 @@ var init_file_preview = __esm({
|
|
|
1647
1661
|
"../contract/src/file-preview.ts"() {
|
|
1648
1662
|
"use strict";
|
|
1649
1663
|
OFFICE_PREVIEW_MAX_BYTES = 20 * 1024 * 1024;
|
|
1650
|
-
FILE_CONTENT_MAX_BYTES =
|
|
1664
|
+
FILE_CONTENT_MAX_BYTES = 200 * 1024 * 1024;
|
|
1651
1665
|
}
|
|
1652
1666
|
});
|
|
1653
1667
|
|
|
@@ -21293,14 +21307,17 @@ var init_kernel_bridge = __esm({
|
|
|
21293
21307
|
});
|
|
21294
21308
|
}
|
|
21295
21309
|
}
|
|
21296
|
-
/**
|
|
21297
|
-
|
|
21310
|
+
/**
|
|
21311
|
+
* 工单级暂停:workorder.paused handler 杀全部在途会话 + 挡新派发。
|
|
21312
|
+
* `reason` 缺省「暂停派发」(人点暂停);建单即挂起的入口传 `WORKORDER_HOLD_AT_CREATE_REASON`。
|
|
21313
|
+
*/
|
|
21314
|
+
async pauseWorkorder(workorderId, actor, reason = "\u6682\u505C\u6D3E\u53D1") {
|
|
21298
21315
|
this.trackedWorkorders.add(workorderId);
|
|
21299
21316
|
await this.commit({
|
|
21300
21317
|
companyId: "",
|
|
21301
21318
|
workorderId,
|
|
21302
21319
|
actorId: actor,
|
|
21303
|
-
event: { kind: "workorder.paused", workorderId, by: actor, reason
|
|
21320
|
+
event: { kind: "workorder.paused", workorderId, by: actor, reason }
|
|
21304
21321
|
});
|
|
21305
21322
|
}
|
|
21306
21323
|
/**
|
|
@@ -22033,12 +22050,12 @@ async function readArtifactFile(kernel, blobs, artifactId, path41, expectedRef,
|
|
|
22033
22050
|
if (!entry) throw new ApiError(404, "NOT_FOUND", "\u5305\u5185\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
22034
22051
|
hash2 = entry.hash;
|
|
22035
22052
|
name = entry.path;
|
|
22036
|
-
if (entry.size !== void 0 && entry.size > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE",
|
|
22053
|
+
if (entry.size !== void 0 && entry.size > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${FILE_CONTENT_MAX_BYTES / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
|
|
22037
22054
|
} else if (output.contentKind !== "inline-blob") {
|
|
22038
22055
|
throw new ApiError(404, "NOT_FOUND", "\u6B64\u4EA7\u7269\u6CA1\u6709\u53EF\u4E0B\u8F7D\u7684\u539F\u6587\u4EF6");
|
|
22039
22056
|
}
|
|
22040
22057
|
const meta = await blobs.meta?.(hash2);
|
|
22041
|
-
if (meta && meta.size > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE",
|
|
22058
|
+
if (meta && meta.size > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${FILE_CONTENT_MAX_BYTES / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
|
|
22042
22059
|
try {
|
|
22043
22060
|
return { bytes: await blobs.get(hash2), name, contentType: meta?.contentType };
|
|
22044
22061
|
} catch {
|
|
@@ -22065,7 +22082,7 @@ async function serveFileRequest(req, res, url, engine) {
|
|
|
22065
22082
|
url.searchParams.get("ref"),
|
|
22066
22083
|
url.searchParams.get("revisionId")
|
|
22067
22084
|
);
|
|
22068
|
-
if (bytes2.length > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE",
|
|
22085
|
+
if (bytes2.length > FILE_CONTENT_MAX_BYTES) throw new ApiError(413, "FILE_TOO_LARGE", `\u6587\u4EF6\u8D85\u8FC7 ${FILE_CONTENT_MAX_BYTES / 1024 / 1024} MiB \u8BFB\u53D6\u9650\u5236`);
|
|
22069
22086
|
res.writeHead(200, {
|
|
22070
22087
|
"content-type": contentType ?? sniffContentType(bytes2) ?? "application/octet-stream",
|
|
22071
22088
|
"content-length": String(bytes2.length),
|
|
@@ -25413,7 +25430,7 @@ async function runProtocolSession(job, cfg) {
|
|
|
25413
25430
|
let appendChain = Promise.resolve();
|
|
25414
25431
|
let eventSeq = 0;
|
|
25415
25432
|
let runtimeSessionId;
|
|
25416
|
-
|
|
25433
|
+
const pendingTurnMessages = [];
|
|
25417
25434
|
const stderrTail = [];
|
|
25418
25435
|
const finish = (info) => {
|
|
25419
25436
|
if (exited) return;
|
|
@@ -25452,7 +25469,15 @@ async function runProtocolSession(job, cfg) {
|
|
|
25452
25469
|
}
|
|
25453
25470
|
});
|
|
25454
25471
|
};
|
|
25472
|
+
const completeTurn = (message) => {
|
|
25473
|
+
if (exited || killReason) return;
|
|
25474
|
+
const result = cfg.turnResult(message, state);
|
|
25475
|
+
if (!result) return;
|
|
25476
|
+
finish(result);
|
|
25477
|
+
child.kill("SIGTERM");
|
|
25478
|
+
};
|
|
25455
25479
|
const handle = (message) => {
|
|
25480
|
+
if (exited || killReason) return;
|
|
25456
25481
|
const responseId = commandId(message.id);
|
|
25457
25482
|
if (isResponse(message) && responseId !== void 0 && pending.has(responseId)) {
|
|
25458
25483
|
const waiter = pending.get(responseId);
|
|
@@ -25468,13 +25493,15 @@ async function runProtocolSession(job, cfg) {
|
|
|
25468
25493
|
const event = { kind: line.kind, payload: line.payload, seq: ++eventSeq, ts: (/* @__PURE__ */ new Date()).toISOString() };
|
|
25469
25494
|
for (const cb of telemetryCbs) cb(event);
|
|
25470
25495
|
}
|
|
25471
|
-
if (
|
|
25472
|
-
|
|
25473
|
-
|
|
25474
|
-
|
|
25475
|
-
|
|
25476
|
-
|
|
25477
|
-
|
|
25496
|
+
if (!started) pendingTurnMessages.push(message);
|
|
25497
|
+
else completeTurn(message);
|
|
25498
|
+
};
|
|
25499
|
+
const consumeLine = (line) => {
|
|
25500
|
+
if (!line) return;
|
|
25501
|
+
transcript.write(line + "\n");
|
|
25502
|
+
try {
|
|
25503
|
+
handle(JSON.parse(line));
|
|
25504
|
+
} catch {
|
|
25478
25505
|
}
|
|
25479
25506
|
};
|
|
25480
25507
|
let buffer = "";
|
|
@@ -25484,13 +25511,7 @@ async function runProtocolSession(job, cfg) {
|
|
|
25484
25511
|
while (newline >= 0) {
|
|
25485
25512
|
const line = buffer.slice(0, newline).replace(/\r$/, "");
|
|
25486
25513
|
buffer = buffer.slice(newline + 1);
|
|
25487
|
-
|
|
25488
|
-
transcript.write(line + "\n");
|
|
25489
|
-
try {
|
|
25490
|
-
handle(JSON.parse(line));
|
|
25491
|
-
} catch {
|
|
25492
|
-
}
|
|
25493
|
-
}
|
|
25514
|
+
consumeLine(line);
|
|
25494
25515
|
newline = buffer.indexOf("\n");
|
|
25495
25516
|
}
|
|
25496
25517
|
});
|
|
@@ -25504,12 +25525,23 @@ async function runProtocolSession(job, cfg) {
|
|
|
25504
25525
|
killReason ??= "timeout";
|
|
25505
25526
|
child.kill("SIGKILL");
|
|
25506
25527
|
}, job.limits.wallClockMs);
|
|
25507
|
-
child.on("error", (error2) =>
|
|
25508
|
-
child.on("exit", (code2) => {
|
|
25528
|
+
child.on("error", (error2) => {
|
|
25509
25529
|
clearTimeout(timer);
|
|
25530
|
+
finish({ code: null, reason: "error", transcriptRef, errorMessage: error2.message });
|
|
25531
|
+
});
|
|
25532
|
+
child.on("close", (code2) => {
|
|
25533
|
+
clearTimeout(timer);
|
|
25534
|
+
consumeLine(buffer.trim());
|
|
25535
|
+
buffer = "";
|
|
25536
|
+
transcript.end();
|
|
25510
25537
|
if (!exited) {
|
|
25511
25538
|
const reason = killReason ?? (code2 === 0 ? "clean" : "error");
|
|
25512
|
-
|
|
25539
|
+
const info = {
|
|
25540
|
+
code: killReason ? null : code2,
|
|
25541
|
+
reason,
|
|
25542
|
+
...reason === "error" && stderrTail.length ? { errorMessage: stderrTail.join("\n").slice(-4e3) } : {}
|
|
25543
|
+
};
|
|
25544
|
+
finish(!killReason && cfg.processExitInfo ? cfg.processExitInfo(info, state) : info);
|
|
25513
25545
|
}
|
|
25514
25546
|
});
|
|
25515
25547
|
void (async () => {
|
|
@@ -25523,10 +25555,7 @@ async function runProtocolSession(job, cfg) {
|
|
|
25523
25555
|
await cfg.start(send, job, dir, task, state);
|
|
25524
25556
|
runtimeSessionId ??= cfg.runtimeSessionId?.(state);
|
|
25525
25557
|
started = true;
|
|
25526
|
-
|
|
25527
|
-
finish({ code: 0, reason: "clean", transcriptRef, ...runtimeSessionId ? { runtimeSessionId } : {} });
|
|
25528
|
-
child.kill("SIGTERM");
|
|
25529
|
-
}
|
|
25558
|
+
for (const message of pendingTurnMessages.splice(0)) completeTurn(message);
|
|
25530
25559
|
} catch (error2) {
|
|
25531
25560
|
if (!exited) {
|
|
25532
25561
|
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -25735,7 +25764,7 @@ function codexSessionsRoot() {
|
|
|
25735
25764
|
}
|
|
25736
25765
|
function listRecentRollouts(root, sinceMs) {
|
|
25737
25766
|
const margin = sinceMs - 6e4;
|
|
25738
|
-
const
|
|
25767
|
+
const found2 = [];
|
|
25739
25768
|
const walk = (d) => {
|
|
25740
25769
|
let ents;
|
|
25741
25770
|
try {
|
|
@@ -25752,16 +25781,16 @@ function listRecentRollouts(root, sinceMs) {
|
|
|
25752
25781
|
m2 = fs8.statSync(p2).mtimeMs;
|
|
25753
25782
|
} catch {
|
|
25754
25783
|
}
|
|
25755
|
-
if (m2 >= margin)
|
|
25784
|
+
if (m2 >= margin) found2.push({ f: p2, m: m2 });
|
|
25756
25785
|
}
|
|
25757
25786
|
}
|
|
25758
25787
|
};
|
|
25759
25788
|
walk(root);
|
|
25760
|
-
return
|
|
25789
|
+
return found2.sort((a, b2) => b2.m - a.m).map((x2) => x2.f);
|
|
25761
25790
|
}
|
|
25762
25791
|
function findRolloutsBySessionId(root, sessionId) {
|
|
25763
25792
|
const suffix = `-${sessionId}.jsonl`;
|
|
25764
|
-
const
|
|
25793
|
+
const found2 = [];
|
|
25765
25794
|
const walk = (d) => {
|
|
25766
25795
|
let ents;
|
|
25767
25796
|
try {
|
|
@@ -25772,11 +25801,11 @@ function findRolloutsBySessionId(root, sessionId) {
|
|
|
25772
25801
|
for (const ent of ents) {
|
|
25773
25802
|
const p2 = path10.join(d, ent.name);
|
|
25774
25803
|
if (ent.isDirectory()) walk(p2);
|
|
25775
|
-
else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix))
|
|
25804
|
+
else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(suffix)) found2.push(p2);
|
|
25776
25805
|
}
|
|
25777
25806
|
};
|
|
25778
25807
|
walk(root);
|
|
25779
|
-
return
|
|
25808
|
+
return found2;
|
|
25780
25809
|
}
|
|
25781
25810
|
function telemetryFromRolloutLines(lines, sinceMs) {
|
|
25782
25811
|
const usage = parseCodexTokenUsage(lines, sinceMs);
|
|
@@ -26036,6 +26065,15 @@ function normalizeCodexConsoleLine(line, state = createCodexNormalizeState()) {
|
|
|
26036
26065
|
tool.output.push(line);
|
|
26037
26066
|
return [];
|
|
26038
26067
|
}
|
|
26068
|
+
function codexTurnErrorMessage(value2) {
|
|
26069
|
+
const error2 = record2(value2);
|
|
26070
|
+
const code2 = error2?.codexErrorInfo;
|
|
26071
|
+
const label = typeof code2 === "string" ? code2 : code2 ? JSON.stringify(code2) : "unknown";
|
|
26072
|
+
const message = typeof error2?.message === "string" && error2.message.trim() ? error2.message.trim() : "Codex turn failed without error details";
|
|
26073
|
+
const summary = code2 === "cyberPolicy" || code2 === "misalignmentPolicyViolation" ? `Codex \u5B89\u5168\u7B56\u7565\u62D2\u7EDD [${label}]\uFF1A${message}` : `Codex [${label}]: ${message}`;
|
|
26074
|
+
const detail = typeof error2?.additionalDetails === "string" ? error2.additionalDetails.trim() : "";
|
|
26075
|
+
return [summary, detail].filter(Boolean).join("\n").slice(0, 4e3);
|
|
26076
|
+
}
|
|
26039
26077
|
var import_node_child_process7, import_node_crypto11, fs8, os5, path10, readline2, CODEX_BLOCKED_FLAGS, CODEX_ARGV_PROMPT_MAX_BYTES, CODEX_APP_SERVER_BLOCKED_FLAGS, CodexAdapter;
|
|
26040
26078
|
var init_codex = __esm({
|
|
26041
26079
|
"../adapters/src/codex/index.ts"() {
|
|
@@ -26119,6 +26157,7 @@ var init_codex = __esm({
|
|
|
26119
26157
|
threadId: void 0,
|
|
26120
26158
|
turnId: void 0,
|
|
26121
26159
|
active: false,
|
|
26160
|
+
errorsByTurn: /* @__PURE__ */ new Map(),
|
|
26122
26161
|
pendingInputs: /* @__PURE__ */ new Set(),
|
|
26123
26162
|
codex: createCodexNormalizeState(),
|
|
26124
26163
|
// 本次 run 的用量:`thread/tokenUsage/updated` 的 `last`(见该解析函数注释)按轮次记下,
|
|
@@ -26202,6 +26241,9 @@ ${task}` : task;
|
|
|
26202
26241
|
}
|
|
26203
26242
|
}
|
|
26204
26243
|
if (method === "turn/completed") {
|
|
26244
|
+
const turn = record2(params?.turn);
|
|
26245
|
+
const failure = typeof turn?.id === "string" ? state.errorsByTurn.get(turn.id) : void 0;
|
|
26246
|
+
if (params?.threadId !== state.threadId || turn?.id !== state.turnId || turn?.status !== "completed" || turn.error || failure && !failure.willRetry) return [];
|
|
26205
26247
|
return [{ ...base, itemId: "turn-end", itemType: "control", operation: "completed", payload: { code: "output_block_completed" } }];
|
|
26206
26248
|
}
|
|
26207
26249
|
return [];
|
|
@@ -26209,10 +26251,15 @@ ${task}` : task;
|
|
|
26209
26251
|
normalize(message, state) {
|
|
26210
26252
|
const params = message.params;
|
|
26211
26253
|
const method = typeof message.method === "string" ? message.method : "";
|
|
26254
|
+
if (method === "error" && params?.threadId === state.threadId && typeof params?.turnId === "string") {
|
|
26255
|
+
state.errorsByTurn.set(params.turnId, { error: params.error, willRetry: params.willRetry === true });
|
|
26256
|
+
}
|
|
26212
26257
|
if (method === "thread/started") {
|
|
26213
26258
|
const thread = params?.thread;
|
|
26214
|
-
if (typeof thread?.id === "string"
|
|
26215
|
-
|
|
26259
|
+
if (typeof thread?.id === "string" && (!state.threadId || state.threadId === thread.id)) {
|
|
26260
|
+
state.threadId = thread.id;
|
|
26261
|
+
if (typeof thread.path === "string") state.rolloutPath = thread.path;
|
|
26262
|
+
}
|
|
26216
26263
|
}
|
|
26217
26264
|
if (method === "thread/tokenUsage/updated") {
|
|
26218
26265
|
const turn = parseCodexAppServerTurnUsage(params);
|
|
@@ -26220,7 +26267,7 @@ ${task}` : task;
|
|
|
26220
26267
|
}
|
|
26221
26268
|
if (method === "turn/started") {
|
|
26222
26269
|
const turn = params?.turn;
|
|
26223
|
-
if (typeof turn?.id === "string") {
|
|
26270
|
+
if (params?.threadId === state.threadId && state.turnRequested && typeof turn?.id === "string" && (!state.turnId || state.turnId === turn.id)) {
|
|
26224
26271
|
state.turnId = turn.id;
|
|
26225
26272
|
state.active = true;
|
|
26226
26273
|
if (state.turnRequested) state.runTurnIds.add(turn.id);
|
|
@@ -26241,10 +26288,29 @@ ${task}` : task;
|
|
|
26241
26288
|
}
|
|
26242
26289
|
return [];
|
|
26243
26290
|
},
|
|
26244
|
-
|
|
26245
|
-
if (message.method !== "turn/completed" || !state.active) return
|
|
26291
|
+
turnResult(message, state) {
|
|
26292
|
+
if (message.method !== "turn/completed" || !state.active) return void 0;
|
|
26293
|
+
const params = record2(message.params);
|
|
26294
|
+
const turn = record2(params?.turn);
|
|
26295
|
+
if (params?.threadId !== state.threadId || !state.turnId || turn?.id !== state.turnId) return void 0;
|
|
26246
26296
|
state.active = false;
|
|
26247
|
-
return
|
|
26297
|
+
if (turn.status === "interrupted") return { code: null, reason: "cancelled" };
|
|
26298
|
+
const failure = state.errorsByTurn.get(state.turnId);
|
|
26299
|
+
const error2 = turn.error ?? (turn.status === "failed" || !failure?.willRetry ? failure?.error : void 0);
|
|
26300
|
+
if (turn.status === "completed" && !error2) return { code: 0, reason: "clean" };
|
|
26301
|
+
return { code: 1, reason: "error", errorMessage: codexTurnErrorMessage(error2) };
|
|
26302
|
+
},
|
|
26303
|
+
processExitInfo(info, state) {
|
|
26304
|
+
const error2 = state.turnId ? state.errorsByTurn.get(state.turnId)?.error : void 0;
|
|
26305
|
+
return {
|
|
26306
|
+
code: info.code === 0 ? 1 : info.code,
|
|
26307
|
+
reason: "error",
|
|
26308
|
+
errorMessage: [
|
|
26309
|
+
error2 ? codexTurnErrorMessage(error2) : void 0,
|
|
26310
|
+
info.errorMessage,
|
|
26311
|
+
"Codex app-server exited before reporting a terminal result"
|
|
26312
|
+
].filter(Boolean).join("\n").slice(0, 4e3)
|
|
26313
|
+
};
|
|
26248
26314
|
},
|
|
26249
26315
|
runtimeSessionId: (state) => state.threadId,
|
|
26250
26316
|
exitExtras: (state) => {
|
|
@@ -28304,8 +28370,8 @@ ${task}` : task;
|
|
|
28304
28370
|
normalize(message) {
|
|
28305
28371
|
return normalizePi(JSON.stringify(message));
|
|
28306
28372
|
},
|
|
28307
|
-
|
|
28308
|
-
return message.type === "agent_settled";
|
|
28373
|
+
turnResult(message) {
|
|
28374
|
+
return message.type === "agent_settled" ? { code: 0, reason: "clean" } : void 0;
|
|
28309
28375
|
},
|
|
28310
28376
|
runtimeSessionId(state) {
|
|
28311
28377
|
return state.sessionId;
|
|
@@ -30815,8 +30881,26 @@ function runBackgroundAssistantChatTurn(deps) {
|
|
|
30815
30881
|
}
|
|
30816
30882
|
};
|
|
30817
30883
|
void Promise.resolve(deps.session.done).then(() => finalize("done").catch(() => void 0)).catch((err) => {
|
|
30884
|
+
const chatExit = err instanceof Error ? err.chatExit : void 0;
|
|
30885
|
+
const text5 = chatExit?.summary ?? (err instanceof Error ? err.message : String(err));
|
|
30886
|
+
const extra = {
|
|
30887
|
+
...chatExit?.reason ? { reason: chatExit.reason } : {},
|
|
30888
|
+
...chatExit?.detail ? { detail: chatExit.detail } : {}
|
|
30889
|
+
};
|
|
30890
|
+
const part = { type: "error", seq: ++localSeq, text: text5, ...extra };
|
|
30891
|
+
localParts.push(part);
|
|
30892
|
+
liveTurn?.emit(part);
|
|
30818
30893
|
try {
|
|
30819
|
-
itemLedger.recordError(
|
|
30894
|
+
const outcome = itemLedger.recordError(text5, extra);
|
|
30895
|
+
liveTurn?.publishServerItem({
|
|
30896
|
+
itemId: outcome.itemId,
|
|
30897
|
+
itemType: "control",
|
|
30898
|
+
startedVersion: outcome.startedVersion,
|
|
30899
|
+
version: outcome.version,
|
|
30900
|
+
ord: outcome.ord,
|
|
30901
|
+
status: "failed",
|
|
30902
|
+
payload: { code: "error", note: text5, ...extra }
|
|
30903
|
+
});
|
|
30820
30904
|
} catch {
|
|
30821
30905
|
}
|
|
30822
30906
|
return finalize("error").catch(() => void 0);
|
|
@@ -178838,14 +178922,14 @@ var require_Alias = __commonJS({
|
|
|
178838
178922
|
if (ctx)
|
|
178839
178923
|
ctx.aliasResolveCache = nodes;
|
|
178840
178924
|
}
|
|
178841
|
-
let
|
|
178925
|
+
let found2 = void 0;
|
|
178842
178926
|
for (const node2 of nodes) {
|
|
178843
178927
|
if (node2 === this)
|
|
178844
178928
|
break;
|
|
178845
178929
|
if (node2.anchor === this.source)
|
|
178846
|
-
|
|
178930
|
+
found2 = node2;
|
|
178847
178931
|
}
|
|
178848
|
-
return
|
|
178932
|
+
return found2;
|
|
178849
178933
|
}
|
|
178850
178934
|
toJSON(_arg, ctx) {
|
|
178851
178935
|
if (!ctx)
|
|
@@ -181970,7 +182054,7 @@ var require_resolve_props = __commonJS({
|
|
|
181970
182054
|
let tag = null;
|
|
181971
182055
|
let newlineAfterProp = null;
|
|
181972
182056
|
let comma = null;
|
|
181973
|
-
let
|
|
182057
|
+
let found2 = null;
|
|
181974
182058
|
let start = null;
|
|
181975
182059
|
for (const token of tokens) {
|
|
181976
182060
|
if (reqSpace) {
|
|
@@ -182007,7 +182091,7 @@ var require_resolve_props = __commonJS({
|
|
|
182007
182091
|
if (atNewline) {
|
|
182008
182092
|
if (comment)
|
|
182009
182093
|
comment += token.source;
|
|
182010
|
-
else if (!
|
|
182094
|
+
else if (!found2 || indicator !== "seq-item-ind")
|
|
182011
182095
|
spaceBefore = true;
|
|
182012
182096
|
} else
|
|
182013
182097
|
commentSep += token.source;
|
|
@@ -182041,9 +182125,9 @@ var require_resolve_props = __commonJS({
|
|
|
182041
182125
|
case indicator:
|
|
182042
182126
|
if (anchor || tag)
|
|
182043
182127
|
onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`);
|
|
182044
|
-
if (
|
|
182128
|
+
if (found2)
|
|
182045
182129
|
onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow3 ?? "collection"}`);
|
|
182046
|
-
|
|
182130
|
+
found2 = token;
|
|
182047
182131
|
atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind";
|
|
182048
182132
|
hasSpace = false;
|
|
182049
182133
|
break;
|
|
@@ -182072,7 +182156,7 @@ var require_resolve_props = __commonJS({
|
|
|
182072
182156
|
onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation");
|
|
182073
182157
|
return {
|
|
182074
182158
|
comma,
|
|
182075
|
-
found,
|
|
182159
|
+
found: found2,
|
|
182076
182160
|
spaceBefore,
|
|
182077
182161
|
comment,
|
|
182078
182162
|
hasNewline,
|
|
@@ -201214,8 +201298,8 @@ async function resolveOutlineKnowledgeBinding(credentials, input) {
|
|
|
201214
201298
|
const collections = (await api.listCollections()).filter(isWritableCollection);
|
|
201215
201299
|
for (const collection of collections) {
|
|
201216
201300
|
const documents = await api.listDocuments(collection["id"]);
|
|
201217
|
-
const
|
|
201218
|
-
if (
|
|
201301
|
+
const found2 = documents.find((document4) => string5(document4["url"]) === pasted);
|
|
201302
|
+
if (found2) return bindingFromDocument(api, found2, collection, identity);
|
|
201219
201303
|
}
|
|
201220
201304
|
throw new Error("\u627E\u4E0D\u5230\u8BE5\u6839\u6587\u6863\uFF0C\u6216\u5F53\u524D Token \u5BF9\u5176\u6240\u5728 Collection \u6CA1\u6709\u5199\u6743\u9650");
|
|
201221
201305
|
}
|
|
@@ -204172,10 +204256,10 @@ async function runCommand(kernel, blobs, oplog, engineStore, actor, command, arg
|
|
|
204172
204256
|
}
|
|
204173
204257
|
let artifactId;
|
|
204174
204258
|
if (escalationId !== void 0) {
|
|
204175
|
-
const
|
|
204176
|
-
if (!
|
|
204177
|
-
if (
|
|
204178
|
-
artifactId =
|
|
204259
|
+
const found2 = escalationById(kernel.model, escalationId);
|
|
204260
|
+
if (!found2) throw new Error(`\u4E0A\u62A5 ${escalationId} \u4E0D\u5B58\u5728\uFF08id \u89C1 \`oasis status <\u8282\u70B9>\` \u7684\u300C\u4E0A\u62A5\u300D\u6BB5\uFF09\u3002`);
|
|
204261
|
+
if (found2.resolved) throw new Error(`\u4E0A\u62A5 ${escalationId} \u5DF2\u7ECF\u5173\u95ED\uFF0C\u65E0\u9700\u518D\u5173\u3002`);
|
|
204262
|
+
artifactId = found2.artifactId;
|
|
204179
204263
|
} else {
|
|
204180
204264
|
const argArtifactId = optStr(args, "artifactId");
|
|
204181
204265
|
if (argArtifactId === void 0) {
|
|
@@ -204457,7 +204541,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}`] : []
|
|
|
204457
204541
|
}
|
|
204458
204542
|
if (args["dispatch"] === false) {
|
|
204459
204543
|
await ctx.artifactState?.setWorkspaceDispatchHold(workspace, true);
|
|
204460
|
-
await kernel.pauseWorkorder?.(workspace, actor);
|
|
204544
|
+
await kernel.pauseWorkorder?.(workspace, actor, WORKORDER_HOLD_AT_CREATE_REASON);
|
|
204461
204545
|
}
|
|
204462
204546
|
return {
|
|
204463
204547
|
message: planned.issues.length > 0 ? `planned ${workspace}: created ${planned.spawned.length} nodes\uFF0C${planned.issues.length} \u4E2A staffing \u63D0\u793A\u5DF2\u5199\u5165\u5DE5\u5355` : `planned ${workspace}: created ${planned.spawned.length} nodes`,
|
|
@@ -206700,7 +206784,7 @@ async function startOasisServer(opts) {
|
|
|
206700
206784
|
}
|
|
206701
206785
|
if (body2.dispatch === false) {
|
|
206702
206786
|
await opts.artifactState?.setWorkspaceDispatchHold(workspace, true);
|
|
206703
|
-
await engine.kernel.pauseWorkorder?.(workspace, actor);
|
|
206787
|
+
await engine.kernel.pauseWorkorder?.(workspace, actor, WORKORDER_HOLD_AT_CREATE_REASON);
|
|
206704
206788
|
}
|
|
206705
206789
|
await wodraftPlannerIssues.remove(workspace);
|
|
206706
206790
|
if (opts.chatSession && body2.chatSessionId && workspace !== "ws:default") {
|
|
@@ -213283,6 +213367,15 @@ var init_service4 = __esm({
|
|
|
213283
213367
|
async getWorkspaceBinding(workOrderId) {
|
|
213284
213368
|
return this.artifacts.getWorkspaceBinding(workOrderId);
|
|
213285
213369
|
}
|
|
213370
|
+
/**
|
|
213371
|
+
* 工单负责人(改挂项目门禁的「条目本人」一支)。与列表「负责人」列**同一个出口**:
|
|
213372
|
+
* `buildWorkorderSummaries` 的 `owner`(页头总纲根的 owner)——判权限与界面上显示的负责人不许是两套。
|
|
213373
|
+
* 内核里没有这张工单 → null。
|
|
213374
|
+
*/
|
|
213375
|
+
workorderOwnerOf(workOrderId) {
|
|
213376
|
+
const summary = buildWorkorderSummaries(this.kernel.model).find((w2) => w2.id === workOrderId);
|
|
213377
|
+
return summary?.owner.id ?? null;
|
|
213378
|
+
}
|
|
213286
213379
|
/** 上传一个项目普通文件:内容入 blob 库,记一条元信息旁存(非 oplog 事实)。 */
|
|
213287
213380
|
async addProjectFile(input) {
|
|
213288
213381
|
if (!this.blobs) throw new Error("\u9879\u76EE\u6587\u4EF6\u4E0A\u4F20\u9700\u8981 blob \u5E93\uFF08domain \u672A\u6CE8\u5165 blobs\uFF09");
|
|
@@ -215385,6 +215478,10 @@ var init_dev_store = __esm({
|
|
|
215385
215478
|
async listSessionsByWorkspace(workspace) {
|
|
215386
215479
|
return [...this.sessions.values()].filter((s2) => s2.workspace === workspace && !s2.analyzedRunId && s2.visibility !== "internal").sort((a, b2) => b2.touchedAt.localeCompare(a.touchedAt)).map((s2) => this.withQuality(s2));
|
|
215387
215480
|
}
|
|
215481
|
+
/** 语义同 postgres 版:项目「关联对话」,不按人过滤;排除分析 / 内部 / 挂在任务上的会话,created_at 倒序。 */
|
|
215482
|
+
async listSessionsByProject(projectId2) {
|
|
215483
|
+
return [...this.sessions.values()].filter((s2) => (s2.projectId ?? null) === projectId2 && !s2.analyzedRunId && s2.visibility !== "internal" && !s2.workspace).sort((a, b2) => b2.createdAt.localeCompare(a.createdAt) || a.id.localeCompare(b2.id));
|
|
215484
|
+
}
|
|
215388
215485
|
async listAllAnalysisSessions(opts = {}) {
|
|
215389
215486
|
const limit = opts.limit ?? 100;
|
|
215390
215487
|
const sorted = [...this.sessions.values()].filter((s2) => !!s2.analyzedRunId).sort((a, b2) => b2.touchedAt.localeCompare(a.touchedAt) || b2.id.localeCompare(a.id));
|
|
@@ -219169,7 +219266,9 @@ var init_sink = __esm({
|
|
|
219169
219266
|
st.openTools.set(callId, {
|
|
219170
219267
|
toolName: typeof p2["name"] === "string" ? p2["name"] : "tool",
|
|
219171
219268
|
input: p2["input"] ?? null,
|
|
219172
|
-
startedAt: event.ts
|
|
219269
|
+
startedAt: event.ts,
|
|
219270
|
+
// 子 Agent 里的调用:adapter 给 payload 标了派出它的那次调用(Claude 的 parent_tool_use_id)。
|
|
219271
|
+
parentCallId: typeof p2["parentToolCallId"] === "string" ? p2["parentToolCallId"] : null
|
|
219173
219272
|
});
|
|
219174
219273
|
return;
|
|
219175
219274
|
}
|
|
@@ -219185,7 +219284,7 @@ var init_sink = __esm({
|
|
|
219185
219284
|
id: `tc_${st.runId}_${callId}`,
|
|
219186
219285
|
runId: st.runId,
|
|
219187
219286
|
callId,
|
|
219188
|
-
parentCallId:
|
|
219287
|
+
parentCallId: open3.parentCallId,
|
|
219189
219288
|
toolName: open3.toolName,
|
|
219190
219289
|
toolKind: toolKindOf(open3.toolName),
|
|
219191
219290
|
status: isErr ? "failed" : "succeeded",
|
|
@@ -219965,6 +220064,7 @@ var init_chat_recovery = __esm({
|
|
|
219965
220064
|
durationMs: Date.parse(endedAt) - Date.parse(startedAt),
|
|
219966
220065
|
exitCode: info.code,
|
|
219967
220066
|
exitReason: info.reason ?? null,
|
|
220067
|
+
...exitFailed(info) && info.reason !== "cancelled" ? { errorCode: info.reason ?? "error", errorMessage: formatExitError(info) } : {},
|
|
219968
220068
|
transcriptRef: info.transcriptRef ?? null,
|
|
219969
220069
|
usage: info.usage ?? null,
|
|
219970
220070
|
// 重启前 create/dispatch 路已把 actor/runtime fallback 模型写进 run;退出帧没报 model 时必须保留,
|
|
@@ -220262,6 +220362,9 @@ var init_daemon_hub = __esm({
|
|
|
220262
220362
|
this.wss = new import_websocket_server.default({
|
|
220263
220363
|
server,
|
|
220264
220364
|
path: path41,
|
|
220365
|
+
// workdir_read returns base64 plus a JSON envelope. ws defaults to 100 MiB,
|
|
220366
|
+
// which would disconnect the node before a 200 MiB original reaches HTTP.
|
|
220367
|
+
maxPayload: 4 * Math.ceil(FILE_CONTENT_MAX_BYTES / 3) + 64 * 1024,
|
|
220265
220368
|
...resolveNode ? {
|
|
220266
220369
|
// 异步校验:verifyClient 允许延迟回调,故可在此 await 一次 DB 查询后再放行/拒绝。
|
|
220267
220370
|
verifyClient: (info, cb) => {
|
|
@@ -224914,6 +225017,359 @@ var init_assistant_tier_dispatch = __esm({
|
|
|
224914
225017
|
}
|
|
224915
225018
|
});
|
|
224916
225019
|
|
|
225020
|
+
// ../server/src/domains/chat-sessions/files-view.ts
|
|
225021
|
+
function reasonFromWorkdirCode(code2) {
|
|
225022
|
+
switch (code2) {
|
|
225023
|
+
case "NODE_OFFLINE":
|
|
225024
|
+
return "NODE_OFFLINE";
|
|
225025
|
+
case "TIMEOUT":
|
|
225026
|
+
return "TIMEOUT";
|
|
225027
|
+
default:
|
|
225028
|
+
return "ERROR";
|
|
225029
|
+
}
|
|
225030
|
+
}
|
|
225031
|
+
function joinRel2(dir, name) {
|
|
225032
|
+
return dir ? `${dir}/${name}` : name;
|
|
225033
|
+
}
|
|
225034
|
+
function workdirFileItem(rel, name, size, mtime) {
|
|
225035
|
+
return {
|
|
225036
|
+
id: `workdir:${rel}`,
|
|
225037
|
+
name,
|
|
225038
|
+
kind: "file",
|
|
225039
|
+
// 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
|
|
225040
|
+
// 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
|
|
225041
|
+
content_type: null,
|
|
225042
|
+
size,
|
|
225043
|
+
updated_at: mtime,
|
|
225044
|
+
source: "workdir",
|
|
225045
|
+
blob_ref: null,
|
|
225046
|
+
path: rel,
|
|
225047
|
+
message_id: null,
|
|
225048
|
+
// 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
|
|
225049
|
+
// 这里是**按能力不渲染**的判据,不是置灰假钮。
|
|
225050
|
+
editable: false
|
|
225051
|
+
};
|
|
225052
|
+
}
|
|
225053
|
+
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
225054
|
+
const items = [];
|
|
225055
|
+
if (typeof bridge.walk === "function") {
|
|
225056
|
+
const walked = await bridge.walk(
|
|
225057
|
+
loc.nodeId,
|
|
225058
|
+
{ workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
|
|
225059
|
+
WORKDIR_TREE_MAX_ENTRIES
|
|
225060
|
+
);
|
|
225061
|
+
if (walked.ok) {
|
|
225062
|
+
let truncated2 = walked.truncated === true;
|
|
225063
|
+
for (const entry of walked.entries) {
|
|
225064
|
+
if (entry.type !== "file") continue;
|
|
225065
|
+
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
225066
|
+
truncated2 = true;
|
|
225067
|
+
break;
|
|
225068
|
+
}
|
|
225069
|
+
const slash = entry.path.lastIndexOf("/");
|
|
225070
|
+
items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
|
|
225071
|
+
}
|
|
225072
|
+
return { ok: true, items, truncated: truncated2 };
|
|
225073
|
+
}
|
|
225074
|
+
if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
|
|
225075
|
+
if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
|
|
225076
|
+
}
|
|
225077
|
+
const deadline = now() + WORKDIR_WALK_BUDGET_MS;
|
|
225078
|
+
let listCalls = 0;
|
|
225079
|
+
let truncated = false;
|
|
225080
|
+
const listDir = (path41) => {
|
|
225081
|
+
listCalls += 1;
|
|
225082
|
+
return bridge.list(loc.nodeId, {
|
|
225083
|
+
workdirKey: loc.workdirKey,
|
|
225084
|
+
runtimeKind: loc.runtimeKind,
|
|
225085
|
+
rtId: loc.rtId,
|
|
225086
|
+
path: path41
|
|
225087
|
+
});
|
|
225088
|
+
};
|
|
225089
|
+
const root = await listDir("");
|
|
225090
|
+
if (!root.ok) {
|
|
225091
|
+
if (root.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
|
|
225092
|
+
return { ok: false, reason: reasonFromWorkdirCode(root.code) };
|
|
225093
|
+
}
|
|
225094
|
+
let level = [{ path: "", outcome: root }];
|
|
225095
|
+
for (let depth = 0; depth <= WORKDIR_WALK_MAX_DEPTH; depth += 1) {
|
|
225096
|
+
const nextDirs = [];
|
|
225097
|
+
for (const { path: dir, outcome } of level) {
|
|
225098
|
+
if (!outcome.ok) {
|
|
225099
|
+
truncated = true;
|
|
225100
|
+
continue;
|
|
225101
|
+
}
|
|
225102
|
+
if (outcome.truncated) truncated = true;
|
|
225103
|
+
for (const entry of outcome.entries) {
|
|
225104
|
+
if (entry.type === "dir") {
|
|
225105
|
+
nextDirs.push(joinRel2(dir, entry.name));
|
|
225106
|
+
continue;
|
|
225107
|
+
}
|
|
225108
|
+
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
225109
|
+
truncated = true;
|
|
225110
|
+
continue;
|
|
225111
|
+
}
|
|
225112
|
+
items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
|
|
225113
|
+
}
|
|
225114
|
+
}
|
|
225115
|
+
if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
|
|
225116
|
+
if (nextDirs.length > 0) truncated = true;
|
|
225117
|
+
break;
|
|
225118
|
+
}
|
|
225119
|
+
const budget = WORKDIR_WALK_MAX_LIST_CALLS - listCalls;
|
|
225120
|
+
if (budget <= 0 || now() >= deadline) {
|
|
225121
|
+
truncated = true;
|
|
225122
|
+
break;
|
|
225123
|
+
}
|
|
225124
|
+
const take = nextDirs.slice(0, budget);
|
|
225125
|
+
if (take.length < nextDirs.length) truncated = true;
|
|
225126
|
+
const outcomes = await Promise.all(take.map((p2) => listDir(p2)));
|
|
225127
|
+
level = take.map((p2, i) => ({ path: p2, outcome: outcomes[i] }));
|
|
225128
|
+
}
|
|
225129
|
+
return { ok: true, items, truncated };
|
|
225130
|
+
}
|
|
225131
|
+
async function collectMessageFiles(messages, blobs) {
|
|
225132
|
+
const rows = [];
|
|
225133
|
+
for (const message of messages) {
|
|
225134
|
+
if (!Array.isArray(message.attachments)) continue;
|
|
225135
|
+
message.attachments.forEach((attachment, index2) => {
|
|
225136
|
+
if (!attachment || typeof attachment.name !== "string" || !attachment.name) return;
|
|
225137
|
+
const blobRef = typeof attachment.blobRef === "string" && attachment.blobRef ? attachment.blobRef : null;
|
|
225138
|
+
rows.push({
|
|
225139
|
+
item: {
|
|
225140
|
+
id: `msg:${message.id}:${index2}`,
|
|
225141
|
+
name: attachment.name,
|
|
225142
|
+
kind: "file",
|
|
225143
|
+
updated_at: attachment.updatedAt ?? message.createdAt,
|
|
225144
|
+
source: "message",
|
|
225145
|
+
blob_ref: blobRef,
|
|
225146
|
+
path: null,
|
|
225147
|
+
message_id: message.id,
|
|
225148
|
+
// 没有 blobRef 的是**历史纯文本附件**(契约注释:legacy text attachments may only
|
|
225149
|
+
// contain `name`)——没有可写回的载体,一律不给编辑入口。
|
|
225150
|
+
editable: blobRef !== null && isEditableFileName(attachment.name)
|
|
225151
|
+
},
|
|
225152
|
+
blobRef,
|
|
225153
|
+
declaredType: typeof attachment.contentType === "string" && attachment.contentType ? attachment.contentType : null
|
|
225154
|
+
});
|
|
225155
|
+
});
|
|
225156
|
+
}
|
|
225157
|
+
const metaCache = /* @__PURE__ */ new Map();
|
|
225158
|
+
const metaOf = (ref2) => {
|
|
225159
|
+
if (!blobs) return Promise.resolve(null);
|
|
225160
|
+
let hit = metaCache.get(ref2);
|
|
225161
|
+
if (!hit) {
|
|
225162
|
+
hit = blobs.meta(ref2).catch(() => null);
|
|
225163
|
+
metaCache.set(ref2, hit);
|
|
225164
|
+
}
|
|
225165
|
+
return hit;
|
|
225166
|
+
};
|
|
225167
|
+
return Promise.all(rows.map(async ({ item, blobRef, declaredType }) => {
|
|
225168
|
+
const meta = blobRef ? await metaOf(blobRef) : null;
|
|
225169
|
+
return {
|
|
225170
|
+
...item,
|
|
225171
|
+
content_type: declaredType ?? meta?.contentType ?? null,
|
|
225172
|
+
size: meta?.size ?? null
|
|
225173
|
+
};
|
|
225174
|
+
}));
|
|
225175
|
+
}
|
|
225176
|
+
function mergeChatSessionFiles(messageItems, workdirItems, materializedCopies = /* @__PURE__ */ new Set()) {
|
|
225177
|
+
const out = [];
|
|
225178
|
+
const seenMessageKeys = /* @__PURE__ */ new Set();
|
|
225179
|
+
for (const item of [...messageItems].sort((a, b2) => b2.updated_at.localeCompare(a.updated_at))) {
|
|
225180
|
+
const key = `${item.name}\0${item.blob_ref ?? ""}`;
|
|
225181
|
+
if (seenMessageKeys.has(key)) continue;
|
|
225182
|
+
seenMessageKeys.add(key);
|
|
225183
|
+
out.push(item);
|
|
225184
|
+
}
|
|
225185
|
+
const seenWorkdirPaths = /* @__PURE__ */ new Set();
|
|
225186
|
+
for (const item of workdirItems) {
|
|
225187
|
+
if (item.path && materializedCopies.has(item.path)) continue;
|
|
225188
|
+
if (seenWorkdirPaths.has(item.id)) continue;
|
|
225189
|
+
seenWorkdirPaths.add(item.id);
|
|
225190
|
+
out.push(item);
|
|
225191
|
+
}
|
|
225192
|
+
out.sort((a, b2) => b2.updated_at.localeCompare(a.updated_at) || a.name.localeCompare(b2.name));
|
|
225193
|
+
return out;
|
|
225194
|
+
}
|
|
225195
|
+
async function findMaterializedAttachmentCopies(messageItems, workdirItems, read) {
|
|
225196
|
+
const copies = /* @__PURE__ */ new Set();
|
|
225197
|
+
const attachments = messageItems.filter((item) => item.blob_ref && /^[a-f0-9]{64}$/.test(item.blob_ref));
|
|
225198
|
+
let budget = CHAT_ATTACHMENT_MAX_BYTES;
|
|
225199
|
+
const deadline = Date.now() + WORKDIR_WALK_BUDGET_MS;
|
|
225200
|
+
let reads = 0;
|
|
225201
|
+
for (const file of workdirItems) {
|
|
225202
|
+
const rootFile = file.path === file.name;
|
|
225203
|
+
const inputFile = file.path?.startsWith("inputs/attachments/") && !file.path.slice("inputs/attachments/".length).includes("/");
|
|
225204
|
+
if (!file.path || !rootFile && !inputFile) continue;
|
|
225205
|
+
const candidates = attachments.filter((attachment) => {
|
|
225206
|
+
const safe = safeAttachmentName(attachment.name);
|
|
225207
|
+
const numbered = file.name.endsWith(`-${safe}`) && /^\d+$/.test(file.name.slice(0, -(safe.length + 1)));
|
|
225208
|
+
return (rootFile ? file.name === attachment.name : file.name === safe || numbered) && attachment.size !== null && attachment.size === file.size;
|
|
225209
|
+
});
|
|
225210
|
+
if (Date.now() >= deadline) break;
|
|
225211
|
+
if (!candidates.length || file.size === null || file.size > budget || reads >= 10) continue;
|
|
225212
|
+
reads += 1;
|
|
225213
|
+
budget -= file.size;
|
|
225214
|
+
let timer;
|
|
225215
|
+
try {
|
|
225216
|
+
const result = await Promise.race([
|
|
225217
|
+
read(file.path, file.size),
|
|
225218
|
+
new Promise((resolve10) => {
|
|
225219
|
+
timer = setTimeout(() => resolve10(null), Math.max(0, deadline - Date.now()));
|
|
225220
|
+
})
|
|
225221
|
+
]);
|
|
225222
|
+
if (!result?.ok) continue;
|
|
225223
|
+
const bytes2 = Buffer.from(result.contentBase64, "base64");
|
|
225224
|
+
if (bytes2.length !== file.size) continue;
|
|
225225
|
+
const hash2 = (0, import_node_crypto50.createHash)("sha256").update(bytes2).digest("hex");
|
|
225226
|
+
if (candidates.some((attachment) => attachment.blob_ref === hash2)) copies.add(file.path);
|
|
225227
|
+
} catch {
|
|
225228
|
+
} finally {
|
|
225229
|
+
if (timer) clearTimeout(timer);
|
|
225230
|
+
}
|
|
225231
|
+
}
|
|
225232
|
+
return copies;
|
|
225233
|
+
}
|
|
225234
|
+
var import_node_crypto50, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS, WORKDIR_TREE_MAX_ENTRIES;
|
|
225235
|
+
var init_files_view = __esm({
|
|
225236
|
+
"../server/src/domains/chat-sessions/files-view.ts"() {
|
|
225237
|
+
"use strict";
|
|
225238
|
+
import_node_crypto50 = require("node:crypto");
|
|
225239
|
+
init_chat_attachment_files();
|
|
225240
|
+
init_src();
|
|
225241
|
+
WORKDIR_WALK_MAX_DEPTH = 3;
|
|
225242
|
+
WORKDIR_WALK_MAX_ENTRIES = 200;
|
|
225243
|
+
WORKDIR_WALK_MAX_LIST_CALLS = 20;
|
|
225244
|
+
WORKDIR_WALK_BUDGET_MS = 1e4;
|
|
225245
|
+
WORKDIR_TREE_MAX_ENTRIES = 2e3;
|
|
225246
|
+
}
|
|
225247
|
+
});
|
|
225248
|
+
|
|
225249
|
+
// ../server/src/domains/projects/chat-files.ts
|
|
225250
|
+
function isProjectChatSession(session) {
|
|
225251
|
+
if (session.visibility === "internal") return false;
|
|
225252
|
+
if (session.analyzedRunId) return false;
|
|
225253
|
+
if (session.workspace) return false;
|
|
225254
|
+
return true;
|
|
225255
|
+
}
|
|
225256
|
+
function chatFolderTitle(session) {
|
|
225257
|
+
const title = session.title?.trim();
|
|
225258
|
+
return title ? title : "\u672A\u547D\u540D\u5BF9\u8BDD";
|
|
225259
|
+
}
|
|
225260
|
+
function outputDisplayName(path41) {
|
|
225261
|
+
return path41.startsWith("outputs/") ? path41.slice("outputs/".length) : path41;
|
|
225262
|
+
}
|
|
225263
|
+
async function collectProjectChatFiles(chat, projectId2, meta, options = {}) {
|
|
225264
|
+
const scope = isUncategorizedProjectId(projectId2) ? null : projectId2;
|
|
225265
|
+
const [attachmentMessages, outputSessions] = await Promise.all([
|
|
225266
|
+
chat.listAttachmentMessages ? chat.listAttachmentMessages(scope) : Promise.resolve([]),
|
|
225267
|
+
chat.listOutputCopies ? chat.listOutputCopies(scope) : Promise.resolve([])
|
|
225268
|
+
]);
|
|
225269
|
+
const sessionIds = [.../* @__PURE__ */ new Set([
|
|
225270
|
+
...attachmentMessages.map((m2) => m2.sessionId),
|
|
225271
|
+
...outputSessions.map((s2) => s2.sessionId)
|
|
225272
|
+
])];
|
|
225273
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
225274
|
+
await Promise.all(sessionIds.map(async (id) => {
|
|
225275
|
+
const session = await chat.getSession(id);
|
|
225276
|
+
if (!session) return;
|
|
225277
|
+
if ((session.projectId ?? null) !== scope) return;
|
|
225278
|
+
if (!isProjectChatSession(session)) return;
|
|
225279
|
+
if (options.isDelegationChild && await options.isDelegationChild(id).catch(() => false)) return;
|
|
225280
|
+
sessions.set(id, session);
|
|
225281
|
+
}));
|
|
225282
|
+
const out = [];
|
|
225283
|
+
const base = (session) => ({
|
|
225284
|
+
projectId: projectId2,
|
|
225285
|
+
chatSessionId: session.id,
|
|
225286
|
+
chatTitle: chatFolderTitle(session),
|
|
225287
|
+
chatOwnerId: session.humanActorId
|
|
225288
|
+
});
|
|
225289
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
225290
|
+
for (const message of attachmentMessages) {
|
|
225291
|
+
if (!sessions.has(message.sessionId)) continue;
|
|
225292
|
+
const list2 = bySession.get(message.sessionId) ?? [];
|
|
225293
|
+
list2.push(message);
|
|
225294
|
+
bySession.set(message.sessionId, list2);
|
|
225295
|
+
}
|
|
225296
|
+
for (const [sessionId, messages] of bySession) {
|
|
225297
|
+
const session = sessions.get(sessionId);
|
|
225298
|
+
const rows = await collectMessageFiles(messages.map((m2) => ({ ...m2, id: m2.messageId })), { meta });
|
|
225299
|
+
for (const row of mergeChatSessionFiles(rows, [])) {
|
|
225300
|
+
if (row.blob_ref === null) continue;
|
|
225301
|
+
out.push({
|
|
225302
|
+
...base(session),
|
|
225303
|
+
fileId: `chat:${sessionId}:${row.id}`,
|
|
225304
|
+
name: row.name,
|
|
225305
|
+
blobId: row.blob_ref,
|
|
225306
|
+
...row.content_type ? { contentType: row.content_type } : {},
|
|
225307
|
+
size: row.size,
|
|
225308
|
+
uploadedBy: session.humanActorId,
|
|
225309
|
+
uploadedAt: row.updated_at,
|
|
225310
|
+
chatSource: "attachment"
|
|
225311
|
+
});
|
|
225312
|
+
}
|
|
225313
|
+
}
|
|
225314
|
+
for (const record8 of outputSessions) {
|
|
225315
|
+
const session = sessions.get(record8.sessionId);
|
|
225316
|
+
if (!session) continue;
|
|
225317
|
+
const seen = new Set(out.filter((file) => file.chatSessionId === session.id).map((file) => `${file.name}\0${file.blobId}`));
|
|
225318
|
+
const copies = record8.copies ?? {};
|
|
225319
|
+
for (const path41 of Object.keys(copies).sort((a, b2) => a.localeCompare(b2))) {
|
|
225320
|
+
const copy = copies[path41];
|
|
225321
|
+
if (!copy?.blobRef) continue;
|
|
225322
|
+
if (seen.has(`${copy.name}\0${copy.blobRef}`)) continue;
|
|
225323
|
+
out.push({
|
|
225324
|
+
...base(session),
|
|
225325
|
+
fileId: `chat:${session.id}:out:${copy.path}`,
|
|
225326
|
+
name: outputDisplayName(copy.path),
|
|
225327
|
+
blobId: copy.blobRef,
|
|
225328
|
+
...copy.contentType ? { contentType: copy.contentType } : {},
|
|
225329
|
+
size: copy.size,
|
|
225330
|
+
uploadedBy: session.aiActorId,
|
|
225331
|
+
uploadedAt: Number.isNaN(Date.parse(copy.mtime)) ? copy.copiedAt : new Date(copy.mtime).toISOString(),
|
|
225332
|
+
chatSource: "output"
|
|
225333
|
+
});
|
|
225334
|
+
}
|
|
225335
|
+
}
|
|
225336
|
+
return out;
|
|
225337
|
+
}
|
|
225338
|
+
var init_chat_files2 = __esm({
|
|
225339
|
+
"../server/src/domains/projects/chat-files.ts"() {
|
|
225340
|
+
"use strict";
|
|
225341
|
+
init_files_view();
|
|
225342
|
+
init_uncategorized_project();
|
|
225343
|
+
}
|
|
225344
|
+
});
|
|
225345
|
+
|
|
225346
|
+
// ../server/src/domains/projects/chat-sessions.ts
|
|
225347
|
+
async function listProjectChatSessions(chat, projectId2, delegationChildIds) {
|
|
225348
|
+
if (!chat.listSessionsByProject) return [];
|
|
225349
|
+
const scope = isUncategorizedProjectId(projectId2) ? null : projectId2;
|
|
225350
|
+
const sessions = (await chat.listSessionsByProject(scope)).filter((s2) => (s2.projectId ?? null) === scope && isProjectChatSession(s2));
|
|
225351
|
+
const children = delegationChildIds && sessions.length > 0 ? await delegationChildIds(sessions.map((s2) => s2.id)).catch(() => /* @__PURE__ */ new Set()) : /* @__PURE__ */ new Set();
|
|
225352
|
+
return sessions.filter((s2) => !children.has(s2.id)).map(toRow);
|
|
225353
|
+
}
|
|
225354
|
+
function toRow(s2) {
|
|
225355
|
+
const title = s2.title?.trim();
|
|
225356
|
+
return {
|
|
225357
|
+
id: s2.id,
|
|
225358
|
+
title: title ? title : null,
|
|
225359
|
+
humanActorId: s2.humanActorId,
|
|
225360
|
+
aiActorId: s2.aiActorId,
|
|
225361
|
+
createdAt: s2.createdAt,
|
|
225362
|
+
projectId: s2.projectId ?? null
|
|
225363
|
+
};
|
|
225364
|
+
}
|
|
225365
|
+
var init_chat_sessions = __esm({
|
|
225366
|
+
"../server/src/domains/projects/chat-sessions.ts"() {
|
|
225367
|
+
"use strict";
|
|
225368
|
+
init_chat_files2();
|
|
225369
|
+
init_uncategorized_project();
|
|
225370
|
+
}
|
|
225371
|
+
});
|
|
225372
|
+
|
|
224917
225373
|
// ../server/src/domains/projects/routes.ts
|
|
224918
225374
|
function projectsDomain(opts) {
|
|
224919
225375
|
const svc = async (req) => opts.resolveService ? opts.resolveService(req.auth.companyId) : opts.service;
|
|
@@ -224927,6 +225383,28 @@ function projectsDomain(opts) {
|
|
|
224927
225383
|
if (role === "owner" || role === "admin") return;
|
|
224928
225384
|
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`);
|
|
224929
225385
|
};
|
|
225386
|
+
const mayReassignProject = async (req, item) => {
|
|
225387
|
+
if (item.actorId && item.actorId === req.auth.actor) return true;
|
|
225388
|
+
if (item.currentProjectId && !isUncategorizedProjectId(item.currentProjectId)) {
|
|
225389
|
+
const project = await (await svc(req)).getProject(item.currentProjectId);
|
|
225390
|
+
if (project?.createdBy && project.createdBy.id === req.auth.actor) return true;
|
|
225391
|
+
}
|
|
225392
|
+
if (!opts.resolveMemberRole) return false;
|
|
225393
|
+
const role = await opts.resolveMemberRole(req.auth.companyId, req.auth.actor);
|
|
225394
|
+
return role === "owner" || role === "admin";
|
|
225395
|
+
};
|
|
225396
|
+
const requireWorkorderReassignPermission = async (req, workOrderId) => {
|
|
225397
|
+
if (!opts.resolveMemberRole) return;
|
|
225398
|
+
const service = await svc(req);
|
|
225399
|
+
const binding = await service.getWorkspaceBinding(workOrderId).catch(() => null);
|
|
225400
|
+
const allowed = await mayReassignProject(req, {
|
|
225401
|
+
actorId: service.workorderOwnerOf(workOrderId),
|
|
225402
|
+
currentProjectId: binding?.projectId ?? null
|
|
225403
|
+
});
|
|
225404
|
+
if (!allowed) {
|
|
225405
|
+
throw new ApiError(403, "WORKORDER_REASSIGN_FORBIDDEN", "\u53EA\u6709\u4EFB\u52A1\u8D1F\u8D23\u4EBA\u3001\u9879\u76EE\u8D1F\u8D23\u4EBA\u6216\u7EC4\u7EC7\u7BA1\u7406\u5458\u53EF\u4EE5\u79FB\u52A8\u8FD9\u4E2A\u4EFB\u52A1");
|
|
225406
|
+
}
|
|
225407
|
+
};
|
|
224930
225408
|
return (router) => {
|
|
224931
225409
|
router.post("/api/projects", async (req) => {
|
|
224932
225410
|
const body2 = req.body;
|
|
@@ -225044,6 +225522,45 @@ function projectsDomain(opts) {
|
|
|
225044
225522
|
const items = await (await svc(req)).listWorkordersForProject(req.params.id);
|
|
225045
225523
|
return { status: 200, body: { items } };
|
|
225046
225524
|
});
|
|
225525
|
+
router.get("/api/projects/:id/chat-sessions", async (req) => {
|
|
225526
|
+
const project = await (await svc(req)).getProject(req.params.id);
|
|
225527
|
+
if (!project) throw new ApiError(404, "NOT_FOUND", `\u6CA1\u6709\u8FD9\u4E2A\u9879\u76EE\uFF1A${req.params.id}`);
|
|
225528
|
+
if (!opts.chatSessionsFor) return { status: 200, body: { items: [] } };
|
|
225529
|
+
const companyId = req.auth.companyId;
|
|
225530
|
+
const rows = await listProjectChatSessions(
|
|
225531
|
+
await opts.chatSessionsFor(companyId),
|
|
225532
|
+
req.params.id,
|
|
225533
|
+
opts.delegationChildIds ? (ids2) => opts.delegationChildIds(companyId, ids2) : void 0
|
|
225534
|
+
);
|
|
225535
|
+
return { status: 200, body: { items: rows.map(toApiProjectChatSession) } };
|
|
225536
|
+
});
|
|
225537
|
+
router.patch("/api/chat-sessions/:id/project", async (req) => {
|
|
225538
|
+
if (!opts.chatSessionsFor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
225539
|
+
const store = await opts.chatSessionsFor(req.auth.companyId);
|
|
225540
|
+
const session = await store.getSession(req.params.id);
|
|
225541
|
+
if (!session || !isProjectChatSession(session)) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
225542
|
+
if (opts.delegationChildIds && (await opts.delegationChildIds(req.auth.companyId, [session.id])).has(session.id)) {
|
|
225543
|
+
throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
225544
|
+
}
|
|
225545
|
+
const allowed = await mayReassignProject(req, {
|
|
225546
|
+
actorId: session.humanActorId,
|
|
225547
|
+
currentProjectId: session.projectId ?? null
|
|
225548
|
+
});
|
|
225549
|
+
if (!allowed) {
|
|
225550
|
+
throw new ApiError(403, "CHAT_SESSION_REASSIGN_FORBIDDEN", "\u53EA\u6709\u5BF9\u8BDD\u53D1\u8D77\u4EBA\u3001\u9879\u76EE\u8D1F\u8D23\u4EBA\u6216\u7EC4\u7EC7\u7BA1\u7406\u5458\u53EF\u4EE5\u79FB\u52A8\u8FD9\u4E2A\u5BF9\u8BDD");
|
|
225551
|
+
}
|
|
225552
|
+
const b2 = req.body ?? {};
|
|
225553
|
+
if (!("project_id" in b2) && !("projectId" in b2)) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 project_id");
|
|
225554
|
+
const raw = "project_id" in b2 ? b2.project_id : b2.projectId;
|
|
225555
|
+
const target = raw === null || raw === "" || raw === "null" || typeof raw === "string" && isUncategorizedProjectId(raw) ? null : raw;
|
|
225556
|
+
if (target !== null) {
|
|
225557
|
+
if (typeof target !== "string") throw new ApiError(400, "BAD_REQUEST", "project_id \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6216 null");
|
|
225558
|
+
const project = await (await svc(req)).getProject(target);
|
|
225559
|
+
if (!project) throw new ApiError(404, "NOT_FOUND", `\u6CA1\u6709\u8FD9\u4E2A\u9879\u76EE\uFF1A${target}`);
|
|
225560
|
+
}
|
|
225561
|
+
await store.updateSession(session.id, { projectId: target });
|
|
225562
|
+
return { status: 200, body: { chat_session_id: session.id, project_id: target } };
|
|
225563
|
+
});
|
|
225047
225564
|
router.post("/api/projects/:id/files", async (req) => {
|
|
225048
225565
|
const b2 = req.body ?? {};
|
|
225049
225566
|
const base642 = b2.content_base64 ?? b2.contentBase64;
|
|
@@ -225250,6 +225767,7 @@ ${errs.join("\n")}`);
|
|
|
225250
225767
|
return { status: 200, body: toApiNodeOutputValidation(validation) };
|
|
225251
225768
|
});
|
|
225252
225769
|
router.patch("/api/work-orders/:workOrderId/project", async (req) => {
|
|
225770
|
+
await requireWorkorderReassignPermission(req, req.params.workOrderId);
|
|
225253
225771
|
const b2 = req.body ?? {};
|
|
225254
225772
|
const nulledPatch = "project_id" in b2 && b2.project_id === null || "projectId" in b2 && b2.projectId === null || b2.project_id === "null" || b2.project_id === "" || b2.projectId === "null" || b2.projectId === "";
|
|
225255
225773
|
if (nulledPatch) {
|
|
@@ -225270,6 +225788,7 @@ ${errs.join("\n")}`);
|
|
|
225270
225788
|
}
|
|
225271
225789
|
});
|
|
225272
225790
|
router.delete("/api/work-orders/:workOrderId/project", async (req) => {
|
|
225791
|
+
await requireWorkorderReassignPermission(req, req.params.workOrderId);
|
|
225273
225792
|
try {
|
|
225274
225793
|
await (await svc(req)).unlinkWorkorder(req.params.workOrderId);
|
|
225275
225794
|
return { status: 200, body: { work_order_id: req.params.workOrderId, project_id: null } };
|
|
@@ -225743,6 +226262,16 @@ function toApiWorkOrderDetail(detail) {
|
|
|
225743
226262
|
}))
|
|
225744
226263
|
};
|
|
225745
226264
|
}
|
|
226265
|
+
function toApiProjectChatSession(row) {
|
|
226266
|
+
return {
|
|
226267
|
+
id: row.id,
|
|
226268
|
+
title: row.title,
|
|
226269
|
+
human_actor_id: row.humanActorId,
|
|
226270
|
+
ai_actor_id: row.aiActorId,
|
|
226271
|
+
created_at: row.createdAt,
|
|
226272
|
+
project_id: row.projectId
|
|
226273
|
+
};
|
|
226274
|
+
}
|
|
225746
226275
|
function toApiProjectFilesView(view) {
|
|
225747
226276
|
return {
|
|
225748
226277
|
total: view.total,
|
|
@@ -225801,6 +226330,8 @@ var init_routes5 = __esm({
|
|
|
225801
226330
|
"use strict";
|
|
225802
226331
|
init_router();
|
|
225803
226332
|
init_src();
|
|
226333
|
+
init_chat_files2();
|
|
226334
|
+
init_chat_sessions();
|
|
225804
226335
|
init_uncategorized_project();
|
|
225805
226336
|
}
|
|
225806
226337
|
});
|
|
@@ -228095,11 +228626,11 @@ function fromRow(row) {
|
|
|
228095
228626
|
for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
|
|
228096
228627
|
return r;
|
|
228097
228628
|
}
|
|
228098
|
-
var
|
|
228629
|
+
var import_node_crypto51, import_node_fs14, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
|
|
228099
228630
|
var init_notification_store = __esm({
|
|
228100
228631
|
"../server/src/domains/collab/notification-store.ts"() {
|
|
228101
228632
|
"use strict";
|
|
228102
|
-
|
|
228633
|
+
import_node_crypto51 = require("node:crypto");
|
|
228103
228634
|
import_node_fs14 = __toESM(require("node:fs"), 1);
|
|
228104
228635
|
DEFAULT_PAGE = 50;
|
|
228105
228636
|
MAX_PAGE = 200;
|
|
@@ -228132,7 +228663,7 @@ var init_notification_store = __esm({
|
|
|
228132
228663
|
async insert(n) {
|
|
228133
228664
|
const existing = this.findLive(n.recipient, n.sourceKey, n.category);
|
|
228134
228665
|
if (existing) return existing;
|
|
228135
|
-
const row = { ...n, id: `ntf:${(0,
|
|
228666
|
+
const row = { ...n, id: `ntf:${(0, import_node_crypto51.randomUUID)()}`, updatedAt: n.enteredAt };
|
|
228136
228667
|
this.rows.set(row.id, row);
|
|
228137
228668
|
this.save();
|
|
228138
228669
|
return row;
|
|
@@ -228151,13 +228682,22 @@ var init_notification_store = __esm({
|
|
|
228151
228682
|
this.rows.set(id, next);
|
|
228152
228683
|
this.save();
|
|
228153
228684
|
}
|
|
228685
|
+
async rekey(id, sourceKey, patch, at) {
|
|
228686
|
+
const r = this.rows.get(id);
|
|
228687
|
+
if (!r || r.status !== "pending" || this.findLive(r.recipient, sourceKey, r.category)) return false;
|
|
228688
|
+
this.rows.set(id, { ...r, sourceKey, ...patch.nodeId ? { nodeId: patch.nodeId } : {} });
|
|
228689
|
+
await this.refresh(id, patch, at);
|
|
228690
|
+
return true;
|
|
228691
|
+
}
|
|
228154
228692
|
async close(id, closure) {
|
|
228155
228693
|
const r = this.rows.get(id);
|
|
228156
228694
|
if (!r || r.status !== "pending") return false;
|
|
228157
228695
|
const next = { ...r, status: closure.status, closedAt: closure.closedAt, updatedAt: closure.closedAt };
|
|
228158
228696
|
if (closure.closedBy) next.closedBy = closure.closedBy;
|
|
228159
|
-
if (closure.status === "handled")
|
|
228160
|
-
|
|
228697
|
+
if (closure.status === "handled") {
|
|
228698
|
+
next.result = closure.result;
|
|
228699
|
+
next.readAt ??= closure.closedAt;
|
|
228700
|
+
} else next.invalidReason = closure.invalidReason;
|
|
228161
228701
|
this.rows.set(id, next);
|
|
228162
228702
|
this.save();
|
|
228163
228703
|
return true;
|
|
@@ -228176,6 +228716,16 @@ var init_notification_store = __esm({
|
|
|
228176
228716
|
}
|
|
228177
228717
|
if (changed) this.save();
|
|
228178
228718
|
}
|
|
228719
|
+
async countUnread(recipient) {
|
|
228720
|
+
let pending = 0;
|
|
228721
|
+
let all2 = 0;
|
|
228722
|
+
for (const r of this.rows.values()) {
|
|
228723
|
+
if (r.recipient !== recipient || r.readAt) continue;
|
|
228724
|
+
all2++;
|
|
228725
|
+
if (r.status === "pending") pending++;
|
|
228726
|
+
}
|
|
228727
|
+
return { pending, all: all2 };
|
|
228728
|
+
}
|
|
228179
228729
|
async purgeBefore(cutoff) {
|
|
228180
228730
|
let n = 0;
|
|
228181
228731
|
for (const [id, r] of this.rows) {
|
|
@@ -228236,7 +228786,7 @@ var init_notification_store = __esm({
|
|
|
228236
228786
|
return new _PostgresNotificationStore(sql, schema);
|
|
228237
228787
|
}
|
|
228238
228788
|
async insert(n) {
|
|
228239
|
-
const id = `ntf:${(0,
|
|
228789
|
+
const id = `ntf:${(0, import_node_crypto51.randomUUID)()}`;
|
|
228240
228790
|
const r = await this.sql.query(
|
|
228241
228791
|
`INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
|
|
228242
228792
|
title, workspace_title, entered_at, updated_at, status, read_at, item)
|
|
@@ -228289,9 +228839,34 @@ var init_notification_store = __esm({
|
|
|
228289
228839
|
]
|
|
228290
228840
|
);
|
|
228291
228841
|
}
|
|
228842
|
+
async rekey(id, sourceKey, patch, at) {
|
|
228843
|
+
try {
|
|
228844
|
+
const r = await this.sql.query(
|
|
228845
|
+
`UPDATE ${this.t} SET source_key = $2, node_id = COALESCE($3, node_id), title = $4, workspace_title = $5,
|
|
228846
|
+
actor_id = $6, card_id = $7, item = $8, updated_at = $9
|
|
228847
|
+
WHERE id = $1 AND status = 'pending'`,
|
|
228848
|
+
[
|
|
228849
|
+
id,
|
|
228850
|
+
sourceKey,
|
|
228851
|
+
patch.nodeId ?? null,
|
|
228852
|
+
patch.title,
|
|
228853
|
+
patch.workspaceTitle ?? null,
|
|
228854
|
+
patch.actorId ?? null,
|
|
228855
|
+
patch.cardId ?? null,
|
|
228856
|
+
patch.item ? JSON.stringify(patch.item) : null,
|
|
228857
|
+
at
|
|
228858
|
+
]
|
|
228859
|
+
);
|
|
228860
|
+
return (r.rowCount ?? 0) > 0;
|
|
228861
|
+
} catch (err) {
|
|
228862
|
+
if (err.code === "23505") return false;
|
|
228863
|
+
throw err;
|
|
228864
|
+
}
|
|
228865
|
+
}
|
|
228292
228866
|
async close(id, closure) {
|
|
228293
228867
|
const r = await this.sql.query(
|
|
228294
|
-
`UPDATE ${this.t} SET status = $2, closed_at = $3, closed_by = $4, result = $5, invalid_reason = $6, updated_at = $3
|
|
228868
|
+
`UPDATE ${this.t} SET status = $2, closed_at = $3, closed_by = $4, result = $5, invalid_reason = $6, updated_at = $3,
|
|
228869
|
+
read_at = CASE WHEN $2 = 'handled' THEN COALESCE(read_at, $3::timestamptz) ELSE read_at END
|
|
228295
228870
|
WHERE id = $1 AND status = 'pending'`,
|
|
228296
228871
|
[
|
|
228297
228872
|
id,
|
|
@@ -228331,6 +228906,15 @@ var init_notification_store = __esm({
|
|
|
228331
228906
|
[recipient, [...ids2], at]
|
|
228332
228907
|
);
|
|
228333
228908
|
}
|
|
228909
|
+
async countUnread(recipient) {
|
|
228910
|
+
const r = await this.sql.query(
|
|
228911
|
+
`SELECT count(*) FILTER (WHERE status = 'pending')::int AS pending, count(*)::int AS "all"
|
|
228912
|
+
FROM ${this.t} WHERE recipient = $1 AND read_at IS NULL`,
|
|
228913
|
+
[recipient]
|
|
228914
|
+
);
|
|
228915
|
+
const row = r.rows[0] ?? {};
|
|
228916
|
+
return { pending: Number(row["pending"] ?? 0), all: Number(row["all"] ?? 0) };
|
|
228917
|
+
}
|
|
228334
228918
|
async purgeBefore(cutoff) {
|
|
228335
228919
|
const r = await this.sql.query(
|
|
228336
228920
|
`DELETE FROM ${this.t}
|
|
@@ -229153,13 +229737,13 @@ function nameKey(name) {
|
|
|
229153
229737
|
function cleanName(name) {
|
|
229154
229738
|
return name.trim().replace(/\s+/g, " ");
|
|
229155
229739
|
}
|
|
229156
|
-
var import_node_fs15, import_node_path20,
|
|
229740
|
+
var import_node_fs15, import_node_path20, import_node_crypto52, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
|
|
229157
229741
|
var init_tags = __esm({
|
|
229158
229742
|
"../server/src/domains/collab/tags.ts"() {
|
|
229159
229743
|
"use strict";
|
|
229160
229744
|
import_node_fs15 = __toESM(require("node:fs"), 1);
|
|
229161
229745
|
import_node_path20 = __toESM(require("node:path"), 1);
|
|
229162
|
-
|
|
229746
|
+
import_node_crypto52 = __toESM(require("node:crypto"), 1);
|
|
229163
229747
|
SEP = "::";
|
|
229164
229748
|
keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
|
|
229165
229749
|
prefixOf = (companyId) => `${companyId}${SEP}`;
|
|
@@ -229177,7 +229761,7 @@ var init_tags = __esm({
|
|
|
229177
229761
|
if (!name) return null;
|
|
229178
229762
|
if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
|
|
229179
229763
|
const tag = {
|
|
229180
|
-
id: `tag-${
|
|
229764
|
+
id: `tag-${import_node_crypto52.default.randomBytes(6).toString("hex")}`,
|
|
229181
229765
|
name,
|
|
229182
229766
|
...input.color?.trim() ? { color: input.color.trim() } : {},
|
|
229183
229767
|
...input.description?.trim() ? { description: input.description.trim() } : {}
|
|
@@ -230028,7 +230612,27 @@ function collabDomain(opts) {
|
|
|
230028
230612
|
limit
|
|
230029
230613
|
});
|
|
230030
230614
|
const last = items.at(-1);
|
|
230031
|
-
|
|
230615
|
+
const unread = await store.countUnread(req.auth.actor);
|
|
230616
|
+
const body2 = { items, cursor: last && items.length === limit ? notificationCursor(last) : null, unread };
|
|
230617
|
+
return { status: 200, body: body2 };
|
|
230618
|
+
});
|
|
230619
|
+
router.post("/api/notifications/read", async (req) => {
|
|
230620
|
+
if (!opts.resolveNotificationLedger) {
|
|
230621
|
+
return { status: 501, body: { error: { code: "not_implemented", message: "\u672C\u90E8\u7F72\u672A\u542F\u7528\u901A\u77E5\u53F0\u8D26" } } };
|
|
230622
|
+
}
|
|
230623
|
+
const ids2 = req.body?.ids;
|
|
230624
|
+
if (!Array.isArray(ids2) || ids2.length === 0 || ids2.length > 200 || !ids2.every((id) => typeof id === "string" && id.length > 0)) {
|
|
230625
|
+
return { status: 400, body: { error: { code: "bad_request", message: "ids \u5FC5\u987B\u662F 1\u2013200 \u4E2A\u901A\u77E5 id" } } };
|
|
230626
|
+
}
|
|
230627
|
+
let store;
|
|
230628
|
+
try {
|
|
230629
|
+
store = await opts.resolveNotificationLedger(req.auth.companyId);
|
|
230630
|
+
} catch (err) {
|
|
230631
|
+
console.warn(`[notifications] \u65E0\u53EF\u4FE1\u516C\u53F8\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u6807\u5DF2\u8BFB\uFF08\u4E0D\u56DE\u843D\u9ED8\u8BA4\u516C\u53F8\uFF09: ${String(err)}`);
|
|
230632
|
+
}
|
|
230633
|
+
if (!store) return { status: 503, body: { error: { code: "unavailable", message: "\u901A\u77E5\u53F0\u8D26\u6682\u4E0D\u53EF\u7528" } } };
|
|
230634
|
+
await store.markRead(req.auth.actor, ids2, (/* @__PURE__ */ new Date()).toISOString());
|
|
230635
|
+
return { status: 200, body: { ok: true } };
|
|
230032
230636
|
});
|
|
230033
230637
|
const companyOf = (req) => req.auth.companyId ?? "company:default";
|
|
230034
230638
|
const noTagStore = { status: 501, body: { error: { code: "not_implemented", message: "\u672C\u90E8\u7F72\u672A\u542F\u7528\u5DE5\u5355\u6807\u7B7E" } } };
|
|
@@ -230316,326 +230920,6 @@ var init_collab = __esm({
|
|
|
230316
230920
|
}
|
|
230317
230921
|
});
|
|
230318
230922
|
|
|
230319
|
-
// ../server/src/domains/chat-sessions/files-view.ts
|
|
230320
|
-
function reasonFromWorkdirCode(code2) {
|
|
230321
|
-
switch (code2) {
|
|
230322
|
-
case "NODE_OFFLINE":
|
|
230323
|
-
return "NODE_OFFLINE";
|
|
230324
|
-
case "TIMEOUT":
|
|
230325
|
-
return "TIMEOUT";
|
|
230326
|
-
default:
|
|
230327
|
-
return "ERROR";
|
|
230328
|
-
}
|
|
230329
|
-
}
|
|
230330
|
-
function joinRel2(dir, name) {
|
|
230331
|
-
return dir ? `${dir}/${name}` : name;
|
|
230332
|
-
}
|
|
230333
|
-
function workdirFileItem(rel, name, size, mtime) {
|
|
230334
|
-
return {
|
|
230335
|
-
id: `workdir:${rel}`,
|
|
230336
|
-
name,
|
|
230337
|
-
kind: "file",
|
|
230338
|
-
// 节点侧列目录只报 name/type/size/mtime,没有 MIME——诚实留 null,
|
|
230339
|
-
// 不拿后缀猜(红线:取不到就是取不到,别拿别的字段顶)。
|
|
230340
|
-
content_type: null,
|
|
230341
|
-
size,
|
|
230342
|
-
updated_at: mtime,
|
|
230343
|
-
source: "workdir",
|
|
230344
|
-
blob_ref: null,
|
|
230345
|
-
path: rel,
|
|
230346
|
-
message_id: null,
|
|
230347
|
-
// 工作区文件本期不开写回(brief §4①:要动 node↔server 协议帧,超预算)。
|
|
230348
|
-
// 这里是**按能力不渲染**的判据,不是置灰假钮。
|
|
230349
|
-
editable: false
|
|
230350
|
-
};
|
|
230351
|
-
}
|
|
230352
|
-
async function collectWorkdirFiles(bridge, loc, now = Date.now) {
|
|
230353
|
-
const items = [];
|
|
230354
|
-
if (typeof bridge.walk === "function") {
|
|
230355
|
-
const walked = await bridge.walk(
|
|
230356
|
-
loc.nodeId,
|
|
230357
|
-
{ workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: "" },
|
|
230358
|
-
WORKDIR_TREE_MAX_ENTRIES
|
|
230359
|
-
);
|
|
230360
|
-
if (walked.ok) {
|
|
230361
|
-
let truncated2 = walked.truncated === true;
|
|
230362
|
-
for (const entry of walked.entries) {
|
|
230363
|
-
if (entry.type !== "file") continue;
|
|
230364
|
-
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
230365
|
-
truncated2 = true;
|
|
230366
|
-
break;
|
|
230367
|
-
}
|
|
230368
|
-
const slash = entry.path.lastIndexOf("/");
|
|
230369
|
-
items.push(workdirFileItem(entry.path, entry.path.slice(slash + 1), entry.size, entry.mtime));
|
|
230370
|
-
}
|
|
230371
|
-
return { ok: true, items, truncated: truncated2 };
|
|
230372
|
-
}
|
|
230373
|
-
if (walked.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
|
|
230374
|
-
if (walked.code !== "UNSUPPORTED") return { ok: false, reason: reasonFromWorkdirCode(walked.code) };
|
|
230375
|
-
}
|
|
230376
|
-
const deadline = now() + WORKDIR_WALK_BUDGET_MS;
|
|
230377
|
-
let listCalls = 0;
|
|
230378
|
-
let truncated = false;
|
|
230379
|
-
const listDir = (path41) => {
|
|
230380
|
-
listCalls += 1;
|
|
230381
|
-
return bridge.list(loc.nodeId, {
|
|
230382
|
-
workdirKey: loc.workdirKey,
|
|
230383
|
-
runtimeKind: loc.runtimeKind,
|
|
230384
|
-
rtId: loc.rtId,
|
|
230385
|
-
path: path41
|
|
230386
|
-
});
|
|
230387
|
-
};
|
|
230388
|
-
const root = await listDir("");
|
|
230389
|
-
if (!root.ok) {
|
|
230390
|
-
if (root.code === "NOT_FOUND") return { ok: true, items: [], truncated: false };
|
|
230391
|
-
return { ok: false, reason: reasonFromWorkdirCode(root.code) };
|
|
230392
|
-
}
|
|
230393
|
-
let level = [{ path: "", outcome: root }];
|
|
230394
|
-
for (let depth = 0; depth <= WORKDIR_WALK_MAX_DEPTH; depth += 1) {
|
|
230395
|
-
const nextDirs = [];
|
|
230396
|
-
for (const { path: dir, outcome } of level) {
|
|
230397
|
-
if (!outcome.ok) {
|
|
230398
|
-
truncated = true;
|
|
230399
|
-
continue;
|
|
230400
|
-
}
|
|
230401
|
-
if (outcome.truncated) truncated = true;
|
|
230402
|
-
for (const entry of outcome.entries) {
|
|
230403
|
-
if (entry.type === "dir") {
|
|
230404
|
-
nextDirs.push(joinRel2(dir, entry.name));
|
|
230405
|
-
continue;
|
|
230406
|
-
}
|
|
230407
|
-
if (items.length >= WORKDIR_WALK_MAX_ENTRIES) {
|
|
230408
|
-
truncated = true;
|
|
230409
|
-
continue;
|
|
230410
|
-
}
|
|
230411
|
-
items.push(workdirFileItem(joinRel2(dir, entry.name), entry.name, entry.size, entry.mtime));
|
|
230412
|
-
}
|
|
230413
|
-
}
|
|
230414
|
-
if (depth === WORKDIR_WALK_MAX_DEPTH || nextDirs.length === 0) {
|
|
230415
|
-
if (nextDirs.length > 0) truncated = true;
|
|
230416
|
-
break;
|
|
230417
|
-
}
|
|
230418
|
-
const budget = WORKDIR_WALK_MAX_LIST_CALLS - listCalls;
|
|
230419
|
-
if (budget <= 0 || now() >= deadline) {
|
|
230420
|
-
truncated = true;
|
|
230421
|
-
break;
|
|
230422
|
-
}
|
|
230423
|
-
const take = nextDirs.slice(0, budget);
|
|
230424
|
-
if (take.length < nextDirs.length) truncated = true;
|
|
230425
|
-
const outcomes = await Promise.all(take.map((p2) => listDir(p2)));
|
|
230426
|
-
level = take.map((p2, i) => ({ path: p2, outcome: outcomes[i] }));
|
|
230427
|
-
}
|
|
230428
|
-
return { ok: true, items, truncated };
|
|
230429
|
-
}
|
|
230430
|
-
async function collectMessageFiles(messages, blobs) {
|
|
230431
|
-
const rows = [];
|
|
230432
|
-
for (const message of messages) {
|
|
230433
|
-
if (!Array.isArray(message.attachments)) continue;
|
|
230434
|
-
message.attachments.forEach((attachment, index2) => {
|
|
230435
|
-
if (!attachment || typeof attachment.name !== "string" || !attachment.name) return;
|
|
230436
|
-
const blobRef = typeof attachment.blobRef === "string" && attachment.blobRef ? attachment.blobRef : null;
|
|
230437
|
-
rows.push({
|
|
230438
|
-
item: {
|
|
230439
|
-
id: `msg:${message.id}:${index2}`,
|
|
230440
|
-
name: attachment.name,
|
|
230441
|
-
kind: "file",
|
|
230442
|
-
updated_at: attachment.updatedAt ?? message.createdAt,
|
|
230443
|
-
source: "message",
|
|
230444
|
-
blob_ref: blobRef,
|
|
230445
|
-
path: null,
|
|
230446
|
-
message_id: message.id,
|
|
230447
|
-
// 没有 blobRef 的是**历史纯文本附件**(契约注释:legacy text attachments may only
|
|
230448
|
-
// contain `name`)——没有可写回的载体,一律不给编辑入口。
|
|
230449
|
-
editable: blobRef !== null && isEditableFileName(attachment.name)
|
|
230450
|
-
},
|
|
230451
|
-
blobRef,
|
|
230452
|
-
declaredType: typeof attachment.contentType === "string" && attachment.contentType ? attachment.contentType : null
|
|
230453
|
-
});
|
|
230454
|
-
});
|
|
230455
|
-
}
|
|
230456
|
-
const metaCache = /* @__PURE__ */ new Map();
|
|
230457
|
-
const metaOf = (ref2) => {
|
|
230458
|
-
if (!blobs) return Promise.resolve(null);
|
|
230459
|
-
let hit = metaCache.get(ref2);
|
|
230460
|
-
if (!hit) {
|
|
230461
|
-
hit = blobs.meta(ref2).catch(() => null);
|
|
230462
|
-
metaCache.set(ref2, hit);
|
|
230463
|
-
}
|
|
230464
|
-
return hit;
|
|
230465
|
-
};
|
|
230466
|
-
return Promise.all(rows.map(async ({ item, blobRef, declaredType }) => {
|
|
230467
|
-
const meta = blobRef ? await metaOf(blobRef) : null;
|
|
230468
|
-
return {
|
|
230469
|
-
...item,
|
|
230470
|
-
content_type: declaredType ?? meta?.contentType ?? null,
|
|
230471
|
-
size: meta?.size ?? null
|
|
230472
|
-
};
|
|
230473
|
-
}));
|
|
230474
|
-
}
|
|
230475
|
-
function mergeChatSessionFiles(messageItems, workdirItems, materializedCopies = /* @__PURE__ */ new Set()) {
|
|
230476
|
-
const out = [];
|
|
230477
|
-
const seenMessageKeys = /* @__PURE__ */ new Set();
|
|
230478
|
-
for (const item of [...messageItems].sort((a, b2) => b2.updated_at.localeCompare(a.updated_at))) {
|
|
230479
|
-
const key = `${item.name}\0${item.blob_ref ?? ""}`;
|
|
230480
|
-
if (seenMessageKeys.has(key)) continue;
|
|
230481
|
-
seenMessageKeys.add(key);
|
|
230482
|
-
out.push(item);
|
|
230483
|
-
}
|
|
230484
|
-
const seenWorkdirPaths = /* @__PURE__ */ new Set();
|
|
230485
|
-
for (const item of workdirItems) {
|
|
230486
|
-
if (item.path && materializedCopies.has(item.path)) continue;
|
|
230487
|
-
if (seenWorkdirPaths.has(item.id)) continue;
|
|
230488
|
-
seenWorkdirPaths.add(item.id);
|
|
230489
|
-
out.push(item);
|
|
230490
|
-
}
|
|
230491
|
-
out.sort((a, b2) => b2.updated_at.localeCompare(a.updated_at) || a.name.localeCompare(b2.name));
|
|
230492
|
-
return out;
|
|
230493
|
-
}
|
|
230494
|
-
async function findMaterializedAttachmentCopies(messageItems, workdirItems, read) {
|
|
230495
|
-
const copies = /* @__PURE__ */ new Set();
|
|
230496
|
-
const attachments = messageItems.filter((item) => item.blob_ref && /^[a-f0-9]{64}$/.test(item.blob_ref));
|
|
230497
|
-
let budget = CHAT_ATTACHMENT_MAX_BYTES;
|
|
230498
|
-
const deadline = Date.now() + WORKDIR_WALK_BUDGET_MS;
|
|
230499
|
-
let reads = 0;
|
|
230500
|
-
for (const file of workdirItems) {
|
|
230501
|
-
const rootFile = file.path === file.name;
|
|
230502
|
-
const inputFile = file.path?.startsWith("inputs/attachments/") && !file.path.slice("inputs/attachments/".length).includes("/");
|
|
230503
|
-
if (!file.path || !rootFile && !inputFile) continue;
|
|
230504
|
-
const candidates = attachments.filter((attachment) => {
|
|
230505
|
-
const safe = safeAttachmentName(attachment.name);
|
|
230506
|
-
const numbered = file.name.endsWith(`-${safe}`) && /^\d+$/.test(file.name.slice(0, -(safe.length + 1)));
|
|
230507
|
-
return (rootFile ? file.name === attachment.name : file.name === safe || numbered) && attachment.size !== null && attachment.size === file.size;
|
|
230508
|
-
});
|
|
230509
|
-
if (Date.now() >= deadline) break;
|
|
230510
|
-
if (!candidates.length || file.size === null || file.size > budget || reads >= 10) continue;
|
|
230511
|
-
reads += 1;
|
|
230512
|
-
budget -= file.size;
|
|
230513
|
-
let timer;
|
|
230514
|
-
try {
|
|
230515
|
-
const result = await Promise.race([
|
|
230516
|
-
read(file.path, file.size),
|
|
230517
|
-
new Promise((resolve10) => {
|
|
230518
|
-
timer = setTimeout(() => resolve10(null), Math.max(0, deadline - Date.now()));
|
|
230519
|
-
})
|
|
230520
|
-
]);
|
|
230521
|
-
if (!result?.ok) continue;
|
|
230522
|
-
const bytes2 = Buffer.from(result.contentBase64, "base64");
|
|
230523
|
-
if (bytes2.length !== file.size) continue;
|
|
230524
|
-
const hash2 = (0, import_node_crypto52.createHash)("sha256").update(bytes2).digest("hex");
|
|
230525
|
-
if (candidates.some((attachment) => attachment.blob_ref === hash2)) copies.add(file.path);
|
|
230526
|
-
} catch {
|
|
230527
|
-
} finally {
|
|
230528
|
-
if (timer) clearTimeout(timer);
|
|
230529
|
-
}
|
|
230530
|
-
}
|
|
230531
|
-
return copies;
|
|
230532
|
-
}
|
|
230533
|
-
var import_node_crypto52, WORKDIR_WALK_MAX_DEPTH, WORKDIR_WALK_MAX_ENTRIES, WORKDIR_WALK_MAX_LIST_CALLS, WORKDIR_WALK_BUDGET_MS, WORKDIR_TREE_MAX_ENTRIES;
|
|
230534
|
-
var init_files_view = __esm({
|
|
230535
|
-
"../server/src/domains/chat-sessions/files-view.ts"() {
|
|
230536
|
-
"use strict";
|
|
230537
|
-
import_node_crypto52 = require("node:crypto");
|
|
230538
|
-
init_chat_attachment_files();
|
|
230539
|
-
init_src();
|
|
230540
|
-
WORKDIR_WALK_MAX_DEPTH = 3;
|
|
230541
|
-
WORKDIR_WALK_MAX_ENTRIES = 200;
|
|
230542
|
-
WORKDIR_WALK_MAX_LIST_CALLS = 20;
|
|
230543
|
-
WORKDIR_WALK_BUDGET_MS = 1e4;
|
|
230544
|
-
WORKDIR_TREE_MAX_ENTRIES = 2e3;
|
|
230545
|
-
}
|
|
230546
|
-
});
|
|
230547
|
-
|
|
230548
|
-
// ../server/src/domains/projects/chat-files.ts
|
|
230549
|
-
function chatFolderTitle(session) {
|
|
230550
|
-
const title = session.title?.trim();
|
|
230551
|
-
return title ? title : "\u672A\u547D\u540D\u5BF9\u8BDD";
|
|
230552
|
-
}
|
|
230553
|
-
function outputDisplayName(path41) {
|
|
230554
|
-
return path41.startsWith("outputs/") ? path41.slice("outputs/".length) : path41;
|
|
230555
|
-
}
|
|
230556
|
-
async function collectProjectChatFiles(chat, projectId2, meta, options = {}) {
|
|
230557
|
-
const scope = isUncategorizedProjectId(projectId2) ? null : projectId2;
|
|
230558
|
-
const [attachmentMessages, outputSessions] = await Promise.all([
|
|
230559
|
-
chat.listAttachmentMessages ? chat.listAttachmentMessages(scope) : Promise.resolve([]),
|
|
230560
|
-
chat.listOutputCopies ? chat.listOutputCopies(scope) : Promise.resolve([])
|
|
230561
|
-
]);
|
|
230562
|
-
const sessionIds = [.../* @__PURE__ */ new Set([
|
|
230563
|
-
...attachmentMessages.map((m2) => m2.sessionId),
|
|
230564
|
-
...outputSessions.map((s2) => s2.sessionId)
|
|
230565
|
-
])];
|
|
230566
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
230567
|
-
await Promise.all(sessionIds.map(async (id) => {
|
|
230568
|
-
const session = await chat.getSession(id);
|
|
230569
|
-
if (!session) return;
|
|
230570
|
-
if ((session.projectId ?? null) !== scope) return;
|
|
230571
|
-
if (session.visibility === "internal" || session.analyzedRunId) return;
|
|
230572
|
-
if (options.isDelegationChild && await options.isDelegationChild(id).catch(() => false)) return;
|
|
230573
|
-
sessions.set(id, session);
|
|
230574
|
-
}));
|
|
230575
|
-
const out = [];
|
|
230576
|
-
const base = (session) => ({
|
|
230577
|
-
projectId: projectId2,
|
|
230578
|
-
chatSessionId: session.id,
|
|
230579
|
-
chatTitle: chatFolderTitle(session),
|
|
230580
|
-
chatOwnerId: session.humanActorId
|
|
230581
|
-
});
|
|
230582
|
-
const bySession = /* @__PURE__ */ new Map();
|
|
230583
|
-
for (const message of attachmentMessages) {
|
|
230584
|
-
if (!sessions.has(message.sessionId)) continue;
|
|
230585
|
-
const list2 = bySession.get(message.sessionId) ?? [];
|
|
230586
|
-
list2.push(message);
|
|
230587
|
-
bySession.set(message.sessionId, list2);
|
|
230588
|
-
}
|
|
230589
|
-
for (const [sessionId, messages] of bySession) {
|
|
230590
|
-
const session = sessions.get(sessionId);
|
|
230591
|
-
const rows = await collectMessageFiles(messages.map((m2) => ({ ...m2, id: m2.messageId })), { meta });
|
|
230592
|
-
for (const row of mergeChatSessionFiles(rows, [])) {
|
|
230593
|
-
if (row.blob_ref === null) continue;
|
|
230594
|
-
out.push({
|
|
230595
|
-
...base(session),
|
|
230596
|
-
fileId: `chat:${sessionId}:${row.id}`,
|
|
230597
|
-
name: row.name,
|
|
230598
|
-
blobId: row.blob_ref,
|
|
230599
|
-
...row.content_type ? { contentType: row.content_type } : {},
|
|
230600
|
-
size: row.size,
|
|
230601
|
-
uploadedBy: session.humanActorId,
|
|
230602
|
-
uploadedAt: row.updated_at,
|
|
230603
|
-
chatSource: "attachment"
|
|
230604
|
-
});
|
|
230605
|
-
}
|
|
230606
|
-
}
|
|
230607
|
-
for (const record8 of outputSessions) {
|
|
230608
|
-
const session = sessions.get(record8.sessionId);
|
|
230609
|
-
if (!session) continue;
|
|
230610
|
-
const seen = new Set(out.filter((file) => file.chatSessionId === session.id).map((file) => `${file.name}\0${file.blobId}`));
|
|
230611
|
-
const copies = record8.copies ?? {};
|
|
230612
|
-
for (const path41 of Object.keys(copies).sort((a, b2) => a.localeCompare(b2))) {
|
|
230613
|
-
const copy = copies[path41];
|
|
230614
|
-
if (!copy?.blobRef) continue;
|
|
230615
|
-
if (seen.has(`${copy.name}\0${copy.blobRef}`)) continue;
|
|
230616
|
-
out.push({
|
|
230617
|
-
...base(session),
|
|
230618
|
-
fileId: `chat:${session.id}:out:${copy.path}`,
|
|
230619
|
-
name: outputDisplayName(copy.path),
|
|
230620
|
-
blobId: copy.blobRef,
|
|
230621
|
-
...copy.contentType ? { contentType: copy.contentType } : {},
|
|
230622
|
-
size: copy.size,
|
|
230623
|
-
uploadedBy: session.aiActorId,
|
|
230624
|
-
uploadedAt: Number.isNaN(Date.parse(copy.mtime)) ? copy.copiedAt : new Date(copy.mtime).toISOString(),
|
|
230625
|
-
chatSource: "output"
|
|
230626
|
-
});
|
|
230627
|
-
}
|
|
230628
|
-
}
|
|
230629
|
-
return out;
|
|
230630
|
-
}
|
|
230631
|
-
var init_chat_files2 = __esm({
|
|
230632
|
-
"../server/src/domains/projects/chat-files.ts"() {
|
|
230633
|
-
"use strict";
|
|
230634
|
-
init_files_view();
|
|
230635
|
-
init_uncategorized_project();
|
|
230636
|
-
}
|
|
230637
|
-
});
|
|
230638
|
-
|
|
230639
230923
|
// ../server/src/domains/projects/index.ts
|
|
230640
230924
|
function createProjectsDomain(opts) {
|
|
230641
230925
|
const build = (companyId, deps) => {
|
|
@@ -230694,7 +230978,9 @@ function createProjectsDomain(opts) {
|
|
|
230694
230978
|
service,
|
|
230695
230979
|
...opts.resolveCompany ? { resolveService } : {},
|
|
230696
230980
|
...opts.defaultBranchResolver ? { defaultBranchResolver: opts.defaultBranchResolver } : {},
|
|
230697
|
-
...opts.resolveMemberRole ? { resolveMemberRole: opts.resolveMemberRole } : {}
|
|
230981
|
+
...opts.resolveMemberRole ? { resolveMemberRole: opts.resolveMemberRole } : {},
|
|
230982
|
+
...opts.chatSessionsFor ? { chatSessionsFor: opts.chatSessionsFor } : {},
|
|
230983
|
+
...opts.delegationChildIds ? { delegationChildIds: opts.delegationChildIds } : {}
|
|
230698
230984
|
}),
|
|
230699
230985
|
...opts.resolveCompany ? { resolveService } : {}
|
|
230700
230986
|
};
|
|
@@ -233521,21 +233807,21 @@ function nodesDomain(deps) {
|
|
|
233521
233807
|
return { status: 200, body: { override } };
|
|
233522
233808
|
});
|
|
233523
233809
|
router.get("/api/runtimes/:runtimeId/activity", async (req) => {
|
|
233524
|
-
const
|
|
233525
|
-
if (
|
|
233810
|
+
const found2 = await runtimeInScope(req);
|
|
233811
|
+
if (found2.denied) return found2.denied;
|
|
233526
233812
|
const reader = await deps.runtimeRuns?.(req.auth.companyId);
|
|
233527
|
-
const activity = reader ? await reader.runtimeRunActivity(
|
|
233813
|
+
const activity = reader ? await reader.runtimeRunActivity(found2.runtimeId) : null;
|
|
233528
233814
|
return { status: 200, body: { activity } };
|
|
233529
233815
|
});
|
|
233530
233816
|
router.get("/api/runtimes/:runtimeId/usage", async (req) => {
|
|
233531
|
-
const
|
|
233532
|
-
if (
|
|
233817
|
+
const found2 = await runtimeInScope(req);
|
|
233818
|
+
if (found2.denied) return found2.denied;
|
|
233533
233819
|
const granularity = req.query.get("granularity") === "day" ? "day" : "hour";
|
|
233534
233820
|
const window2 = parseRuntimeUsageWindow(req.query, granularity, Date.now());
|
|
233535
233821
|
if (typeof window2 === "string") return { status: 400, body: { error: window2 } };
|
|
233536
233822
|
const reader = await deps.runtimeRuns?.(req.auth.companyId);
|
|
233537
|
-
const runs = reader ? await reader.listRuntimeRuns(
|
|
233538
|
-
return { status: 200, body: { items: bucketRuntimeRuns(
|
|
233823
|
+
const runs = reader ? await reader.listRuntimeRuns(found2.runtimeId, window2) : null;
|
|
233824
|
+
return { status: 200, body: { items: bucketRuntimeRuns(found2.runtimeId, runs ?? [], granularity) } };
|
|
233539
233825
|
});
|
|
233540
233826
|
router.get("/api/runtimes/:runtimeId/config", async (req) => {
|
|
233541
233827
|
const runtimeId = req.params["runtimeId"];
|
|
@@ -233931,9 +234217,56 @@ var init_node_store = __esm({
|
|
|
233931
234217
|
}
|
|
233932
234218
|
});
|
|
233933
234219
|
|
|
234220
|
+
// ../server/src/domains/collab/task-notices.ts
|
|
234221
|
+
function taskNoticeRecipients(model, workorderId, snapshot, members, trigger) {
|
|
234222
|
+
const out = /* @__PURE__ */ new Set();
|
|
234223
|
+
for (const a of model.artifacts.values()) {
|
|
234224
|
+
if (a.workspace === workorderId && isHumanActorId(a.owner)) out.add(a.owner);
|
|
234225
|
+
}
|
|
234226
|
+
for (const r of snapshot?.requirements ?? []) if (isHumanActorId(r.reviewerActorId)) out.add(r.reviewerActorId);
|
|
234227
|
+
for (const r of snapshot?.reviews ?? []) if (isHumanActorId(r.reviewerActorId)) out.add(r.reviewerActorId);
|
|
234228
|
+
if (trigger) out.delete(trigger);
|
|
234229
|
+
return [...out].filter((id) => members.has(id)).sort();
|
|
234230
|
+
}
|
|
234231
|
+
function settleTrigger(events, accept) {
|
|
234232
|
+
const e = accept.event;
|
|
234233
|
+
if (e.by && isHumanActorId(e.by)) return e.by;
|
|
234234
|
+
const workId = e.workId;
|
|
234235
|
+
const before = events.filter((x2) => x2.seq <= accept.seq);
|
|
234236
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
234237
|
+
const x2 = before[i].event;
|
|
234238
|
+
if (x2.kind === "review.response" && x2.targetWorkId === workId && x2.verdict === "approve" && x2.reviewerActorId && isHumanActorId(x2.reviewerActorId)) {
|
|
234239
|
+
return x2.reviewerActorId;
|
|
234240
|
+
}
|
|
234241
|
+
if (x2.kind === "work.conclude" && x2.workId === workId && isHumanActorId(before[i].actorId)) return before[i].actorId;
|
|
234242
|
+
}
|
|
234243
|
+
return void 0;
|
|
234244
|
+
}
|
|
234245
|
+
function isCreationHold(paused) {
|
|
234246
|
+
return paused.event.reason === WORKORDER_HOLD_AT_CREATE_REASON;
|
|
234247
|
+
}
|
|
234248
|
+
function endsCreationHold(pauses, resumed) {
|
|
234249
|
+
let last;
|
|
234250
|
+
for (const p2 of pauses) if (p2.seq < resumed.seq && p2.event.kind === "workorder.paused") last = p2;
|
|
234251
|
+
return last !== void 0 && isCreationHold(last);
|
|
234252
|
+
}
|
|
234253
|
+
var TASK_NOTICE_TITLE;
|
|
234254
|
+
var init_task_notices = __esm({
|
|
234255
|
+
"../server/src/domains/collab/task-notices.ts"() {
|
|
234256
|
+
"use strict";
|
|
234257
|
+
init_src();
|
|
234258
|
+
TASK_NOTICE_TITLE = {
|
|
234259
|
+
"workorder-settled": "\u4EFB\u52A1\u5DF2\u5B8C\u6210",
|
|
234260
|
+
"workorder-cancelled": "\u4EFB\u52A1\u5DF2\u53D6\u6D88",
|
|
234261
|
+
"workorder-paused": "\u4EFB\u52A1\u5DF2\u6682\u505C",
|
|
234262
|
+
"workorder-resumed": "\u4EFB\u52A1\u5DF2\u6062\u590D"
|
|
234263
|
+
};
|
|
234264
|
+
}
|
|
234265
|
+
});
|
|
234266
|
+
|
|
233934
234267
|
// ../server/src/domains/collab/notification-ledger.ts
|
|
233935
234268
|
function isLedgerItem(item) {
|
|
233936
|
-
return item.actionRequired &&
|
|
234269
|
+
return item.actionRequired && LEDGER_INBOX_KINDS.has(item.kind);
|
|
233937
234270
|
}
|
|
233938
234271
|
function generationOf(model, item) {
|
|
233939
234272
|
if (item.kind === "rework") return item.revisionId;
|
|
@@ -233943,7 +234276,17 @@ function generationOf(model, item) {
|
|
|
233943
234276
|
}
|
|
233944
234277
|
return void 0;
|
|
233945
234278
|
}
|
|
234279
|
+
function staffingRoleOf(model, item) {
|
|
234280
|
+
if (item.kind !== "needs-staffing" || !item.artifactId) return void 0;
|
|
234281
|
+
const owner = model.artifacts.get(item.artifactId)?.owner;
|
|
234282
|
+
return owner?.startsWith(PENDING_ROLE_PREFIX) ? owner.slice(PENDING_ROLE_PREFIX.length) : void 0;
|
|
234283
|
+
}
|
|
234284
|
+
function staffingRoleOfKey(sourceKey) {
|
|
234285
|
+
return STAFFING_ROLE_KEY.exec(sourceKey)?.[1];
|
|
234286
|
+
}
|
|
233946
234287
|
function notificationSourceKey(model, item) {
|
|
234288
|
+
const role = staffingRoleOf(model, item);
|
|
234289
|
+
if (role && item.workspace) return `needs-staffing:${item.workspace}:role:${role}`;
|
|
233947
234290
|
const base = inboxItemKey(item);
|
|
233948
234291
|
if (!base) return void 0;
|
|
233949
234292
|
const gen = generationOf(model, item);
|
|
@@ -233953,6 +234296,10 @@ function generationOfKey(sourceKey) {
|
|
|
233953
234296
|
const at = sourceKey.lastIndexOf("@");
|
|
233954
234297
|
return at >= 0 ? sourceKey.slice(at + 1) : void 0;
|
|
233955
234298
|
}
|
|
234299
|
+
function workNumber(workId) {
|
|
234300
|
+
const m2 = /:(\d+)$/.exec(workId);
|
|
234301
|
+
return m2 ? Number(m2[1]) : void 0;
|
|
234302
|
+
}
|
|
233956
234303
|
function nodeOfWork(workId) {
|
|
233957
234304
|
if (!workId.startsWith("wk:")) return null;
|
|
233958
234305
|
const body2 = workId.slice(3);
|
|
@@ -233981,38 +234328,41 @@ function classifyClosure(row, facts) {
|
|
|
233981
234328
|
const o = facts.draftOutcome;
|
|
233982
234329
|
return o.by === me ? handled2(me, o.approved ? "\u5DF2\u786E\u8BA4" : "\u5DF2\u9A73\u56DE", facts.now) : invalidated("handled-by-other", facts.now, o.by);
|
|
233983
234330
|
}
|
|
233984
|
-
const
|
|
233985
|
-
|
|
234331
|
+
const evidence = [];
|
|
234332
|
+
const add = (f2) => {
|
|
234333
|
+
if (f2) evidence.push(f2);
|
|
234334
|
+
};
|
|
234335
|
+
add(resolvedByMe(row, evs, me, onNode));
|
|
233986
234336
|
const sealCancel = evs.find((rec) => {
|
|
233987
234337
|
const e = payload(rec, "plan.delete_node");
|
|
233988
234338
|
return !!e && e.nodeId === node2 && e.sealReason === "cancelled";
|
|
233989
234339
|
});
|
|
233990
|
-
if (facts.workorderCancelled
|
|
233991
|
-
if (sealCancel) return invalidated("node-cancelled", at(sealCancel), sealCancel.actorId);
|
|
234340
|
+
if (sealCancel) add({ rec: sealCancel, closure: invalidated(facts.workorderCancelled ? "workorder-cancelled" : "node-cancelled", at(sealCancel), sealCancel.actorId) });
|
|
233992
234341
|
const deleted = evs.find((rec) => {
|
|
233993
234342
|
const e = payload(rec, "plan.delete_node");
|
|
233994
|
-
return !!e && e.nodeId === node2 && e.sealReason !== "accepted";
|
|
234343
|
+
return !!e && e.nodeId === node2 && e.sealReason !== "accepted" && e.sealReason !== "cancelled";
|
|
233995
234344
|
});
|
|
233996
|
-
if (deleted)
|
|
233997
|
-
|
|
233998
|
-
if (other) return other;
|
|
234345
|
+
if (deleted) add({ rec: deleted, closure: invalidated("node-cancelled", at(deleted), deleted.actorId) });
|
|
234346
|
+
add(resolvedByOther(row, evs, me, onNode));
|
|
233999
234347
|
const gen = generationOfKey(row.sourceKey);
|
|
234000
234348
|
if (gen) {
|
|
234349
|
+
const genNo = workNumber(gen);
|
|
234350
|
+
const isNewer = (workId) => workId !== gen && (genNo === void 0 || (workNumber(workId) ?? Number.POSITIVE_INFINITY) > genNo);
|
|
234001
234351
|
const newer = evs.find((rec) => {
|
|
234002
234352
|
const c = payload(rec, "work.create");
|
|
234003
|
-
if (c && c.nodeId === node2 && c.workId
|
|
234353
|
+
if (c && c.nodeId === node2 && isNewer(c.workId)) return true;
|
|
234004
234354
|
const s2 = payload(rec, "work.submit_output");
|
|
234005
|
-
return !!s2 && nodeOfWork(s2.workId) === node2 && s2.workId
|
|
234355
|
+
return !!s2 && nodeOfWork(s2.workId) === node2 && isNewer(s2.workId);
|
|
234006
234356
|
});
|
|
234007
|
-
if (newer)
|
|
234357
|
+
if (newer) add({ rec: newer, closure: invalidated("superseded", at(newer), newer.actorId) });
|
|
234008
234358
|
}
|
|
234009
234359
|
const reassign = evs.find((rec) => {
|
|
234010
234360
|
const a = payload(rec, "plan.assign_actor");
|
|
234011
234361
|
if (a && a.nodeId === node2 && a.assigneeActorId !== me && ownerKinds.has(row.kind)) return true;
|
|
234012
234362
|
const r = payload(rec, "plan.update_review_requirements");
|
|
234013
|
-
return !!r && r.nodeId === node2 && reviewKinds.has(row.kind);
|
|
234363
|
+
return !!r && r.nodeId === node2 && reviewKinds.has(row.kind) && !r.reviewers.some((x2) => x2.reviewerActorId === me);
|
|
234014
234364
|
});
|
|
234015
|
-
if (reassign)
|
|
234365
|
+
if (reassign) add({ rec: reassign, closure: invalidated("reassigned", at(reassign), reassign.actorId) });
|
|
234016
234366
|
const myReviewPrefix = gen ? `rv:${gen}:${me}:` : void 0;
|
|
234017
234367
|
const withdrawn = evs.find((rec) => {
|
|
234018
234368
|
const k2 = payload(rec, "review.kill") ?? payload(rec, "review.timeout");
|
|
@@ -234020,9 +234370,15 @@ function classifyClosure(row, facts) {
|
|
|
234020
234370
|
if (item?.waitingRowId !== void 0 && k2.reviewId === item.waitingRowId) return true;
|
|
234021
234371
|
return row.kind === "review-request" && !!myReviewPrefix && k2.reviewId.startsWith(myReviewPrefix);
|
|
234022
234372
|
});
|
|
234023
|
-
if (withdrawn)
|
|
234373
|
+
if (withdrawn) add({ rec: withdrawn, closure: invalidated("source-changed", at(withdrawn), withdrawn.actorId) });
|
|
234374
|
+
const first = evidence.reduce((best, f2) => !best || f2.rec.seq < best.rec.seq ? f2 : best, null);
|
|
234375
|
+
if (first) return first.closure;
|
|
234376
|
+
if (facts.workorderCancelled) return invalidated("workorder-cancelled", facts.now);
|
|
234024
234377
|
return null;
|
|
234025
234378
|
}
|
|
234379
|
+
function staffingNodeMatches(row, nodeId) {
|
|
234380
|
+
return staffingRoleOfKey(row.sourceKey) !== void 0 || nodeId === row.nodeId;
|
|
234381
|
+
}
|
|
234026
234382
|
function issueIdsOf(row) {
|
|
234027
234383
|
const it = row.item;
|
|
234028
234384
|
return new Set([it?.gap?.gapId, it?.escalationId, it?.annotationId].filter((x2) => !!x2));
|
|
@@ -234034,16 +234390,16 @@ function resolvedByMe(row, evs, me, onNode) {
|
|
|
234034
234390
|
switch (row.kind) {
|
|
234035
234391
|
case "review-request": {
|
|
234036
234392
|
const v2 = payload(rec, "review.response");
|
|
234037
|
-
if (v2 && v2.reviewerActorId === me && onNode(rec) && (!gen || v2.targetWorkId === gen)) return handled2(me, verdictWord(v2.verdict), rec.createdAt);
|
|
234393
|
+
if (v2 && v2.reviewerActorId === me && onNode(rec) && (!gen || v2.targetWorkId === gen)) return found(rec, handled2(me, verdictWord(v2.verdict), rec.createdAt));
|
|
234038
234394
|
const acc = payload(rec, "work.accept");
|
|
234039
|
-
if (acc && acc.by === me && gen && acc.workId === gen) return handled2(me, "\u5DF2\u9A8C\u6536", rec.createdAt);
|
|
234395
|
+
if (acc && acc.by === me && gen && acc.workId === gen) return found(rec, handled2(me, "\u5DF2\u9A8C\u6536", rec.createdAt));
|
|
234040
234396
|
const rej = payload(rec, "work.reject");
|
|
234041
|
-
if (rej && rej.by === me && gen && rej.workId === gen) return handled2(me, "\u5DF2\u6253\u56DE", rec.createdAt);
|
|
234397
|
+
if (rej && rej.by === me && gen && rej.workId === gen) return found(rec, handled2(me, "\u5DF2\u6253\u56DE", rec.createdAt));
|
|
234042
234398
|
break;
|
|
234043
234399
|
}
|
|
234044
234400
|
case "review-waiting": {
|
|
234045
234401
|
const v2 = payload(rec, "review.response");
|
|
234046
|
-
if (v2 && v2.reviewerActorId === me && item?.waitingRowId && v2.reviewId === item.waitingRowId) return handled2(me, verdictWord(v2.verdict), rec.createdAt);
|
|
234402
|
+
if (v2 && v2.reviewerActorId === me && item?.waitingRowId && v2.reviewId === item.waitingRowId) return found(rec, handled2(me, verdictWord(v2.verdict), rec.createdAt));
|
|
234047
234403
|
break;
|
|
234048
234404
|
}
|
|
234049
234405
|
case "work-waiting":
|
|
@@ -234051,24 +234407,24 @@ function resolvedByMe(row, evs, me, onNode) {
|
|
|
234051
234407
|
case "rework":
|
|
234052
234408
|
case "stale": {
|
|
234053
234409
|
const s2 = payload(rec, "work.submit_output") ?? payload(rec, "work.conclude");
|
|
234054
|
-
if (s2 && doerOf(rec) === me && onNode(rec) && !(row.kind === "rework" && s2.workId === gen)) return handled2(me, "\u5DF2\u63D0\u4EA4", rec.createdAt);
|
|
234410
|
+
if (s2 && doerOf(rec) === me && onNode(rec) && !(row.kind === "rework" && s2.workId === gen)) return found(rec, handled2(me, "\u5DF2\u63D0\u4EA4", rec.createdAt));
|
|
234055
234411
|
break;
|
|
234056
234412
|
}
|
|
234057
234413
|
case "pushback": {
|
|
234058
234414
|
const r = payload(rec, "issue.resolve");
|
|
234059
|
-
if (r && r.by === me && issueIdsOf(row).has(r.issueId)) return handled2(me, "\u5DF2\u5904\u7406", rec.createdAt);
|
|
234415
|
+
if (r && r.by === me && issueIdsOf(row).has(r.issueId)) return found(rec, handled2(me, "\u5DF2\u5904\u7406", rec.createdAt));
|
|
234060
234416
|
const s2 = payload(rec, "work.submit_output");
|
|
234061
|
-
if (s2 && doerOf(rec) === me && onNode(rec)) return handled2(me, "\u5DF2\u63D0\u4EA4", rec.createdAt);
|
|
234417
|
+
if (s2 && doerOf(rec) === me && onNode(rec)) return found(rec, handled2(me, "\u5DF2\u63D0\u4EA4", rec.createdAt));
|
|
234062
234418
|
break;
|
|
234063
234419
|
}
|
|
234064
234420
|
case "gap-escalation": {
|
|
234065
234421
|
const r = payload(rec, "issue.resolve");
|
|
234066
|
-
if (r && r.by === me && issueIdsOf(row).has(r.issueId)) return handled2(me, "\u5DF2\u89E3\u51B3", rec.createdAt);
|
|
234422
|
+
if (r && r.by === me && issueIdsOf(row).has(r.issueId)) return found(rec, handled2(me, "\u5DF2\u89E3\u51B3", rec.createdAt));
|
|
234067
234423
|
break;
|
|
234068
234424
|
}
|
|
234069
234425
|
case "needs-staffing": {
|
|
234070
234426
|
const a = payload(rec, "plan.assign_actor");
|
|
234071
|
-
if (a && rec.actorId === me && a.nodeId
|
|
234427
|
+
if (a && rec.actorId === me && staffingNodeMatches(row, a.nodeId) && a.assigneeActorId) return found(rec, handled2(me, "\u5DF2\u6307\u6D3E", rec.createdAt));
|
|
234072
234428
|
break;
|
|
234073
234429
|
}
|
|
234074
234430
|
default:
|
|
@@ -234085,21 +234441,25 @@ function resolvedByOther(row, evs, me, onNode) {
|
|
|
234085
234441
|
switch (row.kind) {
|
|
234086
234442
|
case "review-request":
|
|
234087
234443
|
case "review-waiting": {
|
|
234088
|
-
const v2 = payload(rec, "review.response");
|
|
234089
|
-
if (v2 && onNode(rec) && (!gen || v2.targetWorkId === gen)) return invalidated("handled-by-other", rec.createdAt, who);
|
|
234090
234444
|
const acc = payload(rec, "work.accept") ?? payload(rec, "work.reject");
|
|
234091
|
-
if (acc && gen && acc.workId === gen)
|
|
234445
|
+
if (acc && gen && acc.workId === gen) {
|
|
234446
|
+
const decider = evs.filter((x2) => x2.seq <= rec.seq).reverse().find((x2) => {
|
|
234447
|
+
const vv = payload(x2, "review.response");
|
|
234448
|
+
return !!vv && vv.targetWorkId === gen && vv.reviewerActorId !== me;
|
|
234449
|
+
});
|
|
234450
|
+
return found(rec, invalidated("handled-by-other", rec.createdAt, decider ? doerOf(decider) : who));
|
|
234451
|
+
}
|
|
234092
234452
|
break;
|
|
234093
234453
|
}
|
|
234094
234454
|
case "gap-escalation":
|
|
234095
234455
|
case "pushback": {
|
|
234096
234456
|
const r = payload(rec, "issue.resolve");
|
|
234097
|
-
if (r && issueIdsOf(row).has(r.issueId)) return invalidated("handled-by-other", rec.createdAt, who);
|
|
234457
|
+
if (r && issueIdsOf(row).has(r.issueId)) return found(rec, invalidated("handled-by-other", rec.createdAt, who));
|
|
234098
234458
|
break;
|
|
234099
234459
|
}
|
|
234100
234460
|
case "needs-staffing": {
|
|
234101
234461
|
const a = payload(rec, "plan.assign_actor");
|
|
234102
|
-
if (a && a.nodeId
|
|
234462
|
+
if (a && staffingNodeMatches(row, a.nodeId) && a.assigneeActorId) return found(rec, invalidated("handled-by-other", rec.createdAt, rec.actorId));
|
|
234103
234463
|
break;
|
|
234104
234464
|
}
|
|
234105
234465
|
case "assigned":
|
|
@@ -234107,7 +234467,7 @@ function resolvedByOther(row, evs, me, onNode) {
|
|
|
234107
234467
|
case "stale":
|
|
234108
234468
|
case "work-waiting": {
|
|
234109
234469
|
const acc = payload(rec, "work.accept");
|
|
234110
|
-
if (acc && onNode(rec)) return invalidated("handled-by-other", rec.createdAt, who);
|
|
234470
|
+
if (acc && onNode(rec)) return found(rec, invalidated("handled-by-other", rec.createdAt, who));
|
|
234111
234471
|
break;
|
|
234112
234472
|
}
|
|
234113
234473
|
default:
|
|
@@ -234119,24 +234479,13 @@ function resolvedByOther(row, evs, me, onNode) {
|
|
|
234119
234479
|
function needsGrace(missingSinceMs, nowMs, graceMs) {
|
|
234120
234480
|
return nowMs - missingSinceMs < graceMs;
|
|
234121
234481
|
}
|
|
234122
|
-
var
|
|
234482
|
+
var STAFFING_ROLE_KEY, CLOSURE_EVENT_KINDS, payload, handled2, invalidated, found, ownerKinds, reviewKinds, verdictWord;
|
|
234123
234483
|
var init_notification_ledger = __esm({
|
|
234124
234484
|
"../server/src/domains/collab/notification-ledger.ts"() {
|
|
234125
234485
|
"use strict";
|
|
234126
234486
|
init_src5();
|
|
234127
234487
|
init_src();
|
|
234128
|
-
|
|
234129
|
-
"review-request",
|
|
234130
|
-
"rework",
|
|
234131
|
-
"stale",
|
|
234132
|
-
"pushback",
|
|
234133
|
-
"assigned",
|
|
234134
|
-
"needs-staffing",
|
|
234135
|
-
"gap-escalation",
|
|
234136
|
-
"change-confirm",
|
|
234137
|
-
"review-waiting",
|
|
234138
|
-
"work-waiting"
|
|
234139
|
-
]);
|
|
234488
|
+
STAFFING_ROLE_KEY = /^needs-staffing:.+:role:([^:@]+)$/;
|
|
234140
234489
|
CLOSURE_EVENT_KINDS = [
|
|
234141
234490
|
"review.response",
|
|
234142
234491
|
"review.kill",
|
|
@@ -234155,6 +234504,7 @@ var init_notification_ledger = __esm({
|
|
|
234155
234504
|
payload = (rec, kind) => rec.event.kind === kind ? rec.event : null;
|
|
234156
234505
|
handled2 = (closedBy, result, at) => ({ status: "handled", closedAt: at, closedBy, result });
|
|
234157
234506
|
invalidated = (invalidReason, at, closedBy) => ({ status: "invalidated", closedAt: at, invalidReason, ...closedBy ? { closedBy } : {} });
|
|
234507
|
+
found = (rec, closure) => ({ rec, closure });
|
|
234158
234508
|
ownerKinds = /* @__PURE__ */ new Set(["assigned", "rework", "stale", "pushback", "work-waiting"]);
|
|
234159
234509
|
reviewKinds = /* @__PURE__ */ new Set(["review-request", "review-waiting"]);
|
|
234160
234510
|
verdictWord = (verdict) => verdict === "approve" ? "\u5BA1\u6838\u901A\u8FC7" : verdict === "request_changes" || verdict === "reject" ? "\u8981\u6C42\u4FEE\u6539" : "\u5DF2\u5BA1\u6838";
|
|
@@ -234218,15 +234568,18 @@ function displayOf(model, item, recipient, reviewOf) {
|
|
|
234218
234568
|
function sameDisplay(row, d) {
|
|
234219
234569
|
return row.title === d.title && row.workspaceTitle === d.workspaceTitle && row.actorId === d.actorId && row.cardId === d.cardId && JSON.stringify(row.item ?? null) === JSON.stringify(d.item ?? null);
|
|
234220
234570
|
}
|
|
234221
|
-
var DAY2, EMPTY, EVENT_WINDOW_SLACK_MS, CLOSURE_EVENT_LIMIT, DRAFT_OUTCOME_TTL_MS, SLOW_RECONCILE_MS, rowKey, NotificationLedgerWorker;
|
|
234571
|
+
var DAY2, EMPTY, EVENT_WINDOW_SLACK_MS, CLOSURE_EVENT_LIMIT, DRAFT_OUTCOME_TTL_MS, SLOW_RECONCILE_MS, TASK_NOTICE_FRESH_MS, TASK_NOTICE_EVENT_LIMIT, rowKey, NotificationLedgerWorker;
|
|
234222
234572
|
var init_notification_reconciler = __esm({
|
|
234223
234573
|
"../server/src/domains/collab/notification-reconciler.ts"() {
|
|
234224
234574
|
"use strict";
|
|
234225
234575
|
init_src5();
|
|
234576
|
+
init_src();
|
|
234226
234577
|
init_inbox();
|
|
234227
234578
|
init_waiting_inbox();
|
|
234228
234579
|
init_node_state();
|
|
234229
234580
|
init_review_activity();
|
|
234581
|
+
init_workorder_terminal();
|
|
234582
|
+
init_task_notices();
|
|
234230
234583
|
init_notification_ledger();
|
|
234231
234584
|
DAY2 = 24 * 36e5;
|
|
234232
234585
|
EMPTY = { inserted: 0, refreshed: 0, closed: 0, pending: 0 };
|
|
@@ -234234,6 +234587,8 @@ var init_notification_reconciler = __esm({
|
|
|
234234
234587
|
CLOSURE_EVENT_LIMIT = 400;
|
|
234235
234588
|
DRAFT_OUTCOME_TTL_MS = 36e5;
|
|
234236
234589
|
SLOW_RECONCILE_MS = 2e3;
|
|
234590
|
+
TASK_NOTICE_FRESH_MS = 15 * 6e4;
|
|
234591
|
+
TASK_NOTICE_EVENT_LIMIT = 500;
|
|
234237
234592
|
rowKey = (recipient, sourceKey) => `${recipient}\0${sourceKey}`;
|
|
234238
234593
|
NotificationLedgerWorker = class {
|
|
234239
234594
|
constructor(deps) {
|
|
@@ -234256,7 +234611,7 @@ var init_notification_reconciler = __esm({
|
|
|
234256
234611
|
state(companyId) {
|
|
234257
234612
|
let st = this.states.get(companyId);
|
|
234258
234613
|
if (!st) {
|
|
234259
|
-
st = { running: false, rerun: false, lastRunAt: 0, snaps: /* @__PURE__ */ new Map(), lastSeenAt: /* @__PURE__ */ new Map(), missingSince: /* @__PURE__ */ new Map(), drafts: /* @__PURE__ */ new Map(), lastPurgeAt: 0, primed: false };
|
|
234614
|
+
st = { running: false, rerun: false, lastRunAt: 0, snaps: /* @__PURE__ */ new Map(), lastSeenAt: /* @__PURE__ */ new Map(), missingSince: /* @__PURE__ */ new Map(), drafts: /* @__PURE__ */ new Map(), lastPurgeAt: 0, primed: false, terminals: /* @__PURE__ */ new Map(), noticeSeq: 0 };
|
|
234260
234615
|
this.states.set(companyId, st);
|
|
234261
234616
|
}
|
|
234262
234617
|
return st;
|
|
@@ -234375,6 +234730,14 @@ var init_notification_reconciler = __esm({
|
|
|
234375
234730
|
const pending = await ctx.store.listPending();
|
|
234376
234731
|
const byKey = new Map(pending.map((r) => [rowKey(r.recipient, r.sourceKey), r]));
|
|
234377
234732
|
const markersOf = /* @__PURE__ */ new Map();
|
|
234733
|
+
const adopted = /* @__PURE__ */ new Set();
|
|
234734
|
+
const roleOfRow = (r) => {
|
|
234735
|
+
const fromKey = staffingRoleOfKey(r.sourceKey);
|
|
234736
|
+
if (fromKey) return fromKey;
|
|
234737
|
+
const owner = r.nodeId ? model.artifacts.get(r.nodeId)?.owner : void 0;
|
|
234738
|
+
return owner?.startsWith(PENDING_ROLE_PREFIX) ? owner.slice(PENDING_ROLE_PREFIX.length) : void 0;
|
|
234739
|
+
};
|
|
234740
|
+
const staffingOrphans = pending.filter((r) => r.kind === "needs-staffing" && !current.has(rowKey(r.recipient, r.sourceKey)));
|
|
234378
234741
|
for (const [k2, c] of current) {
|
|
234379
234742
|
const display = displayOf(model, c.item, c.recipient, reviewOf);
|
|
234380
234743
|
const row = byKey.get(k2);
|
|
@@ -234387,6 +234750,15 @@ var init_notification_reconciler = __esm({
|
|
|
234387
234750
|
}
|
|
234388
234751
|
continue;
|
|
234389
234752
|
}
|
|
234753
|
+
const role = staffingRoleOf(model, c.item);
|
|
234754
|
+
const orphan = role ? staffingOrphans.find((r) => !adopted.has(r.id) && r.recipient === c.recipient && r.workorderId === c.item.workspace && roleOfRow(r) === role) : void 0;
|
|
234755
|
+
if (orphan && await ctx.store.rekey(orphan.id, c.sourceKey, { ...display, ...c.item.artifactId ? { nodeId: c.item.artifactId } : {} }, nowIso)) {
|
|
234756
|
+
adopted.add(orphan.id);
|
|
234757
|
+
st.missingSince.delete(orphan.id);
|
|
234758
|
+
st.lastSeenAt.set(orphan.id, nowIso);
|
|
234759
|
+
stats.refreshed++;
|
|
234760
|
+
continue;
|
|
234761
|
+
}
|
|
234390
234762
|
const readAt = st.primed ? void 0 : await this.alreadyReadAt(ctx, c.recipient, c.item, markersOf, nowIso);
|
|
234391
234763
|
const sinceOk = Number.isFinite(Date.parse(c.item.since));
|
|
234392
234764
|
const enteredAt = st.primed || !sinceOk ? nowIso : new Date(c.item.since).toISOString();
|
|
@@ -234409,7 +234781,7 @@ var init_notification_reconciler = __esm({
|
|
|
234409
234781
|
const cancelledOf = /* @__PURE__ */ new Map();
|
|
234410
234782
|
let waitingForGrace = false;
|
|
234411
234783
|
for (const row of pending) {
|
|
234412
|
-
if (current.has(rowKey(row.recipient, row.sourceKey))) continue;
|
|
234784
|
+
if (current.has(rowKey(row.recipient, row.sourceKey)) || adopted.has(row.id)) continue;
|
|
234413
234785
|
const closure = await this.judge(st, ctx, model, row, nowMs, nowIso, eventsOf, cancelledOf);
|
|
234414
234786
|
if (!closure) {
|
|
234415
234787
|
waitingForGrace = true;
|
|
@@ -234420,6 +234792,7 @@ var init_notification_reconciler = __esm({
|
|
|
234420
234792
|
st.lastSeenAt.delete(row.id);
|
|
234421
234793
|
}
|
|
234422
234794
|
stats.pending = current.size;
|
|
234795
|
+
stats.notices = await this.reconcileTaskNotices(st, ctx, model, nowMs, loadSnapshot2);
|
|
234423
234796
|
if (nowMs - st.lastPurgeAt > (this.deps.purgeEveryMs ?? 6 * 36e5)) {
|
|
234424
234797
|
st.lastPurgeAt = nowMs;
|
|
234425
234798
|
const purged = await ctx.store.purgeBefore(new Date(nowMs - (this.deps.retentionMs ?? 90 * DAY2)).toISOString());
|
|
@@ -234458,6 +234831,74 @@ var init_notification_reconciler = __esm({
|
|
|
234458
234831
|
if (needsGrace(since, nowMs, this.graceMs)) return null;
|
|
234459
234832
|
return { status: "invalidated", closedAt: nowIso, invalidReason: "source-changed" };
|
|
234460
234833
|
}
|
|
234834
|
+
/**
|
|
234835
|
+
* 任务告知(第 2 步 B)。完成 / 取消从当前状态现算(与结案播报、任务列表同一判据),暂停 / 恢复读引擎事件。
|
|
234836
|
+
*
|
|
234837
|
+
* **不补历史**:进程第一次对账时(上线当天、重启后)只发最近 {@link TASK_NOTICE_FRESH_MS} 内发生的变化——
|
|
234838
|
+
* 早就结束的任务不能在上线那一刻集体「任务已完成」一遍。之后每拍新看到的变化都发。
|
|
234839
|
+
* 来源键 = `<种类>:<工单>@<那件事的 seq>`:同一件事重启后再算一遍撞唯一约束即收手;任务重开后再完成是新的一件事。
|
|
234840
|
+
*/
|
|
234841
|
+
async reconcileTaskNotices(st, ctx, model, nowMs, loadSnapshot2) {
|
|
234842
|
+
const store = ctx.engineStore;
|
|
234843
|
+
if (!store) return 0;
|
|
234844
|
+
const members = new Set(ctx.humans);
|
|
234845
|
+
const titleOf = workspaceTitleMap(model);
|
|
234846
|
+
const fresh = (iso7) => nowMs - Date.parse(iso7) <= TASK_NOTICE_FRESH_MS;
|
|
234847
|
+
let sent = 0;
|
|
234848
|
+
const emit = async (workorderId, kind, rec, trigger) => {
|
|
234849
|
+
const recipients = taskNoticeRecipients(model, workorderId, await loadSnapshot2(workorderId), members, trigger);
|
|
234850
|
+
const title = titleOf.get(workorderId);
|
|
234851
|
+
for (const recipient of recipients) {
|
|
234852
|
+
await ctx.store.insert({
|
|
234853
|
+
recipient,
|
|
234854
|
+
category: "task-notice",
|
|
234855
|
+
sourceKey: `${kind}:${workorderId}@${rec.seq}`,
|
|
234856
|
+
kind,
|
|
234857
|
+
workorderId,
|
|
234858
|
+
title: TASK_NOTICE_TITLE[kind],
|
|
234859
|
+
...title ? { workspaceTitle: title } : {},
|
|
234860
|
+
...trigger ? { actorId: trigger } : {},
|
|
234861
|
+
enteredAt: new Date(rec.createdAt).toISOString()
|
|
234862
|
+
});
|
|
234863
|
+
sent++;
|
|
234864
|
+
}
|
|
234865
|
+
};
|
|
234866
|
+
const pauses = await store.transaction((tx) => tx.listEvents(void 0, st.noticeSeq, TASK_NOTICE_EVENT_LIMIT, ["workorder.paused", "workorder.resumed"]));
|
|
234867
|
+
for (const rec of pauses) {
|
|
234868
|
+
st.noticeSeq = Math.max(st.noticeSeq, rec.seq);
|
|
234869
|
+
if (!st.primed && !fresh(rec.createdAt)) continue;
|
|
234870
|
+
const e = rec.event;
|
|
234871
|
+
const paused = e.kind === "workorder.paused";
|
|
234872
|
+
if (paused ? isCreationHold(rec) : endsCreationHold(
|
|
234873
|
+
await store.transaction((tx) => tx.listEvents(rec.workorderId, 0, TASK_NOTICE_EVENT_LIMIT, ["workorder.paused"])),
|
|
234874
|
+
rec
|
|
234875
|
+
)) continue;
|
|
234876
|
+
await emit(rec.workorderId, paused ? "workorder-paused" : "workorder-resumed", rec, e.by ?? rec.actorId);
|
|
234877
|
+
}
|
|
234878
|
+
const terminals = computeWorkorderTerminals(model);
|
|
234879
|
+
const now = /* @__PURE__ */ new Set();
|
|
234880
|
+
for (const t of terminals) {
|
|
234881
|
+
const kind = t.kind;
|
|
234882
|
+
now.add(t.workOrderId);
|
|
234883
|
+
if (st.terminals.get(t.workOrderId) === kind) continue;
|
|
234884
|
+
st.terminals.set(t.workOrderId, kind);
|
|
234885
|
+
if (!st.primed && !(t.terminalAt && fresh(t.terminalAt))) continue;
|
|
234886
|
+
const cancelled = kind === "workorder-cancelled";
|
|
234887
|
+
const evs = await store.transaction((tx) => tx.listEvents(
|
|
234888
|
+
t.workOrderId,
|
|
234889
|
+
0,
|
|
234890
|
+
TASK_NOTICE_EVENT_LIMIT,
|
|
234891
|
+
cancelled ? ["plan.delete_node"] : ["work.accept", "review.response", "work.conclude"]
|
|
234892
|
+
));
|
|
234893
|
+
const decisive = [...evs].reverse().find((rec) => cancelled ? rec.event.sealReason === "cancelled" : rec.event.kind === "work.accept");
|
|
234894
|
+
if (!decisive) continue;
|
|
234895
|
+
if (!st.primed && !fresh(decisive.createdAt)) continue;
|
|
234896
|
+
const trigger = cancelled ? decisive.actorId : settleTrigger(evs, decisive);
|
|
234897
|
+
await emit(t.workOrderId, kind, decisive, trigger && !isSystemActorId(trigger) ? trigger : void 0);
|
|
234898
|
+
}
|
|
234899
|
+
for (const wid of [...st.terminals.keys()]) if (!now.has(wid)) st.terminals.delete(wid);
|
|
234900
|
+
return sent;
|
|
234901
|
+
}
|
|
234461
234902
|
/** 台账上线前已经看过的待办不重新亮红点:沿用 `/api/inbox` 的未读判据(基线 + 逐条水位)。 */
|
|
234462
234903
|
async alreadyReadAt(ctx, recipient, item, cache, nowIso) {
|
|
234463
234904
|
if (!ctx.markers) return void 0;
|
|
@@ -234822,7 +235263,7 @@ function makeSeededWorkorderCreator(deps) {
|
|
|
234822
235263
|
}
|
|
234823
235264
|
if (input.dispatch === false && artifactState) {
|
|
234824
235265
|
await artifactState.setWorkspaceDispatchHold(workspace, true);
|
|
234825
|
-
await kernel.pauseWorkorder?.(workspace, input.actor);
|
|
235266
|
+
await kernel.pauseWorkorder?.(workspace, input.actor, WORKORDER_HOLD_AT_CREATE_REASON);
|
|
234826
235267
|
}
|
|
234827
235268
|
return { workspace, rootArtifactId: briefId, spawned: planned.spawned };
|
|
234828
235269
|
};
|
|
@@ -234847,6 +235288,7 @@ var init_create_seeded_workorder = __esm({
|
|
|
234847
235288
|
"use strict";
|
|
234848
235289
|
import_node_crypto59 = require("node:crypto");
|
|
234849
235290
|
init_uncategorized_project();
|
|
235291
|
+
init_src();
|
|
234850
235292
|
init_planner();
|
|
234851
235293
|
enc2 = (s2) => new TextEncoder().encode(s2);
|
|
234852
235294
|
}
|
|
@@ -235703,6 +236145,37 @@ var init_trace_health = __esm({
|
|
|
235703
236145
|
}
|
|
235704
236146
|
});
|
|
235705
236147
|
|
|
236148
|
+
// ../server/src/domains/trace/subagent-events.ts
|
|
236149
|
+
function subagentRunEventFromLive(event) {
|
|
236150
|
+
if (event.itemType !== "control") return null;
|
|
236151
|
+
const payload2 = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload : void 0;
|
|
236152
|
+
if (payload2?.code !== SUBAGENT_TASK_CODE || typeof payload2.toolCallId !== "string") return null;
|
|
236153
|
+
const raw = payload2.task;
|
|
236154
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
236155
|
+
const task = raw;
|
|
236156
|
+
const cost = task.estimatedCostUsdMicros ?? estimateSubagentCostUsdMicros(task.usage);
|
|
236157
|
+
const enriched = cost !== void 0 ? { ...task, estimatedCostUsdMicros: cost } : task;
|
|
236158
|
+
const eventType = task.status === "running" ? "sub_agent.started" : "sub_agent.finished";
|
|
236159
|
+
return {
|
|
236160
|
+
eventType,
|
|
236161
|
+
stream: "tool",
|
|
236162
|
+
...task.status === "failed" ? { level: "error" } : {},
|
|
236163
|
+
...task.description ? { message: task.description } : {},
|
|
236164
|
+
payload: {
|
|
236165
|
+
kind: "subagent_task",
|
|
236166
|
+
raw: { code: SUBAGENT_TASK_CODE, toolCallId: payload2.toolCallId, task: enriched }
|
|
236167
|
+
},
|
|
236168
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
236169
|
+
};
|
|
236170
|
+
}
|
|
236171
|
+
var init_subagent_events = __esm({
|
|
236172
|
+
"../server/src/domains/trace/subagent-events.ts"() {
|
|
236173
|
+
"use strict";
|
|
236174
|
+
init_src();
|
|
236175
|
+
init_subagent_cost();
|
|
236176
|
+
}
|
|
236177
|
+
});
|
|
236178
|
+
|
|
235706
236179
|
// ../server/src/domains/trace/index.ts
|
|
235707
236180
|
function createTraceDomain(opts) {
|
|
235708
236181
|
const service = new TraceService({ store: opts.store });
|
|
@@ -235735,6 +236208,7 @@ var init_trace2 = __esm({
|
|
|
235735
236208
|
init_chat_parts();
|
|
235736
236209
|
init_trace_health();
|
|
235737
236210
|
init_resilient_appender();
|
|
236211
|
+
init_subagent_events();
|
|
235738
236212
|
}
|
|
235739
236213
|
});
|
|
235740
236214
|
|
|
@@ -237385,7 +237859,12 @@ async function buildChatProjectBundle(projectId2, projects, readBlob) {
|
|
|
237385
237859
|
"",
|
|
237386
237860
|
"\u751F\u6210 Brief \u548C\u534F\u4F5C\u56FE\u524D\uFF0C\u5148\u9605\u8BFB\u672C\u6587\u4EF6\u5217\u51FA\u7684\u516C\u5F00\u8F93\u5165\u3002\u4E0D\u8981\u5047\u8BBE Benchmark \u4ED3\u5E93\u3001\u9690\u85CF\u6D4B\u8BD5\u3001Oracle \u6216\u53C2\u8003\u7B54\u6848\u53EF\u89C1\u3002"
|
|
237387
237861
|
].join("\n");
|
|
237388
|
-
return {
|
|
237862
|
+
return {
|
|
237863
|
+
files,
|
|
237864
|
+
binaryFiles,
|
|
237865
|
+
contextLines,
|
|
237866
|
+
project: { id: project.id, name: project.name, ...project.description ? { description: project.description } : {} }
|
|
237867
|
+
};
|
|
237389
237868
|
}
|
|
237390
237869
|
var textFile, safeProjectPath;
|
|
237391
237870
|
var init_project_bundle = __esm({
|
|
@@ -238095,7 +238574,7 @@ function createChatSessionsDomain(opts) {
|
|
|
238095
238574
|
const outcome = await bridge.read(
|
|
238096
238575
|
loc.nodeId,
|
|
238097
238576
|
{ workdirKey: loc.workdirKey, runtimeKind: loc.runtimeKind, rtId: loc.rtId, path: req.query?.get("path") ?? "" },
|
|
238098
|
-
|
|
238577
|
+
FILE_CONTENT_MAX_BYTES
|
|
238099
238578
|
);
|
|
238100
238579
|
if (outcome.ok) {
|
|
238101
238580
|
const buf = Buffer.from(outcome.contentBase64, "base64");
|
|
@@ -238223,8 +238702,8 @@ function createChatSessionsDomain(opts) {
|
|
|
238223
238702
|
});
|
|
238224
238703
|
};
|
|
238225
238704
|
}
|
|
238226
|
-
var import_node_crypto61, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS
|
|
238227
|
-
var
|
|
238705
|
+
var import_node_crypto61, DEFAULT_SNAPSHOT_ITEMS, MAX_SNAPSHOT_ITEMS;
|
|
238706
|
+
var init_chat_sessions2 = __esm({
|
|
238228
238707
|
"../server/src/domains/chat-sessions/index.ts"() {
|
|
238229
238708
|
"use strict";
|
|
238230
238709
|
import_node_crypto61 = require("node:crypto");
|
|
@@ -238247,7 +238726,6 @@ var init_chat_sessions = __esm({
|
|
|
238247
238726
|
init_project_bundle();
|
|
238248
238727
|
DEFAULT_SNAPSHOT_ITEMS = 400;
|
|
238249
238728
|
MAX_SNAPSHOT_ITEMS = 1e3;
|
|
238250
|
-
WORKDIR_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
238251
238729
|
}
|
|
238252
238730
|
});
|
|
238253
238731
|
|
|
@@ -241140,11 +241618,11 @@ function automationsDomain(opts) {
|
|
|
241140
241618
|
});
|
|
241141
241619
|
router.get("/api/automations/:id/runs/:runId", async (req) => {
|
|
241142
241620
|
const automation = await mustGet(req);
|
|
241143
|
-
const
|
|
241144
|
-
if (!
|
|
241621
|
+
const found2 = await service.getRun(req.params.runId);
|
|
241622
|
+
if (!found2 || found2.automationId !== automation.id) {
|
|
241145
241623
|
throw new ApiError(404, "NOT_FOUND", `\u6CA1\u6709\u8FD9\u6761\u8FD0\u884C\u8BB0\u5F55\uFF1A${req.params.runId}`);
|
|
241146
241624
|
}
|
|
241147
|
-
return { status: 200, body:
|
|
241625
|
+
return { status: 200, body: found2 };
|
|
241148
241626
|
});
|
|
241149
241627
|
router.get("/api/automations/:id/deliveries", async (req) => {
|
|
241150
241628
|
const automation = await mustGet(req);
|
|
@@ -250504,6 +250982,20 @@ var init_postgres_chat_sessions = __esm({
|
|
|
250504
250982
|
);
|
|
250505
250983
|
return r.rows.map(rowToSessionWithQuality);
|
|
250506
250984
|
}
|
|
250985
|
+
async listSessionsByProject(projectId2) {
|
|
250986
|
+
const r = await this.pool.query(
|
|
250987
|
+
`SELECT s.*
|
|
250988
|
+
FROM ${this.s}.chat_sessions s
|
|
250989
|
+
WHERE s.company_id = $2
|
|
250990
|
+
AND s.project_id IS NOT DISTINCT FROM $1
|
|
250991
|
+
AND s.analyzed_run_id IS NULL
|
|
250992
|
+
AND s.visibility IS DISTINCT FROM 'internal'
|
|
250993
|
+
AND COALESCE(s.workspace, '') = ''
|
|
250994
|
+
ORDER BY s.created_at DESC, s.id`,
|
|
250995
|
+
[projectId2, this.companyId]
|
|
250996
|
+
);
|
|
250997
|
+
return r.rows.map(rowToSession);
|
|
250998
|
+
}
|
|
250507
250999
|
async listSessionsByAnalyzedRun(analyzedRunId) {
|
|
250508
251000
|
const r = await this.pool.query(
|
|
250509
251001
|
`SELECT s.*, lm.status AS last_assistant_status, lm.content AS last_assistant_content
|
|
@@ -260921,7 +261413,7 @@ var init_src11 = __esm({
|
|
|
260921
261413
|
init_experiments2();
|
|
260922
261414
|
init_trace2();
|
|
260923
261415
|
init_execution_continuity2();
|
|
260924
|
-
|
|
261416
|
+
init_chat_sessions2();
|
|
260925
261417
|
init_dev_store();
|
|
260926
261418
|
init_continuation3();
|
|
260927
261419
|
init_knowledge2();
|
|
@@ -261326,6 +261818,54 @@ function buildChatTurnPrompts(input) {
|
|
|
261326
261818
|
};
|
|
261327
261819
|
}
|
|
261328
261820
|
|
|
261821
|
+
// ../cli/src/chat-task-md.ts
|
|
261822
|
+
var CHAT_PROJECT_SECTION_TITLE = "## \u5F53\u524D\u9879\u76EE\uFF08\u4EE5\u672C\u8F6E\u4E3A\u51C6\uFF09";
|
|
261823
|
+
var CHAT_PROJECT_INPUTS_POINTER = [
|
|
261824
|
+
"",
|
|
261825
|
+
"## \u5F53\u524D\u9879\u76EE\u516C\u5F00\u8F93\u5165",
|
|
261826
|
+
"",
|
|
261827
|
+
"\u751F\u6210 Brief \u524D\u5FC5\u987B\u5148\u8BFB\u53D6 `PROJECT_CONTEXT.md` \u53CA\u5176\u4E2D\u5217\u51FA\u7684 `project/` \u6587\u4EF6\uFF1B\u4ED3\u5E93\u7B49\u5916\u90E8\u4F4D\u7F6E\u4E5F\u8BB0\u5F55\u5728\u8BE5\u6587\u4EF6\u4E2D\u3002"
|
|
261828
|
+
];
|
|
261829
|
+
function buildChatProjectSection(project, opts) {
|
|
261830
|
+
if (project.state === "unknown") return [];
|
|
261831
|
+
if (project.state === "none") {
|
|
261832
|
+
return opts.announceNone ? [CHAT_PROJECT_SECTION_TITLE, "", "\u672C\u5BF9\u8BDD\u672A\u5F52\u5C5E\u4EFB\u4F55\u9879\u76EE\u3002"] : [];
|
|
261833
|
+
}
|
|
261834
|
+
const description = project.description?.trim();
|
|
261835
|
+
const descriptionLines = !description ? ["\u9879\u76EE\u63CF\u8FF0: \uFF08\u672A\u586B\u5199\uFF09"] : description.includes("\n") ? ["\u9879\u76EE\u63CF\u8FF0:", description] : [`\u9879\u76EE\u63CF\u8FF0: ${description}`];
|
|
261836
|
+
return [
|
|
261837
|
+
CHAT_PROJECT_SECTION_TITLE,
|
|
261838
|
+
"",
|
|
261839
|
+
`project_id: ${project.id}`,
|
|
261840
|
+
`\u9879\u76EE\u540D\u79F0: ${project.name}`,
|
|
261841
|
+
...descriptionLines
|
|
261842
|
+
];
|
|
261843
|
+
}
|
|
261844
|
+
function buildChatTaskMd(p2) {
|
|
261845
|
+
return [
|
|
261846
|
+
...p2.headerLines,
|
|
261847
|
+
// 项目段紧跟抬头:这一轮在哪个项目里,是读后面所有内容的前提。
|
|
261848
|
+
...p2.projectSection.length ? ["", ...p2.projectSection] : [],
|
|
261849
|
+
...p2.attachmentPaths.length ? ["", "## \u7528\u6237\u9644\u4EF6", "", "\u7528\u6237\u968F\u6D88\u606F\u9644\u5E26\u4E86\u4EE5\u4E0B\u6587\u4EF6\uFF08\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u53EF\u76F4\u63A5\u8BFB\u53D6\uFF09\uFF1A", ...p2.attachmentPaths.map((x2) => `- ${x2}`)] : [],
|
|
261850
|
+
// 委派子会话的主会话对话快照(提案 §6.1.4):一句固定说明 + 一份清单;没有这个文件时整段不拼。
|
|
261851
|
+
// 指针与文件由 delegationBundleAdditions **一处**给出,拆开迟早漂成「有指针没文件」。
|
|
261852
|
+
...p2.parentConversationLines,
|
|
261853
|
+
// 委派两条路搬过来的文件(去程 `--file` / 回程专家产物):**不能复用「## 用户附件」的措辞**
|
|
261854
|
+
// (专家的产物会被介绍成用户附带的)。
|
|
261855
|
+
...p2.workspaceLines,
|
|
261856
|
+
...p2.projectInputsPointer,
|
|
261857
|
+
// 诊断 1be339c7:身份段紧贴在「## 用户消息」之前——让 agent 先知道「你在跟谁说话」,再读那条消息。
|
|
261858
|
+
...p2.whoLines.length ? ["", ...p2.whoLines] : [],
|
|
261859
|
+
"",
|
|
261860
|
+
"## \u7528\u6237\u6D88\u606F",
|
|
261861
|
+
"",
|
|
261862
|
+
p2.message,
|
|
261863
|
+
...p2.memoryLines,
|
|
261864
|
+
// 压在最末:这条要盖住紧邻其上的「收工前想一下」,否则记忆写入就落到正文之后(见常量注释)。
|
|
261865
|
+
...p2.finalReplyLines
|
|
261866
|
+
].join("\n");
|
|
261867
|
+
}
|
|
261868
|
+
|
|
261329
261869
|
// ../cli/src/company-dispatch-bridge.ts
|
|
261330
261870
|
var CompanyDispatchBridges = class {
|
|
261331
261871
|
constructor(opts) {
|
|
@@ -261802,6 +262342,7 @@ init_src10();
|
|
|
261802
262342
|
init_src11();
|
|
261803
262343
|
init_src11();
|
|
261804
262344
|
init_src11();
|
|
262345
|
+
init_src11();
|
|
261805
262346
|
init_src9();
|
|
261806
262347
|
init_src();
|
|
261807
262348
|
init_trajectory();
|
|
@@ -262696,6 +263237,15 @@ async function startServe(opts) {
|
|
|
262696
263237
|
const members = await listCompanyMembers(companyId);
|
|
262697
263238
|
return members.find((m2) => m2.actorId === actorId)?.role;
|
|
262698
263239
|
},
|
|
263240
|
+
// 项目「关联对话」列表与对话改挂项目(2026-09-14)。闭包在请求时才读 chatStoreFor /
|
|
263241
|
+
// delegationStoreFor,避开它们在下方声明的 TDZ(与 listChatFiles 同法)。
|
|
263242
|
+
chatSessionsFor: (companyId) => chatStoreFor(companyId),
|
|
263243
|
+
// 委派子会话不进项目(ADR 0555);一次台账查询批量判,不逐条点查。
|
|
263244
|
+
delegationChildIds: async (companyId, sessionIds) => {
|
|
263245
|
+
const ledger = await delegationStoreFor(companyId).catch(() => null);
|
|
263246
|
+
if (!ledger) return /* @__PURE__ */ new Set();
|
|
263247
|
+
return new Set(await ledger.listChildSessionIds(sessionIds));
|
|
263248
|
+
},
|
|
262699
263249
|
resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
|
|
262700
263250
|
defaultBranchResolver,
|
|
262701
263251
|
// 展示:GET content-locations 给 git 仓补 resolvedDefaultBranch
|
|
@@ -263651,6 +264201,7 @@ async function startServe(opts) {
|
|
|
263651
264201
|
};
|
|
263652
264202
|
const readMarkerStoreFor = async (companyId) => pgPool && companyId ? (await openCompanyPlane(companyId)).readMarkers : readMarkerStore;
|
|
263653
264203
|
const fileNotificationStore = pgPool ? void 0 : new MemoryNotificationStore(path29.join(opts.dir, "notifications.json"));
|
|
264204
|
+
const notificationLedgerEnabled = process.env["OASIS_NOTIFICATION_LEDGER"] !== "off";
|
|
263654
264205
|
const notificationStoreFor = async (companyId) => pgPool ? (await openCompanyPlane(companyId ?? defaultCompanyId)).notifications : fileNotificationStore;
|
|
263655
264206
|
const actorMemoryFor = async (companyId) => pgPool && companyId ? (await openCompanyPlane(companyId)).memory : actorMemoryService;
|
|
263656
264207
|
const workorderDraftsFor = async (companyId) => pgPool && companyId ? (await openCompanyPlane(companyId)).drafts : workorderDrafts;
|
|
@@ -264395,7 +264946,7 @@ async function startServe(opts) {
|
|
|
264395
264946
|
readMarkers: readMarkerStore,
|
|
264396
264947
|
// bundle=`inbox`:水位按当前公司取(与上面 resolveChatSession 成对)。
|
|
264397
264948
|
...pgPool ? { resolveReadMarkers: readMarkerStoreFor } : {},
|
|
264398
|
-
resolveNotificationLedger: notificationStoreFor,
|
|
264949
|
+
...notificationLedgerEnabled ? { resolveNotificationLedger: notificationStoreFor } : {},
|
|
264399
264950
|
escalationReminders: escalationDelivery.policy,
|
|
264400
264951
|
defaultCompanyId,
|
|
264401
264952
|
// 工单标签词表(组织级受控、旁账,与 playbook-overrides 同款侧存)。
|
|
@@ -265189,7 +265740,11 @@ async function startServe(opts) {
|
|
|
265189
265740
|
}
|
|
265190
265741
|
}
|
|
265191
265742
|
}
|
|
265192
|
-
const
|
|
265743
|
+
const turnChatStore = chatSessionId ? await chatStoreFor(runCompanyId).catch((err) => {
|
|
265744
|
+
console.warn(`[chat] \u53D6\u516C\u53F8 ${runCompanyId} \u7684\u4F1A\u8BDD\u5B58\u50A8\u5931\u8D25\uFF08\u672C\u8F6E\u6309\u65E0\u4F1A\u8BDD\u8BB0\u5F55\u5904\u7406\uFF09: ${String(err)}`);
|
|
265745
|
+
return null;
|
|
265746
|
+
}) : null;
|
|
265747
|
+
const analyzedRunId = chatSessionId ? (await turnChatStore?.getSession(chatSessionId).catch(() => null))?.analyzedRunId ?? void 0 : void 0;
|
|
265193
265748
|
const actorConfigVersion = await actorService.latestConfig(actorId).then((c) => c?.version ?? null).catch(() => null);
|
|
265194
265749
|
const candidateTraceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
265195
265750
|
const traceCreateInput = (target, startedAt) => ({
|
|
@@ -265243,7 +265798,7 @@ async function startServe(opts) {
|
|
|
265243
265798
|
requireNodeConnectors();
|
|
265244
265799
|
}
|
|
265245
265800
|
}
|
|
265246
|
-
const priorChatSession = chatSessionId ? await
|
|
265801
|
+
const priorChatSession = chatSessionId ? await turnChatStore?.getSession(chatSessionId).catch(() => null) : null;
|
|
265247
265802
|
const freshChatWorkdir = chatSessionId ? chatNodeChanged(
|
|
265248
265803
|
{ nodeId: priorChatSession?.runtimeId, runtimeKind: priorChatSession?.runtimeKind },
|
|
265249
265804
|
{ nodeId: binding.nodeId, runtimeKind: binding.runtimeKind }
|
|
@@ -265301,17 +265856,19 @@ async function startServe(opts) {
|
|
|
265301
265856
|
const attachmentFiles = { ...preparedAttachments.files };
|
|
265302
265857
|
const attachmentBinary = { ...preparedAttachments.binaryFiles };
|
|
265303
265858
|
const attachmentPaths = [...preparedAttachments.paths];
|
|
265304
|
-
let
|
|
265859
|
+
let turnProject = priorChatSession ? { state: "none" } : { state: "unknown" };
|
|
265305
265860
|
if (priorChatSession?.projectId) {
|
|
265306
|
-
const
|
|
265307
|
-
|
|
265308
|
-
|
|
265309
|
-
|
|
265310
|
-
)
|
|
265861
|
+
const projectId2 = priorChatSession.projectId;
|
|
265862
|
+
const projectBundle = await projectStateFor(runCompanyId).then((projects) => buildChatProjectBundle(projectId2, projects, (blobId) => assets.get(blobId))).catch((err) => {
|
|
265863
|
+
console.warn(`[chat] \u8BFB\u9879\u76EE ${projectId2} \u5931\u8D25\uFF08\u672C\u8F6E\u4E0D\u5199\u9879\u76EE\u6BB5\uFF09: ${String(err)}`);
|
|
265864
|
+
return void 0;
|
|
265865
|
+
});
|
|
265311
265866
|
if (projectBundle) {
|
|
265312
265867
|
Object.assign(attachmentFiles, projectBundle.files);
|
|
265313
265868
|
Object.assign(attachmentBinary, projectBundle.binaryFiles);
|
|
265314
|
-
|
|
265869
|
+
turnProject = { state: "bound", ...projectBundle.project };
|
|
265870
|
+
} else if (projectBundle === void 0) {
|
|
265871
|
+
turnProject = { state: "unknown" };
|
|
265315
265872
|
}
|
|
265316
265873
|
}
|
|
265317
265874
|
const sessionLine = chatSessionId ? `chat_session_id: ${chatSessionId}` : `runtime_session_id: ${runtimeSessionId}`;
|
|
@@ -265334,29 +265891,21 @@ async function startServe(opts) {
|
|
|
265334
265891
|
const chatMemoryLines = chatMemorySvc ? await chatMemorySvc.list({ actorId }, { scope: "all", keywords: ngramQueryTerms([message]), limit: MEMORY_INJECT_MAX, offset: 0 }).then((out) => [...memoryIndexLines(out.items), ...memoryHowToLines()]).catch(() => []) : [];
|
|
265335
265892
|
const parentConversationBundle = delegationBundleAdditions(parentConversationSnapshot);
|
|
265336
265893
|
const files = {
|
|
265337
|
-
"TASK.md":
|
|
265338
|
-
|
|
265339
|
-
|
|
265340
|
-
//
|
|
265341
|
-
|
|
265342
|
-
|
|
265343
|
-
|
|
265344
|
-
|
|
265345
|
-
|
|
265346
|
-
|
|
265347
|
-
|
|
265348
|
-
...workspaceBundle?.taskLines ?? [],
|
|
265349
|
-
...projectContextLines.length ? ["", "## \u5F53\u524D\u9879\u76EE\u516C\u5F00\u8F93\u5165", "", "\u751F\u6210 Brief \u524D\u5FC5\u987B\u5148\u8BFB\u53D6 `PROJECT_CONTEXT.md` \u53CA\u5176\u4E2D\u5217\u51FA\u7684 `project/` \u6587\u4EF6\uFF1B\u4ED3\u5E93\u7B49\u5916\u90E8\u4F4D\u7F6E\u4E5F\u8BB0\u5F55\u5728\u8BE5\u6587\u4EF6\u4E2D\u3002"] : [],
|
|
265350
|
-
// 诊断 1be339c7:身份段紧贴在「## 用户消息」之前——让 agent 先知道「你在跟谁说话」,再读那条消息。
|
|
265351
|
-
...whoLines.length ? ["", ...whoLines] : [],
|
|
265352
|
-
"",
|
|
265353
|
-
"## \u7528\u6237\u6D88\u606F",
|
|
265354
|
-
"",
|
|
265894
|
+
"TASK.md": buildChatTaskMd({
|
|
265895
|
+
headerLines: taskHeader,
|
|
265896
|
+
// 「未归属」只对普通对话说:挂在工单上的对话(协调者 / 上报讨论房)项目看工单绑定,
|
|
265897
|
+
// 委派子会话建出来本就不带项目;知识库单轮受限生成连抬头都是另一套。
|
|
265898
|
+
projectSection: isolatedKnowledgeTurn ? [] : buildChatProjectSection(turnProject, { announceNone: !workspace && !delegatedChildTurn }),
|
|
265899
|
+
attachmentPaths,
|
|
265900
|
+
parentConversationLines: parentConversationBundle.taskLines,
|
|
265901
|
+
workspaceLines: workspaceBundle?.taskLines ?? [],
|
|
265902
|
+
// 项目公开输入指针:文字、位置、出现条件(查到了项目)都与改动前一致。
|
|
265903
|
+
projectInputsPointer: turnProject.state === "bound" ? CHAT_PROJECT_INPUTS_POINTER : [],
|
|
265904
|
+
whoLines,
|
|
265355
265905
|
message,
|
|
265356
|
-
|
|
265357
|
-
|
|
265358
|
-
|
|
265359
|
-
].join("\n")
|
|
265906
|
+
memoryLines: chatMemoryLines,
|
|
265907
|
+
finalReplyLines: isolatedKnowledgeTurn ? [] : chatFinalReplyRule({ memoryEnabled: chatMemoryEnabled })
|
|
265908
|
+
})
|
|
265360
265909
|
};
|
|
265361
265910
|
Object.assign(files, attachmentFiles);
|
|
265362
265911
|
Object.assign(files, parentConversationBundle.files);
|
|
@@ -265472,6 +266021,8 @@ async function startServe(opts) {
|
|
|
265472
266021
|
handle.onLiveEvent?.((event) => {
|
|
265473
266022
|
if (liveEventCbs.length === 0) bufferedLiveEvents.push(event);
|
|
265474
266023
|
for (const cb of liveEventCbs) cb(event);
|
|
266024
|
+
const subagentEvent = subagentRunEventFromLive(event);
|
|
266025
|
+
if (subagentEvent) enqueueTrace(() => traceAppender.append([subagentEvent]).then(() => void 0));
|
|
265475
266026
|
});
|
|
265476
266027
|
let capturedNativeSessionId;
|
|
265477
266028
|
const done = new Promise((resolve10, reject) => {
|
|
@@ -265498,6 +266049,7 @@ async function startServe(opts) {
|
|
|
265498
266049
|
durationMs: durationMs3,
|
|
265499
266050
|
exitCode: info.code,
|
|
265500
266051
|
exitReason: info.reason ?? null,
|
|
266052
|
+
...!isHealthySessionExit(info) && info.reason !== "cancelled" ? { errorCode: info.reason ?? "error", errorMessage: formatExitError(info) } : {},
|
|
265501
266053
|
transcriptRef: info.transcriptRef ?? null,
|
|
265502
266054
|
usage,
|
|
265503
266055
|
effectiveModel,
|
|
@@ -267202,28 +267754,28 @@ async function startServe(opts) {
|
|
|
267202
267754
|
const isDefault = cid === defaultCompanyId;
|
|
267203
267755
|
const cacheKey = `${cid}::${workspace}`;
|
|
267204
267756
|
if (initiatorCache.has(cacheKey)) return initiatorCache.get(cacheKey);
|
|
267205
|
-
let
|
|
267757
|
+
let found2 = null;
|
|
267206
267758
|
const kernelToRead = engine2?.kernel ?? newEngine.kernel;
|
|
267207
267759
|
const store = kernelToRead.getStore?.();
|
|
267208
267760
|
if (store) {
|
|
267209
267761
|
try {
|
|
267210
267762
|
const events = await store.transaction((tx) => tx.listEvents(workspace));
|
|
267211
267763
|
const spawn10 = events.find((e) => e.event.kind === "plan.changed");
|
|
267212
|
-
|
|
267764
|
+
found2 = spawn10?.actorId ?? null;
|
|
267213
267765
|
} catch {
|
|
267214
267766
|
}
|
|
267215
267767
|
}
|
|
267216
|
-
if (
|
|
267768
|
+
if (found2 === null && isDefault) {
|
|
267217
267769
|
for await (const entry of oplog.readAll()) {
|
|
267218
267770
|
const op = entry.op;
|
|
267219
267771
|
if (op.kind === "spawn_artifact" && op.payload.workspace === workspace) {
|
|
267220
|
-
|
|
267772
|
+
found2 = op.actor;
|
|
267221
267773
|
break;
|
|
267222
267774
|
}
|
|
267223
267775
|
}
|
|
267224
267776
|
}
|
|
267225
|
-
initiatorCache.set(cacheKey,
|
|
267226
|
-
return
|
|
267777
|
+
initiatorCache.set(cacheKey, found2);
|
|
267778
|
+
return found2;
|
|
267227
267779
|
};
|
|
267228
267780
|
const leadOf = async (actor, registry2) => {
|
|
267229
267781
|
const reg = registry2 ?? registryStore;
|
|
@@ -267407,7 +267959,7 @@ async function startServe(opts) {
|
|
|
267407
267959
|
},
|
|
267408
267960
|
log: (message) => console.warn(message)
|
|
267409
267961
|
});
|
|
267410
|
-
if (
|
|
267962
|
+
if (notificationLedgerEnabled) {
|
|
267411
267963
|
const ledgerPeople = /* @__PURE__ */ new Map();
|
|
267412
267964
|
const worker = new NotificationLedgerWorker({
|
|
267413
267965
|
listCompanies: async () => engineRouter.liveEntries().map((e) => e.companyId),
|
|
@@ -268731,7 +269283,7 @@ var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
|
268731
269283
|
var import_node_path26 = __toESM(require("node:path"), 1);
|
|
268732
269284
|
init_src6();
|
|
268733
269285
|
init_src();
|
|
268734
|
-
var
|
|
269286
|
+
var WORKDIR_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
268735
269287
|
var WORKDIR_LIST_MAX_ENTRIES = 500;
|
|
268736
269288
|
var WORKDIR_WALK_MAX_ENTRIES2 = 1e4;
|
|
268737
269289
|
var WORKDIR_WALK_BUDGET_MS2 = 1e4;
|
|
@@ -268909,7 +269461,7 @@ async function verifyOpenedFd(fh, base, fallback) {
|
|
|
268909
269461
|
return { anchor: `/proc/self/fd/${fh.fd}` };
|
|
268910
269462
|
}
|
|
268911
269463
|
async function handleWorkdirRead(req) {
|
|
268912
|
-
const maxBytes = req.maxBytes ??
|
|
269464
|
+
const maxBytes = req.maxBytes ?? WORKDIR_READ_MAX_BYTES;
|
|
268913
269465
|
try {
|
|
268914
269466
|
const resolved = await resolveWithinWorkdir(req);
|
|
268915
269467
|
if (!resolved.ok) return resolved;
|
|
@@ -275308,20 +275860,20 @@ function instancePaths(name) {
|
|
|
275308
275860
|
};
|
|
275309
275861
|
}
|
|
275310
275862
|
function listInstances() {
|
|
275311
|
-
const
|
|
275312
|
-
if (fs43.existsSync(instancePaths(DEFAULT_INSTANCE).configFile))
|
|
275863
|
+
const found2 = [];
|
|
275864
|
+
if (fs43.existsSync(instancePaths(DEFAULT_INSTANCE).configFile)) found2.push(DEFAULT_INSTANCE);
|
|
275313
275865
|
let entries = [];
|
|
275314
275866
|
try {
|
|
275315
275867
|
entries = fs43.readdirSync(instancesRoot());
|
|
275316
275868
|
} catch {
|
|
275317
|
-
return
|
|
275869
|
+
return found2;
|
|
275318
275870
|
}
|
|
275319
275871
|
for (const name of entries.sort()) {
|
|
275320
275872
|
if (name === DEFAULT_INSTANCE) continue;
|
|
275321
275873
|
if (!isValidInstanceName(name)) continue;
|
|
275322
|
-
if (fs43.existsSync(instancePaths(name).configFile))
|
|
275874
|
+
if (fs43.existsSync(instancePaths(name).configFile)) found2.push(name);
|
|
275323
275875
|
}
|
|
275324
|
-
return
|
|
275876
|
+
return found2;
|
|
275325
275877
|
}
|
|
275326
275878
|
function referencedVersionLabels() {
|
|
275327
275879
|
const labels = /* @__PURE__ */ new Set();
|
|
@@ -275480,7 +276032,7 @@ function shimScript() {
|
|
|
275480
276032
|
}
|
|
275481
276033
|
|
|
275482
276034
|
// src/index.ts
|
|
275483
|
-
var PKG_VERSION = true ? "2.2.
|
|
276035
|
+
var PKG_VERSION = true ? "2.2.34" : "dev";
|
|
275484
276036
|
var LOCAL_BIN = localBin();
|
|
275485
276037
|
var NPM_PREFIX = npmPrefix();
|
|
275486
276038
|
var INSTANCE = DEFAULT_INSTANCE;
|