@sema-agent/core 5.26.0 → 5.27.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 (34) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/agents/agent-transcript-tool.d.ts +5 -2
  3. package/dist/agents/agent-transcript-tool.js +2 -1
  4. package/dist/agents/send-message-tool.d.ts +4 -1
  5. package/dist/agents/subagent.d.ts +5 -2
  6. package/dist/core/checkpoint-store.d.ts +7 -2
  7. package/dist/core/hooks.d.ts +39 -4
  8. package/dist/core/hooks.js +15 -12
  9. package/dist/core/memory-engine/engine.d.ts +8 -5
  10. package/dist/core/memory-engine/engine.js +20 -6
  11. package/dist/core/memory-engine/file-backend.d.ts +81 -0
  12. package/dist/core/memory-engine/file-backend.js +250 -24
  13. package/dist/core/memory-engine/types.d.ts +8 -1
  14. package/dist/core/memory-vector.d.ts +6 -1
  15. package/dist/core/memory-vector.js +14 -4
  16. package/dist/core/memory.js +1 -6
  17. package/dist/core/permission-rule-model.d.ts +70 -5
  18. package/dist/core/permission-rule-model.js +58 -0
  19. package/dist/core/runner/prepare-memory.js +14 -9
  20. package/dist/core/runner/prepare-task.d.ts +9 -3
  21. package/dist/core/runner/prepare-task.js +29 -8
  22. package/dist/core/runner/runtask.d.ts +8 -1
  23. package/dist/core/task-registry-agent.d.ts +4 -3
  24. package/dist/core/task-registry.d.ts +6 -3
  25. package/dist/core/tool-policy.d.ts +9 -2
  26. package/dist/core/types.d.ts +35 -7
  27. package/dist/engine/loop/types.d.ts +10 -3
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +1 -1
  30. package/dist/orchestration/run-workflow-tool.d.ts +5 -3
  31. package/dist/orchestration/workflow.d.ts +9 -6
  32. package/dist/stores/file/checkpoint-store.d.ts +2 -1
  33. package/dist/stores/file/index.d.ts +1 -1
  34. package/package.json +1 -1
@@ -354,7 +354,10 @@ export interface Prepared {
354
354
  * deferred; a caller with no accessor has no deferred family to describe. */
355
355
  staticFaceFor?: (name: string) => boolean;
356
356
  /**
357
- * design/138 S1 — the MemoryEngine session (present iff `deps.memoryBackend` + `spec.memory.enabled`).
357
+ * design/138 S1 — the MemoryEngine session. Present when `deps.memoryBackend` + `spec.memory.enabled`
358
+ * hold AND the engine mount succeeded: a materialize failure without a `config.memory_*` code is
359
+ * fail-open (reported via `deps.onError`, the task runs memory-less), leaving this absent even though
360
+ * both flags hold.
358
361
  * `harvest` is the swallow-guarded boundary hook (task terminal in runtask + the checkpoint mint
359
362
  * point in commitSuspendSaga): it runs the FULL gate set (containment/secret/caps/deletion fuse),
360
363
  * commits entry patches to the backend, self-heals the derived index, and re-baselines (a second
@@ -1296,8 +1299,11 @@ export interface RunInternals {
1296
1299
  * design/99 (nested-subagent live tree) — an OPT-IN, DISPLAY-ONLY event sink a deployment sets on the TOP run to
1297
1300
  * receive a subagent's live `task_progress` ticks (which otherwise stay in the child's ISOLATED stream). Threaded
1298
1301
  * recursively down the delegation tree (via `ctx.forwardEvent`), so every nested subagent's ticks bubble to the
1299
- * SAME sink. The Runner forwards ONLY `task_progress` through it; the child stream is NEVER merged into the
1300
- * parent's MODEL context (this is purely a render channel). Absent unless the deployment opted in.
1302
+ * SAME sink. The Runner's ctx wrapper forwards `task_progress` always; when the run's spec sets
1303
+ * `forwardSubagentEvents: true` it ALSO forwards the child's content events (`text_delta` / `reasoning_delta` /
1304
+ * `tool_start` / `tool_end` — the subagent viewing pane, carrying the same UNTRUSTED-RAW/consumer-must-redact
1305
+ * contract as the main stream's tool events). Either way the child stream is NEVER merged into the parent's
1306
+ * MODEL context (this is purely a render channel). Absent unless the deployment opted in.
1301
1307
  */
1302
1308
  onForwardEvent?: (event: TaskEvent) => void;
1303
1309
  /**
@@ -29,7 +29,7 @@ import { inlineUntrusted } from "../untrusted-text.js";
29
29
  import { policyAskClassOf } from "../ask-class.js";
30
30
  import { emitTrace } from "../trace.js";
31
31
  import { createSessionRulePolicy } from "./session-rule-policy.js";
32
- import { cloneObserverInput, createHookEnvCapabilities, createPreToolUseConstraintPolicy, formatHookFeedback, runToolGate } from "../hooks.js";
32
+ import { cloneObserverInput, createHookEnvCapabilities, createPreToolUseConstraintPolicy, formatHookFeedback, persistedRuleMandateOf, runToolGate } from "../hooks.js";
33
33
  import { orgRuleVerdictFor } from "../permission-rule-org.js";
34
34
  import { reconcileInterruptedSession } from "../session-reconcile.js";
35
35
  import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
@@ -3260,9 +3260,25 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3260
3260
  },
3261
3261
  };
3262
3262
  })();
3263
- const ruleSuggestionsOf = (toolName, args) => {
3263
+ const ruleSuggestionsOf = (toolName, args, ask) => {
3264
3264
  if (permissionRuleLane === undefined || toolName !== PERSISTED_RULE_TOOL)
3265
3265
  return {};
3266
+ if ((spec.principal === undefined || spec.principal === "") && deps.localOwnerRules !== true)
3267
+ return {};
3268
+ if (ask?.requiresRealApproval === true ||
3269
+ ask?.persistedRuleShadowed !== undefined ||
3270
+ ask?.decisionReason === "hook" ||
3271
+ ask?.inheritedUnresolved === true ||
3272
+ ask?.ancestorResolved === true) {
3273
+ return {};
3274
+ }
3275
+ if (persistedRuleMandateOf({
3276
+ egress: egressTools.has(toolName),
3277
+ irreversibility: irreversibilityTier.get(toolName),
3278
+ shellGated: shellGatedBash,
3279
+ }) !== undefined) {
3280
+ return {};
3281
+ }
3266
3282
  const command = args?.command;
3267
3283
  if (typeof command !== "string")
3268
3284
  return {};
@@ -3315,7 +3331,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3315
3331
  toolName: creq.toolName,
3316
3332
  toolCallId: creq.toolCallId,
3317
3333
  args: editArgs,
3318
- ...ruleSuggestionsOf(creq.toolName, editArgs),
3334
+ ...ruleSuggestionsOf(creq.toolName, editArgs, { ...(re ?? {}), ancestorResolved: true }),
3319
3335
  message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
3320
3336
  ...askSourceIdentity(),
3321
3337
  ...riskAxesOf(creq.toolName),
@@ -3400,7 +3416,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3400
3416
  toolName: creq.toolName,
3401
3417
  toolCallId: creq.toolCallId,
3402
3418
  args: presentedArgs,
3403
- ...ruleSuggestionsOf(creq.toolName, presentedArgs),
3419
+ ...ruleSuggestionsOf(creq.toolName, presentedArgs, { ...(first.action === "ask" ? first : {}), ancestorResolved: true }),
3404
3420
  message: first.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
3405
3421
  ...askSourceIdentity(),
3406
3422
  ...riskAxesOf(creq.toolName),
@@ -3487,7 +3503,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3487
3503
  toolName: creq.toolName,
3488
3504
  toolCallId: creq.toolCallId,
3489
3505
  args: presentedArgs,
3490
- ...ruleSuggestionsOf(creq.toolName, presentedArgs),
3506
+ ...ruleSuggestionsOf(creq.toolName, presentedArgs, { ...(decision.action === "ask" ? decision : {}), ancestorResolved: true }),
3491
3507
  message: decision.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
3492
3508
  ...askSourceIdentity(),
3493
3509
  ...riskAxesOf(creq.toolName),
@@ -3800,7 +3816,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3800
3816
  const preview = approvalPreviewOf(req.toolName, req.args);
3801
3817
  return preview !== undefined ? { preview } : {};
3802
3818
  })(),
3803
- ...ruleSuggestionsOf(req.toolName, req.args),
3819
+ ...ruleSuggestionsOf(req.toolName, req.args, decision.action === "ask" ? decision : undefined),
3804
3820
  message: decision.message ?? `approval required for "${req.toolName}"`,
3805
3821
  ...askSourceIdentity(),
3806
3822
  ...riskAxesOf(req.toolName),
@@ -4287,7 +4303,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4287
4303
  }
4288
4304
  };
4289
4305
  const suspendAsk = parkLaneArmed && checkpointStore !== undefined
4290
- ? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule) => {
4306
+ ? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason) => {
4291
4307
  const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
4292
4308
  if (syncFirstEligible &&
4293
4309
  runtimeCaps?.forceDurableGate !== true &&
@@ -4442,7 +4458,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4442
4458
  const preview = approvalPreviewOf(req.toolName, parkedArgs);
4443
4459
  return preview !== undefined ? { preview } : {};
4444
4460
  })(),
4445
- ...ruleSuggestionsOf(req.toolName, parkedArgs),
4461
+ ...ruleSuggestionsOf(req.toolName, parkedArgs, {
4462
+ ...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
4463
+ ...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
4464
+ ...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
4465
+ ...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
4466
+ }),
4446
4467
  boundInputHash: boundInputHashOf(parkedArgs),
4447
4468
  batchToolCallIds,
4448
4469
  completedCallIds,
@@ -250,7 +250,14 @@ export declare class Runner {
250
250
  * env_failed re-resume supplied a decision ≠ the persisted winner), `reopened_concurrently` (the
251
251
  * optimistic-concurrency `rev` changed under a concurrent resolve/reopen — re-resume against current
252
252
  * state), `unsupported_version` (checkpoint newer than this worker / remote handle with no factory),
253
- * `already_resolved` (lost the CAS — idempotent no-op).
253
+ * `already_resolved` (lost the CAS — idempotent no-op), `walltime_axis_retired` (pre-CAS: the
254
+ * persisted ledger carries the retired wall-clock budget axis — a worker of the previous release can
255
+ * still finish it), `resume_aborted` (the caller's signal was already aborted at entry, or aborted
256
+ * during the resume — pre-CAS/edit-re-adjudication legs leave the checkpoint pending and resumable,
257
+ * the post-claim leg leaves it consumed; the message says which), `reopen_failed` (an aborted resume
258
+ * needed to reopen the checkpoint and the store refused, or the reopen failed in flight — the message
259
+ * distinguishes terminally-consumed from state-unprovable). Each code's full contract is on
260
+ * {@link CheckpointError.code}.
254
261
  */
255
262
  resume(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig,
256
263
  /** RB-48② (1.404): same TRUSTED run-internals seam as {@link resumeStream} — the convenience wrapper
@@ -415,9 +415,10 @@ export declare function notFoundRunningAgentsTail(footer: {
415
415
  * so a structured consumer lost the failure FACT at exactly the moment it could no longer reach the
416
416
  * live handle, left to scrape the prose body's `error:` line.
417
417
  *
418
- * The status GATES live here too (killed ⇒ stoppedBy, failed ⇒ error/errorCode/retryable) rather than
419
- * at the call sites: which facts a given status may carry is part of the same contract, and a gate
420
- * copied per face is the same drift with extra steps. `error` is model/provider-influenceable text, so
418
+ * The fact gates live here too (failed ⇒ error/errorCode/retryable/retryAfterMs; `stoppedBy` rides
419
+ * whenever the ROW carries it, on any status a reaper-settled `failed` row keeps its attribution,
420
+ * see the widening note in the builder) rather than at the call sites: which facts a given row may
421
+ * carry is part of the same contract, and a gate copied per face is the same drift with extra steps. `error` is model/provider-influenceable text, so
421
422
  * the fencing + bounding (RB-386②'s posture) happens once, here, for both faces.
422
423
  * The parked projection is this builder with fewer facts, not a third literal. */
423
424
  export interface AgentPollDetailsInput {
@@ -33,8 +33,10 @@ export interface RegisterBackgroundBashInput extends TaskAccess {
33
33
  /** CC 2.1.209 对齐批A A5: the task's OUTPUT FILE — the launch receipt advertises it and the watcher
34
34
  * mirrors every polled increment into it (append-only, unbounded — unlike the rolling in-memory
35
35
  * spool), so "Read the output file path" is a real alternative to TaskOutput. Terminal
36
- * task-notifications carry the same path (`output_file`). Only meaningful with `onTerminal` (the
37
- * watcher is the writer); callers create the file (empty) before registering. */
36
+ * task-notifications carry the same path (`output_file`). Primarily meaningful with `onTerminal`
37
+ * (only the watcher mirrors increments live); a watcher-less row still gets ONE final best-effort
38
+ * append at TaskStop/run-teardown (the tail drain), so the file is written even without a watcher —
39
+ * there is just no live mirror. Callers create the file (empty) before registering. */
38
40
  outputFile?: string;
39
41
  }
40
42
  export interface TaskPollOptions {
@@ -369,7 +371,8 @@ export declare class TaskRegistry {
369
371
  * BEFORE the abort()/status flip — the ordering is load-bearing: the guard below refuses markers on a
370
372
  * non-running handle, so a caller that flips first loses its claim and attribution falls back to
371
373
  * "system". First-marker-wins: an earlier marker (e.g. a service-wire "user") is never overwritten.
372
- * Applies to background_agent AND background_bash (same class); workflow cancellation is out of scope. */
374
+ * Applies to background_agent, background_bash AND monitor handles (the markable kinds the monitor
375
+ * watcher/stop lanes mark and read it too); workflow cancellation is out of scope. */
373
376
  markStopSource(id: string, source: StopSource): void;
374
377
  /**
375
378
  * [1712] / RB-164 — attribute an ENV-LEVEL blanket sweep before it runs.
@@ -632,9 +632,16 @@ export interface AskRequest {
632
632
  /**
633
633
  * design/179 §4 — the persistable allow-rule forms this exact call could be covered by, so a surface can
634
634
  * offer "allow, and stop asking me this" with something concrete behind it. Present only when a
635
- * persisted allow-rule lane is armed AND the call is one the lane can speak for: a compound, a
635
+ * persisted allow-rule lane is armed AND the call is one the lane can speak for (a compound, a
636
636
  * redirection or a substitution yields NO suggestion, which is the honest answer rather than an option
637
- * that would be refused on redemption.
637
+ * that would be refused on redemption) AND the ask is one a persisted rule could actually clear — a
638
+ * mandated ask (operator shellGate:"always", the tool's own egress/irreversibility marks, a
639
+ * `requiresRealApproval` demand) and an ask carrying {@link persistedRuleShadowed} offer none.
640
+ *
641
+ * CONTRACT — array order is display order, narrowest first: the EXACT form is always index 0, a
642
+ * broader reviewed PREFIX form (at most one) follows. Basis ≤ 2. Selection indices and redemption
643
+ * tickets are keyed against this order, and the durable park row carries the same array under the
644
+ * same contract.
638
645
  *
639
646
  * ADVISORY display metadata, never adjudication input, and never a rule by itself: minting one is a
640
647
  * separate act that goes through the approval-record protocol, so a surface that ignores this field
@@ -855,8 +855,11 @@ export interface ToolExecuteContext {
855
855
  * `RunInternals.onForwardEvent`) to receive a SUBAGENT's live `task_progress` ticks that otherwise stay in the
856
856
  * child's ISOLATED stream. A delegation tool threads it to the child so nested progress bubbles to one sink. This
857
857
  * is a DISPLAY channel ONLY — the child stream is NEVER merged into the parent's MODEL context, and nothing
858
- * security-relevant consumes a forwarded event. Present ONLY when the deployment opted in; the Runner filters it
859
- * to `task_progress` (a tool cannot push other event types). ⚠️ Forwarded ticks are UNTRUSTED display hints any
858
+ * security-relevant consumes a forwarded event. Present ONLY when the deployment opted in. The tool-ctx wrapper
859
+ * passes `task_progress` unconditionally and when the deployment sets `forwardSubagentEvents: true`the
860
+ * transcript classes too (text_delta/reasoning_delta/tool_start/tool_end); other event types never cross it.
861
+ * The delegation lane's OWN tap is trusted and forwards the child's FULL event stream (bg frames tagged
862
+ * with bgAgentId). ⚠️ Forwarded ticks are UNTRUSTED display hints — any
860
863
  * tool holding this ctx could forge one, so a consumer validates `parentTaskId` against its known runs.
861
864
  */
862
865
  forwardEvent?: (event: TaskEvent) => void;
@@ -1871,7 +1874,9 @@ export interface TaskSpec {
1871
1874
  * write-capable fs tools (CC's same gate); zero cost/behavior change otherwise. `false` opts out.
1872
1875
  */
1873
1876
  lspDiagnostics?: boolean;
1874
- /** In-process hooks (PreToolUse/PostToolUse/UserPromptSubmit) for this task. Overrides `RunnerDeps.hooks`. */
1877
+ /** In-process hooks for this task the full `Hooks` lifecycle seam (tool pre/post/failure/batch
1878
+ * interception, prompt gating, stop pushback, compaction taps, permission-denied observation; each
1879
+ * member's contract is on the interface). Overrides `RunnerDeps.hooks`. */
1875
1880
  hooks?: import("./hooks.js").Hooks;
1876
1881
  /**
1877
1882
  * design/45 **durable suspend-on-approval** (F4). When set AND a `CheckpointStore` is wired
@@ -4729,10 +4734,15 @@ export interface RunnerDeps {
4729
4734
  * task's own `lspManager` overrides this. Unset ⇒ no `lsp` tool. */
4730
4735
  lspManager?: import("./lsp.js").LspServerManager;
4731
4736
  /**
4732
- * Default in-process hooks for all tasks (design/37): PreToolUse (rewrite/restrict args + inject
4733
- * context), PostToolUse (rewrite output + inject context), UserPromptSubmit (block/inject before the
4734
- * objective becomes a message). A task's own `hooks` overrides this. A PreToolUse hook's `allow`
4735
- * never bypasses `toolPolicy` the policy is always the final say.
4737
+ * Default in-process hooks for all tasks (design/37) the FULL lifecycle seam of the `Hooks`
4738
+ * interface, not just the tool-call trio: `preToolUse` (rewrite/restrict args + inject context),
4739
+ * `postToolUse` (rewrite output + inject context), `userPromptSubmit` (block/inject before the
4740
+ * objective becomes a message), plus `stop` (push back when the run would otherwise end and continue
4741
+ * it), `postToolUseFailure` / `postToolBatch` / `permissionDenied` (failure, batch-boundary and
4742
+ * deny observers), `preCompact` / `postCompact` (compaction gate + observer), `stopFailure`
4743
+ * (API-error terminal observer) and the `preToolUseObservational` declaration flag — each member's
4744
+ * contract is documented on the `Hooks` interface itself. A task's own `hooks` overrides this. A
4745
+ * PreToolUse hook's `allow` never bypasses `toolPolicy` — the policy is always the final say.
4736
4746
  */
4737
4747
  hooks?: import("./hooks.js").Hooks;
4738
4748
  /**
@@ -4750,12 +4760,30 @@ export interface RunnerDeps {
4750
4760
  * - `"prompt-constitution"` — a `stableSystem` provider returned an ALREADY-assembled prompt
4751
4761
  * (constitution anchor found); core passed it through un-doubled. Upgrade the provider to return
4752
4762
  * only the role base (or set `replaceAll: true` to own the whole base).
4763
+ * - `"degraded"` — the task kept running with a capability quietly reduced: a question auto-answered
4764
+ * with no human present, a skipped env git snapshot, a lost checkpoint-put confirmation, an
4765
+ * unroutable descendant notification, and similar best-effort arms. The run proceeds; the reduced
4766
+ * arm is what is being disclosed (`classification` names it).
4767
+ * - `"config"` — a deployment-wiring problem detected while preparing or tearing down a task: tool
4768
+ * mount conflicts, refused/ignored knob values, gate-off notices, store/env teardown legs. The
4769
+ * broadest class by call-site count — most misconfigurations announce here rather than failing
4770
+ * the task.
4753
4771
  * - `"memory"` — a post-task memory-consolidation pass failed or skipped a malformed reconcile
4754
4772
  * decision (design/41). Best-effort: the notes the model saved are kept; the task still succeeds.
4755
4773
  * - `"mcp"` — an MCP server failed to connect / list its tools and was skipped (fail-open, design/29);
4756
4774
  * the task runs with the remaining servers' tools. One call per failed server.
4757
4775
  * - `"a2a"` — the A2A sibling of `"mcp"`: a declared peer's agent card could not be fetched/read, or it
4758
4776
  * advertises no transport this client speaks, so the peer was skipped. One call per skipped peer.
4777
+ * - `"interrupt-reconcile"` — repairing an interrupted session's transcript on re-entry (synthesizing
4778
+ * tool results for orphaned calls, flushing held session writes) failed; the run proceeds on the
4779
+ * unrepaired transcript.
4780
+ * - `"suggestions"` — the best-effort follow-up-suggestions pass failed or timed out; the result
4781
+ * simply carries no suggestions.
4782
+ * - `"rewind"` — the per-turn working-tree snapshot backing file rewind failed or was skipped (e.g. a
4783
+ * too-large root inside its cooldown window); turns without a snapshot cannot be rewound to.
4784
+ * - `"hook"` — a deployment hook misbehaved: a callback threw, or returned a verdict it was not
4785
+ * allowed to (e.g. a declared-observational hook). The engine applies the hook's own documented
4786
+ * fallback (swallow, or fail-closed deny, per its contract) and reports the fact here.
4759
4787
  */
4760
4788
  onError?: (err: unknown, context: {
4761
4789
  phase: "compaction" | "prompt-cache" | "prompt-constitution" | "degraded" | "config" | "memory" | "mcp" | "a2a" | "interrupt-reconcile" | "suggestions" | "rewind" | "hook";
@@ -23,7 +23,11 @@ export type ToolExecutionMode = "sequential" | "parallel";
23
23
  * Controls how many queued user messages are injected when the agent loop reaches a queue drain point.
24
24
  *
25
25
  * - "all": drain and inject every queued message at that point.
26
- * - "one-at-a-time": drain and inject only the oldest queued message, leaving the rest queued for later drain points.
26
+ * - "one-at-a-time": drain and inject only the oldest queued message, leaving the rest queued for later
27
+ * drain points. Exception: when the oldest frame is an engine note (a task-notification sidecar
28
+ * payload), every consecutive engine-note frame behind it drains with it as ONE batch, stopping at
29
+ * the first non-note frame (e.g. a real user steer) — N buffered completion notices are one
30
+ * boundary's worth of frames, not N sequential turns.
27
31
  */
28
32
  export type QueueMode = "all" | "one-at-a-time";
29
33
  /** A single tool call content block emitted by an assistant message. */
@@ -394,8 +398,11 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
394
398
  }
395
399
  /**
396
400
  * Thinking/reasoning level for models that support it.
397
- * Note: "xhigh" is only supported by selected model families. Use model thinking-level metadata
398
- * from openclaw/plugin-sdk/llm to detect support for a concrete model.
401
+ * Note: "xhigh" is only supported by selected model families. Per-model/per-endpoint support is
402
+ * declared in this package's model metadata (`src/engine/llm/types.ts`): the compat blocks'
403
+ * accepted-tier sets (`reasoningEffortLevels` on the OpenAI-family compats, `effortLevels` on the
404
+ * Anthropic Messages compat — a requested tier above the declared set clamps down rather than
405
+ * erroring), and `Model.thinkingLevelMap`, where `null` marks a level as unsupported.
399
406
  */
400
407
  export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
401
408
  export interface BashExecutionMessage {
package/dist/index.d.ts CHANGED
@@ -87,7 +87,7 @@ export type { InvariantKind, FunctionContract, Invariant, InvariantViolation, Ch
87
87
  export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
88
88
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
89
89
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
90
- export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, } from "./core/tool-result-store.js";
90
+ export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, } from "./core/tool-result-store.js";
91
91
  export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_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 RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
92
92
  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";
93
93
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
68
68
  export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
69
69
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
70
70
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
71
- export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
71
+ export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
72
72
  export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_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";
73
73
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, } from "./core/usage-window-store.js";
74
74
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
@@ -229,9 +229,11 @@ export interface RunWorkflowToolDeps {
229
229
  * has inherited this hook since [893]④a; the workflow lane never did — the governance whitelist
230
230
  * rightly blocks SCRIPTS from setting it, but host inheritance is a different lane. */
231
231
  parentGetApiKeyAndHeaders?: import("../core/types.js").TaskSpec["getApiKeyAndHeaders"];
232
- /** The HOST run's display sink (its `RunInternals.onForwardEvent`, already filtered to `task_progress`
233
- * by the runner's ctx wrapper)threaded via `startWorkflow` into every spawned agent's trusted
234
- * internals so a workflow child's progress ticks bubble to the deployment's one sink, the same
232
+ /** The HOST run's display sink (its `RunInternals.onForwardEvent` behind the runner's ctx wrapper:
233
+ * `task_progress` always, plus the children's content events — `text_delta`/`reasoning_delta`/
234
+ * `tool_start`/`tool_end`, UNTRUSTED-RAW: the consumer must redact when the HOST spec set
235
+ * `forwardSubagentEvents: true`) — threaded via `startWorkflow` into every spawned agent's trusted
236
+ * internals so a workflow child's events bubble to the deployment's one sink, the same
235
237
  * channel a `createSubagentTool` delegation threads. Display-only; absent ⇒ ticks stay in each
236
238
  * child's own stream. (A dep, not read off the execute ctx: the mounted tool's `AgentTool.execute`
237
239
  * wrapper builds a minimal `{toolCallId, signal}` ctx — the rich-ctx injection only wraps
@@ -31,8 +31,9 @@ export declare const WORKFLOW_SUBAGENT_APPEND_SCHEMA = "---\n\nNOTE: You are run
31
31
  * F4 agentType (CC 198 锚 pretty.js:446608-446627): resolve `opts.agentType` against the registry
32
32
  * (deployment SHADOW over built-ins) and fold the definition into the child spec — persona as
33
33
  * `systemPrompt` (so {@link withWorkflowChildPersona} composes the return-contract NOTE via the
34
- * custom-persona APPEND arm = CC `O0m` semantics), model/thinking/maxTurns/skills/memory when the spec
35
- * didn't pin them, and allow/denyTools as a ToolPolicy (combined deny-wins with any spec policy).
34
+ * custom-persona APPEND arm = CC `O0m` semantics), model/thinking/maxTurns/skills/memory/
35
+ * memoryPersistenceCapable when the spec didn't pin them, and allow/denyTools as a ToolPolicy
36
+ * (combined deny-wins with any spec policy).
36
37
  * The definition is DEPLOYMENT-TRUSTED (registry-declared, not script-authored), so its model bypasses
37
38
  * the script-facing modelName allowlist by design — same trust tier as the Agent tool's registry.
38
39
  */
@@ -382,10 +383,12 @@ export interface RunWorkflowOptions {
382
383
  * from ctx): every spawned agent composes the same closure (codex F2a). */
383
384
  parentCenterArtifactDigest?: string;
384
385
  parentCenterSourceRevision?: string;
385
- /** The launching run's display sink (`RunInternals.onForwardEvent`, already filtered by the runner to
386
- * `task_progress`) threaded into every spawned agent's trusted internals so the children's progress
387
- * ticks bubble out of their isolated streams to the deployment's one sink (fleet footer/monitor rows).
388
- * Display-only; absent ticks stay in each child's own stream. */
386
+ /** The launching run's display sink (`RunInternals.onForwardEvent` behind the runner's ctx wrapper:
387
+ * `task_progress` always, PLUS the children's content events `text_delta`/`reasoning_delta`/
388
+ * `tool_start`/`tool_end`, UNTRUSTED-RAW: the consumer must redact when the HOST spec set
389
+ * `forwardSubagentEvents: true`) threaded into every spawned agent's trusted internals so the
390
+ * children's events bubble out of their isolated streams to the deployment's one sink (fleet
391
+ * footer/monitor rows). Display-only; absent ⇒ ticks stay in each child's own stream. */
389
392
  onForwardEvent?: (event: TaskEvent) => void;
390
393
  /** Parent effective-policy inheritance (tighten-only): the HOST task's evaluated gate chain
391
394
  * (`ToolExecuteContext.inheritedGateForChildren()` — its session rules + toolPolicy/frozen onAsk +
@@ -1,7 +1,8 @@
1
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
  /** When false, an `appendLine` for a state transition is NOT fsync'd. The checkpoint COMMIT POINT always
4
- * fsyncs regardless (its crash-safety depends on it); this only affects whether `put` fsyncs. Default true. */
4
+ * fsyncs regardless (its crash-safety depends on it); this governs the `put` and `setPendingSteer`
5
+ * (steer-append) commits only — `resolve`/`reopen`/`expire` fsync unconditionally. Default true. */
5
6
  fsync?: boolean;
6
7
  /** Compact the ledger into a snapshot once it exceeds this many events (then truncate). Default 1000. */
7
8
  compactEvery?: number;
@@ -52,7 +52,7 @@ export interface FileStorageBackendOptions {
52
52
  /** `TtlSessionStore` idle-eviction policy. Default `"forget"` (§7 decision 5 — durable history is never
53
53
  * deleted by an idle timer). */
54
54
  evict?: EvictPolicy;
55
- /** Checkpoint store tuning (fsync cadence for `put`, ledger compaction threshold). */
55
+ /** Checkpoint store tuning (fsync cadence for `put`/`setPendingSteer`, ledger compaction threshold). */
56
56
  checkpoint?: FileCheckpointStoreOptions;
57
57
  /** design/101 §E19 — file-snapshot enumerator bounds (maxFiles/maxBytes/ignoreDirs). Default
58
58
  * {@link DEFAULT_SNAPSHOT_BOUNDS}. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.26.0",
3
+ "version": "5.27.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",