oasis_test 0.1.120 → 0.1.122
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 +1747 -380
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2877,11 +2877,18 @@ var init_execution_continuity = __esm({
|
|
|
2877
2877
|
});
|
|
2878
2878
|
|
|
2879
2879
|
// ../contract/src/automation.ts
|
|
2880
|
-
|
|
2880
|
+
function normalizeChatTarget(raw) {
|
|
2881
|
+
if (raw?.mode === "existing_session" && typeof raw.sessionId === "string" && raw.sessionId) {
|
|
2882
|
+
return { mode: "existing_session", sessionId: raw.sessionId };
|
|
2883
|
+
}
|
|
2884
|
+
return DEFAULT_CHAT_TARGET;
|
|
2885
|
+
}
|
|
2886
|
+
var AUTOMATION_EVENT_NAMES, DEFAULT_CHAT_TARGET;
|
|
2881
2887
|
var init_automation = __esm({
|
|
2882
2888
|
"../contract/src/automation.ts"() {
|
|
2883
2889
|
"use strict";
|
|
2884
2890
|
AUTOMATION_EVENT_NAMES = ["workorder.completed"];
|
|
2891
|
+
DEFAULT_CHAT_TARGET = { mode: "new_session" };
|
|
2885
2892
|
}
|
|
2886
2893
|
});
|
|
2887
2894
|
|
|
@@ -2899,6 +2906,19 @@ var init_chat = __esm({
|
|
|
2899
2906
|
}
|
|
2900
2907
|
});
|
|
2901
2908
|
|
|
2909
|
+
// ../contract/src/chat-turn.ts
|
|
2910
|
+
function automationTurnSourceRunId(automationRunId) {
|
|
2911
|
+
return `automation:${automationRunId}`;
|
|
2912
|
+
}
|
|
2913
|
+
function automationTurnMessageKey(automationRunId) {
|
|
2914
|
+
return `automation-message:${automationRunId}`;
|
|
2915
|
+
}
|
|
2916
|
+
var init_chat_turn = __esm({
|
|
2917
|
+
"../contract/src/chat-turn.ts"() {
|
|
2918
|
+
"use strict";
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
|
|
2902
2922
|
// ../contract/src/knowledge.ts
|
|
2903
2923
|
var init_knowledge = __esm({
|
|
2904
2924
|
"../contract/src/knowledge.ts"() {
|
|
@@ -2952,6 +2972,7 @@ var init_src2 = __esm({
|
|
|
2952
2972
|
init_automation();
|
|
2953
2973
|
init_playbook();
|
|
2954
2974
|
init_chat();
|
|
2975
|
+
init_chat_turn();
|
|
2955
2976
|
init_knowledge();
|
|
2956
2977
|
init_http_api();
|
|
2957
2978
|
}
|
|
@@ -18615,6 +18636,111 @@ var init_command_policy = __esm({
|
|
|
18615
18636
|
}
|
|
18616
18637
|
});
|
|
18617
18638
|
|
|
18639
|
+
// ../server/src/chat-turn-gate.ts
|
|
18640
|
+
function localTurnSourceRunId(source, turnId) {
|
|
18641
|
+
return `${source}:${turnId}`;
|
|
18642
|
+
}
|
|
18643
|
+
function localTurnMessageKey(source, turnId) {
|
|
18644
|
+
return `${source}-message:${turnId}`;
|
|
18645
|
+
}
|
|
18646
|
+
async function openChatTurn(args) {
|
|
18647
|
+
const turns = args.turns;
|
|
18648
|
+
if (!turns) return { ok: true, turn: null };
|
|
18649
|
+
const nowIso = args.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
18650
|
+
const turnId = (args.newId ?? import_node_crypto4.randomUUID)();
|
|
18651
|
+
const messageKey = localTurnMessageKey(args.source, turnId);
|
|
18652
|
+
const claim = await turns.claimTurn({
|
|
18653
|
+
turnId,
|
|
18654
|
+
chatSessionId: args.chatSessionId,
|
|
18655
|
+
source: args.source,
|
|
18656
|
+
sourceRunId: localTurnSourceRunId(args.source, turnId),
|
|
18657
|
+
messageKey,
|
|
18658
|
+
reservedAt: nowIso()
|
|
18659
|
+
});
|
|
18660
|
+
if (claim.outcome === "busy") return { ok: false, activeTurn: claim.activeTurn };
|
|
18661
|
+
const id = claim.turn.id;
|
|
18662
|
+
let settled = false;
|
|
18663
|
+
return {
|
|
18664
|
+
ok: true,
|
|
18665
|
+
turn: {
|
|
18666
|
+
id,
|
|
18667
|
+
messageKey: claim.turn.messageKey,
|
|
18668
|
+
async start(handles) {
|
|
18669
|
+
await turns.settleTurn(id, {
|
|
18670
|
+
status: "running",
|
|
18671
|
+
startedAt: nowIso(),
|
|
18672
|
+
...handles.assistantRunId ? { assistantRunId: handles.assistantRunId } : {},
|
|
18673
|
+
...handles.dispatchId ? { dispatchId: handles.dispatchId } : {}
|
|
18674
|
+
}).catch((e) => args.log?.(`[chat-turn] \u8F6E\u6B21\u8F6C running \u5931\u8D25 ${id}: ${String(e)}`));
|
|
18675
|
+
},
|
|
18676
|
+
async settle(patch) {
|
|
18677
|
+
if (settled) return;
|
|
18678
|
+
settled = true;
|
|
18679
|
+
await turns.settleTurn(id, patch).catch((e) => args.log?.(`[chat-turn] \u8F6E\u6B21\u6536\u53E3\u5931\u8D25 ${id}: ${String(e)}`));
|
|
18680
|
+
}
|
|
18681
|
+
}
|
|
18682
|
+
};
|
|
18683
|
+
}
|
|
18684
|
+
async function reconcileChatTurn(turn, deps) {
|
|
18685
|
+
if (deps.isOwned?.(turn)) return { settled: false, turnId: turn.id, reason: "owned" };
|
|
18686
|
+
const grace = deps.noRunGraceMs ?? 10 * 6e4;
|
|
18687
|
+
const age = deps.now - Date.parse(turn.reservedAt);
|
|
18688
|
+
const runId = turn.assistantRunId ?? turn.dispatchId;
|
|
18689
|
+
if (!runId) {
|
|
18690
|
+
if (age < grace) return { settled: false, turnId: turn.id, reason: "in_grace" };
|
|
18691
|
+
await deps.turns.settleTurn(turn.id, {
|
|
18692
|
+
status: "orphaned",
|
|
18693
|
+
completedAt: new Date(deps.now).toISOString(),
|
|
18694
|
+
lastError: "\u5360\u4F4D\u540E\u672A\u63D0\u4EA4 runtime\uFF08\u8FDB\u7A0B\u4E2D\u65AD\uFF09\uFF0C\u8D85\u5BBD\u9650\u5224\u5B64\u513F\u5E76\u91CA\u653E\u4F1A\u8BDD\u69FD"
|
|
18695
|
+
});
|
|
18696
|
+
return { settled: true, turnId: turn.id, status: "orphaned", reason: "\u5360\u4F4D\u540E\u672A\u63D0\u4EA4 runtime" };
|
|
18697
|
+
}
|
|
18698
|
+
const run = await deps.getRun(runId).catch(() => null);
|
|
18699
|
+
if (run && (run.status === "running" || run.status === "queued")) {
|
|
18700
|
+
return { settled: false, turnId: turn.id, reason: "still_running" };
|
|
18701
|
+
}
|
|
18702
|
+
if (!run) {
|
|
18703
|
+
if (age < grace) return { settled: false, turnId: turn.id, reason: "in_grace" };
|
|
18704
|
+
await deps.turns.settleTurn(turn.id, {
|
|
18705
|
+
status: "orphaned",
|
|
18706
|
+
completedAt: new Date(deps.now).toISOString(),
|
|
18707
|
+
lastError: `\u8D26\u672C\u67E5\u65E0 run ${runId}\uFF0C\u8D85\u5BBD\u9650\u5224\u5B64\u513F\u5E76\u91CA\u653E\u4F1A\u8BDD\u69FD`
|
|
18708
|
+
});
|
|
18709
|
+
return { settled: true, turnId: turn.id, status: "orphaned", reason: `\u8D26\u672C\u67E5\u65E0 run ${runId}` };
|
|
18710
|
+
}
|
|
18711
|
+
const status = run.status === "succeeded" ? "succeeded" : run.status === "cancelled" ? "cancelled" : "failed";
|
|
18712
|
+
await deps.turns.settleTurn(turn.id, {
|
|
18713
|
+
status,
|
|
18714
|
+
completedAt: run.endedAt ?? new Date(deps.now).toISOString(),
|
|
18715
|
+
...status === "succeeded" ? {} : { lastError: `run ${runId} \u7EC8\u6001 ${run.status}\uFF08\u7531\u6062\u590D\u5668\u6536\u53E3\uFF09` }
|
|
18716
|
+
});
|
|
18717
|
+
return { settled: true, turnId: turn.id, status, reason: `run \u7EC8\u6001 ${run.status}` };
|
|
18718
|
+
}
|
|
18719
|
+
async function sweepStaleChatTurns(deps) {
|
|
18720
|
+
const before = new Date(deps.now - (deps.staleAfterMs ?? 2 * 6e4)).toISOString();
|
|
18721
|
+
const stale = await deps.turns.listStaleActiveTurns(before, deps.limit ?? 50);
|
|
18722
|
+
const settled = [];
|
|
18723
|
+
for (const turn of stale) {
|
|
18724
|
+
const outcome = await reconcileChatTurn(turn, deps).catch((e) => {
|
|
18725
|
+
deps.log?.(`[chat-turn-sweep] \u6838\u5B9E\u8F6E\u6B21 ${turn.id} \u5931\u8D25: ${String(e)}`);
|
|
18726
|
+
return null;
|
|
18727
|
+
});
|
|
18728
|
+
if (!outcome?.settled) continue;
|
|
18729
|
+
settled.push(outcome);
|
|
18730
|
+
deps.log?.(
|
|
18731
|
+
`[chat-turn-sweep] \u8F6E\u6B21 ${turn.id}\uFF08\u4F1A\u8BDD ${turn.chatSessionId}\uFF09\u6536\u53E3\u4E3A ${outcome.status}\uFF08${outcome.reason}\uFF09\uFF0C\u4F1A\u8BDD\u69FD\u5DF2\u91CA\u653E`
|
|
18732
|
+
);
|
|
18733
|
+
}
|
|
18734
|
+
return settled;
|
|
18735
|
+
}
|
|
18736
|
+
var import_node_crypto4;
|
|
18737
|
+
var init_chat_turn_gate = __esm({
|
|
18738
|
+
"../server/src/chat-turn-gate.ts"() {
|
|
18739
|
+
"use strict";
|
|
18740
|
+
import_node_crypto4 = require("node:crypto");
|
|
18741
|
+
}
|
|
18742
|
+
});
|
|
18743
|
+
|
|
18618
18744
|
// ../channels/src/memory-store.ts
|
|
18619
18745
|
var init_memory_store = __esm({
|
|
18620
18746
|
"../channels/src/memory-store.ts"() {
|
|
@@ -18800,11 +18926,11 @@ var init_parse = __esm({
|
|
|
18800
18926
|
|
|
18801
18927
|
// ../channels/src/feishu/crypto.ts
|
|
18802
18928
|
function decryptFeishu(encrypt, encryptKey) {
|
|
18803
|
-
const key = (0,
|
|
18929
|
+
const key = (0, import_node_crypto5.createHash)("sha256").update(encryptKey).digest();
|
|
18804
18930
|
const data = Buffer.from(encrypt, "base64");
|
|
18805
18931
|
const iv = data.subarray(0, 16);
|
|
18806
18932
|
const ciphertext = data.subarray(16);
|
|
18807
|
-
const decipher = (0,
|
|
18933
|
+
const decipher = (0, import_node_crypto5.createDecipheriv)("aes-256-cbc", key, iv);
|
|
18808
18934
|
decipher.setAutoPadding(true);
|
|
18809
18935
|
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
18810
18936
|
}
|
|
@@ -18838,11 +18964,11 @@ function verifyToken(decoded, expected) {
|
|
|
18838
18964
|
const token = headerToken ?? decoded["token"];
|
|
18839
18965
|
return token === expected;
|
|
18840
18966
|
}
|
|
18841
|
-
var
|
|
18967
|
+
var import_node_crypto5;
|
|
18842
18968
|
var init_crypto = __esm({
|
|
18843
18969
|
"../channels/src/feishu/crypto.ts"() {
|
|
18844
18970
|
"use strict";
|
|
18845
|
-
|
|
18971
|
+
import_node_crypto5 = require("node:crypto");
|
|
18846
18972
|
}
|
|
18847
18973
|
});
|
|
18848
18974
|
|
|
@@ -18978,11 +19104,12 @@ var init_src6 = __esm({
|
|
|
18978
19104
|
function errMsg(e) {
|
|
18979
19105
|
return e instanceof Error ? e.message : String(e);
|
|
18980
19106
|
}
|
|
18981
|
-
var
|
|
19107
|
+
var import_node_crypto6, FEISHU, ChannelService, CardStreamer;
|
|
18982
19108
|
var init_service = __esm({
|
|
18983
19109
|
"../server/src/channels/service.ts"() {
|
|
18984
19110
|
"use strict";
|
|
18985
|
-
|
|
19111
|
+
import_node_crypto6 = require("node:crypto");
|
|
19112
|
+
init_chat_turn_gate();
|
|
18986
19113
|
init_src6();
|
|
18987
19114
|
FEISHU = "feishu";
|
|
18988
19115
|
ChannelService = class {
|
|
@@ -19062,7 +19189,7 @@ var init_service = __esm({
|
|
|
19062
19189
|
return existing.chatSessionId;
|
|
19063
19190
|
}
|
|
19064
19191
|
}
|
|
19065
|
-
const id = (0,
|
|
19192
|
+
const id = (0, import_node_crypto6.randomUUID)();
|
|
19066
19193
|
const now = this.now();
|
|
19067
19194
|
if (cs) {
|
|
19068
19195
|
const rt = await this.deps.resolveRuntime?.(a.aiActorId).catch(() => null);
|
|
@@ -19098,8 +19225,20 @@ var init_service = __esm({
|
|
|
19098
19225
|
if (cs) {
|
|
19099
19226
|
const sess = await cs.getSession(intent.chatSessionId).catch(() => null);
|
|
19100
19227
|
resumeRt = sess?.runtimeSessionId ?? void 0;
|
|
19101
|
-
await cs.appendMessage({ id: (0,
|
|
19228
|
+
await cs.appendMessage({ id: (0, import_node_crypto6.randomUUID)(), sessionId: intent.chatSessionId, role: "user", content: intent.message, createdAt: this.now() }).catch(() => void 0);
|
|
19102
19229
|
}
|
|
19230
|
+
const opened = await openChatTurn({
|
|
19231
|
+
turns: this.deps.chatTurns,
|
|
19232
|
+
chatSessionId: intent.chatSessionId,
|
|
19233
|
+
source: "channel",
|
|
19234
|
+
now: () => this.now(),
|
|
19235
|
+
log: (m2) => this.log(m2)
|
|
19236
|
+
});
|
|
19237
|
+
if (!opened.ok) {
|
|
19238
|
+
await client.sendText(intent.chatId, "\u23F3 \u4E0A\u4E00\u6761\u8FD8\u5728\u5904\u7406\u4E2D\uFF0C\u7B49\u5B83\u8DD1\u5B8C\u518D\u53D1\u8FD9\u6761\u5427\uFF08\u8FD9\u6B21\u6CA1\u6709\u6392\u961F\uFF09\u3002").catch(() => void 0);
|
|
19239
|
+
return;
|
|
19240
|
+
}
|
|
19241
|
+
const held = opened.turn;
|
|
19103
19242
|
let session;
|
|
19104
19243
|
try {
|
|
19105
19244
|
session = await this.deps.dispatchChat({
|
|
@@ -19109,18 +19248,21 @@ var init_service = __esm({
|
|
|
19109
19248
|
...resumeRt ? { sessionId: resumeRt } : {}
|
|
19110
19249
|
});
|
|
19111
19250
|
} catch (err) {
|
|
19251
|
+
await held?.settle({ status: "failed", completedAt: this.now(), lastError: errMsg(err) });
|
|
19112
19252
|
await client.sendText(intent.chatId, `\u26A0\uFE0F \u6682\u65F6\u65E0\u6CD5\u5904\u7406\uFF1A${errMsg(err)}`).catch(() => void 0);
|
|
19113
19253
|
return;
|
|
19114
19254
|
}
|
|
19255
|
+
await held?.start({ assistantRunId: session.runId ?? null, dispatchId: session.runId ?? null });
|
|
19115
19256
|
const streamer = new CardStreamer(client, intent.chatId, this.cardIntervalMs, this.log);
|
|
19116
19257
|
session.onOutput((chunk) => streamer.push(chunk));
|
|
19117
19258
|
void session.done.then(async () => {
|
|
19259
|
+
await held?.settle({ status: "succeeded", completedAt: this.now() });
|
|
19118
19260
|
await streamer.finalize();
|
|
19119
19261
|
if (cs) {
|
|
19120
19262
|
const reply = streamer.text();
|
|
19121
19263
|
if (reply || session.runId) {
|
|
19122
19264
|
await cs.appendMessage({
|
|
19123
|
-
id: (0,
|
|
19265
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
19124
19266
|
sessionId: intent.chatSessionId,
|
|
19125
19267
|
role: "assistant",
|
|
19126
19268
|
content: reply,
|
|
@@ -19137,6 +19279,7 @@ var init_service = __esm({
|
|
|
19137
19279
|
}).catch(() => void 0);
|
|
19138
19280
|
}
|
|
19139
19281
|
}).catch(async (err) => {
|
|
19282
|
+
await held?.settle({ status: "failed", completedAt: this.now(), lastError: errMsg(err) });
|
|
19140
19283
|
await streamer.error(errMsg(err));
|
|
19141
19284
|
});
|
|
19142
19285
|
}
|
|
@@ -40197,7 +40340,7 @@ var require_websocket = __commonJS({
|
|
|
40197
40340
|
var http2 = require("http");
|
|
40198
40341
|
var net = require("net");
|
|
40199
40342
|
var tls = require("tls");
|
|
40200
|
-
var { randomBytes: randomBytes9, createHash:
|
|
40343
|
+
var { randomBytes: randomBytes9, createHash: createHash22 } = require("crypto");
|
|
40201
40344
|
var { Duplex, Readable } = require("stream");
|
|
40202
40345
|
var { URL: URL2 } = require("url");
|
|
40203
40346
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -40865,7 +41008,7 @@ var require_websocket = __commonJS({
|
|
|
40865
41008
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
40866
41009
|
return;
|
|
40867
41010
|
}
|
|
40868
|
-
const digest =
|
|
41011
|
+
const digest = createHash22("sha1").update(key + GUID).digest("base64");
|
|
40869
41012
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
40870
41013
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
40871
41014
|
return;
|
|
@@ -41234,7 +41377,7 @@ var require_websocket_server = __commonJS({
|
|
|
41234
41377
|
var EventEmitter = require("events");
|
|
41235
41378
|
var http2 = require("http");
|
|
41236
41379
|
var { Duplex } = require("stream");
|
|
41237
|
-
var { createHash:
|
|
41380
|
+
var { createHash: createHash22 } = require("crypto");
|
|
41238
41381
|
var extension2 = require_extension();
|
|
41239
41382
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
41240
41383
|
var subprotocol2 = require_subprotocol();
|
|
@@ -41541,7 +41684,7 @@ var require_websocket_server = __commonJS({
|
|
|
41541
41684
|
);
|
|
41542
41685
|
}
|
|
41543
41686
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
41544
|
-
const digest =
|
|
41687
|
+
const digest = createHash22("sha1").update(key + GUID).digest("base64");
|
|
41545
41688
|
const headers = [
|
|
41546
41689
|
"HTTP/1.1 101 Switching Protocols",
|
|
41547
41690
|
"Upgrade: websocket",
|
|
@@ -149144,7 +149287,7 @@ function slug4(name) {
|
|
|
149144
149287
|
return s2 || "user";
|
|
149145
149288
|
}
|
|
149146
149289
|
function short6() {
|
|
149147
|
-
return (0,
|
|
149290
|
+
return (0, import_node_crypto7.randomBytes)(4).toString("hex").slice(0, 6);
|
|
149148
149291
|
}
|
|
149149
149292
|
async function assertBindingWritePermission(caller, targetHumanId, deps) {
|
|
149150
149293
|
if (caller === targetHumanId) return true;
|
|
@@ -149209,11 +149352,11 @@ async function resolveWorkorderPrincipals(caller, cliOverrides, deps) {
|
|
|
149209
149352
|
}
|
|
149210
149353
|
return { error: "UNBOUND_AGENT_NO_OWNER", message: `owner \u65E0\u6CD5\u89E3\u6790\uFF1A\u8C03\u7528\u65B9 ${caller} \u65E2\u975E\u5728\u5C97\u771F\u4EBA\u4E5F\u975E\u5DF2\u7ED1\u52A9\u7406` };
|
|
149211
149354
|
}
|
|
149212
|
-
var
|
|
149355
|
+
var import_node_crypto7, DEFAULT_ASSISTANT_PROMPT, DEFAULT_ASSISTANT_SKILLS, DEFAULT_ASSISTANT_RUNTIME_KIND, ASSISTANT_ARCHIVED_CODE, AssistantLazyCreateError, AssistantsService;
|
|
149213
149356
|
var init_assistants = __esm({
|
|
149214
149357
|
"../server/src/domains/actors/assistants.ts"() {
|
|
149215
149358
|
"use strict";
|
|
149216
|
-
|
|
149359
|
+
import_node_crypto7 = require("node:crypto");
|
|
149217
149360
|
init_src2();
|
|
149218
149361
|
DEFAULT_ASSISTANT_PROMPT = "\u4F60\u662F\u8FD9\u540D\u771F\u4EBA\u5458\u5DE5\u7684\u4E13\u5C5E\u4E2A\u4EBA\u52A9\u7406\u3002\u4F60\u4EE5\u5176\u300C\u5DE5\u5355\u7BA1\u7406\u8005\u300D\u8EAB\u4EFD\u4EE3\u8DD1\uFF0C\u9075\u5FAA\u4E09\u6863\u6743\u9650\u6A21\u578B\uFF1A\u6863 A\uFF08\u52A0\u8FB9\u3001\u65B0\u5EFA\u8282\u70B9\u3001\u6539\u6807\u9898\u4E0E brief\u3001\u5E38\u89C4\u8BC4\u8BBA\u7B54\u7591\u3001\u63A8\u8FDB\u81EA\u5DF1\u63A5\u7684\u6D3B\uFF09\u53EF\u9006\u6539\u52A8\u76F4\u63A5\u751F\u6548\uFF1B\u6863 B\uFF08\u65AD\u8FB9\u3001\u5E9F\u8282\u70B9\u3001\u6539\u6D3E\u3001\u64A4\u90E8\u4EF6\uFF09\u9500\u6BC1\u6027\u6539\u52A8\u81EA\u52A8\u6253\u5305\u6210\u5F85\u786E\u8BA4\u63D0\u6848\uFF0C\u4EA4\u7531\u672C\u4EBA\u786E\u8BA4\u540E\u751F\u6548\uFF1B\u6863 C\uFF08force-conclude\u3001promote \u7B49\u6CBB\u7406\u547D\u4EE4\uFF09\u8D70\u4EBA\u5DE5\u6388\u6743\u5361\u3002\u4F60\u4E0D\u627F\u62C5\u4E1A\u52A1\u4E13\u4E1A\u5C97\u4F4D\u804C\u8D23\uFF0C\u53EA\u5728\u5176\u6388\u6743\u8303\u56F4\u5185\u534F\u52A9\u5EFA\u5355\u3001\u8DDF\u8FDB\u4E0E\u6C9F\u901A\u3002";
|
|
149219
149362
|
DEFAULT_ASSISTANT_SKILLS = [];
|
|
@@ -151488,6 +151631,59 @@ var init_injected_context = __esm({
|
|
|
151488
151631
|
}
|
|
151489
151632
|
});
|
|
151490
151633
|
|
|
151634
|
+
// ../server/src/domains/chat-sessions/continuation.ts
|
|
151635
|
+
function chatNodeChanged(recorded, current) {
|
|
151636
|
+
const nodeChanged = Boolean(recorded?.nodeId && current?.nodeId && recorded.nodeId !== current.nodeId);
|
|
151637
|
+
const kindChanged = Boolean(recorded?.runtimeKind && current?.runtimeKind && recorded.runtimeKind !== current.runtimeKind);
|
|
151638
|
+
return nodeChanged || kindChanged;
|
|
151639
|
+
}
|
|
151640
|
+
function toHistory(messages, excludeMessageKey) {
|
|
151641
|
+
return messages.filter((m2) => m2.role !== "system" && !(m2.role === "assistant" && m2.status === "running")).filter((m2) => !excludeMessageKey || m2.messageKey !== excludeMessageKey).map((m2) => ({
|
|
151642
|
+
role: m2.role,
|
|
151643
|
+
content: m2.role === "user" ? stripInjectedChatContext(m2.content) : m2.content
|
|
151644
|
+
}));
|
|
151645
|
+
}
|
|
151646
|
+
async function planChatContinuation(input) {
|
|
151647
|
+
const { session } = input;
|
|
151648
|
+
const currentRuntimeId = input.currentRuntime?.nodeId ?? null;
|
|
151649
|
+
const currentRuntimeKind = input.currentRuntime?.runtimeKind ?? null;
|
|
151650
|
+
const runtimeChanged = chatNodeChanged(
|
|
151651
|
+
{ nodeId: session.runtimeId, runtimeKind: session.runtimeKind },
|
|
151652
|
+
{ nodeId: currentRuntimeId, runtimeKind: currentRuntimeKind }
|
|
151653
|
+
);
|
|
151654
|
+
const limit = input.historyLimit ?? 20;
|
|
151655
|
+
const effectiveSessionId = runtimeChanged ? void 0 : session.runtimeSessionId ?? void 0;
|
|
151656
|
+
const needsHistoryInjection = runtimeChanged || !session.runtimeSessionId;
|
|
151657
|
+
const readHistory = async () => toHistory(await input.listMessages(session.id, limit), input.excludeMessageKey);
|
|
151658
|
+
const history = needsHistoryInjection ? await readHistory() : [];
|
|
151659
|
+
const runtimeMessage = buildRuntimeChatPrompt({
|
|
151660
|
+
userMessage: input.userMessage,
|
|
151661
|
+
...history.length ? { history } : {},
|
|
151662
|
+
...input.draftWorkspace ? { draftWorkspace: input.draftWorkspace } : {}
|
|
151663
|
+
});
|
|
151664
|
+
const fallbackHistory = effectiveSessionId ? await readHistory() : [];
|
|
151665
|
+
const fallbackMessage = fallbackHistory.length ? buildRuntimeChatPrompt({
|
|
151666
|
+
userMessage: input.userMessage,
|
|
151667
|
+
history: fallbackHistory,
|
|
151668
|
+
...input.draftWorkspace ? { draftWorkspace: input.draftWorkspace } : {}
|
|
151669
|
+
}) : void 0;
|
|
151670
|
+
return {
|
|
151671
|
+
runtimeMessage,
|
|
151672
|
+
...fallbackMessage ? { fallbackMessage } : {},
|
|
151673
|
+
...effectiveSessionId ? { effectiveSessionId } : {},
|
|
151674
|
+
runtimeChanged,
|
|
151675
|
+
currentRuntimeId,
|
|
151676
|
+
currentRuntimeKind,
|
|
151677
|
+
injectedHistoryCount: history.length
|
|
151678
|
+
};
|
|
151679
|
+
}
|
|
151680
|
+
var init_continuation = __esm({
|
|
151681
|
+
"../server/src/domains/chat-sessions/continuation.ts"() {
|
|
151682
|
+
"use strict";
|
|
151683
|
+
init_injected_context();
|
|
151684
|
+
}
|
|
151685
|
+
});
|
|
151686
|
+
|
|
151491
151687
|
// ../server/src/domains/knowledge/config-store.ts
|
|
151492
151688
|
function normalizedHttpUrl(value, field, allowEmpty = false) {
|
|
151493
151689
|
if (allowEmpty && (value === void 0 || value === null || value === "")) return "";
|
|
@@ -151637,11 +151833,11 @@ var init_config_store = __esm({
|
|
|
151637
151833
|
function clone2(value) {
|
|
151638
151834
|
return structuredClone(value);
|
|
151639
151835
|
}
|
|
151640
|
-
var
|
|
151836
|
+
var import_node_crypto8, import_promises2, import_node_path2, MemoryKnowledgeRunStore, FileKnowledgeRunStore;
|
|
151641
151837
|
var init_run_store = __esm({
|
|
151642
151838
|
"../server/src/domains/knowledge/run-store.ts"() {
|
|
151643
151839
|
"use strict";
|
|
151644
|
-
|
|
151840
|
+
import_node_crypto8 = require("node:crypto");
|
|
151645
151841
|
import_promises2 = require("node:fs/promises");
|
|
151646
151842
|
import_node_path2 = require("node:path");
|
|
151647
151843
|
MemoryKnowledgeRunStore = class {
|
|
@@ -151758,7 +151954,7 @@ var init_run_store = __esm({
|
|
|
151758
151954
|
}
|
|
151759
151955
|
async persist(runs) {
|
|
151760
151956
|
const shape = { version: 1, runs };
|
|
151761
|
-
const temp = `${this.file}.${process.pid}.${(0,
|
|
151957
|
+
const temp = `${this.file}.${process.pid}.${(0, import_node_crypto8.randomUUID)()}.tmp`;
|
|
151762
151958
|
try {
|
|
151763
151959
|
await (0, import_promises2.writeFile)(temp, `${JSON.stringify(shape, null, 2)}
|
|
151764
151960
|
`, { mode: 384 });
|
|
@@ -152301,11 +152497,11 @@ function retryableKnowledgeRun(record8) {
|
|
|
152301
152497
|
const latestAnswerAttempt = [...record8.attempts ?? []].reverse().find((attempt) => attempt.kind !== "fast");
|
|
152302
152498
|
return record8.status === "succeeded" && record8.request.action === "ask" && record8.executionHealth === "degraded" && latestAnswerAttempt?.status === "failed";
|
|
152303
152499
|
}
|
|
152304
|
-
var
|
|
152500
|
+
var import_node_crypto9, ACTION_LABEL, STATUS_MARKER, FINAL_ANSWER_MARKER, ACTIVE_STATUSES, RETRYABLE_STATUSES, HEARTBEAT_INTERVAL_MS, LEASE_MS, MAX_CONCURRENT_READS, ACTION_FLOW, KnowledgeService;
|
|
152305
152501
|
var init_service2 = __esm({
|
|
152306
152502
|
"../server/src/domains/knowledge/service.ts"() {
|
|
152307
152503
|
"use strict";
|
|
152308
|
-
|
|
152504
|
+
import_node_crypto9 = require("node:crypto");
|
|
152309
152505
|
init_src5();
|
|
152310
152506
|
init_config_store();
|
|
152311
152507
|
init_run_store();
|
|
@@ -152347,7 +152543,7 @@ var init_service2 = __esm({
|
|
|
152347
152543
|
constructor(options) {
|
|
152348
152544
|
this.options = options;
|
|
152349
152545
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
152350
|
-
this.id = options.id ??
|
|
152546
|
+
this.id = options.id ?? import_node_crypto9.randomUUID;
|
|
152351
152547
|
this.runStore = options.runStore ?? new MemoryKnowledgeRunStore();
|
|
152352
152548
|
this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;
|
|
152353
152549
|
this.leaseMs = options.leaseMs ?? LEASE_MS;
|
|
@@ -153612,7 +153808,7 @@ function trustedKnowledgeInvocationId(body, turn) {
|
|
|
153612
153808
|
if (body.action !== "ask") return void 0;
|
|
153613
153809
|
const scope = turn?.artifactId ?? turn?.dispatchId;
|
|
153614
153810
|
if (!scope) return void 0;
|
|
153615
|
-
const fingerprint = (0,
|
|
153811
|
+
const fingerprint = (0, import_node_crypto10.createHash)("sha256").update(JSON.stringify({
|
|
153616
153812
|
action: body.action,
|
|
153617
153813
|
message: body.message?.trim() ?? "",
|
|
153618
153814
|
queryMode: body.queryMode === "deep" ? "deep" : "fast"
|
|
@@ -153711,11 +153907,11 @@ function knowledgeRoutes(service) {
|
|
|
153711
153907
|
});
|
|
153712
153908
|
};
|
|
153713
153909
|
}
|
|
153714
|
-
var
|
|
153910
|
+
var import_node_crypto10;
|
|
153715
153911
|
var init_routes = __esm({
|
|
153716
153912
|
"../server/src/domains/knowledge/routes.ts"() {
|
|
153717
153913
|
"use strict";
|
|
153718
|
-
|
|
153914
|
+
import_node_crypto10 = require("node:crypto");
|
|
153719
153915
|
init_src5();
|
|
153720
153916
|
init_router();
|
|
153721
153917
|
}
|
|
@@ -154920,7 +155116,7 @@ function createAppJwt(appId, privateKeyPem, nowMs) {
|
|
|
154920
155116
|
exp: nowSec + JWT_TTL_SEC,
|
|
154921
155117
|
iss: appId
|
|
154922
155118
|
}));
|
|
154923
|
-
const signer = (0,
|
|
155119
|
+
const signer = (0, import_node_crypto11.createSign)("RSA-SHA256");
|
|
154924
155120
|
signer.update(`${header}.${payload}`);
|
|
154925
155121
|
signer.end();
|
|
154926
155122
|
return `${header}.${payload}.${b64url(signer.sign(privateKeyPem))}`;
|
|
@@ -155024,11 +155220,11 @@ async function exchangeManifestCode(code, deps = {}) {
|
|
|
155024
155220
|
...typeof body["webhook_secret"] === "string" ? { webhookSecret: body["webhook_secret"] } : {}
|
|
155025
155221
|
};
|
|
155026
155222
|
}
|
|
155027
|
-
var
|
|
155223
|
+
var import_node_crypto11, GITHUB_API, JWT_TTL_SEC, JWT_IAT_SKEW_SEC, defaultDeps2;
|
|
155028
155224
|
var init_app_auth = __esm({
|
|
155029
155225
|
"../connectors/src/github/app-auth.ts"() {
|
|
155030
155226
|
"use strict";
|
|
155031
|
-
|
|
155227
|
+
import_node_crypto11 = require("node:crypto");
|
|
155032
155228
|
GITHUB_API = "https://api.github.com";
|
|
155033
155229
|
JWT_TTL_SEC = 9 * 60;
|
|
155034
155230
|
JWT_IAT_SKEW_SEC = 60;
|
|
@@ -157288,7 +157484,7 @@ var init_readiness = __esm({
|
|
|
157288
157484
|
function deriveBoardKey(artifactId) {
|
|
157289
157485
|
const tail = artifactId.split(":").pop() ?? artifactId;
|
|
157290
157486
|
const ascii = tail.replace(/[^a-zA-Z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 24);
|
|
157291
|
-
const sha8 = (0,
|
|
157487
|
+
const sha8 = (0, import_node_crypto12.createHash)("sha256").update(artifactId).digest("hex").slice(0, 8);
|
|
157292
157488
|
return ascii ? `od-${ascii}-${sha8}` : `od-${sha8}`;
|
|
157293
157489
|
}
|
|
157294
157490
|
async function odApi(base, fetchImpl, method, p2, body) {
|
|
@@ -157331,7 +157527,7 @@ function materializeContext(resolvedDir, files) {
|
|
|
157331
157527
|
}
|
|
157332
157528
|
}
|
|
157333
157529
|
async function putVisibleUserTurn(base, fetchImpl, args) {
|
|
157334
|
-
const messageId = (0,
|
|
157530
|
+
const messageId = (0, import_node_crypto12.randomUUID)();
|
|
157335
157531
|
await odApi(
|
|
157336
157532
|
base,
|
|
157337
157533
|
fetchImpl,
|
|
@@ -157346,8 +157542,8 @@ async function startDesignRun(base, fetchImpl, args) {
|
|
|
157346
157542
|
agentId: args.agentId,
|
|
157347
157543
|
projectId: args.boardKey,
|
|
157348
157544
|
conversationId: args.conversationId,
|
|
157349
|
-
assistantMessageId: (0,
|
|
157350
|
-
clientRequestId: (0,
|
|
157545
|
+
assistantMessageId: (0, import_node_crypto12.randomUUID)(),
|
|
157546
|
+
clientRequestId: (0, import_node_crypto12.randomUUID)(),
|
|
157351
157547
|
message: args.message,
|
|
157352
157548
|
// 提案 design-conversational-driving 坑②:systemPrompt 从写死改为可选——缺省用引擎侧默认,
|
|
157353
157549
|
// 让设计意图由驱动方每轮的话(message)表达,而非焊死一句。
|
|
@@ -157528,11 +157724,11 @@ async function runDesignChatTurn(opts) {
|
|
|
157528
157724
|
const { artifacts, fileCount } = await fetchResultPackage(opts.base, fetchImpl, runId);
|
|
157529
157725
|
return { boardKey, conversationId, runId, userMessageId, artifacts, fileCount };
|
|
157530
157726
|
}
|
|
157531
|
-
var
|
|
157727
|
+
var import_node_crypto12, fs4, os, path3;
|
|
157532
157728
|
var init_driver = __esm({
|
|
157533
157729
|
"../adapters/src/open-design/driver.ts"() {
|
|
157534
157730
|
"use strict";
|
|
157535
|
-
|
|
157731
|
+
import_node_crypto12 = require("node:crypto");
|
|
157536
157732
|
fs4 = __toESM(require("node:fs"), 1);
|
|
157537
157733
|
os = __toESM(require("node:os"), 1);
|
|
157538
157734
|
path3 = __toESM(require("node:path"), 1);
|
|
@@ -157625,7 +157821,7 @@ function resolveWorkRoots(workRoot) {
|
|
|
157625
157821
|
}
|
|
157626
157822
|
function workdirSlug(workdirKey) {
|
|
157627
157823
|
const safe = workdirKey.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 48);
|
|
157628
|
-
const h = (0,
|
|
157824
|
+
const h = (0, import_node_crypto13.createHash)("sha256").update(workdirKey).digest("hex").slice(0, 8);
|
|
157629
157825
|
return `${safe}-${h}`;
|
|
157630
157826
|
}
|
|
157631
157827
|
function sessionDirFor(workRoot, runtimeKind, workdirKey) {
|
|
@@ -157757,11 +157953,11 @@ function prepareWorkdir(args) {
|
|
|
157757
157953
|
function legacyChatSessionDir(workRoot, rtId) {
|
|
157758
157954
|
return import_node_path13.default.join(resolveLegacyWorkRoot(workRoot), "oasis-chat-sessions", rtId.replace(/[^a-zA-Z0-9_-]+/g, "_"));
|
|
157759
157955
|
}
|
|
157760
|
-
var
|
|
157956
|
+
var import_node_crypto13, import_node_fs9, import_node_os6, import_node_path13, META_FILE, LOCK_FILE, NEW_WORK_ROOT, slug5, LEGACY_ROOTS, LEGACY_ONESHOT_PREFIX;
|
|
157761
157957
|
var init_session_paths = __esm({
|
|
157762
157958
|
"../adapters/src/_core/session-paths.ts"() {
|
|
157763
157959
|
"use strict";
|
|
157764
|
-
|
|
157960
|
+
import_node_crypto13 = require("node:crypto");
|
|
157765
157961
|
import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
157766
157962
|
import_node_os6 = __toESM(require("node:os"), 1);
|
|
157767
157963
|
import_node_path13 = __toESM(require("node:path"), 1);
|
|
@@ -157789,7 +157985,7 @@ function executionEnvHash(p2) {
|
|
|
157789
157985
|
binPkg: p2.binPackageDir ?? null,
|
|
157790
157986
|
extraRo: [...p2.extraRoBindDirs ?? []].sort()
|
|
157791
157987
|
});
|
|
157792
|
-
return (0,
|
|
157988
|
+
return (0, import_node_crypto14.createHash)("sha256").update(material).digest("hex").slice(0, 16);
|
|
157793
157989
|
}
|
|
157794
157990
|
function persistContainerName(workdirKey) {
|
|
157795
157991
|
return `oasis-wd-${workdirSlug(workdirKey)}`;
|
|
@@ -157909,11 +158105,11 @@ function wrapForContainer(p2) {
|
|
|
157909
158105
|
function killContainerArgs(containerName) {
|
|
157910
158106
|
return ["kill", containerName];
|
|
157911
158107
|
}
|
|
157912
|
-
var
|
|
158108
|
+
var import_node_crypto14, path6, CONTAINER_DEFAULT_PATH, CONTAINER_HOME, EE_HASH_LABEL;
|
|
157913
158109
|
var init_container_wrap = __esm({
|
|
157914
158110
|
"../adapters/src/_core/container-wrap.ts"() {
|
|
157915
158111
|
"use strict";
|
|
157916
|
-
|
|
158112
|
+
import_node_crypto14 = require("node:crypto");
|
|
157917
158113
|
path6 = __toESM(require("node:path"), 1);
|
|
157918
158114
|
init_session_paths();
|
|
157919
158115
|
CONTAINER_DEFAULT_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
@@ -158380,12 +158576,12 @@ function classifyExit(result) {
|
|
|
158380
158576
|
if (status === 429 || status === 529) return "rate-limit";
|
|
158381
158577
|
return "error";
|
|
158382
158578
|
}
|
|
158383
|
-
var import_node_child_process7,
|
|
158579
|
+
var import_node_child_process7, import_node_crypto15, fs9, path8, readline, CLAUDE_BLOCKED_FLAGS, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
|
|
158384
158580
|
var init_claude_code = __esm({
|
|
158385
158581
|
"../adapters/src/claude-code/index.ts"() {
|
|
158386
158582
|
"use strict";
|
|
158387
158583
|
import_node_child_process7 = require("node:child_process");
|
|
158388
|
-
|
|
158584
|
+
import_node_crypto15 = require("node:crypto");
|
|
158389
158585
|
fs9 = __toESM(require("node:fs"), 1);
|
|
158390
158586
|
path8 = __toESM(require("node:path"), 1);
|
|
158391
158587
|
readline = __toESM(require("node:readline"), 1);
|
|
@@ -158455,7 +158651,7 @@ var init_claude_code = __esm({
|
|
|
158455
158651
|
}
|
|
158456
158652
|
async spawn(job) {
|
|
158457
158653
|
const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
158458
|
-
const id = `claude-${slug6}-${(0,
|
|
158654
|
+
const id = `claude-${slug6}-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
|
|
158459
158655
|
const dir = job.workdirKey ? prepareWorkdir({
|
|
158460
158656
|
workRoot: this.opts.workRoot,
|
|
158461
158657
|
runtimeKind: "claude",
|
|
@@ -159934,13 +160130,13 @@ function normalizeCodexConsoleLine(line, state = createCodexNormalizeState()) {
|
|
|
159934
160130
|
tool.output.push(line);
|
|
159935
160131
|
return [];
|
|
159936
160132
|
}
|
|
159937
|
-
var import_node_child_process9,
|
|
160133
|
+
var import_node_child_process9, import_node_crypto16, fs10, os5, path10, readline2, CODEX_BLOCKED_FLAGS, CODEX_ARGV_PROMPT_MAX_BYTES, CodexAdapter;
|
|
159938
160134
|
var init_codex = __esm({
|
|
159939
160135
|
"../adapters/src/codex/index.ts"() {
|
|
159940
160136
|
"use strict";
|
|
159941
160137
|
import_node_child_process9 = require("node:child_process");
|
|
159942
160138
|
init_container_runtime();
|
|
159943
|
-
|
|
160139
|
+
import_node_crypto16 = require("node:crypto");
|
|
159944
160140
|
fs10 = __toESM(require("node:fs"), 1);
|
|
159945
160141
|
os5 = __toESM(require("node:os"), 1);
|
|
159946
160142
|
path10 = __toESM(require("node:path"), 1);
|
|
@@ -159986,7 +160182,7 @@ var init_codex = __esm({
|
|
|
159986
160182
|
capabilities = { appendInput: false };
|
|
159987
160183
|
async spawn(job) {
|
|
159988
160184
|
const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
159989
|
-
const id = `codex-${slug6}-${(0,
|
|
160185
|
+
const id = `codex-${slug6}-${(0, import_node_crypto16.randomUUID)().slice(0, 8)}`;
|
|
159990
160186
|
const startedAtMs = Date.now();
|
|
159991
160187
|
const dir = job.workdirKey ? prepareWorkdir({
|
|
159992
160188
|
workRoot: this.opts.workRoot,
|
|
@@ -160775,7 +160971,7 @@ async function runACPSession(job, cfg) {
|
|
|
160775
160971
|
}
|
|
160776
160972
|
const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
160777
160973
|
const binTag = path11.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
|
|
160778
|
-
const id = `${binTag}-${slug6}-${(0,
|
|
160974
|
+
const id = `${binTag}-${slug6}-${(0, import_node_crypto17.randomUUID)().slice(0, 8)}`;
|
|
160779
160975
|
const dir = job.workdirKey ? prepareWorkdir({
|
|
160780
160976
|
workRoot: cfg.workRoot,
|
|
160781
160977
|
runtimeKind: cfg.runtimeKind ?? "subprocess",
|
|
@@ -161085,12 +161281,12 @@ ${task}` : task;
|
|
|
161085
161281
|
}
|
|
161086
161282
|
};
|
|
161087
161283
|
}
|
|
161088
|
-
var import_node_child_process10,
|
|
161284
|
+
var import_node_child_process10, import_node_crypto17, fs11, path11, readline3, ACPClient, LEGACY_APPROVE_OPTION_ID;
|
|
161089
161285
|
var init_acp = __esm({
|
|
161090
161286
|
"../adapters/src/_core/acp.ts"() {
|
|
161091
161287
|
"use strict";
|
|
161092
161288
|
import_node_child_process10 = require("node:child_process");
|
|
161093
|
-
|
|
161289
|
+
import_node_crypto17 = require("node:crypto");
|
|
161094
161290
|
fs11 = __toESM(require("node:fs"), 1);
|
|
161095
161291
|
path11 = __toESM(require("node:path"), 1);
|
|
161096
161292
|
readline3 = __toESM(require("node:readline"), 1);
|
|
@@ -161588,7 +161784,7 @@ var init_reasonix = __esm({
|
|
|
161588
161784
|
// ../adapters/src/_core/subprocess.ts
|
|
161589
161785
|
async function materialize(job, cfg) {
|
|
161590
161786
|
const slug6 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
|
|
161591
|
-
const id = `${slug6}-${(0,
|
|
161787
|
+
const id = `${slug6}-${(0, import_node_crypto18.randomUUID)().slice(0, 8)}`;
|
|
161592
161788
|
const dir = job.workdirKey ? prepareWorkdir({
|
|
161593
161789
|
workRoot: cfg.workRoot,
|
|
161594
161790
|
runtimeKind: cfg.runtimeKind ?? "subprocess",
|
|
@@ -161860,12 +162056,12 @@ async function runOneShotText(job, cfg) {
|
|
|
161860
162056
|
}
|
|
161861
162057
|
});
|
|
161862
162058
|
}
|
|
161863
|
-
var import_node_child_process11,
|
|
162059
|
+
var import_node_child_process11, import_node_crypto18, fs12, path12, readline4;
|
|
161864
162060
|
var init_subprocess = __esm({
|
|
161865
162061
|
"../adapters/src/_core/subprocess.ts"() {
|
|
161866
162062
|
"use strict";
|
|
161867
162063
|
import_node_child_process11 = require("node:child_process");
|
|
161868
|
-
|
|
162064
|
+
import_node_crypto18 = require("node:crypto");
|
|
161869
162065
|
fs12 = __toESM(require("node:fs"), 1);
|
|
161870
162066
|
path12 = __toESM(require("node:path"), 1);
|
|
161871
162067
|
readline4 = __toESM(require("node:readline"), 1);
|
|
@@ -162312,11 +162508,11 @@ function normalizeOpenClaw(line) {
|
|
|
162312
162508
|
}
|
|
162313
162509
|
return [];
|
|
162314
162510
|
}
|
|
162315
|
-
var
|
|
162511
|
+
var import_node_crypto19, OpenClawAdapter;
|
|
162316
162512
|
var init_openclaw = __esm({
|
|
162317
162513
|
"../adapters/src/openclaw/index.ts"() {
|
|
162318
162514
|
"use strict";
|
|
162319
|
-
|
|
162515
|
+
import_node_crypto19 = require("node:crypto");
|
|
162320
162516
|
init_subprocess();
|
|
162321
162517
|
OpenClawAdapter = class {
|
|
162322
162518
|
constructor(opts = {}) {
|
|
@@ -162334,7 +162530,7 @@ var init_openclaw = __esm({
|
|
|
162334
162530
|
---
|
|
162335
162531
|
|
|
162336
162532
|
${task}` : task;
|
|
162337
|
-
const sessionId = job2.runtimeSessionId ?? `oasis-${(0,
|
|
162533
|
+
const sessionId = job2.runtimeSessionId ?? `oasis-${(0, import_node_crypto19.randomUUID)().slice(0, 8)}`;
|
|
162338
162534
|
return [
|
|
162339
162535
|
"agent",
|
|
162340
162536
|
...opts.mode !== "gateway" ? ["--local"] : [],
|
|
@@ -162818,11 +163014,6 @@ function resolveCallbackBase(supplied, requestHost) {
|
|
|
162818
163014
|
}
|
|
162819
163015
|
return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
162820
163016
|
}
|
|
162821
|
-
function chatNodeChanged(recorded, current) {
|
|
162822
|
-
const nodeChanged = Boolean(recorded?.nodeId && current?.nodeId && recorded.nodeId !== current.nodeId);
|
|
162823
|
-
const kindChanged = Boolean(recorded?.runtimeKind && current?.runtimeKind && recorded.runtimeKind !== current.runtimeKind);
|
|
162824
|
-
return nodeChanged || kindChanged;
|
|
162825
|
-
}
|
|
162826
163017
|
function wantsChatParts(req) {
|
|
162827
163018
|
const accept = req.headers.accept;
|
|
162828
163019
|
return typeof accept === "string" && accept.includes("application/x-ndjson");
|
|
@@ -164266,12 +164457,12 @@ async function startOasisServer(opts) {
|
|
|
164266
164457
|
if (!store || !dispatch) return;
|
|
164267
164458
|
const session = await store.getSession(origin).catch(() => null);
|
|
164268
164459
|
if (!session) return;
|
|
164269
|
-
const { randomUUID:
|
|
164460
|
+
const { randomUUID: randomUUID31 } = await import("node:crypto");
|
|
164270
164461
|
const approved = resolution.status === "approved";
|
|
164271
164462
|
const by = ctx.authorizedBy ?? "\u7BA1\u7406\u8005";
|
|
164272
164463
|
const label = ctx.effectLabel || ctx.command || "";
|
|
164273
164464
|
const statusText = approved ? `\u547D\u4EE4\u5DF2\u7531 ${by} \u6279\u51C6\u5E76\u4EE5\u5176\u540D\u4E49\u6267\u884C${label ? `\uFF1A${label}` : ""}\u3002` : `\u547D\u4EE4\u5DF2\u88AB ${by} \u9A73\u56DE${ctx.reason ? `\uFF1A${ctx.reason}` : ""}\u3002`;
|
|
164274
|
-
await store.appendMessage({ id:
|
|
164465
|
+
await store.appendMessage({ id: randomUUID31(), sessionId: origin, role: "system", content: statusText, createdAt: (/* @__PURE__ */ new Date()).toISOString() }).catch(() => void 0);
|
|
164275
164466
|
if (liveChat.isRunning(origin)) return;
|
|
164276
164467
|
const prompt = approved ? `\u4E0A\u4E00\u6761\u5F85\u6388\u6743\u547D\u4EE4\u5DF2\u88AB ${by} \u6279\u51C6\u5E76\u4EE5\u5176\u540D\u4E49\u6267\u884C\uFF08\u547D\u4EE4\uFF1A${label}\uFF09\u3002\u8BF7\u7EE7\u7EED\u539F\u4EFB\u52A1\u3002` : `\u4E0A\u4E00\u6761\u5F85\u6388\u6743\u547D\u4EE4\u5DF2\u88AB ${by} \u9A73\u56DE${ctx.reason ? `\uFF08\u7406\u7531\uFF1A${ctx.reason}\uFF09` : ""}\u3002\u522B\u91CD\u8BD5\u540C\u4E00\u6761\u2014\u2014\u8BF7\u6539\u65B9\u6848\u6216\u5148\u5411\u53D1\u8D77\u4EBA\u95EE\u6E05\u695A\u3002`;
|
|
164277
164468
|
let dispatched;
|
|
@@ -164292,7 +164483,7 @@ async function startOasisServer(opts) {
|
|
|
164292
164483
|
void dispatched.done.then(async () => {
|
|
164293
164484
|
if (text || dispatched.runId) {
|
|
164294
164485
|
await store.appendMessage({
|
|
164295
|
-
id:
|
|
164486
|
+
id: randomUUID31(),
|
|
164296
164487
|
sessionId: origin,
|
|
164297
164488
|
role: "assistant",
|
|
164298
164489
|
content: text,
|
|
@@ -164307,6 +164498,7 @@ async function startOasisServer(opts) {
|
|
|
164307
164498
|
store: opts.channels.store,
|
|
164308
164499
|
dispatchChat: (req) => opts.dispatchChat(req),
|
|
164309
164500
|
chatSession: opts.chatSession,
|
|
164501
|
+
chatTurns: opts.chatTurns,
|
|
164310
164502
|
revealVar: (key, actorId) => opts.actors.service.revealResolvedVariable(key, actorId),
|
|
164311
164503
|
// 决策 0080 第一期:探活成功时把 bot 的 app_name 落到连接器记录(account)供显示——
|
|
164312
164504
|
// 此前名称在 botInfo 后被丢弃,前端身份位只能显示 App ID(cli_xxx)。
|
|
@@ -165097,11 +165289,11 @@ async function startOasisServer(opts) {
|
|
|
165097
165289
|
const itemKey = `${baseItemKey}:${ctx.ownerId}`;
|
|
165098
165290
|
let chatSessionId;
|
|
165099
165291
|
if (chatStore) {
|
|
165100
|
-
const { randomUUID:
|
|
165292
|
+
const { randomUUID: randomUUID31 } = await import("node:crypto");
|
|
165101
165293
|
const known = discussSessions.get(itemKey);
|
|
165102
165294
|
if (known && await chatStore.getSession(known).catch(() => null)) chatSessionId = known;
|
|
165103
165295
|
if (!chatSessionId) {
|
|
165104
|
-
chatSessionId =
|
|
165296
|
+
chatSessionId = randomUUID31();
|
|
165105
165297
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
165106
165298
|
const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
|
|
165107
165299
|
const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
|
|
@@ -165555,38 +165747,62 @@ async function startOasisServer(opts) {
|
|
|
165555
165747
|
}
|
|
165556
165748
|
const registry2 = engine.registry ?? opts.registry;
|
|
165557
165749
|
const currentBinding = persistTarget && registry2 ? await registry2.getBinding(persistTarget.aiActorId).catch(() => null) : null;
|
|
165558
|
-
const currentRuntimeId = currentBinding?.status === "active" ? currentBinding.nodeId : null;
|
|
165559
|
-
const currentRuntimeKind = currentBinding?.status === "active" ? currentBinding.runtimeKind : null;
|
|
165560
|
-
const runtimeChanged = chatNodeChanged(
|
|
165561
|
-
{ nodeId: persistTarget?.runtimeId, runtimeKind: persistTarget?.runtimeKind },
|
|
165562
|
-
{ nodeId: currentRuntimeId, runtimeKind: currentRuntimeKind }
|
|
165563
|
-
);
|
|
165564
165750
|
const persistedUserMessage = persistTarget ? stripInjectedChatContext(body.message) : body.message;
|
|
165565
|
-
const needsHistoryInjection = Boolean(persistTarget && (runtimeChanged || !persistTarget.runtimeSessionId));
|
|
165566
|
-
const history = needsHistoryInjection && chatStore && persistTarget ? (await chatStore.listMessages(persistTarget.id, 20)).filter((message) => message.role !== "system" && !(message.role === "assistant" && message.status === "running")).map((message) => ({
|
|
165567
|
-
role: message.role,
|
|
165568
|
-
content: message.role === "user" ? stripInjectedChatContext(message.content) : message.content
|
|
165569
|
-
})) : [];
|
|
165570
165751
|
const draft = persistTarget && opts.workorderDrafts ? await opts.workorderDrafts.forSession(persistTarget.id) : void 0;
|
|
165571
165752
|
const draftWorkspace = draft && ![...engine.kernel.model.artifacts.values()].some((artifact) => artifact.workspace === draft.workspace) ? draft.workspace : void 0;
|
|
165572
|
-
const
|
|
165753
|
+
const plan = persistTarget && chatStore ? await planChatContinuation({
|
|
165754
|
+
session: persistTarget,
|
|
165755
|
+
userMessage: persistedUserMessage,
|
|
165756
|
+
listMessages: (sid, limit) => chatStore.listMessages(sid, limit),
|
|
165757
|
+
currentRuntime: currentBinding?.status === "active" ? { nodeId: currentBinding.nodeId, runtimeKind: currentBinding.runtimeKind } : null,
|
|
165758
|
+
...draftWorkspace ? { draftWorkspace } : {}
|
|
165759
|
+
}) : null;
|
|
165760
|
+
const currentRuntimeId = plan?.currentRuntimeId ?? null;
|
|
165761
|
+
const currentRuntimeKind = plan?.currentRuntimeKind ?? null;
|
|
165762
|
+
const runtimeMessage = plan ? plan.runtimeMessage : buildRuntimeChatPrompt({
|
|
165573
165763
|
userMessage: persistedUserMessage,
|
|
165574
|
-
...history.length ? { history } : {},
|
|
165575
165764
|
...draftWorkspace ? { draftWorkspace } : {}
|
|
165576
165765
|
});
|
|
165577
|
-
const effectiveSessionId =
|
|
165578
|
-
const
|
|
165579
|
-
|
|
165580
|
-
|
|
165581
|
-
|
|
165582
|
-
|
|
165583
|
-
|
|
165584
|
-
|
|
165585
|
-
|
|
165586
|
-
|
|
165587
|
-
|
|
165588
|
-
|
|
165589
|
-
|
|
165766
|
+
const effectiveSessionId = plan ? plan.effectiveSessionId : body.sessionId || void 0;
|
|
165767
|
+
const fallbackMessage = plan?.fallbackMessage;
|
|
165768
|
+
let heldTurn = null;
|
|
165769
|
+
if (persistTarget) {
|
|
165770
|
+
const opened = await openChatTurn({
|
|
165771
|
+
turns: opts.chatTurns,
|
|
165772
|
+
chatSessionId: persistTarget.id,
|
|
165773
|
+
source: "human",
|
|
165774
|
+
log: (m2) => console.warn(m2)
|
|
165775
|
+
});
|
|
165776
|
+
if (!opened.ok) {
|
|
165777
|
+
throw new ApiError(
|
|
165778
|
+
409,
|
|
165779
|
+
"CHAT_SESSION_BUSY",
|
|
165780
|
+
`\u8FD9\u4E2A\u4F1A\u8BDD\u6B63\u5728\u8DD1\u4E00\u8F6E\uFF0C\u8FD9\u53E5\u8BDD\u6CA1\u6709\u53D1\u51FA\u2014\u2014\u4E0D\u6392\u961F\u3001\u4E5F\u4E0D\u63D2\u8BDD\u3002\u7B49\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\u518D\u53D1\uFF0C\u6216\u8005\u7528\u300C\u63D2\u5165\u300D\u628A\u5B83\u9001\u8FDB\u6B63\u5728\u8DD1\u7684\u90A3\u4E00\u8F6E\u3002\uFF08\u5360\u7740\u7684\u8F6E\u6B21 ${opened.activeTurn.id}\uFF0C\u6765\u6E90 ${opened.activeTurn.source}\uFF09`
|
|
165781
|
+
);
|
|
165782
|
+
}
|
|
165783
|
+
heldTurn = opened.turn;
|
|
165784
|
+
}
|
|
165785
|
+
let session;
|
|
165786
|
+
try {
|
|
165787
|
+
session = await opts.dispatchChat({
|
|
165788
|
+
actorId: body.actorId,
|
|
165789
|
+
message: runtimeMessage,
|
|
165790
|
+
...effectiveSessionId ? { sessionId: effectiveSessionId } : {},
|
|
165791
|
+
...fallbackMessage ? { fallbackMessage } : {},
|
|
165792
|
+
...body.chatSessionId ? { chatSessionId: body.chatSessionId } : {},
|
|
165793
|
+
...body.workspace ? { workspace: body.workspace } : {},
|
|
165794
|
+
...currentCompanyId !== void 0 ? { companyId: currentCompanyId } : {},
|
|
165795
|
+
...attachments.length ? { attachments } : {}
|
|
165796
|
+
});
|
|
165797
|
+
} catch (err) {
|
|
165798
|
+
await heldTurn?.settle({
|
|
165799
|
+
status: "failed",
|
|
165800
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
165801
|
+
lastError: err instanceof Error ? err.message : String(err)
|
|
165802
|
+
});
|
|
165803
|
+
throw err;
|
|
165804
|
+
}
|
|
165805
|
+
await heldTurn?.start({ assistantRunId: session.runId ?? null, dispatchId: session.runId ?? null });
|
|
165590
165806
|
if (chatStore && persistTarget) {
|
|
165591
165807
|
if (currentRuntimeId) {
|
|
165592
165808
|
await chatStore.updateSession(persistTarget.id, {
|
|
@@ -165596,14 +165812,14 @@ async function startOasisServer(opts) {
|
|
|
165596
165812
|
runtimeSessionId: session.nativeSessionId ?? session.id
|
|
165597
165813
|
}).catch(() => void 0);
|
|
165598
165814
|
}
|
|
165599
|
-
const { randomUUID:
|
|
165815
|
+
const { randomUUID: randomUUID32 } = await import("node:crypto");
|
|
165600
165816
|
const persistAttachments = attachments.map((a) => ({
|
|
165601
165817
|
name: a.name,
|
|
165602
165818
|
...a.blobRef !== void 0 ? { blobRef: a.blobRef } : {},
|
|
165603
165819
|
...a.contentType !== void 0 ? { contentType: a.contentType } : {}
|
|
165604
165820
|
}));
|
|
165605
165821
|
await chatStore.appendMessage({
|
|
165606
|
-
id:
|
|
165822
|
+
id: randomUUID32(),
|
|
165607
165823
|
sessionId: persistTarget.id,
|
|
165608
165824
|
role: "user",
|
|
165609
165825
|
content: persistedUserMessage,
|
|
@@ -165613,8 +165829,8 @@ async function startOasisServer(opts) {
|
|
|
165613
165829
|
}
|
|
165614
165830
|
let assistantMsgId;
|
|
165615
165831
|
if (chatStore && persistTarget) {
|
|
165616
|
-
const { randomUUID:
|
|
165617
|
-
const id =
|
|
165832
|
+
const { randomUUID: randomUUID32 } = await import("node:crypto");
|
|
165833
|
+
const id = randomUUID32();
|
|
165618
165834
|
await chatStore.appendMessage({
|
|
165619
165835
|
id,
|
|
165620
165836
|
sessionId: persistTarget.id,
|
|
@@ -165650,9 +165866,9 @@ async function startOasisServer(opts) {
|
|
|
165650
165866
|
}).catch(() => void 0);
|
|
165651
165867
|
} else {
|
|
165652
165868
|
if (!assistantText && !session.runId && parts.length === 0) return;
|
|
165653
|
-
const { randomUUID:
|
|
165869
|
+
const { randomUUID: randomUUID32 } = await import("node:crypto");
|
|
165654
165870
|
await chatStore.appendMessage({
|
|
165655
|
-
id:
|
|
165871
|
+
id: randomUUID32(),
|
|
165656
165872
|
sessionId: persistTarget.id,
|
|
165657
165873
|
role: "assistant",
|
|
165658
165874
|
content: assistantText,
|
|
@@ -165663,6 +165879,12 @@ async function startOasisServer(opts) {
|
|
|
165663
165879
|
...parts.length ? { parts } : {}
|
|
165664
165880
|
}).catch(() => void 0);
|
|
165665
165881
|
}
|
|
165882
|
+
await heldTurn?.settle({
|
|
165883
|
+
status: status === "done" ? "succeeded" : "failed",
|
|
165884
|
+
completedAt: finishedAt,
|
|
165885
|
+
...session.runId ? { assistantRunId: session.runId, dispatchId: session.runId } : {},
|
|
165886
|
+
...status === "done" ? {} : { lastError: "\u8FD9\u4E00\u8F6E\u4EE5 error \u6536\u5C3E" }
|
|
165887
|
+
});
|
|
165666
165888
|
await chatStore.updateSession(persistTarget.id, {
|
|
165667
165889
|
...currentRuntimeId ? { runtimeId: currentRuntimeId } : {},
|
|
165668
165890
|
...currentRuntimeKind ? { runtimeKind: currentRuntimeKind } : {},
|
|
@@ -165885,8 +166107,8 @@ async function startOasisServer(opts) {
|
|
|
165885
166107
|
}
|
|
165886
166108
|
}
|
|
165887
166109
|
const { spawn: spawn9 } = await import("node:child_process");
|
|
165888
|
-
const { randomUUID:
|
|
165889
|
-
const sessionId = body.sessionId ??
|
|
166110
|
+
const { randomUUID: randomUUID31 } = await import("node:crypto");
|
|
166111
|
+
const sessionId = body.sessionId ?? randomUUID31();
|
|
165890
166112
|
const args = [
|
|
165891
166113
|
"-p",
|
|
165892
166114
|
body.message,
|
|
@@ -166111,8 +166333,8 @@ async function startOasisServer(opts) {
|
|
|
166111
166333
|
res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: `\u56DE\u8C03\u5730\u5740\u4E0D\u53EF\u7528\uFF0C\u5DF2\u4E2D\u6B62\uFF08\u907F\u514D\u5728 GitHub \u4E0A\u7559\u4E0B\u5B64\u513F App\uFF09\uFF1A${e.message}` }));
|
|
166112
166334
|
return;
|
|
166113
166335
|
}
|
|
166114
|
-
const { randomUUID:
|
|
166115
|
-
const state =
|
|
166336
|
+
const { randomUUID: randomUUID31 } = await import("node:crypto");
|
|
166337
|
+
const state = randomUUID31();
|
|
166116
166338
|
const nameSlug = (actorName || actorId?.split(":").pop() || owner?.login || "bot").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "").slice(0, 24) || "bot";
|
|
166117
166339
|
const manifest = buildAppManifest({
|
|
166118
166340
|
employeeSlug: nameSlug,
|
|
@@ -166563,8 +166785,11 @@ var init_server3 = __esm({
|
|
|
166563
166785
|
init_planner();
|
|
166564
166786
|
init_chat_parts();
|
|
166565
166787
|
init_injected_context();
|
|
166788
|
+
init_continuation();
|
|
166789
|
+
init_chat_turn_gate();
|
|
166566
166790
|
init_knowledge2();
|
|
166567
166791
|
init_src8();
|
|
166792
|
+
init_continuation();
|
|
166568
166793
|
githubAppPending = new PendingAppCreations();
|
|
166569
166794
|
defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
|
|
166570
166795
|
enc = (s2) => new TextEncoder().encode(s2);
|
|
@@ -166662,7 +166887,7 @@ function createNodeTokenStore(file) {
|
|
|
166662
166887
|
return {
|
|
166663
166888
|
issue(nodeId) {
|
|
166664
166889
|
const table = read();
|
|
166665
|
-
const token = `ont_${(0,
|
|
166890
|
+
const token = `ont_${(0, import_node_crypto20.randomBytes)(24).toString("base64url")}`;
|
|
166666
166891
|
table[token] = nodeId;
|
|
166667
166892
|
write(table);
|
|
166668
166893
|
return token;
|
|
@@ -166704,7 +166929,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
166704
166929
|
return {
|
|
166705
166930
|
issue(name, ttlMs = defaultTtlMs) {
|
|
166706
166931
|
const m2 = prune();
|
|
166707
|
-
const token = `ent_${(0,
|
|
166932
|
+
const token = `ent_${(0, import_node_crypto20.randomBytes)(24).toString("base64url")}`;
|
|
166708
166933
|
const expiresAt2 = Date.now() + ttlMs;
|
|
166709
166934
|
m2.set(token, { name, expiresAt: expiresAt2 });
|
|
166710
166935
|
write(m2);
|
|
@@ -166734,33 +166959,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
|
|
|
166734
166959
|
}
|
|
166735
166960
|
};
|
|
166736
166961
|
}
|
|
166737
|
-
var
|
|
166962
|
+
var import_node_crypto20, fs15, path15, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
|
|
166738
166963
|
var init_tokens = __esm({
|
|
166739
166964
|
"../server/src/tokens.ts"() {
|
|
166740
166965
|
"use strict";
|
|
166741
|
-
|
|
166966
|
+
import_node_crypto20 = require("node:crypto");
|
|
166742
166967
|
fs15 = __toESM(require("node:fs"), 1);
|
|
166743
166968
|
path15 = __toESM(require("node:path"), 1);
|
|
166744
166969
|
SESSION_TOKEN_PREFIX = "oat_v2_";
|
|
166745
166970
|
b64url2 = (value) => Buffer.from(value).toString("base64url");
|
|
166746
166971
|
fromB64url = (value) => Buffer.from(value, "base64url").toString("utf8");
|
|
166747
166972
|
readOrCreateSecret = (file) => {
|
|
166748
|
-
if (!file) return (0,
|
|
166973
|
+
if (!file) return (0, import_node_crypto20.randomBytes)(32);
|
|
166749
166974
|
try {
|
|
166750
166975
|
const raw = fs15.readFileSync(file, "utf8").trim();
|
|
166751
166976
|
if (raw) return Buffer.from(raw, "base64url");
|
|
166752
166977
|
} catch {
|
|
166753
166978
|
}
|
|
166754
|
-
const secret = (0,
|
|
166979
|
+
const secret = (0, import_node_crypto20.randomBytes)(32);
|
|
166755
166980
|
fs15.mkdirSync(path15.dirname(file), { recursive: true });
|
|
166756
166981
|
fs15.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
|
|
166757
166982
|
return secret;
|
|
166758
166983
|
};
|
|
166759
|
-
sign = (secret, payload) => (0,
|
|
166984
|
+
sign = (secret, payload) => (0, import_node_crypto20.createHmac)("sha256", secret).update(payload).digest("base64url");
|
|
166760
166985
|
signatureMatches = (actual, expected) => {
|
|
166761
166986
|
const a = Buffer.from(actual);
|
|
166762
166987
|
const b2 = Buffer.from(expected);
|
|
166763
|
-
return a.length === b2.length && (0,
|
|
166988
|
+
return a.length === b2.length && (0, import_node_crypto20.timingSafeEqual)(a, b2);
|
|
166764
166989
|
};
|
|
166765
166990
|
isTokenClaims = (value) => {
|
|
166766
166991
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -166807,31 +167032,31 @@ function createDataplaneTokenIssuer(opts = {}) {
|
|
|
166807
167032
|
}
|
|
166808
167033
|
};
|
|
166809
167034
|
}
|
|
166810
|
-
var
|
|
167035
|
+
var import_node_crypto21, fs16, path16, PREFIX, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims;
|
|
166811
167036
|
var init_session_dataplane_token = __esm({
|
|
166812
167037
|
"../server/src/session-dataplane-token.ts"() {
|
|
166813
167038
|
"use strict";
|
|
166814
|
-
|
|
167039
|
+
import_node_crypto21 = require("node:crypto");
|
|
166815
167040
|
fs16 = __toESM(require("node:fs"), 1);
|
|
166816
167041
|
path16 = __toESM(require("node:path"), 1);
|
|
166817
167042
|
init_src2();
|
|
166818
167043
|
PREFIX = "osd_";
|
|
166819
167044
|
b64url3 = (v2) => Buffer.from(v2, "utf8").toString("base64url");
|
|
166820
167045
|
fromB64url2 = (v2) => Buffer.from(v2, "base64url").toString("utf8");
|
|
166821
|
-
sign2 = (secret, payload) => (0,
|
|
167046
|
+
sign2 = (secret, payload) => (0, import_node_crypto21.createHmac)("sha256", secret).update(payload).digest("base64url");
|
|
166822
167047
|
sigMatches = (actual, expected) => {
|
|
166823
167048
|
const a = Buffer.from(actual);
|
|
166824
167049
|
const b2 = Buffer.from(expected);
|
|
166825
|
-
return a.length === b2.length && (0,
|
|
167050
|
+
return a.length === b2.length && (0, import_node_crypto21.timingSafeEqual)(a, b2);
|
|
166826
167051
|
};
|
|
166827
167052
|
readOrCreateSecret2 = (file) => {
|
|
166828
|
-
if (!file) return (0,
|
|
167053
|
+
if (!file) return (0, import_node_crypto21.randomBytes)(32);
|
|
166829
167054
|
try {
|
|
166830
167055
|
const raw = fs16.readFileSync(file, "utf8").trim();
|
|
166831
167056
|
if (raw) return Buffer.from(raw, "base64url");
|
|
166832
167057
|
} catch {
|
|
166833
167058
|
}
|
|
166834
|
-
const secret = (0,
|
|
167059
|
+
const secret = (0, import_node_crypto21.randomBytes)(32);
|
|
166835
167060
|
fs16.mkdirSync(path16.dirname(file), { recursive: true });
|
|
166836
167061
|
fs16.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
|
|
166837
167062
|
return secret;
|
|
@@ -168195,7 +168420,9 @@ var init_memory_automation_store = __esm({
|
|
|
168195
168420
|
collaborators: [],
|
|
168196
168421
|
createdBy: input.createdBy ?? null,
|
|
168197
168422
|
createdAt: now,
|
|
168198
|
-
updatedAt: now
|
|
168423
|
+
updatedAt: now,
|
|
168424
|
+
chatTarget: input.chatTarget ?? null,
|
|
168425
|
+
ownerHumanActorId: input.ownerHumanActorId ?? null
|
|
168199
168426
|
};
|
|
168200
168427
|
this.automations.set(id, structuredClone(a));
|
|
168201
168428
|
if (input.triggers?.length) await this.replaceTriggers(id, input.triggers);
|
|
@@ -168678,11 +168905,11 @@ function onlyHits(pool, q) {
|
|
|
168678
168905
|
if (!q.requireMatch || q.keywords.length === 0) return [...pool];
|
|
168679
168906
|
return pool.filter((r) => memoryMatchScore(r, q.keywords) > 0);
|
|
168680
168907
|
}
|
|
168681
|
-
var
|
|
168908
|
+
var import_node_crypto22, MemoryActorMemoryStore, toIndexEntry;
|
|
168682
168909
|
var init_memory_actor_memory_store = __esm({
|
|
168683
168910
|
"../testkit/src/memory-actor-memory-store.ts"() {
|
|
168684
168911
|
"use strict";
|
|
168685
|
-
|
|
168912
|
+
import_node_crypto22 = require("node:crypto");
|
|
168686
168913
|
init_src2();
|
|
168687
168914
|
MemoryActorMemoryStore = class {
|
|
168688
168915
|
rows = /* @__PURE__ */ new Map();
|
|
@@ -168736,7 +168963,7 @@ var init_memory_actor_memory_store = __esm({
|
|
|
168736
168963
|
async write(input, now) {
|
|
168737
168964
|
if (input.memId === void 0) {
|
|
168738
168965
|
const rec = {
|
|
168739
|
-
memId: `mem:${(0,
|
|
168966
|
+
memId: `mem:${(0, import_node_crypto22.randomUUID)()}`,
|
|
168740
168967
|
actorId: input.actorId,
|
|
168741
168968
|
projectId: input.projectId,
|
|
168742
168969
|
keywords: [...input.keywords],
|
|
@@ -170628,11 +170855,11 @@ var init_service3 = __esm({
|
|
|
170628
170855
|
});
|
|
170629
170856
|
|
|
170630
170857
|
// ../server/src/dev-store.ts
|
|
170631
|
-
var
|
|
170858
|
+
var import_node_crypto23, fs17, path17, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
|
|
170632
170859
|
var init_dev_store = __esm({
|
|
170633
170860
|
"../server/src/dev-store.ts"() {
|
|
170634
170861
|
"use strict";
|
|
170635
|
-
|
|
170862
|
+
import_node_crypto23 = require("node:crypto");
|
|
170636
170863
|
fs17 = __toESM(require("node:fs"), 1);
|
|
170637
170864
|
path17 = __toESM(require("node:path"), 1);
|
|
170638
170865
|
init_src2();
|
|
@@ -170686,7 +170913,7 @@ var init_dev_store = __esm({
|
|
|
170686
170913
|
return path17.join(this.dir, hash);
|
|
170687
170914
|
}
|
|
170688
170915
|
async put(bytes) {
|
|
170689
|
-
const hash = (0,
|
|
170916
|
+
const hash = (0, import_node_crypto23.createHash)("sha256").update(bytes).digest("hex");
|
|
170690
170917
|
const file = this.fileOf(hash);
|
|
170691
170918
|
if (!fs17.existsSync(file)) fs17.writeFileSync(file, bytes);
|
|
170692
170919
|
return hash;
|
|
@@ -171186,6 +171413,10 @@ var init_dev_store = __esm({
|
|
|
171186
171413
|
this.sessionWorkOrders.delete(id);
|
|
171187
171414
|
}
|
|
171188
171415
|
async appendMessage(m2) {
|
|
171416
|
+
if (m2.messageKey) {
|
|
171417
|
+
const existing = await this.getMessageByKey(m2.messageKey);
|
|
171418
|
+
if (existing) return existing;
|
|
171419
|
+
}
|
|
171189
171420
|
const list = this.messages.get(m2.sessionId) ?? [];
|
|
171190
171421
|
const seq = (list.length ? Math.max(...list.map((x2) => x2.seq)) : 0) + 1;
|
|
171191
171422
|
const record8 = { ...m2, seq };
|
|
@@ -171193,6 +171424,26 @@ var init_dev_store = __esm({
|
|
|
171193
171424
|
this.messages.set(m2.sessionId, list);
|
|
171194
171425
|
return record8;
|
|
171195
171426
|
}
|
|
171427
|
+
async getMessageByKey(messageKey) {
|
|
171428
|
+
for (const list of this.messages.values()) {
|
|
171429
|
+
const hit = list.find((x2) => x2.messageKey === messageKey);
|
|
171430
|
+
if (hit) return hit;
|
|
171431
|
+
}
|
|
171432
|
+
return null;
|
|
171433
|
+
}
|
|
171434
|
+
async countMessages(sessionId) {
|
|
171435
|
+
return (this.messages.get(sessionId) ?? []).length;
|
|
171436
|
+
}
|
|
171437
|
+
/** 选择器搜索(ADR 会话投递 D8):与 PG 版同口径——三元组精确 + 标题/ID 前缀 + touchedAt 倒序。 */
|
|
171438
|
+
async searchSessionsForTarget(q) {
|
|
171439
|
+
const text = q.text?.trim().toLowerCase();
|
|
171440
|
+
const all = [...this.sessions.values()].filter((s2) => (s2.companyId ?? null) === q.companyId && s2.humanActorId === q.humanActorId && s2.aiActorId === q.aiActorId && !s2.analyzedRunId && (!text || (s2.title ?? "").toLowerCase().includes(text) || s2.id.toLowerCase().startsWith(text))).sort((a, b2) => b2.touchedAt.localeCompare(a.touchedAt) || b2.id.localeCompare(a.id));
|
|
171441
|
+
const page = all.slice(q.offset, q.offset + q.limit);
|
|
171442
|
+
return {
|
|
171443
|
+
items: page.map((s2) => ({ ...s2, messageCount: (this.messages.get(s2.id) ?? []).length })),
|
|
171444
|
+
hasMore: q.offset + q.limit < all.length
|
|
171445
|
+
};
|
|
171446
|
+
}
|
|
171196
171447
|
async updateMessage(id, patch) {
|
|
171197
171448
|
for (const list of this.messages.values()) {
|
|
171198
171449
|
const item = list.find((x2) => x2.id === id);
|
|
@@ -171277,6 +171528,94 @@ var init_dev_store = __esm({
|
|
|
171277
171528
|
fs17.writeFileSync(this.file, JSON.stringify(snap, null, 2));
|
|
171278
171529
|
}
|
|
171279
171530
|
};
|
|
171531
|
+
MemoryChatTurnStore = class {
|
|
171532
|
+
turns = /* @__PURE__ */ new Map();
|
|
171533
|
+
/** 同步临界区:故意不 await,保证 claim 是原子的。 */
|
|
171534
|
+
claimSync(input) {
|
|
171535
|
+
for (const t of this.turns.values()) {
|
|
171536
|
+
if (t.source === input.source && t.sourceRunId === input.sourceRunId) return { outcome: "resumed", turn: t };
|
|
171537
|
+
}
|
|
171538
|
+
for (const t of this.turns.values()) {
|
|
171539
|
+
if (t.chatSessionId === input.chatSessionId && (t.status === "reserved" || t.status === "running")) {
|
|
171540
|
+
return { outcome: "busy", activeTurn: t };
|
|
171541
|
+
}
|
|
171542
|
+
}
|
|
171543
|
+
const turn = {
|
|
171544
|
+
id: input.turnId,
|
|
171545
|
+
chatSessionId: input.chatSessionId,
|
|
171546
|
+
source: input.source,
|
|
171547
|
+
sourceRunId: input.sourceRunId,
|
|
171548
|
+
messageKey: input.messageKey,
|
|
171549
|
+
status: "reserved",
|
|
171550
|
+
dispatchId: null,
|
|
171551
|
+
assistantRunId: null,
|
|
171552
|
+
reservedAt: input.reservedAt,
|
|
171553
|
+
startedAt: null,
|
|
171554
|
+
completedAt: null,
|
|
171555
|
+
lastError: null
|
|
171556
|
+
};
|
|
171557
|
+
this.turns.set(turn.id, turn);
|
|
171558
|
+
return { outcome: "claimed", turn };
|
|
171559
|
+
}
|
|
171560
|
+
async claimTurn(input) {
|
|
171561
|
+
const claim = this.claimSync(input);
|
|
171562
|
+
if (claim.outcome === "claimed") this.persist();
|
|
171563
|
+
return claim;
|
|
171564
|
+
}
|
|
171565
|
+
async getTurn(turnId) {
|
|
171566
|
+
return this.turns.get(turnId) ?? null;
|
|
171567
|
+
}
|
|
171568
|
+
async getTurnBySourceRun(source, sourceRunId) {
|
|
171569
|
+
for (const t of this.turns.values()) if (t.source === source && t.sourceRunId === sourceRunId) return t;
|
|
171570
|
+
return null;
|
|
171571
|
+
}
|
|
171572
|
+
async activeTurn(chatSessionId) {
|
|
171573
|
+
for (const t of this.turns.values()) {
|
|
171574
|
+
if (t.chatSessionId === chatSessionId && (t.status === "reserved" || t.status === "running")) return t;
|
|
171575
|
+
}
|
|
171576
|
+
return null;
|
|
171577
|
+
}
|
|
171578
|
+
async settleTurn(turnId, patch) {
|
|
171579
|
+
const t = this.turns.get(turnId);
|
|
171580
|
+
if (!t) return;
|
|
171581
|
+
this.turns.set(turnId, {
|
|
171582
|
+
...t,
|
|
171583
|
+
status: patch.status,
|
|
171584
|
+
...patch.dispatchId !== void 0 ? { dispatchId: patch.dispatchId } : {},
|
|
171585
|
+
...patch.assistantRunId !== void 0 ? { assistantRunId: patch.assistantRunId } : {},
|
|
171586
|
+
...patch.startedAt !== void 0 ? { startedAt: patch.startedAt } : {},
|
|
171587
|
+
...patch.completedAt !== void 0 ? { completedAt: patch.completedAt } : {},
|
|
171588
|
+
...patch.lastError !== void 0 ? { lastError: patch.lastError } : {}
|
|
171589
|
+
});
|
|
171590
|
+
this.persist();
|
|
171591
|
+
}
|
|
171592
|
+
async listStaleActiveTurns(reservedBefore, limit = 50) {
|
|
171593
|
+
return [...this.turns.values()].filter((t) => (t.status === "reserved" || t.status === "running") && t.reservedAt < reservedBefore).sort((a, b2) => a.reservedAt.localeCompare(b2.reservedAt)).slice(0, limit);
|
|
171594
|
+
}
|
|
171595
|
+
/** 文件版覆写。内存版不落盘。 */
|
|
171596
|
+
persist() {
|
|
171597
|
+
}
|
|
171598
|
+
};
|
|
171599
|
+
FileChatTurnStore = class _FileChatTurnStore extends MemoryChatTurnStore {
|
|
171600
|
+
constructor(file) {
|
|
171601
|
+
super();
|
|
171602
|
+
this.file = file;
|
|
171603
|
+
}
|
|
171604
|
+
static async open(file) {
|
|
171605
|
+
const store = new _FileChatTurnStore(file);
|
|
171606
|
+
if (fs17.existsSync(file)) {
|
|
171607
|
+
const snap = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
171608
|
+
for (const t of snap.turns ?? []) store.turns.set(t.id, t);
|
|
171609
|
+
} else {
|
|
171610
|
+
fs17.mkdirSync(path17.dirname(file), { recursive: true });
|
|
171611
|
+
fs17.writeFileSync(file, JSON.stringify({ turns: [] }, null, 2));
|
|
171612
|
+
}
|
|
171613
|
+
return store;
|
|
171614
|
+
}
|
|
171615
|
+
persist() {
|
|
171616
|
+
fs17.writeFileSync(this.file, JSON.stringify({ turns: [...this.turns.values()] }, null, 2));
|
|
171617
|
+
}
|
|
171618
|
+
};
|
|
171280
171619
|
MemoryReadMarkerStore = class _MemoryReadMarkerStore {
|
|
171281
171620
|
markers = /* @__PURE__ */ new Map();
|
|
171282
171621
|
static key(actorId, scope) {
|
|
@@ -171461,11 +171800,11 @@ function typeDefError(def, mode) {
|
|
|
171461
171800
|
}
|
|
171462
171801
|
return null;
|
|
171463
171802
|
}
|
|
171464
|
-
var
|
|
171803
|
+
var import_node_crypto24, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
|
|
171465
171804
|
var init_types_admin = __esm({
|
|
171466
171805
|
"../server/src/types-admin.ts"() {
|
|
171467
171806
|
"use strict";
|
|
171468
|
-
|
|
171807
|
+
import_node_crypto24 = require("node:crypto");
|
|
171469
171808
|
init_zod();
|
|
171470
171809
|
init_src5();
|
|
171471
171810
|
init_config_audit();
|
|
@@ -171592,7 +171931,7 @@ var init_types_admin = __esm({
|
|
|
171592
171931
|
async trace(change, target, by, detail) {
|
|
171593
171932
|
try {
|
|
171594
171933
|
await this.audit?.({
|
|
171595
|
-
id: `reg_${(0,
|
|
171934
|
+
id: `reg_${(0, import_node_crypto24.randomUUID)()}`,
|
|
171596
171935
|
actor: by ?? SYSTEM_CONFIG_ACTOR,
|
|
171597
171936
|
kind: "registry_change",
|
|
171598
171937
|
target,
|
|
@@ -171740,11 +172079,11 @@ function roleDefError(def, mode) {
|
|
|
171740
172079
|
}
|
|
171741
172080
|
return null;
|
|
171742
172081
|
}
|
|
171743
|
-
var
|
|
172082
|
+
var import_node_crypto25, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
|
|
171744
172083
|
var init_roles_admin = __esm({
|
|
171745
172084
|
"../server/src/roles-admin.ts"() {
|
|
171746
172085
|
"use strict";
|
|
171747
|
-
|
|
172086
|
+
import_node_crypto25 = require("node:crypto");
|
|
171748
172087
|
init_zod();
|
|
171749
172088
|
init_src5();
|
|
171750
172089
|
init_config_audit();
|
|
@@ -171769,7 +172108,7 @@ var init_roles_admin = __esm({
|
|
|
171769
172108
|
async trace(change, target, by, detail) {
|
|
171770
172109
|
try {
|
|
171771
172110
|
await this.deps.audit?.({
|
|
171772
|
-
id: `reg_${(0,
|
|
172111
|
+
id: `reg_${(0, import_node_crypto25.randomUUID)()}`,
|
|
171773
172112
|
actor: by ?? SYSTEM_CONFIG_ACTOR,
|
|
171774
172113
|
kind: "registry_change",
|
|
171775
172114
|
target,
|
|
@@ -173747,7 +174086,7 @@ function wireRecoveredChatTurn(deps) {
|
|
|
173747
174086
|
const segText = assistantText;
|
|
173748
174087
|
const segParts = collectedParts();
|
|
173749
174088
|
const closingId = currentMsgId;
|
|
173750
|
-
const nextId = (0,
|
|
174089
|
+
const nextId = (0, import_node_crypto26.randomUUID)();
|
|
173751
174090
|
assistantText = "";
|
|
173752
174091
|
currentMsgId = nextId;
|
|
173753
174092
|
segmentBaseSeq = live.bufferedParts().reduce((max, p2) => p2.seq > max ? p2.seq : max, 0);
|
|
@@ -173760,7 +174099,7 @@ function wireRecoveredChatTurn(deps) {
|
|
|
173760
174099
|
...segParts.length ? { parts: segParts } : {}
|
|
173761
174100
|
}).catch(() => void 0);
|
|
173762
174101
|
await store.appendMessage({
|
|
173763
|
-
id: (0,
|
|
174102
|
+
id: (0, import_node_crypto26.randomUUID)(),
|
|
173764
174103
|
sessionId: plan.chatSessionId,
|
|
173765
174104
|
role: "user",
|
|
173766
174105
|
content: text,
|
|
@@ -173939,11 +174278,11 @@ async function reconcileChatExitFrame(deps) {
|
|
|
173939
174278
|
}
|
|
173940
174279
|
return false;
|
|
173941
174280
|
}
|
|
173942
|
-
var
|
|
174281
|
+
var import_node_crypto26, asObj3, partsOf, maxPartSeq, exitFailed, exitErrorText, runTerminalPatch;
|
|
173943
174282
|
var init_chat_recovery = __esm({
|
|
173944
174283
|
"../server/src/chat-recovery.ts"() {
|
|
173945
174284
|
"use strict";
|
|
173946
|
-
|
|
174285
|
+
import_node_crypto26 = require("node:crypto");
|
|
173947
174286
|
init_chat_parts();
|
|
173948
174287
|
init_sink();
|
|
173949
174288
|
init_resilient_appender();
|
|
@@ -173970,8 +174309,8 @@ var init_chat_recovery = __esm({
|
|
|
173970
174309
|
|
|
173971
174310
|
// ../server/src/auth/crypto.ts
|
|
173972
174311
|
function hashPassword(password) {
|
|
173973
|
-
const salt = (0,
|
|
173974
|
-
const dk = (0,
|
|
174312
|
+
const salt = (0, import_node_crypto27.randomBytes)(16);
|
|
174313
|
+
const dk = (0, import_node_crypto27.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
|
|
173975
174314
|
return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
|
|
173976
174315
|
}
|
|
173977
174316
|
function verifyPassword(password, stored) {
|
|
@@ -173988,8 +174327,8 @@ function verifyPassword(password, stored) {
|
|
|
173988
174327
|
return false;
|
|
173989
174328
|
}
|
|
173990
174329
|
if (expected.length === 0) return false;
|
|
173991
|
-
const dk = (0,
|
|
173992
|
-
return dk.length === expected.length && (0,
|
|
174330
|
+
const dk = (0, import_node_crypto27.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
|
|
174331
|
+
return dk.length === expected.length && (0, import_node_crypto27.timingSafeEqual)(dk, expected);
|
|
173993
174332
|
}
|
|
173994
174333
|
function b64urlJson(value) {
|
|
173995
174334
|
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
@@ -173997,17 +174336,17 @@ function b64urlJson(value) {
|
|
|
173997
174336
|
function signSession(claims, secret) {
|
|
173998
174337
|
const head = b64urlJson({ alg: "HS256", typ: "JWT" });
|
|
173999
174338
|
const body = b64urlJson(claims);
|
|
174000
|
-
const sig = (0,
|
|
174339
|
+
const sig = (0, import_node_crypto27.createHmac)("sha256", secret).update(`${head}.${body}`).digest("base64url");
|
|
174001
174340
|
return `${head}.${body}.${sig}`;
|
|
174002
174341
|
}
|
|
174003
174342
|
function verifySession(token, secret, nowMs) {
|
|
174004
174343
|
const parts = token.split(".");
|
|
174005
174344
|
if (parts.length !== 3) return null;
|
|
174006
174345
|
const [head, body, sig] = parts;
|
|
174007
|
-
const expected = (0,
|
|
174346
|
+
const expected = (0, import_node_crypto27.createHmac)("sha256", secret).update(`${head}.${body}`).digest("base64url");
|
|
174008
174347
|
const got = Buffer.from(sig);
|
|
174009
174348
|
const exp = Buffer.from(expected);
|
|
174010
|
-
if (got.length !== exp.length || !(0,
|
|
174349
|
+
if (got.length !== exp.length || !(0, import_node_crypto27.timingSafeEqual)(got, exp)) return null;
|
|
174011
174350
|
let claims;
|
|
174012
174351
|
try {
|
|
174013
174352
|
claims = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
|
|
@@ -174019,21 +174358,21 @@ function verifySession(token, secret, nowMs) {
|
|
|
174019
174358
|
return claims;
|
|
174020
174359
|
}
|
|
174021
174360
|
function generateCode() {
|
|
174022
|
-
return String((0,
|
|
174361
|
+
return String((0, import_node_crypto27.randomInt)(0, 1e6)).padStart(6, "0");
|
|
174023
174362
|
}
|
|
174024
174363
|
function hashCode(code, secret) {
|
|
174025
|
-
return (0,
|
|
174364
|
+
return (0, import_node_crypto27.createHmac)("sha256", secret).update(`code:${code}`).digest("base64url");
|
|
174026
174365
|
}
|
|
174027
174366
|
function safeEqualHash(a, b2) {
|
|
174028
174367
|
const ba = Buffer.from(a);
|
|
174029
174368
|
const bb = Buffer.from(b2);
|
|
174030
|
-
return ba.length === bb.length && (0,
|
|
174369
|
+
return ba.length === bb.length && (0, import_node_crypto27.timingSafeEqual)(ba, bb);
|
|
174031
174370
|
}
|
|
174032
|
-
var
|
|
174371
|
+
var import_node_crypto27, SCRYPT_N, SCRYPT_KEYLEN;
|
|
174033
174372
|
var init_crypto2 = __esm({
|
|
174034
174373
|
"../server/src/auth/crypto.ts"() {
|
|
174035
174374
|
"use strict";
|
|
174036
|
-
|
|
174375
|
+
import_node_crypto27 = require("node:crypto");
|
|
174037
174376
|
SCRYPT_N = 16384;
|
|
174038
174377
|
SCRYPT_KEYLEN = 32;
|
|
174039
174378
|
}
|
|
@@ -174165,6 +174504,13 @@ var init_daemon_hub = __esm({
|
|
|
174165
174504
|
sessionMeta = /* @__PURE__ */ new Map();
|
|
174166
174505
|
sessionConnectListeners = [];
|
|
174167
174506
|
sessionDisconnectListeners = [];
|
|
174507
|
+
/**
|
|
174508
|
+
* 「这次派发归谁管、现在什么状态」的认领口。**必须由 hub 汇总,不能各 adapter 自己判**——
|
|
174509
|
+
* serve 里同时有两个 DaemonHubAdapter(chat 一个、dispatch 一个),一次派发只属于其中一个。
|
|
174510
|
+
* 让 adapter 自己判「我不认识就 evict」,另一个 adapter 会把别人的活会话一枪打死
|
|
174511
|
+
* (2026-08-25 真机实测:chat 会话刚连上就收到 evict(unknown),用户看到「对话已中断」)。
|
|
174512
|
+
*/
|
|
174513
|
+
sessionClaimCheckers = [];
|
|
174168
174514
|
resolveDataplane;
|
|
174169
174515
|
/** 每个 daemon 最近一次收到任何帧的时间戳(含它自发的心跳 pong)——liveness 判定的依据。 */
|
|
174170
174516
|
lastSeen = /* @__PURE__ */ new Map();
|
|
@@ -174374,6 +174720,7 @@ var init_daemon_hub = __esm({
|
|
|
174374
174720
|
this.sessionSockets.set(dispatchId, ws);
|
|
174375
174721
|
this.sessionMeta.set(dispatchId, { nodeId, sessionId: msg.sessionId, connectedAt: Date.now() });
|
|
174376
174722
|
for (const l of this.sessionConnectListeners) l({ dispatchId, sessionId: msg.sessionId, nodeId });
|
|
174723
|
+
this.judgeSession(dispatchId);
|
|
174377
174724
|
}
|
|
174378
174725
|
return;
|
|
174379
174726
|
}
|
|
@@ -174395,6 +174742,28 @@ var init_daemon_hub = __esm({
|
|
|
174395
174742
|
});
|
|
174396
174743
|
ws.on("error", (err) => console.error(`[hub] session ${dispatchId} error:`, err.message));
|
|
174397
174744
|
}
|
|
174745
|
+
/**
|
|
174746
|
+
* 注册一个认领判断。返回:
|
|
174747
|
+
* live —— 这次派发在我这儿还活着(别动它)
|
|
174748
|
+
* settled —— 我这儿已经收尾/收割了(该 evict,让旧进程自尽)
|
|
174749
|
+
* unknown —— 我不认识(可能归别人管,**不构成 evict 理由**)
|
|
174750
|
+
*/
|
|
174751
|
+
registerSessionClaimCheck(fn) {
|
|
174752
|
+
this.sessionClaimCheckers.push(fn);
|
|
174753
|
+
}
|
|
174754
|
+
/** 会话连上来时问一圈:没人说 live、且有人说 settled,才 evict。全 unknown = 没人认领,也 evict。 */
|
|
174755
|
+
judgeSession(dispatchId) {
|
|
174756
|
+
if (this.sessionClaimCheckers.length === 0) return;
|
|
174757
|
+
const verdicts = this.sessionClaimCheckers.map((f2) => {
|
|
174758
|
+
try {
|
|
174759
|
+
return f2(dispatchId);
|
|
174760
|
+
} catch {
|
|
174761
|
+
return "unknown";
|
|
174762
|
+
}
|
|
174763
|
+
});
|
|
174764
|
+
if (verdicts.includes("live")) return;
|
|
174765
|
+
this.evictSession(dispatchId, verdicts.includes("settled") ? "reaped" : "unknown");
|
|
174766
|
+
}
|
|
174398
174767
|
/** 会话连接建立(`session_hello` 通过校验)时触发——2b 的收割判据挂在这上面。 */
|
|
174399
174768
|
addSessionConnectListener(l) {
|
|
174400
174769
|
this.sessionConnectListeners.push(l);
|
|
@@ -174931,13 +175300,13 @@ function variableKeyFromEnv(env = process.env) {
|
|
|
174931
175300
|
return buf;
|
|
174932
175301
|
}
|
|
174933
175302
|
if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
|
|
174934
|
-
return (0,
|
|
175303
|
+
return (0, import_node_crypto28.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
|
|
174935
175304
|
}
|
|
174936
|
-
var
|
|
175305
|
+
var import_node_crypto28, REDACTED_MARKER, ActorsService, mask, changedKeys;
|
|
174937
175306
|
var init_service4 = __esm({
|
|
174938
175307
|
"../server/src/domains/actors/service.ts"() {
|
|
174939
175308
|
"use strict";
|
|
174940
|
-
|
|
175309
|
+
import_node_crypto28 = require("node:crypto");
|
|
174941
175310
|
init_skill_fetcher();
|
|
174942
175311
|
init_identity();
|
|
174943
175312
|
init_migrate();
|
|
@@ -175425,7 +175794,7 @@ ${input.description}
|
|
|
175425
175794
|
*/
|
|
175426
175795
|
buildSkillFile(skillId, path37, content, now) {
|
|
175427
175796
|
const bytes = new TextEncoder().encode(content);
|
|
175428
|
-
const blobHash = (0,
|
|
175797
|
+
const blobHash = (0, import_node_crypto28.createHash)("sha256").update(bytes).digest("hex");
|
|
175429
175798
|
return { skillId, path: path37, content, blobHash, size: bytes.length, updatedAt: now };
|
|
175430
175799
|
}
|
|
175431
175800
|
/**
|
|
@@ -175896,15 +176265,15 @@ ${input.description}
|
|
|
175896
176265
|
return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
|
|
175897
176266
|
}
|
|
175898
176267
|
encrypt(plain) {
|
|
175899
|
-
const iv = (0,
|
|
175900
|
-
const cipher = (0,
|
|
176268
|
+
const iv = (0, import_node_crypto28.randomBytes)(12);
|
|
176269
|
+
const cipher = (0, import_node_crypto28.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
|
|
175901
176270
|
const enc4 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
|
|
175902
176271
|
return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc4.toString("base64")].join(".");
|
|
175903
176272
|
}
|
|
175904
176273
|
decrypt(packed) {
|
|
175905
176274
|
const [iv, tag, data] = packed.split(".");
|
|
175906
176275
|
if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
|
|
175907
|
-
const decipher = (0,
|
|
176276
|
+
const decipher = (0, import_node_crypto28.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
|
|
175908
176277
|
decipher.setAuthTag(Buffer.from(tag, "base64"));
|
|
175909
176278
|
return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
|
|
175910
176279
|
}
|
|
@@ -176868,7 +177237,7 @@ async function listSkillMetadataForActor(args) {
|
|
|
176868
177237
|
usedDirs.add(dir);
|
|
176869
177238
|
const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
|
|
176870
177239
|
let latest = "1970-01-01T00:00:00.000Z";
|
|
176871
|
-
const hasher = (0,
|
|
177240
|
+
const hasher = (0, import_node_crypto29.createHash)("sha256");
|
|
176872
177241
|
for (const m2 of sorted) {
|
|
176873
177242
|
if (m2.updatedAt > latest) latest = m2.updatedAt;
|
|
176874
177243
|
hasher.update(m2.path);
|
|
@@ -176893,11 +177262,11 @@ async function listSkillMetadataForActor(args) {
|
|
|
176893
177262
|
const dir = sanitizeSkillDir(c.name, c.slug);
|
|
176894
177263
|
if (usedDirs.has(dir)) continue;
|
|
176895
177264
|
usedDirs.add(dir);
|
|
176896
|
-
const hasher = (0,
|
|
177265
|
+
const hasher = (0, import_node_crypto29.createHash)("sha256");
|
|
176897
177266
|
for (const p2 of paths) {
|
|
176898
177267
|
hasher.update(p2);
|
|
176899
177268
|
hasher.update("\0");
|
|
176900
|
-
hasher.update((0,
|
|
177269
|
+
hasher.update((0, import_node_crypto29.createHash)("sha256").update(c.files[p2]).digest("hex"));
|
|
176901
177270
|
hasher.update("\0");
|
|
176902
177271
|
}
|
|
176903
177272
|
out.push({
|
|
@@ -176918,11 +177287,11 @@ async function listSkillMetadataForActor(args) {
|
|
|
176918
177287
|
const dir = sanitizeSkillDir(b2.name, b2.id);
|
|
176919
177288
|
if (usedDirs.has(dir)) continue;
|
|
176920
177289
|
usedDirs.add(dir);
|
|
176921
|
-
const hasher = (0,
|
|
177290
|
+
const hasher = (0, import_node_crypto29.createHash)("sha256");
|
|
176922
177291
|
for (const p2 of paths) {
|
|
176923
177292
|
hasher.update(p2);
|
|
176924
177293
|
hasher.update("\0");
|
|
176925
|
-
hasher.update((0,
|
|
177294
|
+
hasher.update((0, import_node_crypto29.createHash)("sha256").update(b2.files[p2]).digest("hex"));
|
|
176926
177295
|
hasher.update("\0");
|
|
176927
177296
|
}
|
|
176928
177297
|
out.push({
|
|
@@ -176942,7 +177311,7 @@ function builtinSkillToFiles(b2) {
|
|
|
176942
177311
|
skillId: `builtin:${b2.id}`,
|
|
176943
177312
|
path: path37,
|
|
176944
177313
|
content,
|
|
176945
|
-
blobHash: (0,
|
|
177314
|
+
blobHash: (0, import_node_crypto29.createHash)("sha256").update(content).digest("hex"),
|
|
176946
177315
|
size: Buffer.byteLength(content, "utf8"),
|
|
176947
177316
|
updatedAt: "1970-01-01T00:00:00.000Z"
|
|
176948
177317
|
}));
|
|
@@ -176966,7 +177335,7 @@ function sanitizeSkillDir(name, fallbackId) {
|
|
|
176966
177335
|
if (byName) return byName;
|
|
176967
177336
|
const byId = fallbackId ? fold2(fallbackId) : "";
|
|
176968
177337
|
if (byId) return byId;
|
|
176969
|
-
return `skill-${(0,
|
|
177338
|
+
return `skill-${(0, import_node_crypto29.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
|
|
176970
177339
|
}
|
|
176971
177340
|
async function materializeSkillFiles(args) {
|
|
176972
177341
|
const skills = await args.service.listInstalledSkillsForActor(args.actorId);
|
|
@@ -176999,7 +177368,7 @@ function connectorSkillToFiles(c) {
|
|
|
176999
177368
|
skillId: c.id,
|
|
177000
177369
|
path: path37,
|
|
177001
177370
|
content,
|
|
177002
|
-
blobHash: (0,
|
|
177371
|
+
blobHash: (0, import_node_crypto29.createHash)("sha256").update(content).digest("hex"),
|
|
177003
177372
|
size: Buffer.byteLength(content, "utf8"),
|
|
177004
177373
|
updatedAt: "1970-01-01T00:00:00.000Z"
|
|
177005
177374
|
}));
|
|
@@ -177016,11 +177385,11 @@ function connectorSkillDisplayRows(connectorSkills, enabledConnectors) {
|
|
|
177016
177385
|
...s2.version !== void 0 ? { version: s2.version } : {}
|
|
177017
177386
|
}));
|
|
177018
177387
|
}
|
|
177019
|
-
var
|
|
177388
|
+
var import_node_crypto29;
|
|
177020
177389
|
var init_skill_materializer = __esm({
|
|
177021
177390
|
"../server/src/domains/actors/skill-materializer.ts"() {
|
|
177022
177391
|
"use strict";
|
|
177023
|
-
|
|
177392
|
+
import_node_crypto29 = require("node:crypto");
|
|
177024
177393
|
}
|
|
177025
177394
|
});
|
|
177026
177395
|
|
|
@@ -178248,7 +178617,7 @@ function createActorsDomain(opts) {
|
|
|
178248
178617
|
...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
|
|
178249
178618
|
audit: async (entry) => {
|
|
178250
178619
|
await opts.audit?.({
|
|
178251
|
-
id: `reg_${(0,
|
|
178620
|
+
id: `reg_${(0, import_node_crypto30.randomUUID)()}`,
|
|
178252
178621
|
actor: entry.by,
|
|
178253
178622
|
kind: "registry_change",
|
|
178254
178623
|
target: entry.actorId,
|
|
@@ -178290,11 +178659,11 @@ function createActorsDomain(opts) {
|
|
|
178290
178659
|
register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {} })
|
|
178291
178660
|
};
|
|
178292
178661
|
}
|
|
178293
|
-
var
|
|
178662
|
+
var import_node_crypto30;
|
|
178294
178663
|
var init_actors = __esm({
|
|
178295
178664
|
"../server/src/domains/actors/index.ts"() {
|
|
178296
178665
|
"use strict";
|
|
178297
|
-
|
|
178666
|
+
import_node_crypto30 = require("node:crypto");
|
|
178298
178667
|
init_service4();
|
|
178299
178668
|
init_routes2();
|
|
178300
178669
|
init_service4();
|
|
@@ -179402,11 +179771,11 @@ var init_projects = __esm({
|
|
|
179402
179771
|
});
|
|
179403
179772
|
|
|
179404
179773
|
// ../server/src/domains/companies/service.ts
|
|
179405
|
-
var
|
|
179774
|
+
var import_node_crypto31, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, SLUG_RE, CompanyError, CompaniesService;
|
|
179406
179775
|
var init_service5 = __esm({
|
|
179407
179776
|
"../server/src/domains/companies/service.ts"() {
|
|
179408
179777
|
"use strict";
|
|
179409
|
-
|
|
179778
|
+
import_node_crypto31 = require("node:crypto");
|
|
179410
179779
|
ROLES = ["owner", "admin", "member"];
|
|
179411
179780
|
INVITABLE_ROLES = ["admin", "member"];
|
|
179412
179781
|
INVITATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -179479,7 +179848,7 @@ var init_service5 = __esm({
|
|
|
179479
179848
|
const existing = await this.store.getAccountByEmail(normalized4);
|
|
179480
179849
|
if (existing) return existing;
|
|
179481
179850
|
const account = {
|
|
179482
|
-
id: `actor:human:${(0,
|
|
179851
|
+
id: `actor:human:${(0, import_node_crypto31.randomUUID)()}`,
|
|
179483
179852
|
email: normalized4,
|
|
179484
179853
|
name: name ?? normalized4,
|
|
179485
179854
|
status: "active"
|
|
@@ -179643,7 +180012,7 @@ var init_service5 = __esm({
|
|
|
179643
180012
|
}
|
|
179644
180013
|
const nowMs = Date.parse(this.now());
|
|
179645
180014
|
const invitation = {
|
|
179646
|
-
id: `invitation:${(0,
|
|
180015
|
+
id: `invitation:${(0, import_node_crypto31.randomUUID)()}`,
|
|
179647
180016
|
companyId,
|
|
179648
180017
|
email: mail,
|
|
179649
180018
|
role,
|
|
@@ -180914,7 +181283,7 @@ function nodesDomain(deps) {
|
|
|
180914
181283
|
nodeId = incomingNodeId;
|
|
180915
181284
|
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
180916
181285
|
} else {
|
|
180917
|
-
nodeId = `node-${(0,
|
|
181286
|
+
nodeId = `node-${(0, import_node_crypto32.randomUUID)().slice(0, 8)}`;
|
|
180918
181287
|
deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
|
|
180919
181288
|
}
|
|
180920
181289
|
const existingNode = await deps.nodeStore.getNode(nodeId);
|
|
@@ -181050,11 +181419,11 @@ function nodesDomain(deps) {
|
|
|
181050
181419
|
});
|
|
181051
181420
|
};
|
|
181052
181421
|
}
|
|
181053
|
-
var
|
|
181422
|
+
var import_node_crypto32, import_node_fs14, import_node_url6, import_node_path18;
|
|
181054
181423
|
var init_routes6 = __esm({
|
|
181055
181424
|
"../server/src/domains/nodes/routes.ts"() {
|
|
181056
181425
|
"use strict";
|
|
181057
|
-
|
|
181426
|
+
import_node_crypto32 = require("node:crypto");
|
|
181058
181427
|
import_node_fs14 = require("node:fs");
|
|
181059
181428
|
import_node_url6 = require("node:url");
|
|
181060
181429
|
import_node_path18 = require("node:path");
|
|
@@ -182670,13 +183039,13 @@ function nameKey(name) {
|
|
|
182670
183039
|
function cleanName(name) {
|
|
182671
183040
|
return name.trim().replace(/\s+/g, " ");
|
|
182672
183041
|
}
|
|
182673
|
-
var import_node_fs15, import_node_path19,
|
|
183042
|
+
var import_node_fs15, import_node_path19, import_node_crypto33, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
|
|
182674
183043
|
var init_tags = __esm({
|
|
182675
183044
|
"../server/src/domains/collab/tags.ts"() {
|
|
182676
183045
|
"use strict";
|
|
182677
183046
|
import_node_fs15 = __toESM(require("node:fs"), 1);
|
|
182678
183047
|
import_node_path19 = __toESM(require("node:path"), 1);
|
|
182679
|
-
|
|
183048
|
+
import_node_crypto33 = __toESM(require("node:crypto"), 1);
|
|
182680
183049
|
SEP = "::";
|
|
182681
183050
|
keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
|
|
182682
183051
|
prefixOf = (companyId) => `${companyId}${SEP}`;
|
|
@@ -182694,7 +183063,7 @@ var init_tags = __esm({
|
|
|
182694
183063
|
if (!name) return null;
|
|
182695
183064
|
if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
|
|
182696
183065
|
const tag = {
|
|
182697
|
-
id: `tag-${
|
|
183066
|
+
id: `tag-${import_node_crypto33.default.randomBytes(6).toString("hex")}`,
|
|
182698
183067
|
name,
|
|
182699
183068
|
...input.color?.trim() ? { color: input.color.trim() } : {},
|
|
182700
183069
|
...input.description?.trim() ? { description: input.description.trim() } : {}
|
|
@@ -183551,7 +183920,7 @@ function createBundle(root, target, input) {
|
|
|
183551
183920
|
}
|
|
183552
183921
|
}
|
|
183553
183922
|
const bytes = fs23.statSync(absBundle).size;
|
|
183554
|
-
const digest = (0,
|
|
183923
|
+
const digest = (0, import_node_crypto34.createHash)("sha256").update(fs23.readFileSync(absBundle)).digest("hex");
|
|
183555
183924
|
const relBase = `inputs/git-bundles/${safeRepo}-${commit.slice(0, 12)}`;
|
|
183556
183925
|
return {
|
|
183557
183926
|
absoluteBundlePath: absBundle,
|
|
@@ -183647,12 +184016,12 @@ function classifyGitError(fallback, err, where) {
|
|
|
183647
184016
|
function oneLine(msg) {
|
|
183648
184017
|
return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
|
|
183649
184018
|
}
|
|
183650
|
-
var import_node_child_process14,
|
|
184019
|
+
var import_node_child_process14, import_node_crypto34, fs23, os6, path21, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
|
|
183651
184020
|
var init_readonly_bundle = __esm({
|
|
183652
184021
|
"../server/src/git/readonly-bundle.ts"() {
|
|
183653
184022
|
"use strict";
|
|
183654
184023
|
import_node_child_process14 = require("node:child_process");
|
|
183655
|
-
|
|
184024
|
+
import_node_crypto34 = require("node:crypto");
|
|
183656
184025
|
fs23 = __toESM(require("node:fs"), 1);
|
|
183657
184026
|
os6 = __toESM(require("node:os"), 1);
|
|
183658
184027
|
path21 = __toESM(require("node:path"), 1);
|
|
@@ -183672,7 +184041,7 @@ var init_readonly_bundle = __esm({
|
|
|
183672
184041
|
|
|
183673
184042
|
// ../server/src/domains/collab/create-seeded-workorder.ts
|
|
183674
184043
|
function makeSeededWorkorderCreator(deps) {
|
|
183675
|
-
const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0,
|
|
184044
|
+
const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto35.randomUUID)().slice(0, 8)}`);
|
|
183676
184045
|
return async (input) => {
|
|
183677
184046
|
const workspace = input.idempotencyKey ? workspaceForKey(input.idempotencyKey) : genWorkspace();
|
|
183678
184047
|
if (input.idempotencyKey) {
|
|
@@ -183758,7 +184127,7 @@ function makeSeededWorkorderCreator(deps) {
|
|
|
183758
184127
|
};
|
|
183759
184128
|
}
|
|
183760
184129
|
function workspaceForKey(key) {
|
|
183761
|
-
return `ws:wo-${(0,
|
|
184130
|
+
return `ws:wo-${(0, import_node_crypto35.createHash)("sha256").update(key).digest("hex").slice(0, 8)}`;
|
|
183762
184131
|
}
|
|
183763
184132
|
function existingWorkorder(kernel, workspace) {
|
|
183764
184133
|
const arts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === workspace);
|
|
@@ -183771,11 +184140,11 @@ function existingWorkorder(kernel, workspace) {
|
|
|
183771
184140
|
spawned: arts.map((a) => ({ id: a.id, type: a.type, owner: a.owner }))
|
|
183772
184141
|
};
|
|
183773
184142
|
}
|
|
183774
|
-
var
|
|
184143
|
+
var import_node_crypto35, enc2;
|
|
183775
184144
|
var init_create_seeded_workorder = __esm({
|
|
183776
184145
|
"../server/src/domains/collab/create-seeded-workorder.ts"() {
|
|
183777
184146
|
"use strict";
|
|
183778
|
-
|
|
184147
|
+
import_node_crypto35 = require("node:crypto");
|
|
183779
184148
|
init_ephemeral_project();
|
|
183780
184149
|
init_planner();
|
|
183781
184150
|
enc2 = (s2) => new TextEncoder().encode(s2);
|
|
@@ -184966,12 +185335,12 @@ function todoAction(node) {
|
|
|
184966
185335
|
if (node.progress.recoverability === "partial") return `\u8282\u70B9 ${node.nodeState}\uFF0Ccheckpoint \u90E8\u5206\u53EF\u6062\u590D\uFF0C\u9700\u5173\u6CE8\u5DF2\u6709\u4EA7\u51FA`;
|
|
184967
185336
|
return `\u8282\u70B9 ${node.nodeState}\uFF0Ccheckpoint \u4E0D\u53EF\u6062\u590D`;
|
|
184968
185337
|
}
|
|
184969
|
-
var
|
|
185338
|
+
var import_node_crypto36, DONE_STATES, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, TODO_RANK, ExecutionContinuityService;
|
|
184970
185339
|
var init_service7 = __esm({
|
|
184971
185340
|
"../server/src/domains/execution-continuity/service.ts"() {
|
|
184972
185341
|
"use strict";
|
|
184973
185342
|
init_src2();
|
|
184974
|
-
|
|
185343
|
+
import_node_crypto36 = require("node:crypto");
|
|
184975
185344
|
init_src5();
|
|
184976
185345
|
DONE_STATES = /* @__PURE__ */ new Set(["succeeded", "no_change"]);
|
|
184977
185346
|
BLOCKING_STATES = /* @__PURE__ */ new Set(["stalled", "failed", "blocked"]);
|
|
@@ -185201,7 +185570,7 @@ var init_service7 = __esm({
|
|
|
185201
185570
|
if (typeof expectedHash !== "string" || !this.readBundleFile) return null;
|
|
185202
185571
|
const injectedResume = this.readBundleFile(attempt.id, "execution/RESUME.md");
|
|
185203
185572
|
if (!injectedResume) return null;
|
|
185204
|
-
const actualHash = (0,
|
|
185573
|
+
const actualHash = (0, import_node_crypto36.createHash)("sha256").update(injectedResume).digest("hex");
|
|
185205
185574
|
if (actualHash !== expectedHash) return null;
|
|
185206
185575
|
const recoverySnapshot = parseResumePackSnapshot(injectedResume);
|
|
185207
185576
|
if (!recoverySnapshot || recoverySnapshot.scenario !== "redispatch" || !recoverySnapshot.previousAttempt || recoverySnapshot.node.nodeId !== attempt.nodeId || recoverySnapshot.jobKey !== attempt.jobKey || recoverySnapshot.part !== attempt.part) return null;
|
|
@@ -185702,7 +186071,7 @@ function createChatSessionsDomain(opts) {
|
|
|
185702
186071
|
if (!text.trim()) throw new ApiError(400, "BAD_REQUEST", "text required");
|
|
185703
186072
|
const now = Date.now();
|
|
185704
186073
|
sweepDrafts(now);
|
|
185705
|
-
const id = `cd_${(0,
|
|
186074
|
+
const id = `cd_${(0, import_node_crypto37.randomUUID)()}`;
|
|
185706
186075
|
drafts.set(id, { text, actor: req.auth.actor, expiresAt: now + DRAFT_TTL_MS });
|
|
185707
186076
|
return { status: 201, body: { id, expiresInMs: DRAFT_TTL_MS } };
|
|
185708
186077
|
});
|
|
@@ -185735,7 +186104,7 @@ function createChatSessionsDomain(opts) {
|
|
|
185735
186104
|
const runtimeId = await currentRuntimeId(body.aiActorId) ?? "unknown";
|
|
185736
186105
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
185737
186106
|
const session = {
|
|
185738
|
-
id: (0,
|
|
186107
|
+
id: (0, import_node_crypto37.randomUUID)(),
|
|
185739
186108
|
humanActorId: req.auth.actor,
|
|
185740
186109
|
aiActorId: body.aiActorId,
|
|
185741
186110
|
runtimeId,
|
|
@@ -185808,7 +186177,7 @@ function createChatSessionsDomain(opts) {
|
|
|
185808
186177
|
if (body.role !== "user" && body.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
|
|
185809
186178
|
const role = body.role;
|
|
185810
186179
|
const msg = await store.appendMessage({
|
|
185811
|
-
id: (0,
|
|
186180
|
+
id: (0, import_node_crypto37.randomUUID)(),
|
|
185812
186181
|
sessionId: req.params.id,
|
|
185813
186182
|
role,
|
|
185814
186183
|
content: role === "user" ? stripInjectedChatContext(body.content) : body.content,
|
|
@@ -185818,6 +186187,47 @@ function createChatSessionsDomain(opts) {
|
|
|
185818
186187
|
await store.updateSession(req.params.id, { touchedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
185819
186188
|
return { status: 201, body: msg };
|
|
185820
186189
|
});
|
|
186190
|
+
router.get("/api/chat-sessions/:id/turns/active", async (req) => {
|
|
186191
|
+
if (!opts.chatTurns) throw new ApiError(404, "NOT_FOUND", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C");
|
|
186192
|
+
const session = await store.getSession(req.params.id);
|
|
186193
|
+
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
186194
|
+
const turn = await opts.chatTurns.activeTurn(req.params.id);
|
|
186195
|
+
return { status: 200, body: { turn } };
|
|
186196
|
+
});
|
|
186197
|
+
router.post("/api/chat-sessions/:id/turns/:turnId/settle", async (req) => {
|
|
186198
|
+
if (!opts.chatTurns) throw new ApiError(404, "NOT_FOUND", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C");
|
|
186199
|
+
const session = await store.getSession(req.params.id);
|
|
186200
|
+
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
186201
|
+
const turn = await opts.chatTurns.getTurn(req.params.turnId);
|
|
186202
|
+
if (!turn || turn.chatSessionId !== req.params.id) throw new ApiError(404, "NOT_FOUND", "turn not found");
|
|
186203
|
+
const body = req.body ?? {};
|
|
186204
|
+
if (turn.status !== "reserved" && turn.status !== "running") {
|
|
186205
|
+
return { status: 200, body: { settled: false, reason: "already_terminal", turn } };
|
|
186206
|
+
}
|
|
186207
|
+
if (body.force) {
|
|
186208
|
+
await opts.chatTurns.settleTurn(turn.id, {
|
|
186209
|
+
status: "cancelled",
|
|
186210
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
186211
|
+
lastError: `\u8FD0\u7EF4\u5F3A\u5236\u7F6E\u7EC8\u6001\uFF08${req.auth.actor}\uFF09\uFF1A${body.reason ?? "\u672A\u586B\u539F\u56E0"}`
|
|
186212
|
+
});
|
|
186213
|
+
return { status: 200, body: { settled: true, status: "cancelled", turn: await opts.chatTurns.getTurn(turn.id) } };
|
|
186214
|
+
}
|
|
186215
|
+
const outcome = await reconcileChatTurn(turn, {
|
|
186216
|
+
turns: opts.chatTurns,
|
|
186217
|
+
getRun: opts.getRun ?? (async () => null),
|
|
186218
|
+
now: Date.now(),
|
|
186219
|
+
// 运维入口不吃「宽限」——人是在明确要求现在就核实一次。
|
|
186220
|
+
noRunGraceMs: 0
|
|
186221
|
+
});
|
|
186222
|
+
if (!outcome.settled) {
|
|
186223
|
+
throw new ApiError(
|
|
186224
|
+
409,
|
|
186225
|
+
"CHAT_TURN_STILL_RUNNING",
|
|
186226
|
+
`\u8FD9\u4E00\u8F6E\u5728\u8D26\u672C\u91CC\u4ECD\u662F\u6D3B\u7684\uFF08${outcome.reason}\uFF09\uFF0C\u4E0D\u80FD\u7F6E\u7EC8\u6001\u3002\u786E\u8BA4\u6267\u884C\u4EBA\u5DF2\u7ECF\u6B7B\u4E86\u518D\u52A0 force=true\u3002`
|
|
186227
|
+
);
|
|
186228
|
+
}
|
|
186229
|
+
return { status: 200, body: { settled: true, status: outcome.status, reason: outcome.reason, turn: await opts.chatTurns.getTurn(turn.id) } };
|
|
186230
|
+
});
|
|
185821
186231
|
router.post("/api/chat-sessions/:id/work-orders", async (req) => {
|
|
185822
186232
|
const session = await store.getSession(req.params.id);
|
|
185823
186233
|
if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
|
|
@@ -185913,16 +186323,17 @@ function createChatSessionsDomain(opts) {
|
|
|
185913
186323
|
});
|
|
185914
186324
|
};
|
|
185915
186325
|
}
|
|
185916
|
-
var
|
|
186326
|
+
var import_node_crypto37, WORKDIR_READ_MAX_BYTES;
|
|
185917
186327
|
var init_chat_sessions = __esm({
|
|
185918
186328
|
"../server/src/domains/chat-sessions/index.ts"() {
|
|
185919
186329
|
"use strict";
|
|
185920
|
-
|
|
186330
|
+
import_node_crypto37 = require("node:crypto");
|
|
185921
186331
|
init_chat_session();
|
|
185922
186332
|
init_router();
|
|
185923
186333
|
init_workorders();
|
|
185924
186334
|
init_chat_parts();
|
|
185925
186335
|
init_injected_context();
|
|
186336
|
+
init_chat_turn_gate();
|
|
185926
186337
|
init_project_bundle();
|
|
185927
186338
|
WORKDIR_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
185928
186339
|
}
|
|
@@ -186451,7 +186862,9 @@ var init_registry3 = __esm({
|
|
|
186451
186862
|
|
|
186452
186863
|
// ../server/src/domains/automations/attribution.ts
|
|
186453
186864
|
function automationSubstantiveChange(before, after) {
|
|
186454
|
-
return before.executionMode !== after.executionMode || (before.agentId ?? null) !== (after.agentId ?? null) || (before.playbookRef ?? null) !== (after.playbookRef ?? null) || !jsonEq(before.payload, after.payload) || (before.titleTemplate ?? null) !== (after.titleTemplate ?? null) || before.dispatch !== after.dispatch || before.enabled !== after.enabled
|
|
186865
|
+
return before.executionMode !== after.executionMode || (before.agentId ?? null) !== (after.agentId ?? null) || (before.playbookRef ?? null) !== (after.playbookRef ?? null) || !jsonEq(before.payload, after.payload) || (before.titleTemplate ?? null) !== (after.titleTemplate ?? null) || before.dispatch !== after.dispatch || before.enabled !== after.enabled || // 目标会话策略(ADR 会话投递 D1):改「发到哪儿」= 改怎么跑,问责随之转移。
|
|
186866
|
+
// 归一后比较,`null` 与 `{mode:"new_session"}` 是同一件事,不该被判成变更。
|
|
186867
|
+
!jsonEq(chatTargetKey(before), chatTargetKey(after)) || (before.ownerHumanActorId ?? null) !== (after.ownerHumanActorId ?? null);
|
|
186455
186868
|
}
|
|
186456
186869
|
function triggerSubstantiveChange(before, after) {
|
|
186457
186870
|
if (before.enabled !== after.enabled) return true;
|
|
@@ -186470,13 +186883,20 @@ function configSummaryOf(a) {
|
|
|
186470
186883
|
payload: a.payload ?? null,
|
|
186471
186884
|
titleTemplate: a.titleTemplate ?? null,
|
|
186472
186885
|
dispatch: a.dispatch,
|
|
186473
|
-
enabled: a.enabled
|
|
186886
|
+
enabled: a.enabled,
|
|
186887
|
+
chatTarget: chatTargetKey(a),
|
|
186888
|
+
ownerHumanActorId: a.ownerHumanActorId ?? null
|
|
186474
186889
|
};
|
|
186475
186890
|
}
|
|
186891
|
+
function chatTargetKey(a) {
|
|
186892
|
+
const t = normalizeChatTarget(a.chatTarget);
|
|
186893
|
+
return t.mode === "existing_session" ? { mode: t.mode, sessionId: t.sessionId } : { mode: "new_session" };
|
|
186894
|
+
}
|
|
186476
186895
|
var jsonEq;
|
|
186477
186896
|
var init_attribution = __esm({
|
|
186478
186897
|
"../server/src/domains/automations/attribution.ts"() {
|
|
186479
186898
|
"use strict";
|
|
186899
|
+
init_src2();
|
|
186480
186900
|
jsonEq = (a, b2) => JSON.stringify(a ?? null) === JSON.stringify(b2 ?? null);
|
|
186481
186901
|
}
|
|
186482
186902
|
});
|
|
@@ -187292,8 +187712,11 @@ var init_scheduler = __esm({
|
|
|
187292
187712
|
});
|
|
187293
187713
|
|
|
187294
187714
|
// ../server/src/domains/automations/service.ts
|
|
187715
|
+
function busyError(chatSessionId, blockedBy) {
|
|
187716
|
+
return `\u76EE\u6807\u4F1A\u8BDD ${chatSessionId} \u6B63\u6709\u4E00\u8F6E\u5728\u8DD1${blockedBy ? `\uFF08${blockedBy}\uFF09` : ""}\u2014\u2014\u672C\u6B21\u89E6\u53D1\u5DF2\u4E22\u5F03\uFF0C\u4E0D\u6392\u961F\u3001\u4E0D\u8865\u8DD1`;
|
|
187717
|
+
}
|
|
187295
187718
|
function genWebhookToken() {
|
|
187296
|
-
return `owt_${(0,
|
|
187719
|
+
return `owt_${(0, import_node_crypto38.randomBytes)(32).toString("base64url")}`;
|
|
187297
187720
|
}
|
|
187298
187721
|
function validateTitleTemplate(tpl) {
|
|
187299
187722
|
const residue = tpl.replace(DATE_TOKEN, "");
|
|
@@ -187349,11 +187772,12 @@ function appendSourceNote(brief, automation, source, atIso) {
|
|
|
187349
187772
|
---
|
|
187350
187773
|
\u672C\u5DE5\u5355\u7531\u81EA\u52A8\u5316\u300C${automation.name}\u300D\u4E8E ${atIso} \u89E6\u53D1\uFF08\u6765\u6E90\uFF1A${source}\uFF09\u3002\u5F00\u5DE5\u540E\u8BF7\u628A\u5DE5\u5355\u6807\u9898\u6539\u6210\u51C6\u786E\u53CD\u6620\u5B9E\u9645\u5DE5\u4F5C\u7684\u63CF\u8FF0\u3002`;
|
|
187351
187774
|
}
|
|
187352
|
-
var
|
|
187775
|
+
var import_node_crypto38, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
|
|
187353
187776
|
var init_service8 = __esm({
|
|
187354
187777
|
"../server/src/domains/automations/service.ts"() {
|
|
187355
187778
|
"use strict";
|
|
187356
|
-
|
|
187779
|
+
import_node_crypto38 = require("node:crypto");
|
|
187780
|
+
init_src2();
|
|
187357
187781
|
init_registry3();
|
|
187358
187782
|
init_attribution();
|
|
187359
187783
|
init_scheduler();
|
|
@@ -187361,12 +187785,14 @@ var init_service8 = __esm({
|
|
|
187361
187785
|
AutomationsService = class {
|
|
187362
187786
|
store;
|
|
187363
187787
|
deps;
|
|
187788
|
+
targets;
|
|
187364
187789
|
genId;
|
|
187365
187790
|
now;
|
|
187366
187791
|
log;
|
|
187367
187792
|
constructor(opts) {
|
|
187368
187793
|
this.store = opts.store;
|
|
187369
187794
|
this.deps = opts.fire;
|
|
187795
|
+
if (opts.chatTargets) this.targets = opts.chatTargets;
|
|
187370
187796
|
this.genId = opts.genId ?? ((p2) => `${p2}_${Math.random().toString(36).slice(2, 12)}`);
|
|
187371
187797
|
this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
187372
187798
|
if (opts.log) this.log = opts.log;
|
|
@@ -187409,6 +187835,8 @@ var init_service8 = __esm({
|
|
|
187409
187835
|
if (input.enabled !== before.enabled) patch.pauseReason = null;
|
|
187410
187836
|
}
|
|
187411
187837
|
if (input.subscribers !== void 0) patch.subscribers = input.subscribers;
|
|
187838
|
+
if (input.chatTarget !== void 0) patch.chatTarget = input.chatTarget;
|
|
187839
|
+
if (input.ownerHumanActorId !== void 0) patch.ownerHumanActorId = input.ownerHumanActorId;
|
|
187412
187840
|
const updated = await this.store.updateAutomation(id, patch);
|
|
187413
187841
|
let substantive = automationSubstantiveChange(before, updated);
|
|
187414
187842
|
if (input.triggers !== void 0) {
|
|
@@ -187505,6 +187933,83 @@ var init_service8 = __esm({
|
|
|
187505
187933
|
createdAt: this.now().toISOString()
|
|
187506
187934
|
});
|
|
187507
187935
|
}
|
|
187936
|
+
/* ---------- 固定目标会话(ADR 会话投递 D1/D3/D8) ---------- */
|
|
187937
|
+
/** 固定会话功能是否可用(接缝 + 轮次账本都在才算)。前端读能力位,不自己按部署形态猜。 */
|
|
187938
|
+
chatTargetSupported() {
|
|
187939
|
+
return Boolean(this.targets && this.deps.chatTurns);
|
|
187940
|
+
}
|
|
187941
|
+
/** 鉴权 actor → 人类归属者(D3)。 */
|
|
187942
|
+
resolveOwnerHuman(actor) {
|
|
187943
|
+
return this.targets ? this.targets.resolveOwnerHuman(actor) : Promise.resolve(null);
|
|
187944
|
+
}
|
|
187945
|
+
/**
|
|
187946
|
+
* 保存期校验(D3):目标必须当场可投递。返回 null = 通过,否则给出具体 issue。
|
|
187947
|
+
* **不做任何自动修正**——不改绑会话的 AI 身份、不替换归属人、不回退成新会话。
|
|
187948
|
+
*/
|
|
187949
|
+
async validateChatTarget(a, sessionId) {
|
|
187950
|
+
if (!this.targets) return "session_missing";
|
|
187951
|
+
const r = await this.targets.inspect({
|
|
187952
|
+
companyId: a.companyId,
|
|
187953
|
+
agentId: a.agentId,
|
|
187954
|
+
ownerHumanActorId: a.ownerHumanActorId,
|
|
187955
|
+
sessionId
|
|
187956
|
+
});
|
|
187957
|
+
return r.ok ? null : r.issue;
|
|
187958
|
+
}
|
|
187959
|
+
/** 详情/列表用的目标只读投影(含成本观测)。非固定会话返回 null。 */
|
|
187960
|
+
async chatTargetView(a) {
|
|
187961
|
+
const target = normalizeChatTarget(a.chatTarget);
|
|
187962
|
+
if (target.mode !== "existing_session") return null;
|
|
187963
|
+
if (!this.targets) {
|
|
187964
|
+
return { sessionId: target.sessionId, available: false, issue: "session_missing", title: null, touchedAt: null, messageCount: null };
|
|
187965
|
+
}
|
|
187966
|
+
const r = await this.targets.inspect({
|
|
187967
|
+
companyId: a.companyId,
|
|
187968
|
+
agentId: a.agentId,
|
|
187969
|
+
ownerHumanActorId: a.ownerHumanActorId,
|
|
187970
|
+
sessionId: target.sessionId
|
|
187971
|
+
});
|
|
187972
|
+
if (r.ok) {
|
|
187973
|
+
return { sessionId: target.sessionId, available: true, title: r.title, touchedAt: r.touchedAt, messageCount: r.messageCount };
|
|
187974
|
+
}
|
|
187975
|
+
return {
|
|
187976
|
+
sessionId: target.sessionId,
|
|
187977
|
+
available: false,
|
|
187978
|
+
issue: r.issue,
|
|
187979
|
+
title: r.title ?? null,
|
|
187980
|
+
touchedAt: r.touchedAt ?? null,
|
|
187981
|
+
messageCount: null
|
|
187982
|
+
};
|
|
187983
|
+
}
|
|
187984
|
+
/** 选择器搜索(D8)。接缝缺省时给空集合而不是报错——前端展示「不可用」即可。 */
|
|
187985
|
+
async searchChatTargets(args) {
|
|
187986
|
+
if (!this.targets) return { items: [], hasMore: false };
|
|
187987
|
+
return this.targets.search(args);
|
|
187988
|
+
}
|
|
187989
|
+
/**
|
|
187990
|
+
* 「为这条自动化新建长期目标会话」的**服务端复合入口**(D1):建会话 + 回填 chatTarget。
|
|
187991
|
+
*
|
|
187992
|
+
* 之所以不让前端拼「建会话 → 更新自动化」:后者中途失败会留下一个没有来源、谁也不认领的
|
|
187993
|
+
* 孤儿会话。这里的补偿方向刻意选成「回填失败就把刚建的会话删掉」——宁可这次白建,
|
|
187994
|
+
* 也不让一个自动化指着半个配置。
|
|
187995
|
+
*/
|
|
187996
|
+
async createChatTargetSession(automation, editor) {
|
|
187997
|
+
if (!this.targets) throw new Error("\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u76EE\u6807\u63A5\u7F1D");
|
|
187998
|
+
if (automation.executionMode !== "chat" || !automation.agentId) throw new Error("\u53EA\u6709 chat \u6A21\u5F0F\u4E14\u5DF2\u8BBE\u6267\u884C\u4EBA\u7684\u81EA\u52A8\u5316\u624D\u80FD\u5EFA\u957F\u671F\u76EE\u6807\u4F1A\u8BDD");
|
|
187999
|
+
if (!automation.ownerHumanActorId) throw new Error("\u8FD9\u6761\u81EA\u52A8\u5316\u6CA1\u6709\u53EF\u7528\u7684\u4EBA\u7C7B\u5F52\u5C5E\u8005\uFF0C\u65E0\u6CD5\u5EFA\u957F\u671F\u76EE\u6807\u4F1A\u8BDD");
|
|
188000
|
+
const { sessionId } = await this.targets.createTargetSession({
|
|
188001
|
+
companyId: automation.companyId,
|
|
188002
|
+
agentId: automation.agentId,
|
|
188003
|
+
ownerHumanActorId: automation.ownerHumanActorId,
|
|
188004
|
+
title: `\u81EA\u52A8\u5316 \xB7 ${automation.name}`.slice(0, 80)
|
|
188005
|
+
});
|
|
188006
|
+
try {
|
|
188007
|
+
return await this.updateAutomation(automation.id, { chatTarget: { mode: "existing_session", sessionId } }, editor);
|
|
188008
|
+
} catch (e) {
|
|
188009
|
+
await this.targets.deleteSession(sessionId).catch(() => void 0);
|
|
188010
|
+
throw e;
|
|
188011
|
+
}
|
|
188012
|
+
}
|
|
187508
188013
|
/* ---------- firing(ADR 0156 D1/D3) ---------- */
|
|
187509
188014
|
/**
|
|
187510
188015
|
* 触发一次协作。链路:幂等落 run(pending) → 准入检查 → 真剧本解析 → SeededWorkorderCreator 建单
|
|
@@ -187546,7 +188051,7 @@ var init_service8 = __esm({
|
|
|
187546
188051
|
*/
|
|
187547
188052
|
async dispatchRun(automation, trigger, run, source, opts = {}) {
|
|
187548
188053
|
const skip = await this.admissionSkip(automation, source);
|
|
187549
|
-
if (skip) return this.settleSkip(run, skip.reasonCode, skip.error);
|
|
188054
|
+
if (skip) return this.settleSkip(run, skip.reasonCode, skip.error, skip.result);
|
|
187550
188055
|
if (automation.executionMode === "chat") {
|
|
187551
188056
|
return this.fireChat(automation, trigger, run, opts);
|
|
187552
188057
|
}
|
|
@@ -187689,7 +188194,10 @@ var init_service8 = __esm({
|
|
|
187689
188194
|
if (!automation.enabled) return { reasonCode: "automation_paused", error: "\u81EA\u52A8\u5316\u5DF2\u6682\u505C" };
|
|
187690
188195
|
if (automation.executionMode === "chat") {
|
|
187691
188196
|
if (!automation.agentId) return { reasonCode: "config_error", error: "chat \u6A21\u5F0F\u7F3A\u5C11\u6267\u884C\u4EBA\uFF08agentId\uFF09" };
|
|
187692
|
-
if (!this.deps.
|
|
188197
|
+
if (!this.deps.chat) return { reasonCode: "config_error", error: "\u672C\u5B9E\u4F8B\u672A\u63A5 chat \u6267\u884C" };
|
|
188198
|
+
if (!this.deps.chatTurns) {
|
|
188199
|
+
return { reasonCode: "config_error", error: "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C\uFF08\u65E0\u6CD5\u5224\u5B9A\u76EE\u6807\u4F1A\u8BDD\u662F\u5426\u5728\u8DD1\uFF09" };
|
|
188200
|
+
}
|
|
187693
188201
|
if (source === "event" && !this.deps.chatSupportsIdempotency) {
|
|
187694
188202
|
return { reasonCode: "config_error", error: "\u5BF9\u8BDD\u6267\u884C\u6682\u4E0D\u652F\u6301\u8BA2\u9605\u89E6\u53D1\uFF08chat \u9002\u914D\u5668\u7F3A\u6301\u4E45\u5E42\u7B49\u952E\uFF09" };
|
|
187695
188203
|
}
|
|
@@ -187706,29 +188214,67 @@ var init_service8 = __esm({
|
|
|
187706
188214
|
return { reasonCode: "config_error", error: `\u5267\u672C ${automation.playbookRef} \u4E0D\u652F\u6301\u5EFA\u5355` };
|
|
187707
188215
|
}
|
|
187708
188216
|
if (automation.projectId && this.deps.projectExists && !await this.deps.projectExists(automation.projectId)) {
|
|
187709
|
-
return {
|
|
188217
|
+
return {
|
|
188218
|
+
reasonCode: "target_unavailable",
|
|
188219
|
+
error: `\u76EE\u6807\u9879\u76EE\u4E0D\u5B58\u5728\uFF1A${automation.projectId}`,
|
|
188220
|
+
result: { targetKind: "project", projectId: automation.projectId }
|
|
188221
|
+
};
|
|
187710
188222
|
}
|
|
187711
188223
|
return null;
|
|
187712
188224
|
}
|
|
187713
188225
|
/**
|
|
187714
|
-
* chat 模式执行(ADR 0159
|
|
187715
|
-
*
|
|
188226
|
+
* chat 模式执行(ADR 0159 + 会话投递 D2)。三步各自可单测、职责不重叠:
|
|
188227
|
+
*
|
|
188228
|
+
* 1. **解析目标**(不写消息、不 dispatch):`new_session` 由 run id 确定性映射出一个会话,
|
|
188229
|
+
* `existing_session` 读固定会话并复查 D3 五条。
|
|
188230
|
+
* 2. **持久占位**:拿到 active slot 才有资格产生副作用;拿不到 = 别人在跑 → 本次触发丢弃。
|
|
188231
|
+
* 3. **消费占位**:写确定性消息、真 dispatch、收流收口。
|
|
188232
|
+
*
|
|
188233
|
+
* 进程重启会丢 onDone 回调——syncRuns 对超时 running 兜底回收(chat_lost)。
|
|
187716
188234
|
*/
|
|
187717
188235
|
async fireChat(automation, trigger, run, opts) {
|
|
187718
188236
|
const now = this.now();
|
|
187719
188237
|
const tz = trigger?.schedule?.tz;
|
|
187720
188238
|
const title = automation.titleTemplate ? automation.titleTemplate.replace(DATE_TOKEN, renderDate(now, tz)) : `${automation.name} ${renderDate(now, tz)}`;
|
|
187721
188239
|
const prompt = buildChatPrompt(automation, title, opts.envelope);
|
|
187722
|
-
|
|
188240
|
+
let target;
|
|
188241
|
+
try {
|
|
188242
|
+
target = await this.deps.chat.resolveTarget({ automation, runId: run.id, title });
|
|
188243
|
+
} catch (e) {
|
|
188244
|
+
return this.failChatRun(run, e instanceof Error ? e.message : String(e));
|
|
188245
|
+
}
|
|
188246
|
+
if (!target.ok) return this.settleSkip(run, target.reasonCode, target.error, target.result);
|
|
188247
|
+
const claim = await this.claimChatTurn(run, target.chatSessionId).catch((e) => e);
|
|
188248
|
+
if (claim instanceof Error) return this.failChatRun(run, claim.message);
|
|
188249
|
+
if (claim.outcome === "busy") {
|
|
188250
|
+
await this.store.settleRun(run.id, {
|
|
188251
|
+
status: "skipped",
|
|
188252
|
+
reasonCode: "session_busy",
|
|
188253
|
+
error: busyError(target.chatSessionId, claim.activeTurn.sourceRunId),
|
|
188254
|
+
result: { chatSessionId: target.chatSessionId, blockedByTurnId: claim.activeTurn.id },
|
|
188255
|
+
completedAt: this.now().toISOString()
|
|
188256
|
+
});
|
|
188257
|
+
this.log?.(`[automations] run ${run.id} \u8DF3\u8FC7\uFF1A\u76EE\u6807\u4F1A\u8BDD ${target.chatSessionId} \u6B63\u5FD9\uFF08active turn ${claim.activeTurn.id}\uFF09`);
|
|
188258
|
+
return await this.store.getRun(run.id);
|
|
188259
|
+
}
|
|
188260
|
+
const turn = claim.turn;
|
|
188261
|
+
const chatSessionId = target.chatSessionId;
|
|
187723
188262
|
try {
|
|
187724
|
-
|
|
187725
|
-
{
|
|
188263
|
+
await this.deps.chat.dispatchTurn(
|
|
188264
|
+
{
|
|
188265
|
+
automation,
|
|
188266
|
+
chatSessionId,
|
|
188267
|
+
turnId: turn.id,
|
|
188268
|
+
messageKey: turn.messageKey,
|
|
188269
|
+
prompt,
|
|
188270
|
+
runId: run.id
|
|
188271
|
+
},
|
|
187726
188272
|
async (out) => {
|
|
187727
188273
|
try {
|
|
187728
188274
|
if (out.ok) {
|
|
187729
188275
|
await this.store.settleRun(run.id, {
|
|
187730
188276
|
status: "succeeded",
|
|
187731
|
-
result: { chatSessionId, ...out.reply ? { reply: clipReply(out.reply) } : {} },
|
|
188277
|
+
result: { chatSessionId, turnId: turn.id, ...out.reply ? { reply: clipReply(out.reply) } : {} },
|
|
187732
188278
|
completedAt: this.now().toISOString()
|
|
187733
188279
|
});
|
|
187734
188280
|
} else {
|
|
@@ -187736,7 +188282,7 @@ var init_service8 = __esm({
|
|
|
187736
188282
|
status: "failed",
|
|
187737
188283
|
error: out.error ?? "\u4F1A\u8BDD\u6267\u884C\u5931\u8D25",
|
|
187738
188284
|
reasonCode: "spawn_failed",
|
|
187739
|
-
result: { chatSessionId },
|
|
188285
|
+
result: { chatSessionId, turnId: turn.id },
|
|
187740
188286
|
completedAt: this.now().toISOString()
|
|
187741
188287
|
});
|
|
187742
188288
|
}
|
|
@@ -187747,26 +188293,56 @@ var init_service8 = __esm({
|
|
|
187747
188293
|
);
|
|
187748
188294
|
await this.store.settleRun(run.id, {
|
|
187749
188295
|
status: "running",
|
|
187750
|
-
result: { chatSessionId },
|
|
188296
|
+
result: { chatSessionId, turnId: turn.id },
|
|
187751
188297
|
completedAt: null
|
|
187752
188298
|
});
|
|
187753
188299
|
return await this.store.getRun(run.id);
|
|
187754
188300
|
} catch (e) {
|
|
187755
188301
|
const msg = e instanceof Error ? e.message : String(e);
|
|
187756
|
-
await this.
|
|
187757
|
-
|
|
187758
|
-
|
|
187759
|
-
|
|
187760
|
-
|
|
188302
|
+
await this.deps.chatTurns.settleTurn(turn.id, { status: "failed", completedAt: this.now().toISOString(), lastError: msg }).catch((err) => this.log?.(`[automations] \u8F6E\u6B21\u6536\u53E3\u5931\u8D25 ${turn.id}: ${String(err)}`));
|
|
188303
|
+
return this.failChatRun(run, msg, { chatSessionId, turnId: turn.id });
|
|
188304
|
+
}
|
|
188305
|
+
}
|
|
188306
|
+
/**
|
|
188307
|
+
* 占位(ADR 会话投递 D5)。存储层给了复合操作就走复合(claim 与 busy 落账同一事务);
|
|
188308
|
+
* 没给则退回两步——单进程内存实现语义等价,多实例部署一律有复合实现。
|
|
188309
|
+
*/
|
|
188310
|
+
async claimChatTurn(run, chatSessionId) {
|
|
188311
|
+
const input = {
|
|
188312
|
+
turnId: this.genId("cturn"),
|
|
188313
|
+
chatSessionId,
|
|
188314
|
+
source: "automation",
|
|
188315
|
+
sourceRunId: automationTurnSourceRunId(run.id),
|
|
188316
|
+
messageKey: automationTurnMessageKey(run.id),
|
|
188317
|
+
reservedAt: this.now().toISOString()
|
|
188318
|
+
};
|
|
188319
|
+
if (this.store.claimChatTurnForRun) {
|
|
188320
|
+
return this.store.claimChatTurnForRun({
|
|
188321
|
+
...input,
|
|
188322
|
+
automationRunId: run.id,
|
|
188323
|
+
busyError: busyError(chatSessionId, ""),
|
|
188324
|
+
now: this.now().toISOString()
|
|
187761
188325
|
});
|
|
187762
|
-
return await this.store.getRun(run.id);
|
|
187763
188326
|
}
|
|
188327
|
+
return this.deps.chatTurns.claimTurn(input);
|
|
187764
188328
|
}
|
|
187765
|
-
|
|
188329
|
+
/** chat 路径的失败收口(基础设施故障,非 event 源,ADR 会话投递 D7)。 */
|
|
188330
|
+
async failChatRun(run, error2, result) {
|
|
188331
|
+
await this.store.settleRun(run.id, {
|
|
188332
|
+
status: "failed",
|
|
188333
|
+
error: error2,
|
|
188334
|
+
reasonCode: "spawn_failed",
|
|
188335
|
+
...result !== void 0 ? { result } : {},
|
|
188336
|
+
completedAt: this.now().toISOString()
|
|
188337
|
+
});
|
|
188338
|
+
return await this.store.getRun(run.id);
|
|
188339
|
+
}
|
|
188340
|
+
async settleSkip(run, reasonCode, error2, result) {
|
|
187766
188341
|
await this.store.settleRun(run.id, {
|
|
187767
188342
|
status: "skipped",
|
|
187768
188343
|
reasonCode,
|
|
187769
188344
|
error: error2,
|
|
188345
|
+
...result !== void 0 ? { result } : {},
|
|
187770
188346
|
completedAt: this.now().toISOString()
|
|
187771
188347
|
});
|
|
187772
188348
|
return await this.store.getRun(run.id);
|
|
@@ -188005,11 +188581,11 @@ function verifySignature(rawBody, headers, secret) {
|
|
|
188005
188581
|
if (!secret) return "not_required";
|
|
188006
188582
|
const given = header1(headers, "x-hub-signature-256");
|
|
188007
188583
|
if (!given) return "missing";
|
|
188008
|
-
const expected = `sha256=${(0,
|
|
188584
|
+
const expected = `sha256=${(0, import_node_crypto39.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
|
|
188009
188585
|
const a = Buffer.from(given);
|
|
188010
188586
|
const b2 = Buffer.from(expected);
|
|
188011
188587
|
if (a.length !== b2.length) return "invalid";
|
|
188012
|
-
return (0,
|
|
188588
|
+
return (0, import_node_crypto39.timingSafeEqual)(a, b2) ? "valid" : "invalid";
|
|
188013
188589
|
}
|
|
188014
188590
|
function eventAllowed(filters, envelope) {
|
|
188015
188591
|
if (!filters || filters.length === 0) return true;
|
|
@@ -188039,11 +188615,11 @@ function splitEvent(envelope) {
|
|
|
188039
188615
|
}
|
|
188040
188616
|
return { eventName, actionCandidates: candidates };
|
|
188041
188617
|
}
|
|
188042
|
-
var
|
|
188618
|
+
var import_node_crypto39, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
|
|
188043
188619
|
var init_webhook = __esm({
|
|
188044
188620
|
"../server/src/domains/automations/webhook.ts"() {
|
|
188045
188621
|
"use strict";
|
|
188046
|
-
|
|
188622
|
+
import_node_crypto39 = require("node:crypto");
|
|
188047
188623
|
MAX_BODY_BYTES = 256 * 1024;
|
|
188048
188624
|
AutomationWebhookService = class {
|
|
188049
188625
|
store;
|
|
@@ -188330,7 +188906,9 @@ function buildAutomationSummary(automation, triggers, recent, canWrite) {
|
|
|
188330
188906
|
nextRunAt,
|
|
188331
188907
|
recent: ordered.slice(0, RECENT_LIMIT).map(stripRunPayload),
|
|
188332
188908
|
canWrite,
|
|
188333
|
-
capabilities: { eventTrigger: { supported: true } }
|
|
188909
|
+
capabilities: { eventTrigger: { supported: true } },
|
|
188910
|
+
// 固定目标会话的只读投影要查会话表,纯派生函数够不着——路由层用 service.chatTargetView 覆盖。
|
|
188911
|
+
chatTargetView: null
|
|
188334
188912
|
};
|
|
188335
188913
|
}
|
|
188336
188914
|
function deriveNextRunAt(triggers) {
|
|
@@ -188444,6 +189022,23 @@ function assertModeConfig(mode, agentId, playbookRef) {
|
|
|
188444
189022
|
if (!playbookRef?.trim()) throw new ApiError(400, "BAD_REQUEST", "workorder \u6A21\u5F0F\u7F3A\u5C11 playbookRef");
|
|
188445
189023
|
assertPlaybook(playbookRef);
|
|
188446
189024
|
}
|
|
189025
|
+
function validateChatTarget(raw, mode) {
|
|
189026
|
+
if (raw === void 0) return void 0;
|
|
189027
|
+
if (raw === null) return null;
|
|
189028
|
+
if (mode !== "chat") throw new ApiError(400, "BAD_REQUEST", "chatTarget \u53EA\u5728\u5BF9\u8BDD\u6267\u884C\uFF08chat\uFF09\u6A21\u5F0F\u4E0B\u6709\u610F\u4E49");
|
|
189029
|
+
const t = raw;
|
|
189030
|
+
if (t?.mode === "new_session") {
|
|
189031
|
+
if (t.sessionId !== void 0) throw new ApiError(400, "BAD_REQUEST", "chatTarget.mode=new_session \u65F6\u4E0D\u80FD\u5E26 sessionId");
|
|
189032
|
+
return { mode: "new_session" };
|
|
189033
|
+
}
|
|
189034
|
+
if (t?.mode === "existing_session") {
|
|
189035
|
+
if (typeof t.sessionId !== "string" || !t.sessionId.trim()) {
|
|
189036
|
+
throw new ApiError(400, "BAD_REQUEST", "chatTarget.mode=existing_session \u9700\u8981 sessionId");
|
|
189037
|
+
}
|
|
189038
|
+
return { mode: "existing_session", sessionId: t.sessionId.trim() };
|
|
189039
|
+
}
|
|
189040
|
+
throw new ApiError(400, "BAD_REQUEST", "chatTarget.mode \u53EA\u80FD\u662F new_session \u6216 existing_session");
|
|
189041
|
+
}
|
|
188447
189042
|
function assertTitleTemplate(tpl) {
|
|
188448
189043
|
if (!tpl) return;
|
|
188449
189044
|
const unknown2 = validateTitleTemplate(tpl);
|
|
@@ -188518,7 +189113,9 @@ function automationsDomain(opts) {
|
|
|
188518
189113
|
return {
|
|
188519
189114
|
...buildAutomationSummary(automation, decorated, recent, canWrite),
|
|
188520
189115
|
// 能力位服务端算(ADR 0163 §10):前端只消费不推断,否则前后端两套规则必漂移。
|
|
188521
|
-
capabilities: { eventTrigger: service.eventTriggerCapability(automation) }
|
|
189116
|
+
capabilities: { eventTrigger: service.eventTriggerCapability(automation) },
|
|
189117
|
+
// 固定目标会话的可用性同理:D3 五条只在服务端有真值,前端复制一份必然漂移。
|
|
189118
|
+
chatTargetView: await service.chatTargetView(automation)
|
|
188522
189119
|
};
|
|
188523
189120
|
};
|
|
188524
189121
|
const assertEventTriggerWritable = async (mode, cfg, existing, req) => {
|
|
@@ -188548,6 +189145,15 @@ function automationsDomain(opts) {
|
|
|
188548
189145
|
throw new ApiError(400, "EVENT_TRIGGER_UNSUPPORTED", "\u8FD9\u6761\u81EA\u52A8\u5316\u6709\u8BA2\u9605\u89E6\u53D1\u5668\uFF0C\u6539\u6210\u5BF9\u8BDD\u6267\u884C\u4F1A\u5931\u53BB\u6301\u4E45\u5E42\u7B49\u4FDD\u8BC1\u2014\u2014\u8BF7\u5148\u5220\u9664\u8BA2\u9605\u89E6\u53D1\u5668");
|
|
188549
189146
|
}
|
|
188550
189147
|
};
|
|
189148
|
+
const assertChatTargetUsable = async (a, target) => {
|
|
189149
|
+
if (target?.mode !== "existing_session") return;
|
|
189150
|
+
if (!service.chatTargetSupported()) {
|
|
189151
|
+
throw new ApiError(400, "CHAT_TARGET_UNSUPPORTED", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C\uFF0C\u6682\u4E0D\u652F\u6301\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD");
|
|
189152
|
+
}
|
|
189153
|
+
const issue2 = await service.validateChatTarget(a, target.sessionId);
|
|
189154
|
+
if (issue2) throw new ApiError(400, "CHAT_TARGET_UNAVAILABLE", TARGET_ISSUE_MESSAGE[issue2]);
|
|
189155
|
+
};
|
|
189156
|
+
const resolveOwner2 = async (existing, req) => existing ?? await service.resolveOwnerHuman(req.auth.actor);
|
|
188551
189157
|
return (router) => {
|
|
188552
189158
|
router.get("/api/automations/templates", async () => {
|
|
188553
189159
|
const body = { items: AUTOMATION_TEMPLATES };
|
|
@@ -188561,6 +189167,32 @@ function automationsDomain(opts) {
|
|
|
188561
189167
|
const body = { next: cronNextN(expr, tz, 3) };
|
|
188562
189168
|
return { status: 200, body };
|
|
188563
189169
|
});
|
|
189170
|
+
router.get("/api/automations/chat-session-options", async (req) => {
|
|
189171
|
+
const agentId = req.query?.get("agentId") ?? "";
|
|
189172
|
+
if (!agentId.startsWith("actor:agent:")) throw new ApiError(400, "BAD_REQUEST", "\u9700\u8981 agentId\uFF08actor:agent:*\uFF09");
|
|
189173
|
+
if (!service.chatTargetSupported()) {
|
|
189174
|
+
throw new ApiError(400, "CHAT_TARGET_UNSUPPORTED", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C\uFF0C\u6682\u4E0D\u652F\u6301\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD");
|
|
189175
|
+
}
|
|
189176
|
+
const owner = await service.resolveOwnerHuman(req.auth.actor);
|
|
189177
|
+
if (!owner) {
|
|
189178
|
+
const empty = { items: [], hasMore: false };
|
|
189179
|
+
return { status: 200, body: empty };
|
|
189180
|
+
}
|
|
189181
|
+
const limitRaw = Number(req.query?.get("limit"));
|
|
189182
|
+
const offsetRaw = Number(req.query?.get("offset"));
|
|
189183
|
+
const limit = Number.isInteger(limitRaw) && limitRaw > 0 ? Math.min(limitRaw, 50) : 20;
|
|
189184
|
+
const offset = Number.isInteger(offsetRaw) && offsetRaw > 0 ? offsetRaw : 0;
|
|
189185
|
+
const text = req.query?.get("q")?.trim();
|
|
189186
|
+
const body = await service.searchChatTargets({
|
|
189187
|
+
companyId: companyOf(req),
|
|
189188
|
+
agentId,
|
|
189189
|
+
ownerHumanActorId: owner,
|
|
189190
|
+
...text ? { text } : {},
|
|
189191
|
+
limit,
|
|
189192
|
+
offset
|
|
189193
|
+
});
|
|
189194
|
+
return { status: 200, body };
|
|
189195
|
+
});
|
|
188564
189196
|
router.get("/api/automations", async (req) => {
|
|
188565
189197
|
const includeArchived = req.query.get("includeArchived") === "1";
|
|
188566
189198
|
const automations = await service.listAutomations(companyOf(req), { includeArchived });
|
|
@@ -188614,12 +189246,30 @@ function automationsDomain(opts) {
|
|
|
188614
189246
|
const subscribers = assertActorIds(b2.subscribers, "subscribers");
|
|
188615
189247
|
const triggers = validateTriggers(b2.triggers);
|
|
188616
189248
|
await assertEventTriggerInputs(mode, triggers, [], req);
|
|
189249
|
+
const chatTarget = validateChatTarget(b2.chatTarget, mode);
|
|
189250
|
+
const createTargetSession = Boolean(b2.createChatTargetSession);
|
|
189251
|
+
if (createTargetSession) {
|
|
189252
|
+
if (mode !== "chat") throw new ApiError(400, "BAD_REQUEST", "createChatTargetSession \u53EA\u5728\u5BF9\u8BDD\u6267\u884C\uFF08chat\uFF09\u6A21\u5F0F\u4E0B\u6709\u610F\u4E49");
|
|
189253
|
+
if (chatTarget?.mode === "existing_session") {
|
|
189254
|
+
throw new ApiError(400, "BAD_REQUEST", "createChatTargetSession \u4E0E chatTarget.existing_session \u4E92\u65A5");
|
|
189255
|
+
}
|
|
189256
|
+
if (!service.chatTargetSupported()) {
|
|
189257
|
+
throw new ApiError(400, "CHAT_TARGET_UNSUPPORTED", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C\uFF0C\u6682\u4E0D\u652F\u6301\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD");
|
|
189258
|
+
}
|
|
189259
|
+
}
|
|
189260
|
+
const ownerHumanActorId = mode === "chat" ? await service.resolveOwnerHuman(req.auth.actor) : null;
|
|
189261
|
+
if (mode === "chat" && !ownerHumanActorId && (chatTarget?.mode === "existing_session" || createTargetSession)) {
|
|
189262
|
+
throw new ApiError(400, "CHAT_TARGET_NO_OWNER", "\u89E3\u6790\u4E0D\u5230\u4EBA\u7C7B\u5F52\u5C5E\u8005\uFF0C\u65E0\u6CD5\u914D\u7F6E\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD");
|
|
189263
|
+
}
|
|
189264
|
+
await assertChatTargetUsable({ companyId: companyOf(req), agentId: b2.agentId ?? null, ownerHumanActorId }, chatTarget);
|
|
188617
189265
|
const created = await service.createAutomation({
|
|
188618
189266
|
companyId: companyOf(req),
|
|
188619
189267
|
name: b2.name,
|
|
188620
189268
|
...b2.description !== void 0 ? { description: b2.description } : {},
|
|
188621
189269
|
executionMode: mode,
|
|
188622
189270
|
...b2.agentId !== void 0 ? { agentId: b2.agentId } : {},
|
|
189271
|
+
...chatTarget !== void 0 ? { chatTarget } : {},
|
|
189272
|
+
...ownerHumanActorId ? { ownerHumanActorId } : {},
|
|
188623
189273
|
...b2.projectId !== void 0 ? { projectId: b2.projectId } : {},
|
|
188624
189274
|
...b2.playbookRef !== void 0 ? { playbookRef: b2.playbookRef } : {},
|
|
188625
189275
|
...b2.payload !== void 0 ? { payload: b2.payload } : {},
|
|
@@ -188631,7 +189281,8 @@ function automationsDomain(opts) {
|
|
|
188631
189281
|
// 一律取鉴权身份,不收请求体(归属不可伪造)
|
|
188632
189282
|
...triggers !== void 0 ? { triggers } : {}
|
|
188633
189283
|
});
|
|
188634
|
-
|
|
189284
|
+
const withTarget = createTargetSession ? await service.createChatTargetSession(created, req.auth.actor) : created;
|
|
189285
|
+
return { status: 201, body: await summarize3(withTarget, req) };
|
|
188635
189286
|
});
|
|
188636
189287
|
router.patch("/api/automations/:id", async (req) => {
|
|
188637
189288
|
const automation = await mustGet(req);
|
|
@@ -188650,6 +189301,14 @@ function automationsDomain(opts) {
|
|
|
188650
189301
|
const kept = triggers !== void 0 ? [] : (await service.listTriggers(automation.id)).filter((t) => t.kind === "event" && t.event).map((t) => t.event);
|
|
188651
189302
|
await assertEventTriggerInputs(effMode, triggers, kept, req);
|
|
188652
189303
|
if (triggers === void 0) await assertModeKeepsEventTriggers(automation, effMode);
|
|
189304
|
+
const chatTarget = validateChatTarget(b2.chatTarget, effMode);
|
|
189305
|
+
const effTarget = chatTarget !== void 0 ? chatTarget : automation.chatTarget;
|
|
189306
|
+
const nextTarget = effMode === "workorder" ? null : effTarget;
|
|
189307
|
+
const ownerHumanActorId = effMode === "chat" ? await resolveOwner2(automation.ownerHumanActorId, req) : null;
|
|
189308
|
+
if (effMode === "chat" && nextTarget?.mode === "existing_session" && !ownerHumanActorId) {
|
|
189309
|
+
throw new ApiError(400, "CHAT_TARGET_NO_OWNER", "\u89E3\u6790\u4E0D\u5230\u4EBA\u7C7B\u5F52\u5C5E\u8005\uFF0C\u65E0\u6CD5\u914D\u7F6E\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD");
|
|
189310
|
+
}
|
|
189311
|
+
await assertChatTargetUsable({ companyId: automation.companyId, agentId: effAgent ?? null, ownerHumanActorId }, nextTarget);
|
|
188653
189312
|
const updated = await service.updateAutomation(
|
|
188654
189313
|
automation.id,
|
|
188655
189314
|
{
|
|
@@ -188664,12 +189323,28 @@ function automationsDomain(opts) {
|
|
|
188664
189323
|
...b2.dispatch !== void 0 ? { dispatch: Boolean(b2.dispatch) } : {},
|
|
188665
189324
|
...b2.enabled !== void 0 ? { enabled: b2.enabled } : {},
|
|
188666
189325
|
...subscribers !== void 0 ? { subscribers } : {},
|
|
188667
|
-
...triggers !== void 0 ? { triggers } : {}
|
|
189326
|
+
...triggers !== void 0 ? { triggers } : {},
|
|
189327
|
+
...nextTarget !== automation.chatTarget ? { chatTarget: nextTarget } : {},
|
|
189328
|
+
...ownerHumanActorId !== automation.ownerHumanActorId ? { ownerHumanActorId } : {}
|
|
188668
189329
|
},
|
|
188669
189330
|
req.auth.actor
|
|
188670
189331
|
);
|
|
188671
189332
|
return { status: 200, body: await summarize3(updated, req) };
|
|
188672
189333
|
});
|
|
189334
|
+
router.post("/api/automations/:id/chat-target-session", async (req) => {
|
|
189335
|
+
const automation = await mustGet(req);
|
|
189336
|
+
await requireWrite(automation, req);
|
|
189337
|
+
if (automation.executionMode !== "chat") throw new ApiError(400, "BAD_REQUEST", "\u53EA\u6709\u5BF9\u8BDD\u6267\u884C\uFF08chat\uFF09\u6A21\u5F0F\u624D\u6709\u76EE\u6807\u4F1A\u8BDD");
|
|
189338
|
+
if (!automation.agentId) throw new ApiError(400, "BAD_REQUEST", "\u5148\u7ED9\u8FD9\u6761\u81EA\u52A8\u5316\u8BBE\u7F6E\u6267\u884C\u4EBA");
|
|
189339
|
+
if (!service.chatTargetSupported()) {
|
|
189340
|
+
throw new ApiError(400, "CHAT_TARGET_UNSUPPORTED", "\u672C\u5B9E\u4F8B\u672A\u63A5\u4F1A\u8BDD\u8F6E\u6B21\u8D26\u672C\uFF0C\u6682\u4E0D\u652F\u6301\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD");
|
|
189341
|
+
}
|
|
189342
|
+
const owner = await resolveOwner2(automation.ownerHumanActorId, req);
|
|
189343
|
+
if (!owner) throw new ApiError(400, "CHAT_TARGET_NO_OWNER", "\u89E3\u6790\u4E0D\u5230\u4EBA\u7C7B\u5F52\u5C5E\u8005\uFF0C\u65E0\u6CD5\u5EFA\u957F\u671F\u76EE\u6807\u4F1A\u8BDD");
|
|
189344
|
+
const withOwner = automation.ownerHumanActorId ? automation : await service.updateAutomation(automation.id, { ownerHumanActorId: owner }, req.auth.actor);
|
|
189345
|
+
const updated = await service.createChatTargetSession(withOwner, req.auth.actor);
|
|
189346
|
+
return { status: 201, body: await summarize3(updated, req) };
|
|
189347
|
+
});
|
|
188673
189348
|
router.post("/api/automations/:id/enabled", async (req) => {
|
|
188674
189349
|
const automation = await mustGet(req);
|
|
188675
189350
|
await requireWrite(automation, req);
|
|
@@ -188817,7 +189492,7 @@ function automationsDomain(opts) {
|
|
|
188817
189492
|
});
|
|
188818
189493
|
};
|
|
188819
189494
|
}
|
|
188820
|
-
var RECENT_LIMIT2;
|
|
189495
|
+
var RECENT_LIMIT2, TARGET_ISSUE_MESSAGE;
|
|
188821
189496
|
var init_routes9 = __esm({
|
|
188822
189497
|
"../server/src/domains/automations/routes.ts"() {
|
|
188823
189498
|
"use strict";
|
|
@@ -188830,6 +189505,13 @@ var init_routes9 = __esm({
|
|
|
188830
189505
|
init_registry3();
|
|
188831
189506
|
init_migrate();
|
|
188832
189507
|
RECENT_LIMIT2 = 5;
|
|
189508
|
+
TARGET_ISSUE_MESSAGE = {
|
|
189509
|
+
session_missing: "\u76EE\u6807\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u5220\u9664",
|
|
189510
|
+
cross_company: "\u76EE\u6807\u4F1A\u8BDD\u5C5E\u4E8E\u5176\u5B83\u516C\u53F8\uFF0C\u4E0D\u80FD\u8DE8\u516C\u53F8\u6295\u9012",
|
|
189511
|
+
company_unknown: "\u8FD9\u6761\u5386\u53F2\u4F1A\u8BDD\u6CA1\u6709\u516C\u53F8\u5F52\u5C5E\uFF0C\u4E0D\u80FD\u4F5C\u4E3A\u56FA\u5B9A\u76EE\u6807\u2014\u2014\u8BF7\u65B0\u5EFA\u4E00\u4E2A\u957F\u671F\u4F1A\u8BDD",
|
|
189512
|
+
owner_mismatch: "\u76EE\u6807\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u8FD9\u6761\u81EA\u52A8\u5316\u7684\u5F52\u5C5E\u4EBA",
|
|
189513
|
+
agent_mismatch: "\u76EE\u6807\u4F1A\u8BDD\u7684 AI \u8EAB\u4EFD\u4E0E\u6267\u884C\u4EBA\u4E0D\u4E00\u81F4\u2014\u2014\u8BF7\u6362\u4E00\u4E2A\u4F1A\u8BDD\u6216\u6539\u6267\u884C\u4EBA\uFF08\u4E0D\u4F1A\u81EA\u52A8\u6539\u7ED1\uFF09"
|
|
189514
|
+
};
|
|
188833
189515
|
}
|
|
188834
189516
|
});
|
|
188835
189517
|
|
|
@@ -188902,6 +189584,7 @@ function createAutomationsDomain(opts) {
|
|
|
188902
189584
|
const service = new AutomationsService({
|
|
188903
189585
|
store: opts.store,
|
|
188904
189586
|
fire: opts.fire,
|
|
189587
|
+
...opts.chatTargets ? { chatTargets: opts.chatTargets } : {},
|
|
188905
189588
|
...opts.genId ? { genId: opts.genId } : {},
|
|
188906
189589
|
...opts.now ? { now: opts.now } : {},
|
|
188907
189590
|
...opts.log ? { log: opts.log } : {}
|
|
@@ -189086,7 +189769,7 @@ function playbooksDomain(opts) {
|
|
|
189086
189769
|
opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
|
|
189087
189770
|
try {
|
|
189088
189771
|
await opts.audit?.({
|
|
189089
|
-
id: `reg_${(0,
|
|
189772
|
+
id: `reg_${(0, import_node_crypto40.randomUUID)()}`,
|
|
189090
189773
|
actor: req.auth.actor,
|
|
189091
189774
|
kind: "registry_change",
|
|
189092
189775
|
target: `playbook:${ref2}#${nodeKey}`,
|
|
@@ -189103,11 +189786,11 @@ function playbooksDomain(opts) {
|
|
|
189103
189786
|
});
|
|
189104
189787
|
};
|
|
189105
189788
|
}
|
|
189106
|
-
var
|
|
189789
|
+
var import_node_crypto40;
|
|
189107
189790
|
var init_routes10 = __esm({
|
|
189108
189791
|
"../server/src/domains/playbooks/routes.ts"() {
|
|
189109
189792
|
"use strict";
|
|
189110
|
-
|
|
189793
|
+
import_node_crypto40 = require("node:crypto");
|
|
189111
189794
|
init_router();
|
|
189112
189795
|
init_registry3();
|
|
189113
189796
|
init_planner();
|
|
@@ -189219,17 +189902,17 @@ function completionFingerprint(workspace, members) {
|
|
|
189219
189902
|
workspace,
|
|
189220
189903
|
members: [...members].sort((a, b2) => a.artifactId < b2.artifactId ? -1 : a.artifactId > b2.artifactId ? 1 : 0).map((m2) => ({ artifactId: m2.artifactId, currentRevisionId: m2.currentRevisionId ?? null }))
|
|
189221
189904
|
});
|
|
189222
|
-
return (0,
|
|
189905
|
+
return (0, import_node_crypto41.createHash)("sha256").update(canonical).digest("hex");
|
|
189223
189906
|
}
|
|
189224
189907
|
function eventConsumptionKey(args) {
|
|
189225
189908
|
const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
|
|
189226
|
-
return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0,
|
|
189909
|
+
return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto41.createHash)("sha256").update(raw).digest("hex")}`;
|
|
189227
189910
|
}
|
|
189228
|
-
var
|
|
189911
|
+
var import_node_crypto41, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
|
|
189229
189912
|
var init_project_event_poller = __esm({
|
|
189230
189913
|
"../server/src/automations/project-event-poller.ts"() {
|
|
189231
189914
|
"use strict";
|
|
189232
|
-
|
|
189915
|
+
import_node_crypto41 = require("node:crypto");
|
|
189233
189916
|
EVENT_KEY_VERSION = "v1";
|
|
189234
189917
|
EMPTY_METRICS = () => ({
|
|
189235
189918
|
scanDurationMs: 0,
|
|
@@ -189448,11 +190131,11 @@ function remoteAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs) {
|
|
|
189448
190131
|
entry.appendWaiters.push({ resolve: resolve10, timer });
|
|
189449
190132
|
});
|
|
189450
190133
|
}
|
|
189451
|
-
var
|
|
190134
|
+
var import_node_crypto42, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_IDS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
|
|
189452
190135
|
var init_daemon_adapter = __esm({
|
|
189453
190136
|
"../server/src/daemon-adapter.ts"() {
|
|
189454
190137
|
"use strict";
|
|
189455
|
-
|
|
190138
|
+
import_node_crypto42 = require("node:crypto");
|
|
189456
190139
|
init_src2();
|
|
189457
190140
|
STASH_TTL_MS = 5 * 6e4;
|
|
189458
190141
|
STASH_MAX_FRAMES_PER_ID = 500;
|
|
@@ -189468,10 +190151,16 @@ var init_daemon_adapter = __esm({
|
|
|
189468
190151
|
this.log = opts.log ?? ((m2) => console.warn(m2));
|
|
189469
190152
|
this.health = opts.health;
|
|
189470
190153
|
this.stashEnabled = opts.stashUnknownFrames ?? false;
|
|
190154
|
+
this.onSettleWithoutFrame = opts.onSettleWithoutFrame;
|
|
189471
190155
|
hub.addMessageListener((_daemonId, msg) => this.onDaemonMessage(msg));
|
|
189472
190156
|
hub.addDisconnectListener((daemonId) => this.onNodeDown(daemonId));
|
|
189473
190157
|
hub.addConnectListener((daemon) => this.onNodeUp(daemon));
|
|
189474
190158
|
hub.addSessionConnectListener?.((i) => this.onSessionUp(i.dispatchId));
|
|
190159
|
+
hub.registerSessionClaimCheck?.((dispatchId) => {
|
|
190160
|
+
const e = this.pending.get(dispatchId);
|
|
190161
|
+
if (e) return e.exited ? "settled" : "live";
|
|
190162
|
+
return this.settledDispatchIds.has(dispatchId) ? "settled" : "unknown";
|
|
190163
|
+
});
|
|
189475
190164
|
hub.addSessionDisconnectListener?.((i) => this.onSessionDown(i.dispatchId));
|
|
189476
190165
|
}
|
|
189477
190166
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -189482,6 +190171,8 @@ var init_daemon_adapter = __esm({
|
|
|
189482
190171
|
log;
|
|
189483
190172
|
health;
|
|
189484
190173
|
stashEnabled;
|
|
190174
|
+
/** 服务端自判死时的回调(见 DaemonHubAdapterOptions.onSettleWithoutFrame)。 */
|
|
190175
|
+
onSettleWithoutFrame;
|
|
189485
190176
|
/** 未知会话帧暂存(stashUnknownFrames 开启时):dispatchId → 断链/重启窗口补发上来的帧。 */
|
|
189486
190177
|
stash = /* @__PURE__ */ new Map();
|
|
189487
190178
|
/** 断联节点 → 收割定时器。重连取消;到期收割。 */
|
|
@@ -189489,6 +190180,9 @@ var init_daemon_adapter = __esm({
|
|
|
189489
190180
|
/** 会话粒度的失联倒计时(键=dispatchId)。与上面按 nodeId 的那张表并存,判据不同、互不替代。 */
|
|
189490
190181
|
sessionReapTimers = /* @__PURE__ */ new Map();
|
|
189491
190182
|
sessionReapGraceMs;
|
|
190183
|
+
/** 近期已收尾的 dispatchId(有界)。用于回答「这次派发我收过了」——否则收尾后 pending 里就没了,
|
|
190184
|
+
* 旧会话重连时会被判成 unknown,分不清「我收割过它」和「压根不归我管」。 */
|
|
190185
|
+
settledDispatchIds = /* @__PURE__ */ new Set();
|
|
189492
190186
|
sessionAuth;
|
|
189493
190187
|
/** 取走(并清除)某会话的暂存帧;无暂存返回空数组对。 */
|
|
189494
190188
|
drainStash(dispatchId) {
|
|
@@ -189520,7 +190214,15 @@ var init_daemon_adapter = __esm({
|
|
|
189520
190214
|
}
|
|
189521
190215
|
}
|
|
189522
190216
|
/** 统一结算一次会话退出:清 ack 定时器、置 exited、触发回调、从 pending 摘除。幂等(已退出即跳过)。 */
|
|
189523
|
-
|
|
190217
|
+
/**
|
|
190218
|
+
* 收尾一次派发。
|
|
190219
|
+
*
|
|
190220
|
+
* `fromFrame` = 这次收尾是不是**节点报上来的真退出帧**触发的。false(默认)意味着服务端
|
|
190221
|
+
* 自己判的死——存在性快照 / 会话失联收割 / socket 非 OPEN。那三条走不到 serve 挂在 hub
|
|
190222
|
+
* message listener 上的 work.response 桥,引擎那条 work 会一直挂 running 直到 30 分钟超时,
|
|
190223
|
+
* 所以这里要显式喊一声 `onSettleWithoutFrame`。**新增判死路径时默认值就是对的**,别顺手传 true。
|
|
190224
|
+
*/
|
|
190225
|
+
settle(dispatchId, info, fromFrame = false) {
|
|
189524
190226
|
const entry = this.pending.get(dispatchId);
|
|
189525
190227
|
if (!entry || entry.exited) return;
|
|
189526
190228
|
if (entry.ackTimer) {
|
|
@@ -189536,6 +190238,22 @@ var init_daemon_adapter = __esm({
|
|
|
189536
190238
|
else this.health?.recordReachable(entry.nodeId);
|
|
189537
190239
|
for (const cb of entry.exitCbs.splice(0)) cb(info);
|
|
189538
190240
|
this.pending.delete(dispatchId);
|
|
190241
|
+
this.settledDispatchIds.add(dispatchId);
|
|
190242
|
+
if (this.settledDispatchIds.size > 2e3) {
|
|
190243
|
+
const it = this.settledDispatchIds.values();
|
|
190244
|
+
for (let i = 0; i < 500; i++) {
|
|
190245
|
+
const v2 = it.next();
|
|
190246
|
+
if (v2.done) break;
|
|
190247
|
+
this.settledDispatchIds.delete(v2.value);
|
|
190248
|
+
}
|
|
190249
|
+
}
|
|
190250
|
+
if (!fromFrame) {
|
|
190251
|
+
try {
|
|
190252
|
+
this.onSettleWithoutFrame?.(dispatchId, info);
|
|
190253
|
+
} catch (e) {
|
|
190254
|
+
this.log(`[dispatch-delivery] onSettleWithoutFrame \u629B\u9519\uFF08\u4E0D\u5F71\u54CD\u6536\u5C3E\uFF09\uFF1A${String(e)}`);
|
|
190255
|
+
}
|
|
190256
|
+
}
|
|
189539
190257
|
}
|
|
189540
190258
|
/** 当前在途(未退出)会话数;传 runtimeKind 时收窄到单个 runtime 实例。 */
|
|
189541
190259
|
activeRunCount(nodeId, runtimeKind) {
|
|
@@ -189628,11 +190346,6 @@ var init_daemon_adapter = __esm({
|
|
|
189628
190346
|
clearTimeout(timer);
|
|
189629
190347
|
this.sessionReapTimers.delete(dispatchId);
|
|
189630
190348
|
}
|
|
189631
|
-
const entry = this.pending.get(dispatchId);
|
|
189632
|
-
if (!entry || entry.exited) {
|
|
189633
|
-
this.log(`[dispatch-delivery] ${dispatchId}\uFF1A\u4F1A\u8BDD\u8FDE\u4E0A\u6765\u4E86\uFF0C\u4F46\u670D\u52A1\u7AEF\u5DF2\u4E0D\u8BA4\u5B83\uFF08\u5DF2\u6536\u5272/\u5DF2\u91CD\u6D3E\uFF09\u2192 evict`);
|
|
189634
|
-
this.hub.evictSession?.(dispatchId, entry ? "reaped" : "unknown");
|
|
189635
|
-
}
|
|
189636
190349
|
}
|
|
189637
190350
|
/**
|
|
189638
190351
|
* 会话连接断开(D2′.4):**断联即该会话失联**,不再经由节点代为证明。
|
|
@@ -189678,7 +190391,7 @@ var init_daemon_adapter = __esm({
|
|
|
189678
190391
|
if (entry) this.health?.recordReachable(entry.nodeId);
|
|
189679
190392
|
} else if (msg.type === "session_exited") {
|
|
189680
190393
|
const info = msg.info.reason || msg.info.code !== null ? msg.info : { ...msg.info, reason: "error" };
|
|
189681
|
-
if (this.pending.has(msg.dispatchId)) this.settle(msg.dispatchId, info);
|
|
190394
|
+
if (this.pending.has(msg.dispatchId)) this.settle(msg.dispatchId, info, true);
|
|
189682
190395
|
else if (this.stashEnabled) this.stashFrame(msg.dispatchId, { type: "exit", info });
|
|
189683
190396
|
} else if (msg.type === "session_event") {
|
|
189684
190397
|
this.confirmDelivery(msg.dispatchId, "started");
|
|
@@ -189702,7 +190415,7 @@ var init_daemon_adapter = __esm({
|
|
|
189702
190415
|
async spawn(job) {
|
|
189703
190416
|
const nodeId = job.binding?.nodeId;
|
|
189704
190417
|
if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
|
|
189705
|
-
const dispatchId = job.dispatchId ?? `dispatch:${(0,
|
|
190418
|
+
const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto42.randomUUID)()}`;
|
|
189706
190419
|
const entry = {
|
|
189707
190420
|
nodeId,
|
|
189708
190421
|
...job.binding?.runtimeKind ? { runtimeKind: job.binding.runtimeKind } : {},
|
|
@@ -189859,7 +190572,7 @@ var init_daemon_adapter = __esm({
|
|
|
189859
190572
|
}));
|
|
189860
190573
|
}
|
|
189861
190574
|
request(nodeId, buildFrame) {
|
|
189862
|
-
const requestId = (0,
|
|
190575
|
+
const requestId = (0, import_node_crypto42.randomUUID)();
|
|
189863
190576
|
const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
|
|
189864
190577
|
if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
|
|
189865
190578
|
return new Promise((resolve10) => {
|
|
@@ -189967,13 +190680,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
|
|
|
189967
190680
|
if (sha(body) === lastPushedHash) return { kind: "echo" };
|
|
189968
190681
|
return { kind: "human-edit", content: body, updatedBy: page.updatedBy };
|
|
189969
190682
|
}
|
|
189970
|
-
var
|
|
190683
|
+
var import_node_crypto43, sha, enc3, dec, MirrorEngine, MapMirrorIdentities;
|
|
189971
190684
|
var init_engine = __esm({
|
|
189972
190685
|
"../server/src/mirror/engine.ts"() {
|
|
189973
190686
|
"use strict";
|
|
189974
|
-
|
|
190687
|
+
import_node_crypto43 = require("node:crypto");
|
|
189975
190688
|
init_src2();
|
|
189976
|
-
sha = (s2) => (0,
|
|
190689
|
+
sha = (s2) => (0, import_node_crypto43.createHash)("sha256").update(s2, "utf8").digest("hex");
|
|
189977
190690
|
enc3 = (s2) => new TextEncoder().encode(s2);
|
|
189978
190691
|
dec = (b2) => new TextDecoder().decode(b2);
|
|
189979
190692
|
MirrorEngine = class {
|
|
@@ -190180,11 +190893,11 @@ function humanExitCause(exit) {
|
|
|
190180
190893
|
const base = (exit.reason ? label[exit.reason] : void 0) ?? `\u4F1A\u8BDD\u5F02\u5E38\u7ED3\u675F\uFF08${exit.reason ?? "\u65E0\u9000\u51FA\u4FE1\u606F"}\uFF09`;
|
|
190181
190894
|
return exit.errorMessage ? `${base}\uFF1A${exit.errorMessage.slice(0, 200)}` : base;
|
|
190182
190895
|
}
|
|
190183
|
-
var
|
|
190896
|
+
var import_node_crypto44, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
|
|
190184
190897
|
var init_worker = __esm({
|
|
190185
190898
|
"../server/src/coordinator/worker.ts"() {
|
|
190186
190899
|
"use strict";
|
|
190187
|
-
|
|
190900
|
+
import_node_crypto44 = require("node:crypto");
|
|
190188
190901
|
init_src5();
|
|
190189
190902
|
init_identity();
|
|
190190
190903
|
AUTONOMOUS_ETHOS = `\u4F60\u662F\u8FD9\u4E2A\u5DE5\u5355\u7684**\u7BA1\u7406\u8005**\u2014\u2014\u804C\u8D23\u662F\u8BA9\u5B83\u987A\u7545\u8DD1\u5B8C\u3002\u7CFB\u7EDF\u5728\u67D0\u4E2A\u8282\u70B9\u5361\u4F4F\u3001\u673A\u68B0\u5206\u8BCA\u786E\u8BA4"\u9700\u8981\u4F60"\u65F6\u5524\u8D77\u4F60\uFF08\u65E0\u4EBA\u5728\u573A\uFF0C\u4F60\u8FD9\u4E00\u8F6E\u628A\u80FD\u505A\u7684\u505A\u6389\uFF09\u3002\u4F60\u7684\u624B\u6BB5\u5F88\u5BBD\uFF1A
|
|
@@ -190431,7 +191144,7 @@ ${HIGH_RISK_COMMANDS}`;
|
|
|
190431
191144
|
return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
|
|
190432
191145
|
}
|
|
190433
191146
|
evaluationKey(kind, artifactId, evidence) {
|
|
190434
|
-
const fingerprint = (0,
|
|
191147
|
+
const fingerprint = (0, import_node_crypto44.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
|
|
190435
191148
|
return `${kind}:${artifactId}:${fingerprint}`;
|
|
190436
191149
|
}
|
|
190437
191150
|
artifactEvidence(id) {
|
|
@@ -190808,8 +191521,8 @@ ${ctx.nodeFault}
|
|
|
190808
191521
|
this.deps.log?.(`[coordinator] ${workspace} \u5168\u5C40\u4E0A\u4E0B\u6587\u8BFB\u53D6\u5931\u8D25\uFF0C\u7EE7\u7EED\u6CBF\u7528\u539F\u534F\u8C03\u8005\u4EFB\u52A1\uFF1A${String(error2)}`);
|
|
190809
191522
|
}
|
|
190810
191523
|
}
|
|
190811
|
-
const runtimeSessionId = opts?.resumeSessionId ?? (0,
|
|
190812
|
-
const coordinatorRunId = `coordinate:${(0,
|
|
191524
|
+
const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto44.randomUUID)();
|
|
191525
|
+
const coordinatorRunId = `coordinate:${(0, import_node_crypto44.randomUUID)()}`;
|
|
190813
191526
|
const taskWithContext = coordinatorContext ? [
|
|
190814
191527
|
task,
|
|
190815
191528
|
``,
|
|
@@ -190850,7 +191563,7 @@ ${ctx.nodeFault}
|
|
|
190850
191563
|
const startedAtMs = Date.now();
|
|
190851
191564
|
let trajectoryStarted = false;
|
|
190852
191565
|
if (this.deps.trajectory) {
|
|
190853
|
-
const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content]) => [name, (0,
|
|
191566
|
+
const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content]) => [name, (0, import_node_crypto44.createHash)("sha256").update(content).digest("hex")]));
|
|
190854
191567
|
const session = {
|
|
190855
191568
|
runId: coordinatorRunId,
|
|
190856
191569
|
runtimeSessionId,
|
|
@@ -191216,6 +191929,7 @@ var init_src10 = __esm({
|
|
|
191216
191929
|
init_recovery_plan();
|
|
191217
191930
|
init_live_chat();
|
|
191218
191931
|
init_chat_recovery();
|
|
191932
|
+
init_chat_turn_gate();
|
|
191219
191933
|
init_crypto2();
|
|
191220
191934
|
init_new_engine();
|
|
191221
191935
|
init_daemon_hub();
|
|
@@ -191246,6 +191960,7 @@ var init_src10 = __esm({
|
|
|
191246
191960
|
init_execution_continuity2();
|
|
191247
191961
|
init_chat_sessions();
|
|
191248
191962
|
init_dev_store();
|
|
191963
|
+
init_continuation();
|
|
191249
191964
|
init_knowledge2();
|
|
191250
191965
|
init_automations();
|
|
191251
191966
|
init_automations();
|
|
@@ -191273,11 +191988,11 @@ var init_src10 = __esm({
|
|
|
191273
191988
|
});
|
|
191274
191989
|
|
|
191275
191990
|
// ../storage/src/postgres.ts
|
|
191276
|
-
var
|
|
191991
|
+
var import_node_crypto45, ident3, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
|
|
191277
191992
|
var init_postgres = __esm({
|
|
191278
191993
|
"../storage/src/postgres.ts"() {
|
|
191279
191994
|
"use strict";
|
|
191280
|
-
|
|
191995
|
+
import_node_crypto45 = require("node:crypto");
|
|
191281
191996
|
init_esm();
|
|
191282
191997
|
init_src2();
|
|
191283
191998
|
ident3 = (s2) => {
|
|
@@ -191456,7 +192171,7 @@ var init_postgres = __esm({
|
|
|
191456
192171
|
return new _PostgresBlobStore(pool, schema);
|
|
191457
192172
|
}
|
|
191458
192173
|
async put(bytes) {
|
|
191459
|
-
const hash = (0,
|
|
192174
|
+
const hash = (0, import_node_crypto45.createHash)("sha256").update(bytes).digest("hex");
|
|
191460
192175
|
await this.pool.query(
|
|
191461
192176
|
`INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
|
|
191462
192177
|
[hash, Buffer.from(bytes), bytes.byteLength, sniffContentType(bytes) ?? null]
|
|
@@ -195724,6 +196439,144 @@ var init_postgres_trace = __esm({
|
|
|
195724
196439
|
}
|
|
195725
196440
|
});
|
|
195726
196441
|
|
|
196442
|
+
// ../storage/src/postgres-chat-turns.ts
|
|
196443
|
+
async function ensureChatTurnsTable(pool, schema = "public") {
|
|
196444
|
+
const s2 = ident10(schema);
|
|
196445
|
+
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
196446
|
+
await pool.query(`
|
|
196447
|
+
CREATE TABLE IF NOT EXISTS "${s2}".chat_session_turns (
|
|
196448
|
+
id text PRIMARY KEY,
|
|
196449
|
+
chat_session_id text NOT NULL,
|
|
196450
|
+
source text NOT NULL,
|
|
196451
|
+
source_run_id text NOT NULL,
|
|
196452
|
+
message_key text NOT NULL,
|
|
196453
|
+
status text NOT NULL,
|
|
196454
|
+
dispatch_id text,
|
|
196455
|
+
assistant_run_id text,
|
|
196456
|
+
reserved_at timestamptz NOT NULL,
|
|
196457
|
+
started_at timestamptz,
|
|
196458
|
+
completed_at timestamptz,
|
|
196459
|
+
last_error text
|
|
196460
|
+
)`);
|
|
196461
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_session_turns_source_uq ON "${s2}".chat_session_turns (source, source_run_id)`);
|
|
196462
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_session_turns_msgkey_uq ON "${s2}".chat_session_turns (message_key)`);
|
|
196463
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_session_turns_active_uq ON "${s2}".chat_session_turns (chat_session_id) WHERE status IN ('reserved','running')`);
|
|
196464
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS chat_session_turns_stale_idx ON "${s2}".chat_session_turns (reserved_at) WHERE status IN ('reserved','running')`);
|
|
196465
|
+
}
|
|
196466
|
+
var ident10, PostgresChatTurnStore, isUniqueViolation2, iso3, rowToTurn;
|
|
196467
|
+
var init_postgres_chat_turns = __esm({
|
|
196468
|
+
"../storage/src/postgres-chat-turns.ts"() {
|
|
196469
|
+
"use strict";
|
|
196470
|
+
init_esm();
|
|
196471
|
+
ident10 = (s2) => {
|
|
196472
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
196473
|
+
return s2;
|
|
196474
|
+
};
|
|
196475
|
+
PostgresChatTurnStore = class _PostgresChatTurnStore {
|
|
196476
|
+
constructor(pool, schema) {
|
|
196477
|
+
this.pool = pool;
|
|
196478
|
+
this.s = `"${ident10(schema)}"`;
|
|
196479
|
+
}
|
|
196480
|
+
s;
|
|
196481
|
+
static async open(pool, schema = "public") {
|
|
196482
|
+
await ensureChatTurnsTable(pool, schema);
|
|
196483
|
+
return new _PostgresChatTurnStore(pool, schema);
|
|
196484
|
+
}
|
|
196485
|
+
async claimTurn(input) {
|
|
196486
|
+
try {
|
|
196487
|
+
const r = await this.pool.query(
|
|
196488
|
+
`INSERT INTO ${this.s}.chat_session_turns
|
|
196489
|
+
(id, chat_session_id, source, source_run_id, message_key, status, reserved_at)
|
|
196490
|
+
VALUES ($1,$2,$3,$4,$5,'reserved',$6)
|
|
196491
|
+
RETURNING *`,
|
|
196492
|
+
[input.turnId, input.chatSessionId, input.source, input.sourceRunId, input.messageKey, input.reservedAt]
|
|
196493
|
+
);
|
|
196494
|
+
return { outcome: "claimed", turn: rowToTurn(r.rows[0]) };
|
|
196495
|
+
} catch (err) {
|
|
196496
|
+
if (!isUniqueViolation2(err)) throw err;
|
|
196497
|
+
const mine = await this.getTurnBySourceRun(input.source, input.sourceRunId);
|
|
196498
|
+
if (mine) return { outcome: "resumed", turn: mine };
|
|
196499
|
+
const active = await this.activeTurn(input.chatSessionId);
|
|
196500
|
+
if (active) return { outcome: "busy", activeTurn: active };
|
|
196501
|
+
throw err;
|
|
196502
|
+
}
|
|
196503
|
+
}
|
|
196504
|
+
async getTurn(turnId) {
|
|
196505
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.chat_session_turns WHERE id = $1`, [turnId]);
|
|
196506
|
+
return r.rows[0] ? rowToTurn(r.rows[0]) : null;
|
|
196507
|
+
}
|
|
196508
|
+
async getTurnBySourceRun(source, sourceRunId) {
|
|
196509
|
+
const r = await this.pool.query(
|
|
196510
|
+
`SELECT * FROM ${this.s}.chat_session_turns WHERE source = $1 AND source_run_id = $2`,
|
|
196511
|
+
[source, sourceRunId]
|
|
196512
|
+
);
|
|
196513
|
+
return r.rows[0] ? rowToTurn(r.rows[0]) : null;
|
|
196514
|
+
}
|
|
196515
|
+
async activeTurn(chatSessionId) {
|
|
196516
|
+
const r = await this.pool.query(
|
|
196517
|
+
`SELECT * FROM ${this.s}.chat_session_turns
|
|
196518
|
+
WHERE chat_session_id = $1 AND status IN ('reserved','running') LIMIT 1`,
|
|
196519
|
+
[chatSessionId]
|
|
196520
|
+
);
|
|
196521
|
+
return r.rows[0] ? rowToTurn(r.rows[0]) : null;
|
|
196522
|
+
}
|
|
196523
|
+
async settleTurn(turnId, patch) {
|
|
196524
|
+
const sets = [];
|
|
196525
|
+
const args = [];
|
|
196526
|
+
args.push(patch.status);
|
|
196527
|
+
sets.push(`status = $${args.length}`);
|
|
196528
|
+
if (patch.dispatchId !== void 0) {
|
|
196529
|
+
args.push(patch.dispatchId);
|
|
196530
|
+
sets.push(`dispatch_id = $${args.length}`);
|
|
196531
|
+
}
|
|
196532
|
+
if (patch.assistantRunId !== void 0) {
|
|
196533
|
+
args.push(patch.assistantRunId);
|
|
196534
|
+
sets.push(`assistant_run_id = $${args.length}`);
|
|
196535
|
+
}
|
|
196536
|
+
if (patch.startedAt !== void 0) {
|
|
196537
|
+
args.push(patch.startedAt);
|
|
196538
|
+
sets.push(`started_at = $${args.length}`);
|
|
196539
|
+
}
|
|
196540
|
+
if (patch.completedAt !== void 0) {
|
|
196541
|
+
args.push(patch.completedAt);
|
|
196542
|
+
sets.push(`completed_at = $${args.length}`);
|
|
196543
|
+
}
|
|
196544
|
+
if (patch.lastError !== void 0) {
|
|
196545
|
+
args.push(patch.lastError);
|
|
196546
|
+
sets.push(`last_error = $${args.length}`);
|
|
196547
|
+
}
|
|
196548
|
+
args.push(turnId);
|
|
196549
|
+
await this.pool.query(`UPDATE ${this.s}.chat_session_turns SET ${sets.join(", ")} WHERE id = $${args.length}`, args);
|
|
196550
|
+
}
|
|
196551
|
+
async listStaleActiveTurns(reservedBefore, limit = 50) {
|
|
196552
|
+
const r = await this.pool.query(
|
|
196553
|
+
`SELECT * FROM ${this.s}.chat_session_turns
|
|
196554
|
+
WHERE status IN ('reserved','running') AND reserved_at < $1
|
|
196555
|
+
ORDER BY reserved_at ASC LIMIT $2`,
|
|
196556
|
+
[reservedBefore, limit]
|
|
196557
|
+
);
|
|
196558
|
+
return r.rows.map(rowToTurn);
|
|
196559
|
+
}
|
|
196560
|
+
};
|
|
196561
|
+
isUniqueViolation2 = (err) => Boolean(err && typeof err === "object" && err.code === "23505");
|
|
196562
|
+
iso3 = (v2) => v2 != null ? new Date(v2).toISOString() : null;
|
|
196563
|
+
rowToTurn = (row) => ({
|
|
196564
|
+
id: row.id,
|
|
196565
|
+
chatSessionId: row.chat_session_id,
|
|
196566
|
+
source: row.source,
|
|
196567
|
+
sourceRunId: row.source_run_id,
|
|
196568
|
+
messageKey: row.message_key,
|
|
196569
|
+
status: row.status,
|
|
196570
|
+
dispatchId: row.dispatch_id ?? null,
|
|
196571
|
+
assistantRunId: row.assistant_run_id ?? null,
|
|
196572
|
+
reservedAt: iso3(row.reserved_at),
|
|
196573
|
+
startedAt: iso3(row.started_at),
|
|
196574
|
+
completedAt: iso3(row.completed_at),
|
|
196575
|
+
lastError: row.last_error ?? null
|
|
196576
|
+
});
|
|
196577
|
+
}
|
|
196578
|
+
});
|
|
196579
|
+
|
|
195727
196580
|
// ../storage/src/postgres-automations.ts
|
|
195728
196581
|
function storedEventConfig(t) {
|
|
195729
196582
|
return {
|
|
@@ -195767,26 +196620,27 @@ function genWebhookToken2() {
|
|
|
195767
196620
|
const b64 = btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
195768
196621
|
return `owt_${b64}`;
|
|
195769
196622
|
}
|
|
195770
|
-
var
|
|
196623
|
+
var ident11, genId, iso4, PostgresAutomationStore, chatTurnIso, rowToChatTurn, rowToAutomation, normalizeStoredChatTarget, rowToTrigger, rowToRun2, rowToDelivery;
|
|
195771
196624
|
var init_postgres_automations = __esm({
|
|
195772
196625
|
"../storage/src/postgres-automations.ts"() {
|
|
195773
196626
|
"use strict";
|
|
195774
196627
|
init_esm();
|
|
196628
|
+
init_postgres_chat_turns();
|
|
195775
196629
|
init_src2();
|
|
195776
|
-
|
|
196630
|
+
ident11 = (s2) => {
|
|
195777
196631
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
195778
196632
|
return s2;
|
|
195779
196633
|
};
|
|
195780
196634
|
genId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
|
|
195781
|
-
|
|
196635
|
+
iso4 = (v2) => v2 != null ? new Date(v2).toISOString() : null;
|
|
195782
196636
|
PostgresAutomationStore = class _PostgresAutomationStore {
|
|
195783
196637
|
constructor(pool, schema) {
|
|
195784
196638
|
this.pool = pool;
|
|
195785
|
-
this.s = `"${
|
|
196639
|
+
this.s = `"${ident11(schema)}"`;
|
|
195786
196640
|
}
|
|
195787
196641
|
s;
|
|
195788
196642
|
static async open(pool, schema = "public") {
|
|
195789
|
-
const s2 =
|
|
196643
|
+
const s2 = ident11(schema);
|
|
195790
196644
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
195791
196645
|
await pool.query(`
|
|
195792
196646
|
CREATE TABLE IF NOT EXISTS "${s2}".automations (
|
|
@@ -195811,7 +196665,11 @@ var init_postgres_automations = __esm({
|
|
|
195811
196665
|
`ADD COLUMN IF NOT EXISTS pause_reason text`,
|
|
195812
196666
|
`ADD COLUMN IF NOT EXISTS archived_at text`,
|
|
195813
196667
|
`ADD COLUMN IF NOT EXISTS subscribers jsonb NOT NULL DEFAULT '[]'::jsonb`,
|
|
195814
|
-
`ADD COLUMN IF NOT EXISTS collaborators jsonb NOT NULL DEFAULT '[]'::jsonb
|
|
196668
|
+
`ADD COLUMN IF NOT EXISTS collaborators jsonb NOT NULL DEFAULT '[]'::jsonb`,
|
|
196669
|
+
// ADR 会话投递 D1:chat 模式的目标会话策略。NULL = 每次新开会话(存量零变化)。
|
|
196670
|
+
`ADD COLUMN IF NOT EXISTS chat_target jsonb`,
|
|
196671
|
+
// ADR 会话投递 D3:固定会话的人类归属者(进规则快照,不随触发漂移)。
|
|
196672
|
+
`ADD COLUMN IF NOT EXISTS owner_human_actor_id text`
|
|
195815
196673
|
]) {
|
|
195816
196674
|
await pool.query(`ALTER TABLE "${s2}".automations ${ddl}`);
|
|
195817
196675
|
}
|
|
@@ -195902,6 +196760,7 @@ var init_postgres_automations = __esm({
|
|
|
195902
196760
|
created_at text NOT NULL
|
|
195903
196761
|
)`);
|
|
195904
196762
|
await pool.query(`CREATE INDEX IF NOT EXISTS automation_rule_versions_idx ON "${s2}".automation_rule_versions (automation_id, created_at DESC)`);
|
|
196763
|
+
await ensureChatTurnsTable(pool, schema);
|
|
195905
196764
|
return new _PostgresAutomationStore(pool, schema);
|
|
195906
196765
|
}
|
|
195907
196766
|
now() {
|
|
@@ -195930,13 +196789,15 @@ var init_postgres_automations = __esm({
|
|
|
195930
196789
|
collaborators: [],
|
|
195931
196790
|
createdBy: input.createdBy ?? null,
|
|
195932
196791
|
createdAt: now,
|
|
195933
|
-
updatedAt: now
|
|
196792
|
+
updatedAt: now,
|
|
196793
|
+
chatTarget: input.chatTarget ?? null,
|
|
196794
|
+
ownerHumanActorId: input.ownerHumanActorId ?? null
|
|
195934
196795
|
};
|
|
195935
196796
|
await this.pool.query(
|
|
195936
196797
|
`INSERT INTO ${this.s}.automations
|
|
195937
196798
|
(id, company_id, name, description, execution_mode, agent_id, project_id, playbook_ref, payload, title_template, dispatch,
|
|
195938
|
-
enabled, pause_reason, archived_at, subscribers, collaborators, created_by, created_at, updated_at)
|
|
195939
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10,$11,$12,$13,$14,$15::jsonb,$16::jsonb,$17,$18,$19)`,
|
|
196799
|
+
enabled, pause_reason, archived_at, subscribers, collaborators, created_by, created_at, updated_at, chat_target, owner_human_actor_id)
|
|
196800
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10,$11,$12,$13,$14,$15::jsonb,$16::jsonb,$17,$18,$19,$20::jsonb,$21)`,
|
|
195940
196801
|
[
|
|
195941
196802
|
a.id,
|
|
195942
196803
|
a.companyId,
|
|
@@ -195956,7 +196817,9 @@ var init_postgres_automations = __esm({
|
|
|
195956
196817
|
JSON.stringify(a.collaborators),
|
|
195957
196818
|
a.createdBy,
|
|
195958
196819
|
a.createdAt,
|
|
195959
|
-
a.updatedAt
|
|
196820
|
+
a.updatedAt,
|
|
196821
|
+
a.chatTarget !== null ? JSON.stringify(a.chatTarget) : null,
|
|
196822
|
+
a.ownerHumanActorId
|
|
195960
196823
|
]
|
|
195961
196824
|
);
|
|
195962
196825
|
if (input.triggers?.length) await this.replaceTriggers(id, input.triggers);
|
|
@@ -195989,7 +196852,9 @@ var init_postgres_automations = __esm({
|
|
|
195989
196852
|
pauseReason: { col: "pause_reason" },
|
|
195990
196853
|
archivedAt: { col: "archived_at" },
|
|
195991
196854
|
subscribers: { col: "subscribers", json: true },
|
|
195992
|
-
collaborators: { col: "collaborators", json: true }
|
|
196855
|
+
collaborators: { col: "collaborators", json: true },
|
|
196856
|
+
chatTarget: { col: "chat_target", json: true },
|
|
196857
|
+
ownerHumanActorId: { col: "owner_human_actor_id" }
|
|
195993
196858
|
};
|
|
195994
196859
|
async updateAutomation(id, patch) {
|
|
195995
196860
|
const sets = [];
|
|
@@ -196377,7 +197242,7 @@ var init_postgres_automations = __esm({
|
|
|
196377
197242
|
);
|
|
196378
197243
|
return r.rows.map((row) => ({
|
|
196379
197244
|
trigger: rowToTrigger(row),
|
|
196380
|
-
plannedAt:
|
|
197245
|
+
plannedAt: iso4(row.planned_at_old)
|
|
196381
197246
|
}));
|
|
196382
197247
|
}
|
|
196383
197248
|
async rescheduleTrigger(triggerId, nextRunAt, lastFiredAt) {
|
|
@@ -196433,7 +197298,83 @@ var init_postgres_automations = __esm({
|
|
|
196433
197298
|
);
|
|
196434
197299
|
return r.rows[0] ? rowToRun2(r.rows[0]) : null;
|
|
196435
197300
|
}
|
|
197301
|
+
/**
|
|
197302
|
+
* 复合占位(ADR 会话投递 D5 第 4 步)。一个事务、三种出口:
|
|
197303
|
+
* - INSERT 成功 → `claimed`,调用方才可以开始产生副作用;
|
|
197304
|
+
* - 撞 `(source, source_run_id)` → `resumed`,同一触发重来,取回同一轮(**不是** busy,也不另落 run);
|
|
197305
|
+
* - 撞每会话 active 唯一索引 → `busy`:**在同一事务里**把这条 automation run 落
|
|
197306
|
+
* `skipped/session_busy/completedAt`,然后提交。两件事一起成立或一起不成立,中间没有窗口。
|
|
197307
|
+
*
|
|
197308
|
+
* 触发幂等键(`planned_at` / `idempotency_key`)**照旧保留在行上不清空**——清了就等于允许
|
|
197309
|
+
* 下一次重扫补跑这次已经明确丢弃的触发。
|
|
197310
|
+
*/
|
|
197311
|
+
async claimChatTurnForRun(input) {
|
|
197312
|
+
const client = await this.pool.connect();
|
|
197313
|
+
try {
|
|
197314
|
+
await client.query("BEGIN");
|
|
197315
|
+
try {
|
|
197316
|
+
const r = await client.query(
|
|
197317
|
+
`INSERT INTO ${this.s}.chat_session_turns
|
|
197318
|
+
(id, chat_session_id, source, source_run_id, message_key, status, reserved_at)
|
|
197319
|
+
VALUES ($1,$2,$3,$4,$5,'reserved',$6)
|
|
197320
|
+
RETURNING *`,
|
|
197321
|
+
[input.turnId, input.chatSessionId, input.source, input.sourceRunId, input.messageKey, input.reservedAt]
|
|
197322
|
+
);
|
|
197323
|
+
await client.query("COMMIT");
|
|
197324
|
+
return { outcome: "claimed", turn: rowToChatTurn(r.rows[0]) };
|
|
197325
|
+
} catch (err) {
|
|
197326
|
+
if (!isUniqueViolation2(err)) throw err;
|
|
197327
|
+
await client.query("ROLLBACK");
|
|
197328
|
+
await client.query("BEGIN");
|
|
197329
|
+
const mine = await client.query(
|
|
197330
|
+
`SELECT * FROM ${this.s}.chat_session_turns WHERE source = $1 AND source_run_id = $2`,
|
|
197331
|
+
[input.source, input.sourceRunId]
|
|
197332
|
+
);
|
|
197333
|
+
if (mine.rows[0]) {
|
|
197334
|
+
await client.query("COMMIT");
|
|
197335
|
+
return { outcome: "resumed", turn: rowToChatTurn(mine.rows[0]) };
|
|
197336
|
+
}
|
|
197337
|
+
const active = await client.query(
|
|
197338
|
+
`SELECT * FROM ${this.s}.chat_session_turns
|
|
197339
|
+
WHERE chat_session_id = $1 AND status IN ('reserved','running') LIMIT 1`,
|
|
197340
|
+
[input.chatSessionId]
|
|
197341
|
+
);
|
|
197342
|
+
if (!active.rows[0]) {
|
|
197343
|
+
await client.query("ROLLBACK");
|
|
197344
|
+
throw err;
|
|
197345
|
+
}
|
|
197346
|
+
await client.query(
|
|
197347
|
+
`UPDATE ${this.s}.automation_runs
|
|
197348
|
+
SET status = 'skipped', reason_code = 'session_busy', error = $2, completed_at = $3
|
|
197349
|
+
WHERE id = $1`,
|
|
197350
|
+
[input.automationRunId, input.busyError, input.now]
|
|
197351
|
+
);
|
|
197352
|
+
await client.query("COMMIT");
|
|
197353
|
+
return { outcome: "busy", activeTurn: rowToChatTurn(active.rows[0]) };
|
|
197354
|
+
}
|
|
197355
|
+
} catch (e) {
|
|
197356
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
197357
|
+
throw e;
|
|
197358
|
+
} finally {
|
|
197359
|
+
client.release();
|
|
197360
|
+
}
|
|
197361
|
+
}
|
|
196436
197362
|
};
|
|
197363
|
+
chatTurnIso = (v2) => v2 != null ? new Date(v2).toISOString() : null;
|
|
197364
|
+
rowToChatTurn = (row) => ({
|
|
197365
|
+
id: row.id,
|
|
197366
|
+
chatSessionId: row.chat_session_id,
|
|
197367
|
+
source: row.source,
|
|
197368
|
+
sourceRunId: row.source_run_id,
|
|
197369
|
+
messageKey: row.message_key,
|
|
197370
|
+
status: row.status,
|
|
197371
|
+
dispatchId: row.dispatch_id ?? null,
|
|
197372
|
+
assistantRunId: row.assistant_run_id ?? null,
|
|
197373
|
+
reservedAt: chatTurnIso(row.reserved_at),
|
|
197374
|
+
startedAt: chatTurnIso(row.started_at),
|
|
197375
|
+
completedAt: chatTurnIso(row.completed_at),
|
|
197376
|
+
lastError: row.last_error ?? null
|
|
197377
|
+
});
|
|
196437
197378
|
rowToAutomation = (row) => ({
|
|
196438
197379
|
id: row.id,
|
|
196439
197380
|
companyId: row.company_id ?? "",
|
|
@@ -196453,8 +197394,19 @@ var init_postgres_automations = __esm({
|
|
|
196453
197394
|
collaborators: row.collaborators ?? [],
|
|
196454
197395
|
createdBy: row.created_by ?? null,
|
|
196455
197396
|
createdAt: row.created_at,
|
|
196456
|
-
updatedAt: row.updated_at
|
|
197397
|
+
updatedAt: row.updated_at,
|
|
197398
|
+
// NULL / 脏值一律读成「每次新开会话」——发布前后行为一致(ADR 会话投递 §6)。
|
|
197399
|
+
chatTarget: normalizeStoredChatTarget(row.chat_target),
|
|
197400
|
+
ownerHumanActorId: row.owner_human_actor_id ?? null
|
|
196457
197401
|
});
|
|
197402
|
+
normalizeStoredChatTarget = (raw) => {
|
|
197403
|
+
const t = raw;
|
|
197404
|
+
if (t?.mode === "existing_session" && typeof t.sessionId === "string" && t.sessionId) {
|
|
197405
|
+
return { mode: "existing_session", sessionId: t.sessionId };
|
|
197406
|
+
}
|
|
197407
|
+
if (t?.mode === "new_session") return { mode: "new_session" };
|
|
197408
|
+
return null;
|
|
197409
|
+
};
|
|
196458
197410
|
rowToTrigger = (row) => {
|
|
196459
197411
|
const t = {
|
|
196460
197412
|
id: row.id,
|
|
@@ -196468,9 +197420,9 @@ var init_postgres_automations = __esm({
|
|
|
196468
197420
|
t.schedule = {
|
|
196469
197421
|
cron: row.cron ?? "",
|
|
196470
197422
|
tz: row.tz ?? "UTC",
|
|
196471
|
-
nextRunAt:
|
|
197423
|
+
nextRunAt: iso4(row.next_run_at),
|
|
196472
197424
|
lastFiredAt: row.last_fired_at ?? null,
|
|
196473
|
-
claimedAt:
|
|
197425
|
+
claimedAt: iso4(row.claimed_at)
|
|
196474
197426
|
};
|
|
196475
197427
|
}
|
|
196476
197428
|
if (t.kind === "webhook") {
|
|
@@ -196500,7 +197452,7 @@ var init_postgres_automations = __esm({
|
|
|
196500
197452
|
status: row.status,
|
|
196501
197453
|
spawnedWorkspaceId: row.spawned_workspace_id ?? null,
|
|
196502
197454
|
spawnedArtifactId: row.spawned_artifact_id ?? null,
|
|
196503
|
-
plannedAt:
|
|
197455
|
+
plannedAt: iso4(row.planned_at),
|
|
196504
197456
|
deliveryId: row.delivery_id ?? null,
|
|
196505
197457
|
idempotencyKey: row.idempotency_key ?? null,
|
|
196506
197458
|
ruleVersionId: row.rule_version_id ?? null,
|
|
@@ -196532,24 +197484,24 @@ var init_postgres_automations = __esm({
|
|
|
196532
197484
|
});
|
|
196533
197485
|
|
|
196534
197486
|
// ../storage/src/postgres-chat-sessions.ts
|
|
196535
|
-
var
|
|
197487
|
+
var ident12, PostgresChatSessionStore, rowToSession, rowToSessionWithQuality, rowToMessage;
|
|
196536
197488
|
var init_postgres_chat_sessions = __esm({
|
|
196537
197489
|
"../storage/src/postgres-chat-sessions.ts"() {
|
|
196538
197490
|
"use strict";
|
|
196539
197491
|
init_esm();
|
|
196540
197492
|
init_chat_session();
|
|
196541
|
-
|
|
197493
|
+
ident12 = (s2) => {
|
|
196542
197494
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
196543
197495
|
return s2;
|
|
196544
197496
|
};
|
|
196545
197497
|
PostgresChatSessionStore = class _PostgresChatSessionStore {
|
|
196546
197498
|
constructor(pool, schema) {
|
|
196547
197499
|
this.pool = pool;
|
|
196548
|
-
this.s = `"${
|
|
197500
|
+
this.s = `"${ident12(schema)}"`;
|
|
196549
197501
|
}
|
|
196550
197502
|
s;
|
|
196551
197503
|
static async open(pool, schema = "public") {
|
|
196552
|
-
const s2 =
|
|
197504
|
+
const s2 = ident12(schema);
|
|
196553
197505
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
196554
197506
|
await pool.query(`
|
|
196555
197507
|
CREATE TABLE IF NOT EXISTS "${s2}".chat_sessions (
|
|
@@ -196592,6 +197544,10 @@ var init_postgres_chat_sessions = __esm({
|
|
|
196592
197544
|
)
|
|
196593
197545
|
UPDATE "${s2}".chat_messages m SET seq = ranked.rn FROM ranked WHERE m.id = ranked.id AND m.seq <> ranked.rn`);
|
|
196594
197546
|
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_messages_session_seq_uq ON "${s2}".chat_messages (session_id, seq)`);
|
|
197547
|
+
await pool.query(`ALTER TABLE "${s2}".chat_sessions ADD COLUMN IF NOT EXISTS company_id text`);
|
|
197548
|
+
await pool.query(`CREATE INDEX IF NOT EXISTS chat_sessions_target_idx ON "${s2}".chat_sessions (company_id, human_actor_id, ai_actor_id, touched_at DESC)`);
|
|
197549
|
+
await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS message_key text`);
|
|
197550
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_messages_key_uq ON "${s2}".chat_messages (message_key) WHERE message_key IS NOT NULL`);
|
|
196595
197551
|
await pool.query(`
|
|
196596
197552
|
CREATE TABLE IF NOT EXISTS "${s2}".chat_session_work_orders (
|
|
196597
197553
|
session_id text NOT NULL,
|
|
@@ -196603,9 +197559,9 @@ var init_postgres_chat_sessions = __esm({
|
|
|
196603
197559
|
}
|
|
196604
197560
|
async createSession(s2) {
|
|
196605
197561
|
await this.pool.query(
|
|
196606
|
-
`INSERT INTO ${this.s}.chat_sessions (id, human_actor_id, ai_actor_id, runtime_id, runtime_kind, runtime_session_id, title, touched_at, created_at, workspace, analyzed_run_id, project_id, escalation_id)
|
|
196607
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)`,
|
|
196608
|
-
[s2.id, s2.humanActorId, s2.aiActorId, s2.runtimeId, s2.runtimeKind ?? null, s2.runtimeSessionId ?? null, s2.title ?? null, s2.touchedAt, s2.createdAt, s2.workspace ?? null, s2.analyzedRunId ?? null, s2.projectId ?? null, s2.escalationId ?? null]
|
|
197562
|
+
`INSERT INTO ${this.s}.chat_sessions (id, human_actor_id, ai_actor_id, runtime_id, runtime_kind, runtime_session_id, title, touched_at, created_at, workspace, analyzed_run_id, project_id, escalation_id, company_id)
|
|
197563
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
|
|
197564
|
+
[s2.id, s2.humanActorId, s2.aiActorId, s2.runtimeId, s2.runtimeKind ?? null, s2.runtimeSessionId ?? null, s2.title ?? null, s2.touchedAt, s2.createdAt, s2.workspace ?? null, s2.analyzedRunId ?? null, s2.projectId ?? null, s2.escalationId ?? null, s2.companyId ?? null]
|
|
196609
197565
|
);
|
|
196610
197566
|
}
|
|
196611
197567
|
async listSessions(humanActorId) {
|
|
@@ -196718,11 +197674,11 @@ var init_postgres_chat_sessions = __esm({
|
|
|
196718
197674
|
async appendMessage(m2) {
|
|
196719
197675
|
const insert = async () => {
|
|
196720
197676
|
const r = await this.pool.query(
|
|
196721
|
-
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts, attachments, status, completed_at)
|
|
196722
|
-
SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7,$8,$9,$10
|
|
197677
|
+
`INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts, attachments, status, completed_at, message_key)
|
|
197678
|
+
SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7,$8,$9,$10,$11
|
|
196723
197679
|
FROM ${this.s}.chat_messages WHERE session_id = $2
|
|
196724
197680
|
RETURNING seq`,
|
|
196725
|
-
[m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null, m2.attachments !== void 0 ? JSON.stringify(m2.attachments) : null, m2.status ?? null, m2.completedAt ?? null]
|
|
197681
|
+
[m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null, m2.attachments !== void 0 ? JSON.stringify(m2.attachments) : null, m2.status ?? null, m2.completedAt ?? null, m2.messageKey ?? null]
|
|
196726
197682
|
);
|
|
196727
197683
|
return Number(r.rows[0].seq);
|
|
196728
197684
|
};
|
|
@@ -196730,12 +197686,87 @@ var init_postgres_chat_sessions = __esm({
|
|
|
196730
197686
|
try {
|
|
196731
197687
|
return { ...m2, seq: await insert() };
|
|
196732
197688
|
} catch (err) {
|
|
196733
|
-
const
|
|
196734
|
-
if (
|
|
197689
|
+
const isUniqueViolation3 = err && typeof err === "object" && err.code === "23505";
|
|
197690
|
+
if (isUniqueViolation3 && m2.messageKey && String(err.constraint ?? "").includes("chat_messages_key_uq")) {
|
|
197691
|
+
const existing = await this.getMessageByKey(m2.messageKey);
|
|
197692
|
+
if (existing) return existing;
|
|
197693
|
+
}
|
|
197694
|
+
if (isUniqueViolation3 && attempt < 10) continue;
|
|
196735
197695
|
throw err;
|
|
196736
197696
|
}
|
|
196737
197697
|
}
|
|
196738
197698
|
}
|
|
197699
|
+
/** 按幂等键取回消息(崩溃重放路径)。 */
|
|
197700
|
+
async getMessageByKey(messageKey) {
|
|
197701
|
+
const r = await this.pool.query(`SELECT * FROM ${this.s}.chat_messages WHERE message_key = $1`, [messageKey]);
|
|
197702
|
+
return r.rows[0] ? rowToMessage(r.rows[0]) : null;
|
|
197703
|
+
}
|
|
197704
|
+
async countMessages(sessionId) {
|
|
197705
|
+
const r = await this.pool.query(`SELECT count(*)::int AS n FROM ${this.s}.chat_messages WHERE session_id = $1`, [sessionId]);
|
|
197706
|
+
return Number(r.rows[0]?.n ?? 0);
|
|
197707
|
+
}
|
|
197708
|
+
/**
|
|
197709
|
+
* 选择器搜索(ADR 会话投递 D8):`(company, human, ai)` 三元组精确圈定,标题 ILIKE **或** id 前缀命中。
|
|
197710
|
+
* 分析会话排除在外(它们不进主聊天列表,更不该当自动化的长期目标)。
|
|
197711
|
+
* 多取一条判 `hasMore`,避免再跑一次 count。
|
|
197712
|
+
*/
|
|
197713
|
+
async searchSessionsForTarget(q) {
|
|
197714
|
+
const args = [q.companyId, q.humanActorId, q.aiActorId];
|
|
197715
|
+
let where = `s.company_id = $1 AND s.human_actor_id = $2 AND s.ai_actor_id = $3 AND s.analyzed_run_id IS NULL`;
|
|
197716
|
+
const text = q.text?.trim();
|
|
197717
|
+
if (text) {
|
|
197718
|
+
const literal2 = text.replace(/[%_\\]/g, (c) => `\\${c}`);
|
|
197719
|
+
args.push(`%${literal2}%`);
|
|
197720
|
+
args.push(`${literal2}%`);
|
|
197721
|
+
where += ` AND (s.title ILIKE $${args.length - 1} ESCAPE '\\' OR s.id LIKE $${args.length} ESCAPE '\\')`;
|
|
197722
|
+
}
|
|
197723
|
+
args.push(q.limit + 1, q.offset);
|
|
197724
|
+
const r = await this.pool.query(
|
|
197725
|
+
`SELECT s.*, (SELECT count(*)::int FROM ${this.s}.chat_messages m WHERE m.session_id = s.id) AS message_count
|
|
197726
|
+
FROM ${this.s}.chat_sessions s
|
|
197727
|
+
WHERE ${where}
|
|
197728
|
+
ORDER BY s.touched_at DESC, s.id DESC
|
|
197729
|
+
LIMIT $${args.length - 1} OFFSET $${args.length}`,
|
|
197730
|
+
args
|
|
197731
|
+
);
|
|
197732
|
+
const hasMore = r.rows.length > q.limit;
|
|
197733
|
+
const rows = hasMore ? r.rows.slice(0, q.limit) : r.rows;
|
|
197734
|
+
return {
|
|
197735
|
+
items: rows.map((row) => ({ ...rowToSession(row), messageCount: Number(row.message_count ?? 0) })),
|
|
197736
|
+
hasMore
|
|
197737
|
+
};
|
|
197738
|
+
}
|
|
197739
|
+
/**
|
|
197740
|
+
* 存量会话的公司回填(一次性、幂等、**只填能唯一判定的行**)。
|
|
197741
|
+
*
|
|
197742
|
+
* 判据只认一种:会话归属人在 `company_members` 里**只出现在一家公司**(agent 不是账号、
|
|
197743
|
+
* 不在这张表里,所以实际生效的是归属人这一条)。命中多家或一家都命不中 → 保持 NULL。
|
|
197744
|
+
*
|
|
197745
|
+
* 这不是保守过头——用「现在的成员关系」追认「过去的创建事实」,一个换过公司的人就能把旧会话
|
|
197746
|
+
* 认到新公司名下,而那是跨公司泄漏。判不出来的代价只是「不能被自动化选作固定目标」,
|
|
197747
|
+
* 用户新建一个长期会话即可。
|
|
197748
|
+
*
|
|
197749
|
+
* 返回真正回填的行数(稳态为 0)。表不存在(无控制面)时直接跳过。
|
|
197750
|
+
*/
|
|
197751
|
+
async backfillCompanyIds() {
|
|
197752
|
+
const exists = await this.pool.query(
|
|
197753
|
+
`SELECT to_regclass($1) IS NOT NULL AS ok`,
|
|
197754
|
+
[`${this.s}.company_members`.replace(/"/g, "")]
|
|
197755
|
+
);
|
|
197756
|
+
if (!exists.rows[0]?.ok) return 0;
|
|
197757
|
+
const r = await this.pool.query(`
|
|
197758
|
+
WITH candidate AS (
|
|
197759
|
+
SELECT s.id, min(m.company_id) AS company_id, count(DISTINCT m.company_id) AS n
|
|
197760
|
+
FROM ${this.s}.chat_sessions s
|
|
197761
|
+
JOIN ${this.s}.company_members m ON m.actor_id IN (s.human_actor_id, s.ai_actor_id)
|
|
197762
|
+
WHERE s.company_id IS NULL
|
|
197763
|
+
GROUP BY s.id
|
|
197764
|
+
HAVING count(DISTINCT m.company_id) = 1
|
|
197765
|
+
)
|
|
197766
|
+
UPDATE ${this.s}.chat_sessions s SET company_id = c.company_id
|
|
197767
|
+
FROM candidate c WHERE s.id = c.id AND s.company_id IS NULL`);
|
|
197768
|
+
return r.rowCount ?? 0;
|
|
197769
|
+
}
|
|
196739
197770
|
async updateMessage(id, patch) {
|
|
196740
197771
|
const sets = [];
|
|
196741
197772
|
const args = [];
|
|
@@ -196816,6 +197847,7 @@ var init_postgres_chat_sessions = __esm({
|
|
|
196816
197847
|
};
|
|
196817
197848
|
rowToSession = (row) => ({
|
|
196818
197849
|
id: row.id,
|
|
197850
|
+
companyId: row.company_id ?? null,
|
|
196819
197851
|
humanActorId: row.human_actor_id,
|
|
196820
197852
|
aiActorId: row.ai_actor_id,
|
|
196821
197853
|
runtimeId: row.runtime_id,
|
|
@@ -196847,30 +197879,33 @@ var init_postgres_chat_sessions = __esm({
|
|
|
196847
197879
|
...row.parts != null ? { parts: row.parts } : {},
|
|
196848
197880
|
...row.attachments != null ? { attachments: row.attachments } : {},
|
|
196849
197881
|
...row.status != null ? { status: row.status } : {},
|
|
196850
|
-
...row.completed_at != null ? { completedAt: new Date(row.completed_at).toISOString() } : {}
|
|
197882
|
+
...row.completed_at != null ? { completedAt: new Date(row.completed_at).toISOString() } : {},
|
|
197883
|
+
// 幂等键必须**读得回来**:写进去却映射不出来,等于这一列只对唯一索引有效,对代码不可见。
|
|
197884
|
+
// 固定会话的续跑历史要按它把「本轮那条 user 消息」排掉,漏了它那条消息会既当历史又当正文。
|
|
197885
|
+
...row.message_key != null ? { messageKey: row.message_key } : {}
|
|
196851
197886
|
});
|
|
196852
197887
|
}
|
|
196853
197888
|
});
|
|
196854
197889
|
|
|
196855
197890
|
// ../storage/src/postgres-nodes.ts
|
|
196856
|
-
var
|
|
197891
|
+
var import_node_crypto46, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToRuntime;
|
|
196857
197892
|
var init_postgres_nodes = __esm({
|
|
196858
197893
|
"../storage/src/postgres-nodes.ts"() {
|
|
196859
197894
|
"use strict";
|
|
196860
|
-
|
|
197895
|
+
import_node_crypto46 = require("node:crypto");
|
|
196861
197896
|
init_esm();
|
|
196862
|
-
|
|
197897
|
+
ident13 = (s2) => {
|
|
196863
197898
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
196864
197899
|
return s2;
|
|
196865
197900
|
};
|
|
196866
197901
|
PostgresNodeStore = class _PostgresNodeStore {
|
|
196867
197902
|
constructor(pool, schema) {
|
|
196868
197903
|
this.pool = pool;
|
|
196869
|
-
this.s = `"${
|
|
197904
|
+
this.s = `"${ident13(schema)}"`;
|
|
196870
197905
|
}
|
|
196871
197906
|
s;
|
|
196872
197907
|
static async open(pool, schema = "public") {
|
|
196873
|
-
const s2 =
|
|
197908
|
+
const s2 = ident13(schema);
|
|
196874
197909
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
196875
197910
|
await pool.query(`
|
|
196876
197911
|
CREATE TABLE IF NOT EXISTS "${s2}".nodes (
|
|
@@ -196992,12 +198027,12 @@ var init_postgres_nodes = __esm({
|
|
|
196992
198027
|
PostgresNodeTokenStore = class _PostgresNodeTokenStore {
|
|
196993
198028
|
constructor(pool, schema) {
|
|
196994
198029
|
this.pool = pool;
|
|
196995
|
-
this.s = `"${
|
|
198030
|
+
this.s = `"${ident13(schema)}"`;
|
|
196996
198031
|
}
|
|
196997
198032
|
s;
|
|
196998
198033
|
cache = /* @__PURE__ */ new Map();
|
|
196999
198034
|
static async open(pool, schema = "public") {
|
|
197000
|
-
const s2 =
|
|
198035
|
+
const s2 = ident13(schema);
|
|
197001
198036
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
197002
198037
|
await pool.query(`CREATE TABLE IF NOT EXISTS "${s2}".node_tokens (token text PRIMARY KEY, node_id text NOT NULL)`);
|
|
197003
198038
|
const store = new _PostgresNodeTokenStore(pool, schema);
|
|
@@ -197006,7 +198041,7 @@ var init_postgres_nodes = __esm({
|
|
|
197006
198041
|
return store;
|
|
197007
198042
|
}
|
|
197008
198043
|
issue(nodeId) {
|
|
197009
|
-
const token = `ont_${(0,
|
|
198044
|
+
const token = `ont_${(0, import_node_crypto46.randomBytes)(24).toString("base64url")}`;
|
|
197010
198045
|
this.cache.set(token, nodeId);
|
|
197011
198046
|
void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
|
|
197012
198047
|
return token;
|
|
@@ -197058,23 +198093,23 @@ function rowToPrice(row) {
|
|
|
197058
198093
|
updatedAt: new Date(row["updated_at"]).toISOString()
|
|
197059
198094
|
};
|
|
197060
198095
|
}
|
|
197061
|
-
var
|
|
198096
|
+
var ident14, PostgresModelPriceStore;
|
|
197062
198097
|
var init_postgres_model_prices = __esm({
|
|
197063
198098
|
"../storage/src/postgres-model-prices.ts"() {
|
|
197064
198099
|
"use strict";
|
|
197065
198100
|
init_esm();
|
|
197066
|
-
|
|
198101
|
+
ident14 = (s2) => {
|
|
197067
198102
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
197068
198103
|
return s2;
|
|
197069
198104
|
};
|
|
197070
198105
|
PostgresModelPriceStore = class _PostgresModelPriceStore {
|
|
197071
198106
|
constructor(pool, schema) {
|
|
197072
198107
|
this.pool = pool;
|
|
197073
|
-
this.s = `"${
|
|
198108
|
+
this.s = `"${ident14(schema)}"`;
|
|
197074
198109
|
}
|
|
197075
198110
|
s;
|
|
197076
198111
|
static async open(pool, schema = "public") {
|
|
197077
|
-
const s2 =
|
|
198112
|
+
const s2 = ident14(schema);
|
|
197078
198113
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
197079
198114
|
await pool.query(`
|
|
197080
198115
|
CREATE TABLE IF NOT EXISTS "${s2}".model_prices (
|
|
@@ -197171,24 +198206,24 @@ async function backfillTypeRegistryFromFile(store, file) {
|
|
|
197171
198206
|
console.log(`[type-registry-backfill] \u51B3\u7B56 0062 \u56DE\u586B\uFF1A${defs.length} \u4E2A\u4EA7\u7269\u7C7B\u578B\u5DF2\u8FC1\u5165 Postgres`);
|
|
197172
198207
|
return { filled: defs.length };
|
|
197173
198208
|
}
|
|
197174
|
-
var import_node_fs18,
|
|
198209
|
+
var import_node_fs18, ident15, PostgresTypeRegistryStore;
|
|
197175
198210
|
var init_postgres_type_registry = __esm({
|
|
197176
198211
|
"../storage/src/postgres-type-registry.ts"() {
|
|
197177
198212
|
"use strict";
|
|
197178
198213
|
import_node_fs18 = __toESM(require("node:fs"), 1);
|
|
197179
198214
|
init_esm();
|
|
197180
|
-
|
|
198215
|
+
ident15 = (s2) => {
|
|
197181
198216
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
197182
198217
|
return s2;
|
|
197183
198218
|
};
|
|
197184
198219
|
PostgresTypeRegistryStore = class _PostgresTypeRegistryStore {
|
|
197185
198220
|
constructor(pool, schema) {
|
|
197186
198221
|
this.pool = pool;
|
|
197187
|
-
this.s = `"${
|
|
198222
|
+
this.s = `"${ident15(schema)}"`;
|
|
197188
198223
|
}
|
|
197189
198224
|
s;
|
|
197190
198225
|
static async open(pool, schema = "public") {
|
|
197191
|
-
const s2 =
|
|
198226
|
+
const s2 = ident15(schema);
|
|
197192
198227
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
197193
198228
|
await pool.query(`
|
|
197194
198229
|
CREATE TABLE IF NOT EXISTS "${s2}".artifact_types (
|
|
@@ -197218,14 +198253,14 @@ var init_postgres_type_registry = __esm({
|
|
|
197218
198253
|
});
|
|
197219
198254
|
|
|
197220
198255
|
// ../storage/src/postgres-actor-memory.ts
|
|
197221
|
-
var
|
|
198256
|
+
var import_node_crypto47, matchClause, ident16, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
|
|
197222
198257
|
var init_postgres_actor_memory = __esm({
|
|
197223
198258
|
"../storage/src/postgres-actor-memory.ts"() {
|
|
197224
198259
|
"use strict";
|
|
197225
|
-
|
|
198260
|
+
import_node_crypto47 = require("node:crypto");
|
|
197226
198261
|
init_src2();
|
|
197227
198262
|
matchClause = (q) => q.requireMatch && q.keywords.length > 0 ? " WHERE m > 0" : "";
|
|
197228
|
-
|
|
198263
|
+
ident16 = (s2) => {
|
|
197229
198264
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
197230
198265
|
return s2;
|
|
197231
198266
|
};
|
|
@@ -197233,11 +198268,11 @@ var init_postgres_actor_memory = __esm({
|
|
|
197233
198268
|
constructor(pool, schema, trigram) {
|
|
197234
198269
|
this.pool = pool;
|
|
197235
198270
|
this.trigram = trigram;
|
|
197236
|
-
this.s = `"${
|
|
198271
|
+
this.s = `"${ident16(schema)}"`;
|
|
197237
198272
|
}
|
|
197238
198273
|
s;
|
|
197239
198274
|
static async open(pool, schema = "public") {
|
|
197240
|
-
const s2 =
|
|
198275
|
+
const s2 = ident16(schema);
|
|
197241
198276
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
197242
198277
|
await pool.query(`
|
|
197243
198278
|
CREATE TABLE IF NOT EXISTS "${s2}".actor_memories (
|
|
@@ -197427,7 +198462,7 @@ var init_postgres_actor_memory = __esm({
|
|
|
197427
198462
|
}
|
|
197428
198463
|
async write(input, now) {
|
|
197429
198464
|
if (input.memId === void 0) {
|
|
197430
|
-
const memId = `mem:${(0,
|
|
198465
|
+
const memId = `mem:${(0, import_node_crypto47.randomUUID)()}`;
|
|
197431
198466
|
const r2 = await this.pool.query(
|
|
197432
198467
|
`INSERT INTO ${this.s}.actor_memories
|
|
197433
198468
|
(mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
|
|
@@ -197521,23 +198556,23 @@ var init_postgres_actor_memory = __esm({
|
|
|
197521
198556
|
});
|
|
197522
198557
|
|
|
197523
198558
|
// ../storage/src/postgres-inbox-read.ts
|
|
197524
|
-
var
|
|
198559
|
+
var ident17, PostgresReadMarkerStore;
|
|
197525
198560
|
var init_postgres_inbox_read = __esm({
|
|
197526
198561
|
"../storage/src/postgres-inbox-read.ts"() {
|
|
197527
198562
|
"use strict";
|
|
197528
198563
|
init_esm();
|
|
197529
|
-
|
|
198564
|
+
ident17 = (s2) => {
|
|
197530
198565
|
if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
|
|
197531
198566
|
return s2;
|
|
197532
198567
|
};
|
|
197533
198568
|
PostgresReadMarkerStore = class _PostgresReadMarkerStore {
|
|
197534
198569
|
constructor(pool, schema) {
|
|
197535
198570
|
this.pool = pool;
|
|
197536
|
-
this.s = `"${
|
|
198571
|
+
this.s = `"${ident17(schema)}"`;
|
|
197537
198572
|
}
|
|
197538
198573
|
s;
|
|
197539
198574
|
static async open(pool, schema = "public") {
|
|
197540
|
-
const s2 =
|
|
198575
|
+
const s2 = ident17(schema);
|
|
197541
198576
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
197542
198577
|
await pool.query(`
|
|
197543
198578
|
CREATE TABLE IF NOT EXISTS "${s2}".inbox_read_markers (
|
|
@@ -197615,18 +198650,18 @@ function rowToRecord2(r) {
|
|
|
197615
198650
|
reason: r.reason
|
|
197616
198651
|
};
|
|
197617
198652
|
}
|
|
197618
|
-
var
|
|
198653
|
+
var ident18, PostgresDispatchStore;
|
|
197619
198654
|
var init_postgres_dispatches = __esm({
|
|
197620
198655
|
"../storage/src/postgres-dispatches.ts"() {
|
|
197621
198656
|
"use strict";
|
|
197622
|
-
|
|
198657
|
+
ident18 = (schema) => schema.replace(/[^a-zA-Z0-9_]/g, "");
|
|
197623
198658
|
PostgresDispatchStore = class _PostgresDispatchStore {
|
|
197624
198659
|
constructor(pool, s2) {
|
|
197625
198660
|
this.pool = pool;
|
|
197626
198661
|
this.s = s2;
|
|
197627
198662
|
}
|
|
197628
198663
|
static async open(pool, schema = "public") {
|
|
197629
|
-
const s2 =
|
|
198664
|
+
const s2 = ident18(schema);
|
|
197630
198665
|
await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
|
|
197631
198666
|
await pool.query(`
|
|
197632
198667
|
CREATE TABLE IF NOT EXISTS "${s2}".dispatches (
|
|
@@ -197764,6 +198799,7 @@ __export(src_exports, {
|
|
|
197764
198799
|
PostgresBlobStore: () => PostgresBlobStore,
|
|
197765
198800
|
PostgresChannelStore: () => PostgresChannelStore,
|
|
197766
198801
|
PostgresChatSessionStore: () => PostgresChatSessionStore,
|
|
198802
|
+
PostgresChatTurnStore: () => PostgresChatTurnStore,
|
|
197767
198803
|
PostgresControlPlaneStore: () => PostgresControlPlaneStore,
|
|
197768
198804
|
PostgresDispatchStore: () => PostgresDispatchStore,
|
|
197769
198805
|
PostgresHumanPrefsStore: () => PostgresHumanPrefsStore,
|
|
@@ -197780,8 +198816,10 @@ __export(src_exports, {
|
|
|
197780
198816
|
buildTrigger: () => buildTrigger,
|
|
197781
198817
|
createPgPool: () => createPgPool,
|
|
197782
198818
|
dropSchemaIfExists: () => dropSchemaIfExists,
|
|
198819
|
+
ensureChatTurnsTable: () => ensureChatTurnsTable,
|
|
197783
198820
|
genWebhookToken: () => genWebhookToken2,
|
|
197784
198821
|
hasPgUnstorable: () => hasPgUnstorable,
|
|
198822
|
+
isUniqueViolation: () => isUniqueViolation2,
|
|
197785
198823
|
scrubPgJson: () => scrubPgJson,
|
|
197786
198824
|
scrubPgString: () => scrubPgString
|
|
197787
198825
|
});
|
|
@@ -197799,6 +198837,7 @@ var init_src11 = __esm({
|
|
|
197799
198837
|
init_pg_sanitize();
|
|
197800
198838
|
init_postgres_automations();
|
|
197801
198839
|
init_postgres_chat_sessions();
|
|
198840
|
+
init_postgres_chat_turns();
|
|
197802
198841
|
init_postgres_nodes();
|
|
197803
198842
|
init_postgres_model_prices();
|
|
197804
198843
|
init_postgres_type_registry();
|
|
@@ -197940,20 +198979,20 @@ var fs42 = __toESM(require("node:fs"));
|
|
|
197940
198979
|
var os13 = __toESM(require("node:os"));
|
|
197941
198980
|
var path36 = __toESM(require("node:path"));
|
|
197942
198981
|
var import_node_child_process20 = require("node:child_process");
|
|
197943
|
-
var
|
|
198982
|
+
var import_node_crypto54 = require("node:crypto");
|
|
197944
198983
|
|
|
197945
198984
|
// ../cli/src/cli.ts
|
|
197946
198985
|
var fs37 = __toESM(require("node:fs"), 1);
|
|
197947
198986
|
var os11 = __toESM(require("node:os"), 1);
|
|
197948
198987
|
var path31 = __toESM(require("node:path"), 1);
|
|
197949
|
-
var
|
|
198988
|
+
var import_node_crypto52 = require("node:crypto");
|
|
197950
198989
|
|
|
197951
198990
|
// ../cli/src/serve.ts
|
|
197952
198991
|
var fs31 = __toESM(require("node:fs"), 1);
|
|
197953
198992
|
var os7 = __toESM(require("node:os"), 1);
|
|
197954
198993
|
var path25 = __toESM(require("node:path"), 1);
|
|
197955
198994
|
var import_node_child_process15 = require("node:child_process");
|
|
197956
|
-
var
|
|
198995
|
+
var import_node_crypto48 = require("node:crypto");
|
|
197957
198996
|
var import_node_url7 = require("node:url");
|
|
197958
198997
|
init_src5();
|
|
197959
198998
|
init_src10();
|
|
@@ -198217,6 +199256,14 @@ init_src10();
|
|
|
198217
199256
|
init_src9();
|
|
198218
199257
|
init_src2();
|
|
198219
199258
|
init_trajectory();
|
|
199259
|
+
function deterministicUuid(seed) {
|
|
199260
|
+
const h = (0, import_node_crypto48.createHash)("sha256").update(seed).digest();
|
|
199261
|
+
const b2 = Buffer.from(h.subarray(0, 16));
|
|
199262
|
+
b2[6] = b2[6] & 15 | 80;
|
|
199263
|
+
b2[8] = b2[8] & 63 | 128;
|
|
199264
|
+
const hex = b2.toString("hex");
|
|
199265
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
199266
|
+
}
|
|
198220
199267
|
function builtinSkillsDir() {
|
|
198221
199268
|
const override = process.env["OASIS_BUILTIN_SKILLS_DIR"]?.trim();
|
|
198222
199269
|
if (override) return override;
|
|
@@ -198844,11 +199891,19 @@ async function startServe(opts) {
|
|
|
198844
199891
|
let projectStateStore;
|
|
198845
199892
|
let artifactStateStore;
|
|
198846
199893
|
let chatSessionStore;
|
|
199894
|
+
let chatTurnStore;
|
|
198847
199895
|
let readMarkerStore;
|
|
198848
199896
|
if (pgDsn && pgPool) {
|
|
198849
199897
|
projectStateStore = await PostgresProjectStateStore.open(pgPool, pgSchema);
|
|
198850
199898
|
artifactStateStore = await PostgresArtifactStateStore.open(pgPool, pgSchema);
|
|
198851
|
-
|
|
199899
|
+
const pgChatSessions = await PostgresChatSessionStore.open(pgPool, pgSchema);
|
|
199900
|
+
chatSessionStore = pgChatSessions;
|
|
199901
|
+
chatTurnStore = await PostgresChatTurnStore.open(pgPool, pgSchema);
|
|
199902
|
+
const backfilled = await pgChatSessions.backfillCompanyIds().catch((e) => {
|
|
199903
|
+
console.warn(`[serve] \u4F1A\u8BDD\u516C\u53F8\u5F52\u5C5E\u56DE\u586B\u8DF3\u8FC7\uFF1A${String(e)}`);
|
|
199904
|
+
return 0;
|
|
199905
|
+
});
|
|
199906
|
+
if (backfilled > 0) console.log(`[serve] \u4F1A\u8BDD\u516C\u53F8\u5F52\u5C5E\uFF1A\u56DE\u586B ${backfilled} \u6761\uFF08\u552F\u4E00\u53EF\u5224\u5B9A\u7684\u5386\u53F2\u4F1A\u8BDD\uFF09`);
|
|
198852
199907
|
const pgReadMarkers = await PostgresReadMarkerStore.open(pgPool, pgSchema);
|
|
198853
199908
|
readMarkerStore = pgReadMarkers;
|
|
198854
199909
|
const seeded = await pgReadMarkers.seedFromChatSessions();
|
|
@@ -198860,6 +199915,7 @@ async function startServe(opts) {
|
|
|
198860
199915
|
projectStateStore = await FileProjectStateStore.open(path25.join(opts.dir, "artifact-document-projects.json"));
|
|
198861
199916
|
artifactStateStore = await FileArtifactStateStore.open(path25.join(opts.dir, "artifact-document-state.json"));
|
|
198862
199917
|
chatSessionStore = await FileChatSessionStore.open(path25.join(opts.dir, "chat-sessions.json"));
|
|
199918
|
+
chatTurnStore = await FileChatTurnStore.open(path25.join(opts.dir, "chat-session-turns.json"));
|
|
198863
199919
|
readMarkerStore = await FileReadMarkerStore.open(path25.join(opts.dir, "inbox-read-markers.json"));
|
|
198864
199920
|
console.log("[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
198865
199921
|
console.log("[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
|
|
@@ -199052,6 +200108,19 @@ async function startServe(opts) {
|
|
|
199052
200108
|
}
|
|
199053
200109
|
});
|
|
199054
200110
|
let automationDispatchChat = null;
|
|
200111
|
+
const inspectChatTarget = async (args) => {
|
|
200112
|
+
const session = await chatSessionStore.getSession(args.sessionId).catch(() => null);
|
|
200113
|
+
if (!session) return { ok: false, issue: "session_missing" };
|
|
200114
|
+
const base = { title: session.title ?? null, touchedAt: session.touchedAt };
|
|
200115
|
+
if (!session.companyId) return { ok: false, issue: "company_unknown", ...base };
|
|
200116
|
+
if (session.companyId !== args.companyId) return { ok: false, issue: "cross_company", ...base };
|
|
200117
|
+
if (!args.ownerHumanActorId || session.humanActorId !== args.ownerHumanActorId) {
|
|
200118
|
+
return { ok: false, issue: "owner_mismatch", ...base };
|
|
200119
|
+
}
|
|
200120
|
+
if (!args.agentId || session.aiActorId !== args.agentId) return { ok: false, issue: "agent_mismatch", ...base };
|
|
200121
|
+
const messageCount = chatSessionStore.countMessages ? await chatSessionStore.countMessages(args.sessionId).catch(() => 0) : (await chatSessionStore.listMessages(args.sessionId).catch(() => [])).length;
|
|
200122
|
+
return { ok: true, title: session.title ?? null, touchedAt: session.touchedAt, messageCount };
|
|
200123
|
+
};
|
|
199055
200124
|
const automations = createAutomationsDomain({
|
|
199056
200125
|
store: automationStore,
|
|
199057
200126
|
fire: {
|
|
@@ -199075,48 +200144,131 @@ async function startServe(opts) {
|
|
|
199075
200144
|
workorderState: makeAutomationWorkorderStateResolver(kernel),
|
|
199076
200145
|
// 回复抓取(ADR 0157):工单 done 时把最终交付物正文并进 run.result.reply,运行历史直接展示。
|
|
199077
200146
|
workorderReply: makeAutomationReplyResolver(kernel, blobs),
|
|
199078
|
-
//
|
|
199079
|
-
|
|
199080
|
-
|
|
199081
|
-
|
|
199082
|
-
|
|
199083
|
-
|
|
199084
|
-
|
|
199085
|
-
|
|
199086
|
-
|
|
199087
|
-
|
|
199088
|
-
|
|
199089
|
-
|
|
199090
|
-
|
|
199091
|
-
|
|
199092
|
-
|
|
199093
|
-
|
|
199094
|
-
|
|
199095
|
-
|
|
199096
|
-
|
|
199097
|
-
|
|
199098
|
-
|
|
199099
|
-
|
|
199100
|
-
|
|
199101
|
-
|
|
199102
|
-
|
|
199103
|
-
|
|
199104
|
-
|
|
199105
|
-
|
|
199106
|
-
|
|
199107
|
-
|
|
199108
|
-
|
|
200147
|
+
// 轮次账本(ADR 会话投递 D4):busy 的唯一口径。
|
|
200148
|
+
chatTurns: chatTurnStore,
|
|
200149
|
+
// chat 执行(ADR 0159 + 会话投递 D2):**建会话与派发已经拆开**。
|
|
200150
|
+
// resolveTarget —— 只决定「发到哪个会话」;
|
|
200151
|
+
// dispatchTurn —— 只消费一条已经持久占位的 turn(写消息 + 真 dispatch + 收流)。
|
|
200152
|
+
// 拆开之前这两件事挤在一个 runChat 里,于是「发到已有会话」无处插入、「会话在跑吗」无处判定。
|
|
200153
|
+
chat: {
|
|
200154
|
+
async resolveTarget({ automation, runId, title }) {
|
|
200155
|
+
const target = normalizeChatTarget(automation.chatTarget);
|
|
200156
|
+
const agentId = automation.agentId;
|
|
200157
|
+
if (target.mode === "existing_session") {
|
|
200158
|
+
const check2 = await inspectChatTarget({
|
|
200159
|
+
companyId: automation.companyId,
|
|
200160
|
+
agentId: automation.agentId,
|
|
200161
|
+
ownerHumanActorId: automation.ownerHumanActorId,
|
|
200162
|
+
sessionId: target.sessionId
|
|
200163
|
+
});
|
|
200164
|
+
if (!check2.ok) {
|
|
200165
|
+
return {
|
|
200166
|
+
ok: false,
|
|
200167
|
+
reasonCode: "target_unavailable",
|
|
200168
|
+
error: `\u56FA\u5B9A\u76EE\u6807\u4F1A\u8BDD\u4E0D\u53EF\u7528\uFF08${check2.issue}\uFF09\uFF1A${target.sessionId}`,
|
|
200169
|
+
result: { targetKind: "chat_session", chatSessionId: target.sessionId, targetIssue: check2.issue }
|
|
200170
|
+
};
|
|
200171
|
+
}
|
|
200172
|
+
return { ok: true, chatSessionId: target.sessionId };
|
|
200173
|
+
}
|
|
200174
|
+
const chatSessionId = deterministicUuid(`automation-chat-session:${runId}`);
|
|
200175
|
+
const existing = await chatSessionStore.getSession(chatSessionId);
|
|
200176
|
+
if (existing) return { ok: true, chatSessionId };
|
|
200177
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
200178
|
+
try {
|
|
200179
|
+
await chatSessionStore.createSession({
|
|
200180
|
+
id: chatSessionId,
|
|
200181
|
+
companyId: automation.companyId,
|
|
200182
|
+
humanActorId: automation.ownerHumanActorId ?? agentId,
|
|
200183
|
+
aiActorId: agentId,
|
|
200184
|
+
runtimeId: "",
|
|
200185
|
+
runtimeSessionId: null,
|
|
200186
|
+
title: `\u81EA\u52A8\u5316 \xB7 ${title}`.slice(0, 80),
|
|
200187
|
+
touchedAt: nowIso,
|
|
200188
|
+
createdAt: nowIso
|
|
200189
|
+
});
|
|
200190
|
+
} catch (e) {
|
|
200191
|
+
if (!await chatSessionStore.getSession(chatSessionId)) throw e;
|
|
200192
|
+
}
|
|
200193
|
+
return { ok: true, chatSessionId };
|
|
200194
|
+
},
|
|
200195
|
+
async dispatchTurn({ automation, chatSessionId, turnId, messageKey, prompt }, onDone) {
|
|
200196
|
+
if (!automationDispatchChat) throw new Error("chat \u6D3E\u53D1\u5C1A\u672A\u5C31\u7EEA\uFF08serve \u542F\u52A8\u4E2D\uFF09");
|
|
200197
|
+
const agentId = automation.agentId;
|
|
200198
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
200199
|
+
await chatSessionStore.appendMessage({
|
|
200200
|
+
id: deterministicUuid(`automation-chat-message:${messageKey}`),
|
|
200201
|
+
sessionId: chatSessionId,
|
|
200202
|
+
role: "user",
|
|
200203
|
+
content: prompt,
|
|
200204
|
+
createdAt: nowIso,
|
|
200205
|
+
messageKey
|
|
200206
|
+
});
|
|
200207
|
+
await chatSessionStore.updateSession(chatSessionId, { touchedAt: nowIso }).catch(() => void 0);
|
|
200208
|
+
const targetSession = await chatSessionStore.getSession(chatSessionId).catch(() => null);
|
|
200209
|
+
const agentBinding = await registryStore.getBinding(agentId).catch(() => null);
|
|
200210
|
+
const plan = targetSession ? await planChatContinuation({
|
|
200211
|
+
session: targetSession,
|
|
200212
|
+
userMessage: prompt,
|
|
200213
|
+
listMessages: (sid, limit) => chatSessionStore.listMessages(sid, limit),
|
|
200214
|
+
currentRuntime: agentBinding?.status === "active" ? { nodeId: agentBinding.nodeId, runtimeKind: agentBinding.runtimeKind } : null,
|
|
200215
|
+
excludeMessageKey: messageKey
|
|
200216
|
+
}) : null;
|
|
200217
|
+
const session = await automationDispatchChat({
|
|
200218
|
+
actorId: agentId,
|
|
200219
|
+
message: plan?.runtimeMessage ?? prompt,
|
|
200220
|
+
...plan?.effectiveSessionId ? { sessionId: plan.effectiveSessionId } : {},
|
|
200221
|
+
...plan?.fallbackMessage ? { fallbackMessage: plan.fallbackMessage } : {},
|
|
200222
|
+
chatSessionId,
|
|
200223
|
+
companyId: automation.companyId
|
|
200224
|
+
});
|
|
200225
|
+
if (plan?.currentRuntimeId) {
|
|
200226
|
+
await chatSessionStore.updateSession(chatSessionId, {
|
|
200227
|
+
runtimeId: plan.currentRuntimeId,
|
|
200228
|
+
...plan.currentRuntimeKind ? { runtimeKind: plan.currentRuntimeKind } : {},
|
|
200229
|
+
runtimeSessionId: session.nativeSessionId ?? session.id
|
|
199109
200230
|
}).catch(() => void 0);
|
|
199110
200231
|
}
|
|
199111
|
-
await
|
|
199112
|
-
|
|
199113
|
-
|
|
199114
|
-
|
|
199115
|
-
|
|
200232
|
+
await chatTurnStore.settleTurn(turnId, {
|
|
200233
|
+
status: "running",
|
|
200234
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
200235
|
+
...session.runId ? { assistantRunId: session.runId, dispatchId: session.runId } : {}
|
|
200236
|
+
}).catch((e) => console.warn(`[automations] \u8F6E\u6B21\u8F6C running \u5931\u8D25 ${turnId}: ${String(e)}`));
|
|
200237
|
+
let buf = "";
|
|
200238
|
+
session.onOutput((c) => {
|
|
200239
|
+
buf += c;
|
|
200240
|
+
});
|
|
200241
|
+
void session.done.then(async () => {
|
|
200242
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
200243
|
+
if (buf || session.runId) {
|
|
200244
|
+
await chatSessionStore.appendMessage({
|
|
200245
|
+
id: (0, import_node_crypto48.randomUUID)(),
|
|
200246
|
+
sessionId: chatSessionId,
|
|
200247
|
+
role: "assistant",
|
|
200248
|
+
content: buf,
|
|
200249
|
+
createdAt: finishedAt,
|
|
200250
|
+
...session.runId ? { runId: session.runId } : {}
|
|
200251
|
+
}).catch(() => void 0);
|
|
200252
|
+
}
|
|
200253
|
+
await chatSessionStore.updateSession(chatSessionId, {
|
|
200254
|
+
...plan?.currentRuntimeId ? { runtimeId: plan.currentRuntimeId } : {},
|
|
200255
|
+
...plan?.currentRuntimeKind ? { runtimeKind: plan.currentRuntimeKind } : {},
|
|
200256
|
+
runtimeSessionId: session.nativeSessionId ?? session.id,
|
|
200257
|
+
touchedAt: finishedAt
|
|
200258
|
+
}).catch(() => void 0);
|
|
200259
|
+
await chatTurnStore.settleTurn(turnId, { status: "succeeded", completedAt: (/* @__PURE__ */ new Date()).toISOString() }).catch(() => void 0);
|
|
200260
|
+
await onDone({ ok: true, reply: buf });
|
|
200261
|
+
}).catch(async (err) => {
|
|
200262
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
200263
|
+
await chatTurnStore.settleTurn(turnId, { status: "failed", completedAt: (/* @__PURE__ */ new Date()).toISOString(), lastError: msg }).catch(() => void 0);
|
|
200264
|
+
await onDone({ ok: false, error: msg });
|
|
200265
|
+
});
|
|
200266
|
+
}
|
|
199116
200267
|
},
|
|
199117
|
-
// 订阅触发 × chat 的 fail-closed 开关(ADR 0163 §4.4
|
|
199118
|
-
//
|
|
199119
|
-
//
|
|
200268
|
+
// 订阅触发 × chat 的 fail-closed 开关(ADR 0163 §4.4 / 会话投递 §4)。
|
|
200269
|
+
// 本次交付补齐了持久 messageKey、turn 占位与确定性 run→session 映射,但 §4.2 的 8 条
|
|
200270
|
+
// 开闸条件里还有几条没有证据(真库并发、多进程崩溃点、dispatch 侧稳定幂等查询、指标告警),
|
|
200271
|
+
// **本 ADR 明确不授权改这里**。放开是一次独立变更,须先补齐那些证据。
|
|
199120
200272
|
chatSupportsIdempotency: false,
|
|
199121
200273
|
// chat 准入(≈ multica AgentReadiness):在岗 + 有 active runtime 绑定,注定失败的触发别去开会话。
|
|
199122
200274
|
agentReady: async (agentId) => {
|
|
@@ -199131,6 +200283,51 @@ async function startServe(opts) {
|
|
|
199131
200283
|
return { ok: true };
|
|
199132
200284
|
}
|
|
199133
200285
|
},
|
|
200286
|
+
// 固定目标会话接缝(ADR 会话投递 D3/D8):可用性判据、选择器搜索、长期会话创建三件事
|
|
200287
|
+
// 一律在服务端,前端只消费结论。
|
|
200288
|
+
chatTargets: {
|
|
200289
|
+
inspect: inspectChatTarget,
|
|
200290
|
+
createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
|
|
200291
|
+
const sessionId = (0, import_node_crypto48.randomUUID)();
|
|
200292
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
200293
|
+
const binding = await registryStore.getBinding(agentId).catch(() => null);
|
|
200294
|
+
await chatSessionStore.createSession({
|
|
200295
|
+
id: sessionId,
|
|
200296
|
+
companyId,
|
|
200297
|
+
humanActorId: ownerHumanActorId,
|
|
200298
|
+
aiActorId: agentId,
|
|
200299
|
+
runtimeId: binding?.nodeId ?? "",
|
|
200300
|
+
runtimeSessionId: null,
|
|
200301
|
+
title,
|
|
200302
|
+
touchedAt: nowIso,
|
|
200303
|
+
createdAt: nowIso
|
|
200304
|
+
});
|
|
200305
|
+
return { sessionId };
|
|
200306
|
+
},
|
|
200307
|
+
search: async ({ companyId, agentId, ownerHumanActorId, text, limit, offset }) => {
|
|
200308
|
+
if (!chatSessionStore.searchSessionsForTarget) return { items: [], hasMore: false };
|
|
200309
|
+
const page = await chatSessionStore.searchSessionsForTarget({
|
|
200310
|
+
companyId,
|
|
200311
|
+
humanActorId: ownerHumanActorId,
|
|
200312
|
+
aiActorId: agentId,
|
|
200313
|
+
...text ? { text } : {},
|
|
200314
|
+
limit,
|
|
200315
|
+
offset
|
|
200316
|
+
});
|
|
200317
|
+
return {
|
|
200318
|
+
items: page.items.map((x2) => ({ id: x2.id, title: x2.title ?? null, touchedAt: x2.touchedAt, messageCount: x2.messageCount })),
|
|
200319
|
+
hasMore: page.hasMore
|
|
200320
|
+
};
|
|
200321
|
+
},
|
|
200322
|
+
deleteSession: async (sessionId) => {
|
|
200323
|
+
await chatSessionStore.deleteSession(sessionId);
|
|
200324
|
+
},
|
|
200325
|
+
resolveOwnerHuman: async (actor) => {
|
|
200326
|
+
if (actor.startsWith("actor:human:")) return actor;
|
|
200327
|
+
const bound = await assistantBindStore.getByAgent(actor).catch(() => null);
|
|
200328
|
+
return bound?.humanActorId ?? null;
|
|
200329
|
+
}
|
|
200330
|
+
},
|
|
199134
200331
|
isAdmin: async (companyId, actor) => {
|
|
199135
200332
|
const member = await controlPlaneStore.getMember(companyId, actor);
|
|
199136
200333
|
return member?.role === "owner" || member?.role === "admin";
|
|
@@ -199565,7 +200762,7 @@ async function startServe(opts) {
|
|
|
199565
200762
|
}));
|
|
199566
200763
|
}
|
|
199567
200764
|
const limits = { wallClockMs: wallClockForKind("planner") };
|
|
199568
|
-
const artifactId = `artifact:planner:${(0,
|
|
200765
|
+
const artifactId = `artifact:planner:${(0, import_node_crypto48.randomUUID)()}`;
|
|
199569
200766
|
const handle = await chatRemoteAdapter.spawn({
|
|
199570
200767
|
actor: planner.id,
|
|
199571
200768
|
// companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
|
|
@@ -199659,6 +200856,9 @@ async function startServe(opts) {
|
|
|
199659
200856
|
store: chatSessionStore,
|
|
199660
200857
|
registry: registryStore,
|
|
199661
200858
|
trace: traceStore,
|
|
200859
|
+
// 会话槽的观测与运维出口(ADR 会话投递 D4):看这条会话占着哪一轮、把卡死的那一轮置终态。
|
|
200860
|
+
chatTurns: chatTurnStore,
|
|
200861
|
+
getRun: (id) => traceStore.getRun(id),
|
|
199662
200862
|
kernel,
|
|
199663
200863
|
artifactState: artifactStateStore,
|
|
199664
200864
|
workdir: () => workdirBridge ?? void 0,
|
|
@@ -199669,6 +200869,8 @@ async function startServe(opts) {
|
|
|
199669
200869
|
assistants: assistantsService,
|
|
199670
200870
|
liveChat,
|
|
199671
200871
|
...chatSessionStore ? { chatSession: chatSessionStore } : {},
|
|
200872
|
+
// ADR 会话投递 D4:/api/chat 与渠道入站据它占同一个会话槽(自动化侧经 automations 域接同一实例)。
|
|
200873
|
+
chatTurns: chatTurnStore,
|
|
199672
200874
|
knowledge: {
|
|
199673
200875
|
configStore: knowledgeConfigStore,
|
|
199674
200876
|
runStore: knowledgeRunStore,
|
|
@@ -199982,10 +201184,10 @@ async function startServe(opts) {
|
|
|
199982
201184
|
{ nodeId: priorChatSession?.runtimeId, runtimeKind: priorChatSession?.runtimeKind },
|
|
199983
201185
|
{ nodeId: binding.nodeId, runtimeKind: binding.runtimeKind }
|
|
199984
201186
|
) : false;
|
|
199985
|
-
const runtimeSessionId = sessionId ?? (0,
|
|
201187
|
+
const runtimeSessionId = sessionId ?? (0, import_node_crypto48.randomUUID)();
|
|
199986
201188
|
const resumeRuntimeSession = Boolean(sessionId);
|
|
199987
|
-
const traceRunId = `chat-run:${(0,
|
|
199988
|
-
const artifactId = `artifact:chat:${(0,
|
|
201189
|
+
const traceRunId = `chat-run:${(0, import_node_crypto48.randomUUID)()}`;
|
|
201190
|
+
const artifactId = `artifact:chat:${(0, import_node_crypto48.randomUUID)()}`;
|
|
199989
201191
|
const traceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
199990
201192
|
let lastProgressTouchMs = 0;
|
|
199991
201193
|
const PROGRESS_TOUCH_THROTTLE_MS2 = 2e4;
|
|
@@ -200533,6 +201735,20 @@ async function startServe(opts) {
|
|
|
200533
201735
|
chatSweepTimer.unref?.();
|
|
200534
201736
|
}
|
|
200535
201737
|
}
|
|
201738
|
+
const chatTurnOrphanGraceMs = Number(process.env.OASIS_CHAT_TURN_ORPHAN_GRACE_MS ?? 10 * 6e4);
|
|
201739
|
+
const chatTurnSweepTimer = setInterval(() => {
|
|
201740
|
+
void sweepStaleChatTurns({
|
|
201741
|
+
turns: chatTurnStore,
|
|
201742
|
+
getRun: (id) => traceStore.getRun(id),
|
|
201743
|
+
// 本进程在飞的轮不动——它自己的收尾路径会写终态(liveChat 只认人工/渠道那条,自动化轮
|
|
201744
|
+
// 在 dispatch 后立刻有 assistantRunId、其 run 处于 running,走上面的权威判据同样保住)。
|
|
201745
|
+
isOwned: (turn) => Boolean(turn.assistantRunId && liveChat.runFor(turn.chatSessionId) === turn.assistantRunId),
|
|
201746
|
+
now: Date.now(),
|
|
201747
|
+
noRunGraceMs: chatTurnOrphanGraceMs,
|
|
201748
|
+
log: (m2) => console.log(m2)
|
|
201749
|
+
}).catch((err) => console.error(`[chat-turn-sweep] tick \u5931\u8D25: ${String(err)}`));
|
|
201750
|
+
}, 3e4);
|
|
201751
|
+
chatTurnSweepTimer.unref?.();
|
|
200536
201752
|
const hubAdapterOpts = {
|
|
200537
201753
|
log: (m2) => console.warn(m2),
|
|
200538
201754
|
health: nodeHealth,
|
|
@@ -200544,7 +201760,16 @@ async function startServe(opts) {
|
|
|
200544
201760
|
}
|
|
200545
201761
|
};
|
|
200546
201762
|
chatRemoteAdapter = new DaemonHubAdapter(hub, { ...hubAdapterOpts, stashUnknownFrames: true });
|
|
200547
|
-
dispatchRemoteAdapter = new DaemonHubAdapter(hub, {
|
|
201763
|
+
dispatchRemoteAdapter = new DaemonHubAdapter(hub, {
|
|
201764
|
+
...hubAdapterOpts,
|
|
201765
|
+
stashUnknownFrames: true,
|
|
201766
|
+
// 服务端自判死(存在性快照 / 会话失联收割 / socket 非 OPEN)没有退出帧,走不到下面那个
|
|
201767
|
+
// message listener 里的 work.response 桥 → work 空转到 30 分钟超时。这里补上同一半逻辑。
|
|
201768
|
+
// 只挂 dispatch 路:chat 路的派发不对应 work,它自有 chat-recovery 收尾。
|
|
201769
|
+
onSettleWithoutFrame: (dispatchId, info) => {
|
|
201770
|
+
void bridgeServerSideExitToEngine(dispatchId, info);
|
|
201771
|
+
}
|
|
201772
|
+
});
|
|
200548
201773
|
let newEngineProduce = null;
|
|
200549
201774
|
let newEngineReplyWork = null;
|
|
200550
201775
|
let newEngineReview = null;
|
|
@@ -200580,6 +201805,51 @@ async function startServe(opts) {
|
|
|
200580
201805
|
const reviewToSession = /* @__PURE__ */ new Map();
|
|
200581
201806
|
const dispatchToReview = /* @__PURE__ */ new Map();
|
|
200582
201807
|
let busRef = null;
|
|
201808
|
+
async function bridgeServerSideExitToEngine(dispatchId, info) {
|
|
201809
|
+
const bus = busRef;
|
|
201810
|
+
if (!bus) return;
|
|
201811
|
+
const outcome = info.code === 0 ? "completed" : "failed";
|
|
201812
|
+
const detail = {
|
|
201813
|
+
reason: `server-side settle: ${info.reason ?? "unknown"}`,
|
|
201814
|
+
...info.errorMessage ? { errorMessage: info.errorMessage } : {}
|
|
201815
|
+
};
|
|
201816
|
+
try {
|
|
201817
|
+
const mapping = dispatchToWork.get(dispatchId);
|
|
201818
|
+
if (mapping) {
|
|
201819
|
+
dispatchToWork.delete(dispatchId);
|
|
201820
|
+
workToSession.delete(mapping.workId);
|
|
201821
|
+
console.log(`[new-engine] \u670D\u52A1\u7AEF\u81EA\u5224\u6B7B\uFF08\u65E0\u9000\u51FA\u5E27\uFF09\uFF1A${dispatchId} \u2192 work.response(${outcome}) ${mapping.workId}`);
|
|
201822
|
+
await bus.submit({
|
|
201823
|
+
companyId: "",
|
|
201824
|
+
workorderId: mapping.workorderId,
|
|
201825
|
+
actorId: SYSTEM_ACTOR,
|
|
201826
|
+
event: {
|
|
201827
|
+
kind: "work.response",
|
|
201828
|
+
workorderId: mapping.workorderId,
|
|
201829
|
+
workId: mapping.workId,
|
|
201830
|
+
outcome,
|
|
201831
|
+
detail
|
|
201832
|
+
}
|
|
201833
|
+
});
|
|
201834
|
+
} else {
|
|
201835
|
+
await fallbackDispatchExitToWork({ dispatchId, info }, bus);
|
|
201836
|
+
}
|
|
201837
|
+
const revMapping = dispatchToReview.get(dispatchId);
|
|
201838
|
+
if (revMapping) {
|
|
201839
|
+
dispatchToReview.delete(dispatchId);
|
|
201840
|
+
reviewToSession.delete(revMapping.reviewId);
|
|
201841
|
+
console.log(`[new-engine] \u670D\u52A1\u7AEF\u81EA\u5224\u6B7B\uFF08\u65E0\u9000\u51FA\u5E27\uFF09\uFF1A${dispatchId} \u2192 review.timeout ${revMapping.reviewId}`);
|
|
201842
|
+
await bus.submit({
|
|
201843
|
+
companyId: "",
|
|
201844
|
+
workorderId: revMapping.workorderId,
|
|
201845
|
+
actorId: SYSTEM_ACTOR,
|
|
201846
|
+
event: { kind: "review.timeout", workorderId: revMapping.workorderId, reviewId: revMapping.reviewId }
|
|
201847
|
+
});
|
|
201848
|
+
}
|
|
201849
|
+
} catch (err) {
|
|
201850
|
+
console.error(`[new-engine] \u670D\u52A1\u7AEF\u81EA\u5224\u6B7B\u6536\u53E3\u5931\u8D25 ${dispatchId}:`, err);
|
|
201851
|
+
}
|
|
201852
|
+
}
|
|
200583
201853
|
async function fallbackDispatchExitToWork(msg, bus) {
|
|
200584
201854
|
try {
|
|
200585
201855
|
const entry = journalRing.find(
|
|
@@ -200653,7 +201923,7 @@ async function startServe(opts) {
|
|
|
200653
201923
|
console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
|
|
200654
201924
|
return;
|
|
200655
201925
|
}
|
|
200656
|
-
const ledgerId = `dispatch:${(0,
|
|
201926
|
+
const ledgerId = `dispatch:${(0, import_node_crypto48.randomUUID)()}`;
|
|
200657
201927
|
try {
|
|
200658
201928
|
await dispatchLedger.insertOpen({
|
|
200659
201929
|
id: ledgerId,
|
|
@@ -200729,7 +201999,7 @@ async function startServe(opts) {
|
|
|
200729
201999
|
const kernelModel = newKernel.model;
|
|
200730
202000
|
const art = kernelModel.artifacts.get(nodeId);
|
|
200731
202001
|
const revWid = art?.workspace ?? "";
|
|
200732
|
-
const ledgerId = `dispatch:${(0,
|
|
202002
|
+
const ledgerId = `dispatch:${(0, import_node_crypto48.randomUUID)()}`;
|
|
200733
202003
|
try {
|
|
200734
202004
|
await dispatchLedger.insertOpen({
|
|
200735
202005
|
id: ledgerId,
|
|
@@ -201239,8 +202509,8 @@ async function startServe(opts) {
|
|
|
201239
202509
|
const agentMs = opts.sla?.agentMs ?? 10 * 6e4;
|
|
201240
202510
|
const quotaStarvationMs = opts.sla?.quotaStarvationMs ?? 6 * 36e5;
|
|
201241
202511
|
const reviewStallMs = opts.sla?.reviewStallMs ?? 60 * 6e4;
|
|
201242
|
-
const fmtSince = (
|
|
201243
|
-
const min2 = Math.floor(Math.max(0, now - Date.parse(
|
|
202512
|
+
const fmtSince = (iso5, now) => {
|
|
202513
|
+
const min2 = Math.floor(Math.max(0, now - Date.parse(iso5)) / 6e4);
|
|
201244
202514
|
if (!Number.isFinite(min2)) return "\u521A\u624D";
|
|
201245
202515
|
if (min2 < 1) return "\u4E0D\u5230 1 \u5206\u949F\u524D";
|
|
201246
202516
|
if (min2 < 60) return `${min2} \u5206\u949F\u524D`;
|
|
@@ -202187,6 +203457,10 @@ var SessionProcessState = class {
|
|
|
202187
203457
|
},
|
|
202188
203458
|
onHandle: (h) => {
|
|
202189
203459
|
this.handle = h;
|
|
203460
|
+
if (this.stopping) {
|
|
203461
|
+
void h.kill().catch(() => {
|
|
203462
|
+
});
|
|
203463
|
+
}
|
|
202190
203464
|
},
|
|
202191
203465
|
onExited: () => {
|
|
202192
203466
|
this.exited = true;
|
|
@@ -202358,7 +203632,7 @@ var SessionProcessState = class {
|
|
|
202358
203632
|
// ../cli/src/daemon/ws-client.ts
|
|
202359
203633
|
init_wrapper();
|
|
202360
203634
|
var import_node_os9 = require("node:os");
|
|
202361
|
-
var
|
|
203635
|
+
var import_node_crypto49 = require("node:crypto");
|
|
202362
203636
|
init_src8();
|
|
202363
203637
|
init_src7();
|
|
202364
203638
|
|
|
@@ -202693,8 +203967,21 @@ var DaemonWsClient = class {
|
|
|
202693
203967
|
ws = null;
|
|
202694
203968
|
pingTimer = null;
|
|
202695
203969
|
stopped = false;
|
|
202696
|
-
/** 收到 update
|
|
203970
|
+
/** 收到 update 但本进程内还有活跃会话时置真:延后到全部结束再自更新,避免打断执行中的 agent。 */
|
|
202697
203971
|
pendingUpdate = false;
|
|
203972
|
+
/** 第一次延后这次更新的时刻。用于给「等空闲」封顶——否则长跑会话能把更新无限期挂住(ADR 0115 缺陷 3)。 */
|
|
203973
|
+
pendingUpdateSince = null;
|
|
203974
|
+
/** 「等空闲」的上限;到点就照做(会打断本进程内的会话)。env OASIS_UPDATE_DEFER_MAX_MS 可调。 */
|
|
203975
|
+
updateDeferMaxMs = (() => {
|
|
203976
|
+
const raw = Number(process.env["OASIS_UPDATE_DEFER_MAX_MS"]);
|
|
203977
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 30 * 6e4;
|
|
203978
|
+
})();
|
|
203979
|
+
/** 记下首次延后时刻并回答「还在上限内吗」。 */
|
|
203980
|
+
deferUpdateWithinLimit() {
|
|
203981
|
+
this.pendingUpdate = true;
|
|
203982
|
+
this.pendingUpdateSince ??= Date.now();
|
|
203983
|
+
return Date.now() - this.pendingUpdateSince < this.updateDeferMaxMs;
|
|
203984
|
+
}
|
|
202698
203985
|
/** 停机排空中(见 {@link drain}):不再接新派发、不再触发延后的自更新。 */
|
|
202699
203986
|
draining = false;
|
|
202700
203987
|
/** 已回 dispatch_received、尚未完成 runtime spawn 的派发;也随 hello 上报供重连对账。 */
|
|
@@ -203011,9 +204298,13 @@ var DaemonWsClient = class {
|
|
|
203011
204298
|
break;
|
|
203012
204299
|
}
|
|
203013
204300
|
const active = this.sessions.activeCount();
|
|
203014
|
-
if (active > 0) {
|
|
203015
|
-
this.
|
|
203016
|
-
|
|
204301
|
+
if (active > 0 && this.deferUpdateWithinLimit()) {
|
|
204302
|
+
log2("[node-cli]", `\u2190 update\uFF1A\u5F53\u524D\u6709 ${active} \u4E2A\u6267\u884C\u4E2D\u4F1A\u8BDD\uFF08\u672C\u8FDB\u7A0B\u5185\uFF09\uFF0C\u5EF6\u540E\u5230\u5168\u90E8\u7ED3\u675F\u6216 ${Math.round(this.updateDeferMaxMs / 6e4)} \u5206\u949F\u4E0A\u9650`);
|
|
204303
|
+
} else if (active > 0) {
|
|
204304
|
+
log2("[node-cli]", `\u2190 update\uFF1A\u5DF2\u5EF6\u540E ${Math.round((Date.now() - (this.pendingUpdateSince ?? Date.now())) / 6e4)} \u5206\u949F\u4ECD\u672A\u7A7A\u95F2\uFF0C\u8D85\u8FC7\u4E0A\u9650\uFF0C\u73B0\u5728\u5C31\u66F4\u65B0\uFF08\u4F1A\u6253\u65AD ${active} \u4E2A\u672C\u8FDB\u7A0B\u5185\u4F1A\u8BDD\uFF09`);
|
|
204305
|
+
this.pendingUpdate = false;
|
|
204306
|
+
this.pendingUpdateSince = null;
|
|
204307
|
+
this.onUpdate();
|
|
203017
204308
|
} else {
|
|
203018
204309
|
log2("[node-cli]", "\u2190 update\uFF1A\u7A7A\u95F2,\u7ACB\u5373\u62C9\u53D6\u6700\u65B0\u7248\u672C\u5E76\u91CD\u542F");
|
|
203019
204310
|
this.onUpdate();
|
|
@@ -203030,7 +204321,7 @@ var DaemonWsClient = class {
|
|
|
203030
204321
|
*/
|
|
203031
204322
|
async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
|
|
203032
204323
|
if (artifactIds.length === 0) return {};
|
|
203033
|
-
const requestId = (0,
|
|
204324
|
+
const requestId = (0, import_node_crypto49.randomUUID)();
|
|
203034
204325
|
return new Promise((resolve10, reject) => {
|
|
203035
204326
|
const timer = setTimeout(() => {
|
|
203036
204327
|
this.gcPending.delete(requestId);
|
|
@@ -203097,9 +204388,11 @@ var DaemonWsClient = class {
|
|
|
203097
204388
|
}
|
|
203098
204389
|
/** 会话结束后调用:若之前收到过 update 但因忙延后了,且现已空闲,则触发自更新。 */
|
|
203099
204390
|
maybeRunPendingUpdate() {
|
|
204391
|
+
if (!this.pendingUpdate) return;
|
|
203100
204392
|
if (this.draining) return;
|
|
203101
204393
|
if (this.pendingUpdate && this.sessions.activeCount() === 0) {
|
|
203102
204394
|
this.pendingUpdate = false;
|
|
204395
|
+
this.pendingUpdateSince = null;
|
|
203103
204396
|
log2("[node-cli]", "\u6240\u6709\u4F1A\u8BDD\u5DF2\u7ED3\u675F,\u6267\u884C\u5EF6\u540E\u7684\u66F4\u65B0\uFF1A\u62C9\u53D6\u6700\u65B0\u7248\u672C\u5E76\u91CD\u542F");
|
|
203104
204397
|
this.onUpdate?.();
|
|
203105
204398
|
}
|
|
@@ -203374,7 +204667,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
|
|
|
203374
204667
|
// ../cli/src/daemon/machine-id.ts
|
|
203375
204668
|
var import_node_child_process18 = require("node:child_process");
|
|
203376
204669
|
var import_node_fs21 = require("node:fs");
|
|
203377
|
-
var
|
|
204670
|
+
var import_node_crypto50 = require("node:crypto");
|
|
203378
204671
|
var import_node_os12 = require("node:os");
|
|
203379
204672
|
function linuxMachineId() {
|
|
203380
204673
|
for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
@@ -203440,7 +204733,7 @@ var defaultSources = {
|
|
|
203440
204733
|
function resolveNodeId(sources = {}) {
|
|
203441
204734
|
const s2 = { ...defaultSources, ...sources };
|
|
203442
204735
|
const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
|
|
203443
|
-
const digest = (0,
|
|
204736
|
+
const digest = (0, import_node_crypto50.createHash)("sha256").update(material).digest("hex").slice(0, 12);
|
|
203444
204737
|
return `node-${digest}`;
|
|
203445
204738
|
}
|
|
203446
204739
|
|
|
@@ -203704,18 +204997,19 @@ var COMMAND_DECLS = {
|
|
|
203704
204997
|
"automation-get": {
|
|
203705
204998
|
group: "\u81EA\u52A8\u5316",
|
|
203706
204999
|
usage: "oasis automation-get <id>",
|
|
203707
|
-
description: "\u81EA\u52A8\u5316\u8BE6\u60C5\uFF1A\u89C4\u5219 + \u89E6\u53D1\u5668 + \u6700\u8FD1\u8FD0\u884C\u6295\u5F71\u3002",
|
|
205000
|
+
description: "\u81EA\u52A8\u5316\u8BE6\u60C5\uFF1A\u89C4\u5219 + \u89E6\u53D1\u5668 + \u6700\u8FD1\u8FD0\u884C\u6295\u5F71\u3002\u5BF9\u8BDD\u6267\u884C\u4F1A\u5E26 automation.chatTarget\uFF08\u76EE\u6807\u4F1A\u8BDD\u7B56\u7565\uFF09\u4E0E chatTargetView\uFF08\u56FA\u5B9A\u4F1A\u8BDD\u662F\u5426\u4ECD\u53EF\u6295\u9012\u3001\u6D88\u606F\u91CF\u3001\u6700\u8FD1\u6D3B\u52A8\uFF09\u3002",
|
|
203708
205001
|
positional: [{ name: "<id>", required: true, desc: "\u81EA\u52A8\u5316 id" }],
|
|
203709
205002
|
examples: ["oasis automation-get auto_abc123"]
|
|
203710
205003
|
},
|
|
203711
205004
|
"automation-create": {
|
|
203712
205005
|
group: "\u81EA\u52A8\u5316",
|
|
203713
|
-
usage: "oasis automation-create --name <\u540D> [--mode chat|workorder] [--agent <actorId>] [--playbook <ref>] [--payload '<JSON>'] [--cron '0 9 * * 1' --tz Asia/Shanghai] [--webhook generic|github] [--project <id>] [--title-template '\u6668\u62A5 {{date}}'] [--dispatch true] [--subscribers a,b]",
|
|
205006
|
+
usage: "oasis automation-create --name <\u540D> [--mode chat|workorder] [--agent <actorId>] [--chat-target new|session:<\u4F1A\u8BDDid>|create] [--playbook <ref>] [--payload '<JSON>'] [--cron '0 9 * * 1' --tz Asia/Shanghai] [--webhook generic|github] [--project <id>] [--title-template '\u6668\u62A5 {{date}}'] [--dispatch true] [--subscribers a,b]",
|
|
203714
205007
|
description: "\u65B0\u5EFA\u81EA\u52A8\u5316\u3002chat \u6A21\u5F0F\uFF08\u9ED8\u8BA4\u7ED9\u4E86 --agent \u5373\u662F\uFF09\uFF1A\u5F00\u4F1A\u8BDD\u628A\u4EFB\u52A1\u53D1\u7ED9\u6267\u884C\u4EBA\u3001\u56DE\u590D\u8FDB\u8FD0\u884C\u5386\u53F2\uFF1Bworkorder \u6A21\u5F0F\uFF1A\u6309\u5267\u672C\u5EFA\u5DE5\u5355\uFF08\u9ED8\u8BA4 dispatch-hold \u5F85\u4EBA\u653E\u884C\uFF09\u3002",
|
|
203715
205008
|
flags: [
|
|
203716
205009
|
{ name: "name", desc: "\u663E\u793A\u540D", required: true },
|
|
203717
205010
|
{ name: "mode", desc: "chat|workorder\uFF08\u7F3A\u7701\uFF1A\u7ED9\u4E86 --agent \u4E3A chat\uFF0C\u5426\u5219 workorder\uFF09" },
|
|
203718
205011
|
{ name: "agent", desc: "\u6267\u884C\u4EBA agent actor id\uFF08chat \u6A21\u5F0F\u5FC5\u586B\uFF09" },
|
|
205012
|
+
{ name: "chat-target", desc: "\u5BF9\u8BDD\u6267\u884C\u7684\u76EE\u6807\u4F1A\u8BDD\uFF1Anew=\u6BCF\u6B21\u65B0\u5F00\uFF08\u7F3A\u7701\uFF09\uFF5Csession:<\u4F1A\u8BDDid>=\u56FA\u5B9A\u53D1\u5230\u8BE5\u4F1A\u8BDD\uFF5Ccreate=\u73B0\u5EFA\u4E00\u4E2A\u957F\u671F\u76EE\u6807\u4F1A\u8BDD\u5E76\u7ED1\u4E0A" },
|
|
203719
205013
|
{ name: "playbook", desc: "\u5267\u672C ref\uFF08workorder \u6A21\u5F0F\u5FC5\u586B\uFF1Boasis playbooks \u53EF\u67E5\uFF09" },
|
|
203720
205014
|
{ name: "payload", desc: "\u5267\u672C\u5165\u53C2\uFF08JSON\uFF0C\u5BF9\u5E94\u5267\u672C inputs\uFF09" },
|
|
203721
205015
|
{ name: "cron", desc: "5 \u5B57\u6BB5 cron\uFF08\u7ED9\u4E86\u5373\u5EFA schedule \u89E6\u53D1\u5668\uFF09" },
|
|
@@ -203730,13 +205024,14 @@ var COMMAND_DECLS = {
|
|
|
203730
205024
|
},
|
|
203731
205025
|
"automation-update": {
|
|
203732
205026
|
group: "\u81EA\u52A8\u5316",
|
|
203733
|
-
usage: "oasis automation-update <id> [--name <\u540D>] [--mode chat|workorder] [--agent <actorId>] [--playbook <ref>] [--payload '<JSON>'] [--project <id>] [--title-template <t>] [--dispatch true|false] [--enabled true|false] [--subscribers a,b]",
|
|
205027
|
+
usage: "oasis automation-update <id> [--name <\u540D>] [--mode chat|workorder] [--agent <actorId>] [--chat-target new|session:<\u4F1A\u8BDDid>|create] [--playbook <ref>] [--payload '<JSON>'] [--project <id>] [--title-template <t>] [--dispatch true|false] [--enabled true|false] [--subscribers a,b]",
|
|
203734
205028
|
description: "\u66F4\u65B0\u81EA\u52A8\u5316\u3002\u5B9E\u8D28\u53D8\u66F4\uFF08\u5267\u672C/\u5165\u53C2/\u6A21\u677F/dispatch/\u542F\u505C\uFF09\u4F1A\u628A\u95EE\u8D23\u8F6C\u79FB\u7ED9\u7F16\u8F91\u8005\uFF08\u5F52\u56E0\uFF09\u3002",
|
|
203735
205029
|
positional: [{ name: "<id>", required: true, desc: "\u81EA\u52A8\u5316 id" }],
|
|
203736
205030
|
flags: [
|
|
203737
205031
|
{ name: "name", desc: "\u663E\u793A\u540D" },
|
|
203738
205032
|
{ name: "mode", desc: "chat|workorder" },
|
|
203739
205033
|
{ name: "agent", desc: "\u6267\u884C\u4EBA agent actor id" },
|
|
205034
|
+
{ name: "chat-target", desc: "\u5BF9\u8BDD\u6267\u884C\u7684\u76EE\u6807\u4F1A\u8BDD\uFF1Anew=\u6BCF\u6B21\u65B0\u5F00\uFF5Csession:<\u4F1A\u8BDDid>=\u56FA\u5B9A\u53D1\u5230\u8BE5\u4F1A\u8BDD\uFF5Ccreate=\u73B0\u5EFA\u4E00\u4E2A\u957F\u671F\u76EE\u6807\u4F1A\u8BDD\u5E76\u7ED1\u4E0A\uFF08\u4E0D\u7ED9=\u4E0D\u52A8\uFF09" },
|
|
203740
205035
|
{ name: "playbook", desc: "\u5267\u672C ref" },
|
|
203741
205036
|
{ name: "payload", desc: "\u5267\u672C\u5165\u53C2\uFF08JSON\uFF09" },
|
|
203742
205037
|
{ name: "project", desc: "\u76EE\u6807\u9879\u76EE id" },
|
|
@@ -204330,6 +205625,30 @@ var COMMAND_DECLS = {
|
|
|
204330
205625
|
description: "\u5B64\u513F blob \u56DE\u6536\uFF08mark-and-sweep\uFF1B\u64CD\u4F5C\u8005\u547D\u4EE4\uFF0CF1\uFF09\u3002",
|
|
204331
205626
|
examples: ["oasis gc"]
|
|
204332
205627
|
},
|
|
205628
|
+
"chat-turn": {
|
|
205629
|
+
group: "\u8FD0\u7EF4",
|
|
205630
|
+
usage: "oasis chat-turn <\u4F1A\u8BDDid>",
|
|
205631
|
+
description: "\u770B\u8FD9\u6761 chat \u4F1A\u8BDD\u5F53\u524D\u5360\u7740\u7684\u90A3\u4E00\u8F6E\uFF08\u8F6E\u6B21\u8D26\u672C\u7684 active \u884C\uFF09\u3002\u300C\u76EE\u6807\u4F1A\u8BDD\u6B63\u5FD9 / session_busy\u300D\u7684\u552F\u4E00\u53E3\u5F84\u5C31\u662F\u5B83\u2014\u2014\u6392\u67E5\u81EA\u52A8\u5316\u4E3A\u4EC0\u4E48\u4E00\u76F4\u88AB\u8DF3\u8FC7\u5148\u770B\u8FD9\u91CC\u3002",
|
|
205632
|
+
positional: [{ name: "<\u4F1A\u8BDDid>", required: true, desc: "chat \u4F1A\u8BDD id" }],
|
|
205633
|
+
examples: ["oasis chat-turn 84f48795-1c2d-4e5f-8a9b-0c1d2e3f4a5b"]
|
|
205634
|
+
},
|
|
205635
|
+
"chat-turn-settle": {
|
|
205636
|
+
group: "\u8FD0\u7EF4",
|
|
205637
|
+
usage: "oasis chat-turn-settle <\u4F1A\u8BDDid> <\u8F6E\u6B21id> [--force true] [--reason <\u8BF4\u660E>]",
|
|
205638
|
+
description: "\u628A\u5361\u6B7B\u7684\u4E00\u8F6E\u7F6E\u7EC8\u6001\u3001\u653E\u5F00\u4F1A\u8BDD\u69FD\uFF08\u8FDB\u7A0B\u88AB\u6740\u7559\u4E0B\u7684 running \u884C\u4F1A\u8BA9\u4F1A\u8BDD\u6C38\u4E45\u5224\u5FD9\uFF09\u3002\u9ED8\u8BA4\u5148\u5411 trace \u8D26\u672C\u6838\u5B9E\uFF1A\u8FD8\u5728\u8DD1\u5C31\u62D2\u7EDD\uFF1B--force true \u662F\u660E\u786E\u7684\u4EBA\u5DE5\u63A8\u7FFB\uFF0C\u843D cancelled \u5E76\u8BB0\u4E0B\u539F\u56E0\u3002",
|
|
205639
|
+
positional: [
|
|
205640
|
+
{ name: "<\u4F1A\u8BDDid>", required: true, desc: "chat \u4F1A\u8BDD id" },
|
|
205641
|
+
{ name: "<\u8F6E\u6B21id>", required: true, desc: "\u8F6E\u6B21 id\uFF08oasis chat-turn \u91CC\u7684 id\uFF0Crun.result.blockedByTurnId \u540C\u503C\uFF09" }
|
|
205642
|
+
],
|
|
205643
|
+
flags: [
|
|
205644
|
+
{ name: "force", desc: "true=\u4E0D\u6838\u5B9E\u76F4\u63A5\u7F6E cancelled\uFF08\u786E\u8BA4\u6267\u884C\u4EBA\u771F\u7684\u6B7B\u4E86\u518D\u7528\uFF09" },
|
|
205645
|
+
{ name: "reason", desc: "\u5F3A\u5236\u7F6E\u7EC8\u6001\u7684\u539F\u56E0\uFF08\u4F1A\u8BB0\u8FDB\u8F6E\u6B21\u7684 lastError\uFF09" }
|
|
205646
|
+
],
|
|
205647
|
+
examples: [
|
|
205648
|
+
"oasis chat-turn-settle 84f48795-\u2026 cturn_nbgv5c7g8u",
|
|
205649
|
+
'oasis chat-turn-settle 84f48795-\u2026 cturn_nbgv5c7g8u --force true --reason "\u8282\u70B9\u5DF2\u91CD\u88C5\uFF0C\u8FD9\u4E00\u8F6E\u7684\u8FDB\u7A0B\u4E0D\u5B58\u5728\u4E86"'
|
|
205650
|
+
]
|
|
205651
|
+
},
|
|
204333
205652
|
// —— 读产物 ——
|
|
204334
205653
|
status: {
|
|
204335
205654
|
group: "\u8BFB\u4EA7\u7269",
|
|
@@ -205057,13 +206376,13 @@ function ownFlagsFromDecl(command) {
|
|
|
205057
206376
|
}
|
|
205058
206377
|
|
|
205059
206378
|
// ../cli/src/connector-effect-runner.ts
|
|
205060
|
-
var
|
|
206379
|
+
var import_node_crypto51 = require("node:crypto");
|
|
205061
206380
|
var import_node_child_process19 = require("node:child_process");
|
|
205062
206381
|
init_src8();
|
|
205063
206382
|
function stableKey(effect) {
|
|
205064
206383
|
const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
|
|
205065
206384
|
const canonical = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
|
|
205066
|
-
return `sha256:${(0,
|
|
206385
|
+
return `sha256:${(0, import_node_crypto51.createHash)("sha256").update(canonical).digest("hex")}`;
|
|
205067
206386
|
}
|
|
205068
206387
|
async function runConnectorEffect(connector, toolArgs, deps) {
|
|
205069
206388
|
const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
|
|
@@ -205190,6 +206509,18 @@ function need(flags, name) {
|
|
|
205190
206509
|
if (v2 === void 0) throw new Error(`\u7F3A\u5C11 --${name}\uFF08oasis \u4E0D\u5E26\u53C2\u6570\u53EF\u770B\u7528\u6CD5\uFF09`);
|
|
205191
206510
|
return v2;
|
|
205192
206511
|
}
|
|
206512
|
+
function parseChatTargetFlag(raw) {
|
|
206513
|
+
if (raw === void 0) return {};
|
|
206514
|
+
const v2 = raw.trim();
|
|
206515
|
+
if (v2 === "new") return { chatTarget: { mode: "new_session" } };
|
|
206516
|
+
if (v2 === "create") return { createChatTargetSession: true };
|
|
206517
|
+
if (v2.startsWith("session:")) {
|
|
206518
|
+
const sessionId = v2.slice("session:".length).trim();
|
|
206519
|
+
if (!sessionId) throw new Error("--chat-target session:<\u4F1A\u8BDDid> \u7F3A\u5C11\u4F1A\u8BDD id");
|
|
206520
|
+
return { chatTarget: { mode: "existing_session", sessionId } };
|
|
206521
|
+
}
|
|
206522
|
+
throw new Error(`--chat-target \u53EA\u80FD\u662F new | session:<\u4F1A\u8BDDid> | create\uFF08\u6536\u5230\uFF1A${raw}\uFF09`);
|
|
206523
|
+
}
|
|
205193
206524
|
function needPos(positional, i, usage) {
|
|
205194
206525
|
const v2 = positional[i];
|
|
205195
206526
|
if (v2 === void 0) throw new Error(`\u7528\u6CD5: ${usage}`);
|
|
@@ -205658,7 +206989,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
|
|
|
205658
206989
|
const store = createNodeTokenStore(path31.join(dir, "node-tokens.json"));
|
|
205659
206990
|
const sub = positional[0];
|
|
205660
206991
|
if (sub === "issue") {
|
|
205661
|
-
const id = flags.get("id") ?? `node-${(0,
|
|
206992
|
+
const id = flags.get("id") ?? `node-${(0, import_node_crypto52.randomUUID)()}`;
|
|
205662
206993
|
const token2 = store.issue(id);
|
|
205663
206994
|
println(token2);
|
|
205664
206995
|
process.stderr.write(
|
|
@@ -206214,10 +207545,12 @@ ${p2.ref} ${p2.name}`);
|
|
|
206214
207545
|
triggers.push({ kind: "webhook", webhook: { provider: flags.get("webhook") || "generic" } });
|
|
206215
207546
|
}
|
|
206216
207547
|
const mode = flags.get("mode") ?? (flags.has("agent") ? "chat" : "workorder");
|
|
207548
|
+
const chatTargetFlag = parseChatTargetFlag(flags.get("chat-target"));
|
|
206217
207549
|
const result = await api.post("/api/automations", {
|
|
206218
207550
|
name: need(flags, "name"),
|
|
206219
207551
|
executionMode: mode,
|
|
206220
207552
|
...flags.has("agent") ? { agentId: flags.get("agent") } : {},
|
|
207553
|
+
...chatTargetFlag,
|
|
206221
207554
|
...mode === "workorder" ? { playbookRef: need(flags, "playbook") } : {},
|
|
206222
207555
|
...flags.has("payload") ? { payload: JSON.parse(flags.get("payload")) } : {},
|
|
206223
207556
|
...flags.has("project") ? { projectId: flags.get("project") } : {},
|
|
@@ -206231,8 +207564,14 @@ ${p2.ref} ${p2.name}`);
|
|
|
206231
207564
|
}
|
|
206232
207565
|
case "automation-update": {
|
|
206233
207566
|
const id = needPos(positional, 0, "oasis automation-update <id> [--name ...] [--enabled true|false] ...");
|
|
207567
|
+
const chatTargetFlag = parseChatTargetFlag(flags.get("chat-target"));
|
|
207568
|
+
if ("createChatTargetSession" in chatTargetFlag) {
|
|
207569
|
+
println(JSON.stringify(await api.post(`/api/automations/${encodeURIComponent(id)}/chat-target-session`, {}), null, 2));
|
|
207570
|
+
break;
|
|
207571
|
+
}
|
|
206234
207572
|
const result = await api.request("PATCH", `/api/automations/${encodeURIComponent(id)}`, {
|
|
206235
207573
|
...flags.has("name") ? { name: flags.get("name") } : {},
|
|
207574
|
+
...chatTargetFlag,
|
|
206236
207575
|
...flags.has("mode") ? { executionMode: flags.get("mode") } : {},
|
|
206237
207576
|
...flags.has("agent") ? { agentId: flags.get("agent") } : {},
|
|
206238
207577
|
...flags.has("playbook") ? { playbookRef: flags.get("playbook") } : {},
|
|
@@ -207576,6 +208915,34 @@ ${res.warning}`);
|
|
|
207576
208915
|
println(message);
|
|
207577
208916
|
break;
|
|
207578
208917
|
}
|
|
208918
|
+
case "chat-turn": {
|
|
208919
|
+
const sid = needPos(positional, 0, "oasis chat-turn <\u4F1A\u8BDDid>");
|
|
208920
|
+
const { turn } = await api.request(
|
|
208921
|
+
"GET",
|
|
208922
|
+
`/api/chat-sessions/${encodeURIComponent(sid)}/turns/active`
|
|
208923
|
+
);
|
|
208924
|
+
if (!turn) {
|
|
208925
|
+
println("\u8FD9\u6761\u4F1A\u8BDD\u5F53\u524D\u7A7A\u95F2\uFF08\u6CA1\u6709 active \u8F6E\u6B21\uFF09\u3002");
|
|
208926
|
+
break;
|
|
208927
|
+
}
|
|
208928
|
+
println(JSON.stringify(turn, null, 2));
|
|
208929
|
+
println("");
|
|
208930
|
+
println(`\u5361\u4F4F\u4E86\uFF1F\u5148\u6838\u5B9E\u6267\u884C\u4EBA\u6B7B\u6CA1\u6B7B\uFF0C\u518D \`oasis chat-turn-settle ${sid} ${String(turn["id"])}\`\u3002`);
|
|
208931
|
+
break;
|
|
208932
|
+
}
|
|
208933
|
+
case "chat-turn-settle": {
|
|
208934
|
+
const sid = needPos(positional, 0, "oasis chat-turn-settle <\u4F1A\u8BDDid> <\u8F6E\u6B21id>");
|
|
208935
|
+
const turnId = needPos(positional, 1, "oasis chat-turn-settle <\u4F1A\u8BDDid> <\u8F6E\u6B21id>");
|
|
208936
|
+
const res = await api.post(
|
|
208937
|
+
`/api/chat-sessions/${encodeURIComponent(sid)}/turns/${encodeURIComponent(turnId)}/settle`,
|
|
208938
|
+
{
|
|
208939
|
+
...flags.get("force") === "true" ? { force: true } : {},
|
|
208940
|
+
...flags.has("reason") ? { reason: flags.get("reason") } : {}
|
|
208941
|
+
}
|
|
208942
|
+
);
|
|
208943
|
+
println(res.settled ? `\u5DF2\u7F6E\u7EC8\u6001\uFF1A${res.status}${res.reason ? `\uFF08${res.reason}\uFF09` : ""}\u2014\u2014\u4F1A\u8BDD\u69FD\u5DF2\u653E\u5F00\u3002` : `\u672A\u5904\u7F6E\uFF1A${res.reason ?? "\u8FD9\u4E00\u8F6E\u5DF2\u7ECF\u662F\u7EC8\u6001"}\u3002`);
|
|
208944
|
+
break;
|
|
208945
|
+
}
|
|
207579
208946
|
case "queue": {
|
|
207580
208947
|
const artifactId = needPos(positional, 0, "oasis queue <artifactId>");
|
|
207581
208948
|
const { head, queue } = await api.view("queue", artifactId);
|
|
@@ -208204,12 +209571,12 @@ var LEGACY_SYSTEMD_UNIT = "oasis-node.service";
|
|
|
208204
209571
|
var LEGACY_LAUNCHD_LABEL = "com.oasis.node";
|
|
208205
209572
|
|
|
208206
209573
|
// src/install.ts
|
|
208207
|
-
var
|
|
209574
|
+
var import_node_crypto53 = require("node:crypto");
|
|
208208
209575
|
var fs41 = __toESM(require("node:fs"));
|
|
208209
209576
|
var path35 = __toESM(require("node:path"));
|
|
208210
209577
|
function versionLabel(sourceFile, pkgVersion, npmPrefixDir) {
|
|
208211
209578
|
if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return pkgVersion;
|
|
208212
|
-
const digest = (0,
|
|
209579
|
+
const digest = (0, import_node_crypto53.createHash)("sha256").update(fs41.readFileSync(sourceFile)).digest("hex").slice(0, 8);
|
|
208213
209580
|
return `${pkgVersion}+local.${digest}`;
|
|
208214
209581
|
}
|
|
208215
209582
|
function isFromNpmPrefix(sourceFile, npmPrefixDir) {
|
|
@@ -208332,7 +209699,7 @@ function shimScript() {
|
|
|
208332
209699
|
}
|
|
208333
209700
|
|
|
208334
209701
|
// src/index.ts
|
|
208335
|
-
var PKG_VERSION = true ? "0.1.
|
|
209702
|
+
var PKG_VERSION = true ? "0.1.122" : "dev";
|
|
208336
209703
|
var LOCAL_BIN = localBin();
|
|
208337
209704
|
var NPM_PREFIX = npmPrefix();
|
|
208338
209705
|
var INSTANCE = DEFAULT_INSTANCE;
|
|
@@ -208592,7 +209959,7 @@ function newInstanceName() {
|
|
|
208592
209959
|
const existing = new Set(listInstances());
|
|
208593
209960
|
if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
|
|
208594
209961
|
for (; ; ) {
|
|
208595
|
-
const n = `inst-${(0,
|
|
209962
|
+
const n = `inst-${(0, import_node_crypto54.randomBytes)(3).toString("hex")}`;
|
|
208596
209963
|
if (!existing.has(n)) return n;
|
|
208597
209964
|
}
|
|
208598
209965
|
}
|