@sema-agent/core 7.18.0 → 7.19.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 (72) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/dist/brain/anthropic.js +6 -3
  3. package/dist/brain/open-responses.js +10 -4
  4. package/dist/brain/openai.js +5 -2
  5. package/dist/brain/reasoning.d.ts +13 -13
  6. package/dist/brain/reasoning.js +2 -2
  7. package/dist/brain/stream-engine.js +2 -1
  8. package/dist/brain/stream-shared.d.ts +3 -0
  9. package/dist/brain/stream-shared.js +1 -0
  10. package/dist/core/ask-origin.d.ts +16 -0
  11. package/dist/core/ask-origin.js +8 -0
  12. package/dist/core/auto-mode-arming.d.ts +6 -0
  13. package/dist/core/auto-mode-arming.js +5 -5
  14. package/dist/core/auto-mode-defaults.d.ts +43 -15
  15. package/dist/core/auto-mode-defaults.js +4 -1
  16. package/dist/core/auto-mode.d.ts +43 -14
  17. package/dist/core/auto-mode.js +79 -10
  18. package/dist/core/checkpoint-store.d.ts +7 -0
  19. package/dist/core/checkpoint-store.js +2 -2
  20. package/dist/core/engine-notice.d.ts +30 -0
  21. package/dist/core/gate-fold.js +5 -1
  22. package/dist/core/gate-lanes.js +7 -7
  23. package/dist/core/gate-outcome.d.ts +25 -0
  24. package/dist/core/gate-outcome.js +7 -0
  25. package/dist/core/governance-codes.d.ts +1 -1
  26. package/dist/core/governance-codes.js +6 -0
  27. package/dist/core/hooks.d.ts +1 -1
  28. package/dist/core/read-root-candidate.d.ts +12 -0
  29. package/dist/core/read-root-candidate.js +24 -0
  30. package/dist/core/roles.d.ts +9 -1
  31. package/dist/core/roles.js +4 -0
  32. package/dist/core/runner/classifier-ladder.d.ts +149 -11
  33. package/dist/core/runner/classifier-ladder.js +159 -10
  34. package/dist/core/runner/contracts.d.ts +2 -0
  35. package/dist/core/runner/denial-limit-arms.js +2 -2
  36. package/dist/core/runner/prepare-ask-lane.js +1 -0
  37. package/dist/core/runner/prepare-caps-and-workflow.js +20 -31
  38. package/dist/core/runner/prepare-config-doors.d.ts +6 -0
  39. package/dist/core/runner/prepare-config-doors.js +8 -10
  40. package/dist/core/runner/prepare-project-context.d.ts +5 -1
  41. package/dist/core/runner/prepare-project-context.js +3 -2
  42. package/dist/core/runner/prepare-task.js +1 -1
  43. package/dist/core/runner/resume-apply.js +3 -3
  44. package/dist/core/runner/run-leg.js +10 -4
  45. package/dist/core/runner/run-turn-boundary.js +12 -4
  46. package/dist/core/runner/synthetic-tools.d.ts +67 -29
  47. package/dist/core/runner/synthetic-tools.js +51 -22
  48. package/dist/core/runner/turn-attachments.d.ts +43 -12
  49. package/dist/core/runner/turn-attachments.js +58 -14
  50. package/dist/core/sensitive-path-policy.d.ts +58 -17
  51. package/dist/core/sensitive-path-policy.js +33 -5
  52. package/dist/core/skill-listing-budget.d.ts +112 -0
  53. package/dist/core/skill-listing-budget.js +58 -0
  54. package/dist/core/task-spec.d.ts +26 -0
  55. package/dist/core/tool-policy.d.ts +19 -4
  56. package/dist/core/tool-policy.js +6 -7
  57. package/dist/core/tool-spec.d.ts +37 -4
  58. package/dist/core/types.d.ts +1 -1
  59. package/dist/core/write-protect.d.ts +8 -3
  60. package/dist/core/write-protect.js +8 -5
  61. package/dist/index.d.ts +4 -4
  62. package/dist/index.js +3 -3
  63. package/dist/tools/fs/bash-readonly-classifier.d.ts +27 -0
  64. package/dist/tools/fs/bash-readonly-classifier.js +59 -6
  65. package/dist/tools/fs/fs-bash.js +7 -3
  66. package/dist/tools/fs/read-deny.d.ts +4 -3
  67. package/dist/tools/fs/safety.d.ts +15 -0
  68. package/dist/tools/fs/safety.js +7 -0
  69. package/package.json +1 -1
  70. package/test/export-surface.snapshot.json +15 -5
  71. package/wire-changes/core@7.18.0.json +1273 -0
  72. package/wire-changes/core@7.19.0.json +457 -0
package/dist/index.d.ts CHANGED
@@ -128,7 +128,7 @@ export { FileStorageBackend, FileSessionRepo, FileCheckpointStore, FileMemorySto
128
128
  export { CacheBreakDetector, type CacheBreakFinding, type ToolFingerprintInput } from "./core/cache-break-detector.js";
129
129
  export { maybeCompact, type MaybeCompactOptions, type CompactionWindowSafetyInfo } from "./core/auto-compaction.js";
130
130
  export { brainToRuntime } from "./core/runtime.js";
131
- export { createSensitivePathPolicy, RECOMMENDED_SENSITIVE_PATTERNS } from "./core/sensitive-path-policy.js";
131
+ export { createSensitivePathPolicy, RECOMMENDED_SENSITIVE_PATTERNS, RECOMMENDED_SENSITIVE_ASK_PATTERNS } from "./core/sensitive-path-policy.js";
132
132
  export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/fs-write-gate-policy.js";
133
133
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
134
134
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
@@ -177,7 +177,7 @@ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource, type AutoModeDenySource }
177
177
  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, screenGateOutcome } from "./core/gate-outcome.js";
178
178
  export { type AskCarry } from "./core/hooks.js";
179
179
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps, type SwappableDepSeat, type SwappableDeps } from "./core/swappable-deps.js";
180
- export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE, 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";
180
+ 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";
181
181
  export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_CLASSIFIER_MAX_TOKENS } from "./core/auto-mode-defaults.js";
182
182
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
183
183
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -239,7 +239,7 @@ export { stripEngineMetadata } from "./internal/llm.js";
239
239
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn, type CompiledEventPrompt, type EventDedupe, type EventDefaultPolicy } from "./prompt-assembly/event-registry.js";
240
240
  export { contextUsageSections, type ContextUsageSection } from "./prompt-assembly/section-usage.js";
241
241
  export type { CompiledSection, ComposedPrompt, PackSectionDeclaration, PromptCacheClass, PromptCarrier, PromptMutability, PromptOwner, PromptPack, PromptRenderCadence, PromptRuntimeFacts, PromptSlot, PromptTrust, SectionRenderInputs, } from "./prompt-assembly/types.js";
242
- export { type ReasoningIntensity, type ReasoningResolution, type ResolvedReasoning, type ReasoningFormat, DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, type OffCapabilityModel, resolveReasoningProfile, type ReasoningTier, type ReasoningProfileFlags, } from "./brain/reasoning.js";
242
+ 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";
243
243
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, type ScenarioId, type CodeReviewMode, type ScenarioProfile, type RunScenarioOptions, type RunScenarioResult, } from "./scenarios/scenario-registry.js";
244
244
  export { teacherMode, TEACHER_PROFILE, type TeacherModePair, type TeacherProfile, } from "./scenarios/teacher-quickstart.js";
245
245
  export { loadOrchestrationEnv, DEFAULT_REASONING_INTENSITY, type OrchestrationMode, type OrchestrationEnv, } from "./scenarios/env.js";
@@ -329,7 +329,7 @@ export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS, type RouteAdjudicationVector } f
329
329
  export { type BrainTimeoutConfig } from "./brain/timeout.js";
330
330
  export { createAssistantMessageEventStream } from "./internal/llm.js";
331
331
  export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
332
- export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, TrackFileEditHook, TrackEditRequest, TrackEditResult, FileEditedHook, FileEditedNotice, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, ResumePreflightInfo, ResumePreflightVerdict, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, TerminalCause, PausedCause, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, ShellGateDoctrine, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolInputVerdict, ToolInputValidationContext, ReversibilityVerdict, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
332
+ export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, TrackFileEditHook, TrackEditRequest, TrackEditResult, FileEditedHook, FileEditedNotice, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, ResumePreflightInfo, ResumePreflightVerdict, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, TerminalCause, PausedCause, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, ShellGateDoctrine, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolInputVerdict, ToolInputValidationContext, ReversibilityVerdict, ReadRootGrantCandidate, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
333
333
  export { Type } from "typebox";
334
334
  export type { TSchema, Static } from "typebox";
335
335
  export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";
package/dist/index.js CHANGED
@@ -103,7 +103,7 @@ export { FileStorageBackend, FileSessionRepo, FileCheckpointStore, FileMemorySto
103
103
  export { CacheBreakDetector } from "./core/cache-break-detector.js";
104
104
  export { maybeCompact } from "./core/auto-compaction.js";
105
105
  export { brainToRuntime } from "./core/runtime.js";
106
- export { createSensitivePathPolicy, RECOMMENDED_SENSITIVE_PATTERNS } from "./core/sensitive-path-policy.js";
106
+ export { createSensitivePathPolicy, RECOMMENDED_SENSITIVE_PATTERNS, RECOMMENDED_SENSITIVE_ASK_PATTERNS } from "./core/sensitive-path-policy.js";
107
107
  export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
108
108
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
109
109
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
@@ -150,7 +150,7 @@ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./core/runtime-cap
150
150
  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";
151
151
  export {} from "./core/hooks.js";
152
152
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps } from "./core/swappable-deps.js";
153
- export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE, AUTO_MODE_CLASSIFIER_FALLBACKS, isAutoModeClassifierFallback, AUTO_MODE_FALLBACK_CAUSES, isAutoModeFallbackCause, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, } from "./core/auto-mode.js";
153
+ 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";
154
154
  export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_CLASSIFIER_MAX_TOKENS } from "./core/auto-mode-defaults.js";
155
155
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
156
156
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -196,7 +196,7 @@ export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore
196
196
  export { stripEngineMetadata } from "./internal/llm.js";
197
197
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn } from "./prompt-assembly/event-registry.js";
198
198
  export { contextUsageSections } from "./prompt-assembly/section-usage.js";
199
- export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, resolveReasoningProfile, } from "./brain/reasoning.js";
199
+ export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffSpelled, declaredOffSpelling, resolveReasoningProfile, } from "./brain/reasoning.js";
200
200
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, } from "./scenarios/scenario-registry.js";
201
201
  export { teacherMode, TEACHER_PROFILE, } from "./scenarios/teacher-quickstart.js";
202
202
  export { loadOrchestrationEnv, DEFAULT_REASONING_INTENSITY, } from "./scenarios/env.js";
@@ -413,6 +413,21 @@ export interface CompoundReadonlyVerdict {
413
413
  * path is unchanged.
414
414
  */
415
415
  recursiveReadPaths?: readonly string[];
416
+ /**
417
+ * The ONE directory whose addition to the declared roots would make every objection this walk raised go
418
+ * away. It rides the SAME arm as the containment sentence and is dropped wherever that sentence is (a
419
+ * SHAPE refusal takes both — `tail -f <out-of-root>` stays refused after any grant), and on that arm the
420
+ * claim is MEASURED, not argued: the walk runs again with the proposed root in place and the directory is
421
+ * offered only if that walk has no objection left. `..`-traversing spellings are excluded on their own
422
+ * account ({@link outOfRootSpellings}): that is a disagreement between the folded text and the kernel,
423
+ * which no replay of the text can settle. VALUE: the deepest directory PROPERLY
424
+ * containing every {@link outOfRootPaths} entry, folded lexically — zero I/O, so a path that is itself a
425
+ * directory contributes its PARENT (`ls /etc/ssl` ⇒ `/etc`) where the reference implementation narrows
426
+ * with a stat; refused rather than widened when the result would be a family root or would contain the
427
+ * declared home or a declared {@link BashReadonlyRootBoundary.roots} entry. Meaning and spelling
428
+ * contract: {@link import("../../core/tool-spec.js").ReadRootGrantCandidate}.
429
+ */
430
+ outOfRootGrantCandidate?: string;
416
431
  }
417
432
  /**
418
433
  * The single minting point for the out-of-root-read approval option text, so a gate rendering
@@ -425,6 +440,18 @@ export interface CompoundReadonlyVerdict {
425
440
  * uses the host's native separator.
426
441
  */
427
442
  export declare function formatOutOfRootReadApprovalOption(directory: string): string;
443
+ /**
444
+ * The deepest directory containing both, or `undefined` when they share none (two path families).
445
+ *
446
+ * LINEAR, and that is the point: both paths are already folded, so the answer is their common prefix cut
447
+ * at a separator. Climbing one directory at a time re-folds the whole remaining path each step — the same
448
+ * answer at quadratic cost, measured at 4.9 s of synchronous work for one 32 KB command naming a
449
+ * 16 000-segment operand, on a walk that runs BEFORE the gate arms its probe deadline. The family-aware
450
+ * comparator keeps the last word: a prefix it cannot confirm contains both paths yields no candidate.
451
+ * Exported for its unit tests — the family confirmation is reachable from a command only through shapes
452
+ * the compound grammar refuses first, so a command-level pin cannot tell a live guard from an absent one.
453
+ */
454
+ export declare function deepestCommonDir(a: string, b: string): string | undefined;
428
455
  /** Resolve one operand against the boundary's base, WITHOUT touching the filesystem. Returns undefined
429
456
  * when the token cannot be resolved statically — the fail-closed direction, reported as its own
430
457
  * demotion rather than silently treated as in-root.
@@ -1,4 +1,4 @@
1
- import { isAbsoluteForFamily, isAbsolutePathForm, isBlockedDevicePath, isShellRootedSpellingUnmapped, joinForFamily, nativeUncSpellingOf, normalizeAbsPathLexically, win32NamespaceScreen, pathFamilyOf, withinAnyRoot } from "./safety.js";
1
+ import { hasWin32AliasedComponent, isAbsoluteForFamily, isAbsolutePathForm, isBlockedDevicePath, isShellRootedSpellingUnmapped, joinForFamily, nativeUncSpellingOf, normalizeAbsPathLexically, win32NamespaceScreen, pathFamilyOf, withinAnyRoot } from "./safety.js";
2
2
  import { lexShellText, literalTildeSpelling, quoteMask, substitutionsInHeredocBody, SUBSTITUTION_PLACEHOLDER, wordsOf, } from "./bash-lexer.js";
3
3
  import { ASSIGNMENT_WORD, COMMAND_LAUNCHERS, NOT_AUTO_ALLOWED, SHELL_BLOCK_WORDS, argvReentryUnjudged, isBareControlWord, programBasename, programPositionRefusalSentence, readProgramPosition, } from "./bash-program-position.js";
4
4
  export { NOT_AUTO_ALLOWED } from "./bash-program-position.js";
@@ -135,6 +135,49 @@ export function formatOutOfRootReadApprovalOption(directory) {
135
135
  const leaf = cut >= 0 ? trimmed.slice(cut + 1) : trimmed;
136
136
  return `Yes, allow reading from ${leaf || directory}/ from this project`;
137
137
  }
138
+ function parentDirLexically(p) {
139
+ return normalizeAbsPathLexically(`${p}/..`);
140
+ }
141
+ export function deepestCommonDir(a, b) {
142
+ if (a === b)
143
+ return a;
144
+ const shorter = Math.min(a.length, b.length);
145
+ let end = 0;
146
+ let i = 0;
147
+ while (i < shorter && a[i] === b[i]) {
148
+ if (a[i] === "/" || a[i] === "\\")
149
+ end = i;
150
+ i++;
151
+ }
152
+ if (i === shorter && (a[shorter] === "/" || a[shorter] === "\\" || b[shorter] === "/" || b[shorter] === "\\"))
153
+ end = shorter;
154
+ const cut = a.slice(0, end);
155
+ const candidate = cut.includes("/") || cut.includes("\\") ? cut : a.slice(0, end + 1);
156
+ if (candidate.length === 0 || !withinAnyRoot([candidate], a) || !withinAnyRoot([candidate], b))
157
+ return undefined;
158
+ return candidate;
159
+ }
160
+ function outOfRootGrantCandidateOf(paths, boundary) {
161
+ if (paths.length === 0)
162
+ return undefined;
163
+ if (paths.some(hasWin32AliasedComponent))
164
+ return undefined;
165
+ let candidate = parentDirLexically(paths[0]);
166
+ for (const path of paths.slice(1)) {
167
+ if (candidate === undefined)
168
+ return undefined;
169
+ candidate = deepestCommonDir(candidate, parentDirLexically(path));
170
+ }
171
+ if (candidate === undefined)
172
+ return undefined;
173
+ if (parentDirLexically(candidate) === candidate)
174
+ return undefined;
175
+ if (boundary.homeDir !== undefined && withinAnyRoot([candidate], boundary.homeDir))
176
+ return undefined;
177
+ if (boundary.roots.some((root) => withinAnyRoot([candidate], root)))
178
+ return undefined;
179
+ return candidate;
180
+ }
138
181
  const NO_PATH_OPERAND_COMMANDS = new Set([
139
182
  "pwd", "echo", "whoami", "uname", "which", "tr", "basename", "dirname",
140
183
  "cal", "uptime", "id", "groups", "nproc", "locale", "free", "true", "false", "seq", "expr", "type", "sleep",
@@ -1632,10 +1675,10 @@ export function classifyCompoundReadonlyDetailed(command, allow, boundary, opts)
1632
1675
  shapeRefusal ??= `multiple directory changes in one command require approval for clarity ${NOT_AUTO_ALLOWED}`;
1633
1676
  }
1634
1677
  if (boundary !== undefined) {
1635
- const walk = evaluateReadBoundary(foldedSegments, boundary, foldedReach);
1678
+ const walk = evaluateReadBoundary(foldedSegments, boundary, true, foldedReach);
1636
1679
  if (shapeRefusal === undefined)
1637
1680
  return walk;
1638
- const { reason: _walkSentence, ...structure } = walk;
1681
+ const { reason: _walkSentence, outOfRootGrantCandidate: _walkGrantCandidate, ...structure } = walk;
1639
1682
  return { ...structure, reason: shapeRefusal };
1640
1683
  }
1641
1684
  if (shapeRefusal !== undefined)
@@ -1646,7 +1689,7 @@ export function classifyCompoundReadonlyDetailed(command, allow, boundary, opts)
1646
1689
  return {};
1647
1690
  }
1648
1691
  const BASE_CANDIDATE_MAX = 256;
1649
- function evaluateReadBoundary(foldedSegments, boundary, reach) {
1692
+ function evaluateReadBoundary(foldedSegments, boundary, mintGrantCandidate, reach) {
1650
1693
  const seen = { outside: new Set(), inside: new Set(), undecided: new Set(), recursive: new Set(), unresolved: new Set(), checkedSpelled: new Set(), undecidedSpelled: new Set(), outsideSpelled: new Set() };
1651
1694
  const outside = [];
1652
1695
  const inside = [];
@@ -1804,10 +1847,20 @@ function evaluateReadBoundary(foldedSegments, boundary, reach) {
1804
1847
  return { ...checkedField, ...undecidedField };
1805
1848
  const paths = outside.map((o) => `"${o.path}"`).join(", ");
1806
1849
  const allowed = boundary.roots.length > 0 ? boundary.roots.join(", ") : "(none)";
1850
+ const grantCandidate = (() => {
1851
+ if (!mintGrantCandidate || outOfRootSpellings.length > 0)
1852
+ return undefined;
1853
+ const proposed = outOfRootGrantCandidateOf(outside.map((o) => o.path), boundary);
1854
+ if (proposed === undefined)
1855
+ return undefined;
1856
+ const replay = evaluateReadBoundary(foldedSegments, { ...boundary, roots: [...boundary.roots, proposed] }, false, reach);
1857
+ return replay.reason === undefined && (replay.undecidedPaths ?? []).length === 0 ? proposed : undefined;
1858
+ })();
1807
1859
  return {
1808
1860
  reason: `"${outside[0].command}" reads ${paths}, outside the allowed directories for this session: ${allowed} ${NOT_AUTO_ALLOWED}`,
1809
1861
  outOfRootRead: true,
1810
1862
  outOfRootPaths: outside.map((o) => o.path),
1863
+ ...(grantCandidate !== undefined ? { outOfRootGrantCandidate: grantCandidate } : {}),
1811
1864
  ...outsideField,
1812
1865
  ...checkedField,
1813
1866
  ...undecidedField,
@@ -1825,7 +1878,7 @@ export function classifySimpleCommandReadBoundary(command, boundary) {
1825
1878
  if (familyRefusal !== undefined)
1826
1879
  return { reason: familyRefusal, refusedWhole: true };
1827
1880
  }
1828
- return evaluateReadBoundary([toks], boundary);
1881
+ return evaluateReadBoundary([toks], boundary, true);
1829
1882
  }
1830
1883
  const NOT_GATED = { gated: false, paths: [] };
1831
1884
  const UNREADABLE_WHITELIST_WORD = /[$`()*?[\]{}~]/;
@@ -1885,7 +1938,7 @@ export function classifyOutOfRootReadGate(command, allow, boundary) {
1885
1938
  return NOT_GATED;
1886
1939
  if (baseMoved)
1887
1940
  return { gated: true, paths: [] };
1888
- const verdict = evaluateReadBoundary(readers, containmentOnly, readerReach);
1941
+ const verdict = evaluateReadBoundary(readers, containmentOnly, false, readerReach);
1889
1942
  return verdict.outOfRootRead === true ? { gated: true, paths: verdict.outOfRootPaths ?? [] } : NOT_GATED;
1890
1943
  };
1891
1944
  const direct = judge(command);
@@ -50,13 +50,17 @@ export function bashReversibilityProbe(allow, boundary) {
50
50
  if (loop.reason === undefined)
51
51
  return { reversible: true };
52
52
  if (boundaryDeclared(loop) || unjudgedRead(command, allowSet, resolved, loop))
53
- return { reversible: false, mandated: true };
54
- return { reversible: false, ...boundaryGate(detailed) };
53
+ return { reversible: false, mandated: true, ...readRootCandidateOf(loop) };
54
+ return { reversible: false, ...boundaryGate(detailed), ...readRootCandidateOf(detailed) };
55
55
  };
56
56
  }
57
57
  function boundaryUnread(verdict) {
58
58
  return boundaryUndecided(verdict) || boundaryUnjudged(verdict);
59
59
  }
60
+ function readRootCandidateOf(verdict) {
61
+ const dir = verdict.outOfRootGrantCandidate;
62
+ return dir !== undefined ? { readRootCandidate: { dir, clearsThisAsk: true } } : {};
63
+ }
60
64
  function boundaryUndecided(verdict) {
61
65
  return verdict.undecidedPaths !== undefined && verdict.undecidedPaths.length > 0;
62
66
  }
@@ -134,7 +138,7 @@ export function bashReadBoundaryProbe(boundary) {
134
138
  const loopVerdict = seatVerdict(loop);
135
139
  const mandated = [compoundVerdict, loopVerdict].find((verdict) => verdict?.mandated === true);
136
140
  if (mandated !== undefined)
137
- return mandated;
141
+ return { ...mandated, ...readRootCandidateOf(detailed), ...readRootCandidateOf(loop) };
138
142
  if (readBoundaryMandate(command, allowSet, resolved, detailed).mandated === true)
139
143
  return { reversible: false, mandated: true };
140
144
  return compoundVerdict ?? loopVerdict ?? { reversible: true };
@@ -104,11 +104,12 @@ export interface ReadDenyBuiltinRow {
104
104
  * `.sema`/`.claude` also hold agents, skills, hooks and (under the shell's layout,
105
105
  * `~/.sema/engine-data`) the engine data root with the memory library — material the model reads
106
106
  * and edits as ordinary work, and a directory-wide row would need a data-root passthrough (one more
107
- * rule) to keep it readable. The write face guards the directories (`sensitive-path-policy.ts`): a
108
- * write there is self-authorization; a read of a non-credential file there is debugging. Token
107
+ * rule) to keep it readable. Token
109
108
  * STORES of the same tooling (`.credentials.json`, an agent CLI's `auth.json`, …) are credentials rows, not
110
109
  * agent-config rows: a deployment that switches this tier off to debug its agent configuration must
111
- * not also expose the OAuth stores beside it.
110
+ * not also expose the OAuth stores beside it. These rows are ALSO the opt-in write deny set's
111
+ * agent-configuration block ({@link import("../../core/sensitive-path-policy.js").agentConfigDenyPatterns},
112
+ * which reads them from here; contract sentence there), so a row added here is judged by both faces.
112
113
  */
113
114
  export declare const READ_FACE_BUILTIN_DENY_TABLE: readonly ReadDenyBuiltinRow[];
114
115
  /**
@@ -258,6 +258,21 @@ export declare function isBinaryContent(sample: string): boolean;
258
258
  * test with no verdict of its own.
259
259
  */
260
260
  export declare function isUncPath(path: string): boolean;
261
+ /**
262
+ * True when a WIN-FORM spelling carries a component Win32's own normalization would trim — a trailing dot
263
+ * or space on anything but the `.`/`..` specials (the same client-side strip {@link uncCanonicalKey}
264
+ * folds, stated once and read here rather than re-derived).
265
+ *
266
+ * Such a spelling is an ALIAS: `x. ` and `x` name ONE directory to the OS and two different strings to a
267
+ * lexical comparator. A caller that must reason about a spelling byte-wise — a directory offered to a
268
+ * person as the thing to declare, a common-prefix cut between two operands — cannot pick one of the two
269
+ * readings on the person's behalf, so it withholds. POSIX paths are never aliased this way: a trailing dot
270
+ * or space there is an ordinary filename character.
271
+ *
272
+ * The UNC ANCHOR is exempt for the reason the key minter states: a host's trailing dot is DNS (the FQDN
273
+ * root) and share-name normalization is the server's business; neither is the client-side strip.
274
+ */
275
+ export declare function hasWin32AliasedComponent(p: string): boolean;
261
276
  /** The two spelling families a tree can be in. Read from the ROOT's spelling (the cwd's when a deployment
262
277
  * supplies no root) — never from `process.platform`: the exec env may be remote and of the other family. */
263
278
  export type PathFamily = "win" | "posix";
@@ -314,6 +314,13 @@ function foldUncLexically(p) {
314
314
  const anchor = `\\\\${host}${share !== undefined ? `\\${share}` : ""}`;
315
315
  return { key: out.length > 0 ? `${anchor}\\${out.join("\\")}` : anchor, climbedAboveShare: climbed };
316
316
  }
317
+ export function hasWin32AliasedComponent(p) {
318
+ if (!isWinFormPath(p))
319
+ return false;
320
+ const parts = p.split(/[\\/]+/).filter((seg) => seg.length > 0);
321
+ const tail = p.startsWith("\\\\") || p.startsWith("//") ? parts.slice(2) : parts;
322
+ return tail.some((seg) => seg !== "." && seg !== ".." && /[. ]$/.test(seg));
323
+ }
317
324
  export function pathFamilyOf(bases) {
318
325
  const tree = bases.root ?? bases.cwd;
319
326
  return tree === undefined ? undefined : isWinFormPath(tree) ? "win" : "posix";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.18.0",
3
+ "version": "7.19.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_comment": "design/87 L3 — frozen public export surface of src/index.ts (name -> kind). DO NOT edit by hand to silence a red test. A removed/changed entry = a SemVer-BREAKING change; bump MAJOR and update this fixture in the SAME commit (design/87 §4.2 / §5.2). Regenerate via REGEN in test/export-surface.test.ts.",
3
3
  "_tierComment": "#435 v1 — machine-readable layering of the public surface: stable = demonstrated by README.md / src/examples; internal = an `Internal`-marked name or a runner/engine deep-subtree declaration (the model seam src/engine/llm is excluded — it is the BYOM contract, not an engine internal); advanced = a supported export the front door does not walk you through. THIS IS AN INITIAL HEURISTIC, derived mechanically and expected to be refined ticket by ticket: no human reviewed these 1700+ entries one by one, and nothing here claims otherwise. Known bias: a short or English-word export name (ok, err, Result, Usage) can match ordinary prose in README.md and land `stable` on a coincidence. Every export MUST carry a tier — a new export with no row fails the gate in export-surface.test.ts.",
4
- "count": 2373,
4
+ "count": 2378,
5
5
  "exports": {
6
6
  "A2ATaskState": "type",
7
7
  "A2ATaskStateReversal": "type",
@@ -54,6 +54,7 @@
54
54
  "AUTO_MODE_DENIAL_LIMIT_DEFAULTS": "variable",
55
55
  "AUTO_MODE_DENY_SOURCES": "variable",
56
56
  "AUTO_MODE_FALLBACK_CAUSES": "variable",
57
+ "AUTO_MODE_PARSE_FAILURES": "variable",
57
58
  "AUTO_MODE_PERMISSIONS_EXTERNAL": "variable",
58
59
  "AUTO_MODE_UNAVAILABLE_CAUSES": "variable",
59
60
  "AcquiredSession": "interface",
@@ -149,6 +150,7 @@
149
150
  "AutoModeDenySource": "type",
150
151
  "AutoModeFallbackCause": "type",
151
152
  "AutoModeLadderRung": "interface",
153
+ "AutoModeParseFailure": "type",
152
154
  "AutoModeRebuildOptions": "interface",
153
155
  "AutoModeRebuildRefusal": "type",
154
156
  "AutoModeRebuildResult": "type",
@@ -206,7 +208,6 @@
206
208
  "CHALLENGE_LEDGER_MAX_EVENTS": "variable",
207
209
  "CIRCUIT_OPEN_MARKER": "variable",
208
210
  "CLASSIFIER_DENY_CAUSES": "variable",
209
- "CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE": "variable",
210
211
  "COARSE_SHELL_TOOLS": "variable",
211
212
  "CODE_AGENT_PROMPT": "variable",
212
213
  "CODE_REVIEW_PROMPT": "variable",
@@ -1050,6 +1051,7 @@
1050
1051
  "REAL_APPROVAL_CHECKPOINT_VERSION": "variable",
1051
1052
  "REASONING_BUDGET_SHARE": "variable",
1052
1053
  "RECALL_CAVEAT": "variable",
1054
+ "RECOMMENDED_SENSITIVE_ASK_PATTERNS": "variable",
1053
1055
  "RECOMMENDED_SENSITIVE_PATTERNS": "variable",
1054
1056
  "REMOTE_HARVEST_PER_FILE_BYTES": "variable",
1055
1057
  "REMOTE_MASS_DELETION_FUSE_RATIO": "variable",
@@ -1081,6 +1083,7 @@
1081
1083
  "ReadOnlyCommandRow": "interface",
1082
1084
  "ReadOnlyFlagArity": "type",
1083
1085
  "ReadOnlyShellVerdict": "type",
1086
+ "ReadRootGrantCandidate": "interface",
1084
1087
  "RealApprovalGateBit": "interface",
1085
1088
  "ReasoningFormat": "type",
1086
1089
  "ReasoningIntensity": "type",
@@ -1700,6 +1703,7 @@
1700
1703
  "checkpointVersionOf": "function",
1701
1704
  "clampHopChain": "function",
1702
1705
  "classifierMayAnswer": "function",
1706
+ "classifierParseFailureDenyMessage": "function",
1703
1707
  "classifierUnavailableDenyMessage": "function",
1704
1708
  "classifyCompoundReadonly": "function",
1705
1709
  "classifyCompoundReadonlyDetailed": "function",
@@ -1943,6 +1947,7 @@
1943
1947
  "isAutoModeClassifierFallback": "function",
1944
1948
  "isAutoModeDenySource": "function",
1945
1949
  "isAutoModeFallbackCause": "function",
1950
+ "isAutoModeParseFailure": "function",
1946
1951
  "isAutoModeUnavailableCause": "function",
1947
1952
  "isCanonicalPeerAddress": "function",
1948
1953
  "isClassifierDenyCause": "function",
@@ -2328,7 +2333,7 @@
2328
2333
  "termSet": "function",
2329
2334
  "terminalForTier": "function",
2330
2335
  "terminalProjection": "function",
2331
- "thinkingOffExpressible": "function",
2336
+ "thinkingOffSpelled": "function",
2332
2337
  "tightenTaskSpec": "function",
2333
2338
  "toA2ATaskState": "function",
2334
2339
  "toolCallFaceOf": "function",
@@ -2429,6 +2434,7 @@
2429
2434
  "AUTO_MODE_DENIAL_LIMIT_DEFAULTS": "advanced",
2430
2435
  "AUTO_MODE_DENY_SOURCES": "advanced",
2431
2436
  "AUTO_MODE_FALLBACK_CAUSES": "advanced",
2437
+ "AUTO_MODE_PARSE_FAILURES": "advanced",
2432
2438
  "AUTO_MODE_PERMISSIONS_EXTERNAL": "advanced",
2433
2439
  "AUTO_MODE_UNAVAILABLE_CAUSES": "advanced",
2434
2440
  "AcquiredSession": "advanced",
@@ -2524,6 +2530,7 @@
2524
2530
  "AutoModeDenySource": "advanced",
2525
2531
  "AutoModeFallbackCause": "advanced",
2526
2532
  "AutoModeLadderRung": "advanced",
2533
+ "AutoModeParseFailure": "advanced",
2527
2534
  "AutoModeRebuildOptions": "advanced",
2528
2535
  "AutoModeRebuildRefusal": "advanced",
2529
2536
  "AutoModeRebuildResult": "advanced",
@@ -2581,7 +2588,6 @@
2581
2588
  "CHALLENGE_LEDGER_MAX_EVENTS": "advanced",
2582
2589
  "CIRCUIT_OPEN_MARKER": "advanced",
2583
2590
  "CLASSIFIER_DENY_CAUSES": "advanced",
2584
- "CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE": "advanced",
2585
2591
  "COARSE_SHELL_TOOLS": "stable",
2586
2592
  "CODE_AGENT_PROMPT": "advanced",
2587
2593
  "CODE_REVIEW_PROMPT": "advanced",
@@ -3425,6 +3431,7 @@
3425
3431
  "REAL_APPROVAL_CHECKPOINT_VERSION": "advanced",
3426
3432
  "REASONING_BUDGET_SHARE": "advanced",
3427
3433
  "RECALL_CAVEAT": "advanced",
3434
+ "RECOMMENDED_SENSITIVE_ASK_PATTERNS": "advanced",
3428
3435
  "RECOMMENDED_SENSITIVE_PATTERNS": "advanced",
3429
3436
  "REMOTE_HARVEST_PER_FILE_BYTES": "advanced",
3430
3437
  "REMOTE_MASS_DELETION_FUSE_RATIO": "advanced",
@@ -3456,6 +3463,7 @@
3456
3463
  "ReadOnlyCommandRow": "advanced",
3457
3464
  "ReadOnlyFlagArity": "advanced",
3458
3465
  "ReadOnlyShellVerdict": "advanced",
3466
+ "ReadRootGrantCandidate": "advanced",
3459
3467
  "RealApprovalGateBit": "advanced",
3460
3468
  "ReasoningFormat": "advanced",
3461
3469
  "ReasoningIntensity": "advanced",
@@ -4075,6 +4083,7 @@
4075
4083
  "checkpointVersionOf": "advanced",
4076
4084
  "clampHopChain": "advanced",
4077
4085
  "classifierMayAnswer": "advanced",
4086
+ "classifierParseFailureDenyMessage": "advanced",
4078
4087
  "classifierUnavailableDenyMessage": "advanced",
4079
4088
  "classifyCompoundReadonly": "advanced",
4080
4089
  "classifyCompoundReadonlyDetailed": "advanced",
@@ -4318,6 +4327,7 @@
4318
4327
  "isAutoModeClassifierFallback": "advanced",
4319
4328
  "isAutoModeDenySource": "advanced",
4320
4329
  "isAutoModeFallbackCause": "advanced",
4330
+ "isAutoModeParseFailure": "advanced",
4321
4331
  "isAutoModeUnavailableCause": "advanced",
4322
4332
  "isCanonicalPeerAddress": "advanced",
4323
4333
  "isClassifierDenyCause": "advanced",
@@ -4703,7 +4713,7 @@
4703
4713
  "termSet": "stable",
4704
4714
  "terminalForTier": "advanced",
4705
4715
  "terminalProjection": "internal",
4706
- "thinkingOffExpressible": "advanced",
4716
+ "thinkingOffSpelled": "advanced",
4707
4717
  "tightenTaskSpec": "stable",
4708
4718
  "toA2ATaskState": "advanced",
4709
4719
  "toolCallFaceOf": "advanced",