@sema-agent/core 7.38.2 → 7.39.0

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 (98) hide show
  1. package/CHANGELOG.md +175 -6
  2. package/dist/core/ask-origin.d.ts +16 -7
  3. package/dist/core/ask-origin.js +4 -2
  4. package/dist/core/auto-mode-directive.d.ts +114 -19
  5. package/dist/core/auto-mode-directive.js +67 -19
  6. package/dist/core/checkpoint-store.d.ts +72 -41
  7. package/dist/core/checkpoint-store.js +28 -48
  8. package/dist/core/classifier-unavailable-arm.js +2 -2
  9. package/dist/core/decision-receipt.d.ts +2 -0
  10. package/dist/core/decision-receipt.js +1 -1
  11. package/dist/core/engine-notice.d.ts +41 -17
  12. package/dist/core/engine-notice.js +26 -14
  13. package/dist/core/gate-outcome.d.ts +80 -42
  14. package/dist/core/gate-outcome.js +21 -6
  15. package/dist/core/governance-codes.d.ts +1 -3
  16. package/dist/core/governance-codes.js +0 -3
  17. package/dist/core/hooks.d.ts +1 -1
  18. package/dist/core/hooks.js +2 -2
  19. package/dist/core/human-input-projection.d.ts +12 -0
  20. package/dist/core/human-input-projection.js +12 -0
  21. package/dist/core/memory-engine/engine.js +11 -11
  22. package/dist/core/memory-engine/layout.d.ts +21 -2
  23. package/dist/core/memory-engine/layout.js +17 -5
  24. package/dist/core/mode-policy.d.ts +26 -0
  25. package/dist/core/mode-policy.js +37 -1
  26. package/dist/core/output-style.d.ts +21 -0
  27. package/dist/core/output-style.js +29 -0
  28. package/dist/core/real-approval.d.ts +25 -0
  29. package/dist/core/real-approval.js +27 -0
  30. package/dist/core/reminder-mint.d.ts +0 -9
  31. package/dist/core/reminder-mint.js +0 -11
  32. package/dist/core/retired-keys.d.ts +2 -1
  33. package/dist/core/retired-keys.js +3 -1
  34. package/dist/core/runner/assemble-result.d.ts +2 -0
  35. package/dist/core/runner/assemble-result.js +1 -1
  36. package/dist/core/runner/compaction-restate.d.ts +45 -4
  37. package/dist/core/runner/compaction-restate.js +89 -11
  38. package/dist/core/runner/contracts.d.ts +6 -6
  39. package/dist/core/runner/denial-limit-arms.js +4 -4
  40. package/dist/core/runner/inherited-ask-grants.d.ts +4 -2
  41. package/dist/core/runner/inherited-ask-grants.js +0 -3
  42. package/dist/core/runner/permission-rule-lanes.d.ts +18 -0
  43. package/dist/core/runner/permission-rule-lanes.js +35 -25
  44. package/dist/core/runner/prepare-context-lane.d.ts +1 -0
  45. package/dist/core/runner/prepare-context-lane.js +6 -0
  46. package/dist/core/runner/prepare-inherited-gate.js +2 -9
  47. package/dist/core/runner/prepare-task.js +10 -24
  48. package/dist/core/runner/prepare-wiring-manifest.js +2 -2
  49. package/dist/core/runner/prompt-block.d.ts +9 -0
  50. package/dist/core/runner/prompt-block.js +8 -0
  51. package/dist/core/runner/resume-admission.js +4 -1
  52. package/dist/core/runner/resume-apply.js +28 -18
  53. package/dist/core/runner/resume-policy-outcome.js +10 -4
  54. package/dist/core/runner/run-attachment-seats.js +1 -1
  55. package/dist/core/runner/run-identity-wiring.js +9 -2
  56. package/dist/core/runner/run-leg.js +42 -31
  57. package/dist/core/runner/run-settle-and-teardown.js +4 -3
  58. package/dist/core/runner/run-turn-boundary.js +4 -3
  59. package/dist/core/runner/runtask.d.ts +4 -2
  60. package/dist/core/runner/steer-admission.d.ts +9 -0
  61. package/dist/core/runner/steer-admission.js +8 -0
  62. package/dist/core/runner/stream-steer-verb.js +16 -11
  63. package/dist/core/runner/synthetic-tools.d.ts +4 -0
  64. package/dist/core/runner/tool-turn-repair-seat.js +1 -1
  65. package/dist/core/runner/turn-attachments.d.ts +16 -22
  66. package/dist/core/runner/turn-attachments.js +3 -20
  67. package/dist/core/runner-deps.d.ts +1 -1
  68. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +8 -0
  69. package/dist/core/store-contracts/checkpoint-store-contract.js +23 -0
  70. package/dist/core/task-event.d.ts +7 -2
  71. package/dist/core/task-spec.d.ts +29 -7
  72. package/dist/core/terminal-cause.d.ts +44 -3
  73. package/dist/core/terminal-cause.js +5 -0
  74. package/dist/core/tool-policy.d.ts +55 -29
  75. package/dist/core/tool-policy.js +42 -31
  76. package/dist/core/types.d.ts +1 -1
  77. package/dist/core/types.js +1 -1
  78. package/dist/core/wiring-manifest.d.ts +17 -3
  79. package/dist/core/wiring-manifest.js +8 -3
  80. package/dist/engine/harness/agent-harness.d.ts +10 -0
  81. package/dist/engine/harness/agent-harness.js +12 -1
  82. package/dist/engine/llm/tool-turns.d.ts +15 -2
  83. package/dist/engine/llm/tool-turns.js +2 -2
  84. package/dist/index.d.ts +4 -3
  85. package/dist/index.js +4 -3
  86. package/dist/prompt-assembly/event-registry.js +2 -1
  87. package/dist/stores/file/fs-atomic.d.ts +29 -4
  88. package/dist/stores/file/fs-atomic.js +55 -6
  89. package/dist/tools/fs/index.d.ts +2 -1
  90. package/dist/tools/fs/read-deny-home.d.ts +3 -2
  91. package/dist/tools/fs/read-deny-home.js +6 -4
  92. package/dist/tools/fs/shell-read-check.d.ts +10 -3
  93. package/dist/tools/fs/shell-read-check.js +2 -2
  94. package/package.json +1 -1
  95. package/test/export-surface.snapshot.json +21 -1
  96. package/wire-changes/core@7.38.2.json +20 -2
  97. package/wire-changes/core@7.38.3.json +36 -0
  98. package/wire-changes/core@7.39.0.json +845 -0
@@ -42,7 +42,9 @@ export function resolveSubagentTranscriptTier(agentStoreWired, sessionStore) {
42
42
  e.code = "config.store_durability_invalid";
43
43
  throw e;
44
44
  }
45
- export function deriveAskEffective(form, parkEffective) {
45
+ export function deriveAskEffective(form, parkEffective, mode) {
46
+ if (mode === "dontAsk")
47
+ return "auto_deny";
46
48
  switch (form) {
47
49
  case "callback":
48
50
  return "human_reachable";
@@ -59,6 +61,9 @@ export function deriveAskEffective(form, parkEffective) {
59
61
  }
60
62
  }
61
63
  }
64
+ export function askSeatEffective(manifest) {
65
+ return deriveAskEffective(manifest.ask.form, manifest.parkLane.effective);
66
+ }
62
67
  export function askReachesPerson(ask) {
63
68
  const effective = ask.effective ?? "unresolved";
64
69
  switch (effective) {
@@ -148,7 +153,7 @@ export function deriveWiringManifest(facts) {
148
153
  ask: {
149
154
  form: facts.askForm,
150
155
  ...(facts.askProvenance !== undefined ? { provenance: facts.askProvenance } : {}),
151
- ...(facts.half === "effective" ? { effective: deriveAskEffective(facts.askForm, parkLane.effective) } : {}),
156
+ ...(facts.half === "effective" ? { effective: deriveAskEffective(facts.askForm, parkLane.effective, facts.permissionMode?.effective) } : {}),
152
157
  },
153
158
  question: {
154
159
  wired: questionChannel,
@@ -200,7 +205,7 @@ export function deriveWiringManifest(facts) {
200
205
  const { provenance: _askSeat, ...askForHash } = assembly.ask;
201
206
  const { provenance: _questionSeat, ...questionForHash } = assembly.question;
202
207
  manifest.configFingerprint = createHash("sha256")
203
- .update(canonicalize({ ...assembly, ask: askForHash, question: questionForHash }))
208
+ .update(canonicalize({ ...assembly, ask: { ...askForHash, effective: deriveAskEffective(facts.askForm, parkLane.effective) }, question: questionForHash }))
204
209
  .digest("hex")
205
210
  .slice(0, 16);
206
211
  }
@@ -194,6 +194,9 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
194
194
  * with every request view that needed one (the transcript is never rewritten, so a broken turn is repaired, and
195
195
  * reported, on each request; the sink owns its dedup). Advisory: a throwing sink never breaks the request. */
196
196
  onToolTurnRepaired?: (repairs: readonly ToolTurnRepair[]) => void;
197
+ /** The assistant messages standing in the transcript when the current run began — what the request view reads as
198
+ * "before this run" (a message is the run's own unless it is in here). */
199
+ private runHistory?;
197
200
  /** backlog #259 — USER-authored queue remnants at agent_end: steers/follow-ups whose receipts said
198
201
  * "queued" but that no turn will ever drain (the run ended first). The engine-note sweep above
199
202
  * hands ENGINE payloads back for redelivery; user inputs have no redelivery semantics (a steer
@@ -212,6 +215,13 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
212
215
  * Advisory (swallow-guarded at the call sites via the runner's own closure); never re-entered
213
216
  * for the same payload — consumption deletes the sidecar entry in the same step. */
214
217
  onEngineNoteConsumed?: (payload: unknown) => void;
218
+ /** backlog #1273 — runner-set sink fired ONCE per CALLER frame at the moment the frame is in the transcript (its
219
+ * `message_end` append — the next request carries it), with the opaque park record the frame rode in on
220
+ * ({@link UserMessageProvenance.parkRecord}). The consumption point of a caller input, deliberately NOT the drain:
221
+ * a drained frame can still be dropped before its turn starts (a halt latched or an abort landing after the drain)
222
+ * and goes back into the park / undrained account, so a drain-time report would announce an input the model never
223
+ * saw. Advisory (swallow-guarded); the record is released in the same step, so a delivered frame reports once. */
224
+ onUserInputConsumed?: (parkRecord: unknown) => void;
215
225
  /** #1036 — THE RECEIPT SINK: a frame reached the TRANSCRIPT, reported at the `message_end` append
216
226
  * that proves it, with its carrier and entry id. Advisory. */
217
227
  onFrameCommitted?: (carrier: CommittedFrameCarrier, entryId: string) => void;
@@ -212,12 +212,14 @@ export class AgentHarness {
212
212
  thinkingLevel;
213
213
  onUndrainedEngineNotes;
214
214
  onToolTurnRepaired;
215
+ runHistory;
215
216
  onUndrainedUserInputs;
216
217
  onEngineNoteConsumed;
218
+ onUserInputConsumed;
217
219
  onFrameCommitted;
218
220
  engineInjectionsHeld;
219
221
  holdToolTurnLaw(messages) {
220
- const view = ensureToolTurnInvariants(messages);
222
+ const view = ensureToolTurnInvariants(messages, this.runHistory);
221
223
  if (view.repairs.length > 0) {
222
224
  try {
223
225
  this.onToolTurnRepaired?.(view.repairs);
@@ -603,7 +605,15 @@ export class AgentHarness {
603
605
  if (!this.drainedPendingInjection.includes(message))
604
606
  return;
605
607
  this.unbookInFlight([message]);
608
+ const parkRecord = userInputParkRecords.get(message);
606
609
  userInputParkRecords.delete(message);
610
+ if (parkRecord !== undefined) {
611
+ try {
612
+ this.onUserInputConsumed?.(parkRecord);
613
+ }
614
+ catch {
615
+ }
616
+ }
607
617
  if (entryId !== undefined) {
608
618
  const payload = engineNotePayloads.get(message);
609
619
  if (payload !== undefined) {
@@ -878,6 +888,7 @@ export class AgentHarness {
878
888
  async executeTurn(turnState, text, options, abortController) {
879
889
  let activeTurnState = turnState;
880
890
  const userMessage = createUserMessage(text, options?.images, options);
891
+ this.runHistory = new WeakSet(turnState.messages.filter((m) => m.role === "assistant"));
881
892
  const beforeResult = await this.emitHook({
882
893
  type: "before_agent_start",
883
894
  prompt: text,
@@ -43,6 +43,13 @@
43
43
  * wording, whatever the tool. (The persisted reconcile refines with the run's tool effects — a read-only or
44
44
  * idempotent call's close says it may simply be re-issued — and, on a live abort, with the calls it saw start.)
45
45
  * Returns the SAME array when nothing needed restoring.
46
+ *
47
+ * RUN OWNERSHIP. The caller may hand over the assistant messages that stood in its transcript before its run began
48
+ * (`priorMessages`, by object). A `missing_result` repair on one of them is marked `beforeRun`: the call went
49
+ * unanswered before this run started, so closing it on the wire is a fact about the transcript the run inherited, not
50
+ * an event of the run. Membership is by message, never by position — a request projection that inserts, drops or
51
+ * replaces other messages cannot move it. The mark says that and nothing else: the repair, the synthesized result and
52
+ * the operator's account of them are the same with or without it. No `priorMessages` ⇒ no mark.
46
53
  */
47
54
  import type { AssertAllKeysHandled } from "../../core/ask-origin.js";
48
55
  import type { AssistantMessageDiagnostic } from "./diagnostics.js";
@@ -97,6 +104,9 @@ export interface ToolTurnRepair {
97
104
  * ids a later call of the message repeated; for `orphan_result` the ids of the results left out, once each, in
98
105
  * transcript order; otherwise the calls whose result was moved up, synthesized, or folded with a second one. */
99
106
  readonly toolCallIds: readonly string[];
107
+ /** `missing_result` only, and only when the caller handed over its run's prior messages
108
+ * ({@link ensureToolTurnInvariants}'s `priorMessages`): the message holding these calls is one of them. */
109
+ readonly beforeRun?: true;
100
110
  }
101
111
  export interface ToolTurnView {
102
112
  /** The request view — the input array itself when {@link repairs} is empty. */
@@ -105,5 +115,8 @@ export interface ToolTurnView {
105
115
  }
106
116
  /** The text standing between two results recorded for one call when the view folds them into one. */
107
117
  export declare const SECOND_RESULT_SEPARATOR = "[another result was recorded for this same call:]";
108
- /** Restore the tool-turn law on a provider-bound message list (see the module header). */
109
- export declare function ensureToolTurnInvariants(messages: Message[]): ToolTurnView;
118
+ /** Restore the tool-turn law on a provider-bound message list (see the module header). `priorMessages` = the calling
119
+ * run's inherited assistant messages (module header, RUN OWNERSHIP). */
120
+ export declare function ensureToolTurnInvariants(messages: Message[], priorMessages?: {
121
+ has(message: object): boolean;
122
+ }): ToolTurnView;
@@ -47,7 +47,7 @@ function callsOf(m) {
47
47
  return m.role === "assistant" && Array.isArray(m.content) ? m.content.filter((c) => c?.type === "toolCall") : [];
48
48
  }
49
49
  export const SECOND_RESULT_SEPARATOR = "[another result was recorded for this same call:]";
50
- export function ensureToolTurnInvariants(messages) {
50
+ export function ensureToolTurnInvariants(messages, priorMessages) {
51
51
  const anchor = [];
52
52
  messages.forEach((m, i) => anchor.push(m.role === "toolResult" && i > 0 ? anchor[i - 1] : i));
53
53
  const sites = new Map();
@@ -132,7 +132,7 @@ export function ensureToolTurnInvariants(messages) {
132
132
  if (late.length > 0)
133
133
  repairs.push({ form: "late_result", toolCallIds: inCallOrder(late) });
134
134
  if (missing.length > 0)
135
- repairs.push({ form: "missing_result", toolCallIds: missing.map((c) => c.id) });
135
+ repairs.push({ form: "missing_result", toolCallIds: missing.map((c) => c.id), ...(priorMessages?.has(m) === true ? { beforeRun: true } : {}) });
136
136
  if (duplicate.length > 0)
137
137
  repairs.push({ form: "duplicate_result", toolCallIds: inCallOrder(duplicate) });
138
138
  });
package/dist/index.d.ts CHANGED
@@ -66,7 +66,7 @@ export { ASK_UNRESOLVABLE_CAUSES } from "./core/ask-unresolvable-notice.js";
66
66
  export { USER_STEER_UNDRAINED_CAUSES, type UserSteerUndrainedCause } from "./core/engine-notice.js";
67
67
  export { TOOL_TURN_INCOMPLETE_CAUSES, type ToolTurnIncompleteCause } from "./internal/llm.js";
68
68
  export { DURABLE_MANDATE_SOURCES } from "./core/tool-policy.js";
69
- export { TERMINAL_CAUSE_IS_REPLAYABLE, type TerminalFailureDetail } from "./core/terminal-cause.js";
69
+ export { TERMINAL_CAUSE_IS_REPLAYABLE, type TerminalFailureDetail, type TerminalBlockedDetail, PROMPT_BLOCK_CAUSES, type PromptBlockCause, isPromptBlockCause } from "./core/terminal-cause.js";
70
70
  export { RESERVED_AGENT_NAMES } from "./agents/subagent.js";
71
71
  export { WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS } from "./orchestration/workflow-size-guideline.js";
72
72
  export { ENGINE_AUTHORITY_ENVELOPE_TAGS } from "./core/untrusted-text.js";
@@ -191,14 +191,14 @@ export { createAllowDenyPolicy, createApprovalPolicy,
191
191
  * members that seat has no lane for (`"unavailable"`, a routing answer; `updatedInput`, an operator edit a
192
192
  * policy verdict cannot get re-adjudicated). Exported so a host can NAME it instead of deriving it through
193
193
  * `Parameters<typeof createApprovalPolicy>[0]["approve"]`. */
194
- type ApprovalSeatOutcome, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathDenyRules, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
194
+ type ApprovalSeatOutcome, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, receiveHookResult, isEngineRefusal, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathDenyRules, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
195
195
  export { type ReadRootGrantScreen } from "./core/read-root-grant-screen.js";
196
196
  export { type AskOrigin, ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, type PersistedRuleMandate, isPersistedRuleMandate, screenAskClassMembers } from "./core/ask-origin.js";
197
197
  export { PERMISSION_MODES, type PermissionMode, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, type AskClass, isAskClass, MODE_ARBITRATION, type ModeArbitrationRow, RELEASABLE_ASK_CLASSES, MODE_RELEASE_REASONS, type ModeReleaseReason, isModeReleaseReason, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, type AskClassification, type AskClassFacts, type ToolMarkSource, arbitrateByMode, type Arbitration, fileCommandOptionsSentence } from "./core/ask-origin.js";
198
198
  export { modePolicyOf, type ModePolicy, PERMISSION_MODE_FOLD_REASONS, type PermissionModeFoldReason, type PermissionModeReceipt, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
199
199
  export { readDenyClassifierRules, renderClassifierDenyRules, type DerivedDenyRule } from "./core/auto-mode-prompt.js";
200
200
  export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource, type AutoModeDenySource } from "./core/runtime-caps.js";
201
- export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, type ClassifierDenyCause, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, type GateDisposition, type GateOutcome, type ModeRelease, type ModeRefusal, screenGateOutcome } from "./core/gate-outcome.js";
201
+ export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_PARTIES, type SettlementParty, isSettlementParty, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, type ClassifierDenyCause, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, type GateDisposition, type GateOutcome, type ModeRelease, type ModeRefusal, screenGateOutcome } from "./core/gate-outcome.js";
202
202
  export { type AskCarry } from "./core/hooks.js";
203
203
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps, type SwappableDepSeat, type SwappableDeps } from "./core/swappable-deps.js";
204
204
  export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, classifierParseFailureDenyMessage, AUTO_MODE_PARSE_FAILURES, isAutoModeParseFailure, type AutoModeParseFailure, type AutoModeUnavailableCause, AUTO_MODE_CLASSIFIER_FALLBACKS, isAutoModeClassifierFallback, AUTO_MODE_FALLBACK_CAUSES, isAutoModeFallbackCause, type AutoModeClassifierFallback, type AutoModeFallbackCause, type AutoModeClassifierRound, type AutoModeLadderRung, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassified, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitCounts, type DenialLimitFallbackFace, type UnarmedDenialLimitFallback, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
@@ -264,6 +264,7 @@ export { buildTurnPromptSnapshot, loweringRecordFor, LOWERING_VERSION, PREFIX_IN
264
264
  export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, type PromptArtifactSource, type PromptArtifactStore, type PromptSourceState, type PromptSourceStateStore, } from "./prompt-assembly/artifact-store.js";
265
265
  export { stripEngineMetadata } from "./internal/llm.js";
266
266
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn, type CompiledEventPrompt, type EventDedupe, type EventDefaultPolicy } from "./prompt-assembly/event-registry.js";
267
+ export { OUTPUT_STYLE_NAME_MAX } from "./core/output-style.js";
267
268
  export { contextUsageSections, type ContextUsageSection } from "./prompt-assembly/section-usage.js";
268
269
  export type { CompiledSection, ComposedPrompt, PackSectionDeclaration, PromptCacheClass, PromptCarrier, PromptMutability, PromptOwner, PromptPack, PromptRenderCadence, PromptRuntimeFacts, PromptSlot, PromptTrust, SectionRenderInputs, } from "./prompt-assembly/types.js";
269
270
  export { type ReasoningIntensity, type ReasoningResolution, type ResolvedReasoning, type ReasoningFormat, DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffSpelled, declaredOffSpelling, type OffCapabilityModel, resolveReasoningProfile, type ReasoningTier, type ReasoningProfileFlags, } from "./brain/reasoning.js";
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ export { ASK_UNRESOLVABLE_CAUSES } from "./core/ask-unresolvable-notice.js";
52
52
  export { USER_STEER_UNDRAINED_CAUSES } from "./core/engine-notice.js";
53
53
  export { TOOL_TURN_INCOMPLETE_CAUSES } from "./internal/llm.js";
54
54
  export { DURABLE_MANDATE_SOURCES } from "./core/tool-policy.js";
55
- export { TERMINAL_CAUSE_IS_REPLAYABLE } from "./core/terminal-cause.js";
55
+ export { TERMINAL_CAUSE_IS_REPLAYABLE, PROMPT_BLOCK_CAUSES, isPromptBlockCause } from "./core/terminal-cause.js";
56
56
  export { RESERVED_AGENT_NAMES } from "./agents/subagent.js";
57
57
  export { WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS } from "./orchestration/workflow-size-guideline.js";
58
58
  export { ENGINE_AUTHORITY_ENVELOPE_TAGS } from "./core/untrusted-text.js";
@@ -156,14 +156,14 @@ export { hasScheduler, isValidCronExpr, SchedulerError } from "./core/scheduler.
156
156
  export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
157
157
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
158
158
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
159
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathDenyRules, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
159
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, receiveHookResult, isEngineRefusal, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathDenyRules, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
160
160
  export {} from "./core/read-root-grant-screen.js";
161
161
  export { ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, isPersistedRuleMandate, screenAskClassMembers } from "./core/ask-origin.js";
162
162
  export { PERMISSION_MODES, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, isAskClass, MODE_ARBITRATION, RELEASABLE_ASK_CLASSES, MODE_RELEASE_REASONS, isModeReleaseReason, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, arbitrateByMode, fileCommandOptionsSentence } from "./core/ask-origin.js";
163
163
  export { modePolicyOf, PERMISSION_MODE_FOLD_REASONS, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
164
164
  export { readDenyClassifierRules, renderClassifierDenyRules } from "./core/auto-mode-prompt.js";
165
165
  export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./core/runtime-caps.js";
166
- export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, screenGateOutcome } from "./core/gate-outcome.js";
166
+ export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_PARTIES, isSettlementParty, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, screenGateOutcome } from "./core/gate-outcome.js";
167
167
  export {} from "./core/hooks.js";
168
168
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps } from "./core/swappable-deps.js";
169
169
  export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, classifierParseFailureDenyMessage, AUTO_MODE_PARSE_FAILURES, isAutoModeParseFailure, AUTO_MODE_CLASSIFIER_FALLBACKS, isAutoModeClassifierFallback, AUTO_MODE_FALLBACK_CAUSES, isAutoModeFallbackCause, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, } from "./core/auto-mode.js";
@@ -212,6 +212,7 @@ export { buildTurnPromptSnapshot, loweringRecordFor, LOWERING_VERSION, PREFIX_IN
212
212
  export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, } from "./prompt-assembly/artifact-store.js";
213
213
  export { stripEngineMetadata } from "./internal/llm.js";
214
214
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn } from "./prompt-assembly/event-registry.js";
215
+ export { OUTPUT_STYLE_NAME_MAX } from "./core/output-style.js";
215
216
  export { contextUsageSections } from "./prompt-assembly/section-usage.js";
216
217
  export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffSpelled, declaredOffSpelling, resolveReasoningProfile, } from "./brain/reasoning.js";
217
218
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, } from "./scenarios/scenario-registry.js";
@@ -3,7 +3,7 @@ export const EVENT_PROMPT_REGISTRY = new Map([
3
3
  { kind: "task_reminder", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#TASK_REMINDER_BASE" },
4
4
  { kind: "tool_search_usage_reminder", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderToolSearchUsageReminder" },
5
5
  { kind: "changed_files", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderChangedFiles" },
6
- { kind: "plan_mode", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#PLAN_MODE_FULL_BODY" },
6
+ { kind: "output_style", carrier: "message.user-prefix", trust: "operator", dedupe: "repeat", defaultPolicy: "always", rendererRef: "../output-style.ts#renderOutputStyle" },
7
7
  { kind: "date_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "turn-attachments.ts#renderDateChange" },
8
8
  { kind: "instructions_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", maxBytes: 512, defaultPolicy: "always", rendererRef: "turn-attachments.ts#collectInstructionsChange" },
9
9
  { kind: "workflow_size_guideline_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "run-turn-boundary.ts#workflowSizeGuidelineChangeNotice" },
@@ -16,6 +16,7 @@ export const EVENT_PROMPT_REGISTRY = new Map([
16
16
  { kind: "mcp_instructions", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpInstructionsDelta" },
17
17
  { kind: "mcp_dropped_tools", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpDroppedTools" },
18
18
  { kind: "git_status", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "git-status-frame.ts#renderGitStatusFrameBody" },
19
+ { kind: "permission_posture", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "../auto-mode-directive.ts#POSTURE_TABLE" },
19
20
  { kind: "limit_approach_converge", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "run-turn-boundary.ts#limitApproachFrames" },
20
21
  { kind: "limit_approach_deliver", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "run-turn-boundary.ts#limitApproachFrames" },
21
22
  { kind: "final_verification", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#final-verification" },
@@ -133,10 +133,11 @@ export declare class AppendLog {
133
133
  * onto the remnant, so replay dropped BOTH — after that second append had returned and fsync'd. Measured:
134
134
  * records [1,2,3] + a torn remnant + appends 5,6 replayed as [1,2,3,6].
135
135
  *
136
- * Cost on the healthy path is ONE `fstat` per append (the sizes match and we return); the whole-file read
137
- * only happens when the file actually moved off a boundary. A legitimate concurrent appender (another
138
- * process under the boot-lock handoff) leaves the file newline-terminated, so it is adopted, not truncated; a
139
- * whole record it left without its newline is kept and ended, and only a partial one is cut.
136
+ * Cost on the healthy path is ONE `fstat` per append (the sizes match and we return); the tail read only
137
+ * happens when the file actually moved off a boundary, and it reads the last segment, not the file. A
138
+ * legitimate concurrent appender (another process under the boot-lock handoff) leaves the file
139
+ * newline-terminated, so it is adopted, not truncated; a whole record it left without its newline is kept
140
+ * and ended, and only a partial one is cut.
140
141
  */
141
142
  private repairTail;
142
143
  /**
@@ -171,6 +172,30 @@ export declare class AppendLog {
171
172
  closeForSwap(): void;
172
173
  private releaseFd;
173
174
  }
175
+ /** Bytes read per positioned read while a file's tail is walked back to its last newline. */
176
+ export declare const JSONL_TAIL_READ_BLOCK: number;
177
+ /**
178
+ * The longest last segment (in BYTES) a repair station will judge: V8's maximum string length in UTF-16 code units
179
+ * (Node's `buffer.constants.MAX_STRING_LENGTH`). A segment of N bytes decodes to at most N code units, so one within
180
+ * the bound can never make `toString` throw. One past it MAY still decode (multi-byte text has fewer units than
181
+ * bytes) but is left unjudged either way: telling would mean reading and decoding up to three times the bound.
182
+ */
183
+ export declare const JSONL_TAIL_SEGMENT_LIMIT: number;
184
+ /** A file's last segment (the bytes after its last `\n`, or the whole file when it holds none) — or the fact that it is
185
+ * too long to judge. `start` is the segment's offset; a file that ends on a newline has an empty segment at `size`. */
186
+ export type JsonlTailSegment = {
187
+ unjudgeable: false;
188
+ start: number;
189
+ bytes: Buffer;
190
+ } | {
191
+ unjudgeable: true;
192
+ };
193
+ /**
194
+ * The last segment of an open file of `size` bytes, found by walking back from the end one block at a time to the
195
+ * last newline: a read is bounded by the segment's length, never by the file's. The one reader of "what is the tail"
196
+ * for every repair station; whether the segment is a record is {@link jsonlTextFormOf}'s to say.
197
+ */
198
+ export declare function readTailSegment(fd: number, size: number, limit?: number): JsonlTailSegment;
174
199
  /** Why a {@link BootLock}/{@link acquireStoreDirLock} acquisition was refused, as a branchable code. */
175
200
  export type FileStoreLockErrorCode =
176
201
  /** A LIVE process (this one or another) already holds the directory. Steady state: it ends when that
@@ -1,4 +1,5 @@
1
1
  import { appendFileSync, closeSync, constants as FS, existsSync, fstatSync, ftruncateSync, linkSync, mkdirSync, openSync, readFileSync, readSync, renameSync, truncateSync, unlinkSync, writeFileSync, writeSync, fsyncSync, } from "node:fs";
2
+ import { constants as bufferConstants } from "node:buffer";
2
3
  import { homedir, hostname, uptime } from "node:os";
3
4
  import { hrtime } from "node:process";
4
5
  import { basename, dirname, join, resolve as resolvePath } from "node:path";
@@ -285,23 +286,71 @@ export class AppendLog {
285
286
  }
286
287
  }
287
288
  }
289
+ export const JSONL_TAIL_READ_BLOCK = 64 * 1024;
290
+ export const JSONL_TAIL_SEGMENT_LIMIT = bufferConstants.MAX_STRING_LENGTH;
291
+ function readExactly(fd, into, length, position) {
292
+ let got = 0;
293
+ while (got < length) {
294
+ const n = readSync(fd, into, got, length - got, position + got);
295
+ if (n <= 0)
296
+ throw new Error(`jsonl tail: the file ended ${length - got} bytes early while its last line was read — it shrank under the reader`);
297
+ got += n;
298
+ }
299
+ }
300
+ export function readTailSegment(fd, size, limit = JSONL_TAIL_SEGMENT_LIMIT) {
301
+ const block = Buffer.allocUnsafe(Math.min(JSONL_TAIL_READ_BLOCK, size));
302
+ let start = 0;
303
+ for (let end = size; end > 0;) {
304
+ const from = Math.max(0, end - JSONL_TAIL_READ_BLOCK);
305
+ readExactly(fd, block, end - from, from);
306
+ const newline = block.subarray(0, end - from).lastIndexOf(0x0a);
307
+ if (newline >= 0) {
308
+ start = from + newline + 1;
309
+ break;
310
+ }
311
+ end = from;
312
+ if (size - end > limit)
313
+ return { unjudgeable: true };
314
+ }
315
+ if (size - start > limit)
316
+ return { unjudgeable: true };
317
+ const bytes = Buffer.allocUnsafe(size - start);
318
+ readExactly(fd, bytes, bytes.length, start);
319
+ return { unjudgeable: false, start, bytes };
320
+ }
288
321
  function repairJsonlTail(path) {
289
- let bytes;
322
+ let fd;
290
323
  try {
291
- bytes = readFileSync(path);
324
+ fd = openSync(path, "r");
292
325
  }
293
326
  catch (err) {
294
327
  if (err.code === "ENOENT")
295
328
  return;
296
329
  throw err;
297
330
  }
298
- if (bytes.length === 0 || bytes[bytes.length - 1] === 0x0a)
331
+ let segment;
332
+ try {
333
+ segment = readTailSegment(fd, fstatSync(fd).size);
334
+ }
335
+ finally {
336
+ closeSync(fd);
337
+ }
338
+ if (segment.unjudgeable) {
339
+ appendFileSync(path, "\n");
340
+ try {
341
+ console.warn(`[sema] file store: a tail segment larger than the judgeable bound (${JSONL_TAIL_SEGMENT_LIMIT} bytes) was ended as a line, not judged — ` +
342
+ `${path}. Its bytes are untouched; a reader meets it as an ordinary line.`);
343
+ }
344
+ catch {
345
+ }
346
+ return;
347
+ }
348
+ if (segment.bytes.length === 0)
299
349
  return;
300
- const segmentStart = bytes.lastIndexOf(0x0a) + 1;
301
- if (jsonlTextFormOf(bytes.toString("utf8", segmentStart)).form === "record")
350
+ if (jsonlTextFormOf(segment.bytes.toString("utf8")).form === "record")
302
351
  appendFileSync(path, "\n");
303
352
  else
304
- truncateSync(path, segmentStart);
353
+ truncateSync(path, segment.start);
305
354
  }
306
355
  export class FileStoreLockError extends Error {
307
356
  code;
@@ -192,7 +192,8 @@ export interface HandsToolkitOptions {
192
192
  * not asked and no home is added (each listed home is anchored at its spelling and, where the environment answers one,
193
193
  * its canonical spelling), and the band's home views (a path spelled through a firmlink or a bind mount of a home,
194
194
  * judged at the home) read this list and the environment's declared home; an entry that is not the declared home is probed once,
195
- * under a short bound of its own, and a probe of it that fails costs that entry alone. */
195
+ * under a short bound of its own, and a probe of it that fails costs that entry alone. The public shell judgment takes the
196
+ * same list as {@link import("./shell-read-check.js").ShellReadJudgeInput.readDenyHomes}. */
196
197
  readDenyHomes?: readonly string[];
197
198
  /** Where each home-anchored row's literal head leads beyond the home's spellings
198
199
  * ({@link import("./read-deny-home.js").readDenyHomeLinksOf}, keyed by the row's pattern) — the Runner passes the
@@ -100,8 +100,9 @@ export declare function placementViewsOf(homes: readonly string[]): HomeViewSour
100
100
  * time: no latch). */
101
101
  export declare function sayPlacementLost(): void;
102
102
  /** The home views over a home list fixed now — a mount that places nothing itself (a toolkit handed its placement, or
103
- * one whose table has no home-anchored row) and the public shell judgment: the declared home, and every entry of
104
- * `homes` that is not the declared home as an auxiliary home, read once, at once. */
103
+ * one whose table has no home-anchored row) and the public shell judgment (the `readDenyHomes` its boundary's rows were
104
+ * compiled under): the declared home, and every entry of `homes` that is not the declared home as an auxiliary home,
105
+ * read once, at once. */
105
106
  export declare function homeViewsAt(env: HomeViewEnv, homes: readonly string[]): HomeViewSource;
106
107
  /** The home views of one judgment, off the judge it judges with: the views of the judge's placement. A judge that
107
108
  * carries none (one built outside a mount) reads the declared home alone. */
@@ -312,10 +312,12 @@ export function sayPlacementLost() {
312
312
  catch {
313
313
  }
314
314
  }
315
- export function homeViewsAt(env, homes) {
315
+ function readAuxiliaryHomes(env, homes, opts, signal) {
316
316
  const declared = declaredHomeOf(env);
317
- const auxiliary = [...new Set(homes.filter((h) => isAbsolutePathForm(h) && !isDeclared(declared, h)))];
318
- return homeViewSourceOver(env, auxiliary.map((home) => readAuxiliaryHome(env, home, {})));
317
+ return [...new Set(homes.filter((h) => isAbsolutePathForm(h) && !isDeclared(declared, h)))].map((home) => readAuxiliaryHome(env, home, opts, signal));
318
+ }
319
+ export function homeViewsAt(env, homes) {
320
+ return homeViewSourceOver(env, readAuxiliaryHomes(env, homes, {}));
319
321
  }
320
322
  export function homeViewsOf(env, judge, signal) {
321
323
  return (judge?.homeViews ?? homeViewsAt(env, [])).views(signal);
@@ -338,7 +340,7 @@ export async function placeReadDenyAtHome(env, compile, homes, signal) {
338
340
  return heldAnywhere(false);
339
341
  declaredAt = [...new Set([...own, ...ownSpellings.spellings])];
340
342
  }
341
- const auxiliary = listed.homes.filter((h) => !isDeclared(declared, h)).map((home) => readAuxiliaryHome(env, home, { entries }, signal));
343
+ const auxiliary = readAuxiliaryHomes(env, listed.homes, { entries }, signal);
342
344
  const [heads, readings] = await Promise.all([readDenyHomeLinksOf(env, entries, declaredAt, signal), Promise.all(auxiliary)]);
343
345
  const at = [...new Set([...declaredAt, ...readings.flatMap((r) => r.place)])];
344
346
  const linked = new Map();
@@ -163,6 +163,12 @@ export interface ShellReadJudgeInput {
163
163
  readonly readers?: ReadonlySet<string>;
164
164
  /** The face named in a refusal sentence. Defaults to `Bash`. */
165
165
  readonly tool?: string;
166
+ /** The home list the boundary's deny rows were compiled under (`HandsToolkitOptions.readDenyHomes`'s seat, same list, same
167
+ * meaning: absolute home paths, anything else rejects naming `readDenyHomes`). Present ⇒ the judgment's home views are
168
+ * that table's — the environment's declared home is always among them, every other entry is read once under a short
169
+ * bound of its own — and the account database is not asked and no home is added; `[]` leaves the declared home alone.
170
+ * Absent ⇒ the judgment is its own placement: the declared home and the account's home, asked once for this call. */
171
+ readonly readDenyHomes?: readonly string[];
166
172
  /** Handed to the ENVIRONMENT's own primitives (`canonicalPath`, `listDir`) while the reads are
167
173
  * established; whether an abort stops one is the adapter's answer, and a walk short enough to finish
168
174
  * first finishes. It is NOT a deadline and it does not reach `boundary.alsoInside`, whose signature
@@ -196,9 +202,10 @@ export interface ShellReadJudgment {
196
202
  * pattern on its spelling alone, so the two diverge in both directions.
197
203
  *
198
204
  * A read target is also judged at its HOME VIEW (a path spelled through a firmlink or a bind mount of a home, respelled
199
- * at the home): this judgment is its own placement — the views read the environment's declared home and the account
200
- * home the account database answers once, for this call (`readDenyHomesOf`'s homes); the account home's probes wait at
201
- * most a short bound of their own.
205
+ * at the home). The views read the home list the boundary's rows were compiled under when the input names it
206
+ * ({@link ShellReadJudgeInput.readDenyHomes}); without it this judgment is its own placement — the environment's declared
207
+ * home and the account home the account database answers once, for this call (`readDenyHomesOf`'s homes). Every home
208
+ * beside the declared one waits at most a short bound of its own.
202
209
  *
203
210
  * It carries NO deadline. Bounding an environment-facing wait and substituting a fail-closed verdict is the
204
211
  * gate FOLD's policy on the gate's one deadline seat; the station has never held one on either entry, and a
@@ -1,7 +1,7 @@
1
1
  import { errorResult } from "../../core/tools.js";
2
2
  import { BASH_READONLY_CONFINEMENT_NOTE } from "./fs-shared.js";
3
3
  import { isAbsoluteForFamily, normalizeAbsPathLexically, pathFamilyOf, violationDetails, withinAnyRoot } from "./safety.js";
4
- import { fitMiddle, READ_DENY_SENTENCE_PART_MAX, readDenySentence } from "./read-deny.js";
4
+ import { checkedReadDenyHomes, fitMiddle, READ_DENY_SENTENCE_PART_MAX, readDenySentence } from "./read-deny.js";
5
5
  import { NOT_AUTO_ALLOWED, programPositionRefusalSentence, readProgramPosition } from "./bash-program-position.js";
6
6
  import { bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
7
7
  import { planShellReads } from "./shell-read-plan.js";
@@ -327,7 +327,7 @@ async function runShellReadStation(tool, command, declared, seat, readers, estab
327
327
  }
328
328
  export async function judgeShellRead(input) {
329
329
  const readers = input.readers ?? new Set(bashClassifyDefaultAllow());
330
- const run = await runShellReadStation(input.tool ?? "Bash", input.command, input.boundary, input.seat, readers, createShellReadEstablisher(input.env, input.boundary.roots, { homeViews: homeViewsAt(input.env, declaredReadDenyHomes(input.env)) }), input.env, input.signal);
330
+ const run = await runShellReadStation(input.tool ?? "Bash", input.command, input.boundary, input.seat, readers, createShellReadEstablisher(input.env, input.boundary.roots, { homeViews: homeViewsAt(input.env, input.readDenyHomes === undefined ? declaredReadDenyHomes(input.env) : checkedReadDenyHomes(input.readDenyHomes)) }), input.env, input.signal);
331
331
  const offer = normalizeReadRootCandidate(run.verdict.readRootCandidate);
332
332
  return {
333
333
  plan: run.plan,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.38.2",
3
+ "version": "7.39.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",