@sema-agent/core 7.40.1 → 7.41.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 +188 -0
- package/dist/agents/agent-transcript-tool.d.ts +10 -2
- package/dist/agents/agent-transcript-tool.js +52 -26
- package/dist/agents/list-agents-tool.d.ts +17 -2
- package/dist/agents/list-agents-tool.js +28 -16
- package/dist/agents/peer-session-drain.d.ts +3 -1
- package/dist/agents/peer-session-drain.js +1 -0
- package/dist/agents/subagent-steps.d.ts +12 -2
- package/dist/agents/subagent-steps.js +8 -4
- package/dist/agents/subagent.d.ts +1 -17
- package/dist/agents/subagent.js +12 -24
- package/dist/core/agent-definition.d.ts +19 -40
- package/dist/core/agent-state.d.ts +91 -0
- package/dist/core/agent-state.js +79 -0
- package/dist/core/ask-origin.d.ts +28 -6
- package/dist/core/ask-origin.js +9 -5
- package/dist/core/ask-unresolvable-notice.d.ts +27 -4
- package/dist/core/ask-unresolvable-notice.js +8 -0
- package/dist/core/auto-promote.d.ts +14 -27
- package/dist/core/auto-promote.js +5 -5
- package/dist/core/checkpoint-store.d.ts +11 -12
- package/dist/core/decision-receipt.d.ts +5 -2
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/engine-notice.d.ts +7 -1
- package/dist/core/gate-crash-trace.d.ts +1 -1
- package/dist/core/gate-fold.js +7 -3
- package/dist/core/gate-lanes.js +22 -3
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hooks.d.ts +20 -6
- package/dist/core/hooks.js +28 -28
- package/dist/core/mcp-tool-meta.d.ts +24 -0
- package/dist/core/mcp-tool-meta.js +17 -0
- package/dist/core/mcp.d.ts +1 -6
- package/dist/core/mcp.js +6 -7
- package/dist/core/mode-policy.d.ts +25 -0
- package/dist/core/mode-policy.js +8 -0
- package/dist/core/protocol-table.d.ts +3 -0
- package/dist/core/protocol-table.js +3 -0
- package/dist/core/reminder-mint.d.ts +21 -1
- package/dist/core/reminder-mint.js +20 -0
- package/dist/core/retired-keys.d.ts +2 -1
- package/dist/core/retired-keys.js +13 -2
- package/dist/core/rule-denial-sentence.d.ts +2 -2
- package/dist/core/runner/call-hook-stations.d.ts +106 -0
- package/dist/core/runner/call-hook-stations.js +118 -0
- package/dist/core/runner/contracts.d.ts +35 -25
- package/dist/core/runner/gate-exit.d.ts +5 -1
- package/dist/core/runner/gate-exit.js +10 -6
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -0
- package/dist/core/runner/prepare-delegation-surface.js +4 -4
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -3
- package/dist/core/runner/prepare-gate-stations.js +13 -70
- package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
- package/dist/core/runner/prepare-inherited-gate.js +5 -3
- package/dist/core/runner/prepare-park-ask.d.ts +4 -0
- package/dist/core/runner/prepare-park-ask.js +4 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +6 -5
- package/dist/core/runner/prepare-policy-chain.js +14 -25
- package/dist/core/runner/prepare-project-context.d.ts +15 -16
- package/dist/core/runner/prepare-project-context.js +15 -11
- package/dist/core/runner/prepare-run-refs.d.ts +8 -8
- package/dist/core/runner/prepare-run-refs.js +3 -3
- package/dist/core/runner/prepare-safety-scan.js +3 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -1
- package/dist/core/runner/prepare-suspend-saga.js +2 -1
- package/dist/core/runner/prepare-task.js +14 -12
- package/dist/core/runner/prepare-turn-wiring.d.ts +3 -1
- package/dist/core/runner/prepare-turn-wiring.js +16 -13
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-apply.js +12 -23
- package/dist/core/runner/resume-checkpoint-screen.js +3 -0
- package/dist/core/runner/run-leg.js +61 -47
- package/dist/core/runner/runtask.d.ts +1 -1
- package/dist/core/runner/session-rule-policy.d.ts +7 -19
- package/dist/core/runner/session-rule-policy.js +1 -2
- package/dist/core/runner/skill-base-dir.d.ts +10 -0
- package/dist/core/runner/skill-base-dir.js +4 -0
- package/dist/core/runner/synthetic-tools.d.ts +11 -17
- package/dist/core/runner/synthetic-tools.js +7 -8
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +2 -0
- package/dist/core/session-policy-store.d.ts +63 -11
- package/dist/core/session-policy-store.js +52 -6
- package/dist/core/skill-grant.d.ts +239 -0
- package/dist/core/skill-grant.js +313 -0
- package/dist/core/skills-directory.d.ts +5 -34
- package/dist/core/skills-directory.js +9 -58
- package/dist/core/task-registry-agent.d.ts +14 -2
- package/dist/core/task-registry-agent.js +23 -8
- package/dist/core/task-registry-shared.d.ts +6 -0
- package/dist/core/task-registry.d.ts +16 -0
- package/dist/core/task-registry.js +19 -6
- package/dist/core/task-spec.d.ts +10 -0
- package/dist/core/tool-face.d.ts +3 -2
- package/dist/core/tool-name-aliases.d.ts +38 -12
- package/dist/core/tool-name-aliases.js +16 -0
- package/dist/core/tool-name-readings.d.ts +6 -2
- package/dist/core/tool-name-readings.js +6 -4
- package/dist/core/tool-policy.d.ts +2 -2
- package/dist/core/tool-registry.d.ts +1 -4
- package/dist/core/tool-registry.js +0 -3
- package/dist/core/tool-spec.d.ts +11 -13
- package/dist/core/tools.d.ts +1 -1
- package/dist/core/trace.d.ts +15 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/engine/harness/agent-harness.d.ts +12 -1
- package/dist/engine/harness/agent-harness.js +17 -13
- package/dist/engine/loop/agent-loop.d.ts +16 -1
- package/dist/engine/loop/agent-loop.js +9 -15
- package/dist/engine/loop/types.d.ts +2 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/stores/file/adoption/adopt.js +23 -16
- package/dist/stores/file/index.d.ts +8 -2
- package/dist/stores/file/index.js +2 -2
- package/dist/stores/file/mailbox-store.js +2 -2
- package/dist/stores/file/permission-rule-adopt.js +10 -1
- package/dist/stores/file/permission-rule-store.d.ts +6 -4
- package/dist/stores/file/permission-rule-store.js +127 -59
- package/dist/stores/file/session-policy-store.d.ts +86 -14
- package/dist/stores/file/session-policy-store.js +135 -118
- package/dist/stores/file/task-list-store.d.ts +5 -0
- package/dist/stores/file/task-list-store.js +18 -4
- package/dist/stores/file/usage-window-store.d.ts +9 -1
- package/dist/stores/file/usage-window-store.js +47 -33
- package/dist/tools/fs/safety.d.ts +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +45 -5
- package/wire-changes/core@7.41.0.json +1865 -0
- package/dist/core/runner/active-skill-scope.d.ts +0 -58
- package/dist/core/runner/active-skill-scope.js +0 -133
- package/dist/core/skill-tool-specifier.d.ts +0 -80
- package/dist/core/skill-tool-specifier.js +0 -57
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, relative } from "node:path";
|
|
3
3
|
import { physicalPathOf } from "./physical-path.js";
|
|
4
|
-
import {
|
|
4
|
+
import { assertNoRetiredKeys } from "./retired-keys.js";
|
|
5
5
|
import { thrownText } from "./thrown-value.js";
|
|
6
6
|
const SKILL_FILE = "SKILL.md";
|
|
7
7
|
const RESOURCE_DIRS = ["assets", "references", "scripts"];
|
|
@@ -285,7 +285,6 @@ function isDecodableText(bytes) {
|
|
|
285
285
|
function errText(err) {
|
|
286
286
|
return thrownText(err);
|
|
287
287
|
}
|
|
288
|
-
const ALL_TOOLS_WILDCARD = "*";
|
|
289
288
|
function splitToolNames(declared) {
|
|
290
289
|
let text = declared.trim();
|
|
291
290
|
if (text.startsWith("[") && text.endsWith("]"))
|
|
@@ -319,52 +318,11 @@ function splitToolNames(declared) {
|
|
|
319
318
|
flush();
|
|
320
319
|
return names;
|
|
321
320
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
for (const n of splitToolNames(declared)) {
|
|
326
|
-
const entry = parseSkillToolEntry(n);
|
|
327
|
-
const key = entry.specifier === undefined ? entry.name : `${entry.name}(${entry.specifier})`;
|
|
328
|
-
if (seen.has(key))
|
|
329
|
-
continue;
|
|
330
|
-
seen.add(key);
|
|
331
|
-
names.push(n);
|
|
332
|
-
}
|
|
333
|
-
for (const n of names) {
|
|
334
|
-
const entry = parseSkillToolEntry(n);
|
|
335
|
-
if (entry.specifier !== undefined && !isSkillSpecifierEnforced(entry.name)) {
|
|
336
|
-
warn({
|
|
337
|
-
code: "allowed_tool_pattern_unsupported",
|
|
338
|
-
skill: skillName,
|
|
339
|
-
detail: `allowed-tools entry "${n}" narrows a tool whose calls cannot be matched against a command pattern — only shell tools carry one, so the entry is not enforced and the tool is DISABLED inside this skill frame. Use the bare tool name to allow it fully.`,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if (names.length === 0)
|
|
344
|
-
return undefined;
|
|
345
|
-
if (names.some((n) => n === ALL_TOOLS_WILDCARD))
|
|
346
|
-
return undefined;
|
|
347
|
-
let allowTools = names;
|
|
348
|
-
if (deployedTools !== undefined) {
|
|
349
|
-
const mounted = new Set(deployedTools);
|
|
350
|
-
allowTools = names.filter((n) => mounted.has(parseSkillToolEntry(n).name));
|
|
351
|
-
for (const n of names) {
|
|
352
|
-
if (!mounted.has(parseSkillToolEntry(n).name)) {
|
|
353
|
-
warn({
|
|
354
|
-
code: "allowed_tool_not_mounted",
|
|
355
|
-
skill: skillName,
|
|
356
|
-
detail: `allowed-tools names "${n}", which this deployment does not mount — dropped (a skill declaration may only narrow capability, never add a tool)`,
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
if (disallowed.length > 0) {
|
|
362
|
-
const denied = new Set(disallowed);
|
|
363
|
-
allowTools = allowTools.filter((n) => !denied.has(parseSkillToolEntry(n).name));
|
|
364
|
-
}
|
|
365
|
-
return { allowTools, lineageId: `skill:${skillName}` };
|
|
366
|
-
}
|
|
321
|
+
const RETIRED_SKILLS_DIRECTORY_OPTION_KEYS = Object.freeze({
|
|
322
|
+
deployedTools: "the loader copies a skill's `allowed-tools` / `disallowed-tools` into `SkillSpec.allowedTools` / `disallowedTools` as written and the run's grant table judges them (an entry naming a tool nothing mounts simply never matches) — drop the option",
|
|
323
|
+
});
|
|
367
324
|
export function createSkillsFromDirectory(dir, options = {}) {
|
|
325
|
+
assertNoRetiredKeys(options, RETIRED_SKILLS_DIRECTORY_OPTION_KEYS, "SkillsDirectoryOptions");
|
|
368
326
|
const warn = (w) => {
|
|
369
327
|
options.onWarning?.(w);
|
|
370
328
|
};
|
|
@@ -438,16 +396,8 @@ export function createSkillsFromDirectory(dir, options = {}) {
|
|
|
438
396
|
warn({ code: "description_too_long", skill: name, detail: `\`description\` is ${description.length} chars, over the ${DESCRIPTION_MAX_CHARS} cap — skipped` });
|
|
439
397
|
continue;
|
|
440
398
|
}
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
const manifest = allowed === undefined ? undefined : manifestFromAllowedTools(allowed, disallowed, name, options.deployedTools, warn);
|
|
444
|
-
if (disallowed.length > 0 && manifest === undefined) {
|
|
445
|
-
warn({
|
|
446
|
-
code: "disallowed_tools_unenforced",
|
|
447
|
-
skill: name,
|
|
448
|
-
detail: `disallowed-tools names ${disallowed.map((n) => `"${n}"`).join(", ")}, but this skill produced no allowlist to subtract from (allowed-tools is absent or claims every tool) — the deny declaration is NOT enforced (express it in the task's own tool policy)`,
|
|
449
|
-
});
|
|
450
|
-
}
|
|
399
|
+
const allowedTools = splitToolNames(parsed.fields.get("allowed-tools") ?? "");
|
|
400
|
+
const disallowedTools = splitToolNames(parsed.fields.get("disallowed-tools") ?? "");
|
|
451
401
|
const baseDir = skillPhysicalRoot(skillDir, name, warn);
|
|
452
402
|
const files = baseDir === undefined ? [] : readAttachments(skillDir, baseDir, name, budget, warn);
|
|
453
403
|
skills.push({
|
|
@@ -455,7 +405,8 @@ export function createSkillsFromDirectory(dir, options = {}) {
|
|
|
455
405
|
description,
|
|
456
406
|
content: parsed.body,
|
|
457
407
|
...(baseDir !== undefined ? { baseDir } : {}),
|
|
458
|
-
...(
|
|
408
|
+
...(allowedTools.length > 0 ? { allowedTools } : {}),
|
|
409
|
+
...(disallowedTools.length > 0 ? { disallowedTools } : {}),
|
|
459
410
|
...(files.length > 0 ? { files } : {}),
|
|
460
411
|
});
|
|
461
412
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type BackgroundAgentRecord, type BackgroundAgentStore } from "./background-agent-store.js";
|
|
2
2
|
import { type StopSource, type TaskAccess, type TaskRetrievalStatus, type UnifiedTaskOutput, type UnifiedTaskResult, type BackgroundAgentTaskHandle, type DurableAgentCore, type ParkedClaimTicket, type RegisterBackgroundAgentInput } from "./task-registry-shared.js";
|
|
3
3
|
import { type ToolResultStore } from "./tool-result-store.js";
|
|
4
|
+
import { type AgentState } from "./agent-state.js";
|
|
5
|
+
import type { CheckpointSeat } from "./checkpoint-seat.js";
|
|
4
6
|
/**
|
|
5
7
|
* Subagent transcript persistence (delegation entry caps) — the ACTIVE handles of one delegation
|
|
6
8
|
* tree in THIS process's registry: `background_agent` handles whose status is running/pending
|
|
@@ -493,6 +495,9 @@ export interface AgentPollDetailsInput {
|
|
|
493
495
|
errorRetryAfterMs?: number;
|
|
494
496
|
resultIsPartial?: boolean;
|
|
495
497
|
completionId?: string;
|
|
498
|
+
/** The agent's current state ({@link import("./agent-state.js").readAgentState}) — rides as
|
|
499
|
+
* `agentState` when given; the poll arms always give it, a serving layer may omit it. */
|
|
500
|
+
agentState?: AgentState;
|
|
496
501
|
}
|
|
497
502
|
/** PUBLIC (see the doc above): a serving layer that renders its own protocol projection of a row
|
|
498
503
|
* builds the structured facts HERE, so the status gates and the untrusted-text fencing apply to it
|
|
@@ -510,7 +515,11 @@ export declare function buildAgentPollDetails(input: AgentPollDetailsInput): Uni
|
|
|
510
515
|
* caller has ALREADY read and authorized — it reads no store and applies no predicate, so a
|
|
511
516
|
* deployment passes every row through
|
|
512
517
|
* {@link import("./background-agent-store.js").canAccessAgentRecord} before it gets here. */
|
|
513
|
-
export declare function serveDurableAgentRowLane(row: BackgroundAgentRecord
|
|
518
|
+
export declare function serveDurableAgentRowLane(row: BackgroundAgentRecord,
|
|
519
|
+
/** The row's current state ({@link import("./agent-state.js").readAgentState} over
|
|
520
|
+
* {@link import("./agent-state.js").agentStateSourceOfRecord}(row)): rendered as the `state:` line and
|
|
521
|
+
* carried as `details.agentState`. Absent ⇒ neither appears (a serving layer that does not read it). */
|
|
522
|
+
agentState?: AgentState): UnifiedTaskResult;
|
|
514
523
|
/**
|
|
515
524
|
* design/158 S1 (RB-205-B 半) — spill the FULL text behind a CLIPPED background_agent result to the
|
|
516
525
|
* offload store, and append a ref-disclosure so the middle `clipTaskOutput` drops is not gone for good.
|
|
@@ -553,5 +562,8 @@ oneShot?: boolean,
|
|
|
553
562
|
store?: ToolResultStore,
|
|
554
563
|
/** design/158 S1 — the caller's session id, folded into the minted ref for global uniqueness (same
|
|
555
564
|
* role `sessionId` plays at every other {@link buildToolResultRef} call site). */
|
|
556
|
-
sessionId?: string
|
|
565
|
+
sessionId?: string,
|
|
566
|
+
/** The run's checkpoint seat — the store a parked row's approval card is read from
|
|
567
|
+
* ({@link import("./agent-state.js").readAgentState}). Absent ⇒ a parked row reads `approval unreadable`. */
|
|
568
|
+
checkpointSeat?: CheckpointSeat): Promise<UnifiedTaskResult>;
|
|
557
569
|
export declare function stopBackgroundAgentLane(core: DurableAgentCore, handle: BackgroundAgentTaskHandle): Promise<UnifiedTaskResult>;
|
|
@@ -8,6 +8,7 @@ import { boundedRedactedSummary } from "./untrusted-egress.js";
|
|
|
8
8
|
import { mintCompletionId, commitCompletionIdIfEmpty, clipTaskOutput, assertOwnership, sleepPollStep, alreadyTerminalStopNote, canAccess, normalizeAgentName, closestName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR, } from "./task-registry-shared.js";
|
|
9
9
|
import { buildToolResultRef, OFFLOAD_TOOL_NAME, toolResultProvenanceOf } from "./tool-result-store.js";
|
|
10
10
|
import { thrownLabel } from "./thrown-value.js";
|
|
11
|
+
import { agentStateSourceOfHandle, agentStateSummary, readAgentState } from "./agent-state.js";
|
|
11
12
|
export function activeDelegationHandlesLane(core, scope, rootSessionId) {
|
|
12
13
|
const out = [];
|
|
13
14
|
for (const h of core.handles.values()) {
|
|
@@ -1292,18 +1293,21 @@ export function buildAgentPollDetails(input) {
|
|
|
1292
1293
|
...(failed && input.errorRetryAfterMs !== undefined ? { retryAfterMs: input.errorRetryAfterMs } : {}),
|
|
1293
1294
|
...(input.resultIsPartial === true ? { partial_result: true } : {}),
|
|
1294
1295
|
...(input.completionId !== undefined ? { completionId: input.completionId } : {}),
|
|
1296
|
+
...(input.agentState !== undefined ? { agentState: input.agentState } : {}),
|
|
1295
1297
|
};
|
|
1296
1298
|
}
|
|
1297
|
-
export function serveDurableAgentRowLane(row) {
|
|
1299
|
+
export function serveDurableAgentRowLane(row, agentState) {
|
|
1300
|
+
const stateLine = agentState !== undefined ? `state: ${agentStateSummary(agentState)}\n` : "";
|
|
1298
1301
|
if (row.status === "parked") {
|
|
1299
1302
|
return {
|
|
1300
1303
|
content: delimitUntrusted(`TaskOutput ${row.handle}`, `status: parked
|
|
1301
|
-
The agent is durably suspended, waiting for an approval decision. It resumes when the pending approval is decided (durable approval inbox), or lands failed if the approval expires.`),
|
|
1304
|
+
${stateLine}The agent is durably suspended, waiting for an approval decision. It resumes when the pending approval is decided (durable approval inbox), or lands failed if the approval expires.`),
|
|
1302
1305
|
details: buildAgentPollDetails({
|
|
1303
1306
|
taskId: row.handle,
|
|
1304
1307
|
status: "parked",
|
|
1305
1308
|
retrievalStatus: "success",
|
|
1306
1309
|
...(row.seq !== undefined ? { seq: row.seq } : {}),
|
|
1310
|
+
...(agentState !== undefined ? { agentState } : {}),
|
|
1307
1311
|
}),
|
|
1308
1312
|
};
|
|
1309
1313
|
}
|
|
@@ -1311,7 +1315,7 @@ The agent is durably suspended, waiting for an approval decision. It resumes whe
|
|
|
1311
1315
|
? ` (error_kind: ${row.errorKind}, retryable: ${row.errorRetryable}${row.errorRetryAfterMs !== undefined ? `, retry_after_ms: ${row.errorRetryAfterMs}` : ""})`
|
|
1312
1316
|
: "";
|
|
1313
1317
|
const body = `status: ${row.status}
|
|
1314
|
-
${row.error ? `error: ${row.error}${kindClause}
|
|
1318
|
+
${stateLine}${row.error ? `error: ${row.error}${kindClause}
|
|
1315
1319
|
` : ""}${row.finalOutput ? `--- result${row.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
|
|
1316
1320
|
${clipTaskOutput(row.finalOutputFull ?? row.finalOutput)}` : "(no result text)"}`;
|
|
1317
1321
|
return {
|
|
@@ -1328,6 +1332,7 @@ ${clipTaskOutput(row.finalOutputFull ?? row.finalOutput)}` : "(no result text)"}
|
|
|
1328
1332
|
...(row.errorRetryAfterMs !== undefined ? { errorRetryAfterMs: row.errorRetryAfterMs } : {}),
|
|
1329
1333
|
...(row.resultIsPartial === true ? { resultIsPartial: true } : {}),
|
|
1330
1334
|
...(row.completionId !== undefined ? { completionId: row.completionId } : {}),
|
|
1335
|
+
...(agentState !== undefined ? { agentState } : {}),
|
|
1331
1336
|
}),
|
|
1332
1337
|
...(row.status === "failed" ? { isError: true } : {}),
|
|
1333
1338
|
};
|
|
@@ -1360,21 +1365,26 @@ export async function spillClippedAgentResult(handle, full, clipped, store, sess
|
|
|
1360
1365
|
handle.spillRef = ref;
|
|
1361
1366
|
return disclose(ref);
|
|
1362
1367
|
}
|
|
1363
|
-
export async function pollBackgroundAgentLane(handle, deadline, signal, oneShot, store, sessionId) {
|
|
1368
|
+
export async function pollBackgroundAgentLane(handle, deadline, signal, oneShot, store, sessionId, checkpointSeat) {
|
|
1364
1369
|
while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
|
|
1365
1370
|
await sleepPollStep(deadline, signal);
|
|
1366
1371
|
}
|
|
1367
1372
|
const running = handle.status === "running";
|
|
1368
1373
|
const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
|
|
1369
1374
|
if (handle.status === "parked") {
|
|
1375
|
+
const parkedSource = agentStateSourceOfHandle(handle);
|
|
1376
|
+
const parkedSeq = handle.cycleSeq;
|
|
1377
|
+
const agentState = await readAgentState(parkedSource, checkpointSeat, Date.now());
|
|
1370
1378
|
return {
|
|
1371
1379
|
content: delimitUntrusted(`TaskOutput ${handle.id}`, `status: parked
|
|
1380
|
+
state: ${agentStateSummary(agentState)}
|
|
1372
1381
|
The agent is durably suspended, waiting for an approval decision. It resumes when the pending approval is decided (durable approval inbox), or lands failed if the approval expires.`),
|
|
1373
1382
|
details: buildAgentPollDetails({
|
|
1374
1383
|
taskId: handle.id,
|
|
1375
1384
|
status: "parked",
|
|
1376
1385
|
retrievalStatus: "success",
|
|
1377
|
-
...(
|
|
1386
|
+
...(parkedSeq !== undefined ? { seq: parkedSeq } : {}),
|
|
1387
|
+
agentState,
|
|
1378
1388
|
}),
|
|
1379
1389
|
};
|
|
1380
1390
|
}
|
|
@@ -1390,20 +1400,24 @@ The agent is durably suspended, waiting for an approval decision. It resumes whe
|
|
|
1390
1400
|
cycleSeq: handle.cycleSeq,
|
|
1391
1401
|
stoppedBy: handle.stoppedBy,
|
|
1392
1402
|
completionId: handle.completionId,
|
|
1403
|
+
updatedAt: handle.updatedAt,
|
|
1393
1404
|
};
|
|
1405
|
+
const stateRead = readAgentState(agentStateSourceOfHandle({ status: snap.status, updatedAt: snap.updatedAt }), checkpointSeat, Date.now());
|
|
1406
|
+
const agentState = stateRead instanceof Promise ? await stateRead : stateRead;
|
|
1394
1407
|
const fullResult = snap.result ? (handle.resultFull ?? snap.result) : undefined;
|
|
1395
1408
|
const resultText = fullResult !== undefined ? await spillClippedAgentResult(handle, fullResult, clipTaskOutput(fullResult, handle.outputFile), store, sessionId) : undefined;
|
|
1396
1409
|
const kindClause = snap.status === "failed" && snap.errorKind !== undefined && snap.errorRetryable !== undefined
|
|
1397
1410
|
? ` (error_kind: ${snap.errorKind}, retryable: ${snap.errorRetryable}${snap.errorRetryAfterMs !== undefined ? `, retry_after_ms: ${snap.errorRetryAfterMs}` : ""})`
|
|
1398
1411
|
: "";
|
|
1412
|
+
const stateLine = `state: ${agentStateSummary(agentState)}\n`;
|
|
1399
1413
|
const body = running
|
|
1400
1414
|
? oneShot === true
|
|
1401
1415
|
? `status: running
|
|
1402
|
-
This is a ONE-SHOT submission — there is no later turn for a background notification to land in, so do NOT end your turn expecting one. Actively wait instead: TaskOutput({ task_id: "${handle.id}", block: true }). If it is still running after the wait, wait again (bounded) rather than ending the turn, or write out your best available answer now if you are near your own time budget.`
|
|
1416
|
+
${stateLine}This is a ONE-SHOT submission — there is no later turn for a background notification to land in, so do NOT end your turn expecting one. Actively wait instead: TaskOutput({ task_id: "${handle.id}", block: true }). If it is still running after the wait, wait again (bounded) rather than ending the turn, or write out your best available answer now if you are near your own time budget.`
|
|
1403
1417
|
: `status: running
|
|
1404
|
-
The agent is still working — you will be notified when it completes.`
|
|
1418
|
+
${stateLine}The agent is still working — you will be notified when it completes.`
|
|
1405
1419
|
: `status: ${snap.status}
|
|
1406
|
-
${snap.error ? `error: ${snap.error}${kindClause}
|
|
1420
|
+
${stateLine}${snap.error ? `error: ${snap.error}${kindClause}
|
|
1407
1421
|
` : ""}${snap.result ? `--- result${snap.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
|
|
1408
1422
|
${resultText}` : "(no result text)"}`;
|
|
1409
1423
|
return {
|
|
@@ -1420,6 +1434,7 @@ ${resultText}` : "(no result text)"}`;
|
|
|
1420
1434
|
...(snap.errorRetryAfterMs !== undefined ? { errorRetryAfterMs: snap.errorRetryAfterMs } : {}),
|
|
1421
1435
|
...(snap.resultIsPartial === true ? { resultIsPartial: true } : {}),
|
|
1422
1436
|
...(snap.completionId !== undefined ? { completionId: snap.completionId } : {}),
|
|
1437
|
+
agentState,
|
|
1423
1438
|
}),
|
|
1424
1439
|
...(snap.status === "failed" ? { isError: true } : {}),
|
|
1425
1440
|
};
|
|
@@ -72,6 +72,12 @@ export interface UnifiedTaskOutput {
|
|
|
72
72
|
* not contractually required to stay one); absent when the underlying task hasn't reached a terminal
|
|
73
73
|
* settle yet, or on a pre-P1-3 core build that never minted one. */
|
|
74
74
|
completionId?: string;
|
|
75
|
+
/** A background agent's current state (status word, last activity, the approval card a parked row
|
|
76
|
+
* waits on) — {@link import("./agent-state.js").readAgentState}, the one reading AgentTranscript and
|
|
77
|
+
* ListAgents render too. Present on every background_agent POLL answer that resolved a row (live,
|
|
78
|
+
* durable terminal, durable parked, durable still-running); absent on other lanes, on not-found, and on
|
|
79
|
+
* the stop face. */
|
|
80
|
+
agentState?: import("./agent-state.js").AgentState;
|
|
75
81
|
details?: unknown;
|
|
76
82
|
}
|
|
77
83
|
/**
|
|
@@ -9,6 +9,8 @@ import { type SemaTaskType, type StopSource, type SemaTaskStatus, type TaskRetri
|
|
|
9
9
|
export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
|
|
10
10
|
export type { ParkedClaimTicket, RegisterBackgroundAgentInput } from "./task-registry-shared.js";
|
|
11
11
|
import type { RegisterBackgroundAgentInput, ParkedClaimTicket } from "./task-registry-shared.js";
|
|
12
|
+
import { type AgentStateSource } from "./agent-state.js";
|
|
13
|
+
import type { CheckpointSeat } from "./checkpoint-seat.js";
|
|
12
14
|
export type { SemaTaskType, StopSource, SemaTaskStatus, TaskRetrievalStatus, UnifiedTaskOutput, SemaTaskHandle, TaskAccess, MonitorTimers, RegisterMonitorInput, RegisterWorkflowInput, };
|
|
13
15
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
14
16
|
export interface TaskKind {
|
|
@@ -78,6 +80,10 @@ export interface TaskPollOptions {
|
|
|
78
80
|
* good (see {@link import("./task-registry-agent.js").spillClippedAgentResult}). Absent (a
|
|
79
81
|
* deployment that never wired a tool-result store) ⇒ legal degrade, byte-identical to pre-S1 output. */
|
|
80
82
|
toolResultStore?: ToolResultStore;
|
|
83
|
+
/** The run's checkpoint seat: a PARKED background_agent row's approval card is read from it for the
|
|
84
|
+
* answer's `agentState` ({@link import("./agent-state.js").readAgentState}). Absent ⇒ the card reads
|
|
85
|
+
* `unreadable`; nothing else changes. */
|
|
86
|
+
checkpointSeat?: CheckpointSeat;
|
|
81
87
|
}
|
|
82
88
|
export interface TaskStopOptions {
|
|
83
89
|
workflowStore?: WorkflowRunStore;
|
|
@@ -116,6 +122,9 @@ export interface TaskToolOptions extends TaskAccess {
|
|
|
116
122
|
* spills to — see {@link TaskPollOptions.toolResultStore} (forwarded verbatim into `pollTask`).
|
|
117
123
|
* Absent ⇒ legal degrade, byte-identical to pre-S1 output. */
|
|
118
124
|
toolResultStore?: ToolResultStore;
|
|
125
|
+
/** The run's checkpoint seat — see {@link TaskPollOptions.checkpointSeat} (forwarded verbatim into
|
|
126
|
+
* `pollTask`). The first-party mount passes the same seat the Agent tool parks children into. */
|
|
127
|
+
checkpointSeat?: CheckpointSeat;
|
|
119
128
|
}
|
|
120
129
|
/**
|
|
121
130
|
* RB-49 B-3 (2026-07-25): the row-metadata projection `getAccessibleTask` returns. Was spelled out as a
|
|
@@ -281,7 +290,14 @@ export declare class TaskRegistry {
|
|
|
281
290
|
skipSessionScoped?: boolean;
|
|
282
291
|
sessionScopedOnly?: boolean;
|
|
283
292
|
}): number;
|
|
293
|
+
/** A durable row's answer with its state: only a parked row waits on the card read; any other row is
|
|
294
|
+
* answered in place. */
|
|
284
295
|
private serveDurableAgentRow;
|
|
296
|
+
/** The row facts {@link import("./agent-state.js").readAgentState} reads for a background agent this
|
|
297
|
+
* caller can see — the live handle only (a durable-only row is projected by its reader from the record,
|
|
298
|
+
* {@link import("./agent-state.js").agentStateSourceOfRecord}). Same access guard as a poll; unknown and
|
|
299
|
+
* out-of-scope ids read identically (undefined). */
|
|
300
|
+
agentStateSource(taskId: string, access: TaskAccess): AgentStateSource | undefined;
|
|
285
301
|
resolveBackgroundAgentByName(name: string, access: TaskAccess, opts?: {
|
|
286
302
|
preferRunning?: boolean;
|
|
287
303
|
}): {
|
|
@@ -14,6 +14,7 @@ import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_MISSING_ID_MESS
|
|
|
14
14
|
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, recordBackgroundAgentOrgAdmissionLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, backgroundAgentCycleSeqLane, activeDelegationHandlesLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
15
15
|
import { toolFace } from "./tool-catalog-entries.js";
|
|
16
16
|
import { thrownText } from "./thrown-value.js";
|
|
17
|
+
import { agentStateSourceOfHandle, agentStateSourceOfRecord, agentStateSummary, readAgentState } from "./agent-state.js";
|
|
17
18
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
18
19
|
function shellRowRetained(h) {
|
|
19
20
|
return retainsBackgroundProcesses({ marked: h.retained === true, env: envDeclaresRetain(h.env) });
|
|
@@ -188,8 +189,15 @@ export class TaskRegistry {
|
|
|
188
189
|
abortBackgroundAgentsForOwner(access, opts) {
|
|
189
190
|
return abortBackgroundAgentsForOwnerLane(this.core, access, opts);
|
|
190
191
|
}
|
|
191
|
-
serveDurableAgentRow(row) {
|
|
192
|
-
|
|
192
|
+
serveDurableAgentRow(row, checkpointSeat) {
|
|
193
|
+
const read = readAgentState(agentStateSourceOfRecord(row), checkpointSeat, Date.now());
|
|
194
|
+
return read instanceof Promise ? read.then((agentState) => serveDurableAgentRowLane(row, agentState)) : serveDurableAgentRowLane(row, read);
|
|
195
|
+
}
|
|
196
|
+
agentStateSource(taskId, access) {
|
|
197
|
+
const h = this.handles.get(taskId);
|
|
198
|
+
if (!h || h.type !== "background_agent" || !canAccess(h, access))
|
|
199
|
+
return undefined;
|
|
200
|
+
return agentStateSourceOfHandle(h);
|
|
193
201
|
}
|
|
194
202
|
resolveBackgroundAgentByName(name, access, opts) {
|
|
195
203
|
return resolveBackgroundAgentByNameLane(this.core, name, access, opts);
|
|
@@ -238,8 +246,8 @@ export class TaskRegistry {
|
|
|
238
246
|
runningBackgroundAgentLabels(access) {
|
|
239
247
|
return runningBackgroundAgentLabelsLane(this.core, access);
|
|
240
248
|
}
|
|
241
|
-
async pollBackgroundAgent(handle, deadline, signal, oneShot, toolResultStore, sessionId) {
|
|
242
|
-
return pollBackgroundAgentLane(handle, deadline, signal, oneShot, toolResultStore, sessionId);
|
|
249
|
+
async pollBackgroundAgent(handle, deadline, signal, oneShot, toolResultStore, sessionId, checkpointSeat) {
|
|
250
|
+
return pollBackgroundAgentLane(handle, deadline, signal, oneShot, toolResultStore, sessionId, checkpointSeat);
|
|
243
251
|
}
|
|
244
252
|
async stopBackgroundAgent(handle) {
|
|
245
253
|
return stopBackgroundAgentLane(this.core, handle);
|
|
@@ -863,7 +871,7 @@ export class TaskRegistry {
|
|
|
863
871
|
if (handle.type === "background_bash")
|
|
864
872
|
return this.pollBackgroundBash(handle, taskId, access, opts.filter, deadline, opts.signal);
|
|
865
873
|
if (handle.type === "background_agent")
|
|
866
|
-
return this.pollBackgroundAgent(handle, deadline, opts.signal, opts.oneShot, opts.toolResultStore, access.sessionId);
|
|
874
|
+
return this.pollBackgroundAgent(handle, deadline, opts.signal, opts.oneShot, opts.toolResultStore, access.sessionId, opts.checkpointSeat);
|
|
867
875
|
if (handle.type === "monitor")
|
|
868
876
|
return this.pollMonitor(handle, opts.filter, deadline, opts.signal);
|
|
869
877
|
return this.pollWorkflow(handle, access, deadline, opts.signal);
|
|
@@ -896,9 +904,12 @@ export class TaskRegistry {
|
|
|
896
904
|
if (row.status === "running" && row.writerId === this.writerId)
|
|
897
905
|
break;
|
|
898
906
|
if (row.status !== "running")
|
|
899
|
-
return this.serveDurableAgentRow(row);
|
|
907
|
+
return this.serveDurableAgentRow(row, opts.checkpointSeat);
|
|
900
908
|
if (deadline === undefined || Date.now() >= deadline || opts.signal?.aborted) {
|
|
909
|
+
const stateRead = readAgentState(agentStateSourceOfRecord(row), opts.checkpointSeat, Date.now());
|
|
910
|
+
const agentState = stateRead instanceof Promise ? await stateRead : stateRead;
|
|
901
911
|
const body = `status: unknown (not running on this engine instance)
|
|
912
|
+
state: ${agentStateSummary(agentState)}
|
|
902
913
|
The durable record is still marked running (last update ${new Date(row.updatedAt).toISOString()}), but this engine instance is not executing it — the outcome is unknown here. If its host process is gone, a retention sweep (staleRunningMaxAgeMs) settles the record as interrupted.`;
|
|
903
914
|
return {
|
|
904
915
|
content: delimitUntrusted(`TaskOutput ${row.handle}`, body),
|
|
@@ -910,6 +921,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
910
921
|
retrieval_status: "not_ready",
|
|
911
922
|
error: "not_local",
|
|
912
923
|
code: "not_local",
|
|
924
|
+
agentState,
|
|
913
925
|
},
|
|
914
926
|
};
|
|
915
927
|
}
|
|
@@ -1321,6 +1333,7 @@ export function createTaskOutputTool(opts) {
|
|
|
1321
1333
|
signal: ctx.signal,
|
|
1322
1334
|
oneShot: opts.oneShot,
|
|
1323
1335
|
toolResultStore: opts.toolResultStore,
|
|
1336
|
+
checkpointSeat: opts.checkpointSeat,
|
|
1324
1337
|
});
|
|
1325
1338
|
const { type: taskType, ...rest } = r.details;
|
|
1326
1339
|
return {
|
package/dist/core/task-spec.d.ts
CHANGED
|
@@ -965,6 +965,16 @@ export interface TaskSpec {
|
|
|
965
965
|
* end-user-authored skills are responsible for vetting them: a skill IS instructions by design.
|
|
966
966
|
*/
|
|
967
967
|
skills?: SkillSpec[];
|
|
968
|
+
/**
|
|
969
|
+
* The skills THIS run's human input invoked by name (a shell that parsed `/name` from the person's line), by
|
|
970
|
+
* {@link SkillSpec.name}. Each one's {@link SkillSpec.allowedTools} / {@link SkillSpec.disallowedTools} take effect
|
|
971
|
+
* at the start of the run with no `Skill` call and no card — the person's own invocation is the statement — and last
|
|
972
|
+
* for this run (a steer does not end them; the next runTask starts without them), like a grant the model's activation wrote. Delivering the skill's body
|
|
973
|
+
* stays the caller's business (this key carries names only). Every name must be one of this run's {@link skills};
|
|
974
|
+
* anything else refuses the run before it starts (`config.user_invoked_skill_unknown`), never dropped. Present only
|
|
975
|
+
* when the person actually invoked one; a mid-turn invocation (one riding a steer) grants nothing.
|
|
976
|
+
*/
|
|
977
|
+
userInvokedSkills?: string[];
|
|
968
978
|
/**
|
|
969
979
|
* Fraction of the model's context window the `<skills>` listing may spend. Absent ⇒ `0.01` (1%).
|
|
970
980
|
* Domain `(0, 1]`; anything else — including `0`, a string, `NaN` — REFUSES at the door with
|
package/dist/core/tool-face.d.ts
CHANGED
|
@@ -76,8 +76,9 @@ export type ToolPathAbsence = (typeof TOOL_PATH_ABSENCES)[number];
|
|
|
76
76
|
* slot RESOLVES — as a READER sees it, whichever side it came from. Present ⇔ the tool can be confirmed BY
|
|
77
77
|
* PATH (write confinement, sensitive-path guards, edited-file tracking, the transcript-directory gate, the
|
|
78
78
|
* permission-rule path family). `param` outranks every alias (NotebookEdit's `notebook_path` beats a decoy
|
|
79
|
-
* `file_path`). `skillScopeEligible` (default false) is
|
|
80
|
-
*
|
|
79
|
+
* `file_path`). `skillScopeEligible` (default false) is a SEPARATE authorization domain (its one reader, the
|
|
80
|
+
* skill-scope write fence, is retired and no reader remains; the bit stays a declared member of the roster row) — a
|
|
81
|
+
* confinable write tool is not automatically eligible there.
|
|
81
82
|
*
|
|
82
83
|
* ONE shape, two spellings: a DECLARATION ({@link ToolPathTarget}, what a catalog row / a `ToolSpec` / an
|
|
83
84
|
* MCP overlay writes) and the roster row's NORMALIZED form (`aliases` always an array, `skillScopeEligible`
|
|
@@ -12,17 +12,43 @@
|
|
|
12
12
|
* - `TOMBSTONED_TOOLS` (per-name re-education messages) is gone: a retired name now gets the same
|
|
13
13
|
* loud generic roster miss as any unknown tool.
|
|
14
14
|
*
|
|
15
|
-
* What REMAINS is validation data
|
|
16
|
-
* retired to a one-line guidance string
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
15
|
+
* What REMAINS is validation data: {@link RETIRED_TOOL_NAMES} maps every name the engine ever
|
|
16
|
+
* retired to a one-line guidance string, and {@link judgeRuleToolName} is the ONE reading of it a rule's
|
|
17
|
+
* tool name gets. They exist because deleting the mapping flips stale POLICY entries from "silently
|
|
18
|
+
* matched" to "silently matches nothing" — and for a deny/requireApproval entry that direction WIDENS
|
|
19
|
+
* the surface. `validatePermissionRules` (permission-rules.ts) does NOT classify retired names itself:
|
|
20
|
+
* which names a deployment mounts its OWN tool under is a roster fact, and the judgment takes the roster.
|
|
21
|
+
* A deployment that mounts its OWN tool under one of these names is unaffected: the name is then in the
|
|
22
|
+
* roster and the judgment answers `current`.
|
|
23
|
+
*
|
|
24
|
+
* GRAMMAR of a guidance string, read by {@link judgeRuleToolName} and held by a pin: it opens with
|
|
25
|
+
* `renamed "<name>"` (a 1:1 successor — the judgment's `currentName`), `folded into "<name>"` (merged into a
|
|
26
|
+
* wider tool: writing it would deny more than the entry did, so it is no current name), or `removed — `.
|
|
27
27
|
*/
|
|
28
28
|
export declare const RETIRED_TOOL_NAMES: ReadonlyMap<string, string>;
|
|
29
|
+
/** The three answers {@link judgeRuleToolName} gives — a closed set. */
|
|
30
|
+
export declare const RULE_TOOL_NAME_VERDICTS: readonly ["current", "retired", "unprefixed_namespace"];
|
|
31
|
+
/**
|
|
32
|
+
* Whether a tool name written in a rule is a stale name — one that, against `roster`, names no tool and never can:
|
|
33
|
+
* - `current` — not a stale name: the roster mounts it, or it is neither retired nor a prefix-less `__` name. (Whether a
|
|
34
|
+
* current name matches anything THIS run is a different question — an unknown name is the prepare audit's advisory.)
|
|
35
|
+
* - `retired` — a name in {@link RETIRED_TOOL_NAMES} the roster does not mount; `guidance` is that table's line, and
|
|
36
|
+
* `currentName` the 1:1 successor when there is one (see the module header's grammar).
|
|
37
|
+
* - `unprefixed_namespace` — carries the `__` namespace separator with no protocol prefix. The roster cannot clear it:
|
|
38
|
+
* a caller tool name or alias carrying `__` is refused at prepare, every engine tool name is `__`-free, and every
|
|
39
|
+
* protocol tool is minted with its prefix.
|
|
40
|
+
*
|
|
41
|
+
* `roster` = the tool names (aliases included) the reader knows are mounted: a run's assembled roster at prepare, the
|
|
42
|
+
* names a deployment DECLARES it mounts its own tools under at a session-rule store (an empty set when it declares none).
|
|
43
|
+
*/
|
|
44
|
+
export type RuleToolNameVerdict = {
|
|
45
|
+
readonly kind: "current";
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "retired";
|
|
48
|
+
readonly guidance: string;
|
|
49
|
+
readonly currentName?: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: "unprefixed_namespace";
|
|
52
|
+
};
|
|
53
|
+
/** See {@link RuleToolNameVerdict}. Pure; the one answer to "is this rule's tool name a retired or prefix-less name". */
|
|
54
|
+
export declare function judgeRuleToolName(name: string, roster: ReadonlySet<string>): RuleToolNameVerdict;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { carriesNamespaceSeparator, protocolOf } from "./protocol-table.js";
|
|
1
2
|
export const RETIRED_TOOL_NAMES = new Map([
|
|
2
3
|
["read_file", 'renamed "Read"'],
|
|
3
4
|
["edit_file", 'renamed "Edit"'],
|
|
@@ -40,3 +41,18 @@ export const RETIRED_TOOL_NAMES = new Map([
|
|
|
40
41
|
["Recall", "removed — memory is file-based; read the memory directory with the ordinary file tools"],
|
|
41
42
|
["recall", "removed — memory is file-based; read the memory directory with the ordinary file tools"],
|
|
42
43
|
]);
|
|
44
|
+
export const RULE_TOOL_NAME_VERDICTS = Object.freeze(["current", "retired", "unprefixed_namespace"]);
|
|
45
|
+
const CURRENT = Object.freeze({ kind: "current" });
|
|
46
|
+
const UNPREFIXED = Object.freeze({ kind: "unprefixed_namespace" });
|
|
47
|
+
export function judgeRuleToolName(name, roster) {
|
|
48
|
+
if (roster.has(name))
|
|
49
|
+
return CURRENT;
|
|
50
|
+
const guidance = RETIRED_TOOL_NAMES.get(name);
|
|
51
|
+
if (guidance !== undefined) {
|
|
52
|
+
const currentName = /^renamed "([^"]+)"/.exec(guidance)?.[1];
|
|
53
|
+
return currentName !== undefined ? { kind: "retired", guidance, currentName } : { kind: "retired", guidance };
|
|
54
|
+
}
|
|
55
|
+
if (carriesNamespaceSeparator(name) && protocolOf(name) === undefined)
|
|
56
|
+
return UNPREFIXED;
|
|
57
|
+
return CURRENT;
|
|
58
|
+
}
|
|
@@ -130,11 +130,15 @@ export declare const NAME_READINGS: {
|
|
|
130
130
|
readonly kind: "wire";
|
|
131
131
|
readonly file: string;
|
|
132
132
|
};
|
|
133
|
-
readonly "
|
|
133
|
+
readonly "skillGrant.names": {
|
|
134
134
|
readonly kind: "any";
|
|
135
135
|
readonly file: string;
|
|
136
136
|
};
|
|
137
|
-
readonly "
|
|
137
|
+
readonly "skillGrant.binding": {
|
|
138
|
+
readonly kind: "wire";
|
|
139
|
+
readonly file: string;
|
|
140
|
+
};
|
|
141
|
+
readonly "gate.skillActivationSeat": {
|
|
138
142
|
readonly kind: "wire";
|
|
139
143
|
readonly file: string;
|
|
140
144
|
};
|
|
@@ -5,6 +5,7 @@ const POLICY = "src/core/tool-policy.ts";
|
|
|
5
5
|
const OFFER = "src/core/runner/permission-rule-lanes.ts";
|
|
6
6
|
const PARK = "src/core/runner/prepare-park-ask.ts";
|
|
7
7
|
const HANDLERS = "src/core/runner/run-harness-handlers.ts";
|
|
8
|
+
const STATIONS = "src/core/runner/call-hook-stations.ts";
|
|
8
9
|
const any = (file) => ({ kind: "any", file });
|
|
9
10
|
const wire = (file) => ({ kind: "wire", file });
|
|
10
11
|
const spelling = (file) => ({ kind: "spelling", file });
|
|
@@ -33,8 +34,9 @@ export const NAME_READINGS = {
|
|
|
33
34
|
"policy.projectionReplay": any(POLICY),
|
|
34
35
|
"policy.sessionRules": any("src/core/runner/session-rule-policy.ts"),
|
|
35
36
|
"policy.sessionRules.effect": wire("src/core/runner/session-rule-policy.ts"),
|
|
36
|
-
"
|
|
37
|
-
"
|
|
37
|
+
"skillGrant.names": any("src/core/skill-grant.ts"),
|
|
38
|
+
"skillGrant.binding": wire("src/core/skill-grant.ts"),
|
|
39
|
+
"gate.skillActivationSeat": wire(GATE),
|
|
38
40
|
"policy.permissionRules": any("src/core/permission-rules.ts"),
|
|
39
41
|
"policy.persistedGrammar": any("src/core/permission-rule-model.ts"),
|
|
40
42
|
"policy.durableQuestion": any("src/core/ask-question.ts"),
|
|
@@ -54,8 +56,8 @@ export const NAME_READINGS = {
|
|
|
54
56
|
"verify.wroteThisRun": wire(HANDLERS),
|
|
55
57
|
"verify.wroteThisRunSeed": wire("src/core/runner/run-attachment-seats.ts"),
|
|
56
58
|
"event.toolStartEnd": spelling(HANDLERS),
|
|
57
|
-
"hook.postToolUse": wire(
|
|
58
|
-
"hook.postToolUseFailure": wire(
|
|
59
|
+
"hook.postToolUse": wire(STATIONS),
|
|
60
|
+
"hook.postToolUseFailure": wire(STATIONS),
|
|
59
61
|
"hook.postToolBatch": spelling(HANDLERS),
|
|
60
62
|
"stats.subagentToolCounter": spelling("src/agents/subagent.ts"),
|
|
61
63
|
"activity.sink": spelling(HANDLERS),
|
|
@@ -42,7 +42,7 @@ export interface ToolCallRequest {
|
|
|
42
42
|
* against, read at adjudication time from the same tracked `cwdRef` the fs hand tools resolve with
|
|
43
43
|
* (design/64 §16.3: a resident shell's `cd` persists and moves the fs tools' relative base with it).
|
|
44
44
|
*
|
|
45
|
-
* WHY IT EXISTS: every path guard here (sensitive-path, fs-write gate, session rules,
|
|
45
|
+
* WHY IT EXISTS: every path guard here (sensitive-path, fs-write gate, session rules,
|
|
46
46
|
* transcript integrity) re-resolves the model's spelling itself, and each was handed a STATIC root at
|
|
47
47
|
* wiring time. After a `cd` the two sides of a single call disagreed about which file it was: the tool
|
|
48
48
|
* wrote `<liveCwd>/pre-commit` while the guard judged `<wiringRoot>/pre-commit` — measured on a host
|
|
@@ -50,7 +50,7 @@ export interface ToolCallRequest {
|
|
|
50
50
|
* Reading THIS field makes the guard's resolution base the same value the tool will use.
|
|
51
51
|
*
|
|
52
52
|
* Applies to the call's TARGET only. A guard's own configured directories (accept/exempt domains,
|
|
53
|
-
* `allowDirs
|
|
53
|
+
* `allowDirs`) are DEPLOYMENT text and keep resolving against the static root —
|
|
54
54
|
* a shell `cd` must never relocate an allow-domain.
|
|
55
55
|
*
|
|
56
56
|
* ABSENT when the run has no tracked cwd (read-only hands, no execution env) or when a policy is
|
|
@@ -23,9 +23,6 @@ export declare function catalogToolNames(): ReadonlySet<string>;
|
|
|
23
23
|
/** A-11/12/13/14 — tools this engine can CONFINE BY PATH on a write: `pathTarget.access ≠ read`.
|
|
24
24
|
* Today {Write, Edit, NotebookEdit}. */
|
|
25
25
|
export declare function pathConfinableWriteToolNames(): ReadonlySet<string>;
|
|
26
|
-
/** A-11 (the SECOND authorization domain) — the active-skill `allowPaths` face: confinable AND the
|
|
27
|
-
* definition opted into skill scoping. Today {Write, Edit} — NotebookEdit is deliberately outside. */
|
|
28
|
-
export declare function skillScopeWriteToolNames(): ReadonlySet<string>;
|
|
29
26
|
/** A-8 — every definition that names a path target at all (read or write): the file-tool glob lane. */
|
|
30
27
|
export declare function pathTargetingToolNames(): ReadonlySet<string>;
|
|
31
28
|
/** The tools whose declared path target is a READ (a file read, a search root): the file read station's membership. */
|
|
@@ -75,7 +72,7 @@ export declare function protectivePathTargetOf(call: ToolCallLike): CallPathTarg
|
|
|
75
72
|
export declare const BARE_REQUEST_CODE = "policy.bare_request";
|
|
76
73
|
/**
|
|
77
74
|
* The path target a CONTAINMENT fence reads (a grant admitting a write BY its path: session `allowDirs`, the fs-write
|
|
78
|
-
* gate's accept/exempt dirs
|
|
75
|
+
* gate's accept/exempt dirs): the object
|
|
79
76
|
* face's OWN declaration only — a same-name tool that declares no path target is an UNCONFIRMABLE write (the catalog's
|
|
80
77
|
* slot may not be the one it writes; a decoy in that slot would buy an admission), so the fence fails closed.
|
|
81
78
|
*
|
|
@@ -26,9 +26,6 @@ const names = (pred) => {
|
|
|
26
26
|
export function pathConfinableWriteToolNames() {
|
|
27
27
|
return names((e) => e.face.pathTarget !== undefined && e.face.pathTarget.access !== "read");
|
|
28
28
|
}
|
|
29
|
-
export function skillScopeWriteToolNames() {
|
|
30
|
-
return names((e) => e.face.pathTarget !== undefined && e.face.pathTarget.access !== "read" && e.face.pathTarget.skillScopeEligible === true);
|
|
31
|
-
}
|
|
32
29
|
export function pathTargetingToolNames() {
|
|
33
30
|
return names((e) => e.face.pathTarget !== undefined);
|
|
34
31
|
}
|