@sema-agent/core 4.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/dist/agents/cascade.d.ts +1 -0
- package/dist/agents/cascade.js +1 -1
- package/dist/agents/repair-loop.d.ts +2 -0
- package/dist/agents/repair-loop.js +30 -6
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/roster-store.js +1 -1
- package/dist/agents/send-message-tool.d.ts +1 -0
- package/dist/agents/send-message-tool.js +3 -1
- package/dist/agents/subagent.d.ts +3 -1
- package/dist/agents/subagent.js +22 -34
- package/dist/agents/tool-filter.js +6 -7
- package/dist/agents/verify.d.ts +1 -0
- package/dist/agents/verify.js +1 -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 +1 -1
- package/dist/core/fs-write-gate-policy.js +2 -3
- package/dist/core/hooks.d.ts +1 -0
- package/dist/core/hooks.js +1 -1
- package/dist/core/mcp.js +0 -6
- package/dist/core/permission-rules.d.ts +2 -2
- package/dist/core/permission-rules.js +12 -3
- package/dist/core/runner/active-skill-scope.js +1 -2
- package/dist/core/runner/prepare-task.js +38 -18
- package/dist/core/runner/runtask.js +19 -16
- package/dist/core/runner/session-rule-policy.d.ts +2 -2
- package/dist/core/runner/session-rule-policy.js +4 -4
- package/dist/core/sensitive-path-policy.js +2 -3
- 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/task-registry-shared.d.ts +1 -1
- package/dist/core/task-registry.d.ts +5 -1
- package/dist/core/task-registry.js +12 -30
- package/dist/core/task-tool-shape.d.ts +0 -2
- package/dist/core/task-tool-shape.js +2 -5
- package/dist/core/tool-name-aliases.d.ts +1 -2
- package/dist/core/tool-name-aliases.js +42 -60
- package/dist/core/tool-policy.js +11 -12
- package/dist/core/trace.d.ts +7 -0
- package/dist/core/untrusted-egress.d.ts +4 -2
- package/dist/core/untrusted-egress.js +21 -9
- package/dist/engine/execution-env/node-execution-env.js +1 -1
- package/dist/engine/loop/agent-loop.js +3 -12
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/orchestration/run-spec.js +2 -3
- package/dist/orchestration/run-workflow-tool.js +0 -1
- package/dist/orchestration/workflow-governance.js +2 -1
- package/dist/orchestration/workflow.d.ts +1 -0
- package/dist/orchestration/workflow.js +1 -1
- package/dist/prompt-assembly/packs/sema-default.js +1 -3
- 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/tools/fs/fs-bash.js +6 -8
- package/dist/tools/fs/fs-write.js +0 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.0.1 (2026-08-02)
|
|
4
|
+
|
|
5
|
+
_Post-release review patch: the 5.0.0 review wave (five domain reviewers + two independent codex legs) surfaced silent-widening gaps in the new audit surfaces plus wiring/anchoring stragglers. All fixes are loud-direction; no new API._
|
|
6
|
+
|
|
7
|
+
**Fixed**
|
|
8
|
+
|
|
9
|
+
- **Persisted session/ancestor rules join the retired-name audit (review G-2).** Session-rule policies were composed after the only policy-name audit pass and exposed no name metadata — a stored `{toolDeny: ["bash"]}` compiled clean and silently widened. The audit is now a reusable pass run over every narrowing layer, and `createSessionRulePolicy` returns a `NamedToolPolicy`.
|
|
10
|
+
- **Pre-prefix MCP names reach the hard arm (review G-3).** The `config.legacy_tool_name` check only consulted the static `RETIRED_TOOL_NAMES` map, which cannot enumerate dynamic MCP names — a v4-era policy entry like `github__delete_repository` (auto-prefixed by the retired canonicalizer) degraded to an advisory and the destructive call ran. An unmatched `__`-shaped name without the `mcp__` prefix now hard-fails prepare with prefix guidance; an `mcp__*` miss stays advisory (the server may not be mounted this run).
|
|
11
|
+
- **Repair-loop accounts stay live across an in-flight attempt (review G-4, time half).** A checkpoint minted mid-attempt clones the threaded bundle at that instant, and only a pre-dispatch stamp existed — an attempt that ran 900ms and parked persisted ~zero elapsed time, so the resume re-granted the washed budget. An unref'd 1s interval keeps the stamp current. (The money half — mid-flight partial spend — is tracked separately: it has a double-count design question against the resumed attempt's own stats.)
|
|
12
|
+
- **Permission-rule policies join the retired-name audit (review F-6).** `createPermissionRulePolicy` now returns a `NamedToolPolicy` exposing its compiled tool names as `nameSets`, so the prepare-time policy-name audit covers rule policies: a rule written with a RETIRED name (e.g. `{rule: "bash", behavior: "deny"}`) that matches nothing in the run's roster is now the same HARD `config.legacy_tool_name` prepare failure as a list policy — previously it compiled with zero issues and the deny silently guarded nothing. A deployment mounting its own tool under a retired name is unaffected (roster hit). Behavior note: configs carrying such stale rules now fail loudly at prepare instead of running silently widened.
|
|
13
|
+
- **Name-addressed revives anchor correctly (review F-1).** The SendMessage by-name resolution hand-copy dropped `createdAt`, so a `{to: NAME}` revive's spawn frame fell back to the wake instant for `startedAt` — the exact-id rung was fixed in 5.0.0, the name rung is now too.
|
|
14
|
+
- **Two more revive legs chain `root ?? parent` (review F-8).** The revived child's own spec internals and the re-minted registry row now derive the same root anchor as the roster/observer legs — a pre-floor row no longer yields an internally inconsistent agent (fleet view anchored, `canAccess` root arm blind).
|
|
15
|
+
- **The disclosure channel reaches both remaining composition roots (review F-2/F-3).** The runner's SendMessage mount wires `onNotifyError` to the `observer.notify_failed` trace lane, and the delegation tool's steer-lane resume threads `opts.onObserverError` — a throwing deployment sink on either revive path is now disclosed instead of counted on an unread notifier.
|
|
16
|
+
- **Checkpoint mints read the clock once.** All three checkpoint mint points stamped `createdAt` and `deadline` from separate `Date.now()` reads; a mint straddling a ms tick violated `deadline = createdAt + ttl` by 1ms.
|
|
17
|
+
- Comment-truth sweep: 14 sites still narrating the deleted OLD_TO_NEW/tombstone machinery rewritten to post-5.0.0 behavior; `markStopSourceByShellId`'s orphaned docstring relocated and corrected (env is REQUIRED for effect — an env-less call is a no-op).
|
|
18
|
+
|
|
19
|
+
## 5.0.0 (2026-08-02)
|
|
20
|
+
|
|
21
|
+
_The baggage-unload major: the compatibility faces RB-476 cleared for tool NAMES are now cleared for id arguments, adapters and indexes too (RB-479), plus the revive-frame anchoring fix a downstream consumer was blocked on (RB-478). Everything that used to resolve silently under an old spelling now refuses loudly with the current spelling named._
|
|
22
|
+
|
|
23
|
+
**BREAKING — old tool names no longer resolve (RB-476, both stages)**
|
|
24
|
+
|
|
25
|
+
1. Callable aliases are cleared: `BashOutput`, `KillShell`, `KillBash`, `AgentOutput`, `WorkflowStatus`, `Fork`, `Sleep`, `Remember`, `recall` (and the rest of the rename table) are LOUD roster misses listing the live roster. A deployment's own same-named custom tool still wins (roster hit first).
|
|
26
|
+
2. The durable rename machinery is retired: `canonicalToolName` and `TOMBSTONED_TOOLS` root exports are REMOVED; `tool-name-aliases.ts` now exports only `RETIRED_TOOL_NAMES` (old name → guidance). A persisted pre-rename checkpoint action misses loudly on resume (`resume.tool_unavailable` + reopen) instead of silently folding onto the new tool.
|
|
27
|
+
3. A RETIRED name in a policy deny/ask/allow list that matches nothing in the run's roster is a HARD prepare failure (`config.legacy_tool_name`) — silent deny-widening is not a migration strategy.
|
|
28
|
+
4. The roster miss carries structured details for UI rendering: `{code: "tool.not_found", toolName, availableTools}`.
|
|
29
|
+
|
|
30
|
+
**BREAKING — undeclared id-argument pass-throughs cleared (RB-479-A①/②)**
|
|
31
|
+
|
|
32
|
+
- `TaskOutput` resolves ONLY `task_id`; `TaskStop` resolves ONLY `task_id`/`shell_id` (`shell_id` is the CC-declared surface and stays). The formerly smuggled `bash_id`/`runId` keys land on the loud missing-parameter refusal. The workflow-run poll capability is unchanged — address it as `task_id` (the value is the run id).
|
|
33
|
+
- The registry's internal `legacyToTaskId` shellId index is deleted: the minted `b*` task id is the ONLY registry address; a raw shellId as `task_id` is a loud not_found. `markStopSourceByShellId`/`clearPendingStopSourceByShellId` resolve by the (shellId, env) scan alone (this also removes a single-value index collision defect); calling them without `env` is a no-op.
|
|
34
|
+
- The env-direct background launch receipt drops its `Legacy bash_id=` sentence and the TaskOutput description drops the compatibility-acceptance sentence.
|
|
35
|
+
|
|
36
|
+
**BREAKING — legacy adapters retired (RB-479-A③/④)**
|
|
37
|
+
|
|
38
|
+
- The standalone-Fork `{directive}` argument adapter is deleted: a `{directive}`-shaped Agent call refuses loudly (empty prompt) instead of silently rerouting to the fork lane. `{prompt, subagent_type: "fork"}` is the (unchanged) explicit route; the `general-purpose` alias fold stays (live CC parity).
|
|
39
|
+
- `BackgroundAgentTaskHandle.notify` requires a disposition (`"queued" | "parked" | "dropped_duplicate"`, sync or Promise) — the `void` arm is removed from the type. Run-time behavior is unchanged (a non-conforming undefined still reads as queued).
|
|
40
|
+
|
|
41
|
+
**Fixed — revive frames anchor the row (RB-478, unblocks server residual-arm deletion)**
|
|
42
|
+
|
|
43
|
+
- All three revive-emission legs (SendMessage resume face, revive-claim spawn, roster record) now chain `rootSessionId ?? parentSessionId` UNCONDITIONALLY — a pre-floor row without a persisted root anchor still lands its host mark downstream.
|
|
44
|
+
- A revive frame's `startedAt` is the ROW's original spawn instant (durable `spawnedAt` / registry registration time), not the wake call's `Date.now()`. `AccessibleTaskRow` gains `createdAt` (additive).
|
|
45
|
+
|
|
46
|
+
**Added**
|
|
47
|
+
|
|
48
|
+
- `observer.notify_failed` trace event (additive union member): a host observer callback that throws inside a safe-notify isolation scope is disclosed (bounded, first failure per site). RB-473: the disclosure channel now reaches every deployment-reachable owner — `onNotifyError` options on auto-compaction/cascade/verify/roster-store/workflow/SendMessage, `TaskRegistry.notifierFailureCounts()`, the delegation tool's `onObserverError` (site attached), and the tool gate via the new trace event.
|
|
49
|
+
- Behavior changes declared from the ruling batch: the simple prompt profile's roster shrinks two sections (RB-322, CC-220 alignment); workflow-script-authored child personas carry a provenance head line (RB-461); the default background command budget is 60 minutes, was 30 (RB-366).
|
|
50
|
+
- `test/legacy-residue-gate.test.ts`: a both-ways ratchet freezing the residual compatibility lexicon per file — new compatibility machinery reds the gate until explicitly registered.
|
|
51
|
+
|
|
52
|
+
**Migration**
|
|
53
|
+
|
|
54
|
+
- Old tool names → current names (the refusal text lists the live roster). `bash_id`/`runId` → `task_id`. `{directive}` → `{prompt, subagent_type: "fork"}`. Void notify injectors → return `"queued"`. Policy lists naming retired tools → rename or remove the entry (prepare fails loudly otherwise).
|
|
55
|
+
|
|
3
56
|
## 4.0.0 (2026-08-02)
|
|
4
57
|
|
|
5
58
|
_Refactor campaign (50 candidates, three new discipline gates) + a test-discriminance pass + a three-class defect sweep + the closing four-lens review (22 findings, all dispositioned). Major because four config sentinels change MEANING under unchanged types — ENGINEERING-CODE §J2: same name, same type, new behavior is the semver case that compiles clean and computes wrong._
|
package/dist/agents/cascade.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface CascadeRung {
|
|
|
9
9
|
overrides?: Partial<Pick<TaskSpec, "limits" | "degrade" | "systemPrompt">>;
|
|
10
10
|
}
|
|
11
11
|
export interface CascadeConfig {
|
|
12
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
12
13
|
ladder: CascadeRung[];
|
|
13
14
|
gate?: (result: TaskResult, rung: {
|
|
14
15
|
index: number;
|
package/dist/agents/cascade.js
CHANGED
|
@@ -17,7 +17,7 @@ export async function runCascade(runner, spec, config) {
|
|
|
17
17
|
const gate = config.gate ?? createDefaultGate(spec);
|
|
18
18
|
const startedAt = Date.now();
|
|
19
19
|
const deadlineAt = config.totalTimeoutMs != null ? startedAt + config.totalTimeoutMs : undefined;
|
|
20
|
-
const notifier = createSafeNotifier();
|
|
20
|
+
const notifier = createSafeNotifier(config.onNotifyError !== undefined ? { onError: config.onNotifyError } : undefined);
|
|
21
21
|
const attempts = [];
|
|
22
22
|
let totalCost = 0;
|
|
23
23
|
let ceilingCostKnown = true;
|
|
@@ -87,7 +87,14 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
87
87
|
? { ...config.resumeBundle, diagnostics: [...config.resumeBundle.diagnostics], rejectedHypotheses: [...config.resumeBundle.rejectedHypotheses] }
|
|
88
88
|
: freshBundle();
|
|
89
89
|
const startedAt = Date.now();
|
|
90
|
-
|
|
90
|
+
const seededSpend = config.resumeBundle?.spentMicroUsd;
|
|
91
|
+
const seededActiveMs = config.resumeBundle?.activeElapsedMs;
|
|
92
|
+
const priorActiveMs = typeof seededActiveMs === "number" && Number.isFinite(seededActiveMs) ? Math.max(0, seededActiveMs) : 0;
|
|
93
|
+
let spend = typeof seededSpend === "number" && Number.isFinite(seededSpend) ? Math.max(0, seededSpend) : 0;
|
|
94
|
+
const stampAccount = () => {
|
|
95
|
+
bundle.spentMicroUsd = spend;
|
|
96
|
+
bundle.activeElapsedMs = priorActiveMs + (Date.now() - startedAt);
|
|
97
|
+
};
|
|
91
98
|
let lastResult;
|
|
92
99
|
let lastOracle;
|
|
93
100
|
let ownTokens = 0, ownTurns = 0, ownPrompt = 0, ownTotalInput = 0, ownCached = 0, ownOutput = 0;
|
|
@@ -148,11 +155,11 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
148
155
|
const { sessionId: _drop, ...rest } = implSpec;
|
|
149
156
|
return { ...rest, objective: `${implSpec.objective}\n\n${repairObjective(bundle)}` };
|
|
150
157
|
};
|
|
151
|
-
const exhaustedBeforeDispatch = () => ({
|
|
158
|
+
const exhaustedBeforeDispatch = (message, errorCode) => ({
|
|
152
159
|
taskId: implSpec.taskId ?? "",
|
|
153
160
|
status: "failed",
|
|
154
|
-
result: `repair loop: attempt budget already spent (attemptCount ${bundle.attemptCount} >= maxAttempts ${maxAttempts}) — no attempt dispatched`,
|
|
155
|
-
errorCode: "repair.attempts_exhausted",
|
|
161
|
+
result: message ?? `repair loop: attempt budget already spent (attemptCount ${bundle.attemptCount} >= maxAttempts ${maxAttempts}) — no attempt dispatched`,
|
|
162
|
+
errorCode: errorCode ?? "repair.attempts_exhausted",
|
|
156
163
|
sessionId: implSpec.sessionId ?? "",
|
|
157
164
|
stats: { tokens: 0, turns: 0, costMicroUsd: 0 },
|
|
158
165
|
terminal: "gave_up",
|
|
@@ -164,6 +171,13 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
164
171
|
while (true) {
|
|
165
172
|
if (!restartedOnce && bundle.attemptCount >= maxAttempts)
|
|
166
173
|
return exhaustedBeforeDispatch();
|
|
174
|
+
if (config.costCeilingMicroUsd != null && spend >= config.costCeilingMicroUsd) {
|
|
175
|
+
return exhaustedBeforeDispatch(`repair loop: cost ceiling already spent (carried ${spend} >= ceiling ${config.costCeilingMicroUsd} micro-USD) — no attempt dispatched`, "repair.budget_exhausted");
|
|
176
|
+
}
|
|
177
|
+
if (config.totalTimeoutMs != null && priorActiveMs + (Date.now() - startedAt) >= config.totalTimeoutMs) {
|
|
178
|
+
return exhaustedBeforeDispatch(`repair loop: active time budget already spent (carried ${priorActiveMs + (Date.now() - startedAt)}ms >= ${config.totalTimeoutMs}ms) — no attempt dispatched`, "repair.budget_exhausted");
|
|
179
|
+
}
|
|
180
|
+
stampAccount();
|
|
167
181
|
const isRepair = bundle.attemptCount > 0 && lastResult !== undefined && !restartedOnce;
|
|
168
182
|
const isSeededResume = !restartedOnce &&
|
|
169
183
|
lastResult === undefined &&
|
|
@@ -180,8 +194,17 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
180
194
|
}
|
|
181
195
|
: implSpec;
|
|
182
196
|
restartedOnce = false;
|
|
183
|
-
const
|
|
197
|
+
const liveStamp = setInterval(stampAccount, 1_000);
|
|
198
|
+
liveStamp.unref?.();
|
|
199
|
+
let result;
|
|
200
|
+
try {
|
|
201
|
+
result = await runner.runTask(spec, { repairBundle: bundle });
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
clearInterval(liveStamp);
|
|
205
|
+
}
|
|
184
206
|
spend += (result.stats.costMicroUsd ?? 0) + (result.stats.nested?.costMicroUsd ?? 0);
|
|
207
|
+
stampAccount();
|
|
185
208
|
accumulate(result.stats);
|
|
186
209
|
lastResult = result;
|
|
187
210
|
bundle.attemptCount += 1;
|
|
@@ -210,6 +233,7 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
210
233
|
const oracle = await config.oracle(config.graderEnv, result.result);
|
|
211
234
|
oracleCostMicroUsd += oracle.costMicroUsd ?? 0;
|
|
212
235
|
spend += oracle.costMicroUsd ?? 0;
|
|
236
|
+
stampAccount();
|
|
213
237
|
lastOracle = oracle;
|
|
214
238
|
bundle.oracleTier = oracle.tier;
|
|
215
239
|
const projected = terminalForTier(oracle);
|
|
@@ -230,7 +254,7 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
230
254
|
if (bundle.diagnostics.length < 3)
|
|
231
255
|
bundle.diagnostics.push(oracle.trace);
|
|
232
256
|
}
|
|
233
|
-
const overTime = config.totalTimeoutMs != null && Date.now() - startedAt >= config.totalTimeoutMs;
|
|
257
|
+
const overTime = config.totalTimeoutMs != null && priorActiveMs + (Date.now() - startedAt) >= config.totalTimeoutMs;
|
|
234
258
|
const overCost = config.costCeilingMicroUsd != null && spend >= config.costCeilingMicroUsd;
|
|
235
259
|
if (bundle.attemptCount >= maxAttempts || overTime || overCost) {
|
|
236
260
|
if (bundle.attemptCount >= maxAttempts && !overTime && !overCost && lastResult !== undefined && !restartUsed) {
|
|
@@ -23,6 +23,7 @@ export interface RosterStore {
|
|
|
23
23
|
releaseAgent(agentId: string): void | Promise<void>;
|
|
24
24
|
}
|
|
25
25
|
export interface RosterGcOptions {
|
|
26
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
26
27
|
maxAgeMs?: number;
|
|
27
28
|
maxEntries?: number;
|
|
28
29
|
onEvicted?: (entry: RosterEntry) => void;
|
|
@@ -36,7 +36,7 @@ function capped(entries, opts) {
|
|
|
36
36
|
const max = opts?.maxEntries;
|
|
37
37
|
if (max === undefined || entries.length <= max)
|
|
38
38
|
return entries;
|
|
39
|
-
const notifier = createSafeNotifier();
|
|
39
|
+
const notifier = createSafeNotifier(opts?.onNotifyError !== undefined ? { onError: opts.onNotifyError } : undefined);
|
|
40
40
|
const byAge = [...entries].sort((a, b) => a.createdAt - b.createdAt);
|
|
41
41
|
const dropCount = Math.max(0, entries.length - Math.max(0, max));
|
|
42
42
|
const dropped = new Set(byAge.slice(0, dropCount));
|
|
@@ -36,6 +36,7 @@ export interface SendMessageToolOptions {
|
|
|
36
36
|
content: string;
|
|
37
37
|
details?: unknown;
|
|
38
38
|
}>;
|
|
39
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
39
40
|
enrichCtx?: ToolCtxEnricher;
|
|
40
41
|
}
|
|
41
42
|
export declare const SEND_MESSAGE_SUMMARY_MAX = 200;
|
|
@@ -369,7 +369,7 @@ export function createSendMessageTool(opts) {
|
|
|
369
369
|
}
|
|
370
370
|
if (byName.status === "found") {
|
|
371
371
|
const h = byName.handle;
|
|
372
|
-
target = { task_id: h.id, type: h.type, status: h.status, ...(h.toolUseId !== undefined ? { toolUseId: h.toolUseId } : {}), ...(h.sessionScoped ? { sessionScoped: true } : {}), ...(h.description !== undefined ? { description: h.description } : {}), ...(h.name !== undefined ? { name: h.name } : {}), ...(h.agentType !== undefined ? { agentType: h.agentType } : {}), ...(h.owner !== undefined ? { owner: h.owner } : {}), ...(h.scope !== undefined ? { scope: h.scope } : {}), ...(h.parentTaskId !== undefined ? { parentTaskId: h.parentTaskId } : {}), ...(h.parentSessionId !== undefined ? { parentSessionId: h.parentSessionId } : {}), ...(h.rootSessionId !== undefined ? { rootSessionId: h.rootSessionId } : {}) };
|
|
372
|
+
target = { task_id: h.id, type: h.type, status: h.status, createdAt: h.createdAt, ...(h.toolUseId !== undefined ? { toolUseId: h.toolUseId } : {}), ...(h.sessionScoped ? { sessionScoped: true } : {}), ...(h.description !== undefined ? { description: h.description } : {}), ...(h.name !== undefined ? { name: h.name } : {}), ...(h.agentType !== undefined ? { agentType: h.agentType } : {}), ...(h.owner !== undefined ? { owner: h.owner } : {}), ...(h.scope !== undefined ? { scope: h.scope } : {}), ...(h.parentTaskId !== undefined ? { parentTaskId: h.parentTaskId } : {}), ...(h.parentSessionId !== undefined ? { parentSessionId: h.parentSessionId } : {}), ...(h.rootSessionId !== undefined ? { rootSessionId: h.rootSessionId } : {}) };
|
|
373
373
|
}
|
|
374
374
|
else {
|
|
375
375
|
let rosterHit;
|
|
@@ -525,6 +525,8 @@ export function createSendMessageTool(opts) {
|
|
|
525
525
|
...(row.parentTaskId !== undefined ? { rowParentTaskId: row.parentTaskId } : {}),
|
|
526
526
|
...(row.parentSessionId !== undefined ? { rowParentSessionId: row.parentSessionId } : {}),
|
|
527
527
|
...(row.rootSessionId !== undefined ? { rowRootSessionId: row.rootSessionId } : {}),
|
|
528
|
+
...(row.createdAt !== undefined ? { rowSpawnedAt: row.createdAt } : {}),
|
|
529
|
+
...(opts.onNotifyError !== undefined ? { onNotifyError: opts.onNotifyError } : {}),
|
|
528
530
|
...(opts.notify ? { currentParentNotify: opts.notify } : {}),
|
|
529
531
|
});
|
|
530
532
|
const fromPrefix = senderIsChild ? `(message from teammate "${senderLabel}")\n` : "";
|
|
@@ -10,7 +10,6 @@ export type { SubagentStep, SubagentEditedFile } from "./subagent-steps.js";
|
|
|
10
10
|
export declare function notifyResultField(result: string | undefined): string | undefined;
|
|
11
11
|
export declare function inheritedManifestScopeFor(snapshot: readonly unknown[] | undefined): RunInternals["inheritedManifestScope"];
|
|
12
12
|
export declare const DEFAULT_SUBAGENT_TOOL_NAME = "Agent";
|
|
13
|
-
export declare const LEGACY_SUBAGENT_TOOL_NAME = "Task";
|
|
14
13
|
export declare const EXTRA_TOOLS_MAX_FACTORY_CALLS_PER_TREE = 64;
|
|
15
14
|
export declare const DEFAULT_SUBAGENT_MAX_DEPTH = 3;
|
|
16
15
|
export declare const EXTRA_TOOLS_FAILED_NOTE = "note: extraTools evaluation failed \u2014 the injected tool set was skipped for this spawn.";
|
|
@@ -76,6 +75,8 @@ export declare function createSubagentResume(deps: {
|
|
|
76
75
|
rowParentTaskId?: string;
|
|
77
76
|
rowParentSessionId?: string;
|
|
78
77
|
rowRootSessionId?: string;
|
|
78
|
+
rowSpawnedAt?: number;
|
|
79
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
79
80
|
}): (content: string) => Promise<string>;
|
|
80
81
|
export interface SubagentToolOptions {
|
|
81
82
|
runner: Runner;
|
|
@@ -109,6 +110,7 @@ export interface SubagentToolOptions {
|
|
|
109
110
|
onObserverError?: (err: unknown, info: {
|
|
110
111
|
observedAgent?: string;
|
|
111
112
|
observerAgent?: string;
|
|
113
|
+
site?: string;
|
|
112
114
|
}) => void;
|
|
113
115
|
extraTools?: (ctx: SubagentSpawnContext) => ToolSpec[] | Promise<ToolSpec[]>;
|
|
114
116
|
onExtraToolsError?: (err: unknown, info: {
|
package/dist/agents/subagent.js
CHANGED
|
@@ -5,7 +5,6 @@ import { OUTPUT_TOOL_NAME, REPORT_BLOCKED_TOOL_NAME } from "../core/runner/synth
|
|
|
5
5
|
import { TOOL_SEARCH_NAME } from "../core/runner/tool-disclosure.js";
|
|
6
6
|
import { OFFLOAD_TOOL_NAME } from "../core/tool-result-store.js";
|
|
7
7
|
import { resolveToolSubset, toolNameAllowed } from "./tool-filter.js";
|
|
8
|
-
import { canonicalToolName } from "../core/tool-name-aliases.js";
|
|
9
8
|
import { builtinAgentDefinitions } from "./builtin-agents.js";
|
|
10
9
|
import { SUBAGENT_PROMPT } from "../prompts/default.js";
|
|
11
10
|
import { hasSessionFork } from "../core/session.js";
|
|
@@ -75,7 +74,6 @@ export function inheritedManifestScopeFor(snapshot) {
|
|
|
75
74
|
return frames;
|
|
76
75
|
}
|
|
77
76
|
export const DEFAULT_SUBAGENT_TOOL_NAME = "Agent";
|
|
78
|
-
export const LEGACY_SUBAGENT_TOOL_NAME = "Task";
|
|
79
77
|
export const EXTRA_TOOLS_MAX_FACTORY_CALLS_PER_TREE = 64;
|
|
80
78
|
export const DEFAULT_SUBAGENT_MAX_DEPTH = 3;
|
|
81
79
|
export const EXTRA_TOOLS_FAILED_NOTE = "note: extraTools evaluation failed — the injected tool set was skipped for this spawn.";
|
|
@@ -85,7 +83,7 @@ function countArgLines(v) {
|
|
|
85
83
|
}
|
|
86
84
|
function createToolStatsCounter(delegationToolName) {
|
|
87
85
|
const t = { readCount: 0, searchCount: 0, bashCount: 0, editFileCount: 0, linesAdded: 0, linesRemoved: 0, otherToolCount: 0 };
|
|
88
|
-
const excluded = new Set([delegationToolName, DEFAULT_SUBAGENT_TOOL_NAME
|
|
86
|
+
const excluded = new Set([delegationToolName, DEFAULT_SUBAGENT_TOOL_NAME]);
|
|
89
87
|
const recordEditLines = (args) => {
|
|
90
88
|
if (args === null || typeof args !== "object")
|
|
91
89
|
return;
|
|
@@ -210,7 +208,7 @@ const ccElapsedTag = (ms) => {
|
|
|
210
208
|
};
|
|
211
209
|
const ccCompletionText = (desc, settled, rawStatus, elapsedMs) => `Agent "${desc}" ${settled === "killed" ? "stopped" : settled === "completed" ? "finished" : rawStatus}${ccElapsedTag(elapsedMs)}`;
|
|
212
210
|
const BG_AGENT_COLLATERAL_REAP_REASON = "its parent run ended";
|
|
213
|
-
const RESERVED_AGENT_NAMES = new Set([OUTPUT_TOOL_NAME, TOOL_SEARCH_NAME, OFFLOAD_TOOL_NAME, REPORT_BLOCKED_TOOL_NAME, DEFAULT_SUBAGENT_TOOL_NAME
|
|
211
|
+
const RESERVED_AGENT_NAMES = new Set([OUTPUT_TOOL_NAME, TOOL_SEARCH_NAME, OFFLOAD_TOOL_NAME, REPORT_BLOCKED_TOOL_NAME, DEFAULT_SUBAGENT_TOOL_NAME]);
|
|
214
212
|
export const REPORT_FIELD_MAX = 300;
|
|
215
213
|
function configError(message, code) {
|
|
216
214
|
const e = new Error(message);
|
|
@@ -276,7 +274,7 @@ function createSteerHandle(stream, parentToolCallId, agentName, settled, retain)
|
|
|
276
274
|
};
|
|
277
275
|
}
|
|
278
276
|
export function createSubagentResume(deps) {
|
|
279
|
-
const notifier = createSafeNotifier();
|
|
277
|
+
const notifier = createSafeNotifier(deps.onNotifyError !== undefined ? { onError: deps.onNotifyError } : undefined);
|
|
280
278
|
const resume = async (content) => {
|
|
281
279
|
const ledger = deps.ledger;
|
|
282
280
|
if (!ledger) {
|
|
@@ -373,8 +371,8 @@ export function createSubagentResume(deps) {
|
|
|
373
371
|
...(deps.parentToolCallId !== undefined ? { parentToolCallId: deps.parentToolCallId } : {}),
|
|
374
372
|
...(deps.rowParentTaskId !== undefined ? { parentTaskId: deps.rowParentTaskId } : {}),
|
|
375
373
|
...(deps.rowParentSessionId !== undefined ? { parentSessionId: deps.rowParentSessionId } : {}),
|
|
376
|
-
...(deps.rowRootSessionId !== undefined ? { rootSessionId: deps.rowRootSessionId } : {}),
|
|
377
|
-
startedAt: Date.now(),
|
|
374
|
+
...((deps.rowRootSessionId ?? deps.rowParentSessionId) !== undefined ? { rootSessionId: deps.rowRootSessionId ?? deps.rowParentSessionId } : {}),
|
|
375
|
+
startedAt: deps.rowSpawnedAt ?? Date.now(),
|
|
378
376
|
});
|
|
379
377
|
}
|
|
380
378
|
}
|
|
@@ -643,18 +641,6 @@ function parkCompletionNotify(deps) {
|
|
|
643
641
|
};
|
|
644
642
|
entry.deferredNotify = { ...(payload.seq !== undefined ? { seq: payload.seq } : {}), cancel, flush: () => deliver(false) };
|
|
645
643
|
}
|
|
646
|
-
function adaptLegacyForkArgs(args) {
|
|
647
|
-
const raw = args;
|
|
648
|
-
if (raw !== null && typeof raw === "object" && raw.prompt === undefined && typeof raw.directive === "string") {
|
|
649
|
-
return {
|
|
650
|
-
...args,
|
|
651
|
-
prompt: raw.directive,
|
|
652
|
-
subagent_type: FORK_SUBAGENT_TYPE,
|
|
653
|
-
description: typeof raw.description === "string" ? raw.description : "forked agent (legacy Fork call)",
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
return args;
|
|
657
|
-
}
|
|
658
644
|
export function normalizeSubagentType(value) {
|
|
659
645
|
return value.normalize("NFKC").toLowerCase().replace(/[\p{White_Space}\p{Pd}_]+/gu, "");
|
|
660
646
|
}
|
|
@@ -788,7 +774,7 @@ export function agentWhenToUseText(def, lean = true) {
|
|
|
788
774
|
function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
789
775
|
const maxDepth = opts.maxDepth ?? DEFAULT_SUBAGENT_MAX_DEPTH;
|
|
790
776
|
const toolName = opts.name ?? DEFAULT_SUBAGENT_TOOL_NAME;
|
|
791
|
-
const notifier = createSafeNotifier();
|
|
777
|
+
const notifier = createSafeNotifier(opts.onObserverError !== undefined ? { onError: (f) => opts.onObserverError?.(f.error, { site: f.site }) } : undefined);
|
|
792
778
|
const deploymentNames = new Set((opts.agents ?? []).map((a) => a.name));
|
|
793
779
|
const builtins = opts.builtinAgents === false ? [] : builtinAgentDefinitions(toolName).filter((d) => !deploymentNames.has(d.name));
|
|
794
780
|
const available = [...(opts.agents ?? []), ...builtins].filter((a) => !excluded.has(a.name));
|
|
@@ -825,7 +811,6 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
825
811
|
agentListing,
|
|
826
812
|
...(rosterNames !== undefined ? { agentModels: rosterNames } : {}),
|
|
827
813
|
executionMode: "parallel",
|
|
828
|
-
...(!opts.name ? { aliases: [LEGACY_SUBAGENT_TOOL_NAME] } : {}),
|
|
829
814
|
contract: { contractId: "core.agent@1", implementationRevision: "1" },
|
|
830
815
|
description: `Launch a new agent to handle complex, multi-step tasks. Each agent type has specific capabilities and tools available to it.\n` +
|
|
831
816
|
(opts.purpose ? `\nThis sub-agent is for: ${opts.purpose}.\n` : "") +
|
|
@@ -909,9 +894,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
909
894
|
}
|
|
910
895
|
: {}),
|
|
911
896
|
}),
|
|
912
|
-
prepareArguments: (args) => foldGeneralPurposeAlias(
|
|
897
|
+
prepareArguments: (args) => foldGeneralPurposeAlias(args, generalPurposeShadowed),
|
|
913
898
|
execute: async (args, ctx) => {
|
|
914
|
-
const a = foldGeneralPurposeAlias(
|
|
899
|
+
const a = foldGeneralPurposeAlias(args, generalPurposeShadowed);
|
|
915
900
|
const wantsBackground = opts.background !== undefined && a.run_in_background !== false;
|
|
916
901
|
const reviveClaim = ctx.reviveClaim;
|
|
917
902
|
if (reviveClaim !== undefined && opts.background === undefined) {
|
|
@@ -1137,18 +1122,16 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1137
1122
|
if (injectedTools.length > 0) {
|
|
1138
1123
|
const taken = new Set();
|
|
1139
1124
|
const reserve = (t) => {
|
|
1140
|
-
taken.add(
|
|
1125
|
+
taken.add(t.name);
|
|
1141
1126
|
for (const alias of t.aliases ?? [])
|
|
1142
|
-
taken.add(
|
|
1127
|
+
taken.add(alias);
|
|
1143
1128
|
};
|
|
1144
1129
|
for (const t of opts.tools ?? [])
|
|
1145
1130
|
reserve(t);
|
|
1146
|
-
taken.add(
|
|
1147
|
-
if (!opts.name)
|
|
1148
|
-
taken.add(canonicalToolName(LEGACY_SUBAGENT_TOOL_NAME));
|
|
1131
|
+
taken.add(toolName);
|
|
1149
1132
|
const merged = [...(opts.tools ?? [])];
|
|
1150
1133
|
for (const t of injectedTools) {
|
|
1151
|
-
const names = [
|
|
1134
|
+
const names = [t.name, ...(t.aliases ?? [])];
|
|
1152
1135
|
if (names.some((n) => taken.has(n)))
|
|
1153
1136
|
continue;
|
|
1154
1137
|
for (const n of names)
|
|
@@ -1398,7 +1381,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1398
1381
|
? {
|
|
1399
1382
|
...(reviveClaim.row.parentTaskId !== undefined ? { parentTaskId: reviveClaim.row.parentTaskId } : {}),
|
|
1400
1383
|
...(reviveClaim.row.parentSessionId !== undefined ? { parentSessionId: reviveClaim.row.parentSessionId } : {}),
|
|
1401
|
-
...(reviveClaim.row.rootSessionId
|
|
1384
|
+
...((reviveClaim.row.rootSessionId ?? reviveClaim.row.parentSessionId) !== undefined
|
|
1385
|
+
? { rootSessionId: reviveClaim.row.rootSessionId ?? reviveClaim.row.parentSessionId }
|
|
1386
|
+
: {}),
|
|
1402
1387
|
}
|
|
1403
1388
|
: {
|
|
1404
1389
|
...(ctx.taskId !== undefined ? { parentTaskId: ctx.taskId } : {}),
|
|
@@ -2062,7 +2047,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2062
2047
|
...(reviveRow.name !== undefined ? { name: reviveRow.name } : {}),
|
|
2063
2048
|
...(reviveRow.parentTaskId !== undefined ? { parentTaskId: reviveRow.parentTaskId } : {}),
|
|
2064
2049
|
...(reviveRow.parentSessionId !== undefined ? { parentSessionId: reviveRow.parentSessionId } : {}),
|
|
2065
|
-
...(reviveRow.rootSessionId
|
|
2050
|
+
...((reviveRow.rootSessionId ?? reviveRow.parentSessionId) !== undefined
|
|
2051
|
+
? { rootSessionId: reviveRow.rootSessionId ?? reviveRow.parentSessionId }
|
|
2052
|
+
: {}),
|
|
2066
2053
|
...(reviveRow.model !== undefined ? { model: reviveRow.model } : {}),
|
|
2067
2054
|
...(reviveRow.teamName !== undefined ? { teamName: reviveRow.teamName } : {}),
|
|
2068
2055
|
deliveryChannel: "attaching",
|
|
@@ -2109,7 +2096,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2109
2096
|
return { isError: true, content: `Sub-agent not started in background: ${e instanceof Error ? e.message : String(e)}${wt ? `\n${wt}` : ""}`, details: { error: "register_failed" } };
|
|
2110
2097
|
}
|
|
2111
2098
|
if (agentName !== undefined) {
|
|
2112
|
-
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...((typeof childModel === "string" ? resolveModelDisplayLabel(childModel) : childModel?.id) !== undefined ? { model: typeof childModel === "string" ? childModel : childModel?.id } : {}), ...(reviveRow !== undefined ? (reviveRow.rootSessionId !== undefined ? { rootSessionId: reviveRow.rootSessionId } : {}) : (ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: reviveRow?.spawnedAt ?? Date.now() });
|
|
2099
|
+
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...((typeof childModel === "string" ? resolveModelDisplayLabel(childModel) : childModel?.id) !== undefined ? { model: typeof childModel === "string" ? childModel : childModel?.id } : {}), ...(reviveRow !== undefined ? ((reviveRow.rootSessionId ?? reviveRow.parentSessionId) !== undefined ? { rootSessionId: reviveRow.rootSessionId ?? reviveRow.parentSessionId } : {}) : (ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: reviveRow?.spawnedAt ?? Date.now() });
|
|
2113
2100
|
}
|
|
2114
2101
|
const bgSink = ctx.onBackgroundChildEvent;
|
|
2115
2102
|
const sinkEmit = (event) => {
|
|
@@ -2170,14 +2157,14 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2170
2157
|
? {
|
|
2171
2158
|
...(reviveRow.parentTaskId !== undefined ? { parentTaskId: reviveRow.parentTaskId } : {}),
|
|
2172
2159
|
...(reviveRow.parentSessionId !== undefined ? { parentSessionId: reviveRow.parentSessionId } : {}),
|
|
2173
|
-
...(reviveRow.rootSessionId !== undefined ? { rootSessionId: reviveRow.rootSessionId } : {}),
|
|
2160
|
+
...((reviveRow.rootSessionId ?? reviveRow.parentSessionId) !== undefined ? { rootSessionId: reviveRow.rootSessionId ?? reviveRow.parentSessionId } : {}),
|
|
2174
2161
|
}
|
|
2175
2162
|
: {
|
|
2176
2163
|
...(ctx.taskId !== undefined && ctx.taskId !== ctx.sessionId ? { parentTaskId: ctx.taskId } : {}),
|
|
2177
2164
|
...(ctx.sessionId !== undefined ? { parentSessionId: ctx.sessionId } : {}),
|
|
2178
2165
|
...((ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}),
|
|
2179
2166
|
}),
|
|
2180
|
-
startedAt: Date.now(),
|
|
2167
|
+
startedAt: reviveRow?.spawnedAt ?? Date.now(),
|
|
2181
2168
|
});
|
|
2182
2169
|
const bgRetainLedger = reviveRow !== undefined
|
|
2183
2170
|
? undefined
|
|
@@ -2690,6 +2677,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2690
2677
|
...(opts.background?.notify ? { notify: opts.background.notify } : {}),
|
|
2691
2678
|
sink: ctx.onSubagentSpawn,
|
|
2692
2679
|
...(opts.background ? { registry: opts.background.registry } : {}),
|
|
2680
|
+
...(opts.onObserverError !== undefined ? { onNotifyError: (f) => opts.onObserverError?.(f.error, { site: f.site }) } : {}),
|
|
2693
2681
|
});
|
|
2694
2682
|
notifier.notify(() => ctx.onSubagentSpawn?.(createSteerHandle(stream, ctx.toolCallId, childAgentName, settled, {
|
|
2695
2683
|
resume,
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { canonicalToolName } from "../core/tool-name-aliases.js";
|
|
2
1
|
export function toolNameAllowed(name, allowTools, denyTools) {
|
|
3
|
-
const allow = allowTools !== undefined && !allowTools.includes("*") ? new Set(allowTools
|
|
4
|
-
const deny = denyTools && denyTools.length > 0 ? new Set(denyTools
|
|
5
|
-
const n =
|
|
2
|
+
const allow = allowTools !== undefined && !allowTools.includes("*") ? new Set(allowTools) : undefined;
|
|
3
|
+
const deny = denyTools && denyTools.length > 0 ? new Set(denyTools) : undefined;
|
|
4
|
+
const n = name;
|
|
6
5
|
return (!allow || allow.has(n)) && (!deny || !deny.has(n));
|
|
7
6
|
}
|
|
8
7
|
export function resolveToolSubset(pool, allowTools, denyTools) {
|
|
9
|
-
const allow = allowTools !== undefined && !allowTools.includes("*") ? new Set(allowTools
|
|
10
|
-
const deny = denyTools && denyTools.length > 0 ? new Set(denyTools
|
|
8
|
+
const allow = allowTools !== undefined && !allowTools.includes("*") ? new Set(allowTools) : undefined;
|
|
9
|
+
const deny = denyTools && denyTools.length > 0 ? new Set(denyTools) : undefined;
|
|
11
10
|
if (!allow && !deny)
|
|
12
11
|
return [...pool];
|
|
13
12
|
return pool.filter((t) => {
|
|
14
|
-
const matches = (set) => set.has(
|
|
13
|
+
const matches = (set) => set.has(t.name) || (t.aliases ?? []).some((a) => set.has(a));
|
|
15
14
|
return (!allow || matches(allow)) && (!deny || !matches(deny));
|
|
16
15
|
});
|
|
17
16
|
}
|
package/dist/agents/verify.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare const VerdictSchema: Type.TObject<{
|
|
|
11
11
|
}>;
|
|
12
12
|
export type Verdict = Static<typeof VerdictSchema>;
|
|
13
13
|
export interface VerifyConfig {
|
|
14
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
14
15
|
verifierModel?: ModelRef;
|
|
15
16
|
verifierTools?: ToolSpec[];
|
|
16
17
|
maxRounds?: number;
|
package/dist/agents/verify.js
CHANGED
|
@@ -86,7 +86,7 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
86
86
|
return { ...result, verification: { verdict: "unverified", unverifiedReason: "impl_incomplete", rounds: 0, findings: [] } };
|
|
87
87
|
}
|
|
88
88
|
const maxRounds = Number.isFinite(config.maxRounds) ? Math.max(1, Math.floor(config.maxRounds)) : 2;
|
|
89
|
-
const notifier = createSafeNotifier();
|
|
89
|
+
const notifier = createSafeNotifier(config.onNotifyError !== undefined ? { onError: config.onNotifyError } : undefined);
|
|
90
90
|
const verifierTools = config.verifierTools ?? (specBase.tools ?? []).filter((t) => t.effect === "read");
|
|
91
91
|
const evidenceMode = config.evidence != null && config.evidence.trim() !== "";
|
|
92
92
|
const verifierPrompt = config.verifierPrompt ?? (evidenceMode ? STATIC_VERIFICATION_PROMPT : VERIFICATION_PROMPT);
|
|
@@ -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?: {
|
|
@@ -97,7 +97,7 @@ export async function maybeCompact(opts) {
|
|
|
97
97
|
return { contextUsage, compacted: false, noop: true };
|
|
98
98
|
}
|
|
99
99
|
const trigger = opts.trigger ?? "auto";
|
|
100
|
-
const notifier = createSafeNotifier();
|
|
100
|
+
const notifier = createSafeNotifier(opts.onNotifyError !== undefined ? { onError: opts.onNotifyError } : undefined);
|
|
101
101
|
let hookInstructions;
|
|
102
102
|
if (opts.preCompact) {
|
|
103
103
|
let pre;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { canonicalizeTarget, writeTargetPath } from "../tools/fs/safety.js";
|
|
2
|
-
import { canonicalToolName } from "./tool-name-aliases.js";
|
|
3
2
|
import { PATH_WRITE_TOOLS, isWithin } from "./runner/session-rule-policy.js";
|
|
4
3
|
const ask = (message) => ({ action: "ask", message, decisionReason: "rule" });
|
|
5
4
|
export function createFsWriteGatePolicy(opts) {
|
|
6
5
|
const { env, rootPath, defaultWrite } = opts;
|
|
7
|
-
const gated = new Set([...PATH_WRITE_TOOLS, "NotebookEdit"]
|
|
6
|
+
const gated = new Set([...PATH_WRITE_TOOLS, "NotebookEdit"]);
|
|
8
7
|
const acceptDirs = opts.acceptDirs && opts.acceptDirs.length > 0 ? opts.acceptDirs : undefined;
|
|
9
8
|
const exemptDirs = opts.exemptDirs && opts.exemptDirs.length > 0 ? opts.exemptDirs : undefined;
|
|
10
9
|
return {
|
|
11
10
|
async check(req, signal) {
|
|
12
|
-
const canonical =
|
|
11
|
+
const canonical = req.toolName;
|
|
13
12
|
if (!gated.has(canonical))
|
|
14
13
|
return { action: "allow" };
|
|
15
14
|
const path = writeTargetPath(canonical, req.args);
|