@sema-agent/core 5.6.0 → 5.8.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 +55 -0
- package/dist/agents/cascade.d.ts +1 -1
- package/dist/agents/subagent.d.ts +4 -7
- package/dist/agents/subagent.js +10 -19
- package/dist/agents/teacher.js +3 -3
- package/dist/agents/team.d.ts +1 -1
- package/dist/agents/team.js +1 -1
- package/dist/agents/verify.js +1 -1
- package/dist/brain/anthropic.js +24 -35
- package/dist/brain/openai.js +22 -33
- package/dist/brain/stream-engine.d.ts +0 -2
- package/dist/brain/stream-engine.js +5 -47
- package/dist/brain/stream-shared.d.ts +0 -10
- package/dist/brain/stream-shared.js +0 -23
- package/dist/brain/terminal-cause.d.ts +0 -1
- package/dist/brain/terminal-cause.js +0 -3
- package/dist/brain/timeout.d.ts +4 -2
- package/dist/brain/timeout.js +4 -14
- package/dist/config/catalog.d.ts +3 -11
- package/dist/config/catalog.js +30 -69
- package/dist/config/defaults.d.ts +0 -3
- package/dist/config/defaults.js +0 -3
- package/dist/core/auto-compaction.d.ts +2 -11
- package/dist/core/auto-compaction.js +3 -67
- package/dist/core/checkpoint-store.d.ts +15 -10
- package/dist/core/checkpoint-store.js +13 -10
- package/dist/core/memory-engine/file-backend.js +20 -17
- package/dist/core/memory-engine/layout.d.ts +5 -0
- package/dist/core/memory-engine/layout.js +72 -47
- package/dist/core/runner/assemble-result.d.ts +2 -8
- package/dist/core/runner/assemble-result.js +15 -16
- package/dist/core/runner/prepare-task.d.ts +26 -6
- package/dist/core/runner/prepare-task.js +203 -141
- package/dist/core/runner/runtask.d.ts +0 -2
- package/dist/core/runner/runtask.js +267 -253
- package/dist/core/secret-env.d.ts +2 -0
- package/dist/core/secret-env.js +16 -4
- package/dist/core/session-policy-store.d.ts +5 -0
- package/dist/core/session-policy-store.js +3 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/task-notification.d.ts +1 -0
- package/dist/core/task-notification.js +3 -0
- package/dist/core/task-registry-agent.d.ts +2 -0
- package/dist/core/task-registry-agent.js +33 -1
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/task-registry.js +9 -1
- package/dist/core/tool-errors.js +10 -2
- package/dist/core/trace.d.ts +1 -8
- package/dist/core/types.d.ts +24 -30
- package/dist/core/usage-window-store.d.ts +39 -0
- package/dist/core/usage-window-store.js +115 -0
- package/dist/engine/execution-env/node-execution-env.js +0 -22
- package/dist/engine/harness/agent-harness.d.ts +0 -7
- package/dist/engine/harness/agent-harness.js +13 -53
- package/dist/engine/harness/types.d.ts +2 -15
- package/dist/engine/llm/types.d.ts +1 -2
- package/dist/engine/loop/agent-loop.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +5 -171
- package/dist/engine/loop/types.d.ts +0 -18
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8 -4
- package/dist/orchestration/goal.js +1 -5
- package/dist/orchestration/run-workflow-tool.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow-governance.d.ts +6 -6
- package/dist/orchestration/workflow-governance.js +33 -47
- package/dist/prompt-assembly/event-registry.js +2 -3
- package/dist/stores/cc/lockfile.js +9 -11
- package/dist/stores/cc/mailbox-store.js +49 -38
- package/dist/stores/file/index.d.ts +3 -0
- package/dist/stores/file/index.js +4 -0
- package/dist/stores/file/usage-window-store.d.ts +9 -0
- package/dist/stores/file/usage-window-store.js +86 -0
- package/dist/tools/fs/fs-bash.d.ts +1 -10
- package/dist/tools/fs/fs-bash.js +20 -60
- package/dist/tools/fs/index.d.ts +0 -2
- package/dist/tools/fs/index.js +1 -2
- package/package.json +1 -4
- package/dist/bin/sema-tb.d.ts +0 -31
- package/dist/bin/sema-tb.js +0 -448
- package/dist/bin/tb-env.d.ts +0 -2
- package/dist/bin/tb-env.js +0 -17
- package/dist/brain/walltime.d.ts +0 -1
- package/dist/brain/walltime.js +0 -1
- package/dist/core/runner/call-cap.d.ts +0 -67
- package/dist/core/runner/call-cap.js +0 -145
- package/dist/core/runner/cut-kill.d.ts +0 -10
- package/dist/core/runner/cut-kill.js +0 -37
|
@@ -6,5 +6,7 @@ export interface SecretEnvFinding {
|
|
|
6
6
|
confidence: RedactionConfidence;
|
|
7
7
|
source: RedactionSource;
|
|
8
8
|
}
|
|
9
|
+
export type ExactEnvNameVerdict = "credential" | "exempt" | "unknown";
|
|
10
|
+
export declare function classifyExactEnvName(key: string, credentialNames: ReadonlySet<string>, exemptNames: ReadonlySet<string>): ExactEnvNameVerdict;
|
|
9
11
|
export declare function isSecretEnvKey(key: string): boolean;
|
|
10
12
|
export declare function scrubSecretEnv(env: NodeJS.ProcessEnv, findings?: SecretEnvFinding[]): NodeJS.ProcessEnv;
|
package/dist/core/secret-env.js
CHANGED
|
@@ -4,16 +4,28 @@ const SECRET_ENV_EXACT_NAMES = new Set([
|
|
|
4
4
|
"PGPASSWORD", "MYSQL_PWD", "SSHPASS", "GPG_PASSPHRASE", "GH_PAT", "GITHUB_PAT",
|
|
5
5
|
"NPM_CONFIG__AUTH", "NPM_CONFIG__AUTHTOKEN",
|
|
6
6
|
]);
|
|
7
|
+
const NON_SECRET_ENV_EXACT_NAMES = new Set([
|
|
8
|
+
"PARTITION_KEY", "SORT_KEY", "RANGE_KEY", "IDEMPOTENCY_KEY", "CACHE_KEY", "ROUTING_KEY",
|
|
9
|
+
]);
|
|
7
10
|
const SECRET_ENV_KIND_CONFIDENCE = {
|
|
8
11
|
"exact-name": "high",
|
|
9
12
|
"suffix-rule": "medium",
|
|
10
13
|
};
|
|
14
|
+
export function classifyExactEnvName(key, credentialNames, exemptNames) {
|
|
15
|
+
const upper = key.toUpperCase();
|
|
16
|
+
if (credentialNames.has(upper))
|
|
17
|
+
return "credential";
|
|
18
|
+
if (exemptNames.has(upper))
|
|
19
|
+
return "exempt";
|
|
20
|
+
return "unknown";
|
|
21
|
+
}
|
|
11
22
|
function classifySecretEnvKey(key) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (SECRET_ENV_EXACT_NAMES.has(key.toUpperCase()))
|
|
23
|
+
const byExactName = classifyExactEnvName(key, SECRET_ENV_EXACT_NAMES, NON_SECRET_ENV_EXACT_NAMES);
|
|
24
|
+
if (byExactName === "credential")
|
|
15
25
|
return "exact-name";
|
|
16
|
-
|
|
26
|
+
if (byExactName === "exempt")
|
|
27
|
+
return undefined;
|
|
28
|
+
return SECRET_ENV_RE.test(key) ? "suffix-rule" : undefined;
|
|
17
29
|
}
|
|
18
30
|
export function isSecretEnvKey(key) {
|
|
19
31
|
return classifySecretEnvKey(key) !== undefined;
|
|
@@ -26,6 +26,7 @@ export interface SessionPolicyStore {
|
|
|
26
26
|
listBySession?(sessionId: string): Promise<SessionRulesRecord[]>;
|
|
27
27
|
deleteBySession?(sessionId: string): Promise<void>;
|
|
28
28
|
}
|
|
29
|
+
declare function lexWithin(root: string, p: string): boolean;
|
|
29
30
|
export declare function normalizeRules(rules: SessionPermissionRules): SessionPermissionRules;
|
|
30
31
|
export declare function loosenReasons(prior: SessionPermissionRules, next: SessionPermissionRules): string[];
|
|
31
32
|
export declare function stripRev(rules: SessionPermissionRules): SessionPermissionRules;
|
|
@@ -37,3 +38,7 @@ export declare class InMemorySessionPolicyStore implements SessionPolicyStore {
|
|
|
37
38
|
listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
|
|
38
39
|
deleteBySession(sessionId: string): Promise<void>;
|
|
39
40
|
}
|
|
41
|
+
export declare const __testables: {
|
|
42
|
+
lexWithin: typeof lexWithin;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
|
+
import { isWinFormPath } from "../tools/fs/safety.js";
|
|
2
3
|
export class SessionPolicyError extends Error {
|
|
3
4
|
code;
|
|
4
5
|
constructor(code, message) {
|
|
@@ -9,7 +10,7 @@ export class SessionPolicyError extends Error {
|
|
|
9
10
|
}
|
|
10
11
|
function lexWithin(root, p) {
|
|
11
12
|
const fold = (x) => {
|
|
12
|
-
let n = x.replace(/\\/g, "/");
|
|
13
|
+
let n = isWinFormPath(x) ? x.replace(/\\/g, "/") : x;
|
|
13
14
|
if (/^[A-Za-z]:(\/|$)/.test(n))
|
|
14
15
|
n = n[0].toLowerCase() + n.slice(1);
|
|
15
16
|
return n;
|
|
@@ -116,3 +117,4 @@ export class InMemorySessionPolicyStore {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
export const __testables = { lexWithin };
|
|
@@ -157,8 +157,8 @@ export async function checkpointListByScopeSummaries(store) {
|
|
|
157
157
|
scope: "tenant-a",
|
|
158
158
|
sessionId: "out-of-budget",
|
|
159
159
|
createdAt: 1_700_000_000_001,
|
|
160
|
-
gate: { kind: "resource_limit", reason: "
|
|
161
|
-
pendingAction: { kind: "resource_limit", reason: "
|
|
160
|
+
gate: { kind: "resource_limit", reason: "budget_tokens" },
|
|
161
|
+
pendingAction: { kind: "resource_limit", reason: "budget_tokens" },
|
|
162
162
|
resourceLedger: { totalBudgetMicroUsd: 10_000_000, spentMicroUsd: 4_200_000, spentTokens: 9, spentTurns: 3, sliceCount: 2 },
|
|
163
163
|
});
|
|
164
164
|
const resolved = createCheckpointFixture({ token: "tok-resolved", scope: "tenant-a", sessionId: "done" });
|
|
@@ -45,6 +45,7 @@ export declare function renderTaskNotificationXml(n: TaskNotificationPayload): s
|
|
|
45
45
|
export declare const MAX_PENDING_EVENTS_PER_TASK = 50;
|
|
46
46
|
export declare const MAX_PENDING_EVENTS_PER_SESSION = 200;
|
|
47
47
|
export declare const MAX_PENDING_SESSIONS = 100;
|
|
48
|
+
export declare function isDelegatedAgentTerminal(n: Pick<TaskNotificationPayload, "task_type" | "status">): boolean;
|
|
48
49
|
export interface DrainedPendingNotifications {
|
|
49
50
|
items: TaskNotificationPayload[];
|
|
50
51
|
dropped: Map<string, {
|
|
@@ -83,6 +83,9 @@ export const MAX_PENDING_EVENTS_PER_SESSION = 200;
|
|
|
83
83
|
export const MAX_PENDING_SESSIONS = 100;
|
|
84
84
|
const MAX_TOMBSTONES = 5_000;
|
|
85
85
|
const TERMINAL_STATUSES = new Set(["completed", "failed", "killed", "cancelled"]);
|
|
86
|
+
export function isDelegatedAgentTerminal(n) {
|
|
87
|
+
return n.task_type === "background_agent" && TERMINAL_STATUSES.has(n.status);
|
|
88
|
+
}
|
|
86
89
|
export class PendingSessionNotifications {
|
|
87
90
|
sessions = new Map();
|
|
88
91
|
droppedSessions = 0;
|
|
@@ -106,6 +106,8 @@ export declare function settleRevivedAgentLane(core: DurableAgentCore, id: strin
|
|
|
106
106
|
retryable?: boolean;
|
|
107
107
|
errorKind?: string;
|
|
108
108
|
}): "completed" | "failed" | "killed" | undefined;
|
|
109
|
+
export declare function noteBackgroundAgentActivityLane(core: DurableAgentCore, id: string, now?: number): void;
|
|
110
|
+
export declare function reapStaleSessionBackgroundAgentsLane(core: DurableAgentCore, staleMs: number, now?: number, onTerminal?: (note: () => void) => void): number;
|
|
109
111
|
export declare function unmarkRetainedContinuationLane(core: DurableAgentCore, id: string): void;
|
|
110
112
|
export declare function attachAgentNotifyLane(core: DurableAgentCore, id: string, notify: NonNullable<BackgroundAgentTaskHandle["notify"]>, cycle?: number): void;
|
|
111
113
|
export declare function deliverToRunningAgentLane(core: DurableAgentCore, id: string, access: TaskAccess, notification: import("./task-notification.js").TaskNotificationPayload, opts?: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { uuidv7 } from "../internal/harness.js";
|
|
3
|
-
import { canAccessAgentRecord, BackgroundAgentStoreError, REVIVED_ROW_CLEARED_FIELDS, } from "./background-agent-store.js";
|
|
3
|
+
import { canAccessAgentRecord, BackgroundAgentStoreError, REVIVED_ROW_CLEARED_FIELDS, STALE_RUNNING_REAP_ATTRIBUTION, } from "./background-agent-store.js";
|
|
4
4
|
import { shutdownDebug } from "./shutdown-debug.js";
|
|
5
5
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
6
6
|
import { boundedRedactedSummary } from "./untrusted-egress.js";
|
|
@@ -870,6 +870,38 @@ export function reviveBackgroundAgentLane(core, id, access, abort) {
|
|
|
870
870
|
export function settleRevivedAgentLane(core, id, cycle, outcome) {
|
|
871
871
|
return settleBackgroundAgentLane(core, id, { ...outcome, cycle });
|
|
872
872
|
}
|
|
873
|
+
export function noteBackgroundAgentActivityLane(core, id, now = Date.now()) {
|
|
874
|
+
const handle = core.handles.get(id);
|
|
875
|
+
if (!handle || handle.type !== "background_agent" || handle.status !== "running")
|
|
876
|
+
return;
|
|
877
|
+
handle.updatedAt = now;
|
|
878
|
+
}
|
|
879
|
+
export function reapStaleSessionBackgroundAgentsLane(core, staleMs, now = Date.now(), onTerminal) {
|
|
880
|
+
if (!Number.isFinite(staleMs) || staleMs <= 0) {
|
|
881
|
+
throw new Error(`reapStaleSessionBackgroundAgents: staleMs must be a positive finite number of milliseconds (got ${String(staleMs)})`);
|
|
882
|
+
}
|
|
883
|
+
let reaped = 0;
|
|
884
|
+
for (const handle of core.handles.values()) {
|
|
885
|
+
if (handle.type !== "background_agent" || handle.status !== "running" || handle.sessionScoped !== true)
|
|
886
|
+
continue;
|
|
887
|
+
if (now - handle.updatedAt <= staleMs)
|
|
888
|
+
continue;
|
|
889
|
+
core.markStopSource(handle.id, "system");
|
|
890
|
+
const reapTerminalNote = handle.onReapTerminal;
|
|
891
|
+
handle.abort.abort();
|
|
892
|
+
settleBackgroundAgentLane(core, handle.id, {
|
|
893
|
+
status: "killed",
|
|
894
|
+
error: STALE_RUNNING_REAP_ATTRIBUTION,
|
|
895
|
+
cycle: handle.reviveCycle ?? 0,
|
|
896
|
+
});
|
|
897
|
+
if (reapTerminalNote !== undefined && handle.terminalNotified !== true) {
|
|
898
|
+
handle.terminalNotified = true;
|
|
899
|
+
onTerminal?.(reapTerminalNote);
|
|
900
|
+
}
|
|
901
|
+
reaped++;
|
|
902
|
+
}
|
|
903
|
+
return reaped;
|
|
904
|
+
}
|
|
873
905
|
export function unmarkRetainedContinuationLane(core, id) {
|
|
874
906
|
const handle = core.handles.get(id);
|
|
875
907
|
if (handle && handle.type === "background_agent")
|
|
@@ -145,6 +145,8 @@ export declare class TaskRegistry {
|
|
|
145
145
|
seq?: number;
|
|
146
146
|
cycle?: number;
|
|
147
147
|
}): "completed" | "failed" | "killed" | undefined;
|
|
148
|
+
noteBackgroundAgentActivity(id: string, now?: number): void;
|
|
149
|
+
reapStaleSessionBackgroundAgents(staleMs: number, now?: number): number;
|
|
148
150
|
abortBackgroundAgentsForOwner(access: TaskAccess, opts?: {
|
|
149
151
|
skipSessionScoped?: boolean;
|
|
150
152
|
sessionScopedOnly?: boolean;
|
|
@@ -10,7 +10,7 @@ import { registerWorkflowLane, pollWorkflowLane, stopWorkflowLane } from "./task
|
|
|
10
10
|
import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, renderSpoolBody, spoolDropNote, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
|
|
11
11
|
export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
|
|
12
12
|
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
|
|
13
|
-
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
13
|
+
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
14
14
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
15
15
|
const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
|
|
16
16
|
const BLOCK_MAX_TIMEOUT_MS = 600_000;
|
|
@@ -167,6 +167,14 @@ export class TaskRegistry {
|
|
|
167
167
|
settleBackgroundAgent(id, outcome) {
|
|
168
168
|
return settleBackgroundAgentLane(this.core, id, outcome);
|
|
169
169
|
}
|
|
170
|
+
noteBackgroundAgentActivity(id, now) {
|
|
171
|
+
return noteBackgroundAgentActivityLane(this.core, id, now);
|
|
172
|
+
}
|
|
173
|
+
reapStaleSessionBackgroundAgents(staleMs, now) {
|
|
174
|
+
return reapStaleSessionBackgroundAgentsLane(this.core, staleMs, now, (note) => {
|
|
175
|
+
this.notifier.notify(() => note(), "taskRegistry.reapTerminalNote");
|
|
176
|
+
});
|
|
177
|
+
}
|
|
170
178
|
abortBackgroundAgentsForOwner(access, opts) {
|
|
171
179
|
return abortBackgroundAgentsForOwnerLane(this.core, access, opts);
|
|
172
180
|
}
|
package/dist/core/tool-errors.js
CHANGED
|
@@ -65,9 +65,14 @@ const BRAIN_FLAT_CODES = new Set([
|
|
|
65
65
|
"refusal",
|
|
66
66
|
"length_empty",
|
|
67
67
|
]);
|
|
68
|
+
const EXACT_CODE_CLASS = new Map([
|
|
69
|
+
["limits.max_tokens_exceeded", "budget"],
|
|
70
|
+
["limits.max_cost_exceeded", "budget"],
|
|
71
|
+
["limits.max_turns_exceeded", "limit"],
|
|
72
|
+
["limits.max_walltime_exceeded", "limit"],
|
|
73
|
+
]);
|
|
68
74
|
const DOTTED_PREFIX_CLASS = [
|
|
69
|
-
["
|
|
70
|
-
["limit.", "limit"],
|
|
75
|
+
["limits.", "limit"],
|
|
71
76
|
["output.", "output"],
|
|
72
77
|
["suspend.", "suspend"],
|
|
73
78
|
["review.", "review"],
|
|
@@ -79,6 +84,9 @@ const DOTTED_PREFIX_CLASS = [
|
|
|
79
84
|
export function errorClassOf(errorCode) {
|
|
80
85
|
if (!errorCode)
|
|
81
86
|
return "unknown";
|
|
87
|
+
const exact = EXACT_CODE_CLASS.get(errorCode);
|
|
88
|
+
if (exact !== undefined)
|
|
89
|
+
return exact;
|
|
82
90
|
for (const [prefix, cls] of DOTTED_PREFIX_CLASS) {
|
|
83
91
|
if (errorCode.startsWith(prefix))
|
|
84
92
|
return cls;
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -2,17 +2,10 @@ import type { TaskStatus, ToolEffect } from "./types.js";
|
|
|
2
2
|
import type { ThinkingLevel } from "../internal/harness-types.js";
|
|
3
3
|
import type { ToolManifestRow } from "../prompt-assembly/tool-catalog.js";
|
|
4
4
|
export interface TraceMechanismsSummary {
|
|
5
|
-
finalizeInjected?: true;
|
|
6
|
-
nudgesSent?: number;
|
|
7
|
-
capShrinks?: number;
|
|
8
|
-
callCutoffs?: number;
|
|
9
|
-
toolClamps?: number;
|
|
10
|
-
toolCuts?: number;
|
|
11
|
-
toolCutKills?: number;
|
|
12
|
-
pendingCutTools?: number;
|
|
13
5
|
finalVerifyInjected?: true;
|
|
14
6
|
finalVerifyInjections?: number;
|
|
15
7
|
attachmentsInjected?: number;
|
|
8
|
+
approachNoticesSent?: number;
|
|
16
9
|
repetitionCuts?: number;
|
|
17
10
|
repetitionSpared?: number;
|
|
18
11
|
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -244,6 +244,22 @@ export type ImageInput = {
|
|
|
244
244
|
} | {
|
|
245
245
|
url: string;
|
|
246
246
|
};
|
|
247
|
+
export interface TaskLimits {
|
|
248
|
+
maxTokens?: number;
|
|
249
|
+
maxCostUsd?: number;
|
|
250
|
+
maxTurns?: number;
|
|
251
|
+
maxWalltimeMs?: number;
|
|
252
|
+
maxOutputTokens?: number;
|
|
253
|
+
approachNotice?: false | {
|
|
254
|
+
at?: [number, number];
|
|
255
|
+
};
|
|
256
|
+
budgetStreamCancel?: boolean;
|
|
257
|
+
degrade?: {
|
|
258
|
+
to: ModelRef;
|
|
259
|
+
atCostFraction: number;
|
|
260
|
+
};
|
|
261
|
+
brainCallGuardrailMs?: import("../brain/timeout.js").BrainCallGuardrailKnob;
|
|
262
|
+
}
|
|
247
263
|
export interface TaskSpec {
|
|
248
264
|
taskId?: string;
|
|
249
265
|
objective: string;
|
|
@@ -293,7 +309,7 @@ export interface TaskSpec {
|
|
|
293
309
|
resourceSuspend?: {
|
|
294
310
|
scope: string;
|
|
295
311
|
totalBudgetUsd?: number;
|
|
296
|
-
|
|
312
|
+
totalTokens?: number;
|
|
297
313
|
maxSlices?: number;
|
|
298
314
|
ttlMs?: number;
|
|
299
315
|
};
|
|
@@ -324,17 +340,7 @@ export interface TaskSpec {
|
|
|
324
340
|
note?: string;
|
|
325
341
|
};
|
|
326
342
|
};
|
|
327
|
-
limits?:
|
|
328
|
-
maxTurns?: number;
|
|
329
|
-
timeoutSec?: number;
|
|
330
|
-
maxOutputTokens?: number;
|
|
331
|
-
deadlineNudge?: false | {
|
|
332
|
-
at?: [number, number];
|
|
333
|
-
};
|
|
334
|
-
callCapByDeadline?: false;
|
|
335
|
-
gracefulFinalize?: false;
|
|
336
|
-
brainCallGuardrailMs?: import("../brain/timeout.js").BrainCallGuardrailKnob;
|
|
337
|
-
};
|
|
343
|
+
limits?: TaskLimits;
|
|
338
344
|
configOverrides?: ReadonlyArray<import("../config/catalog.js").ConfigOverrideDeclaration>;
|
|
339
345
|
forwardSubagentEvents?: boolean;
|
|
340
346
|
retainSubagentSessions?: boolean | {
|
|
@@ -343,13 +349,6 @@ export interface TaskSpec {
|
|
|
343
349
|
};
|
|
344
350
|
retainBackgroundProcesses?: boolean;
|
|
345
351
|
streamingToolExecution?: boolean;
|
|
346
|
-
maxCostUsd?: number;
|
|
347
|
-
maxTokens?: number;
|
|
348
|
-
budgetStreamCancel?: boolean;
|
|
349
|
-
degrade?: {
|
|
350
|
-
to: ModelRef;
|
|
351
|
-
atCostFraction: number;
|
|
352
|
-
};
|
|
353
352
|
outputSchema?: TSchema;
|
|
354
353
|
outputRetries?: number;
|
|
355
354
|
tracer?: import("./trace.js").TracerHook;
|
|
@@ -389,7 +388,7 @@ export interface TaskSpec {
|
|
|
389
388
|
} | undefined>;
|
|
390
389
|
signal?: AbortSignal;
|
|
391
390
|
}
|
|
392
|
-
export type TaskStatus = "completed" | "blocked" | "failed" | "
|
|
391
|
+
export type TaskStatus = "completed" | "blocked" | "failed" | "suspended" | "needs_review";
|
|
393
392
|
export interface RemoteEnvFailureNote {
|
|
394
393
|
op: "suspendVM" | "resumeVM" | "postResumeInit";
|
|
395
394
|
code: import("./remote-env.js").RemoteExecutionErrorCode;
|
|
@@ -446,17 +445,10 @@ export interface TaskResult {
|
|
|
446
445
|
costMicroUsd: number;
|
|
447
446
|
};
|
|
448
447
|
mechanisms?: {
|
|
449
|
-
finalizeInjected?: true;
|
|
450
|
-
nudgesSent?: number;
|
|
451
|
-
capShrinks?: number;
|
|
452
|
-
callCutoffs?: number;
|
|
453
|
-
toolClamps?: number;
|
|
454
|
-
toolCuts?: number;
|
|
455
|
-
toolCutKills?: number;
|
|
456
|
-
pendingCutTools?: number;
|
|
457
448
|
finalVerifyInjected?: true;
|
|
458
449
|
finalVerifyInjections?: number;
|
|
459
450
|
attachmentsInjected?: number;
|
|
451
|
+
approachNoticesSent?: number;
|
|
460
452
|
repetitionCuts?: number;
|
|
461
453
|
repetitionSpared?: number;
|
|
462
454
|
repetitionEvents?: Array<{
|
|
@@ -568,7 +560,7 @@ export type TaskEvent = ({
|
|
|
568
560
|
modelFallback?: true;
|
|
569
561
|
fallbackReason?: "window";
|
|
570
562
|
clampedRatio?: number;
|
|
571
|
-
clampReason?: "budget" | "
|
|
563
|
+
clampReason?: "budget" | "tolerance";
|
|
572
564
|
phaseDurations?: import("./auto-compaction.js").CompactionPhaseDurations;
|
|
573
565
|
} & TaskEventIdentity) | ({
|
|
574
566
|
type: "compaction_outcome";
|
|
@@ -577,7 +569,7 @@ export type TaskEvent = ({
|
|
|
577
569
|
reason?: string;
|
|
578
570
|
} & TaskEventIdentity) | ({
|
|
579
571
|
type: "steering_injected";
|
|
580
|
-
source: "
|
|
572
|
+
source: "limit_approach" | "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "workflow_size_guideline_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
|
|
581
573
|
preview: string;
|
|
582
574
|
} & TaskEventIdentity) | ({
|
|
583
575
|
type: "diagnostics";
|
|
@@ -750,6 +742,8 @@ export interface RunnerDeps {
|
|
|
750
742
|
workflowRunStore?: import("./workflow-run-store.js").WorkflowRunStore;
|
|
751
743
|
backgroundAgentStore?: import("./background-agent-store.js").BackgroundAgentStore;
|
|
752
744
|
mailboxStore?: import("./mailbox-store.js").MailboxStore;
|
|
745
|
+
usageWindows?: readonly import("./usage-window-store.js").UsageWindow[];
|
|
746
|
+
usageWindowStore?: import("./usage-window-store.js").UsageWindowStore;
|
|
753
747
|
workflowJournalStore?: import("./workflow-journal-store.js").WorkflowJournalStore;
|
|
754
748
|
workflowScriptStore?: import("../orchestration/workflow-script-store.js").WorkflowScriptStore;
|
|
755
749
|
builtinWorkflows?: boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface UsageWindow {
|
|
2
|
+
windowMs: number;
|
|
3
|
+
maxTokens: number;
|
|
4
|
+
anchor: "first-use" | "rolling";
|
|
5
|
+
}
|
|
6
|
+
export interface UsageSlot {
|
|
7
|
+
at: number;
|
|
8
|
+
tokens: number;
|
|
9
|
+
}
|
|
10
|
+
export interface UsageBucketRow {
|
|
11
|
+
windowMs: number;
|
|
12
|
+
openedAt: number;
|
|
13
|
+
tokens: number;
|
|
14
|
+
}
|
|
15
|
+
export interface UsageWindowRecord {
|
|
16
|
+
slots: readonly UsageSlot[];
|
|
17
|
+
buckets: readonly UsageBucketRow[];
|
|
18
|
+
}
|
|
19
|
+
export interface UsageWindowReading {
|
|
20
|
+
window: UsageWindow;
|
|
21
|
+
tokens: number;
|
|
22
|
+
exhausted: boolean;
|
|
23
|
+
retryAfterMs: number;
|
|
24
|
+
}
|
|
25
|
+
export interface UsageWindowStore {
|
|
26
|
+
charge(key: string, tokens: number, at: number, windows: readonly UsageWindow[]): Promise<void>;
|
|
27
|
+
read(key: string, windows: readonly UsageWindow[], now: number): Promise<readonly UsageWindowReading[]>;
|
|
28
|
+
}
|
|
29
|
+
export declare const GLOBAL_USAGE_KEY = "__usage_global__";
|
|
30
|
+
export declare const EMPTY_USAGE_WINDOW_RECORD: UsageWindowRecord;
|
|
31
|
+
export declare function resolveUsageWindows(windows: readonly UsageWindow[] | undefined): readonly UsageWindow[] | undefined;
|
|
32
|
+
export declare function chargeUsageRecord(record: UsageWindowRecord, tokens: number, at: number, windows: readonly UsageWindow[]): UsageWindowRecord;
|
|
33
|
+
export declare function readUsageRecord(record: UsageWindowRecord, windows: readonly UsageWindow[], now: number): UsageWindowReading[];
|
|
34
|
+
export declare function usageRetryAfterMs(readings: readonly UsageWindowReading[]): number | undefined;
|
|
35
|
+
export declare class InMemoryUsageWindowStore implements UsageWindowStore {
|
|
36
|
+
private readonly records;
|
|
37
|
+
charge(key: string, tokens: number, at: number, windows: readonly UsageWindow[]): Promise<void>;
|
|
38
|
+
read(key: string, windows: readonly UsageWindow[], now: number): Promise<readonly UsageWindowReading[]>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export const GLOBAL_USAGE_KEY = "__usage_global__";
|
|
2
|
+
export const EMPTY_USAGE_WINDOW_RECORD = Object.freeze({
|
|
3
|
+
slots: Object.freeze([]),
|
|
4
|
+
buckets: Object.freeze([]),
|
|
5
|
+
});
|
|
6
|
+
function usageConfigError(code, message) {
|
|
7
|
+
const e = new Error(message);
|
|
8
|
+
e.code = code;
|
|
9
|
+
return e;
|
|
10
|
+
}
|
|
11
|
+
export function resolveUsageWindows(windows) {
|
|
12
|
+
if (windows === undefined)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (!Array.isArray(windows)) {
|
|
15
|
+
throw usageConfigError("config.usage_window_invalid", `RunnerDeps.usageWindows must be an array (got ${typeof windows})`);
|
|
16
|
+
}
|
|
17
|
+
for (const w of windows) {
|
|
18
|
+
if (w === null || typeof w !== "object") {
|
|
19
|
+
throw usageConfigError("config.usage_window_invalid", `RunnerDeps.usageWindows entries must be objects (got ${String(w)})`);
|
|
20
|
+
}
|
|
21
|
+
if (typeof w.windowMs !== "number" || !Number.isFinite(w.windowMs) || w.windowMs <= 0) {
|
|
22
|
+
throw usageConfigError("config.usage_window_invalid", `RunnerDeps.usageWindows[].windowMs must be a finite number of milliseconds > 0 (got ${String(w.windowMs)})`);
|
|
23
|
+
}
|
|
24
|
+
if (typeof w.maxTokens !== "number" || !Number.isFinite(w.maxTokens) || w.maxTokens < 0) {
|
|
25
|
+
throw usageConfigError("config.usage_window_invalid", `RunnerDeps.usageWindows[].maxTokens must be a finite, non-negative number (got ${String(w.maxTokens)})`);
|
|
26
|
+
}
|
|
27
|
+
if (w.anchor !== "first-use" && w.anchor !== "rolling") {
|
|
28
|
+
throw usageConfigError("config.usage_window_invalid", `RunnerDeps.usageWindows[].anchor must be "first-use" or "rolling" (got ${String(w.anchor)})`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return windows;
|
|
32
|
+
}
|
|
33
|
+
function rollingHorizonMs(windows) {
|
|
34
|
+
let widest;
|
|
35
|
+
for (const w of windows) {
|
|
36
|
+
if (w.anchor !== "rolling")
|
|
37
|
+
continue;
|
|
38
|
+
if (widest === undefined || w.windowMs > widest)
|
|
39
|
+
widest = w.windowMs;
|
|
40
|
+
}
|
|
41
|
+
return widest;
|
|
42
|
+
}
|
|
43
|
+
export function chargeUsageRecord(record, tokens, at, windows) {
|
|
44
|
+
const horizon = rollingHorizonMs(windows);
|
|
45
|
+
const keptSlots = horizon === undefined ? [] : record.slots.filter((s) => s.at > at - horizon).map((s) => ({ at: s.at, tokens: s.tokens }));
|
|
46
|
+
if (horizon !== undefined && tokens > 0)
|
|
47
|
+
keptSlots.push({ at, tokens });
|
|
48
|
+
const buckets = [];
|
|
49
|
+
for (const w of windows) {
|
|
50
|
+
if (w.anchor !== "first-use")
|
|
51
|
+
continue;
|
|
52
|
+
if (buckets.some((b) => b.windowMs === w.windowMs))
|
|
53
|
+
continue;
|
|
54
|
+
const prior = record.buckets.find((b) => b.windowMs === w.windowMs);
|
|
55
|
+
const lapsed = prior === undefined || at >= prior.openedAt + w.windowMs;
|
|
56
|
+
if (lapsed) {
|
|
57
|
+
if (tokens > 0)
|
|
58
|
+
buckets.push({ windowMs: w.windowMs, openedAt: at, tokens });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
buckets.push({ windowMs: w.windowMs, openedAt: prior.openedAt, tokens: prior.tokens + tokens });
|
|
62
|
+
}
|
|
63
|
+
return { slots: keptSlots, buckets };
|
|
64
|
+
}
|
|
65
|
+
export function readUsageRecord(record, windows, now) {
|
|
66
|
+
const readings = [];
|
|
67
|
+
for (const w of windows) {
|
|
68
|
+
let tokens = 0;
|
|
69
|
+
let freesAt;
|
|
70
|
+
if (w.anchor === "rolling") {
|
|
71
|
+
const floor = now - w.windowMs;
|
|
72
|
+
let oldest;
|
|
73
|
+
for (const s of record.slots) {
|
|
74
|
+
if (s.at <= floor)
|
|
75
|
+
continue;
|
|
76
|
+
tokens += s.tokens;
|
|
77
|
+
if (oldest === undefined || s.at < oldest)
|
|
78
|
+
oldest = s.at;
|
|
79
|
+
}
|
|
80
|
+
if (oldest !== undefined)
|
|
81
|
+
freesAt = oldest + w.windowMs;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const row = record.buckets.find((b) => b.windowMs === w.windowMs);
|
|
85
|
+
if (row !== undefined && now < row.openedAt + w.windowMs) {
|
|
86
|
+
tokens = row.tokens;
|
|
87
|
+
freesAt = row.openedAt + w.windowMs;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const exhausted = tokens >= w.maxTokens;
|
|
91
|
+
const retryAfterMs = exhausted ? Math.max(1, freesAt === undefined ? w.windowMs : freesAt - now) : 0;
|
|
92
|
+
readings.push({ window: w, tokens, exhausted, retryAfterMs });
|
|
93
|
+
}
|
|
94
|
+
return readings;
|
|
95
|
+
}
|
|
96
|
+
export function usageRetryAfterMs(readings) {
|
|
97
|
+
let worst;
|
|
98
|
+
for (const r of readings) {
|
|
99
|
+
if (!r.exhausted)
|
|
100
|
+
continue;
|
|
101
|
+
if (worst === undefined || r.retryAfterMs > worst)
|
|
102
|
+
worst = r.retryAfterMs;
|
|
103
|
+
}
|
|
104
|
+
return worst;
|
|
105
|
+
}
|
|
106
|
+
export class InMemoryUsageWindowStore {
|
|
107
|
+
records = new Map();
|
|
108
|
+
async charge(key, tokens, at, windows) {
|
|
109
|
+
const prior = this.records.get(key) ?? EMPTY_USAGE_WINDOW_RECORD;
|
|
110
|
+
this.records.set(key, chargeUsageRecord(prior, tokens, at, windows));
|
|
111
|
+
}
|
|
112
|
+
async read(key, windows, now) {
|
|
113
|
+
return readUsageRecord(this.records.get(key) ?? EMPTY_USAGE_WINDOW_RECORD, windows, now);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -432,23 +432,6 @@ export class NodeExecutionEnv {
|
|
|
432
432
|
return;
|
|
433
433
|
}
|
|
434
434
|
closeSpoolFds();
|
|
435
|
-
if (options?.onSpawn && child.pid !== undefined) {
|
|
436
|
-
const spawnedPid = child.pid;
|
|
437
|
-
const spawnedChild = child;
|
|
438
|
-
try {
|
|
439
|
-
options.onSpawn({
|
|
440
|
-
pid: spawnedPid,
|
|
441
|
-
kill: () => {
|
|
442
|
-
abortKillInFlight = true;
|
|
443
|
-
killProcessTree(spawnedPid, {
|
|
444
|
-
stillRunning: () => spawnedChild.exitCode === null && spawnedChild.signalCode === null,
|
|
445
|
-
});
|
|
446
|
-
},
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
catch {
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
435
|
const timeoutMs = resolveExecTimeoutMs(options?.timeout);
|
|
453
436
|
timeoutRef.current =
|
|
454
437
|
timeoutMs === undefined
|
|
@@ -580,11 +563,6 @@ export class NodeExecutionEnv {
|
|
|
580
563
|
return false;
|
|
581
564
|
if (spoolPumpRef.current)
|
|
582
565
|
clearInterval(spoolPumpRef.current);
|
|
583
|
-
try {
|
|
584
|
-
options?.onDetachAdopted?.();
|
|
585
|
-
}
|
|
586
|
-
catch {
|
|
587
|
-
}
|
|
588
566
|
if (timeoutRef.current)
|
|
589
567
|
clearTimeout(timeoutRef.current);
|
|
590
568
|
const dso = stdoutTail.result();
|
|
@@ -17,11 +17,6 @@ export interface HarnessLoopRecovery {
|
|
|
17
17
|
detect: (message: AssistantMessage) => boolean;
|
|
18
18
|
maxContinues?: number;
|
|
19
19
|
};
|
|
20
|
-
walltimeCutoff?: {
|
|
21
|
-
detect: (message: AssistantMessage) => boolean;
|
|
22
|
-
maxContinues?: number;
|
|
23
|
-
hasLiveWriteoutWindow?: () => boolean;
|
|
24
|
-
};
|
|
25
20
|
promptTooLong?: {
|
|
26
21
|
recover: (attempt: number) => Promise<boolean>;
|
|
27
22
|
detect?: (message: AssistantMessage) => boolean;
|
|
@@ -54,9 +49,7 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
|
|
|
54
49
|
private steeringQueueMode;
|
|
55
50
|
private maxOutputTokens?;
|
|
56
51
|
private maxOutputTokensPerCall?;
|
|
57
|
-
private callDeadlineMsPerCall?;
|
|
58
52
|
private stallTimeoutsPerCall?;
|
|
59
|
-
private toolDeadline?;
|
|
60
53
|
private loopTrace?;
|
|
61
54
|
private resilience?;
|
|
62
55
|
private maxToolConcurrency?;
|