oasis_test_v2 2.2.11 → 2.2.13
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 +379 -57
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -28783,6 +28783,8 @@ var init_chat_item_ledger = __esm({
|
|
|
28783
28783
|
* 也不算进本段的 assistant item 计数。
|
|
28784
28784
|
*/
|
|
28785
28785
|
recordUserInput(text5, attachments) {
|
|
28786
|
+
const payload = { role: "user", text: text5 };
|
|
28787
|
+
if (attachments?.length) payload.attachments = attachments;
|
|
28786
28788
|
this.insert({
|
|
28787
28789
|
kind: "text",
|
|
28788
28790
|
role: "user",
|
|
@@ -28793,6 +28795,7 @@ var init_chat_item_ledger = __esm({
|
|
|
28793
28795
|
inContent: false,
|
|
28794
28796
|
persist: true,
|
|
28795
28797
|
forceNullMessage: true,
|
|
28798
|
+
payload,
|
|
28796
28799
|
metadata: {
|
|
28797
28800
|
[CHAT_ITEM_META_LEGACY_CONTENT]: false,
|
|
28798
28801
|
...attachments?.length ? { attachments } : {}
|
|
@@ -29111,7 +29114,8 @@ var init_chat_item_ledger = __esm({
|
|
|
29111
29114
|
ord: created.ord,
|
|
29112
29115
|
version: writeVersion,
|
|
29113
29116
|
role: input.role,
|
|
29114
|
-
text: input.text
|
|
29117
|
+
text: input.text,
|
|
29118
|
+
payload
|
|
29115
29119
|
});
|
|
29116
29120
|
} catch {
|
|
29117
29121
|
}
|
|
@@ -160522,6 +160526,7 @@ var init_live_chat = __esm({
|
|
|
160522
160526
|
if (!turn || turn.status !== "running") {
|
|
160523
160527
|
return say({ accepted: false, reason: "no-live-turn" }, { turnStatus: turn?.status ?? "<no-turn>" });
|
|
160524
160528
|
}
|
|
160529
|
+
if (turn.recovering) return say({ accepted: false, reason: "recovering" }, { recovering: true });
|
|
160525
160530
|
const fn = turn.handle.appendInput;
|
|
160526
160531
|
if (typeof fn !== "function") return say({ accepted: false, reason: "unsupported" });
|
|
160527
160532
|
const pendingItemId = !opts?.system && opts?.clientMessageKey ? `oasis-user:${opts.clientMessageKey}` : void 0;
|
|
@@ -160797,7 +160802,7 @@ async function appendNowThroughQueue(chatSessionId, text5, deps, clientKey, atta
|
|
|
160797
160802
|
...displayText !== merged ? { displayText } : {},
|
|
160798
160803
|
...attachmentRefs.length ? { attachments: attachmentRefs } : {}
|
|
160799
160804
|
});
|
|
160800
|
-
const DEFINITELY_NOT_DELIVERED = /* @__PURE__ */ new Set(["unsupported", "no-live-turn", "session-closing", "turn-finished"]);
|
|
160805
|
+
const DEFINITELY_NOT_DELIVERED = /* @__PURE__ */ new Set(["unsupported", "no-live-turn", "session-closing", "turn-finished", "recovering"]);
|
|
160801
160806
|
const nextState = res.accepted ? "delivered" : DEFINITELY_NOT_DELIVERED.has(res.reason ?? "") ? "queued" : "unconfirmed";
|
|
160802
160807
|
await deps.store.markPendingMessages?.(chatSessionId, ids2, nextState, res.reason).catch(() => void 0);
|
|
160803
160808
|
return { ...res, queued: true, delivered: merged, batch, ...prepared.paths.length ? { attachmentPaths: prepared.paths } : {} };
|
|
@@ -163873,7 +163878,8 @@ async function linkChatAttachmentsToWorkOrder(input) {
|
|
|
163873
163878
|
...candidate.contentType ?? meta?.contentType ? { contentType: candidate.contentType ?? meta?.contentType } : {},
|
|
163874
163879
|
sourceSessionId: input.chatSessionId,
|
|
163875
163880
|
sourceMessageId: candidate.messageId,
|
|
163876
|
-
linkedAt: input.linkedAt
|
|
163881
|
+
linkedAt: input.linkedAt,
|
|
163882
|
+
...input.uploadedBy ? { uploadedBy: input.uploadedBy } : {}
|
|
163877
163883
|
};
|
|
163878
163884
|
try {
|
|
163879
163885
|
await input.artifacts.addWorkOrderFile(file);
|
|
@@ -196760,6 +196766,7 @@ var init_continue_outcome = __esm({
|
|
|
196760
196766
|
"use strict";
|
|
196761
196767
|
REASON_TEXT = {
|
|
196762
196768
|
"no-live-turn": "\u4E13\u5BB6\u6B64\u523B\u6CA1\u6709\u6B63\u5728\u8DD1\u7684\u90A3\u4E00\u8F6E\uFF08\u591A\u534A\u662F\u521A\u6536\u5C3E\u3001\u4E0B\u4E00\u8F6E\u8FD8\u6CA1\u8D77\u6765\uFF09",
|
|
196769
|
+
recovering: "\u670D\u52A1\u521A\u91CD\u542F\uFF0C\u4E13\u5BB6\u90A3\u4E00\u8F6E\u8FD8\u5728\u6062\u590D\u4E2D\uFF08\u8282\u70B9\u91CD\u8FDE\u540E\u5C31\u80FD\u63D2\uFF1B\u8FD9\u53E5\u8BDD\u5DF2\u6392\u961F\uFF09",
|
|
196763
196770
|
unsupported: "\u8FD9\u4E2A\u4E13\u5BB6\u7528\u7684\u6A21\u578B\u4E0D\u652F\u6301\u4E2D\u9014\u63D2\u8BDD\uFF08\u8981\u7B49\u5B83\u8FD9\u4E00\u8F6E\u8DD1\u5B8C\uFF09",
|
|
196764
196771
|
"session-closing": "\u4E13\u5BB6\u90A3\u4E00\u8F6E\u6B63\u5728\u6536\u5C3E\uFF0C\u8FD9\u53E5\u8BDD\u6765\u665A\u4E86\u4E00\u6B65",
|
|
196765
196772
|
"turn-finished": "\u6295\u9012\u8FC7\u7A0B\u4E2D\u4E13\u5BB6\u90A3\u4E00\u8F6E\u5DF2\u7ECF\u6536\u5C3E\u4E86",
|
|
@@ -198844,9 +198851,13 @@ async function mintInstallationToken(args, deps = {}) {
|
|
|
198844
198851
|
const token = typeof body2["token"] === "string" ? body2["token"] : "";
|
|
198845
198852
|
if (!token) throw new Error("github app: token missing in response");
|
|
198846
198853
|
const parsed = typeof body2["expires_at"] === "string" ? Date.parse(body2["expires_at"]) : NaN;
|
|
198854
|
+
const rawPerms = body2["permissions"];
|
|
198855
|
+
const permissions = {};
|
|
198856
|
+
for (const [k2, v2] of Object.entries(rawPerms ?? {})) if (typeof v2 === "string") permissions[k2] = v2;
|
|
198847
198857
|
return {
|
|
198848
198858
|
token,
|
|
198849
|
-
expiresAtMs: Number.isFinite(parsed) ? parsed : nowMs + 60 * 60 * 1e3
|
|
198859
|
+
expiresAtMs: Number.isFinite(parsed) ? parsed : nowMs + 60 * 60 * 1e3,
|
|
198860
|
+
permissions
|
|
198850
198861
|
};
|
|
198851
198862
|
}
|
|
198852
198863
|
async function listAppInstallations(args, deps = {}) {
|
|
@@ -198870,7 +198881,10 @@ async function listAppInstallations(args, deps = {}) {
|
|
|
198870
198881
|
const id = item["id"] === void 0 ? "" : String(item["id"]);
|
|
198871
198882
|
if (!id) return [];
|
|
198872
198883
|
const account = item["account"];
|
|
198873
|
-
|
|
198884
|
+
const rawPerms = item["permissions"];
|
|
198885
|
+
const permissions = {};
|
|
198886
|
+
for (const [k2, v2] of Object.entries(rawPerms ?? {})) if (typeof v2 === "string") permissions[k2] = v2;
|
|
198887
|
+
return [{ installationId: id, account: String(account?.["login"] ?? ""), permissions }];
|
|
198874
198888
|
});
|
|
198875
198889
|
}
|
|
198876
198890
|
async function fetchAppSelfMetadata(args, deps = {}) {
|
|
@@ -201801,6 +201815,15 @@ var init_provision = __esm({
|
|
|
201801
201815
|
});
|
|
201802
201816
|
|
|
201803
201817
|
// ../connectors/src/prepare-job.ts
|
|
201818
|
+
function loginShellShadow(commandName) {
|
|
201819
|
+
const home2 = process.env["HOME"];
|
|
201820
|
+
if (!home2) return "";
|
|
201821
|
+
for (const dir of [(0, import_node_path17.join)(home2, ".local", "bin"), (0, import_node_path17.join)(home2, "bin")]) {
|
|
201822
|
+
const candidate = (0, import_node_path17.join)(dir, commandName);
|
|
201823
|
+
if ((0, import_node_fs11.existsSync)(candidate)) return candidate;
|
|
201824
|
+
}
|
|
201825
|
+
return "";
|
|
201826
|
+
}
|
|
201804
201827
|
async function prepareConnectorsForJob(job, deps) {
|
|
201805
201828
|
const creds = job.connectorCreds ?? [];
|
|
201806
201829
|
const remoteWrappers = job.wrapperPaths ?? [];
|
|
@@ -201836,7 +201859,16 @@ async function prepareConnectorsForJob(job, deps) {
|
|
|
201836
201859
|
localWrappers.push(staged);
|
|
201837
201860
|
status.wrapperReady = true;
|
|
201838
201861
|
}
|
|
201839
|
-
|
|
201862
|
+
const shadow = loginShellShadow(connector.commandName);
|
|
201863
|
+
if (shadow) {
|
|
201864
|
+
status.shadowedBy = shadow;
|
|
201865
|
+
log(
|
|
201866
|
+
"[node-connectors]",
|
|
201867
|
+
`\u26A0 ${slug6}: creds + wrapper \u5DF2\u843D\u5728\u672C\u673A\uFF0C\u4F46 **${shadow} \u4F1A\u5728\u767B\u5F55 shell \u91CC\u6321\u4F4F wrapper**\u2014\u2014Ubuntu \u7684 ~/.profile \u628A $HOME/.local/bin \u524D\u7F6E\u5230 PATH \u6700\u524D\u3002agent \u4E00\u65E6\u7528 \`bash -lc\` \u8C03 ${connector.commandName}\uFF0C\u8D70\u5230\u7684\u662F\u90A3\u4E2A\u5BBF\u4E3B\u526F\u672C\u3001\u914D\u7F6E\u76EE\u5F55\u53C8\u88AB\u9694\u79BB\u6210\u7A7A\u7684\uFF0C\u7ED3\u679C\u5C31\u662F\u300C\u672A\u914D\u7F6E\u300D\u3002\u5904\u7406\u529E\u6CD5\uFF1A\u5220\u6389 ${shadow}\uFF08\u8FDE\u63A5\u5668\u81EA\u5E26\u7684\u771F CLI \u5728 ~/.oasis/connector-tools/bin \u4E0B\uFF0C\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002`
|
|
201868
|
+
);
|
|
201869
|
+
} else {
|
|
201870
|
+
log("[node-connectors]", `${slug6}: creds + wrapper \u5DF2\u843D\u5728\u672C\u673A`);
|
|
201871
|
+
}
|
|
201840
201872
|
} catch (err) {
|
|
201841
201873
|
status.error = String(err);
|
|
201842
201874
|
log(
|
|
@@ -205065,7 +205097,9 @@ async function startOasisServer(opts) {
|
|
|
205065
205097
|
blobMeta: blobMetaAcrossCabinets,
|
|
205066
205098
|
// 工单就是这一刻建出来的,所以「现在」就是任务创建时间。取一次、全部文件共用,
|
|
205067
205099
|
// 保证同一次建单带进来的文件在任务侧显示的是同一个时刻(排期序 28 原文口径)。
|
|
205068
|
-
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
205100
|
+
linkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
205101
|
+
// 建单发起人就是把这些附件带进任务的人——项目页「创建人」列与预览副标据此署名。
|
|
205102
|
+
uploadedBy: actor
|
|
205069
205103
|
}).catch((err) => ({
|
|
205070
205104
|
linked: 0,
|
|
205071
205105
|
warnings: [`\u672A\u80FD\u628A\u672C\u6B21\u5BF9\u8BDD\u7684\u9644\u4EF6\u5E26\u5165\u4EFB\u52A1\u6587\u4EF6\uFF1A${err instanceof Error ? err.message : String(err)}`]
|
|
@@ -206226,13 +206260,16 @@ ${composed}`;
|
|
|
206226
206260
|
只推 user:assistant 的每一条本来就走 `emitV3`。 */
|
|
206227
206261
|
onPersisted: (info) => {
|
|
206228
206262
|
if (info.role !== "user") return;
|
|
206263
|
+
const rowPayload = info.payload && typeof info.payload === "object" && !Array.isArray(info.payload) ? info.payload : void 0;
|
|
206264
|
+
const rowCsid = typeof rowPayload?.["clientSubmitId"] === "string" ? rowPayload["clientSubmitId"] : void 0;
|
|
206265
|
+
const rowAttachments = Array.isArray(rowPayload?.["attachments"]) ? rowPayload["attachments"] : void 0;
|
|
206229
206266
|
opts.liveChat?.publishUserItem(persistTarget?.id ?? session.id, {
|
|
206230
206267
|
itemId: info.itemId,
|
|
206231
206268
|
ord: info.ord,
|
|
206232
206269
|
version: info.version,
|
|
206233
206270
|
text: info.text,
|
|
206234
|
-
...
|
|
206235
|
-
...
|
|
206271
|
+
...rowCsid ? { clientSubmitId: rowCsid } : {},
|
|
206272
|
+
...rowAttachments?.length ? { attachments: rowAttachments } : {}
|
|
206236
206273
|
});
|
|
206237
206274
|
},
|
|
206238
206275
|
sessionId: persistTarget?.id ?? session.id,
|
|
@@ -206737,6 +206774,52 @@ ${composed}`;
|
|
|
206737
206774
|
}
|
|
206738
206775
|
return;
|
|
206739
206776
|
}
|
|
206777
|
+
if (url.pathname.startsWith("/api/connectors/") && /\/auth\/(begin|rollback|commit)$/.test(url.pathname) && req.method === "POST") {
|
|
206778
|
+
const m2 = url.pathname.match(/^\/api\/connectors\/([^/]+)\/auth\/(begin|rollback|commit)$/);
|
|
206779
|
+
try {
|
|
206780
|
+
if (!m2) throw new Error("bad path");
|
|
206781
|
+
const connectorId = decodeURIComponent(m2[1]);
|
|
206782
|
+
const op = m2[2];
|
|
206783
|
+
const { actorId } = JSON.parse(rawBody || "{}");
|
|
206784
|
+
if (!actorId) {
|
|
206785
|
+
res.writeHead(400).end(JSON.stringify({ error: "actorId required" }));
|
|
206786
|
+
return;
|
|
206787
|
+
}
|
|
206788
|
+
const svc = await connectorActorsService();
|
|
206789
|
+
if (!svc) throw new Error("actors service unavailable");
|
|
206790
|
+
const cacheKey = `${currentCompanyId ?? ""}::${actorId}::${connectorId}`;
|
|
206791
|
+
const now = Date.now();
|
|
206792
|
+
for (const [k2, v2] of connectorAuthSnapshots) {
|
|
206793
|
+
if (now - v2.at > CONNECTOR_AUTH_SNAPSHOT_TTL_MS) connectorAuthSnapshots.delete(k2);
|
|
206794
|
+
}
|
|
206795
|
+
if (op === "begin") {
|
|
206796
|
+
const rows = await svc.snapshotActorConnectorVars(actorId, connectorId);
|
|
206797
|
+
connectorAuthSnapshots.set(cacheKey, { rows, at: now });
|
|
206798
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, snapshot: rows.length }));
|
|
206799
|
+
return;
|
|
206800
|
+
}
|
|
206801
|
+
if (op === "commit") {
|
|
206802
|
+
connectorAuthSnapshots.delete(cacheKey);
|
|
206803
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
206804
|
+
return;
|
|
206805
|
+
}
|
|
206806
|
+
const snap = connectorAuthSnapshots.get(cacheKey);
|
|
206807
|
+
if (!snap) {
|
|
206808
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: false, reason: "no_snapshot" }));
|
|
206809
|
+
return;
|
|
206810
|
+
}
|
|
206811
|
+
const out = await svc.restoreActorConnectorVars(
|
|
206812
|
+
actorId,
|
|
206813
|
+
connectorId,
|
|
206814
|
+
snap.rows
|
|
206815
|
+
);
|
|
206816
|
+
connectorAuthSnapshots.delete(cacheKey);
|
|
206817
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, ...out }));
|
|
206818
|
+
} catch (e) {
|
|
206819
|
+
res.writeHead(500).end(JSON.stringify({ error: String(e) }));
|
|
206820
|
+
}
|
|
206821
|
+
return;
|
|
206822
|
+
}
|
|
206740
206823
|
if (req.method === "POST" && /^\/api\/connectors\/[^/]+\/disconnect$/.test(url.pathname)) {
|
|
206741
206824
|
const connId = decodeURIComponent(url.pathname.split("/")[3] ?? "");
|
|
206742
206825
|
const svc = await connectorActorsService();
|
|
@@ -207154,9 +207237,27 @@ ${composed}`;
|
|
|
207154
207237
|
return;
|
|
207155
207238
|
}
|
|
207156
207239
|
const meta = await fetchAppSelfMetadata({ appId, privateKeyPem });
|
|
207157
|
-
const
|
|
207158
|
-
const
|
|
207159
|
-
const
|
|
207240
|
+
const storedInstallationId = await svc.revealResolvedVariable("GITHUB_APP_INSTALLATION_ID", actorId);
|
|
207241
|
+
const installs = await listAppInstallations({ appId, privateKeyPem }).catch(() => []);
|
|
207242
|
+
const chosen = installs.find((i) => i.installationId === storedInstallationId) ?? (installs.length === 1 ? installs[0] : void 0);
|
|
207243
|
+
const installationStale = Boolean(storedInstallationId) && installs.length > 0 && !chosen;
|
|
207244
|
+
const installationId = chosen?.installationId || storedInstallationId;
|
|
207245
|
+
let grantedFrom = "app";
|
|
207246
|
+
let effective = meta.permissions;
|
|
207247
|
+
if (installationId) {
|
|
207248
|
+
const minted = await mintInstallationToken({ appId, privateKeyPem, installationId }).catch(() => null);
|
|
207249
|
+
if (minted && Object.keys(minted.permissions).length > 0) {
|
|
207250
|
+
effective = minted.permissions;
|
|
207251
|
+
grantedFrom = "token";
|
|
207252
|
+
} else if (chosen && Object.keys(chosen.permissions).length > 0) {
|
|
207253
|
+
effective = chosen.permissions;
|
|
207254
|
+
grantedFrom = "installation";
|
|
207255
|
+
} else {
|
|
207256
|
+
effective = await fetchInstallationPermissions({ appId, privateKeyPem, installationId });
|
|
207257
|
+
grantedFrom = "installation";
|
|
207258
|
+
}
|
|
207259
|
+
}
|
|
207260
|
+
const missing = missingAppPermissions({ ...meta.permissions, ...effective });
|
|
207160
207261
|
const requestedAll = Object.entries(meta.permissions).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key));
|
|
207161
207262
|
const grantedKeys = new Set(
|
|
207162
207263
|
Object.entries(effective).filter(([, level]) => Boolean(level)).map(([k2]) => k2)
|
|
@@ -207169,13 +207270,33 @@ ${composed}`;
|
|
|
207169
207270
|
requested: requestedAll,
|
|
207170
207271
|
/** true=读的是这次安装真正被授予的权限;false=只拿到 App 申请的,可能偏乐观。 */
|
|
207171
207272
|
fromInstallation: Boolean(installationId),
|
|
207273
|
+
/** 「已授予」这一列是从哪问出来的:token(最准)/ 安装记录 / 只有 App 申请。 */
|
|
207274
|
+
grantedFrom,
|
|
207275
|
+
/** 读的是哪一处安装——同一个 App 装多处时,人得看得见自己批的是不是这一处。 */
|
|
207276
|
+
installationAccount: chosen?.account ?? "",
|
|
207277
|
+
/** 库里存的 installation 已不在这个 App 的安装列表里(卸载重装过)。 */
|
|
207278
|
+
installationStale,
|
|
207279
|
+
/**
|
|
207280
|
+
* 这个 App 的**其它**安装及各自的权限项数。只在装了不止一处时才有内容。
|
|
207281
|
+
* 它专门回答那句「我明明已经批过了」:批在哪一处,这里一眼看得见。
|
|
207282
|
+
*/
|
|
207283
|
+
otherInstallations: installs.filter((i) => i.installationId !== installationId).map((i) => ({ account: i.account, count: Object.keys(i.permissions).length })),
|
|
207172
207284
|
/**
|
|
207173
|
-
*
|
|
207174
|
-
*
|
|
207285
|
+
* ⚠ 保留只为**兼容老前端**(新卡片不再读它):新口径不分「已授权 / 申请中」,
|
|
207286
|
+
* 见 `detected`。这一格哪天确认没有旧客户端在读了就可以删。
|
|
207175
207287
|
*/
|
|
207176
207288
|
awaitingApproval: requestedAll.filter((r) => !grantedKeys.has(r.key)).map((r) => r.key),
|
|
207177
207289
|
/**
|
|
207178
|
-
*
|
|
207290
|
+
* **检测到的全部权限**(App 申请的 ∪ 真拿得到的),英文原名 + 级别,按名排序。
|
|
207291
|
+
* 界面就照这一份平铺成一排 chip,不分组、不计数、不解释审批状态。
|
|
207292
|
+
*/
|
|
207293
|
+
detected: [.../* @__PURE__ */ new Set([...requestedAll.map((r) => r.key), ...grantedKeys])].sort((a, b2) => a.localeCompare(b2)).map((key) => ({
|
|
207294
|
+
key,
|
|
207295
|
+
// 真给的级别优先;只在 App 侧见过的才回落成它申请的级别。
|
|
207296
|
+
level: String(effective[key] ?? meta.permissions[key] ?? "")
|
|
207297
|
+
})),
|
|
207298
|
+
/**
|
|
207299
|
+
* agent **真正拿得到**的全部权限(优先读 token 自己的权限位),一律用 GitHub 的英文原名 + 级别
|
|
207179
207300
|
* (2026-09-10 发起人:「所有权限都用英文原文,不区分是不是八项权限」)。
|
|
207180
207301
|
*
|
|
207181
207302
|
* 此前这里是 `GITHUB_APP_PERMISSION_LABELS.filter(...)` —— 那张表只有本仓必需的
|
|
@@ -207325,7 +207446,8 @@ ${composed}`;
|
|
|
207325
207446
|
return;
|
|
207326
207447
|
}
|
|
207327
207448
|
try {
|
|
207328
|
-
const
|
|
207449
|
+
const startSvc = await connectorActorsService();
|
|
207450
|
+
const appSecret = startSvc ? await startSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
|
|
207329
207451
|
if (!appSecret) throw new Error("FEISHU_APP_SECRET not found \u2014 please complete app setup first");
|
|
207330
207452
|
const basicAuth = Buffer.from(`${appId}:${appSecret}`).toString("base64");
|
|
207331
207453
|
const resp = await fetch("https://accounts.feishu.cn/oauth/v1/device_authorization", {
|
|
@@ -207355,7 +207477,8 @@ ${composed}`;
|
|
|
207355
207477
|
return;
|
|
207356
207478
|
}
|
|
207357
207479
|
try {
|
|
207358
|
-
const
|
|
207480
|
+
const pollSvc = await connectorActorsService();
|
|
207481
|
+
const appSecret = pollSvc ? await pollSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
|
|
207359
207482
|
if (!appSecret) throw new Error("FEISHU_APP_SECRET not found \u2014 please complete app setup first");
|
|
207360
207483
|
const resp = await fetch("https://open.feishu.cn/open-apis/authen/v2/oauth/token", {
|
|
207361
207484
|
method: "POST",
|
|
@@ -207381,6 +207504,7 @@ ${composed}`;
|
|
|
207381
207504
|
if (svc) {
|
|
207382
207505
|
if (actorId) {
|
|
207383
207506
|
await svc.putVariable({ key: "FEISHU_APP_ID", value: appId, scope: "personal", actorId, connectorId: "feishu", encrypted: true });
|
|
207507
|
+
await svc.putVariable({ key: "FEISHU_APP_SECRET", value: appSecret, scope: "personal", actorId, connectorId: "feishu", encrypted: true });
|
|
207384
207508
|
} else {
|
|
207385
207509
|
await svc.putVariable({ key: "FEISHU_APP_ID", value: appId, scope: "global", connectorId: "feishu", encrypted: true });
|
|
207386
207510
|
await svc.upsertConnector({ id: "feishu", name: "\u98DE\u4E66", mode: "oauth", status: "connected", account: appId });
|
|
@@ -207607,7 +207731,7 @@ ${composed}`;
|
|
|
207607
207731
|
}
|
|
207608
207732
|
};
|
|
207609
207733
|
}
|
|
207610
|
-
var http, import_node_crypto37, githubAppPending, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
|
|
207734
|
+
var http, import_node_crypto37, githubAppPending, connectorAuthSnapshots, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
|
|
207611
207735
|
var init_server3 = __esm({
|
|
207612
207736
|
"../server/src/server.ts"() {
|
|
207613
207737
|
"use strict";
|
|
@@ -207665,6 +207789,8 @@ var init_server3 = __esm({
|
|
|
207665
207789
|
init_src8();
|
|
207666
207790
|
init_continuation();
|
|
207667
207791
|
githubAppPending = new PendingAppCreations();
|
|
207792
|
+
connectorAuthSnapshots = /* @__PURE__ */ new Map();
|
|
207793
|
+
CONNECTOR_AUTH_SNAPSHOT_TTL_MS = 30 * 60 * 1e3;
|
|
207668
207794
|
defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
|
|
207669
207795
|
enc = (s2) => new TextEncoder().encode(s2);
|
|
207670
207796
|
CHAT_OUTPUT_LIMIT = 16e3;
|
|
@@ -208243,10 +208369,15 @@ var init_memory_registry_store = __esm({
|
|
|
208243
208369
|
const mk = this._varKey(v2.key, v2.actorId, v2.projectId);
|
|
208244
208370
|
const ex = this.variables.get(mk);
|
|
208245
208371
|
const keptName = v2.name ?? ex?.name;
|
|
208372
|
+
const keptCreatedBy = ex?.createdBy ?? v2.createdBy;
|
|
208373
|
+
const keptCreatedAt = ex?.createdAt ?? v2.createdAt;
|
|
208246
208374
|
this.variables.set(mk, {
|
|
208247
208375
|
...v2,
|
|
208248
208376
|
...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {},
|
|
208249
|
-
...keptName ? { name: keptName } : {}
|
|
208377
|
+
...keptName ? { name: keptName } : {},
|
|
208378
|
+
...keptCreatedBy ? { createdBy: keptCreatedBy } : {},
|
|
208379
|
+
...keptCreatedAt ? { createdAt: keptCreatedAt } : {},
|
|
208380
|
+
...v2.updatedBy ? { updatedBy: v2.updatedBy } : {}
|
|
208250
208381
|
});
|
|
208251
208382
|
}
|
|
208252
208383
|
async touchVariableUsage(key, actorId, projectId2, when) {
|
|
@@ -210763,8 +210894,14 @@ function foldSingleNodeTasks(items) {
|
|
|
210763
210894
|
paths.add(item.path);
|
|
210764
210895
|
nodeFoldersByTask.set(item.taskId, paths);
|
|
210765
210896
|
}
|
|
210897
|
+
const tasksWithRootFiles = /* @__PURE__ */ new Set();
|
|
210898
|
+
for (const item of items) {
|
|
210899
|
+
if (item.kind === "file" && item.taskId && item.path?.startsWith(`wo:${item.taskId}/file:`)) {
|
|
210900
|
+
tasksWithRootFiles.add(item.taskId);
|
|
210901
|
+
}
|
|
210902
|
+
}
|
|
210766
210903
|
const folded = new Set(
|
|
210767
|
-
[...nodeFoldersByTask].filter(([, paths]) => paths.size === 1).map(([taskId]) => taskId)
|
|
210904
|
+
[...nodeFoldersByTask].filter(([taskId, paths]) => paths.size === 1 && !tasksWithRootFiles.has(taskId)).map(([taskId]) => taskId)
|
|
210768
210905
|
);
|
|
210769
210906
|
if (folded.size === 0) return folded;
|
|
210770
210907
|
for (const item of items) {
|
|
@@ -211705,6 +211842,33 @@ var init_service4 = __esm({
|
|
|
211705
211842
|
});
|
|
211706
211843
|
}
|
|
211707
211844
|
}
|
|
211845
|
+
if (this.artifacts.listWorkOrderFiles) {
|
|
211846
|
+
for (const workOrderId of workorderSummaries.map((w2) => w2.id)) {
|
|
211847
|
+
const workOrderFiles = await this.artifacts.listWorkOrderFiles(workOrderId).catch(() => []);
|
|
211848
|
+
const taskTitle = workorderTitleById.get(workOrderId) ?? null;
|
|
211849
|
+
for (const file of workOrderFiles) {
|
|
211850
|
+
const creatorId = file.uploadedBy ?? null;
|
|
211851
|
+
items.push({
|
|
211852
|
+
kind: "file",
|
|
211853
|
+
id: file.fileId,
|
|
211854
|
+
name: file.name,
|
|
211855
|
+
path: `wo:${workOrderId}/file:${file.fileId}`,
|
|
211856
|
+
filePath: null,
|
|
211857
|
+
depth: 1,
|
|
211858
|
+
type: file.contentType ?? inferTypeFromName(file.name),
|
|
211859
|
+
blobRef: file.blobId,
|
|
211860
|
+
contentType: file.contentType ?? null,
|
|
211861
|
+
size: file.size,
|
|
211862
|
+
taskId: workOrderId,
|
|
211863
|
+
taskTitle,
|
|
211864
|
+
creatorKind: creatorId ? creatorId.startsWith("actor:human:") ? "human" : "agent" : null,
|
|
211865
|
+
creatorId,
|
|
211866
|
+
creatorName: creatorId ? this.resolveActorName(creatorId) : null,
|
|
211867
|
+
updatedAt: file.linkedAt
|
|
211868
|
+
});
|
|
211869
|
+
}
|
|
211870
|
+
}
|
|
211871
|
+
}
|
|
211708
211872
|
const foldedTasks = foldSingleNodeTasks(items);
|
|
211709
211873
|
const taskFolders = /* @__PURE__ */ new Set();
|
|
211710
211874
|
for (const item of [...items]) {
|
|
@@ -211743,6 +211907,8 @@ var init_service4 = __esm({
|
|
|
211743
211907
|
filePath: null,
|
|
211744
211908
|
depth: path41 ? path41.replace(/^\/+/, "").split("/").length - 1 : 0,
|
|
211745
211909
|
type: file.contentType ?? inferTypeFromName(file.name),
|
|
211910
|
+
blobRef: file.blobId,
|
|
211911
|
+
contentType: file.contentType ?? null,
|
|
211746
211912
|
size: file.size,
|
|
211747
211913
|
taskId: null,
|
|
211748
211914
|
taskTitle: null,
|
|
@@ -217065,6 +217231,11 @@ function wireRecoveredChatTurn(deps) {
|
|
|
217065
217231
|
...nextRuntimeSessionId ? { runtimeSessionId: nextRuntimeSessionId } : {},
|
|
217066
217232
|
touchedAt: finishedAt
|
|
217067
217233
|
}).catch(() => void 0);
|
|
217234
|
+
if (deps.onTurnSettled) {
|
|
217235
|
+
await deps.onTurnSettled(failed ? "failed" : "succeeded", finishedAt).catch((err) => {
|
|
217236
|
+
log3(`[chat-recovery] \u4F1A\u8BDD ${plan.chatSessionId} \u6536\u53E3\u8BB0\u8D26\u5931\u8D25\uFF08\u9000\u56DE\u6E05\u626B\u62CD\u515C\u5E95\uFF09: ${String(err)}`);
|
|
217237
|
+
});
|
|
217238
|
+
}
|
|
217068
217239
|
if (!liveTerminalEmitted) {
|
|
217069
217240
|
live.emitLive({
|
|
217070
217241
|
protocolVersion: 2,
|
|
@@ -217388,6 +217559,22 @@ async function recoverChatTurnsAcrossCompanies(deps) {
|
|
|
217388
217559
|
continue;
|
|
217389
217560
|
}
|
|
217390
217561
|
const handle = deps.recover(plan);
|
|
217562
|
+
const settleTurnId = turn?.id;
|
|
217563
|
+
const onTurnSettled = settleTurnId && stores.settleTurn ? async (status, completedAt) => {
|
|
217564
|
+
await stores.settleTurn(settleTurnId, {
|
|
217565
|
+
status,
|
|
217566
|
+
completedAt,
|
|
217567
|
+
...status === "failed" ? { lastError: `\u91CD\u6302\u7684\u8F6E\u4EE5 ${status} \u6536\u5C3E\uFF08run ${plan.runId}\uFF09` } : {}
|
|
217568
|
+
});
|
|
217569
|
+
log3(`[chat-recovery] \u8F6E\u6B21 ${settleTurnId}\uFF08\u4F1A\u8BDD ${plan.chatSessionId}\uFF09\u968F\u91CD\u6302\u6536\u53E3\u4E3A ${status}\uFF0C\u4F1A\u8BDD\u69FD\u5DF2\u91CA\u653E`);
|
|
217570
|
+
await deps.onTurnSettled?.({
|
|
217571
|
+
chatSessionId: plan.chatSessionId,
|
|
217572
|
+
turnId: settleTurnId,
|
|
217573
|
+
status,
|
|
217574
|
+
reason: `\u91CD\u6302\u7684\u8F6E\u4EE5 ${status} \u6536\u5C3E`,
|
|
217575
|
+
...companyId ? { companyId } : {}
|
|
217576
|
+
});
|
|
217577
|
+
} : void 0;
|
|
217391
217578
|
const { done } = wireRecoveredChatTurn({
|
|
217392
217579
|
plan,
|
|
217393
217580
|
handle,
|
|
@@ -217397,6 +217584,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
|
|
|
217397
217584
|
register: () => deps.register(plan, handle),
|
|
217398
217585
|
unregister: () => deps.unregister(plan),
|
|
217399
217586
|
...deps.traceHealth ? { traceHealth: deps.traceHealth } : {},
|
|
217587
|
+
...onTurnSettled ? { onTurnSettled } : {},
|
|
217400
217588
|
...stores.items ? { items: stores.items, itemsTurnId } : {},
|
|
217401
217589
|
log: log3
|
|
217402
217590
|
});
|
|
@@ -219448,6 +219636,46 @@ ${input.description}
|
|
|
219448
219636
|
listActorConnectorConnections(actorId) {
|
|
219449
219637
|
return this.opts.store.listActorConnectorConnections(actorId);
|
|
219450
219638
|
}
|
|
219639
|
+
/**
|
|
219640
|
+
* 授权流程**开始前**给这名员工在这个连接器下的全部个人变量拍一张快照(原样密文,不解密)。
|
|
219641
|
+
*
|
|
219642
|
+
* 为什么需要它(2026-09-10 发起人:「如果本身已经有连接器身份,如果重新连接没走完,
|
|
219643
|
+
* 则保留原身份不覆盖,之前重连到一半把原来的身份覆盖了」):
|
|
219644
|
+
* 「重新连接」是**边走边落库**的——飞书在「创建应用」那一步就把 `FEISHU_APP_ID` /
|
|
219645
|
+
* `FEISHU_APP_SECRET` 覆盖掉了,GitHub 在 manifest 回调那一步就把 `GITHUB_APP_*` 覆盖掉了,
|
|
219646
|
+
* 而这两步都在「用户完成授权」**之前**。人中途关掉弹窗,旧身份就已经没了。
|
|
219647
|
+
*
|
|
219648
|
+
* 前端此前的回滚只删「这次新增的键」(基线差集),**对已存在的键无能为力**——它拿不到旧值
|
|
219649
|
+
* (读侧永远只给掩码)。所以快照必须在服务端拍、也在服务端写回。
|
|
219650
|
+
*/
|
|
219651
|
+
async snapshotActorConnectorVars(actorId, connectorId) {
|
|
219652
|
+
const rows = await this.opts.store.listVariables(actorId);
|
|
219653
|
+
return rows.filter((v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId === connectorId);
|
|
219654
|
+
}
|
|
219655
|
+
/**
|
|
219656
|
+
* 把快照原样写回:**快照里有的恢复旧值,快照之后新冒出来的键删掉**。
|
|
219657
|
+
*
|
|
219658
|
+
* 密文原样搬运,不经解密再加密——少一次明文落到内存里,也不受密钥轮换影响。
|
|
219659
|
+
* 返回改了几行,调用方好把「回滚做了什么」如实说出来(回滚静默是上一版的毛病)。
|
|
219660
|
+
*/
|
|
219661
|
+
async restoreActorConnectorVars(actorId, connectorId, snapshot) {
|
|
219662
|
+
const keep = new Map(snapshot.map((r) => [r.key, r]));
|
|
219663
|
+
const now = await this.snapshotActorConnectorVars(actorId, connectorId);
|
|
219664
|
+
let removed = 0;
|
|
219665
|
+
for (const row of now) {
|
|
219666
|
+
if (keep.has(row.key)) continue;
|
|
219667
|
+
await this.deleteVariable(row.key, actorId);
|
|
219668
|
+
removed += 1;
|
|
219669
|
+
}
|
|
219670
|
+
let restored = 0;
|
|
219671
|
+
for (const row of keep.values()) {
|
|
219672
|
+
const current = now.find((r) => r.key === row.key);
|
|
219673
|
+
if (current && current.valueEncrypted === row.valueEncrypted) continue;
|
|
219674
|
+
await this.opts.store.putVariable({ ...row, updatedAt: this.now() });
|
|
219675
|
+
restored += 1;
|
|
219676
|
+
}
|
|
219677
|
+
return { restored, removed };
|
|
219678
|
+
}
|
|
219451
219679
|
/**
|
|
219452
219680
|
* 员工连接器面板所需状态:连接记录 + 组织级已连接集合 + 该员工已授权(有个人 connector 变量)集合。
|
|
219453
219681
|
* 前端据此算每个 connector 的 hasRecord / 有效 enabled / connected(actor 或 global)。
|
|
@@ -219464,7 +219692,14 @@ ${input.description}
|
|
|
219464
219692
|
vars.filter((v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId).map((v2) => v2.connectorId)
|
|
219465
219693
|
)];
|
|
219466
219694
|
const effective = await this.effectiveConnectorEntries(actorId, config2, connectors);
|
|
219467
|
-
|
|
219695
|
+
const flat = {};
|
|
219696
|
+
for (const v2 of vars) if (v2.scope === "global" && !v2.actorId) flat[v2.key] = this.decodeCiphertext(v2.valueEncrypted);
|
|
219697
|
+
for (const v2 of vars) if (v2.scope === "personal" && (!v2.actorId || v2.actorId === actorId)) flat[v2.key] = this.decodeCiphertext(v2.valueEncrypted);
|
|
219698
|
+
const credentialReady = collectAllConnectorCredentials({
|
|
219699
|
+
vars: flat,
|
|
219700
|
+
actorName: flat["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent"
|
|
219701
|
+
}).map((c) => c.slug);
|
|
219702
|
+
return { connections, globalConnected, actorConnected, effective, credentialReady };
|
|
219468
219703
|
}
|
|
219469
219704
|
/**
|
|
219470
219705
|
* 该员工每个连接器的「有效启用」集合(变量/技能注入闸门):
|
|
@@ -219577,6 +219812,11 @@ ${input.description}
|
|
|
219577
219812
|
deliveryMode: v2.deliveryMode ?? "env",
|
|
219578
219813
|
valueEncrypted: v2.encrypted === false ? `plain:${v2.value}` : this.encrypt(v2.value),
|
|
219579
219814
|
updatedAt: this.now(),
|
|
219815
|
+
/* 创建者 / 创建时间 / 更新者:**由服务端落,不接受调用方自报**。
|
|
219816
|
+
createdBy/createdAt 两格存储层按「第一次落行才写」处理(COALESCE 旧值),所以这里每次都传,
|
|
219817
|
+
写不写得进去由存储层决定;updatedBy 每次覆盖。
|
|
219818
|
+
`by` 缺省(连接器授权流程等系统写入)时三格都不传——**不拿一个假身份填上去**。 */
|
|
219819
|
+
...v2.by ? { createdBy: v2.by, createdAt: this.now(), updatedBy: v2.by } : {},
|
|
219580
219820
|
// ADR 凭据保险库补章 §D2.2:expiresAt 写侧穿透——**显式带 key**(含 undefined),使清空(传空→undefined)
|
|
219581
219821
|
// 也落到底(store 两实现都按此覆盖)。lastUsedAt 从不由写侧传,取用时另刷(§D2.3)。
|
|
219582
219822
|
expiresAt: v2.expiresAt
|
|
@@ -219649,7 +219889,12 @@ ${input.description}
|
|
|
219649
219889
|
updatedAt: r.updatedAt,
|
|
219650
219890
|
// ADR 凭据保险库补章 §D2.1:到期/取用时间戳透传(展示层据此染色;补章 §D2.4)。
|
|
219651
219891
|
...r.expiresAt !== void 0 ? { expiresAt: r.expiresAt } : {},
|
|
219652
|
-
...r.lastUsedAt !== void 0 ? { lastUsedAt: r.lastUsedAt } : {}
|
|
219892
|
+
...r.lastUsedAt !== void 0 ? { lastUsedAt: r.lastUsedAt } : {},
|
|
219893
|
+
// 创建者 / 创建时间 / 更新者(子 PRD《密钥管理》§2 + 2026-09-10 追加)。
|
|
219894
|
+
// 存量行没有这三格,原样不带出去——展示侧出「—」,不拿 updatedAt 冒充 createdAt。
|
|
219895
|
+
...r.createdBy !== void 0 ? { createdBy: r.createdBy } : {},
|
|
219896
|
+
...r.createdAt !== void 0 ? { createdAt: r.createdAt } : {},
|
|
219897
|
+
...r.updatedBy !== void 0 ? { updatedBy: r.updatedBy } : {}
|
|
219653
219898
|
};
|
|
219654
219899
|
});
|
|
219655
219900
|
}
|
|
@@ -221503,6 +221748,8 @@ function actorsDomain(opts) {
|
|
|
221503
221748
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
221504
221749
|
...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
|
|
221505
221750
|
...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
|
|
221751
|
+
// 创建者/更新者取**当前登录人**,不接受 body 传入——自报的「谁建的」等于没有这一格。
|
|
221752
|
+
by: req.auth.actor,
|
|
221506
221753
|
expiresAt: expiresAt2
|
|
221507
221754
|
});
|
|
221508
221755
|
await auditVariableChange(
|
|
@@ -221575,6 +221822,7 @@ function actorsDomain(opts) {
|
|
|
221575
221822
|
...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
|
|
221576
221823
|
...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
|
|
221577
221824
|
...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
|
|
221825
|
+
by: req.auth.actor,
|
|
221578
221826
|
expiresAt: expiresAt2
|
|
221579
221827
|
});
|
|
221580
221828
|
await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
|
|
@@ -223377,6 +223625,9 @@ function toApiProjectFilesView(view) {
|
|
|
223377
223625
|
// 其它外部引用→链接)。交付物整份产物行才有,上传文件与文件夹为 null。
|
|
223378
223626
|
content_kind: item.contentKind ?? null,
|
|
223379
223627
|
content_ref: item.contentRef ?? null,
|
|
223628
|
+
// 上传文件行(项目级上传 / 工单级文件)的 blob 引用 + MIME:前端据此直读 blob 预览。交付物与文件夹为 null。
|
|
223629
|
+
blob_ref: item.blobRef ?? null,
|
|
223630
|
+
content_type: item.contentType ?? null,
|
|
223380
223631
|
size: item.size,
|
|
223381
223632
|
task_id: item.taskId,
|
|
223382
223633
|
task_title: item.taskTitle,
|
|
@@ -228694,6 +228945,9 @@ function toAuditEntry(op) {
|
|
|
228694
228945
|
actor: op.actor,
|
|
228695
228946
|
op: op.kind,
|
|
228696
228947
|
artifactId: op.artifactId,
|
|
228948
|
+
/* 动作名单独发一份(见契约 `AuditEntry.actionLabel`):`summary` 拼着裸 artifactId,
|
|
228949
|
+
不能直接给人看,而「已处理」那一行需要「干了什么 + 在哪个节点」两截并排。 */
|
|
228950
|
+
actionLabel: KIND_LABEL2[op.kind] ?? op.kind,
|
|
228697
228951
|
summary: `${KIND_LABEL2[op.kind] ?? op.kind} \xB7 ${op.artifactId}`
|
|
228698
228952
|
};
|
|
228699
228953
|
}
|
|
@@ -228730,6 +228984,7 @@ function eventToAuditEntry(rec) {
|
|
|
228730
228984
|
actor: rec.actorId,
|
|
228731
228985
|
op: ev.kind,
|
|
228732
228986
|
artifactId,
|
|
228987
|
+
actionLabel: KIND_LABEL2[ev.kind] ?? ev.kind,
|
|
228733
228988
|
summary: `${KIND_LABEL2[ev.kind] ?? ev.kind}${artifactId ? ` \xB7 ${artifactId}` : ""}`
|
|
228734
228989
|
};
|
|
228735
228990
|
}
|
|
@@ -229680,13 +229935,32 @@ async function buildLeadResolver(registry2) {
|
|
|
229680
229935
|
return null;
|
|
229681
229936
|
};
|
|
229682
229937
|
}
|
|
229683
|
-
async function
|
|
229938
|
+
async function memberNameResolver(listMembers, companyId) {
|
|
229939
|
+
if (!listMembers) return void 0;
|
|
229940
|
+
let members;
|
|
229941
|
+
try {
|
|
229942
|
+
members = await listMembers(companyId);
|
|
229943
|
+
} catch (err) {
|
|
229944
|
+
console.warn(`[collab] \u6210\u5458\u6635\u79F0\u8BFB\u53D6\u5931\u8D25\uFF0C\u672C\u6B21\u56DE\u843D\u5458\u5DE5\u6863\u6848\u59D3\u540D: ${String(err)}`);
|
|
229945
|
+
return void 0;
|
|
229946
|
+
}
|
|
229947
|
+
const byActor2 = /* @__PURE__ */ new Map();
|
|
229948
|
+
for (const m2 of members) {
|
|
229949
|
+
const name = m2.displayName?.trim();
|
|
229950
|
+
if (name) byActor2.set(m2.actorId, name);
|
|
229951
|
+
}
|
|
229952
|
+
return (id) => byActor2.get(id);
|
|
229953
|
+
}
|
|
229954
|
+
async function buildResolver(registry2, memberName) {
|
|
229684
229955
|
if (!registry2) return void 0;
|
|
229685
229956
|
const actors = await registry2.listActors();
|
|
229686
229957
|
const byId = new Map(actors.map((a) => [a.id, a]));
|
|
229687
229958
|
return (id) => {
|
|
229688
229959
|
const a = byId.get(id);
|
|
229689
|
-
|
|
229960
|
+
if (!a) return actorRefFallbackOf(id);
|
|
229961
|
+
const fields = actorRefFieldsOf(a);
|
|
229962
|
+
const nickname = memberName?.(id);
|
|
229963
|
+
return nickname ? { ...fields, name: nickname } : fields;
|
|
229690
229964
|
};
|
|
229691
229965
|
}
|
|
229692
229966
|
async function buildProjectResolver(artifacts) {
|
|
@@ -229808,6 +230082,7 @@ function collabDomain(opts) {
|
|
|
229808
230082
|
cache.set(companyId, ctx);
|
|
229809
230083
|
return ctx;
|
|
229810
230084
|
}
|
|
230085
|
+
const resolverFor = async (companyId, registry2) => buildResolver(registry2, await memberNameResolver(opts.listMembers, companyId));
|
|
229811
230086
|
return (router) => {
|
|
229812
230087
|
router.get("/api/organization/usage-summary", async (req) => {
|
|
229813
230088
|
if (!opts.trace) {
|
|
@@ -229852,7 +230127,7 @@ function collabDomain(opts) {
|
|
|
229852
230127
|
});
|
|
229853
230128
|
router.get("/api/workorders", async (req) => {
|
|
229854
230129
|
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
229855
|
-
const resolve10 = await
|
|
230130
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
229856
230131
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
229857
230132
|
const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
|
|
229858
230133
|
const ref2 = (id) => {
|
|
@@ -229883,7 +230158,7 @@ function collabDomain(opts) {
|
|
|
229883
230158
|
});
|
|
229884
230159
|
router.get("/api/workorder-metrics", async (req) => {
|
|
229885
230160
|
const { kernel, registry: registry2, artifacts, engineStore } = await resolveCtx(req.auth.companyId);
|
|
229886
|
-
const resolve10 = await
|
|
230161
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
229887
230162
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
229888
230163
|
const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
|
|
229889
230164
|
const workorders = buildWorkorderSummaries(kernel.model, resolve10, resolveProject, dispatchPausedOf);
|
|
@@ -229930,7 +230205,7 @@ function collabDomain(opts) {
|
|
|
229930
230205
|
});
|
|
229931
230206
|
router.get("/api/workorders/:id", async (req) => {
|
|
229932
230207
|
const { kernel, oplog, blobs, registry: registry2, artifacts, engineStore } = await resolveCtx(req.auth.companyId);
|
|
229933
|
-
const resolve10 = await
|
|
230208
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
229934
230209
|
const resolveProject = await buildProjectResolver(artifacts);
|
|
229935
230210
|
const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
|
|
229936
230211
|
const wsArts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === req.params.id);
|
|
@@ -230005,7 +230280,7 @@ function collabDomain(opts) {
|
|
|
230005
230280
|
if (![...kernel.model.artifacts.values()].some((a) => a.workspace === workorderId)) {
|
|
230006
230281
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${workorderId}` } } };
|
|
230007
230282
|
}
|
|
230008
|
-
const resolve10 = await
|
|
230283
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
230009
230284
|
const ref2 = (id) => {
|
|
230010
230285
|
const extra = resolve10?.(id);
|
|
230011
230286
|
return {
|
|
@@ -230048,7 +230323,7 @@ function collabDomain(opts) {
|
|
|
230048
230323
|
if (!cardId) {
|
|
230049
230324
|
return { status: 400, body: { error: { code: "bad_request", message: "\u7F3A\u5C11 card \u53C2\u6570\uFF08\u52A8\u6001\u5361 id\uFF09" } } };
|
|
230050
230325
|
}
|
|
230051
|
-
const resolve10 = await
|
|
230326
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
230052
230327
|
const ref2 = (id) => {
|
|
230053
230328
|
const extra = resolve10?.(id);
|
|
230054
230329
|
return {
|
|
@@ -230088,7 +230363,7 @@ function collabDomain(opts) {
|
|
|
230088
230363
|
if (!ledger) {
|
|
230089
230364
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${req.params.id}` } } };
|
|
230090
230365
|
}
|
|
230091
|
-
const resolve10 = await
|
|
230366
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
230092
230367
|
const ids2 = /* @__PURE__ */ new Set();
|
|
230093
230368
|
if (ledger.owner) ids2.add(ledger.owner);
|
|
230094
230369
|
if (ledger.manager) ids2.add(ledger.manager);
|
|
@@ -230107,13 +230382,14 @@ function collabDomain(opts) {
|
|
|
230107
230382
|
return { status: 200, body: { ...ledger, actors } };
|
|
230108
230383
|
});
|
|
230109
230384
|
router.get("/api/workorders/:id/files", async (req) => {
|
|
230110
|
-
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
230385
|
+
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
230111
230386
|
const ws = req.params.id;
|
|
230112
230387
|
const exists = [...kernel.model.artifacts.values()].some((a) => a.workspace === ws);
|
|
230113
230388
|
if (!exists) {
|
|
230114
230389
|
return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${ws}` } } };
|
|
230115
230390
|
}
|
|
230116
230391
|
const files = artifacts?.listWorkOrderFiles ? await artifacts.listWorkOrderFiles(ws) : [];
|
|
230392
|
+
const resolve10 = await buildResolver(registry2);
|
|
230117
230393
|
const items = files.map((f2) => ({
|
|
230118
230394
|
id: f2.fileId,
|
|
230119
230395
|
name: f2.name,
|
|
@@ -230123,12 +230399,14 @@ function collabDomain(opts) {
|
|
|
230123
230399
|
updated_at: f2.linkedAt,
|
|
230124
230400
|
blob_ref: f2.blobId,
|
|
230125
230401
|
source_session_id: f2.sourceSessionId ?? null,
|
|
230126
|
-
source_message_id: f2.sourceMessageId ?? null
|
|
230402
|
+
source_message_id: f2.sourceMessageId ?? null,
|
|
230403
|
+
uploaded_by: f2.uploadedBy ?? null,
|
|
230404
|
+
uploader_name: f2.uploadedBy ? resolve10?.(f2.uploadedBy)?.name ?? null : null
|
|
230127
230405
|
}));
|
|
230128
230406
|
return { status: 200, body: { total: items.length, items } };
|
|
230129
230407
|
});
|
|
230130
230408
|
router.post("/api/workorders/:id/files", async (req) => {
|
|
230131
|
-
const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
|
|
230409
|
+
const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
|
|
230132
230410
|
const ws = req.params.id;
|
|
230133
230411
|
const exists = [...kernel.model.artifacts.values()].some((a) => a.workspace === ws);
|
|
230134
230412
|
if (!exists) {
|
|
@@ -230162,7 +230440,9 @@ function collabDomain(opts) {
|
|
|
230162
230440
|
blobId,
|
|
230163
230441
|
size: bytes2.byteLength,
|
|
230164
230442
|
...contentType ? { contentType } : {},
|
|
230165
|
-
linkedAt
|
|
230443
|
+
linkedAt,
|
|
230444
|
+
// 上传者:项目页「创建人」列与预览副标据此署名(建单带入的那条路记的是建单发起人)。
|
|
230445
|
+
uploadedBy: req.auth.actor
|
|
230166
230446
|
};
|
|
230167
230447
|
try {
|
|
230168
230448
|
await artifacts.addWorkOrderFile(file);
|
|
@@ -230172,6 +230452,7 @@ function collabDomain(opts) {
|
|
|
230172
230452
|
body: { error: { code: "write_failed", message: `\u5199 work_order_files \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}` } }
|
|
230173
230453
|
};
|
|
230174
230454
|
}
|
|
230455
|
+
const resolve10 = await buildResolver(registry2);
|
|
230175
230456
|
const item = {
|
|
230176
230457
|
id: fileId,
|
|
230177
230458
|
name,
|
|
@@ -230181,7 +230462,9 @@ function collabDomain(opts) {
|
|
|
230181
230462
|
updated_at: linkedAt,
|
|
230182
230463
|
blob_ref: blobId,
|
|
230183
230464
|
source_session_id: null,
|
|
230184
|
-
source_message_id: null
|
|
230465
|
+
source_message_id: null,
|
|
230466
|
+
uploaded_by: req.auth.actor,
|
|
230467
|
+
uploader_name: resolve10?.(req.auth.actor)?.name ?? null
|
|
230185
230468
|
};
|
|
230186
230469
|
return { status: 201, body: { item } };
|
|
230187
230470
|
});
|
|
@@ -230255,7 +230538,7 @@ function collabDomain(opts) {
|
|
|
230255
230538
|
const [leadOf, resolveActor, initialMarkers] = await Promise.all([
|
|
230256
230539
|
buildLeadResolver(registry2),
|
|
230257
230540
|
// 决策 0026 (c):gap 升级也发卡住节点 owner 的人类上级
|
|
230258
|
-
|
|
230541
|
+
resolverFor(req.auth.companyId, registry2),
|
|
230259
230542
|
// 决策 0042:gap reporter 名册 join
|
|
230260
230543
|
myReadMarkers ? myReadMarkers.listMarkers(me) : Promise.resolve([])
|
|
230261
230544
|
// ADR-0086:知会型条目的已读水位
|
|
@@ -230528,7 +230811,7 @@ function collabDomain(opts) {
|
|
|
230528
230811
|
let teamMembers;
|
|
230529
230812
|
let lead;
|
|
230530
230813
|
if (registry2) {
|
|
230531
|
-
const resolve10 = await
|
|
230814
|
+
const resolve10 = await resolverFor(req.auth.companyId, registry2);
|
|
230532
230815
|
const meRec = await registry2.getActor(me);
|
|
230533
230816
|
if (meRec?.teamId) {
|
|
230534
230817
|
teamMembers = new Set((await registry2.listActors({ teamId: meRec.teamId })).map((m2) => m2.id));
|
|
@@ -233780,7 +234063,10 @@ function createChatSessionsDomain(opts) {
|
|
|
233780
234063
|
const kernel = await kernelFor(req);
|
|
233781
234064
|
const engineStore = kernel?.getStore?.();
|
|
233782
234065
|
const snap = engineStore ? await engineStore.transaction((tx) => tx.loadWorkorder(workorderId)) : null;
|
|
233783
|
-
const resolve10 = await buildResolver(
|
|
234066
|
+
const resolve10 = await buildResolver(
|
|
234067
|
+
await registryFor(req),
|
|
234068
|
+
await memberNameResolver(opts.listMembers, req.auth.companyId)
|
|
234069
|
+
);
|
|
233784
234070
|
const ref2 = (id) => {
|
|
233785
234071
|
const extra = resolve10?.(id);
|
|
233786
234072
|
return { id, ...extra?.name ? { name: extra.name } : {}, ...extra?.role ? { role: extra.role } : {}, ...extra?.avatar ? { avatar: extra.avatar } : {} };
|
|
@@ -240391,7 +240677,11 @@ function rowToVariable(row) {
|
|
|
240391
240677
|
...row.expires_at ? { expiresAt: row.expires_at } : {},
|
|
240392
240678
|
...row.last_used_at ? { lastUsedAt: row.last_used_at } : {},
|
|
240393
240679
|
// 展示名(存量行没有 → 不带这一格,展示侧回落成 key)。
|
|
240394
|
-
...row.name ? { name: row.name } : {}
|
|
240680
|
+
...row.name ? { name: row.name } : {},
|
|
240681
|
+
// 创建者 / 创建时间 / 更新者:2026-09-10 之前的行没有 → 不带这一格,展示侧出「—」。
|
|
240682
|
+
...row.created_by ? { createdBy: row.created_by } : {},
|
|
240683
|
+
...row.created_at ? { createdAt: row.created_at } : {},
|
|
240684
|
+
...row.updated_by ? { updatedBy: row.updated_by } : {}
|
|
240395
240685
|
};
|
|
240396
240686
|
}
|
|
240397
240687
|
var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
|
|
@@ -240562,6 +240852,9 @@ var init_postgres_registry = __esm({
|
|
|
240562
240852
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS name text`);
|
|
240563
240853
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS expires_at text`);
|
|
240564
240854
|
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS last_used_at text`);
|
|
240855
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_by text`);
|
|
240856
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_at text`);
|
|
240857
|
+
await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS updated_by text`);
|
|
240565
240858
|
await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
|
|
240566
240859
|
await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
|
|
240567
240860
|
await pool.query(`
|
|
@@ -241063,11 +241356,17 @@ var init_postgres_registry = __esm({
|
|
|
241063
241356
|
await this.pool.query(
|
|
241064
241357
|
/* `name` 走 COALESCE:**没传 = 不动**。连接器授权流程写变量时手上没有「名称」,
|
|
241065
241358
|
普通赋值会把人在密钥表单里填的名字抹掉——同 connectors.connected_by 那条。 */
|
|
241066
|
-
|
|
241067
|
-
|
|
241359
|
+
/* created_by / created_at 走 COALESCE(**旧值**, 新值):**第一次落行才写**,之后每次 upsert
|
|
241360
|
+
都保留原值——否则「谁建的」会被最后一次改的人顶掉,这一格就没有意义了。
|
|
241361
|
+
updated_by 反过来:每次都覆盖。三者与 name 的 COALESCE 方向相反,别照抄。 */
|
|
241362
|
+
`INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at, name, created_by, created_at, updated_by)
|
|
241363
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
|
|
241068
241364
|
ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
|
|
241069
241365
|
SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9, expires_at=$10,
|
|
241070
|
-
name=COALESCE($11, ${this.s}.variables.name)
|
|
241366
|
+
name=COALESCE($11, ${this.s}.variables.name),
|
|
241367
|
+
created_by=COALESCE(${this.s}.variables.created_by, $12),
|
|
241368
|
+
created_at=COALESCE(${this.s}.variables.created_at, $13),
|
|
241369
|
+
updated_by=$14`,
|
|
241071
241370
|
[
|
|
241072
241371
|
v2.key,
|
|
241073
241372
|
v2.scope,
|
|
@@ -241079,7 +241378,10 @@ var init_postgres_registry = __esm({
|
|
|
241079
241378
|
v2.valueEncrypted,
|
|
241080
241379
|
v2.updatedAt,
|
|
241081
241380
|
v2.expiresAt ?? null,
|
|
241082
|
-
v2.name ?? null
|
|
241381
|
+
v2.name ?? null,
|
|
241382
|
+
v2.createdBy ?? null,
|
|
241383
|
+
v2.createdAt ?? null,
|
|
241384
|
+
v2.updatedBy ?? null
|
|
241083
241385
|
]
|
|
241084
241386
|
);
|
|
241085
241387
|
}
|
|
@@ -242050,8 +242352,10 @@ var init_postgres_projects = __esm({
|
|
|
242050
242352
|
content_type text,
|
|
242051
242353
|
source_session_id text,
|
|
242052
242354
|
source_message_id text,
|
|
242053
|
-
linked_at timestamptz NOT NULL
|
|
242355
|
+
linked_at timestamptz NOT NULL,
|
|
242356
|
+
uploaded_by text
|
|
242054
242357
|
)`);
|
|
242358
|
+
await pool.query(`ALTER TABLE "${s2}".work_order_files ADD COLUMN IF NOT EXISTS uploaded_by text`);
|
|
242055
242359
|
await pool.query(`CREATE INDEX IF NOT EXISTS work_order_files_wo_idx ON "${s2}".work_order_files (work_order_id)`);
|
|
242056
242360
|
return new _PostgresArtifactStateStore(pool, schema);
|
|
242057
242361
|
}
|
|
@@ -242334,11 +242638,12 @@ var init_postgres_projects = __esm({
|
|
|
242334
242638
|
async addWorkOrderFile(file) {
|
|
242335
242639
|
await this.pool.query(
|
|
242336
242640
|
`INSERT INTO ${this.s}.work_order_files
|
|
242337
|
-
(file_id, work_order_id, name, blob_id, size, content_type, source_session_id, source_message_id, linked_at)
|
|
242338
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
|
|
242641
|
+
(file_id, work_order_id, name, blob_id, size, content_type, source_session_id, source_message_id, linked_at, uploaded_by)
|
|
242642
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
|
242339
242643
|
ON CONFLICT (file_id) DO UPDATE SET
|
|
242340
242644
|
work_order_id=$2, name=$3, blob_id=$4, size=$5, content_type=$6,
|
|
242341
|
-
source_session_id=$7, source_message_id=$8, linked_at=$9
|
|
242645
|
+
source_session_id=$7, source_message_id=$8, linked_at=$9,
|
|
242646
|
+
uploaded_by=COALESCE($10, ${this.s}.work_order_files.uploaded_by)`,
|
|
242342
242647
|
[
|
|
242343
242648
|
file.fileId,
|
|
242344
242649
|
file.workOrderId,
|
|
@@ -242348,7 +242653,8 @@ var init_postgres_projects = __esm({
|
|
|
242348
242653
|
file.contentType ?? null,
|
|
242349
242654
|
file.sourceSessionId ?? null,
|
|
242350
242655
|
file.sourceMessageId ?? null,
|
|
242351
|
-
file.linkedAt
|
|
242656
|
+
file.linkedAt,
|
|
242657
|
+
file.uploadedBy ?? null
|
|
242352
242658
|
]
|
|
242353
242659
|
);
|
|
242354
242660
|
}
|
|
@@ -242540,6 +242846,7 @@ var init_postgres_projects = __esm({
|
|
|
242540
242846
|
...row.content_type !== null ? { contentType: row.content_type } : {},
|
|
242541
242847
|
...row.source_session_id !== null ? { sourceSessionId: row.source_session_id } : {},
|
|
242542
242848
|
...row.source_message_id !== null ? { sourceMessageId: row.source_message_id } : {},
|
|
242849
|
+
...row.uploaded_by !== null && row.uploaded_by !== void 0 ? { uploadedBy: row.uploaded_by } : {},
|
|
242543
242850
|
linkedAt: iso2(row.linked_at)
|
|
242544
242851
|
});
|
|
242545
242852
|
}
|
|
@@ -259891,6 +260198,7 @@ async function startServe(opts) {
|
|
|
259891
260198
|
}
|
|
259892
260199
|
return resolved;
|
|
259893
260200
|
};
|
|
260201
|
+
const listCompanyMembers = (companyId) => controlPlaneStore.listMembers(companyId ?? defaultCompanyId);
|
|
259894
260202
|
const server = await startOasisServer({
|
|
259895
260203
|
deployTargets,
|
|
259896
260204
|
kernel: engine.kernel,
|
|
@@ -260138,6 +260446,8 @@ async function startServe(opts) {
|
|
|
260138
260446
|
// 草案图算「类型默认审核人」用(与 /api/cmd 同源类型目录)
|
|
260139
260447
|
roles: roleStore,
|
|
260140
260448
|
// 诊断 f1641dd3-abdaa869:概览角色桶补「默认承接人」(读 RoleDef.defaultOwner)
|
|
260449
|
+
// 真人展示名的第一顺位:本公司成员昵称压过员工档案 name(档案上那份缺省是邮箱)。
|
|
260450
|
+
listMembers: listCompanyMembers,
|
|
260141
260451
|
chatSession: chatSessionStore,
|
|
260142
260452
|
// ADR-0086:chat 知会条目(与 readMarkers 成对)
|
|
260143
260453
|
// ADR「多租户数据面收口」§D2:知会条目按**当前公司**取。装了路由才给(dev 文件模式沿用单实例)。
|
|
@@ -260176,6 +260486,8 @@ async function startServe(opts) {
|
|
|
260176
260486
|
// 装了路由(有 PG)就只走路由,解析不出公司一律 403,不回落默认公司。
|
|
260177
260487
|
...chatStoreRouter ? { resolveStore: chatStoreFor } : {},
|
|
260178
260488
|
registry: registryStore,
|
|
260489
|
+
// 缺口线署名的真人名字与 collab 读面同一口径:本公司成员昵称压过员工档案 name。
|
|
260490
|
+
listMembers: listCompanyMembers,
|
|
260179
260491
|
// ★ wo:acfee725 评审 blocker ①:两个 `/workdir/*` 读口与 files-view 段按**当前公司**解 binding,
|
|
260180
260492
|
// 与派发、与委派回流搬产物同一处(`resolveCompanyBinding`)。上面那个 `registry` 是全局
|
|
260181
260493
|
// store(`public.actor_bindings`),org-registry cutover 之后它是冻结旧镜像——只留给
|
|
@@ -261321,6 +261633,14 @@ async function startServe(opts) {
|
|
|
261321
261633
|
requestId: `project-created:${project.id}`
|
|
261322
261634
|
}, project.id);
|
|
261323
261635
|
});
|
|
261636
|
+
const notifyChatTurnSettled = async (info) => {
|
|
261637
|
+
await server.settleDelegationFromSweep(
|
|
261638
|
+
info.chatSessionId,
|
|
261639
|
+
`\u5B50\u4F1A\u8BDD\u8FD9\u4E00\u8F6E\u88AB\u6062\u590D\u5668\u6536\u53E3\u4E3A ${info.status}\uFF08${info.reason}\uFF09`,
|
|
261640
|
+
info.companyId,
|
|
261641
|
+
{ outcome: info.status === "succeeded" ? "succeeded" : "failed" }
|
|
261642
|
+
);
|
|
261643
|
+
};
|
|
261324
261644
|
const handshakeThrottleOpts = {};
|
|
261325
261645
|
{
|
|
261326
261646
|
const num3 = (name) => {
|
|
@@ -261383,10 +261703,12 @@ async function startServe(opts) {
|
|
|
261383
261703
|
if (hit) return hit;
|
|
261384
261704
|
const sessions = id === defaultCompanyId ? platformChatRecovery : PlatformChatRecoveryStore.open(pgPool, engineSchemaFor(id));
|
|
261385
261705
|
const items = await chatItemsFor(id);
|
|
261706
|
+
const turnsForRecovery = await chatTurnsFor(id).catch(() => void 0);
|
|
261386
261707
|
const plane = {
|
|
261387
261708
|
listRunningAssistantMessages: () => sessions.listRunningAssistantMessages(),
|
|
261388
261709
|
chatStore: sessions,
|
|
261389
261710
|
getTurnForRun: (sid, rid) => sessions.getTurnForRun(sid, rid),
|
|
261711
|
+
...turnsForRecovery ? { settleTurn: (turnId, patch) => turnsForRecovery.settleTurn(turnId, patch) } : {},
|
|
261390
261712
|
...items ? { items } : {}
|
|
261391
261713
|
};
|
|
261392
261714
|
chatRecoveryPlanes.set(id, plane);
|
|
@@ -261412,6 +261734,8 @@ async function startServe(opts) {
|
|
|
261412
261734
|
isWired: (artifactId) => chatLiveSessions.has(`chat::${artifactId}`),
|
|
261413
261735
|
...opts2.stopOnFirstHit ? { stopOnFirstHit: true } : {},
|
|
261414
261736
|
onCompanyError: chatRecoveryCompanyWarn(opts2.label),
|
|
261737
|
+
// 重挂的那一轮收口时当场外报,与清扫拍同一个接收方(见 `notifyChatTurnSettled`)。
|
|
261738
|
+
onTurnSettled: (info) => notifyChatTurnSettled(info),
|
|
261415
261739
|
liveChat,
|
|
261416
261740
|
trace: runRoutedTrace,
|
|
261417
261741
|
traceHealth,
|
|
@@ -261682,14 +262006,12 @@ async function startServe(opts) {
|
|
|
261682
262006
|
本拍是从轮次账本走的(`assistant_run_id` 一直有值),是"这一轮死没死"的权威来源。
|
|
261683
262007
|
`companyId` 由扇出那层填好 —— 台账按公司分 store,少了它会查到默认公司的库。
|
|
261684
262008
|
非委派会话调进去是空转:`settleFromSweep` 按 childSessionId 查不到记录就原样返回。 */
|
|
261685
|
-
|
|
261686
|
-
|
|
261687
|
-
|
|
261688
|
-
|
|
261689
|
-
|
|
261690
|
-
|
|
261691
|
-
);
|
|
261692
|
-
},
|
|
262009
|
+
/* `status` 必须带下去分流。跨重启完成的那一轮走的正是 `succeeded`:
|
|
262010
|
+
`settleRound` 是派发那一刻建的闭包,serve 一重启就随老进程没了。
|
|
262011
|
+
不分流的话,一条**干完活**的委派会被按时钟闭合拍成 failed。
|
|
262012
|
+
接收方与重挂路**同一个**(`notifyChatTurnSettled`)——两条路一处收敛,
|
|
262013
|
+
不会出现「清扫拍收的能通知、重挂收的不能」这种只在重启后显形的差异。 */
|
|
262014
|
+
onTurnSettled: (info) => notifyChatTurnSettled(info),
|
|
261693
262015
|
log: (m2) => console.log(m2),
|
|
261694
262016
|
onCompanyError: (companyId, err) => {
|
|
261695
262017
|
const key = companyId ?? "<default>";
|
|
@@ -271034,7 +271356,7 @@ function shimScript() {
|
|
|
271034
271356
|
}
|
|
271035
271357
|
|
|
271036
271358
|
// src/index.ts
|
|
271037
|
-
var PKG_VERSION = true ? "2.2.
|
|
271359
|
+
var PKG_VERSION = true ? "2.2.13" : "dev";
|
|
271038
271360
|
var LOCAL_BIN = localBin();
|
|
271039
271361
|
var NPM_PREFIX = npmPrefix();
|
|
271040
271362
|
var INSTANCE = DEFAULT_INSTANCE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oasis_test_v2",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.13",
|
|
4
4
|
"description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oasis": "./dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"oasisRelease": {
|
|
29
|
-
"sourceHead": "
|
|
29
|
+
"sourceHead": "b907ec1d1a0ae4a57e7eb938c477f05323b84590"
|
|
30
30
|
}
|
|
31
31
|
}
|