oasis_test_v2 2.2.82 → 2.2.83

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 +976 -414
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -35188,7 +35188,7 @@ var require_websocket = __commonJS({
35188
35188
  var http2 = require("http");
35189
35189
  var net = require("net");
35190
35190
  var tls = require("tls");
35191
- var { randomBytes: randomBytes14, createHash: createHash45 } = require("crypto");
35191
+ var { randomBytes: randomBytes15, createHash: createHash45 } = require("crypto");
35192
35192
  var { Duplex, Readable } = require("stream");
35193
35193
  var { URL: URL2 } = require("url");
35194
35194
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -35726,7 +35726,7 @@ var require_websocket = __commonJS({
35726
35726
  }
35727
35727
  }
35728
35728
  const defaultPort = isSecure ? 443 : 80;
35729
- const key2 = randomBytes14(16).toString("base64");
35729
+ const key2 = randomBytes15(16).toString("base64");
35730
35730
  const request2 = isSecure ? https.request : http2.request;
35731
35731
  const protocolSet = /* @__PURE__ */ new Set();
35732
35732
  let perMessageDeflate;
@@ -130137,7 +130137,7 @@ var require_utils3 = __commonJS({
130137
130137
  var nodeCrypto = require("crypto");
130138
130138
  module2.exports = {
130139
130139
  postgresMd5PasswordHash,
130140
- randomBytes: randomBytes14,
130140
+ randomBytes: randomBytes15,
130141
130141
  deriveKey,
130142
130142
  sha256: sha2563,
130143
130143
  hashByName,
@@ -130147,7 +130147,7 @@ var require_utils3 = __commonJS({
130147
130147
  var webCrypto = nodeCrypto.webcrypto || globalThis.crypto;
130148
130148
  var subtleCrypto = webCrypto.subtle;
130149
130149
  var textEncoder = new TextEncoder();
130150
- function randomBytes14(length) {
130150
+ function randomBytes15(length) {
130151
130151
  return webCrypto.getRandomValues(Buffer.alloc(length));
130152
130152
  }
130153
130153
  async function md5(string7) {
@@ -208705,11 +208705,17 @@ var init_app_adopt = __esm({
208705
208705
  });
208706
208706
 
208707
208707
  // ../connectors/src/github/app-manifest.ts
208708
+ function randomAppNameSuffix() {
208709
+ const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
208710
+ return Array.from((0, import_node_crypto42.randomBytes)(4), (b2) => alphabet[b2 % alphabet.length]).join("");
208711
+ }
208708
208712
  function buildAppManifest(args) {
208709
208713
  const base = args.baseUrl.replace(/\/+$/, "");
208714
+ const suffix = args.nameSuffix ?? randomAppNameSuffix();
208715
+ const room = GITHUB_APP_NAME_MAX - args.namePrefix.length - suffix.length - 2;
208716
+ const middle = args.employeeSlug.slice(0, Math.max(0, room)).replace(/-+$/, "");
208710
208717
  return {
208711
- name: `${args.namePrefix}-${args.employeeSlug}`.slice(0, 34),
208712
- // GitHub App 名上限 34 字符
208718
+ name: [args.namePrefix, middle, suffix].filter(Boolean).join("-"),
208713
208719
  url: base,
208714
208720
  redirect_url: `${base}${args.redirectPath}`,
208715
208721
  public: false,
@@ -208787,10 +208793,11 @@ function checkAppOwnership(conv, want) {
208787
208793
  }
208788
208794
  return null;
208789
208795
  }
208790
- var GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, PENDING_TTL_MS, REQUIRED_WRITE_PERMISSIONS, PERMISSION_RANK, FORBIDDEN_IMPORT_PERMISSION_KEYS;
208796
+ var import_node_crypto42, GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, GITHUB_APP_NAME_MAX, PENDING_TTL_MS, REQUIRED_WRITE_PERMISSIONS, PERMISSION_RANK, FORBIDDEN_IMPORT_PERMISSION_KEYS;
208791
208797
  var init_app_manifest = __esm({
208792
208798
  "../connectors/src/github/app-manifest.ts"() {
208793
208799
  "use strict";
208800
+ import_node_crypto42 = require("node:crypto");
208794
208801
  GITHUB_APP_DEFAULT_PERMISSIONS = {
208795
208802
  contents: "write",
208796
208803
  // clone / push
@@ -208819,6 +208826,7 @@ var init_app_manifest = __esm({
208819
208826
  { key: "workflows", label: "\u5DE5\u4F5C\u6D41\u6587\u4EF6\uFF08\u8BFB\u5199\uFF09", desc: "\u4FEE\u6539 .github/workflows \u4E0B\u7684 CI \u914D\u7F6E" },
208820
208827
  { key: "issues", label: "Issue\uFF08\u8BFB\u5199\uFF09", desc: "\u521B\u5EFA\u4E0E\u8BC4\u8BBA Issue" }
208821
208828
  ];
208829
+ GITHUB_APP_NAME_MAX = 34;
208822
208830
  PENDING_TTL_MS = 15 * 60 * 1e3;
208823
208831
  REQUIRED_WRITE_PERMISSIONS = ["contents", "pull_requests"];
208824
208832
  PERMISSION_RANK = { read: 1, write: 2, admin: 3 };
@@ -208835,6 +208843,195 @@ var init_app_manifest = __esm({
208835
208843
  }
208836
208844
  });
208837
208845
 
208846
+ // ../connectors/src/github/app-inventory.ts
208847
+ function permissionVerdict(perms) {
208848
+ const missingRequired = REQUIRED_WRITE.filter((k2) => perms[k2] !== "write" && perms[k2] !== "admin");
208849
+ const excess = excessAppPermissions(perms);
208850
+ return {
208851
+ missingRequired: [...missingRequired],
208852
+ missing: missingAppPermissions(perms),
208853
+ excess,
208854
+ usable: missingRequired.length === 0 && excess.length === 0
208855
+ };
208856
+ }
208857
+ async function listOrgInstalledApps(args, deps = {}) {
208858
+ const d = { ...defaultDeps3, ...deps };
208859
+ let resp;
208860
+ try {
208861
+ resp = await d.fetch(`${GITHUB_API2}/orgs/${encodeURIComponent(args.org)}/installations?per_page=100`, {
208862
+ headers: {
208863
+ authorization: `Bearer ${args.token}`,
208864
+ accept: "application/vnd.github+json",
208865
+ "x-github-api-version": "2022-11-28"
208866
+ }
208867
+ });
208868
+ } catch (e) {
208869
+ return { status: "error", message: String(e) };
208870
+ }
208871
+ if (resp.status === 401 || resp.status === 403) return { status: "needsAuth", httpStatus: resp.status };
208872
+ const body2 = await resp.json().catch(() => ({}));
208873
+ if (!resp.ok) return { status: "error", message: `GitHub \u8FD4\u56DE HTTP ${resp.status} ${String(body2["message"] ?? "")}`.trim() };
208874
+ const list2 = Array.isArray(body2["installations"]) ? body2["installations"] : [];
208875
+ const all2 = list2.flatMap((raw) => {
208876
+ const item = raw;
208877
+ const appId = item["app_id"] === void 0 ? "" : String(item["app_id"]);
208878
+ const installationId = item["id"] === void 0 ? "" : String(item["id"]);
208879
+ if (!appId || !installationId) return [];
208880
+ const rawPerms = item["permissions"];
208881
+ const permissions = {};
208882
+ for (const [k2, v2] of Object.entries(rawPerms ?? {})) if (typeof v2 === "string") permissions[k2] = v2;
208883
+ return [{ appId, slug: String(item["app_slug"] ?? ""), installationId, org: args.org, permissions }];
208884
+ });
208885
+ const owned = await Promise.all(all2.map(async (app) => {
208886
+ if (!app.slug) return app;
208887
+ try {
208888
+ const r = await d.fetch(`${GITHUB_API2}/apps/${encodeURIComponent(app.slug)}`, {
208889
+ headers: {
208890
+ authorization: `Bearer ${args.token}`,
208891
+ accept: "application/vnd.github+json",
208892
+ "x-github-api-version": "2022-11-28"
208893
+ }
208894
+ });
208895
+ if (!r.ok) return app;
208896
+ const meta = await r.json().catch(() => ({}));
208897
+ const login = meta.owner?.login;
208898
+ return login && login.toLowerCase() !== args.org.toLowerCase() ? null : app;
208899
+ } catch {
208900
+ return app;
208901
+ }
208902
+ }));
208903
+ return { status: "ok", apps: owned.filter((a) => a !== null) };
208904
+ }
208905
+ async function uninstallAppInstallation(args, deps = {}) {
208906
+ const d = { ...defaultDeps3, ...deps };
208907
+ const jwt = createAppJwt(args.appId, args.privateKeyPem, d.nowMs());
208908
+ const resp = await d.fetch(`${GITHUB_API2}/app/installations/${encodeURIComponent(args.installationId)}`, {
208909
+ method: "DELETE",
208910
+ headers: {
208911
+ authorization: `Bearer ${jwt}`,
208912
+ accept: "application/vnd.github+json",
208913
+ "x-github-api-version": "2022-11-28"
208914
+ }
208915
+ });
208916
+ if (resp.status === 204 || resp.status === 404) return;
208917
+ const body2 = await resp.json().catch(() => ({}));
208918
+ throw new Error(`github app: uninstall failed HTTP ${resp.status} ${String(body2["message"] ?? "")}`.trim());
208919
+ }
208920
+ function appSettingsUrl(owner, slug6) {
208921
+ const s2 = encodeURIComponent(slug6);
208922
+ return owner.kind === "org" ? `https://github.com/organizations/${encodeURIComponent(owner.login)}/settings/apps/${s2}` : `https://github.com/settings/apps/${s2}`;
208923
+ }
208924
+ function appInstallPageUrl(slug6) {
208925
+ return `https://github.com/apps/${encodeURIComponent(slug6)}/installations/new`;
208926
+ }
208927
+ function appDeleteUrl(owner, slug6) {
208928
+ return `${appSettingsUrl(owner, slug6)}/advanced`;
208929
+ }
208930
+ function ownerMatches(app, owner) {
208931
+ if (owner.kind === "org") {
208932
+ return app.ownerType === "Organization" && app.ownerLogin.toLowerCase() === owner.login.toLowerCase();
208933
+ }
208934
+ if (app.ownerType !== "User") return false;
208935
+ return !owner.login || app.ownerLogin.toLowerCase() === owner.login.toLowerCase();
208936
+ }
208937
+ function buildReuseCandidates(args) {
208938
+ const { owner } = args;
208939
+ const known = new Set(args.oasisApps.map((a2) => a2.appId));
208940
+ const a = args.oasisApps.filter((app) => ownerMatches(app, owner)).map((app) => {
208941
+ const verdict = app.permissions ? permissionVerdict(app.permissions) : null;
208942
+ const selectable = verdict ? verdict.usable : true;
208943
+ const action = !selectable ? "blocked" : app.installationIds.length === 0 ? "install" : "bind";
208944
+ return {
208945
+ kind: "oasis",
208946
+ action,
208947
+ appId: app.appId,
208948
+ slug: app.slug,
208949
+ ...app.installationIds[0] ? { installationId: app.installationIds[0] } : {},
208950
+ usedBy: app.usedBy,
208951
+ isOrgDefault: app.isOrgDefault,
208952
+ permissionsUnknown: !verdict,
208953
+ verdict,
208954
+ selectable,
208955
+ settingsUrl: appSettingsUrl(owner, app.slug),
208956
+ permissionsUrl: `${appSettingsUrl(owner, app.slug)}/permissions`,
208957
+ installUrl: appInstallPageUrl(app.slug)
208958
+ };
208959
+ });
208960
+ const b2 = owner.kind === "org" ? args.installed.filter((i) => !known.has(i.appId) && i.org.toLowerCase() === owner.login.toLowerCase()).map((i) => {
208961
+ const verdict = permissionVerdict(i.permissions);
208962
+ return {
208963
+ kind: "installed",
208964
+ action: verdict.usable ? "import" : "blocked",
208965
+ appId: i.appId,
208966
+ slug: i.slug,
208967
+ installationId: i.installationId,
208968
+ usedBy: [],
208969
+ isOrgDefault: false,
208970
+ permissionsUnknown: false,
208971
+ verdict,
208972
+ selectable: verdict.usable,
208973
+ settingsUrl: appSettingsUrl(owner, i.slug),
208974
+ permissionsUrl: `${appSettingsUrl(owner, i.slug)}/permissions`,
208975
+ installUrl: appInstallPageUrl(i.slug)
208976
+ };
208977
+ }) : [];
208978
+ return [...a, ...b2].sort((x2, y) => Number(y.selectable) - Number(x2.selectable));
208979
+ }
208980
+ function buildAppInventory(args) {
208981
+ const rows = /* @__PURE__ */ new Map();
208982
+ const ownerOf = (login, type) => !login ? null : type === "Organization" ? { kind: "org", login } : { kind: "user", login };
208983
+ for (const app of args.oasisApps) {
208984
+ const inUse = app.isOrgDefault || app.usedBy.length > 0;
208985
+ const owner = ownerOf(app.ownerLogin, app.ownerType);
208986
+ rows.set(app.appId, {
208987
+ appId: app.appId,
208988
+ slug: app.slug,
208989
+ ownerLogin: app.ownerLogin,
208990
+ hasKey: true,
208991
+ usedBy: app.usedBy,
208992
+ isOrgDefault: app.isOrgDefault,
208993
+ inUse,
208994
+ installationIds: [...app.installationIds],
208995
+ canUninstall: !inUse && app.installationIds.length > 0,
208996
+ deleteUrl: owner && app.slug ? appDeleteUrl(owner, app.slug) : ""
208997
+ });
208998
+ }
208999
+ for (const i of args.installed) {
209000
+ const prev = rows.get(i.appId);
209001
+ if (prev) {
209002
+ if (!prev.installationIds.includes(i.installationId)) prev.installationIds.push(i.installationId);
209003
+ prev.canUninstall = !prev.inUse && prev.installationIds.length > 0;
209004
+ if (!prev.ownerLogin) prev.ownerLogin = i.org;
209005
+ if (!prev.deleteUrl && prev.slug) prev.deleteUrl = appDeleteUrl({ kind: "org", login: i.org }, prev.slug);
209006
+ continue;
209007
+ }
209008
+ rows.set(i.appId, {
209009
+ appId: i.appId,
209010
+ slug: i.slug,
209011
+ ownerLogin: i.org,
209012
+ hasKey: false,
209013
+ usedBy: [],
209014
+ isOrgDefault: false,
209015
+ inUse: false,
209016
+ installationIds: [i.installationId],
209017
+ canUninstall: false,
209018
+ deleteUrl: i.slug ? appDeleteUrl({ kind: "org", login: i.org }, i.slug) : ""
209019
+ });
209020
+ }
209021
+ return [...rows.values()].sort((x2, y) => Number(x2.inUse) - Number(y.inUse) || x2.slug.localeCompare(y.slug));
209022
+ }
209023
+ var GITHUB_API2, defaultDeps3, REQUIRED_WRITE;
209024
+ var init_app_inventory = __esm({
209025
+ "../connectors/src/github/app-inventory.ts"() {
209026
+ "use strict";
209027
+ init_app_auth();
209028
+ init_app_manifest();
209029
+ GITHUB_API2 = "https://api.github.com";
209030
+ defaultDeps3 = { fetch: (...a) => globalThis.fetch(...a), nowMs: () => Date.now() };
209031
+ REQUIRED_WRITE = ["contents", "pull_requests"];
209032
+ }
209033
+ });
209034
+
208838
209035
  // ../connectors/src/registry.ts
208839
209036
  function createConnectorBySlug(slug6) {
208840
209037
  switch (slug6) {
@@ -209291,6 +209488,7 @@ var init_src8 = __esm({
209291
209488
  init_app_auth();
209292
209489
  init_app_adopt();
209293
209490
  init_app_manifest();
209491
+ init_app_inventory();
209294
209492
  init_registry2();
209295
209493
  init_provision();
209296
209494
  init_prepare_job();
@@ -209299,6 +209497,118 @@ var init_src8 = __esm({
209299
209497
  }
209300
209498
  });
209301
209499
 
209500
+ // ../server/src/github-app-inventory.ts
209501
+ async function collectOasisGithubApps(svc) {
209502
+ const rows = (await svc.listVariables()).filter((r) => r.scope === "global" || r.scope === "personal");
209503
+ const agents = await svc.listActors({ kind: "agent", status: "active" });
209504
+ const nameOf2 = new Map(agents.map((a) => [a.id, a.name || a.id]));
209505
+ const plain = (key2, actorId) => rows.find((r) => r.key === key2 && r.actorId === actorId && !r.encrypted)?.maskedValue ?? "";
209506
+ const byAppId = /* @__PURE__ */ new Map();
209507
+ for (const r of rows) {
209508
+ if (r.key !== "GITHUB_APP_ID" || r.encrypted || !r.maskedValue) continue;
209509
+ const e = byAppId.get(r.maskedValue) ?? {
209510
+ slug: "",
209511
+ installationId: "",
209512
+ keyActorId: void 0,
209513
+ usedBy: [],
209514
+ isOrgDefault: false,
209515
+ sources: []
209516
+ };
209517
+ e.sources.push(r.actorId);
209518
+ if (!r.actorId) e.isOrgDefault = true;
209519
+ else if (nameOf2.has(r.actorId) && !e.usedBy.includes(r.actorId)) e.usedBy.push(r.actorId);
209520
+ byAppId.set(r.maskedValue, e);
209521
+ }
209522
+ const out = await Promise.all([...byAppId.entries()].map(async ([appId, e]) => {
209523
+ let pem = null;
209524
+ let keyActorId;
209525
+ for (const src of [...e.sources].sort((a, b2) => Number(Boolean(a)) - Number(Boolean(b2)))) {
209526
+ pem = await svc.revealVariable("GITHUB_APP_PRIVATE_KEY", src).catch(() => null);
209527
+ if (pem) {
209528
+ keyActorId = src;
209529
+ break;
209530
+ }
209531
+ }
209532
+ if (!pem) return null;
209533
+ const slug6 = plain("GITHUB_APP_SLUG", keyActorId);
209534
+ const storedInstallationId = plain("GITHUB_APP_INSTALLATION_ID", keyActorId);
209535
+ const [meta, installs] = await Promise.all([
209536
+ fetchAppSelfMetadata({ appId, privateKeyPem: pem }).catch(() => null),
209537
+ listAppInstallations({ appId, privateKeyPem: pem }).catch(() => null)
209538
+ ]);
209539
+ const stored = installs?.find((i) => i.installationId === storedInstallationId) ?? installs?.[0];
209540
+ const permissions = stored && Object.keys(stored.permissions).length > 0 ? stored.permissions : meta?.permissions ?? null;
209541
+ const app = {
209542
+ appId,
209543
+ slug: meta?.slug || slug6,
209544
+ ownerLogin: meta?.ownerLogin ?? "",
209545
+ ownerType: meta?.ownerType ?? "",
209546
+ usedBy: e.usedBy,
209547
+ usedByNames: e.usedBy.map((id) => nameOf2.get(id) ?? id),
209548
+ isOrgDefault: e.isOrgDefault,
209549
+ // 列不出来(私钥失效等)时退回库里记的那一处,卸载时 404 也当成功。
209550
+ installationIds: installs ? installs.map((i) => i.installationId) : storedInstallationId ? [storedInstallationId] : [],
209551
+ permissions,
209552
+ keyActorId
209553
+ };
209554
+ return app;
209555
+ }));
209556
+ return out.filter((a) => a !== null);
209557
+ }
209558
+ async function ghUser(token, path47) {
209559
+ const r = await fetch(`https://api.github.com${path47}`, {
209560
+ headers: {
209561
+ authorization: `Bearer ${token}`,
209562
+ accept: "application/vnd.github+json",
209563
+ "x-github-api-version": "2022-11-28"
209564
+ }
209565
+ });
209566
+ return { status: r.status, body: r.ok ? await r.json().catch(() => null) : null };
209567
+ }
209568
+ async function githubLoginOf(token) {
209569
+ const r = await ghUser(token, "/user").catch(() => null);
209570
+ return r?.body?.login ?? "";
209571
+ }
209572
+ async function listMemberships(token) {
209573
+ const r = await ghUser(
209574
+ token,
209575
+ "/user/memberships/orgs?state=active&per_page=100"
209576
+ );
209577
+ if (!r.body) return null;
209578
+ return r.body.flatMap((m2) => m2.organization?.login ? [{ login: m2.organization.login, role: m2.role ?? "member" }] : []);
209579
+ }
209580
+ async function orgInstalledAppsFor(token, org) {
209581
+ if (!token) return { state: { status: "needsAuth" }, apps: [] };
209582
+ const m2 = await ghUser(token, `/user/memberships/orgs/${encodeURIComponent(org)}`);
209583
+ if (m2.status === 401) return { state: { status: "needsAuth" }, apps: [] };
209584
+ if (m2.body && m2.body.role !== "admin") return { state: { status: "notAdmin" }, apps: [] };
209585
+ if (!m2.body && m2.status === 404) return { state: { status: "notAdmin" }, apps: [] };
209586
+ if (!m2.body) return { state: { status: "needsAuth" }, apps: [] };
209587
+ const r = await listOrgInstalledApps({ token, org });
209588
+ if (r.status === "ok") return { state: { status: "ok" }, apps: r.apps };
209589
+ if (r.status === "needsAuth") return { state: { status: "needsAuth" }, apps: [] };
209590
+ return { state: { status: "error", message: r.message }, apps: [] };
209591
+ }
209592
+ async function pickUserToken(svc) {
209593
+ const rows = await svc.listVariables();
209594
+ const sources = [void 0, ...rows.filter((r) => r.key === "GITHUB_TOKEN" && r.scope === "personal" && r.actorId).map((r) => r.actorId)];
209595
+ const tried = /* @__PURE__ */ new Set();
209596
+ for (const src of sources) {
209597
+ const token = await svc.revealVariable("GITHUB_TOKEN", src).catch(() => null);
209598
+ if (!token || tried.has(token)) continue;
209599
+ tried.add(token);
209600
+ const memberships = await listMemberships(token).catch(() => null);
209601
+ if (memberships) return { token, memberships };
209602
+ }
209603
+ return null;
209604
+ }
209605
+ var init_github_app_inventory = __esm({
209606
+ "../server/src/github-app-inventory.ts"() {
209607
+ "use strict";
209608
+ init_src8();
209609
+ }
209610
+ });
209611
+
209302
209612
  // ../server/src/server.ts
209303
209613
  function isBundleFrozen(err) {
209304
209614
  return typeof err === "object" && err !== null && err.code === "TENANT_BUNDLE_FROZEN";
@@ -212740,7 +213050,7 @@ async function startOasisServer(opts) {
212740
213050
  entry = {
212741
213051
  // 旧 CLI 未带 waiterId 时给它塞一个服务端本地生成的——它绝不可能出现在 leave 上,
212742
213052
  // 也就绝不会误命中 leftWaiterIds、绝不会被别人的 leave 摘掉。行为与旧 CLI 相同。
212743
- waiterId: clientWaiterId ?? `server:${(0, import_node_crypto42.randomUUID)()}`,
213053
+ waiterId: clientWaiterId ?? `server:${(0, import_node_crypto43.randomUUID)()}`,
212744
213054
  onResolve,
212745
213055
  expiresAt: expiresAt2,
212746
213056
  cleanup: () => clearTimeout(expiryTimer),
@@ -214878,57 +215188,71 @@ ${composed}`;
214878
215188
  }
214879
215189
  if (url.pathname === "/api/connectors/github/app/available" && req.method === "POST") {
214880
215190
  try {
215191
+ const { actorId, owner } = JSON.parse(rawBody || "{}");
214881
215192
  const svc = await connectorActorsService();
214882
215193
  if (!svc) throw new Error("actors service unavailable");
214883
- const rows = await svc.listVariables();
214884
- const plain = (key2, actorId) => rows.find((r) => r.key === key2 && r.actorId === actorId && !r.encrypted)?.maskedValue ?? "";
214885
- const byAppId = /* @__PURE__ */ new Map();
214886
- for (const r of rows) {
214887
- if (r.key !== "GITHUB_APP_ID" || r.encrypted || !r.maskedValue) continue;
214888
- const appId = r.maskedValue;
214889
- const prev = byAppId.get(appId);
214890
- if (prev) {
214891
- prev.usedBy += 1;
214892
- if (!r.actorId && prev.scope === "personal") {
214893
- prev.scope = "global";
214894
- delete prev.actorId;
214895
- prev.installationId = plain("GITHUB_APP_INSTALLATION_ID");
214896
- prev.slug = plain("GITHUB_APP_SLUG");
214897
- }
214898
- continue;
214899
- }
214900
- byAppId.set(appId, {
214901
- appId,
214902
- slug: plain("GITHUB_APP_SLUG", r.actorId),
214903
- installationId: plain("GITHUB_APP_INSTALLATION_ID", r.actorId),
214904
- scope: r.actorId ? "personal" : "global",
214905
- ...r.actorId ? { actorId: r.actorId } : {},
214906
- usedBy: 1
214907
- });
215194
+ const token = await svc.revealResolvedVariable("GITHUB_TOKEN", actorId);
215195
+ const appOwner = owner?.kind === "org" && owner.login ? { kind: "org", login: owner.login } : { kind: "user", ...token ? { login: await githubLoginOf(token) } : {} };
215196
+ const [oasisApps, installed] = await Promise.all([
215197
+ collectOasisGithubApps(svc),
215198
+ appOwner.kind === "org" ? orgInstalledAppsFor(token, appOwner.login) : Promise.resolve({ state: { status: "ok" }, apps: [] })
215199
+ ]);
215200
+ const names = new Map(oasisApps.map((a) => [a.appId, a.usedByNames]));
215201
+ const candidates = buildReuseCandidates({ owner: appOwner, oasisApps, installed: installed.apps }).map((c) => ({ ...c, usedBy: names.get(c.appId) ?? [] }));
215202
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ candidates, installed: installed.state }));
215203
+ } catch (e) {
215204
+ res.writeHead(500, { "content-type": "application/json" }).end(JSON.stringify({ error: String(e) }));
215205
+ }
215206
+ return;
215207
+ }
215208
+ if (url.pathname === "/api/connectors/github/app/inventory" && req.method === "POST") {
215209
+ try {
215210
+ const svc = await connectorActorsService();
215211
+ if (!svc) throw new Error("actors service unavailable");
215212
+ const [oasisApps, picked] = await Promise.all([collectOasisGithubApps(svc), pickUserToken(svc)]);
215213
+ const adminOrgs = picked ? picked.memberships.filter((m2) => m2.role === "admin").map((m2) => m2.login) : [];
215214
+ const perOrg = picked ? await Promise.all(adminOrgs.map((org) => orgInstalledAppsFor(picked.token, org))) : [];
215215
+ const rows = buildAppInventory({ oasisApps, installed: perOrg.flatMap((r) => r.apps) });
215216
+ const names = new Map(oasisApps.map((a) => [a.appId, a.usedByNames]));
215217
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
215218
+ rows: rows.map((r) => ({ ...r, usedBy: names.get(r.appId) ?? [] })),
215219
+ /** 组织里装着的那部分列没列出来:needsAuth = 没有可用的 GitHub 登录;notAdmin = 不是任何组织的管理员。 */
215220
+ installed: !picked ? { status: "needsAuth" } : adminOrgs.length === 0 ? { status: "notAdmin" } : perOrg.find((r) => r.state.status !== "ok")?.state ?? { status: "ok" },
215221
+ orgs: adminOrgs
215222
+ }));
215223
+ } catch (e) {
215224
+ res.writeHead(500, { "content-type": "application/json" }).end(JSON.stringify({ error: String(e) }));
215225
+ }
215226
+ return;
215227
+ }
215228
+ if (url.pathname === "/api/connectors/github/app/uninstall" && req.method === "POST") {
215229
+ try {
215230
+ const { appIds } = JSON.parse(rawBody || "{}");
215231
+ if (!Array.isArray(appIds) || appIds.length === 0 || !appIds.every((x2) => typeof x2 === "string")) {
215232
+ res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u8BF7\u5148\u52FE\u9009\u8981\u5378\u8F7D\u7684 App" }));
215233
+ return;
214908
215234
  }
214909
- const apps = await Promise.all([...byAppId.values()].map(async (a) => {
214910
- const base = {
214911
- appId: a.appId,
214912
- slug: a.slug,
214913
- account: a.slug ? `${a.slug}[bot]` : a.appId,
214914
- isOrgDefault: a.scope === "global",
214915
- usedBy: a.usedBy,
214916
- ...a.slug ? { settingsUrl: appPermissionSettingsUrl({ slug: a.slug }) } : {}
214917
- };
215235
+ const svc = await connectorActorsService();
215236
+ if (!svc) throw new Error("actors service unavailable");
215237
+ const apps = new Map((await collectOasisGithubApps(svc)).map((a) => [a.appId, a]));
215238
+ const results = await Promise.all(appIds.map(async (appId) => {
215239
+ const app = apps.get(appId);
215240
+ if (!app) return { appId, ok: false, error: "Oasis \u6CA1\u6709\u8FD9\u4E2A App \u7684\u79C1\u94A5\uFF0C\u5378\u8F7D\u4E0D\u4E86\uFF0C\u8BF7\u53BB GitHub \u9875\u9762\u5904\u7406" };
215241
+ if (app.isOrgDefault || app.usedBy.length > 0) {
215242
+ return { appId, slug: app.slug, ok: false, error: `\u8FD8\u6709 agent \u5728\u7528\uFF08${app.isOrgDefault ? "\u7EC4\u7EC7\u9ED8\u8BA4" : app.usedByNames.join("\u3001")}\uFF09\uFF0C\u4E0D\u80FD\u5378\u8F7D` };
215243
+ }
215244
+ const pem = await svc.revealVariable("GITHUB_APP_PRIVATE_KEY", app.keyActorId);
215245
+ if (!pem) return { appId, slug: app.slug, ok: false, error: "\u79C1\u94A5\u8BFB\u4E0D\u51FA\u6765\uFF0C\u5378\u8F7D\u4E0D\u4E86" };
214918
215246
  try {
214919
- const pem = await svc.revealVariable("GITHUB_APP_PRIVATE_KEY", a.actorId);
214920
- if (!pem || !a.installationId) return { ...base, permissionsUnknown: true };
214921
- const perms = await fetchInstallationPermissions({
214922
- appId: a.appId,
214923
- privateKeyPem: pem,
214924
- installationId: a.installationId
214925
- });
214926
- return { ...base, missing: missingAppPermissions(perms) };
214927
- } catch {
214928
- return { ...base, permissionsUnknown: true };
215247
+ for (const installationId of app.installationIds) {
215248
+ await uninstallAppInstallation({ appId, privateKeyPem: pem, installationId });
215249
+ }
215250
+ return { appId, slug: app.slug, ok: true };
215251
+ } catch (e) {
215252
+ return { appId, slug: app.slug, ok: false, error: `GitHub \u62D2\u7EDD\u4E86\u5378\u8F7D\uFF1A${String(e).replace(/^Error:\s*/, "")}` };
214929
215253
  }
214930
215254
  }));
214931
- res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ apps }));
215255
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ results }));
214932
215256
  } catch (e) {
214933
215257
  res.writeHead(500, { "content-type": "application/json" }).end(JSON.stringify({ error: String(e) }));
214934
215258
  }
@@ -214957,9 +215281,11 @@ ${composed}`;
214957
215281
  return;
214958
215282
  }
214959
215283
  const pick3 = (key2) => rows.find((r) => r.key === key2 && r.actorId === srcActor && !r.encrypted)?.maskedValue ?? "";
214960
- const installationId = pick3("GITHUB_APP_INSTALLATION_ID");
215284
+ const storedInstallationId = pick3("GITHUB_APP_INSTALLATION_ID");
215285
+ const live = await listAppInstallations({ appId, privateKeyPem: pem }).catch(() => null);
215286
+ const installationId = live ? (live.find((i) => i.installationId === storedInstallationId) ?? live[0])?.installationId ?? "" : storedInstallationId;
214961
215287
  if (!installationId) {
214962
- res.writeHead(409, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u8FD9\u4E2A App \u8FD8\u6CA1\u6709\u5B89\u88C5\u8BB0\u5F55\uFF0C\u65E0\u6CD5\u94F8 token" }));
215288
+ res.writeHead(409, { "content-type": "application/json" }).end(JSON.stringify({ error: "\u8FD9\u4E2A App \u8FD8\u6CA1\u88C5\u5230\u4EFB\u4F55\u8D26\u53F7\u2014\u2014\u5148\u5728 GitHub \u4E0A\u5B89\u88C5\u5B83\uFF0C\u88C5\u597D\u540E\u518D\u70B9\u8FDE\u63A5" }));
214963
215289
  return;
214964
215290
  }
214965
215291
  const slug6 = pick3("GITHUB_APP_SLUG");
@@ -214997,7 +215323,7 @@ ${composed}`;
214997
215323
  const msg = String(e);
214998
215324
  bad(
214999
215325
  400,
215000
- /HTTP 401/.test(msg) ? "App ID \u4E0E\u79C1\u94A5\u5BF9\u4E0D\u4E0A\u2014\u2014\u786E\u8BA4 App ID \u6284\u81EA\u540C\u4E00\u4E2A App \u7684\u8BBE\u7F6E\u9875" : /私钥解析失败/.test(msg) ? msg.replace(/^Error:\s*/, "").replace(/^github app:\s*/, "") : `GitHub \u62D2\u7EDD\u4E86\u8FD9\u5BF9\u51ED\u636E\uFF1A${msg}`
215326
+ /HTTP 401/.test(msg) ? "\u8FD9\u4E2A\u79C1\u94A5\u548C\u6240\u9009\u7684 App \u5BF9\u4E0D\u4E0A\u2014\u2014\u8BF7\u786E\u8BA4\u5B83\u662F\u5728\u8FD9\u4E2A App \u7684\u8BBE\u7F6E\u9875\u70B9 Generate a private key \u4E0B\u8F7D\u7684\u90A3\u4E00\u4EFD\uFF08\u624B\u586B App ID \u65F6\u4E5F\u6838\u5BF9\u4E00\u4E0B\u6709\u6CA1\u6709\u6284\u9519\uFF09" : /私钥解析失败/.test(msg) ? msg.replace(/^Error:\s*/, "").replace(/^github app:\s*/, "") : `GitHub \u62D2\u7EDD\u4E86\u8FD9\u5BF9\u51ED\u636E\uFF1A${msg}`
215001
215327
  );
215002
215328
  return;
215003
215329
  }
@@ -215667,14 +215993,14 @@ async function markDeliveredWithRetry(store, messageId, attempts = 3) {
215667
215993
  }
215668
215994
  return false;
215669
215995
  }
215670
- var http, import_node_crypto42, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, APPEND_TURN_START_WAIT_MS, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
215996
+ var http, import_node_crypto43, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, APPEND_TURN_START_WAIT_MS, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
215671
215997
  var init_server3 = __esm({
215672
215998
  "../server/src/server.ts"() {
215673
215999
  "use strict";
215674
216000
  init_src();
215675
216001
  init_file_limits2();
215676
216002
  http = __toESM(require("node:http"), 1);
215677
- import_node_crypto42 = require("node:crypto");
216003
+ import_node_crypto43 = require("node:crypto");
215678
216004
  init_chat_session();
215679
216005
  init_chat_system_message();
215680
216006
  init_sealed_node_receipt();
@@ -215740,6 +216066,7 @@ var init_server3 = __esm({
215740
216066
  init_child_live_turn();
215741
216067
  init_registry_audit_sink();
215742
216068
  init_src8();
216069
+ init_github_app_inventory();
215743
216070
  init_continuation3();
215744
216071
  CONNECTOR_AUTH_SNAPSHOT_TTL_MS = 30 * 60 * 1e3;
215745
216072
  defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
@@ -215854,7 +216181,7 @@ function createNodeTokenStore(file) {
215854
216181
  return {
215855
216182
  issue(nodeId) {
215856
216183
  const table = read3();
215857
- const token = `ont_${(0, import_node_crypto43.randomBytes)(24).toString("base64url")}`;
216184
+ const token = `ont_${(0, import_node_crypto44.randomBytes)(24).toString("base64url")}`;
215858
216185
  table[token] = nodeId;
215859
216186
  write(table);
215860
216187
  return token;
@@ -215914,7 +216241,7 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215914
216241
  return {
215915
216242
  issue(name, ttlMs = defaultTtlMs, companyId) {
215916
216243
  const m2 = prune();
215917
- const token = `ent_${(0, import_node_crypto43.randomBytes)(24).toString("base64url")}`;
216244
+ const token = `ent_${(0, import_node_crypto44.randomBytes)(24).toString("base64url")}`;
215918
216245
  const expiresAt2 = Date.now() + ttlMs;
215919
216246
  m2.set(token, { name, expiresAt: expiresAt2, ...companyId !== void 0 ? { companyId } : {} });
215920
216247
  write(m2);
@@ -215963,33 +216290,33 @@ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
215963
216290
  }
215964
216291
  };
215965
216292
  }
215966
- var import_node_crypto43, fs22, path21, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
216293
+ var import_node_crypto44, fs22, path21, SESSION_TOKEN_PREFIX, b64url2, fromB64url, readOrCreateSecret, sign, signatureMatches, isTokenClaims;
215967
216294
  var init_tokens = __esm({
215968
216295
  "../server/src/tokens.ts"() {
215969
216296
  "use strict";
215970
- import_node_crypto43 = require("node:crypto");
216297
+ import_node_crypto44 = require("node:crypto");
215971
216298
  fs22 = __toESM(require("node:fs"), 1);
215972
216299
  path21 = __toESM(require("node:path"), 1);
215973
216300
  SESSION_TOKEN_PREFIX = "oat_v2_";
215974
216301
  b64url2 = (value2) => Buffer.from(value2).toString("base64url");
215975
216302
  fromB64url = (value2) => Buffer.from(value2, "base64url").toString("utf8");
215976
216303
  readOrCreateSecret = (file) => {
215977
- if (!file) return (0, import_node_crypto43.randomBytes)(32);
216304
+ if (!file) return (0, import_node_crypto44.randomBytes)(32);
215978
216305
  try {
215979
216306
  const raw = fs22.readFileSync(file, "utf8").trim();
215980
216307
  if (raw) return Buffer.from(raw, "base64url");
215981
216308
  } catch {
215982
216309
  }
215983
- const secret = (0, import_node_crypto43.randomBytes)(32);
216310
+ const secret = (0, import_node_crypto44.randomBytes)(32);
215984
216311
  fs22.mkdirSync(path21.dirname(file), { recursive: true });
215985
216312
  fs22.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
215986
216313
  return secret;
215987
216314
  };
215988
- sign = (secret, payload2) => (0, import_node_crypto43.createHmac)("sha256", secret).update(payload2).digest("base64url");
216315
+ sign = (secret, payload2) => (0, import_node_crypto44.createHmac)("sha256", secret).update(payload2).digest("base64url");
215989
216316
  signatureMatches = (actual, expected) => {
215990
216317
  const a = Buffer.from(actual);
215991
216318
  const b2 = Buffer.from(expected);
215992
- return a.length === b2.length && (0, import_node_crypto43.timingSafeEqual)(a, b2);
216319
+ return a.length === b2.length && (0, import_node_crypto44.timingSafeEqual)(a, b2);
215993
216320
  };
215994
216321
  isTokenClaims = (value2) => {
215995
216322
  if (value2 === null || typeof value2 !== "object") return false;
@@ -216051,31 +216378,31 @@ function createDataplaneTokenIssuer(opts = {}) {
216051
216378
  }
216052
216379
  };
216053
216380
  }
216054
- var import_node_crypto44, fs23, path22, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims, isDataplaneToken;
216381
+ var import_node_crypto45, fs23, path22, PREFIX2, b64url3, fromB64url2, sign2, sigMatches, readOrCreateSecret2, isClaims, isDataplaneToken;
216055
216382
  var init_session_dataplane_token = __esm({
216056
216383
  "../server/src/session-dataplane-token.ts"() {
216057
216384
  "use strict";
216058
- import_node_crypto44 = require("node:crypto");
216385
+ import_node_crypto45 = require("node:crypto");
216059
216386
  fs23 = __toESM(require("node:fs"), 1);
216060
216387
  path22 = __toESM(require("node:path"), 1);
216061
216388
  init_src();
216062
216389
  PREFIX2 = "osd_";
216063
216390
  b64url3 = (v2) => Buffer.from(v2, "utf8").toString("base64url");
216064
216391
  fromB64url2 = (v2) => Buffer.from(v2, "base64url").toString("utf8");
216065
- sign2 = (secret, payload2) => (0, import_node_crypto44.createHmac)("sha256", secret).update(payload2).digest("base64url");
216392
+ sign2 = (secret, payload2) => (0, import_node_crypto45.createHmac)("sha256", secret).update(payload2).digest("base64url");
216066
216393
  sigMatches = (actual, expected) => {
216067
216394
  const a = Buffer.from(actual);
216068
216395
  const b2 = Buffer.from(expected);
216069
- return a.length === b2.length && (0, import_node_crypto44.timingSafeEqual)(a, b2);
216396
+ return a.length === b2.length && (0, import_node_crypto45.timingSafeEqual)(a, b2);
216070
216397
  };
216071
216398
  readOrCreateSecret2 = (file) => {
216072
- if (!file) return (0, import_node_crypto44.randomBytes)(32);
216399
+ if (!file) return (0, import_node_crypto45.randomBytes)(32);
216073
216400
  try {
216074
216401
  const raw = fs23.readFileSync(file, "utf8").trim();
216075
216402
  if (raw) return Buffer.from(raw, "base64url");
216076
216403
  } catch {
216077
216404
  }
216078
- const secret = (0, import_node_crypto44.randomBytes)(32);
216405
+ const secret = (0, import_node_crypto45.randomBytes)(32);
216079
216406
  fs23.mkdirSync(path22.dirname(file), { recursive: true });
216080
216407
  fs23.writeFileSync(file, secret.toString("base64url"), { mode: 384 });
216081
216408
  return secret;
@@ -218877,11 +219204,11 @@ function onlyHits(pool, q2) {
218877
219204
  if (!q2.requireMatch || q2.keywords.length === 0) return [...pool];
218878
219205
  return pool.filter((r) => memoryMatchScore(r, q2.keywords) > 0);
218879
219206
  }
218880
- var import_node_crypto45, MemoryActorMemoryStore, toIndexEntry;
219207
+ var import_node_crypto46, MemoryActorMemoryStore, toIndexEntry;
218881
219208
  var init_memory_actor_memory_store = __esm({
218882
219209
  "../testkit/src/memory-actor-memory-store.ts"() {
218883
219210
  "use strict";
218884
- import_node_crypto45 = require("node:crypto");
219211
+ import_node_crypto46 = require("node:crypto");
218885
219212
  init_src();
218886
219213
  MemoryActorMemoryStore = class {
218887
219214
  rows = /* @__PURE__ */ new Map();
@@ -218935,7 +219262,7 @@ var init_memory_actor_memory_store = __esm({
218935
219262
  async write(input, now) {
218936
219263
  if (input.memId === void 0) {
218937
219264
  const rec2 = {
218938
- memId: `mem:${(0, import_node_crypto45.randomUUID)()}`,
219265
+ memId: `mem:${(0, import_node_crypto46.randomUUID)()}`,
218939
219266
  actorId: input.actorId,
218940
219267
  projectId: input.projectId,
218941
219268
  keywords: [...input.keywords],
@@ -220634,13 +220961,13 @@ var init_stats = __esm({
220634
220961
 
220635
220962
  // ../server/src/domains/projects/project-id.ts
220636
220963
  function randomProjectId() {
220637
- return projectIdFromUuid((0, import_node_crypto46.randomUUID)());
220964
+ return projectIdFromUuid((0, import_node_crypto47.randomUUID)());
220638
220965
  }
220639
- var import_node_crypto46;
220966
+ var import_node_crypto47;
220640
220967
  var init_project_id = __esm({
220641
220968
  "../server/src/domains/projects/project-id.ts"() {
220642
220969
  "use strict";
220643
- import_node_crypto46 = require("node:crypto");
220970
+ import_node_crypto47 = require("node:crypto");
220644
220971
  init_src();
220645
220972
  }
220646
220973
  });
@@ -222715,12 +223042,12 @@ function applyTurnPatch(t, patch) {
222715
223042
  ...patch.lastError !== void 0 ? { lastError: patch.lastError } : {}
222716
223043
  };
222717
223044
  }
222718
- var import_node_crypto47, fs24, path23, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
223045
+ var import_node_crypto48, fs24, path23, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRoleRegistryStore, FileRegistryStore, FileAssistantBindStore, FileHumanPrefsStore, FileProjectStateStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore, MemoryChatTurnStore, FileChatTurnStore, MemoryReadMarkerStore, FileReadMarkerStore;
222719
223046
  var init_dev_store = __esm({
222720
223047
  "../server/src/dev-store.ts"() {
222721
223048
  "use strict";
222722
223049
  init_src();
222723
- import_node_crypto47 = require("node:crypto");
223050
+ import_node_crypto48 = require("node:crypto");
222724
223051
  fs24 = __toESM(require("node:fs"), 1);
222725
223052
  path23 = __toESM(require("node:path"), 1);
222726
223053
  init_src();
@@ -222774,7 +223101,7 @@ var init_dev_store = __esm({
222774
223101
  return path23.join(this.dir, hash3);
222775
223102
  }
222776
223103
  async put(bytes2) {
222777
- const hash3 = (0, import_node_crypto47.createHash)("sha256").update(bytes2).digest("hex");
223104
+ const hash3 = (0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex");
222778
223105
  const file = this.fileOf(hash3);
222779
223106
  if (!fs24.existsSync(file)) fs24.writeFileSync(file, bytes2);
222780
223107
  return hash3;
@@ -224069,7 +224396,7 @@ var init_company_engine_router = __esm({
224069
224396
 
224070
224397
  // ../server/src/skill-market-client.ts
224071
224398
  function digestOf2(bytes2) {
224072
- return `sha256:${(0, import_node_crypto48.createHash)("sha256").update(bytes2).digest("hex")}`;
224399
+ return `sha256:${(0, import_node_crypto49.createHash)("sha256").update(bytes2).digest("hex")}`;
224073
224400
  }
224074
224401
  function createSkillMarketClientFromEnv(env = process.env) {
224075
224402
  const base = env["SKILL_MARKET_BASE_URL"];
@@ -224080,11 +224407,11 @@ function createSkillMarketClientFromEnv(env = process.env) {
224080
224407
  function sleep4(ms) {
224081
224408
  return new Promise((r) => setTimeout(r, ms));
224082
224409
  }
224083
- var import_node_crypto48, DisabledSkillMarketClient, FakeSkillMarketClient, HttpSkillMarketClient;
224410
+ var import_node_crypto49, DisabledSkillMarketClient, FakeSkillMarketClient, HttpSkillMarketClient;
224084
224411
  var init_skill_market_client = __esm({
224085
224412
  "../server/src/skill-market-client.ts"() {
224086
224413
  "use strict";
224087
- import_node_crypto48 = require("node:crypto");
224414
+ import_node_crypto49 = require("node:crypto");
224088
224415
  init_src();
224089
224416
  DisabledSkillMarketClient = class {
224090
224417
  async listSkills(_input) {
@@ -224935,7 +225262,7 @@ async function copySessionOutputs(deps) {
224935
225262
  continue;
224936
225263
  }
224937
225264
  const saved = existing?.[file.path];
224938
- if (saved && savedContentHash(saved) === (0, import_node_crypto49.createHash)("sha256").update(bytes2).digest("hex")) {
225265
+ if (saved && savedContentHash(saved) === (0, import_node_crypto50.createHash)("sha256").update(bytes2).digest("hex")) {
224939
225266
  const aligned = { ...saved, size: bytes2.length, mtime: file.mtime };
224940
225267
  delete aligned.editedAt;
224941
225268
  realigned.push(aligned);
@@ -225032,11 +225359,11 @@ function createOutputCopyScheduler(deps) {
225032
225359
  }
225033
225360
  };
225034
225361
  }
225035
- var import_node_crypto49, OUTPUT_COPY_LIMITS;
225362
+ var import_node_crypto50, OUTPUT_COPY_LIMITS;
225036
225363
  var init_outputs_copy = __esm({
225037
225364
  "../server/src/domains/chat-sessions/outputs-copy.ts"() {
225038
225365
  "use strict";
225039
- import_node_crypto49 = require("node:crypto");
225366
+ import_node_crypto50 = require("node:crypto");
225040
225367
  init_src();
225041
225368
  OUTPUT_COPY_LIMITS = {
225042
225369
  maxFileBytes: FILE_CONTENT_MAX_BYTES,
@@ -225421,11 +225748,11 @@ function typeDefError(def, mode) {
225421
225748
  }
225422
225749
  return null;
225423
225750
  }
225424
- var import_node_crypto50, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
225751
+ var import_node_crypto51, RESERVED_FIELD_NAMES, ARTIFACT_TYPE_ACCENTS, TYPE_DEF_SCHEMA, TypesAdminService;
225425
225752
  var init_types_admin = __esm({
225426
225753
  "../server/src/types-admin.ts"() {
225427
225754
  "use strict";
225428
- import_node_crypto50 = require("node:crypto");
225755
+ import_node_crypto51 = require("node:crypto");
225429
225756
  init_zod();
225430
225757
  init_src7();
225431
225758
  init_config_audit();
@@ -225552,7 +225879,7 @@ var init_types_admin = __esm({
225552
225879
  async trace(change, target, by, detail) {
225553
225880
  try {
225554
225881
  await this.audit?.({
225555
- id: `reg_${(0, import_node_crypto50.randomUUID)()}`,
225882
+ id: `reg_${(0, import_node_crypto51.randomUUID)()}`,
225556
225883
  actor: by ?? SYSTEM_CONFIG_ACTOR,
225557
225884
  kind: "registry_change",
225558
225885
  target,
@@ -225700,11 +226027,11 @@ function roleDefError(def, mode) {
225700
226027
  }
225701
226028
  return null;
225702
226029
  }
225703
- var import_node_crypto51, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
226030
+ var import_node_crypto52, ACCENTS, ROLE_DEF_SCHEMA, RolesAdminService;
225704
226031
  var init_roles_admin = __esm({
225705
226032
  "../server/src/roles-admin.ts"() {
225706
226033
  "use strict";
225707
- import_node_crypto51 = require("node:crypto");
226034
+ import_node_crypto52 = require("node:crypto");
225708
226035
  init_zod();
225709
226036
  init_src7();
225710
226037
  init_config_audit();
@@ -225729,7 +226056,7 @@ var init_roles_admin = __esm({
225729
226056
  async trace(change, target, by, detail) {
225730
226057
  try {
225731
226058
  await this.deps.audit?.({
225732
- id: `reg_${(0, import_node_crypto51.randomUUID)()}`,
226059
+ id: `reg_${(0, import_node_crypto52.randomUUID)()}`,
225733
226060
  actor: by ?? SYSTEM_CONFIG_ACTOR,
225734
226061
  kind: "registry_change",
225735
226062
  target,
@@ -228607,8 +228934,8 @@ var init_chat_recovery = __esm({
228607
228934
 
228608
228935
  // ../server/src/auth/crypto.ts
228609
228936
  function hashPassword(password) {
228610
- const salt = (0, import_node_crypto52.randomBytes)(16);
228611
- const dk = (0, import_node_crypto52.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
228937
+ const salt = (0, import_node_crypto53.randomBytes)(16);
228938
+ const dk = (0, import_node_crypto53.scryptSync)(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, maxmem: 64 * 1024 * 1024 });
228612
228939
  return `scrypt$${SCRYPT_N}$${salt.toString("base64url")}$${dk.toString("base64url")}`;
228613
228940
  }
228614
228941
  function verifyPassword(password, stored) {
@@ -228625,8 +228952,8 @@ function verifyPassword(password, stored) {
228625
228952
  return false;
228626
228953
  }
228627
228954
  if (expected.length === 0) return false;
228628
- const dk = (0, import_node_crypto52.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
228629
- return dk.length === expected.length && (0, import_node_crypto52.timingSafeEqual)(dk, expected);
228955
+ const dk = (0, import_node_crypto53.scryptSync)(password, salt, expected.length, { N, maxmem: 64 * 1024 * 1024 });
228956
+ return dk.length === expected.length && (0, import_node_crypto53.timingSafeEqual)(dk, expected);
228630
228957
  }
228631
228958
  function b64urlJson(value2) {
228632
228959
  return Buffer.from(JSON.stringify(value2)).toString("base64url");
@@ -228634,17 +228961,17 @@ function b64urlJson(value2) {
228634
228961
  function signSession(claims, secret) {
228635
228962
  const head = b64urlJson({ alg: "HS256", typ: "JWT" });
228636
228963
  const body2 = b64urlJson(claims);
228637
- const sig = (0, import_node_crypto52.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
228964
+ const sig = (0, import_node_crypto53.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
228638
228965
  return `${head}.${body2}.${sig}`;
228639
228966
  }
228640
228967
  function verifySession(token, secret, nowMs) {
228641
228968
  const parts = token.split(".");
228642
228969
  if (parts.length !== 3) return null;
228643
228970
  const [head, body2, sig] = parts;
228644
- const expected = (0, import_node_crypto52.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
228971
+ const expected = (0, import_node_crypto53.createHmac)("sha256", secret).update(`${head}.${body2}`).digest("base64url");
228645
228972
  const got = Buffer.from(sig);
228646
228973
  const exp = Buffer.from(expected);
228647
- if (got.length !== exp.length || !(0, import_node_crypto52.timingSafeEqual)(got, exp)) return null;
228974
+ if (got.length !== exp.length || !(0, import_node_crypto53.timingSafeEqual)(got, exp)) return null;
228648
228975
  let claims;
228649
228976
  try {
228650
228977
  claims = JSON.parse(Buffer.from(body2, "base64url").toString("utf8"));
@@ -228656,21 +228983,21 @@ function verifySession(token, secret, nowMs) {
228656
228983
  return claims;
228657
228984
  }
228658
228985
  function generateCode() {
228659
- return String((0, import_node_crypto52.randomInt)(0, 1e6)).padStart(6, "0");
228986
+ return String((0, import_node_crypto53.randomInt)(0, 1e6)).padStart(6, "0");
228660
228987
  }
228661
228988
  function hashCode(code2, secret) {
228662
- return (0, import_node_crypto52.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
228989
+ return (0, import_node_crypto53.createHmac)("sha256", secret).update(`code:${code2}`).digest("base64url");
228663
228990
  }
228664
228991
  function safeEqualHash(a, b2) {
228665
228992
  const ba = Buffer.from(a);
228666
228993
  const bb = Buffer.from(b2);
228667
- return ba.length === bb.length && (0, import_node_crypto52.timingSafeEqual)(ba, bb);
228994
+ return ba.length === bb.length && (0, import_node_crypto53.timingSafeEqual)(ba, bb);
228668
228995
  }
228669
- var import_node_crypto52, SCRYPT_N, SCRYPT_KEYLEN;
228996
+ var import_node_crypto53, SCRYPT_N, SCRYPT_KEYLEN;
228670
228997
  var init_crypto2 = __esm({
228671
228998
  "../server/src/auth/crypto.ts"() {
228672
228999
  "use strict";
228673
- import_node_crypto52 = require("node:crypto");
229000
+ import_node_crypto53 = require("node:crypto");
228674
229001
  SCRYPT_N = 16384;
228675
229002
  SCRYPT_KEYLEN = 32;
228676
229003
  }
@@ -230054,18 +230381,18 @@ function variableKeyFromEnv(env = process.env) {
230054
230381
  return buf;
230055
230382
  }
230056
230383
  if (env.NODE_ENV === "production") throw new Error("OASIS_VAR_KEY is required in production");
230057
- return (0, import_node_crypto53.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
230384
+ return (0, import_node_crypto54.createHash)("sha256").update("oasis-dev-only-variable-key").digest();
230058
230385
  }
230059
230386
  function dedupe2(ids2) {
230060
230387
  return [...new Set(ids2.filter((x2) => typeof x2 === "string" && x2.length > 0))];
230061
230388
  }
230062
- var import_node_crypto53, REDACTED_MARKER, ActorsService, MASK_TAIL_MIN_LENGTH, mask, changedKeys;
230389
+ var import_node_crypto54, REDACTED_MARKER, ActorsService, MASK_TAIL_MIN_LENGTH, mask, changedKeys;
230063
230390
  var init_service5 = __esm({
230064
230391
  "../server/src/domains/actors/service.ts"() {
230065
230392
  "use strict";
230066
230393
  init_file_limits2();
230067
230394
  init_keyed_mutex();
230068
- import_node_crypto53 = require("node:crypto");
230395
+ import_node_crypto54 = require("node:crypto");
230069
230396
  init_skill_fetcher();
230070
230397
  init_identity();
230071
230398
  init_migrate();
@@ -230689,7 +231016,7 @@ ${input.description}
230689
231016
  */
230690
231017
  buildSkillFile(skillId, path47, content3, now) {
230691
231018
  const bytes2 = new TextEncoder().encode(content3);
230692
- const blobHash = (0, import_node_crypto53.createHash)("sha256").update(bytes2).digest("hex");
231019
+ const blobHash = (0, import_node_crypto54.createHash)("sha256").update(bytes2).digest("hex");
230693
231020
  return { skillId, path: path47, content: content3, blobHash, size: bytes2.length, updatedAt: now };
230694
231021
  }
230695
231022
  /**
@@ -231812,15 +232139,15 @@ ${input.description}
231812
232139
  return hit ? { value: hit.value, ...hit.origin ? { origin: hit.origin } : {} } : null;
231813
232140
  }
231814
232141
  encrypt(plain) {
231815
- const iv = (0, import_node_crypto53.randomBytes)(12);
231816
- const cipher = (0, import_node_crypto53.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
232142
+ const iv = (0, import_node_crypto54.randomBytes)(12);
232143
+ const cipher = (0, import_node_crypto54.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
231817
232144
  const enc3 = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
231818
232145
  return [iv.toString("base64"), cipher.getAuthTag().toString("base64"), enc3.toString("base64")].join(".");
231819
232146
  }
231820
232147
  decrypt(packed) {
231821
232148
  const [iv, tag, data] = packed.split(".");
231822
232149
  if (!iv || !tag || typeof data !== "string") throw new Error("malformed ciphertext");
231823
- const decipher = (0, import_node_crypto53.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
232150
+ const decipher = (0, import_node_crypto54.createDecipheriv)("aes-256-gcm", this.opts.variableKey, Buffer.from(iv, "base64"));
231824
232151
  decipher.setAuthTag(Buffer.from(tag, "base64"));
231825
232152
  return Buffer.concat([decipher.update(Buffer.from(data, "base64")), decipher.final()]).toString("utf8");
231826
232153
  }
@@ -232434,7 +232761,7 @@ async function listSkillMetadataForActor(args) {
232434
232761
  usedDirs.add(dir);
232435
232762
  const sorted = [...metas].sort((a, b2) => a.path.localeCompare(b2.path));
232436
232763
  let latest = "1970-01-01T00:00:00.000Z";
232437
- const hasher = (0, import_node_crypto54.createHash)("sha256");
232764
+ const hasher = (0, import_node_crypto55.createHash)("sha256");
232438
232765
  for (const m2 of sorted) {
232439
232766
  if (m2.updatedAt > latest) latest = m2.updatedAt;
232440
232767
  hasher.update(m2.path);
@@ -232459,11 +232786,11 @@ async function listSkillMetadataForActor(args) {
232459
232786
  const dir = sanitizeSkillDir(c.name, c.slug);
232460
232787
  if (usedDirs.has(dir)) continue;
232461
232788
  usedDirs.add(dir);
232462
- const hasher = (0, import_node_crypto54.createHash)("sha256");
232789
+ const hasher = (0, import_node_crypto55.createHash)("sha256");
232463
232790
  for (const p2 of paths) {
232464
232791
  hasher.update(p2);
232465
232792
  hasher.update("\0");
232466
- hasher.update((0, import_node_crypto54.createHash)("sha256").update(c.files[p2]).digest("hex"));
232793
+ hasher.update((0, import_node_crypto55.createHash)("sha256").update(c.files[p2]).digest("hex"));
232467
232794
  hasher.update("\0");
232468
232795
  }
232469
232796
  out.push({
@@ -232484,11 +232811,11 @@ async function listSkillMetadataForActor(args) {
232484
232811
  const dir = sanitizeSkillDir(b2.name, b2.id);
232485
232812
  if (usedDirs.has(dir)) continue;
232486
232813
  usedDirs.add(dir);
232487
- const hasher = (0, import_node_crypto54.createHash)("sha256");
232814
+ const hasher = (0, import_node_crypto55.createHash)("sha256");
232488
232815
  for (const p2 of paths) {
232489
232816
  hasher.update(p2);
232490
232817
  hasher.update("\0");
232491
- hasher.update((0, import_node_crypto54.createHash)("sha256").update(b2.files[p2]).digest("hex"));
232818
+ hasher.update((0, import_node_crypto55.createHash)("sha256").update(b2.files[p2]).digest("hex"));
232492
232819
  hasher.update("\0");
232493
232820
  }
232494
232821
  out.push({
@@ -232508,7 +232835,7 @@ function builtinSkillToFiles(b2) {
232508
232835
  skillId: `builtin:${b2.id}`,
232509
232836
  path: path47,
232510
232837
  content: content3,
232511
- blobHash: (0, import_node_crypto54.createHash)("sha256").update(content3).digest("hex"),
232838
+ blobHash: (0, import_node_crypto55.createHash)("sha256").update(content3).digest("hex"),
232512
232839
  size: Buffer.byteLength(content3, "utf8"),
232513
232840
  updatedAt: "1970-01-01T00:00:00.000Z"
232514
232841
  }));
@@ -232532,7 +232859,7 @@ function sanitizeSkillDir(name, fallbackId) {
232532
232859
  if (byName) return byName;
232533
232860
  const byId = fallbackId ? fold2(fallbackId) : "";
232534
232861
  if (byId) return byId;
232535
- return `skill-${(0, import_node_crypto54.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
232862
+ return `skill-${(0, import_node_crypto55.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`;
232536
232863
  }
232537
232864
  async function materializeSkillFiles(args) {
232538
232865
  const skills = await args.service.listInstalledSkillsForActor(args.actorId);
@@ -232565,7 +232892,7 @@ function connectorSkillToFiles(c) {
232565
232892
  skillId: c.id,
232566
232893
  path: path47,
232567
232894
  content: content3,
232568
- blobHash: (0, import_node_crypto54.createHash)("sha256").update(content3).digest("hex"),
232895
+ blobHash: (0, import_node_crypto55.createHash)("sha256").update(content3).digest("hex"),
232569
232896
  size: Buffer.byteLength(content3, "utf8"),
232570
232897
  updatedAt: "1970-01-01T00:00:00.000Z"
232571
232898
  }));
@@ -232582,11 +232909,11 @@ function connectorSkillDisplayRows(connectorSkills, enabledConnectors) {
232582
232909
  ...s2.version !== void 0 ? { version: s2.version } : {}
232583
232910
  }));
232584
232911
  }
232585
- var import_node_crypto54;
232912
+ var import_node_crypto55;
232586
232913
  var init_skill_materializer = __esm({
232587
232914
  "../server/src/domains/actors/skill-materializer.ts"() {
232588
232915
  "use strict";
232589
- import_node_crypto54 = require("node:crypto");
232916
+ import_node_crypto55 = require("node:crypto");
232590
232917
  }
232591
232918
  });
232592
232919
 
@@ -234693,7 +235020,7 @@ function createActorsDomain(opts) {
234693
235020
  ...kernel !== void 0 ? { onRolesChanged: (a, r) => kernel.setActorRoles(a, r) } : {},
234694
235021
  audit: async (entry) => {
234695
235022
  await opts.audit?.({
234696
- id: `reg_${(0, import_node_crypto55.randomUUID)()}`,
235023
+ id: `reg_${(0, import_node_crypto56.randomUUID)()}`,
234697
235024
  actor: entry.by,
234698
235025
  kind: "registry_change",
234699
235026
  target: entry.actorId,
@@ -234733,11 +235060,11 @@ function createActorsDomain(opts) {
234733
235060
  register: actorsDomain({ resolveCtx, ...opts.resolveTrace ? { resolveTrace: opts.resolveTrace } : {}, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.companyMemberRole ? { companyMemberRole: opts.companyMemberRole } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {}, ...opts.skillMarket ? { skillMarket: opts.skillMarket } : {}, ...opts.resolveNodeTenancy ? { resolveNodeTenancy: opts.resolveNodeTenancy } : {}, ...opts.agentManageGuard ? { agentManageGuard: opts.agentManageGuard } : {} })
234734
235061
  };
234735
235062
  }
234736
- var import_node_crypto55;
235063
+ var import_node_crypto56;
234737
235064
  var init_actors = __esm({
234738
235065
  "../server/src/domains/actors/index.ts"() {
234739
235066
  "use strict";
234740
- import_node_crypto55 = require("node:crypto");
235067
+ import_node_crypto56 = require("node:crypto");
234741
235068
  init_service5();
234742
235069
  init_routes3();
234743
235070
  init_service5();
@@ -236426,6 +236753,13 @@ function isPendingActor(id) {
236426
236753
  function isAgentActor(id) {
236427
236754
  return typeof id === "string" && id.startsWith("actor:agent:");
236428
236755
  }
236756
+ function isBlockingBackpressure(i) {
236757
+ if (i.kind === "gap" || i.kind === "escalation") return false;
236758
+ if (i.intent === "advisory") return false;
236759
+ const from = i.raisedByNodeId;
236760
+ const about = i.aboutNodeId;
236761
+ return !!from && !!about && from !== about;
236762
+ }
236429
236763
  function isDispatchLayerFailure(detail) {
236430
236764
  const reason = detail?.reason;
236431
236765
  return typeof reason === "string" && reason.startsWith("dispatch ");
@@ -236650,12 +236984,8 @@ function buildWorkorderActivity(input) {
236650
236984
  } else if (r.decision === "approved" || r.decision === "completed") h.to = h.to ?? r.decidedAt ?? r.at;
236651
236985
  }
236652
236986
  for (const i of snap.issues) {
236653
- if (i.kind === "gap" || i.kind === "escalation") continue;
236654
- if (i.intent === "advisory") continue;
236655
- const from = i.raisedByNodeId;
236656
- const about = i.aboutNodeId;
236657
- if (!from || !about || from === about) continue;
236658
- addHold(from, { kind: "backpressure", from: i.createdAt, to: i.resolvedAt ?? null, upstream: about });
236987
+ if (!isBlockingBackpressure(i)) continue;
236988
+ addHold(i.raisedByNodeId, { kind: "backpressure", from: i.createdAt, to: i.resolvedAt ?? null, upstream: i.aboutNodeId });
236659
236989
  }
236660
236990
  const HOLD_RANK = { paused: 0, continuationDenied: 1, continuation: 2, backpressure: 3 };
236661
236991
  const holdOf2 = (w2) => {
@@ -236675,11 +237005,50 @@ function buildWorkorderActivity(input) {
236675
237005
  };
236676
237006
  const lastStampOf = (w2) => [w2.createdAt, w2.startedAt, w2.endedAt, w2.deadAt, w2.cancelledAt].filter((t) => !!t).sort().at(-1);
236677
237007
  const concludedRootBriefNodeIds = /* @__PURE__ */ new Set();
237008
+ const blockingBackpressure = snap.issues.filter((i) => isBlockingBackpressure(i));
237009
+ const blockingBackpressureIds = new Set(blockingBackpressure.map((i) => i.id));
237010
+ const backpressureRaisedBy = /* @__PURE__ */ new Map();
237011
+ for (const i of [...blockingBackpressure].sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id))) {
237012
+ const from = i.raisedByNodeId;
237013
+ backpressureRaisedBy.set(from, [...backpressureRaisedBy.get(from) ?? [], i]);
237014
+ }
237015
+ const backpressureOnRaiserCard = /* @__PURE__ */ new Set();
237016
+ const backpressureDriving = /* @__PURE__ */ new Map();
237017
+ {
237018
+ const add = (workId, issueId) => {
237019
+ if (!issueId || !blockingBackpressureIds.has(issueId)) return;
237020
+ const list2 = backpressureDriving.get(workId) ?? [];
237021
+ if (!list2.includes(issueId)) list2.push(issueId);
237022
+ backpressureDriving.set(workId, list2);
237023
+ };
237024
+ for (const rec2 of events) {
237025
+ const ev = rec2.event;
237026
+ if (ev.kind !== "work.create" || !ev.workId) continue;
237027
+ for (const r of ev.reasons ?? []) if (r.kind === "issue") add(ev.workId, r.issueId);
237028
+ }
237029
+ for (const w2 of snap.works) for (const id of w2.frozenIssueIds ?? []) add(w2.id, id);
237030
+ }
237031
+ const backpressureTargetName = (nodeId) => nodeId && rootBriefNodeIds.has(nodeId) ? "\u4EFB\u52A1\u8BF4\u660E" : nodeName(nodeId);
237032
+ const backpressureOwnerOf = (nodeId) => {
237033
+ const assignee = nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0;
237034
+ return assignee && !isPendingActor(assignee) ? assignee : ownerId;
237035
+ };
237036
+ const backpressureRaisersOf = (workId) => {
237037
+ const ids2 = backpressureDriving.get(workId);
237038
+ if (!ids2?.length) return void 0;
237039
+ const authors = [...new Set(ids2.map((id) => issueById.get(id)?.authorActorId).filter((a) => !!a))];
237040
+ return authors.length ? authors.map(nameOf2).join("\u3001") : void 0;
237041
+ };
236678
237042
  for (const [nodeId, works] of worksOfNode) {
236679
237043
  const interrupts = [...interruptsOfNode.get(nodeId) ?? []].sort((a, b2) => a.at.localeCompare(b2.at) || a.issueId.localeCompare(b2.issueId));
236680
237044
  const trips = stopLossOfNode.get(nodeId) ?? [];
236681
237045
  const interventions = [...interventionsOfNode.get(nodeId) ?? []].sort();
236682
237046
  const humanBetween = (prev, w2) => !!prev && interventions.some((t) => t >= lastStampOf(prev) && t <= w2.createdAt);
237047
+ const upstreamRoundBetween = (prev, w2) => {
237048
+ if (!prev) return false;
237049
+ const ancestors = ancestorsOf(nodeId);
237050
+ return snap.works.some((u) => u.lane !== "reply" && ancestors.has(u.nodeId) && u.createdAt > prev.createdAt && u.createdAt <= w2.createdAt);
237051
+ };
236683
237052
  const node2 = nodeById.get(nodeId);
236684
237053
  const nodeCancelledAt = node2?.cancelledAt ?? null;
236685
237054
  const name = nodeName(nodeId);
@@ -236691,17 +237060,20 @@ function buildWorkorderActivity(input) {
236691
237060
  const st = statusOf2(w2);
236692
237061
  const nextStart = nextStartOf.get(w2.id) ?? null;
236693
237062
  const interrupt = interrupts.find((t) => t.at >= w2.createdAt && (!nextStart || t.at < nextStart));
237063
+ const roundEndedAt = w2.endedAt ?? w2.deadAt ?? w2.cancelledAt;
237064
+ const raisedHere = !w2.conclusion && !interrupt ? (backpressureRaisedBy.get(nodeId) ?? []).filter((i) => i.createdAt >= w2.createdAt && (!nextStart || i.createdAt < nextStart) && (!roundEndedAt || i.createdAt <= roundEndedAt)) : [];
237065
+ const halted = !!interrupt || raisedHere.length > 0;
236694
237066
  const tripsHere = trips.filter((t) => t.at >= w2.createdAt && (!nextStart || t.at < nextStart));
236695
237067
  const removedHere = !!nodeCancelledAt && nodeCancelledAt >= w2.createdAt && (!next || nodeCancelledAt < next.createdAt);
236696
237068
  const reassigned = !!next && next.assigneeActorId !== w2.assigneeActorId;
236697
237069
  const killedByPlan = !!(w2.deadAt || w2.cancelledAt) && !!next && (reassigned || (next.nodeVersion ?? 0) > (w2.nodeVersion ?? 0));
236698
237070
  const killedAtOfRound = w2.deadAt ?? w2.cancelledAt;
236699
- const supersededBy = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && killedAtOfRound && next && !killedByWorkorderPause(w2, killedAtOfRound) ? supersedingUpstreamVersion(nodeId, w2, killedAtOfRound, next) : void 0;
236700
- const failedRound = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && !supersededBy && (!!w2.deadAt || !!w2.cancelledAt || st === "failed" || st === "retry" || noOutputRound(w2));
237071
+ const supersededBy = !w2.conclusion && !halted && !removedHere && !killedByPlan && killedAtOfRound && next && !killedByWorkorderPause(w2, killedAtOfRound) ? supersedingUpstreamVersion(nodeId, w2, killedAtOfRound, next) : void 0;
237072
+ const failedRound = !w2.conclusion && !halted && !removedHere && !killedByPlan && !supersededBy && (!!w2.deadAt || !!w2.cancelledAt || st === "failed" || st === "retry" || noOutputRound(w2));
236701
237073
  const foldableFailure = failedRound && !(killedAtOfRound && killedByWorkorderPause(w2, killedAtOfRound));
236702
- const ackedRound = st === "success" && !!w2.upstreamAck && !w2.conclusion && artifacts.length === 0 && !interrupt && !removedHere && !killedByPlan && !supersededBy && !failedRound;
236703
- const held = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && !supersededBy && !failedRound && !ackedRound ? holdOf2(w2) : void 0;
236704
- const handedOverRound = !w2.conclusion && !interrupt && !removedHere && !killedByPlan && !supersededBy && !failedRound && !held && !ackedRound && st === "success" && !!w2.endedAt && !!next;
237074
+ const ackedRound = st === "success" && !!w2.upstreamAck && !w2.conclusion && artifacts.length === 0 && !halted && !removedHere && !killedByPlan && !supersededBy && !failedRound;
237075
+ const held = !w2.conclusion && !halted && !removedHere && !killedByPlan && !supersededBy && !failedRound && !ackedRound ? holdOf2(w2) : void 0;
237076
+ const handedOverRound = !w2.conclusion && !halted && !removedHere && !killedByPlan && !supersededBy && !failedRound && !held && !ackedRound && st === "success" && !!w2.endedAt && !!next;
236705
237077
  const refiredByFuse = tripsHere.some((t) => t.kind === "output_stall");
236706
237078
  const brokenByHuman = humanBetween(works[idx - 1], w2) && !refiredByFuse;
236707
237079
  if (foldableFailure && fold2 && !brokenByHuman) {
@@ -236714,7 +237086,7 @@ function buildWorkorderActivity(input) {
236714
237086
  if (tripsHere.length) fold2.stopLossTrips = [...fold2.stopLossTrips ?? [], ...tripsHere];
236715
237087
  continue;
236716
237088
  }
236717
- const absorb2 = fold2 && !brokenByHuman && !!w2.conclusion && fold2.executorId === w2.assigneeActorId ? fold2 : null;
237089
+ const absorb2 = fold2 && !brokenByHuman && !!w2.conclusion && fold2.executorId === w2.assigneeActorId && !upstreamRoundBetween(works[idx - 1], w2) ? fold2 : null;
236718
237090
  fold2 = null;
236719
237091
  if (handedOverRound && handoverFold && handoverFold.executorId === w2.assigneeActorId) {
236720
237092
  handoverFold.traceWorkIds.push(w2.id);
@@ -236749,6 +237121,7 @@ function buildWorkorderActivity(input) {
236749
237121
  }
236750
237122
  executionCards.push(card2);
236751
237123
  cardOfWork.set(w2.id, card2);
237124
+ if (isHumanActorId(card2.executorId) && backpressureDriving.has(w2.id)) card2.humanTrace = true;
236752
237125
  if (w2.conclusion) {
236753
237126
  card2.phase = "done";
236754
237127
  if (rootBriefNodeIds.has(nodeId)) {
@@ -236764,6 +237137,10 @@ function buildWorkorderActivity(input) {
236764
237137
  card2.status = awaits ? ACTIVITY_COPY.doneAwaitingReview(name) : ACTIVITY_COPY.done(name);
236765
237138
  }
236766
237139
  }
237140
+ const bpRaisers = backpressureRaisersOf(w2.id);
237141
+ if (bpRaisers && (card2.status === ACTIVITY_COPY.done(name) || card2.status === ACTIVITY_COPY.goalConfirmed || card2.status === ACTIVITY_COPY.goalRevised)) {
237142
+ card2.status = ACTIVITY_COPY.backpressureAddressed(bpRaisers, backpressureTargetName(nodeId));
237143
+ }
236767
237144
  } else if (interrupt) {
236768
237145
  card2.phase = "stuck";
236769
237146
  card2.segmentOpen = false;
@@ -236772,6 +237149,18 @@ function buildWorkorderActivity(input) {
236772
237149
  card2.actions = [{ kind: "open_thread", label: BUTTON.view, target: gapThreadSessionId(interrupt.authorId), variant: "quiet" }];
236773
237150
  }
236774
237151
  if (interrupt.at > card2.updatedAt) card2.updatedAt = interrupt.at;
237152
+ } else if (raisedHere.length) {
237153
+ card2.phase = "stuck";
237154
+ card2.segmentOpen = false;
237155
+ card2.awaitingHuman = false;
237156
+ const upstreams = [...new Set(raisedHere.map((i) => i.aboutNodeId))];
237157
+ const targets = upstreams.map(backpressureTargetName);
237158
+ const owners = [...new Set(upstreams.map((u) => nameOf2(backpressureOwnerOf(u))))];
237159
+ card2.status = artifacts.length ? ACTIVITY_COPY.backpressureAfterStage(name, targets, owners) : ACTIVITY_COPY.backpressure(nameOf2(card2.executorId), targets, owners);
237160
+ for (const i of raisedHere) {
237161
+ backpressureOnRaiserCard.add(i.id);
237162
+ if (i.createdAt > card2.updatedAt) card2.updatedAt = i.createdAt;
237163
+ }
236775
237164
  } else if (supersededBy) {
236776
237165
  card2.phase = "stuck";
236777
237166
  card2.segmentOpen = false;
@@ -236934,7 +237323,7 @@ function buildWorkorderActivity(input) {
236934
237323
  const forcedBy = str5(reasons.find((r) => r.kind === "forced")?.actorId);
236935
237324
  const parentWorkId = str5(workById.get(workId)?.parentWorkId);
236936
237325
  const reworkOfRejected = !!parentWorkId && workById.get(parentWorkId)?.acceptanceState === "rejected";
236937
- const status = reasonKinds.has("rework") || reworkOfRejected ? ACTIVITY_COPY.reworking(nodeName(nodeId)) : reasonKinds.has("resumed") ? ACTIVITY_COPY.resumed(nameOf2(d.executorId), nodeName(nodeId)) : reasonKinds.has("spec_changed") ? ACTIVITY_COPY.specChanged(nodeName(nodeId)) : reasonKinds.has("issue") ? ACTIVITY_COPY.issueDriven(nodeName(nodeId)) : forcedBy ? ACTIVITY_COPY.forcedRerun(nameOf2(forcedBy), nodeName(nodeId)) : ACTIVITY_COPY.working(nodeName(nodeId));
237326
+ const status = reasonKinds.has("rework") || reworkOfRejected ? ACTIVITY_COPY.reworking(nodeName(nodeId)) : reasonKinds.has("resumed") ? ACTIVITY_COPY.resumed(nameOf2(d.executorId), nodeName(nodeId)) : reasonKinds.has("spec_changed") ? ACTIVITY_COPY.specChanged(nodeName(nodeId)) : reasonKinds.has("issue") ? backpressureRaisersOf(workId) ? ACTIVITY_COPY.backpressureInProgress(backpressureRaisersOf(workId), backpressureTargetName(nodeId)) : ACTIVITY_COPY.issueDriven(nodeName(nodeId)) : forcedBy ? ACTIVITY_COPY.forcedRerun(nameOf2(forcedBy), nodeName(nodeId)) : ACTIVITY_COPY.working(nodeName(nodeId));
236938
237327
  for (const r of reasons) {
236939
237328
  if (r.kind !== "issue") continue;
236940
237329
  const issueId = str5(r.issueId);
@@ -237052,6 +237441,7 @@ function buildWorkorderActivity(input) {
237052
237441
  const raisedFrom = str5(ev.raisedByNodeId);
237053
237442
  const isBackpressure = !isEscalation && !isGap && !!raisedFrom && raisedFrom !== nodeId;
237054
237443
  if (isBackpressure) backpressureIssueIds.add(issueId);
237444
+ if (isBackpressure && backpressureOnRaiserCard.has(issueId)) break;
237055
237445
  const body2 = str5(ev.body) ?? "";
237056
237446
  if (isEscalation) {
237057
237447
  const linkedGapId = str5(ev.gapId);
@@ -237106,8 +237496,8 @@ function buildWorkorderActivity(input) {
237106
237496
  ...isBackpressure ? { segmentOpen: false, awaitingHuman: false } : {},
237107
237497
  status: isBackpressure ? ACTIVITY_COPY.backpressure(
237108
237498
  nameOf2(authorId),
237109
- nodeName(nodeId),
237110
- nameOf2((nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0) ?? ownerId)
237499
+ [backpressureTargetName(nodeId)],
237500
+ [nameOf2((nodeId ? nodeById.get(nodeId)?.assigneeActorId : void 0) ?? ownerId)]
237111
237501
  ) : ACTIVITY_COPY.commentRaised(nameOf2(authorId), nameOf2(commentTargetId ?? ownerId), nodeName(nodeId)),
237112
237502
  /* **正文一律不上卡**:缺口去缺口线、回压去产物批注、留言去留言线。
237113
237503
  此前只有缺口/回压被排除,留言仍是「正文当小状态 + 展开区再印一遍」,
@@ -237441,7 +237831,7 @@ function buildWorkorderActivity(input) {
237441
237831
  card2.phaseLabel = PHASE_LABEL.escalationPending;
237442
237832
  card2.segmentOpen = true;
237443
237833
  card2.awaitingHuman = true;
237444
- card2.status = ACTIVITY_COPY.backpressureAwaitingOwner(nameOf2(card2.executorId), nodeName(card2.nodeId));
237834
+ card2.status = ACTIVITY_COPY.backpressureAwaitingOwner(nameOf2(card2.executorId), backpressureTargetName(card2.nodeId));
237445
237835
  card2.actions = [{ kind: "open_issue", label: BUTTON.handle, target: issueId, variant: "primary" }];
237446
237836
  }
237447
237837
  for (const card2 of executionCards) {
@@ -237644,7 +238034,7 @@ function buildWorkorderActivity(input) {
237644
238034
  artifacts: d.artifacts,
237645
238035
  actions: d.actions,
237646
238036
  ...traced && d.traceAvailable ? { traceAvailable: true } : {},
237647
- ...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, ...traced ? { traceAvailable: true } : {} } : {},
238037
+ ...d.traceWorkIds?.length ? { traceWorkIds: d.traceWorkIds, ...traced || d.humanTrace ? { traceAvailable: true } : {} } : {},
237648
238038
  .../* @__PURE__ */ ((r) => r ? { runId: r, traceAvailable: true } : {})(traced ? runIdOfCard(d) : void 0)
237649
238039
  };
237650
238040
  });
@@ -237901,6 +238291,7 @@ var init_activity2 = __esm({
237901
238291
  heldByContinuation: (node2) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\u9636\u6BB5\u6210\u679C\uFF0C\u672C\u6279\u7EED\u8DD1\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u7B49\u5F85\u6279\u51C6\u7EE7\u7EED\u3002`,
237902
238292
  heldByContinuationDenied: (node2) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\u9636\u6BB5\u6210\u679C\uFF0C\u7EED\u8DD1\u7533\u8BF7\u672A\u83B7\u6279\u51C6\uFF0C\u8282\u70B9\u5DF2\u6682\u505C\uFF0C\u7B49\u5F85\u653E\u884C\u3002`,
237903
238293
  heldByWorkorderPause: (node2) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\u9636\u6BB5\u6210\u679C\uFF0C\u4EFB\u52A1\u5DF2\u6682\u505C\uFF0C\u7B49\u5F85\u6062\u590D\u3002`,
238294
+ /** 只剩「交完稿、这一轮结束之后才补提」的那一档;同一轮里提的见 {@link ACTIVITY_COPY.backpressureAfterStage}(ADR 0623 补章)。 */
237904
238295
  heldByBackpressure: (node2, upstreams) => `\u300A${node2}\u300B\u5DF2\u4EA4\u4ED8\uFF0C\u7B49\u5F85${upstreams.map((u) => `\u300A${u}\u300B`).join("\u3001")}\u5904\u7406\u4FEE\u6539\u8981\u6C42\u540E\u518D\u6536\u53E3\u3002`,
237905
238296
  /** `works.status=retry`:这一轮没跑成,等引擎重派。谁按的重试由 `plan.node_retry` 补进这句。 */
237906
238297
  awaitingRedispatch: (executor, node2) => `${executor} \u8FD9\u4E00\u8F6E\u6CA1\u8DD1\u6210\u300A${node2}\u300B\uFF0C\u7B49\u5F85\u91CD\u6D3E\u3002`,
@@ -237953,8 +238344,27 @@ var init_activity2 = __esm({
237953
238344
  (「{X} 遇到处理不了的问题,需要 @{Y} 的协助」)——**别在别处复用**:系统代开的两种止损
237954
238345
  上报根本没有人「开口」,套这句话是假话(生产上它已经把 `actor:system` 渲染成了句子的主语)。
237955
238346
  ①「缺口卡」也删掉(F4):它和执行卡挂的是同一段运行、同一份轨迹,点哪张打开的都一样。 */
237956
- /** 回压(下游对上游提修改要求)——发起人 2026-09-08 认可的措辞。 */
237957
- backpressure: (raiser, upstream, owner) => `${raiser} \u5BF9\u300A${upstream}\u300B\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 @${owner} \u5904\u7406\u3002`,
238347
+ /**
238348
+ * 回压(下游对上游提修改要求)——发起人 2026-09-08 认可的措辞。
238349
+ *
238350
+ * 2026-09-23 起它是**提出方那一轮执行卡的结局句**(奕璇定,与缺口 F4 同一做法):提要求这件事就发生在
238351
+ * 那一轮里,不再单独出一张 `issue:` 卡。同一轮对几个上游提了要求时并成一句(《A》、《B》 / @X、@Y)。
238352
+ * 🚨 **之后一个字不改**:要求关掉了也不翻署名、不改写成「已基于…更新了一版」——那是被要求方那张卡的事。
238353
+ */
238354
+ backpressure: (raiser, upstreams, owners) => `${raiser} \u5BF9${upstreams.map((u) => `\u300A${u}\u300B`).join("\u3001")}\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 ${owners.map((o) => `@${o}`).join("\u3001")} \u5904\u7406\u3002`,
238355
+ /**
238356
+ * 同上,但提出方**这一轮先交了阶段成果**(propose 了、没 conclude)、同一轮又提了修改要求(奕璇 2026-09-23 定,
238357
+ * ADR 0623 补章):此前这一档出两张卡——自己那轮「已交付,等待《X》处理修改要求后再收口」+ 一张 `issue:` 回压卡。
238358
+ * 合成这一句:「交了什么」由卡上 / 轨迹里的交付物说,句子说「停在哪、等谁」。之后同样一个字不改。
238359
+ */
238360
+ backpressureAfterStage: (node2, upstreams, owners) => `\u300A${node2}\u300B\u5DF2\u63D0\u4EA4\u9636\u6BB5\u6210\u679C\uFF0C\u5BF9${upstreams.map((u) => `\u300A${u}\u300B`).join("\u3001")}\u63D0\u51FA\u4FEE\u6539\u8981\u6C42\uFF0C\u9700\u8981 ${owners.map((o) => `@${o}`).join("\u3001")} \u5904\u7406\u3002`,
238361
+ /** 被要求方是 agent、正在按修改要求改的那一轮(取代通用的「正在根据反馈调整」:说清是谁提的)。 */
238362
+ backpressureInProgress: (raisers, node2) => `\u6B63\u5728\u6839\u636E ${raisers} \u7684\u4FEE\u6539\u8981\u6C42\u8C03\u6574\u300A${node2}\u300B\u3002`,
238363
+ /**
238364
+ * 被要求方按修改要求交出了新一版(agent / 真人同一句)。取代「《X》已完成」「已修订目标」——
238365
+ * 此前同一件事还另有一张回压卡收口成「{X} 已基于 {Z} 的留言更新了一版」,一件事三张卡。
238366
+ */
238367
+ backpressureAddressed: (raisers, node2) => `\u5DF2\u6309 ${raisers} \u7684\u4FEE\u6539\u8981\u6C42\u66F4\u65B0\u300A${node2}\u300B\u3002`,
237958
238368
  /**
237959
238369
  * 被回压节点的负责人是**真人**时,系统为他开的那一轮(奕璇 2026-09-22 定):这一轮不会自己跑,
237960
238370
  * 是在等他动手——不能再说「正在根据反馈调整」(`issueDriven`),那句会让一张没人在动的卡挂成「进行中」。
@@ -238113,6 +238523,26 @@ function buildWorkorderActivityTrace(input) {
238113
238523
  if (!briefing) continue;
238114
238524
  firstBriefing ??= briefing;
238115
238525
  const sources = sourcesOfWork(work, input, briefing);
238526
+ const humanBackpressureRound = isHumanActorId(work.assigneeActorId) && sources.some((src) => {
238527
+ const issue2 = src.kind === "comment" ? snap.issues.find((i) => i.id === src.issueId) : void 0;
238528
+ return !!issue2 && isBlockingBackpressure(issue2);
238529
+ });
238530
+ if (humanBackpressureRound) {
238531
+ const before = previousVersionOf(work, snap);
238532
+ works.push({
238533
+ workId,
238534
+ at: work.createdAt,
238535
+ nodeTitle: briefing.nodeTitle,
238536
+ ...briefing.spec ? { spec: briefing.spec } : {},
238537
+ sources,
238538
+ attempts: [],
238539
+ artifacts: [
238540
+ ...before ? artifactsOfWork(before, input).map((a) => ({ ...a, name: `${a.name}\uFF08\u6539\u524D\uFF09` })) : [],
238541
+ ...artifactsOfWork(work, input).map((a) => ({ ...a, name: `${a.name}\uFF08\u6539\u540E\uFF09` }))
238542
+ ]
238543
+ });
238544
+ continue;
238545
+ }
238116
238546
  const artifacts = single.attempts.at(-1)?.artifacts ?? artifactsOfWork(work, input);
238117
238547
  works.push({
238118
238548
  workId,
@@ -238186,6 +238616,13 @@ function sourcesOfWork(work, input, briefing) {
238186
238616
  ...briefing.spec ? { content: briefing.spec } : {}
238187
238617
  }];
238188
238618
  }
238619
+ function previousVersionOf(work, snap) {
238620
+ const delivered = new Set(snap.artifacts.map((a) => a.workId));
238621
+ const parentId = work.parentWorkId;
238622
+ const parent = parentId ? snap.works.find((w2) => w2.id === parentId && delivered.has(w2.id)) : void 0;
238623
+ if (parent) return parent;
238624
+ return snap.works.filter((w2) => w2.nodeId === work.nodeId && w2.id !== work.id && w2.createdAt < work.createdAt && delivered.has(w2.id)).sort((a, b2) => a.createdAt.localeCompare(b2.createdAt) || a.id.localeCompare(b2.id)).at(-1);
238625
+ }
238189
238626
  function artifactsOfWork(work, input) {
238190
238627
  const snap = input.snap;
238191
238628
  const workId = work.id;
@@ -238344,6 +238781,7 @@ var init_activity_trace = __esm({
238344
238781
  "use strict";
238345
238782
  init_src6();
238346
238783
  init_review_activity();
238784
+ init_src();
238347
238785
  init_activity2();
238348
238786
  init_workorder_manager();
238349
238787
  init_src7();
@@ -239255,11 +239693,11 @@ function fromRow(row) {
239255
239693
  for (const [k2, v2] of opt) if (v2 !== void 0 && v2 !== null) r[k2] = v2;
239256
239694
  return r;
239257
239695
  }
239258
- var import_node_crypto56, import_node_fs16, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
239696
+ var import_node_crypto57, import_node_fs16, DEFAULT_PAGE, MAX_PAGE, pageSize, MemoryNotificationStore, ident3, iso2, str2, PostgresNotificationStore;
239259
239697
  var init_notification_store = __esm({
239260
239698
  "../server/src/domains/collab/notification-store.ts"() {
239261
239699
  "use strict";
239262
- import_node_crypto56 = require("node:crypto");
239700
+ import_node_crypto57 = require("node:crypto");
239263
239701
  import_node_fs16 = __toESM(require("node:fs"), 1);
239264
239702
  DEFAULT_PAGE = 50;
239265
239703
  MAX_PAGE = 200;
@@ -239292,7 +239730,7 @@ var init_notification_store = __esm({
239292
239730
  async insert(n) {
239293
239731
  const existing = this.findLive(n.recipient, n.sourceKey, n.category);
239294
239732
  if (existing) return existing;
239295
- const row = { ...n, id: `ntf:${(0, import_node_crypto56.randomUUID)()}`, updatedAt: n.enteredAt };
239733
+ const row = { ...n, id: `ntf:${(0, import_node_crypto57.randomUUID)()}`, updatedAt: n.enteredAt };
239296
239734
  this.rows.set(row.id, row);
239297
239735
  this.save();
239298
239736
  return row;
@@ -239425,7 +239863,7 @@ var init_notification_store = __esm({
239425
239863
  return new _PostgresNotificationStore(sql, schema);
239426
239864
  }
239427
239865
  async insert(n) {
239428
- const id = `ntf:${(0, import_node_crypto56.randomUUID)()}`;
239866
+ const id = `ntf:${(0, import_node_crypto57.randomUUID)()}`;
239429
239867
  const r = await this.sql.query(
239430
239868
  `INSERT INTO ${this.t} (id, recipient, category, source_key, kind, workorder_id, node_id, card_id, actor_id,
239431
239869
  title, workspace_title, entered_at, updated_at, status, read_at, item)
@@ -240554,13 +240992,13 @@ function nameKey(name) {
240554
240992
  function cleanName(name) {
240555
240993
  return name.trim().replace(/\s+/g, " ");
240556
240994
  }
240557
- var import_node_fs17, import_node_path21, import_node_crypto57, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
240995
+ var import_node_fs17, import_node_path21, import_node_crypto58, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
240558
240996
  var init_tags = __esm({
240559
240997
  "../server/src/domains/collab/tags.ts"() {
240560
240998
  "use strict";
240561
240999
  import_node_fs17 = __toESM(require("node:fs"), 1);
240562
241000
  import_node_path21 = __toESM(require("node:path"), 1);
240563
- import_node_crypto57 = __toESM(require("node:crypto"), 1);
241001
+ import_node_crypto58 = __toESM(require("node:crypto"), 1);
240564
241002
  SEP = "::";
240565
241003
  keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
240566
241004
  prefixOf = (companyId) => `${companyId}${SEP}`;
@@ -240578,7 +241016,7 @@ var init_tags = __esm({
240578
241016
  if (!name) return null;
240579
241017
  if (this.entries(companyId).some((t) => nameKey(t.name) === nameKey(name))) return null;
240580
241018
  const tag = {
240581
- id: `tag-${import_node_crypto57.default.randomBytes(6).toString("hex")}`,
241019
+ id: `tag-${import_node_crypto58.default.randomBytes(6).toString("hex")}`,
240582
241020
  name,
240583
241021
  ...input.color?.trim() ? { color: input.color.trim() } : {},
240584
241022
  ...input.description?.trim() ? { description: input.description.trim() } : {}
@@ -241967,11 +242405,11 @@ async function isEmployedElsewhere(store, companyId, accountId) {
241967
242405
  const memberships = await store.listMembershipsForAccount(accountId);
241968
242406
  return memberships.some((m2) => m2.companyId !== companyId);
241969
242407
  }
241970
- var import_node_crypto58, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
242408
+ var import_node_crypto59, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, INVITE_LINK_TTL_MS, INVITE_LINK_TOKEN_BYTES, INVITE_LINK_ROLE, SLUG_RE, CompanyError, CompaniesService;
241971
242409
  var init_service6 = __esm({
241972
242410
  "../server/src/domains/companies/service.ts"() {
241973
242411
  "use strict";
241974
- import_node_crypto58 = require("node:crypto");
242412
+ import_node_crypto59 = require("node:crypto");
241975
242413
  init_console_url();
241976
242414
  ROLES = ["owner", "admin", "member"];
241977
242415
  INVITABLE_ROLES = ["admin", "member"];
@@ -242070,9 +242508,13 @@ var init_service6 = __esm({
242070
242508
  await this.store.updateCompany(id, { name });
242071
242509
  return { ...company, name };
242072
242510
  }
242073
- /** 设置/清除公司头像引用("blob:<哈希>";null = 回到默认首字母标识)。 */
242511
+ /**
242512
+ * 设置/清除公司头像引用("blob:<哈希>";null = 回到默认首字母标识)。
242513
+ * 权限(本组织 owner/admin)由路由层 `requireOwnerOrAdmin` 强制;这里只守「组织还可用」
242514
+ * ——与本域其余写面同一条判据(D8.2:闸不只长在路由上),解散之后 404 COMPANY_NOT_FOUND。
242515
+ */
242074
242516
  async setAvatar(id, ref2) {
242075
- const company = await this.requireCompany(id);
242517
+ const company = await this.requireActiveCompany(id);
242076
242518
  await this.store.updateCompany(id, { avatar: ref2 });
242077
242519
  const next = ref2 === null ? (({ avatar: _drop, ...rest }) => rest)(company) : { ...company, avatar: ref2 };
242078
242520
  return next;
@@ -242086,7 +242528,7 @@ var init_service6 = __esm({
242086
242528
  const existing = await this.store.getAccountByEmail(normalized4);
242087
242529
  if (existing) return existing;
242088
242530
  const account = {
242089
- id: `actor:human:${(0, import_node_crypto58.randomUUID)()}`,
242531
+ id: `actor:human:${(0, import_node_crypto59.randomUUID)()}`,
242090
242532
  email: normalized4,
242091
242533
  name: name ?? normalized4,
242092
242534
  status: "active"
@@ -242484,7 +242926,7 @@ var init_service6 = __esm({
242484
242926
  }
242485
242927
  const nowMs = Date.parse(this.now());
242486
242928
  const invitation = {
242487
- id: `invitation:${(0, import_node_crypto58.randomUUID)()}`,
242929
+ id: `invitation:${(0, import_node_crypto59.randomUUID)()}`,
242488
242930
  companyId,
242489
242931
  email: mail,
242490
242932
  role,
@@ -242615,7 +243057,7 @@ var init_service6 = __esm({
242615
243057
  const nowMs = Date.parse(this.now());
242616
243058
  const link = {
242617
243059
  companyId,
242618
- token: (0, import_node_crypto58.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
243060
+ token: (0, import_node_crypto59.randomBytes)(INVITE_LINK_TOKEN_BYTES).toString("base64url"),
242619
243061
  role: INVITE_LINK_ROLE,
242620
243062
  createdAt: new Date(nowMs).toISOString(),
242621
243063
  expiresAt: new Date(nowMs + INVITE_LINK_TTL_MS).toISOString(),
@@ -242672,7 +243114,7 @@ var init_service6 = __esm({
242672
243114
  const reusable = existing.find((inv) => !this.isExpired(inv));
242673
243115
  if (reusable) return reusable;
242674
243116
  const invitation = {
242675
- id: `invitation:${(0, import_node_crypto58.randomUUID)()}`,
243117
+ id: `invitation:${(0, import_node_crypto59.randomUUID)()}`,
242676
243118
  companyId: link.companyId,
242677
243119
  email: mail,
242678
243120
  role: link.role,
@@ -242718,9 +243160,9 @@ var init_service6 = __esm({
242718
243160
  * 登录身份查自己那条 membership;不是成员 → 404。
242719
243161
  */
242720
243162
  async completeMemberOnboarding(companyId, accountId) {
242721
- await this.requireCompany(companyId);
242722
243163
  const member = await this.store.getMember(companyId, accountId);
242723
243164
  if (!member) throw new CompanyError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${accountId}`);
243165
+ await this.requireActiveCompany(companyId);
242724
243166
  if (member.onboardingCompletedAt) return member;
242725
243167
  const next = { ...member, onboardingCompletedAt: this.now() };
242726
243168
  await this.store.upsertMember(next);
@@ -242744,9 +243186,9 @@ var init_service6 = __esm({
242744
243186
  async setMemberOnboardingStep(companyId, accountId, step) {
242745
243187
  const trimmed = step?.trim();
242746
243188
  if (!trimmed) throw new CompanyError(400, "BAD_REQUEST", "step \u4E0D\u80FD\u4E3A\u7A7A");
242747
- await this.requireCompany(companyId);
242748
243189
  const member = await this.store.getMember(companyId, accountId);
242749
243190
  if (!member) throw new CompanyError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${accountId}`);
243191
+ await this.requireActiveCompany(companyId);
242750
243192
  const next = { ...member, onboardingStep: trimmed };
242751
243193
  await this.store.upsertMember(next);
242752
243194
  return next;
@@ -242906,11 +243348,11 @@ var init_service6 = __esm({
242906
243348
  });
242907
243349
 
242908
243350
  // ../server/src/domains/companies/offboarding.ts
242909
- var import_node_crypto59, OWNER_PROTECTED_MESSAGE, MemberOffboardingService;
243351
+ var import_node_crypto60, OWNER_PROTECTED_MESSAGE, MemberOffboardingService;
242910
243352
  var init_offboarding = __esm({
242911
243353
  "../server/src/domains/companies/offboarding.ts"() {
242912
243354
  "use strict";
242913
- import_node_crypto59 = require("node:crypto");
243355
+ import_node_crypto60 = require("node:crypto");
242914
243356
  init_service6();
242915
243357
  OWNER_PROTECTED_MESSAGE = "\u7EC4\u7EC7\u7BA1\u7406\u5458\u4E0D\u53EF\u88AB\u79FB\u9664\uFF0C\u53EA\u80FD\u7531\u672C\u4EBA\u8F6C\u4EA4\u6216\u89E3\u6563\u7EC4\u7EC7";
242916
243358
  MemberOffboardingService = class {
@@ -242987,7 +243429,7 @@ var init_offboarding = __esm({
242987
243429
  const { ownedAgents, inFlight } = await this.snapshot(companyId, accountId);
242988
243430
  const at2 = this.now();
242989
243431
  const started = await this.store.startMemberOffboarding({
242990
- operationId: `off:${(0, import_node_crypto59.randomUUID)()}`,
243432
+ operationId: `off:${(0, import_node_crypto60.randomUUID)()}`,
242991
243433
  companyId,
242992
243434
  accountId,
242993
243435
  initiatedBy,
@@ -243219,6 +243661,13 @@ function companiesDomain(opts) {
243219
243661
  await requireActiveCompany(companyId);
243220
243662
  return me;
243221
243663
  };
243664
+ const requireActiveSelfMember = async (req, companyId) => {
243665
+ const me = await whoami(req);
243666
+ const member = await service.getMember(companyId, me.accountId);
243667
+ if (!member) throw new ApiError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${me.accountId}`);
243668
+ await requireActiveCompany(companyId);
243669
+ return { me, member };
243670
+ };
243222
243671
  return (router) => {
243223
243672
  router.get("/api/companies", async (req) => {
243224
243673
  const me = await whoami(req);
@@ -243268,13 +243717,17 @@ function companiesDomain(opts) {
243268
243717
  return { status: 201, body: company };
243269
243718
  });
243270
243719
  router.post("/api/companies/:id/avatar", async (req) => {
243720
+ const cid = req.params.id;
243721
+ await requireOwnerOrAdmin(req, cid);
243271
243722
  if (!blobs) throw new ApiError(501, "NOT_IMPLEMENTED", "blob \u5B58\u50A8\u672A\u63A5\u5165");
243272
243723
  const ref2 = await storeImageBlob(blobs, req.body?.data);
243273
- const company = await service.setAvatar(req.params.id, ref2).catch(mapErr);
243724
+ const company = await service.setAvatar(cid, ref2).catch(mapErr);
243274
243725
  return { status: 201, body: { avatar: company.avatar } };
243275
243726
  });
243276
243727
  router.delete("/api/companies/:id/avatar", async (req) => {
243277
- await service.setAvatar(req.params.id, null).catch(mapErr);
243728
+ const cid = req.params.id;
243729
+ await requireOwnerOrAdmin(req, cid);
243730
+ await service.setAvatar(cid, null).catch(mapErr);
243278
243731
  return { status: 200, body: { ok: true } };
243279
243732
  });
243280
243733
  router.get("/api/companies/:id/members", async (req) => {
@@ -243284,23 +243737,23 @@ function companiesDomain(opts) {
243284
243737
  return { status: 200, body: { items } };
243285
243738
  });
243286
243739
  router.get("/api/companies/:id/members/me", async (req) => {
243287
- const me = await whoami(req);
243288
- const member = await service.getMember(req.params.id, me.accountId);
243289
- if (!member) throw new ApiError(404, "NOT_FOUND", `\u4E0D\u662F\u8BE5\u516C\u53F8\u6210\u5458\uFF1A${me.accountId}`);
243740
+ const { member } = await requireActiveSelfMember(req, req.params.id);
243290
243741
  return { status: 200, body: member };
243291
243742
  });
243292
243743
  router.post("/api/companies/:id/members/me/onboarding-complete", async (req) => {
243293
- const me = await whoami(req);
243294
- const member = await service.completeMemberOnboarding(req.params.id, me.accountId).catch(mapErr);
243744
+ const cid = req.params.id;
243745
+ const { me } = await requireActiveSelfMember(req, cid);
243746
+ const member = await service.completeMemberOnboarding(cid, me.accountId).catch(mapErr);
243295
243747
  return { status: 200, body: member };
243296
243748
  });
243297
243749
  router.post("/api/companies/:id/members/me/onboarding-step", async (req) => {
243298
- const me = await whoami(req);
243750
+ const cid = req.params.id;
243751
+ const { me } = await requireActiveSelfMember(req, cid);
243299
243752
  const body2 = req.body ?? {};
243300
243753
  if (typeof body2.step !== "string") {
243301
243754
  throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 step: string");
243302
243755
  }
243303
- const member = await service.setMemberOnboardingStep(req.params.id, me.accountId, body2.step).catch(mapErr);
243756
+ const member = await service.setMemberOnboardingStep(cid, me.accountId, body2.step).catch(mapErr);
243304
243757
  return { status: 200, body: member };
243305
243758
  });
243306
243759
  router.patch("/api/companies/:id/members/:accountId", async (req) => {
@@ -243598,18 +244051,18 @@ var init_routes6 = __esm({
243598
244051
 
243599
244052
  // ../server/src/domains/companies/agent-credentials.ts
243600
244053
  function hashAgentToken(token, secret) {
243601
- return HASH_PREFIX + (0, import_node_crypto60.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
244054
+ return HASH_PREFIX + (0, import_node_crypto61.createHmac)("sha256", secret).update(`agent-token:${token}`).digest("base64url");
243602
244055
  }
243603
244056
  function safeEqual(a, b2) {
243604
244057
  const ba = Buffer.from(a);
243605
244058
  const bb = Buffer.from(b2);
243606
- return ba.length === bb.length && (0, import_node_crypto60.timingSafeEqual)(ba, bb);
244059
+ return ba.length === bb.length && (0, import_node_crypto61.timingSafeEqual)(ba, bb);
243607
244060
  }
243608
- var import_node_crypto60, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
244061
+ var import_node_crypto61, HASH_PREFIX, TOKEN_PREFIX, AgentCredentialService;
243609
244062
  var init_agent_credentials = __esm({
243610
244063
  "../server/src/domains/companies/agent-credentials.ts"() {
243611
244064
  "use strict";
243612
- import_node_crypto60 = require("node:crypto");
244065
+ import_node_crypto61 = require("node:crypto");
243613
244066
  init_service6();
243614
244067
  HASH_PREFIX = "ac1$";
243615
244068
  TOKEN_PREFIX = "oak_";
@@ -243634,7 +244087,7 @@ var init_agent_credentials = __esm({
243634
244087
  if (employee.status !== "active") {
243635
244088
  throw new CompanyError(409, "AGENT_LEFT", `\u6570\u5B57\u5458\u5DE5\u5DF2\u79BB\u5F00\uFF0C\u4E0D\u80FD\u7B7E\u53D1\u51ED\u8BC1\uFF1A${agentId}`);
243636
244089
  }
243637
- const token = `${TOKEN_PREFIX}${(0, import_node_crypto60.randomBytes)(32).toString("base64url")}`;
244090
+ const token = `${TOKEN_PREFIX}${(0, import_node_crypto61.randomBytes)(32).toString("base64url")}`;
243638
244091
  const credential = {
243639
244092
  tokenHash: hashAgentToken(token, this.secret),
243640
244093
  companyId,
@@ -244146,7 +244599,7 @@ function gatewayInstanceSlug(gatewayUrl) {
244146
244599
  }
244147
244600
  function connectInstanceName(gatewayUrl, token, companyId) {
244148
244601
  const scope2 = companyId?.trim();
244149
- const suffix = scope2 ? `c${(0, import_node_crypto61.createHash)("sha256").update(`${gatewayUrl}\0${scope2}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
244602
+ const suffix = scope2 ? `c${(0, import_node_crypto62.createHash)("sha256").update(`${gatewayUrl}\0${scope2}`).digest("hex").slice(0, 10)}` : token.replace(/[^a-z0-9]/gi, "").slice(-8).toLowerCase() || "enroll";
244150
244603
  const base = gatewayInstanceSlug(gatewayUrl);
244151
244604
  return base.slice(0, Math.max(1, 64 - suffix.length - 1)) + "-" + suffix;
244152
244605
  }
@@ -244475,12 +244928,12 @@ function buildNpxCmd(p2) {
244475
244928
  const prefix = daemonNpmPrefix();
244476
244929
  return `npm install -g --prefer-online --prefix ${prefix} ${DAEMON_NPM_PACKAGE}@latest && ${prefix}/bin/oasis daemon start --instance ${shellQuote(instanceName)} --server-ws ${shellQuote(p2.gatewayUrl)}${companyPart}${namePart} --pkg ${shellQuote(DAEMON_NPM_PACKAGE)} --upgrade --enroll-token ${shellQuote(p2.token)}`;
244477
244930
  }
244478
- var import_node_crypto61;
244931
+ var import_node_crypto62;
244479
244932
  var init_connect_script = __esm({
244480
244933
  "../server/src/domains/nodes/connect-script.ts"() {
244481
244934
  "use strict";
244482
244935
  init_src();
244483
- import_node_crypto61 = require("node:crypto");
244936
+ import_node_crypto62 = require("node:crypto");
244484
244937
  }
244485
244938
  });
244486
244939
 
@@ -244940,7 +245393,7 @@ function nodesDomain(deps) {
244940
245393
  if (!daemon.meta.adapters.some((kind) => QUOTA_CAPABLE_KINDS.has(kind))) {
244941
245394
  return { ok: false, code: "no-quota-runtime", reason: "\u8FD9\u53F0\u673A\u5668\u4E0A\u6CA1\u6709\u4F1A\u4E0A\u62A5\u989D\u5EA6\u7684\u8FD0\u884C\u65F6\uFF08\u53EA\u6709 Claude Code \u548C Codex \u80FD\u67E5\u5230\u8BA2\u9605\u989D\u5EA6\uFF09" };
244942
245395
  }
244943
- const requestId = (0, import_node_crypto62.randomUUID)();
245396
+ const requestId = (0, import_node_crypto63.randomUUID)();
244944
245397
  const task = new Promise((resolve12) => {
244945
245398
  const timer = setTimeout(() => {
244946
245399
  quotaRefreshWaiters.delete(requestId);
@@ -245316,7 +245769,7 @@ function nodesDomain(deps) {
245316
245769
  }
245317
245770
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
245318
245771
  } else {
245319
- nodeId = `node-${(0, import_node_crypto62.randomUUID)().slice(0, 8)}`;
245772
+ nodeId = `node-${(0, import_node_crypto63.randomUUID)().slice(0, 8)}`;
245320
245773
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
245321
245774
  }
245322
245775
  const existingNode = await deps.nodeStore.getNode(nodeId);
@@ -245582,16 +246035,16 @@ function normalizeGatewayUrl(raw) {
245582
246035
  }
245583
246036
  }
245584
246037
  function newNodeId() {
245585
- return "node-" + (0, import_node_crypto62.randomUUID)().replace(/-/g, "").slice(0, 12);
246038
+ return "node-" + (0, import_node_crypto63.randomUUID)().replace(/-/g, "").slice(0, 12);
245586
246039
  }
245587
- var import_node_crypto62, import_node_fs18, import_node_url6, import_node_path22, NODE_NAME_MAX_LENGTH;
246040
+ var import_node_crypto63, import_node_fs18, import_node_url6, import_node_path22, NODE_NAME_MAX_LENGTH;
245588
246041
  var init_routes7 = __esm({
245589
246042
  "../server/src/domains/nodes/routes.ts"() {
245590
246043
  "use strict";
245591
246044
  init_src();
245592
246045
  init_faults();
245593
246046
  init_runtime_status();
245594
- import_node_crypto62 = require("node:crypto");
246047
+ import_node_crypto63 = require("node:crypto");
245595
246048
  import_node_fs18 = require("node:fs");
245596
246049
  import_node_url6 = require("node:url");
245597
246050
  import_node_path22 = require("node:path");
@@ -247258,7 +247711,7 @@ function createBundle(root, target, input) {
247258
247711
  }
247259
247712
  }
247260
247713
  const bytes2 = fs29.statSync(absBundle).size;
247261
- const digest = (0, import_node_crypto63.createHash)("sha256").update(fs29.readFileSync(absBundle)).digest("hex");
247714
+ const digest = (0, import_node_crypto64.createHash)("sha256").update(fs29.readFileSync(absBundle)).digest("hex");
247262
247715
  const relBase = `inputs/git-bundles/${safeRepo}-${commit.slice(0, 12)}`;
247263
247716
  return {
247264
247717
  absoluteBundlePath: absBundle,
@@ -247354,12 +247807,12 @@ function classifyGitError(fallback, err, where) {
247354
247807
  function oneLine2(msg) {
247355
247808
  return msg.split("\n").map((s2) => s2.trim()).filter(Boolean).slice(0, 3).join(" ");
247356
247809
  }
247357
- var import_node_child_process17, import_node_crypto63, fs29, os8, path26, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
247810
+ var import_node_child_process17, import_node_crypto64, fs29, os8, path26, GitReadonlyBundleError, HEX40, FETCH_TIMEOUT_MS2;
247358
247811
  var init_readonly_bundle = __esm({
247359
247812
  "../server/src/git/readonly-bundle.ts"() {
247360
247813
  "use strict";
247361
247814
  import_node_child_process17 = require("node:child_process");
247362
- import_node_crypto63 = require("node:crypto");
247815
+ import_node_crypto64 = require("node:crypto");
247363
247816
  fs29 = __toESM(require("node:fs"), 1);
247364
247817
  os8 = __toESM(require("node:os"), 1);
247365
247818
  path26 = __toESM(require("node:path"), 1);
@@ -247380,7 +247833,7 @@ var init_readonly_bundle = __esm({
247380
247833
  // ../server/src/domains/collab/create-seeded-workorder.ts
247381
247834
  function makeSeededWorkorderCreator(deps) {
247382
247835
  const locks = createKeyedMutex();
247383
- const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto64.randomUUID)().slice(0, 8)}`);
247836
+ const genWorkspace = deps.genWorkspace ?? (() => `ws:wo-${(0, import_node_crypto65.randomUUID)().slice(0, 8)}`);
247384
247837
  const create = async (input) => {
247385
247838
  const company = input.companyId && deps.resolveCompany ? await deps.resolveCompany(input.companyId) : void 0;
247386
247839
  const kernel = company?.kernel ?? deps.kernel;
@@ -247467,7 +247920,7 @@ function makeSeededWorkorderCreator(deps) {
247467
247920
  return await finishCreation({ workspace, rootArtifactId: planned.rootArtifactId, spawned: planned.spawned });
247468
247921
  };
247469
247922
  return async (input) => {
247470
- const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto64.randomUUID)()}`);
247923
+ const release = await locks.acquire(`${input.companyId ?? ""}\0${input.idempotencyKey ?? (0, import_node_crypto65.randomUUID)()}`);
247471
247924
  try {
247472
247925
  return await create(input);
247473
247926
  } finally {
@@ -247476,7 +247929,7 @@ function makeSeededWorkorderCreator(deps) {
247476
247929
  };
247477
247930
  }
247478
247931
  function workspaceForKey(key2) {
247479
- return `ws:wo-${(0, import_node_crypto64.createHash)("sha256").update(key2).digest("hex").slice(0, 8)}`;
247932
+ return `ws:wo-${(0, import_node_crypto65.createHash)("sha256").update(key2).digest("hex").slice(0, 8)}`;
247480
247933
  }
247481
247934
  function existingWorkorder(snapshot, workspace, actor) {
247482
247935
  if (!snapshot?.nodes.length) return null;
@@ -247488,11 +247941,11 @@ function existingWorkorder(snapshot, workspace, actor) {
247488
247941
  spawned: snapshot.nodes.map((node2) => ({ id: node2.id, type: node2.type, owner: node2.assigneeActorId ?? actor }))
247489
247942
  };
247490
247943
  }
247491
- var import_node_crypto64;
247944
+ var import_node_crypto65;
247492
247945
  var init_create_seeded_workorder = __esm({
247493
247946
  "../server/src/domains/collab/create-seeded-workorder.ts"() {
247494
247947
  "use strict";
247495
- import_node_crypto64 = require("node:crypto");
247948
+ import_node_crypto65 = require("node:crypto");
247496
247949
  init_uncategorized_project();
247497
247950
  init_src();
247498
247951
  init_planner();
@@ -248995,12 +249448,12 @@ function stateFromAttempt(attempt, handoff, checkpoint, now, stalledAfterMs, ope
248995
249448
  if (attempt.status === "no-output") return handoff?.unresolvedIssues.length ? "blocked" : "no_change";
248996
249449
  return "failed";
248997
249450
  }
248998
- var import_node_crypto65, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
249451
+ var import_node_crypto66, BLOCKING_STATES, HANDOFF_OUTCOMES2, RESUME_EXTERNAL_EFFECT_LIMIT, RESUME_SNAPSHOT_PREFIX, RESUME_SNAPSHOT_SUFFIX, ExecutionContinuityService;
248999
249452
  var init_service9 = __esm({
249000
249453
  "../server/src/domains/execution-continuity/service.ts"() {
249001
249454
  "use strict";
249002
249455
  init_src();
249003
- import_node_crypto65 = require("node:crypto");
249456
+ import_node_crypto66 = require("node:crypto");
249004
249457
  init_src7();
249005
249458
  BLOCKING_STATES = /* @__PURE__ */ new Set(["stalled", "failed", "blocked"]);
249006
249459
  HANDOFF_OUTCOMES2 = /* @__PURE__ */ new Set(["succeeded", "no-output", "failed", "cancelled", "timeout", "orphaned"]);
@@ -249231,7 +249684,7 @@ var init_service9 = __esm({
249231
249684
  if (typeof expectedHash !== "string" || !this.readBundleFile) return null;
249232
249685
  const injectedResume = this.readBundleFile(attempt.id, "execution/RESUME.md");
249233
249686
  if (!injectedResume) return null;
249234
- const actualHash = (0, import_node_crypto65.createHash)("sha256").update(injectedResume).digest("hex");
249687
+ const actualHash = (0, import_node_crypto66.createHash)("sha256").update(injectedResume).digest("hex");
249235
249688
  if (actualHash !== expectedHash) return null;
249236
249689
  const recoverySnapshot = parseResumePackSnapshot(injectedResume);
249237
249690
  if (!recoverySnapshot || recoverySnapshot.scenario !== "redispatch" || !recoverySnapshot.previousAttempt || recoverySnapshot.node.nodeId !== attempt.nodeId || recoverySnapshot.jobKey !== attempt.jobKey || recoverySnapshot.part !== attempt.part) return null;
@@ -250413,7 +250866,7 @@ function createChatSessionsDomain(opts) {
250413
250866
  if (!text7.trim()) throw new ApiError(400, "BAD_REQUEST", "text required");
250414
250867
  const now = Date.now();
250415
250868
  sweepDrafts(now);
250416
- const id = `cd_${(0, import_node_crypto66.randomUUID)()}`;
250869
+ const id = `cd_${(0, import_node_crypto67.randomUUID)()}`;
250417
250870
  drafts.set(id, { text: text7, actor: req.auth.actor, expiresAt: now + DRAFT_TTL_MS });
250418
250871
  return { status: 201, body: { id, expiresInMs: DRAFT_TTL_MS } };
250419
250872
  });
@@ -250490,7 +250943,7 @@ function createChatSessionsDomain(opts) {
250490
250943
  const runtimeId = agentDefault.binding?.nodeId ?? "unknown";
250491
250944
  const now = (/* @__PURE__ */ new Date()).toISOString();
250492
250945
  const session = {
250493
- id: (0, import_node_crypto66.randomUUID)(),
250946
+ id: (0, import_node_crypto67.randomUUID)(),
250494
250947
  humanActorId: req.auth.actor,
250495
250948
  // 注:这里**刻意不写 companyId**。新会话要不要带公司归属,按 ws:wo-6d588f8e 的 ADR
250496
250949
  // (每公司 schema、最终移除表内 company 列)统一定,不在本节点自行发明。
@@ -250625,7 +251078,7 @@ function createChatSessionsDomain(opts) {
250625
251078
  if (body2.role !== "user" && body2.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
250626
251079
  const role = body2.role;
250627
251080
  const msg = await store2.appendMessage({
250628
- id: (0, import_node_crypto66.randomUUID)(),
251081
+ id: (0, import_node_crypto67.randomUUID)(),
250629
251082
  sessionId: req.params.id,
250630
251083
  role,
250631
251084
  content: role === "user" ? stripInjectedChatContext(body2.content) : body2.content,
@@ -250667,7 +251120,7 @@ function createChatSessionsDomain(opts) {
250667
251120
  const known = key2 ? cur.find((x2) => x2.clientKey === key2) : void 0;
250668
251121
  if (!known && cur.length >= QUEUE_MAX) throw new ApiError(409, "QUEUE_FULL", `\u961F\u5217\u5DF2\u6EE1\uFF08\u4E0A\u9650 ${QUEUE_MAX} \u6761\uFF09`);
250669
251122
  const item = await store2.enqueuePendingMessage({
250670
- id: (0, import_node_crypto66.randomUUID)(),
251123
+ id: (0, import_node_crypto67.randomUUID)(),
250671
251124
  chatSessionId: sid,
250672
251125
  text: text7,
250673
251126
  ...attachments.length ? { attachments } : {},
@@ -251520,12 +251973,12 @@ async function legacySendFailureCards(entries, deps) {
251520
251973
  }
251521
251974
  return out;
251522
251975
  }
251523
- var import_node_crypto66, DEFAULT_SNAPSHOT_ITEMS, INFLIGHT_INSERT_SETTLE_WAIT_MS, MAX_SNAPSHOT_ITEMS, PROJECTED_CARD_LIMIT, SUBMISSION_NOT_DELIVERED_NOTE, SUBMISSION_UNCONFIRMED_NOTE, LEGACY_CARD_LIMIT;
251976
+ var import_node_crypto67, DEFAULT_SNAPSHOT_ITEMS, INFLIGHT_INSERT_SETTLE_WAIT_MS, MAX_SNAPSHOT_ITEMS, PROJECTED_CARD_LIMIT, SUBMISSION_NOT_DELIVERED_NOTE, SUBMISSION_UNCONFIRMED_NOTE, LEGACY_CARD_LIMIT;
251524
251977
  var init_chat_sessions2 = __esm({
251525
251978
  "../server/src/domains/chat-sessions/index.ts"() {
251526
251979
  "use strict";
251527
251980
  init_file_limits2();
251528
- import_node_crypto66 = require("node:crypto");
251981
+ import_node_crypto67 = require("node:crypto");
251529
251982
  init_src();
251530
251983
  init_chat_session();
251531
251984
  init_persistent_files();
@@ -252985,7 +253438,7 @@ function busyError(chatSessionId, blockedBy) {
252985
253438
  return `\u76EE\u6807\u4F1A\u8BDD ${chatSessionId} \u6B63\u6709\u4E00\u8F6E\u5728\u8DD1${blockedBy ? `\uFF08${blockedBy}\uFF09` : ""}\u2014\u2014\u672C\u6B21\u89E6\u53D1\u5DF2\u4E22\u5F03\uFF0C\u4E0D\u6392\u961F\u3001\u4E0D\u8865\u8DD1`;
252986
253439
  }
252987
253440
  function genWebhookToken() {
252988
- return `owt_${(0, import_node_crypto67.randomBytes)(32).toString("base64url")}`;
253441
+ return `owt_${(0, import_node_crypto68.randomBytes)(32).toString("base64url")}`;
252989
253442
  }
252990
253443
  function validateTitleTemplate(tpl) {
252991
253444
  const residue = tpl.replace(DATE_TOKEN, "");
@@ -253041,11 +253494,11 @@ function appendSourceNote(brief, automation, source, atIso) {
253041
253494
  ---
253042
253495
  \u672C\u5DE5\u5355\u7531\u81EA\u52A8\u5316\u300C${automation.name}\u300D\u4E8E ${atIso} \u89E6\u53D1\uFF08\u6765\u6E90\uFF1A${source}\uFF09\u3002\u5F00\u5DE5\u540E\u8BF7\u628A\u5DE5\u5355\u6807\u9898\u6539\u6210\u51C6\u786E\u53CD\u6620\u5B9E\u9645\u5DE5\u4F5C\u7684\u63CF\u8FF0\u3002`;
253043
253496
  }
253044
- var import_node_crypto67, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
253497
+ var import_node_crypto68, DATE_TOKEN, AutomationsService, EVENT_RETRY_BASE_MS, EVENT_RETRY_MAX_MS, EVENT_RETRY_MAX_ATTEMPTS, eventAttempts, eventLastAttemptAt, eventLastError, CHAT_REPLY_MAX;
253045
253498
  var init_service10 = __esm({
253046
253499
  "../server/src/domains/automations/service.ts"() {
253047
253500
  "use strict";
253048
- import_node_crypto67 = require("node:crypto");
253501
+ import_node_crypto68 = require("node:crypto");
253049
253502
  init_src();
253050
253503
  init_registry3();
253051
253504
  init_attribution();
@@ -253859,11 +254312,11 @@ function verifySignature(rawBody, headers, secret) {
253859
254312
  if (!secret) return "not_required";
253860
254313
  const given = header1(headers, "x-hub-signature-256");
253861
254314
  if (!given) return "missing";
253862
- const expected = `sha256=${(0, import_node_crypto68.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
254315
+ const expected = `sha256=${(0, import_node_crypto69.createHmac)("sha256", secret).update(rawBody).digest("hex")}`;
253863
254316
  const a = Buffer.from(given);
253864
254317
  const b2 = Buffer.from(expected);
253865
254318
  if (a.length !== b2.length) return "invalid";
253866
- return (0, import_node_crypto68.timingSafeEqual)(a, b2) ? "valid" : "invalid";
254319
+ return (0, import_node_crypto69.timingSafeEqual)(a, b2) ? "valid" : "invalid";
253867
254320
  }
253868
254321
  function eventAllowed(filters, envelope) {
253869
254322
  if (!filters || filters.length === 0) return true;
@@ -253893,11 +254346,11 @@ function splitEvent(envelope) {
253893
254346
  }
253894
254347
  return { eventName, actionCandidates: candidates };
253895
254348
  }
253896
- var import_node_crypto68, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
254349
+ var import_node_crypto69, MAX_BODY_BYTES, AutomationWebhookService, header1, PROVIDER_PREFIXES;
253897
254350
  var init_webhook = __esm({
253898
254351
  "../server/src/domains/automations/webhook.ts"() {
253899
254352
  "use strict";
253900
- import_node_crypto68 = require("node:crypto");
254353
+ import_node_crypto69 = require("node:crypto");
253901
254354
  MAX_BODY_BYTES = 256 * 1024;
253902
254355
  AutomationWebhookService = class {
253903
254356
  store;
@@ -255049,7 +255502,7 @@ function playbooksDomain(opts) {
255049
255502
  opts.overrides.set(companyOf(req), ref2, nodeKey, nextOverride);
255050
255503
  try {
255051
255504
  await opts.audit?.({
255052
- id: `reg_${(0, import_node_crypto69.randomUUID)()}`,
255505
+ id: `reg_${(0, import_node_crypto70.randomUUID)()}`,
255053
255506
  actor: req.auth.actor,
255054
255507
  kind: "registry_change",
255055
255508
  target: `playbook:${ref2}#${nodeKey}`,
@@ -255066,11 +255519,11 @@ function playbooksDomain(opts) {
255066
255519
  });
255067
255520
  };
255068
255521
  }
255069
- var import_node_crypto69;
255522
+ var import_node_crypto70;
255070
255523
  var init_routes12 = __esm({
255071
255524
  "../server/src/domains/playbooks/routes.ts"() {
255072
255525
  "use strict";
255073
- import_node_crypto69 = require("node:crypto");
255526
+ import_node_crypto70 = require("node:crypto");
255074
255527
  init_router();
255075
255528
  init_registry3();
255076
255529
  init_planner();
@@ -255199,17 +255652,17 @@ function completionFingerprint(workspace, members) {
255199
255652
  workspace,
255200
255653
  members: [...members].sort((a, b2) => a.artifactId < b2.artifactId ? -1 : a.artifactId > b2.artifactId ? 1 : 0).map((m2) => ({ artifactId: m2.artifactId, currentRevisionId: m2.currentRevisionId ?? null }))
255201
255654
  });
255202
- return (0, import_node_crypto70.createHash)("sha256").update(canonical2).digest("hex");
255655
+ return (0, import_node_crypto71.createHash)("sha256").update(canonical2).digest("hex");
255203
255656
  }
255204
255657
  function eventConsumptionKey(args) {
255205
255658
  const raw = `project-event:${EVENT_KEY_VERSION}:${args.triggerId}:${args.event}:${args.workspace}:${args.fingerprint}`;
255206
- return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto70.createHash)("sha256").update(raw).digest("hex")}`;
255659
+ return raw.length <= 200 ? raw : `project-event:${EVENT_KEY_VERSION}:sha256:${(0, import_node_crypto71.createHash)("sha256").update(raw).digest("hex")}`;
255207
255660
  }
255208
- var import_node_crypto70, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
255661
+ var import_node_crypto71, EVENT_KEY_VERSION, EMPTY_METRICS, AUTOMATION_EVENT_HANDLERS, ProjectEventPoller;
255209
255662
  var init_project_event_poller = __esm({
255210
255663
  "../server/src/automations/project-event-poller.ts"() {
255211
255664
  "use strict";
255212
- import_node_crypto70 = require("node:crypto");
255665
+ import_node_crypto71 = require("node:crypto");
255213
255666
  EVENT_KEY_VERSION = "v1";
255214
255667
  EMPTY_METRICS = () => ({
255215
255668
  scanDurationMs: 0,
@@ -255419,7 +255872,7 @@ function queryInputReceipt(hub, nodeId, dispatchId, clientMessageKey, pending, t
255419
255872
  const viaSession = hub.hasSession?.(dispatchId) === true;
255420
255873
  const supported = viaSession ? hub.supportsInputReceiptQuery?.(dispatchId) === true : daemonMetaOf(hub, nodeId)?.supportsInputReceiptQuery === true;
255421
255874
  if (!supported) return Promise.resolve({ status: "unknown", reason: "unsupported" });
255422
- const requestId = (0, import_node_crypto71.randomUUID)();
255875
+ const requestId = (0, import_node_crypto72.randomUUID)();
255423
255876
  return new Promise((resolve12) => {
255424
255877
  const timer = setTimeout(() => {
255425
255878
  pending.delete(requestId);
@@ -255481,7 +255934,7 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
255481
255934
  if (input.requireConsumptionReceipt && !hub.supportsInputConsumptionReceipts?.(dispatchId)) {
255482
255935
  return Promise.resolve({ accepted: false, reason: "consumption-unsupported" });
255483
255936
  }
255484
- const requestId = (0, import_node_crypto71.randomUUID)();
255937
+ const requestId = (0, import_node_crypto72.randomUUID)();
255485
255938
  const sent = routeToSession(hub, nodeId, dispatchId, { type: "append_input", dispatchId, input, requestId });
255486
255939
  if (!sent) return Promise.resolve({ accepted: false, reason: "session-closing", ...unknownIfInterrupting });
255487
255940
  return new Promise((resolve12) => {
@@ -255494,11 +255947,11 @@ function sendAppendInput(hub, nodeId, dispatchId, entry, input, timeoutMs, sessi
255494
255947
  entry.appendWaiters.push({ requestId, requireConsumptionReceipt: input.requireConsumptionReceipt, resolve: resolve12, timer });
255495
255948
  });
255496
255949
  }
255497
- var import_node_crypto71, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_TOTAL_FRAMES, STASH_MAX_IDS, STASH_LOSS_MAX_IDS, STASH_LOSS_TTL_MS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
255950
+ var import_node_crypto72, STASH_TTL_MS, STASH_MAX_FRAMES_PER_ID, STASH_MAX_TOTAL_FRAMES, STASH_MAX_IDS, STASH_LOSS_MAX_IDS, STASH_LOSS_TTL_MS, DaemonHubAdapter, BindingRouterAdapter, WorkdirBridge;
255498
255951
  var init_daemon_adapter = __esm({
255499
255952
  "../server/src/daemon-adapter.ts"() {
255500
255953
  "use strict";
255501
- import_node_crypto71 = require("node:crypto");
255954
+ import_node_crypto72 = require("node:crypto");
255502
255955
  init_src();
255503
255956
  STASH_TTL_MS = 5 * 6e4;
255504
255957
  STASH_MAX_FRAMES_PER_ID = 1e4;
@@ -255938,7 +256391,7 @@ var init_daemon_adapter = __esm({
255938
256391
  async spawn(job) {
255939
256392
  const nodeId = job.binding?.nodeId;
255940
256393
  if (!nodeId) throw new Error("DaemonHubAdapter \u9700\u8981 job.binding.nodeId\uFF08\u8DEF\u7531\u9519\u8BEF\uFF09");
255941
- const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto71.randomUUID)()}`;
256394
+ const dispatchId = job.dispatchId ?? `dispatch:${(0, import_node_crypto72.randomUUID)()}`;
255942
256395
  if (job.dispatchId && this.pending.has(dispatchId)) {
255943
256396
  return this.recover(dispatchId, nodeId, job.binding?.runtimeKind, job.actor);
255944
256397
  }
@@ -256194,7 +256647,7 @@ var init_daemon_adapter = __esm({
256194
256647
  return daemon?.meta.supportsWorkdirWalk === true;
256195
256648
  }
256196
256649
  request(nodeId, buildFrame) {
256197
- const requestId = (0, import_node_crypto71.randomUUID)();
256650
+ const requestId = (0, import_node_crypto72.randomUUID)();
256198
256651
  const sent = this.hub.dispatch(nodeId, buildFrame(requestId));
256199
256652
  if (!sent) return Promise.resolve({ ok: false, code: "NODE_OFFLINE" });
256200
256653
  return new Promise((resolve12) => {
@@ -256302,13 +256755,13 @@ function classifyPage(page, lastPushedHash, serviceAccount) {
256302
256755
  if (sha(body2) === lastPushedHash) return { kind: "echo" };
256303
256756
  return { kind: "human-edit", content: body2, updatedBy: page.updatedBy };
256304
256757
  }
256305
- var import_node_crypto72, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
256758
+ var import_node_crypto73, sha, enc2, dec, MirrorEngine, MemoryMirrorStateStore, MapMirrorIdentities;
256306
256759
  var init_engine = __esm({
256307
256760
  "../server/src/mirror/engine.ts"() {
256308
256761
  "use strict";
256309
- import_node_crypto72 = require("node:crypto");
256762
+ import_node_crypto73 = require("node:crypto");
256310
256763
  init_src();
256311
- sha = (s2) => (0, import_node_crypto72.createHash)("sha256").update(s2, "utf8").digest("hex");
256764
+ sha = (s2) => (0, import_node_crypto73.createHash)("sha256").update(s2, "utf8").digest("hex");
256312
256765
  enc2 = (s2) => new TextEncoder().encode(s2);
256313
256766
  dec = (b2) => new TextDecoder().decode(b2);
256314
256767
  MirrorEngine = class {
@@ -256589,11 +257042,11 @@ function humanExitCause(exit3) {
256589
257042
  const base = (exit3.reason ? label2[exit3.reason] : void 0) ?? `\u4F1A\u8BDD\u5F02\u5E38\u7ED3\u675F\uFF08${exit3.reason ?? "\u65E0\u9000\u51FA\u4FE1\u606F"}\uFF09`;
256590
257043
  return exit3.errorMessage ? `${base}\uFF1A${exit3.errorMessage.slice(0, 200)}` : base;
256591
257044
  }
256592
- var import_node_crypto73, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, REJECTED_PROPOSALS_IN_TASK, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
257045
+ var import_node_crypto74, AUTONOMOUS_ETHOS, CONVERSATIONAL_ETHOS, SHARED_GRAPH_BODY, AUTONOMOUS_RECOVERY_TOOLS, HIGH_RISK_COMMANDS, REJECTED_PROPOSALS_IN_TASK, COORDINATOR_SYSTEM_PROMPT, CONVERSATIONAL_RECOVERY_TOOLS, CONVERSATIONAL_ESCALATION_TOOLS, CONVERSATIONAL_MANAGER_BODY, MACHINE_EXIT_CODES, CoordinatorWorker;
256593
257046
  var init_worker = __esm({
256594
257047
  "../server/src/coordinator/worker.ts"() {
256595
257048
  "use strict";
256596
- import_node_crypto73 = require("node:crypto");
257049
+ import_node_crypto74 = require("node:crypto");
256597
257050
  init_src7();
256598
257051
  init_identity();
256599
257052
  AUTONOMOUS_ETHOS = `\u4F60\u662F\u8FD9\u4E2A\u5DE5\u5355\u7684**\u7BA1\u7406\u8005**\u2014\u2014\u804C\u8D23\u662F\u8BA9\u5B83\u987A\u7545\u8DD1\u5B8C\u3002\u7CFB\u7EDF\u5728\u67D0\u4E2A\u8282\u70B9\u5361\u4F4F\u3001\u673A\u68B0\u5206\u8BCA\u786E\u8BA4"\u9700\u8981\u4F60"\u65F6\u5524\u8D77\u4F60\uFF08\u65E0\u4EBA\u5728\u573A\uFF0C\u4F60\u8FD9\u4E00\u8F6E\u628A\u80FD\u505A\u7684\u505A\u6389\uFF09\u3002\u4F60\u7684\u624B\u6BB5\u5F88\u5BBD\uFF1A
@@ -256863,7 +257316,7 @@ ${HIGH_RISK_COMMANDS}`;
256863
257316
  return this.deps.kernel.model.lastSeq.get(artifactId) ?? 0;
256864
257317
  }
256865
257318
  evaluationKey(kind, artifactId, evidence) {
256866
- const fingerprint2 = (0, import_node_crypto73.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
257319
+ const fingerprint2 = (0, import_node_crypto74.createHash)("sha256").update(JSON.stringify(evidence)).digest("hex");
256867
257320
  return `${kind}:${artifactId}:${fingerprint2}`;
256868
257321
  }
256869
257322
  artifactEvidence(id) {
@@ -257252,8 +257705,8 @@ ${ctx.nodeFault}
257252
257705
  const taskWithRejections = rejectedSection ? `${task}
257253
257706
 
257254
257707
  ${rejectedSection}` : task;
257255
- const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto73.randomUUID)();
257256
- const coordinatorRunId = `coordinate:${(0, import_node_crypto73.randomUUID)()}`;
257708
+ const runtimeSessionId = opts?.resumeSessionId ?? (0, import_node_crypto74.randomUUID)();
257709
+ const coordinatorRunId = `coordinate:${(0, import_node_crypto74.randomUUID)()}`;
257257
257710
  const taskWithContext = coordinatorContext ? [
257258
257711
  taskWithRejections,
257259
257712
  ``,
@@ -257297,7 +257750,7 @@ ${rejectedSection}` : task;
257297
257750
  const startedAtMs = Date.now();
257298
257751
  let trajectoryStarted = false;
257299
257752
  if (this.deps.trajectory) {
257300
- const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto73.createHash)("sha256").update(content3).digest("hex")]));
257753
+ const bundleManifest = Object.fromEntries(Object.entries(job.bundle.files).map(([name, content3]) => [name, (0, import_node_crypto74.createHash)("sha256").update(content3).digest("hex")]));
257301
257754
  const session = {
257302
257755
  runId: coordinatorRunId,
257303
257756
  runtimeSessionId,
@@ -257508,11 +257961,11 @@ var init_pg_ident = __esm({
257508
257961
  });
257509
257962
 
257510
257963
  // ../storage/src/postgres.ts
257511
- var import_node_crypto74, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
257964
+ var import_node_crypto75, ident4, isUniqueViolation, PostgresOplogStore, PostgresBlobStore;
257512
257965
  var init_postgres = __esm({
257513
257966
  "../storage/src/postgres.ts"() {
257514
257967
  "use strict";
257515
- import_node_crypto74 = require("node:crypto");
257968
+ import_node_crypto75 = require("node:crypto");
257516
257969
  init_pg_ident();
257517
257970
  init_esm();
257518
257971
  init_src();
@@ -257692,7 +258145,7 @@ var init_postgres = __esm({
257692
258145
  return new _PostgresBlobStore(pool, schema);
257693
258146
  }
257694
258147
  async put(bytes2) {
257695
- const hash3 = (0, import_node_crypto74.createHash)("sha256").update(bytes2).digest("hex");
258148
+ const hash3 = (0, import_node_crypto75.createHash)("sha256").update(bytes2).digest("hex");
257696
258149
  await this.pool.query(
257697
258150
  `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
257698
258151
  [hash3, Buffer.from(bytes2), bytes2.byteLength, sniffContentType(bytes2) ?? null]
@@ -257740,7 +258193,7 @@ function stageAtLeast(stage, min2) {
257740
258193
  return STAGES.indexOf(stage) >= STAGES.indexOf(min2);
257741
258194
  }
257742
258195
  function newVariableId() {
257743
- return `var_${(0, import_node_crypto75.randomUUID)()}`;
258196
+ return `var_${(0, import_node_crypto76.randomUUID)()}`;
257744
258197
  }
257745
258198
  async function declareRecordsAware(db) {
257746
258199
  await db.query(`SELECT set_config('${RECORDS_AWARE_SETTING}', 'on', true)`);
@@ -258096,11 +258549,11 @@ async function listBindingTargetIds(db, s2, kind) {
258096
258549
  [kind]
258097
258550
  )).rows.map((x2) => x2.target_id);
258098
258551
  }
258099
- var import_node_crypto75, STAGES, RECORDS_AWARE_SETTING, markLockName;
258552
+ var import_node_crypto76, STAGES, RECORDS_AWARE_SETTING, markLockName;
258100
258553
  var init_variable_bindings = __esm({
258101
258554
  "../storage/src/variable-bindings.ts"() {
258102
258555
  "use strict";
258103
- import_node_crypto75 = require("node:crypto");
258556
+ import_node_crypto76 = require("node:crypto");
258104
258557
  init_src();
258105
258558
  STAGES = ["off", "dual", "read", "records"];
258106
258559
  RECORDS_AWARE_SETTING = "oasis.variable_records_aware";
@@ -268162,11 +268615,11 @@ var init_postgres_delegations = __esm({
268162
268615
  });
268163
268616
 
268164
268617
  // ../storage/src/postgres-nodes.ts
268165
- var import_node_crypto76, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
268618
+ var import_node_crypto77, ident13, PostgresNodeStore, PostgresNodeTokenStore, rowToNode, rowToFault, rowToQuota, rowToRuntime;
268166
268619
  var init_postgres_nodes = __esm({
268167
268620
  "../storage/src/postgres-nodes.ts"() {
268168
268621
  "use strict";
268169
- import_node_crypto76 = require("node:crypto");
268622
+ import_node_crypto77 = require("node:crypto");
268170
268623
  init_pg_ident();
268171
268624
  init_esm();
268172
268625
  ident13 = (s2) => {
@@ -268516,7 +268969,7 @@ var init_postgres_nodes = __esm({
268516
268969
  return store;
268517
268970
  }
268518
268971
  issue(nodeId) {
268519
- const token = `ont_${(0, import_node_crypto76.randomBytes)(24).toString("base64url")}`;
268972
+ const token = `ont_${(0, import_node_crypto77.randomBytes)(24).toString("base64url")}`;
268520
268973
  this.cache.set(token, nodeId);
268521
268974
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
268522
268975
  return token;
@@ -268818,11 +269271,11 @@ var init_postgres_type_registry = __esm({
268818
269271
  });
268819
269272
 
268820
269273
  // ../storage/src/postgres-actor-memory.ts
268821
- var import_node_crypto77, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
269274
+ var import_node_crypto78, matchClause, ident15, PostgresActorMemoryStore, rowToIndexEntry, rowToRecord;
268822
269275
  var init_postgres_actor_memory = __esm({
268823
269276
  "../storage/src/postgres-actor-memory.ts"() {
268824
269277
  "use strict";
268825
- import_node_crypto77 = require("node:crypto");
269278
+ import_node_crypto78 = require("node:crypto");
268826
269279
  init_pg_ident();
268827
269280
  init_src();
268828
269281
  matchClause = (q2) => q2.requireMatch && q2.keywords.length > 0 ? " WHERE m > 0" : "";
@@ -269032,7 +269485,7 @@ var init_postgres_actor_memory = __esm({
269032
269485
  }
269033
269486
  async write(input, now) {
269034
269487
  if (input.memId === void 0) {
269035
- const memId = `mem:${(0, import_node_crypto77.randomUUID)()}`;
269488
+ const memId = `mem:${(0, import_node_crypto78.randomUUID)()}`;
269036
269489
  const r2 = await this.pool.query(
269037
269490
  `INSERT INTO ${this.s}.actor_memories
269038
269491
  (mem_id, actor_id, project_id, keywords, content, version, created_at, updated_at, accessed_at, source_artifact_id, source_session_id)
@@ -270603,14 +271056,14 @@ function companySchemaName(companyId) {
270603
271056
  if (typeof companyId !== "string" || !companyId.trim()) {
270604
271057
  throw new Error("company schema mapping \u9700\u8981\u975E\u7A7A companyId\uFF08fail-closed\uFF1A\u4E0D\u56DE\u9000\u9ED8\u8BA4\u516C\u53F8\uFF09");
270605
271058
  }
270606
- const hex = (0, import_node_crypto78.createHash)("sha256").update(companyId, "utf8").digest("hex");
271059
+ const hex = (0, import_node_crypto79.createHash)("sha256").update(companyId, "utf8").digest("hex");
270607
271060
  return `${COMPANY_SCHEMA_PREFIX}${hex.slice(0, DIGEST_HEX_LEN)}`;
270608
271061
  }
270609
- var import_node_crypto78, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
271062
+ var import_node_crypto79, COMPANY_SCHEMA_PREFIX, DIGEST_HEX_LEN, SchemaMappingConflictError, TenantStorageLocationStore;
270610
271063
  var init_tenant_schema_mapping = __esm({
270611
271064
  "../storage/src/tenant-schema-mapping.ts"() {
270612
271065
  "use strict";
270613
- import_node_crypto78 = require("node:crypto");
271066
+ import_node_crypto79 = require("node:crypto");
270614
271067
  init_esm();
270615
271068
  init_pg_ident();
270616
271069
  COMPANY_SCHEMA_PREFIX = "c_";
@@ -270684,7 +271137,7 @@ var init_tenant_schema_mapping = __esm({
270684
271137
 
270685
271138
  // ../storage/src/company-data-plane-schema.ts
270686
271139
  function canonicalTypeDefHash(def) {
270687
- return `sha256:${(0, import_node_crypto79.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
271140
+ return `sha256:${(0, import_node_crypto80.createHash)("sha256").update(JSON.stringify(def), "utf8").digest("hex")}`;
270688
271141
  }
270689
271142
  function defaultArtifactTypeTemplate() {
270690
271143
  return [
@@ -270866,11 +271319,11 @@ async function seedDefaultArtifactTypes(pool, schema, templates = defaultArtifac
270866
271319
  }
270867
271320
  return { inserted };
270868
271321
  }
270869
- var import_node_crypto79, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
271322
+ var import_node_crypto80, DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION;
270870
271323
  var init_company_data_plane_schema = __esm({
270871
271324
  "../storage/src/company-data-plane-schema.ts"() {
270872
271325
  "use strict";
270873
- import_node_crypto79 = require("node:crypto");
271326
+ import_node_crypto80 = require("node:crypto");
270874
271327
  init_esm();
270875
271328
  init_pg_ident();
270876
271329
  DEFAULT_ARTIFACT_TYPE_TEMPLATE_VERSION = "4c368e5c-types-v1";
@@ -270949,11 +271402,11 @@ function rowToFile2(row) {
270949
271402
  updatedAt: new Date(row.updated_at).toISOString()
270950
271403
  };
270951
271404
  }
270952
- var import_node_crypto80, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
271405
+ var import_node_crypto81, COMPANY_CONFLICT_MSG, CompanyIdentifierConflictError, CompanyOwnedRegistryStore;
270953
271406
  var init_company_owned_registry_store = __esm({
270954
271407
  "../storage/src/company-owned-registry-store.ts"() {
270955
271408
  "use strict";
270956
- import_node_crypto80 = require("node:crypto");
271409
+ import_node_crypto81 = require("node:crypto");
270957
271410
  init_esm();
270958
271411
  init_src();
270959
271412
  init_pg_ident();
@@ -271338,7 +271791,7 @@ var init_company_owned_registry_store = __esm({
271338
271791
  enabled: skill.enabled,
271339
271792
  files: files.map((f2) => ({ path: f2.path, blobHash: f2.blobHash }))
271340
271793
  });
271341
- return (0, import_node_crypto80.createHash)("sha256").update(body2).digest("hex");
271794
+ return (0, import_node_crypto81.createHash)("sha256").update(body2).digest("hex");
271342
271795
  }
271343
271796
  };
271344
271797
  }
@@ -274148,7 +274601,7 @@ function sourceSetConservation(input) {
274148
274601
  return input.sourceFinal === input.copied + input.approvedRuntimeConversion + input.approvedArchive + input.approvedDiscard;
274149
274602
  }
274150
274603
  function payloadChecksum(value2) {
274151
- return (0, import_node_crypto81.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
274604
+ return (0, import_node_crypto82.createHash)("sha256").update(JSON.stringify(value2)).digest("hex");
274152
274605
  }
274153
274606
  async function copyWorkorderDraftsIdempotent(pool, companyId, companySchema, manifestId, opts) {
274154
274607
  const cs = quoteIdent2(companySchema);
@@ -274540,11 +274993,11 @@ async function copyTraceFamilyIdempotent(pool, companyId, companySchema, manifes
274540
274993
  sequences
274541
274994
  };
274542
274995
  }
274543
- var import_node_crypto81, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
274996
+ var import_node_crypto82, MissingInstallerActorsError, TypeCatalogNotCoveredError, ORG_REGISTRY_TABLES, TRACE_FAMILY;
274544
274997
  var init_tenant_migration_operator = __esm({
274545
274998
  "../storage/src/tenant-migration-operator.ts"() {
274546
274999
  "use strict";
274547
- import_node_crypto81 = require("node:crypto");
275000
+ import_node_crypto82 = require("node:crypto");
274548
275001
  init_esm();
274549
275002
  init_pg_ident();
274550
275003
  init_tenancy_attribution();
@@ -274780,8 +275233,8 @@ var init_tenant_new_company_provision = __esm({
274780
275233
  // ../storage/src/knowledge-snapshot-crypto.ts
274781
275234
  function sealSnapshot(snapshot, key2) {
274782
275235
  if (snapshot.content.startsWith(PREFIX3)) return structuredClone(snapshot);
274783
- const iv = (0, import_node_crypto82.randomBytes)(12);
274784
- const cipher = (0, import_node_crypto82.createCipheriv)("aes-256-gcm", bytes(key2), iv);
275236
+ const iv = (0, import_node_crypto83.randomBytes)(12);
275237
+ const cipher = (0, import_node_crypto83.createCipheriv)("aes-256-gcm", bytes(key2), iv);
274785
275238
  cipher.setAAD(aad2(snapshot));
274786
275239
  const encrypted = Buffer.concat([cipher.update(snapshot.content, "utf8"), cipher.final()]);
274787
275240
  return { ...structuredClone(snapshot), content: `${PREFIX3}${iv.toString("base64url")}.${cipher.getAuthTag().toString("base64url")}.${encrypted.toString("base64url")}` };
@@ -274791,7 +275244,7 @@ function openSnapshot(snapshot, key2) {
274791
275244
  const packed = snapshot.content.slice(PREFIX3.length).split(".");
274792
275245
  if (packed.length !== 3 || packed.some((part) => !part)) throw new Error(`knowledge snapshot ${snapshot.ref} ciphertext is malformed`);
274793
275246
  const [iv, tag, data] = packed;
274794
- const decipher = (0, import_node_crypto82.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
275247
+ const decipher = (0, import_node_crypto83.createDecipheriv)("aes-256-gcm", bytes(key2), Buffer.from(iv, "base64url"));
274795
275248
  decipher.setAAD(aad2(snapshot));
274796
275249
  decipher.setAuthTag(Buffer.from(tag, "base64url"));
274797
275250
  return {
@@ -274799,11 +275252,11 @@ function openSnapshot(snapshot, key2) {
274799
275252
  content: Buffer.concat([decipher.update(Buffer.from(data, "base64url")), decipher.final()]).toString("utf8")
274800
275253
  };
274801
275254
  }
274802
- var import_node_crypto82, PREFIX3, bytes, aad2;
275255
+ var import_node_crypto83, PREFIX3, bytes, aad2;
274803
275256
  var init_knowledge_snapshot_crypto = __esm({
274804
275257
  "../storage/src/knowledge-snapshot-crypto.ts"() {
274805
275258
  "use strict";
274806
- import_node_crypto82 = require("node:crypto");
275259
+ import_node_crypto83 = require("node:crypto");
274807
275260
  PREFIX3 = "oasis-knowledge:v1:";
274808
275261
  bytes = (key2) => {
274809
275262
  const value2 = Buffer.from(key2);
@@ -275695,7 +276148,7 @@ function reportedCost(v2) {
275695
276148
  }
275696
276149
  function newUsageBackfillManifestId(now = /* @__PURE__ */ new Date()) {
275697
276150
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
275698
- return `ulb-${stamp}-${(0, import_node_crypto83.randomBytes)(3).toString("hex")}`;
276151
+ return `ulb-${stamp}-${(0, import_node_crypto84.randomBytes)(3).toString("hex")}`;
275699
276152
  }
275700
276153
  async function backfillUsageLedger(ledger, opts) {
275701
276154
  if (!opts.dryRun && !opts.manifestId) throw new Error("execute \u9700\u8981 manifestId");
@@ -276244,11 +276697,11 @@ async function audit(c, s2, manifestId, runId, field, before, after, createdAt)
276244
276697
  [manifestId, runId, field, before == null ? null : JSON.stringify(before), after == null ? null : JSON.stringify(after), createdAt]
276245
276698
  );
276246
276699
  }
276247
- var import_node_crypto83, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, ALLOWED_MODEL_EVIDENCE_TIERS, emptyStep, MODEL_BACKFILL_SOURCE, fourOf, sameUsage, ChatRunLookup;
276700
+ var import_node_crypto84, BUCKETS, ENDED_SQL, START_EVIDENCE_SQL, CHAT_RUN_SQL, ALLOWED_MODEL_EVIDENCE_TIERS, emptyStep, MODEL_BACKFILL_SOURCE, fourOf, sameUsage, ChatRunLookup;
276248
276701
  var init_usage_ledger_backfill = __esm({
276249
276702
  "../storage/src/usage-ledger-backfill.ts"() {
276250
276703
  "use strict";
276251
- import_node_crypto83 = require("node:crypto");
276704
+ import_node_crypto84 = require("node:crypto");
276252
276705
  init_src();
276253
276706
  init_pg_ident();
276254
276707
  init_postgres_trace();
@@ -276827,31 +277280,64 @@ var init_connector_tool_versions = __esm({
276827
277280
  });
276828
277281
 
276829
277282
  // ../server/src/design/board-watch.ts
276830
- function decideBoardLetter(c, nowMs, quietMs) {
276831
- if (c.hasOpenWatchLetter) return false;
276832
- if (c.freshTerminalRunId !== null) return true;
276833
- if (c.boardMtime === null) return false;
276834
- if (!c.hasRevision) return false;
276835
- if (c.lastOpAtMs === null || c.boardMtime <= c.lastOpAtMs) return false;
276836
- if (nowMs - c.boardMtime < quietMs) return false;
276837
- if (c.lastWatchMtime !== null && c.boardMtime <= c.lastWatchMtime) return false;
276838
- return true;
277283
+ function parseOdTime(v2) {
277284
+ if (typeof v2 === "number") return Number.isFinite(v2) ? v2 : null;
277285
+ if (typeof v2 !== "string" || v2.trim().length === 0) return null;
277286
+ const s2 = v2.trim();
277287
+ if (/^\d+(\.\d+)?$/.test(s2)) return Number(s2);
277288
+ const t = Date.parse(s2);
277289
+ return Number.isFinite(t) ? t : null;
277290
+ }
277291
+ function nonEmpty2(v2) {
277292
+ return typeof v2 === "string" && v2.trim().length > 0 ? v2 : null;
277293
+ }
277294
+ function roundOfMessage(m2, conversationId = null) {
277295
+ if (m2["role"] !== "assistant") return null;
277296
+ const status = nonEmpty2(m2["runStatus"]);
277297
+ if (status === null || !TERMINAL_RUN_STATUS.has(status)) return null;
277298
+ const endedAt = parseOdTime(m2["endedAt"]);
277299
+ if (endedAt === null) return null;
277300
+ const runId = nonEmpty2(m2["runId"]);
277301
+ const messageId = nonEmpty2(m2["id"]);
277302
+ if (runId === null && messageId === null) return null;
277303
+ const files = Array.isArray(m2["producedFiles"]) ? m2["producedFiles"] : [];
277304
+ const producedFiles = files.map((f2) => typeof f2 === "string" ? f2 : f2 && typeof f2 === "object" ? nonEmpty2(f2["path"]) ?? nonEmpty2(f2["name"]) : null).filter((s2) => s2 !== null);
277305
+ const { error: error2, errorCode } = messageError(m2, status);
277306
+ return {
277307
+ key: runId ?? `msg:${messageId}`,
277308
+ conversationId,
277309
+ runId,
277310
+ status,
277311
+ agentId: nonEmpty2(m2["agentId"]),
277312
+ endedAt,
277313
+ producedFiles,
277314
+ error: error2,
277315
+ errorCode
277316
+ };
277317
+ }
277318
+ function messageError(m2, status) {
277319
+ const events = Array.isArray(m2["events"]) ? m2["events"] : [];
277320
+ for (let i = events.length - 1; i >= 0; i--) {
277321
+ const e = events[i];
277322
+ if (!e || typeof e !== "object") continue;
277323
+ const ev = e;
277324
+ if (ev["kind"] !== "status" || ev["label"] !== "error") continue;
277325
+ const detail = nonEmpty2(ev["detail"]);
277326
+ if (detail !== null) return { error: detail, errorCode: nonEmpty2(ev["code"]) };
277327
+ }
277328
+ if (status !== "succeeded") {
277329
+ const content3 = nonEmpty2(m2["content"]);
277330
+ if (content3 !== null) return { error: content3, errorCode: null };
277331
+ }
277332
+ return { error: null, errorCode: null };
276839
277333
  }
276840
- var MARKER_PREFIX, boardWatchMarker, parseMarker, RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, TERMINAL_RUN_STATUS, BoardWatcher;
277334
+ var RUN_MARKER_PREFIX, boardWatchRunMarker, parseRunMarker, SINCE_MARKER_PREFIX, boardWatchSinceMarker, parseSinceMarker, TERMINAL_RUN_STATUS, MAX_REASON_CHARS, MAX_LISTED_FILES, BoardWatcher;
276841
277335
  var init_board_watch = __esm({
276842
277336
  "../server/src/design/board-watch.ts"() {
276843
277337
  "use strict";
276844
277338
  init_src7();
276845
- MARKER_PREFIX = "[od-board-watch mtime:";
276846
- boardWatchMarker = (mtimeMs) => `${MARKER_PREFIX}${mtimeMs}]`;
276847
- parseMarker = (body2) => {
276848
- const i = body2.indexOf(MARKER_PREFIX);
276849
- if (i < 0) return null;
276850
- const n = Number.parseInt(body2.slice(i + MARKER_PREFIX.length), 10);
276851
- return Number.isFinite(n) ? n : null;
276852
- };
276853
277339
  RUN_MARKER_PREFIX = "[od-board-watch run:";
276854
- boardWatchRunMarker = (runId) => `${RUN_MARKER_PREFIX}${runId}]`;
277340
+ boardWatchRunMarker = (roundKey) => `${RUN_MARKER_PREFIX}${roundKey}]`;
276855
277341
  parseRunMarker = (body2) => {
276856
277342
  const i = body2.indexOf(RUN_MARKER_PREFIX);
276857
277343
  if (i < 0) return null;
@@ -276860,113 +277346,183 @@ var init_board_watch = __esm({
276860
277346
  const id = body2.slice(i + RUN_MARKER_PREFIX.length, j);
276861
277347
  return id.length > 0 ? id : null;
276862
277348
  };
277349
+ SINCE_MARKER_PREFIX = "[od-board-watch since:";
277350
+ boardWatchSinceMarker = (sinceMs) => `${SINCE_MARKER_PREFIX}${sinceMs}]`;
277351
+ parseSinceMarker = (body2) => {
277352
+ const i = body2.indexOf(SINCE_MARKER_PREFIX);
277353
+ if (i < 0) return null;
277354
+ const n = Number.parseInt(body2.slice(i + SINCE_MARKER_PREFIX.length), 10);
277355
+ return Number.isFinite(n) ? n : null;
277356
+ };
276863
277357
  TERMINAL_RUN_STATUS = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled", "cancelled"]);
277358
+ MAX_REASON_CHARS = 500;
277359
+ MAX_LISTED_FILES = 10;
276864
277360
  BoardWatcher = class {
276865
277361
  constructor(deps) {
276866
277362
  this.deps = deps;
276867
- }
276868
- odGet(path47) {
277363
+ if (!deps.kernel && !(deps.listCompanies && deps.kernelFor)) throw new Error("BoardWatcher \u9700\u8981 kernel\uFF0C\u6216 listCompanies + kernelFor");
277364
+ }
277365
+ /** `<公司>\0<artifactId>` → 本进程第一次读成功时钉住的起算点(还没写过带 since 的信时用),免得在第一封信之前随时间往后漂。 */
277366
+ pinnedSince = /* @__PURE__ */ new Map();
277367
+ /** `<公司>\0<boardKey>\0<conversationId>` → 本进程上次把这个对话的轮次处理完时它的 updatedAt。 */
277368
+ seenConvAt = /* @__PURE__ */ new Map();
277369
+ /** 公司 → 上次记下的失败原文(同样的错只记一次;成功一轮就清掉)。 */
277370
+ companyLastError = /* @__PURE__ */ new Map();
277371
+ async odGet(path47) {
276869
277372
  const fetchImpl = this.deps.fetchImpl ?? fetch;
276870
- return fetchImpl(`${this.deps.odBase}${path47}`, this.deps.odToken ? { headers: { authorization: `Bearer ${this.deps.odToken}` } } : void 0);
276871
- }
276872
- /** 查一块板的交付物(排 task-context/、只算带 artifactManifest 的)。404/网络错 → null(板不存在)。 */
276873
- async boardFiles(boardKey) {
276874
277373
  try {
276875
- const res = await this.odGet(`/api/projects/${encodeURIComponent(boardKey)}/files`);
276876
- if (!res.ok) return null;
276877
- const body2 = await res.json();
276878
- return (body2.files ?? []).filter((f2) => !f2.path.startsWith("task-context/") && f2.artifactManifest !== void 0).map((f2) => ({ path: f2.path, kind: f2.kind ?? null, mtime: f2.mtime ?? null }));
276879
- } catch {
276880
- return null;
277374
+ const res = await fetchImpl(`${this.deps.odBase}${path47}`, this.deps.odToken ? { headers: { authorization: `Bearer ${this.deps.odToken}` } } : void 0);
277375
+ if (res.status === 401 || res.status === 403) return { kind: "denied", status: res.status };
277376
+ if (res.status === 404) return { kind: "missing" };
277377
+ if (!res.ok) return { kind: "error", detail: `HTTP ${res.status}` };
277378
+ return { kind: "ok", value: await res.json() };
277379
+ } catch (err) {
277380
+ return { kind: "error", detail: String(err) };
276881
277381
  }
276882
277382
  }
276883
277383
  /**
276884
- * 查一块板的 run 列表(`GET /api/runs?projectId=`,与 CLI `design status` 同源)。
276885
- * run 注册表是 od 的**内存态**——重启即空,查不到不代表没跑过;所以这里失败一律降级成空数组,
276886
- * 由板文件 mtime 那条慢路径兜底,绝不因为查不到 run 就压掉写信。
277384
+ * 列出一块板上结束时间可能晚于 since 的轮次(跨对话;顺序不重要,调用方按 endedAt 排)。
277385
+ *
277386
+ * 省流:od 的 assistant 消息带整段 events 与 content,每分钟把每个对话的 /messages 全拉一遍,轮次多的板
277387
+ * 一分钟就是几 MB。所以先用 /conversations 列表里的 updatedAt / latestRun.endedAt(取较大)筛:
277388
+ * - 不晚于 since → 这个对话里不可能有要写信的轮次,跳过;
277389
+ * - 和本进程上次看完时一样 → 没变,跳过(seenConvAt,serve 重启即清空,退回只按 since 筛,run marker 去重兜着)。
277390
+ * 两个时间都没有的对话照拉,不猜。返回每个拉过的对话的 updatedAt,由 tick 在它的轮次都写完信后记进 seenConvAt。
276887
277391
  */
276888
- async boardRuns(boardKey) {
276889
- try {
276890
- const res = await this.odGet(`/api/runs?projectId=${encodeURIComponent(boardKey)}`);
276891
- if (!res.ok) return [];
276892
- const body2 = await res.json();
276893
- return (body2.runs ?? []).map((r) => ({ runId: String(r["runId"] ?? r["id"] ?? ""), status: String(r["status"] ?? "unknown") })).filter((r) => r.runId.length > 0);
276894
- } catch {
276895
- return [];
276896
- }
277392
+ async boardRounds(company, boardKey, since) {
277393
+ const base = `/api/projects/${encodeURIComponent(boardKey)}/conversations`;
277394
+ const convs = await this.odGet(base);
277395
+ if (convs.kind !== "ok") return convs;
277396
+ const rounds = [];
277397
+ const fetched = /* @__PURE__ */ new Map();
277398
+ for (const c of convs.value.conversations ?? []) {
277399
+ const cid = nonEmpty2(c["id"]);
277400
+ if (cid === null) continue;
277401
+ const latestRun = c["latestRun"] && typeof c["latestRun"] === "object" ? c["latestRun"] : {};
277402
+ const times = [parseOdTime(c["updatedAt"]), parseOdTime(latestRun["endedAt"])].filter((t) => t !== null);
277403
+ const convAt = times.length > 0 ? Math.max(...times) : null;
277404
+ if (convAt !== null && (convAt <= since || this.seenConvAt.get(`${company}\0${boardKey}\0${cid}`) === convAt)) continue;
277405
+ const msgs = await this.odGet(`${base}/${encodeURIComponent(cid)}/messages`);
277406
+ if (msgs.kind === "missing") continue;
277407
+ if (msgs.kind !== "ok") return msgs;
277408
+ fetched.set(cid, convAt);
277409
+ for (const m2 of msgs.value.messages ?? []) {
277410
+ if (!m2 || typeof m2 !== "object") continue;
277411
+ const r = roundOfMessage(m2, cid);
277412
+ if (r !== null) rounds.push(r);
277413
+ }
277414
+ }
277415
+ return { kind: "ok", value: { rounds, fetched } };
277416
+ }
277417
+ /** 失败轮消息上没有错误原文时,去 `/api/runs` 按 runId 找 od 记的 error;找不到(含 od 重启过)→ null。 */
277418
+ async runErrorFromRegistry(boardKey, runId) {
277419
+ const res = await this.odGet(`/api/runs?projectId=${encodeURIComponent(boardKey)}`);
277420
+ if (res.kind !== "ok") return null;
277421
+ const hit = (res.value.runs ?? []).find((r) => String(r["runId"] ?? r["id"] ?? "") === runId);
277422
+ return hit ? nonEmpty2(hit["error"]) : null;
276897
277423
  }
276898
277424
  async tick(now = Date.now()) {
276899
- const { kernel, boardKeyOf, log: log3 } = this.deps;
276900
- const quietMs = this.deps.quietMs ?? 10 * 6e4;
277425
+ const { log: log3 } = this.deps;
277426
+ const denied = [];
277427
+ const { listCompanies, kernelFor } = this.deps;
277428
+ if (listCompanies && kernelFor) {
277429
+ for (const companyId of await listCompanies()) {
277430
+ try {
277431
+ await this.tickCompany(companyId, await kernelFor(companyId), now, denied);
277432
+ this.companyLastError.delete(companyId);
277433
+ } catch (err) {
277434
+ const msg = String(err);
277435
+ if (this.companyLastError.get(companyId) === msg) continue;
277436
+ this.companyLastError.set(companyId, msg);
277437
+ log3?.(`[board-watch] [${companyId}] \u8FD9\u4E00\u8F6E\u6CA1\u5DE1\u6210\uFF08\u5176\u4F59\u516C\u53F8\u7EE7\u7EED\uFF09\uFF1A${msg}`);
277438
+ }
277439
+ }
277440
+ } else {
277441
+ await this.tickCompany("", this.deps.kernel, now, denied);
277442
+ }
277443
+ if (denied.length > 0) {
277444
+ const codes = [...new Set(denied.map((d) => d.status))].join("/");
277445
+ const eg = denied[0];
277446
+ log3?.(
277447
+ `[board-watch] od \u62D2\u7EDD\u4E86\u5DE1\u68C0\u8BF7\u6C42\uFF08HTTP ${codes}\uFF0C${denied.length} \u5757\u677F\uFF0C\u5982 ${eg.company ? `[${eg.company}] ` : ""}${eg.boardKey}\uFF09\uFF1A${this.deps.odToken ? "\u5E26\u4E86 OASIS_OPEN_DESIGN_TOKEN \u4F46 od \u4E0D\u8BA4\uFF0C\u6838\u5BF9\u5B83\u4E0E od \u7684 OD_API_TOKEN \u662F\u5426\u4E00\u81F4" : "\u6CA1\u914D OASIS_OPEN_DESIGN_TOKEN\uFF0Cod \u8981\u4EE4\u724C"}\u3002\u8FD9\u4E9B\u677F\u672C\u8F6E\u6CA1\u770B\u6210`
277448
+ );
277449
+ }
277450
+ }
277451
+ /** 巡一家公司(company 为空串 = 单 kernel 模式);被拒的板记进 denied,由 tick 统一汇总成一行。 */
277452
+ async tickCompany(company, kernel, now, denied) {
277453
+ const { boardKeyOf, log: log3 } = this.deps;
277454
+ const graceMs = this.deps.firstLookGraceMs ?? 10 * 6e4;
276901
277455
  const maxPerTick = this.deps.maxLettersPerTick ?? 2;
276902
277456
  const actor = this.deps.actor ?? "actor:system";
277457
+ const tag = company ? `[${company}] ` : "";
276903
277458
  const model = kernel.model;
276904
277459
  let written = 0;
276905
- for (const a of model.artifacts.values()) {
276906
- if (a.type !== "design") continue;
276907
- if (lifecycleOf(model, a.id) !== "active") continue;
277460
+ for (const a of [...model.artifacts.values()]) {
276908
277461
  if (written >= maxPerTick) {
276909
- log3?.(`[board-watch] \u672C\u8F6E\u5199\u4FE1\u5DF2\u8FBE\u4E0A\u9650 ${maxPerTick}\uFF0C\u5176\u4F59\u677F\u4E0B\u8F6E\u518D\u770B`);
277462
+ log3?.(`[board-watch] ${tag}\u672C\u8F6E\u5199\u4FE1\u5DF2\u8FBE\u4E0A\u9650 ${maxPerTick}\uFF0C\u5176\u4F59\u677F\u4E0B\u8F6E\u518D\u770B`);
276910
277463
  break;
276911
277464
  }
277465
+ if (a.type !== "design") continue;
277466
+ if (lifecycleOf(model, a.id) !== "active") continue;
276912
277467
  const boardKey = boardKeyOf(a.id);
276913
- const files = await this.boardFiles(boardKey);
276914
- if (files === null) continue;
276915
- const runs = await this.boardRuns(boardKey);
276916
- if (files.length === 0 && runs.length === 0) continue;
276917
- const boardMtime = files.reduce((mx, f2) => f2.mtime !== null && (mx === null || f2.mtime > mx) ? f2.mtime : mx, null);
276918
- const watchLetters = annotationsOf(model, a.id).filter(
276919
- (x2) => x2.author === actor && (parseMarker(x2.body) !== null || parseRunMarker(x2.body) !== null)
276920
- );
276921
- const letteredRuns = new Set(
276922
- watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null)
276923
- );
276924
- const newestTerminalRun = runs.find((r) => TERMINAL_RUN_STATUS.has(r.status)) ?? null;
276925
- const lastOpAt = model.lastOpAt.get(a.id);
276926
- const candidate = {
276927
- boardMtime,
276928
- hasRevision: revisionsOf(model, a.id).length > 0,
276929
- lastOpAtMs: lastOpAt ? Date.parse(lastOpAt) : null,
276930
- hasOpenWatchLetter: watchLetters.some((x2) => x2.state === "open"),
276931
- lastWatchMtime: watchLetters.reduce((mx, x2) => {
276932
- const m2 = parseMarker(x2.body);
276933
- return m2 !== null && (mx === null || m2 > mx) ? m2 : mx;
276934
- }, null),
276935
- freshTerminalRunId: newestTerminalRun !== null && !letteredRuns.has(newestTerminalRun.runId) ? newestTerminalRun.runId : null
276936
- };
276937
- if (!decideBoardLetter(candidate, now, quietMs)) continue;
276938
- const fmt = (fs52) => fs52.slice(0, 5).map((f2) => `- ${f2.path}\uFF08${f2.kind ?? "?"}\uFF09\xB7 ${f2.mtime ? new Date(f2.mtime).toISOString() : "?"}`).join("\n");
276939
- let body2;
276940
- let logLine;
276941
- if (candidate.freshTerminalRunId !== null) {
276942
- const status = newestTerminalRun.status;
276943
- const ok3 = status === "succeeded";
276944
- const listed = files.length > 0 ? fmt([...files].sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0))) : "\uFF08\u677F\u4E0A\u6682\u65E0\u4EA4\u4ED8\u7269\uFF09";
276945
- body2 = [
276946
- // 两个 marker 都带:run marker 管快路径去重,mtime marker 顺手把慢路径也盖住——
276947
- // 否则这封信被 resolve 后,慢路径会就**同一批板内容**再写一封(两路去重各管各的)。
276948
- boardMtime !== null ? `${boardWatchRunMarker(candidate.freshTerminalRunId)} ${boardWatchMarker(boardMtime)}` : boardWatchRunMarker(candidate.freshTerminalRunId),
276949
- ok3 ? `\u753B\u677F ${boardKey} \u7684\u5F15\u64CE\u8DD1\u5B8C\u4E86\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} \u7EC8\u6001=${status}\uFF09\uFF0C\u8D27\u5728\u677F\u4E0A\u7B49\u4F60\u6536\uFF1A` : `\u753B\u677F ${boardKey} \u7684\u5F15\u64CE\u8FD9\u4E00\u8F6E**\u6CA1\u8DD1\u6210**\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} \u7EC8\u6001=${status}\uFF09\u3002\u677F\u4E0A\u5F53\u524D\u5185\u5BB9\uFF1A`,
276950
- listed,
276951
- ``,
276952
- ok3 ? `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${a.id} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002` : `\u5148 \`oasis design status ${a.id}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\uFF1B\u6CA1\u6709\u5C31\u628A\u9700\u6C42\u4E0E\u771F\u5B9E\u6570\u636E\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E \`oasis design chat ${a.id} --message "<\u2026>" --async\`\uFF0C\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${a.id} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
276953
- ].join("\n");
276954
- logLine = `[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u5F15\u64CE\u6536\u5C3E\uFF08run ${candidate.freshTerminalRunId.slice(0, 8)} ${status}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`;
276955
- } else {
276956
- const newest = files.filter((f2) => f2.mtime !== null && candidate.lastOpAtMs !== null && f2.mtime > candidate.lastOpAtMs).sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0));
276957
- body2 = [
276958
- boardWatchMarker(boardMtime),
276959
- `\u753B\u677F ${boardKey} \u4E0A\u6709 Oasis \u4E4B\u5916\u7684\u65B0\u52A8\u9759\uFF08\u4EBA\u5728\u677F\u4E0A\u5BF9\u8BDD/\u6539\u7A3F\uFF0C\u6216\u5F15\u64CE\u4EA7\u4E86\u6CA1\u4EBA\u6536\u7684\u65B0\u7248\uFF09\uFF0C\u6BD4\u672C\u4EA7\u7269\u6700\u8FD1\u4E00\u6B21\u64CD\u4F5C\uFF08${lastOpAt ?? "?"}\uFF09\u65B0\u3001\u4E14\u5DF2\u9759\u9ED8 ${Math.round(quietMs / 6e4)} \u5206\u949F\uFF1A`,
276960
- fmt(newest.length > 0 ? newest : files),
276961
- ``,
276962
- `\u8BF7\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5185\u5BB9\u5408\u610F\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u6536\u5F55\u677F\u4E0A\u4EBA\u5DE5\u8FED\u4EE3>"\` \u6536\u5F55\u4E3A\u65B0\u7248\u672C\uFF1B\u4E0D\u5408\u610F/\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u7248\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
276963
- ].join("\n");
276964
- logLine = `[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u6709\u65B0\u5185\u5BB9\uFF08mtime=${new Date(boardMtime).toISOString()}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`;
277468
+ const pinKey = `${company}\0${a.id}`;
277469
+ const watchLetters = annotationsOf(model, a.id).filter((x2) => x2.author === actor && x2.body.includes("[od-board-watch "));
277470
+ const lettered = new Set(watchLetters.map((x2) => parseRunMarker(x2.body)).filter((s2) => s2 !== null));
277471
+ const recordedSince = watchLetters.reduce((mn, x2) => {
277472
+ const s2 = parseSinceMarker(x2.body);
277473
+ return s2 !== null && (mn === null || s2 < mn) ? s2 : mn;
277474
+ }, null);
277475
+ const since = recordedSince ?? this.pinnedSince.get(pinKey) ?? now - graceMs;
277476
+ const read3 = await this.boardRounds(company, boardKey, since);
277477
+ if (read3.kind === "missing") continue;
277478
+ if (read3.kind === "denied") {
277479
+ denied.push({ company, boardKey, status: read3.status });
277480
+ continue;
276965
277481
  }
276966
- await kernel.annotate({ artifactId: a.id, actor, body: body2, kind: "comment" });
276967
- written++;
276968
- log3?.(logLine);
277482
+ if (read3.kind === "error") {
277483
+ log3?.(`[board-watch] ${tag}\u8BFB\u753B\u677F ${boardKey} \u5931\u8D25\uFF08${read3.detail}\uFF09\uFF0C\u672C\u8F6E\u8DF3\u8FC7`);
277484
+ continue;
277485
+ }
277486
+ if (recordedSince === null && !this.pinnedSince.has(pinKey)) this.pinnedSince.set(pinKey, since);
277487
+ const fresh = read3.value.rounds.filter((r) => r.endedAt > since && !lettered.has(r.key)).sort((x2, y) => x2.endedAt - y.endedAt);
277488
+ const seen = /* @__PURE__ */ new Set();
277489
+ for (const r of fresh) {
277490
+ if (written >= maxPerTick) break;
277491
+ if (seen.has(r.key)) continue;
277492
+ seen.add(r.key);
277493
+ const ok3 = r.status === "succeeded";
277494
+ const reason = ok3 ? null : r.error ?? (r.runId !== null ? await this.runErrorFromRegistry(boardKey, r.runId) : null);
277495
+ await kernel.annotate({ artifactId: a.id, actor, body: this.letterBody(a.id, boardKey, r, since, reason), kind: "comment" });
277496
+ written++;
277497
+ log3?.(`[board-watch] ${tag}${a.id} \u2190 \u677F ${boardKey} \u4E00\u8F6E\u5BF9\u8BDD\u7ED3\u675F\uFF08${r.key.slice(0, 12)} ${r.status}${r.agentId ? ` ${r.agentId}` : ""}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`);
277498
+ }
277499
+ const pending = new Set(fresh.filter((r) => !seen.has(r.key)).map((r) => r.conversationId));
277500
+ for (const [cid, convAt] of read3.value.fetched) {
277501
+ if (convAt !== null && !pending.has(cid)) this.seenConvAt.set(`${company}\0${boardKey}\0${cid}`, convAt);
277502
+ }
277503
+ }
277504
+ }
277505
+ letterBody(artifactId, boardKey, r, since, reason) {
277506
+ const marker = `${boardWatchRunMarker(r.key)} ${boardWatchSinceMarker(since)}`;
277507
+ const round = `${r.runId !== null ? `run ${r.runId.slice(0, 8)}` : `\u6D88\u606F ${r.key.slice(4, 12)}`}\uFF0C\u5F15\u64CE=${r.agentId ?? "?"}\uFF0C\u7ED3\u675F\u4E8E ${new Date(r.endedAt).toISOString()}`;
277508
+ if (r.status === "succeeded") {
277509
+ const files = r.producedFiles.length > 0 ? [...r.producedFiles.slice(0, MAX_LISTED_FILES).map((f2) => `- ${f2}`), ...r.producedFiles.length > MAX_LISTED_FILES ? [`- \u2026\u53E6 ${r.producedFiles.length - MAX_LISTED_FILES} \u4E2A`] : []].join("\n") : "\uFF08\u8FD9\u4E00\u8F6E\u6CA1\u6709\u767B\u8BB0\u4EA7\u51FA\u6587\u4EF6\uFF09";
277510
+ return [
277511
+ marker,
277512
+ `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD\u8DD1\u5B8C\u4E86\uFF08${round}\uFF09\uFF0C\u8FD9\u4E00\u8F6E\u4EA7\u51FA\uFF1A`,
277513
+ files,
277514
+ ``,
277515
+ `\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${artifactId} --pull\` \u2192 \u5BF9\u7740 DESIGN_BRIEF.md \u4E0E inputs/ \u9010\u5B57\u6BB5\u6838\uFF08\u6570\u636E\u5BF9\u4E0D\u5BF9\u3001\u72B6\u6001\u5168\u4E0D\u5168\u3001\u6709\u6CA1\u6709\u7F16\u9020\uFF09\u3002\u591F\u597D\u5C31 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\`\uFF1B\u8981\u6539\u5C31\u56DE\u677F\u4E0A\u518D\u6765\u4E00\u8F6E \`oasis design chat ${artifactId} --turn continue --message "<\u8981\u6539\u4EC0\u4E48>" --async\`\uFF08\u26D4 \u522B\u628A --pull \u4E0B\u6765\u7684\u6587\u4EF6\u5728\u672C\u5730\u624B\u6539\u540E propose\uFF09\u3002\u8FD9\u4E00\u8F6E\u82E5\u662F\u4EBA\u76F4\u63A5\u5728\u677F\u4E0A\u804A\u51FA\u6765\u7684\u3001\u5185\u5BB9\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u8F6E\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
277516
+ ].join("\n");
276969
277517
  }
277518
+ const quoted = reason === null ? null : reason.length > MAX_REASON_CHARS ? `${reason.slice(0, MAX_REASON_CHARS)}\u2026` : reason;
277519
+ return [
277520
+ marker,
277521
+ `\u753B\u677F ${boardKey} \u4E0A\u6709\u4E00\u8F6E\u5BF9\u8BDD**\u6CA1\u8DD1\u6210**\uFF08\u7EC8\u6001=${r.status}\uFF0C${round}\uFF09\u3002`,
277522
+ quoted !== null ? `od \u8BB0\u7684\u539F\u56E0\uFF08\u539F\u6587\uFF09\uFF1A${quoted}${r.errorCode !== null ? `\uFF08\u9519\u8BEF\u7801 ${r.errorCode}\uFF09` : ""}` : `od \u6CA1\u6709\u8BB0\u5F55\u8FD9\u4E00\u8F6E\u7684\u5931\u8D25\u539F\u56E0\u3002`,
277523
+ ``,
277524
+ `\u5148 \`oasis design status ${artifactId}\` \u770B\u677F\u4E0A\u6709\u6CA1\u6709\u53EF\u7528\u7684\u4E0A\u4E00\u7248\uFF1A\u6709\u5C31\u7167\u5E38\u53D6\u8D27\u6838\u5BF9\u3002\u8981\u91CD\u8DD1\u5C31\u81EA\u5DF1\u5224\u65AD\uFF1A\u50CF\u662F\u989D\u5EA6/\u9650\u6D41\u5C31\u7B49\u6062\u590D\u540E\u518D\u6765\uFF0C\u6216\u6362\u4E2A\u5F15\u64CE \`oasis design chat ${artifactId} --agent <\u5F15\u64CE> --message "<\u2026>" --async\`\uFF1B\u50CF\u662F\u9700\u6C42/\u6570\u636E\u95EE\u9898\u5C31\u628A\u5B83\u4EEC\u9489\u5F97\u66F4\u6B7B\u540E\u91CD\u8D77\u4E00\u8F6E\uFF1B\u4ECD\u4E0D\u884C\u7528 \`oasis gap ${artifactId} --desc "<\u5361\u5728\u54EA>"\` \u5192\u6CE1\u7ED9\u4EBA\u3002`
277525
+ ].join("\n");
276970
277526
  }
276971
277527
  };
276972
277528
  }
@@ -277848,8 +278404,8 @@ __export(src_exports2, {
277848
278404
  bindWorkorderProject: () => bindWorkorderProject,
277849
278405
  bindingResolverFromRegistry: () => bindingResolverFromRegistry,
277850
278406
  bindingRuntimeState: () => bindingRuntimeState,
277851
- boardWatchMarker: () => boardWatchMarker,
277852
278407
  boardWatchRunMarker: () => boardWatchRunMarker,
278408
+ boardWatchSinceMarker: () => boardWatchSinceMarker,
277853
278409
  broadcastMessageId: () => broadcastMessageId,
277854
278410
  broadcastMessageText: () => broadcastMessageText,
277855
278411
  bucketRuntimeRuns: () => bucketRuntimeRuns,
@@ -277953,7 +278509,6 @@ __export(src_exports2, {
277953
278509
  cronNext: () => cronNext,
277954
278510
  cronNextN: () => cronNextN,
277955
278511
  deadlineClockTick: () => deadlineClockTick,
277956
- decideBoardLetter: () => decideBoardLetter,
277957
278512
  defaultAvatarRefOf: () => defaultAvatarRefOf,
277958
278513
  delegationArtifactsView: () => delegationArtifactsView,
277959
278514
  delegationBundleAdditions: () => delegationBundleAdditions,
@@ -278104,6 +278659,7 @@ __export(src_exports2, {
278104
278659
  parseConnectorSkillId: () => parseConnectorSkillId,
278105
278660
  parseKnowledgeAnswerOutput: () => parseKnowledgeAnswerOutput,
278106
278661
  parseKnowledgePlan: () => parseKnowledgePlan,
278662
+ parseOdTime: () => parseOdTime,
278107
278663
  parseReadScope: () => parseReadScope,
278108
278664
  parseSkillFrontmatter: () => parseSkillFrontmatter,
278109
278665
  parseSymrefHead: () => parseSymrefHead,
@@ -278193,6 +278749,7 @@ __export(src_exports2, {
278193
278749
  retireCdGateInTypeStore: () => retireCdGateInTypeStore,
278194
278750
  rolesDomain: () => rolesDomain,
278195
278751
  rollbackOrgIdentity: () => rollbackOrgIdentity,
278752
+ roundOfMessage: () => roundOfMessage,
278196
278753
  routeFusedSignal: () => routeFusedSignal,
278197
278754
  runtimeConnectionStatus: () => runtimeConnectionStatus,
278198
278755
  runtimeDriftBlock: () => runtimeDriftBlock,
@@ -278758,14 +279315,14 @@ init_src();
278758
279315
  var os16 = __toESM(require("node:os"));
278759
279316
  var path46 = __toESM(require("node:path"));
278760
279317
  var import_node_child_process26 = require("node:child_process");
278761
- var import_node_crypto94 = require("node:crypto");
279318
+ var import_node_crypto95 = require("node:crypto");
278762
279319
 
278763
279320
  // ../cli/src/cli.ts
278764
279321
  init_src();
278765
279322
  var fs44 = __toESM(require("node:fs"), 1);
278766
279323
  var os13 = __toESM(require("node:os"), 1);
278767
279324
  var path39 = __toESM(require("node:path"), 1);
278768
- var import_node_crypto92 = require("node:crypto");
279325
+ var import_node_crypto93 = require("node:crypto");
278769
279326
 
278770
279327
  // ../cli/src/serve.ts
278771
279328
  init_src11();
@@ -278777,11 +279334,11 @@ var fs36 = __toESM(require("node:fs"), 1);
278777
279334
  var os9 = __toESM(require("node:os"), 1);
278778
279335
  var path30 = __toESM(require("node:path"), 1);
278779
279336
  var import_node_child_process18 = require("node:child_process");
278780
- var import_node_crypto88 = require("node:crypto");
279337
+ var import_node_crypto89 = require("node:crypto");
278781
279338
  var import_node_url7 = require("node:url");
278782
279339
 
278783
279340
  // ../cli/src/artifact-discovery-source.ts
278784
- var import_node_crypto84 = require("node:crypto");
279341
+ var import_node_crypto85 = require("node:crypto");
278785
279342
  init_src7();
278786
279343
  var ArtifactDiscoverySourceUnreadableError = class extends Error {
278787
279344
  constructor(message) {
@@ -278811,7 +279368,7 @@ async function resolveArtifactDiscoverySourceContent(blobs, input) {
278811
279368
  throw new ArtifactDiscoverySourceUnreadableError(`Artifact Revision ${input.revisionId} \u7684 contentKind \u4E0D\u53D7\u652F\u6301\uFF1A${String(input.contentKind)}`);
278812
279369
  }
278813
279370
  const rootBytes = await blobs.get(input.contentRef);
278814
- const sourceFingerprint = (0, import_node_crypto84.createHash)("sha256").update(rootBytes).digest("hex");
279371
+ const sourceFingerprint = (0, import_node_crypto85.createHash)("sha256").update(rootBytes).digest("hex");
278815
279372
  if (input.contentKind === "inline-blob") {
278816
279373
  const sourceContent = decodeText3(rootBytes);
278817
279374
  if (sourceContent === null) {
@@ -278835,7 +279392,7 @@ ${entry.text}`).join("\n\n"),
278835
279392
  }
278836
279393
 
278837
279394
  // ../server/src/domains/discussions/service.ts
278838
- var import_node_crypto85 = require("node:crypto");
279395
+ var import_node_crypto86 = require("node:crypto");
278839
279396
  init_zod();
278840
279397
  init_discussions();
278841
279398
  init_router();
@@ -278897,7 +279454,7 @@ function remapTopics(topics, removed) {
278897
279454
  return [...out].map(([topic_id, summary]) => ({ topic_id, summary }));
278898
279455
  }
278899
279456
  var entryId = (projectId2, id) => JSON.stringify([projectId2, id]);
278900
- var hash2 = (s2) => (0, import_node_crypto85.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
279457
+ var hash2 = (s2) => (0, import_node_crypto86.createHash)("sha256").update(s2).digest("hex").slice(0, 24);
278901
279458
  var canonical = (s2) => s2.normalize("NFKC").trim().toLocaleLowerCase();
278902
279459
  function decisionFingerprint(d) {
278903
279460
  return hash2(JSON.stringify([d.statement, d.rationale, d.alternatives]));
@@ -279117,7 +279674,7 @@ var DiscussionService = class {
279117
279674
  throw new ApiError(409, "DISCUSSION_CONFLICT", "\u9879\u76EE\u8BAE\u9898\u6216\u51B3\u7B56\u5DF2\u53D8\u5316\uFF0C\u8BF7\u8BFB\u53D6\u6700\u65B0\u5185\u5BB9\u540E\u4FEE\u6B63");
279118
279675
  }
279119
279676
  }
279120
- const operation = (value2) => tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
279677
+ const operation = (value2) => tx.put("operation", (0, import_node_crypto86.randomUUID)(), {
279121
279678
  ...value2,
279122
279679
  projectId: projectId2,
279123
279680
  actorId: ctx.actorId,
@@ -279134,7 +279691,7 @@ var DiscussionService = class {
279134
279691
  if (id.startsWith("new:")) {
279135
279692
  if (BUG_TOPIC.test(t.title.trim())) invalid("topics.title", `\u300C${t.title}\u300D\u662F\u5355\u4E2A Bug\uFF1A\u4FEE\u590D\u60C5\u51B5\u5199\u8FDB\u8BA8\u8BBA\u8BB0\u5F55\uFF0C\u5B9A\u51FA\u7684\u89C4\u5219\u6302\u5230\u5BF9\u5E94\u529F\u80FD\u8BAE\u9898\u4E0B\uFF0C\u4E0D\u6309 Bug \u53F7\u5EFA\u8BAE\u9898`);
279136
279693
  const existing = [...topicMap.values()].find((x2) => canonical(x2.title) === canonical(t.title));
279137
- id = existing?.topicId ?? `topic_${(0, import_node_crypto85.randomUUID)()}`;
279694
+ id = existing?.topicId ?? `topic_${(0, import_node_crypto86.randomUUID)()}`;
279138
279695
  if (!existing) createdTopics.add(id);
279139
279696
  } else if (!topicMap.has(id)) invalid("topics.topic_id", `\u8BAE\u9898 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u8BAE\u9898\u4F7F\u7528 new: \u524D\u7F00`);
279140
279697
  topicAliases.set(t.topic_id, id);
@@ -279186,7 +279743,7 @@ var DiscussionService = class {
279186
279743
  if (id.startsWith("new:")) {
279187
279744
  const candidateKey = JSON.stringify([topicId, canonical(d.record.statement)]);
279188
279745
  const existing = [...decisionMap.values()].find((x2) => x2.topicId === topicId && canonical(x2.record.statement) === canonical(d.record.statement));
279189
- id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto85.randomUUID)()}`;
279746
+ id = existing?.decisionId ?? candidateIds.get(candidateKey) ?? `decision_${(0, import_node_crypto86.randomUUID)()}`;
279190
279747
  candidateIds.set(candidateKey, id);
279191
279748
  } else if (!decisionMap.has(id)) invalid("decision_id", `\u51B3\u7B56 ${id} \u4E0D\u5B58\u5728\uFF1B\u65B0\u51B3\u7B56\u4F7F\u7528 new: \u524D\u7F00`);
279192
279749
  decisionAliases.set(d.decision_id, id);
@@ -279448,7 +280005,7 @@ var DiscussionService = class {
279448
280005
  ...row.value.confirmation ? { confirmation: { ...row.value.confirmation, fingerprint: decisionFingerprint(record9) } } : {}
279449
280006
  };
279450
280007
  await tx.put("decision", row.id, value2);
279451
- await tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
280008
+ await tx.put("operation", (0, import_node_crypto86.randomUUID)(), {
279452
280009
  action: "edit",
279453
280010
  kind: "decision",
279454
280011
  projectId: projectId2,
@@ -279512,7 +280069,7 @@ var DiscussionService = class {
279512
280069
  if (!current || current.revision !== row.revision) throw new ApiError(409, "DISCUSSION_CONFLICT", `\u8BA8\u8BBA ${row.id} \u5DF2\u88AB\u4FEE\u6539\uFF0C\u8BF7\u8BFB\u53D6\u6700\u65B0\u5185\u5BB9\u540E\u91CD\u8BD5`);
279513
280070
  const summary = given.get(row.id);
279514
280071
  await tx.put("discussion", row.id, { ...current.value, record: { ...current.value.record, summary } });
279515
- await tx.put("operation", (0, import_node_crypto85.randomUUID)(), {
280072
+ await tx.put("operation", (0, import_node_crypto86.randomUUID)(), {
279516
280073
  action: "edit",
279517
280074
  kind: "discussion",
279518
280075
  projectId: current.value.record.project_id,
@@ -279528,7 +280085,7 @@ var DiscussionService = class {
279528
280085
  });
279529
280086
  }
279530
280087
  async operation(tx, actorId, kind, id, from, to) {
279531
- await tx.put("operation", (0, import_node_crypto85.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
280088
+ await tx.put("operation", (0, import_node_crypto86.randomUUID)(), { actorId, kind, id, from, to, at: new Date(this.now()).toISOString() });
279532
280089
  }
279533
280090
  };
279534
280091
  function pickDiscussionContent(d) {
@@ -279828,7 +280385,7 @@ var DiscussionContext = class {
279828
280385
  };
279829
280386
 
279830
280387
  // ../server/src/domains/discussions/worker.ts
279831
- var import_node_crypto86 = require("node:crypto");
280388
+ var import_node_crypto87 = require("node:crypto");
279832
280389
  var DISCUSSION_IDLE_MS = 15 * 6e4;
279833
280390
  var DISCUSSION_MAX_CORRECTIONS = 3;
279834
280391
  var DISCUSSION_LEASE_MS = 2 * 6e4;
@@ -279854,7 +280411,7 @@ var DiscussionWorker = class {
279854
280411
  if (await this.isReplying(sessionId)) return false;
279855
280412
  const cutoff = eligibleCutoff(await this.service.chat.listMessages(sessionId), this.service.now());
279856
280413
  if (!cutoff) return false;
279857
- const owner = (0, import_node_crypto86.randomUUID)();
280414
+ const owner = (0, import_node_crypto87.randomUUID)();
279858
280415
  const claimed = await this.service.store.transaction(async (tx) => {
279859
280416
  const old = (await tx.get("job", sessionId))?.value;
279860
280417
  if (old?.status === "running" && (old.leaseUntil ?? 0) > this.service.now()) return null;
@@ -280576,7 +281133,7 @@ function createDiscussionRuntime(options) {
280576
281133
  }
280577
281134
 
280578
281135
  // ../server/src/domains/discussions/store.ts
280579
- var import_node_crypto87 = require("node:crypto");
281136
+ var import_node_crypto88 = require("node:crypto");
280580
281137
  var import_promises17 = require("node:fs/promises");
280581
281138
  var import_node_path27 = require("node:path");
280582
281139
  var key = (kind, id) => JSON.stringify([kind, id]);
@@ -280621,7 +281178,7 @@ var MemoryDiscussionStore = class _MemoryDiscussionStore {
280621
281178
  if (changed) {
280622
281179
  if (this.filename) {
280623
281180
  await (0, import_promises17.mkdir)((0, import_node_path27.dirname)(this.filename), { recursive: true });
280624
- const temp = `${this.filename}.${(0, import_node_crypto87.randomUUID)()}.tmp`;
281181
+ const temp = `${this.filename}.${(0, import_node_crypto88.randomUUID)()}.tmp`;
280625
281182
  await (0, import_promises17.writeFile)(temp, JSON.stringify(next), "utf8");
280626
281183
  await (0, import_promises17.rename)(temp, this.filename);
280627
281184
  }
@@ -281570,7 +282127,7 @@ function oasisWrapperScript() {
281570
282127
  const repoRoot = (0, import_node_url7.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
281571
282128
  const tsx = path30.join(repoRoot, "node_modules/.bin/tsx");
281572
282129
  const main = path30.join(repoRoot, "packages/cli/src/main.ts");
281573
- const key2 = (0, import_node_crypto88.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
282130
+ const key2 = (0, import_node_crypto89.createHash)("sha256").update(repoRoot).digest("hex").slice(0, 12);
281574
282131
  const uid = typeof process.getuid === "function" ? process.getuid() : "na";
281575
282132
  const stable = path30.join(os9.tmpdir(), `oasis-cli-wrapper-${uid}-${key2}`);
281576
282133
  let dir;
@@ -281963,7 +282520,7 @@ async function startServe(opts) {
281963
282520
  if (!hub) return Promise.resolve({ ok: false, reason: "\u672C\u8FDB\u7A0B\u6CA1\u6709\u8282\u70B9\u7F51\u5173" });
281964
282521
  const online = hub.connectedDaemons().some((d) => d.daemonId === nodeId);
281965
282522
  if (!online) return Promise.resolve({ ok: false, reason: "\u8282\u70B9\u5F53\u524D\u4E0D\u5728\u7EBF\uFF0C\u7528\u7684\u662F\u5B83\u4E0A\u6B21\u62A5\u7684\u6E05\u5355" });
281966
- const requestId = (0, import_node_crypto88.randomUUID)();
282523
+ const requestId = (0, import_node_crypto89.randomUUID)();
281967
282524
  const task = new Promise((resolve12) => {
281968
282525
  const timer = setTimeout(() => {
281969
282526
  modelRefreshWaiters.delete(requestId);
@@ -282792,7 +283349,7 @@ async function startServe(opts) {
282792
283349
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
282793
283350
  if (buf || session.runId) {
282794
283351
  await turnStore.appendMessage({
282795
- id: (0, import_node_crypto88.randomUUID)(),
283352
+ id: (0, import_node_crypto89.randomUUID)(),
282796
283353
  sessionId: chatSessionId,
282797
283354
  role: "assistant",
282798
283355
  content: buf,
@@ -282838,7 +283395,7 @@ async function startServe(opts) {
282838
283395
  chatTargets: {
282839
283396
  inspect: inspectChatTarget,
282840
283397
  createTargetSession: async ({ companyId, agentId, ownerHumanActorId, title }) => {
282841
- const sessionId = (0, import_node_crypto88.randomUUID)();
283398
+ const sessionId = (0, import_node_crypto89.randomUUID)();
282842
283399
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
282843
283400
  const binding = await registryStore.getBinding(agentId).catch(() => null);
282844
283401
  await (await chatStoreFor(companyId)).createSession({
@@ -284240,7 +284797,7 @@ async function startServe(opts) {
284240
284797
  }));
284241
284798
  }
284242
284799
  const limits = { wallClockMs: wallClockForKind("planner") };
284243
- const artifactId = `artifact:planner:${(0, import_node_crypto88.randomUUID)()}`;
284800
+ const artifactId = `artifact:planner:${(0, import_node_crypto89.randomUUID)()}`;
284244
284801
  const handle = await chatRemoteAdapter.spawn({
284245
284802
  actor: planner.id,
284246
284803
  // companyId 同协调者:planner 用的就是默认公司的 actors 服务,漏签会让它一调公司域端点就 404。
@@ -285161,10 +285718,10 @@ async function startServe(opts) {
285161
285718
  if (isDeletedActor(dispatchTarget)) {
285162
285719
  throw new ApiError(409, ACTOR_DELETED_ERROR_CODE, ACTOR_DELETED_CHAT_MESSAGE);
285163
285720
  }
285164
- const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto88.randomUUID)());
285721
+ const runtimeSessionId = sessionId ?? (requestDispatchId ? deterministicUuid(`chat-runtime:${requestDispatchId}`) : (0, import_node_crypto89.randomUUID)());
285165
285722
  const resumeRuntimeSession = Boolean(sessionId);
285166
- const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto88.randomUUID)()}`;
285167
- const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto88.randomUUID)()}`;
285723
+ const traceRunId = requestDispatchId ? stableChatTraceRunId(requestDispatchId) : `chat-run:${(0, import_node_crypto89.randomUUID)()}`;
285724
+ const artifactId = requestDispatchId ? `artifact:chat:${deterministicUuid(`chat-artifact:${requestDispatchId}`)}` : `artifact:chat:${(0, import_node_crypto89.randomUUID)()}`;
285168
285725
  let stableDispatch = requestDispatchId ? await resolveStableChatDispatch({ store: await traceStoreFor(runCompanyId), runId: traceRunId }) : null;
285169
285726
  if (stableDispatch && !stableDispatch.shouldDispatch) {
285170
285727
  if (isHumanSubmitDispatchId(requestDispatchId)) {
@@ -286622,7 +287179,7 @@ async function startServe(opts) {
286622
287179
  console.log(`[new-engine] dispatchWork ${workId} \u662F human work\u2014\u2014\u4E0D\u6D3E runtime\uFF0C\u7B49\u5F85\u4EBA\u5DE5\u5904\u7406`);
286623
287180
  return;
286624
287181
  }
286625
- const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto88.randomUUID)()}`;
287182
+ const ledgerId = opts2?.protocolRecovery?.dispatchId ?? `dispatch:${(0, import_node_crypto89.randomUUID)()}`;
286626
287183
  try {
286627
287184
  const inserted = await (await dispatchLedgerFor(companyId)).insertOpen({
286628
287185
  id: ledgerId,
@@ -286722,7 +287279,7 @@ async function startServe(opts) {
286722
287279
  const kernelModel = newKernel.model;
286723
287280
  const art = kernelModel.artifacts.get(nodeId);
286724
287281
  const revWid = art?.workspace ?? "";
286725
- const ledgerId = `dispatch:${(0, import_node_crypto88.randomUUID)()}`;
287282
+ const ledgerId = `dispatch:${(0, import_node_crypto89.randomUUID)()}`;
286726
287283
  try {
286727
287284
  await (await dispatchLedgerFor(companyId)).insertOpen({
286728
287285
  id: ledgerId,
@@ -287116,7 +287673,7 @@ async function startServe(opts) {
287116
287673
  resolveSupplementalContext: server.knowledgeGovernance ? async ({ actorId, artifactId, workOrderId, action, part, runtimeKind, taskMarkdown }) => {
287117
287674
  try {
287118
287675
  const governance = server.knowledgeGovernance;
287119
- const taskFingerprint = (0, import_node_crypto88.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
287676
+ const taskFingerprint = (0, import_node_crypto89.createHash)("sha256").update(JSON.stringify({ artifactId, workOrderId, action, part: part ?? null, taskMarkdown })).digest("hex");
287120
287677
  const requestContext = {
287121
287678
  organizationId: companyId,
287122
287679
  actorId,
@@ -288579,7 +289136,12 @@ ${nodeFault}` : "");
288579
289136
  const odBase = (process.env["OASIS_OPEN_DESIGN_URL"] ?? "http://127.0.0.1:7457").replace(/\/$/, "");
288580
289137
  const odToken = process.env["OASIS_OPEN_DESIGN_TOKEN"]?.trim() || void 0;
288581
289138
  const boardWatcher = new BoardWatcher({
288582
- kernel: defaultCompanyKernel,
289139
+ listCompanies: async () => {
289140
+ if (!chatStoreRouter) return [defaultCompanyId];
289141
+ const active = (await controlPlaneStore.listCompanies()).filter((c) => c.status === "active").map((c) => c.id);
289142
+ return [defaultCompanyId, ...active.filter((id) => id !== defaultCompanyId)];
289143
+ },
289144
+ kernelFor: async (companyId) => (await getEngine(companyId)).kernel,
288583
289145
  odBase,
288584
289146
  ...odToken ? { odToken } : {},
288585
289147
  boardKeyOf: deriveBoardKey,
@@ -288588,7 +289150,7 @@ ${nodeFault}` : "");
288588
289150
  boardWatchTimer = setInterval(() => {
288589
289151
  void boardWatcher.tick().catch((err) => console.error(`[board-watch] tick \u5931\u8D25: ${String(err)}`));
288590
289152
  }, 6e4);
288591
- console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u542F\u7528\uFF08od=${odBase}${odToken ? "\uFF0C\u5E26\u4EE4\u724C" : ""}\uFF0C\u9759\u9ED8\u7A97 10min\uFF0C\u6BCF\u8F6E\u4E0A\u9650 2 \u5C01\u4FE1\uFF1BOASIS_BOARD_WATCH=0 \u53EF\u5173\uFF09`);
289153
+ console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u542F\u7528\uFF08od=${odBase}${odToken ? "\uFF0C\u5E26\u4EE4\u724C" : ""}\uFF0C\u5DE1\u6240\u6709\u516C\u53F8\uFF0C\u6BCF\u7ED3\u675F\u4E00\u8F6E\u5BF9\u8BDD\u5199\u4E00\u5C01\u4FE1\uFF0C\u6BCF\u8F6E\u6BCF\u5BB6\u516C\u53F8\u4E0A\u9650 2 \u5C01\uFF1BOASIS_BOARD_WATCH=0 \u53EF\u5173\uFF09`);
288592
289154
  } else {
288593
289155
  console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u88AB OASIS_BOARD_WATCH=${boardWatchFlag} \u663E\u5F0F\u5173\u95ED`);
288594
289156
  }
@@ -289835,7 +290397,7 @@ var RuntimeQuotaMonitor = class {
289835
290397
  init_wrapper();
289836
290398
  init_src();
289837
290399
  var import_node_os11 = require("node:os");
289838
- var import_node_crypto89 = require("node:crypto");
290400
+ var import_node_crypto90 = require("node:crypto");
289839
290401
  init_src8();
289840
290402
  init_src2();
289841
290403
 
@@ -290739,7 +291301,7 @@ var DaemonWsClient = class {
290739
291301
  */
290740
291302
  async queryArtifactStates(artifactIds, timeoutMs = 15e3) {
290741
291303
  if (artifactIds.length === 0) return {};
290742
- const requestId = (0, import_node_crypto89.randomUUID)();
291304
+ const requestId = (0, import_node_crypto90.randomUUID)();
290743
291305
  return new Promise((resolve12, reject) => {
290744
291306
  const timer = setTimeout(() => {
290745
291307
  this.gcPending.delete(requestId);
@@ -291153,7 +291715,7 @@ function forwardSignal(child, sig, killGroup = (pgid, signal) => {
291153
291715
  // ../cli/src/daemon/machine-id.ts
291154
291716
  var import_node_child_process23 = require("node:child_process");
291155
291717
  var import_node_fs24 = require("node:fs");
291156
- var import_node_crypto90 = require("node:crypto");
291718
+ var import_node_crypto91 = require("node:crypto");
291157
291719
  var import_node_os14 = require("node:os");
291158
291720
  function linuxMachineId() {
291159
291721
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
@@ -291219,7 +291781,7 @@ var defaultSources = {
291219
291781
  function resolveNodeId(sources = {}) {
291220
291782
  const s2 = { ...defaultSources, ...sources };
291221
291783
  const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
291222
- const digest = (0, import_node_crypto90.createHash)("sha256").update(material).digest("hex").slice(0, 12);
291784
+ const digest = (0, import_node_crypto91.createHash)("sha256").update(material).digest("hex").slice(0, 12);
291223
291785
  return `node-${digest}`;
291224
291786
  }
291225
291787
 
@@ -293091,13 +293653,13 @@ function ownFlagsFromDecl(command2) {
293091
293653
  }
293092
293654
 
293093
293655
  // ../cli/src/connector-effect-runner.ts
293094
- var import_node_crypto91 = require("node:crypto");
293656
+ var import_node_crypto92 = require("node:crypto");
293095
293657
  var import_node_child_process24 = require("node:child_process");
293096
293658
  init_src8();
293097
293659
  function stableKey(effect) {
293098
293660
  const keyFields = Object.fromEntries(Object.entries(effect.keyFields).sort(([a], [b2]) => a.localeCompare(b2)));
293099
293661
  const canonical2 = JSON.stringify({ kind: effect.kind, target: effect.target, keyFields });
293100
- return `sha256:${(0, import_node_crypto91.createHash)("sha256").update(canonical2).digest("hex")}`;
293662
+ return `sha256:${(0, import_node_crypto92.createHash)("sha256").update(canonical2).digest("hex")}`;
293101
293663
  }
293102
293664
  async function runConnectorEffect(connector, toolArgs, deps) {
293103
293665
  const effect = connector.describeExternalEffect?.(toolArgs) ?? null;
@@ -293652,7 +294214,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
293652
294214
  const timeoutMs = resolveAuthWaitMs();
293653
294215
  const controller = new AbortController();
293654
294216
  const timer = setTimeout(() => controller.abort(), timeoutMs);
293655
- const waiterId = (0, import_node_crypto92.randomUUID)();
294217
+ const waiterId = (0, import_node_crypto93.randomUUID)();
293656
294218
  process.stderr.write(
293657
294219
  `\u7B49\u5F85\u4EBA\u5DE5\u6388\u6743\u4E2D\u2026\u2026\u6700\u591A ${Math.round(timeoutMs / 1e3)} \u79D2\uFF0C\u8D85\u65F6\u4F1A\u81EA\u52A8\u8F6C\u4E3A\u5F85\u5BA1\uFF1B\u8FD9\u4E0D\u662F\u9519\u8BEF\uFF0C\u8BF7\u52FF\u91CD\u8BD5\u540C\u4E00\u6761\u547D\u4EE4\u3002
293658
294220
  `
@@ -293736,7 +294298,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
293736
294298
  }
293737
294299
  }
293738
294300
  }
293739
- var CLI_VERSION = "2.2.82" ? "2.2.82" : "dev";
294301
+ var CLI_VERSION = "2.2.83" ? "2.2.83" : "dev";
293740
294302
  function apiHeaders(token, company) {
293741
294303
  const headers = {
293742
294304
  authorization: `Bearer ${token}`,
@@ -294102,7 +294664,7 @@ async function runCli(argv, println = console.log, progressln = console.error) {
294102
294664
  const store = createNodeTokenStore(path39.join(dir, "node-tokens.json"));
294103
294665
  const sub = positional[0];
294104
294666
  if (sub === "issue") {
294105
- const id = flags.get("id") ?? `node-${(0, import_node_crypto92.randomUUID)()}`;
294667
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto93.randomUUID)()}`;
294106
294668
  const token2 = store.issue(id);
294107
294669
  println(token2);
294108
294670
  process.stderr.write(
@@ -295252,7 +295814,7 @@ ${t.ref} ${t.name} [\u5267\u672C=${t.playbookRef}]`);
295252
295814
  if (!name) throw new Error("\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
295253
295815
  const goal = flags.get("goal")?.trim();
295254
295816
  const slug6 = flags.get("slug");
295255
- const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto92.randomUUID)());
295817
+ const id = slug6 !== void 0 ? projectSlug(slug6) : projectIdFromUuid((0, import_node_crypto93.randomUUID)());
295256
295818
  const result = await api.request("POST", "/api/projects", {
295257
295819
  ...id ? { id } : {},
295258
295820
  name,
@@ -297523,13 +298085,13 @@ function runtimeAssetSourceRoots(input) {
297523
298085
  }
297524
298086
 
297525
298087
  // src/install.ts
297526
- var import_node_crypto93 = require("node:crypto");
298088
+ var import_node_crypto94 = require("node:crypto");
297527
298089
  var fs50 = __toESM(require("node:fs"));
297528
298090
  var path45 = __toESM(require("node:path"));
297529
298091
  function versionLabel(sourceFile, pkgVersion, npmPrefixDir, pkgName = LEGACY_PKG_NAME) {
297530
298092
  const tag = pkgName === LEGACY_PKG_NAME ? "" : `+pkg.${pkgName.replace(/[^A-Za-z0-9-]/g, "-")}`;
297531
298093
  if (isFromNpmPrefix(sourceFile, npmPrefixDir)) return `${pkgVersion}${tag}`;
297532
- const digest = (0, import_node_crypto93.createHash)("sha256").update(fs50.readFileSync(sourceFile)).digest("hex").slice(0, 8);
298094
+ const digest = (0, import_node_crypto94.createHash)("sha256").update(fs50.readFileSync(sourceFile)).digest("hex").slice(0, 8);
297533
298095
  return tag ? `${pkgVersion}${tag}.local.${digest}` : `${pkgVersion}+local.${digest}`;
297534
298096
  }
297535
298097
  function isFromNpmPrefix(sourceFile, npmPrefixDir) {
@@ -297652,7 +298214,7 @@ function shimScript() {
297652
298214
  }
297653
298215
 
297654
298216
  // src/index.ts
297655
- var PKG_VERSION = true ? "2.2.82" : "dev";
298217
+ var PKG_VERSION = true ? "2.2.83" : "dev";
297656
298218
  var LOCAL_BIN = localBin();
297657
298219
  var NPM_PREFIX = npmPrefix();
297658
298220
  var INSTANCE = DEFAULT_INSTANCE;
@@ -297960,7 +298522,7 @@ function newInstanceName() {
297960
298522
  const existing = new Set(listInstances());
297961
298523
  if (!existing.has(DEFAULT_INSTANCE)) return DEFAULT_INSTANCE;
297962
298524
  for (; ; ) {
297963
- const n = `inst-${(0, import_node_crypto94.randomBytes)(3).toString("hex")}`;
298525
+ const n = `inst-${(0, import_node_crypto95.randomBytes)(3).toString("hex")}`;
297964
298526
  if (!existing.has(n)) return n;
297965
298527
  }
297966
298528
  }