@sema-agent/core 3.0.0 → 5.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 +2382 -0
- package/dist/agents/cascade.d.ts +3 -1
- package/dist/agents/cascade.js +31 -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 +48 -24
- package/dist/agents/retain-ledger.d.ts +2 -0
- package/dist/agents/retain-ledger.js +6 -5
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/roster-store.js +3 -5
- package/dist/agents/send-message-tool.d.ts +1 -0
- package/dist/agents/send-message-tool.js +2 -0
- package/dist/agents/subagent.d.ts +3 -1
- package/dist/agents/subagent.js +79 -94
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +21 -24
- package/dist/agents/tool-filter.js +6 -7
- package/dist/agents/verify.d.ts +1 -0
- package/dist/agents/verify.js +15 -14
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/openai.js +24 -21
- 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 +2 -1
- package/dist/core/arg-summary.d.ts +21 -1
- package/dist/core/arg-summary.js +61 -14
- package/dist/core/auto-compaction.d.ts +1 -0
- package/dist/core/auto-compaction.js +16 -25
- 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 +6 -13
- package/dist/core/checkpoint-store.js +11 -5
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/fs-write-gate-policy.js +2 -3
- package/dist/core/hooks.d.ts +4 -1
- package/dist/core/hooks.js +8 -18
- package/dist/core/lsp.js +4 -3
- package/dist/core/mailbox-store.d.ts +7 -10
- package/dist/core/mcp.d.ts +7 -10
- package/dist/core/mcp.js +7 -9
- package/dist/core/memory-engine/engine.d.ts +0 -1
- package/dist/core/memory-engine/engine.js +24 -34
- package/dist/core/memory-engine/file-backend.d.ts +1 -2
- package/dist/core/memory-engine/file-backend.js +25 -36
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +60 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/permission-rules.js +2 -3
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +2 -17
- package/dist/core/runner/assemble-result.d.ts +2 -7
- package/dist/core/runner/assemble-result.js +3 -4
- 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 +36 -61
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +84 -172
- package/dist/core/runner/session-rule-policy.js +7 -5
- 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/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session-reconcile.js +1 -2
- package/dist/core/skill-tool-specifier.js +2 -3
- package/dist/core/skills-directory.js +2 -3
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- 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 +8 -13
- package/dist/core/task-registry-shared.d.ts +23 -1
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +6 -1
- package/dist/core/task-registry.js +52 -87
- package/dist/core/task-tool-shape.d.ts +0 -2
- package/dist/core/task-tool-shape.js +2 -5
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.d.ts +1 -2
- package/dist/core/tool-name-aliases.js +42 -59
- package/dist/core/tool-policy.js +21 -17
- 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 +9 -9
- package/dist/core/types.d.ts +15 -12
- package/dist/core/untrusted-egress.d.ts +4 -2
- package/dist/core/untrusted-egress.js +28 -14
- 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/utils.js +4 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
- package/dist/engine/execution-env/node-execution-env.js +1 -1
- 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 +12 -14
- package/dist/engine/loop/types.d.ts +11 -9
- package/dist/engine/session/memory-repo.d.ts +2 -6
- package/dist/engine/session/session.d.ts +5 -30
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -3
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/orchestration/run-spec.js +6 -4
- package/dist/orchestration/run-workflow-tool.js +3 -36
- package/dist/orchestration/workflow-governance.js +2 -1
- package/dist/orchestration/workflow.d.ts +4 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/packs/sema-default.js +1 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/prompts/default.js +1 -3
- package/dist/prompts/simple-sections.d.ts +0 -1
- package/dist/prompts/simple-sections.js +0 -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/mailbox-store.d.ts +2 -6
- package/dist/stores/file/session-policy-store.js +15 -1
- package/dist/stores/file/session-store.d.ts +2 -6
- 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 +1 -0
- package/dist/tools/fs/bash-readonly-classifier.js +60 -25
- package/dist/tools/fs/fs-bash.js +330 -104
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/fs-write.js +0 -1
- 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/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +7 -5
- package/dist/tools/worktree.d.ts +2 -7
- package/dist/tools/worktree.js +3 -1
- package/package.json +6 -2
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: "" }],
|
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: "" }],
|
|
@@ -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;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
1
2
|
const TAG_RE = /<(tool_call|function_call|tool_calls|function_calls)>\s*([\s\S]*?)\s*<\/\1>/gi;
|
|
2
3
|
const FENCE_RE = /```(?:json|tool_call|tool|function)?\s*([\s\S]*?)```/gi;
|
|
3
4
|
function asRecord(v) {
|
|
@@ -41,7 +42,7 @@ function extractCall(obj, toolNames, framed) {
|
|
|
41
42
|
}
|
|
42
43
|
return {
|
|
43
44
|
type: "toolCall",
|
|
44
|
-
id: `repair_${name}_
|
|
45
|
+
id: mintFallbackToolCallId(`repair_${name}_`),
|
|
45
46
|
name,
|
|
46
47
|
arguments: args,
|
|
47
48
|
};
|
|
@@ -1,2 +1,22 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type RedactionConfidence = "high" | "medium" | "low";
|
|
2
|
+
export interface RedactionFinding {
|
|
3
|
+
kind: string;
|
|
4
|
+
span: readonly [number, number];
|
|
5
|
+
confidence: RedactionConfidence;
|
|
6
|
+
marker: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RedactionReport {
|
|
9
|
+
findings: RedactionFinding[];
|
|
10
|
+
preexistingMarkers?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface RedactionPass {
|
|
13
|
+
kind: string;
|
|
14
|
+
confidence: RedactionConfidence;
|
|
15
|
+
marker: string;
|
|
16
|
+
re: RegExp;
|
|
17
|
+
replace: string | ((match: string, ...groups: string[]) => string);
|
|
18
|
+
}
|
|
19
|
+
export declare function runRedactionPasses(input: string, passes: readonly RedactionPass[], report?: RedactionReport): string;
|
|
20
|
+
export declare const SECRET_PASSES: readonly RedactionPass[];
|
|
21
|
+
export declare function scrubSecrets(s: string, report?: RedactionReport): string;
|
|
2
22
|
export declare function primaryActivityArg(args: unknown): string | undefined;
|
package/dist/core/arg-summary.js
CHANGED
|
@@ -1,24 +1,71 @@
|
|
|
1
1
|
const ACTIVITY_ARG_MAX = 80;
|
|
2
2
|
const SENSITIVE_KEY = /token|secret|key|password|passwd|credential|auth/i;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
function mapBackOnePass(edits, pos) {
|
|
4
|
+
let delta = 0;
|
|
5
|
+
for (const e of edits) {
|
|
6
|
+
const outStart = e.at + delta;
|
|
7
|
+
if (pos < outStart)
|
|
8
|
+
return pos - delta;
|
|
9
|
+
const outEnd = outStart + e.insertedLen;
|
|
10
|
+
if (pos < outEnd)
|
|
11
|
+
return e.at;
|
|
12
|
+
delta += e.insertedLen - e.removedLen;
|
|
13
|
+
}
|
|
14
|
+
return pos - delta;
|
|
15
|
+
}
|
|
16
|
+
const PREEXISTING_MARKER_RE = /\[redacted(?:-[a-z]+)?\]/g;
|
|
17
|
+
export function runRedactionPasses(input, passes, report) {
|
|
18
|
+
if (report !== undefined && report.preexistingMarkers === undefined) {
|
|
19
|
+
report.preexistingMarkers = input.match(PREEXISTING_MARKER_RE)?.length ?? 0;
|
|
20
|
+
}
|
|
21
|
+
const batches = [];
|
|
22
|
+
let cur = input;
|
|
23
|
+
for (const pass of passes) {
|
|
24
|
+
const edits = [];
|
|
25
|
+
cur = cur.replace(pass.re, (...args) => {
|
|
26
|
+
const match = args[0];
|
|
27
|
+
const offset = args[args.length - 2];
|
|
28
|
+
const groups = args.slice(1, -2);
|
|
29
|
+
const inserted = typeof pass.replace === "string"
|
|
30
|
+
? pass.replace.replace(/\$(\d)/g, (_m, d) => groups[Number(d) - 1] ?? "")
|
|
31
|
+
: pass.replace(match, ...groups);
|
|
32
|
+
if (inserted !== match) {
|
|
33
|
+
if (report !== undefined) {
|
|
34
|
+
let s0 = offset;
|
|
35
|
+
let e0 = offset + match.length;
|
|
36
|
+
for (let i = batches.length - 1; i >= 0; i--) {
|
|
37
|
+
s0 = mapBackOnePass(batches[i], s0);
|
|
38
|
+
e0 = mapBackOnePass(batches[i], e0);
|
|
39
|
+
}
|
|
40
|
+
report.findings.push({ kind: pass.kind, confidence: pass.confidence, span: [s0, e0], marker: pass.marker });
|
|
41
|
+
}
|
|
42
|
+
edits.push({ at: offset, removedLen: match.length, insertedLen: inserted.length });
|
|
43
|
+
}
|
|
44
|
+
return inserted;
|
|
45
|
+
});
|
|
46
|
+
batches.push(edits);
|
|
47
|
+
}
|
|
48
|
+
return cur;
|
|
49
|
+
}
|
|
50
|
+
export const SECRET_PASSES = [
|
|
51
|
+
{ kind: "prefixed-token", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])(?:sk|pk|rk|gh[opsur])[-_][A-Za-z0-9_-]{8,}/g, replace: "[redacted]" },
|
|
52
|
+
{ kind: "prefixed-token", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])AIza[A-Za-z0-9_-]{20,}/g, replace: "[redacted]" },
|
|
53
|
+
{ kind: "prefixed-token", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])xox[baprs]-[A-Za-z0-9-]{10,}/g, replace: "[redacted]" },
|
|
54
|
+
{ kind: "prefixed-token", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{12,}/g, replace: "[redacted]" },
|
|
55
|
+
{ kind: "prefixed-token", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{20,}/g, replace: "[redacted]" },
|
|
56
|
+
{ kind: "jwt", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])eyJ[A-Za-z0-9._-]{20,}/g, replace: "[redacted]" },
|
|
57
|
+
{ kind: "private-key-block", confidence: "high", marker: "[redacted]", re: /-----BEGIN[ A-Z]*PRIVATE KEY-----[\s\S]*?(?:-----END[ A-Z]*PRIVATE KEY-----|$)/g, replace: "[redacted]" },
|
|
58
|
+
{ kind: "bearer-token", confidence: "high", marker: "[redacted]", re: /(?<![A-Za-z0-9])Bearer\s+[A-Za-z0-9._~+/-]{8,}=*/gi, replace: "[redacted]" },
|
|
12
59
|
{
|
|
60
|
+
kind: "keyword-value",
|
|
61
|
+
confidence: "medium",
|
|
62
|
+
marker: "[redacted]",
|
|
13
63
|
re: /(?<![A-Za-z0-9])((?:token|secret|key|password|passwd|credential|authorization)["']?\s*[=:]\s*["']?)[^\s"';|&]{4,}/gi,
|
|
14
64
|
replace: "$1[redacted]",
|
|
15
65
|
},
|
|
16
66
|
];
|
|
17
|
-
export function scrubSecrets(s) {
|
|
18
|
-
|
|
19
|
-
for (const { re, replace } of SECRET_PATTERNS)
|
|
20
|
-
out = out.replace(re, replace);
|
|
21
|
-
return out;
|
|
67
|
+
export function scrubSecrets(s, report) {
|
|
68
|
+
return runRedactionPasses(s, SECRET_PASSES, report);
|
|
22
69
|
}
|
|
23
70
|
function truncCodePoints(s) {
|
|
24
71
|
const cp = Array.from(s);
|
|
@@ -23,6 +23,7 @@ export interface CompactionWindowSafetyInfo {
|
|
|
23
23
|
export declare const STALE_ANCHOR_STRUCTURAL_MARGIN = 2;
|
|
24
24
|
export declare function sanitizeCompactionSettings(settings: CompactionSettings, contextWindow: number | undefined): CompactionSettings;
|
|
25
25
|
export interface MaybeCompactOptions {
|
|
26
|
+
onNotifyError?: (failure: import("./safe-notify.js").SafeNotifyFailure) => void;
|
|
26
27
|
session: Session;
|
|
27
28
|
epochDeclaredSections?: import("../prompt-assembly/epoch.js").EpochDeclaredSections;
|
|
28
29
|
centerAdoption?: {
|
|
@@ -5,6 +5,7 @@ import { selectCompactionEpoch } from "../prompt-assembly/epoch.js";
|
|
|
5
5
|
import { SEMA_DEFAULT_PACK } from "../prompt-assembly/packs/sema-default.js";
|
|
6
6
|
import { brainToRuntime } from "./runtime.js";
|
|
7
7
|
import { defuseFenceMarkers, sanitizeUntrustedText } from "./untrusted-text.js";
|
|
8
|
+
import { createSafeNotifier } from "./safe-notify.js";
|
|
8
9
|
export const DEFAULT_COMPACTION_INSTRUCTIONS = "Preserve verbatim every code, identifier, name, number, file path, error message, credential " +
|
|
9
10
|
"reference, and explicit user instruction or decision mentioned anywhere in the conversation. " +
|
|
10
11
|
"When in doubt, keep the specific detail rather than generalizing it.";
|
|
@@ -96,6 +97,7 @@ export async function maybeCompact(opts) {
|
|
|
96
97
|
return { contextUsage, compacted: false, noop: true };
|
|
97
98
|
}
|
|
98
99
|
const trigger = opts.trigger ?? "auto";
|
|
100
|
+
const notifier = createSafeNotifier(opts.onNotifyError !== undefined ? { onError: opts.onNotifyError } : undefined);
|
|
99
101
|
let hookInstructions;
|
|
100
102
|
if (opts.preCompact) {
|
|
101
103
|
let pre;
|
|
@@ -183,11 +185,7 @@ export async function maybeCompact(opts) {
|
|
|
183
185
|
}
|
|
184
186
|
const contentTokens = dry !== undefined ? Math.ceil((dry.conversationChars + (prep.value.previousSummary?.length ?? 0)) / cpt) : 0;
|
|
185
187
|
const observeWindowSafety = (info) => {
|
|
186
|
-
|
|
187
|
-
opts.onWindowSafety?.(info);
|
|
188
|
-
}
|
|
189
|
-
catch {
|
|
190
|
-
}
|
|
188
|
+
notifier.notify(() => opts.onWindowSafety?.(info), "compaction.onWindowSafety");
|
|
191
189
|
};
|
|
192
190
|
if (wantsWindowSafety && dry !== undefined) {
|
|
193
191
|
const tolerance = settings.clampTolerance ?? DEFAULT_CLAMP_TOLERANCE;
|
|
@@ -498,27 +496,20 @@ export async function maybeCompact(opts) {
|
|
|
498
496
|
}
|
|
499
497
|
catch {
|
|
500
498
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
catch {
|
|
510
|
-
}
|
|
499
|
+
const messagesCompacted = prep.value.messagesToSummarize.length;
|
|
500
|
+
notifier.notify(() => opts.onCompaction?.({
|
|
501
|
+
messagesCompacted,
|
|
502
|
+
freedTokens,
|
|
503
|
+
tokensBefore: summaryTokensBefore,
|
|
504
|
+
reused,
|
|
505
|
+
}), "compaction.onCompaction");
|
|
511
506
|
if (opts.postCompact) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
catch {
|
|
521
|
-
}
|
|
507
|
+
await notifier.notifyAsync(() => opts.postCompact?.({
|
|
508
|
+
trigger,
|
|
509
|
+
summary,
|
|
510
|
+
tokensBefore: summaryTokensBefore,
|
|
511
|
+
...(postTriggerTokens !== undefined ? { tokensAfter: postTriggerTokens } : {}),
|
|
512
|
+
}), "compaction.postCompact");
|
|
522
513
|
}
|
|
523
514
|
const endAt = Date.now();
|
|
524
515
|
return {
|
|
@@ -73,18 +73,24 @@ export declare function canAccessAgentRecord(record: Pick<BackgroundAgentRecord,
|
|
|
73
73
|
sessionId?: string;
|
|
74
74
|
}): boolean;
|
|
75
75
|
export declare const STALE_RUNNING_REAP_ATTRIBUTION = "the host process was interrupted while this agent was running \u2014 its in-process state was lost. Check its worktree / output file for partial work before assuming the task landed (stale running row reaped).";
|
|
76
|
+
export interface BackgroundAgentUpdateIfExpect {
|
|
77
|
+
rev: number;
|
|
78
|
+
status?: BackgroundAgentRecord["status"];
|
|
79
|
+
parkedCheckpointToken?: string | null;
|
|
80
|
+
parkClaimId?: string | null;
|
|
81
|
+
}
|
|
82
|
+
export interface BackgroundAgentReapOptions {
|
|
83
|
+
maxAgeMs?: number;
|
|
84
|
+
keep?: number;
|
|
85
|
+
staleRunningMaxAgeMs?: number;
|
|
86
|
+
}
|
|
76
87
|
export interface BackgroundAgentStore {
|
|
77
88
|
put(record: BackgroundAgentRecord): Promise<void>;
|
|
78
89
|
get(handle: string, scope: string): Promise<BackgroundAgentRecord | null>;
|
|
79
90
|
update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
|
|
80
91
|
rev: number;
|
|
81
92
|
}): Promise<boolean>;
|
|
82
|
-
updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect:
|
|
83
|
-
rev: number;
|
|
84
|
-
status?: BackgroundAgentRecord["status"];
|
|
85
|
-
parkedCheckpointToken?: string | null;
|
|
86
|
-
parkClaimId?: string | null;
|
|
87
|
-
}): Promise<boolean>;
|
|
93
|
+
updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: BackgroundAgentUpdateIfExpect): Promise<boolean>;
|
|
88
94
|
listBySession(scope: string, sessionId: string, opts?: {
|
|
89
95
|
status?: BackgroundAgentRecord["status"];
|
|
90
96
|
limit?: number;
|
|
@@ -97,11 +103,7 @@ export interface BackgroundAgentStore {
|
|
|
97
103
|
rev: number;
|
|
98
104
|
}): Promise<boolean>;
|
|
99
105
|
listScopes?(): Promise<string[]>;
|
|
100
|
-
reap(scope: string, now: number, opts?:
|
|
101
|
-
maxAgeMs?: number;
|
|
102
|
-
keep?: number;
|
|
103
|
-
staleRunningMaxAgeMs?: number;
|
|
104
|
-
}): Promise<number>;
|
|
106
|
+
reap(scope: string, now: number, opts?: BackgroundAgentReapOptions): Promise<number>;
|
|
105
107
|
}
|
|
106
108
|
export declare function reconcileParkedAgents(stores: {
|
|
107
109
|
agentStore: BackgroundAgentStore;
|
|
@@ -114,6 +116,13 @@ export declare function reconcileParkedAgents(stores: {
|
|
|
114
116
|
failed: number;
|
|
115
117
|
rolledBack: number;
|
|
116
118
|
}>;
|
|
119
|
+
export interface BackgroundAgentQuery {
|
|
120
|
+
scope: string;
|
|
121
|
+
sessionId?: string;
|
|
122
|
+
status?: BackgroundAgentRecord["status"];
|
|
123
|
+
limit?: number;
|
|
124
|
+
}
|
|
125
|
+
export declare function queryBackgroundAgents(rows: Iterable<BackgroundAgentRecord>, query: BackgroundAgentQuery): BackgroundAgentRowSummary[];
|
|
117
126
|
export declare class InMemoryBackgroundAgentStore implements BackgroundAgentStore {
|
|
118
127
|
private rows;
|
|
119
128
|
private key;
|
|
@@ -122,12 +131,7 @@ export declare class InMemoryBackgroundAgentStore implements BackgroundAgentStor
|
|
|
122
131
|
update(handle: string, scope: string, record: BackgroundAgentRecord, expect: {
|
|
123
132
|
rev: number;
|
|
124
133
|
}): Promise<boolean>;
|
|
125
|
-
updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect:
|
|
126
|
-
rev: number;
|
|
127
|
-
status?: BackgroundAgentRecord["status"];
|
|
128
|
-
parkedCheckpointToken?: string | null;
|
|
129
|
-
parkClaimId?: string | null;
|
|
130
|
-
}): Promise<boolean>;
|
|
134
|
+
updateIf(handle: string, scope: string, record: BackgroundAgentRecord, expect: BackgroundAgentUpdateIfExpect): Promise<boolean>;
|
|
131
135
|
listBySession(scope: string, sessionId: string, opts?: {
|
|
132
136
|
status?: BackgroundAgentRecord["status"];
|
|
133
137
|
limit?: number;
|
|
@@ -140,9 +144,5 @@ export declare class InMemoryBackgroundAgentStore implements BackgroundAgentStor
|
|
|
140
144
|
rev: number;
|
|
141
145
|
}): Promise<boolean>;
|
|
142
146
|
listScopes(): Promise<string[]>;
|
|
143
|
-
reap(scope: string, now: number, opts?:
|
|
144
|
-
maxAgeMs?: number;
|
|
145
|
-
keep?: number;
|
|
146
|
-
staleRunningMaxAgeMs?: number;
|
|
147
|
-
}): Promise<number>;
|
|
147
|
+
reap(scope: string, now: number, opts?: BackgroundAgentReapOptions): Promise<number>;
|
|
148
148
|
}
|
|
@@ -173,6 +173,23 @@ function toSummary(r) {
|
|
|
173
173
|
...(r.usage !== undefined ? { usage: structuredClone(r.usage) } : {}),
|
|
174
174
|
};
|
|
175
175
|
}
|
|
176
|
+
export function queryBackgroundAgents(rows, query) {
|
|
177
|
+
const out = [];
|
|
178
|
+
for (const r of rows) {
|
|
179
|
+
if (r.scope !== query.scope)
|
|
180
|
+
continue;
|
|
181
|
+
if (query.sessionId !== undefined) {
|
|
182
|
+
const anchor = r.sessionScoped ? r.owner : r.parentSessionId;
|
|
183
|
+
if (anchor !== query.sessionId && r.rootSessionId !== query.sessionId)
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (query.status !== undefined && r.status !== query.status)
|
|
187
|
+
continue;
|
|
188
|
+
out.push(toSummary(r));
|
|
189
|
+
}
|
|
190
|
+
out.sort((a, b) => b.spawnedAt - a.spawnedAt);
|
|
191
|
+
return query.limit !== undefined ? out.slice(0, Math.max(0, query.limit)) : out;
|
|
192
|
+
}
|
|
176
193
|
export class InMemoryBackgroundAgentStore {
|
|
177
194
|
rows = new Map();
|
|
178
195
|
key(handle, scope) {
|
|
@@ -220,31 +237,10 @@ export class InMemoryBackgroundAgentStore {
|
|
|
220
237
|
return this.update(handle, scope, record, { rev: expect.rev });
|
|
221
238
|
}
|
|
222
239
|
async listBySession(scope, sessionId, opts) {
|
|
223
|
-
|
|
224
|
-
for (const r of this.rows.values()) {
|
|
225
|
-
if (r.scope !== scope)
|
|
226
|
-
continue;
|
|
227
|
-
const anchor = r.sessionScoped ? r.owner : r.parentSessionId;
|
|
228
|
-
if (anchor !== sessionId && r.rootSessionId !== sessionId)
|
|
229
|
-
continue;
|
|
230
|
-
if (opts?.status !== undefined && r.status !== opts.status)
|
|
231
|
-
continue;
|
|
232
|
-
out.push(toSummary(r));
|
|
233
|
-
}
|
|
234
|
-
out.sort((a, b) => b.spawnedAt - a.spawnedAt);
|
|
235
|
-
return opts?.limit !== undefined ? out.slice(0, Math.max(0, opts.limit)) : out;
|
|
240
|
+
return queryBackgroundAgents(this.rows.values(), { scope, sessionId, status: opts?.status, limit: opts?.limit });
|
|
236
241
|
}
|
|
237
242
|
async listByScope(scope, opts) {
|
|
238
|
-
|
|
239
|
-
for (const r of this.rows.values()) {
|
|
240
|
-
if (r.scope !== scope)
|
|
241
|
-
continue;
|
|
242
|
-
if (opts?.status !== undefined && r.status !== opts.status)
|
|
243
|
-
continue;
|
|
244
|
-
out.push(toSummary(r));
|
|
245
|
-
}
|
|
246
|
-
out.sort((a, b) => b.spawnedAt - a.spawnedAt);
|
|
247
|
-
return opts?.limit !== undefined ? out.slice(0, Math.max(0, opts.limit)) : out;
|
|
243
|
+
return queryBackgroundAgents(this.rows.values(), { scope, status: opts?.status, limit: opts?.limit });
|
|
248
244
|
}
|
|
249
245
|
async delete(handle, scope, expect) {
|
|
250
246
|
const k = this.key(handle, scope);
|