oasis_test_v2 2.2.27 → 2.2.29

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 +406 -44
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1399,13 +1399,6 @@ var init_chat_turn = __esm({
1399
1399
  });
1400
1400
 
1401
1401
  // ../contract/src/delegation.ts
1402
- function isLegacyDelegationOutboundPath(path41) {
1403
- return path41.startsWith(`${LEGACY_DELEGATION_OUTBOUND_DIR}/`);
1404
- }
1405
- function delegationOutboundRoot(roundSummaries) {
1406
- const legacy = roundSummaries.some((round) => (round.files ?? []).some((file) => isLegacyDelegationOutboundPath(file.name)));
1407
- return legacy ? LEGACY_DELEGATION_OUTBOUND_DIR : DELEGATION_OUTBOUND_DIR;
1408
- }
1409
1402
  function trimRoundSummariesForReturn(summaries, recentRounds = DELEGATION_RETURN_RECENT_ROUNDS) {
1410
1403
  const keep = Math.max(0, recentRounds);
1411
1404
  const cut = Math.max(0, summaries.length - keep);
@@ -1446,12 +1439,11 @@ function touchAnchorMessageId(messageId) {
1446
1439
  const hash2 = messageId.indexOf("#");
1447
1440
  return hash2 >= 0 ? messageId.slice(0, hash2) : messageId;
1448
1441
  }
1449
- var DelegationLabelConflictError, DELEGATION_FILE_LIMITS, DELEGATION_INBOUND_DIR, DELEGATION_OUTBOUND_DIR, LEGACY_DELEGATION_OUTBOUND_DIR, DELEGATION_RETURN_RECENT_ROUNDS;
1442
+ var DelegationLabelConflictError, DELEGATION_FILE_LIMITS, DELEGATION_INBOUND_DIR, DELEGATION_OUTBOUND_DIR, DELEGATION_RETURN_RECENT_ROUNDS;
1450
1443
  var init_delegation = __esm({
1451
1444
  "../contract/src/delegation.ts"() {
1452
1445
  "use strict";
1453
1446
  init_session_outputs();
1454
- init_session_outputs();
1455
1447
  DelegationLabelConflictError = class extends Error {
1456
1448
  constructor(parentSessionId, label) {
1457
1449
  super(`delegation label conflict in parent session: parent=${parentSessionId} label=${label ?? "<null>"}`);
@@ -1469,8 +1461,7 @@ var init_delegation = __esm({
1469
1461
  maxTotalBytes: 20 * 1024 * 1024
1470
1462
  };
1471
1463
  DELEGATION_INBOUND_DIR = "inputs/attachments";
1472
- DELEGATION_OUTBOUND_DIR = `${SESSION_OUTPUTS_DIR}/delegations`;
1473
- LEGACY_DELEGATION_OUTBOUND_DIR = "delegations";
1464
+ DELEGATION_OUTBOUND_DIR = "delegations";
1474
1465
  DELEGATION_RETURN_RECENT_ROUNDS = 3;
1475
1466
  }
1476
1467
  });
@@ -1563,7 +1554,7 @@ var init_delegation_prompts = __esm({
1563
1554
  " \u4E2D\u9014\u7F3A\u4EC0\u4E48\u518D\u8981\u4EC0\u4E48\u3002**\u53EA\u6709\u76EE\u6807\u3001\u8303\u56F4\u3001\u6216\u8981\u7528\u6237\u62CD\u677F\u7684\u53D6\u820D**\u624D\u56DE\u5934\u95EE\u4EBA\u3002",
1564
1555
  "- **\u62FF\u4E0D\u51C6\u5F52\u8C01\u8D1F\u8D23\uFF0C\u5C31\u81EA\u5DF1\u505A\u3002**\u6D3E\u9519\u4EBA\u6BD4\u591A\u505A\u4E00\u6B65\u8D35\u3002",
1565
1556
  "- **\u81EA\u5DF1\u505A\u7684\u65F6\u5019**\uFF1A\u5148\u7528\u4E00\u53E5\u666E\u901A\u8BDD\u8BF4\u4F60\u5728\u505A\u4EC0\u4E48\uFF08\u4F8B\uFF1A\u300C\u6211\u6765\u6574\u7406\u8FD9\u4EFD\u9700\u6C42\uFF0C\u5E76\u8865\u51FA\u53EF\u6267\u884C\u7684\u8303\u56F4\u300D\uFF09\uFF0C",
1566
- " \u505A\u5B8C\u628A\u7ED3\u679C\u6216\u6587\u4EF6\u7ED9\u51FA\u6765\u2014\u2014\u6587\u4EF6\u4F1A\u81EA\u52A8\u8FDB\u53F3\u4FA7\u5DE5\u4F5C\u533A\uFF0C\u4E0D\u7528\u4F60\u518D\u8D34\u4E00\u904D\u3002",
1557
+ " \u505A\u5B8C\u628A\u7ED3\u679C\u7ED9\u51FA\u6765\uFF1B\u7ED9\u7528\u6237\u7684\u6587\u4EF6\u653E\u8FDB `outputs/`\uFF08\u4F1A\u51FA\u5361\uFF09\uFF0C\u4E0D\u7528\u518D\u8D34\u3002",
1567
1558
  "- **\u4E2D\u9014\u53D1\u73B0\u8BE5\u6362\u8DEF**\uFF08\u505A\u7740\u505A\u7740\u53D1\u73B0\u8981\u4E09\u4E2A\u4EBA\u4E00\u8D77\uFF09\uFF1A\u76F4\u63A5\u6362\u3002",
1568
1559
  " **\u8FD9\u4E2A\u8F6C\u6298\u4E0D\u7528\u8BB2\u7ED9\u7528\u6237\u542C**\uFF0C\u53EA\u5728\u771F\u9700\u8981\u4ED6\u8865\u4FE1\u606F\u3001\u7ED9\u6388\u6743\u3001\u505A\u53D6\u820D\u65F6\u624D\u5F00\u53E3\u3002",
1569
1560
  "",
@@ -1616,7 +1607,7 @@ var init_delegation_prompts = __esm({
1616
1607
  "### \u8981\u5E26\u6587\u4EF6\u5C31\u660E\u5199",
1617
1608
  "",
1618
1609
  "`--file <\u5DE5\u4F5C\u533A\u91CC\u7684\u8DEF\u5F84>`\uFF0C\u53EF\u4EE5\u7ED9\u591A\u6B21\u3002**\u4E0D\u5199\u5C31\u4E00\u4E2A\u6587\u4EF6\u90FD\u4E0D\u5E26**\u2014\u2014\u4E24\u8FB9\u5DE5\u4F5C\u533A\u662F\u5206\u5F00\u7684\u3002",
1619
- "\u5BF9\u65B9\u7684\u4EA4\u4ED8\u7269\u4F1A\u81EA\u52A8\u56DE\u5230\u8FD9\u4E2A\u5BF9\u8BDD\u7684\u5DE5\u4F5C\u533A\uFF0C\u4F60\u4E0D\u7528\u624B\u5DE5\u642C\u3002"
1610
+ "\u5BF9\u65B9\u4EA4\u56DE\u7684\u6587\u4EF6\u81EA\u52A8\u843D\u8FDB `delegations/<\u77ED\u540D>/`\uFF0C\u4E0D\u51FA\u5361\uFF1B\u8981\u7ED9\u7528\u6237\u770B\u7684\uFF0C\u590D\u5236\u6216\u52A0\u5DE5\u8FDB `outputs/`\uFF0C\u539F\u4EF6\u522B\u52A8\u3002"
1620
1611
  ].join("\n");
1621
1612
  }
1622
1613
  });
@@ -196286,7 +196277,7 @@ async function collectInboundFiles(port, parent, paths, limits, lenient) {
196286
196277
  }
196287
196278
  return { prepared, skipped, totalBytes, unchanged: [] };
196288
196279
  }
196289
- async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FILE_LIMITS, known, outboundRoot = DELEGATION_OUTBOUND_DIR) {
196280
+ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FILE_LIMITS, known) {
196290
196281
  const out = { prepared: [], skipped: [], totalBytes: 0, unchanged: [] };
196291
196282
  if (!port) return out;
196292
196283
  if (port.locate) {
@@ -196363,7 +196354,7 @@ async function prepareOutboundFiles(port, child, dirName, limits = DELEGATION_FI
196363
196354
  out.skipped.push({ source: file.rel, code: "PATH_REJECTED", message: "\u8DEF\u5F84\u4E0D\u5408\u6CD5\uFF0C\u6CA1\u642C" });
196364
196355
  continue;
196365
196356
  }
196366
- const target = `${outboundRoot}/${dirName}/${relative5}`;
196357
+ const target = `${DELEGATION_OUTBOUND_DIR}/${dirName}/${relative5}`;
196367
196358
  const seen = known?.get(target);
196368
196359
  if (seen && file.mtime && seen.size === file.size && seen.mtime === file.mtime) {
196369
196360
  out.unchanged.push(target);
@@ -196445,10 +196436,16 @@ function digestOf(text5) {
196445
196436
  return line.length > DIGEST_CHARS ? `${line.slice(0, DIGEST_CHARS)}\u2026` : line;
196446
196437
  }
196447
196438
  function delegationArtifactsView(record8) {
196448
- return collectDelegationArtifacts(record8.roundSummaries).map((a) => ({
196449
- ...a,
196450
- childMessageId: childRoundMessageIds(record8.childSessionId, a.round).placeholderId
196451
- }));
196439
+ const dirName = delegationDirName(record8.label, record8.expertActorId, record8.id);
196440
+ const prefixes = [`${DELEGATION_OUTBOUND_DIR}/${dirName}/`, `${SESSION_OUTPUTS_DIR}/${DELEGATION_OUTBOUND_DIR}/${dirName}/`];
196441
+ return collectDelegationArtifacts(record8.roundSummaries).map((a) => {
196442
+ const prefix = prefixes.find((p2) => a.name.startsWith(p2));
196443
+ return {
196444
+ ...a,
196445
+ childMessageId: childRoundMessageIds(record8.childSessionId, a.round).placeholderId,
196446
+ ...prefix ? { childPath: `${SESSION_OUTPUTS_DIR}/${a.name.slice(prefix.length)}` } : {}
196447
+ };
196448
+ });
196452
196449
  }
196453
196450
  function childRoundMessageIds(childSessionId, round) {
196454
196451
  const suffix = round <= 1 ? "" : `:${round}`;
@@ -198152,7 +198149,12 @@ function delegationReturnText(terminal, opts) {
198152
198149
  }
198153
198150
  }
198154
198151
  if (opts.movedPaths.length > 0) {
198155
- lines.push("", "\u4E13\u5BB6\u4EA4\u56DE\u7684\u6587\u4EF6\uFF08\u5DF2\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u53EF\u76F4\u63A5\u8BFB\u53D6\uFF09\uFF1A", ...opts.movedPaths.map((p2) => `- ${p2}`));
198152
+ const updated = new Set(opts.updatedPaths ?? []);
198153
+ lines.push(
198154
+ "",
198155
+ "\u4E13\u5BB6\u4EA4\u56DE\u7684\u6587\u4EF6\uFF08\u5DF2\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u53EF\u76F4\u63A5\u8BFB\u53D6\uFF09\uFF1A",
198156
+ ...opts.movedPaths.map((p2) => `- ${p2}\uFF08${updated.has(p2) ? "\u66F4\u65B0\uFF1A\u4E4B\u524D\u4EA4\u56DE\u8FC7\uFF0C\u8FD9\u4E00\u7248\u6709\u6539\u52A8" : "\u65B0\u4EA4"}\uFF09`)
198157
+ );
198156
198158
  }
198157
198159
  if ((opts.unchangedPaths?.length ?? 0) > 0) {
198158
198160
  lines.push("", `\u53E6\u6709 ${opts.unchangedPaths.length} \u4E2A\u6587\u4EF6\u4E0E\u4E0A\u6B21\u4EA4\u56DE\u65F6\u9010\u5B57\u4E00\u81F4\uFF0C\u672C\u8F6E\u6CA1\u6709\u91CD\u4F20\uFF08\u4ECD\u5728\u4F60\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF0C\u8DEF\u5F84\u4E0D\u53D8\uFF09\u3002`);
@@ -198175,6 +198177,11 @@ function delegationReturnText(terminal, opts) {
198175
198177
  "",
198176
198178
  record8.state === "done" ? "\u8FD9\u4E9B\u662F\u6750\u6599\uFF0C\u4E0D\u662F\u7ED9\u7528\u6237\u7684\u6210\u54C1\uFF1A\u4F60\u6709\u8FD9\u6761\u5BF9\u8BDD\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\uFF0C\u81EA\u5DF1\u5224\u65AD\u8BE5\u8BB2\u54EA\u4E00\u53E5\u3001\u8981\u4E0D\u8981\u63A5\u7740\u5F80\u4E0B\u505A\u3002" : "\u5148\u81EA\u5DF1\u5224\u65AD\u80FD\u4E0D\u80FD\u63A5\u4F4F\uFF1A\u63A5\u7BA1\u3001\u6362\u4E2A\u505A\u6CD5\u3001\u8FD8\u662F\u91CD\u6D3E\u3002**\u522B\u628A\u4E13\u4E1A\u5931\u8D25\u539F\u6837\u4E22\u56DE\u7ED9\u7528\u6237**\u3002"
198177
198179
  );
198180
+ if (opts.movedPaths.length > 0 || (opts.unchangedPaths?.length ?? 0) > 0) {
198181
+ lines.push(
198182
+ "\u4EA4\u56DE\u7684\u6587\u4EF6\u653E\u5728 `delegations/` \u4E0B\uFF0C**\u4E0D\u4F1A\u5728\u5BF9\u8BDD\u91CC\u51FA\u5361**\uFF08\u7528\u6237\u53EA\u80FD\u53BB\u53F3\u4FA7\u300C\u76F8\u5173\u6587\u4EF6\u300D\u5217\u8868\u91CC\u7FFB\uFF09\uFF1B\u8981\u7ED9\u7528\u6237\u770B\u7684\uFF0C\u590D\u5236\u6216\u52A0\u5DE5\u540E\u653E\u8FDB `outputs/`\uFF08\u539F\u4EF6\u522B\u79FB\u52A8\u3001\u522B\u5220\u9664\uFF09\u3002" + ((opts.updatedPaths?.length ?? 0) > 0 ? "\u6807\u4E86\u300C\u66F4\u65B0\u300D\u7684\u6587\u4EF6\u5982\u679C\u4F60\u4E4B\u524D\u653E\u8FDB\u8FC7 `outputs/`\uFF0C\u8981\u4E0D\u8981\u6362\u6210\u65B0\u7248\u7531\u4F60\u5224\u65AD\u3002" : "")
198183
+ );
198184
+ }
198178
198185
  return lines.join("\n");
198179
198186
  }
198180
198187
  async function deliverDelegationReturn(deps, terminal) {
@@ -198203,9 +198210,7 @@ async function deliverDelegationReturn(deps, terminal) {
198203
198210
  { chatSessionId: record8.childSessionId, ...parentSession.companyId ? { companyId: parentSession.companyId } : {} },
198204
198211
  dirName,
198205
198212
  void 0,
198206
- known,
198207
- // 已经往老落点(工作区根的 `delegations/`)交过文件的委派继续落那儿——协调者手里的是老路径。
198208
- delegationOutboundRoot(record8.roundSummaries)
198213
+ known
198209
198214
  ).catch((err) => {
198210
198215
  log3(`[delegation-return] \u4EA7\u7269\u642C\u8FD0\u5931\u8D25\uFF08delegation=${record8.id}\uFF09: ${String(err)}`);
198211
198216
  return {
@@ -198229,11 +198234,14 @@ async function deliverDelegationReturn(deps, terminal) {
198229
198234
  );
198230
198235
  };
198231
198236
  const bundleFiles = toBundleFiles(outbound.prepared);
198237
+ const previouslyDelivered = new Set(record8.roundSummaries.flatMap((round) => (round.files ?? []).map((file) => file.name)));
198232
198238
  const text5 = delegationReturnText(terminal, {
198233
198239
  movedPaths: bundleFiles.paths,
198234
198240
  skipped: outbound.skipped,
198235
198241
  recentRounds: deps.recentRounds ?? DELEGATION_RETURN_RECENT_ROUNDS,
198236
- unchangedPaths: outbound.unchanged
198242
+ unchangedPaths: outbound.unchanged,
198243
+ // 「之前交回过」按台账里已经登记的文件名判——这一轮的文件要等投递之后才落账,此刻还不在里面。
198244
+ updatedPaths: bundleFiles.paths.filter((p2) => previouslyDelivered.has(p2))
198237
198245
  });
198238
198246
  const movedFileRefs = outbound.prepared.map((f2) => ({
198239
198247
  name: f2.rel,
@@ -209369,8 +209377,12 @@ var init_memory_registry_store = __esm({
209369
209377
  const keptName = v2.name ?? ex?.name;
209370
209378
  const keptCreatedBy = ex?.createdBy ?? v2.createdBy;
209371
209379
  const keptCreatedAt = ex?.createdAt ?? v2.createdAt;
209380
+ const keptAudience = v2.audience ?? ex?.audience;
209381
+ const keptEnabled = v2.enabledActorIds ?? ex?.enabledActorIds;
209372
209382
  this.variables.set(mk, {
209373
209383
  ...v2,
209384
+ ...keptAudience !== void 0 ? { audience: keptAudience } : {},
209385
+ ...keptEnabled !== void 0 ? { enabledActorIds: [...keptEnabled] } : {},
209374
209386
  ...ex?.lastUsedAt !== void 0 ? { lastUsedAt: ex.lastUsedAt } : {},
209375
209387
  ...keptName ? { name: keptName } : {},
209376
209388
  ...keptCreatedBy ? { createdBy: keptCreatedBy } : {},
@@ -209388,12 +209400,30 @@ var init_memory_registry_store = __esm({
209388
209400
  const ex = this.variables.get(mk);
209389
209401
  if (ex) this.variables.set(mk, { ...ex, valueEncrypted, updatedAt });
209390
209402
  }
209403
+ async patchVariableMeta(key, actorId, patch) {
209404
+ const mk = this._varKey(key, actorId);
209405
+ const ex = this.variables.get(mk);
209406
+ if (!ex) return false;
209407
+ const next = { ...ex, updatedAt: patch.updatedAt };
209408
+ if (patch.audience !== void 0) next.audience = patch.audience;
209409
+ if (patch.enabledActorIds !== void 0) next.enabledActorIds = [...patch.enabledActorIds];
209410
+ if (patch.deliveryMode !== void 0) next.deliveryMode = patch.deliveryMode;
209411
+ if (patch.expiresAt !== void 0) {
209412
+ if (patch.expiresAt === null) delete next.expiresAt;
209413
+ else next.expiresAt = patch.expiresAt;
209414
+ }
209415
+ if (patch.updatedBy !== void 0) next.updatedBy = patch.updatedBy;
209416
+ this.variables.set(mk, next);
209417
+ return true;
209418
+ }
209391
209419
  async getVariableCiphertext(key, actorId) {
209392
209420
  return this.variables.get(this._varKey(key, actorId))?.valueEncrypted ?? null;
209393
209421
  }
209394
209422
  async listVariables(actorId) {
209395
209423
  const all2 = [...this.variables.values()];
209396
- if (actorId !== void 0) return all2.filter((v2) => v2.scope === "global" || v2.actorId === actorId).map((v2) => ({ ...v2 }));
209424
+ if (actorId !== void 0) {
209425
+ return all2.filter((v2) => v2.actorId === actorId || v2.scope === "global" && (v2.audience !== "selected" || (v2.enabledActorIds ?? []).includes(actorId))).map((v2) => ({ ...v2 }));
209426
+ }
209397
209427
  return all2.map((v2) => ({ ...v2 }));
209398
209428
  }
209399
209429
  async resolveProjectVariables(projectId2) {
@@ -209832,6 +209862,27 @@ var init_memory_trace_store = __esm({
209832
209862
  usage: structuredClone(run.usage)
209833
209863
  }));
209834
209864
  }
209865
+ /** 与 PostgresTraceStore.runtimeRunActivity 同语义:按实例数全部 run(不看状态 / usage),取最近开跑。 */
209866
+ async runtimeRunActivity(runtimeInstanceId) {
209867
+ let runCount = 0;
209868
+ let lastRunStartedAt = null;
209869
+ for (const run of this.runs.values()) {
209870
+ if (run.runtimeInstanceId !== runtimeInstanceId) continue;
209871
+ runCount += 1;
209872
+ if (lastRunStartedAt === null || run.startedAt > lastRunStartedAt) lastRunStartedAt = run.startedAt;
209873
+ }
209874
+ return { runCount, lastRunStartedAt };
209875
+ }
209876
+ /** 与 PostgresTraceStore.listRuntimeRuns 同语义:时间窗内该实例的全部 run,按开跑正序。 */
209877
+ async listRuntimeRuns(runtimeInstanceId, range) {
209878
+ return [...this.runs.values()].filter((run) => run.runtimeInstanceId === runtimeInstanceId && run.startedAt >= range.from && run.startedAt <= range.to).sort((a, b2) => a.startedAt < b2.startedAt ? -1 : a.startedAt > b2.startedAt ? 1 : 0).map((run) => ({
209879
+ actorId: run.actorId,
209880
+ runtimeKind: run.runtimeKind,
209881
+ effectiveModel: run.effectiveModel,
209882
+ startedAt: run.startedAt,
209883
+ usage: run.usage ? structuredClone(run.usage) : null
209884
+ }));
209885
+ }
209835
209886
  /* ---------- execution continuity ---------- */
209836
209887
  async getAttempt(attemptId) {
209837
209888
  return this.getRun(attemptId);
@@ -215905,6 +215956,9 @@ var init_company_scoped_registry = __esm({
215905
215956
  async patchVariableValue(...args) {
215906
215957
  return this.base.patchVariableValue(...args);
215907
215958
  }
215959
+ async patchVariableMeta(...args) {
215960
+ return this.base.patchVariableMeta(...args);
215961
+ }
215908
215962
  async getVariableCiphertext(...args) {
215909
215963
  return this.base.getVariableCiphertext(...args);
215910
215964
  }
@@ -220486,6 +220540,9 @@ function variableKeyFromEnv(env = process.env) {
220486
220540
  if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
220487
220541
  return (0, import_node_crypto47.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
220488
220542
  }
220543
+ function dedupe(ids2) {
220544
+ return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
220545
+ }
220489
220546
  var import_node_crypto47, REDACTED_MARKER, ActorsService, mask, changedKeys;
220490
220547
  var init_service5 = __esm({
220491
220548
  "../server/src/domains/actors/service.ts"() {
@@ -221469,7 +221526,31 @@ ${input.description}
221469
221526
  ...v2.by ? { createdBy: v2.by, createdAt: this.now(), updatedBy: v2.by } : {},
221470
221527
  // ADR 凭据保险库补章 §D2.2:expiresAt 写侧穿透——**显式带 key**(含 undefined),使清空(传空→undefined)
221471
221528
  // 也落到底(store 两实现都按此覆盖)。lastUsedAt 从不由写侧传,取用时另刷(§D2.3)。
221472
- expiresAt: v2.expiresAt
221529
+ expiresAt: v2.expiresAt,
221530
+ // 决策 0556:发给谁只对全局行有意义;个人档 / 项目档一律不带(存储层 COALESCE:不带 = 不动)。
221531
+ ...v2.scope === "global" && v2.audience !== void 0 ? { audience: v2.audience } : {},
221532
+ ...v2.scope === "global" && v2.enabledActorIds !== void 0 ? { enabledActorIds: dedupe(v2.enabledActorIds) } : {}
221533
+ });
221534
+ }
221535
+ /**
221536
+ * 只改一行变量的元数据、不碰值(决策 0556)。
221537
+ *
221538
+ * 两个用处:① 管理页「配置Agent」改发给谁(audience / enabledActorIds)——不用为了改名单去取明文重写;
221539
+ * ② 改到期时间 / 注入方式不用连值一起重填(此前 `putVariable` 空值即静默不写,界面只能逼人重输一遍值)。
221540
+ *
221541
+ * 行不存在返回 false,**不会凭空造一条没有值的行**。audience / enabledActorIds 只允许写在全局行上。
221542
+ */
221543
+ async patchVariableMeta(key, selector, patch) {
221544
+ if (selector.actorId !== void 0 && (patch.audience !== void 0 || patch.enabledActorIds !== void 0)) {
221545
+ throw new ApiError(400, "BAD_REQUEST", "audience / enabledActorIds \u53EA\u80FD\u5199\u5728\u7EC4\u7EC7\u9ED8\u8BA4\u503C\uFF08\u5168\u5C40\u884C\uFF09\u4E0A\uFF0C\u4E2A\u4EBA\u6863\u6CA1\u6709\u300C\u53D1\u7ED9\u8C01\u300D");
221546
+ }
221547
+ return this.opts.store.patchVariableMeta(key, selector.actorId, {
221548
+ ...patch.audience !== void 0 ? { audience: patch.audience } : {},
221549
+ ...patch.enabledActorIds !== void 0 ? { enabledActorIds: dedupe(patch.enabledActorIds) } : {},
221550
+ ...patch.deliveryMode !== void 0 ? { deliveryMode: patch.deliveryMode } : {},
221551
+ ...patch.expiresAt !== void 0 ? { expiresAt: patch.expiresAt } : {},
221552
+ ...patch.by ? { updatedBy: patch.by } : {},
221553
+ updatedAt: this.now()
221473
221554
  });
221474
221555
  }
221475
221556
  /** ADR 凭据保险库补章 §D2.3:某一行变量「明文真被取用」→ 刷 lastUsedAt。行按 (key, actorId?, projectId?)
@@ -221544,7 +221625,10 @@ ${input.description}
221544
221625
  // 存量行没有这三格,原样不带出去——展示侧出「—」,不拿 updatedAt 冒充 createdAt。
221545
221626
  ...r.createdBy !== void 0 ? { createdBy: r.createdBy } : {},
221546
221627
  ...r.createdAt !== void 0 ? { createdAt: r.createdAt } : {},
221547
- ...r.updatedBy !== void 0 ? { updatedBy: r.updatedBy } : {}
221628
+ ...r.updatedBy !== void 0 ? { updatedBy: r.updatedBy } : {},
221629
+ // 决策 0556:发给谁。存量行没有 → 不带出去,读侧按「全员生效」理解。
221630
+ ...r.audience !== void 0 ? { audience: r.audience } : {},
221631
+ ...r.enabledActorIds !== void 0 ? { enabledActorIds: r.enabledActorIds } : {}
221548
221632
  };
221549
221633
  });
221550
221634
  }
@@ -222992,6 +223076,10 @@ function actorsDomain(opts) {
222992
223076
  const b2 = req.body;
222993
223077
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
222994
223078
  const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
223079
+ const audienceMeta = parseAudienceMeta(b2);
223080
+ if ((audienceMeta.audience !== void 0 || audienceMeta.enabledActorIds !== void 0) && b2.scope !== "global") {
223081
+ throw new ApiError(400, "BAD_REQUEST", "audience / enabledActorIds \u53EA\u80FD\u5199\u5728 scope=global \u7684\u7EC4\u7EC7\u9ED8\u8BA4\u503C\u4E0A");
223082
+ }
222995
223083
  if (b2.scope !== "global" && b2.scope !== "personal" && b2.scope !== "project") {
222996
223084
  throw new ApiError(
222997
223085
  400,
@@ -223036,6 +223124,7 @@ function actorsDomain(opts) {
223036
223124
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
223037
223125
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
223038
223126
  ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
223127
+ ...audienceMeta,
223039
223128
  // 创建者/更新者取**当前登录人**,不接受 body 传入——自报的「谁建的」等于没有这一格。
223040
223129
  by: req.auth.actor,
223041
223130
  expiresAt: expiresAt2
@@ -223048,6 +223137,18 @@ function actorsDomain(opts) {
223048
223137
  );
223049
223138
  return { status: 201, body: { ok: true } };
223050
223139
  });
223140
+ router.patch("/api/variables/:key", async (req) => {
223141
+ requireVariableManager(req);
223142
+ const { service } = await resolveCtx(req.auth.companyId);
223143
+ const key = req.params.key;
223144
+ const b2 = req.body ?? {};
223145
+ const patch = parseMetaPatch(b2, { allowAudience: true });
223146
+ await requireSecretOwner(req, service, key, {});
223147
+ const ok3 = await service.patchVariableMeta(key, {}, { ...patch, by: req.auth.actor });
223148
+ if (!ok3) throw new ApiError(404, "VARIABLE_NOT_FOUND", `\u6CA1\u6709\u53EB ${key} \u7684\u7EC4\u7EC7\u9ED8\u8BA4\u503C`);
223149
+ await auditVariableChange(req, "credential_write", key, "global");
223150
+ return { status: 200, body: { ok: true } };
223151
+ });
223051
223152
  router.delete("/api/variables/:key", async (req) => {
223052
223153
  requireVariableManager(req);
223053
223154
  const { service } = await resolveCtx(req.auth.companyId);
@@ -223118,6 +223219,19 @@ function actorsDomain(opts) {
223118
223219
  await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
223119
223220
  return { status: 201, body: { ok: true } };
223120
223221
  });
223222
+ router.patch("/api/actors/:id/variables/:key", async (req) => {
223223
+ requireVariableManager(req);
223224
+ const { service } = await resolveCtx(req.auth.companyId);
223225
+ const actorId = req.params.id;
223226
+ const key = req.params.key;
223227
+ const b2 = req.body ?? {};
223228
+ const patch = parseMetaPatch(b2, { allowAudience: false });
223229
+ await requireSecretOwner(req, service, key, { actorId });
223230
+ const ok3 = await service.patchVariableMeta(key, { actorId }, { ...patch, by: req.auth.actor });
223231
+ if (!ok3) throw new ApiError(404, "VARIABLE_NOT_FOUND", `\u8FD9\u4F4D Agent \u6CA1\u6709\u5355\u72EC\u8BBE\u7F6E ${key}`);
223232
+ await auditVariableChange(req, "credential_write", key, `personal:${actorId}`);
223233
+ return { status: 200, body: { ok: true } };
223234
+ });
223121
223235
  router.delete("/api/actors/:id/variables/:key", async (req) => {
223122
223236
  requireVariableManager(req);
223123
223237
  const { service } = await resolveCtx(req.auth.companyId);
@@ -223720,6 +223834,43 @@ function actorsDomain(opts) {
223720
223834
  });
223721
223835
  };
223722
223836
  }
223837
+ function parseAudienceMeta(b2) {
223838
+ const out = {};
223839
+ if (b2.audience !== void 0) {
223840
+ if (b2.audience !== "all" && b2.audience !== "selected") {
223841
+ throw new ApiError(400, "BAD_REQUEST", "audience \u53EA\u80FD\u662F all \u6216 selected");
223842
+ }
223843
+ out.audience = b2.audience;
223844
+ }
223845
+ if (b2.enabledActorIds !== void 0) {
223846
+ if (!Array.isArray(b2.enabledActorIds) || b2.enabledActorIds.some((x2) => typeof x2 !== "string" || !x2)) {
223847
+ throw new ApiError(400, "BAD_REQUEST", "enabledActorIds \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4");
223848
+ }
223849
+ out.enabledActorIds = b2.enabledActorIds;
223850
+ }
223851
+ return out;
223852
+ }
223853
+ function parseMetaPatch(b2, opts) {
223854
+ const out = {};
223855
+ const aud = parseAudienceMeta(b2);
223856
+ if ((aud.audience !== void 0 || aud.enabledActorIds !== void 0) && !opts.allowAudience) {
223857
+ throw new ApiError(400, "BAD_REQUEST", "\u4E2A\u4EBA\u6863\u6CA1\u6709\u300C\u53D1\u7ED9\u8C01\u300D\u2014\u2014audience / enabledActorIds \u8BF7\u5199\u5728\u7EC4\u7EC7\u9ED8\u8BA4\u503C\u4E0A");
223858
+ }
223859
+ Object.assign(out, aud);
223860
+ if (b2.deliveryMode !== void 0) {
223861
+ if (b2.deliveryMode !== "env" && b2.deliveryMode !== "ref" && b2.deliveryMode !== "broker") {
223862
+ throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env\u3001ref \u6216 broker");
223863
+ }
223864
+ out.deliveryMode = b2.deliveryMode;
223865
+ }
223866
+ if (b2.expiresAt !== void 0) {
223867
+ if (b2.expiresAt === null || b2.expiresAt === "") out.expiresAt = null;
223868
+ else if (typeof b2.expiresAt === "string") out.expiresAt = normalizeExpiresAt(b2.expiresAt) ?? null;
223869
+ else throw new ApiError(400, "BAD_REQUEST", "expiresAt \u5FC5\u987B\u662F ISO \u65F6\u95F4\u5B57\u7B26\u4E32\u6216 null");
223870
+ }
223871
+ if (Object.keys(out).length === 0) throw new ApiError(400, "BAD_REQUEST", "\u81F3\u5C11\u8981\u6539\u4E00\u6837\uFF1Aaudience / enabledActorIds / deliveryMode / expiresAt");
223872
+ return out;
223873
+ }
223723
223874
  var CREDENTIAL_REVEAL_SOURCES, RevealRateLimiter;
223724
223875
  var init_routes3 = __esm({
223725
223876
  "../server/src/domains/actors/routes.ts"() {
@@ -231645,6 +231796,84 @@ var init_connect_script = __esm({
231645
231796
  }
231646
231797
  });
231647
231798
 
231799
+ // ../server/src/domains/nodes/runtime-activity.ts
231800
+ function isRuntimeRunReader(store) {
231801
+ const s2 = store;
231802
+ return !!s2 && typeof s2.runtimeRunActivity === "function" && typeof s2.listRuntimeRuns === "function";
231803
+ }
231804
+ function parseRuntimeUsageWindow(query, granularity, nowMs) {
231805
+ const toRaw = query.get("to");
231806
+ const fromRaw = query.get("from");
231807
+ const toMs = toRaw ? Date.parse(toRaw) : nowMs;
231808
+ if (!Number.isFinite(toMs)) return "to \u4E0D\u662F\u5408\u6CD5\u7684 ISO \u65F6\u95F4";
231809
+ const fromMs = fromRaw ? Date.parse(fromRaw) : toMs - DEFAULT_WINDOW_MS[granularity];
231810
+ if (!Number.isFinite(fromMs)) return "from \u4E0D\u662F\u5408\u6CD5\u7684 ISO \u65F6\u95F4";
231811
+ if (fromMs > toMs) return "from \u4E0D\u80FD\u665A\u4E8E to";
231812
+ if (toMs - fromMs > MAX_WINDOW_MS) return "\u65F6\u95F4\u7A97\u6700\u957F 92 \u5929";
231813
+ return { from: new Date(fromMs).toISOString(), to: new Date(toMs).toISOString() };
231814
+ }
231815
+ function runtimeUsageBucket(startedAt, granularity) {
231816
+ const ms = Date.parse(startedAt);
231817
+ if (!Number.isFinite(ms)) return null;
231818
+ const d = new Date(ms);
231819
+ if (granularity === "day") return d.toISOString().slice(0, 10);
231820
+ return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours())).toISOString();
231821
+ }
231822
+ function bucketRuntimeRuns(runtimeInstanceId, runs, granularity) {
231823
+ const rows = /* @__PURE__ */ new Map();
231824
+ for (const run of runs) {
231825
+ const bucket = runtimeUsageBucket(run.startedAt, granularity);
231826
+ if (!bucket) continue;
231827
+ const model = resolvedModel(run.effectiveModel);
231828
+ const key = `${bucket}\0${run.actorId}\0${model ?? ""}`;
231829
+ let row = rows.get(key);
231830
+ if (!row) {
231831
+ row = {
231832
+ actorId: run.actorId,
231833
+ runtimeInstanceId,
231834
+ runtimeKind: run.runtimeKind,
231835
+ effectiveModel: model,
231836
+ bucket,
231837
+ runCount: 0,
231838
+ inputTokens: 0,
231839
+ outputTokens: 0,
231840
+ totalTokens: 0,
231841
+ cacheReadTokens: 0,
231842
+ cacheCreationTokens: 0,
231843
+ costUsdMicros: 0
231844
+ };
231845
+ rows.set(key, row);
231846
+ }
231847
+ row.runCount += 1;
231848
+ const usage = run.usage;
231849
+ if (!usage) continue;
231850
+ const input = nonNegative2(usage.inputTokens);
231851
+ const output = nonNegative2(usage.outputTokens);
231852
+ row.inputTokens += input;
231853
+ row.outputTokens += output;
231854
+ row.totalTokens += input + output;
231855
+ row.cacheReadTokens += nonNegative2(usage.cacheReadTokens);
231856
+ row.cacheCreationTokens += nonNegative2(usage.cacheCreationTokens);
231857
+ const account = accountUsage(model, usage);
231858
+ row.costUsdMicros += account ? account.displayUsdMicros : nonNegative2(usage.costUsdMicros);
231859
+ }
231860
+ return [...rows.values()].sort((a, b2) => {
231861
+ if (a.bucket !== b2.bucket) return a.bucket < b2.bucket ? 1 : -1;
231862
+ if (a.actorId !== b2.actorId) return a.actorId < b2.actorId ? -1 : 1;
231863
+ return (a.effectiveModel ?? "").localeCompare(b2.effectiveModel ?? "");
231864
+ });
231865
+ }
231866
+ var DEFAULT_WINDOW_MS, MAX_WINDOW_MS, nonNegative2;
231867
+ var init_runtime_activity = __esm({
231868
+ "../server/src/domains/nodes/runtime-activity.ts"() {
231869
+ "use strict";
231870
+ init_accounting();
231871
+ DEFAULT_WINDOW_MS = { hour: 24 * 36e5, day: 30 * 864e5 };
231872
+ MAX_WINDOW_MS = 92 * 864e5;
231873
+ nonNegative2 = (v2) => Math.max(v2 ?? 0, 0);
231874
+ }
231875
+ });
231876
+
231648
231877
  // ../server/src/node-health.ts
231649
231878
  function deriveNodeHealth(input) {
231650
231879
  const staleMs = input.staleMs ?? 9e4;
@@ -231944,6 +232173,14 @@ function nodesDomain(deps) {
231944
232173
  body: { error: "ORG_MANAGER_REQUIRED", message: `${verb}\u4EC5\u7EC4\u7EC7\u7BA1\u7406\u8005\u53EF\u64CD\u4F5C\uFF0C\u8BF7\u8054\u7CFB\u7EC4\u7EC7\u7BA1\u7406\u5458` }
231945
232174
  };
231946
232175
  }
232176
+ async function runtimeInScope(req) {
232177
+ const runtimeId = req.params["runtimeId"];
232178
+ if (!runtimeId) return { denied: { status: 400, body: { error: "missing runtimeId" } } };
232179
+ const scope = req.auth.companyId !== void 0 ? { companyId: req.auth.companyId } : void 0;
232180
+ const rt = (await deps.nodeStore.listRuntimes(void 0, scope)).find((r) => r.id === runtimeId);
232181
+ if (!rt) return { denied: { status: 404, body: { error: "runtime not found" } } };
232182
+ return { runtimeId };
232183
+ }
231947
232184
  async function syncConnectedDaemon(daemonId, hub) {
231948
232185
  const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
231949
232186
  if (!d) return;
@@ -232247,6 +232484,23 @@ function nodesDomain(deps) {
232247
232484
  await deps.registry.upsertRuntimeModelOverride(override);
232248
232485
  return { status: 200, body: { override } };
232249
232486
  });
232487
+ router.get("/api/runtimes/:runtimeId/activity", async (req) => {
232488
+ const found = await runtimeInScope(req);
232489
+ if (found.denied) return found.denied;
232490
+ const reader = await deps.runtimeRuns?.(req.auth.companyId);
232491
+ const activity = reader ? await reader.runtimeRunActivity(found.runtimeId) : null;
232492
+ return { status: 200, body: { activity } };
232493
+ });
232494
+ router.get("/api/runtimes/:runtimeId/usage", async (req) => {
232495
+ const found = await runtimeInScope(req);
232496
+ if (found.denied) return found.denied;
232497
+ const granularity = req.query.get("granularity") === "day" ? "day" : "hour";
232498
+ const window2 = parseRuntimeUsageWindow(req.query, granularity, Date.now());
232499
+ if (typeof window2 === "string") return { status: 400, body: { error: window2 } };
232500
+ const reader = await deps.runtimeRuns?.(req.auth.companyId);
232501
+ const runs = reader ? await reader.listRuntimeRuns(found.runtimeId, window2) : null;
232502
+ return { status: 200, body: { items: bucketRuntimeRuns(found.runtimeId, runs ?? [], granularity) } };
232503
+ });
232250
232504
  router.get("/api/runtimes/:runtimeId/config", async (req) => {
232251
232505
  const runtimeId = req.params["runtimeId"];
232252
232506
  if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
@@ -232295,6 +232549,7 @@ var init_routes7 = __esm({
232295
232549
  init_src();
232296
232550
  init_src6();
232297
232551
  init_connect_script();
232552
+ init_runtime_activity();
232298
232553
  init_node_health();
232299
232554
  }
232300
232555
  });
@@ -242323,7 +242578,10 @@ function rowToVariable(row) {
242323
242578
  // 创建者 / 创建时间 / 更新者:2026-09-10 之前的行没有 → 不带这一格,展示侧出「—」。
242324
242579
  ...row.created_by ? { createdBy: row.created_by } : {},
242325
242580
  ...row.created_at ? { createdAt: row.created_at } : {},
242326
- ...row.updated_by ? { updatedBy: row.updated_by } : {}
242581
+ ...row.updated_by ? { updatedBy: row.updated_by } : {},
242582
+ // 决策 0556:NULL(存量行)不带出去——读侧按「全员生效」理解,与决策之前一致。
242583
+ ...row.audience === "all" || row.audience === "selected" ? { audience: row.audience } : {},
242584
+ ...Array.isArray(row.enabled_actor_ids) ? { enabledActorIds: row.enabled_actor_ids } : {}
242327
242585
  };
242328
242586
  }
242329
242587
  var ident4, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
@@ -242498,6 +242756,8 @@ var init_postgres_registry = __esm({
242498
242756
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_by text`);
242499
242757
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS created_at text`);
242500
242758
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS updated_by text`);
242759
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS audience text`);
242760
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS enabled_actor_ids text[]`);
242501
242761
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
242502
242762
  await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
242503
242763
  await pool.query(`
@@ -243003,14 +243263,18 @@ var init_postgres_registry = __esm({
243003
243263
  /* created_by / created_at 走 COALESCE(**旧值**, 新值):**第一次落行才写**,之后每次 upsert
243004
243264
  都保留原值——否则「谁建的」会被最后一次改的人顶掉,这一格就没有意义了。
243005
243265
  updated_by 反过来:每次都覆盖。三者与 name 的 COALESCE 方向相反,别照抄。 */
243006
- `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)
243007
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
243266
+ /* audience / enabled_actor_ids(决策 0556)走 COALESCE(**新值**, 旧值):不传 = 不动——
243267
+ 改默认值时手上不带「发给谁」,普通赋值会把「仅指定 Agent」悄悄打回全员生效。 */
243268
+ `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, audience, enabled_actor_ids)
243269
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
243008
243270
  ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
243009
243271
  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,
243010
243272
  name=COALESCE($11, ${this.s}.variables.name),
243011
243273
  created_by=COALESCE(${this.s}.variables.created_by, $12),
243012
243274
  created_at=COALESCE(${this.s}.variables.created_at, $13),
243013
- updated_by=$14`,
243275
+ updated_by=$14,
243276
+ audience=COALESCE($15, ${this.s}.variables.audience),
243277
+ enabled_actor_ids=COALESCE($16, ${this.s}.variables.enabled_actor_ids)`,
243014
243278
  [
243015
243279
  v2.key,
243016
243280
  v2.scope,
@@ -243025,7 +243289,9 @@ var init_postgres_registry = __esm({
243025
243289
  v2.name ?? null,
243026
243290
  v2.createdBy ?? null,
243027
243291
  v2.createdAt ?? null,
243028
- v2.updatedBy ?? null
243292
+ v2.updatedBy ?? null,
243293
+ v2.audience ?? null,
243294
+ v2.enabledActorIds ?? null
243029
243295
  ]
243030
243296
  );
243031
243297
  }
@@ -243044,6 +243310,34 @@ var init_postgres_registry = __esm({
243044
243310
  [key, aid, valueEncrypted, updatedAt]
243045
243311
  );
243046
243312
  }
243313
+ async patchVariableMeta(key, actorId, patch) {
243314
+ const r = await this.pool.query(
243315
+ `UPDATE ${this.s}.variables SET
243316
+ audience = CASE WHEN $3::boolean THEN $4::text ELSE audience END,
243317
+ enabled_actor_ids = CASE WHEN $5::boolean THEN $6::text[] ELSE enabled_actor_ids END,
243318
+ delivery_mode = CASE WHEN $7::boolean THEN $8::text ELSE delivery_mode END,
243319
+ expires_at = CASE WHEN $9::boolean THEN $10::text ELSE expires_at END,
243320
+ updated_by = CASE WHEN $11::boolean THEN $12::text ELSE updated_by END,
243321
+ updated_at = $13
243322
+ WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'') AND COALESCE(project_id,'')=''`,
243323
+ [
243324
+ key,
243325
+ actorId ?? null,
243326
+ patch.audience !== void 0,
243327
+ patch.audience ?? null,
243328
+ patch.enabledActorIds !== void 0,
243329
+ patch.enabledActorIds ?? null,
243330
+ patch.deliveryMode !== void 0,
243331
+ patch.deliveryMode ?? null,
243332
+ patch.expiresAt !== void 0,
243333
+ patch.expiresAt ?? null,
243334
+ patch.updatedBy !== void 0,
243335
+ patch.updatedBy ?? null,
243336
+ patch.updatedAt
243337
+ ]
243338
+ );
243339
+ return (r.rowCount ?? 0) > 0;
243340
+ }
243047
243341
  async getVariableCiphertext(key, actorId) {
243048
243342
  const aid = actorId ?? null;
243049
243343
  const r = await this.pool.query(
@@ -243056,7 +243350,14 @@ var init_postgres_registry = __esm({
243056
243350
  let r;
243057
243351
  if (actorId !== void 0) {
243058
243352
  r = await this.pool.query(
243059
- `SELECT * FROM ${this.s}.variables WHERE scope='global' OR actor_id=$1 ORDER BY key`,
243353
+ /* 决策 0556:`audience='selected'` 的全局行**只给 enabled_actor_ids 里那几位**。
243354
+ 注入(resolveActorEnv / resolveActorProvisionEntries)、agent 侧 reveal、组织页 Agent 的密钥列表
243355
+ 全都读这一条查询——语义只改在这一处,别在上层各自再滤一遍(滤漏一处就是泄漏)。
243356
+ audience 为 NULL(存量行)或 'all' 时行为与决策之前逐字一致。 */
243357
+ `SELECT * FROM ${this.s}.variables
243358
+ WHERE actor_id=$1
243359
+ OR (scope='global' AND (audience IS DISTINCT FROM 'selected' OR $1 = ANY(COALESCE(enabled_actor_ids, '{}'::text[]))))
243360
+ ORDER BY key`,
243060
243361
  [actorId]
243061
243362
  );
243062
243363
  } else {
@@ -245533,6 +245834,7 @@ var init_postgres_trace = __esm({
245533
245834
  AND left(job_key, char_length('produce::' || node_id || '::')) = 'produce::' || node_id || '::'`);
245534
245835
  await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_effective_model_idx ON "${s2}".agent_runs (effective_model)`);
245535
245836
  await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_actor_started_idx ON "${s2}".agent_runs (actor_id, started_at DESC)`);
245837
+ await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_runtime_started_idx ON "${s2}".agent_runs (runtime_instance_id, started_at DESC)`);
245536
245838
  await pool.query(`CREATE INDEX IF NOT EXISTS agent_runs_workorder_idx ON "${s2}".agent_runs (work_order_id, node_id, attempt_no DESC)`);
245537
245839
  await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS agent_runs_node_attempt_uq ON "${s2}".agent_runs (node_id, attempt_no) WHERE node_id IS NOT NULL AND attempt_no IS NOT NULL`);
245538
245840
  await pool.query(`
@@ -246219,6 +246521,41 @@ var init_postgres_trace = __esm({
246219
246521
  usage: row.usage
246220
246522
  }));
246221
246523
  }
246524
+ /* ---------- 运行时详情页:按实例直接读 run 行 ---------- */
246525
+ /**
246526
+ * 这个运行时实例上一共跑过几轮、最近一轮什么时候开跑。
246527
+ *
246528
+ * 读 `agent_runs` 而不是 `agent_usage_*` 聚合表:chat、工单节点(produce / integrate / review)、
246529
+ * 协调者每一轮**开跑时**就先落一行 run,所以开跑即计入;聚合表只在 run 结束且带 usage 时才 +1,
246530
+ * 失败 / 取消没 usage 的轮次永远不算。状态不过滤——每一轮都是一次运行。
246531
+ */
246532
+ async runtimeRunActivity(runtimeInstanceId) {
246533
+ const r = await this.pool.query(
246534
+ `SELECT count(*)::int AS run_count, max(started_at) AS last_started_at
246535
+ FROM ${this.s}.agent_runs WHERE runtime_instance_id = $1`,
246536
+ [runtimeInstanceId]
246537
+ );
246538
+ const row = r.rows[0] ?? {};
246539
+ return { runCount: Number(row.run_count ?? 0), lastRunStartedAt: row.last_started_at ?? null };
246540
+ }
246541
+ /** 该实例在时间窗内开跑的全部 run(含还没有 usage 的在途 / 失败轮次),按开跑时刻正序。 */
246542
+ async listRuntimeRuns(runtimeInstanceId, range) {
246543
+ const r = await this.pool.query(
246544
+ `SELECT actor_id, runtime_kind, effective_model, started_at, usage
246545
+ FROM ${this.s}.agent_runs
246546
+ WHERE runtime_instance_id = $1 AND started_at >= $2 AND started_at <= $3
246547
+ ORDER BY started_at`,
246548
+ [runtimeInstanceId, range.from, range.to]
246549
+ );
246550
+ return r.rows.map((row) => ({
246551
+ actorId: row.actor_id,
246552
+ runtimeKind: row.runtime_kind ?? "shell",
246553
+ effectiveModel: row.effective_model ?? null,
246554
+ startedAt: row.started_at,
246555
+ // 历史行里有 JSONB 字面量 null,与 SQL NULL 一样当「还没有 usage」。
246556
+ usage: row.usage && typeof row.usage === "object" ? row.usage : null
246557
+ }));
246558
+ }
246222
246559
  /* ---------- execution continuity ---------- */
246223
246560
  async getAttempt(attemptId) {
246224
246561
  return this.getRun(attemptId);
@@ -254234,6 +254571,9 @@ var init_cutover_aware_registry = __esm({
254234
254571
  async patchVariableValue(...args) {
254235
254572
  return (await this.orgStoreForWrite()).patchVariableValue(...args);
254236
254573
  }
254574
+ async patchVariableMeta(...args) {
254575
+ return (await this.orgStoreForWrite()).patchVariableMeta(...args);
254576
+ }
254237
254577
  async getVariableCiphertext(...args) {
254238
254578
  return (await this.orgStore()).getVariableCiphertext(...args);
254239
254579
  }
@@ -254615,6 +254955,15 @@ var init_cutover_aware_trace = __esm({
254615
254955
  async listUsageRuns(filter) {
254616
254956
  return (await this.pick()).listUsageRuns(filter);
254617
254957
  }
254958
+ // ── 运行时详情页的按实例读(不在 TraceStore 契约上:落点 store 没实现就当没有数据源)──────────
254959
+ async runtimeRunActivity(runtimeInstanceId) {
254960
+ const s2 = await this.pick();
254961
+ return typeof s2.runtimeRunActivity === "function" ? s2.runtimeRunActivity(runtimeInstanceId) : null;
254962
+ }
254963
+ async listRuntimeRuns(runtimeInstanceId, range) {
254964
+ const s2 = await this.pick();
254965
+ return typeof s2.listRuntimeRuns === "function" ? s2.listRuntimeRuns(runtimeInstanceId, range) : null;
254966
+ }
254618
254967
  // ── 事件 / 工具调用 / 链接 ────────────────────────────────────────────────
254619
254968
  async appendEvent(runId, event) {
254620
254969
  return (await this.pickWritable()).appendEvent(runId, event);
@@ -258704,7 +259053,7 @@ var init_run_settlement_wiring = __esm({
258704
259053
  });
258705
259054
 
258706
259055
  // ../server/src/deadline-clock.ts
258707
- async function deadlineClockTick(deps, dedupe) {
259056
+ async function deadlineClockTick(deps, dedupe2) {
258708
259057
  const limit = deps.batchLimit ?? DEADLINE_CLOCK_BATCH_LIMIT;
258709
259058
  const lookahead = deps.lookaheadMs ?? DEADLINE_CLOCK_LOOKAHEAD_MS;
258710
259059
  const policy = deps.policy();
@@ -258733,14 +259082,14 @@ async function deadlineClockTick(deps, dedupe) {
258733
259082
  if (overdue > result.oldestDueDelayMs) result.oldestDueDelayMs = overdue;
258734
259083
  const phase = overdue >= 0 ? "due" : "early";
258735
259084
  const bucket = `${c.workId}@${deadline}@${phase}`;
258736
- if (dedupe.has(c.workorderId, bucket)) {
259085
+ if (dedupe2.has(c.workorderId, bucket)) {
258737
259086
  result.deduped++;
258738
259087
  continue;
258739
259088
  }
258740
259089
  const buckets = toPing.get(c.workorderId) ?? [];
258741
259090
  buckets.push(bucket);
258742
259091
  toPing.set(c.workorderId, buckets);
258743
- dedupe.mark(c.workorderId, bucket);
259092
+ dedupe2.mark(c.workorderId, bucket);
258744
259093
  }
258745
259094
  if (page.nextCursor) result.backlog = toPing.size + result.deduped;
258746
259095
  for (const [workorderId, buckets] of toPing) {
@@ -258749,7 +259098,7 @@ async function deadlineClockTick(deps, dedupe) {
258749
259098
  result.pinged++;
258750
259099
  } catch (err) {
258751
259100
  result.failed++;
258752
- for (const b2 of buckets) dedupe.unmark(workorderId, b2);
259101
+ for (const b2 of buckets) dedupe2.unmark(workorderId, b2);
258753
259102
  deps.log?.(`[deadline-clock] ping ${workorderId} \u5931\u8D25: ${String(err)}`);
258754
259103
  }
258755
259104
  }
@@ -258913,6 +259262,7 @@ var init_src11 = __esm({
258913
259262
  init_auth();
258914
259263
  init_sender();
258915
259264
  init_routes7();
259265
+ init_runtime_activity();
258916
259266
  init_runtime_drift();
258917
259267
  init_runtime_tenancy();
258918
259268
  init_types4();
@@ -259278,12 +259628,12 @@ ${detail}`.slice(0, 4200) } : {}
259278
259628
  init_src11();
259279
259629
  init_src();
259280
259630
  var BRAINSTORM_LEGACY_LINE = "\u8BF7\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u95EE\u9898\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002";
259281
- var BRAINSTORM_ARTIFACT_GUARD = "\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u521B\u5EFA\u6216\u4FEE\u6539 artifact\u3002";
259631
+ var BRAINSTORM_ARTIFACT_GUARD = "\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u5165\u7CFB\u7EDF\uFF0C\u5426\u5219\u4E0D\u8981\u76F4\u63A5\u521B\u5EFA\u6216\u4FEE\u6539 artifact\uFF1B\u5DE5\u5355\u8349\u6848\u8981\u7528\u6237\u786E\u8BA4\u624D\u751F\u6548\uFF0C\u4E0D\u5728\u6B64\u5217\u3002";
259282
259632
  var OUTPUTS_CONVENTION = [
259283
259633
  "## \u4EA7\u51FA\u653E\u54EA",
259284
259634
  "",
259285
259635
  "\u8981\u7ED9\u7528\u6237\u770B\u7684\u6210\u54C1\u6587\u4EF6\uFF0C**\u653E\u8FDB\u5DE5\u4F5C\u76EE\u5F55\u4E0B\u7684 `outputs/`**\uFF08\u53EF\u4EE5\u6709\u5B50\u76EE\u5F55\uFF0C\u7ED3\u6784\u81EA\u5DF1\u5B9A\uFF09\u3002",
259286
- "\u53EA\u6709\u8FD9\u4E2A\u76EE\u5F55\u91CC\u7684\u6587\u4EF6\u4F1A\u5728\u5BF9\u8BDD\u91CC\u663E\u793A\u6210\u53EF\u70B9\u5F00\u7684\u6587\u4EF6\u5361\u7247\uFF1B\u5199\u5728\u522B\u5904\u7684\u7528\u6237\u770B\u4E0D\u89C1\u3002",
259636
+ "\u53EA\u6709\u8FD9\u4E2A\u76EE\u5F55\u91CC\u7684\u6587\u4EF6\u4F1A\u5728\u5BF9\u8BDD\u91CC\u663E\u793A\u6210\u53EF\u70B9\u5F00\u7684\u6587\u4EF6\u5361\u7247\uFF1B\u5199\u5728\u522B\u5904\u7684\u4E0D\u51FA\u5361\uFF0C\u7528\u6237\u53EA\u80FD\u53BB\u53F3\u4FA7\u300C\u76F8\u5173\u6587\u4EF6\u300D\u5217\u8868\u91CC\u81EA\u5DF1\u7FFB\u3002",
259287
259637
  "\u4E2D\u95F4\u4EA7\u7269\u3001\u4E34\u65F6\u811A\u672C\u3001\u8349\u7A3F\u4E0D\u8981\u653E\u8FD9\u91CC\u2014\u2014\u653E\u8FDB\u53BB\u5C31\u4F1A\u6446\u5230\u7528\u6237\u9762\u524D\u3002"
259288
259638
  ].join("\n");
259289
259639
  function buildChatTaskHeader(input) {
@@ -261728,6 +262078,11 @@ async function startServe(opts) {
261728
262078
  }) : dispatchLedger;
261729
262079
  const companyRegistryFor = async (companyId) => pgPool ? (await openCompanyPlane(companyId)).overlay : scopedRegistryFor(companyId);
261730
262080
  const defaultRegistry = await companyRegistryFor(defaultCompanyId);
262081
+ const companyLiveTypeDefs = async (companyId) => {
262082
+ if (!pgPool || !cutoverStore) return null;
262083
+ if (!cutoverStore.readsCompanySchema(await cutoverStore.get(companyId, "types"))) return null;
262084
+ return buildLiveSchema(await (await openCompanyPlane(companyId)).types.list());
262085
+ };
261731
262086
  const engineRouter = new CompanyEngineRouter({
261732
262087
  // ★ 字节柜按公司取:`gc` 用的就是 `engine.blobs`,共享柜 + 单公司可达集 = 删光别家。
261733
262088
  defaultEngine: { kernel: defaultCompanyKernel, oplog, blobs: await blobStoreFor(defaultCompanyId), registry: defaultRegistry, assets },
@@ -261743,6 +262098,7 @@ async function startServe(opts) {
261743
262098
  // ★ 同默认公司:漏了这里,非默认公司的引擎照旧按代码默认 30min 判超时(策略文件形同虚设)。
261744
262099
  policy: () => activeTimeoutPolicy
261745
262100
  });
262101
+ for (const d of await companyLiveTypeDefs(companyId) ?? []) engine2.kernel.setType(d);
261746
262102
  const registry2 = await companyRegistryFor(companyId);
261747
262103
  await ensureCompanyActors(controlPlaneStore, companyId, registry2);
261748
262104
  await ensureDefaultExpertTeam(registry2);
@@ -262279,7 +262635,7 @@ async function startServe(opts) {
262279
262635
  () => [engine2.kernel],
262280
262636
  async () => {
262281
262637
  if (primary) {
262282
- for (const d of await store.list()) engine2.kernel.setType(d);
262638
+ for (const d of await companyLiveTypeDefs(cid) ?? []) engine2.kernel.setType(d);
262283
262639
  return;
262284
262640
  }
262285
262641
  await rebuildLiveSchema();
@@ -262310,6 +262666,12 @@ async function startServe(opts) {
262310
262666
  resolveMemberRole: async (companyId, actorId) => {
262311
262667
  const members = await listCompanyMembers(companyId);
262312
262668
  return members.find((m2) => m2.actorId === actorId)?.role;
262669
+ },
262670
+ // 详情页「运行次数 / 最近工作时间」与用量 tab 按实例读 run 行:落到这家公司 trace 当前的读落点
262671
+ //(切走了读公司 schema,没切走读 legacy)。dev 文件态的 FileTraceStore 同样实现了这两个读。
262672
+ runtimeRuns: async (companyId) => {
262673
+ const store = await traceStoreFor(companyId);
262674
+ return isRuntimeRunReader(store) ? store : null;
262313
262675
  }
262314
262676
  }),
262315
262677
  collabDomain({
@@ -264925,7 +265287,7 @@ async function startServe(opts) {
264925
265287
  } else if (!deadlineStoreOf(newEngine.kernel)?.listOpenWorkDeadlines) {
264926
265288
  console.warn("[deadline-clock] engine store \u672A\u5B9E\u73B0 listOpenWorkDeadlines\uFF0C\u65F6\u949F\u672A\u542F\u52A8\u2014\u2014deadline \u65E0\u6700\u8FDF\u89E6\u53D1\u4FDD\u8BC1");
264927
265289
  } else {
264928
- const dedupe = new DeadlineDedupe();
265290
+ const dedupe2 = new DeadlineDedupe();
264929
265291
  const clockCursors = /* @__PURE__ */ new Map();
264930
265292
  let clockTicking = false;
264931
265293
  void (async () => {
@@ -264975,7 +265337,7 @@ async function startServe(opts) {
264975
265337
  batchLimit: DEADLINE_CLOCK_BATCH_LIMIT,
264976
265338
  ...cursor ? { cursor } : {},
264977
265339
  log: (m2) => console.warn(m2)
264978
- }, dedupe);
265340
+ }, dedupe2);
264979
265341
  clockCursors.set(companyId, r.nextCursor);
264980
265342
  if (r.pinged > 0) {
264981
265343
  console.log(`[deadline-clock] [${companyId}] \u6295\u51FA ${r.pinged} \u4E2A workorder.ping\uFF08\u53BB\u91CD ${r.deduped}\u3001\u8C41\u514D ${r.exempt}\uFF09`);
@@ -270610,7 +270972,7 @@ ${round.text}`);
270610
270972
  break;
270611
270973
  }
270612
270974
  println(`\u5DF2\u4EA4\u7ED9 ${to} \u53BB\u505A\uFF08\u5F02\u6B65\uFF0C\u4E0D\u7528\u7B49\uFF09\u3002`);
270613
- println(` \u77ED\u540D\uFF08--continue \u7528\u8FD9\u4E2A\uFF09\uFF1A${started.label}`);
270975
+ println(` \u77ED\u540D\uFF08--follow-up \u7528\u8FD9\u4E2A\uFF09\uFF1A${started.label}`);
270614
270976
  println(` delegationId\uFF1A${started.delegationId}`);
270615
270977
  if (started.attachedFiles?.length) println(` \u5E26\u8FC7\u53BB\u7684\u6587\u4EF6\uFF1A${started.attachedFiles.join("\u3001")}`);
270616
270978
  println("\u7ED3\u679C\u5E72\u5B8C\u4E86\u4F1A\u81EA\u5DF1\u56DE\u5230\u8FD9\u6761\u5BF9\u8BDD\u3002\u8FD9\u4E00\u8F6E\u5148\u6536\u5C3E\u2014\u2014\u53EF\u4EE5\u987A\u624B\u8DDF\u7528\u6237\u8BF4\u4E00\u53E5\u4F60\u628A\u8FD9\u4EF6\u4E8B\u6D3E\u51FA\u53BB\u4E86\u3002");
@@ -273393,7 +273755,7 @@ function shimScript() {
273393
273755
  }
273394
273756
 
273395
273757
  // src/index.ts
273396
- var PKG_VERSION = true ? "2.2.27" : "dev";
273758
+ var PKG_VERSION = true ? "2.2.29" : "dev";
273397
273759
  var LOCAL_BIN = localBin();
273398
273760
  var NPM_PREFIX = npmPrefix();
273399
273761
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.27",
3
+ "version": "2.2.29",
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": "bab451bba29c8bbdea05ec83bdc381b8b6bcceeb"
29
+ "sourceHead": "68eff586a82e2fd668f109ffbdd672ebf77844ac"
30
30
  }
31
31
  }