oasis_test_v2 2.2.68 → 2.2.69

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 +328 -30
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -444,13 +444,14 @@ function isUnread(eventAt, marker) {
444
444
  function isChatSessionUnread(session, marker) {
445
445
  return session.lastTurnQuality !== void 0 && isUnread(session.touchedAt, marker);
446
446
  }
447
- var chatScope, letterScope, INBOX_BASELINE_SCOPE;
447
+ var chatScope, letterScope, INBOX_BASELINE_SCOPE, INBOX_READ_ALL_SCOPE;
448
448
  var init_inbox_read = __esm({
449
449
  "../contract/src/inbox-read.ts"() {
450
450
  "use strict";
451
451
  chatScope = (sessionId) => `chat:${sessionId}`;
452
452
  letterScope = (annotationId) => `letter:${annotationId}`;
453
453
  INBOX_BASELINE_SCOPE = "inbox-baseline";
454
+ INBOX_READ_ALL_SCOPE = "inbox-read-all";
454
455
  }
455
456
  });
456
457
 
@@ -125858,7 +125859,7 @@ function isReplyWork(w2) {
125858
125859
  function replyIssueIdOf(w2) {
125859
125860
  return w2.lane === "reply" ? w2.replyToIssueId ?? null : null;
125860
125861
  }
125861
- var BUSINESS_HANDOFF_POLICY_FIELD, BUSINESS_HANDOFF_POLICY_EXEMPT, DEFAULT_CONFIG, deriveWorkId, deriveReviewId, deriveReplyId;
125862
+ var BUSINESS_HANDOFF_POLICY_FIELD, BUSINESS_HANDOFF_POLICY_EXEMPT, DEFAULT_CONFIG, deriveWorkId, deriveReviewId, ENGINE_SYSTEM_ACTOR, REVIEW_EXHAUSTED_ESC_PREFIX, deriveReplyId;
125862
125863
  var init_model = __esm({
125863
125864
  "../engine/src/model.ts"() {
125864
125865
  "use strict";
@@ -125871,6 +125872,8 @@ var init_model = __esm({
125871
125872
  };
125872
125873
  deriveWorkId = (nodeId, version2) => `wk:${nodeId}:${version2}`;
125873
125874
  deriveReviewId = (workId, reviewer, group) => `rv:${workId}:${reviewer}:${group}`;
125875
+ ENGINE_SYSTEM_ACTOR = "actor:system:engine";
125876
+ REVIEW_EXHAUSTED_ESC_PREFIX = "esc:review-exhausted:";
125874
125877
  deriveReplyId = (issueId, salt) => `ir:${issueId}:${salt}`;
125875
125878
  }
125876
125879
  });
@@ -126414,6 +126417,7 @@ var init_kill = __esm({
126414
126417
  function scan(state, ctx) {
126415
126418
  if (state.workorder.dispatchPaused) return { events: [], terminated: false };
126416
126419
  const events = [];
126420
+ closeSettledReviewEscalations(state, events);
126417
126421
  for (const node2 of state.liveNodes()) {
126418
126422
  const lw = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
126419
126423
  const continuationPolicy = node2.assigneeActorId ? ctx.continuationPolicies?.get(node2.assigneeActorId) : null;
@@ -126942,6 +126946,55 @@ function reviewMaxRetriesOf(policy) {
126942
126946
  const act = policy.resolve({ executor: "agent", scenario: "review" }).timeoutAction;
126943
126947
  return act.kind === "kill-rebuild" ? act.maxRetries : AGENT_REVIEW_MAX_RETRIES;
126944
126948
  }
126949
+ function pushReviewExhaustedEscalation(state, nodeId, workId, reviewId, reviewerActorId, rounds, events) {
126950
+ const node2 = state.node(nodeId);
126951
+ if (!node2) return;
126952
+ if (node2.latestAcceptId === workId) return;
126953
+ const escId = `${REVIEW_EXHAUSTED_ESC_PREFIX}${reviewId}`;
126954
+ if (state.allIssues().some((i) => i.id === escId)) return;
126955
+ events.push({
126956
+ kind: "issue.create",
126957
+ workorderId: state.workorder.id,
126958
+ issueId: escId,
126959
+ issueKind: "escalation",
126960
+ body: `\u300A${node2.title}\u300B\u7684\u8BC4\u5BA1\u8FDE\u7EED ${rounds} \u6B21\u5931\u8D25\uFF0C\u8FD9\u4E00\u8F6E\u8BC4\u5BA1\u6536\u4E0D\u9F50\u2014\u2014\u9700\u8981\u6709\u4EBA\u91CD\u5F00\u3002
126961
+
126962
+ \u672B\u6B21\u8BC4\u5BA1\u4F1A\u8BDD\u7ED3\u675F\u65F6\u6CA1\u6709\u6295\u7968\uFF1B\u62A5\u9519\u539F\u8BDD\u89C1\u8FD9\u4E2A\u8282\u70B9\u7684\u6267\u884C\u8F68\u8FF9\u3002`,
126963
+ aboutNodeId: nodeId,
126964
+ aboutWorkId: workId,
126965
+ raisedByNodeId: null,
126966
+ authorActorId: reviewerActorId,
126967
+ handActorId: null,
126968
+ recipients: [state.workorder.ownerActorId],
126969
+ attachments: [],
126970
+ intent: "advisory"
126971
+ });
126972
+ }
126973
+ function closeSettledReviewEscalations(state, events) {
126974
+ for (const issue2 of state.allIssues()) {
126975
+ if (issue2.resolvedAt !== null || issue2.kind !== "escalation") continue;
126976
+ const id = issue2.id;
126977
+ if (!id.startsWith(REVIEW_EXHAUSTED_ESC_PREFIX)) continue;
126978
+ const node2 = issue2.aboutNodeId ? state.node(issue2.aboutNodeId) : void 0;
126979
+ if (!node2 || node2.cancelledAt) continue;
126980
+ const lw = node2.latestWorkId ? state.work(node2.latestWorkId) : null;
126981
+ if (lw && workState(lw, hasOutput(lw, state.artifactsOf(lw.id).length)) === "dead") continue;
126982
+ const review = state.review(id.slice(REVIEW_EXHAUSTED_ESC_PREFIX.length));
126983
+ if (!review) continue;
126984
+ const head = state.requirementsOf(node2.id).find((r) => r.reviewGroup === review.reviewGroup)?.latestReviewId ?? null;
126985
+ const note = head !== review.id ? "\u8BC4\u5BA1\u5DF2\u91CD\u65B0\u5B89\u6392" : node2.latestAcceptId === review.targetWorkId ? "\u8FD9\u4E00\u7248\u5DF2\u9A8C\u6536" : null;
126986
+ if (note === null) continue;
126987
+ events.push({
126988
+ kind: "issue.resolve",
126989
+ workorderId: state.workorder.id,
126990
+ issueId: issue2.id,
126991
+ state: "resolved",
126992
+ by: SYSTEM_ENGINE_ACTOR,
126993
+ note,
126994
+ viaWorkId: null
126995
+ });
126996
+ }
126997
+ }
126945
126998
  function processReviews(state, nodeId, workId, ctx, events) {
126946
126999
  const policy = ctx.policy ?? CODE_DEFAULT_POLICY;
126947
127000
  const reqs = state.requirementsOf(nodeId);
@@ -127003,15 +127056,19 @@ function processReviews(state, nodeId, workId, ctx, events) {
127003
127056
  }
127004
127057
  } else if (info.status === "failed") {
127005
127058
  const req = reqs.find((r) => r.reviewGroup === g2);
127006
- const overLimit = reviewRetriesExhausted(g2, reviews, { policy }) !== null;
127059
+ const exhausted = reviewRetriesExhausted(g2, reviews, { policy });
127060
+ const overLimit = exhausted !== null;
127007
127061
  if (overLimit && classify(req.reviewerActorId) === "agent") {
127008
127062
  const lrId = req.latestReviewId;
127009
- if (lrId) events.push({
127010
- kind: "review.kill",
127011
- workorderId: state.workorder.id,
127012
- reviewId: lrId,
127013
- reason: "exhausted"
127014
- });
127063
+ if (lrId) {
127064
+ events.push({
127065
+ kind: "review.kill",
127066
+ workorderId: state.workorder.id,
127067
+ reviewId: lrId,
127068
+ reason: "exhausted"
127069
+ });
127070
+ pushReviewExhaustedEscalation(state, nodeId, workId, lrId, req.reviewerActorId, exhausted.rounds, events);
127071
+ }
127015
127072
  } else if (!overLimit) {
127016
127073
  events.push({
127017
127074
  kind: "review.create",
@@ -127202,6 +127259,7 @@ function assessConvergence(state, ctx) {
127202
127259
  const anyUnfinished = state.liveNodes().some((n) => !n.latestAcceptId);
127203
127260
  return { stuck: !anyRunning && !anyBlockingIssue && allIdle && anyUnfinished, codes };
127204
127261
  }
127262
+ var SYSTEM_ENGINE_ACTOR;
127205
127263
  var init_activate = __esm({
127206
127264
  "../engine/src/activate.ts"() {
127207
127265
  "use strict";
@@ -127216,6 +127274,7 @@ var init_activate = __esm({
127216
127274
  init_model();
127217
127275
  init_views2();
127218
127276
  init_kill();
127277
+ SYSTEM_ENGINE_ACTOR = ENGINE_SYSTEM_ACTOR;
127219
127278
  }
127220
127279
  });
127221
127280
 
@@ -135090,6 +135149,7 @@ var init_issue = __esm({
135090
135149
  "../engine/src/handlers/issue.ts"() {
135091
135150
  "use strict";
135092
135151
  init_plan();
135152
+ init_model();
135093
135153
  init_letters();
135094
135154
  issueCreate = {
135095
135155
  name: "issue/create",
@@ -135181,7 +135241,8 @@ var init_issue = __esm({
135181
135241
  }
135182
135242
  if (issue2.resolvedAt !== null) return;
135183
135243
  const humanResolution = e.by.startsWith("actor:human:") && e.by === ctx.actorId;
135184
- if (e.semanticsVersion === 2 && issue2.kind === "escalation" && !humanResolution) {
135244
+ const engineSelfClosure = e.by === ENGINE_SYSTEM_ACTOR && ctx.actorId === ENGINE_SYSTEM_ACTOR && issue2.id.startsWith(REVIEW_EXHAUSTED_ESC_PREFIX);
135245
+ if (e.semanticsVersion === 2 && issue2.kind === "escalation" && !humanResolution && !engineSelfClosure) {
135185
135246
  throw new Error("\u5173\u95ED\u5347\u7EA7\u9700\u8981\u4EBA\u660E\u786E\u786E\u8BA4\uFF1Bagent \u8BF7\u63D0\u4EA4\u5173\u95ED\u6388\u6743\u7533\u8BF7");
135186
135247
  }
135187
135248
  state.updateIssue(e.issueId, {
@@ -213900,7 +213961,10 @@ ${composed}`;
213900
213961
  auth: {
213901
213962
  actor,
213902
213963
  ...companyId !== void 0 ? { companyId } : {},
213903
- ...dispatch && Object.keys(dispatch).length > 0 ? { dispatch } : {}
213964
+ ...dispatch && Object.keys(dispatch).length > 0 ? { dispatch } : {},
213965
+ // 签名 scopes 原样透出(ADR 0609 D1):部署级 operator-token 靠它被认出来。
213966
+ // 只来自签名 token;cookie / 简单 token 没有这一项。
213967
+ ...domainClaims?.scopes ? { scopes: domainClaims.scopes } : {}
213904
213968
  },
213905
213969
  raw: req
213906
213970
  });
@@ -214351,6 +214415,18 @@ var init_tokens = __esm({
214351
214415
  }
214352
214416
  });
214353
214417
 
214418
+ // ../server/src/auth/operator.ts
214419
+ function isOperatorRequest(auth) {
214420
+ return auth.scopes?.includes(OPERATOR_SCOPE) === true;
214421
+ }
214422
+ var OPERATOR_SCOPE;
214423
+ var init_operator = __esm({
214424
+ "../server/src/auth/operator.ts"() {
214425
+ "use strict";
214426
+ OPERATOR_SCOPE = "oasis:operator";
214427
+ }
214428
+ });
214429
+
214354
214430
  // ../server/src/session-dataplane-token.ts
214355
214431
  function createDataplaneTokenIssuer(opts = {}) {
214356
214432
  const secret = readOrCreateSecret2(opts.secretFile);
@@ -233203,6 +233279,7 @@ var init_review_requirement_activity = __esm({
233203
233279
  // ../server/src/domains/collab/activity.ts
233204
233280
  function escalationKindOf(issueId, authorActorId) {
233205
233281
  if (issueId.startsWith(RETRY_EXHAUSTED_ESC_PREFIX)) return "retry_exhausted";
233282
+ if (issueId.startsWith(REVIEW_EXHAUSTED_ESC_PREFIX2)) return "review_exhausted";
233206
233283
  return isSystemActorId(authorActorId) ? "output_stall" : "manual";
233207
233284
  }
233208
233285
  function parseStopLossFacts(kind, body2) {
@@ -234111,8 +234188,12 @@ function buildWorkorderActivity(input) {
234111
234188
  const gapId = issueById.get(id)?.gapId;
234112
234189
  return typeof gapId === "string" && !!issueById.get(gapId);
234113
234190
  };
234114
- const head = kind === "retry_exhausted" ? ACTIVITY_COPY.escalationHeadRetryExhausted(nodeName(latest.nodeId)) : kind === "output_stall" ? ACTIVITY_COPY.escalationHeadOutputStall(nodeName(latest.nodeId)) : gapBehind(latest.id) ? ACTIVITY_COPY.escalationHeadGapEscalated(nameOf(latest.authorId), nodeName(latest.nodeId)) : ACTIVITY_COPY.escalationHeadManual(nameOf(stuckActor));
234115
- const howTo = kind === "retry_exhausted" ? ACTIVITY_COPY.escalationHowToRetryExhausted : kind === "output_stall" ? ACTIVITY_COPY.escalationHowToOutputStall : void 0;
234191
+ const lastReviewFailure = kind === "review_exhausted" ? (input.runFailures ?? []).filter((f2) => f2.nodeId === latest.nodeId && f2.actorId === latest.authorId && f2.startedAt <= latest.at && f2.message.trim()).sort((a, b2) => a.startedAt.localeCompare(b2.startedAt)).at(-1)?.message : void 0;
234192
+ const head = kind === "retry_exhausted" ? ACTIVITY_COPY.escalationHeadRetryExhausted(nodeName(latest.nodeId)) : kind === "review_exhausted" ? ACTIVITY_COPY.escalationHeadReviewExhausted(
234193
+ nodeName(latest.nodeId),
234194
+ lastReviewFailure ? firstSentence(lastReviewFailure) : void 0
234195
+ ) : kind === "output_stall" ? ACTIVITY_COPY.escalationHeadOutputStall(nodeName(latest.nodeId)) : gapBehind(latest.id) ? ACTIVITY_COPY.escalationHeadGapEscalated(nameOf(latest.authorId), nodeName(latest.nodeId)) : ACTIVITY_COPY.escalationHeadManual(nameOf(stuckActor));
234196
+ const howTo = kind === "retry_exhausted" ? ACTIVITY_COPY.escalationHowToRetryExhausted : kind === "review_exhausted" ? ACTIVITY_COPY.escalationHowToReviewExhausted : kind === "output_stall" ? ACTIVITY_COPY.escalationHowToOutputStall : void 0;
234116
234197
  const closing = issueById.get(latest.id);
234117
234198
  const closeEvent = plainResolveEvents.get(latest.id);
234118
234199
  const closedBy = text5(closing?.resolvedBy) ?? closeEvent?.by ?? target;
@@ -234434,7 +234515,7 @@ function buildWorkorderActivity(input) {
234434
234515
  cards.sort((a, b2) => a.at.localeCompare(b2.at) || a.seq - b2.seq || a.id.localeCompare(b2.id));
234435
234516
  return { workorderId, cards, truncated };
234436
234517
  }
234437
- var ACTIVITY_EVENT_KINDS, LEGACY_PAUSE_REASON, ACTIVITY_EVENT_LIMIT, UPSTREAM_ACK_NOTE_CAP, ACTIVITY_COPY, REWORK_ANNOTATION_PREFIX, RECOVERY_ANNOTATION_PREFIX, ENGINE_RETRY_EXHAUSTED, BUTTON, PHASE_LABEL, COORDINATOR_CARD_PREFIX, RETRY_EXHAUSTED_ESC_PREFIX, isStopLoss;
234518
+ var ACTIVITY_EVENT_KINDS, LEGACY_PAUSE_REASON, ACTIVITY_EVENT_LIMIT, UPSTREAM_ACK_NOTE_CAP, ACTIVITY_COPY, REWORK_ANNOTATION_PREFIX, RECOVERY_ANNOTATION_PREFIX, ENGINE_RETRY_EXHAUSTED, BUTTON, PHASE_LABEL, COORDINATOR_CARD_PREFIX, RETRY_EXHAUSTED_ESC_PREFIX, REVIEW_EXHAUSTED_ESC_PREFIX2, isStopLoss;
234438
234519
  var init_activity2 = __esm({
234439
234520
  "../server/src/domains/collab/activity.ts"() {
234440
234521
  "use strict";
@@ -234578,6 +234659,8 @@ var init_activity2 = __esm({
234578
234659
  同一件事,只换后半句(「等 @X 处理」→「X 已处理:…」)。 */
234579
234660
  escalationHeadRetryExhausted: (node2) => `\u300A${node2}\u300B\u91CD\u8BD5\u5DF2\u7528\u5C3D\uFF0C`,
234580
234661
  escalationHeadOutputStall: (node2) => `\u7CFB\u7EDF\u6B62\u635F\uFF1A\u300A${node2}\u300B\u505C\u4F4F\u4E86\uFF0C`,
234662
+ /** 评审连败熔断(Bug 0212):卡住的是**这一轮评审**,不是那一版活——话不能写成「执行异常」。 */
234663
+ escalationHeadReviewExhausted: (node2, lastReason) => `\u300A${node2}\u300B\u7684\u8BC4\u5BA1\u8FDE\u7740\u6CA1\u8DD1\u6210\uFF0C\u8FD9\u4E00\u8F6E\u6536\u4E0D\u9F50${lastReason ? `\uFF08\u672B\u6B21\uFF1A${lastReason}\uFF09` : ""}\uFF0C`,
234581
234664
  /** 人工上报里**关联着缺口**的那一档:协调者接了缺口、补不了,于是升级给人(四种停法的第二种)。 */
234582
234665
  escalationHeadGapEscalated: (coordinator, node2) => `${coordinator} \u8865\u4E0D\u4E86\u300A${node2}\u300B\u7684\u7F3A\u53E3\uFF0C`,
234583
234666
  /** 其余人工上报:主语是卡住的那个执行人(异常是它的,不是上报作者的)。 */
@@ -234593,6 +234676,8 @@ var init_activity2 = __esm({
234593
234676
  /** 「你要做什么」那半句——两种系统止损各一份(F6 的立论就在这里:执行者一样,只有话不一样)。 */
234594
234677
  escalationHowToRetryExhausted: "\u5904\u7406\u5E76\u5173\u6389\u8FD9\u6761\u4E0A\u62A5\u540E\uFF0C\u8FD9\u4E00\u6B65\u4F1A\u81EA\u52A8\u91CD\u8BD5\uFF1B\u591A\u534A\u662F\u989D\u5EA6 / \u767B\u5F55\u8FC7\u671F / \u9650\u6D41\u8FD9\u7C7B\u73AF\u5883\u95EE\u9898\uFF0C\u5148\u770B\u8F68\u8FF9\u518D\u51B3\u5B9A\u91CD\u8BD5\u3002",
234595
234678
  escalationHowToOutputStall: "\u5904\u7406\u5E76\u5173\u6389\u8FD9\u6761\u4E0A\u62A5\u540E\uFF0C\u8FD9\u4E00\u6B65\u4F1A\u81EA\u52A8\u91CD\u8BD5\uFF1B\u4E0D\u67E5\u6E05\u539F\u56E0\u5C31\u91CD\u8BD5\u4F1A\u7EE7\u7EED\u7A7A\u70E7\uFF0C\u5148\u8FDB\u8BA8\u8BBA\u623F\u3002",
234679
+ /** 评审熔断那一份:关掉之后重开的是**评审**,不是这一步的活(活已经交了,等的是审)。 */
234680
+ escalationHowToReviewExhausted: "\u5904\u7406\u5E76\u5173\u6389\u8FD9\u6761\u4E0A\u62A5\u540E\uFF0C\u8FD9\u4E00\u8F6E\u8BC4\u5BA1\u4F1A\u81EA\u52A8\u91CD\u5F00\uFF1B\u591A\u534A\u662F\u8BC4\u5BA1\u5458\u7684\u8FD0\u884C\u73AF\u5883\u51FA\u4E86\u95EE\u9898\uFF08\u989D\u5EA6 / \u767B\u5F55\u8FC7\u671F / \u9650\u6D41\uFF09\uFF0C\u5148\u770B\u8F68\u8FF9\u518D\u51B3\u5B9A\u3002",
234596
234681
  /** 关掉了:接着开头那半句说「谁处理的、怎么处理的」。没写处置说明就只说「已处理」。
234597
234682
  * 处置说明常常是人自己写的一整段、末尾自带句号——**别再补一个**,否则卡面收在「。。」上。 */
234598
234683
  escalationHandled: (head, by, note) => `${head}${by} \u5DF2\u5904\u7406${note ? `\uFF1A${note}` : ""}${note && /[。!?.!?]$/.test(note) ? "" : "\u3002"}`,
@@ -234782,7 +234867,8 @@ var init_activity2 = __esm({
234782
234867
  };
234783
234868
  COORDINATOR_CARD_PREFIX = "coordinator:";
234784
234869
  RETRY_EXHAUSTED_ESC_PREFIX = "esc:retry-exhausted:";
234785
- isStopLoss = (kind) => kind !== "manual";
234870
+ REVIEW_EXHAUSTED_ESC_PREFIX2 = "esc:review-exhausted:";
234871
+ isStopLoss = (kind) => kind === "retry_exhausted" || kind === "output_stall";
234786
234872
  }
234787
234873
  });
234788
234874
 
@@ -235482,6 +235568,15 @@ function backfillInboxUnread(items, markers) {
235482
235568
  item.unread = !Number.isFinite(effective) ? true : sinceMs > effective;
235483
235569
  }
235484
235570
  }
235571
+ function applyReadAllWatermark(items, markers) {
235572
+ const at = markers.find((m2) => m2.scope === INBOX_READ_ALL_SCOPE)?.readAt;
235573
+ const atMs = at ? Date.parse(at) : Number.NaN;
235574
+ if (!Number.isFinite(atMs)) return;
235575
+ for (const item of items) {
235576
+ const sinceMs = Date.parse(item.since);
235577
+ if (Number.isFinite(sinceMs) && sinceMs <= atMs) item.unread = false;
235578
+ }
235579
+ }
235485
235580
  var C_ACTION_LABEL;
235486
235581
  var init_inbox = __esm({
235487
235582
  "../server/src/domains/collab/inbox.ts"() {
@@ -236060,6 +236155,16 @@ var init_notification_store = __esm({
236060
236155
  }
236061
236156
  if (changed) this.save();
236062
236157
  }
236158
+ async markAllRead(recipient, at) {
236159
+ let n = 0;
236160
+ for (const [id, r] of this.rows) {
236161
+ if (r.recipient !== recipient || r.readAt) continue;
236162
+ this.rows.set(id, { ...r, readAt: at });
236163
+ n++;
236164
+ }
236165
+ if (n > 0) this.save();
236166
+ return n;
236167
+ }
236063
236168
  async countUnread(recipient) {
236064
236169
  let pending = 0;
236065
236170
  let all2 = 0;
@@ -236250,6 +236355,13 @@ var init_notification_store = __esm({
236250
236355
  [recipient, [...ids2], at]
236251
236356
  );
236252
236357
  }
236358
+ async markAllRead(recipient, at) {
236359
+ const r = await this.sql.query(
236360
+ `UPDATE ${this.t} SET read_at = $2::timestamptz WHERE recipient = $1 AND read_at IS NULL`,
236361
+ [recipient, at]
236362
+ );
236363
+ return r.rowCount ?? 0;
236364
+ }
236253
236365
  async countUnread(recipient) {
236254
236366
  const r = await this.sql.query(
236255
236367
  `SELECT count(*) FILTER (WHERE status = 'pending')::int AS pending, count(*)::int AS "all"
@@ -236780,7 +236892,7 @@ var init_workorder_metrics = __esm({
236780
236892
  });
236781
236893
 
236782
236894
  // ../server/src/domains/usage/statistics-model.ts
236783
- var UsageQueryError;
236895
+ var UsageQueryError, ZERO_USAGE_AMOUNT;
236784
236896
  var init_statistics_model = __esm({
236785
236897
  "../server/src/domains/usage/statistics-model.ts"() {
236786
236898
  "use strict";
@@ -236791,6 +236903,13 @@ var init_statistics_model = __esm({
236791
236903
  this.name = "UsageQueryError";
236792
236904
  }
236793
236905
  };
236906
+ ZERO_USAGE_AMOUNT = {
236907
+ tokens: { input: 0, cache: 0, cacheRead: 0, cacheCreation: 0, output: 0, total: 0 },
236908
+ costUsdMicros: 0,
236909
+ costParts: { input: 0, cacheRead: 0, cacheCreation: 0, output: 0, unsplit: 0 },
236910
+ costKnown: "full",
236911
+ costUnknownTokens: 0
236912
+ };
236794
236913
  }
236795
236914
  });
236796
236915
 
@@ -237166,6 +237285,11 @@ function bucketIndex(starts, ms) {
237166
237285
  }
237167
237286
  return ans;
237168
237287
  }
237288
+ function pushView(map, key2, v2) {
237289
+ const list2 = map.get(key2);
237290
+ if (list2) list2.push(v2);
237291
+ else map.set(key2, [v2]);
237292
+ }
237169
237293
  function getCell(map, key2) {
237170
237294
  let c = map.get(key2);
237171
237295
  if (!c) {
@@ -237403,12 +237527,15 @@ function attributed(facts, relations) {
237403
237527
  const taskCells = /* @__PURE__ */ new Map();
237404
237528
  const taskActorCells = /* @__PURE__ */ new Map();
237405
237529
  const convSessionCells = /* @__PURE__ */ new Map();
237530
+ const convViews = /* @__PURE__ */ new Map();
237531
+ const taskViews = /* @__PURE__ */ new Map();
237406
237532
  const unlinked = new Cell();
237407
237533
  for (const v2 of facts.views) {
237408
237534
  if (!v2.relevant) continue;
237409
237535
  const attr = runAttribution(relations, v2.run, memo);
237410
237536
  if (attr.kind === "task") {
237411
237537
  getCell(taskCells, attr.workorderId).addView(v2);
237538
+ pushView(taskViews, attr.workorderId, v2);
237412
237539
  let byActor2 = taskActorCells.get(attr.workorderId);
237413
237540
  if (!byActor2) {
237414
237541
  byActor2 = /* @__PURE__ */ new Map();
@@ -237422,6 +237549,7 @@ function attributed(facts, relations) {
237422
237549
  convSessionCells.set(attr.rootSessionId, bySession);
237423
237550
  }
237424
237551
  getCell(bySession, attr.sessionId).addView(v2);
237552
+ pushView(convViews, attr.rootSessionId, v2);
237425
237553
  } else unlinked.addView(v2);
237426
237554
  }
237427
237555
  const taskAmount = /* @__PURE__ */ new Map();
@@ -237453,8 +237581,13 @@ function attributed(facts, relations) {
237453
237581
  }
237454
237582
  const conversations = [];
237455
237583
  const convGroupAcc = /* @__PURE__ */ new Map();
237584
+ const treeViews = /* @__PURE__ */ new Map();
237456
237585
  for (const rootId of /* @__PURE__ */ new Set([...convSessionCells.keys(), ...tasksByRoot.keys()])) {
237457
237586
  const root = relations.sessions.get(rootId);
237587
+ treeViews.set(rootId, [
237588
+ ...convViews.get(rootId) ?? [],
237589
+ ...(tasksByRoot.get(rootId) ?? []).flatMap((workorderId) => taskViews.get(workorderId) ?? [])
237590
+ ]);
237458
237591
  const group = new Cell();
237459
237592
  const rows = [];
237460
237593
  const sessionRows = [];
@@ -237576,9 +237709,83 @@ function attributed(facts, relations) {
237576
237709
  { amount: a.amount, name: a.name, id: a.projectId ?? "" },
237577
237710
  { amount: b2.amount, name: b2.name, id: b2.projectId ?? "" }
237578
237711
  ));
237579
- return { conversations, tasks, projects };
237712
+ return { conversations, tasks, projects, treeViews };
237713
+ }
237714
+ function deriveTreeDuration(views, now) {
237715
+ const nowMs = now.getTime();
237716
+ const spans = [];
237717
+ let countedRuns = 0;
237718
+ let unstartedRuns = 0;
237719
+ let inProgress = false;
237720
+ let firstMs = Infinity;
237721
+ let lastMs = -Infinity;
237722
+ let runtimeMs = 0;
237723
+ for (const v2 of views) {
237724
+ const startMs = v2.run.runtimeStartedAt ? Date.parse(v2.run.runtimeStartedAt) : NaN;
237725
+ if (!Number.isFinite(startMs)) {
237726
+ unstartedRuns += 1;
237727
+ continue;
237728
+ }
237729
+ const endedMs = v2.run.endedAt ? Date.parse(v2.run.endedAt) : NaN;
237730
+ const running = !Number.isFinite(endedMs) && !isRunEnded(v2.run);
237731
+ if (running) inProgress = true;
237732
+ const rawEnd = Number.isFinite(endedMs) ? endedMs : running ? nowMs : startMs;
237733
+ const end = Math.max(rawEnd, startMs);
237734
+ countedRuns += 1;
237735
+ runtimeMs += end - startMs;
237736
+ spans.push({ start: startMs, end });
237737
+ if (startMs < firstMs) firstMs = startMs;
237738
+ if (end > lastMs) lastMs = end;
237739
+ }
237740
+ if (countedRuns === 0) return { ...ZERO_DURATION, unstartedRuns };
237741
+ spans.sort((a, b2) => a.start - b2.start || a.end - b2.end);
237742
+ let activeMs = 0;
237743
+ let curStart = spans[0].start;
237744
+ let curEnd = spans[0].end;
237745
+ for (const span of spans.slice(1)) {
237746
+ if (span.start > curEnd) {
237747
+ activeMs += curEnd - curStart;
237748
+ curStart = span.start;
237749
+ curEnd = span.end;
237750
+ } else if (span.end > curEnd) curEnd = span.end;
237751
+ }
237752
+ activeMs += curEnd - curStart;
237753
+ return {
237754
+ firstStartedAt: new Date(firstMs).toISOString(),
237755
+ lastEndedAt: new Date(lastMs).toISOString(),
237756
+ inProgress,
237757
+ wallClockMs: lastMs - firstMs,
237758
+ runtimeMs,
237759
+ activeMs,
237760
+ countedRuns,
237761
+ unstartedRuns
237762
+ };
237580
237763
  }
237581
- var UNKNOWN_AGENT_NAME, UNREGISTERED_RUNTIME_NAME, UNSPECIFIED_PROJECT_NAME, UNLINKED_ROW_TITLE, UNTITLED_CONVERSATION, BUCKET_KEYS, AmountAcc, FlagAcc, Cell, collator, ModelCells;
237764
+ function buildChatSessionUsage(sessionId, input) {
237765
+ const facts = computeFacts(input);
237766
+ const lists = attributed(facts, input.relations);
237767
+ const attr = sessionAttribution(input.relations, sessionId, /* @__PURE__ */ new Map());
237768
+ const rootId = attr.kind === "conversation" ? attr.rootSessionId : sessionId;
237769
+ const session = input.relations.sessions.get(rootId);
237770
+ const group = lists.conversations.find((g2) => g2.sessionId === rootId) ?? {
237771
+ sessionId: rootId,
237772
+ title: session?.title || UNTITLED_CONVERSATION,
237773
+ projectId: session?.projectId ?? null,
237774
+ createdAt: session?.createdAt ?? null,
237775
+ amount: ZERO_USAGE_AMOUNT,
237776
+ flags: { inProgress: false, usagePending: false, costUnknownRuns: 0 },
237777
+ rows: []
237778
+ };
237779
+ return {
237780
+ companyId: input.companyId,
237781
+ sessionId,
237782
+ window: input.resolved.window,
237783
+ generatedAt: input.now.toISOString(),
237784
+ group,
237785
+ duration: deriveTreeDuration(lists.treeViews.get(rootId) ?? [], input.now)
237786
+ };
237787
+ }
237788
+ var UNKNOWN_AGENT_NAME, UNREGISTERED_RUNTIME_NAME, UNSPECIFIED_PROJECT_NAME, UNLINKED_ROW_TITLE, UNTITLED_CONVERSATION, BUCKET_KEYS, AmountAcc, FlagAcc, Cell, collator, ModelCells, ZERO_DURATION;
237582
237789
  var init_statistics = __esm({
237583
237790
  "../server/src/domains/usage/statistics.ts"() {
237584
237791
  "use strict";
@@ -237586,6 +237793,7 @@ var init_statistics = __esm({
237586
237793
  init_uncategorized_project();
237587
237794
  init_model_pricing();
237588
237795
  init_accounting();
237796
+ init_statistics_model();
237589
237797
  UNKNOWN_AGENT_NAME = "\u672A\u77E5 Agent";
237590
237798
  UNREGISTERED_RUNTIME_NAME = "\u672A\u767B\u8BB0\u8FD0\u884C\u65F6";
237591
237799
  UNSPECIFIED_PROJECT_NAME = UNCATEGORIZED_PROJECT_NAME;
@@ -237704,6 +237912,16 @@ var init_statistics = __esm({
237704
237912
  return [...this.cells].map(([model, c]) => ({ model, runCount: c.runCount, amount: c.amount.toAmount(), flags: c.flags.toFlags() })).filter((r) => r.runCount > 0 || r.amount.tokens.total > 0 || r.amount.costUsdMicros > 0 || r.amount.costKnown !== "full").sort((a, b2) => b2.amount.tokens.total - a.amount.tokens.total || b2.runCount - a.runCount || (a.model === null ? 1 : b2.model === null ? -1 : collator.compare(a.model, b2.model)));
237705
237913
  }
237706
237914
  };
237915
+ ZERO_DURATION = {
237916
+ firstStartedAt: null,
237917
+ lastEndedAt: null,
237918
+ inProgress: false,
237919
+ wallClockMs: null,
237920
+ runtimeMs: 0,
237921
+ activeMs: 0,
237922
+ countedRuns: 0,
237923
+ unstartedRuns: 0
237924
+ };
237707
237925
  }
237708
237926
  });
237709
237927
 
@@ -237959,6 +238177,22 @@ function createUsageReadService(deps) {
237959
238177
  logRequest({ companyId, op: "breakdown", range: r.query.range, dimension, runs: r.runs.length, companyScoped: r.target.companyScoped, startedMs });
237960
238178
  return body2;
237961
238179
  },
238180
+ async conversationUsage(companyId, sessionId, raw) {
238181
+ const startedMs = Date.now();
238182
+ const loads = requestLoads(companyId);
238183
+ const r = await readLedger(companyId, { ...raw, range: raw.range ?? "all" }, loads);
238184
+ const relations = await loadRelations(companyId, loads);
238185
+ const body2 = buildChatSessionUsage(sessionId, {
238186
+ companyId,
238187
+ resolved: r.resolved,
238188
+ runs: r.runs,
238189
+ coverage: r.coverage,
238190
+ now: r.now,
238191
+ relations
238192
+ });
238193
+ logRequest({ companyId, op: "conversation", range: r.query.range, runs: r.runs.length, companyScoped: r.target.companyScoped, startedMs });
238194
+ return body2;
238195
+ },
237962
238196
  async runtimeLedger(companyId, runtimeInstanceId, raw) {
237963
238197
  const startedMs = Date.now();
237964
238198
  const loads = requestLoads(companyId);
@@ -238936,6 +239170,7 @@ function collabDomain(opts) {
238936
239170
  backfillInboxUnread(waitingItems, markers);
238937
239171
  }
238938
239172
  const items = mergeInbox(kernelItems, [...chatItems, ...waitingItems]);
239173
+ if (myReadMarkers) applyReadAllWatermark(items, markers);
238939
239174
  return { status: 200, body: { items: myReadMarkers ? withEscalationAttention(items, req.auth.companyId ?? opts.defaultCompanyId ?? "", markers, Date.now(), opts.escalationReminders) : items } };
238940
239175
  });
238941
239176
  router.post("/api/inbox/escalations/:id/acknowledge", async (req) => {
@@ -239205,6 +239440,28 @@ function collabDomain(opts) {
239205
239440
  await marks.markRead(me, scope2, (/* @__PURE__ */ new Date()).toISOString());
239206
239441
  return { status: 200, body: { ok: true } };
239207
239442
  });
239443
+ router.post("/api/inbox/read-all", async (req) => {
239444
+ const me = req.auth.actor;
239445
+ if (!isHumanActor(me)) {
239446
+ return { status: 403, body: { error: { code: "human_only", message: "\u53EA\u6709\u4EBA\u7C7B\u8EAB\u4EFD\u53EF\u4EE5\u6807\u8BB0\u5DF2\u8BFB" } } };
239447
+ }
239448
+ const marks = opts.resolveReadMarkers ? await opts.resolveReadMarkers(req.auth.companyId).catch(() => void 0) : opts.readMarkers;
239449
+ let ledger;
239450
+ if (opts.resolveNotificationLedger) {
239451
+ try {
239452
+ ledger = await opts.resolveNotificationLedger(req.auth.companyId);
239453
+ } catch (err) {
239454
+ console.warn(`[notifications] \u65E0\u53EF\u4FE1\u516C\u53F8\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u6807\u5DF2\u8BFB\uFF08\u4E0D\u56DE\u843D\u9ED8\u8BA4\u516C\u53F8\uFF09: ${String(err)}`);
239455
+ }
239456
+ }
239457
+ if (!marks && !ledger) {
239458
+ return { status: 503, body: { error: { code: "unavailable", message: "\u5DF2\u8BFB\u6C34\u4F4D\u4E0E\u901A\u77E5\u53F0\u8D26\u90FD\u4E0D\u53EF\u7528" } } };
239459
+ }
239460
+ const readAt = (/* @__PURE__ */ new Date()).toISOString();
239461
+ const notifications = ledger ? await ledger.markAllRead(me, readAt) : 0;
239462
+ if (marks) await marks.markRead(me, INBOX_READ_ALL_SCOPE, readAt);
239463
+ return { status: 200, body: { ok: true, readAt, notifications } };
239464
+ });
239208
239465
  router.get("/api/audit", async (req) => {
239209
239466
  const { engineStore, oplog, kernel } = await resolveCtx(req.auth.companyId);
239210
239467
  const q2 = req.query;
@@ -244601,6 +244858,7 @@ function parseFilters(q2) {
244601
244858
  const actorId = q2.get("actorId");
244602
244859
  const artifactId = q2.get("artifactId");
244603
244860
  const projectId2 = q2.get("projectId");
244861
+ const chatSessionId = q2.get("chatSessionId");
244604
244862
  const runtimeKind = q2.get("runtimeKind");
244605
244863
  const status = q2.get("status");
244606
244864
  const rootRunId = q2.get("rootRunId");
@@ -244611,6 +244869,7 @@ function parseFilters(q2) {
244611
244869
  if (actorId) f2.actorId = actorId;
244612
244870
  if (artifactId) f2.artifactId = artifactId;
244613
244871
  if (projectId2) f2.projectId = projectId2;
244872
+ if (chatSessionId) f2.chatSessionId = chatSessionId;
244614
244873
  if (runtimeKind) f2.runtimeKind = runtimeKind;
244615
244874
  if (status) f2.status = status;
244616
244875
  if (rootRunId) f2.rootRunId = rootRunId;
@@ -246581,9 +246840,12 @@ function createChatSessionsDomain(opts) {
246581
246840
  }
246582
246841
  return manager ? manager : null;
246583
246842
  }
246584
- function ensureSessionReadable(session, actor, kernel) {
246843
+ function ownsSessionOrOperator(session, auth) {
246844
+ return session.humanActorId === auth.actor || isOperatorRequest(auth);
246845
+ }
246846
+ function ensureSessionReadable(session, auth, kernel) {
246585
246847
  if (!session) throw new ApiError(404, "NOT_FOUND", "session not found");
246586
- if (session.humanActorId === actor) return session;
246848
+ if (ownsSessionOrOperator(session, auth)) return session;
246587
246849
  if (session.workspace) {
246588
246850
  const coordinator = coordinatorOfWorkspace(kernel, session.workspace);
246589
246851
  if (coordinator && session.aiActorId === coordinator) return session;
@@ -246748,7 +247010,7 @@ function createChatSessionsDomain(opts) {
246748
247010
  router.get("/api/chat-sessions/:id", async (req) => {
246749
247011
  const store2 = await storeFor(req);
246750
247012
  const [raw, kernel] = await Promise.all([store2.getSession(req.params.id), kernelFor(req)]);
246751
- const session = ensureSessionReadable(raw, req.auth.actor, kernel);
247013
+ const session = ensureSessionReadable(raw, req.auth, kernel);
246752
247014
  const [window3, curRuntimeId] = await Promise.all([
246753
247015
  listRecentTurns(store2, session.id, parseTurns(req.query?.get("turns"))),
246754
247016
  currentRuntimeId(req.auth.companyId, session.aiActorId)
@@ -246889,7 +247151,7 @@ function createChatSessionsDomain(opts) {
246889
247151
  if (!items) throw new ApiError(404, "NOT_FOUND", "\u672C\u5B9E\u4F8B\u672A\u63A5 chat_items \u5FEB\u7167\u8BFB\u53E3");
246890
247152
  const store2 = await storeFor(req);
246891
247153
  const [session, kernel] = await Promise.all([store2.getSession(req.params.id), kernelFor(req)]);
246892
- ensureSessionReadable(session, req.auth.actor, kernel);
247154
+ ensureSessionReadable(session, req.auth, kernel);
246893
247155
  const chatSessionId = req.params.id;
246894
247156
  const sinceVersion = Math.max(0, Number(req.query?.get("sinceVersion") ?? "0") || 0);
246895
247157
  const beforeOrdRaw = Number(req.query?.get("beforeOrd") ?? "0") || 0;
@@ -247060,7 +247322,7 @@ function createChatSessionsDomain(opts) {
247060
247322
  router.get("/api/chat-sessions/:id/delegations", async (req) => {
247061
247323
  const store2 = await storeFor(req);
247062
247324
  const session = await store2.getSession(req.params.id);
247063
- if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
247325
+ if (!session || !ownsSessionOrOperator(session, req.auth)) throw new ApiError(404, "NOT_FOUND", "session not found");
247064
247326
  const wiring = opts.delegations;
247065
247327
  if (!wiring || !wiring.enabled()) return { status: 200, body: { items: [] } };
247066
247328
  const companyId = session.companyId ?? req.auth.companyId ?? void 0;
@@ -247126,7 +247388,7 @@ function createChatSessionsDomain(opts) {
247126
247388
  router.get("/api/chat-sessions/:id/work-orders", async (req) => {
247127
247389
  const store2 = await storeFor(req);
247128
247390
  const session = await store2.getSession(req.params.id);
247129
- if (!session || session.humanActorId !== req.auth.actor) throw new ApiError(404, "NOT_FOUND", "session not found");
247391
+ if (!session || !ownsSessionOrOperator(session, req.auth)) throw new ApiError(404, "NOT_FOUND", "session not found");
247130
247392
  const workOrderIds = await store2.listWorkOrdersForSession(req.params.id);
247131
247393
  const wkKernel = await kernelFor(req);
247132
247394
  if (!wkKernel || workOrderIds.length === 0) return { status: 200, body: { items: [] } };
@@ -247149,6 +247411,29 @@ function createChatSessionsDomain(opts) {
247149
247411
  });
247150
247412
  return { status: 200, body: { items } };
247151
247413
  });
247414
+ router.get("/api/chat-sessions/:id/usage", async (req) => {
247415
+ const store2 = await storeFor(req);
247416
+ const session = await store2.getSession(req.params.id);
247417
+ if (!session || !ownsSessionOrOperator(session, req.auth)) throw new ApiError(404, "NOT_FOUND", "session not found");
247418
+ const companyId = req.auth.companyId ?? session.companyId ?? opts.defaultCompanyId;
247419
+ if (!opts.usageRead || !companyId) {
247420
+ return { status: 503, body: { error: { code: "usage_ledger_unavailable", message: "\u4F7F\u7528\u4E0E\u7EDF\u8BA1\u6570\u636E\u6E90\u672A\u63A5\u5165" } } };
247421
+ }
247422
+ try {
247423
+ return {
247424
+ status: 200,
247425
+ body: await opts.usageRead.conversationUsage(companyId, session.id, {
247426
+ range: req.query.get("range") ?? void 0,
247427
+ asOf: req.query.get("asOf") ?? void 0,
247428
+ timeZone: req.query.get("timeZone") ?? void 0
247429
+ })
247430
+ };
247431
+ } catch (err) {
247432
+ const mapped = usageErrorResponse(err);
247433
+ if (mapped) return mapped;
247434
+ throw err;
247435
+ }
247436
+ });
247152
247437
  router.get("/api/chat-broadcasts/pending", async (req) => {
247153
247438
  if (!opts.broadcastOps) throw new ApiError(404, "NOT_FOUND", "\u672C\u5B9E\u4F8B\u672A\u542F\u7528\u5DE5\u5355\u7EC8\u6001\u64AD\u62A5");
247154
247439
  const store2 = await storeFor(req);
@@ -247605,6 +247890,8 @@ var init_chat_sessions2 = __esm({
247605
247890
  init_persistent_files();
247606
247891
  init_files_view();
247607
247892
  init_router();
247893
+ init_operator();
247894
+ init_usage_read();
247608
247895
  init_collab();
247609
247896
  init_gap_thread();
247610
247897
  init_escalation_opening();
@@ -258375,6 +258662,7 @@ var init_postgres_trace = __esm({
258375
258662
  if (filter.actorId) push2((n) => `r.actor_id = $${n}`, filter.actorId);
258376
258663
  if (filter.artifactId) push2((n) => `r.artifact_id = $${n}`, filter.artifactId);
258377
258664
  if (filter.projectId) push2((n) => `r.project_id = $${n}`, filter.projectId);
258665
+ if (filter.chatSessionId) push2((n) => `r.chat_session_id = $${n}`, filter.chatSessionId);
258378
258666
  if (filter.runtimeKind) push2((n) => `r.runtime_kind = $${n}`, filter.runtimeKind);
258379
258667
  if (filter.status) push2((n) => `r.status = $${n}`, filter.status);
258380
258668
  if (filter.rootRunId) push2((n) => `r.root_run_id = $${n}`, filter.rootRunId);
@@ -258493,7 +258781,7 @@ var init_postgres_trace = __esm({
258493
258781
  */
258494
258782
  async listRunFailures(workOrderId) {
258495
258783
  const r = await this.pool.query(
258496
- `SELECT node_id, error_message, COALESCE(started_at, created_at) AS at
258784
+ `SELECT node_id, actor_id, error_message, COALESCE(started_at, created_at) AS at
258497
258785
  FROM ${this.s}.agent_runs
258498
258786
  WHERE work_order_id = $1 AND node_id IS NOT NULL
258499
258787
  AND error_message IS NOT NULL AND error_message <> ''
@@ -258502,6 +258790,7 @@ var init_postgres_trace = __esm({
258502
258790
  );
258503
258791
  return r.rows.map((row) => ({
258504
258792
  nodeId: row.node_id,
258793
+ actorId: row.actor_id ?? null,
258505
258794
  startedAt: new Date(row.at).toISOString(),
258506
258795
  message: row.error_message
258507
258796
  }));
@@ -272632,6 +272921,7 @@ __export(src_exports2, {
272632
272921
  NdjsonOplogStore: () => NdjsonOplogStore,
272633
272922
  NodeHealthTracker: () => NodeHealthTracker,
272634
272923
  NotificationLedgerWorker: () => NotificationLedgerWorker,
272924
+ OPERATOR_SCOPE: () => OPERATOR_SCOPE,
272635
272925
  ORPHANED_TURN_ASSISTANT_NOTICE: () => ORPHANED_TURN_ASSISTANT_NOTICE,
272636
272926
  OUTPUT_COPY_LIMITS: () => OUTPUT_COPY_LIMITS,
272637
272927
  PARENT_CONVERSATION_PATH: () => PARENT_CONVERSATION_PATH,
@@ -272868,6 +273158,7 @@ __export(src_exports2, {
272868
273158
  isKnowledgeQueryPlanV2: () => isKnowledgeQueryPlanV2,
272869
273159
  isLocationFailureCode: () => isLocationFailureCode,
272870
273160
  isNodeHealthy: () => isNodeHealthy,
273161
+ isOperatorRequest: () => isOperatorRequest,
272871
273162
  isRunTerminalStatus: () => isRunTerminalStatus,
272872
273163
  isRuntimeDrifted: () => isRuntimeDrifted,
272873
273164
  isRuntimeRunReader: () => isRuntimeRunReader,
@@ -273097,6 +273388,7 @@ var init_src11 = __esm({
273097
273388
  "use strict";
273098
273389
  init_server3();
273099
273390
  init_tokens();
273391
+ init_operator();
273100
273392
  init_session_dataplane_token();
273101
273393
  init_dev_store();
273102
273394
  init_control_plane_file_store();
@@ -276768,7 +277060,7 @@ async function startServe(opts) {
276768
277060
  const issuer = createTokenIssuer({ secretFile: path28.join(opts.dir, "session-token-secret") });
276769
277061
  const apiActivity = createApiActivityLedger();
276770
277062
  const operatorActor = `actor:human:${os8.userInfo().username}`;
276771
- const operatorToken = issuer.issue(operatorActor);
277063
+ const operatorToken = issuer.issue(operatorActor, { scopes: [OPERATOR_SCOPE] });
276772
277064
  fs34.writeFileSync(path28.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
276773
277065
  let nodeTokens = createNodeTokenStore(path28.join(opts.dir, "node-tokens.json"));
276774
277066
  const enrollTokens = createEnrollTokenStore(30 * 60 * 1e3, path28.join(opts.dir, "enroll-tokens.json"));
@@ -279200,7 +279492,11 @@ async function startServe(opts) {
279200
279492
  resolveChatItems: (companyId) => chatItemsFor(companyId),
279201
279493
  liveChat,
279202
279494
  // ADR-0587:列表行派生 `unread`,与 collab 域收件箱读同一份按公司的水位(无 PG 时闭包自己回落单实例)。
279203
- resolveReadMarkers: readMarkerStoreFor
279495
+ resolveReadMarkers: readMarkerStoreFor,
279496
+ // ADR 0609 D4:`GET /api/chat-sessions/:id/usage` 的取数口——**与 collab 域是同一个实例**,
279497
+ // 按会话取数与组织统计因此永远同一份账本、同一套归属,不会出现两条路各算各的。
279498
+ usageRead,
279499
+ defaultCompanyId
279204
279500
  })] : []
279205
279501
  ],
279206
279502
  workorderDrafts,
@@ -279747,7 +280043,9 @@ async function startServe(opts) {
279747
280043
  ...c.dispatchId !== void 0 ? { dispatchId: c.dispatchId } : {},
279748
280044
  // ADR-0078 D3:写入围栏的钥匙
279749
280045
  ...c.artifactId !== void 0 ? { artifactId: c.artifactId } : {},
279750
- ...c.sessionId !== void 0 ? { sessionId: c.sessionId } : {}
280046
+ ...c.sessionId !== void 0 ? { sessionId: c.sessionId } : {},
280047
+ // ADR 0609 D1:签名 scopes 透给域路由,`isOperatorRequest` 据此认出部署级 operator-token。
280048
+ ...c.scopes !== void 0 ? { scopes: c.scopes } : {}
279751
280049
  } : null;
279752
280050
  },
279753
280051
  /**
@@ -288074,7 +288372,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
288074
288372
  }
288075
288373
  }
288076
288374
  }
288077
- var CLI_VERSION = "2.2.68" ? "2.2.68" : "dev";
288375
+ var CLI_VERSION = "2.2.69" ? "2.2.69" : "dev";
288078
288376
  function apiHeaders(token, company) {
288079
288377
  const headers = {
288080
288378
  authorization: `Bearer ${token}`,
@@ -291880,7 +292178,7 @@ function shimScript() {
291880
292178
  }
291881
292179
 
291882
292180
  // src/index.ts
291883
- var PKG_VERSION = true ? "2.2.68" : "dev";
292181
+ var PKG_VERSION = true ? "2.2.69" : "dev";
291884
292182
  var LOCAL_BIN = localBin();
291885
292183
  var NPM_PREFIX = npmPrefix();
291886
292184
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.68",
3
+ "version": "2.2.69",
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": "c84c49eb61b038824f5eb651f7653d8fcf31e318"
29
+ "sourceHead": "ac031d8ccbeea2dca6ecc80ad012b61de5ca2888"
30
30
  }
31
31
  }