@sema-agent/core 2.11.0 → 2.13.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/agent-transcript-tool.d.ts +1 -0
- package/dist/agents/agent-transcript-tool.js +1 -1
- package/dist/brain/stream-engine.js +4 -1
- package/dist/core/auto-promote.js +2 -1
- package/dist/core/checkpoint-store.d.ts +1 -0
- package/dist/core/checkpoint-store.js +3 -0
- package/dist/core/git-worktree-env.js +5 -0
- package/dist/core/mcp.d.ts +3 -0
- package/dist/core/mcp.js +91 -7
- package/dist/core/remote-env.d.ts +3 -0
- package/dist/core/remote-env.js +19 -1
- package/dist/core/runner/active-skill-scope.js +34 -6
- package/dist/core/runner/assemble-result.d.ts +3 -0
- package/dist/core/runner/assemble-result.js +4 -1
- package/dist/core/runner/prepare-task.d.ts +4 -0
- package/dist/core/runner/prepare-task.js +120 -25
- package/dist/core/runner/runtask.js +49 -10
- package/dist/core/runner/tool-disclosure.d.ts +1 -0
- package/dist/core/runner/tool-disclosure.js +26 -7
- package/dist/core/session-store.js +15 -4
- package/dist/core/skill-tool-specifier.d.ts +8 -0
- package/dist/core/skill-tool-specifier.js +58 -0
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/skills-directory.js +16 -4
- package/dist/core/types.d.ts +11 -5
- package/dist/core/with-retry.js +0 -1
- package/dist/engine/harness/types.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/internal/llm.d.ts +1 -1
- package/dist/orchestration/workflow.js +8 -4
- package/dist/tools/fs/bash-readonly-classifier.js +38 -6
- package/dist/tools/fs/fs-bash.js +14 -0
- package/dist/tools/scheduler-tools.js +38 -18
- package/dist/tools/web.d.ts +8 -2
- package/dist/tools/web.js +46 -17
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ export interface AgentTranscriptToolOptions {
|
|
|
8
8
|
owner?: string;
|
|
9
9
|
scope?: string;
|
|
10
10
|
sessionId?: string;
|
|
11
|
+
enrichCtx?: import("../core/tools.js").ToolCtxEnricher;
|
|
11
12
|
}
|
|
12
13
|
export declare function createAgentTranscriptTool(opts: AgentTranscriptToolOptions): import("../internal/harness-types.js").AgentTool<Type.TObject<{
|
|
13
14
|
id: Type.TString;
|
|
@@ -43,6 +43,9 @@ function emptyAssistant(model) {
|
|
|
43
43
|
function isAbortError(err) {
|
|
44
44
|
return err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError");
|
|
45
45
|
}
|
|
46
|
+
function isWalltimeCutoff(err) {
|
|
47
|
+
return err instanceof Error && err.message === WALLTIME_CUTOFF_MESSAGE;
|
|
48
|
+
}
|
|
46
49
|
function sleep(ms, signal) {
|
|
47
50
|
return new Promise((resolve) => {
|
|
48
51
|
if (signal?.aborted)
|
|
@@ -85,7 +88,7 @@ export function runStreamingBrain(args) {
|
|
|
85
88
|
.catch((err) => {
|
|
86
89
|
const aborted = signal?.aborted === true || isAbortError(err);
|
|
87
90
|
terminalRetryPhase = "gave_up";
|
|
88
|
-
terminalRetryDetail = aborted ? "cancelled while retrying" : "retries exhausted";
|
|
91
|
+
terminalRetryDetail = aborted ? "cancelled while retrying" : isWalltimeCutoff(err) ? "wall-clock deadline reached while retrying" : "retries exhausted";
|
|
89
92
|
const errorMsg = emptyAssistant(model);
|
|
90
93
|
errorMsg.stopReason = aborted ? "aborted" : "error";
|
|
91
94
|
errorMsg.errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { extractSymbols } from "../tools/fs/repo-map.js";
|
|
2
|
+
import { parseSkillToolEntry } from "./skill-tool-specifier.js";
|
|
2
3
|
export function decideAutoPromote(i) {
|
|
3
4
|
if (i.tripwire.escalate)
|
|
4
5
|
return "needs_human";
|
|
@@ -58,7 +59,7 @@ export function deriveTripwire(artifactText, declared, profileTokens) {
|
|
|
58
59
|
const reasons = [];
|
|
59
60
|
let escalate = false;
|
|
60
61
|
try {
|
|
61
|
-
const allow = new Set(declared.allowTools ?? []);
|
|
62
|
+
const allow = new Set((declared.allowTools ?? []).map((entry) => parseSkillToolEntry(entry).name));
|
|
62
63
|
const symbols = extractSymbols(artifactText);
|
|
63
64
|
for (const sym of symbols) {
|
|
64
65
|
if (!allow.has(sym)) {
|
|
@@ -242,6 +242,7 @@ export interface CheckpointSummary {
|
|
|
242
242
|
contentKind?: "content_ask";
|
|
243
243
|
createdAt?: number;
|
|
244
244
|
toolInput?: string;
|
|
245
|
+
restoreMode?: "snapshot" | "park_only";
|
|
245
246
|
preview?: unknown;
|
|
246
247
|
}
|
|
247
248
|
export declare function summarizeCheckpoint(cp: Checkpoint): CheckpointSummary;
|
|
@@ -181,6 +181,9 @@ export function summarizeCheckpoint(cp) {
|
|
|
181
181
|
...(tool !== undefined ? { toolCallId: tool.toolCallId, toolName: tool.toolName } : {}),
|
|
182
182
|
...(tool?.toolName === ASK_USER_QUESTION_TOOL_NAME ? { contentKind: "content_ask" } : {}),
|
|
183
183
|
...(toolInput !== undefined ? { toolInput } : {}),
|
|
184
|
+
...(cp.state.workspaceHandle !== undefined
|
|
185
|
+
? { restoreMode: cp.state.workspaceHandle.restoreMode === "park_only" ? "park_only" : "snapshot" }
|
|
186
|
+
: {}),
|
|
184
187
|
...(tool?.preview !== undefined ? { preview: tool.preview } : {}),
|
|
185
188
|
};
|
|
186
189
|
}
|
|
@@ -28,6 +28,11 @@ export async function addWorktree(baseEnv, opts) {
|
|
|
28
28
|
const detail = add.ok ? add.value.stderr || add.value.stdout : String(add.error);
|
|
29
29
|
throw new Error(`git worktree add failed: ${detail}`);
|
|
30
30
|
}
|
|
31
|
+
const excludeLine = shq(`/${WORKTREE_PARENT}/`);
|
|
32
|
+
await baseEnv
|
|
33
|
+
.exec(`ex="$(git rev-parse --git-path info/exclude)" && mkdir -p "$(dirname "$ex")" && ` +
|
|
34
|
+
`{ grep -qxF ${excludeLine} "$ex" 2>/dev/null || printf '%s\\n' ${excludeLine} >> "$ex"; }`, { cwd: opts.repoRoot })
|
|
35
|
+
.catch(() => undefined);
|
|
31
36
|
let inner;
|
|
32
37
|
try {
|
|
33
38
|
inner = await opts.rootEnvAt(worktreeDir);
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export interface McpServerStatus {
|
|
|
57
57
|
};
|
|
58
58
|
toolNames?: string[];
|
|
59
59
|
error?: string;
|
|
60
|
+
transportClosed?: boolean;
|
|
60
61
|
}
|
|
61
62
|
export declare const MCP_PREFIX = "mcp__";
|
|
62
63
|
export declare function resolveMcpDeclaredResultSize(meta: Record<string, unknown> | undefined): number | undefined;
|
|
@@ -69,6 +70,8 @@ export declare const MCP_IDLE_TIMEOUT_STDIO_DEFAULT_MS: number;
|
|
|
69
70
|
export declare const MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS: number;
|
|
70
71
|
export declare function mcpIdleTimeoutMs(kind: "stdio" | "http"): number;
|
|
71
72
|
export declare function describeMcpSpecErrorCode(code: unknown): string | undefined;
|
|
73
|
+
export declare function collapseMcpErrorPrefix(message: string): string;
|
|
74
|
+
export declare function networkErrorCode(err: unknown, depth?: number): string | undefined;
|
|
72
75
|
export declare function normalizeMcpName(name: string): string;
|
|
73
76
|
export declare function clampNameSegment(seg: string, max?: number): string;
|
|
74
77
|
export * from "./image-downsample.js";
|
package/dist/core/mcp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
3
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
+
import { StreamableHTTPClientTransport, StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
5
|
import { lstat, mkdir, writeFile } from "node:fs/promises";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
@@ -145,8 +145,24 @@ const MCP_SPEC_ERROR_CODE_NAMES = new Map([
|
|
|
145
145
|
export function describeMcpSpecErrorCode(code) {
|
|
146
146
|
return typeof code === "number" ? MCP_SPEC_ERROR_CODE_NAMES.get(code) : undefined;
|
|
147
147
|
}
|
|
148
|
+
export function collapseMcpErrorPrefix(message) {
|
|
149
|
+
let out = message;
|
|
150
|
+
for (;;) {
|
|
151
|
+
const m = /^MCP error (-?\d+): (?=MCP error \1: )/.exec(out);
|
|
152
|
+
if (m === null)
|
|
153
|
+
return out;
|
|
154
|
+
out = out.slice(m[0].length);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function collapseMcpErrorStampInPlace(err) {
|
|
158
|
+
if (!(err instanceof McpError))
|
|
159
|
+
return;
|
|
160
|
+
const collapsed = collapseMcpErrorPrefix(err.message);
|
|
161
|
+
if (collapsed !== err.message)
|
|
162
|
+
err.message = collapsed;
|
|
163
|
+
}
|
|
148
164
|
function namedMcpFailureText(err) {
|
|
149
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
165
|
+
const detail = err instanceof Error ? collapseMcpErrorPrefix(err.message) : String(err);
|
|
150
166
|
const condition = err instanceof McpError ? describeMcpSpecErrorCode(err.code) : undefined;
|
|
151
167
|
return condition !== undefined ? `${condition}: ${detail}` : detail;
|
|
152
168
|
}
|
|
@@ -155,6 +171,53 @@ function isTransportLost(err) {
|
|
|
155
171
|
return true;
|
|
156
172
|
return err instanceof Error && /not connected|connection closed/i.test(err.message);
|
|
157
173
|
}
|
|
174
|
+
const NETWORK_CODES_NEVER_DELIVERED = new Set([
|
|
175
|
+
"ECONNREFUSED",
|
|
176
|
+
"ENOTFOUND",
|
|
177
|
+
"EAI_AGAIN",
|
|
178
|
+
"EHOSTUNREACH",
|
|
179
|
+
"ENETUNREACH",
|
|
180
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
181
|
+
]);
|
|
182
|
+
export function networkErrorCode(err, depth = 0) {
|
|
183
|
+
if (depth > 5 || !(err instanceof Error))
|
|
184
|
+
return undefined;
|
|
185
|
+
const code = err.code;
|
|
186
|
+
if (typeof code === "string" && /^(?:E[A-Z_]+|UND_ERR_[A-Z_]+)$/.test(code))
|
|
187
|
+
return code;
|
|
188
|
+
if (err instanceof AggregateError) {
|
|
189
|
+
for (const inner of err.errors) {
|
|
190
|
+
const found = networkErrorCode(inner, depth + 1);
|
|
191
|
+
if (found !== undefined)
|
|
192
|
+
return found;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return networkErrorCode(err.cause, depth + 1);
|
|
196
|
+
}
|
|
197
|
+
function describeHttpTransportFailure(err) {
|
|
198
|
+
if (err instanceof McpError)
|
|
199
|
+
return undefined;
|
|
200
|
+
if (err instanceof StreamableHTTPError) {
|
|
201
|
+
const status = typeof err.code === "number" && err.code > 0 ? err.code : undefined;
|
|
202
|
+
return {
|
|
203
|
+
condition: status !== undefined
|
|
204
|
+
? `its HTTP endpoint answered ${status} instead of an MCP response`
|
|
205
|
+
: "its HTTP endpoint answered something that is not an MCP response",
|
|
206
|
+
delivered: "unknown",
|
|
207
|
+
...(status !== undefined ? { httpStatus: status } : {}),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const code = networkErrorCode(err);
|
|
211
|
+
if (code !== undefined) {
|
|
212
|
+
return NETWORK_CODES_NEVER_DELIVERED.has(code)
|
|
213
|
+
? { condition: `its HTTP endpoint could not be reached (${code})`, delivered: "no" }
|
|
214
|
+
: { condition: `the connection to its HTTP endpoint failed (${code})`, delivered: "unknown" };
|
|
215
|
+
}
|
|
216
|
+
if (err instanceof TypeError && /fetch failed|terminated|network/i.test(err.message)) {
|
|
217
|
+
return { condition: "the HTTP request to its endpoint failed at the network layer", delivered: "unknown" };
|
|
218
|
+
}
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
158
221
|
function writeEffectWarning(writeEffect) {
|
|
159
222
|
return writeEffect
|
|
160
223
|
? " This tool is write-capable: treat its side effects as POSSIBLY APPLIED and verify the actual state before retrying."
|
|
@@ -173,6 +236,7 @@ function rethrowHonestMcpError(err, ctx) {
|
|
|
173
236
|
}
|
|
174
237
|
if (ctx.signal?.aborted)
|
|
175
238
|
throw err;
|
|
239
|
+
collapseMcpErrorStampInPlace(err);
|
|
176
240
|
const serverLabel = inlineUntrusted(ctx.server);
|
|
177
241
|
if (err instanceof McpError && err.code === ErrorCode.RequestTimeout) {
|
|
178
242
|
const data = err.data;
|
|
@@ -193,6 +257,21 @@ function rethrowHonestMcpError(err, ctx) {
|
|
|
193
257
|
e.details = { transportLost: true, server: ctx.server };
|
|
194
258
|
throw e;
|
|
195
259
|
}
|
|
260
|
+
const httpFailure = describeHttpTransportFailure(err);
|
|
261
|
+
if (httpFailure !== undefined) {
|
|
262
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
263
|
+
const fenced = `\nThe transport error follows as external/untrusted data:\n${delimitUntrusted(`${ctx.server} transport error`, truncateMcpErrorText(detail))}`;
|
|
264
|
+
const e = new Error(httpFailure.delivered === "no"
|
|
265
|
+
? `${ctx.what} could not reach MCP server "${serverLabel}": ${httpFailure.condition}. The request was not delivered, so the server did not execute it. This server's tools and resources will keep failing until its endpoint is reachable again — do not retry them; use an alternative if one exists.${fenced}`
|
|
266
|
+
: `${ctx.what} failed at the transport layer of MCP server "${serverLabel}": ${httpFailure.condition}. The request may or may not have executed on the server — the outcome is unknown.${writeEffectWarning(ctx.writeEffect)}${fenced}`, { cause: err });
|
|
267
|
+
e.errorKind = httpFailure.delivered === "no" ? "server_disconnected" : "transport_lost";
|
|
268
|
+
e.details = {
|
|
269
|
+
server: ctx.server,
|
|
270
|
+
...(httpFailure.delivered === "no" ? {} : { transportLost: true }),
|
|
271
|
+
...(httpFailure.httpStatus !== undefined ? { httpStatus: httpFailure.httpStatus } : {}),
|
|
272
|
+
};
|
|
273
|
+
throw e;
|
|
274
|
+
}
|
|
196
275
|
if (err instanceof McpError) {
|
|
197
276
|
const condition = describeMcpSpecErrorCode(err.code);
|
|
198
277
|
if (condition !== undefined) {
|
|
@@ -546,12 +625,16 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
546
625
|
serverInstructions.push({ server: spec.name, text: s.instructions });
|
|
547
626
|
}
|
|
548
627
|
const hasInstructions = s.instructions !== undefined;
|
|
549
|
-
|
|
550
|
-
if (
|
|
628
|
+
const announceClose = () => {
|
|
629
|
+
if (disposing)
|
|
630
|
+
return;
|
|
631
|
+
s.status.transportClosed = true;
|
|
632
|
+
if (hasInstructions)
|
|
551
633
|
instructionsDelta.pendingRemovals.push(spec.name);
|
|
552
634
|
};
|
|
553
|
-
|
|
554
|
-
|
|
635
|
+
s.announce.fn = announceClose;
|
|
636
|
+
if (s.health.dead)
|
|
637
|
+
announceClose();
|
|
555
638
|
if (s.resourceServer)
|
|
556
639
|
resourceServers.push(s.resourceServer);
|
|
557
640
|
for (const d of s.dropped)
|
|
@@ -703,7 +786,8 @@ async function readDirViaExtension(rs, uri, signal, timeoutMs, watchdog) {
|
|
|
703
786
|
if (err instanceof McpError && err.code === ErrorCode.InvalidParams) {
|
|
704
787
|
if (pages > 0)
|
|
705
788
|
return { kind: "ok", resources, cursorInvalid: true };
|
|
706
|
-
|
|
789
|
+
const detail = collapseMcpErrorPrefix(err.message);
|
|
790
|
+
return { kind: classifyDirReadInvalidParams(detail), detail };
|
|
707
791
|
}
|
|
708
792
|
if (err instanceof McpError && err.code === ErrorCode.MethodNotFound)
|
|
709
793
|
return { kind: "unsupported" };
|
|
@@ -44,6 +44,8 @@ export interface ExecStreamOptions {
|
|
|
44
44
|
maxOutputBytes?: number;
|
|
45
45
|
}
|
|
46
46
|
export type RemoteExecutionErrorCode = "suspended" | "command_in_flight" | "connect_failed" | "post_resume_failed" | "aborted" | "timeout" | "unsupported" | "auth_transient" | "auth_failed" | "transport_lost" | "unknown";
|
|
47
|
+
export declare const RETRYABLE_REMOTE_ERROR_CODES: readonly RemoteExecutionErrorCode[];
|
|
48
|
+
export declare function isRetryableRemoteErrorCode(code: RemoteExecutionErrorCode): boolean;
|
|
47
49
|
export declare class RemoteExecutionError extends Error {
|
|
48
50
|
readonly code: RemoteExecutionErrorCode;
|
|
49
51
|
constructor(code: RemoteExecutionErrorCode, message: string, cause?: Error);
|
|
@@ -101,5 +103,6 @@ export interface ExecutionEnvFactoryContext {
|
|
|
101
103
|
export type ExecutionEnvFactory = (ctx: ExecutionEnvFactoryContext) => ExecutionEnv | Promise<ExecutionEnv>;
|
|
102
104
|
export declare function hasDestroy(env: ExecutionEnv): env is ExecutionEnv & Pick<RemoteExecutionEnv, "destroy">;
|
|
103
105
|
export declare function isRemoteExecutionEnv(env: ExecutionEnv): env is RemoteExecutionEnv;
|
|
106
|
+
export declare function missingRestoreSurface(env: ExecutionEnv): readonly ("resumeVM" | "postResumeInit")[];
|
|
104
107
|
export declare function isSuspendable(env: ExecutionEnv): env is RemoteExecutionEnv;
|
|
105
108
|
export declare function isIsolated(env: ExecutionEnv): boolean;
|
package/dist/core/remote-env.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
export const RETRYABLE_REMOTE_ERROR_CODES = [
|
|
2
|
+
"auth_transient",
|
|
3
|
+
"connect_failed",
|
|
4
|
+
"timeout",
|
|
5
|
+
"transport_lost",
|
|
6
|
+
];
|
|
7
|
+
export function isRetryableRemoteErrorCode(code) {
|
|
8
|
+
return RETRYABLE_REMOTE_ERROR_CODES.includes(code);
|
|
9
|
+
}
|
|
1
10
|
export class RemoteExecutionError extends Error {
|
|
2
11
|
code;
|
|
3
12
|
constructor(code, message, cause) {
|
|
@@ -18,8 +27,17 @@ export function isRemoteExecutionEnv(env) {
|
|
|
18
27
|
typeof e.capabilities === "object" &&
|
|
19
28
|
e.capabilities !== null);
|
|
20
29
|
}
|
|
30
|
+
export function missingRestoreSurface(env) {
|
|
31
|
+
const e = env;
|
|
32
|
+
const missing = [];
|
|
33
|
+
if (typeof e.resumeVM !== "function")
|
|
34
|
+
missing.push("resumeVM");
|
|
35
|
+
if (typeof e.postResumeInit !== "function")
|
|
36
|
+
missing.push("postResumeInit");
|
|
37
|
+
return missing;
|
|
38
|
+
}
|
|
21
39
|
export function isSuspendable(env) {
|
|
22
|
-
return isRemoteExecutionEnv(env) && env.capabilities.suspendable;
|
|
40
|
+
return isRemoteExecutionEnv(env) && env.capabilities.suspendable && missingRestoreSurface(env).length === 0;
|
|
23
41
|
}
|
|
24
42
|
export function isIsolated(env) {
|
|
25
43
|
return isRemoteExecutionEnv(env) ? env.capabilities.isolation : false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { canonicalizeTarget, fileArgPath } from "../../tools/fs/safety.js";
|
|
2
2
|
import { canonicalToolName } from "../tool-name-aliases.js";
|
|
3
|
+
import { parseSkillToolEntry, skillSpecifierRejection } from "../skill-tool-specifier.js";
|
|
3
4
|
export class ActiveSkillScope {
|
|
4
5
|
frames = [];
|
|
5
6
|
push(frame) {
|
|
@@ -47,12 +48,19 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
const manifests = frames.flatMap((f) => (f.kind === "manifest" ? [f.manifest] : []));
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
const perFrame = manifests.map((m) => {
|
|
52
|
+
const byName = new Map();
|
|
53
|
+
for (const raw of m.allowTools) {
|
|
54
|
+
const entry = parseSkillToolEntry(raw);
|
|
55
|
+
const bucket = byName.get(entry.name);
|
|
56
|
+
if (bucket)
|
|
57
|
+
bucket.push(entry);
|
|
58
|
+
else
|
|
59
|
+
byName.set(entry.name, [entry]);
|
|
60
|
+
}
|
|
61
|
+
return { manifest: m, byName };
|
|
62
|
+
});
|
|
63
|
+
if (perFrame.length === 0 || perFrame.some((f) => !f.byName.has(toolName))) {
|
|
56
64
|
const ids = manifests.map((m) => m.lineageId).join(", ");
|
|
57
65
|
return {
|
|
58
66
|
action: "deny",
|
|
@@ -60,6 +68,26 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
60
68
|
decisionReason: "safety",
|
|
61
69
|
};
|
|
62
70
|
}
|
|
71
|
+
for (const frame of perFrame) {
|
|
72
|
+
const alternatives = frame.byName.get(toolName) ?? [];
|
|
73
|
+
const rejections = [];
|
|
74
|
+
let admitted = false;
|
|
75
|
+
for (const entry of alternatives) {
|
|
76
|
+
const rejection = skillSpecifierRejection(entry, req.args);
|
|
77
|
+
if (rejection === undefined) {
|
|
78
|
+
admitted = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
rejections.push(rejection);
|
|
82
|
+
}
|
|
83
|
+
if (!admitted) {
|
|
84
|
+
return {
|
|
85
|
+
action: "deny",
|
|
86
|
+
reason: `tool "${req.toolName}" is narrowed by skill manifest "${frame.manifest.lineageId}": ${rejections.join("; ")}`,
|
|
87
|
+
decisionReason: "safety",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
63
91
|
const pathConstrainingActive = manifests.some((m) => m.allowPaths && m.allowPaths.length > 0);
|
|
64
92
|
if (pathConstrainingActive && !PATH_WRITE_TOOLS.has(toolName)) {
|
|
65
93
|
const eff = toolEffects?.get(toolName) ?? "write";
|
|
@@ -70,6 +70,7 @@ export interface ResultFlags {
|
|
|
70
70
|
model?: string;
|
|
71
71
|
unpricedSpend?: boolean;
|
|
72
72
|
rewindNotes?: TaskResult["rewindNotes"];
|
|
73
|
+
remoteEnvFailures?: TaskResult["remoteEnvFailures"];
|
|
73
74
|
abortedForTimeout: boolean;
|
|
74
75
|
abortedForTurns: boolean;
|
|
75
76
|
abortedLive?: boolean;
|
|
@@ -81,10 +82,12 @@ export interface ResultFlags {
|
|
|
81
82
|
suspendRef?: {
|
|
82
83
|
token: import("../checkpoint-store.js").CheckpointToken;
|
|
83
84
|
gate: import("../checkpoint-store.js").CheckpointGate;
|
|
85
|
+
restoreMode?: "snapshot" | "park_only";
|
|
84
86
|
};
|
|
85
87
|
reviewRef?: {
|
|
86
88
|
token: import("../checkpoint-store.js").CheckpointToken;
|
|
87
89
|
gate: import("../checkpoint-store.js").CheckpointGate;
|
|
90
|
+
restoreMode?: "snapshot" | "park_only";
|
|
88
91
|
};
|
|
89
92
|
}
|
|
90
93
|
export declare function errorCodeOf(err: unknown): string | undefined;
|
|
@@ -54,6 +54,7 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
54
54
|
let salvagedOutput;
|
|
55
55
|
let checkpointToken;
|
|
56
56
|
let checkpointGate;
|
|
57
|
+
let workspaceRestoreMode;
|
|
57
58
|
const isDegenerate = final?.stopReason === "error" && final.errorMessage === DEGENERATE_MESSAGE;
|
|
58
59
|
const isWalltimeCutoff = final?.stopReason === "error" && final.errorMessage === WALLTIME_CUTOFF_MESSAGE;
|
|
59
60
|
if (flags.outputInvalid) {
|
|
@@ -105,12 +106,14 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
105
106
|
status = "suspended";
|
|
106
107
|
checkpointToken = flags.suspendRef.token;
|
|
107
108
|
checkpointGate = flags.suspendRef.gate;
|
|
109
|
+
workspaceRestoreMode = flags.suspendRef.restoreMode;
|
|
108
110
|
}
|
|
109
111
|
else if (flags.reviewRef) {
|
|
110
112
|
status = "needs_review";
|
|
111
113
|
errorCode = "review.pending";
|
|
112
114
|
checkpointToken = flags.reviewRef.token;
|
|
113
115
|
checkpointGate = flags.reviewRef.gate;
|
|
116
|
+
workspaceRestoreMode = flags.reviewRef.restoreMode;
|
|
114
117
|
}
|
|
115
118
|
else if (flags.abortedLive || final?.stopReason === "aborted") {
|
|
116
119
|
status = flags.abortedForTimeout ? "timeout" : "failed";
|
|
@@ -143,5 +146,5 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
143
146
|
void _internalCompaction;
|
|
144
147
|
if (flags.unpricedSpend)
|
|
145
148
|
delete publicStats.costMicroUsd;
|
|
146
|
-
return { taskId, sessionId, status, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, blockedReason, errorMessage, errorCode, checkpointToken, checkpointGate, ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), stats: publicStats };
|
|
149
|
+
return { taskId, sessionId, status, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, blockedReason, errorMessage, errorCode, checkpointToken, checkpointGate, ...(workspaceRestoreMode !== undefined ? { workspaceRestoreMode } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: flags.remoteEnvFailures } : {}), stats: publicStats };
|
|
147
150
|
}
|
|
@@ -127,12 +127,15 @@ export interface Prepared {
|
|
|
127
127
|
token?: CheckpointToken;
|
|
128
128
|
gate?: CheckpointGate;
|
|
129
129
|
scope?: string;
|
|
130
|
+
restoreMode?: "snapshot" | "park_only";
|
|
130
131
|
};
|
|
131
132
|
reviewRef: {
|
|
132
133
|
token?: CheckpointToken;
|
|
133
134
|
gate?: CheckpointGate;
|
|
134
135
|
scope?: string;
|
|
136
|
+
restoreMode?: "snapshot" | "park_only";
|
|
135
137
|
};
|
|
138
|
+
remoteEnvFailures: NonNullable<TaskResult["remoteEnvFailures"]>;
|
|
136
139
|
suspendLoopRef: {
|
|
137
140
|
hit: boolean;
|
|
138
141
|
};
|
|
@@ -340,4 +343,5 @@ export declare function batchContextAt(messages: AgentMessage[], currentId: stri
|
|
|
340
343
|
completedCallIds: string[];
|
|
341
344
|
};
|
|
342
345
|
export declare function rebaseWorkspacePath(p: string, fromRaw: string, toRaw: string): string;
|
|
346
|
+
export declare function rebaseWorkspacePathAcross(p: string, froms: readonly string[], to: string): string;
|
|
343
347
|
export declare function prepareTask(spec: TaskSpec, deps: RunnerDeps, sessions: SessionStore, resume?: PrepareResume, internals?: RunInternals, runnerSelf?: Runner): Promise<Prepared>;
|