@sema-agent/cli 1.0.88 → 1.0.89

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.
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@sema-agent/cli",
3
- "version": "1.0.88",
3
+ "version": "1.0.89",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sema-agent/cli",
9
- "version": "1.0.88",
9
+ "version": "1.0.89",
10
10
  "license": "BUSL-1.1",
11
11
  "dependencies": {
12
12
  "@sema-agent/sdk": "7.2.0",
13
- "@sema-agent/server": "7.39.0"
13
+ "@sema-agent/server": "7.41.0"
14
14
  },
15
15
  "bin": {
16
16
  "sema": "sema.js"
@@ -620,9 +620,9 @@
620
620
  }
621
621
  },
622
622
  "node_modules/@sema-agent/core": {
623
- "version": "5.53.0",
624
- "resolved": "https://registry.npmjs.org/@sema-agent/core/-/core-5.53.0.tgz",
625
- "integrity": "sha512-EbI/NEk+1HH0lUCi4qyE78bsVFgXwZ8Ed7sZxgJ6j0alAk5A/3KJHAzLkHBE+S83JDiAQa/JrojoZr14flllKw==",
623
+ "version": "5.54.0",
624
+ "resolved": "https://registry.npmjs.org/@sema-agent/core/-/core-5.54.0.tgz",
625
+ "integrity": "sha512-PDfPjymn7VjLXf0YX4xm1PILqCBgClS+ajrSnm2VheHG0U3Z2eP2O56vJlSyDWVuqwggJBADf31Zv1zBTQQbzg==",
626
626
  "license": "BUSL-1.1",
627
627
  "dependencies": {
628
628
  "@modelcontextprotocol/sdk": "1.30.0",
@@ -669,12 +669,12 @@
669
669
  }
670
670
  },
671
671
  "node_modules/@sema-agent/server": {
672
- "version": "7.39.0",
673
- "resolved": "https://registry.npmjs.org/@sema-agent/server/-/server-7.39.0.tgz",
674
- "integrity": "sha512-zdAcfeVSjYTU8DBidcW/tlXePRP/hHYYReFQtGz0Uk5LyxBeX1Pi1mHKhv1HL0wDP+ttQoYUL9y9DjHMinuGvA==",
672
+ "version": "7.41.0",
673
+ "resolved": "https://registry.npmjs.org/@sema-agent/server/-/server-7.41.0.tgz",
674
+ "integrity": "sha512-Uj5cbo1xJOhw0LKOZBINGAUmZGva+Km/m5auhvXec4F38iJGtzsMJIe6klELlytXv9YypXXQnzPYyLPA0YmhVQ==",
675
675
  "license": "BUSL-1.1",
676
676
  "dependencies": {
677
- "@sema-agent/core": "^5.52.0",
677
+ "@sema-agent/core": "^5.53.0",
678
678
  "@sema-agent/registry-core": "^0.19.0",
679
679
  "e2b": "^2.28.0",
680
680
  "libsodium-wrappers": "^0.8.4",
@@ -1484,9 +1484,9 @@
1484
1484
  "license": "ISC"
1485
1485
  },
1486
1486
  "node_modules/jose": {
1487
- "version": "6.2.9",
1488
- "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.9.tgz",
1489
- "integrity": "sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==",
1487
+ "version": "6.2.10",
1488
+ "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.10.tgz",
1489
+ "integrity": "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==",
1490
1490
  "license": "MIT",
1491
1491
  "funding": {
1492
1492
  "url": "https://github.com/sponsors/panva"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/cli",
3
- "version": "1.0.88",
3
+ "version": "1.0.89",
4
4
  "description": "Sema — your own Claude Code-grade coding agent, in the terminal.",
5
5
  "license": "BUSL-1.1",
6
6
  "bin": {
@@ -23,13 +23,13 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "@sema-agent/sdk": "7.2.0",
26
- "@sema-agent/server": "7.39.0"
26
+ "@sema-agent/server": "7.41.0"
27
27
  },
28
28
  "overrides": {
29
- "@sema-agent/core": "5.53.0",
29
+ "@sema-agent/core": "5.54.0",
30
30
  "@sema-agent/registry-core": "0.19.0"
31
31
  },
32
- "semaEngineVersion": "server 7.39.0 / core 5.53.0 / registry-core 0.19.0",
32
+ "semaEngineVersion": "server 7.41.0 / core 5.54.0 / registry-core 0.19.0",
33
33
  "engines": {
34
34
  "node": ">=20.3"
35
35
  }
package/sema-main.js CHANGED
@@ -59404,7 +59404,7 @@ function eventToSdkMessage(ev, ctx) {
59404
59404
  // falls back to the mock synthesizer (events.d.ts:79). Not a renderable CC SDKMessage —
59405
59405
  // a side-channel arm `sdkMessagesToCcEvents` reads; CC's own renderer never sees it.
59406
59406
  case "tool_end": {
59407
- let structured = ev.structured, toolEndErrorCode = ev.errorCode, toolEndSettledBy = ev.settledBy, toolEndApprover = ev.approver;
59407
+ let structured = ev.structured, toolEndErrorCode = ev.errorCode, toolEndSettledBy = ev.settledBy, toolEndApprover = ev.approver, collateralAbort = ev._sema_collateral_abort;
59408
59408
  return projected(stamp(ctx, {
59409
59409
  type: "tool_end_result",
59410
59410
  toolCallId: ev.toolCallId,
@@ -59421,6 +59421,8 @@ function eventToSdkMessage(ev, ctx) {
59421
59421
  // #263 半场:结算种类 + 结算方身份(开集、缺席不铸、在场原样;顶部大段头注是判据本体)。
59422
59422
  ...typeof toolEndSettledBy == "string" && toolEndSettledBy.length > 0 ? { settledBy: toolEndSettledBy } : {},
59423
59423
  ...typeof toolEndApprover == "string" && toolEndApprover.length > 0 ? { approver: toolEndApprover } : {},
59424
+ // #324:连坐 abort 机读位(严格 true 才上臂;见上方头注)。
59425
+ ...collateralAbort === !0 ? { _sema_collateral_abort: !0 } : {},
59424
59426
  ...ev.eventId !== void 0 ? { eventId: ev.eventId } : {},
59425
59427
  ...ev.parentToolCallId !== void 0 ? { parentToolCallId: ev.parentToolCallId } : {}
59426
59428
  }));
@@ -65649,6 +65651,9 @@ function isAskTool(toolName2) {
65649
65651
  function isGatedToolName(name) {
65650
65652
  return isAskToolLoose(name) ? !0 : typeof name != "string" ? !1 : toolNameIsFsWrite(name) || toolNameIsShellExec(name);
65651
65653
  }
65654
+ function isFsOrShellToolName(name) {
65655
+ return typeof name == "string" && (toolNameIsFsWrite(name) || toolNameIsShellExec(name));
65656
+ }
65652
65657
  function isGatedToolEnd(ev) {
65653
65658
  return isGatedToolName(ev.toolName) ? !0 : toolEndOutputText(ev.output) === ENGINE_ABORT_TOOL_RESULT;
65654
65659
  }
@@ -65703,14 +65708,14 @@ function routeToolEnd(ev, callId, led) {
65703
65708
  throw new Error("frameRouter: tool_end \u6709\u5E8F\u81C2\u5168\u90E8\u843D\u7A7A \u2014\u2014 generic-close \u4E0D\u518D\u662F\u65E0\u6761\u4EF6\u515C\u5E95\u81C2");
65704
65709
  }
65705
65710
  function routeSuspended(ev, ctx) {
65706
- return ev.gate && isAskToolLoose(ev.gate.toolName) && ctx.taskId.current ? { kind: "park", gate: "ask" } : ev.gate && isToolApprovalGate(ev.gate) && ctx.taskId.current ? { kind: "park", gate: "fs" } : { kind: "yield", events: [ev] };
65711
+ return ev.gate && isAskToolLoose(ev.gate.toolName) && ctx.taskId.current ? (ctx.led.noteParkGate({ fsOrShellFamily: !1 }), { kind: "park", gate: "ask" }) : ev.gate && isToolApprovalGate(ev.gate) && ctx.taskId.current ? (ctx.led.noteParkGate({ fsOrShellFamily: isFsOrShellToolName(ev.gate.toolName) }), { kind: "park", gate: "fs" }) : { kind: "yield", events: [ev] };
65707
65712
  }
65708
65713
  function routeDone(ev, ctx) {
65709
65714
  let { led } = ctx;
65710
65715
  if (isAskGatePark(ev.result))
65711
- return ctx.taskId.current = ctx.taskId.current ?? ev.result.taskId, { kind: "park", gate: "ask", pendingDone: ev };
65716
+ return ctx.taskId.current = ctx.taskId.current ?? ev.result.taskId, led.noteParkGate({ fsOrShellFamily: !1 }), { kind: "park", gate: "ask", pendingDone: ev };
65712
65717
  if (isFsApprovalPark(ev.result))
65713
- return ctx.taskId.current = ctx.taskId.current ?? ev.result.taskId, { kind: "park", gate: "fs", pendingDone: ev };
65718
+ return ctx.taskId.current = ctx.taskId.current ?? ev.result.taskId, led.noteParkGate({ fsOrShellFamily: isFsOrShellToolName(ev.result.checkpointGate?.toolName) }), { kind: "park", gate: "fs", pendingDone: ev };
65714
65719
  let events3 = [...led.flushHeld()], r = ev.result, realTaskId = ctx.taskId.current;
65715
65720
  return realTaskId && r && r.taskId === r.sessionId && r.taskId !== realTaskId ? events3.push({ ...ev, result: { ...ev.result, taskId: realTaskId } }) : events3.push(ev), { kind: "end", events: events3 };
65716
65721
  }
@@ -65797,7 +65802,17 @@ var HITL_REJECT_MESSAGE, ENGINE_ABORT_TOOL_RESULT, TOOL_END_ARM_ORDER, TOOL_END_
65797
65802
 
65798
65803
  // node_modules/@sema-agent/client-core/dist/hitl/gateLedger.js
65799
65804
  function createGateLedger() {
65800
- let startedCalls = /* @__PURE__ */ new Set(), endedCalls = /* @__PURE__ */ new Set(), heldAskEnds = /* @__PURE__ */ new Map(), gatedStartArgsByCall = /* @__PURE__ */ new Map(), deniedCalls = /* @__PURE__ */ new Set(), pendingFsCalls = [], resolvedAnswers = /* @__PURE__ */ new Map(), decidedGates = /* @__PURE__ */ new Set(), lastFsOrShellGatedCall, denyStampNextFsEnd = !1, seq;
65805
+ let startedCalls = /* @__PURE__ */ new Set(), endedCalls = /* @__PURE__ */ new Set(), heldAskEnds = /* @__PURE__ */ new Map(), gatedStartArgsByCall = /* @__PURE__ */ new Map(), deniedCalls = /* @__PURE__ */ new Set(), pendingFsCalls = [], resolvedAnswers = /* @__PURE__ */ new Map(), decidedGates = /* @__PURE__ */ new Set(), fsOrShellFamilyGate = !1, droppedFromBatch = /* @__PURE__ */ new Set(), batchParkCount = 0, heldSinceLastPark = !1, fsOrShellGatedCalls = /* @__PURE__ */ new Set(), lastFsOrShellGatedCall, resolveGateSubjects = () => {
65806
+ if (batchParkCount !== 1 || !fsOrShellFamilyGate)
65807
+ return;
65808
+ for (let id of heldAskEnds.keys())
65809
+ if (!startedCalls.has(id))
65810
+ return;
65811
+ let family = [.../* @__PURE__ */ new Set([...heldAskEnds.keys(), ...droppedFromBatch])].filter((id) => fsOrShellGatedCalls.has(id));
65812
+ return family.length === 1 ? new Set(family) : void 0;
65813
+ }, retireBatchState = () => {
65814
+ fsOrShellFamilyGate = !1, droppedFromBatch.clear(), batchParkCount = 0, heldSinceLastPark = !1;
65815
+ }, denyStampNextFsEnd = !1, seq;
65801
65816
  return {
65802
65817
  noteSeq(ev) {
65803
65818
  let rawSeq = ev.id ?? ev.seq;
@@ -65822,18 +65837,19 @@ function createGateLedger() {
65822
65837
  endedCalls.add(callId);
65823
65838
  },
65824
65839
  hold(callId, ev) {
65825
- heldAskEnds.set(callId, ev);
65840
+ heldAskEnds.has(callId) || (heldSinceLastPark = !0), heldAskEnds.set(callId, ev);
65826
65841
  },
65827
65842
  dropHeld(callId) {
65828
- heldAskEnds.delete(callId);
65843
+ heldAskEnds.delete(callId) && droppedFromBatch.add(callId), heldAskEnds.size === 0 && retireBatchState();
65829
65844
  },
65830
65845
  heldCount() {
65831
65846
  return heldAskEnds.size;
65832
65847
  },
65833
65848
  *flushHeld() {
65834
- for (let [callId, held] of heldAskEnds)
65835
- endedCalls.add(callId), yield held;
65836
- heldAskEnds.clear();
65849
+ let subjects = resolveGateSubjects();
65850
+ for (let [callId, ev] of heldAskEnds)
65851
+ endedCalls.add(callId), yield subjects !== void 0 && !subjects.has(callId) ? { ...ev, [SEMA_COLLATERAL_ABORT_KEY]: !0 } : ev;
65852
+ heldAskEnds.clear(), retireBatchState();
65837
65853
  },
65838
65854
  markDenied(callId) {
65839
65855
  deniedCalls.add(callId);
@@ -65861,11 +65877,14 @@ function createGateLedger() {
65861
65877
  return pendingFsCalls.length > 0 ? pendingFsCalls[pendingFsCalls.length - 1] : void 0;
65862
65878
  },
65863
65879
  noteFsOrShellGate(callId) {
65864
- lastFsOrShellGatedCall = callId;
65880
+ lastFsOrShellGatedCall = callId, fsOrShellGatedCalls.add(callId);
65865
65881
  },
65866
65882
  lastFsOrShellGatedCallId() {
65867
65883
  return lastFsOrShellGatedCall;
65868
65884
  },
65885
+ noteParkGate(evidence) {
65886
+ batchParkCount > 0 && !heldSinceLastPark || (batchParkCount += 1, fsOrShellFamilyGate = evidence.fsOrShellFamily, heldSinceLastPark = !1);
65887
+ },
65869
65888
  rememberAnswer(callId, answered) {
65870
65889
  resolvedAnswers.set(callId, answered);
65871
65890
  },
@@ -65886,8 +65905,9 @@ function createGateLedger() {
65886
65905
  }
65887
65906
  };
65888
65907
  }
65889
- var init_gateLedger = __esm({
65908
+ var SEMA_COLLATERAL_ABORT_KEY, init_gateLedger = __esm({
65890
65909
  "node_modules/@sema-agent/client-core/dist/hitl/gateLedger.js"() {
65910
+ SEMA_COLLATERAL_ABORT_KEY = "_sema_collateral_abort";
65891
65911
  }
65892
65912
  });
65893
65913
 
@@ -66078,6 +66098,7 @@ var init_askGateWire = __esm({
66078
66098
  init_frameRouter();
66079
66099
  init_parkResolver();
66080
66100
  init_frameRouter();
66101
+ init_gateLedger();
66081
66102
  init_parkResolver();
66082
66103
  }
66083
66104
  });
@@ -68834,6 +68855,7 @@ __export(dist_exports, {
68834
68855
  SEAT_VALIDATOR_COVERAGE_EXEMPT: () => SEAT_VALIDATOR_COVERAGE_EXEMPT,
68835
68856
  SEAT_VALIDATOR_KEY_COVERAGE: () => SEAT_VALIDATOR_KEY_COVERAGE,
68836
68857
  SELF_ORCHESTRATION_ENV: () => SELF_ORCHESTRATION_ENV,
68858
+ SEMA_COLLATERAL_ABORT_KEY: () => SEMA_COLLATERAL_ABORT_KEY,
68837
68859
  SEND_MESSAGE_REQUEST_KEYS: () => SEND_MESSAGE_REQUEST_KEYS,
68838
68860
  SESSION_SEARCH_RESULT_KEYS: () => SESSION_SEARCH_RESULT_KEYS,
68839
68861
  SKILL_CAPS: () => SKILL_CAPS,
@@ -110687,7 +110709,7 @@ var import_stack_utils, import_jsx_runtime6, cleanupPath, stackUtils, init_Error
110687
110709
  // build-src/src/ink/components/TerminalSizeContext.tsx
110688
110710
  var import_react9, TerminalSizeContext, init_TerminalSizeContext = __esm({
110689
110711
  "build-src/src/ink/components/TerminalSizeContext.tsx"() {
110690
- import_react9 = __toESM(require_react()), TerminalSizeContext = (0, import_react9.createContext)(null);
110712
+ import_react9 = __toESM(require_react(), 1), TerminalSizeContext = (0, import_react9.createContext)(null);
110691
110713
  }
110692
110714
  });
110693
110715
 
@@ -184639,8 +184661,12 @@ function stripImagesFromMessages(messages) {
184639
184661
  } : message;
184640
184662
  });
184641
184663
  }
184664
+ function hasPtlRetryMarkerHead(messages) {
184665
+ let head = messages[0];
184666
+ return head?.type === "user" && head.isMeta === !0 && head.message.content === PTL_RETRY_MARKER;
184667
+ }
184642
184668
  function truncateHeadForPTLRetry(messages, ptlResponse) {
184643
- let input = messages[0]?.type === "user" && messages[0].isMeta && messages[0].message.content === PTL_RETRY_MARKER ? messages.slice(1) : messages, groups = groupMessagesByApiRound(input);
184669
+ let input = hasPtlRetryMarkerHead(messages) ? messages.slice(1) : messages, groups = groupMessagesByApiRound(input);
184644
184670
  if (groups.length < 2) return null;
184645
184671
  let tokenGap = getPromptTooLongTokenGap(ptlResponse), dropCount;
184646
184672
  if (tokenGap !== void 0) {
@@ -184896,11 +184922,14 @@ async function partialCompactConversation(allMessages, pivotIndex, context3, cac
184896
184922
  User context: ${userFeedback}` : hookResult.newCustomInstructions ? customInstructions = hookResult.newCustomInstructions : userFeedback && (customInstructions = `User context: ${userFeedback}`), context3.setStreamMode?.("requesting"), context3.setResponseLength?.(() => 0), context3.onCompactProgress?.({ type: "compact_start" });
184897
184923
  let compactPrompt = getPartialCompactPrompt(customInstructions, direction), summaryRequest = createUserMessage({
184898
184924
  content: compactPrompt
184899
- }), failureMetadata = {
184925
+ }), apiMessages = direction === "up_to" ? messagesToSummarize : allMessages, retryCacheSafeParams = direction === "up_to" ? { ...cacheSafeParams, forkContextMessages: messagesToSummarize } : cacheSafeParams, summarizedCount = () => {
184926
+ let real = apiMessages.length - (hasPtlRetryMarkerHead(apiMessages) ? 1 : 0);
184927
+ return Math.min(real, messagesToSummarize.length);
184928
+ }, failureMetadata = () => ({
184900
184929
  preCompactTokenCount,
184901
184930
  direction,
184902
- messagesSummarized: messagesToSummarize.length
184903
- }, apiMessages = direction === "up_to" ? messagesToSummarize : allMessages, retryCacheSafeParams = direction === "up_to" ? { ...cacheSafeParams, forkContextMessages: messagesToSummarize } : cacheSafeParams, summaryResponse, summary, ptlAttempts = 0;
184931
+ messagesSummarized: summarizedCount()
184932
+ }), summaryResponse, summary, ptlAttempts = 0;
184904
184933
  for (; summaryResponse = await streamCompactSummary({
184905
184934
  messages: apiMessages,
184906
184935
  summaryRequest,
@@ -184913,7 +184942,7 @@ User context: ${userFeedback}` : hookResult.newCustomInstructions ? customInstru
184913
184942
  let truncated = ptlAttempts <= MAX_PTL_RETRIES ? truncateHeadForPTLRetry(apiMessages, summaryResponse) : null;
184914
184943
  if (!truncated)
184915
184944
  throw {
184916
- ...failureMetadata
184945
+ ...failureMetadata()
184917
184946
  }, new Error(ERROR_MESSAGE_PROMPT_TOO_LONG);
184918
184947
  apiMessages.length - truncated.length, truncated.length, apiMessages = truncated, retryCacheSafeParams = {
184919
184948
  ...retryCacheSafeParams,
@@ -184923,10 +184952,10 @@ User context: ${userFeedback}` : hookResult.newCustomInstructions ? customInstru
184923
184952
  if (summary) {
184924
184953
  if (startsWithApiErrorPrefix(summary) || isGovernanceStopRowText(summary))
184925
184954
  throw {
184926
- ...failureMetadata
184955
+ ...failureMetadata()
184927
184956
  }, new Error(summary);
184928
184957
  } else throw {
184929
- ...failureMetadata
184958
+ ...failureMetadata()
184930
184959
  }, new Error(
184931
184960
  "Failed to generate conversation summary - response did not contain valid text content"
184932
184961
  );
@@ -184974,13 +185003,15 @@ User context: ${userFeedback}` : hookResult.newCustomInstructions ? customInstru
184974
185003
  }), postCompactTokenCount = tokenCountFromLastAPIResponse([
184975
185004
  summaryResponse
184976
185005
  ]), compactionUsage = getTokenUsage(summaryResponse);
184977
- messagesToKeep.length, messagesToSummarize.length, compactionUsage?.input_tokens, compactionUsage?.output_tokens, compactionUsage?.cache_read_input_tokens, compactionUsage?.cache_creation_input_tokens;
185006
+ messagesToKeep.length, summarizedCount(), compactionUsage?.input_tokens, compactionUsage?.output_tokens, compactionUsage?.cache_read_input_tokens, compactionUsage?.cache_creation_input_tokens;
184978
185007
  let lastPreCompactUuid = direction === "up_to" ? allMessages.slice(0, pivotIndex).findLast((m2) => m2.type !== "progress")?.uuid : messagesToKeep.at(-1)?.uuid, boundaryMarker = createCompactBoundaryMessage(
184979
185008
  "manual",
184980
185009
  preCompactTokenCount ?? 0,
184981
185010
  lastPreCompactUuid,
184982
185011
  userFeedback,
184983
- messagesToSummarize.length
185012
+ // 同族同修:边界标记里的这一位与屏上那一位是**同一个量**,两处读同一个函数,
185013
+ // 否则同一次压缩会在转录里留两个互相矛盾的 N。
185014
+ summarizedCount()
184984
185015
  ), preCompactDiscovered = extractDiscoveredToolNames(allMessages);
184985
185016
  preCompactDiscovered.size > 0 && (boundaryMarker.compactMetadata.preCompactDiscoveredTools = [
184986
185017
  ...preCompactDiscovered
@@ -184991,7 +185022,8 @@ User context: ${userFeedback}` : hookResult.newCustomInstructions ? customInstru
184991
185022
  isCompactSummary: !0,
184992
185023
  ...messagesToKeep.length > 0 ? {
184993
185024
  summarizeMetadata: {
184994
- messagesSummarized: messagesToSummarize.length,
185025
+ // 屏上 "Summarized N messages" 的 N(CompactSummary.tsx)——读真参与摘要的条数。
185026
+ messagesSummarized: summarizedCount(),
184995
185027
  userContext: userFeedback,
184996
185028
  direction
184997
185029
  }
@@ -304426,7 +304458,7 @@ function useClaudeAiLimits() {
304426
304458
  }
304427
304459
  var import_react69, init_claudeAiLimitsHook = __esm({
304428
304460
  "build-src/src/services/claudeAiLimitsHook.ts"() {
304429
- import_react69 = __toESM(require_react());
304461
+ import_react69 = __toESM(require_react(), 1);
304430
304462
  init_claudeAiLimits();
304431
304463
  }
304432
304464
  });
@@ -308028,7 +308060,7 @@ function UserToolErrorMessage(t0) {
308028
308060
  verbose,
308029
308061
  isTranscriptMode
308030
308062
  } = t0, param = normalizeEngineReminderParam(rawParam);
308031
- if (typeof param.content == "string" && (param.content.startsWith(INTERRUPTED_BATCH_FOLD_PREFIX) || param.content.startsWith(REPEATED_NOTICE_FOLD_PREFIX) || param.content.startsWith(REPEATED_ERROR_FOLD_PREFIX)))
308063
+ if (typeof param.content == "string" && (param.content.startsWith(INTERRUPTED_BATCH_FOLD_PREFIX) || param.content.startsWith(REPEATED_NOTICE_FOLD_PREFIX) || param.content.startsWith(REPEATED_ERROR_FOLD_PREFIX) || param.content.startsWith(COLLATERAL_ABORT_FOLD_PREFIX)))
308032
308064
  return /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(MessageResponse, { height: 1, children: /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(ThemedText, { dimColor: !0, children: param.content }) });
308033
308065
  if (typeof param.content == "string" && param.content.includes(INTERRUPT_MESSAGE_FOR_TOOL_USE)) {
308034
308066
  let t12;
@@ -308073,7 +308105,7 @@ function UserToolErrorMessage(t0) {
308073
308105
  isTranscriptMode
308074
308106
  }) ?? /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(FallbackToolUseErrorMessage, { result: param.content, verbose }), $3[7] = isTranscriptMode, $3[8] = param.content, $3[9] = progressMessagesForMessage, $3[10] = tool, $3[11] = tools, $3[12] = verbose, $3[13] = t1) : t1 = $3[13], t1;
308075
308107
  }
308076
- var import_compiler_runtime105, import_jsx_runtime128, INTERRUPTED_BATCH_FOLD_PREFIX, REPEATED_NOTICE_FOLD_PREFIX, REPEATED_ERROR_FOLD_PREFIX, init_UserToolErrorMessage = __esm({
308108
+ var import_compiler_runtime105, import_jsx_runtime128, INTERRUPTED_BATCH_FOLD_PREFIX, REPEATED_NOTICE_FOLD_PREFIX, REPEATED_ERROR_FOLD_PREFIX, COLLATERAL_ABORT_FOLD_PREFIX, init_UserToolErrorMessage = __esm({
308077
308109
  "build-src/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx"() {
308078
308110
  import_compiler_runtime105 = __toESM(require_compiler_runtime(), 1);
308079
308111
  init_systemReminderTag();
@@ -308087,7 +308119,7 @@ var import_compiler_runtime105, import_jsx_runtime128, INTERRUPTED_BATCH_FOLD_PR
308087
308119
  init_RejectedPlanMessage();
308088
308120
  init_RejectedToolUseMessage();
308089
308121
  import_jsx_runtime128 = __toESM(require_jsx_runtime(), 1);
308090
- INTERRUPTED_BATCH_FOLD_PREFIX = "Interrupted with the batch: ", REPEATED_NOTICE_FOLD_PREFIX = "Same notice as above \xD7", REPEATED_ERROR_FOLD_PREFIX = "Same error as above \xD7";
308122
+ INTERRUPTED_BATCH_FOLD_PREFIX = "Interrupted with the batch: ", REPEATED_NOTICE_FOLD_PREFIX = "Same notice as above \xD7", REPEATED_ERROR_FOLD_PREFIX = "Same error as above \xD7", COLLATERAL_ABORT_FOLD_PREFIX = "Aborted alongside the pending approval: ";
308091
308123
  }
308092
308124
  });
308093
308125
 
@@ -362727,6 +362759,9 @@ var norm, warnedCrossEndpointEntries, PER_MODEL_BASEURL_MIN, init_modelEndpointS
362727
362759
  var seamQuery_exports = {};
362728
362760
  __export(seamQuery_exports, {
362729
362761
  SCENARIO_KEYS: () => SCENARIO_KEYS,
362762
+ _resetInteractiveCancelStateForTest: () => _resetInteractiveCancelStateForTest,
362763
+ _setInteractiveCancelTimeoutMsForTest: () => _setInteractiveCancelTimeoutMsForTest,
362764
+ bestEffortInteractiveCancel: () => bestEffortInteractiveCancel,
362730
362765
  canUseTool: () => canUseTool,
362731
362766
  cancelEngineRun: () => cancelEngineRun,
362732
362767
  drainingRetryDelayMs: () => drainingRetryDelayMs2,
@@ -363171,7 +363206,7 @@ Fix: run \`sema doctor\` to diagnose, or restart sema to retry.`
363171
363206
  } catch {
363172
363207
  }
363173
363208
  noteEngineTurnStart();
363174
- let setInProgress = params.toolUseContext?.setInProgressToolUseIDs, engineOpenToolIds = /* @__PURE__ */ new Set();
363209
+ let setInProgress = params.toolUseContext?.setInProgressToolUseIDs, engineOpenToolIds = /* @__PURE__ */ new Set(), foregroundRunRegistered = null;
363175
363210
  try {
363176
363211
  let sessionId = req.sessionId ?? mockClientIds.sessionId;
363177
363212
  try {
@@ -363237,7 +363272,7 @@ Fix: run \`sema doctor\` to diagnose, or restart sema to retry.`
363237
363272
  let reattachArmed = typeof client.runs.get == "function" && !isBgHeadlessWorker(), rawLive = client.tasks.stream(req, {
363238
363273
  signal,
363239
363274
  onRunBound: (id) => {
363240
- boundRunId = id, armTerminationIntentOnAbort(signal, id);
363275
+ boundRunId = id, armTerminationIntentOnAbort(signal, id), isMainLaneQuerySource(params.querySource) && !isBackgroundJournalSuppressed() && (foregroundInteractiveRunId = id, foregroundRunRegistered = id);
363241
363276
  }
363242
363277
  });
363243
363278
  if (pendingInterruptedSettledRow !== null) {
@@ -363455,7 +363490,7 @@ ${notifSupplement}` : baseErrorContent
363455
363490
  return next;
363456
363491
  });
363457
363492
  }
363458
- disarmAllEngineDetachHints(params.toolUseContext), setActiveEngineTaskId(null), noteEngineTurnBoundary();
363493
+ disarmAllEngineDetachHints(params.toolUseContext), setActiveEngineTaskId(null), foregroundRunRegistered !== null && foregroundInteractiveRunId === foregroundRunRegistered && (foregroundInteractiveRunId = null), noteEngineTurnBoundary();
363459
363494
  }
363460
363495
  }
363461
363496
  function isHitlClient(c2) {
@@ -363484,6 +363519,52 @@ async function bestEffortDurableCancel() {
363484
363519
  } catch {
363485
363520
  }
363486
363521
  }
363522
+ function _setInteractiveCancelTimeoutMsForTest(ms) {
363523
+ escCancelTimeoutOverrideMs = ms;
363524
+ }
363525
+ function _resetInteractiveCancelStateForTest() {
363526
+ foregroundInteractiveRunId = null, escCancelInFlight.clear(), escCancelTimeoutOverrideMs = null;
363527
+ }
363528
+ function bestEffortInteractiveCancel() {
363529
+ try {
363530
+ let taskId = foregroundInteractiveRunId;
363531
+ if (taskId === null) return;
363532
+ let runs = client.runs, cancelFn = runs.cancel;
363533
+ if (typeof cancelFn != "function") {
363534
+ escCancelProbe(`no runs.cancel verb on this lane \u2014 nothing sent for ${taskId}`);
363535
+ return;
363536
+ }
363537
+ if (escCancelInFlight.has(taskId)) {
363538
+ escCancelProbe(`a cancel is already in flight for ${taskId} \u2014 this press yields to it`);
363539
+ return;
363540
+ }
363541
+ let prior = terminationIntentSource(taskId);
363542
+ escCancelInFlight.add(taskId), recordTerminationIntent(taskId, "esc-cancel"), (async () => {
363543
+ try {
363544
+ await cancelFn.call(runs, taskId, {
363545
+ signal: AbortSignal.timeout(escCancelTimeoutOverrideMs ?? ESC_CANCEL_TIMEOUT_MS)
363546
+ }), escCancelProbe(`cancel accepted for ${taskId}`);
363547
+ } catch (e) {
363548
+ terminationIntentSource(taskId) === "esc-cancel" && (prior === null ? forgetTerminationIntent(taskId) : recordTerminationIntent(taskId, prior)), escCancelProbe(
363549
+ `cancel refused for ${taskId} (${String(
363550
+ e?.errorCode ?? "transport"
363551
+ )}) \u2014 ledger restored to ${String(prior)}`
363552
+ );
363553
+ } finally {
363554
+ escCancelInFlight.delete(taskId);
363555
+ }
363556
+ })();
363557
+ } catch {
363558
+ }
363559
+ }
363560
+ function escCancelProbe(msg) {
363561
+ let line = `[sema][seamQuery] esc-cancel: ${msg}`;
363562
+ try {
363563
+ logForDebugging(line);
363564
+ } catch {
363565
+ }
363566
+ process.env.SEMA_DEBUG && console.error(line);
363567
+ }
363487
363568
  async function probeEngineRunState(taskId) {
363488
363569
  return (await probeEngineRunLiveness(taskId)).state;
363489
363570
  }
@@ -363602,7 +363683,7 @@ function resumeRunningArmWiring() {
363602
363683
  shellSessionId: () => getSessionId()
363603
363684
  };
363604
363685
  }
363605
- var BG_TEAR_MAX_RESENDS, BG_RECOVERY_WAIT_MS, DRAINING_MAX_RETRIES, drainingBackoffMs, client, clientExplicitlySet, seamSettings, printResolvedSettingsProvider, seamWebSearch, seamSkills, seamMcp, seamRewindCapture, seamPendingRewind, INTERRUPT_WIRE_MARKER, seamPendingInterrupt, seamHistorySeedPending, seamSandbox, seamAutoSandboxInjected, HISTORY_SEED_MAX_CHARS, SCENARIO_KEYS, API_ERROR_PREFIX, defaultGatePrompt, canUseTool, RUN_PROBE_TIMEOUT_MS, init_seamQuery = __esm({
363686
+ var BG_TEAR_MAX_RESENDS, BG_RECOVERY_WAIT_MS, DRAINING_MAX_RETRIES, drainingBackoffMs, client, clientExplicitlySet, seamSettings, printResolvedSettingsProvider, seamWebSearch, seamSkills, seamMcp, seamRewindCapture, seamPendingRewind, INTERRUPT_WIRE_MARKER, seamPendingInterrupt, seamHistorySeedPending, seamSandbox, seamAutoSandboxInjected, HISTORY_SEED_MAX_CHARS, SCENARIO_KEYS, API_ERROR_PREFIX, defaultGatePrompt, canUseTool, foregroundInteractiveRunId, escCancelInFlight, ESC_CANCEL_TIMEOUT_MS, escCancelTimeoutOverrideMs, RUN_PROBE_TIMEOUT_MS, init_seamQuery = __esm({
363606
363687
  "build-src/src/sema/seamQuery.ts"() {
363607
363688
  init_hotReconcile();
363608
363689
  init_adapter();
@@ -363723,6 +363804,7 @@ var BG_TEAR_MAX_RESENDS, BG_RECOVERY_WAIT_MS, DRAINING_MAX_RETRIES, drainingBack
363723
363804
  updatedInput: input,
363724
363805
  toolUseID
363725
363806
  }), canUseTool = isHitlClient(client) ? makeHitlCanUseTool2(new HitlBridge(client, mockClientIds.taskId), defaultGatePrompt) : makeCanUseTool();
363807
+ foregroundInteractiveRunId = null, escCancelInFlight = /* @__PURE__ */ new Set(), ESC_CANCEL_TIMEOUT_MS = 5e3, escCancelTimeoutOverrideMs = null;
363726
363808
  RUN_PROBE_TIMEOUT_MS = 5e3;
363727
363809
  }
363728
363810
  });
@@ -392957,18 +393039,14 @@ var sema_brand_default, init_sema_brand = __esm({
392957
393039
  _doc: "displayName/email \u7559 null = \u8FD0\u884C\u65F6\u56DE\u9000(\u540D\u5B57\u53D6\u672C\u673A OS \u7528\u6237\u540D,\u90AE\u7BB1\u4E0D\u663E\u793A)\u3002\u522B\u70E7\u4E2A\u4EBA\u4FE1\u606F\u8FDB\u54C1\u724C\u6587\u4EF6(F-S1-1:\u65B0\u7528\u6237\u66FE\u770B\u5230 Welcome back Clay!)\u3002"
392958
393040
  },
392959
393041
  whatsNew: {
392960
- version: "1.0.88",
393042
+ version: "1.0.89",
392961
393043
  notes: [
392962
- "Approval cards now show a live countdown to their decision window, with the card turning urgent in the last 60 seconds and withdrawing itself when the window closes \u2014 so a pending approval never silently looks alive after it expired.",
392963
- "Countdowns only appear on live approvals: when a session re-attaches or resumes, replayed history never fakes a timer (those cards simply show without a countdown, exactly as before).",
392964
- "When a delegation is refused because all concurrent seats are busy, the transcript now says so and tells you a seat frees up as soon as a sibling agent finishes \u2014 nothing to reconfigure.",
392965
- "When the retained-window delegation quota is reached, the transcript now points to the actual way forward: continue an existing agent with SendMessage, or wait for old rows to be reaped \u2014 no need to switch sessions.",
392966
- "Calls to an MCP server that an operator revoked mid-session now explain that the request was never sent and stays refused until the server is approved again.",
392967
- "Engine updated to server 7.39.0 (core 5.53, including the prompt-cache fix that stops MCP tool refreshes from silently breaking cache reuse every turn)."
393044
+ "When an approval card is waiting for your decision and the engine aborts the other in-flight tool calls running alongside it, the transcript now folds those into one summary line ('Aborted alongside the pending approval: N more tool calls (ctrl+o for detail)') instead of repeating a full error block for each one \u2014 the call you're actually deciding on stays fully visible, full detail is still one ctrl+o away.",
393045
+ "Engine updated to server 7.41.0 (core 5.54)."
392968
393046
  ]
392969
393047
  },
392970
- productVersion: "1.0.88",
392971
- announcement: "sema 1.0.88 \u2014 approval cards now show a live countdown to their decision window (live streams only; replayed history never fakes a timer), and delegation/MCP refusals explain the actual next step (wait for a seat, reuse via SendMessage, or re-approve a revoked server)"
393048
+ productVersion: "1.0.89",
393049
+ announcement: "sema 1.0.89 \u2014 tool calls aborted alongside a pending approval now fold into one summary line instead of repeating a full error per call, so the approval you're deciding on doesn't get buried"
392972
393050
  };
392973
393051
  }
392974
393052
  });
@@ -393724,11 +393802,11 @@ function readErrorResult(msg) {
393724
393802
  if (!block2 || block2.type !== "tool_result" || block2.is_error !== !0 || typeof block2.tool_use_id != "string") return null;
393725
393803
  let isPlainText = typeof block2.content == "string", normalized = normalizeResultContent(block2.content);
393726
393804
  if (normalized === null) return null;
393727
- let text2 = isPlainText ? unwrapWholeSystemReminder(normalized).trim() : normalized, family = familyOf(text2, isPlainText);
393805
+ let text2 = isPlainText ? unwrapWholeSystemReminder(normalized).trim() : normalized, collateral = msg[SEMA_COLLATERAL_ABORT_KEY] === !0, family = familyOf(text2, isPlainText, collateral);
393728
393806
  return family === null ? null : { text: text2, toolUseId: block2.tool_use_id, family };
393729
393807
  }
393730
- function familyOf(text2, isPlainText) {
393731
- return isPlainText ? text2 === INTERRUPTED_REPEAT_SENTENCE ? "interrupted-batch" : text2.startsWith(APPROVAL_DENIED_ANCHOR) || text2.includes(APPROVAL_WINDOW_ANCHOR) ? "repeated-notice" : "repeated-error" : "repeated-error";
393808
+ function familyOf(text2, isPlainText, collateral) {
393809
+ return collateral ? "collateral-abort" : isPlainText ? text2 === INTERRUPTED_REPEAT_SENTENCE ? "interrupted-batch" : text2.startsWith(APPROVAL_DENIED_ANCHOR) || text2.includes(APPROVAL_WINDOW_ANCHOR) ? "repeated-notice" : "repeated-error" : "repeated-error";
393732
393810
  }
393733
393811
  function assistantToolUseId(msg) {
393734
393812
  if (!msg || msg.type !== "assistant") return null;
@@ -393743,6 +393821,7 @@ function foldRow(from, text2, count3) {
393743
393821
  let block2 = firstBlock(from), {
393744
393822
  toolUseResult: _droppedToolUseResult,
393745
393823
  _sema_degraded: _droppedDegraded,
393824
+ [SEMA_COLLATERAL_ABORT_KEY]: _droppedCollateralAbort,
393746
393825
  ...rest
393747
393826
  } = from;
393748
393827
  return {
@@ -393799,14 +393878,8 @@ function collapseRepeatedToolErrors(messages, verbose) {
393799
393878
  let unit = run2[k2];
393800
393879
  unit.span === 2 && result.push(messages[unit.start]);
393801
393880
  }
393802
- let prefix = first.view.family === "repeated-notice" ? REPEATED_NOTICE_FOLD_PREFIX : REPEATED_ERROR_FOLD_PREFIX;
393803
- result.push(
393804
- foldRow(
393805
- run2[1].result,
393806
- `${prefix}${String(run2.length - 1)}`,
393807
- run2.length - 1
393808
- )
393809
- );
393881
+ let folded = run2.length - 1, text2 = first.view.family === "repeated-notice" ? `${REPEATED_NOTICE_FOLD_PREFIX}${String(folded)}` : first.view.family === "collateral-abort" ? `${COLLATERAL_ABORT_FOLD_PREFIX}${String(folded)} more tool ${folded === 1 ? "call" : "calls"} (ctrl+o for detail)` : `${REPEATED_ERROR_FOLD_PREFIX}${String(folded)}`;
393882
+ result.push(foldRow(run2[1].result, text2, folded));
393810
393883
  }
393811
393884
  i = j3;
393812
393885
  }
@@ -393814,11 +393887,13 @@ function collapseRepeatedToolErrors(messages, verbose) {
393814
393887
  }
393815
393888
  var INTERRUPTED_REPEAT_SENTENCE, APPROVAL_DENIED_ANCHOR, APPROVAL_WINDOW_ANCHOR, MIN_RUN, init_collapseRepeatedToolErrors = __esm({
393816
393889
  "build-src/src/utils/collapseRepeatedToolErrors.ts"() {
393890
+ init_dist();
393817
393891
  init_UserToolErrorMessage();
393818
393892
  INTERRUPTED_REPEAT_SENTENCE = "[INTERRUPTED] Same as an earlier interrupted call in this batch: it never started and had no side effects \u2014 safe to re-issue.", APPROVAL_DENIED_ANCHOR = 'approval denied for "', APPROVAL_WINDOW_ANCHOR = "was not answered before the approver's own window elapsed", MIN_RUN = {
393819
393893
  "interrupted-batch": 2,
393820
393894
  "repeated-notice": 2,
393821
- "repeated-error": 3
393895
+ "repeated-error": 3,
393896
+ "collateral-abort": 2
393822
393897
  };
393823
393898
  }
393824
393899
  });
@@ -397312,18 +397387,14 @@ var require_sema_brand = __commonJS({
397312
397387
  _doc: "displayName/email \u7559 null = \u8FD0\u884C\u65F6\u56DE\u9000(\u540D\u5B57\u53D6\u672C\u673A OS \u7528\u6237\u540D,\u90AE\u7BB1\u4E0D\u663E\u793A)\u3002\u522B\u70E7\u4E2A\u4EBA\u4FE1\u606F\u8FDB\u54C1\u724C\u6587\u4EF6(F-S1-1:\u65B0\u7528\u6237\u66FE\u770B\u5230 Welcome back Clay!)\u3002"
397313
397388
  },
397314
397389
  whatsNew: {
397315
- version: "1.0.88",
397390
+ version: "1.0.89",
397316
397391
  notes: [
397317
- "Approval cards now show a live countdown to their decision window, with the card turning urgent in the last 60 seconds and withdrawing itself when the window closes \u2014 so a pending approval never silently looks alive after it expired.",
397318
- "Countdowns only appear on live approvals: when a session re-attaches or resumes, replayed history never fakes a timer (those cards simply show without a countdown, exactly as before).",
397319
- "When a delegation is refused because all concurrent seats are busy, the transcript now says so and tells you a seat frees up as soon as a sibling agent finishes \u2014 nothing to reconfigure.",
397320
- "When the retained-window delegation quota is reached, the transcript now points to the actual way forward: continue an existing agent with SendMessage, or wait for old rows to be reaped \u2014 no need to switch sessions.",
397321
- "Calls to an MCP server that an operator revoked mid-session now explain that the request was never sent and stays refused until the server is approved again.",
397322
- "Engine updated to server 7.39.0 (core 5.53, including the prompt-cache fix that stops MCP tool refreshes from silently breaking cache reuse every turn)."
397392
+ "When an approval card is waiting for your decision and the engine aborts the other in-flight tool calls running alongside it, the transcript now folds those into one summary line ('Aborted alongside the pending approval: N more tool calls (ctrl+o for detail)') instead of repeating a full error block for each one \u2014 the call you're actually deciding on stays fully visible, full detail is still one ctrl+o away.",
397393
+ "Engine updated to server 7.41.0 (core 5.54)."
397323
397394
  ]
397324
397395
  },
397325
- productVersion: "1.0.88",
397326
- announcement: "sema 1.0.88 \u2014 approval cards now show a live countdown to their decision window (live streams only; replayed history never fakes a timer), and delegation/MCP refusals explain the actual next step (wait for a seat, reuse via SendMessage, or re-approve a revoked server)"
397396
+ productVersion: "1.0.89",
397397
+ announcement: "sema 1.0.89 \u2014 tool calls aborted alongside a pending approval now fold into one summary line instead of repeating a full error per call, so the approval you're deciding on doesn't get buried"
397327
397398
  };
397328
397399
  }
397329
397400
  });
@@ -431513,6 +431584,14 @@ function deriveUUID(parentUUID, index) {
431513
431584
  let hex3 = index.toString(16).padStart(12, "0");
431514
431585
  return `${parentUUID.slice(0, 24)}${hex3}`;
431515
431586
  }
431587
+ function preserveSemaRenderSidecars(from) {
431588
+ let src = from;
431589
+ if (src === null || typeof src != "object") return {};
431590
+ let out6 = {};
431591
+ for (let key of SEMA_RENDER_SIDECAR_KEYS)
431592
+ src[key] !== void 0 && (out6[key] = src[key]);
431593
+ return out6;
431594
+ }
431516
431595
  function normalizeMessages(messages) {
431517
431596
  let isNewChain = !1;
431518
431597
  return messages.flatMap((message) => {
@@ -431573,7 +431652,10 @@ function normalizeMessages(messages) {
431573
431652
  imagePasteIds: imageId !== void 0 ? [imageId] : void 0,
431574
431653
  origin: message.origin
431575
431654
  }),
431576
- uuid: isNewChain ? deriveUUID(message.uuid, index) : message.uuid
431655
+ uuid: isNewChain ? deriveUUID(message.uuid, index) : message.uuid,
431656
+ // 🔴 sema #324:顶层 sema sidecar 透传 —— 判据与族扫见 SEMA_RENDER_SIDECAR_KEYS 头注
431657
+ // (白名单重建把它们丢在消费者上游 = 折叠族在真管线上恒不触发)。
431658
+ ...preserveSemaRenderSidecars(message)
431577
431659
  };
431578
431660
  });
431579
431661
  }
@@ -433984,8 +434066,9 @@ ${raw2}
433984
434066
  IMPORTANT: After completing your current task, you MUST address the user's message above. Do not ignore it.`;
433985
434067
  }
433986
434068
  }
433987
- var MEMORY_CORRECTION_HINT, TOOL_REFERENCE_TURN_BOUNDARY, INTERRUPT_MESSAGE, INTERRUPT_MESSAGE_FOR_TOOL_USE, CANCEL_MESSAGE2, REJECT_MESSAGE2, REJECT_MESSAGE_WITH_REASON_PREFIX, SUBAGENT_REJECT_MESSAGE, SUBAGENT_REJECT_MESSAGE_WITH_REASON_PREFIX, PLAN_REJECTION_PREFIX, DENIAL_WORKAROUND_GUIDANCE, NO_RESPONSE_REQUESTED, SYNTHETIC_TOOL_RESULT_PLACEHOLDER, AUTO_MODE_REJECTION_PREFIX, SYNTHETIC_MODEL, SYNTHETIC_MESSAGES, EMPTY_LOOKUPS, EMPTY_STRING_SET, STRIPPED_TAGS_RE, PLAN_PHASE4_CONTROL, PLAN_PHASE4_TRIM, PLAN_PHASE4_CUT, PLAN_PHASE4_CAP, init_messages4 = __esm({
434069
+ var MEMORY_CORRECTION_HINT, TOOL_REFERENCE_TURN_BOUNDARY, INTERRUPT_MESSAGE, INTERRUPT_MESSAGE_FOR_TOOL_USE, CANCEL_MESSAGE2, REJECT_MESSAGE2, REJECT_MESSAGE_WITH_REASON_PREFIX, SUBAGENT_REJECT_MESSAGE, SUBAGENT_REJECT_MESSAGE_WITH_REASON_PREFIX, PLAN_REJECTION_PREFIX, DENIAL_WORKAROUND_GUIDANCE, NO_RESPONSE_REQUESTED, SYNTHETIC_TOOL_RESULT_PLACEHOLDER, AUTO_MODE_REJECTION_PREFIX, SYNTHETIC_MODEL, SYNTHETIC_MESSAGES, SEMA_RENDER_SIDECAR_KEYS, EMPTY_LOOKUPS, EMPTY_STRING_SET, STRIPPED_TAGS_RE, PLAN_PHASE4_CONTROL, PLAN_PHASE4_TRIM, PLAN_PHASE4_CUT, PLAN_PHASE4_CAP, init_messages4 = __esm({
433988
434070
  "build-src/src/utils/messages.ts"() {
434071
+ init_dist();
433989
434072
  init_systemReminderTag();
433990
434073
  init_isObject();
433991
434074
  init_last();
@@ -434058,6 +434141,12 @@ Rejected plan:
434058
434141
  REJECT_MESSAGE2,
434059
434142
  NO_RESPONSE_REQUESTED
434060
434143
  ]);
434144
+ SEMA_RENDER_SIDECAR_KEYS = [
434145
+ // 1.0.89 提货批族扫:键名走 client-core 包常量(铸点同一个符号),不再手抄字面量。
434146
+ // `_sema_degraded` 包侧今天没铸出公面常量 ⇒ 保留字面量,那一把到货再换(不硬凑)。
434147
+ SEMA_COLLATERAL_ABORT_KEY,
434148
+ "_sema_degraded"
434149
+ ];
434061
434150
  EMPTY_LOOKUPS = {
434062
434151
  siblingToolUseIDs: /* @__PURE__ */ new Map(),
434063
434152
  progressMessagesByToolUseID: /* @__PURE__ */ new Map(),
@@ -435336,6 +435425,8 @@ var import_react198, EMPTY2, current3, listeners8, init_enginePromptSuggestionsS
435336
435425
  // build-src/src/sema/upstreamBridge.ts
435337
435426
  var upstreamBridge_exports = {};
435338
435427
  __export(upstreamBridge_exports, {
435428
+ collateralAbortCallIdOf: () => collateralAbortCallIdOf,
435429
+ collateralAbortStampTargetOf: () => collateralAbortStampTargetOf,
435339
435430
  deriveNotificationResidualLines: () => deriveNotificationResidualLines,
435340
435431
  engineTaskNotificationDedupKey: () => taskNotificationDedupKeyFromWire,
435341
435432
  sdkMessagesToCcEvents: () => sdkMessagesToCcEvents,
@@ -435429,6 +435520,26 @@ function isTaskNotificationRow(msg) {
435429
435520
  function stampTranscriptArrival(msg) {
435430
435521
  typeof msg.timestamp != "string" && (msg.timestamp = (/* @__PURE__ */ new Date()).toISOString());
435431
435522
  }
435523
+ function collateralAbortCallIdOf(frame) {
435524
+ let f = frame;
435525
+ if (f === null || typeof f != "object" || f.type !== "tool_end_result" || f[SEMA_COLLATERAL_ABORT_KEY] !== !0) return;
435526
+ let id = f.toolCallId;
435527
+ if (!(typeof id != "string" || id.length === 0))
435528
+ return id.length <= MAX_COLLATERAL_ABORT_ID_CHARS ? id : void 0;
435529
+ }
435530
+ function collateralAbortStampTargetOf(msg) {
435531
+ let content = msg?.message?.content;
435532
+ if (!Array.isArray(content)) return;
435533
+ let found;
435534
+ for (let block2 of content) {
435535
+ let b3 = block2;
435536
+ if (b3?.type === "tool_result") {
435537
+ if (found !== void 0 || typeof b3.tool_use_id != "string" || b3.tool_use_id.length === 0) return;
435538
+ found = b3.tool_use_id;
435539
+ }
435540
+ }
435541
+ return found;
435542
+ }
435432
435543
  async function* sdkMessagesToCcEvents(sdk, meta3) {
435433
435544
  function streamEvt(event) {
435434
435545
  return {
@@ -435594,10 +435705,20 @@ async function* sdkMessagesToCcEvents(sdk, meta3) {
435594
435705
  rowYielded: !1
435595
435706
  });
435596
435707
  }
435597
- let macrotask = () => new Promise((resolve57) => setTimeout(resolve57, 0)), curFrame = null, curFrameType = null, curFrameNo = 0, guidanceLane = createToolEndGuidanceLane(), teed = (async function* () {
435708
+ let macrotask = () => new Promise((resolve57) => setTimeout(resolve57, 0)), curFrame = null, curFrameType = null, curFrameNo = 0, guidanceLane = createToolEndGuidanceLane(), collateralAbortCalls = /* @__PURE__ */ new Set();
435709
+ function noteCollateralAbortFrame(m2) {
435710
+ let id = collateralAbortCallIdOf(m2);
435711
+ id !== void 0 && (collateralAbortCalls.size >= MAX_COLLATERAL_ABORT_IDS || collateralAbortCalls.add(id));
435712
+ }
435713
+ function stampCollateralAbort(msg) {
435714
+ if (collateralAbortCalls.size === 0) return;
435715
+ let id = collateralAbortStampTargetOf(msg);
435716
+ id === void 0 || !collateralAbortCalls.has(id) || (msg[SEMA_COLLATERAL_ABORT_KEY] = !0);
435717
+ }
435718
+ let teed = (async function* () {
435598
435719
  for await (let raw2 of sdk) {
435599
435720
  let m2 = raw2;
435600
- curFrame = m2, curFrameType = typeof m2.type == "string" ? m2.type : null, curFrameNo++, observeFrame(m2), recordAgentToolStatsFromFrame(m2), guidanceLane.note(curFrameNo, m2), yield raw2;
435721
+ curFrame = m2, curFrameType = typeof m2.type == "string" ? m2.type : null, curFrameNo++, observeFrame(m2), recordAgentToolStatsFromFrame(m2), guidanceLane.note(curFrameNo, m2), noteCollateralAbortFrame(m2), yield raw2;
435601
435722
  }
435602
435723
  curFrame = null, curFrameType = null, curFrameNo++;
435603
435724
  })(), seenFrameNo = -1, prevFrameWasStreamEvent = !1, lastOutWasDelta = !1, macrotaskPending = !1;
@@ -435610,17 +435731,17 @@ async function* sdkMessagesToCcEvents(sdk, meta3) {
435610
435731
  let msg = reduceApiErrorEnvelope(out6.message);
435611
435732
  noteModelEvidenceFrom(msg);
435612
435733
  let w2 = notifWatch.get(seenFrameNo);
435613
- w2 !== void 0 && !w2.rowYielded && isTaskNotificationRow(msg) && (w2.rowYielded = !0), stampTranscriptArrival(msg), yield msg;
435734
+ w2 !== void 0 && !w2.rowYielded && isTaskNotificationRow(msg) && (w2.rowYielded = !0), stampTranscriptArrival(msg), stampCollateralAbort(msg), yield msg;
435614
435735
  } else
435615
435736
  for (let ev of consumeChrome(out6.event)) yield ev;
435616
435737
  lastOutWasDelta = isDelta;
435617
435738
  }
435618
435739
  seenFrameNo >= 0 && guidanceLane.flushFrame(seenFrameNo), notifEpilogue(Number.MAX_SAFE_INTEGER);
435619
435740
  } finally {
435620
- guidanceLane.discard(), noteBridgeDriveEnd();
435741
+ guidanceLane.discard(), collateralAbortCalls.clear(), noteBridgeDriveEnd();
435621
435742
  }
435622
435743
  }
435623
- var BRIDGE_PROBE_ENV, init_upstreamBridge = __esm({
435744
+ var BRIDGE_PROBE_ENV, MAX_COLLATERAL_ABORT_IDS, MAX_COLLATERAL_ABORT_ID_CHARS, init_upstreamBridge = __esm({
435624
435745
  "build-src/src/sema/upstreamBridge.ts"() {
435625
435746
  init_dist();
435626
435747
  init_bridgeAdapter();
@@ -435655,6 +435776,7 @@ var BRIDGE_PROBE_ENV, init_upstreamBridge = __esm({
435655
435776
  tick: "SEMA_TICK_PROBE",
435656
435777
  hook: "SEMA_HOOK_PROBE"
435657
435778
  };
435779
+ MAX_COLLATERAL_ABORT_IDS = 1024, MAX_COLLATERAL_ABORT_ID_CHARS = 256;
435658
435780
  }
435659
435781
  });
435660
435782
 
@@ -451695,7 +451817,7 @@ var init_strict_output_schema = __esm({
451695
451817
  // node_modules/@sema-agent/core/dist/core/mcp.js
451696
451818
  import { tmpdir as tmpdir13 } from "node:os";
451697
451819
  import { join as join170 } from "node:path";
451698
- var MCP_PREFIX, MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS, MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS, MCP_BLOB_DIR, init_mcp4 = __esm({
451820
+ var MCP_PREFIX, MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS, MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS, MCP_BLOB_DIR, LIST_ENTRY_FINGERPRINT_MAX_CHARS, LOOP_REWIND_FINGERPRINT_MAX_CHARS, init_mcp4 = __esm({
451699
451821
  "node_modules/@sema-agent/core/dist/core/mcp.js"() {
451700
451822
  init_protocol_table();
451701
451823
  init_protocol_naming();
@@ -451703,6 +451825,7 @@ var MCP_PREFIX, MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS, MCP_IDLE_TIMEOUT_HTTP_DEFAULT
451703
451825
  init_stdio3();
451704
451826
  init_streamableHttp();
451705
451827
  init_types12();
451828
+ init_protocol();
451706
451829
  init_image_downsample();
451707
451830
  init_surrogate_safe_slice();
451708
451831
  init_tool_errors();
@@ -451712,7 +451835,7 @@ var MCP_PREFIX, MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS, MCP_IDLE_TIMEOUT_HTTP_DEFAULT
451712
451835
  init_strict_output_schema();
451713
451836
  init_protocol_naming();
451714
451837
  init_image_downsample();
451715
- MCP_PREFIX = MCP_NAMESPACE.prefix, MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS = 30 * 6e4, MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS = 5 * 6e4, MCP_BLOB_DIR = join170(tmpdir13(), "sema-mcp-blobs");
451838
+ MCP_PREFIX = MCP_NAMESPACE.prefix, MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS = 30 * 6e4, MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS = 5 * 6e4, MCP_BLOB_DIR = join170(tmpdir13(), "sema-mcp-blobs"), LIST_ENTRY_FINGERPRINT_MAX_CHARS = 8 * 1024, LOOP_REWIND_FINGERPRINT_MAX_CHARS = 1024 * 1024;
451716
451839
  }
451717
451840
  });
451718
451841
 
@@ -451824,12 +451947,30 @@ function parseLeadingCommandName(command8, options) {
451824
451947
  reject: "shell operators (pipes, redirects, ;, &&, command substitution, subshells, variable expansion) are not allowed \u2014 run a single simple command"
451825
451948
  };
451826
451949
  let first = trimmed2.split(/\s+/)[0];
451827
- return first.includes("/") ? { reject: "the command must be a bare name resolved via PATH (no path prefix)" } : first.includes("=") ? { reject: "leading environment-variable assignments are not allowed" } : /["'{}*?[\]~]/.test(first) ? { reject: "the command name must be a bare token (no quotes, braces, globs, or ~)" } : { name: first };
451950
+ return first.includes("/") && options?.pathPrefixedNameIsText !== !0 ? { reject: "the command must be a bare name resolved via PATH (no path prefix)" } : first.includes("=") ? { reject: "leading environment-variable assignments are not allowed" } : /["'{}*?[\]~]/.test(first) ? { reject: "the command name must be a bare token (no quotes, braces, globs, or ~)" } : { name: first };
451951
+ }
451952
+ function splitShellCompoundSegments(source, options) {
451953
+ if (SHELL_SEGMENT_HARD_REJECT.test(source))
451954
+ return { reject: "redirection, command/variable substitution, subshells, escapes, and line breaks are not allowed" };
451955
+ let scanned = options?.trailingTerminator !== "keep" ? source.replace(/;\s*$/, "") : source, segments = [], pipeFed = [!1], mask = quoteMask(scanned), quoted = (i) => mask.balanced && mask.quoted[i] === !0, cur = "";
451956
+ for (let i = 0; i < scanned.length; i++) {
451957
+ let c2 = scanned[i];
451958
+ if (quoted(i))
451959
+ cur += c2;
451960
+ else if (c2 === "&")
451961
+ if (scanned[i + 1] === "&")
451962
+ segments.push(cur), cur = "", pipeFed.push(!1), i++;
451963
+ else
451964
+ return { reject: "`&` backgrounding is not allowed \u2014 a backgrounded process outlives the command" };
451965
+ else c2 === "|" ? scanned[i + 1] === "|" ? (segments.push(cur), cur = "", pipeFed.push(!1), i++) : (segments.push(cur), cur = "", pipeFed.push(!0)) : c2 === ";" ? (segments.push(cur), cur = "", pipeFed.push(!1)) : cur += c2;
451966
+ }
451967
+ return segments.push(cur), { segments, pipeFed };
451828
451968
  }
451829
- var SHELL_OPERATORS2, init_bash_readonly_classifier = __esm({
451969
+ var SHELL_OPERATORS2, SHELL_SEGMENT_HARD_REJECT, init_bash_readonly_classifier = __esm({
451830
451970
  "node_modules/@sema-agent/core/dist/tools/fs/bash-readonly-classifier.js"() {
451831
451971
  init_safety();
451832
451972
  SHELL_OPERATORS2 = /[;&|<>$()`\n\r\\]/;
451973
+ SHELL_SEGMENT_HARD_REJECT = /[<>$()`\n\r\\]/;
451833
451974
  }
451834
451975
  });
451835
451976
 
@@ -452282,6 +452423,25 @@ var init_retain_ledger = __esm({
452282
452423
  import { closeSync as closeSync14, constants as FS, existsSync as existsSync29, fstatSync as fstatSync4, linkSync as linkSync2, mkdirSync as mkdirSync23, openSync as openSync15, readFileSync as readFileSync40, readSync as readSync4, realpathSync as realpathSync12, renameSync as renameSync14, truncateSync, unlinkSync as unlinkSync12, writeFileSync as writeFileSync22, writeSync as writeSync8, fsyncSync as fsyncSync11 } from "node:fs";
452283
452424
  import { basename as basename51, dirname as dirname77, join as join171, resolve as resolvePath3 } from "node:path";
452284
452425
  import { createHash as createHash25, randomBytes as randomBytes25 } from "node:crypto";
452426
+ function writeThenLink(target, content) {
452427
+ ensureDir(dirnameOf(target));
452428
+ let tmp = `${target}.${process.pid}.${randomBytes25(6).toString("hex")}.tmp`, linked = !1;
452429
+ try {
452430
+ let fd2 = openSync15(tmp, FS.O_CREAT | FS.O_EXCL | FS.O_WRONLY, 384);
452431
+ try {
452432
+ writeFileSync22(fd2, content), fsyncSync11(fd2);
452433
+ } finally {
452434
+ closeSync14(fd2);
452435
+ }
452436
+ linkSync2(tmp, target), linked = !0;
452437
+ } finally {
452438
+ try {
452439
+ unlinkSync12(tmp);
452440
+ } catch {
452441
+ }
452442
+ }
452443
+ linked && fsyncDir(dirnameOf(target));
452444
+ }
452285
452445
  function ensureDir(dir) {
452286
452446
  mkdirSync23(dir, { recursive: !0, mode: 448 });
452287
452447
  }
@@ -452374,7 +452534,48 @@ function truncateTornTail(path28) {
452374
452534
  `);
452375
452535
  truncateSync(path28, lastNl === -1 ? 0 : Buffer.byteLength(raw2.slice(0, lastNl + 1), "utf8"));
452376
452536
  }
452377
- var AppendLog, init_fs_atomic = __esm({
452537
+ function acquireStoreDirLock(dir, opts) {
452538
+ let fileName = opts.fileName ?? "LOCK", lockPath2 = join171(dir, fileName), key = `${fileName}@${canonicalStoreKey(dir)}`, lease = (entry2) => {
452539
+ let released = !1;
452540
+ return {
452541
+ lockPath: lockPath2,
452542
+ release: () => {
452543
+ if (!released && (released = !0, heldStoreDirLocks.get(key) === entry2 && (entry2.refs -= 1, !(entry2.refs > 0)))) {
452544
+ if (entry2.lock.release()) {
452545
+ heldStoreDirLocks.delete(key);
452546
+ return;
452547
+ }
452548
+ console.warn(`[sema] file store: the ${opts.label} directory fence at ${lockPath2} could not be handed back \u2014 the removal could not be proven, so a lock file naming this live process (pid ${process.pid}) may still be there. While it is, another process opening that directory is refused by name; the next open of it in THIS process finishes the cleanup or refuses by name. After this process exits the file is reclaimed as a stale lock ONLY if it is readable and its directory writable \u2014 otherwise it has to be removed by hand.`);
452549
+ }
452550
+ }
452551
+ };
452552
+ }, existing = heldStoreDirLocks.get(key);
452553
+ if (existing !== void 0) {
452554
+ if (existing.refs > 0 || existing.lock.resumeAbandoned())
452555
+ return existing.refs += 1, lease(existing);
452556
+ heldStoreDirLocks.delete(key);
452557
+ }
452558
+ let lock2 = new BootLock(lockPath2);
452559
+ try {
452560
+ lock2.acquire();
452561
+ } catch (err8) {
452562
+ if (err8 instanceof FileStoreLockError) {
452563
+ let who = err8.ownerPid !== void 0 ? ` (pid ${err8.ownerPid})` : "", single = "This backend is SINGLE-WRITER per directory: a second process replays a private view (both sides can win a once-only decision) and its next compaction erases the records it never loaded.";
452564
+ throw new FileStoreLockError(err8.code, err8.code === "store.lock_unreadable" ? `file store: the ${opts.label} directory (${dir}) cannot be opened \u2014 ${err8.message}` : err8.code === "store.dir_claiming" ? `file store: another OS process${who} is claiming this data dir \u2014 it is reclaiming the ${opts.label} directory (${dir}) from a lock a crashed owner left behind. ${single} Retry in a moment: either that process finishes and owns the directory, or it dies and the next start reclaims it.` : `file store: another OS process${who} is using the ${opts.label} directory (${dir}). ${single} Stop the other process, point this one at a different data root, or use the pg/tidb backend for real multi-process concurrency.`, err8.code === "store.lock_unreadable" ? err8.lockPath : lockPath2, err8.ownerPid);
452565
+ }
452566
+ throw err8;
452567
+ }
452568
+ let entry = { lock: lock2, refs: 1 };
452569
+ return heldStoreDirLocks.set(key, entry), lease(entry);
452570
+ }
452571
+ function isProcessRunning4(pid) {
452572
+ try {
452573
+ return process.kill(pid, 0), !0;
452574
+ } catch (err8) {
452575
+ return err8.code === "EPERM";
452576
+ }
452577
+ }
452578
+ var AppendLog, FileStoreLockError, MAX_RECLAIM_DEPTH, MAX_ACQUIRE_ATTEMPTS, abandonedLocks, BootLock, heldStoreDirLocks, init_fs_atomic = __esm({
452378
452579
  "node_modules/@sema-agent/core/dist/stores/file/fs-atomic.js"() {
452379
452580
  AppendLog = class {
452380
452581
  fd;
@@ -452451,6 +452652,127 @@ var AppendLog, init_fs_atomic = __esm({
452451
452652
  }
452452
452653
  }
452453
452654
  };
452655
+ FileStoreLockError = class extends Error {
452656
+ code;
452657
+ lockPath;
452658
+ ownerPid;
452659
+ name = "FileStoreLockError";
452660
+ constructor(code2, message, lockPath2, ownerPid) {
452661
+ super(message), this.code = code2, this.lockPath = lockPath2, this.ownerPid = ownerPid;
452662
+ }
452663
+ }, MAX_RECLAIM_DEPTH = 8, MAX_ACQUIRE_ATTEMPTS = 4, abandonedLocks = /* @__PURE__ */ new Set(), BootLock = class _BootLock {
452664
+ lockPath;
452665
+ held = !1;
452666
+ spentVerdict;
452667
+ constructor(lockPath2) {
452668
+ this.lockPath = lockPath2;
452669
+ }
452670
+ acquire() {
452671
+ this.acquireWithin(0);
452672
+ }
452673
+ acquireWithin(depth) {
452674
+ for (let attempt = 0; attempt < MAX_ACQUIRE_ATTEMPTS; attempt++) {
452675
+ try {
452676
+ this.writeLock(), this.held = !0, abandonedLocks.delete(this.abandonKey);
452677
+ return;
452678
+ } catch (err8) {
452679
+ if (err8.code !== "EEXIST")
452680
+ throw err8;
452681
+ }
452682
+ let owner = this.inspectLock();
452683
+ if (owner.state === "absent")
452684
+ continue;
452685
+ if (owner.state === "unreadable")
452686
+ throw new FileStoreLockError("store.lock_unreadable", `file store: lock file at ${this.lockPath} is unreadable/corrupt; remove it manually if it is stale`, this.lockPath);
452687
+ if (owner.pid === process.pid && abandonedLocks.has(this.abandonKey)) {
452688
+ this.pruneOwnAbandoned();
452689
+ return;
452690
+ }
452691
+ if (isProcessRunning4(owner.pid))
452692
+ throw new FileStoreLockError("store.dir_in_use", `file store: another instance (pid ${owner.pid}) owns this data dir (${dirnameOf(this.lockPath)})`, this.lockPath, owner.pid);
452693
+ if (depth >= MAX_RECLAIM_DEPTH)
452694
+ throw new FileStoreLockError("store.lock_unreadable", `file store: lock file at ${this.lockPath} is buried under ${depth} nested stale reclaim gates (repeated crashes mid-reclaim); remove the ${basenameOf(this.lockPath)}.claim.* files manually`, this.lockPath);
452695
+ let gate = new _BootLock(`${this.lockPath}.claim.${owner.pid}`);
452696
+ try {
452697
+ gate.acquireWithin(depth + 1);
452698
+ } catch (err8) {
452699
+ throw err8 instanceof FileStoreLockError && err8.code === "store.dir_in_use" && err8.ownerPid !== void 0 ? new FileStoreLockError("store.dir_claiming", `file store: another instance${err8.ownerPid !== void 0 ? ` (pid ${err8.ownerPid})` : ""} is claiming this data dir (${dirnameOf(this.lockPath)}) from a stale lock`, this.lockPath, err8.ownerPid) : err8;
452700
+ }
452701
+ try {
452702
+ let under = this.inspectLock();
452703
+ if (under.state === "owner" && under.pid === owner.pid)
452704
+ try {
452705
+ unlinkSync12(this.lockPath);
452706
+ } catch {
452707
+ }
452708
+ else if (under.state !== "absent")
452709
+ continue;
452710
+ this.publishOrLoseRace();
452711
+ return;
452712
+ } finally {
452713
+ gate.release();
452714
+ }
452715
+ }
452716
+ throw new FileStoreLockError("store.dir_in_use", `file store: ownership of this data dir (${dirnameOf(this.lockPath)}) kept changing under us (${MAX_ACQUIRE_ATTEMPTS} attempts); another instance is starting and stopping on the same directory`, this.lockPath);
452717
+ }
452718
+ publishOrLoseRace() {
452719
+ try {
452720
+ this.writeLock(), this.held = !0, abandonedLocks.delete(this.abandonKey);
452721
+ } catch (err8) {
452722
+ throw err8.code === "EEXIST" ? new FileStoreLockError("store.dir_in_use", "file store: another instance won the lock race for this data dir", this.lockPath) : err8;
452723
+ }
452724
+ }
452725
+ pruneOwnAbandoned() {
452726
+ try {
452727
+ unlinkSync12(this.lockPath);
452728
+ } catch (err8) {
452729
+ throw new FileStoreLockError("store.lock_unreadable", `file store: this process still owns the lock file at ${this.lockPath} from an earlier release that could not remove it, and removing it now failed too (${String(err8.message)}); remove it manually`, this.lockPath, process.pid);
452730
+ }
452731
+ abandonedLocks.delete(this.abandonKey), this.publishOrLoseRace();
452732
+ }
452733
+ writeLock() {
452734
+ writeThenLink(this.lockPath, String(process.pid));
452735
+ }
452736
+ get abandonKey() {
452737
+ return resolvePath3(this.lockPath);
452738
+ }
452739
+ inspectLock() {
452740
+ let raw2;
452741
+ try {
452742
+ raw2 = readFileSync40(this.lockPath, "utf8").trim();
452743
+ } catch (err8) {
452744
+ return err8.code === "ENOENT" ? { state: "absent" } : { state: "unreadable" };
452745
+ }
452746
+ if (!/^[1-9][0-9]*$/.test(raw2))
452747
+ return { state: "unreadable" };
452748
+ let pid = Number.parseInt(raw2, 10);
452749
+ return Number.isInteger(pid) && pid > 0 ? { state: "owner", pid } : { state: "unreadable" };
452750
+ }
452751
+ release() {
452752
+ return this.held ? (this.held = !1, this.spentVerdict = this.removeOwnLock(), this.spentVerdict) : this.spentVerdict ?? !0;
452753
+ }
452754
+ removeOwnLock() {
452755
+ let before = this.inspectLock();
452756
+ if (before.state === "absent" || before.state === "owner" && before.pid !== process.pid)
452757
+ return abandonedLocks.delete(this.abandonKey), !0;
452758
+ if (before.state === "unreadable")
452759
+ return abandonedLocks.add(this.abandonKey), !1;
452760
+ try {
452761
+ unlinkSync12(this.lockPath);
452762
+ } catch {
452763
+ }
452764
+ let after = this.inspectLock(), gone = after.state === "absent" || after.state === "owner" && after.pid !== process.pid;
452765
+ return gone ? abandonedLocks.delete(this.abandonKey) : abandonedLocks.add(this.abandonKey), gone;
452766
+ }
452767
+ resumeAbandoned() {
452768
+ if (this.held)
452769
+ return !0;
452770
+ if (!abandonedLocks.has(this.abandonKey))
452771
+ return !1;
452772
+ let owner = this.inspectLock();
452773
+ return owner.state !== "owner" || owner.pid !== process.pid ? !1 : (this.held = !0, this.spentVerdict = void 0, abandonedLocks.delete(this.abandonKey), !0);
452774
+ }
452775
+ }, heldStoreDirLocks = /* @__PURE__ */ new Map();
452454
452776
  }
452455
452777
  });
452456
452778
 
@@ -453279,6 +453601,28 @@ function foldSpacing(s) {
453279
453601
  function reject2(code2, message) {
453280
453602
  return { reject: { code: code2, message } };
453281
453603
  }
453604
+ function commandBasename(name) {
453605
+ return name.slice(name.lastIndexOf("/") + 1);
453606
+ }
453607
+ function ruleLaneShapeOf(command8, reading) {
453608
+ let split = splitShellCompoundSegments(command8, { trailingTerminator: reading.terminator });
453609
+ if ("reject" in split)
453610
+ return { reject: split.reject };
453611
+ let names = [];
453612
+ for (let segment2 of split.segments) {
453613
+ let floor = parseLeadingCommandName(segment2, { ...RULE_LANE_FLOOR, quotedOperatorsAreText: reading.quotedOperatorsAreText });
453614
+ if ("reject" in floor)
453615
+ return {
453616
+ reject: split.segments.length > 1 ? `the segment "${escapeForDisclosure(segment2.trim())}" is not a single simple command (${floor.reject})` : floor.reject
453617
+ };
453618
+ if (SHELL_RESERVED_WORDS.has(floor.name))
453619
+ return {
453620
+ reject: `"${escapeForDisclosure(floor.name)}" is a shell keyword, not a command name \u2014 a control structure's real program is not named by the text this lane compares`
453621
+ };
453622
+ names.push(floor.name);
453623
+ }
453624
+ return { segments: split.segments, names };
453625
+ }
453282
453626
  function escapeForDisclosure(value) {
453283
453627
  let text2;
453284
453628
  try {
@@ -453305,11 +453649,14 @@ function parseAllowRuleText(text2, opts) {
453305
453649
  let content = parsed.ruleContent, prefixBody = /^(.+):\*$/.exec(content)?.[1], match = prefixBody !== void 0 ? "prefix" : "exact", body = prefixBody ?? content;
453306
453650
  if (body.includes("*"))
453307
453651
  return reject2("unsupported.wildcard", `wildcard rule forms are not supported in this version ("${text2}")`);
453308
- let floor = parseLeadingCommandName(body);
453309
- if ("reject" in floor)
453310
- return body.trim() === "" ? reject2("invalid.empty_command", `rule "${text2}" names no command`) : reject2("invalid.not_simple_command", `rule "${text2}" is not a single simple command (${floor.reject})`);
453311
- if (match === "prefix" && BARE_INTERPRETER_NAMES.has(floor.name) && opts?.direction !== "tighten")
453312
- return reject2("invalid.bare_interpreter_prefix", `prefix rule "${text2}" is headed by the interpreter "${floor.name}" \u2014 such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`);
453652
+ let shape = ruleLaneShapeOf(body, MATCH_READING);
453653
+ if ("reject" in shape)
453654
+ return body.trim() === "" ? reject2("invalid.empty_command", `rule "${text2}" names no command`) : reject2("invalid.not_simple_command", `rule "${text2}" is not a command this lane can name (${shape.reject})`);
453655
+ if (match === "prefix" && shape.segments.length > 1)
453656
+ return reject2("invalid.not_simple_command", `prefix rule "${text2}" names more than one command \u2014 a prefix admits anything appended to its body, so only the exact form may name a connector chain`);
453657
+ let head = commandBasename(shape.names[0] ?? "");
453658
+ if (match === "prefix" && BARE_INTERPRETER_NAMES.has(head) && opts?.direction !== "tighten")
453659
+ return reject2("invalid.bare_interpreter_prefix", `prefix rule "${text2}" is headed by the interpreter "${head}" \u2014 such a rule authorizes running arbitrary programs, which one approval click cannot be read as having granted (an exact rule naming the whole command line is accepted)`);
453313
453660
  let command8 = foldSpacing(body);
453314
453661
  return command8 === void 0 ? reject2("invalid.unbalanced_quotes", `rule "${text2}" has unbalanced quoting, so the command it names is not determinable`) : { rule: { rule: formatAllowRuleText(command8, match), tool: "Bash", match, command: command8 } };
453315
453662
  }
@@ -453319,19 +453666,11 @@ function formatAllowRuleText(command8, match) {
453319
453666
  function isRuleLive(rule) {
453320
453667
  return rule.adds.length > 0;
453321
453668
  }
453322
- var MAX_RULE_TEXT_CHARS, BARE_INTERPRETER_NAMES, SUGGESTION_LEXICON, LEXICON_BODIES, CONTROL_CHARS_RE, CONTROL_CHARS_GLOBAL_RE, DISCLOSED_RULE_TEXT_MAX_CHARS, init_permission_rule_model = __esm({
453669
+ var MAX_RULE_TEXT_CHARS, BARE_INTERPRETER_NAMES, SHELL_RESERVED_WORDS, SUGGESTION_LEXICON, LEXICON_BODIES, RULE_LANE_FLOOR, MATCH_READING, CONTROL_CHARS_RE, CONTROL_CHARS_GLOBAL_RE, DISCLOSED_RULE_TEXT_MAX_CHARS, init_permission_rule_model = __esm({
453323
453670
  "node_modules/@sema-agent/core/dist/core/permission-rule-model.js"() {
453324
453671
  init_permission_rules();
453325
453672
  init_bash_readonly_classifier();
453326
453673
  MAX_RULE_TEXT_CHARS = 512, BARE_INTERPRETER_NAMES = /* @__PURE__ */ new Set([
453327
- "bash",
453328
- "sh",
453329
- "zsh",
453330
- "ksh",
453331
- "csh",
453332
- "tcsh",
453333
- "dash",
453334
- "fish",
453335
453674
  "node",
453336
453675
  "deno",
453337
453676
  "bun",
@@ -453342,15 +453681,108 @@ var MAX_RULE_TEXT_CHARS, BARE_INTERPRETER_NAMES, SUGGESTION_LEXICON, LEXICON_BOD
453342
453681
  "ruby",
453343
453682
  "php",
453344
453683
  "osascript",
453345
- "env",
453684
+ "java",
453685
+ "dotnet",
453686
+ "mono",
453687
+ "lua",
453688
+ "luajit",
453689
+ "julia",
453690
+ "Rscript",
453691
+ "tclsh",
453692
+ "groovy",
453693
+ "scala",
453694
+ "awk",
453695
+ "gawk",
453696
+ "mawk",
453697
+ "nawk",
453698
+ "sed",
453699
+ "bash",
453700
+ "sh",
453701
+ "zsh",
453702
+ "ksh",
453703
+ "csh",
453704
+ "tcsh",
453705
+ "dash",
453706
+ "fish",
453707
+ "cmd",
453708
+ "powershell",
453709
+ "pwsh",
453710
+ "busybox",
453711
+ "source",
453712
+ ".",
453713
+ "trap",
453346
453714
  "eval",
453347
453715
  "exec",
453716
+ "command",
453717
+ "builtin",
453718
+ "enable",
453719
+ "compgen",
453720
+ "complete",
453721
+ "bind",
453722
+ "mapfile",
453723
+ "readarray",
453724
+ "env",
453725
+ "noglob",
453348
453726
  "xargs",
453349
- "nohup",
453350
453727
  "sudo",
453351
453728
  "doas",
453729
+ "pkexec",
453352
453730
  "su",
453353
- "ssh"
453731
+ "runuser",
453732
+ "ssh",
453733
+ "chroot",
453734
+ "setpriv",
453735
+ "nohup",
453736
+ "nice",
453737
+ "ionice",
453738
+ "chrt",
453739
+ "taskset",
453740
+ "stdbuf",
453741
+ "timeout",
453742
+ "time",
453743
+ "watch",
453744
+ "setsid",
453745
+ "flock",
453746
+ "unshare",
453747
+ "nsenter",
453748
+ "script",
453749
+ "numactl",
453750
+ "prlimit",
453751
+ "systemd-run",
453752
+ "parallel",
453753
+ "strace",
453754
+ "ltrace",
453755
+ "valgrind",
453756
+ "gdb",
453757
+ "lldb",
453758
+ "perf",
453759
+ "firejail",
453760
+ "bwrap"
453761
+ ]), SHELL_RESERVED_WORDS = /* @__PURE__ */ new Set([
453762
+ "!",
453763
+ "[[",
453764
+ "]]",
453765
+ "{",
453766
+ "}",
453767
+ "((",
453768
+ "))",
453769
+ "case",
453770
+ "esac",
453771
+ "coproc",
453772
+ "do",
453773
+ "done",
453774
+ "elif",
453775
+ "else",
453776
+ "fi",
453777
+ "for",
453778
+ "function",
453779
+ "if",
453780
+ "in",
453781
+ "select",
453782
+ "then",
453783
+ "time",
453784
+ "until",
453785
+ "while"
453354
453786
  ]), SUGGESTION_LEXICON = [
453355
453787
  "git status",
453356
453788
  "git log",
@@ -453478,6 +453910,7 @@ var MAX_RULE_TEXT_CHARS, BARE_INTERPRETER_NAMES, SUGGESTION_LEXICON, LEXICON_BOD
453478
453910
  "gh search prs",
453479
453911
  "gh search code"
453480
453912
  ], LEXICON_BODIES = SUGGESTION_LEXICON.map((b3) => b3.split(" "));
453913
+ RULE_LANE_FLOOR = { pathPrefixedNameIsText: !0 }, MATCH_READING = { terminator: "keep", quotedOperatorsAreText: !1 };
453481
453914
  CONTROL_CHARS_RE = /[\u0000-\u0008\u000A-\u001F\u007F-\u009F\p{Cf}\u2028\u2029]/u, CONTROL_CHARS_GLOBAL_RE = new RegExp(CONTROL_CHARS_RE.source, "gu"), DISCLOSED_RULE_TEXT_MAX_CHARS = 120;
453482
453915
  }
453483
453916
  });
@@ -455392,17 +455825,24 @@ var LedgerCore, SharedLedgerTable, init_shared_ledger = __esm({
455392
455825
  locks = /* @__PURE__ */ new Map();
455393
455826
  events = 0;
455394
455827
  handle;
455828
+ fence;
455829
+ released = !1;
455395
455830
  constructor(key, paths3, model, table) {
455396
455831
  this.key = key, this.paths = paths3, this.model = model, this.table = table;
455397
455832
  }
455398
455833
  bootstrap() {
455399
- for (let row2 of readJsonlRecords(this.paths.snapshotPath))
455400
- this.rows.set(this.model.keyOf(row2), row2);
455401
- let events3 = readJsonlRecords(this.paths.ledgerPath);
455402
- this.events = events3.length;
455403
- for (let ev of events3)
455404
- this.model.apply(this.rows, ev);
455405
- this.handle = new AppendLog(this.paths.ledgerPath);
455834
+ this.fence = acquireStoreDirLock(this.paths.dir, { label: this.model.label });
455835
+ try {
455836
+ for (let row2 of readJsonlRecords(this.paths.snapshotPath))
455837
+ this.rows.set(this.model.keyOf(row2), row2);
455838
+ let events3 = readJsonlRecords(this.paths.ledgerPath);
455839
+ this.events = events3.length;
455840
+ for (let ev of events3)
455841
+ this.model.apply(this.rows, ev);
455842
+ this.handle = new AppendLog(this.paths.ledgerPath);
455843
+ } catch (e) {
455844
+ throw this.fence.release(), this.fence = void 0, e;
455845
+ }
455406
455846
  }
455407
455847
  withLock(key, fn2) {
455408
455848
  let run2 = (this.locks.get(key) ?? Promise.resolve()).then(() => fn2(), () => fn2()), tail = run2.then(() => {
@@ -455420,13 +455860,15 @@ var LedgerCore, SharedLedgerTable, init_shared_ledger = __esm({
455420
455860
  }
455421
455861
  }
455422
455862
  compact() {
455863
+ if (this.released)
455864
+ throw new Error("file ledger: this authority was released (its store was closed) \u2014 refusing to compact a directory it no longer owns");
455423
455865
  let lines = [...this.rows.values()].map((r) => JSON.stringify(r)).join(`
455424
455866
  `);
455425
455867
  atomicWriteFile2(this.paths.tmpDir, this.paths.snapshotPath, lines.length ? `${lines}
455426
455868
  ` : ""), this.log.closeForSwap(), atomicWriteFile2(this.paths.tmpDir, this.paths.ledgerPath, ""), this.events = 0;
455427
455869
  }
455428
455870
  release() {
455429
- this.refs -= 1, !(this.refs > 0) && (this.table.get(this.key) === this && this.table.delete(this.key), this.handle?.close());
455871
+ this.refs -= 1, !(this.refs > 0) && (this.table.get(this.key) === this && this.table.delete(this.key), this.released = !0, this.handle?.close(), this.fence?.release(), this.fence = void 0);
455430
455872
  }
455431
455873
  get pendingEvents() {
455432
455874
  return this.events;
@@ -455508,6 +455950,7 @@ var CHECKPOINT_LEDGER_EVENT_REGISTRY, KNOWN_CHECKPOINT_LEDGER_EVENTS, checkpoint
455508
455950
  steer_append: !0
455509
455951
  }, KNOWN_CHECKPOINT_LEDGER_EVENTS = new Set(Object.keys(CHECKPOINT_LEDGER_EVENT_REGISTRY));
455510
455952
  checkpointLedgers = new SharedLedgerTable({
455953
+ label: "checkpoint ledger",
455511
455954
  keyOf: (cp2) => cp2.token,
455512
455955
  apply: applyCheckpointEvent
455513
455956
  });
@@ -455585,6 +456028,7 @@ var agentLedgers, FileBackgroundAgentStore, init_background_agent_store2 = __esm
455585
456028
  init_shared_ledger();
455586
456029
  init_marker();
455587
456030
  agentLedgers = new SharedLedgerTable({
456031
+ label: "background-agent ledger",
455588
456032
  keyOf: (r) => FileBackgroundAgentStore.key(r.handle, r.scope),
455589
456033
  apply: (rows2, ev) => {
455590
456034
  if (ev.t === "delete") {
@@ -456009,6 +456453,7 @@ var init_file2 = __esm({
456009
456453
  init_strategy_store2();
456010
456454
  init_fs_atomic();
456011
456455
  init_fs_atomic();
456456
+ init_fs_atomic();
456012
456457
  init_marker();
456013
456458
  init_adopt();
456014
456459
  }
@@ -456391,6 +456836,7 @@ var runLedgers, init_workflow_run_store2 = __esm({
456391
456836
  init_shared_ledger();
456392
456837
  init_marker();
456393
456838
  runLedgers = new SharedLedgerTable({
456839
+ label: "workflow-run ledger",
456394
456840
  keyOf: (run2) => run2.id,
456395
456841
  apply: (runs, ev) => {
456396
456842
  if (ev.t === "delete") {
@@ -502060,7 +502506,7 @@ function REPL({
502060
502506
  item.reject(new Error("Prompt cancelled by user"));
502061
502507
  setPromptQueue([]), abortController?.abort("user-cancel");
502062
502508
  } else activeRemote.isRemoteMode ? activeRemote.cancelRequest() : abortController?.abort("user-cancel");
502063
- setAbortController(null), mrOnTurnComplete(messagesRef.current, !0);
502509
+ activeRemote.isRemoteMode || bestEffortInteractiveCancel(), setAbortController(null), mrOnTurnComplete(messagesRef.current, !0);
502064
502510
  }
502065
502511
  }
502066
502512
  let handleQueuedCommandOnCancel = (0, import_react332.useCallback)(() => {
@@ -532303,7 +532749,7 @@ Usage: sema --remote "your task description"`, () => gracefulShutdown(1));
532303
532749
  pendingHookMessages
532304
532750
  }, renderAndRun);
532305
532751
  }
532306
- }).version("sema 1.0.88", "-v, --version", "Output the version number"), program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)"), program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux."), canUserConfigureAdvisor() && program2.addOption(new Option("--advisor <model>", "Enable the server-side advisor tool with the specified model (alias or full ID).").hideHelp()), program2.addOption(new Option("--bg, --background", "Start the session as a background agent and return immediately (manage with `sema agents`)")), program2.command("ps").description("List background sessions").action(async () => {
532752
+ }).version("sema 1.0.89", "-v, --version", "Output the version number"), program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)"), program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux."), canUserConfigureAdvisor() && program2.addOption(new Option("--advisor <model>", "Enable the server-side advisor tool with the specified model (alias or full ID).").hideHelp()), program2.addOption(new Option("--bg, --background", "Start the session as a background agent and return immediately (manage with `sema agents`)")), program2.command("ps").description("List background sessions").action(async () => {
532307
532753
  await (await Promise.resolve().then(() => (init_bg2(), bg_exports))).psHandler([]), process.exit(process.exitCode ?? 0);
532308
532754
  }), program2.command("logs [id]").description("Print a background session's recent terminal output").action(async (id) => {
532309
532755
  await (await Promise.resolve().then(() => (init_bg2(), bg_exports))).logsHandler(id, []), process.exit(process.exitCode ?? 0);
package/sema.js CHANGED
@@ -71,7 +71,7 @@ function installBootSignalTailFrame(argv2 = process.argv.slice(2)) {
71
71
  installBootSignalTailFrame();
72
72
 
73
73
  // build-src/src/sema/preludeEntry.ts
74
- var versionLine = "sema 1.0.88" ? "sema 1.0.88" : "";
74
+ var versionLine = "sema 1.0.89" ? "sema 1.0.89" : "";
75
75
  var argv = process.argv.slice(2);
76
76
  var wantsVersionFastPath = argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v");
77
77
  if (wantsVersionFastPath && versionLine) {