@sema-agent/core 7.5.2 → 7.6.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 +67 -0
- package/dist/agents/agent-transcript-tool.d.ts +2 -2
- package/dist/agents/cascade.d.ts +4 -5
- package/dist/agents/cascade.js +12 -10
- package/dist/agents/repair-loop.d.ts +7 -5
- package/dist/agents/repair-loop.js +13 -15
- package/dist/agents/retain-ledger.d.ts +2 -3
- package/dist/agents/send-message-tool.d.ts +2 -2
- package/dist/agents/session-util.d.ts +2 -2
- package/dist/agents/subagent.d.ts +27 -46
- package/dist/agents/subagent.js +119 -105
- package/dist/agents/suspend-guard.d.ts +31 -19
- package/dist/agents/suspend-guard.js +14 -8
- package/dist/agents/teacher.d.ts +2 -2
- package/dist/agents/teacher.js +9 -9
- package/dist/agents/team.d.ts +6 -5
- package/dist/agents/team.js +10 -8
- package/dist/agents/verify.d.ts +8 -9
- package/dist/agents/verify.js +17 -17
- package/dist/core/a2a.js +2 -1
- package/dist/core/agent-definition.d.ts +172 -0
- package/dist/core/agent-definition.js +1 -0
- package/dist/core/ask-origin.d.ts +60 -7
- package/dist/core/ask-origin.js +26 -1
- package/dist/core/checkpoint-store.d.ts +78 -76
- package/dist/core/checkpoint-store.js +17 -1
- package/dist/core/delegation-frames.d.ts +298 -0
- package/dist/core/delegation-frames.js +21 -0
- package/dist/core/engine-notice.d.ts +555 -0
- package/dist/core/engine-notice.js +55 -0
- package/dist/core/gate-fold.d.ts +12 -0
- package/dist/core/gate-fold.js +158 -0
- package/dist/core/gate-lanes.d.ts +93 -0
- package/dist/core/gate-lanes.js +626 -0
- package/dist/core/gate-outcome.d.ts +189 -0
- package/dist/core/gate-outcome.js +70 -0
- package/dist/core/hands-band.d.ts +134 -0
- package/dist/core/hands-band.js +1 -0
- package/dist/core/hooks.d.ts +22 -177
- package/dist/core/hooks.js +53 -851
- package/dist/core/mcp-failure.d.ts +142 -0
- package/dist/core/mcp-failure.js +145 -0
- package/dist/core/mcp-server-spec.d.ts +217 -0
- package/dist/core/mcp-server-spec.js +1 -0
- package/dist/core/mcp.d.ts +21 -77
- package/dist/core/mcp.js +76 -150
- package/dist/core/model-seat.d.ts +99 -0
- package/dist/core/model-seat.js +1 -0
- package/dist/core/pause-registry.d.ts +131 -0
- package/dist/core/pause-registry.js +27 -0
- package/dist/core/reminder-mint.d.ts +10 -0
- package/dist/core/reminder-mint.js +3 -0
- package/dist/core/runner/assemble-result.d.ts +32 -41
- package/dist/core/runner/assemble-result.js +55 -74
- package/dist/core/runner/contracts.d.ts +427 -69
- package/dist/core/runner/denial-limit-arms.d.ts +1 -1
- package/dist/core/runner/denial-limit-arms.js +3 -3
- package/dist/core/runner/gate-exit.d.ts +242 -0
- package/dist/core/runner/gate-exit.js +124 -0
- package/dist/core/runner/park-commit.d.ts +17 -23
- package/dist/core/runner/park-commit.js +14 -15
- package/dist/core/runner/prepare-ask-lane.d.ts +0 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -5
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -6
- package/dist/core/runner/prepare-boundary-parks.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -7
- package/dist/core/runner/prepare-gate-stations.js +29 -54
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +44 -26
- package/dist/core/runner/prepare-park-ask.d.ts +2 -4
- package/dist/core/runner/prepare-park-ask.js +5 -5
- package/dist/core/runner/prepare-task.d.ts +2 -2
- package/dist/core/runner/prepare-task.js +8 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +7 -15
- package/dist/core/runner/prepare-wiring-manifest.js +9 -10
- package/dist/core/runner/runtask.d.ts +20 -102
- package/dist/core/runner/runtask.js +119 -121
- package/dist/core/runner/terminal-projection.d.ts +22 -0
- package/dist/core/runner/terminal-projection.js +28 -0
- package/dist/core/runner-deps.d.ts +1416 -0
- package/dist/core/runner-deps.js +1 -0
- package/dist/core/runtime-caps.d.ts +164 -0
- package/dist/core/runtime-caps.js +1 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +4 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +8 -2
- package/dist/core/task-event.d.ts +910 -0
- package/dist/core/task-event.js +1 -0
- package/dist/core/task-limits.d.ts +110 -0
- package/dist/core/task-limits.js +1 -0
- package/dist/core/task-result.d.ts +809 -0
- package/dist/core/task-result.js +1 -0
- package/dist/core/task-spec.d.ts +1370 -0
- package/dist/core/task-spec.js +1 -0
- package/dist/core/task-stream.d.ts +382 -0
- package/dist/core/task-stream.js +1 -0
- package/dist/core/terminal-cause.d.ts +137 -0
- package/dist/core/terminal-cause.js +9 -0
- package/dist/core/tool-policy.d.ts +43 -139
- package/dist/core/tool-policy.js +79 -112
- package/dist/core/tool-spec.d.ts +1174 -0
- package/dist/core/tool-spec.js +1 -0
- package/dist/core/types.d.ts +27 -7789
- package/dist/core/types.js +2 -76
- package/dist/core/warm-resume.d.ts +2 -2
- package/dist/core/wiring-manifest.d.ts +6 -3
- package/dist/core/workflow-journal-store.js +3 -4
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +9 -6
- package/dist/orchestration/builtin-workflows.d.ts +2 -2
- package/dist/orchestration/builtin-workflows.js +1 -1
- package/dist/orchestration/goal.d.ts +2 -2
- package/dist/orchestration/goal.js +8 -7
- package/dist/orchestration/run-spec.d.ts +2 -2
- package/dist/orchestration/run-spec.js +5 -3
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/run-workflow-tool.js +4 -4
- package/dist/orchestration/workflow-governance.d.ts +4 -4
- package/dist/orchestration/workflow-governance.js +4 -2
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-primitives.js +1 -1
- package/dist/orchestration/workflow.d.ts +15 -4
- package/dist/orchestration/workflow.js +64 -39
- package/dist/prompts/supervisor.d.ts +1 -1
- package/dist/prompts/supervisor.js +3 -3
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/scenarios/scenario-registry.js +1 -1
- package/dist/scenarios/teacher-quickstart.d.ts +2 -2
- package/dist/server/http.d.ts +2 -2
- package/dist/stores/file/fs-atomic.d.ts +88 -12
- package/dist/stores/file/fs-atomic.js +184 -55
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +1 -0
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +82 -22
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { TaskResult } from "../core/types.js";
|
|
1
|
+
import type { PausedCause, TaskResult, TerminalCause } from "../core/types.js";
|
|
2
2
|
/**
|
|
3
|
-
* design/45 §11 Q6 — the **durable-suspend hard boundary** (v1). A `
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* "unexpected.
|
|
10
|
-
*
|
|
3
|
+
* design/45 §11 Q6 — the **durable-suspend hard boundary** (v1). A `paused` terminal is only safe to
|
|
4
|
+
* handle at the **top-level `runTask`** boundary: the orchestrators (cascade / verify / teacher / team)
|
|
5
|
+
* run their work as *nested* `runTask`s, and none of them can drive a resume from the inside. If a nested
|
|
6
|
+
* task pauses on a durable gate, the orchestrator must **fail fast** — not silently treat it as an
|
|
7
|
+
* ordinary failure (which would escalate/re-run side effects) and not release its pinned session (which
|
|
8
|
+
* would orphan the checkpoint). It maps `paused → failed` with the code `"unexpected.suspended"` (an
|
|
9
|
+
* approval / resource pause) or `"unexpected.needs_review"` (a review pause) and **carries the pause's own
|
|
10
|
+
* cause on the failed terminal's `nestedPause` seat** so the top-level caller can still resume.
|
|
11
11
|
*
|
|
12
12
|
* The structural prevention is upstream — an orchestrator should run nested tasks under a **non-durable**
|
|
13
13
|
* approval policy (e.g. the verifier already forces `handsReadOnly` + headless auto-deny, DESIGN#5) so a
|
|
@@ -15,20 +15,32 @@ import type { TaskResult } from "../core/types.js";
|
|
|
15
15
|
* a hard boundary, or a deployment wires durable-suspend into verify and produces silent errors."
|
|
16
16
|
*/
|
|
17
17
|
export declare const UNEXPECTED_SUSPENDED = "unexpected.suspended";
|
|
18
|
-
/** design/80 D-B: the
|
|
19
|
-
*
|
|
18
|
+
/** design/80 D-B: the review durable-pause family (dry-run `needs_review` AND `plan_review`) hits the SAME
|
|
19
|
+
* nested hard boundary as an approval pause — a distinct code so a caller can tell a review pause from an
|
|
20
|
+
* approval. */
|
|
20
21
|
export declare const UNEXPECTED_NEEDS_REVIEW = "unexpected.needs_review";
|
|
21
22
|
/**
|
|
22
|
-
* design/80 D-B: the durable-pause hard boundary covers
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* design/80 D-B: the durable-pause hard boundary covers EVERY `paused` cause — an approval gate, a resource
|
|
24
|
+
* slice, a dry-run review, a `plan_review` plan-gate. All of them persist a checkpoint + PIN their session,
|
|
25
|
+
* and a nested orchestrator can't drive their resume — so it must surface the pause and NOT release the
|
|
26
|
+
* pinned session (releasing it orphans the checkpoint). A type guard, so the caller reads the token/gate
|
|
27
|
+
* off the narrowed cause.
|
|
27
28
|
*/
|
|
28
|
-
export declare function isDurablePause(
|
|
29
|
+
export declare function isDurablePause(terminal: TerminalCause): terminal is PausedCause;
|
|
30
|
+
/** A result whose terminal is known to be a pause — what {@link isDurablePause} narrows a result's
|
|
31
|
+
* `terminal` to, spelled as a type so an orchestrator can HOLD a paused result without re-checking. */
|
|
32
|
+
export type PausedResult = TaskResult & {
|
|
33
|
+
terminal: PausedCause;
|
|
34
|
+
};
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
36
|
+
* Which family a pause belongs to — its gate kind's registry row: `"suspended"` (an approval gate, a
|
|
37
|
+
* resource slice, a platform park) or `"needs_review"` (a dry-run or plan review). The same row the
|
|
38
|
+
* status word is projected from; read here by the orchestrators that name the family in their own faces.
|
|
39
|
+
*/
|
|
40
|
+
export declare function pauseFamily(t: PausedCause): "suspended" | "needs_review";
|
|
41
|
+
/**
|
|
42
|
+
* If `result` durably paused, return it mapped to a `failed` result whose terminal carries the pause on
|
|
43
|
+
* `nestedPause`; otherwise return it unchanged. The orchestrator should also **stop** (not escalate/re-run)
|
|
32
44
|
* and **not release** the session when this maps a pause — the checkpoint still references that session.
|
|
33
45
|
*/
|
|
34
46
|
export declare function mapNestedSuspend(result: TaskResult): TaskResult;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
import { PAUSE_REGISTRY } from "../core/pause-registry.js";
|
|
1
2
|
export const UNEXPECTED_SUSPENDED = "unexpected.suspended";
|
|
2
3
|
export const UNEXPECTED_NEEDS_REVIEW = "unexpected.needs_review";
|
|
3
|
-
export function isDurablePause(
|
|
4
|
-
return
|
|
4
|
+
export function isDurablePause(terminal) {
|
|
5
|
+
return terminal.kind === "paused";
|
|
6
|
+
}
|
|
7
|
+
export function pauseFamily(t) {
|
|
8
|
+
return PAUSE_REGISTRY[t.gate.kind].taskStatus;
|
|
5
9
|
}
|
|
6
10
|
export function mapNestedSuspend(result) {
|
|
7
|
-
|
|
11
|
+
const t = result.terminal;
|
|
12
|
+
if (!isDurablePause(t)) {
|
|
8
13
|
return result;
|
|
9
14
|
}
|
|
15
|
+
const word = pauseFamily(t);
|
|
16
|
+
const code = word === "needs_review" ? UNEXPECTED_NEEDS_REVIEW : UNEXPECTED_SUSPENDED;
|
|
17
|
+
const message = `a nested task durably paused ("${word}") on a HITL gate; durable suspend/resume and plan/` +
|
|
18
|
+
"dry-run review are only supported at the top-level runTask (design/45 v1 / design/80). Resume it via " +
|
|
19
|
+
"the pause's token, or run the orchestrated task under a non-durable policy so it auto-adjudicates instead of pausing.";
|
|
10
20
|
return {
|
|
11
21
|
...result,
|
|
12
|
-
|
|
13
|
-
errorCode: result.status === "needs_review" ? UNEXPECTED_NEEDS_REVIEW : UNEXPECTED_SUSPENDED,
|
|
14
|
-
errorMessage: `a nested task durably paused ("${result.status}") on a HITL gate; durable suspend/resume and plan/` +
|
|
15
|
-
"dry-run review are only supported at the top-level runTask (design/45 v1 / design/80). Resume it via " +
|
|
16
|
-
"checkpointToken, or run the orchestrated task under a non-durable policy so it auto-adjudicates instead of pausing.",
|
|
22
|
+
terminal: { kind: "failed", code, message, nestedPause: t },
|
|
17
23
|
};
|
|
18
24
|
}
|
package/dist/agents/teacher.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RunnerSelfSeat } from "../core/runner/contracts.js";
|
|
2
2
|
import { type StrategyOrigin, type StrategyStore, type StrategyStoreIncident } from "../core/strategy-store.js";
|
|
3
3
|
import type { ModelRef, TaskResult, TaskSpec } from "../core/types.js";
|
|
4
4
|
/**
|
|
@@ -156,4 +156,4 @@ export declare function parseTeacherAdvice(text: string): TeacherAdvice;
|
|
|
156
156
|
* completed output (catches "passes but semantically wrong"). Correction-then-takeover, with hard
|
|
157
157
|
* cost guards (escalation cap, teacher-spend ratio, bounded teacher turns).
|
|
158
158
|
*/
|
|
159
|
-
export declare function runWithTeacher(runner:
|
|
159
|
+
export declare function runWithTeacher(runner: RunnerSelfSeat, studentSpec: TaskSpec, teacher?: TeacherConfig): Promise<TeacherRunResult>;
|
package/dist/agents/teacher.js
CHANGED
|
@@ -119,7 +119,7 @@ export async function runWithTeacher(runner, studentSpec, teacher = {}) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
const result = await runTeacherCore(runner, spec, teacher);
|
|
122
|
-
if (store && scope && result.
|
|
122
|
+
if (store && scope && result.terminal.kind === "completed" && result.escalations.length > 0) {
|
|
123
123
|
const advice = result.escalations[result.escalations.length - 1].teacher;
|
|
124
124
|
if (advice.strategy && advice.confidence >= (teacher.minConfidenceToStore ?? 2)) {
|
|
125
125
|
try {
|
|
@@ -271,7 +271,7 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
271
271
|
...(studentSpec.restoreGatedTools !== undefined
|
|
272
272
|
? { restoreGatedTools: Array.isArray(studentSpec.restoreGatedTools) ? [...studentSpec.restoreGatedTools] : studentSpec.restoreGatedTools }
|
|
273
273
|
: {}),
|
|
274
|
-
...(studentSpec.checkpointStore ===
|
|
274
|
+
...(studentSpec.checkpointStore === "disabled" ? { checkpointStore: "disabled" } : {}),
|
|
275
275
|
};
|
|
276
276
|
const helperBase = () => teacher.helperModel
|
|
277
277
|
? { ...inheritedDeploymentConfig, model: teacher.helperModel }
|
|
@@ -407,9 +407,8 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
407
407
|
const syntheticAborted = (sid) => ({
|
|
408
408
|
taskId: sid ?? "aborted",
|
|
409
409
|
sessionId: sid ?? "aborted",
|
|
410
|
-
|
|
410
|
+
terminal: { kind: "failed", message: "student run did not settle promptly after stuck-abort" },
|
|
411
411
|
result: "",
|
|
412
|
-
errorMessage: "student run did not settle promptly after stuck-abort",
|
|
413
412
|
stats: { turns: 0, tokens: 0 },
|
|
414
413
|
});
|
|
415
414
|
const abortFallback = (sid) => {
|
|
@@ -451,7 +450,7 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
451
450
|
toolLog.length = 0;
|
|
452
451
|
const { result, stuck, recent } = await runStudent();
|
|
453
452
|
sessionId = result.sessionId;
|
|
454
|
-
if (isDurablePause(result.
|
|
453
|
+
if (isDurablePause(result.terminal)) {
|
|
455
454
|
return finalize(mapNestedSuspend(result));
|
|
456
455
|
}
|
|
457
456
|
let trigger;
|
|
@@ -459,10 +458,10 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
459
458
|
if (stuck) {
|
|
460
459
|
trigger = "stuck";
|
|
461
460
|
}
|
|
462
|
-
else if (result.
|
|
461
|
+
else if (result.terminal.kind === "blocked") {
|
|
463
462
|
trigger = "blocked";
|
|
464
463
|
}
|
|
465
|
-
else if (result.
|
|
464
|
+
else if (result.terminal.kind === "failed") {
|
|
466
465
|
trigger = "failed";
|
|
467
466
|
}
|
|
468
467
|
else if (!verifyOutput) {
|
|
@@ -482,7 +481,8 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
482
481
|
return finalize(result);
|
|
483
482
|
}
|
|
484
483
|
if (trigger === "failed" &&
|
|
485
|
-
|
|
484
|
+
result.terminal.kind === "failed" &&
|
|
485
|
+
/truncat|max[ _-]?tokens?|maximum tokens?|token (limit|budget)|context (length|window)|no answer text|(consumed|exceeded).{0,12}budget/i.test(result.terminal.message ?? "")) {
|
|
486
486
|
return finalize(result);
|
|
487
487
|
}
|
|
488
488
|
if (escalations.length >= maxEscalations) {
|
|
@@ -528,7 +528,7 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
528
528
|
signal: extSignal,
|
|
529
529
|
});
|
|
530
530
|
addTeacher(r);
|
|
531
|
-
if (isDurablePause(r.
|
|
531
|
+
if (isDurablePause(r.terminal)) {
|
|
532
532
|
return finalize(mapNestedSuspend(r));
|
|
533
533
|
}
|
|
534
534
|
return finalize(r);
|
package/dist/agents/team.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { McpServerSpec, ModelRef, ModelRole,
|
|
1
|
+
import type { RunnerSelfSeat } from "../core/runner/contracts.js";
|
|
2
|
+
import type { McpServerSpec, ModelRef, ModelRole, TaskSpec, ToolSpec } from "../core/types.js";
|
|
3
|
+
import type { CheckpointGate, CheckpointToken } from "../core/checkpoint-store.js";
|
|
3
4
|
export interface TeamMember {
|
|
4
5
|
/** The member's role / specialty, e.g. "安全评审" or "performance". */
|
|
5
6
|
role: string;
|
|
@@ -58,7 +59,7 @@ export type TeamEvent = {
|
|
|
58
59
|
type: "done";
|
|
59
60
|
};
|
|
60
61
|
export interface TeamDiscussionOptions {
|
|
61
|
-
runner:
|
|
62
|
+
runner: RunnerSelfSeat;
|
|
62
63
|
/** Default model for members / synthesizer without their own. If omitted, members resolve the
|
|
63
64
|
* `team` role and the synthesizer the `synthesize` role (both fall back to `default`). */
|
|
64
65
|
model?: ModelRef;
|
|
@@ -206,8 +207,8 @@ export interface TeamResult {
|
|
|
206
207
|
* the discussion STOPS (no synthesis on a half-done team) and surfaces the resume capability so the caller
|
|
207
208
|
* can resume the paused member via the token, then re-run. `conclusionValid` is false in this case. */
|
|
208
209
|
durablePause?: boolean;
|
|
209
|
-
checkpointToken?:
|
|
210
|
-
checkpointGate?:
|
|
210
|
+
checkpointToken?: CheckpointToken;
|
|
211
|
+
checkpointGate?: CheckpointGate;
|
|
211
212
|
}
|
|
212
213
|
/**
|
|
213
214
|
* Run a multi-round discussion across several role-specialized agents, then synthesize a conclusion.
|
package/dist/agents/team.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveToolSubset } from "./tool-filter.js";
|
|
2
2
|
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
3
|
+
import { terminalProjection } from "../core/runner/terminal-projection.js";
|
|
3
4
|
import { sanitizeUntrustedText } from "../core/untrusted-text.js";
|
|
4
5
|
const DISCUSSION_WRAPPER_TAGS = ["statement", "discussion", "discussion-final", "earlier-discussion-summary", "previous-summary", "topic"];
|
|
5
6
|
const RETIRED_WRAPPER_TAGS = ["team-discussion", "team-discussion-final"];
|
|
@@ -87,7 +88,7 @@ export async function runTeamDiscussion(opts) {
|
|
|
87
88
|
return {
|
|
88
89
|
status: "failed",
|
|
89
90
|
result: conclusion,
|
|
90
|
-
errorCode: p.errorCode,
|
|
91
|
+
errorCode: terminalProjection(mapNestedSuspend(p).terminal).errorCode,
|
|
91
92
|
conclusion,
|
|
92
93
|
conclusionValid: false,
|
|
93
94
|
transcript,
|
|
@@ -96,8 +97,8 @@ export async function runTeamDiscussion(opts) {
|
|
|
96
97
|
failures,
|
|
97
98
|
transcriptCompactions,
|
|
98
99
|
durablePause: true,
|
|
99
|
-
checkpointToken: p.
|
|
100
|
-
checkpointGate: p.
|
|
100
|
+
checkpointToken: p.terminal.token,
|
|
101
|
+
checkpointGate: p.terminal.gate,
|
|
101
102
|
};
|
|
102
103
|
};
|
|
103
104
|
const runTurn = async (objective, systemPrompt, modelRef, role, tools, mcp) => {
|
|
@@ -122,16 +123,17 @@ export async function runTeamDiscussion(opts) {
|
|
|
122
123
|
tokens += res.stats.tokens + (res.stats.nested?.tokens ?? 0);
|
|
123
124
|
turns += res.stats.turns + (res.stats.nested?.turns ?? 0);
|
|
124
125
|
costMicroUsd += (res.stats.costMicroUsd ?? 0) + (res.stats.nested?.costMicroUsd ?? 0);
|
|
125
|
-
if (isDurablePause(res.
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
if (isDurablePause(res.terminal)) {
|
|
127
|
+
pendingPause = { ...res, terminal: res.terminal };
|
|
128
|
+
const m = terminalProjection(mapNestedSuspend(res).terminal);
|
|
128
129
|
return { text: `[unavailable: ${m.status}/${m.errorCode}]`, ok: false };
|
|
129
130
|
}
|
|
130
131
|
await opts.runner.sessions.release(res.sessionId).catch(() => undefined);
|
|
131
|
-
if (res.
|
|
132
|
+
if (res.terminal.kind === "completed" && res.result.trim()) {
|
|
132
133
|
return { text: res.result, ok: true };
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
+
const face = terminalProjection(res.terminal);
|
|
136
|
+
last = `[unavailable: ${face.status}${face.errorCode ? `/${face.errorCode}` : ""}${face.errorMessage ? ` — ${face.errorMessage}` : ""}]`;
|
|
135
137
|
}
|
|
136
138
|
return { text: last || "[unavailable]", ok: false };
|
|
137
139
|
};
|
package/dist/agents/verify.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
|
-
import type {
|
|
3
|
-
import type { RunInternals } from "../core/runner/prepare-task.js";
|
|
2
|
+
import type { ResumeTaskConfig, RunInternals, RunnerSelfSeat } from "../core/runner/contracts.js";
|
|
4
3
|
import type { CheckpointGate, CheckpointToken, ResumeOutcome } from "../core/checkpoint-store.js";
|
|
5
|
-
import type { ModelRef, TaskResult, TaskSpec, ToolSpec } from "../core/types.js";
|
|
4
|
+
import type { ModelRef, TaskResult, TaskSpec, TaskStatus, ToolSpec } from "../core/types.js";
|
|
6
5
|
/**
|
|
7
6
|
* Verification gate (developer mode, design/28 §4). An **independent falsification-style verifier** runs after
|
|
8
7
|
* an implementation task and tries to BREAK it — read-only, evidence-required, returning a structured
|
|
@@ -131,7 +130,7 @@ export interface VerifyConfig {
|
|
|
131
130
|
*/
|
|
132
131
|
onVerifierPause?: (info: {
|
|
133
132
|
round: number;
|
|
134
|
-
status:
|
|
133
|
+
status: TaskStatus;
|
|
135
134
|
sessionId: string;
|
|
136
135
|
checkpointToken?: CheckpointToken;
|
|
137
136
|
checkpointGate?: CheckpointGate;
|
|
@@ -200,7 +199,7 @@ export interface VerificationOutcome {
|
|
|
200
199
|
*/
|
|
201
200
|
verifierPause?: {
|
|
202
201
|
/** The verifier run's terminal pause status (`"suspended"` or `"needs_review"`). */
|
|
203
|
-
status:
|
|
202
|
+
status: TaskStatus;
|
|
204
203
|
/** The paused verifier's own session id (independent of the implementation's). */
|
|
205
204
|
sessionId: string;
|
|
206
205
|
/** The resume capability for the verifier's checkpoint. Undefined only if the pause carried none. */
|
|
@@ -242,7 +241,7 @@ export interface VerificationResult extends TaskResult {
|
|
|
242
241
|
* override is given, the verifier uses {@link STATIC_VERIFICATION_PROMPT} (read the diff/results; don't try
|
|
243
242
|
* to execute — that was L2's job), avoiding the "PARTIAL on everything" failure in a read-only sandbox.
|
|
244
243
|
*/
|
|
245
|
-
export declare function verifyCompleted(runner:
|
|
244
|
+
export declare function verifyCompleted(runner: RunnerSelfSeat, result: TaskResult, specBase: ResumeTaskConfig, objective: string, config: VerifyConfig,
|
|
246
245
|
/**
|
|
247
246
|
* The TRUSTED, run-scoped internals channel — the same object `Runner.runTask` takes as its second
|
|
248
247
|
* argument — forwarded to EVERY leg this gate drives under the composition forward contract (identity,
|
|
@@ -303,7 +302,7 @@ internals?: RunInternals): Promise<VerificationResult>;
|
|
|
303
302
|
* failed-with-token (verdict `unverified`); the caller approves and calls {@link resumeWithVerification}
|
|
304
303
|
* to resume AND verify (design/51 P1-b: the durable + HITL + verify integration).
|
|
305
304
|
*/
|
|
306
|
-
export declare function runWithVerification(runner:
|
|
305
|
+
export declare function runWithVerification(runner: RunnerSelfSeat, implSpec: TaskSpec, config?: VerifyConfig,
|
|
307
306
|
/** The trusted run-scoped internals channel, forwarded UNCHANGED to every leg this entry drives —
|
|
308
307
|
* the implementation run, then (on completion) the verifier + fix legs of the gate. Full contract,
|
|
309
308
|
* including the attribution and forwarded-whole rules, on {@link verifyCompleted}. Absent ⇒ every
|
|
@@ -324,7 +323,7 @@ internals?: RunInternals): Promise<VerificationResult>;
|
|
|
324
323
|
* after approval, so a pre-suspend diff would grade stale code. The verifier already verifies the current working
|
|
325
324
|
* tree (verifierObjective), so dropping the stale diff closes the timing window.
|
|
326
325
|
*/
|
|
327
|
-
export declare function resumeWithVerification(runner:
|
|
326
|
+
export declare function resumeWithVerification(runner: RunnerSelfSeat, token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig, objective: string, config?: VerifyConfig,
|
|
328
327
|
/** The trusted run-scoped internals channel, forwarded UNCHANGED to every leg this entry drives —
|
|
329
328
|
* `Runner.resume`'s own internals argument for the RESUMED implementation leg, then (on completion)
|
|
330
329
|
* the verifier + fix legs of the gate. Note that this seat carries the resume-path duty of the same
|
|
@@ -344,7 +343,7 @@ export interface DeveloperTaskConfig extends VerifyConfig {
|
|
|
344
343
|
* Pair with a role map that gives a strong implementation model and cheaper helper/verifier models for
|
|
345
344
|
* "auto model selection" (design/28 §3.2): `roles: { default: strong, subagent: cheap, verifier: strong }`.
|
|
346
345
|
*/
|
|
347
|
-
export declare function runDeveloperTask(runner:
|
|
346
|
+
export declare function runDeveloperTask(runner: RunnerSelfSeat, spec: TaskSpec, config?: DeveloperTaskConfig,
|
|
348
347
|
/** The trusted run-scoped internals channel, forwarded UNCHANGED to every leg this entry drives — in
|
|
349
348
|
* BOTH arms: the lone implementation run when `verify: false`, and the whole gate otherwise. The two
|
|
350
349
|
* arms differ in ONE respect: the gate's door refusal of `isolation` (see {@link verifyCompleted})
|
package/dist/agents/verify.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { CODE_AGENT_PROMPT } from "../prompts/default.js";
|
|
3
3
|
import { releaseSession } from "./session-util.js";
|
|
4
|
-
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
4
|
+
import { mapNestedSuspend, isDurablePause, pauseFamily } from "./suspend-guard.js";
|
|
5
5
|
import { delimitUntrusted, sanitizeUntrustedText } from "../core/untrusted-text.js";
|
|
6
6
|
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
7
7
|
import { createCumulativeStatsTracker } from "./cumulative-stats.js";
|
|
@@ -102,7 +102,7 @@ function verifierObjective(originalObjective, impl, evidence) {
|
|
|
102
102
|
}
|
|
103
103
|
export async function verifyCompleted(runner, result, specBase, objective, config, internals) {
|
|
104
104
|
refuseUnhonorableInternals(internals, "door");
|
|
105
|
-
if (result.
|
|
105
|
+
if (result.terminal.kind !== "completed") {
|
|
106
106
|
return { ...result, verification: { verdict: "unverified", unverifiedReason: "impl_incomplete", rounds: 0, findings: [] } };
|
|
107
107
|
}
|
|
108
108
|
const maxRounds = Number.isFinite(config.maxRounds) ? Math.max(1, Math.floor(config.maxRounds)) : 2;
|
|
@@ -168,10 +168,10 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
168
168
|
}, legInternals());
|
|
169
169
|
try {
|
|
170
170
|
const cost = (v.stats.costMicroUsd ?? 0) + (v.stats.nested?.costMicroUsd ?? 0);
|
|
171
|
-
return { verdict: v.structuredOutput, cost, ...(isDurablePause(v.
|
|
171
|
+
return { verdict: v.structuredOutput, cost, ...(isDurablePause(v.terminal) ? { paused: { ...v, terminal: v.terminal } } : {}) };
|
|
172
172
|
}
|
|
173
173
|
finally {
|
|
174
|
-
if (!isDurablePause(v.
|
|
174
|
+
if (!isDurablePause(v.terminal))
|
|
175
175
|
await releaseSession(runner, v.sessionId);
|
|
176
176
|
}
|
|
177
177
|
};
|
|
@@ -202,18 +202,18 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
202
202
|
findings: [],
|
|
203
203
|
verifierCost: verifierSpend,
|
|
204
204
|
verifierPause: {
|
|
205
|
-
status:
|
|
206
|
-
sessionId:
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
status: pauseFamily(paused.terminal),
|
|
206
|
+
sessionId: paused.sessionId,
|
|
207
|
+
checkpointToken: paused.terminal.token,
|
|
208
|
+
checkpointGate: paused.terminal.gate,
|
|
209
209
|
},
|
|
210
210
|
};
|
|
211
211
|
notifier.notify(() => config.onVerifierPause?.({
|
|
212
212
|
round,
|
|
213
|
-
status: paused.
|
|
213
|
+
status: pauseFamily(paused.terminal),
|
|
214
214
|
sessionId: paused.sessionId,
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
checkpointToken: paused.terminal.token,
|
|
216
|
+
checkpointGate: paused.terminal.gate,
|
|
217
217
|
}), VERIFY_ON_VERIFIER_PAUSE_SITE);
|
|
218
218
|
notifier.notify(() => config.onRound?.({ round, verdict: outcome.verdict, findings: outcome.findings }), VERIFY_ON_ROUND_SITE);
|
|
219
219
|
break;
|
|
@@ -248,10 +248,10 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
248
248
|
}, legInternals());
|
|
249
249
|
spend += (current.stats.costMicroUsd ?? 0) + (current.stats.nested?.costMicroUsd ?? 0);
|
|
250
250
|
implAccount.add(current.stats);
|
|
251
|
-
if (isDurablePause(current.
|
|
251
|
+
if (isDurablePause(current.terminal)) {
|
|
252
252
|
return { ...mapNestedSuspend(carryFrozenPosture(withImplAccount(current))), verification: outcome };
|
|
253
253
|
}
|
|
254
|
-
if (current.
|
|
254
|
+
if (current.terminal.kind !== "completed")
|
|
255
255
|
break;
|
|
256
256
|
}
|
|
257
257
|
return { ...carryFrozenPosture(withImplAccount(current)), verification: outcome };
|
|
@@ -259,8 +259,8 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
259
259
|
export async function runWithVerification(runner, implSpec, config = {}, internals) {
|
|
260
260
|
refuseUnhonorableInternals(internals, "door");
|
|
261
261
|
const result = await runner.runTask(implSpec, internals);
|
|
262
|
-
if (isDurablePause(result.
|
|
263
|
-
return { ...mapNestedSuspend(result), verification: { verdict: "unverified", unverifiedReason: result.
|
|
262
|
+
if (isDurablePause(result.terminal)) {
|
|
263
|
+
return { ...mapNestedSuspend(result), verification: { verdict: "unverified", unverifiedReason: pauseFamily(result.terminal), rounds: 0, findings: [] } };
|
|
264
264
|
}
|
|
265
265
|
refuseUnhonorableInternals(internals, "before_leg");
|
|
266
266
|
return verifyCompleted(runner, result, implSpec, implSpec.objective, config, internals);
|
|
@@ -268,8 +268,8 @@ export async function runWithVerification(runner, implSpec, config = {}, interna
|
|
|
268
268
|
export async function resumeWithVerification(runner, token, outcome, taskConfig, objective, config = {}, internals) {
|
|
269
269
|
refuseUnhonorableInternals(internals, "door");
|
|
270
270
|
const result = await runner.resume(token, outcome, taskConfig, internals);
|
|
271
|
-
if (isDurablePause(result.
|
|
272
|
-
return { ...mapNestedSuspend(result), verification: { verdict: "unverified", unverifiedReason: result.
|
|
271
|
+
if (isDurablePause(result.terminal)) {
|
|
272
|
+
return { ...mapNestedSuspend(result), verification: { verdict: "unverified", unverifiedReason: pauseFamily(result.terminal), rounds: 0, findings: [] } };
|
|
273
273
|
}
|
|
274
274
|
refuseUnhonorableInternals(internals, "before_leg");
|
|
275
275
|
return verifyCompleted(runner, result, taskConfig, objective, config, internals);
|
package/dist/core/a2a.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { A2A_TASK_STATES } from "./a2a-task-state.js";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveProtocolHttpHeaders } from "./mcp.js";
|
|
5
|
+
import { describeHttpTransportFailure } from "./mcp-failure.js";
|
|
5
6
|
import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolName } from "./protocol-naming.js";
|
|
6
7
|
import { A2A_NAMESPACE } from "./protocol-table.js";
|
|
7
8
|
import { delimitUntrusted, inlineUntrusted } from "./untrusted-text.js";
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A delegated AGENT's definition and the skills it may mount: `AgentDefinition` (the agent a subagent
|
|
3
|
+
* tool spawns), `SkillManifest` (a skill's on-disk front matter) and `SkillSpec` (the wired form the
|
|
4
|
+
* runner mounts). Layer 0 vocabulary; `types.ts` re-exports the three, so no consumer's import changes.
|
|
5
|
+
*/
|
|
6
|
+
import type { ThinkingLevel } from "../internal/harness.js";
|
|
7
|
+
import type { ModelRef } from "./model-seat.js";
|
|
8
|
+
import type { TaskSpec } from "./task-spec.js";
|
|
9
|
+
/**
|
|
10
|
+
* A declarative, reusable named sub-agent (design/38 1B). Hand a set to {@link createSubagentTool} and the
|
|
11
|
+
* model picks one (by `whenToUse`) to delegate to; the chosen definition's config builds the child task.
|
|
12
|
+
*
|
|
13
|
+
* **Deployment-side config, NOT user input** — `systemPrompt`/`allowTools` shape what the sub-agent can do
|
|
14
|
+
* and see, so they must be set by the deployment, never taken from an end-user message.
|
|
15
|
+
*/
|
|
16
|
+
export interface AgentDefinition {
|
|
17
|
+
/** Stable identifier + the value the delegation tool's `agent` parameter selects. Must be unique. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Guidance shown to the model for WHEN to pick this agent (listed in the delegation tool's description). */
|
|
20
|
+
whenToUse?: string;
|
|
21
|
+
/**
|
|
22
|
+
* F4 (CC 2.1.198 parity, `whenToUseLean` — 锚 pretty.js:409174 `rbm` + selection `tIl` :453029
|
|
23
|
+
* `(lean && e.whenToUseLean) || e.whenToUse`): a SHORT variant of {@link whenToUse} preferred on
|
|
24
|
+
* lean-prompt surfaces. sema posture: the engine runs the CC lean-prompt arm wholesale (web.ts
|
|
25
|
+
* lean-card precedent), so roster listings/descriptions prefer this text whenever present;
|
|
26
|
+
* {@link whenToUse} remains the fallback. Optional — a single-text agent behaves exactly as before.
|
|
27
|
+
*/
|
|
28
|
+
whenToUseLean?: string;
|
|
29
|
+
/** Tool allowlist over the delegation pool (`["*"]` = all). Replaces (does not merge with) the tool's
|
|
30
|
+
* own `allowTools`/`denyTools` when this agent is selected. F1 (CC parity): unlike the TOOL-level
|
|
31
|
+
* allow/deny (which never filters the nested delegation tool — that stays `maxDepth`-governed), a
|
|
32
|
+
* PER-AGENT allow/deny ALSO governs the nested delegation tool: an agent whose allow/deny excludes
|
|
33
|
+
* the delegation tool's name cannot re-delegate (CC Explore/Plan `disallowedTools` include Agent,
|
|
34
|
+
* pretty.js:409189/486948). */
|
|
35
|
+
allowTools?: string[];
|
|
36
|
+
/** Tool denylist (a deny always wins). See {@link allowTools} for the per-agent delegation-tool note. */
|
|
37
|
+
denyTools?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* F7 (CC 2.1.198 parity, per-agent `skills` — 锚 pretty.js:422126 `e.skills ?? []`, GA/ungated in 198:
|
|
40
|
+
* CC preloads each named skill's prompt content into the child's opening messages, warning on misses).
|
|
41
|
+
* sema shape: full {@link SkillSpec} objects (sema has no ambient skill registry to resolve bare names
|
|
42
|
+
* against) threaded into the chosen child's `TaskSpec.skills` — disclosed in its `<skills>` block +
|
|
43
|
+
* invocable via the `skill` tool. Deployment-trusted, same trust tier as `systemPrompt`.
|
|
44
|
+
*/
|
|
45
|
+
skills?: SkillSpec[];
|
|
46
|
+
/**
|
|
47
|
+
* F7 (CC 2.1.198 parity, per-agent `background` — 锚 pretty.js:453611 `H.background===!0` OR-ed into
|
|
48
|
+
* the background predicate, GA/ungated): `true` = this agent ALWAYS launches on the background lane
|
|
49
|
+
* (an omitted `run_in_background` no longer means sync, and the per-call parameter cannot force sync —
|
|
50
|
+
* CC-same OR semantics). Requires the delegation tool's `background` surface; without one the child
|
|
51
|
+
* runs synchronously (CC-same quiet fallback — CC's env kill-switch arm, :453611 `&& !sJt`).
|
|
52
|
+
*/
|
|
53
|
+
background?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* F7 (CC 2.1.198 parity, per-agent `isolation` — 锚 pretty.js:453599 `z = a ?? H.isolation`, GA):
|
|
56
|
+
* default isolation for this agent; an explicit per-call `isolation` argument wins. sema supports
|
|
57
|
+
* `"worktree"` (CC's `"remote"` arm is a claude.ai-session concern, not ported).
|
|
58
|
+
*/
|
|
59
|
+
isolation?: "worktree";
|
|
60
|
+
/** Model for this agent. **Omit to INHERIT the caller's current model** (design/38 1B finding 7) — the
|
|
61
|
+
* recommended default; a concrete `ModelRef` pins it instead. */
|
|
62
|
+
model?: ModelRef;
|
|
63
|
+
/** Thinking level for this agent. **Omit to INHERIT the caller's current thinking level** (the model
|
|
64
|
+
* companion above — a mid-run degrade/boost on the parent carries into new children); a concrete
|
|
65
|
+
* level pins it instead. */
|
|
66
|
+
thinking?: ThinkingLevel;
|
|
67
|
+
/** System prompt / persona for this agent. Falls back to the delegation tool's `systemPrompt`. */
|
|
68
|
+
systemPrompt?: string;
|
|
69
|
+
/** Per-agent turn cap. Falls back to the delegation tool's `limits.maxTurns`. Must be POSITIVE: a
|
|
70
|
+
* non-positive/non-finite value is ignored (CLS-A-3) rather than folded in, because downstream an
|
|
71
|
+
* explicit `0` DISABLES the turn cap — the opposite of the tightening this field expresses. */
|
|
72
|
+
maxTurns?: number;
|
|
73
|
+
/** Long-term memory scope for this agent (same shape as {@link TaskSpec.memory}). */
|
|
74
|
+
memory?: TaskSpec["memory"];
|
|
75
|
+
/**
|
|
76
|
+
* This agent's own {@link TaskSpec.memoryPersistenceCapable} declaration — the same three-state
|
|
77
|
+
* statement, made per definition: an agent whose closure tools persist memory declares `true`
|
|
78
|
+
* (suppressing the read-only-memory disclosure no roster inference can clear), one whose roster
|
|
79
|
+
* looks write-capable but reaches no durable store declares `false` (forcing the disclosure).
|
|
80
|
+
* Arbitration with the spawning run's declaration: a parent's explicit `false` is a FLOOR and wins
|
|
81
|
+
* over a definition `true` (the disclosure is about the deployment's storage, which choosing this
|
|
82
|
+
* agent does not change); otherwise a declared definition value wins over the parent's; absent
|
|
83
|
+
* both, the child spec carries no value and the child's own roster inference decides.
|
|
84
|
+
* On the workflow lane (`agent(…, {agentType})`) the definition value fills an ABSENT governed-spec
|
|
85
|
+
* value only — a base/spec declaration of either polarity wins (the lane's spec-pinned-fields rule).
|
|
86
|
+
*/
|
|
87
|
+
memoryPersistenceCapable?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Observer agents (CC 2.1.206 parity — 逐字锚 docs/CC206-OBSERVER-ANCHORS-2026-07-11.md, schema
|
|
90
|
+
* 面 C @28385708): "Agent type auto-spawned as a background observer whenever this agent runs.
|
|
91
|
+
* The observer receives read-only activity digests and reports via the ObserverReport tool; it
|
|
92
|
+
* never participates in the task." Resolution is warn-and-unobserved on an unknown type and
|
|
93
|
+
* refuses chaining (an observer's own `observer` declaration is ignored). Gated by
|
|
94
|
+
* {@link RuntimeCaps.allowObservers} — EXPLICIT opt-in, default OFF (the sema seat of CC's
|
|
95
|
+
* default-unset env `CLAUDE_CODE_EXPERIMENTAL_OBSERVER_AGENTS` + default-true statsig pair).
|
|
96
|
+
*/
|
|
97
|
+
observer?: string;
|
|
98
|
+
/**
|
|
99
|
+
* CC 面 C: "Supplemental postamble appended (after the harness-owned default) to each activity
|
|
100
|
+
* digest sent to the observer. Blank values are ignored."
|
|
101
|
+
*/
|
|
102
|
+
observerMessage?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A per-skill runtime capability manifest (design/77 §3 Gate-3). When present, loading the skill
|
|
106
|
+
* SCOPES the rest of the invocation to ONLY these tools/paths via runtime deny-narrowing: a tool call
|
|
107
|
+
* not in `allowTools` (intersected across nested skill frames) is DENIED, and a write whose canonical
|
|
108
|
+
* target falls outside `allowPaths` is DENIED — regardless of how permissive the task policy is.
|
|
109
|
+
*
|
|
110
|
+
* 🔴 **Safe-by-construction:** a manifest can only SUBTRACT capability. The narrowing composes via the
|
|
111
|
+
* `deny > ask > allow` fold (`combinePolicies`) where `deny` short-circuits, so a manifest can never
|
|
112
|
+
* *grant* a tool the task policy withheld — whoever sets a manifest can only restrict further.
|
|
113
|
+
* Worst case (the model never pops the frame) = no narrowing, never widening — still ⊆ task policy.
|
|
114
|
+
*
|
|
115
|
+
* The manifest is CALLER-provided here (same trust level as the rest of `SkillSpec`); deriving it
|
|
116
|
+
* automatically (the auto-promote pipeline) is a separate, deferred concern (design/77 §5).
|
|
117
|
+
*/
|
|
118
|
+
export interface SkillManifest {
|
|
119
|
+
/** The ONLY tool names the invocation may call while this skill's frame is active. */
|
|
120
|
+
allowTools: string[];
|
|
121
|
+
/**
|
|
122
|
+
* Optional path allowlist for WRITE tools (`write_file`/`edit_file`): a write whose canonicalized
|
|
123
|
+
* target is not inside one of these (path-segment-aware) prefixes is DENIED. Omit = no path narrowing
|
|
124
|
+
* (only `allowTools` applies). Reads are never path-narrowed (this gate restricts capability, not visibility).
|
|
125
|
+
*
|
|
126
|
+
* 🔴 **Fail-closed (MAJOR-2):** `allowPaths` can only confine tools whose write target is a parseable `path`
|
|
127
|
+
* argument (`write_file`/`edit_file`). While an `allowPaths` constraint is active, any OTHER mutating
|
|
128
|
+
* (effect:write/idempotent) allowlisted tool — notably `bash`, whose command can write anywhere — is DENIED
|
|
129
|
+
* outright, because its target cannot be path-confined. So a manifest that lists `bash` together with
|
|
130
|
+
* `allowPaths` does NOT let bash write/egress anywhere: bash is denied while the path constraint is active.
|
|
131
|
+
* Confine writes to specific paths via `write_file`/`edit_file` only.
|
|
132
|
+
*/
|
|
133
|
+
allowPaths?: string[];
|
|
134
|
+
/** Reserved (design/77 §3): whether egress is permitted under this manifest. Carried through; v1 does
|
|
135
|
+
* not act on it beyond the allowTools intersection (egress tools must also be in `allowTools`). */
|
|
136
|
+
egress?: boolean;
|
|
137
|
+
/** Stable lineage id of the artifact this manifest came from — used in deny reasons + as the scope key. */
|
|
138
|
+
lineageId: string;
|
|
139
|
+
}
|
|
140
|
+
/** A skill made available to this task (passed as an object; not loaded from disk). */
|
|
141
|
+
export interface SkillSpec {
|
|
142
|
+
name: string;
|
|
143
|
+
description: string;
|
|
144
|
+
content: string;
|
|
145
|
+
/** Optional Gate-3 runtime capability manifest (design/77 §3) — see {@link SkillManifest}. */
|
|
146
|
+
manifest?: SkillManifest;
|
|
147
|
+
/**
|
|
148
|
+
* Optional attachments bundled with the skill (additive) — the CC bundled-skill shape (a SKILL.md whose
|
|
149
|
+
* body references sibling files by relative path) translated to sema's brain-outside posture, where the
|
|
150
|
+
* ruling is: **core never writes to disk.** CC extracts a bundle and lets the body reference files on
|
|
151
|
+
* the filesystem; sema core has no filesystem authority of its own, so materializing attachments into a
|
|
152
|
+
* working directory (making `Read`-by-path work) is the DEPLOYMENT's half. What core owns is exactly two
|
|
153
|
+
* faces, both bounded:
|
|
154
|
+
* 1. **disclosure** — each attachment's relative `path` is listed on the skill's `<skills>`-block line
|
|
155
|
+
* (metadata only, capped like the rest of the block), so the model knows the material exists before
|
|
156
|
+
* it loads the skill;
|
|
157
|
+
* 2. **delivery** — a `Skill` invoke appends an `## Attachments` section after the body (which is
|
|
158
|
+
* itself delivered untruncated): each file as `### <path>` + its content, under a SEPARATE total
|
|
159
|
+
* cap (`SKILL_ATTACHMENTS_MAX_CHARS`, 50k chars — an attachments-face budget; truncation/omission
|
|
160
|
+
* is always disclosed in-band). Without deployment-side materialization this inline delivery is the
|
|
161
|
+
* attachment's ONLY reachable face — that is the deliberate minimal shape, not an oversight.
|
|
162
|
+
*
|
|
163
|
+
* `path` is a relative, display-oriented identifier (how the body refers to the file); core never
|
|
164
|
+
* resolves it against a filesystem. `content` carries the SAME trust contract as {@link content}: skill
|
|
165
|
+
* material is CALLER-trusted instructions by design (see the skills module note) — a caller forwarding
|
|
166
|
+
* end-user skills is responsible for vetting attachments exactly like the body.
|
|
167
|
+
*/
|
|
168
|
+
files?: Array<{
|
|
169
|
+
path: string;
|
|
170
|
+
content: string;
|
|
171
|
+
}>;
|
|
172
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|