oasis_test_v2 2.1.0 → 2.1.1

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 +182 -45
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1391,6 +1391,11 @@ var init_delegation_prompts = __esm({
1391
1391
  "**\u522B\u628A\u5224\u65AD\u8FC7\u7A0B\u8BF4\u7ED9\u7528\u6237**\uFF08\u4E0D\u62A5\u300C\u6211\u8BC4\u4F30\u4E86\u4E0B\u80FD\u529B\u8FB9\u754C\u300D\u300C\u5019\u9009\u6709 3 \u4E2A\u300D\u300C\u6B63\u5728\u9009\u4EBA\u300D\uFF09\uFF0C",
1392
1392
  "\u4E5F**\u522B\u8BA9\u7528\u6237\u81EA\u5DF1\u4ECE\u540D\u5355\u91CC\u6311\u4EBA**\u3002\u4ED6\u53EA\u8BE5\u770B\u5230\uFF1A\u4F60\u7684\u8FFD\u95EE\u3001\u4F60\u5728\u505A\u4EC0\u4E48\u3001\u4EE5\u53CA\u9700\u8981\u4ED6\u786E\u8BA4\u65F6\u7684\u90A3\u5F20\u5361\u3002",
1393
1393
  "",
1394
+ "### \u7528\u6237\u76F4\u63A5\u95EE\u300C\u6709\u54EA\u4E9B\u4EBA / agent\u300D\uFF1A\u522B\u8D70\u4E0B\u9762\u8FD9\u5957",
1395
+ "",
1396
+ "\u90A3\u662F\u5728\u95EE\u82B1\u540D\u518C\uFF0C\u4E0D\u662F\u8BA9\u4F60\u5206\u6D41\u3002\u76F4\u63A5 `oasis actors` \u5982\u5B9E\u5217\uFF08**\u542B `(\u65E0\u5C97\u4F4D)` \u7684**\uFF09\u3002",
1397
+ "**\u522B\u62FF `oasis roles` \u7684\u5728\u5C97\u4EBA\u6570\u56DE\u7B54\u8FD9\u4E2A\u95EE\u9898**\u2014\u2014\u5B83\u53EA\u6570\u5C97\u4F4D\u3001\u4E0D\u5217\u4EBA\u3002",
1398
+ "",
1394
1399
  "### \u5148\u770B\u76EE\u6807\u6E05\u4E0D\u6E05\u695A",
1395
1400
  "",
1396
1401
  "\u4E0D\u6E05\u695A\u5C31**\u53EA\u8FFD\u95EE\u3001\u522B\u52A8\u624B**\u2014\u2014\u522B\u628A\u4E00\u4E2A\u6A21\u7CCA\u76EE\u6807\u4E22\u7ED9\u4E13\u5BB6\u6216\u5DE5\u5355\uFF0C\u90A3\u662F\u628A\u4F60\u7684\u4E0D\u786E\u5B9A\u8F6C\u5AC1\u51FA\u53BB\u3002",
@@ -1403,9 +1408,12 @@ var init_delegation_prompts = __esm({
1403
1408
  "",
1404
1409
  " oasis roles # \u6BCF\u4E2A\u5C97\u4F4D\u8D1F\u8D23\xB7\u8BC4\u5BA1\u54EA\u4E9B\u4EA4\u4ED8\u7269\u3001\u8FD8\u6709\u51E0\u4EBA\u5728\u5C97",
1405
1410
  " oasis actors --role <\u5C97\u4F4D> # \u90A3\u4E2A\u5C97\u4F4D\u73B0\u5728\u6709\u8C01\u53EF\u6D3E",
1411
+ " oasis actors # \u5168\u90E8\u5728\u5C97\u7684\u4EBA/agent\uFF08\u672A\u8BBE\u5C97\u4F4D\u7684\u53EA\u6709\u8FD9\u6761\u770B\u5F97\u89C1\uFF09",
1406
1412
  "",
1407
1413
  "**\u53EA\u8BA4\u660E\u786E\u5199\u4E86\u300C\u8D1F\u8D23 <\u67D0\u7C7B\u4EA4\u4ED8\u7269>\u300D\u7684\u5C97\u4F4D\u3002**\u53EA\u6709\u4E00\u4E2A\u540D\u5B57\u3001\u6CA1\u5199\u8D1F\u8D23\u4EC0\u4E48\u7684\uFF0C",
1408
1414
  "\u4E00\u5F8B**\u5F53\u4F5C\u6CA1\u6709\u5BF9\u53E3\u5C97\u4F4D**\u2014\u2014\u90A3\u79CD\u5C97\u4F4D\u6D3E\u8FC7\u53BB\u591A\u534A\u662F\u7A7A\u8F6C\u3002",
1415
+ "**\u4F46\u300C\u6CA1\u6709\u5BF9\u53E3\u5C97\u4F4D\u300D\u2260\u300C\u6CA1\u4EBA\u53EF\u7528\u300D**\uFF1A\u6709\u7684 agent \u6545\u610F\u4E0D\u8BBE\u5C97\u4F4D\uFF08`actors` \u91CC\u663E\u793A `(\u65E0\u5C97\u4F4D)`\uFF09\uFF0C",
1416
+ "\u4E0D\u8FDB\u6309\u5C97\u4F4D\u5206\u6D41\uFF0C\u4F46\u80FD `--to <actorId>` \u70B9\u540D\u6D3E\u3002`roles` \u6570\u51FA 0 \u4EBA**\u4E0D\u7B49\u4E8E**\u6CA1\u6709 agent\u2014\u2014\u5B83\u4E0D\u5217\u4EBA\u3002",
1409
1417
  "",
1410
1418
  "- **\u6709\u5BF9\u53E3\u5C97\u4F4D + \u6709\u4EBA\u5728\u5C97** \u2192 \u518D\u95EE\u4E00\u53E5\uFF1A**\u8FD9\u6D3B\u8981\u90A3\u4E2A\u5C97\u4F4D\u505A\u5224\u65AD\uFF0C\u8FD8\u662F\u53EA\u8981\u6309\u5DF2\u7ED9\u7684\u5185\u5BB9\u548C\u5DF2\u6709\u7684\u98CE\u683C\u6267\u884C\uFF1F**",
1411
1419
  " - \u8981\u51FA\u65B0\u8BBE\u8BA1\u3001\u8981\u4E0B\u7814\u7A76\u7ED3\u8BBA\u3001\u8981\u505A\u6280\u672F\u9009\u578B \u2192 \u5F80 \u2461 \u770B\u662F\u4E0D\u662F\u591A\u4EF6\uFF0C\u7136\u540E\u59D4\u6D3E\u3002",
@@ -17692,12 +17700,12 @@ function mapIssueKindToAnnotationKind(k2) {
17692
17700
  return k2 === "change_request" ? "change-request" : "comment";
17693
17701
  }
17694
17702
  function nodeToArtifact(node2, edges, requirements) {
17695
- const inputs = edges.filter((e) => e.toNodeId === node2.id).map((e) => ({
17703
+ const inputs = edges.map((e) => ({
17696
17704
  to: e.fromNodeId,
17697
17705
  pinned: e.pinnedWorkId ?? null,
17698
17706
  required: e.required
17699
17707
  }));
17700
- const nodeReqs = requirements.filter((r) => r.nodeId === node2.id);
17708
+ const nodeReqs = requirements;
17701
17709
  const reviewers = nodeReqs.length > 0 ? nodeReqs.map((r) => ({
17702
17710
  actor: r.reviewerActorId,
17703
17711
  source: r.source,
@@ -17736,9 +17744,8 @@ function nodeToArtifact(node2, edges, requirements) {
17736
17744
  };
17737
17745
  }
17738
17746
  function workToRevision(w2, workInputs, artifactFiles) {
17739
- const inputs = workInputs.filter((i) => i.workId === w2.id);
17740
17747
  const basedOn = {};
17741
- for (const i of inputs) {
17748
+ for (const i of workInputs) {
17742
17749
  basedOn[i.upstreamNodeId] = i.upstreamWorkId;
17743
17750
  }
17744
17751
  const files = artifactFiles.get(w2.id);
@@ -17781,7 +17788,7 @@ function issueToAnnotation(issue2, replies, recipients) {
17781
17788
  target: issue2.aboutWorkId ?? issue2.aboutNodeId ?? "",
17782
17789
  span: void 0
17783
17790
  };
17784
- const ownReplies = replies.filter((r) => r.issueId === issue2.id).slice().sort((a, b2) => a.createdAt < b2.createdAt ? -1 : a.createdAt > b2.createdAt ? 1 : 0);
17791
+ const ownReplies = replies.slice().sort((a, b2) => a.createdAt < b2.createdAt ? -1 : a.createdAt > b2.createdAt ? 1 : 0);
17785
17792
  const thread = ownReplies.map((r, i) => ({
17786
17793
  author: r.authorActorId,
17787
17794
  ts: r.createdAt,
@@ -17875,9 +17882,8 @@ function buildGateFromRequirements(reqs) {
17875
17882
  });
17876
17883
  return { requirements: contractReqs, eligible, quorum };
17877
17884
  }
17878
- function workToConcludeAttempt(w2, requirements, reviews) {
17885
+ function workToConcludeAttempt(w2, reqs) {
17879
17886
  if (w2.acceptanceState === "draft" || (w2.acceptanceState === null || w2.acceptanceState === "rejected") && w2.status === "success" && w2.conclusion != null) {
17880
- const reqs = requirements.filter((r) => r.nodeId === w2.nodeId);
17881
17887
  if (reqs.length === 0) return null;
17882
17888
  return {
17883
17889
  head: w2.id,
@@ -17889,7 +17895,6 @@ function workToConcludeAttempt(w2, requirements, reviews) {
17889
17895
  };
17890
17896
  }
17891
17897
  if (w2.acceptanceState === "accepted" && w2.outputVersionNo !== null) {
17892
- const reqs = requirements.filter((r) => r.nodeId === w2.nodeId);
17893
17898
  return {
17894
17899
  head: w2.id,
17895
17900
  openedOn: w2.id,
@@ -17901,6 +17906,16 @@ function workToConcludeAttempt(w2, requirements, reviews) {
17901
17906
  }
17902
17907
  return null;
17903
17908
  }
17909
+ function groupRows(rows, keyOf3) {
17910
+ const groups = /* @__PURE__ */ new Map();
17911
+ for (const row of rows) {
17912
+ const key = keyOf3(row);
17913
+ const group = groups.get(key);
17914
+ if (group) group.push(row);
17915
+ else groups.set(key, [row]);
17916
+ }
17917
+ return groups;
17918
+ }
17904
17919
  function bridgeLoadReadModel(input) {
17905
17920
  const { snapshots: snapshots2, reworkWavefront = false, convergenceUnified = false, disabledActors } = input;
17906
17921
  const allNodes = [];
@@ -17930,12 +17945,18 @@ function bridgeLoadReadModel(input) {
17930
17945
  name: a.name
17931
17946
  })));
17932
17947
  }
17948
+ const inputsByWork = groupRows(allWorkInputs, (i) => i.workId);
17949
+ const edgesByTarget = groupRows(allEdges, (e) => e.toNodeId);
17950
+ const requirementsByNode = groupRows(allRequirements, (r) => r.nodeId);
17951
+ const repliesByIssue = groupRows(allReplies, (r) => r.issueId);
17952
+ const reviewById = new Map(allReviews.map((r) => [r.id, r]));
17933
17953
  const acceptBlockers = /* @__PURE__ */ new Map();
17934
17954
  for (const snap of snapshots2) {
17935
17955
  let state = null;
17956
+ const snapshotWorks = new Map(snap.works.map((w2) => [w2.id, w2]));
17936
17957
  for (const node2 of snap.nodes) {
17937
17958
  if (!node2.latestWorkId || node2.latestAcceptId === node2.latestWorkId) continue;
17938
- const lw = snap.works.find((w2) => w2.id === node2.latestWorkId);
17959
+ const lw = snapshotWorks.get(node2.latestWorkId);
17939
17960
  if (!lw || !lw.endedAt || lw.deadAt) continue;
17940
17961
  state ??= new WorkorderState(snap);
17941
17962
  const block = acceptBlock(state, lw.id);
@@ -17952,7 +17973,7 @@ function bridgeLoadReadModel(input) {
17952
17973
  const workById = /* @__PURE__ */ new Map();
17953
17974
  for (const w2 of allWorks) workById.set(w2.id, w2);
17954
17975
  for (const node2 of allNodes) {
17955
- const art = nodeToArtifact(node2, allEdges, allRequirements);
17976
+ const art = nodeToArtifact(node2, edgesByTarget.get(node2.id) ?? [], requirementsByNode.get(node2.id) ?? []);
17956
17977
  const lw = node2.latestWorkId ? workById.get(node2.latestWorkId) : void 0;
17957
17978
  if (lw) {
17958
17979
  const st = lw.status ?? (lw.retryAt ? "retry" : lw.deadAt ? "dead" : lw.endedAt ? lw.outcome === "failed" ? "failed" : "success" : "running");
@@ -17976,7 +17997,7 @@ function bridgeLoadReadModel(input) {
17976
17997
  }
17977
17998
  const revisions = /* @__PURE__ */ new Map();
17978
17999
  for (const w2 of allWorks) {
17979
- revisions.set(w2.id, workToRevision(w2, allWorkInputs, artifactFiles));
18000
+ revisions.set(w2.id, workToRevision(w2, inputsByWork.get(w2.id) ?? [], artifactFiles));
17980
18001
  }
17981
18002
  {
17982
18003
  const worksByNode = /* @__PURE__ */ new Map();
@@ -18007,7 +18028,7 @@ function bridgeLoadReadModel(input) {
18007
18028
  for (const issue2 of allIssues) {
18008
18029
  if (issue2.kind === "gap" || issue2.kind === "escalation") continue;
18009
18030
  annotationSeq.set(issue2.id, 0);
18010
- annotations.set(issue2.id, issueToAnnotation(issue2, allReplies, recipientsByIssue.get(issue2.id) ?? []));
18031
+ annotations.set(issue2.id, issueToAnnotation(issue2, repliesByIssue.get(issue2.id) ?? [], recipientsByIssue.get(issue2.id) ?? []));
18011
18032
  }
18012
18033
  const reviewVotes = /* @__PURE__ */ new Map();
18013
18034
  for (const r of allReviews) {
@@ -18019,7 +18040,7 @@ function bridgeLoadReadModel(input) {
18019
18040
  }
18020
18041
  const concludeAttempts = /* @__PURE__ */ new Map();
18021
18042
  for (const w2 of allWorks) {
18022
- const attempt = workToConcludeAttempt(w2, allRequirements, allReviews);
18043
+ const attempt = workToConcludeAttempt(w2, requirementsByNode.get(w2.nodeId) ?? []);
18023
18044
  if (!attempt) continue;
18024
18045
  const list2 = concludeAttempts.get(w2.nodeId) ?? [];
18025
18046
  list2.push(attempt);
@@ -18038,6 +18059,19 @@ function bridgeLoadReadModel(input) {
18038
18059
  let seq = 0;
18039
18060
  for (const w2 of allWorks) proposeSeq.set(w2.id, seq++);
18040
18061
  }
18062
+ const workTimeRanks = allWorks.map((w2) => ({ at: w2.createdAt, rank: (proposeSeq.get(w2.id) ?? -1) + 1 })).sort((a, b2) => a.at < b2.at ? -1 : a.at > b2.at ? 1 : 0);
18063
+ for (let i = 1; i < workTimeRanks.length; i++) {
18064
+ workTimeRanks[i].rank = Math.max(workTimeRanks[i].rank, workTimeRanks[i - 1].rank);
18065
+ }
18066
+ const seqBefore = (at) => {
18067
+ let low = 0, high = workTimeRanks.length;
18068
+ while (low < high) {
18069
+ const mid = Math.floor((low + high) / 2);
18070
+ if (workTimeRanks[mid].at < at) low = mid + 1;
18071
+ else high = mid;
18072
+ }
18073
+ return low === 0 ? 0 : workTimeRanks[low - 1].rank;
18074
+ };
18041
18075
  const gaps = /* @__PURE__ */ new Map();
18042
18076
  for (const issue2 of allIssues) {
18043
18077
  if (issue2.kind !== "gap") continue;
@@ -18054,14 +18088,7 @@ function bridgeLoadReadModel(input) {
18054
18088
  if (resolved) {
18055
18089
  if (issue2.resolvedNote !== null) gapView.note = issue2.resolvedNote;
18056
18090
  gapView.resolvedAt = issue2.resolvedAt;
18057
- let seqAtResolve = 0;
18058
- for (const w2 of allWorks) {
18059
- if (w2.createdAt < issue2.resolvedAt) {
18060
- const s2 = (proposeSeq.get(w2.id) ?? -1) + 1;
18061
- if (s2 > seqAtResolve) seqAtResolve = s2;
18062
- }
18063
- }
18064
- gapView.resolvedAtSeq = seqAtResolve;
18091
+ gapView.resolvedAtSeq = seqBefore(issue2.resolvedAt);
18065
18092
  }
18066
18093
  const list2 = gaps.get(nodeId) ?? [];
18067
18094
  list2.push(gapView);
@@ -18098,18 +18125,18 @@ function bridgeLoadReadModel(input) {
18098
18125
  for (const node2 of allNodes) {
18099
18126
  if (!node2.latestWorkId) continue;
18100
18127
  if (node2.cancelledAt) continue;
18101
- const lw = allWorks.find((w2) => w2.id === node2.latestWorkId);
18128
+ const lw = workById.get(node2.latestWorkId);
18102
18129
  if (!lw || !lw.deadAt) {
18103
- const head = node2.latestProposedWorkId ? allWorks.find((w2) => w2.id === node2.latestProposedWorkId) : void 0;
18130
+ const head = node2.latestProposedWorkId ? workById.get(node2.latestProposedWorkId) : void 0;
18104
18131
  if (!head || head.deadAt || node2.latestAcceptId === head.id) continue;
18105
- const reqs = allRequirements.filter((r) => r.nodeId === node2.id);
18132
+ const reqs = requirementsByNode.get(node2.id) ?? [];
18106
18133
  if (reqs.length === 0) continue;
18107
18134
  let stoppedAt = null;
18108
18135
  let deadGroups = 0;
18109
18136
  let liveGroups = 0;
18110
18137
  for (const g2 of new Set(reqs.map((r) => r.reviewGroup))) {
18111
18138
  const req = reqs.find((r2) => r2.reviewGroup === g2);
18112
- const r = req?.latestReviewId ? allReviews.find((x2) => x2.id === req.latestReviewId) : void 0;
18139
+ const r = req?.latestReviewId ? reviewById.get(req.latestReviewId) : void 0;
18113
18140
  if (!r || r.targetWorkId !== head.id) {
18114
18141
  liveGroups += 1;
18115
18142
  continue;
@@ -19276,18 +19303,26 @@ var init_kernel_bridge = __esm({
19276
19303
  typeName: a.type
19277
19304
  };
19278
19305
  }
19279
- const typeDef = this.schema.get(a.type);
19280
- const closureGate = humanGateActorsOf(this.model, artifactId);
19281
19306
  const slot = closureSlotOf(this.model, a.workspace);
19282
19307
  const isCurrentClosureSlot = (slot.kind === "closure-report" || slot.kind === "sole-artifact") && slot.id === artifactId;
19283
- const alreadyAccepted = a.latestAccepted === true;
19284
- const applicableClosureGate = isCurrentClosureSlot && !alreadyAccepted ? closureGate : [];
19308
+ const notYetSubmitted = a.latestWorkState !== "success";
19309
+ const closureGate = isCurrentClosureSlot && notYetSubmitted ? humanGateActorsOf(this.model, artifactId) : [];
19310
+ if (!isCurrentClosureSlot || !notYetSubmitted || closureGate.length === 0) {
19311
+ return {
19312
+ reviewers: [],
19313
+ quorum: 0,
19314
+ requirements: [],
19315
+ typeName: a.type
19316
+ };
19317
+ }
19318
+ const typeDef = this.schema.get(a.type);
19285
19319
  const { reviewers: predicted } = resolveNodeReviewers({
19286
19320
  typeName: a.type,
19287
19321
  typeDef,
19288
- ...a.reviewRoles !== void 0 ? { reviewRoles: a.reviewRoles } : {},
19322
+ // reviewRoles=[] 显式覆盖同样传入,避免 resolver 回落到 typeDef 默认角色(0101 语义)。
19323
+ reviewRoles: [],
19289
19324
  roleIndex: this.roleIndex,
19290
- ...applicableClosureGate.length > 0 ? { closureGate: applicableClosureGate } : {}
19325
+ closureGate
19291
19326
  });
19292
19327
  if (predicted.length === 0) {
19293
19328
  return {
@@ -195420,6 +195455,11 @@ var init_store2 = __esm({
195420
195455
  const r = [...this.rows.values()].find((x2) => x2.childSessionId === childSessionId);
195421
195456
  return r ? this.clone(r) : null;
195422
195457
  }
195458
+ async listChildSessionIds(candidateIds) {
195459
+ if (candidateIds.length === 0) return [];
195460
+ const want = new Set(candidateIds);
195461
+ return [...this.rows.values()].map((r) => r.childSessionId).filter((id) => want.has(id));
195462
+ }
195423
195463
  async getByInvocationId(invocationId) {
195424
195464
  const r = [...this.rows.values()].find((x2) => x2.invocationId === invocationId);
195425
195465
  return r ? this.clone(r) : null;
@@ -215209,13 +215249,22 @@ function clientIpOf(req) {
215209
215249
  if (typeof raw === "string" && raw.trim().length > 0) return raw.split(",")[0].trim();
215210
215250
  return req.socket?.remoteAddress ?? "unknown";
215211
215251
  }
215212
- var DaemonHub;
215252
+ function pkgDriftHealDecision(input) {
215253
+ if (!input.nodePkg || input.nodePkg === input.expectedPkg) return "skip";
215254
+ if (input.nowMs - input.lastHealAtMs < PKG_HEAL_COOLDOWN_MS) return "skip";
215255
+ if (input.tries > PKG_HEAL_MAX_TRIES) return "skip";
215256
+ if (input.tries === PKG_HEAL_MAX_TRIES) return "give-up";
215257
+ return "send";
215258
+ }
215259
+ var PKG_HEAL_COOLDOWN_MS, PKG_HEAL_MAX_TRIES, DaemonHub;
215213
215260
  var init_daemon_hub = __esm({
215214
215261
  "../server/src/daemon-hub.ts"() {
215215
215262
  "use strict";
215216
215263
  init_wrapper();
215217
215264
  init_src();
215218
215265
  init_handshake_throttle();
215266
+ PKG_HEAL_COOLDOWN_MS = 10 * 60 * 1e3;
215267
+ PKG_HEAL_MAX_TRIES = 3;
215219
215268
  DaemonHub = class _DaemonHub {
215220
215269
  daemons = /* @__PURE__ */ new Map();
215221
215270
  /** 底层 socket,仅供 sweepLiveness 对半开连接做 terminate(不等 close 握手)用。 */
@@ -215260,6 +215309,9 @@ var init_daemon_hub = __esm({
215260
215309
  wss;
215261
215310
  ghosts = /* @__PURE__ */ new Map();
215262
215311
  lastDisconnectedAt = /* @__PURE__ */ new Map();
215312
+ /** 通道漂移自愈:上次给某节点补发换包指令的时刻(epoch ms)与本进程内已补发次数。 */
215313
+ pkgHealAt = /* @__PURE__ */ new Map();
215314
+ pkgHealTries = /* @__PURE__ */ new Map();
215263
215315
  lastReconnectedAt = /* @__PURE__ */ new Map();
215264
215316
  static GHOST_TTL_MS = 5 * 60 * 1e3;
215265
215317
  pingIntervalMs;
@@ -215675,6 +215727,37 @@ var init_daemon_hub = __esm({
215675
215727
  if (sent) console.warn(`[hub] evict \u4F1A\u8BDD dispatch=${dispatchId} reason=${reason}`);
215676
215728
  return sent;
215677
215729
  }
215730
+ /**
215731
+ * 通道漂移自愈:节点上报的 `daemonPkg` 与本控制面这条线的包不一致时,握手完就补一帧 `update`
215732
+ * 把正确的包名下发过去(ADR 0515)。没有它,迁移要人点两次「更新」——第一次只是把节点升到
215733
+ * "读得懂 pkg"的那一版,第二次才换包。
215734
+ *
215735
+ * 三道闸,缺一会变成重启风暴:
215736
+ * ① 只对**上报了** `daemonPkg` 的节点动手。没上报 = 更老的 bundle,它读不懂这个字段,
215737
+ * 发过去只会在它自己那条线里白重装一遍,纯粹是一次无意义的重启。
215738
+ * ② 每个节点 10 分钟内最多补一次(与节点侧换包冷却同宽)。
215739
+ * ③ 本进程内每个节点最多补 3 次:换包失败的节点会带着旧包名重连,没有这条就是每 10 分钟重启一轮。
215740
+ */
215741
+ healDaemonPkgDrift(daemon) {
215742
+ const decision = pkgDriftHealDecision({
215743
+ nodePkg: daemon.meta.daemonPkg,
215744
+ expectedPkg: DAEMON_NPM_PACKAGE,
215745
+ lastHealAtMs: this.pkgHealAt.get(daemon.daemonId) ?? 0,
215746
+ tries: this.pkgHealTries.get(daemon.daemonId) ?? 0,
215747
+ nowMs: Date.now()
215748
+ });
215749
+ if (decision === "skip") return;
215750
+ if (decision === "give-up") {
215751
+ this.pkgHealTries.set(daemon.daemonId, PKG_HEAL_MAX_TRIES + 1);
215752
+ console.error(`[hub] \u8282\u70B9 ${daemon.daemonId} \u8FDE\u7EED ${PKG_HEAL_MAX_TRIES} \u6B21\u6CA1\u80FD\u4ECE ${daemon.meta.daemonPkg} \u6362\u5230 ${DAEMON_NPM_PACKAGE}\uFF0C\u505C\u6B62\u81EA\u52A8\u8865\u53D1\u2014\u2014\u53BB\u770B\u5B83\u7684 node.log`);
215753
+ return;
215754
+ }
215755
+ const tries = (this.pkgHealTries.get(daemon.daemonId) ?? 0) + 1;
215756
+ this.pkgHealAt.set(daemon.daemonId, Date.now());
215757
+ this.pkgHealTries.set(daemon.daemonId, tries);
215758
+ console.error(`[hub] \u8282\u70B9 ${daemon.daemonId} \u8FD8\u5728 ${daemon.meta.daemonPkg} \u8FD9\u6761\u7EBF\u4E0A\uFF0C\u672C\u63A7\u5236\u9762\u7528 ${DAEMON_NPM_PACKAGE}\u2014\u2014\u8865\u53D1\u6362\u5305\u6307\u4EE4\uFF08\u7B2C ${tries} \u6B21\uFF09`);
215759
+ daemon.send({ type: "update", pkg: DAEMON_NPM_PACKAGE });
215760
+ }
215678
215761
  handleConnection(ws, req) {
215679
215762
  const dataplane = req.oasisDataplane;
215680
215763
  if (dataplane) {
@@ -215706,6 +215789,7 @@ var init_daemon_hub = __esm({
215706
215789
  if (this.lastDisconnectedAt.has(daemonId)) this.lastReconnectedAt.set(daemonId, Date.now());
215707
215790
  this.ghosts.delete(daemonId);
215708
215791
  Object.assign(daemon.meta, msg.meta);
215792
+ this.healDaemonPkgDrift(daemon);
215709
215793
  if (!registered) {
215710
215794
  registered = true;
215711
215795
  this.lastSeen.set(daemonId, Date.now());
@@ -220960,6 +221044,16 @@ var init_service6 = __esm({
220960
221044
  await this.store.updateCompany(companyId, { onboardingVariant: variant });
220961
221045
  return { ...company, onboardingVariant: variant };
220962
221046
  }
221047
+ /**
221048
+ * 改团队名称。**只改 name,不动 slug**——slug 是路由键(company:<slug> 与引擎 schema co_<slug>
221049
+ * 的一部分,`create` 里注释「建成不可改」),改它会让所有历史链接失效。
221050
+ * 校验(非空 / trim)在路由层做完了,这里假设 name 已经是非空 trim 过的字符串。
221051
+ */
221052
+ async renameCompany(id, name) {
221053
+ const company = await this.requireCompany(id);
221054
+ await this.store.updateCompany(id, { name });
221055
+ return { ...company, name };
221056
+ }
220963
221057
  /** 设置/清除公司头像引用("blob:<哈希>";null = 回到默认首字母标识)。 */
220964
221058
  async setAvatar(id, ref2) {
220965
221059
  const company = await this.requireCompany(id);
@@ -221849,14 +221943,29 @@ function companiesDomain(opts) {
221849
221943
  router.patch("/api/companies/:id", async (req) => {
221850
221944
  const cid = req.params.id;
221851
221945
  const b2 = req.body ?? {};
221852
- if (b2.onboardingVariant === void 0) {
221853
- throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11\u53EF\u66F4\u65B0\u5B57\u6BB5\uFF08onboardingVariant\uFF09");
221946
+ const hasName = b2.name !== void 0;
221947
+ const hasVariant = b2.onboardingVariant !== void 0;
221948
+ if (!hasName && !hasVariant) {
221949
+ throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11\u53EF\u66F4\u65B0\u5B57\u6BB5\uFF08name / onboardingVariant\uFF09");
221854
221950
  }
221855
- if (b2.onboardingVariant !== "A" && b2.onboardingVariant !== "B") {
221951
+ let trimmedName;
221952
+ if (hasName) {
221953
+ if (typeof b2.name !== "string" || !b2.name.trim()) {
221954
+ throw new ApiError(400, "BAD_REQUEST", "name \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32");
221955
+ }
221956
+ trimmedName = b2.name.trim();
221957
+ }
221958
+ if (hasVariant && b2.onboardingVariant !== "A" && b2.onboardingVariant !== "B") {
221856
221959
  throw new ApiError(400, "BAD_REQUEST", `onboardingVariant \u987B\u4E3A "A" | "B"`);
221857
221960
  }
221858
221961
  await requireOwnerOrAdmin(req, cid);
221859
- const company = await service.setOnboardingVariant(cid, b2.onboardingVariant).catch(mapErr);
221962
+ let company;
221963
+ if (trimmedName !== void 0) {
221964
+ company = await service.renameCompany(cid, trimmedName).catch(mapErr);
221965
+ }
221966
+ if (hasVariant) {
221967
+ company = await service.setOnboardingVariant(cid, b2.onboardingVariant).catch(mapErr);
221968
+ }
221860
221969
  return { status: 200, body: company };
221861
221970
  });
221862
221971
  router.post("/api/companies", async (req) => {
@@ -223954,7 +224063,6 @@ function buildWorkorderActivity(input) {
223954
224063
  const tail = nodeId.split(":").slice(2).join(":");
223955
224064
  return tail || nodeId;
223956
224065
  };
223957
- const deliverableName = (workId, nodeId) => artifactsByWork.get(workId)?.[0]?.name ?? nodeName(nodeId);
223958
224066
  const groupCards = /* @__PURE__ */ new Map();
223959
224067
  const groupIndexOf = /* @__PURE__ */ new Map();
223960
224068
  const cardOfWork = /* @__PURE__ */ new Map();
@@ -224068,7 +224176,8 @@ function buildWorkorderActivity(input) {
224068
224176
  const workId = String(ev.workId ?? "");
224069
224177
  const d = cardOfWork.get(workId);
224070
224178
  if (!d) break;
224071
- if (ev.outcome === "failed" && isDispatchLayerFailure(ev.detail)) {
224179
+ const zeroOutput = ev.outcome === "completed" && workById.get(workId)?.status === "failed";
224180
+ if (zeroOutput || ev.outcome === "failed" && isDispatchLayerFailure(ev.detail)) {
224072
224181
  cardOfWork.delete(workId);
224073
224182
  if (![...cardOfWork.values()].includes(d)) {
224074
224183
  for (const [k2, v2] of groupCards) if (v2 === d) groupCards.delete(k2);
@@ -224109,7 +224218,7 @@ function buildWorkorderActivity(input) {
224109
224218
  d.artifacts = artifactsByWork.get(workId) ?? d.artifacts;
224110
224219
  if (d.nodeId) groupIndexOf.set(d.nodeId, (groupIndexOf.get(d.nodeId) ?? 0) + 1);
224111
224220
  const node2 = d.nodeId ? nodeById.get(d.nodeId) : void 0;
224112
- const name = deliverableName(workId, d.nodeId);
224221
+ const name = nodeName(d.nodeId);
224113
224222
  if (d.nodeId && rootBriefNodeIds.has(d.nodeId)) {
224114
224223
  const isFirstRound = !concludedRootBriefNodeIds.has(d.nodeId);
224115
224224
  concludedRootBriefNodeIds.add(d.nodeId);
@@ -224146,7 +224255,7 @@ function buildWorkorderActivity(input) {
224146
224255
  if (reviewers.length === 0 && isSystemAccept(by)) break;
224147
224256
  const bypassed = reviewers.length > 0 && isSystemAccept(by);
224148
224257
  const existing = acceptCards.get(workId);
224149
- const status = bypassed ? node2?.isFinalOutput ? ACTIVITY_COPY.autoAccepted(nameOf(reviewers[0])) : ACTIVITY_COPY.autoAcceptedNode(deliverableName(workId, nodeId), nameOf(reviewers[0])) : node2?.isFinalOutput ? ACTIVITY_COPY.accepted : ACTIVITY_COPY.acceptedNode(deliverableName(workId, nodeId));
224258
+ const status = bypassed ? node2?.isFinalOutput ? ACTIVITY_COPY.autoAccepted(nameOf(reviewers[0])) : ACTIVITY_COPY.autoAcceptedNode(nodeName(nodeId), nameOf(reviewers[0])) : node2?.isFinalOutput ? ACTIVITY_COPY.accepted : ACTIVITY_COPY.acceptedNode(nodeName(nodeId));
224150
224259
  if (existing) {
224151
224260
  touch(existing, rec);
224152
224261
  existing.executorId = bypassed ? reviewers[0] : by;
@@ -224177,7 +224286,7 @@ function buildWorkorderActivity(input) {
224177
224286
  const targetWorkId = String(ev.targetWorkId ?? "");
224178
224287
  const reviewerId = str4(ev.reviewerActorId) ?? rec.actorId;
224179
224288
  const isAgent2 = ev.reviewerIsAgent === true;
224180
- const name = deliverableName(targetWorkId, nodeId);
224289
+ const name = nodeName(nodeId);
224181
224290
  reviewCards.set(reviewId, {
224182
224291
  id: `review:${reviewId}`,
224183
224292
  seq: rec.seq,
@@ -224213,7 +224322,7 @@ function buildWorkorderActivity(input) {
224213
224322
  d.executorId = str4(ev.reviewerActorId) ?? d.executorId;
224214
224323
  d.actions = [];
224215
224324
  const nodeId = d.nodeId;
224216
- const name = deliverableName(targetWorkId, nodeId);
224325
+ const name = nodeName(nodeId);
224217
224326
  if (ev.verdict === "approve") {
224218
224327
  d.status = ACTIVITY_COPY.reviewApproved(name);
224219
224328
  } else {
@@ -230192,6 +230301,18 @@ function createChatSessionsDomain(opts) {
230192
230301
  drafts.delete(req.params["id"]);
230193
230302
  return { status: 200, body: { text: draft.text } };
230194
230303
  });
230304
+ async function markDelegationChildren(sessions, callerCompanyId) {
230305
+ const wiring = opts.delegations;
230306
+ if (!wiring || !wiring.enabled() || sessions.length === 0) return sessions;
230307
+ const companyId = sessions[0]?.companyId ?? callerCompanyId;
230308
+ const ledger = await wiring.storeFor(companyId).catch(() => null);
230309
+ if (!ledger) return sessions;
230310
+ const childIds = new Set(
230311
+ await ledger.listChildSessionIds(sessions.map((s2) => s2.id)).catch(() => [])
230312
+ );
230313
+ if (childIds.size === 0) return sessions;
230314
+ return sessions.map((s2) => childIds.has(s2.id) ? { ...s2, isDelegationChild: true } : s2);
230315
+ }
230195
230316
  router.get("/api/chat-sessions", async (req) => {
230196
230317
  const store2 = await storeFor(req);
230197
230318
  if (req.query?.get("analysis") === "all") {
@@ -230206,7 +230327,8 @@ function createChatSessionsDomain(opts) {
230206
230327
  }
230207
230328
  const analyzedRunId = req.query?.get("analyzedRunId") ?? void 0;
230208
230329
  const sessions = analyzedRunId ? await store2.listSessionsByAnalyzedRun(analyzedRunId) : await store2.listSessions(req.auth.actor);
230209
- return { status: 200, body: { items: sessions } };
230330
+ const items = await markDelegationChildren(sessions, req.auth.companyId ?? void 0);
230331
+ return { status: 200, body: { items } };
230210
230332
  });
230211
230333
  router.post("/api/chat-sessions", async (req) => {
230212
230334
  const store2 = await storeFor(req);
@@ -244624,6 +244746,14 @@ var init_postgres_delegations = __esm({
244624
244746
  );
244625
244747
  return r.rows[0] ? toRecord2(r.rows[0]) : null;
244626
244748
  }
244749
+ async listChildSessionIds(candidateIds) {
244750
+ if (candidateIds.length === 0) return [];
244751
+ const r = await this.pool.query(
244752
+ `SELECT child_session_id FROM ${this.s}.chat_session_delegations WHERE child_session_id = ANY($1)`,
244753
+ [[...candidateIds]]
244754
+ );
244755
+ return r.rows.map((row) => row.child_session_id);
244756
+ }
244627
244757
  async getByInvocationId(invocationId) {
244628
244758
  const r = await this.pool.query(
244629
244759
  `SELECT * FROM ${this.s}.chat_session_delegations WHERE invocation_id = $1`,
@@ -265431,10 +265561,16 @@ ${res.warning}`);
265431
265561
  println(role ? `\u6CA1\u6709${all2 ? "" : "\u5728\u5C97\u7684"} ${role} \u5C97\u4F4D\u5458\u5DE5` : "\u6CA1\u6709\u5458\u5DE5");
265432
265562
  break;
265433
265563
  }
265564
+ let anyRoleless = false;
265434
265565
  for (const a of rows) {
265435
- const pos = (a.positions ?? []).join(",") || "(\u65E0\u5C97\u4F4D)";
265566
+ const hasRole = (a.positions ?? []).length > 0;
265567
+ if (!hasRole) anyRoleless = true;
265568
+ const pos = hasRole ? (a.positions ?? []).join(",") : "(\u65E0\u5C97\u4F4D)";
265436
265569
  println(`${a.id} ${a.name ?? ""} ${a.kind} ${pos}${a.status === "active" ? "" : ` [${a.status}]`}`);
265437
265570
  }
265571
+ if (anyRoleless) {
265572
+ println("\u2014\u2014 \u6807\u300C(\u65E0\u5C97\u4F4D)\u300D\u7684\uFF1A\u4E0D\u4F1A\u88AB\u6309\u5C97\u4F4D\u7684\u81EA\u52A8\u5206\u6D41 / \u6D3E\u5355\u9009\u4E2D\uFF0C\u4F46\u53EF\u4EE5\u70B9\u540D\u6307\u6D3E\uFF08`oasis assign <\u8282\u70B9> --owner <id>`\uFF0C\u6216\u59D4\u6D3E\u65F6 `--to <actorId>`\uFF09\u3002");
265573
+ }
265438
265574
  break;
265439
265575
  }
265440
265576
  // 诊断 1be339c7 ③:单人详情——「这个 id 到底是谁」。协调者要从岗位跳到人时用它,chat agent 想核实
@@ -265521,6 +265657,7 @@ ${res.warning}`);
265521
265657
  println(`${r.name} ${r.label} ${flagsText}${uses ? ` ${uses}` : ""}`);
265522
265658
  if (r.description) println(` ${r.description}`);
265523
265659
  }
265660
+ println("\u2014\u2014 \u672C\u547D\u4EE4\u53EA\u5217\u5C97\u4F4D\u4E0E\u5728\u5C97\u4EBA\u6570\uFF0C\u4E0D\u5217\u5177\u4F53\u7684\u4EBA / agent\u3002\u8981\u770B\u6709\u8C01\u53EF\u6D3E\uFF08\u542B\u672A\u8BBE\u5C97\u4F4D\u7684\uFF09\u7528 `oasis actors`\u3002");
265524
265661
  break;
265525
265662
  }
265526
265663
  case "assign": {
@@ -266418,7 +266555,7 @@ function shimScript() {
266418
266555
  }
266419
266556
 
266420
266557
  // src/index.ts
266421
- var PKG_VERSION = true ? "2.1.0" : "dev";
266558
+ var PKG_VERSION = true ? "2.1.1" : "dev";
266422
266559
  var LOCAL_BIN = localBin();
266423
266560
  var NPM_PREFIX = npmPrefix();
266424
266561
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "b2d3dc4e4d8383e1df616a6e1bd2c07c274ad10f"
29
+ "sourceHead": "75bc8ca411b3af4f39aba67b159c6570f2b35a7a"
30
30
  }
31
31
  }