@sema-agent/core 5.20.0 → 5.21.1
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.
- package/CHANGELOG.md +19 -0
- package/dist/agents/agent-definition.d.ts +7 -0
- package/dist/agents/agent-transcript-tool.d.ts +30 -0
- package/dist/agents/builtin-agents.d.ts +60 -0
- package/dist/agents/cascade.d.ts +107 -0
- package/dist/agents/cumulative-stats.d.ts +56 -0
- package/dist/agents/observer.d.ts +162 -0
- package/dist/agents/peer-admission.d.ts +95 -0
- package/dist/agents/repair-loop.d.ts +226 -0
- package/dist/agents/retain-ledger.d.ts +141 -0
- package/dist/agents/roster-store.d.ts +105 -0
- package/dist/agents/send-message-tool.d.ts +92 -0
- package/dist/agents/session-util.d.ts +5 -0
- package/dist/agents/subagent-steps.d.ts +66 -0
- package/dist/agents/subagent.d.ts +600 -0
- package/dist/agents/suspend-guard.d.ts +29 -0
- package/dist/agents/teacher.d.ts +75 -0
- package/dist/agents/team.d.ts +120 -1
- package/dist/agents/tool-filter.d.ts +34 -0
- package/dist/agents/verify.d.ts +198 -0
- package/dist/bench/metrics.d.ts +455 -0
- package/dist/brain/anthropic.d.ts +30 -0
- package/dist/brain/circuit-breaker.d.ts +33 -0
- package/dist/brain/context-overflow.d.ts +60 -3
- package/dist/brain/degrading.d.ts +67 -0
- package/dist/brain/errors.d.ts +42 -0
- package/dist/brain/failover.d.ts +15 -0
- package/dist/brain/media-degrade.d.ts +39 -0
- package/dist/brain/model-presets.d.ts +31 -0
- package/dist/brain/open-responses.d.ts +19 -0
- package/dist/brain/openai.d.ts +46 -0
- package/dist/brain/reasoning.d.ts +106 -1
- package/dist/brain/repetition.d.ts +83 -0
- package/dist/brain/request-params.d.ts +56 -0
- package/dist/brain/retry.d.ts +46 -0
- package/dist/brain/routing.d.ts +12 -0
- package/dist/brain/status-sink.d.ts +30 -1
- package/dist/brain/stream-engine.d.ts +147 -0
- package/dist/brain/stream-shared.d.ts +34 -0
- package/dist/brain/terminal-cause.d.ts +31 -0
- package/dist/brain/timeout.d.ts +107 -0
- package/dist/brain/tool-call-id.d.ts +20 -0
- package/dist/brain/tool-call-repair.d.ts +13 -0
- package/dist/config/catalog.d.ts +47 -0
- package/dist/config/defaults.d.ts +33 -0
- package/dist/core/a2a-task-state.d.ts +53 -0
- package/dist/core/a2a.d.ts +51 -0
- package/dist/core/arg-summary.d.ts +62 -0
- package/dist/core/ask-question.d.ts +272 -2
- package/dist/core/auto-compaction.d.ts +467 -0
- package/dist/core/auto-compaction.js +20 -6
- package/dist/core/auto-mode-prompt-assets.d.ts +8 -0
- package/dist/core/auto-mode-prompt.d.ts +27 -0
- package/dist/core/auto-mode.d.ts +54 -3
- package/dist/core/auto-promote.d.ts +100 -0
- package/dist/core/background-agent-store.d.ts +292 -0
- package/dist/core/background-shell.d.ts +110 -0
- package/dist/core/cache-break-detector.d.ts +34 -0
- package/dist/core/canonical-json.d.ts +57 -0
- package/dist/core/checkpoint-store.d.ts +1574 -16
- package/dist/core/compliance.d.ts +30 -0
- package/dist/core/consolidate-scope.d.ts +75 -0
- package/dist/core/context-edit.d.ts +99 -0
- package/dist/core/context-guard.d.ts +46 -0
- package/dist/core/exec-gate.d.ts +44 -0
- package/dist/core/exec-output-tail.d.ts +61 -0
- package/dist/core/file-snapshot-store.d.ts +104 -0
- package/dist/core/fs-write-gate-policy.d.ts +36 -0
- package/dist/core/git-worktree-env.d.ts +31 -0
- package/dist/core/governance-codes.d.ts +19 -0
- package/dist/core/hooks.d.ts +608 -1
- package/dist/core/human-input-projection.d.ts +37 -0
- package/dist/core/human-input-projection.js +13 -0
- package/dist/core/image-downsample.d.ts +74 -0
- package/dist/core/locked-config.d.ts +37 -0
- package/dist/core/lsp-diagnostics.d.ts +77 -0
- package/dist/core/lsp-protocol.d.ts +29 -0
- package/dist/core/lsp-session.d.ts +60 -1
- package/dist/core/lsp.d.ts +150 -1
- package/dist/core/mailbox-store.d.ts +57 -0
- package/dist/core/mcp.d.ts +381 -0
- package/dist/core/media-byte-cap.d.ts +21 -0
- package/dist/core/memory-admission.d.ts +71 -0
- package/dist/core/memory-engine/content-origin.d.ts +64 -0
- package/dist/core/memory-engine/data-plane.d.ts +43 -0
- package/dist/core/memory-engine/dual-root.d.ts +24 -0
- package/dist/core/memory-engine/engine.d.ts +230 -0
- package/dist/core/memory-engine/engine.js +103 -35
- package/dist/core/memory-engine/file-backend.d.ts +155 -0
- package/dist/core/memory-engine/frontmatter.d.ts +16 -0
- package/dist/core/memory-engine/layout.d.ts +217 -0
- package/dist/core/memory-engine/memory-backend-contract.d.ts +12 -0
- package/dist/core/memory-engine/migrate.d.ts +9 -0
- package/dist/core/memory-engine/scan.d.ts +12 -0
- package/dist/core/memory-engine/scope-contract.d.ts +61 -1
- package/dist/core/memory-engine/sync-client.d.ts +87 -0
- package/dist/core/memory-engine/sync.d.ts +60 -0
- package/dist/core/memory-engine/tools.d.ts +41 -0
- package/dist/core/memory-engine/types.d.ts +188 -0
- package/dist/core/memory-recall.d.ts +141 -0
- package/dist/core/memory-vector.d.ts +20 -0
- package/dist/core/memory.d.ts +458 -0
- package/dist/core/message-utils.d.ts +6 -0
- package/dist/core/oracle-isolation.d.ts +69 -0
- package/dist/core/permission-rule-consent.d.ts +138 -0
- package/dist/core/permission-rule-model.d.ts +122 -0
- package/dist/core/permission-rule-store.d.ts +119 -3
- package/dist/core/permission-rules.d.ts +87 -1
- package/dist/core/present-plan-tool.d.ts +20 -0
- package/dist/core/pricing.d.ts +26 -0
- package/dist/core/property-harness.d.ts +86 -0
- package/dist/core/protocol-naming.d.ts +38 -0
- package/dist/core/protocol-table.d.ts +61 -0
- package/dist/core/push-queue.d.ts +1 -0
- package/dist/core/remote-env.d.ts +383 -1
- package/dist/core/retention-policy.d.ts +33 -0
- package/dist/core/retention.d.ts +51 -0
- package/dist/core/roles.d.ts +59 -0
- package/dist/core/runner/active-skill-scope.d.ts +34 -0
- package/dist/core/runner/assemble-result.d.ts +134 -0
- package/dist/core/runner/compaction-call-options.d.ts +97 -1
- package/dist/core/runner/grounding-signal.d.ts +10 -0
- package/dist/core/runner/image.d.ts +17 -0
- package/dist/core/runner/image.js +29 -15
- package/dist/core/runner/memory-consolidation.d.ts +60 -0
- package/dist/core/runner/prepare-memory.d.ts +59 -0
- package/dist/core/runner/prepare-task.d.ts +1011 -2
- package/dist/core/runner/prepare-task.js +12 -11
- package/dist/core/runner/prompt-suggestions.d.ts +7 -0
- package/dist/core/runner/runtask.d.ts +304 -3
- package/dist/core/runner/session-file-state-replay.d.ts +49 -0
- package/dist/core/runner/session-rule-policy.d.ts +58 -0
- package/dist/core/runner/strict-output-schema.d.ts +18 -3
- package/dist/core/runner/synthetic-tools.d.ts +113 -0
- package/dist/core/runner/teardown-bounded.d.ts +34 -0
- package/dist/core/runner/tool-disclosure.d.ts +227 -0
- package/dist/core/runner/tool-output-projection.d.ts +6 -0
- package/dist/core/runner/turn-attachments.d.ts +634 -0
- package/dist/core/runner/usage-accounting.d.ts +32 -0
- package/dist/core/runtime.d.ts +9 -0
- package/dist/core/safe-notify.d.ts +64 -0
- package/dist/core/safety-axis-vocab.d.ts +23 -0
- package/dist/core/safety-merge-corpus.d.ts +37 -0
- package/dist/core/scheduler.d.ts +121 -0
- package/dist/core/secret-env.d.ts +32 -0
- package/dist/core/select-model.d.ts +15 -0
- package/dist/core/sensitive-path-policy.d.ts +42 -0
- package/dist/core/session-policy-store.d.ts +94 -0
- package/dist/core/session-reconcile.d.ts +80 -0
- package/dist/core/session-store.d.ts +85 -0
- package/dist/core/session.d.ts +153 -0
- package/dist/core/shared-memory/contract.d.ts +22 -0
- package/dist/core/shared-memory/normalize.d.ts +123 -2
- package/dist/core/shared-memory/tools.d.ts +14 -0
- package/dist/core/shared-memory/types.d.ts +105 -0
- package/dist/core/shutdown-debug.d.ts +6 -0
- package/dist/core/side-query.d.ts +38 -0
- package/dist/core/side-query.js +6 -1
- package/dist/core/skill-tool-specifier.d.ts +72 -0
- package/dist/core/skills-directory.d.ts +100 -1
- package/dist/core/spec-contract.d.ts +89 -0
- package/dist/core/store-contracts/background-agent-store-contract.d.ts +31 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +26 -0
- package/dist/core/store-contracts/contract-harness.d.ts +20 -0
- package/dist/core/store-contracts/contract-kit-version.d.ts +27 -0
- package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +8 -0
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +42 -0
- package/dist/core/store-contracts/tool-result-store-contract.d.ts +13 -0
- package/dist/core/strategy-store.d.ts +37 -0
- package/dist/core/stub-env.d.ts +7 -0
- package/dist/core/surrogate-safe-slice.d.ts +35 -0
- package/dist/core/task-notification.d.ts +202 -0
- package/dist/core/task-outcome.d.ts +53 -0
- package/dist/core/task-registry-agent.d.ts +337 -1
- package/dist/core/task-registry-monitor.d.ts +12 -0
- package/dist/core/task-registry-shared.d.ts +540 -0
- package/dist/core/task-registry.d.ts +343 -0
- package/dist/core/task-registry.js +13 -2
- package/dist/core/task-tool-shape.d.ts +44 -0
- package/dist/core/tighten-task-spec.d.ts +21 -0
- package/dist/core/tool-detach.d.ts +21 -0
- package/dist/core/tool-errors.d.ts +131 -0
- package/dist/core/tool-errors.js +4 -0
- package/dist/core/tool-name-aliases.d.ts +27 -0
- package/dist/core/tool-policy.d.ts +555 -0
- package/dist/core/tool-result-budget.d.ts +32 -0
- package/dist/core/tool-result-store.d.ts +174 -1
- package/dist/core/tools.d.ts +45 -0
- package/dist/core/trace.d.ts +323 -0
- package/dist/core/types.d.ts +3859 -2
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-text.d.ts +156 -0
- package/dist/core/usage-window-store.d.ts +95 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/warm-resume.d.ts +17 -0
- package/dist/core/wiring-manifest.d.ts +169 -0
- package/dist/core/with-retry.d.ts +24 -0
- package/dist/core/workflow-journal-store.d.ts +160 -0
- package/dist/core/workflow-run-store-contract.d.ts +25 -0
- package/dist/core/workflow-run-store.d.ts +119 -0
- package/dist/engine/compaction/compaction.d.ts +256 -1
- package/dist/engine/compaction/utils.d.ts +94 -0
- package/dist/engine/execution-env/kill-tree.d.ts +29 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +94 -0
- package/dist/engine/harness/agent-harness.d.ts +116 -0
- package/dist/engine/harness/agent-harness.js +3 -14
- package/dist/engine/harness/messages.d.ts +15 -0
- package/dist/engine/harness/types.d.ts +464 -2
- package/dist/engine/llm/diagnostics.d.ts +4 -0
- package/dist/engine/llm/event-stream.d.ts +3 -0
- package/dist/engine/llm/index.d.ts +7 -0
- package/dist/engine/llm/types.d.ts +500 -3
- package/dist/engine/llm/validation.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +87 -2
- package/dist/engine/loop/runtime-deps.d.ts +7 -0
- package/dist/engine/loop/types.d.ts +424 -0
- package/dist/engine/lsp/frame-decoder.d.ts +13 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +107 -0
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +7 -0
- package/dist/engine/session/import-validate.d.ts +27 -0
- package/dist/engine/session/log-digest.d.ts +93 -0
- package/dist/engine/session/memory-repo.d.ts +6 -0
- package/dist/engine/session/memory-storage.d.ts +2 -0
- package/dist/engine/session/session.d.ts +75 -0
- package/dist/engine/session/storage-base.d.ts +8 -0
- package/dist/fixtures/index.d.ts +36 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.js +0 -1
- package/dist/internal/harness-types.d.ts +6 -0
- package/dist/internal/harness.d.ts +11 -0
- package/dist/internal/llm.d.ts +6 -0
- package/dist/orchestration/builtin-workflows.d.ts +53 -0
- package/dist/orchestration/dev-vm-script-runner.d.ts +19 -0
- package/dist/orchestration/goal.d.ts +57 -0
- package/dist/orchestration/goal.js +3 -0
- package/dist/orchestration/run-spec.d.ts +42 -0
- package/dist/orchestration/run-workflow-tool.d.ts +169 -0
- package/dist/orchestration/workflow-governance.d.ts +61 -0
- package/dist/orchestration/workflow-meta.d.ts +28 -0
- package/dist/orchestration/workflow-observe.d.ts +60 -0
- package/dist/orchestration/workflow-primitives.d.ts +23 -1
- package/dist/orchestration/workflow-sandbox-conformance.d.ts +38 -0
- package/dist/orchestration/workflow-script-runner.d.ts +88 -0
- package/dist/orchestration/workflow-script-store.d.ts +98 -0
- package/dist/orchestration/workflow-size-guideline.d.ts +67 -0
- package/dist/orchestration/workflow-types.d.ts +169 -2
- package/dist/orchestration/workflow.d.ts +358 -0
- package/dist/prompt-assembly/artifact-store.d.ts +33 -0
- package/dist/prompt-assembly/artifact.d.ts +25 -0
- package/dist/prompt-assembly/assemble.d.ts +20 -0
- package/dist/prompt-assembly/composer.d.ts +29 -0
- package/dist/prompt-assembly/epoch.d.ts +55 -1
- package/dist/prompt-assembly/event-registry.d.ts +35 -0
- package/dist/prompt-assembly/explain.d.ts +12 -0
- package/dist/prompt-assembly/tool-catalog.d.ts +38 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +40 -0
- package/dist/prompt-assembly/types.d.ts +115 -0
- package/dist/prompts/coordinator.d.ts +27 -0
- package/dist/prompts/default.d.ts +539 -0
- package/dist/prompts/simple-sections.d.ts +45 -0
- package/dist/prompts/supervisor.d.ts +66 -0
- package/dist/scenarios/env.d.ts +28 -0
- package/dist/scenarios/full-body.d.ts +50 -0
- package/dist/scenarios/scenario-registry.d.ts +60 -0
- package/dist/scenarios/teacher-quickstart.d.ts +27 -0
- package/dist/server/http.d.ts +17 -0
- package/dist/stores/cc/lockfile.d.ts +6 -0
- package/dist/stores/cc/mailbox-store.d.ts +8 -0
- package/dist/stores/cc/roster-adapter.d.ts +4 -0
- package/dist/stores/cc/sidecar-transcript.d.ts +66 -0
- package/dist/stores/file/background-agent-store.d.ts +24 -0
- package/dist/stores/file/checkpoint-store.d.ts +38 -0
- package/dist/stores/file/file-snapshot-store.d.ts +25 -0
- package/dist/stores/file/fs-atomic.d.ts +155 -0
- package/dist/stores/file/index.d.ts +89 -0
- package/dist/stores/file/mailbox-store.d.ts +36 -0
- package/dist/stores/file/memory-store.d.ts +82 -0
- package/dist/stores/file/permission-rule-store.d.ts +79 -0
- package/dist/stores/file/session-policy-store.d.ts +28 -0
- package/dist/stores/file/session-store.d.ts +40 -0
- package/dist/stores/file/shared-ledger.d.ts +83 -0
- package/dist/stores/file/tool-result-store.d.ts +11 -0
- package/dist/stores/file/usage-window-store.d.ts +18 -0
- package/dist/stores/file/workflow-journal-store.d.ts +100 -0
- package/dist/stores/file/workflow-run-store.d.ts +11 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +282 -0
- package/dist/tools/fs/encoding.d.ts +60 -0
- package/dist/tools/fs/fs-bash.d.ts +133 -0
- package/dist/tools/fs/fs-pdf.d.ts +28 -0
- package/dist/tools/fs/fs-search-tools.d.ts +5 -0
- package/dist/tools/fs/fs-search-tools.js +0 -1
- package/dist/tools/fs/fs-shared.d.ts +360 -0
- package/dist/tools/fs/fs-write.d.ts +16 -0
- package/dist/tools/fs/gh-rate-limit.d.ts +22 -0
- package/dist/tools/fs/index.d.ts +79 -0
- package/dist/tools/fs/notebook.d.ts +56 -0
- package/dist/tools/fs/pdf.d.ts +70 -0
- package/dist/tools/fs/pdf.js +8 -2
- package/dist/tools/fs/repo-map.d.ts +10 -0
- package/dist/tools/fs/safety.d.ts +376 -0
- package/dist/tools/fs/search.d.ts +131 -0
- package/dist/tools/loop-tick.d.ts +24 -0
- package/dist/tools/monitor.d.ts +55 -0
- package/dist/tools/scheduler-tools.d.ts +51 -0
- package/dist/tools/sql-adapters.d.ts +18 -0
- package/dist/tools/sql.d.ts +25 -0
- package/dist/tools/task-list.d.ts +77 -0
- package/dist/tools/todo.d.ts +8 -0
- package/dist/tools/web.d.ts +184 -0
- package/dist/tools/worktree.d.ts +81 -0
- package/package.json +2 -2
- package/dist/tools/gitea-issue.d.ts +0 -13
- package/dist/tools/gitea-issue.js +0 -75
|
@@ -1,32 +1,137 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "../internal/harness-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Unified reasoning-intensity abstraction (design/96 S3).
|
|
4
|
+
*
|
|
5
|
+
* The intensity a caller requests IS a {@link ThinkingLevel} — we reuse the existing 7-tier vendor scale
|
|
6
|
+
* (`off | minimal | low | medium | high | xhigh | max`) rather than forking a parallel "fast/standard/deep"
|
|
7
|
+
* enum, so there is ONE knob, not two that must be kept in sync. This module is the single place that maps a
|
|
8
|
+
* requested intensity to what a given endpoint can ACTUALLY honor, and reports the EFFECTIVE intensity — so a
|
|
9
|
+
* request that can't be honored as asked ("high" on a binary enable-only provider like qwen/zai, or
|
|
10
|
+
* "xhigh"/"max" on an endpoint whose `reasoning_effort` only accepts up to "high") is observable rather than
|
|
11
|
+
* silently swallowed or 422'd (design/96 §E honesty red-line; design/46 anti-drift).
|
|
12
|
+
*
|
|
13
|
+
* The brain (openai.ts / anthropic.ts) consumes this. Surfacing the effective intensity to a task result is a
|
|
14
|
+
* later config-domain slice (design/96 S6); here it is a pure, tested mapping.
|
|
15
|
+
*/
|
|
2
16
|
export type ReasoningIntensity = ThinkingLevel;
|
|
17
|
+
/** Type guard: is `v` one of the 7 {@link ThinkingLevel} tiers? (A legacy/unknown reasoning string is not.) */
|
|
3
18
|
export declare function isThinkingLevel(v: unknown): v is ThinkingLevel;
|
|
19
|
+
/** Ordinal rank of a level (`off`=0 … `max`=6). */
|
|
4
20
|
export declare function rankOf(level: ThinkingLevel): number;
|
|
21
|
+
/**
|
|
22
|
+
* The `reasoning_effort` tiers an OpenAI-family endpoint accepts by DEFAULT. Conservative on purpose: a value
|
|
23
|
+
* the endpoint rejects 422s the whole request, so we ship the set stock OpenAI takes
|
|
24
|
+
* (`minimal | low | medium | high`) and clamp anything higher ("xhigh"/"max") down to "high". An endpoint that
|
|
25
|
+
* genuinely accepts a higher tier declares it via `compat.reasoningEffortLevels`.
|
|
26
|
+
*/
|
|
5
27
|
export declare const DEFAULT_EFFORT_LEVELS: readonly ThinkingLevel[];
|
|
6
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The per-provider thinking wire format (mirrors `compat.thinkingFormat`). `"budget"` is the synthetic tag for
|
|
30
|
+
* Anthropic's extended-thinking budget path (no `thinkingFormat` — the anthropic brain doesn't read one), kept
|
|
31
|
+
* here so {@link resolveReasoning} can report ONE endpoint discriminant across both brain families.
|
|
32
|
+
*/
|
|
33
|
+
export type ReasoningFormat = "openai" | "openrouter" | "deepseek" | "together" | "zai" | "qwen" | "qwen-chat-template" | "budget"
|
|
34
|
+
/** Anthropic `output_config.effort` (CC 2.1.198 / Fable-5 class, design/119): the tier is a named
|
|
35
|
+
* effort string, clamped to `compat.effortLevels` — the anthropic-side twin of the OpenAI
|
|
36
|
+
* `reasoning_effort` path (thinking FORM is `thinkingMode`; effort carries the intensity). */
|
|
37
|
+
| "effort";
|
|
38
|
+
/** How a requested intensity resolved against one endpoint's real capability. */
|
|
7
39
|
export interface ReasoningResolution {
|
|
40
|
+
/** The level the caller asked for. */
|
|
8
41
|
requested: ThinkingLevel;
|
|
42
|
+
/** The level actually in effect after mapping to what the endpoint accepts. */
|
|
9
43
|
effective: ThinkingLevel;
|
|
44
|
+
/**
|
|
45
|
+
* True when the endpoint varies behavior by intensity (effort tiers / token budget). False = binary
|
|
46
|
+
* enable-only (qwen / zai / qwen-chat-template, or an effort endpoint that declares
|
|
47
|
+
* `supportsReasoningEffort:false`): thinking is on or off and the tier is NOT honored — read this alongside
|
|
48
|
+
* {@link effective}, which then echoes the request as intent only.
|
|
49
|
+
*/
|
|
10
50
|
graded: boolean;
|
|
51
|
+
/** True when {@link effective} differs from {@link requested} (the request couldn't be honored exactly). */
|
|
11
52
|
clamped: boolean;
|
|
12
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* A {@link ReasoningResolution} enriched with the endpoint discriminant, for observability (design/96 S6).
|
|
56
|
+
* The brain consumes only the {@link ReasoningResolution} fields to shape the request; `format`/`endpoint`
|
|
57
|
+
* are reported (e.g. a `reasoning.resolved` trace event) so a deployment can see WHY a tier was downgraded
|
|
58
|
+
* (binary provider) or clamped (effort-set cap) rather than the resolution being silently swallowed (§E honesty).
|
|
59
|
+
*/
|
|
13
60
|
export interface ResolvedReasoning extends ReasoningResolution {
|
|
61
|
+
/** The wire format the request will use (the load-bearing reason for `graded`/`clamped`). */
|
|
14
62
|
format: ReasoningFormat;
|
|
63
|
+
/** A coarse endpoint label for the trace (`model.api` — e.g. `openai-completions`, `anthropic-messages`). */
|
|
15
64
|
endpoint: string;
|
|
16
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve a requested intensity against a model END-TO-END, dispatching on its wire format the SAME way the
|
|
68
|
+
* brains do — so a trace event and the actual provider request can never drift (design/96 S6). This is the
|
|
69
|
+
* single observability-side resolver: the runner calls it at task start to emit `reasoning.resolved`; the
|
|
70
|
+
* brains keep calling {@link resolveEffort}/{@link resolveBinary}/{@link reasoningBudgetShare} on the hot path.
|
|
71
|
+
*
|
|
72
|
+
* - Anthropic (`api === "anthropic-messages"`) → budget-based: the tier sets a budget share, so it's a real
|
|
73
|
+
* gradient (`graded:true`) and never tier-clamped (`clamped:false`); reported as `format:"budget"`.
|
|
74
|
+
* - Binary enable-only formats (qwen / zai / qwen-chat-template) → `graded:false` (tier not honored).
|
|
75
|
+
* - An effort endpoint with `supportsReasoningEffort:false` → `graded:false` (enable key only, no effort tier).
|
|
76
|
+
* - Otherwise effort-based → clamp DOWN to the endpoint's `reasoningEffortLevels` (default minimal|low|medium|high).
|
|
77
|
+
*
|
|
78
|
+
* `off`/falsy never enables thinking, so it resolves trivially (no clamp, graded:true) — the caller decides
|
|
79
|
+
* whether to emit at all.
|
|
80
|
+
*/
|
|
17
81
|
export declare function resolveReasoning(requested: ThinkingLevel, model: {
|
|
18
82
|
api?: string;
|
|
19
83
|
reasoning?: boolean;
|
|
20
84
|
compat?: unknown;
|
|
21
85
|
}): ResolvedReasoning;
|
|
86
|
+
/**
|
|
87
|
+
* Resolve a requested intensity for an effort-based endpoint (`reasoning_effort` / `reasoning.effort`). Picks
|
|
88
|
+
* the requested tier when supported; otherwise the highest supported tier ≤ requested (clamp DOWN, never
|
|
89
|
+
* silently up); if none is ≤ requested (the endpoint's set is entirely higher — unusual), the lowest supported
|
|
90
|
+
* tier. Never returns a value outside `allowed`, so the brain can't emit a tier the endpoint would 422.
|
|
91
|
+
*/
|
|
22
92
|
export declare function resolveEffort(requested: ThinkingLevel, allowed?: readonly ThinkingLevel[]): ReasoningResolution;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a requested intensity for a binary enable-only endpoint (qwen / zai / qwen-chat-template): the
|
|
95
|
+
* provider only takes an on/off switch, so the intensity tier is NOT honored as a gradient. `graded: false`
|
|
96
|
+
* is the load-bearing honesty signal.
|
|
97
|
+
*/
|
|
23
98
|
export declare function resolveBinary(requested: ThinkingLevel): ReasoningResolution;
|
|
99
|
+
/**
|
|
100
|
+
* Extended-thinking budget as a FRACTION of `max_tokens` per intensity tier, for budget-based endpoints
|
|
101
|
+
* (Anthropic). Lets a higher intensity actually buy a larger thinking budget — before this, the budget was a
|
|
102
|
+
* fixed share regardless of the requested tier, so the intensity had no effect on Anthropic (design/96 §D/§E).
|
|
103
|
+
* The brain clamps the resulting budget to Anthropic's `[1024, max_tokens-1]` window; an explicit
|
|
104
|
+
* `thinkingBudgetTokens` / `thinkingBudgetShare` in config still overrides this table.
|
|
105
|
+
*/
|
|
24
106
|
export declare const REASONING_BUDGET_SHARE: Record<ThinkingLevel, number>;
|
|
107
|
+
/** The extended-thinking budget share for a given intensity tier (see {@link REASONING_BUDGET_SHARE}). */
|
|
25
108
|
export declare function reasoningBudgetShare(level: ThinkingLevel): number;
|
|
109
|
+
/**
|
|
110
|
+
* design/96 §D (S4) — a user-facing NAMED reasoning tier, DISTINCT from the raw 7-tier {@link ThinkingLevel}
|
|
111
|
+
* ({@link ReasoningIntensity}). A profile maps it to engine flags via {@link resolveReasoningProfile}. This is
|
|
112
|
+
* the "ultracode" parity axis: a high tier (`ultra`) buys both a higher thinking level AND a thoroughness/
|
|
113
|
+
* self-verify prompt nudge.
|
|
114
|
+
*/
|
|
26
115
|
export type ReasoningTier = "off" | "fast" | "standard" | "deep" | "ultra";
|
|
116
|
+
/** The engine flags a {@link ReasoningTier} resolves to (design/96 §D table). A profile may override the
|
|
117
|
+
* default; core ships sane defaults. */
|
|
27
118
|
export interface ReasoningProfileFlags {
|
|
119
|
+
/** The vendor thinking level to run at (clamped per-endpoint downstream by {@link resolveEffort}). */
|
|
28
120
|
thinking: ThinkingLevel;
|
|
121
|
+
/**
|
|
122
|
+
* Compose `ORCHESTRATION_AWARENESS` — a high-tier thoroughness/self-verify nudge. 🔴 NOT the `run_workflow`
|
|
123
|
+
* tool (that is gated by a hard sandbox, S8 §6.3); this is purely about how the model reasons. Independent
|
|
124
|
+
* of self-orchestration (a task can be both).
|
|
125
|
+
*/
|
|
29
126
|
awarenessGuidance: boolean;
|
|
127
|
+
/** Advisory HINT that goal mode suits this tier. Core NEVER forces intensity→mode (constitution: the
|
|
128
|
+
* mapping is profile data); a profile/caller decides whether to actually run `runGoal`. */
|
|
30
129
|
goalSuggested: boolean;
|
|
31
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* design/96 §D (S4) — map a named {@link ReasoningTier} to its engine {@link ReasoningProfileFlags} (default
|
|
133
|
+
* mapping). G2 (clay): the intensity→capability mapping is PROFILE-layer data — core gives this primitive +
|
|
134
|
+
* a sane default; a profile/scenario layer calls it (and may override the table) to set a task's `thinking` +
|
|
135
|
+
* prompt flags. Core never forces it. Returns a fresh COPY (callers may mutate).
|
|
136
|
+
*/
|
|
32
137
|
export declare function resolveReasoningProfile(tier: ReasoningTier): ReasoningProfileFlags;
|
|
@@ -1,15 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect **degenerate repetition** — the most common LLM failure mode: a model that gets stuck
|
|
3
|
+
* emitting the same character or short phrase forever (often with no punctuation), e.g. `啊啊啊啊…`
|
|
4
|
+
* or `the answer is the answer is the answer is…`. Brains poll this over the growing output and, on a
|
|
5
|
+
* hit, **cancel the stream** and mark the turn errored, so the loop doesn't keep paying for garbage and
|
|
6
|
+
* downstream (e.g. a team member) treats it as a failed turn rather than a real statement.
|
|
7
|
+
*
|
|
8
|
+
* Only an *exact* tail repetition trips it (character-for-character). The original header claimed
|
|
9
|
+
* normal code "won't false-positive" because repeated structure differs in content — **falsified in
|
|
10
|
+
* the field** (docs/DEADTARGET-WINNING-AVG-COREWARS-2026-07-10.md §〇-3): legitimate code repeats the
|
|
11
|
+
* SAME line verbatim (redcode boot blocks `mov <bootsrc, {bootdst` ×6-8, SQL fixture rows, ASCII-art
|
|
12
|
+
* banners), landed exactly in the rule-2 window, and the cut stream three-struck whole TB tasks dead.
|
|
13
|
+
* Hence two structural allowances, both bounded so true runaway loops are still caught:
|
|
14
|
+
* - rule 1 exempts short symbol runs (ASCII dividers) — a run of a non-letter/digit char only
|
|
15
|
+
* counts as degenerate past `CHAR_RUN_SYMBOL`;
|
|
16
|
+
* - rule 2 exempts *code-line-shaped* units (full-line repeats carrying operand/structure chars or
|
|
17
|
+
* indentation) unless the loop SATURATES the inspection window AND is `SATURATION_REPS` deep in the
|
|
18
|
+
* output — no legitimate boot block is 16+ identical lines deep with nothing after it.
|
|
19
|
+
*
|
|
20
|
+
* Known residual (HRD-BRN-2): rule 2 itself never fires for a unit longer than ~66 chars, because reaching
|
|
21
|
+
* `MIN_REPS` inside the fixed 400-char window requires `p ≤ WINDOW/MIN_REPS`. That is rule 2's general
|
|
22
|
+
* geometry (it applies to prose units too), not the code-line allowance, and widening it means growing
|
|
23
|
+
* the window — deliberately left alone rather than traded for false cuts on long legitimate lines.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The errorMessage a brain stamps on a turn it cut off for degenerate repetition — the human-readable
|
|
27
|
+
* DISPLAY text, and nothing more. It is a single exported constant so the brains do not each invent their
|
|
28
|
+
* own wording, but it is no longer the cross-layer signal: since RB-482 #12 the detection contract is
|
|
29
|
+
* `AssistantMessage.errorKind === "degenerate"` (read via `isDegenerateCutMessage` in
|
|
30
|
+
* ./terminal-cause.js), which `assembleResult` maps to `errorCode="output.degenerate"` + salvages the
|
|
31
|
+
* turn text (design/39). Reword this freely; do NOT match on it. Wording: the output is salvaged now,
|
|
32
|
+
* not discarded.
|
|
33
|
+
*/
|
|
1
34
|
export declare const DEGENERATE_MESSAGE = "degenerate repetition detected \u2014 the model looped on the same text; output cut off at the repetition";
|
|
35
|
+
/**
|
|
36
|
+
* One detector hit — either the event that CUT a stream (`degenerate`) or a repetition that landed in
|
|
37
|
+
* a detection window but was SPARED by a 2e1c161 structural allowance (code-line shape / divider run).
|
|
38
|
+
* Spared events are the RSI raw material for judging whether the allowance is too wide or too narrow
|
|
39
|
+
* (clay 2026-07-10: the detector must report counts AND the detected segment, not just a boolean).
|
|
40
|
+
*/
|
|
2
41
|
export interface RepetitionEvent {
|
|
42
|
+
/** Which rule's window the repetition landed in: rule 1 (`char-run`) or rule 2 (`unit-loop`). */
|
|
3
43
|
rule: "char-run" | "unit-loop";
|
|
44
|
+
/** Repeating-unit length in chars (1 for a char run). */
|
|
4
45
|
period: number;
|
|
46
|
+
/** How many times the unit repeated (the run length for a char run). */
|
|
5
47
|
reps: number;
|
|
48
|
+
/** Sample of the repeated unit/tail, truncated to ≤{@link SEGMENT_MAX} chars. */
|
|
6
49
|
segment: string;
|
|
7
50
|
}
|
|
51
|
+
/** Structured result of one detector pass over a (growing) output tail. */
|
|
8
52
|
export interface RepetitionInspection {
|
|
53
|
+
/** True ⇒ a degenerate loop tripped — the caller should cut the stream (same verdict as `looksDegenerate`). */
|
|
9
54
|
degenerate: boolean;
|
|
55
|
+
/** The event that tripped; present iff `degenerate`. */
|
|
10
56
|
cut?: RepetitionEvent;
|
|
57
|
+
/** Allowance-spared hits (at most one per rule per pass — the smallest period is the fundamental one). */
|
|
11
58
|
spared: RepetitionEvent[];
|
|
12
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Structured detector pass: same verdict as {@link looksDegenerate}, plus WHAT hit (`cut`) and what
|
|
62
|
+
* the 2e1c161 allowances spared. Spared collection is bounded: at most one `char-run` and one
|
|
63
|
+
* `unit-loop` event per pass (rule 2 keeps the smallest period — the fundamental unit — so a p and
|
|
64
|
+
* its 2p/3p multiples don't report the same loop several times).
|
|
65
|
+
*/
|
|
13
66
|
export declare function inspectDegenerate(text: string): RepetitionInspection;
|
|
14
67
|
export declare function looksDegenerate(text: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* T1-4 (brain stream-layer truncation): drop the degenerate TAIL off the text face that tripped a
|
|
70
|
+
* cut, keeping ONE instance of the repeating unit, so the salvaged prefix lands in the final
|
|
71
|
+
* assistant content / `salvagedOutput` without hundreds of chars of loop garbage (which the loop's
|
|
72
|
+
* bounded degenerate-recovery continue would otherwise re-feed the model as context — the very text
|
|
73
|
+
* it must not repeat).
|
|
74
|
+
*
|
|
75
|
+
* Always-on by design (no opt-out flag). The honest argument (fable review tightened the original
|
|
76
|
+
* "zero information loss" claim):
|
|
77
|
+
* - everything removed is a byte-identical duplicate of the unit that is KEPT, so no UNIQUE text is
|
|
78
|
+
* ever swallowed (a non-repeating tail never matches and is returned unchanged). What CAN be lost
|
|
79
|
+
* is a repetition COUNT: if the legitimate text immediately before the loop happened to end with
|
|
80
|
+
* the same unit repeated (e.g. an intentional "very very" directly abutting a "very"-loop), those
|
|
81
|
+
* legitimate repeats fold into the loop and collapse to the single kept instance. This only ever
|
|
82
|
+
* happens on a stream the detector ALREADY cut as degenerate, and the alternative is worse (the
|
|
83
|
+
* full loop tail re-fed to the model);
|
|
84
|
+
* - most legitimate verbatim repetition (code lines, dividers) never gets here at all — the 2e1c161
|
|
85
|
+
* allowances spare it upstream, and a spared stream is never cut;
|
|
86
|
+
* - the loop may have grown far past the 400-char inspection window before the poll fired; the
|
|
87
|
+
* scan below strips ALL trailing repeats (and one trailing PARTIAL unit from post-cut buffered
|
|
88
|
+
* deltas), not just the reps the window measured.
|
|
89
|
+
*
|
|
90
|
+
* Fail-safe floor: the trim only fires when the tail ends with ≥2 exact repeats of the cut event's
|
|
91
|
+
* unit — a WRONG-face call (e.g. the cut tripped in thinking, this is the text face) is structurally
|
|
92
|
+
* a no-op unless that face coincidentally ends with ≥2 exact unit repeats (in which case those — and
|
|
93
|
+
* any partial-unit suffix stripped with them — fold like any other repeats; same bounded cost as
|
|
94
|
+
* above). Thinking/reasoning faces are deliberately never trimmed by the brains: anthropic thinking
|
|
95
|
+
* blocks are signature-sealed for replay (mutating them invalidates the signature), and neither face
|
|
96
|
+
* reaches `salvagedOutput`.
|
|
97
|
+
*/
|
|
15
98
|
export declare function trimDegenerateTail(text: string, cut: RepetitionEvent): string;
|
|
@@ -1,9 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-parameter passthrough (design/40). A deployment can set official provider params it wants but
|
|
3
|
+
* core doesn't model as first-class fields (OpenAI `frequency_penalty`/`presence_penalty`/`top_p`/`seed`/
|
|
4
|
+
* `logit_bias`; Anthropic `top_k`/`top_p`/`stop_sequences`; …) via `Model.extraBody`, and core merges them
|
|
5
|
+
* into the brain's request body. Anchored at the **Model** level (not per-turn) so the serialized body
|
|
6
|
+
* stays byte-stable across turns → the prefix cache holds (design/9/31).
|
|
7
|
+
*
|
|
8
|
+
* Symmetric: each brain owns its structural + tuning keys (`reserved`) and ALWAYS wins — `extraBody` can
|
|
9
|
+
* only supply the long tail, never clobber `messages`/`tools`/`temperature`/etc. Stripping is the safety
|
|
10
|
+
* enforcement (the brains are standalone-usable); `prepareTask` separately warns on a collision (it has
|
|
11
|
+
* `onError`, the brain doesn't — council design/40).
|
|
12
|
+
*/
|
|
13
|
+
/** OpenAI request-body keys core owns; `extraBody` cannot override these. */
|
|
1
14
|
export declare const OPENAI_RESERVED: ReadonlySet<string>;
|
|
15
|
+
/** Anthropic request-body keys core owns; `extraBody` cannot override these. (Note the asymmetry vs
|
|
16
|
+
* OpenAI — e.g. `stop`/`stop_sequences` is NOT reserved here because the Anthropic brain never sets it.) */
|
|
2
17
|
export declare const ANTHROPIC_RESERVED: ReadonlySet<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Open Responses request-body keys core owns; `extraBody` cannot override these (design/163).
|
|
20
|
+
*
|
|
21
|
+
* The last three are NOT keys the adapter sets — they are keys it must be IMPOSSIBLE to set. The
|
|
22
|
+
* Open Responses lane is stateless by contract (this engine is the transcript authority), and
|
|
23
|
+
* `extraBody` is the only other path to the request body, so `store` / `previous_response_id` /
|
|
24
|
+
* `conversation` are reserved purely to close it. A deployment that sets one gets the design/40
|
|
25
|
+
* collision warning at load and a stripped key on the wire, never a silently server-linked turn.
|
|
26
|
+
*/
|
|
3
27
|
export declare const RESPONSES_RESERVED: ReadonlySet<string>;
|
|
28
|
+
/** Reserved set for a model's API. */
|
|
4
29
|
export declare function reservedFor(api: string): ReadonlySet<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Merge `extraBody` into a brain's already-built request `body`, dropping any key the brain owns so core
|
|
32
|
+
* always wins. Returns a NEW object (passthrough keys first, then core keys) when `extraBody` is set; the
|
|
33
|
+
* exact `body` reference (no allocation, byte-identical serialization) when it isn't — so an existing
|
|
34
|
+
* deployment with no `extraBody` is unaffected.
|
|
35
|
+
*/
|
|
5
36
|
export declare function applyExtraBody(body: Record<string, unknown>, extraBody: Record<string, unknown> | undefined, reserved: ReadonlySet<string>): Record<string, unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* [1282] per-call auth REPLACES construction-time auth: drop every auth-bearing header
|
|
39
|
+
* (case-insensitive `authorization` / `x-api-key`) from an already-merged header bag. Called by a
|
|
40
|
+
* brain's buildRequest ONLY when a per-call `options.apiKey` is present — the brain then re-emits
|
|
41
|
+
* the credential in its own wire posture (anthropic `x-api-key`, openai `Bearer`), making the
|
|
42
|
+
* per-call key the request's single credential. Case-insensitivity matters twice: a
|
|
43
|
+
* construction-time `Authorization` (capital A) is what boot env tokens ship, and a case-variant
|
|
44
|
+
* duplicate would otherwise ride the wire alongside the hard-locked lowercase form (fetch Headers
|
|
45
|
+
* folds duplicates into one comma-joined value — broken auth both ways).
|
|
46
|
+
*/
|
|
6
47
|
export declare function stripAuthHeaders(headers: Record<string, string>): void;
|
|
48
|
+
/**
|
|
49
|
+
* The output-cap key(s) each lane's wire form uses. A lane's set is exactly the keys THAT lane's
|
|
50
|
+
* endpoint reads — a stray cap key belonging to another wire form is inert there and must not be
|
|
51
|
+
* mistaken for a bound this request carries.
|
|
52
|
+
*/
|
|
7
53
|
export declare const OUTPUT_CAP_KEYS: Readonly<Record<"openai" | "anthropic" | "responses", readonly string[]>>;
|
|
54
|
+
/**
|
|
55
|
+
* The per-request output cap a FINAL (post-`extraBody`) request body actually carries — the SMALLEST
|
|
56
|
+
* of the lane's cap keys that is present and usable, or `undefined` when none is.
|
|
57
|
+
*
|
|
58
|
+
* Read off the merged body rather than off the knob the adapter set, because those are not the same
|
|
59
|
+
* thing: `extraBody` can contribute a cap under a spelling the adapter did not choose, and a
|
|
60
|
+
* consumer that treats the adapter's own number as "the cap on the wire" would be wrong about a
|
|
61
|
+
* request that carries two. Smallest wins so a consumer reasoning about the bound stays conservative.
|
|
62
|
+
*/
|
|
8
63
|
export declare function effectiveOutputCap(body: Record<string, unknown>, keys: readonly string[]): number | undefined;
|
|
64
|
+
/** Names in `extraBody` that collide with a reserved (core-owned) key — for a load-time warning. */
|
|
9
65
|
export declare function reservedCollisions(extraBody: Record<string, unknown> | undefined, reserved: ReadonlySet<string>): string[];
|
package/dist/brain/retry.d.ts
CHANGED
|
@@ -1,8 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient-failure retry backoff (RB-420-b, CC 2.1.220 parity).
|
|
3
|
+
*
|
|
4
|
+
* The shape is **increasing exponential + light additive jitter**, not full jitter:
|
|
5
|
+
* `min(base * 2^attempt, 32s)` plus up to 25% of that. Full jitter (`random(0, exp)`) made the Nth
|
|
6
|
+
* wait routinely SHORTER than the first, which destroys the one property a rate-limited provider
|
|
7
|
+
* needs from a client — that each successive attempt backs further off. The 25% additive spread is
|
|
8
|
+
* still enough de-synchronization to keep many clients from re-converging into a thundering herd.
|
|
9
|
+
*
|
|
10
|
+
* Provider wait hints ride at ONE layer above the computed value, both as a FLOOR
|
|
11
|
+
* (`max(hint, computed)`) — retrying before the server said just gets rejected again, but a hint must
|
|
12
|
+
* never SHORTEN a backoff we already escalated past it:
|
|
13
|
+
* - `Retry-After` (delta-seconds or HTTP-date), on any retryable response.
|
|
14
|
+
* - `anthropic-ratelimit-unified-reset` (an absolute epoch-seconds stamp of when the rate-limit window
|
|
15
|
+
* reopens), read only off a 429 — it is a rate-limit signal.
|
|
16
|
+
* When both are present the larger wins. Both are clamped so a hostile/buggy value
|
|
17
|
+
* (`Retry-After: 2147483647`, a reset stamp years out) cannot pin the caller for hours.
|
|
18
|
+
*/
|
|
19
|
+
/** Parse a `Retry-After` header (delta-seconds or HTTP-date) to ms; undefined if absent/unparseable. */
|
|
1
20
|
export declare function parseRetryAfter(res: Response | undefined): number | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Parse `anthropic-ratelimit-unified-reset` — an ABSOLUTE epoch-seconds stamp of when the unified
|
|
23
|
+
* rate-limit window reopens — into ms from now. Undefined when absent, unparseable, or already past
|
|
24
|
+
* (a stale stamp carries no wait, so the caller falls back to its computed backoff).
|
|
25
|
+
*/
|
|
2
26
|
export declare function parseRateLimitReset(res: Response | undefined): number | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* A single provider wait hint after clamping, carrying enough of its own history to disclose a
|
|
29
|
+
* truncation: `rawMs` is only set when the response actually asked for more than {@link
|
|
30
|
+
* MAX_HEADER_WAIT_MS} and the hint was cut down to it.
|
|
31
|
+
*/
|
|
3
32
|
export interface ProviderWaitHint {
|
|
33
|
+
/** The value honored (post-clamp), ms. */
|
|
4
34
|
readonly ms: number;
|
|
35
|
+
/** The value the response asked for, ms — present only when it exceeded the clamp. */
|
|
5
36
|
readonly rawMs?: number;
|
|
6
37
|
}
|
|
38
|
+
/** The larger of the response's two wait hints, each clamped; undefined when the response carries
|
|
39
|
+
* neither (or only stale/unparseable ones). Exported so a caller can disclose a truncation instead
|
|
40
|
+
* of silently honoring less than the response asked for.
|
|
41
|
+
*
|
|
42
|
+
* Ranks by the CLAMPED `ms` first (that is the value actually honored — unchanged from before this
|
|
43
|
+
* hint carried `rawMs`), but a tie there is broken by the UNCLAMPED value: two hints can clamp to the
|
|
44
|
+
* same 60s ceiling while asking for very different waits (`Retry-After: 60` vs a reset a day out), and
|
|
45
|
+
* picking whichever came first would silently return the one with no `rawMs` — reporting the smaller
|
|
46
|
+
* ask as if nothing had been truncated at all, even though the pair's honored wait is identical either
|
|
47
|
+
* way. */
|
|
7
48
|
export declare function providerWaitHint(res: Response | undefined): ProviderWaitHint | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Backoff (ms) for retry `attempt` (0-based) of a transient failure: an increasing capped exponential
|
|
51
|
+
* plus ≤25% additive jitter, floored by the response's provider wait hints (see the module note).
|
|
52
|
+
* `rand` is injectable for deterministic tests.
|
|
53
|
+
*/
|
|
8
54
|
export declare function retryBackoffMs(baseDelayMs: number, attempt: number, res?: Response, rand?: () => number): number;
|
package/dist/brain/routing.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import type { Brain } from "../core/types.js";
|
|
2
2
|
export interface RoutingBrainOptions {
|
|
3
|
+
/** Which field of the Model selects the route. Default "provider". */
|
|
3
4
|
by?: "provider" | "api";
|
|
5
|
+
/** Brain used when no route matches. If omitted, an unmatched model yields an error. */
|
|
4
6
|
fallback?: Brain;
|
|
5
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Route to different brains by a Model field (default `model.provider`). Lets one Runner serve
|
|
10
|
+
* multiple providers/gateways; compose with `createFailoverBrain` for per-route failover.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* createRoutingBrain({
|
|
14
|
+
* "vllm": createOpenAIBrain({ baseUrl: VLLM }),
|
|
15
|
+
* "openai": createOpenAIBrain({ baseUrl: OPENAI, apiKey }),
|
|
16
|
+
* }, { fallback: createOpenAIBrain({ baseUrl: VLLM }) });
|
|
17
|
+
*/
|
|
6
18
|
export declare function createRoutingBrain(routes: Record<string, Brain>, opts?: RoutingBrainOptions): Brain;
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import type { BrainStatus } from "../core/types.js";
|
|
2
|
+
/** Run `fn` with a per-task brain-status sink in scope. ALS propagates it through the async brain calls
|
|
3
|
+
* inside `fn` (the harness's prompt → brain.stream → connect/retry loop), so {@link emitBrainStatus}
|
|
4
|
+
* reaches THIS task's sink and nothing else. */
|
|
2
5
|
export declare function runWithStatusSink<T>(emit: (s: BrainStatus) => void, fn: () => Promise<T>): Promise<T>;
|
|
6
|
+
/**
|
|
7
|
+
* Report a brain-layer liveness status to the active per-task sink, if any. A no-op outside a
|
|
8
|
+
* {@link runWithStatusSink} scope (e.g. compaction's `complete`, or a brain used standalone).
|
|
9
|
+
*
|
|
10
|
+
* HRD-BRN-6: swallow-guarded, like its sibling {@link emitBrainTelemetry} — an ADVISORY frame must never
|
|
11
|
+
* change a brain call's outcome. It could: the emit forwards straight into deployment-supplied code, and
|
|
12
|
+
* the circuit breaker's fast-fail emits the `circuit_open` frame from a void-ed async IIFE BEFORE the
|
|
13
|
+
* error terminal is pushed, so a throwing sink took the whole call down (`result()` pending forever plus
|
|
14
|
+
* an unhandled rejection) instead of costing one status frame. `stream-engine.ts` already wrapped its own
|
|
15
|
+
* call site for this exact reason and states the contract ("an advisory frame must never break a settled
|
|
16
|
+
* call"); the guard belongs HERE so every call site inherits it rather than each remembering.
|
|
17
|
+
*/
|
|
3
18
|
export declare function emitBrainStatus(status: BrainStatus): void;
|
|
19
|
+
/**
|
|
20
|
+
* Silent-fallback telemetry (service [398] C1/C4/C5/C6) — the brain→runner TELEMETRY channel, a
|
|
21
|
+
* SECOND ALS sink parallel to the status one. Statuses are user-facing liveness frames (wire
|
|
22
|
+
* `status` events); telemetry is operator-facing fallback accounting the Runner bridges to trace
|
|
23
|
+
* events (`brain.failover` / `breaker.transition` / `brain.retry` / `vision.placeholder`). Kept
|
|
24
|
+
* separate so widening telemetry never bloats the shell-visible status vocabulary. Same contract:
|
|
25
|
+
* fire-and-forget, no-op outside a {@link runWithBrainTelemetry} scope, MUST NOT throw.
|
|
26
|
+
*/
|
|
4
27
|
export type BrainTelemetry = {
|
|
5
28
|
kind: "failover";
|
|
6
29
|
servedIndex: number;
|
|
@@ -18,9 +41,15 @@ export type BrainTelemetry = {
|
|
|
18
41
|
} | {
|
|
19
42
|
kind: "vision_placeholder";
|
|
20
43
|
count: number;
|
|
21
|
-
}
|
|
44
|
+
}
|
|
45
|
+
/** PDF read: a `document` content block was substituted with a text placeholder because the serving
|
|
46
|
+
* API/model cannot take native document input (openai brain always; anthropic brain when the model
|
|
47
|
+
* declares no vision). Silent quality loss → frame (same [398] C4 discipline as vision_placeholder). */
|
|
48
|
+
| {
|
|
22
49
|
kind: "document_placeholder";
|
|
23
50
|
count: number;
|
|
24
51
|
};
|
|
52
|
+
/** Run `fn` with a per-task brain-telemetry sink in scope (Runner-side; compose with runWithStatusSink). */
|
|
25
53
|
export declare function runWithBrainTelemetry<T>(emit: (t: BrainTelemetry) => void, fn: () => Promise<T>): Promise<T>;
|
|
54
|
+
/** Report a brain-layer fallback/telemetry event to the active per-task sink, if any. */
|
|
26
55
|
export declare function emitBrainTelemetry(t: BrainTelemetry): void;
|
|
@@ -1,47 +1,194 @@
|
|
|
1
1
|
import { type AssistantMessage, type Model, createAssistantMessageEventStream } from "../internal/llm.js";
|
|
2
2
|
import { type BrainTimeoutConfig } from "./timeout.js";
|
|
3
|
+
/**
|
|
4
|
+
* Shared streaming engine for the OpenAI- and Anthropic-shaped brains (design/32, consolidation).
|
|
5
|
+
*
|
|
6
|
+
* It owns the **bug-prone machinery that was duplicated verbatim** across `openai.ts` and
|
|
7
|
+
* `anthropic.ts` — the connect-timeout + retry loop, the three-tier timer logic (first-token + idle
|
|
8
|
+
* watchdog) with their flag resets, the SSE read loop + line framing, the one-shot cleanup, and the
|
|
9
|
+
* `.catch/.finally` error wrapping. Centralizing the timer logic is the point: the same first-token /
|
|
10
|
+
* idle reset bug was fixed twice (1.38.2 / 1.40.2) precisely because this lived in two copies.
|
|
11
|
+
*
|
|
12
|
+
* Each provider keeps its OWN request-shaping (`buildRequest`) and SSE parsing + finalization
|
|
13
|
+
* (`makeParser`) — those genuinely differ (chat-completions deltas vs typed content-block events,
|
|
14
|
+
* thinking signatures, per-provider usage/finish-reason) and stay verbatim in the adapters, so this
|
|
15
|
+
* refactor is behavior-preserving (safety net: resilience/streaming/brain tests).
|
|
16
|
+
*/
|
|
3
17
|
export interface StreamEngineConfig extends BrainTimeoutConfig {
|
|
18
|
+
/**
|
|
19
|
+
* Retries for transient failures (network / 5xx / 429) BEFORE the body streams, and for the
|
|
20
|
+
* mid-stream tier-C re-send that shares this budget. Absent ⇒ {@link resolveMaxRetries} (the
|
|
21
|
+
* `SEMA_MAX_RETRIES` env, else 10). An explicit value here is authoritative and NOT clamped —
|
|
22
|
+
* deployment sovereignty, same posture as the timeout fields. Sovereignty covers a chosen NUMBER: a
|
|
23
|
+
* non-finite value (CLS-A-8) falls back to the env/default instead of disabling the budget.
|
|
24
|
+
*/
|
|
4
25
|
maxRetries?: number;
|
|
26
|
+
/** First-retry backoff ms; each further attempt doubles it (capped, +≤25% jitter). Default 500. */
|
|
5
27
|
retryDelayMs?: number;
|
|
6
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the transient-failure retry budget — explicit config > `SEMA_MAX_RETRIES` env > default 10
|
|
31
|
+
* (the same three-level shape as the shell's timeout caps). A non-numeric, negative, or empty env
|
|
32
|
+
* value is ignored rather than silently meaning "zero retries"; an oversized one is clamped.
|
|
33
|
+
*/
|
|
7
34
|
export declare function resolveMaxRetries(configured: number | undefined): number;
|
|
35
|
+
/**
|
|
36
|
+
* Is `rebuilt` still THE SAME REQUEST as `original`, differing only in the output cap and the values
|
|
37
|
+
* that follow from it (ruled 2026-08-05)?
|
|
38
|
+
*
|
|
39
|
+
* `buildRequest` re-reads the adapter's model, config, options and context, so a re-shaped re-send is
|
|
40
|
+
* only trustworthy to the extent that those did not move underneath it. Rather than deep-copying all
|
|
41
|
+
* of them, the two built requests are compared directly: same destination, same headers, and a body
|
|
42
|
+
* identical outside {@link SSERequest.capDerivedPaths}. That covers a changed model id, prompt, tool
|
|
43
|
+
* set, temperature, reasoning posture, or passthrough parameter with one rule instead of a list of
|
|
44
|
+
* things to remember.
|
|
45
|
+
*
|
|
46
|
+
* Conservative on anything it cannot read: a body that is not a JSON object compares by exact string,
|
|
47
|
+
* so an unparseable pair declines rather than being waved through.
|
|
48
|
+
*/
|
|
8
49
|
export declare function sameRequestModuloCap(original: SSERequest, rebuilt: SSERequest, imposedCap: number): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* CC parity (`Q1b`, ruled 2026-08-05) — read the provider's EXPLICIT retry verdict off the response.
|
|
52
|
+
*
|
|
53
|
+
* The status code is an inference about whether a re-send can help; this header is the provider
|
|
54
|
+
* SAYING so, and it outranks the inference in BOTH directions:
|
|
55
|
+
* - `x-should-retry: true` — retry even a status the predicate calls terminal (the upstream knows
|
|
56
|
+
* the condition is transient, e.g. a fenced-off shard behind a 4xx).
|
|
57
|
+
* - `x-should-retry: false` — do NOT retry even a 5xx/429/408/409. Without this, a provider that
|
|
58
|
+
* has already decided the request is unservable is re-sent the same request up to the whole
|
|
59
|
+
* budget (10 by default), which costs the caller and the upstream alike for a known-refused call.
|
|
60
|
+
*
|
|
61
|
+
* Returns `undefined` for an absent header AND for any value other than the two exact tokens — "no
|
|
62
|
+
* opinion", so the status-code predicate decides and the pre-existing behavior is byte-identical.
|
|
63
|
+
* Exact-match (no case folding / trimming) is deliberate: it mirrors CC's comparison, and `Headers`
|
|
64
|
+
* already normalizes surrounding whitespace, so anything else reaching here is a value we did not
|
|
65
|
+
* define and must not guess at.
|
|
66
|
+
*
|
|
67
|
+
* Tolerates a `doFetch` (BYOM injection point) whose Response-shaped return has no usable `headers`.
|
|
68
|
+
*/
|
|
9
69
|
export declare function shouldRetryHeaderVerdict(res: Response | undefined): boolean | undefined;
|
|
10
70
|
export interface SSERequest {
|
|
11
71
|
url: string;
|
|
12
72
|
headers: Record<string, string>;
|
|
13
73
|
body: string;
|
|
74
|
+
/**
|
|
75
|
+
* The per-request output cap this request ACTUALLY carries on the wire, when the adapter sets one
|
|
76
|
+
* (the adapters disagree on the body key, so the engine cannot read it off `body`). Absent means
|
|
77
|
+
* "no cap sent" or "this adapter does not report it".
|
|
78
|
+
*
|
|
79
|
+
* The context-overflow recovery needs it because the number the PROVIDER echoes back in its error
|
|
80
|
+
* is not evidence of what was sent: a stale or malformed 400 reporting a larger cap than the
|
|
81
|
+
* request carried would otherwise let the recovery "lower" the cap to a value ABOVE the caller's
|
|
82
|
+
* own explicit bound. It doubles as the check that an adapter honored an imposed cap at all.
|
|
83
|
+
*/
|
|
14
84
|
outputCapTokens?: number;
|
|
85
|
+
/**
|
|
86
|
+
* The body PATHS this adapter may legitimately re-derive when the engine imposes a different output
|
|
87
|
+
* cap — the cap's own key(s), plus anything computed from it. Dotted for nested leaves
|
|
88
|
+
* (`"thinking.budget_tokens"`), deliberately: naming the whole `thinking` object would excuse a
|
|
89
|
+
* change of reasoning MODE as if it followed from the cap, when only the budget does.
|
|
90
|
+
*
|
|
91
|
+
* The engine uses this to check that a re-shaped re-send is still THE SAME REQUEST. `buildRequest`
|
|
92
|
+
* re-reads the adapter's model/config/options/context, so a deployment that changes any of them
|
|
93
|
+
* while a call is in flight could otherwise turn "lower the cap and try again" into a request with
|
|
94
|
+
* a different model id, prompt, tool set, or reasoning posture. Everything outside these paths must
|
|
95
|
+
* come back identical; an adapter that declares nothing therefore permits no body change at all.
|
|
96
|
+
*/
|
|
15
97
|
capDerivedPaths?: readonly string[];
|
|
98
|
+
/**
|
|
99
|
+
* Called by the engine once, if and only if THIS request becomes the one that is sent.
|
|
100
|
+
*
|
|
101
|
+
* `buildRequest` is also called SPECULATIVELY — the context-overflow recovery builds a candidate
|
|
102
|
+
* and may then decline it. Any bookkeeping the adapter keeps for its own later diagnostics (which
|
|
103
|
+
* output cap actually went out, which knob supplied it, whether thinking was requested) therefore
|
|
104
|
+
* belongs here rather than in the build: assigned during a build, a rejected candidate's numbers
|
|
105
|
+
* would outlive it and be reported against the request that really was sent.
|
|
106
|
+
*/
|
|
16
107
|
onCommitted?: () => void;
|
|
17
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Per-attempt request overrides the ENGINE imposes on a re-send (ruled 2026-08-05). Absent — which is
|
|
111
|
+
* every ordinary build — the adapter shapes its request exactly as before, byte for byte.
|
|
112
|
+
*
|
|
113
|
+
* An adapter MUST honor every field it can express. Ignoring one is not silent: the only producer is
|
|
114
|
+
* the context-overflow recovery, whose no-progress guard sees the unchanged cap come back in the next
|
|
115
|
+
* provider error and fails loud with the original failure instead of looping.
|
|
116
|
+
*/
|
|
18
117
|
export interface BuildRequestOverrides {
|
|
118
|
+
/**
|
|
119
|
+
* The per-request output cap to send, outranking every caller/model knob for THIS attempt. Set when
|
|
120
|
+
* the provider reported that `input + max_tokens` exceeds the context limit; whichever body key the
|
|
121
|
+
* adapter uses for the cap (`max_tokens` / `max_completion_tokens` / `max_output_tokens`) must carry
|
|
122
|
+
* this value, and any field derived from the cap must be re-derived from it — the point of routing
|
|
123
|
+
* this through the adapter rather than patching the serialized body.
|
|
124
|
+
*/
|
|
19
125
|
maxOutputTokens?: number;
|
|
20
126
|
}
|
|
127
|
+
/** Controls that the engine passes to the per-stream parser. */
|
|
21
128
|
export interface StreamControls {
|
|
22
129
|
out: ReturnType<typeof createAssistantMessageEventStream>;
|
|
130
|
+
/** Shared mutable partial-snapshot message; the parser mutates `content` and emits `{ ...partial }`. */
|
|
23
131
|
partial: AssistantMessage;
|
|
132
|
+
/** Call on every CONTENT delta (text/thinking/tool input) — resets the first-token + idle watchdogs. */
|
|
24
133
|
sawContentToken(): void;
|
|
134
|
+
/** Cancel the underlying stream (e.g. degenerate-repetition cutoff). The read loop then ends. */
|
|
25
135
|
cancel(): void;
|
|
26
136
|
}
|
|
27
137
|
export interface StreamParser {
|
|
138
|
+
/** Handle one raw SSE line (`data: {...}`); parse, emit events, call `ctrl.sawContentToken()` on content. */
|
|
28
139
|
onLine(line: string): void;
|
|
140
|
+
/** Build + emit the final `done`/`error` message after the stream ends. */
|
|
29
141
|
finalize(): void;
|
|
142
|
+
/**
|
|
143
|
+
* design/124 §0.5-2: introspection for the mid-stream failure tiering. Read directly off the
|
|
144
|
+
* parser's accumulated state (cheap, no side effects):
|
|
145
|
+
* - `hasSubstantiveText` — non-blank answer text (or a streamed refusal) has been emitted → tier A
|
|
146
|
+
* (partial finalize) territory.
|
|
147
|
+
* - `hasCompletedToolCall` — at least one tool call CLOSED successfully (a `toolcall_end` was
|
|
148
|
+
* emitted, so an in-stream executor may already be running it) → tier A; also the retry-safety
|
|
149
|
+
* assertion (§0.5-4): a re-send is FORBIDDEN once this is true (double execution).
|
|
150
|
+
* - `hasOnlyThinking` — thinking streamed but nothing substantive (no non-blank text, no completed
|
|
151
|
+
* tool call) → tier B (seal + whole-turn retry). A dangling tool-call accumulation alongside the
|
|
152
|
+
* thinking does not veto this (nothing was admitted; the retry's snapshot replacement drops it).
|
|
153
|
+
*/
|
|
30
154
|
snapshot(): {
|
|
31
155
|
hasSubstantiveText: boolean;
|
|
32
156
|
hasCompletedToolCall: boolean;
|
|
33
157
|
hasOnlyThinking: boolean;
|
|
34
158
|
};
|
|
159
|
+
/**
|
|
160
|
+
* design/124 §0.5-2 tier B: seal the UI stream before a whole-turn retry — emit `thinking_end` for
|
|
161
|
+
* any OPEN thinking block (the block state is parser-private; the engine cannot emit a correct
|
|
162
|
+
* close). The retry attempt's parser may then legally REOPEN a thinking block at the SAME
|
|
163
|
+
* contentIndex — consumers render by partial snapshot (replacement semantics), so index reuse is
|
|
164
|
+
* part of the event contract (see AssistantMessageEvent thinking_start note). Idempotent.
|
|
165
|
+
*/
|
|
35
166
|
sealForRetry(): void;
|
|
36
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Run one streaming request end-to-end and drive the provider `parser`. Returns a fresh event stream;
|
|
170
|
+
* never throws (errors are pushed as `error` events, mirroring the Brain contract).
|
|
171
|
+
*/
|
|
37
172
|
export declare function runStreamingBrain(args: {
|
|
38
173
|
model: Model;
|
|
39
174
|
doFetch: typeof fetch;
|
|
40
175
|
signal?: AbortSignal;
|
|
41
176
|
config: StreamEngineConfig;
|
|
177
|
+
/** HTTP error message prefix (e.g. "gateway" / "anthropic"). */
|
|
42
178
|
httpLabel: string;
|
|
179
|
+
/** Build the one request. May throw (→ surfaced as an error event). Called again with
|
|
180
|
+
* {@link BuildRequestOverrides} when the engine re-shapes a re-send (context-overflow recovery). */
|
|
43
181
|
buildRequest: (overrides?: BuildRequestOverrides) => SSERequest;
|
|
182
|
+
/** Create the per-stream parser once the partial + controls exist. */
|
|
44
183
|
makeParser: (ctrl: StreamControls) => StreamParser;
|
|
184
|
+
/**
|
|
185
|
+
* 1.296 件A — per-call ADVISORY stall watchdogs (`StreamOptions.stallTimeouts`). Priority chain:
|
|
186
|
+
* brain construction-time config (`BrainTimeoutConfig.connectTimeoutMs`/`firstTokenTimeoutMs`/
|
|
187
|
+
* `idleTimeoutMs`) wins whenever PRESENT — including an explicit 0/negative, which means
|
|
188
|
+
* "deliberately disabled" — and only an ABSENT (undefined) field adopts the advisory value; both
|
|
189
|
+
* absent ⇒ off. `connectMs` bounds the fetch→response-headers wait: the engine's post-headers
|
|
190
|
+
* timers can't see a gateway that never returns headers.
|
|
191
|
+
*/
|
|
45
192
|
stallTimeouts?: {
|
|
46
193
|
connectMs?: number;
|
|
47
194
|
firstTokenMs?: number;
|