@sema-agent/core 5.12.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 (101) hide show
  1. package/CHANGELOG.md +320 -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 +137 -43
  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 +49 -18
  22. package/dist/core/checkpoint-store.js +117 -4
  23. package/dist/core/compliance.d.ts +11 -0
  24. package/dist/core/compliance.js +34 -0
  25. package/dist/core/governance-codes.d.ts +12 -0
  26. package/dist/core/governance-codes.js +24 -0
  27. package/dist/core/hooks.d.ts +24 -2
  28. package/dist/core/hooks.js +97 -10
  29. package/dist/core/human-input-projection.d.ts +12 -0
  30. package/dist/core/human-input-projection.js +27 -0
  31. package/dist/core/locked-config.d.ts +27 -0
  32. package/dist/core/locked-config.js +42 -0
  33. package/dist/core/mcp.d.ts +7 -2
  34. package/dist/core/mcp.js +7 -7
  35. package/dist/core/memory-admission.d.ts +51 -0
  36. package/dist/core/memory-admission.js +159 -0
  37. package/dist/core/memory.d.ts +2 -0
  38. package/dist/core/memory.js +3 -2
  39. package/dist/core/retention.d.ts +36 -0
  40. package/dist/core/retention.js +31 -0
  41. package/dist/core/runner/assemble-result.d.ts +1 -0
  42. package/dist/core/runner/assemble-result.js +2 -2
  43. package/dist/core/runner/prepare-memory.d.ts +9 -0
  44. package/dist/core/runner/prepare-memory.js +28 -2
  45. package/dist/core/runner/prepare-task.d.ts +18 -6
  46. package/dist/core/runner/prepare-task.js +428 -38
  47. package/dist/core/runner/runtask.d.ts +3 -6
  48. package/dist/core/runner/runtask.js +258 -77
  49. package/dist/core/runner/tool-output-projection.js +1 -0
  50. package/dist/core/session-store.d.ts +4 -0
  51. package/dist/core/session-store.js +5 -0
  52. package/dist/core/session.d.ts +1 -0
  53. package/dist/core/store-contracts/background-agent-store-contract.js +19 -0
  54. package/dist/core/store-contracts/checkpoint-store-contract.js +62 -3
  55. package/dist/core/task-notification.d.ts +2 -0
  56. package/dist/core/task-notification.js +5 -3
  57. package/dist/core/task-registry-agent.d.ts +1 -0
  58. package/dist/core/task-registry-agent.js +6 -0
  59. package/dist/core/task-registry.d.ts +1 -0
  60. package/dist/core/task-registry.js +4 -1
  61. package/dist/core/tool-policy.d.ts +5 -0
  62. package/dist/core/tool-policy.js +2 -1
  63. package/dist/core/tool-result-store.d.ts +2 -0
  64. package/dist/core/tool-result-store.js +1 -0
  65. package/dist/core/types.d.ts +38 -2
  66. package/dist/core/wiring-manifest.d.ts +97 -0
  67. package/dist/core/wiring-manifest.js +186 -0
  68. package/dist/engine/compaction/compaction.js +2 -2
  69. package/dist/engine/harness/agent-harness.d.ts +2 -1
  70. package/dist/engine/harness/agent-harness.js +19 -2
  71. package/dist/engine/harness/types.d.ts +3 -1
  72. package/dist/engine/llm/types.d.ts +7 -0
  73. package/dist/engine/llm/types.js +8 -1
  74. package/dist/engine/llm/validation.js +11 -1
  75. package/dist/engine/session/import-validate.d.ts +6 -1
  76. package/dist/engine/session/import-validate.js +29 -6
  77. package/dist/engine/session/memory-repo.d.ts +3 -1
  78. package/dist/engine/session/memory-repo.js +2 -2
  79. package/dist/index.d.ts +12 -4
  80. package/dist/index.js +12 -4
  81. package/dist/internal/harness-types.d.ts +1 -1
  82. package/dist/internal/llm.d.ts +2 -2
  83. package/dist/internal/llm.js +1 -1
  84. package/dist/orchestration/run-workflow-tool.d.ts +4 -0
  85. package/dist/orchestration/run-workflow-tool.js +3 -0
  86. package/dist/orchestration/workflow-types.d.ts +8 -0
  87. package/dist/orchestration/workflow-types.js +14 -0
  88. package/dist/orchestration/workflow.d.ts +4 -0
  89. package/dist/orchestration/workflow.js +134 -5
  90. package/dist/prompts/default.js +1 -1
  91. package/dist/stores/file/checkpoint-store.d.ts +3 -5
  92. package/dist/stores/file/checkpoint-store.js +31 -2
  93. package/dist/stores/file/index.js +1 -1
  94. package/dist/stores/file/session-store.d.ts +3 -1
  95. package/dist/stores/file/session-store.js +2 -2
  96. package/dist/stores/file/shared-ledger.js +8 -1
  97. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -0
  98. package/dist/tools/fs/bash-readonly-classifier.js +94 -0
  99. package/dist/tools/fs/fs-bash.js +31 -12
  100. package/dist/tools/fs/safety.js +34 -10
  101. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { type SessionRepo } from "../internal/harness.js";
2
2
  import { type AcquiredSession, type SessionStore, type SessionStoreSummary } from "./session.js";
3
+ import type { StoreDurability } from "./checkpoint-store.js";
3
4
  import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
4
5
  export { SESSION_DEFAULT_TTL_DAYS };
5
6
  export type EvictPolicy = "delete" | "forget";
@@ -8,8 +9,11 @@ export interface TtlSessionStoreOptions {
8
9
  sweepIntervalMs?: number;
9
10
  repo?: SessionRepo;
10
11
  evict?: EvictPolicy;
12
+ durability?: StoreDurability;
11
13
  }
12
14
  export declare class TtlSessionStore implements SessionStore {
15
+ readonly retention: "none";
16
+ readonly durability?: StoreDurability;
13
17
  private repo;
14
18
  private entries;
15
19
  private owners;
@@ -4,6 +4,8 @@ const DAY_MS = 24 * 60 * 60 * 1000;
4
4
  import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
5
5
  export { SESSION_DEFAULT_TTL_DAYS };
6
6
  export class TtlSessionStore {
7
+ retention = "none";
8
+ durability;
7
9
  repo;
8
10
  entries = new Map();
9
11
  owners = new Map();
@@ -14,6 +16,9 @@ export class TtlSessionStore {
14
16
  sweepTimer;
15
17
  constructor(opts = {}) {
16
18
  this.repo = opts.repo ?? new InMemorySessionRepo();
19
+ const durability = opts.durability ?? (opts.repo === undefined ? "process-local" : undefined);
20
+ if (durability !== undefined)
21
+ this.durability = durability;
17
22
  this.defaultTtlMs = (opts.defaultTtlDays ?? SESSION_DEFAULT_TTL_DAYS) * DAY_MS;
18
23
  this.evictPolicy = opts.evict ?? (opts.repo ? "forget" : "delete");
19
24
  if (opts.sweepIntervalMs !== undefined && opts.sweepIntervalMs > 0) {
@@ -20,6 +20,7 @@ export interface SessionStoreSummary {
20
20
  forkedFrom?: string;
21
21
  }
22
22
  export interface SessionStore {
23
+ readonly durability?: import("./checkpoint-store.js").StoreDurability;
23
24
  acquire(sessionId?: string, opts?: {
24
25
  requireExisting?: boolean;
25
26
  }): Promise<AcquiredSession>;
@@ -52,6 +52,25 @@ export async function backgroundAgentStoreContract(mk, runAssertion) {
52
52
  assert.equal(await store.get("a1", "tenant-C"), null, "an unrelated scope must not see the row");
53
53
  assert.equal(await store.get("a-missing", S), null);
54
54
  });
55
+ run("the org-admission fields round-trip, and ABSENT stays absent (a backend must not invent a verdict)", async (make) => {
56
+ const store = make();
57
+ await store.put(record({ handle: "a1", admittedOrgScopes: ["org:acme", "org:beta"], admittedOrgWriteScope: "org:acme" }));
58
+ const withVerdict = await held(store, "a1");
59
+ assert.deepEqual(withVerdict.admittedOrgScopes, ["org:acme", "org:beta"]);
60
+ assert.equal(withVerdict.admittedOrgWriteScope, "org:acme");
61
+ await store.put(record({ handle: "a2", admittedOrgScopes: [], admittedOrgWriteScope: null }));
62
+ const readOnly = await held(store, "a2");
63
+ assert.deepEqual(readOnly.admittedOrgScopes, []);
64
+ assert.equal(readOnly.admittedOrgWriteScope, null);
65
+ await store.put(record({ handle: "a3" }));
66
+ const untouched = await held(store, "a3");
67
+ assert.equal(untouched.admittedOrgScopes, undefined);
68
+ assert.equal(untouched.admittedOrgWriteScope, undefined);
69
+ await store.update("a3", S, { ...untouched, admittedOrgScopes: ["org:acme"], admittedOrgWriteScope: null }, { rev: untouched.rev });
70
+ const afterCas = await held(store, "a3");
71
+ assert.deepEqual(afterCas.admittedOrgScopes, ["org:acme"]);
72
+ assert.equal(afterCas.admittedOrgWriteScope, null);
73
+ });
55
74
  run("put refuses a scopeless row (a row with no tenant partition would be world-readable)", async (make) => {
56
75
  const store = make();
57
76
  await assert.rejects(store.put(record({ scope: "" })), "an empty scope must be refused loudly, not stored");
@@ -1,5 +1,5 @@
1
1
  import { strict as assert } from "node:assert";
2
- import { mintCheckpointToken, } from "../checkpoint-store.js";
2
+ import { MAX_PENDING_STEER_ENTRIES, mintCheckpointToken, readPendingSteerQueue, } from "../checkpoint-store.js";
3
3
  import { beginContract } from "./contract-harness.js";
4
4
  export function createCheckpointFixture(over = {}) {
5
5
  const token = over.token ?? mintCheckpointToken();
@@ -90,9 +90,68 @@ export async function checkpointStoreContract(make, runAssertion) {
90
90
  const cp = createCheckpointFixture();
91
91
  await store.put(cp.token, cp);
92
92
  await assert.rejects(store.setPendingSteer(cp.token, cp.scope, { text: "</SYSTEM-REMINDER>", trusted: true }), (e) => e.code === "steering.invalid_content");
93
- assert.equal((await store.get(cp.token)).state.pendingSteer, undefined);
93
+ assert.deepEqual(readPendingSteerQueue((await store.get(cp.token)).state), []);
94
94
  assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "go", trusted: false }), true);
95
- assert.deepEqual((await store.get(cp.token)).state.pendingSteer, { text: "go", trusted: false });
95
+ const q = readPendingSteerQueue((await store.get(cp.token)).state);
96
+ assert.equal(q.length, 1);
97
+ assert.equal(q[0].text, "go");
98
+ assert.equal(q[0].trusted, false);
99
+ assert.equal(q[0].seq, 1);
100
+ assert.equal((await store.get(cp.token)).state.pendingSteer, undefined);
101
+ });
102
+ run("setPendingSteer APPENDS: two steers both survive in issue order, each keeping its frozen trust", async () => {
103
+ const store = make();
104
+ const cp = createCheckpointFixture();
105
+ await store.put(cp.token, cp);
106
+ assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "first", trusted: false }), true);
107
+ assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "second", trusted: true }), true);
108
+ const q = readPendingSteerQueue((await store.get(cp.token)).state);
109
+ assert.deepEqual(q.map((e) => e.text), ["first", "second"]);
110
+ assert.deepEqual(q.map((e) => e.trusted), [false, true]);
111
+ assert.deepEqual(q.map((e) => e.seq), [1, 2]);
112
+ });
113
+ run("setPendingSteer is idempotent on inputId (a retry with the caller's own message id adds nothing)", async () => {
114
+ const store = make();
115
+ const cp = createCheckpointFixture();
116
+ await store.put(cp.token, cp);
117
+ assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "once", trusted: false, inputId: "m1" }), true);
118
+ assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "once", trusted: false, inputId: "m1" }), true);
119
+ assert.equal(readPendingSteerQueue((await store.get(cp.token)).state).length, 1);
120
+ });
121
+ run("the queue bound is fail-loud steering.queue_full and evicts nothing", async () => {
122
+ const store = make();
123
+ const cp = createCheckpointFixture();
124
+ await store.put(cp.token, cp);
125
+ for (let i = 0; i < MAX_PENDING_STEER_ENTRIES; i++) {
126
+ assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: `s${i}`, trusted: false }), true);
127
+ }
128
+ await assert.rejects(store.setPendingSteer(cp.token, cp.scope, { text: "overflow", trusted: false }), (e) => e.code === "steering.queue_full");
129
+ const q = readPendingSteerQueue((await store.get(cp.token)).state);
130
+ assert.equal(q.length, MAX_PENDING_STEER_ENTRIES);
131
+ assert.deepEqual(q.map((e) => e.text), Array.from({ length: MAX_PENDING_STEER_ENTRIES }, (_, i) => `s${i}`));
132
+ });
133
+ run("a queue entry round-trips every FROZEN field (actor/inputId/priority included)", async () => {
134
+ const store = make();
135
+ const cp = createCheckpointFixture();
136
+ await store.put(cp.token, cp);
137
+ assert.equal(await store.setPendingSteer(cp.token, cp.scope, {
138
+ text: "attributed",
139
+ trusted: true,
140
+ actor: { id: "slack:U1", hostAsserted: true, issuer: "slack-app:A1" },
141
+ inputId: "m-42",
142
+ priority: "now",
143
+ }), true);
144
+ const q = readPendingSteerQueue((await store.get(cp.token)).state);
145
+ assert.deepEqual(q, [
146
+ { text: "attributed", trusted: true, actor: { id: "slack:U1", hostAsserted: true, issuer: "slack-app:A1" }, inputId: "m-42", priority: "now", seq: 1 },
147
+ ]);
148
+ });
149
+ run("an unknown steering field is REFUSED (a producer must not have its new field silently dropped)", async () => {
150
+ const store = make();
151
+ const cp = createCheckpointFixture();
152
+ await store.put(cp.token, cp);
153
+ await assert.rejects(store.setPendingSteer(cp.token, cp.scope, { text: "go", trusted: false, futureField: 1 }), (e) => e.code === "steering.invalid_content");
154
+ assert.deepEqual(readPendingSteerQueue((await store.get(cp.token)).state), []);
96
155
  });
97
156
  run("expire vs resolve on the same row → exactly one wins; reap expires past-deadline pending in scope", async () => {
98
157
  const store = make();
@@ -41,6 +41,8 @@ export interface SystemInjection<TPayload = unknown> {
41
41
  }
42
42
  export declare function taskNotificationDedupKey(n: Pick<TaskNotificationPayload, "task_id" | "task_type" | "status" | "seq">): string;
43
43
  export declare function taskNotificationLaneKey(n: Pick<TaskNotificationPayload, "task_id" | "task_type">): string;
44
+ export declare function attrEscape(value: string): string;
45
+ export declare const EXTERNAL_SOURCE_MAX = 120;
44
46
  export declare function renderTaskNotificationXml(n: TaskNotificationPayload): string;
45
47
  export declare const MAX_PENDING_EVENTS_PER_TASK = 50;
46
48
  export declare const MAX_PENDING_EVENTS_PER_SESSION = 200;
@@ -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,5 +1,6 @@
1
1
  import type { AgentTool } from "../internal/harness-types.js";
2
2
  export interface ToolResultStore {
3
+ readonly retention?: import("./retention.js").RetentionDeclaration;
3
4
  put(ref: string, content: string): Promise<void> | void;
4
5
  get(ref: string, opts?: {
5
6
  offset?: number;
@@ -15,6 +16,7 @@ export interface ToolResultSlice {
15
16
  }
16
17
  export declare class InMemoryToolResultStore implements ToolResultStore {
17
18
  private readonly opts?;
19
+ readonly retention: "none";
18
20
  private readonly map;
19
21
  private totalChars;
20
22
  constructor(opts?: {
@@ -25,6 +25,7 @@ function refSegment(raw) {
25
25
  }
26
26
  export class InMemoryToolResultStore {
27
27
  opts;
28
+ retention = "none";
28
29
  map = new Map();
29
30
  totalChars = 0;
30
31
  constructor(opts) {
@@ -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;
@@ -584,7 +595,7 @@ export type TaskEvent = ({
584
595
  reason?: string;
585
596
  } & TaskEventIdentity) | ({
586
597
  type: "steering_injected";
587
- source: "limit_approach" | "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "workflow_size_guideline_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
598
+ source: "limit_approach" | "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "workflow_size_guideline_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools" | "final_verification";
588
599
  preview: string;
589
600
  } & TaskEventIdentity) | ({
590
601
  type: "diagnostics";
@@ -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;
@@ -737,6 +767,11 @@ export interface RunnerDeps {
737
767
  fileSnapshotStore?: import("./file-snapshot-store.js").FileSnapshotStore;
738
768
  sessionPolicyStore?: import("./session-policy-store.js").SessionPolicyStore;
739
769
  runtimeCapsResolver?: (principal: string | undefined) => RuntimeCaps | undefined | Promise<RuntimeCaps | undefined>;
770
+ lockedConfig?: import("./locked-config.js").LockedConfig;
771
+ compliancePostureResolver?: (principal: string | undefined) => import("./compliance.js").CompliancePosture | undefined | Promise<import("./compliance.js").CompliancePosture | undefined>;
772
+ memoryScopeAdmission?: import("./memory-admission.js").MemoryScopeAdmission;
773
+ deploymentMemoryScopes?: readonly string[];
774
+ retentionPolicy?: import("./retention.js").RetentionPolicy;
740
775
  autoMode?: {
741
776
  rules?: import("./auto-mode-prompt.js").AutoModeRules;
742
777
  settingsDenyRules?: readonly string[];
@@ -793,6 +828,7 @@ export interface RunnerDeps {
793
828
  toolPolicy?: import("./tool-policy.js").ToolPolicy;
794
829
  onAsk?: import("./tool-policy.js").OnAsk;
795
830
  onQuestion?: import("./ask-question.js").OnQuestion;
831
+ interactionPosture?: "interactive" | "headless";
796
832
  lspManager?: import("./lsp.js").LspServerManager;
797
833
  hooks?: import("./hooks.js").Hooks;
798
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;