@sema-agent/core 1.429.0 → 1.431.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/dist/agents/subagent.js +51 -0
- package/dist/core/runner/prepare-task.js +1 -0
- package/dist/core/types.d.ts +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +38 -2
- package/package.json +1 -1
package/dist/agents/subagent.js
CHANGED
|
@@ -1569,6 +1569,36 @@ function makeSubagentTool(opts, depth, excluded, extraToolsBudget) {
|
|
|
1569
1569
|
timer.unref?.();
|
|
1570
1570
|
return true;
|
|
1571
1571
|
};
|
|
1572
|
+
if (ctx.autoModeReview) {
|
|
1573
|
+
const spawnToolNames = childTools.map((t) => t.name);
|
|
1574
|
+
const spawnToolsNote = spawnToolNames.length > 0
|
|
1575
|
+
? spawnToolNames.join(", ")
|
|
1576
|
+
: "(none explicitly listed — but if this deployment gave the child a real execution environment, it likely also has the standard file/shell tools: Read/Edit/Write/Bash/Grep/Glob)";
|
|
1577
|
+
const spawnVerdict = await ctx.autoModeReview.decider
|
|
1578
|
+
.decide({
|
|
1579
|
+
req: {
|
|
1580
|
+
toolName: wantsFork ? "Agent(fork)" : "Agent",
|
|
1581
|
+
args: { objective: prompt.slice(0, 2_000), tools: spawnToolNames, systemPrompt: childSystemPrompt?.slice(0, 2_000) },
|
|
1582
|
+
toolCallId: ctx.toolCallId,
|
|
1583
|
+
},
|
|
1584
|
+
askMessage: `Reviewing a sub-agent about to be spawned. Objective: ${prompt.slice(0, 2000)}${prompt.length > 2000 ? "…" : ""}\nTools available to it: ${spawnToolsNote}`,
|
|
1585
|
+
}, ctx.signal)
|
|
1586
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
1587
|
+
if (ctx.signal?.aborted) {
|
|
1588
|
+
await cancelObserver();
|
|
1589
|
+
const wt = await finishWorktree();
|
|
1590
|
+
return { isError: true, content: `Sub-agent not started: the delegating call was aborted.${wt ? `\n${wt}` : ""}`, details: { error: "aborted" } };
|
|
1591
|
+
}
|
|
1592
|
+
if (spawnVerdict.kind === "block") {
|
|
1593
|
+
await cancelObserver();
|
|
1594
|
+
const wt = await finishWorktree();
|
|
1595
|
+
return {
|
|
1596
|
+
isError: true,
|
|
1597
|
+
content: `Sub-agent not started: blocked by the auto-mode classifier${spawnVerdict.reason ? `: ${inlineUntrusted(spawnVerdict.reason)}` : ""}.${wt ? `\n${wt}` : ""}`,
|
|
1598
|
+
details: { error: "autoMode.spawn_blocked" },
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1572
1602
|
if (wantsFork) {
|
|
1573
1603
|
let forkedId;
|
|
1574
1604
|
let releaseForked;
|
|
@@ -2511,6 +2541,26 @@ task_id: ${taskId}
|
|
|
2511
2541
|
child = await opts.runner.runTask(buildChildSpec(ctx.signal), childInternals);
|
|
2512
2542
|
}
|
|
2513
2543
|
await settleObserver(child.status);
|
|
2544
|
+
let handbackWarning;
|
|
2545
|
+
if (ctx.autoModeReview) {
|
|
2546
|
+
const handbackSteps = stepRecorder.recentSteps();
|
|
2547
|
+
const handbackEdits = stepRecorder.editedFiles();
|
|
2548
|
+
if (child.result || handbackSteps || handbackEdits) {
|
|
2549
|
+
const handbackVerdict = await ctx.autoModeReview.decider
|
|
2550
|
+
.decide({
|
|
2551
|
+
req: {
|
|
2552
|
+
toolName: "Agent(handback)",
|
|
2553
|
+
args: { result: child.result?.slice(0, 2_000), toolSteps: handbackSteps, editedFiles: handbackEdits },
|
|
2554
|
+
toolCallId: ctx.toolCallId,
|
|
2555
|
+
},
|
|
2556
|
+
askMessage: "Subagent has finished and is handing back control to the main agent. Review the subagent's work and flag if any action may violate security policy.",
|
|
2557
|
+
}, ctx.signal)
|
|
2558
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
2559
|
+
if (handbackVerdict.kind === "block") {
|
|
2560
|
+
handbackWarning = `SECURITY WARNING: This subagent performed actions that may violate security policy. Reason: ${inlineUntrusted(handbackVerdict.reason ?? "(no reason given)")}. Review the subagent's actions carefully before acting on its output.`;
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2514
2564
|
const ns = child.stats.nested;
|
|
2515
2565
|
ctx.reportUsage?.({
|
|
2516
2566
|
tokens: child.stats.tokens + (ns?.tokens ?? 0),
|
|
@@ -2545,6 +2595,7 @@ task_id: ${taskId}
|
|
|
2545
2595
|
const worktreeLine = await finishWorktree();
|
|
2546
2596
|
const errClass = classifySubagentError(child);
|
|
2547
2597
|
const lines = [
|
|
2598
|
+
...(handbackWarning ? [handbackWarning, ""] : []),
|
|
2548
2599
|
`[Sub-agent report${def ? ` · ${def.name}` : ""}${a.description ? ` · ${String(a.description)}` : ""}]`,
|
|
2549
2600
|
`status: ${child.status}`,
|
|
2550
2601
|
modelNote ?? "",
|
|
@@ -757,6 +757,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
|
|
|
757
757
|
...(centerAdoption?.sourceRevision !== undefined ? { centerSourceRevision: centerAdoption.sourceRevision } : {}),
|
|
758
758
|
activeSkillScope: () => skillScope.active(),
|
|
759
759
|
inheritedGateForChildren,
|
|
760
|
+
...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
|
|
760
761
|
...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
|
|
761
762
|
taskId: hostTaskId,
|
|
762
763
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
package/dist/core/types.d.ts
CHANGED
|
@@ -101,6 +101,9 @@ export interface ToolExecuteContext {
|
|
|
101
101
|
getApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
|
|
102
102
|
activeSkillScope?: () => readonly unknown[];
|
|
103
103
|
inheritedGateForChildren?: () => import("./runner/prepare-task.js").InheritedGate;
|
|
104
|
+
autoModeReview?: {
|
|
105
|
+
decider: import("./auto-mode.js").AutoModeDecider;
|
|
106
|
+
};
|
|
104
107
|
durableApprovalForChildren?: {
|
|
105
108
|
scope: string;
|
|
106
109
|
ttlMs?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export type { ExecStep, ExecStepResult, ExecGateResult, ExecGateOptions } from "
|
|
|
65
65
|
export { sanitizeUntrustedText, delimitUntrusted, inlineUntrusted } from "./core/untrusted-text.js";
|
|
66
66
|
export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
|
|
67
67
|
export type { InvariantKind, FunctionContract, Invariant, InvariantViolation, CheckResult, } from "./core/property-harness.js";
|
|
68
|
-
export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly } from "./tools/fs/index.js";
|
|
68
|
+
export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
|
|
69
69
|
export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, type ToolResultStore, type ToolResultSlice, } from "./core/tool-result-store.js";
|
|
70
70
|
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, WALLTIME_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingWalltimeMs, winnerFromOutcome, validatePendingSteer, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type ResourceLedger, type ResourceLimitReason, } from "./core/checkpoint-store.js";
|
|
71
71
|
export { InMemoryFileSnapshotStore, DEFAULT_SNAPSHOT_BOUNDS } from "./core/file-snapshot-store.js";
|
package/dist/index.js
CHANGED
|
@@ -56,7 +56,7 @@ export { addWorktree, pruneWorktrees, WORKTREE_PARENT } from "./core/git-worktre
|
|
|
56
56
|
export { runExecGate } from "./core/exec-gate.js";
|
|
57
57
|
export { sanitizeUntrustedText, delimitUntrusted, inlineUntrusted } from "./core/untrusted-text.js";
|
|
58
58
|
export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
|
|
59
|
-
export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly } from "./tools/fs/index.js";
|
|
59
|
+
export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
|
|
60
60
|
export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, } from "./core/tool-result-store.js";
|
|
61
61
|
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, WALLTIME_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingWalltimeMs, winnerFromOutcome, validatePendingSteer, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
|
|
62
62
|
export { InMemoryFileSnapshotStore, DEFAULT_SNAPSHOT_BOUNDS } from "./core/file-snapshot-store.js";
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { type ImageDownsampler } from "../../core/mcp.js";
|
|
|
6
6
|
import { type PdfModelCapabilities } from "./pdf.js";
|
|
7
7
|
export { pdfModelCapabilitiesOf, type PdfModelCapabilities } from "./pdf.js";
|
|
8
8
|
export declare const SLICED_READ_MAX_BYTES: number;
|
|
9
|
+
export declare const MAX_EDIT_BYTES: number;
|
|
9
10
|
export type ReadImageDownsamplerOption = ImageDownsampler | false | undefined;
|
|
10
11
|
export declare function msTimeoutToSec(timeoutMs: number | undefined): number;
|
|
11
12
|
export declare function seededFileUnchangedReminder(filePath: string): string;
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -14,6 +14,26 @@ import { PDF_FALLBACK_RENDER_PAGES, PDF_INLINE_PAGE_THRESHOLD, PDF_MAX_EXTRACT_S
|
|
|
14
14
|
export { pdfModelCapabilitiesOf } from "./pdf.js";
|
|
15
15
|
const MAX_READ_BYTES = 256 * 1024;
|
|
16
16
|
export const SLICED_READ_MAX_BYTES = 64 * 1024 * 1024;
|
|
17
|
+
export const MAX_EDIT_BYTES = 1024 * 1024 * 1024;
|
|
18
|
+
function formatByteSize(n) {
|
|
19
|
+
const kb = n / 1024;
|
|
20
|
+
if (kb < 1)
|
|
21
|
+
return `${n} bytes`;
|
|
22
|
+
if (kb < 1024)
|
|
23
|
+
return `${kb.toFixed(1).replace(/\.0$/, "")}KB`;
|
|
24
|
+
const mb = kb / 1024;
|
|
25
|
+
if (mb < 1024)
|
|
26
|
+
return `${mb.toFixed(1).replace(/\.0$/, "")}MB`;
|
|
27
|
+
return `${(mb / 1024).toFixed(1).replace(/\.0$/, "")}GB`;
|
|
28
|
+
}
|
|
29
|
+
function decodeEditBytes(bytes, path) {
|
|
30
|
+
try {
|
|
31
|
+
return { ok: true, value: decodeTextBytes(bytes) };
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { ok: false, message: `Error (Edit): "${path}" is too large to edit (${formatByteSize(bytes.byteLength)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.` };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
17
37
|
async function notReadRefusalText(env, toolName, key, v, signal) {
|
|
18
38
|
const base = `Error (${toolName}): ${v.message}`;
|
|
19
39
|
const info = await env.fileInfo(key, signal);
|
|
@@ -656,11 +676,21 @@ export function makeEditFileTool(env, state, rootCanonical, cwdRef, additionalRo
|
|
|
656
676
|
}
|
|
657
677
|
return `Error (Edit): ${await enoentMessage(env, r.key, cwdRef?.current ?? rootCanonical, ctx.signal)}`;
|
|
658
678
|
}
|
|
679
|
+
const editInfo = await env.fileInfo(r.key, ctx.signal);
|
|
680
|
+
if (editInfo.ok && editInfo.value.size > MAX_EDIT_BYTES) {
|
|
681
|
+
return `Error (Edit): "${path}" is too large to edit (${formatByteSize(editInfo.value.size)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.`;
|
|
682
|
+
}
|
|
659
683
|
if (singleOld === "") {
|
|
660
684
|
const preBin = await env.readBinaryFile(r.key, ctx.signal);
|
|
661
685
|
if (!preBin.ok)
|
|
662
686
|
return `Error (Edit): cannot read "${path}": ${preBin.error.message}`;
|
|
663
|
-
|
|
687
|
+
if (preBin.value.byteLength > MAX_EDIT_BYTES) {
|
|
688
|
+
return `Error (Edit): "${path}" is too large to edit (${formatByteSize(preBin.value.byteLength)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.`;
|
|
689
|
+
}
|
|
690
|
+
const preDecResult = decodeEditBytes(preBin.value, path);
|
|
691
|
+
if (!preDecResult.ok)
|
|
692
|
+
return preDecResult.message;
|
|
693
|
+
const preDec = preDecResult.value;
|
|
664
694
|
if (preDec.malformed)
|
|
665
695
|
return `Error (Edit): "${path}" has a truncated UTF-16 body; repair/convert it with bash first.`;
|
|
666
696
|
if (preDec.text.trim() !== "")
|
|
@@ -686,7 +716,13 @@ export function makeEditFileTool(env, state, rootCanonical, cwdRef, additionalRo
|
|
|
686
716
|
const readBin = await env.readBinaryFile(r.key, ctx.signal);
|
|
687
717
|
if (!readBin.ok)
|
|
688
718
|
return `Error (Edit): cannot read "${path}": ${readBin.error.message}`;
|
|
689
|
-
|
|
719
|
+
if (readBin.value.byteLength > MAX_EDIT_BYTES) {
|
|
720
|
+
return `Error (Edit): "${path}" is too large to edit (${formatByteSize(readBin.value.byteLength)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.`;
|
|
721
|
+
}
|
|
722
|
+
const decodedResult = decodeEditBytes(readBin.value, path);
|
|
723
|
+
if (!decodedResult.ok)
|
|
724
|
+
return decodedResult.message;
|
|
725
|
+
const decoded = decodedResult.value;
|
|
690
726
|
if (decoded.malformed)
|
|
691
727
|
return `Error (Edit): "${path}" has a truncated UTF-16 body; repair/convert it with bash first.`;
|
|
692
728
|
const original = decoded.text;
|