oasis_test_v2 2.2.11 → 2.2.12

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.
Files changed (2) hide show
  1. package/dist/index.js +263 -53
  2. 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
- return [{ installationId: id, account: String(account?.["login"] ?? "") }];
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 = {}) {
@@ -205065,7 +205079,9 @@ async function startOasisServer(opts) {
205065
205079
  blobMeta: blobMetaAcrossCabinets,
205066
205080
  // 工单就是这一刻建出来的,所以「现在」就是任务创建时间。取一次、全部文件共用,
205067
205081
  // 保证同一次建单带进来的文件在任务侧显示的是同一个时刻(排期序 28 原文口径)。
205068
- linkedAt: (/* @__PURE__ */ new Date()).toISOString()
205082
+ linkedAt: (/* @__PURE__ */ new Date()).toISOString(),
205083
+ // 建单发起人就是把这些附件带进任务的人——项目页「创建人」列与预览副标据此署名。
205084
+ uploadedBy: actor
205069
205085
  }).catch((err) => ({
205070
205086
  linked: 0,
205071
205087
  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 +206242,16 @@ ${composed}`;
206226
206242
  只推 user:assistant 的每一条本来就走 `emitV3`。 */
206227
206243
  onPersisted: (info) => {
206228
206244
  if (info.role !== "user") return;
206245
+ const rowPayload = info.payload && typeof info.payload === "object" && !Array.isArray(info.payload) ? info.payload : void 0;
206246
+ const rowCsid = typeof rowPayload?.["clientSubmitId"] === "string" ? rowPayload["clientSubmitId"] : void 0;
206247
+ const rowAttachments = Array.isArray(rowPayload?.["attachments"]) ? rowPayload["attachments"] : void 0;
206229
206248
  opts.liveChat?.publishUserItem(persistTarget?.id ?? session.id, {
206230
206249
  itemId: info.itemId,
206231
206250
  ord: info.ord,
206232
206251
  version: info.version,
206233
206252
  text: info.text,
206234
- ...clientSubmitId ? { clientSubmitId } : {},
206235
- ...effectiveAttachments.length ? { attachments: effectiveAttachments } : {}
206253
+ ...rowCsid ? { clientSubmitId: rowCsid } : {},
206254
+ ...rowAttachments?.length ? { attachments: rowAttachments } : {}
206236
206255
  });
206237
206256
  },
206238
206257
  sessionId: persistTarget?.id ?? session.id,
@@ -207154,8 +207173,26 @@ ${composed}`;
207154
207173
  return;
207155
207174
  }
207156
207175
  const meta = await fetchAppSelfMetadata({ appId, privateKeyPem });
207157
- const installationId = await svc.revealResolvedVariable("GITHUB_APP_INSTALLATION_ID", actorId);
207158
- const effective = installationId ? await fetchInstallationPermissions({ appId, privateKeyPem, installationId }) : meta.permissions;
207176
+ const storedInstallationId = await svc.revealResolvedVariable("GITHUB_APP_INSTALLATION_ID", actorId);
207177
+ const installs = await listAppInstallations({ appId, privateKeyPem }).catch(() => []);
207178
+ const chosen = installs.find((i) => i.installationId === storedInstallationId) ?? (installs.length === 1 ? installs[0] : void 0);
207179
+ const installationStale = Boolean(storedInstallationId) && installs.length > 0 && !chosen;
207180
+ const installationId = chosen?.installationId || storedInstallationId;
207181
+ let grantedFrom = "app";
207182
+ let effective = meta.permissions;
207183
+ if (installationId) {
207184
+ const minted = await mintInstallationToken({ appId, privateKeyPem, installationId }).catch(() => null);
207185
+ if (minted && Object.keys(minted.permissions).length > 0) {
207186
+ effective = minted.permissions;
207187
+ grantedFrom = "token";
207188
+ } else if (chosen && Object.keys(chosen.permissions).length > 0) {
207189
+ effective = chosen.permissions;
207190
+ grantedFrom = "installation";
207191
+ } else {
207192
+ effective = await fetchInstallationPermissions({ appId, privateKeyPem, installationId });
207193
+ grantedFrom = "installation";
207194
+ }
207195
+ }
207159
207196
  const missing = missingAppPermissions(effective);
207160
207197
  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
207198
  const grantedKeys = new Set(
@@ -207169,13 +207206,27 @@ ${composed}`;
207169
207206
  requested: requestedAll,
207170
207207
  /** true=读的是这次安装真正被授予的权限;false=只拿到 App 申请的,可能偏乐观。 */
207171
207208
  fromInstallation: Boolean(installationId),
207209
+ /** 「已授予」这一列是从哪问出来的:token(最准)/ 安装记录 / 只有 App 申请。 */
207210
+ grantedFrom,
207211
+ /** 读的是哪一处安装——同一个 App 装多处时,人得看得见自己批的是不是这一处。 */
207212
+ installationAccount: chosen?.account ?? "",
207213
+ /** 库里存的 installation 已不在这个 App 的安装列表里(卸载重装过)。 */
207214
+ installationStale,
207215
+ /**
207216
+ * 这个 App 的**其它**安装及各自的权限项数。只在装了不止一处时才有内容。
207217
+ * 它专门回答那句「我明明已经批过了」:批在哪一处,这里一眼看得见。
207218
+ */
207219
+ otherInstallations: installs.filter((i) => i.installationId !== installationId).map((i) => ({ account: i.account, count: Object.keys(i.permissions).length })),
207172
207220
  /**
207173
- * App 申请了、但这次安装还没被授予——多半是权限升级还等着组织 owner 批准。
207221
+ * App 申请了、但**这一处安装铸出来的 token 里没有**。
207174
207222
  * **在全集上做差**,不再只看本仓必需的那八项(否则人另外勾的永远报不出来)。
207223
+ *
207224
+ * ⚠ 差集只说明「这一处安装现在给不了」,不等于「人没批」——同一个 App 装多处时
207225
+ * 批准是逐处的(见 `otherInstallations`)。界面别把这层推断说死。
207175
207226
  */
207176
207227
  awaitingApproval: requestedAll.filter((r) => !grantedKeys.has(r.key)).map((r) => r.key),
207177
207228
  /**
207178
- * 这次安装**真正被授予**的全部权限,一律用 GitHub 的英文原名 + 级别
207229
+ * agent **真正拿得到**的全部权限(优先读 token 自己的权限位),一律用 GitHub 的英文原名 + 级别
207179
207230
  * (2026-09-10 发起人:「所有权限都用英文原文,不区分是不是八项权限」)。
207180
207231
  *
207181
207232
  * 此前这里是 `GITHUB_APP_PERMISSION_LABELS.filter(...)` —— 那张表只有本仓必需的
@@ -207325,7 +207376,8 @@ ${composed}`;
207325
207376
  return;
207326
207377
  }
207327
207378
  try {
207328
- const appSecret = opts.actors ? await opts.actors.service.revealVariable("FEISHU_APP_SECRET", actorId) : null;
207379
+ const startSvc = await connectorActorsService();
207380
+ const appSecret = startSvc ? await startSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
207329
207381
  if (!appSecret) throw new Error("FEISHU_APP_SECRET not found \u2014 please complete app setup first");
207330
207382
  const basicAuth = Buffer.from(`${appId}:${appSecret}`).toString("base64");
207331
207383
  const resp = await fetch("https://accounts.feishu.cn/oauth/v1/device_authorization", {
@@ -207355,7 +207407,8 @@ ${composed}`;
207355
207407
  return;
207356
207408
  }
207357
207409
  try {
207358
- const appSecret = opts.actors ? await opts.actors.service.revealVariable("FEISHU_APP_SECRET", actorId) : null;
207410
+ const pollSvc = await connectorActorsService();
207411
+ const appSecret = pollSvc ? await pollSvc.revealResolvedVariable("FEISHU_APP_SECRET", actorId) : null;
207359
207412
  if (!appSecret) throw new Error("FEISHU_APP_SECRET not found \u2014 please complete app setup first");
207360
207413
  const resp = await fetch("https://open.feishu.cn/open-apis/authen/v2/oauth/token", {
207361
207414
  method: "POST",
@@ -207381,6 +207434,7 @@ ${composed}`;
207381
207434
  if (svc) {
207382
207435
  if (actorId) {
207383
207436
  await svc.putVariable({ key: "FEISHU_APP_ID", value: appId, scope: "personal", actorId, connectorId: "feishu", encrypted: true });
207437
+ await svc.putVariable({ key: "FEISHU_APP_SECRET", value: appSecret, scope: "personal", actorId, connectorId: "feishu", encrypted: true });
207384
207438
  } else {
207385
207439
  await svc.putVariable({ key: "FEISHU_APP_ID", value: appId, scope: "global", connectorId: "feishu", encrypted: true });
207386
207440
  await svc.upsertConnector({ id: "feishu", name: "\u98DE\u4E66", mode: "oauth", status: "connected", account: appId });
@@ -208243,10 +208297,15 @@ var init_memory_registry_store = __esm({
208243
208297
  const mk = this._varKey(v2.key, v2.actorId, v2.projectId);
208244
208298
  const ex = this.variables.get(mk);
208245
208299
  const keptName = v2.name ?? ex?.name;
208300
+ const keptCreatedBy = ex?.createdBy ?? v2.createdBy;
208301
+ const keptCreatedAt = ex?.createdAt ?? v2.createdAt;
208246
208302
  this.variables.set(mk, {
208247
208303
  ...v2,
208248
208304
  ...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {},
208249
- ...keptName ? { name: keptName } : {}
208305
+ ...keptName ? { name: keptName } : {},
208306
+ ...keptCreatedBy ? { createdBy: keptCreatedBy } : {},
208307
+ ...keptCreatedAt ? { createdAt: keptCreatedAt } : {},
208308
+ ...v2.updatedBy ? { updatedBy: v2.updatedBy } : {}
208250
208309
  });
208251
208310
  }
208252
208311
  async touchVariableUsage(key, actorId, projectId2, when) {
@@ -210763,8 +210822,14 @@ function foldSingleNodeTasks(items) {
210763
210822
  paths.add(item.path);
210764
210823
  nodeFoldersByTask.set(item.taskId, paths);
210765
210824
  }
210825
+ const tasksWithRootFiles = /* @__PURE__ */ new Set();
210826
+ for (const item of items) {
210827
+ if (item.kind === "file" && item.taskId && item.path?.startsWith(`wo:${item.taskId}/file:`)) {
210828
+ tasksWithRootFiles.add(item.taskId);
210829
+ }
210830
+ }
210766
210831
  const folded = new Set(
210767
- [...nodeFoldersByTask].filter(([, paths]) => paths.size === 1).map(([taskId]) => taskId)
210832
+ [...nodeFoldersByTask].filter(([taskId, paths]) => paths.size === 1 && !tasksWithRootFiles.has(taskId)).map(([taskId]) => taskId)
210768
210833
  );
210769
210834
  if (folded.size === 0) return folded;
210770
210835
  for (const item of items) {
@@ -211705,6 +211770,33 @@ var init_service4 = __esm({
211705
211770
  });
211706
211771
  }
211707
211772
  }
211773
+ if (this.artifacts.listWorkOrderFiles) {
211774
+ for (const workOrderId of workorderSummaries.map((w2) => w2.id)) {
211775
+ const workOrderFiles = await this.artifacts.listWorkOrderFiles(workOrderId).catch(() => []);
211776
+ const taskTitle = workorderTitleById.get(workOrderId) ?? null;
211777
+ for (const file of workOrderFiles) {
211778
+ const creatorId = file.uploadedBy ?? null;
211779
+ items.push({
211780
+ kind: "file",
211781
+ id: file.fileId,
211782
+ name: file.name,
211783
+ path: `wo:${workOrderId}/file:${file.fileId}`,
211784
+ filePath: null,
211785
+ depth: 1,
211786
+ type: file.contentType ?? inferTypeFromName(file.name),
211787
+ blobRef: file.blobId,
211788
+ contentType: file.contentType ?? null,
211789
+ size: file.size,
211790
+ taskId: workOrderId,
211791
+ taskTitle,
211792
+ creatorKind: creatorId ? creatorId.startsWith("actor:human:") ? "human" : "agent" : null,
211793
+ creatorId,
211794
+ creatorName: creatorId ? this.resolveActorName(creatorId) : null,
211795
+ updatedAt: file.linkedAt
211796
+ });
211797
+ }
211798
+ }
211799
+ }
211708
211800
  const foldedTasks = foldSingleNodeTasks(items);
211709
211801
  const taskFolders = /* @__PURE__ */ new Set();
211710
211802
  for (const item of [...items]) {
@@ -211743,6 +211835,8 @@ var init_service4 = __esm({
211743
211835
  filePath: null,
211744
211836
  depth: path41 ? path41.replace(/^\/+/, "").split("/").length - 1 : 0,
211745
211837
  type: file.contentType ?? inferTypeFromName(file.name),
211838
+ blobRef: file.blobId,
211839
+ contentType: file.contentType ?? null,
211746
211840
  size: file.size,
211747
211841
  taskId: null,
211748
211842
  taskTitle: null,
@@ -217065,6 +217159,11 @@ function wireRecoveredChatTurn(deps) {
217065
217159
  ...nextRuntimeSessionId ? { runtimeSessionId: nextRuntimeSessionId } : {},
217066
217160
  touchedAt: finishedAt
217067
217161
  }).catch(() => void 0);
217162
+ if (deps.onTurnSettled) {
217163
+ await deps.onTurnSettled(failed ? "failed" : "succeeded", finishedAt).catch((err) => {
217164
+ log3(`[chat-recovery] \u4F1A\u8BDD ${plan.chatSessionId} \u6536\u53E3\u8BB0\u8D26\u5931\u8D25\uFF08\u9000\u56DE\u6E05\u626B\u62CD\u515C\u5E95\uFF09: ${String(err)}`);
217165
+ });
217166
+ }
217068
217167
  if (!liveTerminalEmitted) {
217069
217168
  live.emitLive({
217070
217169
  protocolVersion: 2,
@@ -217388,6 +217487,22 @@ async function recoverChatTurnsAcrossCompanies(deps) {
217388
217487
  continue;
217389
217488
  }
217390
217489
  const handle = deps.recover(plan);
217490
+ const settleTurnId = turn?.id;
217491
+ const onTurnSettled = settleTurnId && stores.settleTurn ? async (status, completedAt) => {
217492
+ await stores.settleTurn(settleTurnId, {
217493
+ status,
217494
+ completedAt,
217495
+ ...status === "failed" ? { lastError: `\u91CD\u6302\u7684\u8F6E\u4EE5 ${status} \u6536\u5C3E\uFF08run ${plan.runId}\uFF09` } : {}
217496
+ });
217497
+ 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`);
217498
+ await deps.onTurnSettled?.({
217499
+ chatSessionId: plan.chatSessionId,
217500
+ turnId: settleTurnId,
217501
+ status,
217502
+ reason: `\u91CD\u6302\u7684\u8F6E\u4EE5 ${status} \u6536\u5C3E`,
217503
+ ...companyId ? { companyId } : {}
217504
+ });
217505
+ } : void 0;
217391
217506
  const { done } = wireRecoveredChatTurn({
217392
217507
  plan,
217393
217508
  handle,
@@ -217397,6 +217512,7 @@ async function recoverChatTurnsAcrossCompanies(deps) {
217397
217512
  register: () => deps.register(plan, handle),
217398
217513
  unregister: () => deps.unregister(plan),
217399
217514
  ...deps.traceHealth ? { traceHealth: deps.traceHealth } : {},
217515
+ ...onTurnSettled ? { onTurnSettled } : {},
217400
217516
  ...stores.items ? { items: stores.items, itemsTurnId } : {},
217401
217517
  log: log3
217402
217518
  });
@@ -219464,7 +219580,14 @@ ${input.description}
219464
219580
  vars.filter((v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId).map((v2) => v2.connectorId)
219465
219581
  )];
219466
219582
  const effective = await this.effectiveConnectorEntries(actorId, config2, connectors);
219467
- return { connections, globalConnected, actorConnected, effective };
219583
+ const flat = {};
219584
+ for (const v2 of vars) if (v2.scope === "global" && !v2.actorId) flat[v2.key] = this.decodeCiphertext(v2.valueEncrypted);
219585
+ for (const v2 of vars) if (v2.scope === "personal" && (!v2.actorId || v2.actorId === actorId)) flat[v2.key] = this.decodeCiphertext(v2.valueEncrypted);
219586
+ const credentialReady = collectAllConnectorCredentials({
219587
+ vars: flat,
219588
+ actorName: flat["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent"
219589
+ }).map((c) => c.slug);
219590
+ return { connections, globalConnected, actorConnected, effective, credentialReady };
219468
219591
  }
219469
219592
  /**
219470
219593
  * 该员工每个连接器的「有效启用」集合(变量/技能注入闸门):
@@ -219577,6 +219700,11 @@ ${input.description}
219577
219700
  deliveryMode: v2.deliveryMode ?? "env",
219578
219701
  valueEncrypted: v2.encrypted === false ? `plain:${v2.value}` : this.encrypt(v2.value),
219579
219702
  updatedAt: this.now(),
219703
+ /* 创建者 / 创建时间 / 更新者:**由服务端落,不接受调用方自报**。
219704
+ createdBy/createdAt 两格存储层按「第一次落行才写」处理(COALESCE 旧值),所以这里每次都传,
219705
+ 写不写得进去由存储层决定;updatedBy 每次覆盖。
219706
+ `by` 缺省(连接器授权流程等系统写入)时三格都不传——**不拿一个假身份填上去**。 */
219707
+ ...v2.by ? { createdBy: v2.by, createdAt: this.now(), updatedBy: v2.by } : {},
219580
219708
  // ADR 凭据保险库补章 §D2.2:expiresAt 写侧穿透——**显式带 key**(含 undefined),使清空(传空→undefined)
219581
219709
  // 也落到底(store 两实现都按此覆盖)。lastUsedAt 从不由写侧传,取用时另刷(§D2.3)。
219582
219710
  expiresAt: v2.expiresAt
@@ -219649,7 +219777,12 @@ ${input.description}
219649
219777
  updatedAt: r.updatedAt,
219650
219778
  // ADR 凭据保险库补章 §D2.1:到期/取用时间戳透传(展示层据此染色;补章 §D2.4)。
219651
219779
  ...r.expiresAt !== void 0 ? { expiresAt: r.expiresAt } : {},
219652
- ...r.lastUsedAt !== void 0 ? { lastUsedAt: r.lastUsedAt } : {}
219780
+ ...r.lastUsedAt !== void 0 ? { lastUsedAt: r.lastUsedAt } : {},
219781
+ // 创建者 / 创建时间 / 更新者(子 PRD《密钥管理》§2 + 2026-09-10 追加)。
219782
+ // 存量行没有这三格,原样不带出去——展示侧出「—」,不拿 updatedAt 冒充 createdAt。
219783
+ ...r.createdBy !== void 0 ? { createdBy: r.createdBy } : {},
219784
+ ...r.createdAt !== void 0 ? { createdAt: r.createdAt } : {},
219785
+ ...r.updatedBy !== void 0 ? { updatedBy: r.updatedBy } : {}
219653
219786
  };
219654
219787
  });
219655
219788
  }
@@ -221503,6 +221636,8 @@ function actorsDomain(opts) {
221503
221636
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
221504
221637
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
221505
221638
  ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
221639
+ // 创建者/更新者取**当前登录人**,不接受 body 传入——自报的「谁建的」等于没有这一格。
221640
+ by: req.auth.actor,
221506
221641
  expiresAt: expiresAt2
221507
221642
  });
221508
221643
  await auditVariableChange(
@@ -221575,6 +221710,7 @@ function actorsDomain(opts) {
221575
221710
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
221576
221711
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
221577
221712
  ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
221713
+ by: req.auth.actor,
221578
221714
  expiresAt: expiresAt2
221579
221715
  });
221580
221716
  await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
@@ -223377,6 +223513,9 @@ function toApiProjectFilesView(view) {
223377
223513
  // 其它外部引用→链接)。交付物整份产物行才有,上传文件与文件夹为 null。
223378
223514
  content_kind: item.contentKind ?? null,
223379
223515
  content_ref: item.contentRef ?? null,
223516
+ // 上传文件行(项目级上传 / 工单级文件)的 blob 引用 + MIME:前端据此直读 blob 预览。交付物与文件夹为 null。
223517
+ blob_ref: item.blobRef ?? null,
223518
+ content_type: item.contentType ?? null,
223380
223519
  size: item.size,
223381
223520
  task_id: item.taskId,
223382
223521
  task_title: item.taskTitle,
@@ -228694,6 +228833,9 @@ function toAuditEntry(op) {
228694
228833
  actor: op.actor,
228695
228834
  op: op.kind,
228696
228835
  artifactId: op.artifactId,
228836
+ /* 动作名单独发一份(见契约 `AuditEntry.actionLabel`):`summary` 拼着裸 artifactId,
228837
+ 不能直接给人看,而「已处理」那一行需要「干了什么 + 在哪个节点」两截并排。 */
228838
+ actionLabel: KIND_LABEL2[op.kind] ?? op.kind,
228697
228839
  summary: `${KIND_LABEL2[op.kind] ?? op.kind} \xB7 ${op.artifactId}`
228698
228840
  };
228699
228841
  }
@@ -228730,6 +228872,7 @@ function eventToAuditEntry(rec) {
228730
228872
  actor: rec.actorId,
228731
228873
  op: ev.kind,
228732
228874
  artifactId,
228875
+ actionLabel: KIND_LABEL2[ev.kind] ?? ev.kind,
228733
228876
  summary: `${KIND_LABEL2[ev.kind] ?? ev.kind}${artifactId ? ` \xB7 ${artifactId}` : ""}`
228734
228877
  };
228735
228878
  }
@@ -229680,13 +229823,32 @@ async function buildLeadResolver(registry2) {
229680
229823
  return null;
229681
229824
  };
229682
229825
  }
229683
- async function buildResolver(registry2) {
229826
+ async function memberNameResolver(listMembers, companyId) {
229827
+ if (!listMembers) return void 0;
229828
+ let members;
229829
+ try {
229830
+ members = await listMembers(companyId);
229831
+ } catch (err) {
229832
+ console.warn(`[collab] \u6210\u5458\u6635\u79F0\u8BFB\u53D6\u5931\u8D25\uFF0C\u672C\u6B21\u56DE\u843D\u5458\u5DE5\u6863\u6848\u59D3\u540D: ${String(err)}`);
229833
+ return void 0;
229834
+ }
229835
+ const byActor2 = /* @__PURE__ */ new Map();
229836
+ for (const m2 of members) {
229837
+ const name = m2.displayName?.trim();
229838
+ if (name) byActor2.set(m2.actorId, name);
229839
+ }
229840
+ return (id) => byActor2.get(id);
229841
+ }
229842
+ async function buildResolver(registry2, memberName) {
229684
229843
  if (!registry2) return void 0;
229685
229844
  const actors = await registry2.listActors();
229686
229845
  const byId = new Map(actors.map((a) => [a.id, a]));
229687
229846
  return (id) => {
229688
229847
  const a = byId.get(id);
229689
- return a ? actorRefFieldsOf(a) : actorRefFallbackOf(id);
229848
+ if (!a) return actorRefFallbackOf(id);
229849
+ const fields = actorRefFieldsOf(a);
229850
+ const nickname = memberName?.(id);
229851
+ return nickname ? { ...fields, name: nickname } : fields;
229690
229852
  };
229691
229853
  }
229692
229854
  async function buildProjectResolver(artifacts) {
@@ -229808,6 +229970,7 @@ function collabDomain(opts) {
229808
229970
  cache.set(companyId, ctx);
229809
229971
  return ctx;
229810
229972
  }
229973
+ const resolverFor = async (companyId, registry2) => buildResolver(registry2, await memberNameResolver(opts.listMembers, companyId));
229811
229974
  return (router) => {
229812
229975
  router.get("/api/organization/usage-summary", async (req) => {
229813
229976
  if (!opts.trace) {
@@ -229852,7 +230015,7 @@ function collabDomain(opts) {
229852
230015
  });
229853
230016
  router.get("/api/workorders", async (req) => {
229854
230017
  const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
229855
- const resolve10 = await buildResolver(registry2);
230018
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
229856
230019
  const resolveProject = await buildProjectResolver(artifacts);
229857
230020
  const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
229858
230021
  const ref2 = (id) => {
@@ -229883,7 +230046,7 @@ function collabDomain(opts) {
229883
230046
  });
229884
230047
  router.get("/api/workorder-metrics", async (req) => {
229885
230048
  const { kernel, registry: registry2, artifacts, engineStore } = await resolveCtx(req.auth.companyId);
229886
- const resolve10 = await buildResolver(registry2);
230049
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
229887
230050
  const resolveProject = await buildProjectResolver(artifacts);
229888
230051
  const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
229889
230052
  const workorders = buildWorkorderSummaries(kernel.model, resolve10, resolveProject, dispatchPausedOf);
@@ -229930,7 +230093,7 @@ function collabDomain(opts) {
229930
230093
  });
229931
230094
  router.get("/api/workorders/:id", async (req) => {
229932
230095
  const { kernel, oplog, blobs, registry: registry2, artifacts, engineStore } = await resolveCtx(req.auth.companyId);
229933
- const resolve10 = await buildResolver(registry2);
230096
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
229934
230097
  const resolveProject = await buildProjectResolver(artifacts);
229935
230098
  const dispatchPausedOf = await buildDispatchPausedResolver(kernel.model, artifacts);
229936
230099
  const wsArts = [...kernel.model.artifacts.values()].filter((a) => a.workspace === req.params.id);
@@ -230005,7 +230168,7 @@ function collabDomain(opts) {
230005
230168
  if (![...kernel.model.artifacts.values()].some((a) => a.workspace === workorderId)) {
230006
230169
  return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${workorderId}` } } };
230007
230170
  }
230008
- const resolve10 = await buildResolver(registry2);
230171
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
230009
230172
  const ref2 = (id) => {
230010
230173
  const extra = resolve10?.(id);
230011
230174
  return {
@@ -230048,7 +230211,7 @@ function collabDomain(opts) {
230048
230211
  if (!cardId) {
230049
230212
  return { status: 400, body: { error: { code: "bad_request", message: "\u7F3A\u5C11 card \u53C2\u6570\uFF08\u52A8\u6001\u5361 id\uFF09" } } };
230050
230213
  }
230051
- const resolve10 = await buildResolver(registry2);
230214
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
230052
230215
  const ref2 = (id) => {
230053
230216
  const extra = resolve10?.(id);
230054
230217
  return {
@@ -230088,7 +230251,7 @@ function collabDomain(opts) {
230088
230251
  if (!ledger) {
230089
230252
  return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${req.params.id}` } } };
230090
230253
  }
230091
- const resolve10 = await buildResolver(registry2);
230254
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
230092
230255
  const ids2 = /* @__PURE__ */ new Set();
230093
230256
  if (ledger.owner) ids2.add(ledger.owner);
230094
230257
  if (ledger.manager) ids2.add(ledger.manager);
@@ -230107,13 +230270,14 @@ function collabDomain(opts) {
230107
230270
  return { status: 200, body: { ...ledger, actors } };
230108
230271
  });
230109
230272
  router.get("/api/workorders/:id/files", async (req) => {
230110
- const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
230273
+ const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
230111
230274
  const ws = req.params.id;
230112
230275
  const exists = [...kernel.model.artifacts.values()].some((a) => a.workspace === ws);
230113
230276
  if (!exists) {
230114
230277
  return { status: 404, body: { error: { code: "not_found", message: `\u5DE5\u5355\u4E0D\u5B58\u5728: ${ws}` } } };
230115
230278
  }
230116
230279
  const files = artifacts?.listWorkOrderFiles ? await artifacts.listWorkOrderFiles(ws) : [];
230280
+ const resolve10 = await buildResolver(registry2);
230117
230281
  const items = files.map((f2) => ({
230118
230282
  id: f2.fileId,
230119
230283
  name: f2.name,
@@ -230123,12 +230287,14 @@ function collabDomain(opts) {
230123
230287
  updated_at: f2.linkedAt,
230124
230288
  blob_ref: f2.blobId,
230125
230289
  source_session_id: f2.sourceSessionId ?? null,
230126
- source_message_id: f2.sourceMessageId ?? null
230290
+ source_message_id: f2.sourceMessageId ?? null,
230291
+ uploaded_by: f2.uploadedBy ?? null,
230292
+ uploader_name: f2.uploadedBy ? resolve10?.(f2.uploadedBy)?.name ?? null : null
230127
230293
  }));
230128
230294
  return { status: 200, body: { total: items.length, items } };
230129
230295
  });
230130
230296
  router.post("/api/workorders/:id/files", async (req) => {
230131
- const { kernel, artifacts } = await resolveCtx(req.auth.companyId);
230297
+ const { kernel, registry: registry2, artifacts } = await resolveCtx(req.auth.companyId);
230132
230298
  const ws = req.params.id;
230133
230299
  const exists = [...kernel.model.artifacts.values()].some((a) => a.workspace === ws);
230134
230300
  if (!exists) {
@@ -230162,7 +230328,9 @@ function collabDomain(opts) {
230162
230328
  blobId,
230163
230329
  size: bytes2.byteLength,
230164
230330
  ...contentType ? { contentType } : {},
230165
- linkedAt
230331
+ linkedAt,
230332
+ // 上传者:项目页「创建人」列与预览副标据此署名(建单带入的那条路记的是建单发起人)。
230333
+ uploadedBy: req.auth.actor
230166
230334
  };
230167
230335
  try {
230168
230336
  await artifacts.addWorkOrderFile(file);
@@ -230172,6 +230340,7 @@ function collabDomain(opts) {
230172
230340
  body: { error: { code: "write_failed", message: `\u5199 work_order_files \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}` } }
230173
230341
  };
230174
230342
  }
230343
+ const resolve10 = await buildResolver(registry2);
230175
230344
  const item = {
230176
230345
  id: fileId,
230177
230346
  name,
@@ -230181,7 +230350,9 @@ function collabDomain(opts) {
230181
230350
  updated_at: linkedAt,
230182
230351
  blob_ref: blobId,
230183
230352
  source_session_id: null,
230184
- source_message_id: null
230353
+ source_message_id: null,
230354
+ uploaded_by: req.auth.actor,
230355
+ uploader_name: resolve10?.(req.auth.actor)?.name ?? null
230185
230356
  };
230186
230357
  return { status: 201, body: { item } };
230187
230358
  });
@@ -230255,7 +230426,7 @@ function collabDomain(opts) {
230255
230426
  const [leadOf, resolveActor, initialMarkers] = await Promise.all([
230256
230427
  buildLeadResolver(registry2),
230257
230428
  // 决策 0026 (c):gap 升级也发卡住节点 owner 的人类上级
230258
- buildResolver(registry2),
230429
+ resolverFor(req.auth.companyId, registry2),
230259
230430
  // 决策 0042:gap reporter 名册 join
230260
230431
  myReadMarkers ? myReadMarkers.listMarkers(me) : Promise.resolve([])
230261
230432
  // ADR-0086:知会型条目的已读水位
@@ -230528,7 +230699,7 @@ function collabDomain(opts) {
230528
230699
  let teamMembers;
230529
230700
  let lead;
230530
230701
  if (registry2) {
230531
- const resolve10 = await buildResolver(registry2);
230702
+ const resolve10 = await resolverFor(req.auth.companyId, registry2);
230532
230703
  const meRec = await registry2.getActor(me);
230533
230704
  if (meRec?.teamId) {
230534
230705
  teamMembers = new Set((await registry2.listActors({ teamId: meRec.teamId })).map((m2) => m2.id));
@@ -233780,7 +233951,10 @@ function createChatSessionsDomain(opts) {
233780
233951
  const kernel = await kernelFor(req);
233781
233952
  const engineStore = kernel?.getStore?.();
233782
233953
  const snap = engineStore ? await engineStore.transaction((tx) => tx.loadWorkorder(workorderId)) : null;
233783
- const resolve10 = await buildResolver(await registryFor(req));
233954
+ const resolve10 = await buildResolver(
233955
+ await registryFor(req),
233956
+ await memberNameResolver(opts.listMembers, req.auth.companyId)
233957
+ );
233784
233958
  const ref2 = (id) => {
233785
233959
  const extra = resolve10?.(id);
233786
233960
  return { id, ...extra?.name ? { name: extra.name } : {}, ...extra?.role ? { role: extra.role } : {}, ...extra?.avatar ? { avatar: extra.avatar } : {} };
@@ -240391,7 +240565,11 @@ function rowToVariable(row) {
240391
240565
  ...row.expires_at ? { expiresAt: row.expires_at } : {},
240392
240566
  ...row.last_used_at ? { lastUsedAt: row.last_used_at } : {},
240393
240567
  // 展示名(存量行没有 → 不带这一格,展示侧回落成 key)。
240394
- ...row.name ? { name: row.name } : {}
240568
+ ...row.name ? { name: row.name } : {},
240569
+ // 创建者 / 创建时间 / 更新者:2026-09-10 之前的行没有 → 不带这一格,展示侧出「—」。
240570
+ ...row.created_by ? { createdBy: row.created_by } : {},
240571
+ ...row.created_at ? { createdAt: row.created_at } : {},
240572
+ ...row.updated_by ? { updatedBy: row.updated_by } : {}
240395
240573
  };
240396
240574
  }
240397
240575
  var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
@@ -240562,6 +240740,9 @@ var init_postgres_registry = __esm({
240562
240740
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS name text`);
240563
240741
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS expires_at text`);
240564
240742
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS last_used_at text`);
240743
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_by text`);
240744
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_at text`);
240745
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS updated_by text`);
240565
240746
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
240566
240747
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
240567
240748
  await pool.query(`
@@ -241063,11 +241244,17 @@ var init_postgres_registry = __esm({
241063
241244
  await this.pool.query(
241064
241245
  /* `name` 走 COALESCE:**没传 = 不动**。连接器授权流程写变量时手上没有「名称」,
241065
241246
  普通赋值会把人在密钥表单里填的名字抹掉——同 connectors.connected_by 那条。 */
241066
- `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at, name)
241067
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
241247
+ /* created_by / created_at COALESCE(**旧值**, 新值):**第一次落行才写**,之后每次 upsert
241248
+ 都保留原值——否则「谁建的」会被最后一次改的人顶掉,这一格就没有意义了。
241249
+ updated_by 反过来:每次都覆盖。三者与 name 的 COALESCE 方向相反,别照抄。 */
241250
+ `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)
241251
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
241068
241252
  ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
241069
241253
  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)`,
241254
+ name=COALESCE($11, ${this.s}.variables.name),
241255
+ created_by=COALESCE(${this.s}.variables.created_by, $12),
241256
+ created_at=COALESCE(${this.s}.variables.created_at, $13),
241257
+ updated_by=$14`,
241071
241258
  [
241072
241259
  v2.key,
241073
241260
  v2.scope,
@@ -241079,7 +241266,10 @@ var init_postgres_registry = __esm({
241079
241266
  v2.valueEncrypted,
241080
241267
  v2.updatedAt,
241081
241268
  v2.expiresAt ?? null,
241082
- v2.name ?? null
241269
+ v2.name ?? null,
241270
+ v2.createdBy ?? null,
241271
+ v2.createdAt ?? null,
241272
+ v2.updatedBy ?? null
241083
241273
  ]
241084
241274
  );
241085
241275
  }
@@ -242050,8 +242240,10 @@ var init_postgres_projects = __esm({
242050
242240
  content_type text,
242051
242241
  source_session_id text,
242052
242242
  source_message_id text,
242053
- linked_at timestamptz NOT NULL
242243
+ linked_at timestamptz NOT NULL,
242244
+ uploaded_by text
242054
242245
  )`);
242246
+ await pool.query(`ALTER TABLE "${s2}".work_order_files ADD COLUMN IF NOT EXISTS uploaded_by text`);
242055
242247
  await pool.query(`CREATE INDEX IF NOT EXISTS work_order_files_wo_idx ON "${s2}".work_order_files (work_order_id)`);
242056
242248
  return new _PostgresArtifactStateStore(pool, schema);
242057
242249
  }
@@ -242334,11 +242526,12 @@ var init_postgres_projects = __esm({
242334
242526
  async addWorkOrderFile(file) {
242335
242527
  await this.pool.query(
242336
242528
  `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)
242529
+ (file_id, work_order_id, name, blob_id, size, content_type, source_session_id, source_message_id, linked_at, uploaded_by)
242530
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
242339
242531
  ON CONFLICT (file_id) DO UPDATE SET
242340
242532
  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`,
242533
+ source_session_id=$7, source_message_id=$8, linked_at=$9,
242534
+ uploaded_by=COALESCE($10, ${this.s}.work_order_files.uploaded_by)`,
242342
242535
  [
242343
242536
  file.fileId,
242344
242537
  file.workOrderId,
@@ -242348,7 +242541,8 @@ var init_postgres_projects = __esm({
242348
242541
  file.contentType ?? null,
242349
242542
  file.sourceSessionId ?? null,
242350
242543
  file.sourceMessageId ?? null,
242351
- file.linkedAt
242544
+ file.linkedAt,
242545
+ file.uploadedBy ?? null
242352
242546
  ]
242353
242547
  );
242354
242548
  }
@@ -242540,6 +242734,7 @@ var init_postgres_projects = __esm({
242540
242734
  ...row.content_type !== null ? { contentType: row.content_type } : {},
242541
242735
  ...row.source_session_id !== null ? { sourceSessionId: row.source_session_id } : {},
242542
242736
  ...row.source_message_id !== null ? { sourceMessageId: row.source_message_id } : {},
242737
+ ...row.uploaded_by !== null && row.uploaded_by !== void 0 ? { uploadedBy: row.uploaded_by } : {},
242543
242738
  linkedAt: iso2(row.linked_at)
242544
242739
  });
242545
242740
  }
@@ -259891,6 +260086,7 @@ async function startServe(opts) {
259891
260086
  }
259892
260087
  return resolved;
259893
260088
  };
260089
+ const listCompanyMembers = (companyId) => controlPlaneStore.listMembers(companyId ?? defaultCompanyId);
259894
260090
  const server = await startOasisServer({
259895
260091
  deployTargets,
259896
260092
  kernel: engine.kernel,
@@ -260138,6 +260334,8 @@ async function startServe(opts) {
260138
260334
  // 草案图算「类型默认审核人」用(与 /api/cmd 同源类型目录)
260139
260335
  roles: roleStore,
260140
260336
  // 诊断 f1641dd3-abdaa869:概览角色桶补「默认承接人」(读 RoleDef.defaultOwner)
260337
+ // 真人展示名的第一顺位:本公司成员昵称压过员工档案 name(档案上那份缺省是邮箱)。
260338
+ listMembers: listCompanyMembers,
260141
260339
  chatSession: chatSessionStore,
260142
260340
  // ADR-0086:chat 知会条目(与 readMarkers 成对)
260143
260341
  // ADR「多租户数据面收口」§D2:知会条目按**当前公司**取。装了路由才给(dev 文件模式沿用单实例)。
@@ -260176,6 +260374,8 @@ async function startServe(opts) {
260176
260374
  // 装了路由(有 PG)就只走路由,解析不出公司一律 403,不回落默认公司。
260177
260375
  ...chatStoreRouter ? { resolveStore: chatStoreFor } : {},
260178
260376
  registry: registryStore,
260377
+ // 缺口线署名的真人名字与 collab 读面同一口径:本公司成员昵称压过员工档案 name。
260378
+ listMembers: listCompanyMembers,
260179
260379
  // ★ wo:acfee725 评审 blocker ①:两个 `/workdir/*` 读口与 files-view 段按**当前公司**解 binding,
260180
260380
  // 与派发、与委派回流搬产物同一处(`resolveCompanyBinding`)。上面那个 `registry` 是全局
260181
260381
  // store(`public.actor_bindings`),org-registry cutover 之后它是冻结旧镜像——只留给
@@ -261321,6 +261521,14 @@ async function startServe(opts) {
261321
261521
  requestId: `project-created:${project.id}`
261322
261522
  }, project.id);
261323
261523
  });
261524
+ const notifyChatTurnSettled = async (info) => {
261525
+ await server.settleDelegationFromSweep(
261526
+ info.chatSessionId,
261527
+ `\u5B50\u4F1A\u8BDD\u8FD9\u4E00\u8F6E\u88AB\u6062\u590D\u5668\u6536\u53E3\u4E3A ${info.status}\uFF08${info.reason}\uFF09`,
261528
+ info.companyId,
261529
+ { outcome: info.status === "succeeded" ? "succeeded" : "failed" }
261530
+ );
261531
+ };
261324
261532
  const handshakeThrottleOpts = {};
261325
261533
  {
261326
261534
  const num3 = (name) => {
@@ -261383,10 +261591,12 @@ async function startServe(opts) {
261383
261591
  if (hit) return hit;
261384
261592
  const sessions = id === defaultCompanyId ? platformChatRecovery : PlatformChatRecoveryStore.open(pgPool, engineSchemaFor(id));
261385
261593
  const items = await chatItemsFor(id);
261594
+ const turnsForRecovery = await chatTurnsFor(id).catch(() => void 0);
261386
261595
  const plane = {
261387
261596
  listRunningAssistantMessages: () => sessions.listRunningAssistantMessages(),
261388
261597
  chatStore: sessions,
261389
261598
  getTurnForRun: (sid, rid) => sessions.getTurnForRun(sid, rid),
261599
+ ...turnsForRecovery ? { settleTurn: (turnId, patch) => turnsForRecovery.settleTurn(turnId, patch) } : {},
261390
261600
  ...items ? { items } : {}
261391
261601
  };
261392
261602
  chatRecoveryPlanes.set(id, plane);
@@ -261412,6 +261622,8 @@ async function startServe(opts) {
261412
261622
  isWired: (artifactId) => chatLiveSessions.has(`chat::${artifactId}`),
261413
261623
  ...opts2.stopOnFirstHit ? { stopOnFirstHit: true } : {},
261414
261624
  onCompanyError: chatRecoveryCompanyWarn(opts2.label),
261625
+ // 重挂的那一轮收口时当场外报,与清扫拍同一个接收方(见 `notifyChatTurnSettled`)。
261626
+ onTurnSettled: (info) => notifyChatTurnSettled(info),
261415
261627
  liveChat,
261416
261628
  trace: runRoutedTrace,
261417
261629
  traceHealth,
@@ -261682,14 +261894,12 @@ async function startServe(opts) {
261682
261894
  本拍是从轮次账本走的(`assistant_run_id` 一直有值),是"这一轮死没死"的权威来源。
261683
261895
  `companyId` 由扇出那层填好 —— 台账按公司分 store,少了它会查到默认公司的库。
261684
261896
  非委派会话调进去是空转:`settleFromSweep` 按 childSessionId 查不到记录就原样返回。 */
261685
- onTurnSettled: async ({ chatSessionId, status, reason, companyId }) => {
261686
- await server.settleDelegationFromSweep(
261687
- chatSessionId,
261688
- `\u5B50\u4F1A\u8BDD\u8FD9\u4E00\u8F6E\u88AB\u6062\u590D\u5668\u6536\u53E3\u4E3A ${status}\uFF08${reason}\uFF09`,
261689
- companyId,
261690
- { outcome: status === "succeeded" ? "succeeded" : "failed" }
261691
- );
261692
- },
261897
+ /* `status` 必须带下去分流。跨重启完成的那一轮走的正是 `succeeded`:
261898
+ `settleRound` 是派发那一刻建的闭包,serve 一重启就随老进程没了。
261899
+ 不分流的话,一条**干完活**的委派会被按时钟闭合拍成 failed。
261900
+ 接收方与重挂路**同一个**(`notifyChatTurnSettled`)——两条路一处收敛,
261901
+ 不会出现「清扫拍收的能通知、重挂收的不能」这种只在重启后显形的差异。 */
261902
+ onTurnSettled: (info) => notifyChatTurnSettled(info),
261693
261903
  log: (m2) => console.log(m2),
261694
261904
  onCompanyError: (companyId, err) => {
261695
261905
  const key = companyId ?? "<default>";
@@ -271034,7 +271244,7 @@ function shimScript() {
271034
271244
  }
271035
271245
 
271036
271246
  // src/index.ts
271037
- var PKG_VERSION = true ? "2.2.11" : "dev";
271247
+ var PKG_VERSION = true ? "2.2.12" : "dev";
271038
271248
  var LOCAL_BIN = localBin();
271039
271249
  var NPM_PREFIX = npmPrefix();
271040
271250
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.11",
3
+ "version": "2.2.12",
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": "99635443b071b822e6a7e8b4ee1e4940cd488202"
29
+ "sourceHead": "ae18ecf5995f37b7a3f14b601e3a87c21d91ab03"
30
30
  }
31
31
  }