@sema-agent/core 2.13.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2345 -0
- package/dist/agents/cascade.d.ts +6 -1
- package/dist/agents/cascade.js +76 -32
- package/dist/agents/cumulative-stats.d.ts +29 -0
- package/dist/agents/cumulative-stats.js +29 -0
- package/dist/agents/observer.d.ts +4 -0
- package/dist/agents/observer.js +11 -10
- package/dist/agents/repair-loop.d.ts +2 -0
- package/dist/agents/repair-loop.js +110 -3
- package/dist/agents/retain-ledger.d.ts +20 -2
- package/dist/agents/retain-ledger.js +38 -8
- package/dist/agents/roster-store.d.ts +4 -0
- package/dist/agents/roster-store.js +20 -1
- package/dist/agents/subagent.js +66 -64
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +23 -24
- package/dist/agents/team.js +7 -4
- package/dist/agents/verify.d.ts +15 -2
- package/dist/agents/verify.js +39 -3
- package/dist/bin/sema-tb.js +2 -2
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/circuit-breaker.d.ts +1 -0
- package/dist/brain/circuit-breaker.js +26 -9
- package/dist/brain/degrading.js +13 -1
- package/dist/brain/failover.js +2 -0
- package/dist/brain/openai.js +24 -21
- package/dist/brain/repetition.js +10 -1
- package/dist/brain/routing.js +5 -1
- package/dist/brain/status-sink.js +5 -1
- package/dist/brain/stream-engine.js +5 -2
- package/dist/brain/stream-shared.d.ts +1 -0
- package/dist/brain/stream-shared.js +14 -0
- package/dist/brain/terminal-cause.d.ts +4 -0
- package/dist/brain/terminal-cause.js +12 -0
- package/dist/brain/tool-call-id.d.ts +1 -0
- package/dist/brain/tool-call-id.js +3 -0
- package/dist/brain/tool-call-repair.js +34 -15
- package/dist/core/auto-compaction.js +33 -32
- package/dist/core/background-agent-store.d.ts +22 -22
- package/dist/core/background-agent-store.js +19 -23
- package/dist/core/checkpoint-store.d.ts +7 -13
- package/dist/core/checkpoint-store.js +39 -15
- package/dist/core/consolidate-scope.js +4 -3
- package/dist/core/context-edit.js +3 -0
- package/dist/core/context-guard.js +3 -0
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/git-worktree-env.d.ts +6 -1
- package/dist/core/git-worktree-env.js +12 -1
- package/dist/core/hooks.d.ts +4 -1
- package/dist/core/hooks.js +35 -7
- package/dist/core/lsp-diagnostics.js +13 -7
- package/dist/core/lsp-protocol.d.ts +1 -1
- package/dist/core/lsp-protocol.js +20 -8
- package/dist/core/lsp-session.d.ts +12 -2
- package/dist/core/lsp-session.js +114 -49
- package/dist/core/lsp.d.ts +13 -1
- package/dist/core/lsp.js +65 -9
- package/dist/core/mailbox-store.d.ts +7 -10
- package/dist/core/mcp.d.ts +7 -10
- package/dist/core/mcp.js +7 -3
- package/dist/core/memory-engine/data-plane.js +4 -0
- package/dist/core/memory-engine/engine.d.ts +2 -2
- package/dist/core/memory-engine/engine.js +96 -31
- package/dist/core/memory-engine/file-backend.d.ts +2 -2
- package/dist/core/memory-engine/file-backend.js +38 -39
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +71 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/memory.js +3 -0
- package/dist/core/permission-rules.d.ts +2 -1
- package/dist/core/permission-rules.js +24 -3
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +1 -15
- package/dist/core/runner/assemble-result.d.ts +3 -8
- package/dist/core/runner/assemble-result.js +3 -5
- package/dist/core/runner/compaction-call-options.d.ts +7 -0
- package/dist/core/runner/compaction-call-options.js +30 -0
- package/dist/core/runner/prepare-task.d.ts +6 -23
- package/dist/core/runner/prepare-task.js +31 -53
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +86 -178
- package/dist/core/runner/session-rule-policy.js +4 -1
- package/dist/core/runner/teardown-bounded.d.ts +7 -0
- package/dist/core/runner/teardown-bounded.js +36 -0
- package/dist/core/runner/tool-disclosure.d.ts +2 -5
- package/dist/core/runner/turn-attachments.d.ts +5 -15
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/usage-accounting.d.ts +6 -0
- package/dist/core/runner/usage-accounting.js +9 -8
- package/dist/core/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/secret-env.js +5 -1
- package/dist/core/sensitive-path-policy.js +11 -4
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/store-contracts/session-repo-contract.js +40 -0
- package/dist/core/stub-env.d.ts +2 -6
- package/dist/core/surrogate-safe-slice.d.ts +4 -0
- package/dist/core/surrogate-safe-slice.js +18 -0
- package/dist/core/task-registry-monitor.js +15 -14
- package/dist/core/task-registry-shared.d.ts +22 -0
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +40 -57
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.js +2 -1
- package/dist/core/tool-policy.js +72 -19
- package/dist/core/tool-result-store.d.ts +1 -0
- package/dist/core/tool-result-store.js +17 -1
- package/dist/core/trace.d.ts +3 -9
- package/dist/core/types.d.ts +16 -12
- package/dist/core/untrusted-egress.js +7 -5
- package/dist/core/workflow-journal-store.d.ts +9 -20
- package/dist/core/workflow-run-store.d.ts +11 -10
- package/dist/core/workflow-run-store.js +22 -16
- package/dist/engine/compaction/compaction.d.ts +1 -0
- package/dist/engine/compaction/compaction.js +14 -5
- package/dist/engine/compaction/utils.js +4 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +13 -33
- package/dist/engine/harness/types.d.ts +29 -67
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +11 -4
- package/dist/engine/loop/types.d.ts +11 -9
- package/dist/engine/lsp/frame-decoder.js +6 -3
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -0
- package/dist/engine/lsp/node-lsp-manager.js +103 -26
- package/dist/engine/lsp/stdio-lsp-transport.js +16 -7
- package/dist/engine/session/memory-repo.d.ts +2 -6
- package/dist/engine/session/session.d.ts +5 -30
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/run-spec.js +7 -4
- package/dist/orchestration/run-workflow-tool.js +3 -35
- package/dist/orchestration/workflow-governance.js +43 -9
- package/dist/orchestration/workflow-sandbox-conformance.js +85 -51
- package/dist/orchestration/workflow-script-store.js +34 -4
- package/dist/orchestration/workflow.d.ts +3 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/artifact-store.d.ts +2 -0
- package/dist/prompt-assembly/artifact-store.js +39 -24
- package/dist/prompt-assembly/assemble.js +38 -7
- package/dist/prompt-assembly/epoch.js +19 -2
- package/dist/prompt-assembly/event-registry.js +3 -3
- package/dist/prompt-assembly/explain.js +1 -1
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/tool-catalog.js +16 -1
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/prompt-assembly/turn-snapshot.js +4 -1
- package/dist/stores/file/background-agent-store.d.ts +3 -12
- package/dist/stores/file/background-agent-store.js +3 -41
- package/dist/stores/file/checkpoint-store.js +6 -6
- package/dist/stores/file/fs-atomic.d.ts +3 -0
- package/dist/stores/file/fs-atomic.js +53 -8
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +7 -0
- package/dist/stores/file/mailbox-store.d.ts +2 -6
- package/dist/stores/file/session-policy-store.js +15 -1
- package/dist/stores/file/session-store.d.ts +4 -6
- package/dist/stores/file/session-store.js +28 -1
- package/dist/stores/file/workflow-journal-store.d.ts +3 -11
- package/dist/stores/file/workflow-run-store.d.ts +3 -7
- package/dist/stores/file/workflow-run-store.js +4 -17
- package/dist/tools/fs/bash-readonly-classifier.d.ts +2 -0
- package/dist/tools/fs/bash-readonly-classifier.js +73 -21
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +407 -91
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/notebook.d.ts +2 -5
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +43 -14
- package/dist/tools/fs/search.js +5 -5
- package/dist/tools/loop-tick.js +1 -1
- package/dist/tools/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +57 -9
- package/dist/tools/worktree.d.ts +4 -7
- package/dist/tools/worktree.js +235 -143
- package/package.json +9 -2
package/dist/agents/verify.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import type { Runner, ResumeTaskConfig } from "../core/runner/runtask.js";
|
|
3
|
-
import type { CheckpointToken, ResumeOutcome } from "../core/checkpoint-store.js";
|
|
3
|
+
import type { CheckpointGate, CheckpointToken, ResumeOutcome } from "../core/checkpoint-store.js";
|
|
4
4
|
import type { ModelRef, TaskResult, TaskSpec, ToolSpec } from "../core/types.js";
|
|
5
5
|
export declare const VERIFICATION_PROMPT = "You are a verification specialist. Your job is NOT to confirm the implementation works \u2014 it is to try to BREAK it.\n\nYou have two documented failure patterns. First, verification avoidance: faced with a check, you find reasons not to run it \u2014 you read code, narrate what you would test, declare \"PASS,\" and move on. Second, being seduced by the first 80%: a polished result or a passing test suite makes you inclined to pass it, not noticing the edge that crashes, the state that vanishes, the bad input that is unhandled. The first 80% is the easy part. Your entire value is in finding the last 20%.\n\n## Hard boundary \u2014 do not modify the project\nYou are STRICTLY a verifier. Do NOT create, modify, or delete project files; do NOT install packages; do NOT run version-control write operations. Use only the read/probe/execute tools available to you. (If you need a scratch file, use a temp directory, and clean up.)\n\n## Evidence is mandatory\nReading code is NOT verification. Every check must actually run something \u2014 execute the code, hit the endpoint, run the build/tests \u2014 and record the command and its real output. A \"PASS\" with no command output is a skip, not a pass.\n\n## Strategy (adapt to what changed)\n- Build/lib changes: build it, run the full test suite, exercise the public API as a consumer would.\n- Backend/API: start it, call endpoints, check response *shapes* (not just status codes), test error paths.\n- CLI/script: run with representative AND edge inputs (empty, malformed, boundary); check stdout/stderr/exit codes.\n- Bug fix: reproduce the original bug first, verify the fix, then check for regressions and side effects.\n- Refactor (no behavior change): the existing suite must pass unchanged; diff the public surface; same inputs \u2192 same outputs.\nRun the project's own build/tests/linters as a baseline, then apply the type-specific checks. Test results are context, not proof \u2014 the implementer is an LLM too; its tests may be happy-path or circular.\n\n## Adversarial probes (pick the ones that fit)\nBoundary values (0, -1, empty, very long, unicode, max), idempotency (same mutating call twice), orphan operations (ids that don't exist), concurrency (parallel create-if-not-exists). Your verdict must include at least one adversarial probe you actually ran and its result \u2014 even if it was handled correctly.\n\n## Before you FAIL\nCheck you haven't missed why it's actually fine: defensive code elsewhere, intentional behavior documented in comments/specs, or an unfixable external-contract limitation (note that as an observation, not a FAIL). Don't wave away real issues, but don't FAIL on intentional behavior.\n\n## Verdict\nSubmit exactly one verdict via the provided output tool:\n- PASS \u2014 you ran real checks (including \u22651 adversarial probe) and it holds up. Put the commands + observed output in `evidence`.\n- FAIL \u2014 something is broken. Put each concrete problem (with how to reproduce) in `findings`.\n- PARTIAL \u2014 environmental limitation only (no test framework, a tool/server unavailable). Not for \"I'm unsure\": if you can run the check, decide PASS or FAIL. Note what you couldn't verify and why in `findings`.";
|
|
6
6
|
export declare const STATIC_VERIFICATION_PROMPT = "You are a verification judge. Your job is NOT to confirm the change works \u2014 it is to find where it BREAKS.\n\nYou are READ-ONLY by design: the build and tests have ALREADY been run by a separate mechanical gate. Their results and the code change (a diff) are usually supplied to you as evidence; but if little or no diff/results are supplied this round (e.g. a re-verification AFTER a fix), judge the CURRENT working tree directly \u2014 do NOT return PARTIAL merely because a diff is absent. Do NOT try to execute code, run tests, or invoke a runtime \u2014 the environment will refuse it, and that is expected, not a limitation. Judge from any supplied diff/results plus read-only inspection of the working tree (read files, search, list).\n\nYou have two documented failure patterns. First, being seduced by the first 80%: a clean diff or a green test run makes you inclined to pass it, not noticing the edge that crashes, the state that vanishes, the bad input that is unhandled, the cross-module assumption that breaks. Second, hiding behind PARTIAL because you couldn't run something \u2014 that is NOT what PARTIAL is for here; execution was the mechanical gate's job. Your entire value is finding the last 20% by READING.\n\n## What to scrutinize (adapt to the diff)\n- Boundary/edge cases the tests likely miss: 0, -1, empty, very long, unicode, max, malformed input, idempotency, orphan ids, off-by-one, negative numbers, EOF/empty fields.\n- Semantic correctness vs the task spec: does the change actually do what was asked, including cases the tests don't cover (the title()/CSV/base62 class of defect)?\n- Cross-module/integration hazards in the diff: a changed signature/export/contract/default a caller elsewhere still assumes; a deleted helper something depends on.\n- If the provided test results show failures, that is a concrete FAIL with the failing output as evidence.\n\n## Verdict\nSubmit exactly one verdict via the provided output tool:\n- PASS \u2014 you read the diff + results, looked for the edges above, and it holds. Cite the specific things you checked in `evidence`.\n- FAIL \u2014 you found a concrete defect. Put each problem (with the diff location / input that breaks it) in `findings`.\n- PARTIAL \u2014 ONLY when the evidence itself is genuinely insufficient to judge (e.g. the diff is empty or unrelated to the task, no results supplied). NOT for \"I couldn't execute it.\" Say what's missing in `findings`.";
|
|
@@ -24,8 +24,15 @@ export interface VerifyConfig {
|
|
|
24
24
|
verdict: VerificationOutcome["verdict"];
|
|
25
25
|
findings: string[];
|
|
26
26
|
}) => void;
|
|
27
|
+
onVerifierPause?: (info: {
|
|
28
|
+
round: number;
|
|
29
|
+
status: TaskResult["status"];
|
|
30
|
+
sessionId: string;
|
|
31
|
+
checkpointToken?: CheckpointToken;
|
|
32
|
+
checkpointGate?: CheckpointGate;
|
|
33
|
+
}) => void;
|
|
27
34
|
}
|
|
28
|
-
export type UnverifiedReason = "suspended" | "needs_review" | "no_verdict" | "opted_out" | "impl_incomplete" | "could_not_verify";
|
|
35
|
+
export type UnverifiedReason = "suspended" | "needs_review" | "no_verdict" | "opted_out" | "impl_incomplete" | "could_not_verify" | "verifier_paused";
|
|
29
36
|
export interface VerificationOutcome {
|
|
30
37
|
verdict: "PASS" | "FAIL" | "PARTIAL" | "unverified";
|
|
31
38
|
unverifiedReason?: UnverifiedReason;
|
|
@@ -33,6 +40,12 @@ export interface VerificationOutcome {
|
|
|
33
40
|
findings: string[];
|
|
34
41
|
evidence?: string;
|
|
35
42
|
verifierCost?: number;
|
|
43
|
+
verifierPause?: {
|
|
44
|
+
status: TaskResult["status"];
|
|
45
|
+
sessionId: string;
|
|
46
|
+
checkpointToken?: CheckpointToken;
|
|
47
|
+
checkpointGate?: CheckpointGate;
|
|
48
|
+
};
|
|
36
49
|
}
|
|
37
50
|
export interface VerificationResult extends TaskResult {
|
|
38
51
|
verification: VerificationOutcome;
|
package/dist/agents/verify.js
CHANGED
|
@@ -3,6 +3,7 @@ import { CODE_AGENT_PROMPT } from "../prompts/default.js";
|
|
|
3
3
|
import { releaseSession } from "./session-util.js";
|
|
4
4
|
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
5
5
|
import { delimitUntrusted, sanitizeUntrustedText } from "../core/untrusted-text.js";
|
|
6
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
6
7
|
export const VERIFICATION_PROMPT = `You are a verification specialist. Your job is NOT to confirm the implementation works — it is to try to BREAK it.
|
|
7
8
|
|
|
8
9
|
You have two documented failure patterns. First, verification avoidance: faced with a check, you find reasons not to run it — you read code, narrate what you would test, declare "PASS," and move on. Second, being seduced by the first 80%: a polished result or a passing test suite makes you inclined to pass it, not noticing the edge that crashes, the state that vanishes, the bad input that is unhandled. The first 80% is the easy part. Your entire value is in finding the last 20%.
|
|
@@ -58,6 +59,8 @@ export const VerdictSchema = Type.Object({
|
|
|
58
59
|
}),
|
|
59
60
|
evidence: Type.Optional(Type.String({ description: "Commands run + observed output backing the verdict." })),
|
|
60
61
|
});
|
|
62
|
+
const VERIFY_ON_ROUND_SITE = "verify.onRound";
|
|
63
|
+
const VERIFY_ON_VERIFIER_PAUSE_SITE = "verify.onVerifierPause";
|
|
61
64
|
function fixObjective(findings) {
|
|
62
65
|
const list = findings.length
|
|
63
66
|
? findings.map((f) => `- ${sanitizeUntrustedText(f)}`).join("\n")
|
|
@@ -82,7 +85,8 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
82
85
|
if (result.status !== "completed") {
|
|
83
86
|
return { ...result, verification: { verdict: "unverified", unverifiedReason: "impl_incomplete", rounds: 0, findings: [] } };
|
|
84
87
|
}
|
|
85
|
-
const maxRounds = Math.max(1, Math.floor(config.maxRounds
|
|
88
|
+
const maxRounds = Number.isFinite(config.maxRounds) ? Math.max(1, Math.floor(config.maxRounds)) : 2;
|
|
89
|
+
const notifier = createSafeNotifier();
|
|
86
90
|
const verifierTools = config.verifierTools ?? (specBase.tools ?? []).filter((t) => t.effect === "read");
|
|
87
91
|
const evidenceMode = config.evidence != null && config.evidence.trim() !== "";
|
|
88
92
|
const verifierPrompt = config.verifierPrompt ?? (evidenceMode ? STATIC_VERIFICATION_PROMPT : VERIFICATION_PROMPT);
|
|
@@ -102,7 +106,8 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
102
106
|
signal: specBase.signal,
|
|
103
107
|
});
|
|
104
108
|
try {
|
|
105
|
-
|
|
109
|
+
const cost = (v.stats.costMicroUsd ?? 0) + (v.stats.nested?.costMicroUsd ?? 0);
|
|
110
|
+
return { verdict: v.structuredOutput, cost, ...(isDurablePause(v.status) ? { paused: v } : {}) };
|
|
106
111
|
}
|
|
107
112
|
finally {
|
|
108
113
|
if (!isDurablePause(v.status))
|
|
@@ -115,9 +120,40 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
115
120
|
let spend = 0;
|
|
116
121
|
let verifierSpend = 0;
|
|
117
122
|
for (let round = 1; round <= maxRounds; round++) {
|
|
123
|
+
if (round > 1) {
|
|
124
|
+
if (config.totalTimeoutMs != null && Date.now() - startedAt >= config.totalTimeoutMs)
|
|
125
|
+
break;
|
|
126
|
+
if (config.costCeilingMicroUsd != null && spend >= config.costCeilingMicroUsd)
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
118
129
|
const v = await runVerifier(current, round);
|
|
119
130
|
spend += v.cost;
|
|
120
131
|
verifierSpend += v.cost;
|
|
132
|
+
if (v.paused) {
|
|
133
|
+
const paused = v.paused;
|
|
134
|
+
outcome = {
|
|
135
|
+
verdict: "unverified",
|
|
136
|
+
unverifiedReason: "verifier_paused",
|
|
137
|
+
rounds: round,
|
|
138
|
+
findings: [],
|
|
139
|
+
verifierCost: verifierSpend,
|
|
140
|
+
verifierPause: {
|
|
141
|
+
status: v.paused.status,
|
|
142
|
+
sessionId: v.paused.sessionId,
|
|
143
|
+
...(v.paused.checkpointToken ? { checkpointToken: v.paused.checkpointToken } : {}),
|
|
144
|
+
...(v.paused.checkpointGate ? { checkpointGate: v.paused.checkpointGate } : {}),
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
notifier.notify(() => config.onVerifierPause?.({
|
|
148
|
+
round,
|
|
149
|
+
status: paused.status,
|
|
150
|
+
sessionId: paused.sessionId,
|
|
151
|
+
...(paused.checkpointToken ? { checkpointToken: paused.checkpointToken } : {}),
|
|
152
|
+
...(paused.checkpointGate ? { checkpointGate: paused.checkpointGate } : {}),
|
|
153
|
+
}), VERIFY_ON_VERIFIER_PAUSE_SITE);
|
|
154
|
+
notifier.notify(() => config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings }), VERIFY_ON_ROUND_SITE);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
121
157
|
const rawVerdict = v.verdict?.verdict ?? "unverified";
|
|
122
158
|
const verdict = rawVerdict === "PARTIAL" ? "unverified" : rawVerdict;
|
|
123
159
|
const unverifiedReason = !v.verdict ? "no_verdict" : rawVerdict === "PARTIAL" ? "could_not_verify" : undefined;
|
|
@@ -129,7 +165,7 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
129
165
|
evidence: v.verdict?.evidence,
|
|
130
166
|
verifierCost: verifierSpend,
|
|
131
167
|
};
|
|
132
|
-
config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings });
|
|
168
|
+
notifier.notify(() => config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings }), VERIFY_ON_ROUND_SITE);
|
|
133
169
|
if (outcome.verdict !== "FAIL")
|
|
134
170
|
break;
|
|
135
171
|
if (round >= maxRounds)
|
package/dist/bin/sema-tb.js
CHANGED
|
@@ -289,7 +289,7 @@ async function main() {
|
|
|
289
289
|
switch (e.kind) {
|
|
290
290
|
case "brain.call":
|
|
291
291
|
lastServedModel = e.model;
|
|
292
|
-
line(`[call] model=${e.model} in=${e.promptTokens} out=${e.completionTokens} cache=${e.cacheRead} latMs=${e.latencyMs}` +
|
|
292
|
+
line(`[call] model=${e.model} in=${e.promptTokens} tot=${e.totalInputTokens} out=${e.completionTokens} cache=${e.cacheRead} latMs=${e.latencyMs}` +
|
|
293
293
|
(e.firstTokenMs !== undefined ? ` ttftMs=${e.firstTokenMs}` : "") +
|
|
294
294
|
(e.stopReason !== undefined ? ` stop=${e.stopReason}` : "") +
|
|
295
295
|
(e.callCap !== undefined ? ` cap=${e.callCap}${e.capThinkingSkipped ? "(think-skip)" : ""}` : "") +
|
|
@@ -403,7 +403,7 @@ async function main() {
|
|
|
403
403
|
reason.flush();
|
|
404
404
|
turns++;
|
|
405
405
|
const u = ev.usage;
|
|
406
|
-
line(`[turn] ${turns}${u ? ` in=${u.inputTokens} out=${u.outputTokens} cache=${u.cacheReadTokens}` : ""}` +
|
|
406
|
+
line(`[turn] ${turns}${u ? ` in=${u.inputTokens} tot=${u.totalInputTokens} out=${u.outputTokens} cache=${u.cacheReadTokens}` : ""}` +
|
|
407
407
|
(ev.usageMissing ? " usageMissing=1" : "") +
|
|
408
408
|
(ev.stopReason !== undefined ? ` stop=${ev.stopReason}` : ""));
|
|
409
409
|
break;
|
package/dist/brain/anthropic.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {} from "../internal/llm.js";
|
|
2
2
|
import { BrainError } from "./errors.js";
|
|
3
3
|
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
|
|
4
|
-
import { createRepetitionPoll, createWalltimeGate } from "./stream-shared.js";
|
|
4
|
+
import { createRepetitionPoll, createWalltimeGate, parseStreamedToolArgs } from "./stream-shared.js";
|
|
5
|
+
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
5
6
|
import { WALLTIME_CUTOFF_MESSAGE } from "./walltime.js";
|
|
6
7
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
8
|
import { ANTHROPIC_RESERVED, applyExtraBody, stripAuthHeaders } from "./request-params.js";
|
|
@@ -201,23 +202,11 @@ function closeToolUseBlock(acc) {
|
|
|
201
202
|
return acc.closedTc;
|
|
202
203
|
if (!acc.toolName)
|
|
203
204
|
return undefined;
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
const parsed = JSON.parse(acc.toolJson);
|
|
208
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
209
|
-
return undefined;
|
|
210
|
-
args = parsed;
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
return undefined;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
args = {};
|
|
218
|
-
}
|
|
205
|
+
const args = parseStreamedToolArgs(acc.toolJson);
|
|
206
|
+
if (args === undefined)
|
|
207
|
+
return undefined;
|
|
219
208
|
if (!acc.toolId)
|
|
220
|
-
acc.toolId =
|
|
209
|
+
acc.toolId = mintFallbackToolCallId("toolu_");
|
|
221
210
|
acc.closedTc = { type: "toolCall", id: acc.toolId, name: acc.toolName, arguments: args };
|
|
222
211
|
return acc.closedTc;
|
|
223
212
|
}
|
|
@@ -339,6 +328,8 @@ export function createAnthropicBrain(config = {}) {
|
|
|
339
328
|
let stopReason = null;
|
|
340
329
|
let stopDetails;
|
|
341
330
|
let nextContentIndex = 0;
|
|
331
|
+
let malformedFrames = 0;
|
|
332
|
+
let malformedSample;
|
|
342
333
|
let degenerate = false;
|
|
343
334
|
let degenTextBlock;
|
|
344
335
|
const rep = createRepetitionPoll();
|
|
@@ -512,6 +503,8 @@ export function createAnthropicBrain(config = {}) {
|
|
|
512
503
|
evt = JSON.parse(data);
|
|
513
504
|
}
|
|
514
505
|
catch {
|
|
506
|
+
malformedFrames++;
|
|
507
|
+
malformedSample ??= data.slice(0, 160);
|
|
515
508
|
return;
|
|
516
509
|
}
|
|
517
510
|
handleEvent(evt);
|
|
@@ -563,6 +556,12 @@ export function createAnthropicBrain(config = {}) {
|
|
|
563
556
|
text: `\n[note: ${malformed.length} tool call(s) were truncated (stop_reason="${stopReason ?? "?"}") and dropped — re-issue them next turn: ${malformed.join("; ")}]`,
|
|
564
557
|
});
|
|
565
558
|
}
|
|
559
|
+
if (malformedFrames > 0) {
|
|
560
|
+
finalContent.push({
|
|
561
|
+
type: "text",
|
|
562
|
+
text: `\n[note: ${malformedFrames} provider stream frame(s) were not valid JSON and were dropped — the streamed output may be missing content (first sample: ${JSON.stringify(malformedSample)})]`,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
566
565
|
const truncatedEmpty = stopReason === "max_tokens" && noUsableContent && !toolError;
|
|
567
566
|
const dynamicCut = truncatedEmpty && options?.maxTokensDynamic === true;
|
|
568
567
|
const staticReasoningCut = truncatedEmpty && !dynamicCut && reasoningSeen && options?.staticReasoningCutDowngrade === true;
|
|
@@ -586,7 +585,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
586
585
|
? `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}) — the model emitted reasoning although no thinking was requested (a budget-ignoring gateway?) and it consumed the entire output budget; raise max_tokens`
|
|
587
586
|
: `[length_empty] response truncated at max_tokens with no answer text (${maxTokensNote}); raise max_tokens`
|
|
588
587
|
: emptyNoFinish
|
|
589
|
-
? `[stream_torn] model stream ended with no content and no stop_reason — the response was lost (a torn stream or an in-band provider error frame)`
|
|
588
|
+
? `[stream_torn] model stream ended with no content and no stop_reason — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
|
|
590
589
|
: undefined));
|
|
591
590
|
const errored = walltime.cut ||
|
|
592
591
|
degenerate ||
|
|
@@ -595,9 +594,13 @@ export function createAnthropicBrain(config = {}) {
|
|
|
595
594
|
emptyNoFinish ||
|
|
596
595
|
(toolError !== undefined && noUsableContent) ||
|
|
597
596
|
(truncatedEmpty && !dynamicCut && !staticReasoningCut);
|
|
598
|
-
const errorKind =
|
|
599
|
-
? "
|
|
600
|
-
:
|
|
597
|
+
const errorKind = walltime.cut
|
|
598
|
+
? "walltime_cutoff"
|
|
599
|
+
: degenerate
|
|
600
|
+
? "degenerate"
|
|
601
|
+
: !safetyCut && toolError === undefined && truncatedEmpty && !dynamicCut && !staticReasoningCut
|
|
602
|
+
? "length_empty"
|
|
603
|
+
: undefined;
|
|
601
604
|
const finalMessage = {
|
|
602
605
|
...partial,
|
|
603
606
|
content: finalContent.length > 0 ? finalContent : [{ type: "text", text: "" }],
|
|
@@ -11,14 +11,14 @@ class InMemoryBreakerState {
|
|
|
11
11
|
this.m.set(k, s);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
function errorAssistantMessage(model, code, detail) {
|
|
14
|
+
function errorAssistantMessage(model, code, detail, stopReason = "error") {
|
|
15
15
|
return {
|
|
16
16
|
role: "assistant",
|
|
17
17
|
content: [{ type: "text", text: "" }],
|
|
18
18
|
api: model.api,
|
|
19
19
|
provider: model.provider,
|
|
20
20
|
model: model.id,
|
|
21
|
-
stopReason
|
|
21
|
+
stopReason,
|
|
22
22
|
errorMessage: `[${code}] ${detail}`,
|
|
23
23
|
usage: ZERO_USAGE,
|
|
24
24
|
timestamp: Date.now(),
|
|
@@ -44,21 +44,33 @@ export function createCircuitBreakerBrain(inner, opts = {}) {
|
|
|
44
44
|
if (s.phase === "closed")
|
|
45
45
|
return { pass: true };
|
|
46
46
|
if (s.phase === "open") {
|
|
47
|
-
if (s.openedAt === undefined)
|
|
48
|
-
|
|
47
|
+
if (s.openedAt === undefined) {
|
|
48
|
+
state.set(key, { ...s, openedAt: now() });
|
|
49
|
+
return { pass: false, retryInMs: cooldownMs };
|
|
50
|
+
}
|
|
49
51
|
const elapsed = now() - s.openedAt;
|
|
50
52
|
if (elapsed >= cooldownMs) {
|
|
51
|
-
setState(key, s.phase, { ...s, phase: "half-open", halfOpenInFlight: 1 });
|
|
53
|
+
setState(key, s.phase, { ...s, phase: "half-open", halfOpenInFlight: 1, halfOpenAt: now() });
|
|
52
54
|
return { pass: true };
|
|
53
55
|
}
|
|
54
56
|
return { pass: false, retryInMs: cooldownMs - elapsed };
|
|
55
57
|
}
|
|
56
58
|
const inFlight = s.halfOpenInFlight ?? 0;
|
|
57
59
|
if (inFlight < halfOpenProbes) {
|
|
58
|
-
state.set(key, { ...s, halfOpenInFlight: inFlight + 1 });
|
|
60
|
+
state.set(key, { ...s, halfOpenInFlight: inFlight + 1, halfOpenAt: s.halfOpenAt ?? now() });
|
|
59
61
|
return { pass: true };
|
|
60
62
|
}
|
|
61
|
-
|
|
63
|
+
const leaseStart = s.halfOpenAt;
|
|
64
|
+
if (leaseStart === undefined) {
|
|
65
|
+
state.set(key, { ...s, halfOpenAt: now() });
|
|
66
|
+
return { pass: false, retryInMs: cooldownMs };
|
|
67
|
+
}
|
|
68
|
+
const held = now() - leaseStart;
|
|
69
|
+
if (held >= cooldownMs) {
|
|
70
|
+
state.set(key, { ...s, halfOpenInFlight: 1, halfOpenAt: now() });
|
|
71
|
+
return { pass: true };
|
|
72
|
+
}
|
|
73
|
+
return { pass: false, retryInMs: cooldownMs - held };
|
|
62
74
|
};
|
|
63
75
|
const record = (key, failedCode, viaProbe = true) => {
|
|
64
76
|
const s = snap(key);
|
|
@@ -118,9 +130,14 @@ export function createCircuitBreakerBrain(inner, opts = {}) {
|
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
132
|
catch (e) {
|
|
133
|
+
const aborted = options?.signal?.aborted === true || (e instanceof Error && e.name === "AbortError");
|
|
121
134
|
const code = e instanceof BrainError ? e.code : "network";
|
|
122
|
-
record(key, code, !bypass);
|
|
123
|
-
out.push({
|
|
135
|
+
record(key, aborted ? "http" : code, !bypass);
|
|
136
|
+
out.push({
|
|
137
|
+
type: "error",
|
|
138
|
+
reason: aborted ? "aborted" : "error",
|
|
139
|
+
error: errorAssistantMessage(model, code, e instanceof Error ? e.message : String(e), aborted ? "aborted" : "error"),
|
|
140
|
+
});
|
|
124
141
|
}
|
|
125
142
|
})();
|
|
126
143
|
return out;
|
package/dist/brain/degrading.js
CHANGED
|
@@ -91,8 +91,20 @@ export function passthroughStream(brain, model, context, options) {
|
|
|
91
91
|
void (async () => {
|
|
92
92
|
try {
|
|
93
93
|
const inner = await brain.stream(model, context, options);
|
|
94
|
-
|
|
94
|
+
let sawTerminal = false;
|
|
95
|
+
for await (const ev of inner) {
|
|
96
|
+
if (ev.type === "done" || ev.type === "error")
|
|
97
|
+
sawTerminal = true;
|
|
95
98
|
out.push(ev);
|
|
99
|
+
}
|
|
100
|
+
if (!sawTerminal) {
|
|
101
|
+
await inner.result();
|
|
102
|
+
out.push({
|
|
103
|
+
type: "error",
|
|
104
|
+
reason: "error",
|
|
105
|
+
error: errorMessageOf(model, new Error("brain stream ended without a terminal event"), false),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
96
108
|
}
|
|
97
109
|
catch (e) {
|
|
98
110
|
const aborted = options?.signal?.aborted === true ||
|
package/dist/brain/failover.js
CHANGED
|
@@ -53,6 +53,8 @@ export function createFailoverBrain(brains) {
|
|
|
53
53
|
...(lastFailedCode !== undefined ? { errorCode: lastFailedCode } : {}),
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
if (!sawContent && heldStart)
|
|
57
|
+
out.push(heldStart);
|
|
56
58
|
out.push(terminal ?? {
|
|
57
59
|
type: "done",
|
|
58
60
|
reason: message.stopReason,
|
package/dist/brain/openai.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {} from "../internal/llm.js";
|
|
2
2
|
import { repairTextToolCalls } from "./tool-call-repair.js";
|
|
3
3
|
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
|
|
4
|
-
import { createRepetitionPoll, createWalltimeGate } from "./stream-shared.js";
|
|
4
|
+
import { createRepetitionPoll, createWalltimeGate, parseStreamedToolArgs } from "./stream-shared.js";
|
|
5
|
+
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
5
6
|
import { WALLTIME_CUTOFF_MESSAGE } from "./walltime.js";
|
|
6
7
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
8
|
import { OPENAI_RESERVED, applyExtraBody, stripAuthHeaders } from "./request-params.js";
|
|
@@ -12,23 +13,11 @@ function closeToolCallAccum(acc) {
|
|
|
12
13
|
return acc.closedTc;
|
|
13
14
|
if (!acc.name)
|
|
14
15
|
return undefined;
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
const parsed = JSON.parse(acc.args);
|
|
19
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
20
|
-
return undefined;
|
|
21
|
-
args = parsed;
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
args = {};
|
|
29
|
-
}
|
|
16
|
+
const args = parseStreamedToolArgs(acc.args);
|
|
17
|
+
if (args === undefined)
|
|
18
|
+
return undefined;
|
|
30
19
|
if (!acc.id)
|
|
31
|
-
acc.id =
|
|
20
|
+
acc.id = mintFallbackToolCallId("call_");
|
|
32
21
|
acc.closedTc = { type: "toolCall", id: acc.id, name: acc.name, arguments: args };
|
|
33
22
|
return acc.closedTc;
|
|
34
23
|
}
|
|
@@ -356,6 +345,8 @@ export function createOpenAIBrain(config = {}) {
|
|
|
356
345
|
const toolAccum = new Map();
|
|
357
346
|
let finishReason = null;
|
|
358
347
|
let usageRaw;
|
|
348
|
+
let malformedFrames = 0;
|
|
349
|
+
let malformedSample;
|
|
359
350
|
const thinkingBlock = { type: "thinking", thinking: "" };
|
|
360
351
|
const textBlock = { type: "text", text: "" };
|
|
361
352
|
const pushReasoningDelta = (delta) => {
|
|
@@ -438,6 +429,8 @@ export function createOpenAIBrain(config = {}) {
|
|
|
438
429
|
chunk = JSON.parse(data);
|
|
439
430
|
}
|
|
440
431
|
catch {
|
|
432
|
+
malformedFrames++;
|
|
433
|
+
malformedSample ??= data.slice(0, 160);
|
|
441
434
|
return;
|
|
442
435
|
}
|
|
443
436
|
if (chunk.usage)
|
|
@@ -535,6 +528,12 @@ export function createOpenAIBrain(config = {}) {
|
|
|
535
528
|
text: `\n[note: ${malformed.length} tool call(s) were truncated (finish_reason="${finishReason ?? "?"}") and dropped — re-issue them next turn: ${malformed.join("; ")}]`,
|
|
536
529
|
});
|
|
537
530
|
}
|
|
531
|
+
if (malformedFrames > 0) {
|
|
532
|
+
finalContent.push({
|
|
533
|
+
type: "text",
|
|
534
|
+
text: `\n[note: ${malformedFrames} provider stream frame(s) were not valid JSON and were dropped — the streamed output may be missing content (first sample: ${JSON.stringify(malformedSample)})]`,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
538
537
|
const truncatedEmpty = finishReason === "length" && noUsableContent && !toolError;
|
|
539
538
|
const dynamicCut = truncatedEmpty && options?.maxTokensDynamic === true;
|
|
540
539
|
const safetyCut = finishReason === "content_filter";
|
|
@@ -557,7 +556,7 @@ export function createOpenAIBrain(config = {}) {
|
|
|
557
556
|
? `[length_empty] response truncated at max_tokens (finish_reason="length", ${maxTokensNote}) with no answer text — the model spent the whole budget on reasoning (${accumReasoning.length} reasoning chars). Raise max_tokens, or disable thinking for this task.`
|
|
558
557
|
: `[length_empty] response truncated at max_tokens (finish_reason="length", ${maxTokensNote}) with no answer text — raise max_tokens`
|
|
559
558
|
: emptyNoFinish
|
|
560
|
-
? `[stream_torn] model stream ended with no content and no finish_reason — the response was lost (a torn stream or an in-band provider error frame)`
|
|
559
|
+
? `[stream_torn] model stream ended with no content and no finish_reason — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
|
|
561
560
|
: undefined));
|
|
562
561
|
const errored = walltime.cut ||
|
|
563
562
|
degenerate ||
|
|
@@ -566,9 +565,13 @@ export function createOpenAIBrain(config = {}) {
|
|
|
566
565
|
emptyNoFinish ||
|
|
567
566
|
(toolError !== undefined && noUsableContent) ||
|
|
568
567
|
(truncatedEmpty && !dynamicCut);
|
|
569
|
-
const errorKind =
|
|
570
|
-
? "
|
|
571
|
-
:
|
|
568
|
+
const errorKind = walltime.cut
|
|
569
|
+
? "walltime_cutoff"
|
|
570
|
+
: degenerate
|
|
571
|
+
? "degenerate"
|
|
572
|
+
: !safetyCut && !refused && toolError === undefined && truncatedEmpty && !dynamicCut
|
|
573
|
+
? "length_empty"
|
|
574
|
+
: undefined;
|
|
572
575
|
const finalMessage = {
|
|
573
576
|
...partial,
|
|
574
577
|
content: finalContent.length > 0 ? finalContent : [{ type: "text", text: "" }],
|
package/dist/brain/repetition.js
CHANGED
|
@@ -11,6 +11,15 @@ const SEGMENT_MAX = 120;
|
|
|
11
11
|
function clipSegment(s) {
|
|
12
12
|
return s.length > SEGMENT_MAX ? s.slice(0, SEGMENT_MAX) : s;
|
|
13
13
|
}
|
|
14
|
+
function trailingRepeats(text, unit, cap) {
|
|
15
|
+
let n = 0;
|
|
16
|
+
let end = text.length;
|
|
17
|
+
while (n < cap && end - unit.length >= 0 && text.startsWith(unit, end - unit.length)) {
|
|
18
|
+
n++;
|
|
19
|
+
end -= unit.length;
|
|
20
|
+
}
|
|
21
|
+
return n;
|
|
22
|
+
}
|
|
14
23
|
function looksLikeCodeLineUnit(unit) {
|
|
15
24
|
if (!unit.includes("\n")) {
|
|
16
25
|
return false;
|
|
@@ -59,7 +68,7 @@ export function inspectDegenerate(text) {
|
|
|
59
68
|
if (!looksLikeCodeLineUnit(unit)) {
|
|
60
69
|
return { degenerate: true, cut: event, spared };
|
|
61
70
|
}
|
|
62
|
-
if (t === WINDOW && reps
|
|
71
|
+
if (t === WINDOW && reps * p >= t - p && trailingRepeats(text, unit, SATURATION_REPS) >= SATURATION_REPS) {
|
|
63
72
|
return { degenerate: true, cut: event, spared };
|
|
64
73
|
}
|
|
65
74
|
if (!sparedUnitLoop) {
|
package/dist/brain/routing.js
CHANGED
|
@@ -17,7 +17,11 @@ export function createRoutingBrain(routes, opts = {}) {
|
|
|
17
17
|
const by = opts.by ?? "provider";
|
|
18
18
|
const stream = (model, context, options) => {
|
|
19
19
|
const key = by === "api" ? model.api : model.provider;
|
|
20
|
-
const
|
|
20
|
+
const routed = Object.hasOwn(routes, key) ? routes[key] : undefined;
|
|
21
|
+
const matched = routed !== null && typeof routed === "object" && typeof routed.stream === "function"
|
|
22
|
+
? routed
|
|
23
|
+
: undefined;
|
|
24
|
+
const target = matched ?? opts.fallback;
|
|
21
25
|
if (!target) {
|
|
22
26
|
const out = createAssistantMessageEventStream();
|
|
23
27
|
out.push({
|
|
@@ -4,7 +4,11 @@ export function runWithStatusSink(emit, fn) {
|
|
|
4
4
|
return statusSinkStore.run({ emit }, fn);
|
|
5
5
|
}
|
|
6
6
|
export function emitBrainStatus(status) {
|
|
7
|
-
|
|
7
|
+
try {
|
|
8
|
+
statusSinkStore.getStore()?.emit(status);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
}
|
|
8
12
|
}
|
|
9
13
|
const telemetrySinkStore = new AsyncLocalStorage();
|
|
10
14
|
export function runWithBrainTelemetry(emit, fn) {
|
|
@@ -8,7 +8,7 @@ const DEFAULT_MAX_RETRIES = 10;
|
|
|
8
8
|
const MAX_RETRIES_ENV_CEILING = 15;
|
|
9
9
|
const MAX_RETRIES_ENV = "SEMA_MAX_RETRIES";
|
|
10
10
|
export function resolveMaxRetries(configured) {
|
|
11
|
-
if (configured !== undefined)
|
|
11
|
+
if (configured !== undefined && Number.isFinite(configured))
|
|
12
12
|
return configured;
|
|
13
13
|
const raw = process.env[MAX_RETRIES_ENV];
|
|
14
14
|
if (raw !== undefined && raw.trim() !== "") {
|
|
@@ -88,10 +88,13 @@ export function runStreamingBrain(args) {
|
|
|
88
88
|
.catch((err) => {
|
|
89
89
|
const aborted = signal?.aborted === true || isAbortError(err);
|
|
90
90
|
terminalRetryPhase = "gave_up";
|
|
91
|
-
|
|
91
|
+
const walltimeCut = isWalltimeCutoff(err);
|
|
92
|
+
terminalRetryDetail = aborted ? "cancelled while retrying" : walltimeCut ? "wall-clock deadline reached while retrying" : "retries exhausted";
|
|
92
93
|
const errorMsg = emptyAssistant(model);
|
|
93
94
|
errorMsg.stopReason = aborted ? "aborted" : "error";
|
|
94
95
|
errorMsg.errorMessage = err instanceof Error ? err.message : String(err);
|
|
96
|
+
if (walltimeCut)
|
|
97
|
+
errorMsg.errorKind = "walltime_cutoff";
|
|
95
98
|
errorMsg.usageMissing = true;
|
|
96
99
|
out.push({ type: "error", reason: aborted ? "aborted" : "error", error: errorMsg });
|
|
97
100
|
})
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import { inspectDegenerate } from "./repetition.js";
|
|
2
|
+
export function parseStreamedToolArgs(json) {
|
|
3
|
+
if (!json)
|
|
4
|
+
return {};
|
|
5
|
+
let parsed;
|
|
6
|
+
try {
|
|
7
|
+
parsed = JSON.parse(json);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
13
|
+
return undefined;
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
2
16
|
export function createRepetitionPoll() {
|
|
3
17
|
let repCut;
|
|
4
18
|
const repSpared = new Map();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AssistantMessage } from "../internal/llm.js";
|
|
2
|
+
export type TerminalCauseCarrier = Pick<AssistantMessage, "errorKind" | "errorMessage">;
|
|
3
|
+
export declare function isDegenerateCutMessage(message: TerminalCauseCarrier): boolean;
|
|
4
|
+
export declare function isWalltimeCutMessage(message: TerminalCauseCarrier): boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DEGENERATE_MESSAGE } from "./repetition.js";
|
|
2
|
+
import { WALLTIME_CUTOFF_MESSAGE } from "./walltime.js";
|
|
3
|
+
export function isDegenerateCutMessage(message) {
|
|
4
|
+
if (message.errorKind === "degenerate")
|
|
5
|
+
return true;
|
|
6
|
+
return message.errorMessage === DEGENERATE_MESSAGE;
|
|
7
|
+
}
|
|
8
|
+
export function isWalltimeCutMessage(message) {
|
|
9
|
+
if (message.errorKind === "walltime_cutoff")
|
|
10
|
+
return true;
|
|
11
|
+
return message.errorMessage === WALLTIME_CUTOFF_MESSAGE;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function mintFallbackToolCallId(prefix: string): string;
|