@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
@@ -38,10 +38,10 @@ export function taskNotificationDedupKey(n) {
38
38
  export function taskNotificationLaneKey(n) {
39
39
  return n.task_type === "external" ? `external:${n.task_id}` : n.task_id;
40
40
  }
41
- function attrEscape(value) {
41
+ export function attrEscape(value) {
42
42
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
43
43
  }
44
- const EXTERNAL_SOURCE_MAX = 120;
44
+ export const EXTERNAL_SOURCE_MAX = 120;
45
45
  export function renderTaskNotificationXml(n) {
46
46
  const usage = n.usage === undefined
47
47
  ? undefined
@@ -54,7 +54,9 @@ export function renderTaskNotificationXml(n) {
54
54
  }
55
55
  })();
56
56
  const open = n.task_type === "external"
57
- ? `<task-notification type="external"${n.source !== undefined ? ` from="${attrEscape(inlineUntrusted(n.source, EXTERNAL_SOURCE_MAX))}"` : ""}>`
57
+ ? `<task-notification type="external"${n.source !== undefined
58
+ ? ` from="${attrEscape(inlineUntrusted(n.source, EXTERNAL_SOURCE_MAX))}" from-unverified="true"`
59
+ : ""}>`
58
60
  : "<task-notification>";
59
61
  return [
60
62
  open,
@@ -28,6 +28,7 @@ export declare function reapDurableAgentsLane(core: DurableAgentCore, scope: str
28
28
  }>;
29
29
  export declare function releaseDurableTranscriptAnchorLane(core: DurableAgentCore, id: string): void;
30
30
  export declare function bindBackgroundAgentSessionLane(core: DurableAgentCore, id: string, sessionId: string): void;
31
+ export declare function recordBackgroundAgentOrgAdmissionLane(core: DurableAgentCore, id: string, verdict: import("./memory-admission.js").OwnOrgAdmissionVerdict): void;
31
32
  export declare function registerBackgroundAgentLane(core: DurableAgentCore, input: RegisterBackgroundAgentInput): string;
32
33
  export declare function parkBackgroundAgentLane(core: DurableAgentCore, id: string, park: {
33
34
  checkpointToken: string;
@@ -247,6 +247,12 @@ export function bindBackgroundAgentSessionLane(core, id, sessionId) {
247
247
  handle.sessionId = sessionId;
248
248
  durableAgentWriteLane(handle, { sessionId });
249
249
  }
250
+ export function recordBackgroundAgentOrgAdmissionLane(core, id, verdict) {
251
+ const handle = core.handles.get(id);
252
+ if (!handle || handle.type !== "background_agent")
253
+ return;
254
+ durableAgentWriteLane(handle, { admittedOrgScopes: [...verdict.scopes], admittedOrgWriteScope: verdict.writeScope });
255
+ }
250
256
  export function registerBackgroundAgentLane(core, input) {
251
257
  assertOwnership(input, "registerBackgroundAgent");
252
258
  if (input.id !== undefined && !DURABLE_AGENT_HANDLE_RE.test(input.id)) {
@@ -101,6 +101,7 @@ export declare class TaskRegistry {
101
101
  releaseDurableTranscriptAnchor(id: string): void;
102
102
  bindBackgroundAgentSession(id: string, sessionId: string): void;
103
103
  registerBackgroundAgent(input: RegisterBackgroundAgentInput): string;
104
+ recordBackgroundAgentOrgAdmission(id: string, verdict: import("./memory-admission.js").OwnOrgAdmissionVerdict): void;
104
105
  parkBackgroundAgent(id: string, park: {
105
106
  checkpointToken: string;
106
107
  seq?: number;
@@ -10,7 +10,7 @@ import { registerWorkflowLane, pollWorkflowLane, stopWorkflowLane } from "./task
10
10
  import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, renderSpoolBody, spoolDropNote, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
11
11
  export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
12
12
  import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
13
- import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
13
+ import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, recordBackgroundAgentOrgAdmissionLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
14
14
  export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
15
15
  const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
16
16
  const BLOCK_MAX_TIMEOUT_MS = 600_000;
@@ -146,6 +146,9 @@ export class TaskRegistry {
146
146
  registerBackgroundAgent(input) {
147
147
  return registerBackgroundAgentLane(this.core, input);
148
148
  }
149
+ recordBackgroundAgentOrgAdmission(id, verdict) {
150
+ return recordBackgroundAgentOrgAdmissionLane(this.core, id, verdict);
151
+ }
149
152
  parkBackgroundAgent(id, park) {
150
153
  return parkBackgroundAgentLane(this.core, id, park);
151
154
  }
@@ -80,12 +80,17 @@ export interface AskRequest {
80
80
  toolCallId: string;
81
81
  args: unknown;
82
82
  readonly preview?: unknown;
83
+ readonly boundInputHash?: string;
83
84
  message: string;
84
85
  readonly principal?: string;
85
86
  readonly sourceTaskId?: string;
86
87
  readonly fromSubagent?: true;
87
88
  readonly sourceAgentName?: string;
88
89
  readonly requiresRealApproval?: boolean;
90
+ readonly riskAxes?: {
91
+ readonly irreversible?: boolean;
92
+ readonly egress?: boolean;
93
+ };
89
94
  readonly delegation?: AskDelegationProvenance;
90
95
  }
91
96
  export type OnAsk = "deny" | "allow" | ((req: AskRequest, signal?: AbortSignal) => AskOutcome | Promise<AskOutcome>);
@@ -1,6 +1,7 @@
1
1
  import { homedir } from "node:os";
2
2
  import { isAbsolute, join, normalize as normalizePath, sep } from "node:path";
3
3
  import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/fs/index.js";
4
+ import { boundInputHashOf } from "./canonical-json.js";
4
5
  import { writeTargetPath } from "../tools/fs/safety.js";
5
6
  export function decisionText(d) {
6
7
  return d.message;
@@ -606,7 +607,7 @@ export async function resolveAsk(req, onAsk, signal) {
606
607
  decisionReason: "mode",
607
608
  };
608
609
  }
609
- ok = await onAsk({ ...req, args: approverView.value }, signal);
610
+ ok = await onAsk({ ...req, boundInputHash: boundInputHashOf(presented.value), args: approverView.value }, signal);
610
611
  }
611
612
  catch (err) {
612
613
  return {
@@ -1,6 +1,6 @@
1
1
  import type { TSchema } from "typebox";
2
2
  import type { AgentTool, ThinkingLevel } from "../internal/harness.js";
3
- import type { CompleteSimpleFn, DocumentContent, ImageContent, Model, ResilienceOptions, StreamFn, TextContent } from "../internal/llm.js";
3
+ import type { ActorAssertion, CompleteSimpleFn, DocumentContent, ImageContent, Model, ResilienceOptions, StreamFn, TextContent } from "../internal/llm.js";
4
4
  import type { TaskNotificationPayload } from "./task-notification.js";
5
5
  export type ModelRef = string | Model;
6
6
  export interface StaleToolResultOffloadOptions {
@@ -129,6 +129,7 @@ export interface ToolExecuteContext {
129
129
  taskId?: string;
130
130
  sessionId?: string;
131
131
  backgroundScope?: "task" | "session";
132
+ interactionPosture?: "interactive" | "headless";
132
133
  onBackgroundChildEvent?: (event: BackgroundChildEvent) => void;
133
134
  onTaskNotification?: (n: import("./task-notification.js").TaskNotificationPayload, opts?: {
134
135
  priority?: import("./task-notification.js").SystemInjectionPriority;
@@ -274,6 +275,9 @@ export interface TaskSpec {
274
275
  taskId?: string;
275
276
  objective: string;
276
277
  principal?: string;
278
+ actor?: ActorAssertion;
279
+ interactionPosture?: "interactive" | "headless";
280
+ interactiveQuestionFallback?: boolean;
277
281
  images?: ImageInput[];
278
282
  sessionId?: string;
279
283
  requireExistingSession?: boolean;
@@ -436,6 +440,10 @@ export interface TaskResult {
436
440
  code: "conversation_only" | "files_env_unsupported" | "snapshot_store_unconfigured";
437
441
  message: string;
438
442
  }>;
443
+ strandedHumanAnswers?: ReadonlyArray<{
444
+ deliveryId: string;
445
+ toolCallId: string;
446
+ }>;
439
447
  stats: {
440
448
  turns: number;
441
449
  tokens: number;
@@ -517,6 +525,9 @@ export interface ToolActivity {
517
525
  arg?: string;
518
526
  isError?: boolean;
519
527
  }
528
+ export type HumanInputSource = "objective" | "steer" | "next_turn" | "wake" | "external" | "system";
529
+ export type HumanInputDelivery = "applied" | "queued" | "parked_for_wake";
530
+ export type DelegationTaskType = Extract<TaskNotificationPayload["task_type"], "background_agent" | "workflow">;
520
531
  export type TaskEvent = ({
521
532
  type: "text_delta";
522
533
  delta: string;
@@ -613,6 +624,17 @@ export type TaskEvent = ({
613
624
  entryId: string;
614
625
  role: "user" | "assistant" | "toolResult";
615
626
  toolCallId?: string;
627
+ } & TaskEventIdentity) | ({
628
+ type: "human_input";
629
+ inputId: string;
630
+ sessionSeq: number;
631
+ carrier: string;
632
+ source: HumanInputSource;
633
+ issuer?: string;
634
+ actor?: ActorAssertion;
635
+ delivery: HumanInputDelivery;
636
+ principal?: string;
637
+ entryId?: string;
616
638
  } & TaskEventIdentity) | ({
617
639
  type: "status";
618
640
  } & BrainStatus & TaskEventIdentity) | ({
@@ -621,6 +643,7 @@ export type TaskEvent = ({
621
643
  } & TaskEventIdentity) | ({
622
644
  type: "task_progress";
623
645
  taskId: string;
646
+ taskType?: DelegationTaskType;
624
647
  workflowRunId?: string;
625
648
  workflowAgentLabel?: string;
626
649
  parentTaskId?: string;
@@ -635,16 +658,23 @@ export type TaskEvent = ({
635
658
  } & TaskEventIdentity) | ({
636
659
  type: "workspace_changed";
637
660
  cwd: string;
661
+ } & TaskEventIdentity) | ({
662
+ type: "wiring_manifest";
663
+ manifest: import("./wiring-manifest.js").WiringManifest;
638
664
  } & TaskEventIdentity) | {
639
665
  type: "done";
640
666
  result: TaskResult;
641
667
  };
668
+ export type HumanInputEvent = Extract<TaskEvent, {
669
+ type: "human_input";
670
+ }>;
642
671
  export type CompactOutcome = "compacted" | "failed" | "mooted" | "noop" | "blocked" | "disabled";
643
672
  export interface TaskStream extends AsyncIterable<TaskEvent> {
644
673
  result(): Promise<TaskResult>;
645
674
  suggestions(): Promise<string[]>;
646
675
  steer(text: string, options?: {
647
676
  trusted?: boolean;
677
+ actor?: ActorAssertion;
648
678
  }): Promise<void>;
649
679
  notify(payload: import("./task-notification.js").ExternalNotificationInput, opts?: {
650
680
  priority?: import("./task-notification.js").SystemInjectionPriority;
@@ -798,6 +828,7 @@ export interface RunnerDeps {
798
828
  toolPolicy?: import("./tool-policy.js").ToolPolicy;
799
829
  onAsk?: import("./tool-policy.js").OnAsk;
800
830
  onQuestion?: import("./ask-question.js").OnQuestion;
831
+ interactionPosture?: "interactive" | "headless";
801
832
  lspManager?: import("./lsp.js").LspServerManager;
802
833
  hooks?: import("./hooks.js").Hooks;
803
834
  allowImageUrl?: (url: string) => boolean;
@@ -0,0 +1,97 @@
1
+ import type { RunnerDeps, TaskSpec } from "./types.js";
2
+ import { type StoreDurability } from "./checkpoint-store.js";
3
+ export type { StoreDurability };
4
+ export type ManifestDurability = "declared_durable" | "process_local";
5
+ export type WiringLegKind = "root" | "child" | "resume";
6
+ export type AskSeamForm = "callback" | "allow" | "deny" | "absent";
7
+ export type AskEffective = "human_reachable" | "auto_allow" | "auto_deny" | "park_only" | "unresolved";
8
+ export type QuestionChannelState = "wired" | "absent" | "stripped_bg_lane";
9
+ export type SeamProvenance = "spec" | "deps";
10
+ export type ParkLaneReason = "no_checkpoint_store" | "no_durable_approval_opt_in" | "no_force_durable_gate" | "no_armed_safety_vocabulary" | "await_runtime_caps" | "await_tool_vocabulary";
11
+ export interface WiringManifest {
12
+ schemaVersion: 1;
13
+ leg?: {
14
+ kind: WiringLegKind;
15
+ };
16
+ ask: {
17
+ form: AskSeamForm;
18
+ provenance?: SeamProvenance;
19
+ effective?: AskEffective;
20
+ };
21
+ question: {
22
+ wired: QuestionChannelState;
23
+ provenance?: SeamProvenance;
24
+ interactiveToolsWithoutDeliveryFace?: true;
25
+ };
26
+ interaction: {
27
+ posture: "interactive" | "headless" | "absent";
28
+ };
29
+ elicit: {
30
+ seamWired: boolean;
31
+ serversOptedIn: number;
32
+ };
33
+ parkLane: {
34
+ capable: boolean;
35
+ effective: boolean | "unresolved";
36
+ reasons: readonly ParkLaneReason[];
37
+ checkpointDurability?: ManifestDurability;
38
+ };
39
+ session: {
40
+ store: ManifestDurability;
41
+ };
42
+ fleet: {
43
+ backgroundAgentStore: boolean;
44
+ hostChildEventSink: boolean;
45
+ };
46
+ governance: {
47
+ audience: "operator";
48
+ lockedConfig: boolean;
49
+ compliance: boolean;
50
+ memoryAdmission: boolean;
51
+ retention: boolean;
52
+ };
53
+ configFingerprint?: string;
54
+ }
55
+ export interface WiringFacts {
56
+ half: "static" | "effective";
57
+ leg?: WiringLegKind;
58
+ askForm: AskSeamForm;
59
+ askProvenance?: SeamProvenance;
60
+ questionWired: boolean;
61
+ questionProvenance?: SeamProvenance;
62
+ questionStrippedByEngine?: boolean;
63
+ interactiveToolsWithoutDeliveryFace?: boolean;
64
+ interactionPosture?: "interactive" | "headless";
65
+ elicitSeamWired: boolean;
66
+ elicitServersOptedIn: number;
67
+ parkCapable: boolean;
68
+ parkDurableApprovalOptIn: boolean;
69
+ parkForceDurableGate?: boolean;
70
+ parkSafetyVocabularyArmed?: boolean;
71
+ checkpointDurability?: StoreDurability;
72
+ sessionDurability: StoreDurability;
73
+ backgroundAgentStoreWired: boolean;
74
+ hostChildEventSinkWired: boolean;
75
+ lockedConfigWired: boolean;
76
+ complianceWired: boolean;
77
+ memoryAdmissionWired: boolean;
78
+ retentionPolicyWired: boolean;
79
+ }
80
+ export type StaticWiringDeps = Pick<RunnerDeps, "onAsk" | "onQuestion" | "interactionPosture" | "onElicit" | "checkpointStore" | "sessionStore" | "backgroundAgentStore" | "onBackgroundChildEvent" | "lockedConfig" | "compliancePostureResolver" | "memoryScopeAdmission" | "retentionPolicy">;
81
+ export type StaticWiringSpec = Pick<TaskSpec, "onAsk" | "onQuestion" | "checkpointStore" | "durableApproval" | "mcp" | "interactiveTools" | "interactionPosture">;
82
+ export declare function resolveDeclaredDurability(store: {
83
+ readonly durability?: StoreDurability;
84
+ } | undefined, storeName: string): StoreDurability;
85
+ export declare function deriveAskEffective(form: AskSeamForm, parkEffective: boolean | "unresolved"): AskEffective;
86
+ export declare function deriveWiringManifest(facts: WiringFacts): WiringManifest;
87
+ export declare function resolveAskSeamForm(spec: Pick<TaskSpec, "onAsk">, deps: Pick<RunnerDeps, "onAsk">): {
88
+ form: AskSeamForm;
89
+ provenance?: SeamProvenance;
90
+ };
91
+ export declare function resolveQuestionSeam(spec: Pick<TaskSpec, "onQuestion">, deps: Pick<RunnerDeps, "onQuestion">): {
92
+ wired: boolean;
93
+ provenance?: SeamProvenance;
94
+ };
95
+ export declare function countElicitOptIns(spec: Pick<TaskSpec, "mcp">): number;
96
+ export declare function resolveElicitSeam(deps: Pick<RunnerDeps, "onElicit">): boolean;
97
+ export declare function describeStaticWiring(deps: StaticWiringDeps, spec?: StaticWiringSpec): WiringManifest;
@@ -0,0 +1,186 @@
1
+ import { createHash } from "node:crypto";
2
+ import { canonicalize } from "./canonical-json.js";
3
+ import { resolveCheckpointStore } from "./checkpoint-store.js";
4
+ import { isLiveQuestionFace } from "./ask-question.js";
5
+ export function resolveDeclaredDurability(store, storeName) {
6
+ const declared = store?.durability;
7
+ if (declared === undefined)
8
+ return "process-local";
9
+ if (declared === "durable" || declared === "process-local")
10
+ return declared;
11
+ const e = new Error(`${storeName}.durability declares ${JSON.stringify(declared)} — not a recognized StoreDurability ` +
12
+ `("durable" | "process-local"). Fix the declaration; an unparseable durability cannot be folded to either arm.`);
13
+ e.code = "config.store_durability_invalid";
14
+ throw e;
15
+ }
16
+ const manifestDurabilityOf = (d) => (d === "durable" ? "declared_durable" : "process_local");
17
+ export function deriveAskEffective(form, parkEffective) {
18
+ switch (form) {
19
+ case "callback":
20
+ return "human_reachable";
21
+ case "allow":
22
+ return "auto_allow";
23
+ case "deny":
24
+ return "auto_deny";
25
+ case "absent":
26
+ return parkEffective === true ? "park_only" : parkEffective === "unresolved" ? "unresolved" : "auto_deny";
27
+ default: {
28
+ const _exhaustive = form;
29
+ void _exhaustive;
30
+ throw new Error(`unreachable ask form ${String(form)}`);
31
+ }
32
+ }
33
+ }
34
+ function deriveParkLane(facts) {
35
+ if (!facts.parkCapable) {
36
+ return { capable: false, effective: false, reasons: ["no_checkpoint_store"] };
37
+ }
38
+ const durability = facts.checkpointDurability !== undefined ? { checkpointDurability: manifestDurabilityOf(facts.checkpointDurability) } : {};
39
+ if (facts.parkDurableApprovalOptIn || facts.parkForceDurableGate === true || facts.parkSafetyVocabularyArmed === true) {
40
+ return { capable: true, effective: true, reasons: [], ...durability };
41
+ }
42
+ const reasons = ["no_durable_approval_opt_in"];
43
+ const unresolved = facts.parkForceDurableGate === undefined || facts.parkSafetyVocabularyArmed === undefined;
44
+ if (facts.parkForceDurableGate === undefined)
45
+ reasons.push("await_runtime_caps");
46
+ else
47
+ reasons.push("no_force_durable_gate");
48
+ if (facts.parkSafetyVocabularyArmed === undefined)
49
+ reasons.push("await_tool_vocabulary");
50
+ else
51
+ reasons.push("no_armed_safety_vocabulary");
52
+ return { capable: true, effective: unresolved ? "unresolved" : false, reasons, ...durability };
53
+ }
54
+ export function deriveWiringManifest(facts) {
55
+ if (facts.half === "static" && facts.leg !== undefined) {
56
+ throw new Error("a static wiring manifest has no leg — leg identity is an effective-half fact");
57
+ }
58
+ if (facts.half === "effective" && facts.leg === undefined) {
59
+ throw new Error("an effective wiring manifest requires its leg kind");
60
+ }
61
+ if (facts.half === "effective" && (facts.parkForceDurableGate === undefined || facts.parkSafetyVocabularyArmed === undefined)) {
62
+ throw new Error("an effective wiring manifest requires the resolved park atoms (parkForceDurableGate, parkSafetyVocabularyArmed) — " +
63
+ "`unresolved` is a static-half fact; report the static half instead, or resolve the atoms first.");
64
+ }
65
+ const parkLane = deriveParkLane(facts);
66
+ const questionChannel = facts.questionWired
67
+ ? "wired"
68
+ : facts.half === "effective" && facts.questionStrippedByEngine === true
69
+ ? "stripped_bg_lane"
70
+ : "absent";
71
+ const manifest = {
72
+ schemaVersion: 1,
73
+ ...(facts.leg !== undefined ? { leg: { kind: facts.leg } } : {}),
74
+ ask: {
75
+ form: facts.askForm,
76
+ ...(facts.askProvenance !== undefined ? { provenance: facts.askProvenance } : {}),
77
+ ...(facts.half === "effective" ? { effective: deriveAskEffective(facts.askForm, parkLane.effective) } : {}),
78
+ },
79
+ question: {
80
+ wired: questionChannel,
81
+ ...(questionChannel === "wired" && facts.questionProvenance !== undefined ? { provenance: facts.questionProvenance } : {}),
82
+ ...(facts.interactiveToolsWithoutDeliveryFace === true ? { interactiveToolsWithoutDeliveryFace: true } : {}),
83
+ },
84
+ interaction: { posture: facts.interactionPosture ?? "absent" },
85
+ elicit: { seamWired: facts.elicitSeamWired, serversOptedIn: facts.elicitServersOptedIn },
86
+ parkLane,
87
+ session: { store: manifestDurabilityOf(facts.sessionDurability) },
88
+ fleet: { backgroundAgentStore: facts.backgroundAgentStoreWired, hostChildEventSink: facts.hostChildEventSinkWired },
89
+ governance: {
90
+ audience: "operator",
91
+ lockedConfig: facts.lockedConfigWired,
92
+ compliance: facts.complianceWired,
93
+ memoryAdmission: facts.memoryAdmissionWired,
94
+ retention: facts.retentionPolicyWired,
95
+ },
96
+ };
97
+ if (facts.half === "effective") {
98
+ const { leg: _leg, ...assembly } = manifest;
99
+ const { provenance: _askSeat, ...askForHash } = assembly.ask;
100
+ const { provenance: _questionSeat, ...questionForHash } = assembly.question;
101
+ manifest.configFingerprint = createHash("sha256")
102
+ .update(canonicalize({ ...assembly, ask: askForHash, question: questionForHash }))
103
+ .digest("hex")
104
+ .slice(0, 16);
105
+ }
106
+ return manifest;
107
+ }
108
+ export function resolveAskSeamForm(spec, deps) {
109
+ const seat = spec.onAsk !== undefined ? { value: spec.onAsk, provenance: "spec" } : deps.onAsk !== undefined ? { value: deps.onAsk, provenance: "deps" } : undefined;
110
+ if (seat === undefined)
111
+ return { form: "absent" };
112
+ const v = seat.value;
113
+ const form = typeof v === "function" ? "callback" : v === "allow" ? "allow" : v === "deny" ? "deny" : undefined;
114
+ if (form === undefined) {
115
+ const e = new Error(`the resolved onAsk seat (${seat.provenance}) holds ${JSON.stringify(v)} — not an OnAsk ` +
116
+ `("allow" | "deny" | approver function); refusing to classify it.`);
117
+ e.code = "config.interaction_wiring";
118
+ throw e;
119
+ }
120
+ return { form, provenance: seat.provenance };
121
+ }
122
+ export function resolveQuestionSeam(spec, deps) {
123
+ const seat = spec.onQuestion !== undefined ? { value: spec.onQuestion, provenance: "spec" } : deps.onQuestion !== undefined ? { value: deps.onQuestion, provenance: "deps" } : undefined;
124
+ if (seat === undefined)
125
+ return { wired: false };
126
+ if (typeof seat.value !== "function") {
127
+ const e = new Error(`the resolved onQuestion seat (${seat.provenance}) holds ${seat.value === null ? "null" : typeof seat.value} — not an OnQuestion ` +
128
+ `callback; refusing to report it as a wired question channel (omit the key, or wire a function).`);
129
+ e.code = "config.interaction_wiring";
130
+ throw e;
131
+ }
132
+ if (!isLiveQuestionFace(seat.value))
133
+ return { wired: false };
134
+ return { wired: true, provenance: seat.provenance };
135
+ }
136
+ export function countElicitOptIns(spec) {
137
+ return (spec.mcp ?? []).filter((s) => s.elicitation === true).length;
138
+ }
139
+ export function resolveElicitSeam(deps) {
140
+ if (deps.onElicit === undefined)
141
+ return false;
142
+ if (typeof deps.onElicit !== "function") {
143
+ const e = new Error(`RunnerDeps.onElicit holds ${deps.onElicit === null ? "null" : typeof deps.onElicit} — not an OnElicit ` +
144
+ `callback; refusing to report it as a wired elicitation seam (omit the key, or wire a function).`);
145
+ e.code = "config.interaction_wiring";
146
+ throw e;
147
+ }
148
+ return true;
149
+ }
150
+ export function describeStaticWiring(deps, spec = {}) {
151
+ const ask = resolveAskSeamForm(spec, deps);
152
+ const question = resolveQuestionSeam(spec, deps);
153
+ const checkpointStore = resolveCheckpointStore(spec, deps);
154
+ const capable = checkpointStore !== undefined;
155
+ return deriveWiringManifest({
156
+ half: "static",
157
+ askForm: ask.form,
158
+ ...(ask.provenance !== undefined ? { askProvenance: ask.provenance } : {}),
159
+ questionWired: question.wired,
160
+ ...(question.provenance !== undefined ? { questionProvenance: question.provenance } : {}),
161
+ ...(() => {
162
+ const posture = spec.interactionPosture ?? deps.interactionPosture;
163
+ if (posture === undefined)
164
+ return {};
165
+ if (posture !== "interactive" && posture !== "headless") {
166
+ const e = new Error(`interactionPosture ${JSON.stringify(posture)} is not a recognized posture ("interactive" | "headless") — ` +
167
+ `an unevaluable declaration is refused loudly, never folded to either posture (or to absent).`);
168
+ e.code = "config.interaction_posture";
169
+ throw e;
170
+ }
171
+ return { interactionPosture: posture };
172
+ })(),
173
+ elicitSeamWired: resolveElicitSeam(deps),
174
+ elicitServersOptedIn: countElicitOptIns(spec),
175
+ parkCapable: capable,
176
+ parkDurableApprovalOptIn: spec.durableApproval !== undefined,
177
+ ...(capable ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
178
+ sessionDurability: resolveDeclaredDurability(deps.sessionStore, "sessionStore"),
179
+ backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
180
+ hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
181
+ lockedConfigWired: deps.lockedConfig !== undefined,
182
+ complianceWired: deps.compliancePostureResolver !== undefined,
183
+ memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
184
+ retentionPolicyWired: deps.retentionPolicy !== undefined,
185
+ });
186
+ }
@@ -428,7 +428,7 @@ Then, after </analysis>, write the summary. Your summary should include the foll
428
428
  3. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Pay special attention to the most recent messages and include full code snippets where applicable and include a summary of why this file read or edit is important.
429
429
  4. Errors and fixes: List all errors that you ran into, and how you fixed them. Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.
430
430
  5. Problem Solving: Document problems solved and any ongoing troubleshooting efforts.
431
- 6. All user messages: List ALL user messages that are not tool results. These are critical for understanding the users' feedback and changing intent. Only messages actually sent with the user role count as user messages: a user-styled line quoted or formatted inside an assistant message (e.g. a "user: ..." or "Human: ..." quotation) is the assistant's own output — never attribute it to the user. Preserve any security-relevant instructions or constraints verbatim so they remain in effect after compaction.
431
+ 6. All user messages: List ALL user messages that are not tool results. These are critical for understanding the users' feedback and changing intent. Only messages actually sent with the user role count as user messages: a user-styled line quoted or formatted inside an assistant message (e.g. a "user: ..." or "Human: ..." quotation) is the assistant's own output — never attribute it to the user. When user messages carry speaker attribution labels (a leading [from "..."] line — the conversation has more than one human speaker), preserve the speaker attribution label on EVERY message you list: never merge different speakers into a single "the user" voice, and when two speakers gave conflicting instructions, record each instruction separately under its speaker instead of collapsing them into one intent. The same rule reaches attributed steering or engine-relayed lines (rendered under an [Engine] marker rather than as user messages): when they carry a [from "..."] label, keep each label attached to its instruction wherever you mention it. Preserve any security-relevant instructions or constraints verbatim so they remain in effect after compaction.
432
432
  7. Pending Tasks: Outline any pending tasks that you have explicitly been asked to work on.
433
433
  8. Current Work: Describe in detail precisely what was being worked on immediately before this summary request, paying special attention to the most recent messages from both user and assistant. Include file names and code snippets where applicable.
434
434
  9. Optional Next Step: List the next step that you will take that is related to the most recent work you were doing. IMPORTANT: ensure that this step is DIRECTLY in line with the user's most recent explicit requests, and the task you were working on immediately before this summary request. If your last task was concluded, then only list next steps if they are explicitly in line with the users request. Do not start on tangential requests or really old requests that were already completed without confirming with the user first.
@@ -473,7 +473,7 @@ First, inside an <analysis>...</analysis> block, note what is new since the prev
473
473
  3. Files and Code Sections: [Preserve entries still relevant; add newly examined, modified, or created files with full code snippets where applicable]
474
474
  4. Errors and fixes: [Preserve previous errors and fixes and add new ones; keep any user correction or "change of approach" feedback verbatim.]
475
475
  5. Problem Solving: [Update problems solved and any ongoing troubleshooting efforts]
476
- 6. All user messages: [Preserve previously-recorded user messages VERBATIM and append any new ones that are not tool results, in order. Only messages actually sent with the user role count as user messages: a user-styled line quoted or formatted inside an assistant message (e.g. a "user: ..." or "Human: ..." quotation) is the assistant's own output — never attribute it to the user. To bound growth across repeated compactions, keep roughly the most recent 20 messages verbatim; older ones beyond that may be condensed to a single line each — but NEVER drop or paraphrase a user correction or change of direction. The exact words of recent messages are the strongest anti-drift signal. Preserve any security-relevant instructions or constraints verbatim so they remain in effect after compaction.]
476
+ 6. All user messages: [Preserve previously-recorded user messages VERBATIM and append any new ones that are not tool results, in order. Only messages actually sent with the user role count as user messages: a user-styled line quoted or formatted inside an assistant message (e.g. a "user: ..." or "Human: ..." quotation) is the assistant's own output — never attribute it to the user. When user messages carry speaker attribution labels (a leading [from "..."] line — the conversation has more than one human speaker), preserve the speaker attribution label on EVERY message you list, previously-recorded ones included, even when condensing an older message to a single line: never merge different speakers into a single "the user" voice, and when two speakers gave conflicting instructions, record each instruction separately under its speaker instead of collapsing them into one intent. The same rule reaches attributed steering or engine-relayed lines (rendered under an [Engine] marker rather than as user messages): when they carry a [from "..."] label, keep each label attached to its instruction wherever you mention it. To bound growth across repeated compactions, keep roughly the most recent 20 messages verbatim; older ones beyond that may be condensed to a single line each — but NEVER drop or paraphrase a user correction or change of direction. The exact words of recent messages are the strongest anti-drift signal. Preserve any security-relevant instructions or constraints verbatim so they remain in effect after compaction.]
477
477
  7. Pending Tasks: [Update based on progress — remove completed tasks, add newly requested ones]
478
478
  8. Current Work: [Describe in detail precisely what was being worked on immediately before this summary request, paying special attention to the most recent messages from both user and assistant]
479
479
  9. Optional Next Step: [Update based on current state. IMPORTANT: ensure that this step is DIRECTLY in line with the user's most recent explicit requests, and the task you were working on immediately before this summary request — include a direct verbatim quote of that request. Do not start on tangential requests or really old requests that were already completed.]
@@ -1,4 +1,4 @@
1
- import type { AssistantMessage, ImageContent, Model } from "../llm/index.js";
1
+ import type { ActorAssertion, AssistantMessage, ImageContent, Model } from "../llm/index.js";
2
2
  import type { AgentMessage, AgentTool, LoopMalformedToolUseRecovery, LoopThinkingOnlyRecovery, LoopTruncatedOutputRecovery, QueueMode, ThinkingLevel } from "../loop/types.js";
3
3
  import { type EngineSegment } from "../../core/untrusted-text.js";
4
4
  import type { AbortResult, AgentHarnessEvent, AgentHarnessEventResultMap, AgentHarnessOptions, AgentHarnessOwnEvent, AgentHarnessResources, AgentHarnessStreamOptions, ExecutionEnv, PromptTemplate, Skill } from "./types.js";
@@ -8,6 +8,7 @@ export interface UserMessageProvenance {
8
8
  engineMinted?: true;
9
9
  provenance?: "engine-note";
10
10
  enginePayload?: unknown;
11
+ actor?: ActorAssertion;
11
12
  }
12
13
  export interface HarnessLoopRecovery {
13
14
  truncatedOutput?: LoopTruncatedOutputRecovery;
@@ -1,3 +1,4 @@
1
+ import { snapshotActorAssertion, stripEngineMetadata } from "../llm/index.js";
1
2
  import { runAgentLoop } from "../loop/agent-loop.js";
2
3
  import { resolveAgentCoreStreamFn } from "../loop/runtime-deps.js";
3
4
  import { normalizeEngineSegments } from "../../core/untrusted-text.js";
@@ -24,6 +25,12 @@ function createUserMessage(text, images, provenance) {
24
25
  : {}),
25
26
  ...(provenance?.engineMinted === true && !engineNote ? { engineMinted: true } : {}),
26
27
  ...(engineNote ? { provenance: "engine-note" } : {}),
28
+ ...(provenance?.actor !== undefined &&
29
+ typeof provenance.actor.id === "string" &&
30
+ provenance.actor.id.length > 0 &&
31
+ typeof provenance.actor.hostAsserted === "boolean"
32
+ ? { actor: snapshotActorAssertion(provenance.actor) }
33
+ : {}),
27
34
  };
28
35
  }
29
36
  const engineNotePayloads = new WeakMap();
@@ -455,7 +462,7 @@ export class AgentHarness {
455
462
  ...(this.streamingToolExecution === true && (this.getHandlers("tool_call")?.size ?? 0) === 0
456
463
  ? { streamingToolExecution: true }
457
464
  : {}),
458
- convertToLlm,
465
+ convertToLlm: (messages) => stripEngineMetadata(convertToLlm(messages)),
459
466
  shouldStopAfterTurn: () => this._stopAfterTurn,
460
467
  transformContext: async (messages) => {
461
468
  const result = await this.emitHook({ type: "context", messages: [...messages] });
@@ -336,7 +336,9 @@ export interface SessionRepo<TMetadata extends SessionMetadata = SessionMetadata
336
336
  delete(metadata: TMetadata): Promise<void>;
337
337
  fork(source: TMetadata, options: SessionForkOptions & TCreateOptions): Promise<Session<TMetadata>>;
338
338
  exportEntries?(sessionId: string): Promise<SessionTreeEntry[]>;
339
- importEntries?(sessionId: string, owner: string | undefined, entries: SessionTreeEntry[]): Promise<void>;
339
+ importEntries?(sessionId: string, owner: string | undefined, entries: SessionTreeEntry[], options?: {
340
+ preserveActorAssertions?: boolean;
341
+ }): Promise<void>;
340
342
  }
341
343
  export interface JsonlSessionCreateOptions extends SessionCreateOptions {
342
344
  cwd: string;
@@ -110,6 +110,12 @@ export interface Usage {
110
110
  };
111
111
  }
112
112
  export type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";
113
+ export interface ActorAssertion {
114
+ id: string;
115
+ hostAsserted: boolean;
116
+ issuer?: string;
117
+ }
118
+ export declare function snapshotActorAssertion(actor: ActorAssertion): ActorAssertion;
113
119
  export interface UserMessage {
114
120
  role: "user";
115
121
  content: string | (TextContent | ImageContent)[];
@@ -121,6 +127,7 @@ export interface UserMessage {
121
127
  }>;
122
128
  engineMinted?: true;
123
129
  provenance?: "engine-note";
130
+ actor?: ActorAssertion;
124
131
  }
125
132
  export interface AssistantMessage {
126
133
  role: "assistant";
@@ -1,8 +1,15 @@
1
+ export function snapshotActorAssertion(actor) {
2
+ return {
3
+ id: actor.id,
4
+ hostAsserted: actor.hostAsserted,
5
+ ...(actor.issuer !== undefined ? { issuer: actor.issuer } : {}),
6
+ };
7
+ }
1
8
  export function stripEngineMetadata(messages) {
2
9
  return messages.map((m) => {
3
10
  if (m.role !== "user")
4
11
  return m;
5
- const { enginePrefixChars: _a, engineSegments: _b, engineMinted: _c, provenance: _d, ...rest } = m;
12
+ const { enginePrefixChars: _a, engineSegments: _b, engineMinted: _c, provenance: _d, actor: _e, ...rest } = m;
6
13
  return rest;
7
14
  });
8
15
  }
@@ -1,13 +1,18 @@
1
1
  import { type SessionTreeEntry } from "../harness/types.js";
2
+ export interface ImportValidatorOptions {
3
+ preserveActorAssertions?: boolean;
4
+ }
2
5
  export declare class StreamingImportValidator {
3
6
  private readonly seen;
4
7
  private readonly parentOf;
5
8
  private rootCount;
6
9
  private runningLeaf;
7
10
  private done;
11
+ private readonly preserveActorAssertions;
12
+ constructor(options?: ImportValidatorOptions);
8
13
  step(entry: SessionTreeEntry): void;
9
14
  finish(): {
10
15
  leafId: string | null;
11
16
  };
12
17
  }
13
- export declare function validateEntriesForImport(entries: SessionTreeEntry[]): SessionTreeEntry[];
18
+ export declare function validateEntriesForImport(entries: SessionTreeEntry[], options?: ImportValidatorOptions): SessionTreeEntry[];