@smthrs/harness 0.0.0-stage → 1.0.0-rc.3
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 +387 -0
- package/LICENSE +21 -0
- package/README.md +138 -2
- package/dist/cjs/AgentEvent.d.ts +3092 -0
- package/dist/cjs/AgentEvent.d.ts.map +1 -0
- package/dist/cjs/AgentEvent.js +1116 -0
- package/dist/cjs/AgentEvent.js.map +7 -0
- package/dist/cjs/CallLedger.d.ts +378 -0
- package/dist/cjs/CallLedger.d.ts.map +1 -0
- package/dist/cjs/CallLedger.js +240 -0
- package/dist/cjs/CallLedger.js.map +7 -0
- package/dist/cjs/Cell.d.ts +774 -0
- package/dist/cjs/Cell.d.ts.map +1 -0
- package/dist/cjs/Cell.js +431 -0
- package/dist/cjs/Cell.js.map +7 -0
- package/dist/cjs/CellCalls.d.ts +115 -0
- package/dist/cjs/CellCalls.d.ts.map +1 -0
- package/dist/cjs/CellCalls.js +98 -0
- package/dist/cjs/CellCalls.js.map +7 -0
- package/dist/cjs/CellHistory.d.ts +102 -0
- package/dist/cjs/CellHistory.d.ts.map +1 -0
- package/dist/cjs/CellHistory.js +54 -0
- package/dist/cjs/CellHistory.js.map +7 -0
- package/dist/cjs/CellTurn.d.ts +1071 -0
- package/dist/cjs/CellTurn.d.ts.map +1 -0
- package/dist/cjs/CellTurn.js +2618 -0
- package/dist/cjs/CellTurn.js.map +7 -0
- package/dist/cjs/CellValidation.d.ts +94 -0
- package/dist/cjs/CellValidation.d.ts.map +1 -0
- package/dist/cjs/CellValidation.js +218 -0
- package/dist/cjs/CellValidation.js.map +7 -0
- package/dist/cjs/Compaction.d.ts +158 -0
- package/dist/cjs/Compaction.d.ts.map +1 -0
- package/dist/cjs/Compaction.js +189 -0
- package/dist/cjs/Compaction.js.map +7 -0
- package/dist/cjs/CompletionClaim.d.ts +801 -0
- package/dist/cjs/CompletionClaim.d.ts.map +1 -0
- package/dist/cjs/CompletionClaim.js +303 -0
- package/dist/cjs/CompletionClaim.js.map +7 -0
- package/dist/cjs/ContextWindow.d.ts +435 -0
- package/dist/cjs/ContextWindow.d.ts.map +1 -0
- package/dist/cjs/ContextWindow.js +319 -0
- package/dist/cjs/ContextWindow.js.map +7 -0
- package/dist/cjs/EngineLike.d.ts +546 -0
- package/dist/cjs/EngineLike.d.ts.map +1 -0
- package/dist/cjs/EngineLike.js +114 -0
- package/dist/cjs/EngineLike.js.map +7 -0
- package/dist/cjs/ExternalTranscript.d.ts +349 -0
- package/dist/cjs/ExternalTranscript.d.ts.map +1 -0
- package/dist/cjs/ExternalTranscript.js +826 -0
- package/dist/cjs/ExternalTranscript.js.map +7 -0
- package/dist/cjs/FailedCall.d.ts +132 -0
- package/dist/cjs/FailedCall.d.ts.map +1 -0
- package/dist/cjs/FailedCall.js +53 -0
- package/dist/cjs/FailedCall.js.map +7 -0
- package/dist/cjs/FlowBinding.d.ts +289 -0
- package/dist/cjs/FlowBinding.d.ts.map +1 -0
- package/dist/cjs/FlowBinding.js +251 -0
- package/dist/cjs/FlowBinding.js.map +7 -0
- package/dist/cjs/HarnessError.d.ts +57 -0
- package/dist/cjs/HarnessError.d.ts.map +1 -0
- package/dist/cjs/HarnessError.js +75 -0
- package/dist/cjs/HarnessError.js.map +7 -0
- package/dist/cjs/Judgement.d.ts +289 -0
- package/dist/cjs/Judgement.d.ts.map +1 -0
- package/dist/cjs/Judgement.js +240 -0
- package/dist/cjs/Judgement.js.map +7 -0
- package/dist/cjs/Monitor.d.ts +374 -0
- package/dist/cjs/Monitor.d.ts.map +1 -0
- package/dist/cjs/Monitor.js +233 -0
- package/dist/cjs/Monitor.js.map +7 -0
- package/dist/cjs/NarrowedCheck.d.ts +523 -0
- package/dist/cjs/NarrowedCheck.d.ts.map +1 -0
- package/dist/cjs/NarrowedCheck.js +263 -0
- package/dist/cjs/NarrowedCheck.js.map +7 -0
- package/dist/cjs/Notifications.d.ts +42 -0
- package/dist/cjs/Notifications.d.ts.map +1 -0
- package/dist/cjs/Notifications.js +177 -0
- package/dist/cjs/Notifications.js.map +7 -0
- package/dist/cjs/Plan.d.ts +127 -0
- package/dist/cjs/Plan.d.ts.map +1 -0
- package/dist/cjs/Plan.js +77 -0
- package/dist/cjs/Plan.js.map +7 -0
- package/dist/cjs/QuickJSSandbox.d.ts +151 -0
- package/dist/cjs/QuickJSSandbox.d.ts.map +1 -0
- package/dist/cjs/QuickJSSandbox.js +987 -0
- package/dist/cjs/QuickJSSandbox.js.map +7 -0
- package/dist/cjs/Relevance.d.ts +213 -0
- package/dist/cjs/Relevance.d.ts.map +1 -0
- package/dist/cjs/Relevance.js +183 -0
- package/dist/cjs/Relevance.js.map +7 -0
- package/dist/cjs/Sandbox.d.ts +637 -0
- package/dist/cjs/Sandbox.d.ts.map +1 -0
- package/dist/cjs/Sandbox.js +260 -0
- package/dist/cjs/Sandbox.js.map +7 -0
- package/dist/cjs/Steering.d.ts +464 -0
- package/dist/cjs/Steering.d.ts.map +1 -0
- package/dist/cjs/Steering.js +153 -0
- package/dist/cjs/Steering.js.map +7 -0
- package/dist/cjs/StructuredOutput.d.ts +252 -0
- package/dist/cjs/StructuredOutput.d.ts.map +1 -0
- package/dist/cjs/StructuredOutput.js +266 -0
- package/dist/cjs/StructuredOutput.js.map +7 -0
- package/dist/cjs/Sufficiency.d.ts +195 -0
- package/dist/cjs/Sufficiency.d.ts.map +1 -0
- package/dist/cjs/Sufficiency.js +110 -0
- package/dist/cjs/Sufficiency.js.map +7 -0
- package/dist/cjs/Supervisor.d.ts +719 -0
- package/dist/cjs/Supervisor.d.ts.map +1 -0
- package/dist/cjs/Supervisor.js +313 -0
- package/dist/cjs/Supervisor.js.map +7 -0
- package/dist/cjs/Tokens.d.ts +86 -0
- package/dist/cjs/Tokens.d.ts.map +1 -0
- package/dist/cjs/Tokens.js +72 -0
- package/dist/cjs/Tokens.js.map +7 -0
- package/dist/cjs/Transcript.d.ts +171 -0
- package/dist/cjs/Transcript.d.ts.map +1 -0
- package/dist/cjs/Transcript.js +342 -0
- package/dist/cjs/Transcript.js.map +7 -0
- package/dist/cjs/TruncatedOutput.d.ts +186 -0
- package/dist/cjs/TruncatedOutput.d.ts.map +1 -0
- package/dist/cjs/TruncatedOutput.js +143 -0
- package/dist/cjs/TruncatedOutput.js.map +7 -0
- package/dist/cjs/UnmovedTree.d.ts +113 -0
- package/dist/cjs/UnmovedTree.d.ts.map +1 -0
- package/dist/cjs/UnmovedTree.js +38 -0
- package/dist/cjs/UnmovedTree.js.map +7 -0
- package/dist/cjs/UnresolvedFailure.d.ts +196 -0
- package/dist/cjs/UnresolvedFailure.d.ts.map +1 -0
- package/dist/cjs/UnresolvedFailure.js +77 -0
- package/dist/cjs/UnresolvedFailure.js.map +7 -0
- package/dist/cjs/VacuousVerification.d.ts +237 -0
- package/dist/cjs/VacuousVerification.d.ts.map +1 -0
- package/dist/cjs/VacuousVerification.js +91 -0
- package/dist/cjs/VacuousVerification.js.map +7 -0
- package/dist/cjs/VariablesPanel.d.ts +117 -0
- package/dist/cjs/VariablesPanel.d.ts.map +1 -0
- package/dist/cjs/VariablesPanel.js +108 -0
- package/dist/cjs/VariablesPanel.js.map +7 -0
- package/dist/cjs/index.d.ts +172 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +99 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/bytes.d.ts +36 -0
- package/dist/cjs/internal/bytes.d.ts.map +1 -0
- package/dist/cjs/internal/bytes.js +57 -0
- package/dist/cjs/internal/bytes.js.map +7 -0
- package/dist/cjs/internal/cellPrompt.d.ts +122 -0
- package/dist/cjs/internal/cellPrompt.d.ts.map +1 -0
- package/dist/cjs/internal/cellPrompt.js +146 -0
- package/dist/cjs/internal/cellPrompt.js.map +7 -0
- package/dist/cjs/internal/compactable.d.ts +44 -0
- package/dist/cjs/internal/compactable.d.ts.map +1 -0
- package/dist/cjs/internal/compactable.js +56 -0
- package/dist/cjs/internal/compactable.js.map +7 -0
- package/dist/cjs/internal/compactionMarks.d.ts +278 -0
- package/dist/cjs/internal/compactionMarks.d.ts.map +1 -0
- package/dist/cjs/internal/compactionMarks.js +212 -0
- package/dist/cjs/internal/compactionMarks.js.map +7 -0
- package/dist/cjs/internal/demandText.d.ts +95 -0
- package/dist/cjs/internal/demandText.d.ts.map +1 -0
- package/dist/cjs/internal/demandText.js +70 -0
- package/dist/cjs/internal/demandText.js.map +7 -0
- package/dist/cjs/internal/elide.d.ts +109 -0
- package/dist/cjs/internal/elide.d.ts.map +1 -0
- package/dist/cjs/internal/elide.js +59 -0
- package/dist/cjs/internal/elide.js.map +7 -0
- package/dist/cjs/internal/frame.d.ts +463 -0
- package/dist/cjs/internal/frame.d.ts.map +1 -0
- package/dist/cjs/internal/frame.js +514 -0
- package/dist/cjs/internal/frame.js.map +7 -0
- package/dist/cjs/internal/nonNegativeSafeInt.d.ts +20 -0
- package/dist/cjs/internal/nonNegativeSafeInt.d.ts.map +1 -0
- package/dist/cjs/internal/nonNegativeSafeInt.js +29 -0
- package/dist/cjs/internal/nonNegativeSafeInt.js.map +7 -0
- package/dist/cjs/internal/paidUsage.d.ts +52 -0
- package/dist/cjs/internal/paidUsage.d.ts.map +1 -0
- package/dist/cjs/internal/paidUsage.js +70 -0
- package/dist/cjs/internal/paidUsage.js.map +7 -0
- package/dist/cjs/internal/printChannel.d.ts +233 -0
- package/dist/cjs/internal/printChannel.d.ts.map +1 -0
- package/dist/cjs/internal/printChannel.js +165 -0
- package/dist/cjs/internal/printChannel.js.map +7 -0
- package/dist/cjs/internal/printsObservation.d.ts +26 -0
- package/dist/cjs/internal/printsObservation.d.ts.map +1 -0
- package/dist/cjs/internal/printsObservation.js +27 -0
- package/dist/cjs/internal/printsObservation.js.map +7 -0
- package/dist/cjs/internal/refusal.d.ts +40 -0
- package/dist/cjs/internal/refusal.d.ts.map +1 -0
- package/dist/cjs/internal/refusal.js +45 -0
- package/dist/cjs/internal/refusal.js.map +7 -0
- package/dist/cjs/internal/supervision.d.ts +174 -0
- package/dist/cjs/internal/supervision.d.ts.map +1 -0
- package/dist/cjs/internal/supervision.js +402 -0
- package/dist/cjs/internal/supervision.js.map +7 -0
- package/dist/cjs/internal/unfinishedWork.d.ts +99 -0
- package/dist/cjs/internal/unfinishedWork.d.ts.map +1 -0
- package/dist/cjs/internal/unfinishedWork.js +80 -0
- package/dist/cjs/internal/unfinishedWork.js.map +7 -0
- package/dist/cjs/internal/unobservedCall.d.ts +112 -0
- package/dist/cjs/internal/unobservedCall.d.ts.map +1 -0
- package/dist/cjs/internal/unobservedCall.js +345 -0
- package/dist/cjs/internal/unobservedCall.js.map +7 -0
- package/dist/cjs/internal/untrustedData.d.ts +14 -0
- package/dist/cjs/internal/untrustedData.d.ts.map +1 -0
- package/dist/cjs/internal/untrustedData.js +30 -0
- package/dist/cjs/internal/untrustedData.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/AgentEvent.d.ts +3092 -0
- package/dist/esm/AgentEvent.d.ts.map +1 -0
- package/dist/esm/AgentEvent.js +1610 -0
- package/dist/esm/AgentEvent.js.map +1 -0
- package/dist/esm/CallLedger.d.ts +378 -0
- package/dist/esm/CallLedger.d.ts.map +1 -0
- package/dist/esm/CallLedger.js +506 -0
- package/dist/esm/CallLedger.js.map +1 -0
- package/dist/esm/Cell.d.ts +774 -0
- package/dist/esm/Cell.d.ts.map +1 -0
- package/dist/esm/Cell.js +772 -0
- package/dist/esm/Cell.js.map +1 -0
- package/dist/esm/CellCalls.d.ts +115 -0
- package/dist/esm/CellCalls.d.ts.map +1 -0
- package/dist/esm/CellCalls.js +97 -0
- package/dist/esm/CellCalls.js.map +1 -0
- package/dist/esm/CellHistory.d.ts +102 -0
- package/dist/esm/CellHistory.d.ts.map +1 -0
- package/dist/esm/CellHistory.js +91 -0
- package/dist/esm/CellHistory.js.map +1 -0
- package/dist/esm/CellTurn.d.ts +1071 -0
- package/dist/esm/CellTurn.d.ts.map +1 -0
- package/dist/esm/CellTurn.js +3410 -0
- package/dist/esm/CellTurn.js.map +1 -0
- package/dist/esm/CellValidation.d.ts +94 -0
- package/dist/esm/CellValidation.d.ts.map +1 -0
- package/dist/esm/CellValidation.js +335 -0
- package/dist/esm/CellValidation.js.map +1 -0
- package/dist/esm/Compaction.d.ts +158 -0
- package/dist/esm/Compaction.d.ts.map +1 -0
- package/dist/esm/Compaction.js +216 -0
- package/dist/esm/Compaction.js.map +1 -0
- package/dist/esm/CompletionClaim.d.ts +801 -0
- package/dist/esm/CompletionClaim.d.ts.map +1 -0
- package/dist/esm/CompletionClaim.js +833 -0
- package/dist/esm/CompletionClaim.js.map +1 -0
- package/dist/esm/ContextWindow.d.ts +435 -0
- package/dist/esm/ContextWindow.d.ts.map +1 -0
- package/dist/esm/ContextWindow.js +427 -0
- package/dist/esm/ContextWindow.js.map +1 -0
- package/dist/esm/EngineLike.d.ts +546 -0
- package/dist/esm/EngineLike.d.ts.map +1 -0
- package/dist/esm/EngineLike.js +218 -0
- package/dist/esm/EngineLike.js.map +1 -0
- package/dist/esm/ExternalTranscript.d.ts +349 -0
- package/dist/esm/ExternalTranscript.d.ts.map +1 -0
- package/dist/esm/ExternalTranscript.js +987 -0
- package/dist/esm/ExternalTranscript.js.map +1 -0
- package/dist/esm/FailedCall.d.ts +132 -0
- package/dist/esm/FailedCall.d.ts.map +1 -0
- package/dist/esm/FailedCall.js +131 -0
- package/dist/esm/FailedCall.js.map +1 -0
- package/dist/esm/FlowBinding.d.ts +289 -0
- package/dist/esm/FlowBinding.d.ts.map +1 -0
- package/dist/esm/FlowBinding.js +376 -0
- package/dist/esm/FlowBinding.js.map +1 -0
- package/dist/esm/HarnessError.d.ts +57 -0
- package/dist/esm/HarnessError.d.ts.map +1 -0
- package/dist/esm/HarnessError.js +85 -0
- package/dist/esm/HarnessError.js.map +1 -0
- package/dist/esm/Judgement.d.ts +289 -0
- package/dist/esm/Judgement.d.ts.map +1 -0
- package/dist/esm/Judgement.js +305 -0
- package/dist/esm/Judgement.js.map +1 -0
- package/dist/esm/Monitor.d.ts +374 -0
- package/dist/esm/Monitor.d.ts.map +1 -0
- package/dist/esm/Monitor.js +370 -0
- package/dist/esm/Monitor.js.map +1 -0
- package/dist/esm/NarrowedCheck.d.ts +523 -0
- package/dist/esm/NarrowedCheck.d.ts.map +1 -0
- package/dist/esm/NarrowedCheck.js +612 -0
- package/dist/esm/NarrowedCheck.js.map +1 -0
- package/dist/esm/Notifications.d.ts +42 -0
- package/dist/esm/Notifications.d.ts.map +1 -0
- package/dist/esm/Notifications.js +215 -0
- package/dist/esm/Notifications.js.map +1 -0
- package/dist/esm/Plan.d.ts +127 -0
- package/dist/esm/Plan.d.ts.map +1 -0
- package/dist/esm/Plan.js +97 -0
- package/dist/esm/Plan.js.map +1 -0
- package/dist/esm/QuickJSSandbox.d.ts +151 -0
- package/dist/esm/QuickJSSandbox.d.ts.map +1 -0
- package/dist/esm/QuickJSSandbox.js +1364 -0
- package/dist/esm/QuickJSSandbox.js.map +1 -0
- package/dist/esm/Relevance.d.ts +213 -0
- package/dist/esm/Relevance.d.ts.map +1 -0
- package/dist/esm/Relevance.js +254 -0
- package/dist/esm/Relevance.js.map +1 -0
- package/dist/esm/Sandbox.d.ts +637 -0
- package/dist/esm/Sandbox.d.ts.map +1 -0
- package/dist/esm/Sandbox.js +464 -0
- package/dist/esm/Sandbox.js.map +1 -0
- package/dist/esm/Steering.d.ts +464 -0
- package/dist/esm/Steering.d.ts.map +1 -0
- package/dist/esm/Steering.js +193 -0
- package/dist/esm/Steering.js.map +1 -0
- package/dist/esm/StructuredOutput.d.ts +252 -0
- package/dist/esm/StructuredOutput.d.ts.map +1 -0
- package/dist/esm/StructuredOutput.js +430 -0
- package/dist/esm/StructuredOutput.js.map +1 -0
- package/dist/esm/Sufficiency.d.ts +195 -0
- package/dist/esm/Sufficiency.d.ts.map +1 -0
- package/dist/esm/Sufficiency.js +207 -0
- package/dist/esm/Sufficiency.js.map +1 -0
- package/dist/esm/Supervisor.d.ts +719 -0
- package/dist/esm/Supervisor.d.ts.map +1 -0
- package/dist/esm/Supervisor.js +575 -0
- package/dist/esm/Supervisor.js.map +1 -0
- package/dist/esm/Tokens.d.ts +86 -0
- package/dist/esm/Tokens.d.ts.map +1 -0
- package/dist/esm/Tokens.js +92 -0
- package/dist/esm/Tokens.js.map +1 -0
- package/dist/esm/Transcript.d.ts +171 -0
- package/dist/esm/Transcript.d.ts.map +1 -0
- package/dist/esm/Transcript.js +424 -0
- package/dist/esm/Transcript.js.map +1 -0
- package/dist/esm/TruncatedOutput.d.ts +186 -0
- package/dist/esm/TruncatedOutput.d.ts.map +1 -0
- package/dist/esm/TruncatedOutput.js +257 -0
- package/dist/esm/TruncatedOutput.js.map +1 -0
- package/dist/esm/UnmovedTree.d.ts +113 -0
- package/dist/esm/UnmovedTree.d.ts.map +1 -0
- package/dist/esm/UnmovedTree.js +90 -0
- package/dist/esm/UnmovedTree.js.map +1 -0
- package/dist/esm/UnresolvedFailure.d.ts +196 -0
- package/dist/esm/UnresolvedFailure.d.ts.map +1 -0
- package/dist/esm/UnresolvedFailure.js +218 -0
- package/dist/esm/UnresolvedFailure.js.map +1 -0
- package/dist/esm/VacuousVerification.d.ts +237 -0
- package/dist/esm/VacuousVerification.d.ts.map +1 -0
- package/dist/esm/VacuousVerification.js +245 -0
- package/dist/esm/VacuousVerification.js.map +1 -0
- package/dist/esm/VariablesPanel.d.ts +117 -0
- package/dist/esm/VariablesPanel.d.ts.map +1 -0
- package/dist/esm/VariablesPanel.js +142 -0
- package/dist/esm/VariablesPanel.js.map +1 -0
- package/dist/esm/index.d.ts +172 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +172 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/bytes.d.ts +36 -0
- package/dist/esm/internal/bytes.d.ts.map +1 -0
- package/dist/esm/internal/bytes.js +70 -0
- package/dist/esm/internal/bytes.js.map +1 -0
- package/dist/esm/internal/cellPrompt.d.ts +122 -0
- package/dist/esm/internal/cellPrompt.d.ts.map +1 -0
- package/dist/esm/internal/cellPrompt.js +276 -0
- package/dist/esm/internal/cellPrompt.js.map +1 -0
- package/dist/esm/internal/compactable.d.ts +44 -0
- package/dist/esm/internal/compactable.d.ts.map +1 -0
- package/dist/esm/internal/compactable.js +71 -0
- package/dist/esm/internal/compactable.js.map +1 -0
- package/dist/esm/internal/compactionMarks.d.ts +278 -0
- package/dist/esm/internal/compactionMarks.d.ts.map +1 -0
- package/dist/esm/internal/compactionMarks.js +317 -0
- package/dist/esm/internal/compactionMarks.js.map +1 -0
- package/dist/esm/internal/demandText.d.ts +95 -0
- package/dist/esm/internal/demandText.d.ts.map +1 -0
- package/dist/esm/internal/demandText.js +128 -0
- package/dist/esm/internal/demandText.js.map +1 -0
- package/dist/esm/internal/elide.d.ts +109 -0
- package/dist/esm/internal/elide.d.ts.map +1 -0
- package/dist/esm/internal/elide.js +123 -0
- package/dist/esm/internal/elide.js.map +1 -0
- package/dist/esm/internal/frame.d.ts +463 -0
- package/dist/esm/internal/frame.d.ts.map +1 -0
- package/dist/esm/internal/frame.js +861 -0
- package/dist/esm/internal/frame.js.map +1 -0
- package/dist/esm/internal/nonNegativeSafeInt.d.ts +20 -0
- package/dist/esm/internal/nonNegativeSafeInt.d.ts.map +1 -0
- package/dist/esm/internal/nonNegativeSafeInt.js +20 -0
- package/dist/esm/internal/nonNegativeSafeInt.js.map +1 -0
- package/dist/esm/internal/paidUsage.d.ts +52 -0
- package/dist/esm/internal/paidUsage.d.ts.map +1 -0
- package/dist/esm/internal/paidUsage.js +72 -0
- package/dist/esm/internal/paidUsage.js.map +1 -0
- package/dist/esm/internal/printChannel.d.ts +233 -0
- package/dist/esm/internal/printChannel.d.ts.map +1 -0
- package/dist/esm/internal/printChannel.js +376 -0
- package/dist/esm/internal/printChannel.js.map +1 -0
- package/dist/esm/internal/printsObservation.d.ts +26 -0
- package/dist/esm/internal/printsObservation.d.ts.map +1 -0
- package/dist/esm/internal/printsObservation.js +29 -0
- package/dist/esm/internal/printsObservation.js.map +1 -0
- package/dist/esm/internal/refusal.d.ts +40 -0
- package/dist/esm/internal/refusal.d.ts.map +1 -0
- package/dist/esm/internal/refusal.js +47 -0
- package/dist/esm/internal/refusal.js.map +1 -0
- package/dist/esm/internal/supervision.d.ts +174 -0
- package/dist/esm/internal/supervision.d.ts.map +1 -0
- package/dist/esm/internal/supervision.js +485 -0
- package/dist/esm/internal/supervision.js.map +1 -0
- package/dist/esm/internal/unfinishedWork.d.ts +99 -0
- package/dist/esm/internal/unfinishedWork.d.ts.map +1 -0
- package/dist/esm/internal/unfinishedWork.js +85 -0
- package/dist/esm/internal/unfinishedWork.js.map +1 -0
- package/dist/esm/internal/unobservedCall.d.ts +112 -0
- package/dist/esm/internal/unobservedCall.d.ts.map +1 -0
- package/dist/esm/internal/unobservedCall.js +501 -0
- package/dist/esm/internal/unobservedCall.js.map +1 -0
- package/dist/esm/internal/untrustedData.d.ts +14 -0
- package/dist/esm/internal/untrustedData.d.ts.map +1 -0
- package/dist/esm/internal/untrustedData.js +15 -0
- package/dist/esm/internal/untrustedData.js.map +1 -0
- package/docs/README.md +129 -0
- package/docs/api.md +1869 -0
- package/docs/concepts.md +238 -0
- package/docs/external-transcripts.md +245 -0
- package/docs/guides/bind-flows.md +167 -0
- package/docs/guides/drive-the-loop.md +265 -0
- package/docs/guides/run-cells.md +262 -0
- package/docs/guides/workerd.md +144 -0
- package/docs/installation.md +69 -0
- package/docs/quickstart.md +121 -0
- package/docs/reference.md +954 -0
- package/docs/troubleshooting.md +177 -0
- package/package.json +463 -3
- package/src/AgentEvent.ts +1772 -0
- package/src/CallLedger.ts +560 -0
- package/src/Cell.ts +926 -0
- package/src/CellCalls.ts +198 -0
- package/src/CellHistory.ts +128 -0
- package/src/CellTurn.ts +4450 -0
- package/src/CellValidation.ts +382 -0
- package/src/Compaction.ts +330 -0
- package/src/CompletionClaim.ts +1013 -0
- package/src/ContextWindow.ts +669 -0
- package/src/EngineLike.ts +614 -0
- package/src/ExternalTranscript.ts +1143 -0
- package/src/FailedCall.ts +163 -0
- package/src/FlowBinding.ts +603 -0
- package/src/HarnessError.ts +98 -0
- package/src/Judgement.ts +526 -0
- package/src/Monitor.ts +579 -0
- package/src/NarrowedCheck.ts +694 -0
- package/src/Notifications.ts +262 -0
- package/src/Plan.ts +113 -0
- package/src/QuickJSSandbox.ts +1557 -0
- package/src/Relevance.ts +352 -0
- package/src/Sandbox.ts +908 -0
- package/src/Steering.ts +405 -0
- package/src/StructuredOutput.ts +484 -0
- package/src/Sufficiency.ts +247 -0
- package/src/Supervisor.ts +798 -0
- package/src/Tokens.ts +108 -0
- package/src/Transcript.ts +513 -0
- package/src/TruncatedOutput.ts +297 -0
- package/src/UnmovedTree.ts +121 -0
- package/src/UnresolvedFailure.ts +240 -0
- package/src/VacuousVerification.ts +280 -0
- package/src/VariablesPanel.ts +165 -0
- package/src/index.ts +204 -0
- package/src/internal/bytes.ts +70 -0
- package/src/internal/cellPrompt.ts +337 -0
- package/src/internal/compactable.ts +76 -0
- package/src/internal/compactionMarks.ts +454 -0
- package/src/internal/demandText.ts +145 -0
- package/src/internal/elide.ts +130 -0
- package/src/internal/frame.ts +1178 -0
- package/src/internal/nonNegativeSafeInt.ts +24 -0
- package/src/internal/paidUsage.ts +90 -0
- package/src/internal/printChannel.ts +434 -0
- package/src/internal/printsObservation.ts +31 -0
- package/src/internal/refusal.ts +53 -0
- package/src/internal/supervision.ts +652 -0
- package/src/internal/unfinishedWork.ts +123 -0
- package/src/internal/unobservedCall.ts +536 -0
- package/src/internal/untrustedData.ts +19 -0
package/docs/api.md
ADDED
|
@@ -0,0 +1,1869 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "API reference"
|
|
3
|
+
description: "The complete @smthrs/harness API: the cell-first controller, the sandbox and engine ports, the cell contract, flow binding, and every error union, with behavior and signatures from source."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`@smthrs/harness` is the built-in agent loop, expressed as pure translation
|
|
7
|
+
plus a small set of service ports. It holds no scheduler, no database, no
|
|
8
|
+
transport and no provider client: `EngineLike` is the port a durable engine
|
|
9
|
+
answers, `Sandbox` is the port a script realm answers, `Steering.Source` is the
|
|
10
|
+
port a notification queue answers, and [`@smthrs/agent`](/api/agent) is the
|
|
11
|
+
assembled production composition over the durable engine.
|
|
12
|
+
|
|
13
|
+
The loop is cell-first. One frame is
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
model -> generated cell -> realm evaluation -> individually durable flow calls -> next transition
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The model emits fenced `cell` blocks; they run as one program inside a realm
|
|
20
|
+
that outlives the frame; the only authority the program holds is
|
|
21
|
+
`ctx.call(flowName, input)`; and the program states its intent by calling
|
|
22
|
+
`ctx.done(output)`, `ctx.park(reason, message)`, or neither, which continues.
|
|
23
|
+
`Sandbox.replTransition` turns that into the transition the journal records.
|
|
24
|
+
|
|
25
|
+
## Example
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import * as Cell from "@smthrs/harness/Cell"
|
|
29
|
+
import * as QuickJSSandbox from "@smthrs/harness/QuickJSSandbox"
|
|
30
|
+
import { Effect } from "effect"
|
|
31
|
+
|
|
32
|
+
const frame = await Effect.runPromise(Effect.scoped(Effect.gen(function*() {
|
|
33
|
+
const realm = yield* (yield* QuickJSSandbox.make).openRealm!({ flows: {} })
|
|
34
|
+
const call = () => Effect.succeed(new Cell.CallResult({ outcome: "success", value: null }))
|
|
35
|
+
return yield* realm.evaluate({ cell: Cell.source(`ctx.done("hi")`), frame: 0, call })
|
|
36
|
+
})))
|
|
37
|
+
console.log(
|
|
38
|
+
frame.outcome._tag,
|
|
39
|
+
frame.outcome._tag === "settled" && frame.outcome.transition._tag === "complete"
|
|
40
|
+
? frame.outcome.transition.output
|
|
41
|
+
: ""
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
settled hi
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Entry points
|
|
50
|
+
|
|
51
|
+
| Import | What it is |
|
|
52
|
+
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `@smthrs/harness` | The root barrel: the 33 namespaces listed in the module index. |
|
|
54
|
+
| `@smthrs/harness/<Module>` | Any top-level module directly, for example `@smthrs/harness/CellTurn`. |
|
|
55
|
+
| `@smthrs/harness/QuickJSSandbox` | The QuickJS-WASM `Sandbox` binding. Not re-exported from the root, because it carries an embedded WebAssembly build. |
|
|
56
|
+
| `@smthrs/harness/package.json` | The package manifest. |
|
|
57
|
+
|
|
58
|
+
The `./internal/*` and `./*/index` subpaths map to `null` and do not resolve.
|
|
59
|
+
|
|
60
|
+
## Module index
|
|
61
|
+
|
|
62
|
+
34 public modules, 536 documented exports. Each module's full export table is
|
|
63
|
+
in the [module and export inventory](./reference.md); the sections below state
|
|
64
|
+
behavior and signatures.
|
|
65
|
+
|
|
66
|
+
| Module | Public exports | What it is |
|
|
67
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `HarnessError` | `HarnessErrorCode`, `HarnessError` | Stable failures reported at the harness translation boundary. |
|
|
69
|
+
| `AgentEvent` | `Observer`, `Journal`, `RelevanceKind`, `CompactionMark`, `CompactionPin`, `MonitorKind`, `Stance`, `Suppression`, `UnjudgedReason`, `DisciplineArmed`, `TurnOpened`, `ModelRequested`, `ModelSelected`, `SeatFailedOver`, `ModelParked`, `ModelUnparked`, `ModelDelta`, `ModelRetried`, `ModelSettled`, `CellProduced`, `CellRejectedInFrame`, `CellCallStarted`, `CellCallSettled`, `CellPrinted`, `CellSettled`, `TransitionApplied`, `ReadOnlyDemandIssued`, `ReadOnlyDemanded`, `RepeatDemanded`, `NarrowedDemanded`, `UnmovedDemanded`, `UnresolvedDemanded`, `FailedCallDemanded`, `UnobservedDemanded`, `NarrowOnlyDemanded`, `ClaimDemanded`, `DecisionAnswer`, `decisionAnswers`, `DecisionSettled`, `SupervisorSettled`, `SupervisorUnjudged`, `DecisionUnjudged`, `RelevanceSettled`, `RelevanceRestored`, `SeatPanel`, `SeatRouted`, `SufficiencyObserved`, `VacuousVerificationObserved`, `MutationObserved`, `CheckpointMinted`, `Suspended`, `CompactionSettled`, `SteeringDrained`, `TurnClosed`, `PermissionRequired`, `Aborted`, `Resolved`, `AgentEvent`, `eventType` | Serializable events emitted by harness adapters. |
|
|
70
|
+
| `Plan` | `Child`, `Batch`, `ChildResult`, `ChildProgress`, `ChildSettled`, `SpliceEvent` | Local structural plan nodes used at the harness-to-engine boundary. |
|
|
71
|
+
| `EngineLike` | `SuspendReasonCode`, `SuspendReason`, `SealedModelStep`, `BoundaryIdentity`, `DurableSchema`, `RecordBoundary`, `Observation`, `Snapshot`, `Binding`, `Resolved`, `CaptureRequest`, `EngineLike`, `make`, `layer`, `resolve`, `makeNoop`, `layerNoop` | Narrow engine port consumed by the built-in harness. |
|
|
72
|
+
| `Tokens` | `Count`, `Segment`, `Accounting`, `Estimator`, `estimate`, `count`, `combine` | Deterministic token accounting for context windows. |
|
|
73
|
+
| `ContextWindow` | `TypeId`, `SegmentKind`, `SegmentZone`, `Content`, `Mark`, `ContextWindowErrorCode`, `ContextWindowError`, `Segment`, `ContextWindow`, `SegmentInput`, `MakeOptions`, `makeSegment`, `make`, `empty`, `appendTurn`, `prefixDigest`, `compactMarked`, `compactPrefix`, `compact`, `render` | The immutable, provider-neutral context assembled for one model request. |
|
|
74
|
+
| `Transcript` | `journalVersion`, `validateJournal`, `TranscriptErrorCode`, `TranscriptError`, `ProjectedMessage`, `ProjectedState`, `CellEvidence`, `projectStateResult`, `projectResult` | Transcript projection from durable journal entries. |
|
|
75
|
+
| `ExternalTranscript` | `AgentKind`, `EditedFile`, `Part`, `Entry`, `ExternalTranscriptErrorCode`, `ExternalTranscriptError`, `codexReleases`, `CodexState`, `CodexCall`, `codexStart`, `Decoded`, `decodeCodex`, `claudeReleases`, `ClaudeCall`, `ClaudeState`, `claudeStart`, `decodeClaude` | Read-only conversation entries decoded from another agent's transcript file. |
|
|
76
|
+
| `Compaction` | `summaryInstruction`, `InvalidStep`, `Summarizer`, `CompactionStep`, `TokenAccounting`, `shouldCompact`, `selectPrefix`, `declare`, `summaryRequest`, `apply` | Declarations for sealed transcript-summary steps. |
|
|
77
|
+
| `Steering` | `Delivery`, `SteerInsert`, `QueueInsert`, `Insert`, `SeatChange`, `ThinkingChange`, `Item`, `Queue`, `Drain`, `BoundaryInput`, `DrainRecord`, `drainRecord`, `empty`, `enqueue`, `drainAtClose`, `Source`, `SourceInput`, `make`, `makeNoop`, `layer`, `layerNoop` | Turn-boundary steering values and their source contract. |
|
|
78
|
+
| `Notifications` | `Options`, `make`, `layer` | Adapter from the durable notification queue to harness turn boundaries. |
|
|
79
|
+
| `Cell` | `Language`, `Source`, `digestOf`, `source`, `Continue`, `Complete`, `Park`, `Transition`, `renderText`, `RejectionCode`, `Settled`, `Raised`, `Rejected`, `Outcome`, `FlowProjection`, `project`, `CallFailureCode`, `defaultCallFailureCode`, `callFailureHint`, `CallIdentity`, `declarationDigest`, `Call`, `baseCheckpoint`, `checkpoint`, `checkpointOf`, `CallResult`, `CallSuccess`, `CallFailure`, `CallResultVariant`, `decodeCallResult`, `decodeOutcome`, `decodeTransition`, `callFailure`, `Extracted`, `extract` | The cell contract. |
|
|
80
|
+
| `Sandbox` | `SandboxErrorCode`, `SandboxError`, `Invocation`, `Mint`, `Minter`, `mintUnavailable`, `Handler`, `Limits`, `Capabilities`, `defaultLimits`, `minimumSteps`, `minimumTimeMs`, `minimumMemoryBytes`, `printFrameBytes`, `printStatementFloor`, `printRetainedBytes`, `withDefaults`, `Intent`, `replTransition`, `RealmEvaluation`, `RealmFrame`, `Realm`, `RealmOptions`, `Sandbox`, `make`, `layer`, `makeNoop`, `layerNoop`, `realmUnsupported`, `callTimedOut`, `compile`, `PendingCall`, `driveCell`, `raisedOutcome` | The deterministic script sandbox port. |
|
|
81
|
+
| `CellTurn` | `defaultMaxFrames`, `defaultReadOnlyFrames`, `defaultModelCallMs`, `defaultRepeatFrames`, `defaultNarrowingDemands`, `defaultUnmovedDemands`, `defaultUnresolvedDemands`, `defaultClaimDemands`, `defaultRevalidations`, `defaultMaxCheckpoints`, `State`, `Input`, `MemoryRow`, `Memory`, `make`, `teach`, `instructionsSegment`, `memorySegment`, `run` | The cell-first controller. |
|
|
82
|
+
| `CellHistory` | `ExecutedCell`, `Service`, `CellHistory`, `make`, `makeCells`, `makeNoop`, `layer`, `layerCells`, `layerNoop` | The source of every cell the current turn executed. |
|
|
83
|
+
| `CellCalls` | `Implementation`, `Prompt`, `PromptRunner`, `Options`, `Resolver`, `make` | Registry-backed resolution for the flow calls a cell makes. |
|
|
84
|
+
| `FlowBinding` | `Declared`, `DescriptorOptions`, `descriptorOf`, `Binding`, `Options`, `make`, `provide`, `Source`, `source`, `Catalog`, `empty`, `catalogResult`, `catalog`, `registry` | The executable-flow binding contract. |
|
|
85
|
+
| `StructuredOutput` | `StructuredOutputFailureCode`, `OutputIssueCode`, `OutputIssue`, `StructuredOutputFailure`, `maxIssues`, `jsonSchema`, `digest`, `instructions`, `issuesDigest`, `correction`, `lastBalanced`, `candidates`, `decode` | Turning one agent's final text into a value the declared output schema accepts, or into a typed failure. |
|
|
86
|
+
| `TruncatedOutput` | `flagSuffix`, `droppedSuffix`, `flagKey`, `minimumBytes`, `retained`, `Capture`, `Reuse`, `captures`, `reuse`, `refusal`, `retain`, `Ledger` | The truncation ledger: which bytes this run was handed as a fragment. |
|
|
87
|
+
| `CallLedger` | `bound`, `width`, `members`, `Entry`, `Ledger`, `subject`, `target`, `digest`, `payload`, `Settlement`, `entry`, `settled`, `remember`, `render` | The call ledger: what this run has already asked, rendered every frame. |
|
|
88
|
+
| `NarrowedCheck` | `retained`, `maxTerms`, `labelWidth`, `label`, `targeting`, `names`, `lex`, `terms`, `conditions`, `Check`, `Narrowing`, `check`, `narrows`, `find`, `demand`, `Only`, `findOnly`, `demandOnly`, `remember`, `Ledger` | The narrowing ledger: which checks this run has run, and over which tree. |
|
|
89
|
+
| `CellValidation` | `Validation`, `normalize`, `validate` | Cell validation at the boundary. |
|
|
90
|
+
| `CompletionClaim` | `outputBytes`, `proseBytes`, `disprovenAt`, `overclaimedAt`, `unsupportedAt`, `inventedAt`, `leafBytes`, `resultBytes`, `receipt`, `Reported`, `Settled`, `record`, `checksRunLimit`, `Check`, `Ran`, `Evidence`, `classifier`, `sentenceLimit`, `sentences`, `sentenceMarker`, `sentenceOf`, `sentenceClassifier`, `Probabilities`, `Reading`, `find`, `unrecorded`, `newest`, `unjudged`, `unproven`, `read`, `demand`, `quote`, `prose` | The completion nothing in the record contradicts. |
|
|
91
|
+
| `Supervisor` | `frameBytes`, `recentFrames`, `recalledLimit`, `closeGraceMs`, `thrashingAt`, `offTargetAt`, `suspectAt`, `contextAt`, `Level`, `levels`, `Help`, `emotions`, `Emotion`, `Frame`, `Signals`, `Recalled`, `skillLimit`, `skillBytes`, `calledLimit`, `Skill`, `skill`, `Snapshot`, `monitorPrefix`, `MonitorQuestions`, `classifierFor`, `classifier`, `Reading`, `read`, `Memory`, `memoryNone`, `nudge`, `recalledInsert`, `head`, `tail`, `prose` | The reading Jev takes of a run while it is still running. |
|
|
92
|
+
| `Monitor` | `Kind`, `Common`, `Questioned`, `Derived`, `Monitor`, `Budget`, `Input`, `budgets`, `idPattern`, `questionId`, `make`, `InvalidMonitor`, `validate`, `lint`, `paranoidText`, `carefulText`, `stepBackText`, `clarifyText`, `moods`, `defaults`, `skillText`, `skills`, `useJevText`, `useJev`, `questions`, `Row`, `Candidate`, `Evaluation`, `evaluate`, `Entry`, `Ledger`, `fresh`, `Gated`, `gate` | What one supervisor reading can put in front of the run, and when. |
|
|
93
|
+
| `Judgement` | `Unjudged`, `unconfigured`, `Asked`, `Read`, `measured`, `read`, `maxStateBytes`, `ItemQuestions`, `ItemAnswers`, `PerItemOptions`, `ItemState`, `ItemsRead`, `PerItem`, `perItem`, `decision`, `unjudgedEvent`, `Recorded`, `recorded`, `emitRecorded`, `taskBytes`, `task` | The one way this package asks Jev a question. |
|
|
94
|
+
| `Relevance` | `Kind`, `Item`, `Context`, `withholdAt`, `itemBytes`, `reader`, `Verdict`, `Reading`, `judge`, `settled`, `flowItem`, `Document`, `Chunk`, `chunks`, `render` | What a run is shown of the human-provided items it could be shown. |
|
|
95
|
+
| `UnmovedTree` | `Unmoved`, `find`, `demand` | The completion with nothing behind it. |
|
|
96
|
+
| `UnresolvedFailure` | `exitStatusKey`, `failed`, `exitStatus`, `passed`, `Displaced`, `revisits`, `find`, `demand` | The failing check a completion stepped around. |
|
|
97
|
+
| `FailedCall` | `cap`, `heading`, `stated`, `Failure`, `reason`, `inspects`, `find`, `demand`, `state` | The completion its own cell wrote before a call in it failed. |
|
|
98
|
+
| `Sufficiency` | `retained`, `Failure`, `Ledger`, `remember`, `Sufficient`, `find`, `observation` | The evidence that is already complete. |
|
|
99
|
+
| `VacuousVerification` | `retained`, `Pass`, `Ledger`, `remember`, `stored`, `find`, `observation` | The proof that was already true before anything changed. Not wired into `CellTurn`. |
|
|
100
|
+
| `VariablesPanel` | `bound`, `Binding`, `Stamp`, `Ledger`, `stamp`, `render` | The variables panel: what the realm holds, stated every frame. |
|
|
101
|
+
| `QuickJSSandbox` _(subpath)_ | `cacheSuccessful`, `VariantService`, `Variant`, `layerVariantLive`, `layerVariant`, `ComputeClockService`, `ComputeClock`, `layerClockLive`, `loadModule`, `makeWithVariant`, `makeWithClock`, `make`, `layerWithVariant`, `layer` | The QuickJS-WASM sandbox binding. |
|
|
102
|
+
|
|
103
|
+
## Durability
|
|
104
|
+
|
|
105
|
+
Every `ctx.call` is its own keyed, journaled, permission-gated boundary at the
|
|
106
|
+
tier the flow declares, so a crash or a permission park mid-cell is recoverable:
|
|
107
|
+
the cell source re-executes from the top, boundaries that already settled replay
|
|
108
|
+
their recorded values, and execution reaches the parked call deterministically.
|
|
109
|
+
|
|
110
|
+
`EngineLike.record` is the same mechanism for the controller's own reads of the
|
|
111
|
+
world. The controller's state is rebuilt by re-execution, so a read that
|
|
112
|
+
bypasses a record is a replay divergence and, downstream of one, a duplicate
|
|
113
|
+
irreversible effect. `(name, identity)` together form the record key: two
|
|
114
|
+
records in one frame may share an identity as long as their names differ.
|
|
115
|
+
|
|
116
|
+
One call is deliberately not covered by that guarantee. `EngineLike.call` is
|
|
117
|
+
where a cell reaches a durable wait, and a `Flow.suspend` raised inside an
|
|
118
|
+
enclosing activity suspends that activity's attempt rather than the run, so the
|
|
119
|
+
call is issued first and its settlement is recorded after. A call the `callMs`
|
|
120
|
+
ceiling interrupted therefore settles nowhere, and a re-executed frame issues it
|
|
121
|
+
again on the host. The cell's branch is stable either way, because the recorded
|
|
122
|
+
settlement is what the replayed frame is handed; what the run pays for twice is
|
|
123
|
+
the interrupted call itself.
|
|
124
|
+
|
|
125
|
+
## Limits
|
|
126
|
+
|
|
127
|
+
`Sandbox.defaultLimits` fills every ceiling a caller omits. A caller may raise
|
|
128
|
+
any single ceiling; the others keep their defaults, so a partial override cannot
|
|
129
|
+
disable them.
|
|
130
|
+
|
|
131
|
+
| Limit | Default | Scope | What it bounds |
|
|
132
|
+
| ----------------- | ------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `calls` | 64 | per frame | Flow calls one cell may make. A `ctx.checkpoint()` mint settles on the same channel and counts. |
|
|
134
|
+
| `memoryBytes` | 128 MiB | per **run** | What the realm's own names hold, weighed by the panel probe at each frame's close. |
|
|
135
|
+
| `steps` | 1,000 | per frame | Interrupt checks, not bytecode operations. At least `Sandbox.minimumSteps`. |
|
|
136
|
+
| `timeMs` | 30,000 | per frame | The cell's own JavaScript time. Time suspended in a `ctx.call` or `ctx.checkpoint()` does not count. At least `Sandbox.minimumTimeMs`. |
|
|
137
|
+
| `totalMs` | 900,000 | per frame | Whole-evaluation time, host calls included. The backstop for a call that never settles. |
|
|
138
|
+
| `pauseTotalMsFor` | none | per frame | Flow names whose pending calls pause `totalMs`; the host must bound or settle these calls separately. |
|
|
139
|
+
| `callMs` | 120,000 | per call | Wall-clock time one flow call may take before it settles as a resolved timeout. |
|
|
140
|
+
|
|
141
|
+
`memoryBytes` is a run budget rather than a frame budget because a realm outlives
|
|
142
|
+
its frames. `runtime.setMemoryLimit` covers the object graph but does not count
|
|
143
|
+
string data on the shipped QuickJS variant, so the panel probe weighs the realm's
|
|
144
|
+
own names and a frame that opens over the ceiling is refused before it runs. The
|
|
145
|
+
reading is cleared with the refusal, because freeing is itself done by a cell.
|
|
146
|
+
|
|
147
|
+
## Bytes
|
|
148
|
+
|
|
149
|
+
Text delivery and retention bounds use UTF-8 bytes measured by one shared
|
|
150
|
+
helper. The print channel, retention ceiling and call ledger's line sizes use
|
|
151
|
+
this unit, including CJK and emoji payloads. Elision notices state the real
|
|
152
|
+
UTF-8 byte count.
|
|
153
|
+
|
|
154
|
+
The supplemental memory probe is a separate estimate, not a UTF-8 byte count or
|
|
155
|
+
an exact heap measurement. Strings and property names contribute their UTF-16
|
|
156
|
+
code-unit lengths. Objects have a base weight of 8; non-string scalars,
|
|
157
|
+
functions, accessors and cycle references have fixed weights of 8. The probe walks named globals, array indices,
|
|
158
|
+
enumerable own string-keyed data properties, Map keys and values, and Set values.
|
|
159
|
+
Collection entries use the same traversal budget as properties: 200,000 values
|
|
160
|
+
and depth 32. An incomplete traversal causes the next frame to be refused.
|
|
161
|
+
Cycles stop at ancestor references; shared objects on separate paths are counted
|
|
162
|
+
again. Accessors are not invoked. Closure state, weak collection entries and
|
|
163
|
+
other storage unreachable by these paths are not measured. The native allocator
|
|
164
|
+
limit remains active, but this supplemental estimate is not a hard bound on all
|
|
165
|
+
memory retained by the realm.
|
|
166
|
+
|
|
167
|
+
## Failure categories
|
|
168
|
+
|
|
169
|
+
Three closed unions carry every failure a caller branches on. None of them is
|
|
170
|
+
open-ended, and none of them is prose a consumer must parse.
|
|
171
|
+
|
|
172
|
+
- `HarnessError.HarnessErrorCode`: the translation boundary's own failures.
|
|
173
|
+
Folded into `@smthrs/agent`'s `AgentFailure` union, so a code that nothing
|
|
174
|
+
raises is a promise this package cannot keep; the set holds only codes that
|
|
175
|
+
are raised.
|
|
176
|
+
- `Cell.CallFailureCode`: why one flow call did not succeed, as the cell reads
|
|
177
|
+
it. A failed call **resolves** with `{ ok: false, error: { code, message, hint } }`
|
|
178
|
+
rather than throwing, so the recovery branch the model already wrote still
|
|
179
|
+
runs. `Cell.callFailureHint` names the one move that recovers each class.
|
|
180
|
+
- `Sandbox.SandboxErrorCode`: why a realm could not run a cell at all, as
|
|
181
|
+
distinct from a cell that ran and failed.
|
|
182
|
+
|
|
183
|
+
`StructuredOutput.StructuredOutputFailureCode` is the fourth, for the boundary
|
|
184
|
+
that decodes an agent's final text into a declared schema.
|
|
185
|
+
`ExternalTranscript.ExternalTranscriptErrorCode` is the fifth, for decoding a
|
|
186
|
+
transcript another agent wrote.
|
|
187
|
+
|
|
188
|
+
## Copy and mutation semantics
|
|
189
|
+
|
|
190
|
+
`ContextWindow` is immutable: the arrays a segment and a window expose are
|
|
191
|
+
frozen, so a runtime mutation throws rather than silently invalidating the
|
|
192
|
+
digest computed once at construction. `Cell` values, `CallLedger` entries and
|
|
193
|
+
every `AgentEvent` are schema classes and are never mutated in place. The one
|
|
194
|
+
value the controller mutates is its own `State`, which is journaled at each
|
|
195
|
+
frame boundary.
|
|
196
|
+
|
|
197
|
+
## What this package does not do
|
|
198
|
+
|
|
199
|
+
- It declares no provider tools. The cell-first loop seals every model request
|
|
200
|
+
with `tools: []` and `toolChoice: "none"`. A window still carries the active
|
|
201
|
+
tool set it was constructed with, and `AgentEvent.TurnOpened.activeToolNames`
|
|
202
|
+
still journals it, so a foreign-adapter loop has somewhere to put one; nothing
|
|
203
|
+
in this package produces a non-empty set.
|
|
204
|
+
- It runs no scheduler and owns no storage. `Plan` describes child batches;
|
|
205
|
+
`EngineLike` splices them.
|
|
206
|
+
|
|
207
|
+
## CellTurn
|
|
208
|
+
|
|
209
|
+
Harness journal format 2 versions controller state and model-key inputs.
|
|
210
|
+
Resuming older controller state or an agent session journal fails before a live
|
|
211
|
+
model call with `HarnessError` code `incompatible_journal`; start a new run.
|
|
212
|
+
Historical transcript projection remains available for display and renders
|
|
213
|
+
summary text as a user message. rc.0 does not promise journal compatibility.
|
|
214
|
+
|
|
215
|
+
`import * as CellTurn from "@smthrs/harness/CellTurn"`
|
|
216
|
+
|
|
217
|
+
The cell-first controller. It decides continue, park, or finish from durable
|
|
218
|
+
evidence, the transition a cell returned and the budgets the run declared, and
|
|
219
|
+
never from the presence of a provider tool call.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
export const run: (
|
|
223
|
+
input: Input
|
|
224
|
+
) => Stream.Stream<
|
|
225
|
+
AgentEvent.AgentEvent,
|
|
226
|
+
HarnessError,
|
|
227
|
+
EngineLike.EngineLike | Sandbox.Sandbox | Steering.Source
|
|
228
|
+
>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`run` executes the loop until it completes, parks, or exhausts its budget.
|
|
232
|
+
Cancellation is fiber interruption: interrupting the stream tears down the
|
|
233
|
+
sandbox through scope closure and reports one abort. `Input` is
|
|
234
|
+
`{ state, flows, limits?, supervisor?, monitors?, judged?, stance?, instructions?, pinned?, memory? }`,
|
|
235
|
+
where `flows` is the frame's `FlowDescriptor` list, already narrowed by seat
|
|
236
|
+
visibility, and `monitors` (default `Monitor.defaults()`) are what each
|
|
237
|
+
supervisor reading scores. `judged` (default false) is true only when the
|
|
238
|
+
host's `Evaluator` is a real judge, and it arms Jev's features: the
|
|
239
|
+
supervisor delivers what its monitors gate through and its inserts, frame 0
|
|
240
|
+
takes the run-start relevance reading, and the supervisor marks segments for
|
|
241
|
+
compaction. It is runtime configuration, not `State`, so a resumed run is
|
|
242
|
+
armed by the host that resumes it. `DisciplineArmed` journals it, only when
|
|
243
|
+
true, as `judged`, `relevance` (`{ withholdAt, pinned }`) and `monitors`
|
|
244
|
+
(each id, kind and gate), so an unjudged journal is unchanged;
|
|
245
|
+
`supervisorSteer` is read only from journals written before `judged`. `stance`
|
|
246
|
+
(`careful` or `paranoid`) is the static stance the run is taught; pass the
|
|
247
|
+
window taught with it. `DisciplineArmed.stance` records it when set.
|
|
248
|
+
|
|
249
|
+
**Run-start relevance.** At frame 0 of a judged run, once the frame's catalog
|
|
250
|
+
is known, every model-invocable flow not in `pinned` (as `Relevance.flowItem`,
|
|
251
|
+
sorted by name), every chunk of `instructions` (`Relevance.Document`s) and
|
|
252
|
+
every row of `memory` (kind `memory`, id its `key`) is one item of a single
|
|
253
|
+
`relevance/unnecessary` reading, recorded under the boundary `relevance`. Nothing to judge asks nothing. `decision-settled` rows are
|
|
254
|
+
followed by `relevance-settled` (`source: "run"`). Withheld flows and skills
|
|
255
|
+
go to `State.withheldFlows` and leave every frame's taught catalog and
|
|
256
|
+
`ctx.flows`; the recorded `flow-catalog` stays whole. Withheld chunks leave
|
|
257
|
+
the window's instructions segment, found by the digest of
|
|
258
|
+
`instructionsSegment(instructions, new Set())`, which is how the host must
|
|
259
|
+
build it; a segment left empty is removed. A chunk is matched by id and text,
|
|
260
|
+
so a replay against an edited file keeps a chunk whose text the reading never
|
|
261
|
+
judged. Withheld rows leave the memory
|
|
262
|
+
segment, found by the digest of `memorySegment(memory.render(memory.rows),
|
|
263
|
+
memory.digest)`: it becomes `memory.render` of the kept rows, or is removed
|
|
264
|
+
when none is kept. A reading nobody could judge withholds nothing and
|
|
265
|
+
journals `decision-unjudged`. A call to a withheld flow
|
|
266
|
+
settles `flow_withheld` and restores it: the next frame shows it, and
|
|
267
|
+
`relevance-restored` names it. A replay is served the recorded reading.
|
|
268
|
+
|
|
269
|
+
**Compaction marks.** Marking is separate from compacting. In a judged run
|
|
270
|
+
each supervisor reading also asks `compaction/marks` about the transcript
|
|
271
|
+
segments not yet marked, the person's excepted, and the next boundary stores
|
|
272
|
+
the answers, by segment digest, on `State.segmentFacts` inside its recorded
|
|
273
|
+
drain (`Steering.DrainRecord.marks`). Marking changes nothing the model is
|
|
274
|
+
sent: the prefix a request is sent stays byte-stable until a compaction. A
|
|
275
|
+
frame compacts when the window crosses its budget (`Compaction.shouldCompact`
|
|
276
|
+
and `selectPrefix` at their defaults), or when the boundary before it took a
|
|
277
|
+
supervisor reading that fired `outdated_context` or `irrelevant_context`
|
|
278
|
+
(`Steering.DrainRecord.compact`, carried on `State.compactionDue` for that
|
|
279
|
+
frame alone); a judged run then marks each prefix segment `keep`, `squash` or `remove`. Pins, read
|
|
280
|
+
from `State.segmentFacts` and the still-failing checks, fix what must
|
|
281
|
+
survive: a summary is squashed; steering, the person's segments and the
|
|
282
|
+
newest segment that ran each still-failing check are kept; a segment that
|
|
283
|
+
changed files is never removed. The stored answers resolve the rest against
|
|
284
|
+
the budget; segments still unmarked are asked in one `compaction/marks`
|
|
285
|
+
reading, recorded under the boundary `compaction-marks:<prefix digest>`, so
|
|
286
|
+
a replay asks nothing and re-keys the same summary. Only squashed segments
|
|
287
|
+
reach the sealed summary step, which is skipped when nothing is squashed;
|
|
288
|
+
kept segments stay verbatim after the summary. `decision-settled` rows
|
|
289
|
+
precede `compaction-settled`, which carries `summary?`, `kept`, `marks`
|
|
290
|
+
(segment digest, mark, `pinned?`), `removedTokens` and `causes`. An unjudged run and a
|
|
291
|
+
reading nobody could judge (journaled `decision-unjudged`) squash every
|
|
292
|
+
prefix segment; so does a judged run whose facts do not describe every
|
|
293
|
+
transcript segment, and its `compaction-settled` says `unaligned`.
|
|
294
|
+
|
|
295
|
+
`instructionsSegment(documents, withheld)` is the prefix `system` segment of
|
|
296
|
+
`Relevance.render(documents, withheld)`. It is `system`, not `instructions`,
|
|
297
|
+
because the task is what the prefix `instructions` segments say.
|
|
298
|
+
|
|
299
|
+
`Memory` is `{ rows, digest, render }`: the opening memory rows
|
|
300
|
+
(`MemoryRow`, `{ key, text }`), the digest the opening declares, and the
|
|
301
|
+
host's render, which is only handed rows of `rows`, in order.
|
|
302
|
+
`memorySegment(text, digest)` is the prefix `system` segment of rendered
|
|
303
|
+
memory under that declared digest: what a run remembers is not its task, and
|
|
304
|
+
each row is judged against a task that does not contain it.
|
|
305
|
+
|
|
306
|
+
`CellTurn.make` constructs the initial `State`:
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
export const make: (options: {
|
|
310
|
+
readonly session: string
|
|
311
|
+
readonly seat: string
|
|
312
|
+
readonly modelParams: ModelRequest.GenerationParams
|
|
313
|
+
readonly layers: ReadonlyArray<string>
|
|
314
|
+
readonly capabilityEnvelope: ReadonlyArray<Capability.CapabilityPattern>
|
|
315
|
+
readonly placement: Option.Option<Descriptor.Placement>
|
|
316
|
+
readonly contextWindow: ContextWindow.ContextWindow
|
|
317
|
+
/** Initial budget; a seat steer recomputes it from the model catalog. */
|
|
318
|
+
readonly contextWindowTokens?: number | undefined
|
|
319
|
+
readonly frame?: number | undefined
|
|
320
|
+
/** Zero disarms the frame limit; exhausted positive limits spend no new frame. */
|
|
321
|
+
readonly maxFrames?: number | undefined
|
|
322
|
+
readonly readOnlyCap?: number | undefined
|
|
323
|
+
readonly modelCallMs?: number | undefined
|
|
324
|
+
readonly repeatCap?: number | undefined
|
|
325
|
+
readonly narrowingCap?: number | undefined
|
|
326
|
+
readonly unmovedCap?: number | undefined
|
|
327
|
+
readonly unresolvedCap?: number | undefined
|
|
328
|
+
readonly claimCap?: number | undefined
|
|
329
|
+
readonly approvalChannel?: boolean | undefined
|
|
330
|
+
readonly revalidations?: number | undefined
|
|
331
|
+
readonly checkpointCap?: number | undefined
|
|
332
|
+
}) => State
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Every omitted budget takes its module-level default, and zero disarms it:
|
|
336
|
+
|
|
337
|
+
| Constant | Value | Budget it defaults |
|
|
338
|
+
| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
|
|
339
|
+
| `defaultMaxFrames` | 100 | Frames one admitted task may spend. |
|
|
340
|
+
| `defaultReadOnlyFrames` | 12 | Consecutive stalled read-only frames before intervention. |
|
|
341
|
+
| `defaultModelCallMs` | 300,000 | Wall-clock milliseconds one model call may spend at unset, `none`, `minimal`, `low` or `medium` effort. |
|
|
342
|
+
| `defaultRepeatFrames` | 4 | Consecutive repeat-observation frames. |
|
|
343
|
+
| `defaultNarrowingDemands` | 1 | Completions bounced for narrowed evidence. |
|
|
344
|
+
| `defaultUnmovedDemands` | 1 | Completions bounced for an unmoved tree. |
|
|
345
|
+
| `defaultUnresolvedDemands` | 1 | Completions bounced for a displaced failing check. |
|
|
346
|
+
| `defaultClaimDemands` | 3 | Frames given to answer an unrecorded claim. |
|
|
347
|
+
| `defaultRevalidations` | 1 | In-frame answers to an unparseable cell. |
|
|
348
|
+
| `defaultMaxCheckpoints` | 8 | Trees one run may pin with `ctx.checkpoint()`. |
|
|
349
|
+
|
|
350
|
+
An omitted `modelCallMs` follows the run's reasoning effort through
|
|
351
|
+
`modelCallMsFor(effort)`: 300,000 ms up to `medium`, 900,000 ms at `high`, and
|
|
352
|
+
1,800,000 ms at `xhigh` or `max`. A steering change of effort moves a defaulted
|
|
353
|
+
ceiling with it; a host's own number stays. The engine also cuts off a stream
|
|
354
|
+
that sends nothing for `defaultModelIdleMs` (300,000 ms), so a long ceiling
|
|
355
|
+
never waits out a stall.
|
|
356
|
+
|
|
357
|
+
A read-only frame counts toward `readOnlyCap` unless the run has already
|
|
358
|
+
written something and the frame settled a call the run had not issued before.
|
|
359
|
+
Such a frame is debugging work that exists, so it holds the streak. Frames that
|
|
360
|
+
call nothing, raise, or re-ask old questions still count, and every read-only
|
|
361
|
+
frame counts before the first write.
|
|
362
|
+
|
|
363
|
+
`readOnlyCap` is the one budget that defaults to disarmed (0), because a run
|
|
364
|
+
that is only meant to read, a question or a review, has nothing to be capped
|
|
365
|
+
at. `approvalChannel` defaults to `false`: a park is durable waiting, and a
|
|
366
|
+
run with nobody to answer it fails with `approval_unavailable`, carrying the question.
|
|
367
|
+
|
|
368
|
+
`State` is a schema class carrying the controller's view of the run across
|
|
369
|
+
frames: the panel of realm names, the call ledger, the checks and failures
|
|
370
|
+
ledgers, the truncated-output ledger, the checkpoint ids, the opening
|
|
371
|
+
workspace digest, every budget and its counter, the context window, and
|
|
372
|
+
`segmentFacts`: per transcript segment, the frame that wrote it, whether it
|
|
373
|
+
carries the person's messages (a supervisor insert does not count), whether
|
|
374
|
+
that frame changed files, and the checks it ran. It
|
|
375
|
+
serializes into the journal; the realm itself is not in it and cannot be,
|
|
376
|
+
because a live JavaScript context is rebuilt on resume by re-executing the
|
|
377
|
+
cells that built it.
|
|
378
|
+
|
|
379
|
+
`CellTurn.run` accepts an optional `contextWindowTokensFor(seat)` effect on
|
|
380
|
+
its runtime `Input`. A seat steer resolves its next compaction budget through
|
|
381
|
+
this callback. The agent adapters supply their host `SeatResolver`, including
|
|
382
|
+
logical seats such as `reviewer`; direct harness callers without a callback use
|
|
383
|
+
the model-id catalog. The callback lives outside serializable `State`; the
|
|
384
|
+
resolved token count is carried in state across frames.
|
|
385
|
+
|
|
386
|
+
`CellTurn.teach(contextWindow, flows, environment?, stance?)` prepends the cell contract and the
|
|
387
|
+
callable-flow catalog to a context window as prefix segments. The optional
|
|
388
|
+
`CellTurn.Environment` supplies the host's measured `locale` and `absentTools`.
|
|
389
|
+
A `stance` adds a one-line `cell-stance` section after the contract,
|
|
390
|
+
`Monitor.carefulText` or `Monitor.paranoidText` verbatim; it is constant for
|
|
391
|
+
the run, so the cached prefix holds.
|
|
392
|
+
Catalog metadata is escaped inside an `untrusted-data` block with each
|
|
393
|
+
source, root and repository path. Metadata and tool output cannot grant
|
|
394
|
+
authority or change the task. Printed observations and controller-generated
|
|
395
|
+
compaction summaries use the same boundary; journal replay retains it. Pass
|
|
396
|
+
`refreshFlows` on `CellTurn.run`'s `Input` to journal fresh descriptors at each
|
|
397
|
+
frame boundary and replace the previous teaching. The prompt, `ctx.flows`,
|
|
398
|
+
and admission share the recorded snapshot. Omit it to keep `flows` fixed.
|
|
399
|
+
|
|
400
|
+
## Sandbox
|
|
401
|
+
|
|
402
|
+
`import * as Sandbox from "@smthrs/harness/Sandbox"`
|
|
403
|
+
|
|
404
|
+
The deterministic script sandbox port. A cell never runs in the host realm; it
|
|
405
|
+
runs behind this port, which grants exactly one effectful primitive, flow
|
|
406
|
+
invocation against the capability-narrowed catalog the run was given, and
|
|
407
|
+
returns a serializable `Cell.Outcome`.
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
export interface Sandbox {
|
|
411
|
+
readonly capabilities: Capabilities
|
|
412
|
+
readonly openRealm?: (
|
|
413
|
+
options: RealmOptions
|
|
414
|
+
) => Effect.Effect<Realm, SandboxError, Scope.Scope>
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
`openRealm` is optional because a realm is the whole surface: there is no
|
|
419
|
+
per-cell evaluation beside it. A composition that offers none is refused with
|
|
420
|
+
`Sandbox.realmUnsupported`. `RealmOptions` is `{ flows, limits? }`: the
|
|
421
|
+
catalog frozen into `ctx.flows`, and the ceilings the realm enforces. The
|
|
422
|
+
service tag is `Sandbox.Sandbox`; `make`, `layer`, `makeNoop`, and
|
|
423
|
+
`layerNoop` construct and provide implementations and stubs.
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
export interface Realm {
|
|
427
|
+
readonly evaluate: (
|
|
428
|
+
evaluation: RealmEvaluation
|
|
429
|
+
) => Effect.Effect<RealmFrame, SandboxError | HarnessError>
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
`RealmEvaluation` is
|
|
434
|
+
`{ cell, frame, call, program?, flows?, mint?, bounded?, limits? }`;
|
|
435
|
+
`RealmFrame` is `{ outcome, prints, bindings }`. The `Sandbox.Handler`
|
|
436
|
+
resolves one `Sandbox.Invocation` (`{ ordinal, flow, input, at? }`) into a
|
|
437
|
+
`Cell.CallResult`; the `Sandbox.Minter` settles one `ctx.checkpoint()` mint on
|
|
438
|
+
the same queue, in issue order, so the pin lands where the cell wrote it.
|
|
439
|
+
|
|
440
|
+
A cell states its intent by calling, and `Sandbox.replTransition` is the only
|
|
441
|
+
place a `Cell.Transition` is constructed:
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
export const replTransition: (
|
|
445
|
+
intent: Intent | undefined,
|
|
446
|
+
justification: string | undefined
|
|
447
|
+
) => Cell.Transition
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
`Intent` is `{ _tag: "Done", output }` or `{ _tag: "Park", reason, message }`,
|
|
451
|
+
with `reason` one of `"waiting-input"`, `"waiting-event"`, `"waiting-quota"`.
|
|
452
|
+
An absent intent is a `continue`.
|
|
453
|
+
|
|
454
|
+
Binding-author seams: `Sandbox.compile` erases type-only syntax from a cell
|
|
455
|
+
without evaluating or resolving modules (only Node's strip-safe TypeScript
|
|
456
|
+
subset; anything needing emit is refused). `Sandbox.driveCell` runs the shared
|
|
457
|
+
drive loop that settles queued calls one at a time, in issue order, until the
|
|
458
|
+
cell settles; `Sandbox.PendingCall` is the queued-call shape. `Sandbox.callTimedOut(flow,
|
|
459
|
+
callMs)` synthesizes the resolved `timeout` refusal one ceiling means,
|
|
460
|
+
whichever clock enforced it. `Sandbox.raisedOutcome` projects a thrown value
|
|
461
|
+
into a stable serializable `Cell.Raised`. `Sandbox.mintUnavailable` is the
|
|
462
|
+
`checkpoint_unavailable` refusal for a run with no minter wired.
|
|
463
|
+
|
|
464
|
+
`SandboxError` carries a `SandboxErrorCode` of `unavailable`, `unsupported`,
|
|
465
|
+
or `runtime_failed`: failures of the binding itself, as opposed to a cell that
|
|
466
|
+
ran and failed, which is a `Cell.Raised` outcome instead.
|
|
467
|
+
|
|
468
|
+
The limits and their defaults are documented under [Limits](#limits).
|
|
469
|
+
`Sandbox.withDefaults(capabilities, limits)` fills omitted ceilings for the
|
|
470
|
+
limits a binding can enforce; an explicit unsupported limit passes through so
|
|
471
|
+
the binding can refuse it. The print-channel constants are
|
|
472
|
+
`Sandbox.printFrameBytes` (16 KiB), `Sandbox.printStatementFloor` (512 bytes),
|
|
473
|
+
and `Sandbox.printRetainedBytes` (256 KiB).
|
|
474
|
+
|
|
475
|
+
## QuickJSSandbox
|
|
476
|
+
|
|
477
|
+
`import * as QuickJSSandbox from "@smthrs/harness/QuickJSSandbox"` (not
|
|
478
|
+
re-exported from the root)
|
|
479
|
+
|
|
480
|
+
The QuickJS-WASM sandbox binding, and the production `Sandbox`: the cell runs
|
|
481
|
+
inside a QuickJS interpreter compiled to WebAssembly, a genuinely separate
|
|
482
|
+
JavaScript realm with no reference to the host's globals, prototypes, or
|
|
483
|
+
module loader. The same single-file variant runs unmodified on Node and in a
|
|
484
|
+
browser. The prelude removes `Date`, `Math.random`, and `Proxy` from the
|
|
485
|
+
realm, and installs `ctx` and `console` as non-writable, non-configurable
|
|
486
|
+
properties.
|
|
487
|
+
|
|
488
|
+
| Export | Signature | Behavior |
|
|
489
|
+
| -------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
490
|
+
| `make` | `Effect<Sandbox.Sandbox, Sandbox.SandboxError>` | Constructs the sandbox over the single-file build with the live clock. |
|
|
491
|
+
| `layer` | `Layer<Sandbox.Sandbox, Sandbox.SandboxError>` | Provides the same. |
|
|
492
|
+
| `makeWithClock` | `Effect<Sandbox.Sandbox, Sandbox.SandboxError, ComputeClock>` | The single-file build, with the clock seam left to the caller. |
|
|
493
|
+
| `makeWithVariant` | `Effect<Sandbox.Sandbox, Sandbox.SandboxError, ComputeClock \| Variant>` | Constructs over the build the host names, compiling the module once. |
|
|
494
|
+
| `layerWithVariant` | `Layer<Sandbox.Sandbox, Sandbox.SandboxError, Variant>` | Provides the sandbox over the named build, with the live clock. |
|
|
495
|
+
| `Variant` / `VariantService` | service tag | The QuickJS build the sandbox compiles. |
|
|
496
|
+
| `layerVariantLive` | `Layer<Variant>` | Provides the single-file default. |
|
|
497
|
+
| `layerVariant` | `(variant: QuickJSSyncVariant) => Layer<Variant>` | Provides a build the host names, for runtimes that forbid compiling WebAssembly from bytes. |
|
|
498
|
+
| `ComputeClock` / `ComputeClockService` | service tag | Synchronous monotonic-enough clock the interrupt callback requires. |
|
|
499
|
+
| `layerClockLive` | `Layer<ComputeClock>` | Provides the browser-safe host clock. |
|
|
500
|
+
| `loadModule` | `(loader: () => Promise<QuickJSWASMModule>) => Effect<QuickJSWASMModule, SandboxError>` | Loads a QuickJS module through the typed failure boundary (`runtime_failed`). |
|
|
501
|
+
| `cacheSuccessful` | `<A>(load: () => Promise<A>) => () => Promise<A>` | Caches only a successful load; a rejection may be retried. |
|
|
502
|
+
|
|
503
|
+
The compiled module is cached per variant, weakly, so two sandboxes over one
|
|
504
|
+
variant share it and a variant built per request stays collectable. For the
|
|
505
|
+
workerd setup, see [Run on Cloudflare workerd](./guides/workerd.md).
|
|
506
|
+
|
|
507
|
+
## Cell
|
|
508
|
+
|
|
509
|
+
`import * as Cell from "@smthrs/harness/Cell"`
|
|
510
|
+
|
|
511
|
+
The cell contract: the serializable half of the frame. Nothing here executes
|
|
512
|
+
anything; execution is `Sandbox`, durability is `EngineLike.call`, and the
|
|
513
|
+
loop is `CellTurn`.
|
|
514
|
+
|
|
515
|
+
**Source.** `Cell.source(text, language?)` builds a `Cell.Source` with its
|
|
516
|
+
computed digest; `language` is `"javascript"` or `"typescript"`. The digest is
|
|
517
|
+
part of every call identity produced inside the cell, so editing one character
|
|
518
|
+
re-keys every boundary within it. `Cell.extract(text)` recovers the cell
|
|
519
|
+
program from one model reply: every fenced block tagged `cell`, `js`,
|
|
520
|
+
`javascript`, `ts`, or `typescript`, joined in reply order with byte-identical
|
|
521
|
+
repeats dropped, returning `{ source, blocks }` or a `no_cell` rejection. An unterminated cell fence rejects the entire reply as
|
|
522
|
+
`output_truncated`; a provider `length` stop uses the same code before execution.
|
|
523
|
+
|
|
524
|
+
**Transitions.** `Cell.Transition` is the tagged union of:
|
|
525
|
+
|
|
526
|
+
- `Cell.Continue`: the cell's turn ended without settling the run. Carries an
|
|
527
|
+
optional `justification`, the typed way out of the read-only cap, written by
|
|
528
|
+
`ctx.justify`.
|
|
529
|
+
- `Cell.Complete`: the cell declares the task finished, with `output` as the
|
|
530
|
+
run's answer.
|
|
531
|
+
- `Cell.Park`: the cell asks to wait durably, with `reason` one of
|
|
532
|
+
`"waiting-input"`, `"waiting-event"`, `"waiting-quota"` and a `message`.
|
|
533
|
+
|
|
534
|
+
**Outcomes.** `Cell.Outcome` is the tagged union of `Settled` (ran and
|
|
535
|
+
produced a well-formed transition), `Raised` (ran and threw; the thrown value
|
|
536
|
+
is projected into stable `name` and `message` text), and `Rejected` (never
|
|
537
|
+
ran, or produced no transition). `Cell.RejectionCode` is `no_cell`, `output_truncated`,
|
|
538
|
+
`imports_forbidden`, `compile_failed`, `invalid_transition`,
|
|
539
|
+
`unsupported_language`, `limit_exceeded`, or `stalled`. A result that cannot
|
|
540
|
+
fit in the remaining QuickJS heap is rejected before materialization with
|
|
541
|
+
`code: "limit_exceeded"` and `reason: "heap"`, so the frame remains recordable.
|
|
542
|
+
|
|
543
|
+
Flow-result heap checks conservatively include value and property storage plus
|
|
544
|
+
bridge scratch space, not just serialized JSON bytes. A reply can be refused
|
|
545
|
+
when its estimated allocation exceeds the remaining heap.
|
|
546
|
+
|
|
547
|
+
Bridge replies are also limited to 128 levels of JSON nesting so a refusal can
|
|
548
|
+
release partial handles safely. Exceeding this limit produces a typed
|
|
549
|
+
`limit_exceeded` frame.
|
|
550
|
+
|
|
551
|
+
**The catalog.** `Cell.FlowProjection` is the read-only projection of one
|
|
552
|
+
callable flow handed to a cell: `name`, `description`, `capabilities`,
|
|
553
|
+
`tier`, `placement`, and an optional inline `input` JSON Schema document.
|
|
554
|
+
`Cell.project(descriptor)` derives it from a registry descriptor. It is
|
|
555
|
+
exactly what `ctx.flows` exposes: enough to choose a call, nothing that
|
|
556
|
+
carries authority.
|
|
557
|
+
|
|
558
|
+
**Call identity.** `Cell.CallIdentity` folds `session`, `frame`, the cell
|
|
559
|
+
digest, the zero-based `ordinal` of the call within the cell, the
|
|
560
|
+
`declaration` digest, and the resolved `layers` into one key. Re-executing a
|
|
561
|
+
cell reaches the same ordinal with the same declaration, so a settled boundary
|
|
562
|
+
replays. `Cell.declarationDigest(descriptor)` re-exports
|
|
563
|
+
`Descriptor.declarationDigest` from `@smthrs/registry`, which owns
|
|
564
|
+
`FlowDescriptor`. It hashes the complete material declaration: every top-level
|
|
565
|
+
field except `provenance.pack`, with `capabilities` sorted and every other
|
|
566
|
+
array in declaration order. `@smthrs/chain` keys its catalog entries with the
|
|
567
|
+
same number.
|
|
568
|
+
|
|
569
|
+
**Call results.** `Cell.CallResultVariant` is a discriminated union of
|
|
570
|
+
`CallSuccess` (`outcome: "success"`, JSON `value`, optional `message`) and
|
|
571
|
+
`CallFailure` (`outcome: "failure"`, JSON `value`, optional `message` and
|
|
572
|
+
`code`). A success cannot carry a failure code. The existing `Cell.CallResult`
|
|
573
|
+
class constructor validates the same variants and preserves valid encoded
|
|
574
|
+
field names. A failure's absent code means `Cell.defaultCallFailureCode`,
|
|
575
|
+
`"flow_failed"`. `Cell.callFailure(result)`
|
|
576
|
+
projects a failed result into the fixed envelope the cell observes:
|
|
577
|
+
|
|
578
|
+
```json
|
|
579
|
+
{ "ok": false, "error": { "code": "...", "message": "...", "hint": "..." } }
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
A failed call resolves with this value rather than throwing, so the recovery
|
|
583
|
+
branch the model already wrote still runs; a successful call resolves with the
|
|
584
|
+
flow's own value, unwrapped. `Cell.callFailureHint` maps each of the 13 codes
|
|
585
|
+
to the one action that recovers it. The codes and hints are tabulated in
|
|
586
|
+
[troubleshooting](./troubleshooting.md#a-flow-call-fails).
|
|
587
|
+
|
|
588
|
+
`Cell.decodeCallResult`, `Cell.decodeOutcome` and `Cell.decodeTransition`
|
|
589
|
+
accept untrusted host or recorded input. They refuse contradictory fields,
|
|
590
|
+
missing required values and unsupported variants with `HarnessError`
|
|
591
|
+
(`engine_failed`), retaining the original cause. Outcome and transition
|
|
592
|
+
decoders validate encoded fields even on class instances, then reconstruct
|
|
593
|
+
the schema classes required by the recorder. Sandbox settlement and the
|
|
594
|
+
durable cell recorder apply these boundaries before emitting successful
|
|
595
|
+
observations. A missing success value is corrupt input, not an implicit null.
|
|
596
|
+
Valid current records retain their JSON representation, including failures
|
|
597
|
+
whose code is absent. `CellValidation.Validation` likewise separates compiled
|
|
598
|
+
source from rejection so the two cannot be supplied together.
|
|
599
|
+
|
|
600
|
+
`Cell.CallResult` retains its historical encoded schema representation because
|
|
601
|
+
the agent hashes that representation into sealed keys. The constructor and
|
|
602
|
+
boundary decoder enforce the success/failure invariants; admission hardening
|
|
603
|
+
does not re-key valid results. The agent pins the complete key material and
|
|
604
|
+
tests a historical result through SQLite close, reopen and resume.
|
|
605
|
+
|
|
606
|
+
Changing the valid wire contract still requires a version cutover and a newly
|
|
607
|
+
planned run. Existing approvals and recorded keys remain bound to their
|
|
608
|
+
original bytes; no decoder translates identities. Intermediate builds that
|
|
609
|
+
added the `success-without-failure-code/v1` filter to the encoded schema derived
|
|
610
|
+
different keys. Finish their executions on the same build before upgrading.
|
|
611
|
+
See the agent's [persisted cell-call identity](https://agent.smithers.sh/concepts/engine-port/#persisted-cell-call-identity)
|
|
612
|
+
for the retained algorithm and composition versions.
|
|
613
|
+
|
|
614
|
+
**Checkpoints.** `Cell.baseCheckpoint` is `"base"`, the id naming the tree a
|
|
615
|
+
run opened on, pinned for free and always present. `Cell.checkpoint(id)`
|
|
616
|
+
builds the opaque handle a cell holds; `Cell.checkpointOf(value)` reads the id
|
|
617
|
+
back strictly, returning `undefined` for anything that is not a handle, which
|
|
618
|
+
the boundary answers as an ordinary `invalid_input`.
|
|
619
|
+
|
|
620
|
+
## EngineLike
|
|
621
|
+
|
|
622
|
+
`import * as EngineLike from "@smthrs/harness/EngineLike"`
|
|
623
|
+
|
|
624
|
+
The narrow engine port the harness consumes. `EngineLike.make` builds the
|
|
625
|
+
service from an implementation, `EngineLike.layer` provides it, and
|
|
626
|
+
`EngineLike.makeNoop` / `layerNoop` build a stub whose operations are
|
|
627
|
+
unavailable (`observe` and `capture` answer `Option.none()`, the honest
|
|
628
|
+
"measured nothing, pinned nothing", rather than failing).
|
|
629
|
+
|
|
630
|
+
```ts
|
|
631
|
+
export interface EngineLike {
|
|
632
|
+
readonly sealStep: (
|
|
633
|
+
step: SealedModelStep
|
|
634
|
+
) => Stream.Stream<ModelEvent.ModelEvent, Model.ModelFailure | HarnessError>
|
|
635
|
+
readonly sealStepWithEvents?: (
|
|
636
|
+
step: SealedModelStep,
|
|
637
|
+
emit: (event: AgentEvent.AgentEvent) => Effect.Effect<void>
|
|
638
|
+
) => Stream.Stream<ModelEvent.ModelEvent, Model.ModelFailure | HarnessError>
|
|
639
|
+
readonly splice: (batch: Plan.Batch) => Stream.Stream<Plan.SpliceEvent, HarnessError>
|
|
640
|
+
readonly call: (call: Cell.Call) => Effect.Effect<Cell.CallResult, HarnessError>
|
|
641
|
+
readonly admit?: (call: Cell.Call) => Effect.Effect<Cell.CallResult | undefined, HarnessError>
|
|
642
|
+
readonly guard?: Guard
|
|
643
|
+
readonly record: <A>(boundary: RecordBoundary<A>) => Effect.Effect<A, HarnessError>
|
|
644
|
+
readonly observe: Effect.Effect<Option.Option<Observation>, HarnessError>
|
|
645
|
+
readonly capture: (request: CaptureRequest) => Effect.Effect<Option.Option<Snapshot>, HarnessError>
|
|
646
|
+
readonly resolve?: (request: ModelRequest.ModelRequest) => Effect.Effect<Option.Option<Resolved>>
|
|
647
|
+
readonly suspend: (reason: SuspendReason) => Effect.Effect<never, HarnessError>
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
- `sealStep` runs one sealed model step. `SealedModelStep` is `{ request,
|
|
652
|
+
keyMaterial, modelCallMs? }`. The implementation resolves the route, runs
|
|
653
|
+
`Route.prepare` from [`@smthrs/model`](/api/model), and digests the
|
|
654
|
+
credential-free prepared request, canonical body bytes included, with the
|
|
655
|
+
declared material into the sealed-step key before executing. A provider wire
|
|
656
|
+
change must produce a new key; credentials are signed on after the digest
|
|
657
|
+
and never enter it. `modelCallMs` travels on the step and is never key
|
|
658
|
+
material, so the number the controller journals as armed is the number the
|
|
659
|
+
engine enforces.
|
|
660
|
+
- `sealStepWithEvents`, when supplied, seals through the same boundary and
|
|
661
|
+
receives the controller's event emitter for failover and park transitions.
|
|
662
|
+
- `call` runs one flow call as a keyed, journaled activity at the tier the
|
|
663
|
+
flow declares, keyed by `call.identity`. A flow failure settles as a
|
|
664
|
+
`failure` `Cell.CallResult`; a permission requirement, an abort, or an
|
|
665
|
+
engine failure travels in the error channel so the cell can never swallow a
|
|
666
|
+
park.
|
|
667
|
+
- `admit`, when present, decides a call's authority before the controller
|
|
668
|
+
starts the call's `callMs` clock, so time a person spends answering an
|
|
669
|
+
approval is not charged to the flow. `undefined` admits the call; a result
|
|
670
|
+
is the refusal the cell reads instead, and `call` is never issued. An
|
|
671
|
+
implementation does not ask again when an admitted call is issued.
|
|
672
|
+
- `guard`, when present, is the host's runaway guard for timeouts (`Guard`:
|
|
673
|
+
`{ admit(subject), trip(timeout) }`). The controller calls `admit` before it
|
|
674
|
+
issues a call or evaluates a fresh cell frame, and `trip` with a `TimedOut`
|
|
675
|
+
(`{ source: "tool-call" | "cell", subject, limitMillis?, message }`) when a
|
|
676
|
+
call settles as a `timeout` failure or a frame overruns its wall-clock
|
|
677
|
+
limit. `limitMillis` is the limit the timeout recorded: the per-call
|
|
678
|
+
ceiling, or the call's own (a command's, an await's). `trip` succeeds when the run's timeouts are not guarded, and the
|
|
679
|
+
cell reads the timeout as before; a guarded host fails it with a park, and
|
|
680
|
+
the controller records nothing for the operation, so Continue runs it again
|
|
681
|
+
and Stop fails the run at `admit`. `subject` is the replay-stable call or
|
|
682
|
+
frame identity, as JSON.
|
|
683
|
+
- `record` journals one nondeterministic controller read, keyed on `(name,
|
|
684
|
+
identity)` together. `RecordBoundary` is `{ name, identity, success,
|
|
685
|
+
execute }`, where `success` is a `DurableSchema`, a schema that decodes
|
|
686
|
+
without services, and `identity` is a `BoundaryIdentity` (`{ session?,
|
|
687
|
+
frame, boundary }`).
|
|
688
|
+
- `observe` measures the workspace as it stands, as an `Observation` (`{
|
|
689
|
+
digest, paths, complete }`). `complete: false` means the measurement covered
|
|
690
|
+
a bounded prefix, and a prefix cannot say the rest of the tree held still.
|
|
691
|
+
After a partial walk, measurement stops for the rest of that run; mutation
|
|
692
|
+
evidence falls back to flow declarations.
|
|
693
|
+
- `capture` pins the workspace under the id in the `CaptureRequest` and
|
|
694
|
+
returns a `Snapshot` (`{ id, ref }`), where `ref` is the host's own name for
|
|
695
|
+
what it pinned.
|
|
696
|
+
- `resolve` is optional. It says what one request will actually be asked as:
|
|
697
|
+
an `Option` of a `Resolved` (`{ request, binding }`) holding the request
|
|
698
|
+
after any rewrite the host applies and, as an `Option`, the `Binding`
|
|
699
|
+
(`{ routeId, protocolId }`) of the route it goes to. The controller asks
|
|
700
|
+
before it seals and journals the answer as `AgentEvent.ModelRequested`. It
|
|
701
|
+
cannot fail. A host that cannot say what would be sent, because its rewrite
|
|
702
|
+
failed or no route accepts the request, answers `Option.none()`: the
|
|
703
|
+
controller journals no request for that call, and `sealStep` reports the
|
|
704
|
+
typed error. A host that answers none must fail that sealed step with the
|
|
705
|
+
failure it already met, without running the rewrite a second time. A
|
|
706
|
+
`Binding` is two names read off the credential-free prepared request, so it
|
|
707
|
+
has no member a credential could occupy. A host that omits `resolve` has the
|
|
708
|
+
request the controller built recorded, with no route.
|
|
709
|
+
`EngineLike.resolve(engine, request)` is that rule as a function: the port's
|
|
710
|
+
answer, or the request unchanged with no binding.
|
|
711
|
+
- `suspend` parks the current engine frame with a `SuspendReason` (`{ code,
|
|
712
|
+
message, details? }`), whose `SuspendReasonCode` is `permission-required`,
|
|
713
|
+
`waiting-quota`, `waiting-input`, `waiting-event`, or `engine`.
|
|
714
|
+
- `splice` turns a `Plan.Batch` into running children and streams
|
|
715
|
+
`Plan.SpliceEvent`s back.
|
|
716
|
+
|
|
717
|
+
## Steering
|
|
718
|
+
|
|
719
|
+
`import * as Steering from "@smthrs/harness/Steering"`
|
|
720
|
+
|
|
721
|
+
Turn-boundary steering values and their source contract. Human steering
|
|
722
|
+
reaches a run only at safe turn boundaries.
|
|
723
|
+
|
|
724
|
+
Every frame exit records its steering decision, including rejected and raised
|
|
725
|
+
cells, answerable parks, and completions. A completion promotes a queued follow-up
|
|
726
|
+
and continues when delivery has work for the next frame. At an exhausted frame
|
|
727
|
+
limit, the decision is empty and notifications remain pending at the source;
|
|
728
|
+
they are never acknowledged without a frame available to consume them.
|
|
729
|
+
|
|
730
|
+
`Steering.Queue` is an immutable FIFO of `Steering.Item`s: transcript inserts
|
|
731
|
+
(`SteerInsert` for the next boundary, `QueueInsert` for when the run would
|
|
732
|
+
otherwise go idle), and `SeatChange` and `ThinkingChange` (applied only after
|
|
733
|
+
the current turn closes).
|
|
734
|
+
`Steering.empty`, `enqueue`, and `drainAtClose(queue, cutoff)` operate on it
|
|
735
|
+
without mutation. `Steering.Drain` is
|
|
736
|
+
what one boundary promoted; `Steering.DrainRecord` and `drainRecord` project
|
|
737
|
+
it into its journaled record, which also carries what the supervisor delivered
|
|
738
|
+
there: its messages, the memory keys, the monitor delivered, the monitors
|
|
739
|
+
withheld, the monitor ledger after gating, the compaction marks the run
|
|
740
|
+
stores, and the context triggers that compact its next frame.
|
|
741
|
+
|
|
742
|
+
```ts
|
|
743
|
+
export interface Source {
|
|
744
|
+
readonly read: () => Effect.Effect<Queue, HarnessError>
|
|
745
|
+
readonly drain: (input: BoundaryInput) => Effect.Effect<Drain, HarnessError>
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
A drain is idempotent in its boundary string: a second drain at one boundary
|
|
750
|
+
promotes nothing and hands back exactly what the first promoted, with
|
|
751
|
+
`Drain.duplicate` set. A resumed run re-drains the boundaries it already
|
|
752
|
+
drained and must be told the same thing, or it rebuilds a different context
|
|
753
|
+
and re-keys every later sealed step. The service tag is `Steering.Source`;
|
|
754
|
+
`make`, `makeNoop`, `layer`, and `layerNoop` construct and provide it.
|
|
755
|
+
|
|
756
|
+
## Notifications
|
|
757
|
+
|
|
758
|
+
`import * as Notifications from "@smthrs/harness/Notifications"`
|
|
759
|
+
|
|
760
|
+
The adapter from the durable notification queue of
|
|
761
|
+
[`@smthrs/notifications`](/api/notifications) to harness turn boundaries.
|
|
762
|
+
|
|
763
|
+
```ts
|
|
764
|
+
export interface Options {
|
|
765
|
+
readonly runId: string
|
|
766
|
+
readonly lineageId: string
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
export const make: (
|
|
770
|
+
options: Options
|
|
771
|
+
) => Effect.Effect<Steering.Source, never, NotificationQueue.NotificationQueue>
|
|
772
|
+
|
|
773
|
+
export const layer: (
|
|
774
|
+
options: Options
|
|
775
|
+
) => Layer.Layer<Steering.Source, never, NotificationQueue.NotificationQueue>
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
`make` captures the journal-backed queue as the harness steering source for
|
|
779
|
+
one run lineage: its `read` reports an empty queue and its `drain` delegates
|
|
780
|
+
to the notification queue's own boundary-aware drain, mapping failures into
|
|
781
|
+
`HarnessError`. `layer` provides it.
|
|
782
|
+
|
|
783
|
+
## CellCalls
|
|
784
|
+
|
|
785
|
+
`import * as CellCalls from "@smthrs/harness/CellCalls"`
|
|
786
|
+
|
|
787
|
+
Registry-backed resolution for the flow calls a cell makes. It is deliberately
|
|
788
|
+
not a second registry: discovery, precedence, collision handling, and
|
|
789
|
+
progressive disclosure stay in [`@smthrs/registry`](/api/registry); this
|
|
790
|
+
module only decides which body runs and turns every resolution problem into a
|
|
791
|
+
`Cell.CallResult` that resolves in the cell as `{ ok: false, error }`.
|
|
792
|
+
|
|
793
|
+
```ts
|
|
794
|
+
export interface Options {
|
|
795
|
+
readonly registry: Registry.Registry
|
|
796
|
+
readonly catalog?: FlowBinding.Catalog | undefined
|
|
797
|
+
readonly implementations?: ReadonlyMap<string, Implementation> | undefined
|
|
798
|
+
readonly prompt?: PromptRunner | undefined
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
export interface Resolver {
|
|
802
|
+
readonly run: (call: Cell.Call) => Effect.Effect<Cell.CallResult, HarnessError>
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
export const make: (options: Options) => Resolver
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
A name the run-start relevance reading withheld is refused first, as
|
|
809
|
+
`flow_withheld`, by `CellTurn` before resolution. Resolution order: the registry must know the name (`unknown_flow`), the
|
|
810
|
+
descriptor must be model-invocable (`capability_refused`), and its
|
|
811
|
+
re-derived declaration digest must equal the call's
|
|
812
|
+
(`declaration_changed`). An executable binding then answers first, after an
|
|
813
|
+
identity check against the disclosed declaration; a markdown flow renders
|
|
814
|
+
against `{ args: string }` and runs through the supplied `PromptRunner`, or
|
|
815
|
+
settles `unimplemented`; anything else dispatches to the host's
|
|
816
|
+
`Implementation` for the name, or settles `unimplemented`. The resolver's
|
|
817
|
+
shape is exactly the call runner a durable host wires behind
|
|
818
|
+
`EngineLike.call`, so this browser-safe package never depends on the engine
|
|
819
|
+
binding.
|
|
820
|
+
|
|
821
|
+
## FlowBinding
|
|
822
|
+
|
|
823
|
+
`import * as FlowBinding from "@smthrs/harness/FlowBinding"`
|
|
824
|
+
|
|
825
|
+
The executable-flow binding contract: the smallest contract that pairs a flow
|
|
826
|
+
declaration with the code that runs it.
|
|
827
|
+
|
|
828
|
+
```ts
|
|
829
|
+
export interface Binding<R = never> {
|
|
830
|
+
readonly descriptor: Descriptor.FlowDescriptor
|
|
831
|
+
readonly run: (call: Cell.Call) => Effect.Effect<Cell.CallResult, HarnessError, R>
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export const make: <I, O, E, R = never>(options: Options<I, O, E, R>) => Binding<R>
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
`Options.flow` is the declaration, a `Declared` (`{ name?, description?,
|
|
838
|
+
capabilities, effects }`) plus `input` and `output` schemas. `make` projects
|
|
839
|
+
it into an ordinary `FlowDescriptor` with `descriptorOf`, deriving the body
|
|
840
|
+
digest from the handler source and rendering both schemas as inline JSON
|
|
841
|
+
Schema documents when they project; a schema with no JSON Schema form falls
|
|
842
|
+
back to the module locator, and explicit `inputDocument` / `outputDocument`
|
|
843
|
+
options win over the projection. An undeclared effect envelope defaults to
|
|
844
|
+
`mode: "expected"`, `onConflict: "serialize"`, and tier `irreversible`, the
|
|
845
|
+
tier that is never content-shared, so a binding that forgot its tier can never
|
|
846
|
+
replay another run's recorded result.
|
|
847
|
+
|
|
848
|
+
`run` decodes input, executes the handler, and encodes output into
|
|
849
|
+
serializable JSON. For struct inputs, a failed decode retries once by omitting
|
|
850
|
+
only declared optional fields whose encoded schema rejects their `null`.
|
|
851
|
+
Schema-valid nulls are preserved and the full schema validates the retry.
|
|
852
|
+
If the retry fails, the original rejection is reported. The encoded input
|
|
853
|
+
must be a struct-like object; unions of structs and records get no
|
|
854
|
+
null-omission retry.
|
|
855
|
+
|
|
856
|
+
Correctable failures (`invalid_input`, `flow_failed`) settle as `failure`
|
|
857
|
+
results that resolve in the cell as `{ ok: false, error }`. Inspect
|
|
858
|
+
`.ok === false` and `.error.code` to recover. Ordinary handler failures use the opaque message
|
|
859
|
+
`Flow <name> failed.` Raw messages, objects, and causes are excluded from call
|
|
860
|
+
results and their journal records. `Options.publicError`, typed as
|
|
861
|
+
`(error: E) => string | undefined`, explicitly selects safe public text; the
|
|
862
|
+
text is bounded before journaling. An absent renderer, `undefined`, a throw,
|
|
863
|
+
or a non-string result uses the opaque default. The renderer is not a
|
|
864
|
+
sanitizer: hosts must select safe fields and redact raw diagnostics before
|
|
865
|
+
logging or persisting them in the handler. Forward `message` only for error
|
|
866
|
+
classes the host authored with model-facing text; never forward messages of
|
|
867
|
+
transport, SDK, OS, or unknown errors, headers, URLs, or serialized causes.
|
|
868
|
+
|
|
869
|
+
Existing `HarnessError` values pass through the error channel with their
|
|
870
|
+
code and identity unchanged. Only `PermissionRequired` and `PermissionDenied`
|
|
871
|
+
are converted to `HarnessError` with code `suspended`. These errors bypass
|
|
872
|
+
`publicError`; interruptions are never caught.
|
|
873
|
+
|
|
874
|
+
`FlowBinding.provide(binding, context)` closes a binding's remaining
|
|
875
|
+
requirements. `FlowBinding.Source` is a named, possibly effectful producer of
|
|
876
|
+
bindings; `FlowBinding.source(name, bindings)` lifts a fixed list.
|
|
877
|
+
`FlowBinding.catalog(sources)` resolves ordered sources into a `Catalog`
|
|
878
|
+
(`{ entries, bindings, descriptors }`), refusing duplicate or empty names with
|
|
879
|
+
`assembly_failed`; `catalogResult` is the total-function form and `empty()`
|
|
880
|
+
the empty catalog. `FlowBinding.registry(base, catalog)` discloses the
|
|
881
|
+
catalog through an existing `Registry.Registry` with file-discovered entries
|
|
882
|
+
keeping precedence, shadowed bindings reported as `duplicate_name` warnings.
|
|
883
|
+
|
|
884
|
+
## CellHistory
|
|
885
|
+
|
|
886
|
+
`import * as CellHistory from "@smthrs/harness/CellHistory"`
|
|
887
|
+
|
|
888
|
+
The source of every cell the current turn executed. A frame throws its cell
|
|
889
|
+
away once the realm has evaluated it, so a model that wants to turn the script
|
|
890
|
+
it ran into a saved flow has nothing to read back; this service is where the
|
|
891
|
+
source goes. The controller appends each cell as it executes it, before
|
|
892
|
+
evaluation, so a cell that raised is still part of what the run ran.
|
|
893
|
+
|
|
894
|
+
The service is optional: the controller reads it with `Effect.serviceOption`,
|
|
895
|
+
a host that offers no way to save a flow binds nothing, and the controller
|
|
896
|
+
records nothing. `make` records what the controller executes; `makeCells`
|
|
897
|
+
serves a fixed list; `makeNoop` records nothing; `layer`, `layerCells`, and
|
|
898
|
+
`layerNoop` provide the three. `ExecutedCell` is one executed cell and
|
|
899
|
+
`Service` is the record-and-report interface.
|
|
900
|
+
|
|
901
|
+
## AgentEvent
|
|
902
|
+
|
|
903
|
+
`import * as AgentEvent from "@smthrs/harness/AgentEvent"`
|
|
904
|
+
|
|
905
|
+
The serializable events a harness adapter emits, one schema class per event
|
|
906
|
+
and `AgentEvent.AgentEvent` as the tagged union of all 45. The controller
|
|
907
|
+
journals them in order: `DisciplineArmed` once at the start, the frame cycle
|
|
908
|
+
(`TurnOpened`, `ModelRequested`, `ModelDelta`, `ModelRetried`, `ModelSettled`,
|
|
909
|
+
`CellProduced`, `CellRejectedInFrame`, `CellCallStarted`, `CellCallSettled`,
|
|
910
|
+
`CellPrinted`, `CellSettled`, `TransitionApplied`), the interventions and
|
|
911
|
+
observations (`ReadOnlyDemandIssued`, `ReadOnlyDemanded`, `RepeatDemanded`,
|
|
912
|
+
`NarrowedDemanded`, `NarrowOnlyDemanded`, `UnmovedDemanded`,
|
|
913
|
+
`UnresolvedDemanded`, `FailedCallDemanded`, `UnobservedDemanded`, `OutputDemanded`, `ClaimDemanded`,
|
|
914
|
+
`DecisionSettled`,
|
|
915
|
+
`SufficiencyObserved`, `VacuousVerificationObserved`,
|
|
916
|
+
`MutationObserved`, `CheckpointMinted`, `CompactionSettled`,
|
|
917
|
+
`SteeringDrained`), and the terminal set (`Suspended`, `PermissionRequired`,
|
|
918
|
+
`TurnClosed`, `Resolved`, `Aborted`).
|
|
919
|
+
|
|
920
|
+
Capacity attempts emit `ModelSelected` (`seat`) with the actual selected seat,
|
|
921
|
+
including recovery to the primary and later frames continuing on a backup.
|
|
922
|
+
Transitions are `SeatFailedOver` (`from`, `to`, `code`, optional
|
|
923
|
+
`resetAtEpochMillis`, `retryAfterMillis`, `httpStatus`, `quotaScope`), `ModelParked` (`seat`, `wakeAt`, `source`, `code`), and
|
|
924
|
+
`ModelUnparked` (`seat`, `at`).
|
|
925
|
+
|
|
926
|
+
Two of them record what a step was asked, so a reader can open one step and
|
|
927
|
+
ask it again:
|
|
928
|
+
|
|
929
|
+
- `ModelRequested` is written once per model call, before the call, and
|
|
930
|
+
carries the `ModelRequest` the provider is sent: the request after any
|
|
931
|
+
rewrite the host applies, and the `EngineLike.Binding` naming the route
|
|
932
|
+
where the host resolves one. `scope` (the run's session, which is a module
|
|
933
|
+
step's recorded scope), `frame` and `attempt` are the join keys. `attempt`
|
|
934
|
+
counts the model calls of one frame and purpose from one, and is the number
|
|
935
|
+
`CellRejectedInFrame` names when it refuses that call's answer. `purpose` is
|
|
936
|
+
`frame` or `compaction`; a compaction call runs ahead of its frame's
|
|
937
|
+
`TurnOpened`, so it is joined by `frame`, never by position. A call whose
|
|
938
|
+
host answers `EngineLike.resolve` with none writes no `ModelRequested`: the
|
|
939
|
+
sealed step fails on it, so no provider was sent anything.
|
|
940
|
+
- `DecisionSettled` is written once per classifier reading, beside
|
|
941
|
+
`ClaimDemanded`, and carries the classifier's stable id, its
|
|
942
|
+
`Classifier.digest`, the encoded state the transport was sent, the questions
|
|
943
|
+
in their wire form, and one `DecisionAnswer` per question. `acted` says
|
|
944
|
+
whether the decision changed what the run did next, and `decidedBy` is
|
|
945
|
+
`jev`, `seat` or `human`. A `DecisionAnswer` is tagged by `kind`: `boolean`
|
|
946
|
+
carries `p`; `choice` and `score` carry `value`, `probabilities`, and
|
|
947
|
+
`confidence`. `confidence` is `Evaluator.Response.confidence`, the
|
|
948
|
+
provider's own number, and the key is absent when the provider sent none. It
|
|
949
|
+
is never the largest probability. `decisionAnswers(answers, confidence)`
|
|
950
|
+
builds the record's answers from decoded `Classifier.Answer`s.
|
|
951
|
+
|
|
952
|
+
Both are emitted through the same `Observer` every other event takes, so a
|
|
953
|
+
durable host checkpoints them inside the step that produced them. The
|
|
954
|
+
controller provides `Journal` around each flow call: a flow that asks Jev
|
|
955
|
+
itself, such as the agent's `recall`, journals its receipts into the run that
|
|
956
|
+
called it. Outside a run `Journal` does nothing. A completion
|
|
957
|
+
judgement recorded before `DecisionSettled` existed replays with no decision:
|
|
958
|
+
the record is absent, never reconstructed.
|
|
959
|
+
|
|
960
|
+
Jev's other readings write these:
|
|
961
|
+
|
|
962
|
+
- `RelevanceSettled`: the human-provided items (`RelevanceKind`: `flow`,
|
|
963
|
+
`skill`, `instruction`, `memory`) one reading kept and withheld, by id and
|
|
964
|
+
digest, never text. `source` is `run`, `supervisor`, or `recall` for a
|
|
965
|
+
recall the model called; an item is withheld only at or above
|
|
966
|
+
`withholdAt`.
|
|
967
|
+
- `RelevanceRestored`: a withheld `flow` the run called by name.
|
|
968
|
+
- `SeatRouted`: the `seat`, `modelId` and prompt `variant` a run that
|
|
969
|
+
`declared` no model was routed to, from the available `candidates`, with
|
|
970
|
+
the `backups` it fails over to and, for a panel route, the `SeatPanel`
|
|
971
|
+
(member seats with their backups, and the `merger`, which is `seat`).
|
|
972
|
+
`decidedBy` is `jev`; rows written before the routing graph may say `only`
|
|
973
|
+
and carry `confidence`.
|
|
974
|
+
- `DecisionUnjudged`: a reading nobody could judge, with `UnjudgedReason`
|
|
975
|
+
(shared with `SupervisorUnjudged`), the `items` it covered, and, on both,
|
|
976
|
+
the `usage` each request paid when the provider metered it before the
|
|
977
|
+
reading failed. Every evaluator `usage` names its `modelId` when known.
|
|
978
|
+
|
|
979
|
+
Fields later writers add to existing events are optional, so older journals
|
|
980
|
+
decode unchanged: `DecisionSettled.usage`; `CompactionSettled.kept`, `marks`
|
|
981
|
+
(`CompactionMark` `keep`, `squash` or `remove`, with the `CompactionPin` that
|
|
982
|
+
overrode a removal, by segment digest), `removedTokens`, `unaligned` and
|
|
983
|
+
`causes` (`CompactionCause` `budget`, `outdated_context` or
|
|
984
|
+
`irrelevant_context`), with `summary` optional; `SupervisorSettled.monitors` (`MonitorKind` `mood`, `skill` or
|
|
985
|
+
`lint`) and `skillsCapped`; `DisciplineArmed.judged`,
|
|
986
|
+
`relevance`, `monitors` and `stance` (`careful` or `paranoid`), with
|
|
987
|
+
`supervisorSteer` optional; `SteeringDrained.monitor`, `suppressed`
|
|
988
|
+
(each with a `Suppression` reason) and `memory`; `SeatRouted.backups` and
|
|
989
|
+
`panel`.
|
|
990
|
+
|
|
991
|
+
Every member's `_tag` is the kebab-case discriminant `switch` and
|
|
992
|
+
`Stream.filter` match on. `AgentEvent.eventType` is keyed by the camelCase
|
|
993
|
+
property name and holds the journal event type, the one table `CellTurn`
|
|
994
|
+
writes and `Transcript` reads:
|
|
995
|
+
|
|
996
|
+
| `_tag` | `eventType` property | Journal event type |
|
|
997
|
+
| ------------------------------- | ----------------------------- | ------------------------------------------------ |
|
|
998
|
+
| `aborted` | `aborted` | `flows.harness.aborted.v1` |
|
|
999
|
+
| `cell-call-settled` | `cellCallSettled` | `flows.harness.cell-call-settled.v1` |
|
|
1000
|
+
| `cell-call-started` | `cellCallStarted` | `flows.harness.cell-call-started.v1` |
|
|
1001
|
+
| `cell-printed` | `cellPrinted` | `flows.harness.cell-printed.v1` |
|
|
1002
|
+
| `cell-produced` | `cellProduced` | `flows.harness.cell-produced.v1` |
|
|
1003
|
+
| `cell-rejected-in-frame` | `cellRejectedInFrame` | `flows.harness.cell-rejected-in-frame.v1` |
|
|
1004
|
+
| `cell-settled` | `cellSettled` | `flows.harness.cell-settled.v1` |
|
|
1005
|
+
| `checkpoint-minted` | `checkpointMinted` | `flows.harness.checkpoint-minted.v1` |
|
|
1006
|
+
| `claim-demanded` | `claimDemanded` | `flows.harness.claim-demanded.v1` |
|
|
1007
|
+
| `compaction-settled` | `compactionSettled` | `flows.harness.compaction-settled.v1` |
|
|
1008
|
+
| `decision-settled` | `decisionSettled` | `flows.harness.decision-settled.v1` |
|
|
1009
|
+
| `decision-unjudged` | `decisionUnjudged` | `flows.harness.decision-unjudged.v1` |
|
|
1010
|
+
| `discipline-armed` | `disciplineArmed` | `flows.harness.discipline-armed.v1` |
|
|
1011
|
+
| `model-delta` | `modelDelta` | `flows.harness.model-delta.v1` |
|
|
1012
|
+
| `model-requested` | `modelRequested` | `flows.harness.model-requested.v1` |
|
|
1013
|
+
| `seat-failed-over` | `seatFailedOver` | `flows.harness.seat-failed-over.v1` |
|
|
1014
|
+
| `model-parked` | `modelParked` | `flows.harness.model-parked.v1` |
|
|
1015
|
+
| `model-unparked` | `modelUnparked` | `flows.harness.model-unparked.v1` |
|
|
1016
|
+
| `model-retried` | `modelRetried` | `flows.harness.model-retried.v1` |
|
|
1017
|
+
| `model-settled` | `modelSettled` | `flows.harness.model-settled.v1` |
|
|
1018
|
+
| `mutation-observed` | `mutationObserved` | `flows.harness.mutation-observed.v1` |
|
|
1019
|
+
| `narrow-only-demanded` | `narrowOnlyDemanded` | `flows.harness.narrow-only-demanded.v1` |
|
|
1020
|
+
| `narrowed-demanded` | `narrowedDemanded` | `flows.harness.narrowed-demanded.v1` |
|
|
1021
|
+
| `permission-required` | `permissionRequired` | `flows.harness.permission-required.v1` |
|
|
1022
|
+
| `read-only-demand-issued` | `readOnlyDemandIssued` | `flows.harness.read-only-demand-issued.v1` |
|
|
1023
|
+
| `read-only-demanded` | `readOnlyDemanded` | `flows.harness.read-only-demanded.v1` |
|
|
1024
|
+
| `relevance-restored` | `relevanceRestored` | `flows.harness.relevance-restored.v1` |
|
|
1025
|
+
| `relevance-settled` | `relevanceSettled` | `flows.harness.relevance-settled.v1` |
|
|
1026
|
+
| `repeat-demanded` | `repeatDemanded` | `flows.harness.repeat-demanded.v1` |
|
|
1027
|
+
| `resolved` | `resolved` | `flows.harness.resolved.v1` |
|
|
1028
|
+
| `seat-routed` | `seatRouted` | `flows.harness.seat-routed.v1` |
|
|
1029
|
+
| `steering-drained` | `steeringDrained` | `flows.harness.steering-drained.v1` |
|
|
1030
|
+
| `sufficiency-observed` | `sufficiencyObserved` | `flows.harness.sufficiency-observed.v1` |
|
|
1031
|
+
| `suspended` | `suspended` | `flows.harness.suspended.v1` |
|
|
1032
|
+
| `transition-applied` | `transitionApplied` | `flows.harness.transition-applied.v1` |
|
|
1033
|
+
| `turn-closed` | `turnClosed` | `flows.harness.turn-closed.v1` |
|
|
1034
|
+
| `turn-opened` | `turnOpened` | `flows.harness.turn-opened.v1` |
|
|
1035
|
+
| `unmoved-demanded` | `unmovedDemanded` | `flows.harness.unmoved-demanded.v1` |
|
|
1036
|
+
| `unresolved-demanded` | `unresolvedDemanded` | `flows.harness.unresolved-demanded.v1` |
|
|
1037
|
+
| `failed-call-demanded` | `failedCallDemanded` | `flows.harness.failed-call-demanded.v1` |
|
|
1038
|
+
| `unobserved-demanded` | `unobservedDemanded` | `flows.harness.unobserved-demanded.v1` |
|
|
1039
|
+
| `output-demanded` | `outputDemanded` | `flows.harness.output-demanded.v1` |
|
|
1040
|
+
| `vacuous-verification-observed` | `vacuousVerificationObserved` | `flows.harness.vacuous-verification-observed.v1` |
|
|
1041
|
+
|
|
1042
|
+
## HarnessError
|
|
1043
|
+
|
|
1044
|
+
`import * as HarnessError from "@smthrs/harness/HarnessError"`
|
|
1045
|
+
|
|
1046
|
+
The translation boundary's own failures.
|
|
1047
|
+
|
|
1048
|
+
```ts
|
|
1049
|
+
export class HarnessError extends Schema.TaggedError<HarnessError>()("/harness/HarnessError", {
|
|
1050
|
+
code: HarnessErrorCode,
|
|
1051
|
+
message: Schema.String,
|
|
1052
|
+
cause: Schema.optional(Schema.Defect())
|
|
1053
|
+
}) {}
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
`HarnessErrorCode` is `assembly_failed`, `approval_unavailable`, `incompatible_journal`,
|
|
1057
|
+
`render_failed`, `model_failed`, `engine_failed`, `read_only_cap`,
|
|
1058
|
+
`completion_unjudged`, `completion_incomplete`, `claim_unproven`, or `suspended`. The set is closed to codes this package and `@smthrs/agent`
|
|
1059
|
+
actually raise, and `test/Contracts.test.ts` pins every member to a
|
|
1060
|
+
construction site; a foreign CLI adapter declares its own family beside the
|
|
1061
|
+
adapter rather than borrowing this one. Interrupting a run raises no
|
|
1062
|
+
`HarnessError`: `CellTurn` emits `AgentEvent.Aborted` and forwards the
|
|
1063
|
+
interrupt cause unchanged. A failed projection is a
|
|
1064
|
+
`Transcript.TranscriptError`, not a `HarnessError`. `cause` is a
|
|
1065
|
+
`Schema.Defect` so a live `Error` attached as cause still encodes to JSON for
|
|
1066
|
+
the durable exit schema. `FlowBinding.make` passes existing `HarnessError`
|
|
1067
|
+
values through unchanged; only permission requirements and denials are
|
|
1068
|
+
converted to code `suspended`. A handler error the binding's `timedOut`
|
|
1069
|
+
classifies becomes a `timeout` call result whose `value` records
|
|
1070
|
+
`{ limitMillis }` when the error says its limit: `bash` and `test` classify a
|
|
1071
|
+
command past its given or default limit, and `agent/await` a wait that gave
|
|
1072
|
+
up. Other handler errors become opaque `flow_failed` call results. Either carries only the safe
|
|
1073
|
+
`publicError` text the binding supplies.
|
|
1074
|
+
|
|
1075
|
+
## StructuredOutput
|
|
1076
|
+
|
|
1077
|
+
`import * as StructuredOutput from "@smthrs/harness/StructuredOutput"`
|
|
1078
|
+
|
|
1079
|
+
Turning one agent's final text into a value the declared output schema
|
|
1080
|
+
accepts, or into a typed failure. A model may answer with bare JSON, prose
|
|
1081
|
+
wrapped around JSON, a fenced block, or JSON of the wrong shape; downstream
|
|
1082
|
+
nodes never receive that ambiguity. Extraction never relaxes validation: it
|
|
1083
|
+
only decides which bytes are offered to the schema.
|
|
1084
|
+
|
|
1085
|
+
```ts
|
|
1086
|
+
export const decode: <S extends Schema.Top>(
|
|
1087
|
+
schema: S,
|
|
1088
|
+
text: string,
|
|
1089
|
+
attempt: { readonly corrections: number; readonly limit: number }
|
|
1090
|
+
) => Effect.Effect<S["Type"], StructuredOutputFailure, S["DecodingServices"]>
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
`decode` tries every `candidates(text)` entry in order, the complete
|
|
1094
|
+
BOM-stripped response first and then the balanced JSON container whose
|
|
1095
|
+
matching close ends last (`lastBalanced`), then the answer itself, untrimmed,
|
|
1096
|
+
as a string unless it is a JSON string literal, and returns the first the
|
|
1097
|
+
schema accepts. The last is how `ctx.done("text")` reaches a string schema.
|
|
1098
|
+
When none does, the `StructuredOutputFailure` reports the issues of
|
|
1099
|
+
the last candidate, with `code` one of `invalid_json`, `schema_mismatch`,
|
|
1100
|
+
`no_candidate`, or `correction_exhausted` once the budget is spent.
|
|
1101
|
+
|
|
1102
|
+
The prompt half is `instructions(schema)`, which renders the declared schema
|
|
1103
|
+
as a JSON Schema document for the run's system teaching and asks for the value
|
|
1104
|
+
itself as the `ctx.done` argument, not a JSON string: the model is told the
|
|
1105
|
+
shape before it answers, and the answer is still validated locally.
|
|
1106
|
+
`digest(schema)` is the schema's canonical digest, `jsonSchema(schema)` its
|
|
1107
|
+
JSON Schema document, `issuesDigest(failure)` the digest of one failure's
|
|
1108
|
+
rendered issues, and `correction(failure)` the teaching appended when a
|
|
1109
|
+
candidate failed to decode. A failure carries at most `maxIssues` (5)
|
|
1110
|
+
`OutputIssue`s (`{ code, path, message }`), so a wide struct that mismatched
|
|
1111
|
+
everywhere cannot spend the correction prompt restating the schema.
|
|
1112
|
+
|
|
1113
|
+
## ContextWindow
|
|
1114
|
+
|
|
1115
|
+
`import * as ContextWindow from "@smthrs/harness/ContextWindow"`
|
|
1116
|
+
|
|
1117
|
+
The immutable, provider-neutral context assembled for one model request. Every
|
|
1118
|
+
value it exposes is frozen, the arrays, the segments, and the messages, parts,
|
|
1119
|
+
and tool declarations they hold, so a runtime mutation throws in strict mode
|
|
1120
|
+
instead of silently invalidating the digest computed once at construction.
|
|
1121
|
+
|
|
1122
|
+
```ts
|
|
1123
|
+
export const make: (options: MakeOptions) => ContextWindow
|
|
1124
|
+
export const empty: (modelId: string) => ContextWindow
|
|
1125
|
+
export const makeSegment: (input: SegmentInput) => Segment
|
|
1126
|
+
export const render: (self: ContextWindow) => ModelRequest.ModelRequest
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
`MakeOptions` is `{ modelId, segments?, activeTools?, replaced? }`. A
|
|
1130
|
+
`Segment` is a stable, typed slice with a `SegmentKind`, a `SegmentZone`
|
|
1131
|
+
(prefix or tail of the cache breakpoint), its content parts, a digest, and an
|
|
1132
|
+
estimated token count computed once at construction. `appendTurn` appends one
|
|
1133
|
+
settled assistant message; `prefixDigest`, `compactPrefix`, and
|
|
1134
|
+
`compact` replace an exact compactable prefix while retaining every suffix
|
|
1135
|
+
segment, failing with a `ContextWindowError` when the declared prefix does not
|
|
1136
|
+
match. `compactMarked(window, prefixLength, marks, summary)` applies one
|
|
1137
|
+
`Mark` per prefix segment, `keep`, `squash` or `remove`: the result holds the
|
|
1138
|
+
segments before the prefix, one summary segment when any segment is squashed,
|
|
1139
|
+
the kept segments as the same objects, then the suffix. The summary is required
|
|
1140
|
+
exactly when a segment is squashed. `compactPrefix` is `compactMarked` with
|
|
1141
|
+
every segment squashed. `render` projects the window into the `ModelRequest` of
|
|
1142
|
+
[`@smthrs/model`](/api/model).
|
|
1143
|
+
|
|
1144
|
+
`appendTurn` and `compact` are dual, so each one takes the window first or
|
|
1145
|
+
returns a function of it, and a `ContextWindow` is `Pipeable`. The direct and
|
|
1146
|
+
the piped forms are the same call:
|
|
1147
|
+
|
|
1148
|
+
```ts
|
|
1149
|
+
const request = ContextWindow.render(ContextWindow.appendTurn(window, assistant))
|
|
1150
|
+
|
|
1151
|
+
const piped = window.pipe(
|
|
1152
|
+
ContextWindow.appendTurn(assistant),
|
|
1153
|
+
ContextWindow.render
|
|
1154
|
+
)
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
`ModelCatalog.contextWindowTokensFor(modelId)` of
|
|
1158
|
+
[`@smthrs/model`](/api/model) supplies the shared context-limit catalog used by
|
|
1159
|
+
seat resolution and seat steering (128,000 tokens for unknown models). A
|
|
1160
|
+
thinking-only steer preserves the current budget.
|
|
1161
|
+
|
|
1162
|
+
## Tokens
|
|
1163
|
+
|
|
1164
|
+
`import * as Tokens from "@smthrs/harness/Tokens"`
|
|
1165
|
+
|
|
1166
|
+
Deterministic token accounting for context windows. `Tokens.estimate`
|
|
1167
|
+
approximates four characters per token, with code punctuation and newline
|
|
1168
|
+
density accounting for the shorter tokens of source text; it is a
|
|
1169
|
+
deterministic local approximation, not provider billing data.
|
|
1170
|
+
`Tokens.count(text, estimator?)` counts one text (defaulting to `estimate`)
|
|
1171
|
+
into a `Tokens.Count` (`{ value, estimated }`), and `Tokens.combine` sums
|
|
1172
|
+
per-segment `Tokens.Segment` counts into one `Tokens.Accounting`, split by
|
|
1173
|
+
cache zone.
|
|
1174
|
+
|
|
1175
|
+
## Transcript
|
|
1176
|
+
|
|
1177
|
+
`import * as Transcript from "@smthrs/harness/Transcript"`
|
|
1178
|
+
|
|
1179
|
+
Transcript projection from durable journal entries. The transcript grows:
|
|
1180
|
+
what the model saw is what it said plus what the harness answered, in journal
|
|
1181
|
+
order. `Transcript.projectResult` projects model-visible messages in canonical
|
|
1182
|
+
journal sequence order; `Transcript.projectStateResult` projects the same
|
|
1183
|
+
events into typed state (`ProjectedState`, with the compaction replacement
|
|
1184
|
+
identity when one was recorded), preserving malformed-payload failures as
|
|
1185
|
+
typed `TranscriptError`s instead of throwing. `CellEvidence` is the
|
|
1186
|
+
schema-decoded cell evidence the rebuild consumes. `CompactionSettled.retainedMessageCount`
|
|
1187
|
+
records the number of messages in the live retained suffix, across all retained
|
|
1188
|
+
segments. Projection applies these boundaries in journal order, replacing only
|
|
1189
|
+
the preceding prefix with the summary. Older events without the field replace
|
|
1190
|
+
all messages preceding the event.
|
|
1191
|
+
|
|
1192
|
+
## ExternalTranscript
|
|
1193
|
+
|
|
1194
|
+
`import * as ExternalTranscript from "@smthrs/harness/ExternalTranscript"`
|
|
1195
|
+
|
|
1196
|
+
Read-only conversation entries decoded from the transcript file of an agent a
|
|
1197
|
+
person runs beside Smithers. Two formats decode: `decodeCodex` reads the
|
|
1198
|
+
`rollout-*.jsonl` file Codex CLI writes under `$CODEX_HOME/sessions`, and
|
|
1199
|
+
`decodeClaude` reads the `<session>.jsonl` file Claude Code writes under
|
|
1200
|
+
`~/.claude/projects/<project>`. Decoding is pure. It performs no I/O, reads no
|
|
1201
|
+
clock, needs no provider and registers no importer; the host reads the file and
|
|
1202
|
+
keeps the state.
|
|
1203
|
+
|
|
1204
|
+
Every `Entry` carries `origin: "external"`, `agent_kind` (`codex` or
|
|
1205
|
+
`claude-code`), `format_version`, `session_id`, `source_id`
|
|
1206
|
+
(`<session>:<line>`), `read_only: true`, `seq` (entry order from 0), `at` (the
|
|
1207
|
+
record's time in epoch milliseconds, 0 when it has none), an optional
|
|
1208
|
+
`turn_id`, `role` and one `Part`. Neither format has a schema version of its
|
|
1209
|
+
own, so `format_version` is an adapter profile named by the CLI release:
|
|
1210
|
+
`codex-rollout/<major.minor>` from `session_meta`, or `claude-code/<major.minor>`
|
|
1211
|
+
from the `version` on each Claude Code record. `role` is `user` for the owner's
|
|
1212
|
+
prompts and goals and `assistant` for everything else, by record kind;
|
|
1213
|
+
transcript text never sets it, and who the owner and the agent participant are
|
|
1214
|
+
is the caller's to supply.
|
|
1215
|
+
|
|
1216
|
+
A `Part` is a `prompt`, a `goal`, `text` (`final` on the turn's answer),
|
|
1217
|
+
`reasoning`, `encrypted` (a placeholder for a message body Codex encrypted,
|
|
1218
|
+
never dropped), `tool` (call id, command, `reads` labels such as `Read mvp.md`
|
|
1219
|
+
when the call only reads, searches or lists, status, exit code, output and
|
|
1220
|
+
duration), `edit` (call id, each `EditedFile` with its unified diff, and
|
|
1221
|
+
`applied` or `failed`), `search`, `helper`, `compaction`, or `error` for a
|
|
1222
|
+
failure the agent reported.
|
|
1223
|
+
|
|
1224
|
+
Every kind a decoder skips is named in its source. A record, event, item,
|
|
1225
|
+
status line, attachment or content block outside those names is refused with
|
|
1226
|
+
`unsupported_record`: a new kind stops the import where a person can see it
|
|
1227
|
+
and is never shown as something else. The mapping tables, the recorded
|
|
1228
|
+
captures behind them and the host caller are in
|
|
1229
|
+
[external-transcripts.md](external-transcripts.md).
|
|
1230
|
+
|
|
1231
|
+
### Codex
|
|
1232
|
+
|
|
1233
|
+
Entries come from `event_msg` rows: `item_completed` items,
|
|
1234
|
+
`thread_goal_updated` rows whose objective or status changed, a
|
|
1235
|
+
`task_complete` that carries the failure its turn ended in, and
|
|
1236
|
+
`turn_aborted`. `reasoning` is only a summary Codex chose to write.
|
|
1237
|
+
`response_item` rows repeat the items for the model and are skipped, except
|
|
1238
|
+
two the items leave out. An `agent_message` is a message between this agent
|
|
1239
|
+
and one it works with: a body with ciphertext is one `encrypted` placeholder
|
|
1240
|
+
that stands for the whole body, and a readable body is `text`. A
|
|
1241
|
+
`custom_tool_call_output` that reports a failed code-mode script is a failed
|
|
1242
|
+
`tool` whose command is the script its `custom_tool_call` requested, followed
|
|
1243
|
+
by a failed `edit` when the report names the file an `apply_patch` could not
|
|
1244
|
+
change. The request waits in `state.calls` until its output arrives.
|
|
1245
|
+
|
|
1246
|
+
### Claude Code
|
|
1247
|
+
|
|
1248
|
+
Claude Code chains its conversation records (`user`, `assistant`, `system`,
|
|
1249
|
+
`attachment`) by `uuid`. Thirteen named bookkeeping rows (`mode`,
|
|
1250
|
+
`permission-mode`, `queue-operation`, `last-prompt`, `ai-title`,
|
|
1251
|
+
`file-history-snapshot`, `cost-state` and the like) are skipped, as are
|
|
1252
|
+
`isSidechain` rows, which belong to a subagent's own transcript. The session
|
|
1253
|
+
is the `sessionId` of the first conversation record, and `turn_id` is the
|
|
1254
|
+
`promptId` of the latest user record.
|
|
1255
|
+
|
|
1256
|
+
| Record | Part |
|
|
1257
|
+
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
1258
|
+
| `user` text the owner wrote (`origin` `human` or none) | `prompt`; a slash command reads as `/<name> <args>` and a shell command as `!<command>` |
|
|
1259
|
+
| `user` `[Request interrupted by user…]` | `error` with that text |
|
|
1260
|
+
| `user` `isMeta`, `isCompactSummary`, other origins, local command output | Skipped: skill bodies, caveats, summaries, task notifications, peer messages, `<local-command-stdout>` |
|
|
1261
|
+
| `assistant` `text` | `text`, `final` when the message's `stop_reason` is `end_turn` |
|
|
1262
|
+
| `assistant` `thinking` | `reasoning` when it has a body; empty and `redacted_thinking` blocks are skipped |
|
|
1263
|
+
| `assistant` with `isApiErrorMessage` | `error` with Claude Code's message |
|
|
1264
|
+
| `tool_use` `Bash`, then its `tool_result` | `tool` with the command, `ok` or `error`, the exit code Claude Code reports as `Exit code N`, output and duration |
|
|
1265
|
+
| `Read`, `Grep`, `Glob`, `LS` | `tool` with a `reads` label: `Read a.ts`, `Searched "q" in src`, `Listed apps` |
|
|
1266
|
+
| `Edit`, `Write`, `MultiEdit`, `NotebookEdit` | `edit`: Claude Code's reported hunks, or the requested change when none; `failed` when the result is an error |
|
|
1267
|
+
| `WebSearch`, `WebFetch` | `search` for the query or address |
|
|
1268
|
+
| `Agent`, `Task` | `helper` named by `subagent_type`, with the call's description |
|
|
1269
|
+
| any other tool | `tool` whose command is the tool's name and its input as JSON |
|
|
1270
|
+
| `system` `compact_boundary` | `compaction`; nine named status subtypes are skipped |
|
|
1271
|
+
| `attachment` `queued_command` the owner typed while the agent worked | `prompt`; 44 named context attachments are skipped |
|
|
1272
|
+
| a record, status line, attachment or content block of any other kind | Refused: `unsupported_record` naming it |
|
|
1273
|
+
|
|
1274
|
+
A tool call waits in `state.calls` from its `tool_use` record until the
|
|
1275
|
+
`tool_result` with the same id arrives, and becomes one entry at the result's
|
|
1276
|
+
line, so two calls issued together appear in the order their results arrive.
|
|
1277
|
+
A call whose result never arrives emits nothing. A `Write` is `added` when
|
|
1278
|
+
Claude Code reports it created the file; a requested replacement Claude Code
|
|
1279
|
+
did not apply has no known position, so its hunk starts at line 1. Entries
|
|
1280
|
+
follow file order: when the owner edits and resends a prompt, Claude Code
|
|
1281
|
+
writes the new branch after the old one and both appear. A record that yields
|
|
1282
|
+
several entries numbers the later ones `<session>:<line>#<n>`.
|
|
1283
|
+
|
|
1284
|
+
### Decoding
|
|
1285
|
+
|
|
1286
|
+
`decodeCodex(state, chunk)` and `decodeClaude(state, chunk)` return
|
|
1287
|
+
`Result<Decoded<State>, ExternalTranscriptError>`: the entries the chunk
|
|
1288
|
+
completed and the `CodexState` or `ClaudeState` to pass with the next chunk.
|
|
1289
|
+
Start from `codexStart` or `claudeStart`. Any partition of the same text
|
|
1290
|
+
yields the same entries, and a last line without its newline waits in
|
|
1291
|
+
`state.pending` until it completes. The state is plain JSON, held calls
|
|
1292
|
+
included, so a host can persist it and resume tailing. Chunks are strings; a
|
|
1293
|
+
host reading bytes decodes them with a streaming `TextDecoder` first.
|
|
1294
|
+
|
|
1295
|
+
`ExternalTranscriptError.code` is one of:
|
|
1296
|
+
|
|
1297
|
+
- `missing_version`: a Codex row comes before the `session_meta` row, or a
|
|
1298
|
+
Claude Code conversation record carries no `version`.
|
|
1299
|
+
- `unsupported_version`: the release is missing (Codex) or outside
|
|
1300
|
+
`codexReleases` (`0.159`, `0.160`) or `claudeReleases` (`2.1`).
|
|
1301
|
+
- `unsupported_record`: a complete, well-formed record, event, item, status
|
|
1302
|
+
line, attachment or content block of a kind this module does not name. The
|
|
1303
|
+
message names the kind.
|
|
1304
|
+
- `malformed_record`: a complete line is not a JSON record; a record names no
|
|
1305
|
+
type; a conversation record has no `uuid`, session or message content; a
|
|
1306
|
+
message part has no text; a tool request names no call.
|
|
1307
|
+
|
|
1308
|
+
Each error carries the 1-based `line` the decode stopped at, and a failed
|
|
1309
|
+
decode returns no entries from its chunk: a host that must keep the entries
|
|
1310
|
+
before the refused line decodes one record at a time. `Fault` classifies all
|
|
1311
|
+
four as `dependency`: the agent wrote a transcript this release cannot read.
|
|
1312
|
+
|
|
1313
|
+
## Compaction
|
|
1314
|
+
|
|
1315
|
+
`import * as Compaction from "@smthrs/harness/Compaction"`
|
|
1316
|
+
|
|
1317
|
+
Declarations for sealed transcript-summary steps.
|
|
1318
|
+
`shouldCompact(accounting, { reserve?, keepRecent? }?)` returns whether the
|
|
1319
|
+
model context crossed its reserved threshold; `selectPrefix(window, { keepRecent? }?)`
|
|
1320
|
+
selects the
|
|
1321
|
+
longest compactable prefix while preserving a whole recent suffix; `declare`
|
|
1322
|
+
builds the sealed `CompactionStep` for one prefix and `Summarizer` without
|
|
1323
|
+
invoking a model; `summaryRequest` builds the model request input for the
|
|
1324
|
+
step, with `summaryInstruction` as its stable instruction; and `apply`
|
|
1325
|
+
splices a recorded summary into a projected window, failing with
|
|
1326
|
+
`InvalidStep` when the declaration does not match the window it is applied
|
|
1327
|
+
to. `declare(window, prefixLength, summarizer, marks?)` takes one
|
|
1328
|
+
`ContextWindow.Mark` per prefix segment and records them with a `marksDigest`;
|
|
1329
|
+
without marks every segment is squashed. `summaryRequest` carries only the
|
|
1330
|
+
squashed segments' messages and fails when nothing is squashed, and
|
|
1331
|
+
`apply(window, step, summary?)` requires a summary exactly when a segment is
|
|
1332
|
+
squashed. A length or digest mismatch is `InvalidStep`. `Summarizer.params` is an optional `ModelRequest.GenerationParams`.
|
|
1333
|
+
`summaryRequest` schema-decodes supplied parameters, including JSON-round-tripped
|
|
1334
|
+
values, and returns `InvalidStep` for invalid values or unknown parameter keys.
|
|
1335
|
+
Defaults apply only when `params` is absent.
|
|
1336
|
+
|
|
1337
|
+
## Plan
|
|
1338
|
+
|
|
1339
|
+
`import * as Plan from "@smthrs/harness/Plan"`
|
|
1340
|
+
|
|
1341
|
+
Local structural plan nodes used at the harness-to-engine boundary. `Child`
|
|
1342
|
+
is one flow invocation elaborated from a model tool call; `Batch` is the
|
|
1343
|
+
children passed through to the engine, in source order; `ChildResult`,
|
|
1344
|
+
`ChildProgress`, and `ChildSettled` carry outcomes back; and `SpliceEvent` is
|
|
1345
|
+
the streaming union `EngineLike.splice` emits. Source order is retained only
|
|
1346
|
+
for result correlation; graph dependencies are the sole sequencing signal.
|
|
1347
|
+
|
|
1348
|
+
## CallLedger
|
|
1349
|
+
|
|
1350
|
+
`import * as CallLedger from "@smthrs/harness/CallLedger"`
|
|
1351
|
+
|
|
1352
|
+
The call ledger: what this run has already asked, rendered every frame. Every
|
|
1353
|
+
settled call contributes one line the harness derives on its own, ordinal,
|
|
1354
|
+
flow, what the call was about, whether it settled ok, and a structural digest
|
|
1355
|
+
of what came back, so a model sees what it already asked without asking
|
|
1356
|
+
again. A line carries no payloads: it says `stdout=4096b`, never the bytes.
|
|
1357
|
+
A call that writes also says so, names its byte count, and names an earlier
|
|
1358
|
+
identical write when one settled. `entry` records one settled call,
|
|
1359
|
+
`remember` folds a frame's calls into the run's `Ledger` bounded to `bound`
|
|
1360
|
+
(30, newest last), `settled` counts through aged-out lines, and `render`
|
|
1361
|
+
renders the ledger for the state section, only the calls after `since` when an
|
|
1362
|
+
earlier section in the window already listed the rest. Line fields clip to `width` (120),
|
|
1363
|
+
and a result digest names at most `members` (6) members.
|
|
1364
|
+
|
|
1365
|
+
## NarrowedCheck
|
|
1366
|
+
|
|
1367
|
+
`import * as NarrowedCheck from "@smthrs/harness/NarrowedCheck"`
|
|
1368
|
+
|
|
1369
|
+
The narrowing ledger: which checks this run has run, and over which tree. A
|
|
1370
|
+
check is only evidence for the tree it ran over, and a completion whose last
|
|
1371
|
+
check is a narrowed version of one the run already ran in full, taken after
|
|
1372
|
+
the workspace moved, reports an unknown as proven. `check` records one
|
|
1373
|
+
settled call as a `Check` unless its input is a payload; `narrows` decides
|
|
1374
|
+
whether one call's terms strictly narrow another's; `find` finds the broadest
|
|
1375
|
+
check a completing frame narrowed and did not re-run, and `demand` states the
|
|
1376
|
+
sentence; `findOnly` and `demandOnly` cover the narrow-only shape, where no
|
|
1377
|
+
broader reading exists at all. `remember` folds a frame's checks into the
|
|
1378
|
+
run's `Ledger`, bounded to `retained` (32) distinct checks and `maxTerms`
|
|
1379
|
+
(256) terms each.
|
|
1380
|
+
|
|
1381
|
+
## CellValidation
|
|
1382
|
+
|
|
1383
|
+
`import * as CellValidation from "@smthrs/harness/CellValidation"`
|
|
1384
|
+
|
|
1385
|
+
Cell validation at the boundary. `validate(cell)` parses one cell and reports
|
|
1386
|
+
everything the parse can decide, a `Validation`: module syntax, non-erasable
|
|
1387
|
+
TypeScript, syntax errors with their line, and the compiled text when it
|
|
1388
|
+
parses. The controller answers a cell that does not parse inside the same
|
|
1389
|
+
frame, at cached-prefix price, instead of ending the frame on it.
|
|
1390
|
+
`normalize(compiled)` rewrites a cell's top-level declarations so the
|
|
1391
|
+
persistent realm behaves like a notebook: a name declared again rebinds
|
|
1392
|
+
instead of dying on redeclaration. Nothing here executes anything, and the
|
|
1393
|
+
only outcome is a rejection the model is asked to fix in this frame.
|
|
1394
|
+
|
|
1395
|
+
## UnmovedTree
|
|
1396
|
+
|
|
1397
|
+
`import * as UnmovedTree from "@smthrs/harness/UnmovedTree"`
|
|
1398
|
+
|
|
1399
|
+
The completion with nothing behind it. `find` compares the digest of the tree
|
|
1400
|
+
the run opened on against the digest the completing frame closed on; equal
|
|
1401
|
+
digests mean the tree the completion describes is the tree the run was handed.
|
|
1402
|
+
`demand` states that the tree never moved and names the two answers that end
|
|
1403
|
+
it: make the change, or say why no change is needed. Nothing here reads the
|
|
1404
|
+
completion's text, and "no change is needed" is a legitimate answer.
|
|
1405
|
+
|
|
1406
|
+
## FailedCall
|
|
1407
|
+
|
|
1408
|
+
`import * as FailedCall from "@smthrs/harness/FailedCall"`
|
|
1409
|
+
|
|
1410
|
+
The completion its own cell wrote before a call in it failed. A cell is
|
|
1411
|
+
written whole before any call in it settles, and a failed call resolves
|
|
1412
|
+
`{ ok: false }` rather than throwing, so a `ctx.done` after a failed request
|
|
1413
|
+
still runs. When a completing frame settled a failed call and the cell never
|
|
1414
|
+
reads a call envelope's `ok` or `error` (`inspects`), `find` names the
|
|
1415
|
+
failures, and the controller hands the frame back once (`cap`) with `demand`,
|
|
1416
|
+
journaled as `FailedCallDemanded`. A completion that already quotes the
|
|
1417
|
+
flow's failure reason is spared. With no frame left or the demand spent, the
|
|
1418
|
+
answer stands with the failures appended by `state` under `stated`. Nothing
|
|
1419
|
+
here asks a model or reads what the claim means.
|
|
1420
|
+
|
|
1421
|
+
The same blindness without a failure is handed back too. Several `cell`
|
|
1422
|
+
blocks in one reply run as one program, so a reply that makes a call, keeps
|
|
1423
|
+
its result only for the model (printed or bound, read by nothing but
|
|
1424
|
+
`console.*`), and calls `ctx.done` completes before the model has seen that
|
|
1425
|
+
result. The controller refuses that completion once per run, journals
|
|
1426
|
+
`UnobservedDemanded` with each call's flow, ordinal and result head, and
|
|
1427
|
+
continues the frame: the next model turn reads what the cell printed, the
|
|
1428
|
+
call results, and "Answer after reading the calls' output". A cell whose
|
|
1429
|
+
program reads a result in a condition, in another call's input, or in the
|
|
1430
|
+
completion itself (`if (after.exitCode === 0) ctx.done(…)`,
|
|
1431
|
+
`ctx.done((await ctx.call(…)).stdout)`), a cell that hands a result to
|
|
1432
|
+
anything the parse cannot see through (a function of its own, a `.then`
|
|
1433
|
+
callback, an alias), and a cell whose calls are effects with discarded
|
|
1434
|
+
results, are spared. `FailedCallDemanded` takes precedence, and with no frame
|
|
1435
|
+
left the answer stands; a refused answer is also what the run ends on when
|
|
1436
|
+
the budget runs out before it completes again.
|
|
1437
|
+
|
|
1438
|
+
`Input.output` (`OutputCheck`, `{ check, cap }`) is the host's shape for the
|
|
1439
|
+
final output. Before any demand above, `check(output, corrected)` answers a
|
|
1440
|
+
completion with a correction note or `undefined`; the verdict is a recorded
|
|
1441
|
+
boundary. A refused completion is handed back to the same session, with what
|
|
1442
|
+
its cell printed and the note, and journaled as `OutputDemanded`
|
|
1443
|
+
(`{ note, nextFrame }`). The realm and every settled call stay, so the
|
|
1444
|
+
correction repeats no work. At most `cap` completions are handed back, and
|
|
1445
|
+
only while a frame is left; past either the completion stands and the host
|
|
1446
|
+
decides. `AgentAction` supplies it from the declared output schema.
|
|
1447
|
+
|
|
1448
|
+
## UnresolvedFailure
|
|
1449
|
+
|
|
1450
|
+
`import * as UnresolvedFailure from "@smthrs/harness/UnresolvedFailure"`
|
|
1451
|
+
|
|
1452
|
+
The failing check a completion stepped around. `exitStatus` reads a settled
|
|
1453
|
+
call's `exitStatusKey` (`exitCode`), and `failed` and `passed` read it for a
|
|
1454
|
+
failing or passing status;
|
|
1455
|
+
`revisits` decides whether a later check asks about the same subject as an
|
|
1456
|
+
earlier one; `find` finds the failing check a completion replaced rather than
|
|
1457
|
+
answered; and `demand` states which reading failed, which one replaced it,
|
|
1458
|
+
and what ends it. A failing check alone is not the trigger: the run itself
|
|
1459
|
+
must have demonstrated the subject was still live by returning to it.
|
|
1460
|
+
|
|
1461
|
+
## CompletionClaim
|
|
1462
|
+
|
|
1463
|
+
`import * as CompletionClaim from "@smthrs/harness/CompletionClaim"`
|
|
1464
|
+
|
|
1465
|
+
The sixth brake on a completion, and the only one that is not a measurement.
|
|
1466
|
+
It declares one classifier, `completion/claim`, over the facts the harness
|
|
1467
|
+
already holds: the task as the person stated it, the completion message,
|
|
1468
|
+
whether the tree moved, every check the run took in its workspace as
|
|
1469
|
+
`Evidence.checksRun` (command and outcome, newest
|
|
1470
|
+
`checksRunLimit` (24) kept), the last check the completing frame ran with
|
|
1471
|
+
its result clipped to the newest `outputBytes` (4 KiB), and `Evidence.callsRun`.
|
|
1472
|
+
The latter lists the newest 30 settled calls from the durable call ledger,
|
|
1473
|
+
including classification and reads that report no exit status. Each receipt
|
|
1474
|
+
names the flow, bounded input subject, settlement status and structural result
|
|
1475
|
+
summary. It records that a result was obtained without carrying its full
|
|
1476
|
+
payload. Direct callers may omit this field for compatibility; the controller
|
|
1477
|
+
always supplies it. It asks Jev three
|
|
1478
|
+
questions through the `Evaluator` service. Does the completion satisfy the
|
|
1479
|
+
person's current request, does the claim assert something the evidence does not show,
|
|
1480
|
+
and does the claim report having run a command, or having obtained a result,
|
|
1481
|
+
that the evidence does not record? `read` asks and returns all three
|
|
1482
|
+
probabilities with the latency; it returns nothing only where there is no task
|
|
1483
|
+
and no claim to form a question about, and fails with `completion_unjudged`
|
|
1484
|
+
for every other reason it could not get an answer. The reading also carries
|
|
1485
|
+
`asked`: the encoded evidence exactly as the transport was sent it, and every
|
|
1486
|
+
answer as an `AgentEvent.DecisionAnswer`. The controller journals that as
|
|
1487
|
+
`AgentEvent.DecisionSettled`; a host reader that returns no `asked` journals
|
|
1488
|
+
the reading and no decision.
|
|
1489
|
+
|
|
1490
|
+
The completeness question treats labeled prior conversation as context and
|
|
1491
|
+
judges the newest request after `The person now says:`. A purely conversational
|
|
1492
|
+
request can be satisfied by the answer itself, without a file edit, flow call
|
|
1493
|
+
or check. A soft demand uses neutral completion-review guidance: answer the
|
|
1494
|
+
current request in its requested format, and support only the actions and
|
|
1495
|
+
results actually claimed. It does not allege invented work merely because
|
|
1496
|
+
the completeness reading was low.
|
|
1497
|
+
|
|
1498
|
+
The controller bounds original task evidence to 8 KiB, keeping both ends so
|
|
1499
|
+
a long history or task cannot silently discard the newest request at the end.
|
|
1500
|
+
|
|
1501
|
+
`CellTurn` includes later accepted instructions in the task evidence, in
|
|
1502
|
+
admission order, with later changes taking precedence. It labels this block
|
|
1503
|
+
with the same `The person now says:` marker used to identify the newest
|
|
1504
|
+
request, so the original request's marker does not continue to select a
|
|
1505
|
+
superseded instruction. It keeps the newest
|
|
1506
|
+
4 KiB of steering separately from tool output and conversation summaries, so
|
|
1507
|
+
compacting a transcript cannot erase the recorded change of task. Replay
|
|
1508
|
+
rebuilds this bounded evidence from recorded steering drains; a pending,
|
|
1509
|
+
undelivered instruction cannot change a replayed completion.
|
|
1510
|
+
|
|
1511
|
+
`CellTurn` records the whole completion decision at an engine boundary keyed
|
|
1512
|
+
by the session, frame and cell digest. Replay uses that decision, including
|
|
1513
|
+
the classifier reading and usage, so a resumed turn keeps its previous
|
|
1514
|
+
decision without asking Jev again. Provider refusals retain their code, HTTP
|
|
1515
|
+
status when present and detail through the journal's error schema.
|
|
1516
|
+
|
|
1517
|
+
**All three ask; only the third refuses.** `find` hands the completion back at
|
|
1518
|
+
any of three heights, `disprovenAt` (0.3) or below on the first question,
|
|
1519
|
+
`overclaimedAt` (0.8) or above on the second, `unsupportedAt` (0.5) or above on
|
|
1520
|
+
the third, and `demand` renders the prose the next frame reads. `unrecorded`
|
|
1521
|
+
says whether a reading is one the brake ends a run over, which is `inventedAt`
|
|
1522
|
+
(0.85) or above on the third question and nothing else.
|
|
1523
|
+
|
|
1524
|
+
The split was measured using the original completeness wording, whose positive
|
|
1525
|
+
criteria named only workspace changes and checks. A corpus of eighteen completion states over the live
|
|
1526
|
+
gate's own planted repository, asked six times each on 2026-09-19, found the
|
|
1527
|
+
first two questions unable to separate a lie from an honest answer: "is the
|
|
1528
|
+
task done" at or below 0.3 fired on eight of the twelve honest completions and
|
|
1529
|
+
two of the six lies, and an honest unchecked fix read 0.10 on it against a flat
|
|
1530
|
+
lie's 0.22; "asserts more than the evidence shows" fired on five of the twelve
|
|
1531
|
+
honest ones, because thin evidence shows nothing by construction. The narrow
|
|
1532
|
+
question refused none of the twelve honest completions and ended four of the
|
|
1533
|
+
six lies, with the highest honest reading at 0.75 and the lowest ended lie at
|
|
1534
|
+
0.94; `inventedAt` sits between them. All three still bounce, because a bounce
|
|
1535
|
+
costs a frame rather than an answer and is sometimes the only thing in this
|
|
1536
|
+
package with anything to say about a completion. The verdict is narrow for the
|
|
1537
|
+
mirror reason: a false refusal costs a run its answer, while a false pass costs
|
|
1538
|
+
nothing here, because the five deterministic brakes still ran and the truth bar
|
|
1539
|
+
still judges what they let through. The module's own header carries the corpus
|
|
1540
|
+
and the two lies that still get through.
|
|
1541
|
+
|
|
1542
|
+
**A long claim is read one sentence at a time.** When the whole claim reads at
|
|
1543
|
+
`unsupportedAt` or above on the third question and has more than one sentence,
|
|
1544
|
+
`read` asks `sentenceClassifier` the same question of each sentence in one
|
|
1545
|
+
request, and the claim's `invented` is its most invented sentence. Two graded
|
|
1546
|
+
runs of 2026-09-26 whose container work was all on the record read 0.91 and
|
|
1547
|
+
0.87 as whole claims and at most 0.71 and 0.73 by sentence; a fabricated
|
|
1548
|
+
sentence appended to each read 0.94 and 0.92. The sentence reading journals
|
|
1549
|
+
its own `decision-settled`, and a transport failure on it fails the turn as
|
|
1550
|
+
`completion_unjudged` like the first. Each listed check in
|
|
1551
|
+
`Evidence.checksRun` carries its `receipt`, the tail of every string it
|
|
1552
|
+
returned, so a sentence quoting what a check printed has something to be read
|
|
1553
|
+
against. `record` builds that list from every call that reported an exit
|
|
1554
|
+
status, writes included, and keeps what a re-run command reported `before` when
|
|
1555
|
+
it changed, so "failed before the fix and passed after" is on the record. A
|
|
1556
|
+
sentence never ends inside inline code.
|
|
1557
|
+
|
|
1558
|
+
It never passes a claim on its own. A confident "complete" ends no run,
|
|
1559
|
+
bypasses no other demand, and does nothing but write a journal line;
|
|
1560
|
+
`judgeCompletion` consults it only once the five deterministic demands have
|
|
1561
|
+
found nothing.
|
|
1562
|
+
|
|
1563
|
+
**It never goes quiet either.** Every completion with a claim is read, and
|
|
1564
|
+
`claimCap` (3) is the number of frames the run is _given_ to answer one, not
|
|
1565
|
+
the number of completions that are read. The first such claim is handed
|
|
1566
|
+
back with the demand prose, like any other completion demand; a claim with no
|
|
1567
|
+
bounce left, because the cap is spent or because no frame remains to hand it
|
|
1568
|
+
back to, and reading at `inventedAt` or above, fails the turn as
|
|
1569
|
+
`claim_unproven` through `unproven`, carrying all three probabilities. One
|
|
1570
|
+
below `inventedAt` stands there instead: it was handed back once, the run
|
|
1571
|
+
answered, and refusing the answer as well is what destroyed honest runs. A
|
|
1572
|
+
claim demand clears `State.bouncedCompletion` only when the reading is one the
|
|
1573
|
+
brake would refuse, so the frame-budget notice cannot restore a sentence the
|
|
1574
|
+
brake refused and does restore one it would have let stand. The cap stopped at
|
|
1575
|
+
the bounce until this, and the second claim stood unread: on a real seat a run
|
|
1576
|
+
bounced at frame 1 re-claimed the identical sentence and finished `stop` on it
|
|
1577
|
+
over a repository whose test exits 1. Zero disarms the brake outright: no
|
|
1578
|
+
request, no reading, no failure.
|
|
1579
|
+
|
|
1580
|
+
**It never falls back.** A completion Jev could not judge fails the turn as
|
|
1581
|
+
`completion_unjudged`, carrying the reason: `unconfigured` where the host
|
|
1582
|
+
delivered no transport, and the evaluator's own `unreachable`, `refused`,
|
|
1583
|
+
`timeout`, `empty`, `invalid_answer` or `invalid_question` otherwise. So
|
|
1584
|
+
`Evaluator.Evaluator` is a **required service** of `read`, of
|
|
1585
|
+
`judgeCompletion`, of `CellTurn.run` and of `Agent.run` above them, and every
|
|
1586
|
+
host binds a real or deliberately scripted judge at composition time.
|
|
1587
|
+
`Evaluator.layerFromSeat(seat)` judges with a resolved subscription seat and
|
|
1588
|
+
needs no provider API key or gateway key. A judge that fails during a run
|
|
1589
|
+
produces `completion_unjudged`.
|
|
1590
|
+
|
|
1591
|
+
Offline hosts bind `Evaluator.layerScripted` and compute answers from the actual
|
|
1592
|
+
evidence, dispatched by question id. One evaluator serves every classifier in
|
|
1593
|
+
a composition. See the [whole-host scripted judge](https://github.com/smithersai/smithers/blob/main/flows/test/fixtures/scripted-judge.ts).
|
|
1594
|
+
Constant approval is not a judge. `layerUnavailable()` is an explicit outage
|
|
1595
|
+
fixture for classifier tests. The five deterministic brakes still run unchanged.
|
|
1596
|
+
|
|
1597
|
+
## Judgement
|
|
1598
|
+
|
|
1599
|
+
`import * as Judgement from "@smthrs/harness/Judgement"`
|
|
1600
|
+
|
|
1601
|
+
The one way this package asks Jev a question. Every reading goes through it:
|
|
1602
|
+
the supervisor's and the completion brake's, and every relevance, compaction,
|
|
1603
|
+
monitor and routing reading.
|
|
1604
|
+
|
|
1605
|
+
`measured(classifier, state)` asks through the bound `Evaluator` and returns a
|
|
1606
|
+
`Read`: the decoded answers and an `Asked` (classifier id, digest, questions,
|
|
1607
|
+
the state as it was sent, `DecisionAnswer`s with the provider's own
|
|
1608
|
+
confidence, integer `latencyMs`, and `usage` when the transport reported any).
|
|
1609
|
+
It fails with the transport's own `ClassifierError`. `read` is the fail-closed
|
|
1610
|
+
layer over it and needs no `Evaluator` in its type: a host that binds none
|
|
1611
|
+
fails `unconfigured`, and a transport failure fails as `Unjudged` with the
|
|
1612
|
+
transport's code and `Evaluator.publicMessage` as its `detail`.
|
|
1613
|
+
|
|
1614
|
+
`perItem({ id, description, context, item, questions, concurrency?, maxStateBytes? })`
|
|
1615
|
+
asks the same questions about each of many items. Each question is built from
|
|
1616
|
+
the item's index, and its id is `${key}_${index}`. `classifierFor(n)` is the
|
|
1617
|
+
classifier for `n` items, the same object for the same `n`. Its state is
|
|
1618
|
+
`{ context, items }`. `read(context, items)` packs items in order, greedily,
|
|
1619
|
+
into requests whose encoded state fits `maxStateBytes` (by default 262,144
|
|
1620
|
+
bytes, the `jev` flow's ceiling). Each request numbers its items from 0, and
|
|
1621
|
+
requests run at most `concurrency` (`Classifier.defaultConcurrency`) at once.
|
|
1622
|
+
Answers come back one record per item in input order, with every request's
|
|
1623
|
+
`Asked`. The first failed request fails the reading, and partial answers are
|
|
1624
|
+
never returned. No items send no request. An item too large to send alone
|
|
1625
|
+
fails `invalid_question`.
|
|
1626
|
+
|
|
1627
|
+
`recorded(engine, { name, identity, classifier, value, items }, execute)`
|
|
1628
|
+
takes a reading inside a run through `EngineLike.record`. A success records
|
|
1629
|
+
the value and one `decision-settled` row per request (`decision`: decided by
|
|
1630
|
+
`jev`, usage carried). A failure records `value: null` and one
|
|
1631
|
+
`decision-unjudged` row over `items` (`unjudgedEvent`). A replayed frame is
|
|
1632
|
+
served the record and asks nothing. `emitRecorded` journals the decisions in
|
|
1633
|
+
order, then the unjudged row. `task` bounds a run's task to `taskBytes`
|
|
1634
|
+
(4,096) with both ends kept.
|
|
1635
|
+
|
|
1636
|
+
## Relevance
|
|
1637
|
+
|
|
1638
|
+
`import * as Relevance from "@smthrs/harness/Relevance"`
|
|
1639
|
+
|
|
1640
|
+
What a run is shown of the human-provided items it could be shown: catalog
|
|
1641
|
+
flows and skills, chunks of instruction files such as `AGENTS.md`, and memory
|
|
1642
|
+
rows. Each is an `Item` (`{ kind, id, text }`, `Kind` being
|
|
1643
|
+
`AgentEvent.RelevanceKind`).
|
|
1644
|
+
|
|
1645
|
+
`judge(context, items)` asks Jev, through `reader` (a `Judgement.perItem`
|
|
1646
|
+
reading with classifier `relevance/unnecessary` and one boolean
|
|
1647
|
+
`unnecessary_<i>` per item), whether each item is unnecessary for
|
|
1648
|
+
`context.task`. An item is withheld only at a probability at or above
|
|
1649
|
+
`withholdAt` (0.9); everything else is kept. Each item's text is sent
|
|
1650
|
+
head-kept to `itemBytes` (1,024), and an item that was cut is always kept:
|
|
1651
|
+
Jev cannot be confident about text it was not shown. The `Reading` holds a
|
|
1652
|
+
`Verdict` per item in
|
|
1653
|
+
input order (`p`, `withheld`, and the digest of the whole text), every
|
|
1654
|
+
request's `Asked`, `latencyMs` and summed `usage`. It fails as
|
|
1655
|
+
`Judgement.Unjudged`, and the caller then keeps every item and journals
|
|
1656
|
+
`decision-unjudged`.
|
|
1657
|
+
|
|
1658
|
+
`settled(reading, { scope, frame, source })` is the `relevance-settled` row:
|
|
1659
|
+
ids and digests, never text. `flowItem(descriptor)` is a catalog entry as an
|
|
1660
|
+
item, `skill` for a markdown body and `flow` otherwise, its description
|
|
1661
|
+
marked untrusted. `chunks(documents)` splits `Document`s (`{ path, text }`)
|
|
1662
|
+
into `Chunk`s with ids `${path}#${n}`: one per markdown heading and per
|
|
1663
|
+
top-level list item, never inside a fence. `render(documents, withheld)` is the
|
|
1664
|
+
project-instructions block of every chunk not withheld; `""` when nothing is
|
|
1665
|
+
left.
|
|
1666
|
+
|
|
1667
|
+
## Supervisor
|
|
1668
|
+
|
|
1669
|
+
`import * as Supervisor from "@smthrs/harness/Supervisor"`
|
|
1670
|
+
|
|
1671
|
+
The reading Jev takes of a run while it is still running, off the cell loop's
|
|
1672
|
+
hot path. Every other control reads a completion or one measured fact; this
|
|
1673
|
+
one reads the run's shape. It declares one classifier, `supervisor/turn`, over
|
|
1674
|
+
a `Snapshot` the controller assembles from what it already holds: the task
|
|
1675
|
+
(with every later instruction the run accepted), the newest `recentFrames`
|
|
1676
|
+
(3) frames as `Frame` (cell head, prose head, prints tail, each bounded by
|
|
1677
|
+
`frameBytes`, the transition, whether the frame moved the tree), the counts the
|
|
1678
|
+
deterministic controls keep as `Signals` (read-only and repeat streaks,
|
|
1679
|
+
mutations, checks run and failing, unanswered failures, failed calls, demands
|
|
1680
|
+
spent). It also carries `skills`: up to `skillLimit` (12)
|
|
1681
|
+
Markdown skills from the frame's relevance-filtered catalog the model may
|
|
1682
|
+
call and has not, sorted by name and offered only when `read` is in that
|
|
1683
|
+
catalog, each a `Skill` of name, Markdown body path and the description's
|
|
1684
|
+
first `skillBytes` (256) wrapped as untrusted data (`skill`); `called`, the
|
|
1685
|
+
newest `calledLimit` (64) distinct flows the call ledger names; and
|
|
1686
|
+
`jevAvailable`, whether the catalog has `jev`. A skill the relevance gate
|
|
1687
|
+
withheld is never offered. Nothing is re-derived: the supervisor reads the
|
|
1688
|
+
harness's evidence and never measures a second time.
|
|
1689
|
+
|
|
1690
|
+
One Jev call per snapshot answers every question at once; eleven are fixed.
|
|
1691
|
+
Five are about the run, the `triggers`: `thrashing` (at or
|
|
1692
|
+
above `thrashingAt`, 0.5), `off_target` (`on_target` at or below
|
|
1693
|
+
`offTargetAt`, 0.5), `suspect` (at or above `suspectAt`, 0.5),
|
|
1694
|
+
`outdated_context` and `irrelevant_context` (each at or above `contextAt`,
|
|
1695
|
+
0.5). `triggered(reading)` names the triggers that fired. The two
|
|
1696
|
+
`contextTriggers` never nudge: `compacting(reading)` names the ones that
|
|
1697
|
+
fired, and a judged run whose boundary takes that reading compacts its
|
|
1698
|
+
context at the next frame by the marks it stored, whatever the budget, its
|
|
1699
|
+
`compaction-settled` naming them as `causes`. `crosses(reading)` is any other
|
|
1700
|
+
trigger firing; the `supervisor` lint monitor and the offline replay both
|
|
1701
|
+
call it, and `nudge` names the counts behind it. Five are
|
|
1702
|
+
operational states scored on `Level` (`none`, `mild`, `strong`), each naming
|
|
1703
|
+
the evidence it reads: `frustrated`, `anxious`, `scared`, `confused`,
|
|
1704
|
+
`confident`. `needs_help` is a choice over `Help` (`none`, `clarification`,
|
|
1705
|
+
`permission`, `stuck`, `risky_action`): the one word a person is shown, and it
|
|
1706
|
+
never steers the run. Each monitor question the run's `Monitor`s add is one
|
|
1707
|
+
more boolean, keyed `monitor_<id>` (`monitorPrefix`; `MonitorQuestions`).
|
|
1708
|
+
`read(snapshot, extra)` asks them all and puts each monitor's probability on
|
|
1709
|
+
`Reading.monitors` by monitor id. `classifierFor(extra)` is the classifier for
|
|
1710
|
+
that set of monitor ids, declared once per set, and `classifier` the bare one
|
|
1711
|
+
over the eleven fixed questions. `prose` strips fenced cells from a reply.
|
|
1712
|
+
|
|
1713
|
+
Recalled memory goes through `Relevance`, not `supervisor/turn`. Beside each
|
|
1714
|
+
reading the fiber recalls past every row whose key the run has already been
|
|
1715
|
+
shown, keeps the first `recalledLimit` (6) it has not, and asks
|
|
1716
|
+
`Relevance.judge` about the rest as `memory` items keyed by row, with the
|
|
1717
|
+
task and the head of the frame's prose as `recent`; the two readings run
|
|
1718
|
+
concurrently. Rows Jev is confident the run does not need are withheld; the
|
|
1719
|
+
rest, or every row when relevance cannot answer, go to the next boundary,
|
|
1720
|
+
rendered by `recalledInsert`. That boundary drops any row shown since the
|
|
1721
|
+
offer, records the keys it delivered on `SteeringDrained.memory`, and folds
|
|
1722
|
+
them into `CellTurn.State.memoryShown`, so a row is shown to a run once.
|
|
1723
|
+
|
|
1724
|
+
`CellTurn` offers each frame's snapshot from the frame's own live turn
|
|
1725
|
+
boundary to a one-slot sliding queue and continues at once; a forked fiber on
|
|
1726
|
+
the loop's scope takes the newest snapshot, recalls, asks through
|
|
1727
|
+
`EngineLike.record` (keyed on the session, the frame and the cell digest),
|
|
1728
|
+
and journals, in order, the supervisor's `DecisionSettled` (the snapshot and
|
|
1729
|
+
every answer), the memory reading's `DecisionSettled` rows, its
|
|
1730
|
+
`RelevanceSettled` (`source: "supervisor"`) or a `DecisionUnjudged` for
|
|
1731
|
+
`relevance/unnecessary`, the marks reading's `DecisionSettled` rows or a
|
|
1732
|
+
`DecisionUnjudged` for `compaction/marks`, and last
|
|
1733
|
+
`AgentEvent.SupervisorSettled`, whose `monitors` holds each monitor's value
|
|
1734
|
+
and whether it crossed, and whose `skillsCapped` is true when more than
|
|
1735
|
+
`skillLimit` skills could have been offered. Each reading scores the run's monitors, then
|
|
1736
|
+
`Monitor.skills` of its snapshot, then `Monitor.useJev()`. What
|
|
1737
|
+
the run is told is the monitors' to decide: the reading records each
|
|
1738
|
+
monitor's raw value and every crossed monitor's message, and the next
|
|
1739
|
+
boundary that executes gates them with `Monitor.gate` against
|
|
1740
|
+
`CellTurn.State.monitorLedger` inside its own recorded drain, only when
|
|
1741
|
+
`CellTurn.Input.judged`. At most one monitor message is delivered, then the
|
|
1742
|
+
recalled rows, in the drain's own `supervisor` field rather than among the
|
|
1743
|
+
person's steering inserts; a reading is dropped as stale after that boundary.
|
|
1744
|
+
A reading that fails on one side still delivers the other. The model reads
|
|
1745
|
+
them above the frame's ask; the task the completion brake and later snapshots
|
|
1746
|
+
read never includes them. Each `SupervisorSettled` states the delivery it ran
|
|
1747
|
+
under as `steer`. `Memory` is a port with `memoryNone` behind it unless the
|
|
1748
|
+
host adapts a store. The supervisor only recalls through it; a recall that
|
|
1749
|
+
fails is journaled as `AgentEvent.SupervisorMemoryFailed` and the reading goes
|
|
1750
|
+
on. The supervisor writes no memory: when `supervisor.remember` is on,
|
|
1751
|
+
`@smthrs/agent`'s `MemoryMine` mines the run's transcript once, at run end,
|
|
1752
|
+
and writes through `Memory.remember`.
|
|
1753
|
+
|
|
1754
|
+
It never falls back. A snapshot nobody could judge is journaled as
|
|
1755
|
+
`AgentEvent.SupervisorUnjudged` with `UnjudgedReason` (`unconfigured`,
|
|
1756
|
+
`interrupted`, or the transport's own code; an `unreachable` detail is
|
|
1757
|
+
`Evaluator.unreachableMessage`, never the transport's text), gates no
|
|
1758
|
+
monitor, leaves the ledger alone and reports no level. A replayed frame is never
|
|
1759
|
+
offered, so a resumed run re-asks nothing and re-delivers exactly what its
|
|
1760
|
+
recorded drains hold. A reading in flight when the run ends gets
|
|
1761
|
+
`closeGraceMs` (1,500) to settle; one still unanswered is interrupted and
|
|
1762
|
+
journaled `interrupted`, and the run never waits longer. The offline replay over
|
|
1763
|
+
archived journals that measures the triggers' precision is
|
|
1764
|
+
`evals/swebench/lib/jev-replay.mjs`.
|
|
1765
|
+
|
|
1766
|
+
## Monitor
|
|
1767
|
+
|
|
1768
|
+
`import * as Monitor from "@smthrs/harness/Monitor"`
|
|
1769
|
+
|
|
1770
|
+
What one supervisor reading can put in front of the run, and when. A
|
|
1771
|
+
`Monitor` turns a `Supervisor.Reading` into a probability and a sentence: a
|
|
1772
|
+
`Questioned` monitor adds one boolean question (`questionId(id)`,
|
|
1773
|
+
`monitor_<id>`) to the supervisor's single Jev call, asked of a snapshot when
|
|
1774
|
+
its `applies` holds; a `Derived` monitor scores answers the supervisor already
|
|
1775
|
+
asks and costs nothing. `make(input)` fills a monitor's budget from its
|
|
1776
|
+
kind's `budgets`: `at` (crosses when `p >= at`), `consecutive` crossed
|
|
1777
|
+
readings before delivery, `cooldownFrames` after a delivery, `limit`
|
|
1778
|
+
deliveries per run, and `priority`. `validate` admits a set or fails with
|
|
1779
|
+
`InvalidMonitor` naming the first id that cannot run (`idPattern`, a duplicate
|
|
1780
|
+
id, an id each reading adds itself (`skill_*`, `use_jev`),
|
|
1781
|
+
`at` outside `(0, 1]`, or a count that is not a non-negative integer).
|
|
1782
|
+
|
|
1783
|
+
`lint()` is the supervisor's legacy nudge: it crosses exactly when
|
|
1784
|
+
`Supervisor.crosses` does and says `Supervisor.nudge`, with no streak,
|
|
1785
|
+
cooldown or limit. `moods()` are the stance monitors `paranoid`, `careful`,
|
|
1786
|
+
`step_back` and `clarify`, saying `paranoidText`, `carefulText`,
|
|
1787
|
+
`stepBackText` and `clarifyText`. `paranoid` crosses on a signal the lint
|
|
1788
|
+
does not own: a frame whose transition is `complete` in the snapshot marks a
|
|
1789
|
+
bounced completion and an increased demand counter. It requires strong
|
|
1790
|
+
confidence; the rest read the emotions and help the reading already holds.
|
|
1791
|
+
`defaults()` is both, and what `CellTurn.Input.monitors` takes when omitted.
|
|
1792
|
+
|
|
1793
|
+
Every reading adds two more. `skills(snapshot)` is one `skill` monitor per
|
|
1794
|
+
offered skill (at 0.85, limit 1), id `skill_<name made safe>` with a digest
|
|
1795
|
+
suffix when two names collide, asking whether reading `skills[i]` now would
|
|
1796
|
+
change what the run does next; it says `skillText(name, path)`, which names
|
|
1797
|
+
the skill and a `read` call for its body and never quotes its description.
|
|
1798
|
+
`useJev()` is the `use_jev` lint (at 0.85, limit 2), asked only while
|
|
1799
|
+
`jevAvailable`, for frames that print lists and choose among them by hand; it
|
|
1800
|
+
says `useJevText`.
|
|
1801
|
+
|
|
1802
|
+
`questions(monitors, snapshot)` is what a snapshot adds to the call.
|
|
1803
|
+
`evaluate` runs at reading time: a `Row` per monitor with a value, and a
|
|
1804
|
+
`Candidate` with its rendered text per crossed one. `gate` runs at the
|
|
1805
|
+
recorded steering drain against the durable `Ledger` (an `Entry` of
|
|
1806
|
+
`streak`, `delivered` and `lastFrame` per id; `fresh` when absent): each row
|
|
1807
|
+
moves its streak, each candidate is withheld for `streak`, `cooldown` or
|
|
1808
|
+
`limit` in that order, and of the rest the highest priority takes the one
|
|
1809
|
+
slot and the others are withheld for `slot`. `Gated` is the message, the
|
|
1810
|
+
suppressions and the next ledger. Both halves are pure: nothing here asks,
|
|
1811
|
+
journals or steers. `CellTurn` records the delivered id and the
|
|
1812
|
+
suppressions on `SteeringDrained.monitor` and `suppressed`, and the ledger
|
|
1813
|
+
on the drain, so a replay delivers exactly what it delivered the first time
|
|
1814
|
+
and a resumed run keeps its cooldowns and limits. Delivery is silent unless
|
|
1815
|
+
the run is judged; `DisciplineArmed.monitors` lists the armed monitors then.
|
|
1816
|
+
|
|
1817
|
+
## Sufficiency
|
|
1818
|
+
|
|
1819
|
+
`import * as Sufficiency from "@smthrs/harness/Sufficiency"`
|
|
1820
|
+
|
|
1821
|
+
The evidence that is already complete: the counterweight to the demands.
|
|
1822
|
+
`remember` records one frame's failing checks against the mutation epoch they
|
|
1823
|
+
ran in, bounded to `retained` (16); `find` finds a failing-before, passing-after
|
|
1824
|
+
pair over one subject; and `observation` states, once per run, that the run
|
|
1825
|
+
holds both halves of its own evidence. It asks for nothing, refuses nothing,
|
|
1826
|
+
and spends no cap.
|
|
1827
|
+
|
|
1828
|
+
## VacuousVerification
|
|
1829
|
+
|
|
1830
|
+
`import * as VacuousVerification from "@smthrs/harness/VacuousVerification"`
|
|
1831
|
+
|
|
1832
|
+
The proof that was already true before anything changed. **This control is
|
|
1833
|
+
not wired into `CellTurn`: nothing in a production run reads it, and no run
|
|
1834
|
+
is told anything by it.** The module and
|
|
1835
|
+
`AgentEvent.VacuousVerificationObserved` are exported so a host can wire it in
|
|
1836
|
+
and measure it on its own. Its `stored` input reads a reserved `verification`
|
|
1837
|
+
key out of durable state that nothing currently writes, so a host that turns
|
|
1838
|
+
it on first decides where a run declares its verification. Once it is wired,
|
|
1839
|
+
`find` locates the pristine-tree pass a stored verification stands on and
|
|
1840
|
+
`observation` states the fact once per distinct input.
|
|
1841
|
+
|
|
1842
|
+
## TruncatedOutput
|
|
1843
|
+
|
|
1844
|
+
`import * as TruncatedOutput from "@smthrs/harness/TruncatedOutput"`
|
|
1845
|
+
|
|
1846
|
+
The truncation ledger: which bytes this run was handed as a fragment. A flow
|
|
1847
|
+
that caps a captured stream returns the part that fit and declares the cut in
|
|
1848
|
+
a sibling flag, and a later write of those same bytes is refused, because
|
|
1849
|
+
there is no case in which writing a known fragment over a file is what the
|
|
1850
|
+
caller meant. `captures` reads every truncated payload one settled call
|
|
1851
|
+
result declares (the `flagSuffix` and `droppedSuffix` conventions beside a
|
|
1852
|
+
named payload, or the bare `flagKey` for a single-payload flow, at or above
|
|
1853
|
+
`minimumBytes` (1,024)); `reuse` finds the first input field carrying an earlier
|
|
1854
|
+
capture verbatim; `refusal` states the `truncated_write` refusal; and
|
|
1855
|
+
`retain` bounds the `Ledger` to the `retained` (16) most recent distinct captures.
|
|
1856
|
+
|
|
1857
|
+
## VariablesPanel
|
|
1858
|
+
|
|
1859
|
+
`import * as VariablesPanel from "@smthrs/harness/VariablesPanel"`
|
|
1860
|
+
|
|
1861
|
+
The variables panel: what the realm holds, stated every frame. A run's memory
|
|
1862
|
+
is the set of names the realm is holding, and the model can only act on what
|
|
1863
|
+
the prompt says about them. Every line is a name, its type, one cheap size,
|
|
1864
|
+
and when it was last bound; nothing is serialized whole. `stamp` re-stamps
|
|
1865
|
+
the panel `Ledger` against the bindings a frame closed on, and `render`
|
|
1866
|
+
renders it for one frame's prompt, printing at most `bound` (64) names before it
|
|
1867
|
+
counts instead. A binding rewritten to a value of the same type and size
|
|
1868
|
+
reads as unchanged: the panel is a roster, and the run's own prints are what
|
|
1869
|
+
say a value moved.
|