@sema-agent/core 5.13.0 → 5.14.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 (86) hide show
  1. package/CHANGELOG.md +296 -0
  2. package/dist/agents/send-message-tool.js +1 -0
  3. package/dist/agents/subagent.d.ts +4 -0
  4. package/dist/agents/subagent.js +133 -41
  5. package/dist/brain/anthropic.js +33 -10
  6. package/dist/brain/context-overflow.d.ts +20 -0
  7. package/dist/brain/context-overflow.js +58 -0
  8. package/dist/brain/open-responses.js +24 -10
  9. package/dist/brain/openai.js +29 -11
  10. package/dist/brain/request-params.d.ts +2 -0
  11. package/dist/brain/request-params.js +16 -0
  12. package/dist/brain/stream-engine.d.ts +9 -1
  13. package/dist/brain/stream-engine.js +256 -27
  14. package/dist/brain/timeout.d.ts +1 -0
  15. package/dist/brain/timeout.js +1 -0
  16. package/dist/core/a2a.d.ts +2 -2
  17. package/dist/core/a2a.js +3 -3
  18. package/dist/core/ask-question.d.ts +47 -2
  19. package/dist/core/ask-question.js +209 -28
  20. package/dist/core/background-agent-store.d.ts +2 -0
  21. package/dist/core/checkpoint-store.d.ts +41 -17
  22. package/dist/core/checkpoint-store.js +114 -3
  23. package/dist/core/hooks.d.ts +24 -2
  24. package/dist/core/hooks.js +97 -10
  25. package/dist/core/human-input-projection.d.ts +12 -0
  26. package/dist/core/human-input-projection.js +27 -0
  27. package/dist/core/mcp.d.ts +7 -2
  28. package/dist/core/mcp.js +7 -7
  29. package/dist/core/memory-admission.d.ts +4 -0
  30. package/dist/core/memory-admission.js +3 -0
  31. package/dist/core/runner/assemble-result.d.ts +1 -0
  32. package/dist/core/runner/assemble-result.js +1 -1
  33. package/dist/core/runner/prepare-task.d.ts +16 -6
  34. package/dist/core/runner/prepare-task.js +301 -24
  35. package/dist/core/runner/runtask.d.ts +3 -6
  36. package/dist/core/runner/runtask.js +186 -36
  37. package/dist/core/runner/tool-output-projection.js +1 -0
  38. package/dist/core/session-store.d.ts +3 -0
  39. package/dist/core/session-store.js +4 -0
  40. package/dist/core/session.d.ts +1 -0
  41. package/dist/core/store-contracts/background-agent-store-contract.js +19 -0
  42. package/dist/core/store-contracts/checkpoint-store-contract.js +62 -3
  43. package/dist/core/task-notification.d.ts +2 -0
  44. package/dist/core/task-notification.js +5 -3
  45. package/dist/core/task-registry-agent.d.ts +1 -0
  46. package/dist/core/task-registry-agent.js +6 -0
  47. package/dist/core/task-registry.d.ts +1 -0
  48. package/dist/core/task-registry.js +4 -1
  49. package/dist/core/tool-policy.d.ts +5 -0
  50. package/dist/core/tool-policy.js +2 -1
  51. package/dist/core/types.d.ts +32 -1
  52. package/dist/core/wiring-manifest.d.ts +97 -0
  53. package/dist/core/wiring-manifest.js +186 -0
  54. package/dist/engine/compaction/compaction.js +2 -2
  55. package/dist/engine/harness/agent-harness.d.ts +2 -1
  56. package/dist/engine/harness/agent-harness.js +8 -1
  57. package/dist/engine/harness/types.d.ts +3 -1
  58. package/dist/engine/llm/types.d.ts +7 -0
  59. package/dist/engine/llm/types.js +8 -1
  60. package/dist/engine/session/import-validate.d.ts +6 -1
  61. package/dist/engine/session/import-validate.js +29 -6
  62. package/dist/engine/session/memory-repo.d.ts +3 -1
  63. package/dist/engine/session/memory-repo.js +2 -2
  64. package/dist/index.d.ts +7 -4
  65. package/dist/index.js +7 -4
  66. package/dist/internal/harness-types.d.ts +1 -1
  67. package/dist/internal/llm.d.ts +2 -2
  68. package/dist/internal/llm.js +1 -1
  69. package/dist/orchestration/run-workflow-tool.d.ts +4 -0
  70. package/dist/orchestration/run-workflow-tool.js +3 -0
  71. package/dist/orchestration/workflow-types.d.ts +8 -0
  72. package/dist/orchestration/workflow-types.js +14 -0
  73. package/dist/orchestration/workflow.d.ts +4 -0
  74. package/dist/orchestration/workflow.js +134 -5
  75. package/dist/prompts/default.js +1 -1
  76. package/dist/stores/file/checkpoint-store.d.ts +3 -5
  77. package/dist/stores/file/checkpoint-store.js +31 -2
  78. package/dist/stores/file/index.js +1 -1
  79. package/dist/stores/file/session-store.d.ts +3 -1
  80. package/dist/stores/file/session-store.js +2 -2
  81. package/dist/stores/file/shared-ledger.js +8 -1
  82. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -0
  83. package/dist/tools/fs/bash-readonly-classifier.js +94 -0
  84. package/dist/tools/fs/fs-bash.js +31 -12
  85. package/dist/tools/fs/safety.js +34 -10
  86. package/package.json +1 -1
@@ -17,6 +17,10 @@ export class StreamingImportValidator {
17
17
  rootCount = 0;
18
18
  runningLeaf = null;
19
19
  done = false;
20
+ preserveActorAssertions;
21
+ constructor(options) {
22
+ this.preserveActorAssertions = options?.preserveActorAssertions === true;
23
+ }
20
24
  step(entry) {
21
25
  if (this.done) {
22
26
  throw new SessionError("invalid_session", "StreamingImportValidator.step() called after finish()");
@@ -55,7 +59,7 @@ export class StreamingImportValidator {
55
59
  }
56
60
  if (e.type === "message") {
57
61
  validateMessageContentShape(e.message, `Entry "${e.id}"`);
58
- normalizeEngineProvenanceStamp(e.message);
62
+ normalizeEngineProvenanceStamp(e.message, this.preserveActorAssertions);
59
63
  }
60
64
  else if (e.type === "custom_message") {
61
65
  validateContentShape(e.content, CUSTOM_CONTENT_BLOCK_TYPES, true, `Entry "${e.id}".content`);
@@ -208,12 +212,15 @@ export class StreamingImportValidator {
208
212
  return { leafId: this.runningLeaf };
209
213
  }
210
214
  }
211
- export function validateEntriesForImport(entries) {
212
- const v = new StreamingImportValidator();
213
- for (const e of entries)
215
+ export function validateEntriesForImport(entries, options) {
216
+ const protectedEntries = entries.map((e) => e.type === "message" && typeof e.message === "object" && e.message !== null && !Array.isArray(e.message)
217
+ ? { ...e, message: { ...e.message } }
218
+ : e);
219
+ const v = new StreamingImportValidator(options);
220
+ for (const e of protectedEntries)
214
221
  v.step(e);
215
222
  v.finish();
216
- return entries;
223
+ return protectedEntries;
217
224
  }
218
225
  const TEXT_IMAGE_BLOCK_TYPES = new Set(["text", "image"]);
219
226
  const CUSTOM_CONTENT_BLOCK_TYPES = TEXT_IMAGE_BLOCK_TYPES;
@@ -235,12 +242,28 @@ function describeShape(value) {
235
242
  }
236
243
  return `a ${typeof value}`;
237
244
  }
238
- function normalizeEngineProvenanceStamp(message) {
245
+ function normalizeEngineProvenanceStamp(message, preserveActorAssertions = false) {
239
246
  if (message === null || typeof message !== "object")
240
247
  return;
241
248
  const m = message;
242
249
  if (m.role !== "user")
243
250
  return;
251
+ if (m.actor !== undefined) {
252
+ const a = m.actor;
253
+ const wellFormed = preserveActorAssertions &&
254
+ a !== null &&
255
+ typeof a === "object" &&
256
+ typeof a.id === "string" &&
257
+ a.id.length > 0 &&
258
+ typeof a.hostAsserted === "boolean" &&
259
+ (a.issuer === undefined || (typeof a.issuer === "string" && a.issuer.length > 0));
260
+ if (wellFormed) {
261
+ m.actor = { id: a.id, hostAsserted: a.hostAsserted, ...(a.issuer !== undefined ? { issuer: a.issuer } : {}) };
262
+ }
263
+ else {
264
+ delete m.actor;
265
+ }
266
+ }
244
267
  if (m.engineMinted !== undefined && m.engineMinted !== true)
245
268
  delete m.engineMinted;
246
269
  if (m.provenance !== undefined && m.provenance !== "engine-note")
@@ -11,5 +11,7 @@ export declare class InMemorySessionRepo implements SessionRepo<SessionMetadata,
11
11
  delete(metadata: SessionMetadata): Promise<void>;
12
12
  fork(sourceMetadata: SessionMetadata, options: SessionForkOptions): Promise<Session>;
13
13
  exportEntries(sessionId: string): Promise<SessionTreeEntry[]>;
14
- importEntries(sessionId: string, owner: string | undefined, entries: SessionTreeEntry[]): Promise<void>;
14
+ importEntries(sessionId: string, owner: string | undefined, entries: SessionTreeEntry[], options?: {
15
+ preserveActorAssertions?: boolean;
16
+ }): Promise<void>;
15
17
  }
@@ -49,9 +49,9 @@ export class InMemorySessionRepo {
49
49
  const session = await this.open({ id: sessionId, createdAt: "" });
50
50
  return session.getStorage().getEntries();
51
51
  }
52
- async importEntries(sessionId, owner, entries) {
52
+ async importEntries(sessionId, owner, entries, options) {
53
53
  void owner;
54
- const validated = validateEntriesForImport(entries);
54
+ const validated = validateEntriesForImport(entries, options);
55
55
  const metadata = { id: sessionId, createdAt: createTimestamp() };
56
56
  this.sessions.set(sessionId, toSession(new InMemorySessionStorage({ metadata, entries: validated })));
57
57
  }
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export { createTaskListTools, createMemoryTaskListStore, assertJsonMetadata, typ
15
15
  export { assembleCodeTools, type CodeToolsConfig, CODE_ROLE } from "./scenarios/full-body.js";
16
16
  export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, type AggregateBudgetOptions, } from "./core/tool-result-budget.js";
17
17
  export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES, type MediaStripInfo } from "./core/media-byte-cap.js";
18
- export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, type OnQuestion, type AskQuestion, type AskQuestionOption, type AskQuestionRequest, type QuestionAnswer, type QuestionAnswerItem, } from "./core/ask-question.js";
18
+ export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, type OnQuestionOutcome, type QuestionUnavailable, type OnQuestion, type AskQuestion, type AskQuestionOption, type AskQuestionRequest, type QuestionAnswer, type QuestionAnswerItem, type AskQuestionCardDetails, type AskUserQuestionToolOptions, type AskAnswerContinuationSource, type SyntheticContinuationReason, } from "./core/ask-question.js";
19
19
  export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, LSP_FAILURE_BRAND, brandLspFailure, lspFailureOf, SharedAbortScope, settleOnAbort, type LspNoneReason, type LspOperation, type LspServerManager, type LspSession, type LspResult, type LspLocation, type LspSymbolInfo, type LspRequestParams, type LspToolOptions, type LspTransport, type LspReadText, } from "./core/lsp.js";
20
20
  export { buildRequest, parseResult, callHierarchyMethod, pathToUri } from "./core/lsp-protocol.js";
21
21
  export { TransportLspSession, type SessionWarmup } from "./core/lsp-session.js";
@@ -82,7 +82,7 @@ export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW,
82
82
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
83
83
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
84
84
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, type ToolResultStore, type ToolResultSlice, } from "./core/tool-result-store.js";
85
- export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
85
+ export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
86
86
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, type UsageWindow, type UsageWindowStore, type UsageWindowReading, type UsageWindowRecord, type UsageSlot, type UsageBucketRow, } from "./core/usage-window-store.js";
87
87
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
88
88
  export { ENV_LIFETIME_SUSPEND_MARGIN_MS, USAGE_WINDOW_REAP_MARGIN_MS } from "./core/runner/prepare-task.js";
@@ -97,7 +97,10 @@ export { createSensitivePathPolicy, RECOMMENDED_SENSITIVE_PATTERNS } from "./cor
97
97
  export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/fs-write-gate-policy.js";
98
98
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
99
99
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
100
- export { renderTaskNotificationXml, taskNotificationDedupKey, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, } from "./core/task-notification.js";
100
+ export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, } from "./core/task-notification.js";
101
+ export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, type WiringManifest, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
102
+ export { type StoreDurability } from "./core/checkpoint-store.js";
103
+ export { projectHumanInput, buildHumanInputEvent, type HumanInputSource, type HumanInputFrame, type HumanInputEvent, type HumanInputDelivery, } from "./core/human-input-projection.js";
101
104
  export { TaskRegistry, defaultTaskRegistry, createTaskOutputTool, createTaskStopTool, type SemaTaskType, type SemaTaskStatus, type SemaTaskHandle, type ParkedClaimTicket, type TaskAccess, type UnifiedTaskOutput, type TaskRetrievalStatus, type StopSource, type RegisterMonitorInput, type MonitorTimers, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccessWorkflowRun, DURABLE_AGENT_HANDLE_RE, } from "./core/task-registry.js";
102
105
  export { InMemoryMailboxStore, type MailboxStore, type MailboxMessage, type MailboxLease } from "./core/mailbox-store.js";
103
106
  export { FileMailboxStore, type FileMailboxStoreOptions } from "./stores/file/mailbox-store.js";
@@ -123,7 +126,7 @@ export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, typ
123
126
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
124
127
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
125
128
  export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRule, wildcardMatch, type PermissionRule, type ParsedPermissionRule, type PermissionRuleIssue, type PermissionRuleCaps, type PermissionRulePolicyOptions, } from "./core/permission-rules.js";
126
- export { formatHookFeedback, runToolGate, type Hooks, type HookToolContext, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
129
+ export { formatHookFeedback, runToolGate, createHookEnvCapabilities, type Hooks, type HookToolContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
127
130
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, type NormalizedMemorySpec, type MemorySpecInput, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, type Embedder, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, type UtilityGate, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryNoteType, type StructuredNoteInput, } from "./core/memory.js";
128
131
  export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
129
132
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ export { createTaskListTools, createMemoryTaskListStore, assertJsonMetadata } fr
12
12
  export { assembleCodeTools, CODE_ROLE } from "./scenarios/full-body.js";
13
13
  export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, } from "./core/tool-result-budget.js";
14
14
  export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "./core/media-byte-cap.js";
15
- export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, } from "./core/ask-question.js";
15
+ export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, } from "./core/ask-question.js";
16
16
  export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, LSP_FAILURE_BRAND, brandLspFailure, lspFailureOf, SharedAbortScope, settleOnAbort, } from "./core/lsp.js";
17
17
  export { buildRequest, parseResult, callHierarchyMethod, pathToUri } from "./core/lsp-protocol.js";
18
18
  export { TransportLspSession } from "./core/lsp-session.js";
@@ -70,7 +70,7 @@ export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW,
70
70
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
71
71
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
72
72
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, } from "./core/tool-result-store.js";
73
- export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
73
+ export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
74
74
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, } from "./core/usage-window-store.js";
75
75
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
76
76
  export { ENV_LIFETIME_SUSPEND_MARGIN_MS, USAGE_WINDOW_REAP_MARGIN_MS } from "./core/runner/prepare-task.js";
@@ -84,7 +84,10 @@ export { createSensitivePathPolicy, RECOMMENDED_SENSITIVE_PATTERNS } from "./cor
84
84
  export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
85
85
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
86
86
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
87
- export { renderTaskNotificationXml, taskNotificationDedupKey, SystemInjectionQueue, } from "./core/task-notification.js";
87
+ export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, SystemInjectionQueue, } from "./core/task-notification.js";
88
+ export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, } from "./core/wiring-manifest.js";
89
+ export {} from "./core/checkpoint-store.js";
90
+ export { projectHumanInput, buildHumanInputEvent, } from "./core/human-input-projection.js";
88
91
  export { TaskRegistry, defaultTaskRegistry, createTaskOutputTool, createTaskStopTool, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccessWorkflowRun, DURABLE_AGENT_HANDLE_RE, } from "./core/task-registry.js";
89
92
  export { InMemoryMailboxStore } from "./core/mailbox-store.js";
90
93
  export { FileMailboxStore } from "./stores/file/mailbox-store.js";
@@ -108,7 +111,7 @@ export { parseAutoModeResponse, createAutoModeDecider, } from "./core/auto-mode.
108
111
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
109
112
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
110
113
  export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRule, wildcardMatch, } from "./core/permission-rules.js";
111
- export { formatHookFeedback, runToolGate, } from "./core/hooks.js";
114
+ export { formatHookFeedback, runToolGate, createHookEnvCapabilities, } from "./core/hooks.js";
112
115
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, } from "./core/memory.js";
113
116
  export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, migrateScope, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, syncMemoryScope, } from "./core/memory-engine/index.js";
114
117
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";
@@ -2,7 +2,7 @@ export type { CompactionPreparation, SummarizationClampDryRun } from "../engine/
2
2
  export type { InvokedSkillRetention } from "../engine/compaction/utils.js";
3
3
  export type { AgentCoreRuntimeDeps } from "../engine/loop/runtime-deps.js";
4
4
  export type { AgentMessage, AgentTool, AgentToolResult, AgentToolUpdateCallback, ThinkingLevel, ToolExecutionMode, } from "../engine/loop/types.js";
5
- export type { AgentHarnessEvent, CompactionSettings, ExecutionEnv, ExecutionErrorCode, FileErrorCode, FileInfo, Result, Session, SessionMetadata, SessionRepo, SessionStorage, SessionTreeEntry, Skill, } from "../engine/harness/types.js";
5
+ export type { AgentHarnessEvent, CompactionSettings, ExecutionEnv, ExecutionErrorCode, FileError, FileErrorCode, FileInfo, Result, Session, SessionMetadata, SessionRepo, SessionStorage, SessionTreeEntry, Skill, } from "../engine/harness/types.js";
6
6
  export type { ExecutionEnvExecOptions, ExecResult } from "../engine/harness/types.js";
7
7
  export type { SessionWriteOptions, CompactionEntry } from "../engine/harness/types.js";
8
8
  export type { ActiveWorktreeSession, WorkspaceState } from "../engine/harness/types.js";
@@ -1,2 +1,2 @@
1
- export { createAssistantMessageEventStream, stripEngineMetadata } from "../engine/llm/index.js";
2
- export type { AnthropicMessagesCompat, OpenAICompletionsCompat, OpenAIResponsesCompat, AssistantMessage, AssistantMessageDiagnostic, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, Model, ResilienceOptions, SimpleStreamOptions, StallTimeouts, StopReason, StreamFn, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage, Usage, UserMessage, } from "../engine/llm/index.js";
1
+ export { createAssistantMessageEventStream, snapshotActorAssertion, stripEngineMetadata } from "../engine/llm/index.js";
2
+ export type { ActorAssertion, AnthropicMessagesCompat, OpenAICompletionsCompat, OpenAIResponsesCompat, AssistantMessage, AssistantMessageDiagnostic, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, Model, ResilienceOptions, SimpleStreamOptions, StallTimeouts, StopReason, StreamFn, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage, Usage, UserMessage, } from "../engine/llm/index.js";
@@ -1 +1 @@
1
- export { createAssistantMessageEventStream, stripEngineMetadata } from "../engine/llm/index.js";
1
+ export { createAssistantMessageEventStream, snapshotActorAssertion, stripEngineMetadata } from "../engine/llm/index.js";
@@ -81,6 +81,7 @@ export interface RunWorkflowToolDeps {
81
81
  sourceTaskId?: string;
82
82
  principal?: string;
83
83
  oneShot?: boolean;
84
+ parentInteractionPosture?: "interactive" | "headless";
84
85
  workflowDepth?: number;
85
86
  parentCwd?: string;
86
87
  parentThinking?: () => import("../core/types.js").TaskSpec["thinking"];
@@ -88,5 +89,8 @@ export interface RunWorkflowToolDeps {
88
89
  parentGetApiKeyAndHeaders?: import("../core/types.js").TaskSpec["getApiKeyAndHeaders"];
89
90
  forwardEvent?: (event: import("../core/types.js").TaskEvent) => void;
90
91
  inheritedGateForChildren?: () => import("../core/runner/prepare-task.js").InheritedGate;
92
+ autoModeReview?: () => {
93
+ decider: import("../core/auto-mode.js").AutoModeDecider;
94
+ } | undefined;
91
95
  }
92
96
  export declare function createRunWorkflowTool(d: RunWorkflowToolDeps): Promise<AgentTool>;
@@ -388,6 +388,7 @@ export async function createRunWorkflowTool(d) {
388
388
  }
389
389
  let handle;
390
390
  const workflowTaskId = d.taskRegistry?.mintTaskId("workflow");
391
+ const autoModeReview = ctx.autoModeReview ?? d.autoModeReview?.();
391
392
  try {
392
393
  handle = startWorkflow(d.runner, scriptFn, {
393
394
  store: d.store,
@@ -413,6 +414,7 @@ export async function createRunWorkflowTool(d) {
413
414
  ...(d.parentCwd !== undefined ? { parentCwd: d.parentCwd } : {}),
414
415
  parentToolCallId: ctx.toolCallId,
415
416
  ...(sourceTaskId !== undefined ? { parentTaskId: sourceTaskId } : {}),
417
+ ...((ctx.interactionPosture ?? d.parentInteractionPosture) !== undefined ? { interactionPosture: (ctx.interactionPosture ?? d.parentInteractionPosture) } : {}),
416
418
  ...(d.parentModel !== undefined ? { defaultModel: d.parentModel } : {}),
417
419
  ...(d.parentGetApiKeyAndHeaders !== undefined ? { defaultGetApiKeyAndHeaders: d.parentGetApiKeyAndHeaders } : {}),
418
420
  ...(ctx.centerArtifactDigest !== undefined ? { parentCenterArtifactDigest: ctx.centerArtifactDigest } : {}),
@@ -421,6 +423,7 @@ export async function createRunWorkflowTool(d) {
421
423
  ...((ctx.inheritedGateForChildren ?? d.inheritedGateForChildren) !== undefined
422
424
  ? { inheritedGate: (ctx.inheritedGateForChildren ?? d.inheritedGateForChildren)() }
423
425
  : {}),
426
+ ...(autoModeReview !== undefined ? { autoModeReview } : {}),
424
427
  }, { workflowDepth: d.workflowDepth });
425
428
  }
426
429
  catch (err) {
@@ -187,6 +187,14 @@ export declare class WorkflowAgentStalledError extends Error {
187
187
  readonly code = "workflow.agent_stalled";
188
188
  constructor(attempts: number, stallMs: number, lastResult?: TaskResult | undefined);
189
189
  }
190
+ export declare const WORKFLOW_SPAWN_BLOCKED_ERROR_CODE = "autoMode.spawn_blocked";
191
+ export declare class WorkflowAgentBlockedError extends Error {
192
+ readonly label: string;
193
+ readonly category: string;
194
+ readonly reason: string;
195
+ readonly code = "workflow.agent_blocked";
196
+ constructor(label: string, category: string, reason: string);
197
+ }
190
198
  export declare class WorkflowMaxAgentsError extends Error {
191
199
  readonly max: number;
192
200
  readonly code = "workflow.max_agents";
@@ -41,6 +41,20 @@ export class WorkflowAgentStalledError extends Error {
41
41
  this.name = "WorkflowAgentStalledError";
42
42
  }
43
43
  }
44
+ export const WORKFLOW_SPAWN_BLOCKED_ERROR_CODE = "autoMode.spawn_blocked";
45
+ export class WorkflowAgentBlockedError extends Error {
46
+ label;
47
+ category;
48
+ reason;
49
+ code = "workflow.agent_blocked";
50
+ constructor(label, category, reason) {
51
+ super(`workflow agent "${label}" was not started: blocked by the pre-spawn review${reason ? `: ${reason}` : ""}`);
52
+ this.label = label;
53
+ this.category = category;
54
+ this.reason = reason;
55
+ this.name = "WorkflowAgentBlockedError";
56
+ }
57
+ }
44
58
  export class WorkflowMaxAgentsError extends Error {
45
59
  max;
46
60
  code = "workflow.max_agents";
@@ -96,12 +96,16 @@ export interface RunWorkflowOptions {
96
96
  parentCwd?: string;
97
97
  parentToolCallId?: string;
98
98
  parentTaskId?: string;
99
+ interactionPosture?: "interactive" | "headless";
99
100
  defaultModel?: () => import("../internal/llm.js").Model | undefined;
100
101
  defaultGetApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
101
102
  parentCenterArtifactDigest?: string;
102
103
  parentCenterSourceRevision?: string;
103
104
  onForwardEvent?: (event: TaskEvent) => void;
104
105
  inheritedGate?: import("../core/runner/prepare-task.js").InheritedGate;
106
+ autoModeReview?: {
107
+ decider: import("../core/auto-mode.js").AutoModeDecider;
108
+ };
105
109
  phases?: ReadonlyArray<{
106
110
  title: string;
107
111
  detail?: string;
@@ -16,12 +16,43 @@ import { boundedRedactedSummary } from "../core/untrusted-egress.js";
16
16
  import { delimitUntrusted } from "../core/untrusted-text.js";
17
17
  import { OUTPUT_TOOL_NAME } from "../core/runner/synthetic-tools.js";
18
18
  import { compileOutputSchema } from "../core/runner/strict-output-schema.js";
19
- import { WorkflowBudgetExceededError, WorkflowNestingError, WorkflowAgentSchemaError, WorkflowAgentStalledError, WorkflowMaxAgentsError } from "./workflow-types.js";
19
+ import { WorkflowBudgetExceededError, WorkflowNestingError, WorkflowAgentBlockedError, WorkflowAgentSchemaError, WorkflowAgentStalledError, WorkflowMaxAgentsError, WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } from "./workflow-types.js";
20
20
  export * from "./workflow-types.js";
21
21
  const MAX_TRANSCRIPT_CHARS = 4000;
22
22
  const WORKFLOW_RESULT_MAX = 4000;
23
23
  const WORKFLOW_RESULT_FULL_MAX = 200_000;
24
24
  const MAX_ACTIVITY = 30;
25
+ const MAX_REVIEWED_SCHEMA_BYTES = 4096;
26
+ const MAX_REVIEW_REASON_CHARS = 500;
27
+ const MAX_REVIEWED_PROMPT_CHARS = 2_000;
28
+ const MAX_SCHEMA_WALK_DEPTH = 32;
29
+ function isUsableSchema(v) {
30
+ if (v === true)
31
+ return true;
32
+ return typeof v === "object" && v !== null && !Array.isArray(v);
33
+ }
34
+ function isSingleFaced(v, depth = 0) {
35
+ if (typeof v !== "object" || v === null)
36
+ return true;
37
+ if (depth > MAX_SCHEMA_WALK_DEPTH)
38
+ return false;
39
+ if (typeof v.toJSON === "function")
40
+ return false;
41
+ for (const key of Object.keys(v)) {
42
+ if (typeof Object.getOwnPropertyDescriptor(v, key)?.get === "function")
43
+ return false;
44
+ }
45
+ for (const child of Object.values(v)) {
46
+ if (!isSingleFaced(child, depth + 1))
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+ function forReview(text) {
52
+ if (text.length <= MAX_REVIEWED_PROMPT_CHARS)
53
+ return text;
54
+ return `${text.slice(0, MAX_REVIEWED_PROMPT_CHARS)}\n[…TRUNCATED FOR REVIEW: ${text.length - MAX_REVIEWED_PROMPT_CHARS} further characters follow that the child WILL receive and this review did NOT see]`;
55
+ }
25
56
  function workflowModelLabel(spec) {
26
57
  const model = spec.model;
27
58
  if (model === undefined)
@@ -473,6 +504,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
473
504
  const sem = createSemaphore(concurrency);
474
505
  const spawnAttribution = {
475
506
  isDelegatedChild: true,
507
+ ...(opts.interactionPosture !== undefined ? { parentInteractionPosture: opts.interactionPosture } : {}),
476
508
  ...(opts.parentToolCallId !== undefined ? { parentToolCallId: opts.parentToolCallId } : {}),
477
509
  ...(opts.parentTaskId !== undefined ? { parentTaskId: opts.parentTaskId } : {}),
478
510
  ...(opts.parentCenterArtifactDigest !== undefined ? { parentCenterArtifactDigest: opts.parentCenterArtifactDigest } : {}),
@@ -782,6 +814,83 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
782
814
  const model = workflowModelLabel(specForIdentity);
783
815
  return { label, phase, phaseInstance, groupId, inheritedModelSnap, callKey, prompt, model };
784
816
  };
817
+ const reviewSpawnBeforeLaunch = async (lane, label, callKey, runSpec, effectiveSignal) => {
818
+ const review = opts.autoModeReview;
819
+ if (review === undefined)
820
+ return runSpec;
821
+ const schema = runSpec.outputSchema;
822
+ let schemaText;
823
+ let specToLaunch = runSpec;
824
+ if (schema !== undefined) {
825
+ try {
826
+ schemaText = JSON.stringify(schema);
827
+ }
828
+ catch {
829
+ schemaText = undefined;
830
+ }
831
+ if (schemaText === undefined) {
832
+ throw new WorkflowAgentBlockedError(label, "", "the child's output schema could not be serialized for review");
833
+ }
834
+ const schemaBytes = Buffer.byteLength(schemaText, "utf8");
835
+ if (schemaBytes > MAX_REVIEWED_SCHEMA_BYTES) {
836
+ throw new WorkflowAgentBlockedError(label, "", `output schema too large to classify safely (${schemaBytes} bytes, cap ${MAX_REVIEWED_SCHEMA_BYTES})`);
837
+ }
838
+ if (!isSingleFaced(schema)) {
839
+ throw new WorkflowAgentBlockedError(label, "", "the child's output schema cannot be read as one unambiguous value (it overrides its own serialization, or nests deeper than the review can walk)");
840
+ }
841
+ const normalized = JSON.parse(schemaText);
842
+ if (!isUsableSchema(normalized)) {
843
+ throw new WorkflowAgentBlockedError(label, "", "the child's output schema does not normalize to a JSON Schema object");
844
+ }
845
+ specToLaunch = { ...runSpec, outputSchema: normalized };
846
+ }
847
+ const toolNames = (runSpec.tools ?? []).map((t) => t.name);
848
+ const toolsNote = `${toolNames.length > 0 ? toolNames.join(", ") : "(none explicitly listed)"}` +
849
+ ` — this is the EXPLICIT roster only. If this deployment gave the child a real execution environment,` +
850
+ ` its own prepare additionally mounts the standard file/shell toolkit (Read/Edit/Write/Bash/Grep/Glob)` +
851
+ ` on top of this list. Treat the absence of a tool here as unknown, not as denied.`;
852
+ const remoteSources = [
853
+ ...(runSpec.mcp ?? []).map((m) => `mcp:${m.name}`),
854
+ ...(runSpec.a2a ?? []).map((p) => `a2a:${p.name}`),
855
+ ];
856
+ const shownSources = remoteSources.slice(0, 20).map((s) => s.slice(0, 80));
857
+ const remoteNote = remoteSources.length > 0
858
+ ? `\nThis child also mounts tools from ${remoteSources.length} external capability source(s) — ${shownSources.join(", ")}${remoteSources.length > shownSources.length ? `, +${remoteSources.length - shownSources.length} more` : ""}. Each mounts its own tools under a "<source>__*" namespace; those tool names are not resolved yet and are NOT in the roster above. Treat this child as able to reach outside this process.`
859
+ : "";
860
+ const objective = forReview(runSpec.objective ?? "");
861
+ const systemPrompt = runSpec.systemPrompt !== undefined ? forReview(runSpec.systemPrompt) : undefined;
862
+ const imageCount = runSpec.images?.length ?? 0;
863
+ const imagesNote = imageCount > 0
864
+ ? `\n${imageCount} image attachment(s) ride with this child. Their CONTENT was NOT inspected by this review — the classifier leg is text-only. Treat any instruction that defers to attached or embedded content as unreviewed.`
865
+ : "";
866
+ const labelForReview = label.slice(0, 80);
867
+ const verdict = await review.decider
868
+ .decide({
869
+ req: {
870
+ toolName: "Workflow(agent)",
871
+ args: {
872
+ objective,
873
+ tools: toolNames,
874
+ ...(systemPrompt !== undefined ? { systemPrompt } : {}),
875
+ ...(schemaText !== undefined ? { outputSchema: schemaText } : {}),
876
+ ...(imageCount > 0 ? { uninspectedImageAttachments: imageCount } : {}),
877
+ ...(remoteSources.length > 0 ? { externalCapabilitySources: shownSources } : {}),
878
+ },
879
+ toolCallId: `${runId}:${callKey}`,
880
+ },
881
+ askMessage: `Reviewing a workflow-spawned sub-agent ("${labelForReview}", ${lane}) about to be started. ` +
882
+ `Objective: ${objective}\nTools available to it: ${toolsNote}${remoteNote}${imagesNote}`,
883
+ }, effectiveSignal)
884
+ .catch(() => ({ kind: "unavailable", cause: "error" }));
885
+ if (effectiveSignal?.aborted)
886
+ throw new Error("workflow aborted");
887
+ if (finalized)
888
+ throw new Error(`workflow run already finalized — ${lane} cannot spawn after the run ended`);
889
+ if (verdict.kind === "block") {
890
+ throw new WorkflowAgentBlockedError(label, verdict.category, boundedRedactedSummary(verdict.reason, MAX_REVIEW_REASON_CHARS));
891
+ }
892
+ return specToLaunch;
893
+ };
785
894
  const createUsageBeat = (rec) => {
786
895
  let beatTokens = 0;
787
896
  let beatTurns = 0;
@@ -931,15 +1040,16 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
931
1040
  const typedSpec = typedSpec0.model === undefined && inheritedModelSnap !== undefined ? { ...typedSpec0, model: inheritedModelSnap } : typedSpec0;
932
1041
  const framedSpec = withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema);
933
1042
  const authInherit = framedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
934
- const runSpec = agentOpts.schema
1043
+ const preReviewSpec = agentOpts.schema
935
1044
  ? { ...framedSpec, ...authInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
936
1045
  : { ...framedSpec, ...authInherit, signal: effectiveSignal };
1046
+ const runSpec = await reviewSpawnBeforeLaunch("ctx.agent", label, callKey, preReviewSpec, effectiveSignal);
937
1047
  const enrichedForward = opts.onForwardEvent !== undefined
938
1048
  ? (e) => {
939
1049
  opts.onForwardEvent(e.type === "task_progress" ? { ...e, workflowRunId: runId, workflowAgentLabel: label } : e);
940
1050
  }
941
1051
  : undefined;
942
- const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
1052
+ const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), delegationTaskType: "workflow", agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
943
1053
  let attempts = 0;
944
1054
  let throttleRetried = false;
945
1055
  let lastAttemptReason;
@@ -1128,6 +1238,10 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1128
1238
  rec.output = boundedRedactedSummary(salvaged.structuredOutput ?? salvaged.result, MAX_TRANSCRIPT_CHARS) || (rec.errorMessage ?? "");
1129
1239
  rec.stats = { tokens: salvaged.stats.tokens ?? 0, turns: salvaged.stats.turns ?? 0, costMicroUsd: salvaged.stats.costMicroUsd };
1130
1240
  }
1241
+ if (err instanceof WorkflowAgentBlockedError) {
1242
+ rec.errorCode = WORKFLOW_SPAWN_BLOCKED_ERROR_CODE;
1243
+ rec.errorMessage = boundedRedactedSummary(err.message, MAX_TRANSCRIPT_CHARS);
1244
+ }
1131
1245
  if (err instanceof WorkflowAgentStalledError && err.attempts > 1) {
1132
1246
  rec.attempts = err.attempts;
1133
1247
  rec.lastAttemptReason = "stalled";
@@ -1139,6 +1253,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1139
1253
  sessionId: "",
1140
1254
  status: "failed",
1141
1255
  result: boundedRedactedSummary(err instanceof Error ? err.message : String(err), 500),
1256
+ ...(err instanceof WorkflowAgentBlockedError ? { errorCode: WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } : {}),
1142
1257
  stats: { turns: 0, tokens: 0, costMicroUsd: 0 },
1143
1258
  }, label).catch(() => undefined);
1144
1259
  bceTerminal(callKey, "failed", rec.output ?? (err instanceof Error ? err.message : String(err)), rec.sessionId, rec.stats);
@@ -1184,7 +1299,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1184
1299
  if (!finalized) {
1185
1300
  rec.status = "failed";
1186
1301
  rec.endedAt = now();
1187
- emit({ type: "agent_end", runId, label, phase, ...(groupId !== undefined ? { groupId } : {}), status: "failed", ts: rec.endedAt });
1302
+ emit({ type: "agent_end", runId, label, phase, ...(groupId !== undefined ? { groupId } : {}), status: "failed", ...(rec.errorCode !== undefined ? { errorCode: rec.errorCode } : {}), ts: rec.endedAt });
1188
1303
  void persist("update");
1189
1304
  }
1190
1305
  };
@@ -1205,7 +1320,8 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1205
1320
  const baseRunSpec = agentOpts.schema
1206
1321
  ? { ...framedSpec, ...authInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1207
1322
  : { ...framedSpec, ...authInherit, signal: effectiveSignal };
1208
- const runSpec = childSessionId !== undefined ? { ...baseRunSpec, sessionId: childSessionId } : baseRunSpec;
1323
+ const preReviewSpec = childSessionId !== undefined ? { ...baseRunSpec, sessionId: childSessionId } : baseRunSpec;
1324
+ const runSpec = await reviewSpawnBeforeLaunch("ctx.agentStream", label, callKey, preReviewSpec, effectiveSignal);
1209
1325
  const enrichedForwardS = opts.onForwardEvent !== undefined
1210
1326
  ? (e) => {
1211
1327
  opts.onForwardEvent(e.type === "task_progress" ? { ...e, workflowRunId: runId, workflowAgentLabel: label } : e);
@@ -1216,6 +1332,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1216
1332
  ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}),
1217
1333
  ...spawnAttribution,
1218
1334
  ...(enrichedForwardS !== undefined ? { onForwardEvent: enrichedForwardS } : {}),
1335
+ delegationTaskType: "workflow",
1219
1336
  agentName: label,
1220
1337
  onActivity,
1221
1338
  onWorkspaceResolved: createWorkspaceObserver(rec),
@@ -1236,6 +1353,18 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1236
1353
  stream = workflowDepthStore.run({ depth: depth + 1 }, () => runner.runTaskStream(runSpec, undefined, runInternals));
1237
1354
  }
1238
1355
  catch (err) {
1356
+ if (!finalized && err instanceof WorkflowAgentBlockedError) {
1357
+ rec.errorCode = WORKFLOW_SPAWN_BLOCKED_ERROR_CODE;
1358
+ rec.errorMessage = boundedRedactedSummary(err.message, MAX_TRANSCRIPT_CHARS);
1359
+ void journalAppend(callKey, {
1360
+ taskId: callKey,
1361
+ sessionId: "",
1362
+ status: "failed",
1363
+ result: boundedRedactedSummary(err.message, 500),
1364
+ errorCode: WORKFLOW_SPAWN_BLOCKED_ERROR_CODE,
1365
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0 },
1366
+ }).catch(() => undefined);
1367
+ }
1239
1368
  recordFailed();
1240
1369
  releaseOnce();
1241
1370
  bceTerminal(callKey, "failed", err instanceof Error ? err.message : String(err), undefined, undefined);
@@ -183,7 +183,7 @@ Use this directory for ALL temporary file needs:
183
183
 
184
184
  Only use \`/tmp\` if the user explicitly requests it.
185
185
 
186
- The scratchpad directory is session-specific, isolated from the user's project, and can generally be used without permission prompts.`;
186
+ The scratchpad directory is session-specific, isolated from the user's project, and can generally be used without permission prompts. Treat it as ephemeral — it may not survive a long suspension or a resume on a different worker; keep durable outputs in the working directory.`;
187
187
  }
188
188
  export const GIT_STATUS_MAX_CHARS = 2000;
189
189
  export function buildGitSnapshot(p) {
@@ -1,9 +1,10 @@
1
- import { type Checkpoint, type CheckpointFaultMode, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type ReopenReason, type ResolveExpectation, type ResumeOutcome } from "../../core/checkpoint-store.js";
1
+ import { type Checkpoint, type PendingSteerInput, type CheckpointFaultMode, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type ReopenReason, type ResolveExpectation, type ResumeOutcome } from "../../core/checkpoint-store.js";
2
2
  export interface FileCheckpointStoreOptions {
3
3
  fsync?: boolean;
4
4
  compactEvery?: number;
5
5
  }
6
6
  export declare class FileCheckpointStore implements CheckpointStore {
7
+ readonly durability: "durable";
7
8
  private readonly fsyncEnabled;
8
9
  private readonly compactEvery;
9
10
  private readonly ledger;
@@ -17,10 +18,7 @@ export declare class FileCheckpointStore implements CheckpointStore {
17
18
  get(token: CheckpointToken): Promise<Checkpoint | null>;
18
19
  resolve(token: CheckpointToken, scope: string, outcome: ResumeOutcome, expect?: ResolveExpectation): Promise<boolean>;
19
20
  reopen(token: CheckpointToken, scope: string, reason: ReopenReason): Promise<boolean>;
20
- setPendingSteer(token: CheckpointToken, scope: string, steer: {
21
- text: string;
22
- trusted: boolean;
23
- }): Promise<boolean>;
21
+ setPendingSteer(token: CheckpointToken, scope: string, steer: PendingSteerInput): Promise<boolean>;
24
22
  expire(token: CheckpointToken, scope: string): Promise<boolean>;
25
23
  reap(scope: string, cutoff: number): Promise<number>;
26
24
  listByScope(scope: string): Promise<CheckpointSummary[]>;
@@ -1,7 +1,20 @@
1
1
  import { join } from "node:path";
2
- import { CheckpointError, checkpointOccMatches, checkpointRowMatches, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js";
2
+ import { appendPendingSteer, CheckpointError, checkpointOccMatches, checkpointRowMatches, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js";
3
3
  import { SharedLedgerTable } from "./shared-ledger.js";
4
+ const CHECKPOINT_LEDGER_EVENT_REGISTRY = {
5
+ put: true,
6
+ resolve: true,
7
+ reopen: true,
8
+ expire: true,
9
+ steer: true,
10
+ steer_append: true,
11
+ };
12
+ const KNOWN_CHECKPOINT_LEDGER_EVENTS = new Set(Object.keys(CHECKPOINT_LEDGER_EVENT_REGISTRY));
4
13
  function applyCheckpointEvent(cps, ev) {
14
+ if (!KNOWN_CHECKPOINT_LEDGER_EVENTS.has(ev.t)) {
15
+ throw new CheckpointError("checkpoint.unsupported_version", `file checkpoint ledger: unrecognised event kind "${String(ev.t)}" — ` +
16
+ `it was written by a newer worker; refusing the replay rather than serving an incomplete authority`);
17
+ }
5
18
  if (ev.t === "put") {
6
19
  cps.set(ev.token, ev.cp);
7
20
  return;
@@ -28,6 +41,17 @@ function applyCheckpointEvent(cps, ev) {
28
41
  case "steer":
29
42
  cp.state.pendingSteer = ev.steer;
30
43
  break;
44
+ case "steer_append": {
45
+ const queue = cp.state.pendingSteerQueue ?? [];
46
+ if (!queue.some((e) => e.inputId === ev.entry.inputId))
47
+ cp.state.pendingSteerQueue = [...queue, ev.entry];
48
+ break;
49
+ }
50
+ default: {
51
+ const _exhaustive = ev;
52
+ void _exhaustive;
53
+ break;
54
+ }
31
55
  }
32
56
  }
33
57
  const checkpointLedgers = new SharedLedgerTable({
@@ -35,6 +59,7 @@ const checkpointLedgers = new SharedLedgerTable({
35
59
  apply: applyCheckpointEvent,
36
60
  });
37
61
  export class FileCheckpointStore {
62
+ durability = "durable";
38
63
  fsyncEnabled;
39
64
  compactEvery;
40
65
  ledger;
@@ -114,7 +139,11 @@ export class FileCheckpointStore {
114
139
  if (!checkpointRowMatches(cp, scope, "pending")) {
115
140
  return false;
116
141
  }
117
- this.commit({ t: "steer", token, steer: clean }, this.fsyncEnabled);
142
+ const next = appendPendingSteer(cp.state, clean);
143
+ const appended = next[next.length - 1];
144
+ if (appended === undefined || appended.inputId !== clean.inputId)
145
+ return true;
146
+ this.commit({ t: "steer_append", token, entry: appended }, this.fsyncEnabled);
118
147
  return true;
119
148
  });
120
149
  }