@a5c-ai/babysitter-sdk 6.0.2 → 6.0.3-staging.032e07a1ffa0

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.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Milestone C — ATTRIBUTION: correlation metadata on the effect result (AC-14, §6.3).
3
+ *
4
+ * Records `{ sessionId, modelId, attestationRequestId }` (and, where present,
5
+ * `toolCallId` + the attestation `producer` marker) on the effect-result metadata
6
+ * (serializer `StoredTaskResult.metadata`) so an auditor can correlate a resolved
7
+ * effect back to the exact model turn — and the exact producer (authoritative proxy
8
+ * vs. correlation-grade in-process) — that authorized it.
9
+ *
10
+ * The proxy attestation sidecar is keyed by request id (AC-14) and indexed by
11
+ * tool-call id (AC-16); recording those ids here threads the correlation through to
12
+ * the persisted effect result.
13
+ */
14
+ /**
15
+ * The producer that emitted the model-decision attestation this effect correlates
16
+ * to: the AUTHORITATIVE out-of-agent `'proxy'` variant, or the correlation-grade
17
+ * in-agent `'in-process'` variant (AC-39). Distinguishing them in the audit record
18
+ * is load-bearing: only the proxy variant is authoritative.
19
+ */
20
+ export type AttestationProducer = "proxy" | "in-process";
21
+ export interface AttestationAttribution {
22
+ /** The session that produced the model turn. */
23
+ sessionId: string;
24
+ /** The model id that drove the turn (`endpoint.model`). */
25
+ modelId: string;
26
+ /** The proxy sidecar request id keying the attestation (AC-14). */
27
+ attestationRequestId: string;
28
+ /** The bound tool-call id, when the effect correlates to a specific call (AC-16). */
29
+ toolCallId?: string;
30
+ /** Which producer signed the attestation (AC-39 correlation-grade distinction). */
31
+ producer?: AttestationProducer;
32
+ }
33
+ /**
34
+ * The shape recorded on `StoredTaskResult.metadata`. A plain JSON record (no
35
+ * undefined values) so it survives `stableClone` + serialization + disk round-trip.
36
+ */
37
+ export interface AttestationAttributionMetadata {
38
+ sessionId: string;
39
+ modelId: string;
40
+ attestationRequestId: string;
41
+ toolCallId?: string;
42
+ producer?: AttestationProducer;
43
+ }
44
+ /**
45
+ * Build the correlation metadata block for an effect result from an attestation
46
+ * attribution. Only defined fields are included so the persisted metadata stays a
47
+ * clean JSON record.
48
+ */
49
+ export declare function buildAttestationAttributionMetadata(attribution: AttestationAttribution): AttestationAttributionMetadata;
50
+ //# sourceMappingURL=attestation-attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation-attribution.d.ts","sourceRoot":"","sources":["../../src/breakpoints/attestation-attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,YAAY,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,mCAAmC,CACjD,WAAW,EAAE,sBAAsB,GAClC,8BAA8B,CAahC"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Milestone C — ATTRIBUTION: correlation metadata on the effect result (AC-14, §6.3).
4
+ *
5
+ * Records `{ sessionId, modelId, attestationRequestId }` (and, where present,
6
+ * `toolCallId` + the attestation `producer` marker) on the effect-result metadata
7
+ * (serializer `StoredTaskResult.metadata`) so an auditor can correlate a resolved
8
+ * effect back to the exact model turn — and the exact producer (authoritative proxy
9
+ * vs. correlation-grade in-process) — that authorized it.
10
+ *
11
+ * The proxy attestation sidecar is keyed by request id (AC-14) and indexed by
12
+ * tool-call id (AC-16); recording those ids here threads the correlation through to
13
+ * the persisted effect result.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.buildAttestationAttributionMetadata = buildAttestationAttributionMetadata;
17
+ /**
18
+ * Build the correlation metadata block for an effect result from an attestation
19
+ * attribution. Only defined fields are included so the persisted metadata stays a
20
+ * clean JSON record.
21
+ */
22
+ function buildAttestationAttributionMetadata(attribution) {
23
+ const meta = {
24
+ sessionId: attribution.sessionId,
25
+ modelId: attribution.modelId,
26
+ attestationRequestId: attribution.attestationRequestId,
27
+ };
28
+ if (typeof attribution.toolCallId === "string" && attribution.toolCallId.length > 0) {
29
+ meta.toolCallId = attribution.toolCallId;
30
+ }
31
+ if (attribution.producer === "proxy" || attribution.producer === "in-process") {
32
+ meta.producer = attribution.producer;
33
+ }
34
+ return meta;
35
+ }
@@ -0,0 +1,31 @@
1
+ export interface SignedBreakpointEnforcementConfig {
2
+ /** Whether a valid human signature is REQUIRED for this breakpoint answer. */
3
+ signatureRequired: boolean;
4
+ /** Directory of trusted public keys, forwarded to proven `verifyAnswer`. */
5
+ trustedKeysDir?: string;
6
+ /**
7
+ * The set of trusted `human` root fingerprints (AC-35), sourced ONLY from the
8
+ * integrity-protected, manifest-verified trust-roots config (never task.json). When
9
+ * provided (non-undefined), the answer's raw signature MUST resolve to a fingerprint
10
+ * in THIS set, else the answer is rejected — so a key an attacker drops into a run-dir
11
+ * directory is not trusted even if the raw signature otherwise verifies (defect 2).
12
+ * When `undefined`, no membership check is applied (legacy pure-decision callers /
13
+ * unit tests that assert only the raw-verify + completeness behavior).
14
+ */
15
+ trustedHumanFingerprints?: string[];
16
+ }
17
+ export interface SignedBreakpointEnforcementDecision {
18
+ /** True iff the answer may be committed (resolve the effect); false → reject. */
19
+ accepted: boolean;
20
+ /** Human-readable reason (always set when rejected; fail-closed diagnostics). */
21
+ reason?: string;
22
+ /** The resolved human fingerprint, on a verified accept (for attribution). */
23
+ publicKeyFingerprint?: string;
24
+ }
25
+ /**
26
+ * Decide whether a breakpoint answer may be committed under the signed-breakpoint
27
+ * enforcement policy. Fail-closed for every adversarial / error case when a signature
28
+ * is required.
29
+ */
30
+ export declare function enforceSignedBreakpointAnswer(answer: Record<string, unknown>, config: SignedBreakpointEnforcementConfig): Promise<SignedBreakpointEnforcementDecision>;
31
+ //# sourceMappingURL=enforce-signed-breakpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce-signed-breakpoint.d.ts","sourceRoot":"","sources":["../../src/breakpoints/enforce-signed-breakpoint.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,iCAAiC;IAChD,8EAA8E;IAC9E,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,mCAAmC;IAClD,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAkBD;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,iCAAiC,GACxC,OAAO,CAAC,mCAAmC,CAAC,CAqE9C"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceSignedBreakpointAnswer = enforceSignedBreakpointAnswer;
4
+ /**
5
+ * Milestone C — ENFORCED signed human breakpoints (SDK).
6
+ *
7
+ * The enforcement DECISION for a breakpoint answer when the policy requires a
8
+ * signature. Wired into the task_post / commitEffectResult path (see
9
+ * `runtime/commitEffectResult.ts`) so that:
10
+ * - when a signature is REQUIRED, an UNSIGNED / invalid / wrong-key / tampered /
11
+ * unsigned-`approved` answer is REJECTED fail-closed (the commit is refused, the
12
+ * effect stays PENDING, no EFFECT_RESOLVED is emitted); a validly-signed answer is
13
+ * ACCEPTED (AC-28, AC-4, §5 step 7, AC-48);
14
+ * - when a signature is NOT required (the pre-Milestone-C default), an unsigned
15
+ * answer is UNAFFECTED — existing runs do not break (back-compat).
16
+ *
17
+ * This is a PURE decision function (no filesystem / run wiring) so it is unit-testable;
18
+ * it reuses the shipped `proven-verification.ts` (which does the optional dynamic import
19
+ * of `@a5c-ai/tasks-adapter/proven` and the raw signature check).
20
+ *
21
+ * OVERRIDING RULE: fail closed. When a signature is required, ANY error during
22
+ * verification — verifier throws, the proven module is not installed, an exception in
23
+ * the completeness check — is a REJECTION, never a silent accept. No fallbacks.
24
+ */
25
+ const proven_verification_1 = require("./proven-verification");
26
+ /**
27
+ * The security-critical fields that MUST be inside the human-signed set for an
28
+ * `approved` bit to be honored as a human approval (AC-48 / AC-2 completeness rule
29
+ * applied to the legacy proven answer). An answer whose `signedFields` omits any of
30
+ * these — or names one that is absent from the answer — is treated as UNSIGNED for
31
+ * that field and rejected, even if the raw signature otherwise verifies.
32
+ */
33
+ const REQUIRED_SIGNED_FIELDS = ["breakpointId", "approved", "responderId"];
34
+ /**
35
+ * AC-48 completeness: every REQUIRED_SIGNED_FIELDS entry must appear in the answer's
36
+ * `signedFields` AND be present on the answer object. Returns a deny-reason string, or
37
+ * null when the answer's signed set is complete.
38
+ */
39
+ function completenessDenyReason(answer) {
40
+ const signedFields = answer.signedFields;
41
+ if (!Array.isArray(signedFields))
42
+ return "signedFields is not an array";
43
+ const signed = new Set(signedFields.map((f) => String(f)));
44
+ for (const field of REQUIRED_SIGNED_FIELDS) {
45
+ if (!signed.has(field))
46
+ return `required field not signed: ${field}`;
47
+ if (!(field in answer))
48
+ return `signed field absent from answer: ${field}`;
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Decide whether a breakpoint answer may be committed under the signed-breakpoint
54
+ * enforcement policy. Fail-closed for every adversarial / error case when a signature
55
+ * is required.
56
+ */
57
+ async function enforceSignedBreakpointAnswer(answer, config) {
58
+ // Back-compat: no signature required → the answer is unaffected (accept).
59
+ if (!config || config.signatureRequired !== true) {
60
+ return { accepted: true };
61
+ }
62
+ try {
63
+ // An unsigned answer (missing signature fields) can NEVER satisfy a signature
64
+ // requirement — reject before touching the verifier (AC-28 spoofed approval).
65
+ if (!(0, proven_verification_1.hasSignatureFields)(answer)) {
66
+ return { accepted: false, reason: "signature required but answer is unsigned" };
67
+ }
68
+ // AC-48: the `approved`/identity/target fields MUST be inside the signed set, else
69
+ // the `approved` bit is unsigned and must not be honored — reject.
70
+ const completenessDeny = completenessDenyReason(answer);
71
+ if (completenessDeny) {
72
+ return { accepted: false, reason: `signed set incomplete: ${completenessDeny}` };
73
+ }
74
+ // Raw signature verification via the proven subsystem (optional dynamic import).
75
+ // Any thrown exception / module-not-found is caught below → reject (fail closed).
76
+ const verification = await (0, proven_verification_1.verifyBreakpointResult)(answer, {
77
+ enabled: true,
78
+ trustedKeysDir: config.trustedKeysDir,
79
+ });
80
+ if (!verification.verified) {
81
+ return {
82
+ accepted: false,
83
+ reason: verification.reason ?? "signature verification failed",
84
+ };
85
+ }
86
+ // AC-35 (defect 2): WHICH keys are trusted must come from the integrity-protected
87
+ // trust-roots config, NOT from wherever proven happened to resolve a key. Even
88
+ // though the raw signature verified, require the resolving fingerprint to be a
89
+ // trusted `human` root — so a self-signed answer using a key the attacker dropped
90
+ // into a run-dir directory is rejected.
91
+ if (config.trustedHumanFingerprints !== undefined) {
92
+ const resolvedFp = verification.verificationResult?.publicKeyFingerprint;
93
+ if (typeof resolvedFp !== "string" || resolvedFp.length === 0) {
94
+ return { accepted: false, reason: "signer fingerprint could not be resolved" };
95
+ }
96
+ if (!config.trustedHumanFingerprints.includes(resolvedFp)) {
97
+ return {
98
+ accepted: false,
99
+ reason: `signer ${resolvedFp} is not a trusted human root`,
100
+ };
101
+ }
102
+ }
103
+ // The signature verified AND the security-critical fields were signed; only honor
104
+ // an actual approval (a signed rejection never yields an accepted approval).
105
+ if (answer.approved !== true) {
106
+ return { accepted: false, reason: "answer is not an approval (approved !== true)" };
107
+ }
108
+ return {
109
+ accepted: true,
110
+ publicKeyFingerprint: verification.verificationResult?.publicKeyFingerprint,
111
+ };
112
+ }
113
+ catch (err) {
114
+ // Fail closed: never a pass-through on error.
115
+ return {
116
+ accepted: false,
117
+ reason: `enforcement error: ${err?.message ?? String(err)}`,
118
+ };
119
+ }
120
+ }
@@ -0,0 +1,38 @@
1
+ export interface TrustedBreakpointPolicy {
2
+ /**
3
+ * True when a config anchor is pinned (enforcement is a live requirement for this
4
+ * deployment). When false, enforcement is inactive (back-compat) and the caller
5
+ * leaves unsigned breakpoint answers unaffected.
6
+ */
7
+ enforcementActive: boolean;
8
+ /**
9
+ * Set ONLY when `enforcementActive` is true and the trust anchor could NOT be
10
+ * established (missing/edited/corrupt/untrusted config). The caller MUST treat every
11
+ * breakpoint that the (trusted, when readable) policy marks as requiring a signature
12
+ * as a REJECT — the requirement cannot be trustworthily determined ⇒ deny.
13
+ */
14
+ configError?: string;
15
+ /**
16
+ * Decide whether the given breakpoint requires a signature. When the trusted policy is
17
+ * unreadable this is a FAIL-CLOSED default (returns true) so a config error denies.
18
+ */
19
+ requiresSignature(breakpointId: string | undefined): boolean;
20
+ /** The trusted `human` root fingerprints from the manifest-verified trust-roots (AC-35). */
21
+ trustedHumanFingerprints: string[];
22
+ /**
23
+ * The trust-anchored base dir handed to proven `verifyAnswer` for raw-signature
24
+ * checking. This is the manifest-verified `.policy` dir's key store — NOT any
25
+ * task.json-supplied directory (defect 2). The gate ALSO checks the resolved
26
+ * fingerprint is in `trustedHumanFingerprints`, so a raw verify against a stray key
27
+ * is still rejected.
28
+ */
29
+ trustedKeysBaseDir?: string;
30
+ }
31
+ /**
32
+ * Load + verify the trust-anchored breakpoint policy for a run/project.
33
+ *
34
+ * `projectRoot` is the anchor for the default `.policy` location (typically the run's
35
+ * project root, resolvable from the run dir). It is NOT the per-effect task dir.
36
+ */
37
+ export declare function loadTrustedBreakpointPolicy(projectRoot: string): Promise<TrustedBreakpointPolicy>;
38
+ //# sourceMappingURL=trusted-breakpoint-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trusted-breakpoint-policy.d.ts","sourceRoot":"","sources":["../../src/breakpoints/trusted-breakpoint-policy.ts"],"names":[],"mappings":"AA8FA,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,iBAAiB,EAAE,OAAO,CAAC;IAC3B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAC7D,4FAA4F;IAC5F,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA+BD;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,uBAAuB,CAAC,CA+HlC"}
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.loadTrustedBreakpointPolicy = loadTrustedBreakpointPolicy;
40
+ /**
41
+ * Milestone C (AC-7 / AC-28 / AC-35 / AC-36 / AC-37 / AC-46 / AC-47 / AC-48) —
42
+ * the TRUST-ANCHORED source of the signed-breakpoint requirement + trusted human roots.
43
+ *
44
+ * ─────────────────────────────────────────────────────────────────────────────
45
+ * WHY THIS EXISTS (the two closed defects).
46
+ *
47
+ * The prior wiring sourced BOTH the `signatureRequired` flag AND the `trustedKeysDir`
48
+ * from the per-effect `tasks/<effectId>/task.json` — a file the (in-scope, §11 AC-33)
49
+ * workspace-writable agent can freely DELETE, CORRUPT, or rewrite. That gave the
50
+ * adversary two trivial escapes:
51
+ * (defect 1) delete/corrupt task.json so the gate could not read `signatureRequired`
52
+ * and FAILED OPEN — an unsigned `{approved:true}` then committed; and
53
+ * (defect 2) point `trustedKeysDir` at a directory holding the AGENT's own public
54
+ * key, so a self-signed "human approval" verified.
55
+ *
56
+ * This module makes BOTH un-attacker-controllable by sourcing them ONLY from the
57
+ * out-of-agent, integrity-protected trust anchor the design §10 defines:
58
+ *
59
+ * pinned config root FP + pinned min-epoch floor (OFF the workspace — env/deploy)
60
+ * → `.policy/config-manifest.json` (one signed `SignedEnvelope<ConfigManifestPayload>`,
61
+ * AC-36/AC-46, verified by `verifyConfigManifest` against the pinned root)
62
+ * → covers `.policy/trust-roots.json` (the trusted `human` roots, AC-35) and
63
+ * `.policy/breakpoint-policy.json` (which breakpoints require a signature).
64
+ *
65
+ * A config file whose sha256 is not the one bound in the current-epoch signed manifest,
66
+ * or a manifest signed by anything other than the pinned config root, or an epoch below
67
+ * the pinned floor, is REJECTED (deny-all) — so editing any of these files on the
68
+ * workspace cannot forge a lower requirement or inject a trusted key. The trust source
69
+ * is therefore the signed manifest, NEVER the mutable per-effect task.json.
70
+ *
71
+ * FAIL CLOSED. When the pinned config anchor is present (a policy-enforced deployment),
72
+ * ANY failure to establish trust — missing/edited/corrupt manifest or config file, wrong
73
+ * signer, below-floor epoch, unreadable trust-roots — is surfaced as
74
+ * `{ enforcementActive: true, configError: ... }`, and the caller MUST reject the post
75
+ * for any breakpoint the trusted policy marks as requiring a signature (the requirement
76
+ * cannot be trustworthily determined ⇒ deny). It is NEVER silently skipped.
77
+ *
78
+ * BACK-COMPAT. When NO config anchor is pinned (`POLICY_CONFIG_ROOT_FP` unset — the
79
+ * pre-Milestone-C default deployment), enforcement is inactive and unsigned breakpoint
80
+ * answers are unaffected, exactly as before. Opting in is a deploy operation (pinning the
81
+ * off-workspace root + floor), which an in-workspace agent cannot do.
82
+ */
83
+ const node_fs_1 = require("node:fs");
84
+ const node_path_1 = __importDefault(require("node:path"));
85
+ // ── Config anchor pins (OFF the workspace: env / deploy image, AC-37/AC-47) ──────
86
+ const CONFIG_ROOT_FP_ENV = "POLICY_CONFIG_ROOT_FP";
87
+ const CONFIG_MIN_EPOCH_ENV = "POLICY_CONFIG_MIN_EPOCH";
88
+ /** The config-root PUBLIC key material (PEM/DER-base64), or a path to it. Pinned off-workspace. */
89
+ const CONFIG_ROOT_KEY_ENV = "POLICY_CONFIG_ROOT_KEY";
90
+ const CONFIG_ROOT_KEY_PATH_ENV = "POLICY_CONFIG_ROOT_KEY_PATH";
91
+ /** The `.policy` config directory. Off-workspace by default; env override for tests/deploys. */
92
+ const CONFIG_DIR_ENV = "POLICY_CONFIG_DIR";
93
+ const MANIFEST_FILE = "config-manifest.json";
94
+ const TRUST_ROOTS_FILE = "trust-roots.json";
95
+ const BREAKPOINT_POLICY_FILE = "breakpoint-policy.json";
96
+ function readEnv(name) {
97
+ const v = process.env[name];
98
+ return typeof v === "string" && v.length > 0 ? v : undefined;
99
+ }
100
+ /** Resolve the `.policy` config dir: explicit env pin, else `<projectRoot>/.policy`. */
101
+ function resolveConfigDir(projectRoot) {
102
+ return readEnv(CONFIG_DIR_ENV) ?? node_path_1.default.join(projectRoot, ".policy");
103
+ }
104
+ async function readFileOrUndefined(file) {
105
+ try {
106
+ return await node_fs_1.promises.readFile(file, "utf8");
107
+ }
108
+ catch (err) {
109
+ const e = err;
110
+ if (e.code === "ENOENT")
111
+ return undefined;
112
+ throw err;
113
+ }
114
+ }
115
+ /**
116
+ * Load + verify the trust-anchored breakpoint policy for a run/project.
117
+ *
118
+ * `projectRoot` is the anchor for the default `.policy` location (typically the run's
119
+ * project root, resolvable from the run dir). It is NOT the per-effect task dir.
120
+ */
121
+ async function loadTrustedBreakpointPolicy(projectRoot) {
122
+ const pinnedConfigRootFp = readEnv(CONFIG_ROOT_FP_ENV);
123
+ // No off-workspace anchor pinned → enforcement inactive (pre-Milestone-C default).
124
+ if (!pinnedConfigRootFp) {
125
+ return {
126
+ enforcementActive: false,
127
+ requiresSignature: () => false,
128
+ trustedHumanFingerprints: [],
129
+ };
130
+ }
131
+ // Anchor IS pinned: any failure below is a fail-closed config error (deny), never skip.
132
+ const denied = (reason) => ({
133
+ enforcementActive: true,
134
+ configError: reason,
135
+ // Fail closed: with no trusted policy readable, treat every breakpoint as requiring
136
+ // a signature so the caller denies (a config error must never let an unsigned answer
137
+ // through).
138
+ requiresSignature: () => true,
139
+ trustedHumanFingerprints: [],
140
+ });
141
+ try {
142
+ const minEpoch = Number(readEnv(CONFIG_MIN_EPOCH_ENV) ?? "1");
143
+ if (!Number.isFinite(minEpoch))
144
+ return denied("POLICY_CONFIG_MIN_EPOCH is not a number");
145
+ // Resolve the config-root PUBLIC key material from the off-workspace pin.
146
+ let configRootKey = readEnv(CONFIG_ROOT_KEY_ENV);
147
+ if (!configRootKey) {
148
+ const keyPath = readEnv(CONFIG_ROOT_KEY_PATH_ENV);
149
+ if (keyPath)
150
+ configRootKey = await readFileOrUndefined(keyPath);
151
+ }
152
+ if (!configRootKey)
153
+ return denied("config-root key material is not pinned");
154
+ const configDir = resolveConfigDir(projectRoot);
155
+ const manifestRaw = await readFileOrUndefined(node_path_1.default.join(configDir, MANIFEST_FILE));
156
+ if (manifestRaw === undefined)
157
+ return denied(`missing ${MANIFEST_FILE}`);
158
+ let manifest;
159
+ try {
160
+ manifest = JSON.parse(manifestRaw);
161
+ }
162
+ catch {
163
+ return denied(`corrupt ${MANIFEST_FILE}`);
164
+ }
165
+ // Read every config file the manifest could cover; the verifier requires each
166
+ // on-disk file to be listed in the manifest with a matching hash (AC-36 step 3).
167
+ const files = {};
168
+ for (const name of [TRUST_ROOTS_FILE, BREAKPOINT_POLICY_FILE]) {
169
+ const bytes = await readFileOrUndefined(node_path_1.default.join(configDir, name));
170
+ if (bytes !== undefined)
171
+ files[name] = bytes;
172
+ }
173
+ if (files[TRUST_ROOTS_FILE] === undefined)
174
+ return denied(`missing ${TRUST_ROOTS_FILE}`);
175
+ // Dynamically import the ESM policy-adapter (same pattern as proven-verification.ts).
176
+ let adapter;
177
+ try {
178
+ const mod = "@a5c-ai/policy-adapter";
179
+ adapter = (await Promise.resolve(`${mod}`).then(s => __importStar(require(s))));
180
+ }
181
+ catch (err) {
182
+ // The trust core is REQUIRED to establish the anchor; if it cannot load, deny.
183
+ return denied(`policy-adapter unavailable: ${err?.message ?? String(err)}`);
184
+ }
185
+ if (typeof adapter.verifyConfigManifest !== "function") {
186
+ return denied("policy-adapter.verifyConfigManifest missing");
187
+ }
188
+ const configRoot = {
189
+ fingerprint: pinnedConfigRootFp,
190
+ kind: "config",
191
+ publicKey: configRootKey,
192
+ label: "pinned-config-root",
193
+ };
194
+ const verification = adapter.verifyConfigManifest(manifest, {
195
+ pinnedConfigRootFp,
196
+ configRoot,
197
+ minEpoch,
198
+ files,
199
+ });
200
+ if (!verification.valid) {
201
+ return denied(`config manifest not trusted: ${verification.reason ?? "invalid"}`);
202
+ }
203
+ // The manifest verified: trust-roots.json + breakpoint-policy.json are now
204
+ // integrity-anchored. Parse the trusted human roots (AC-35).
205
+ let trustRootsDoc;
206
+ try {
207
+ trustRootsDoc = JSON.parse(files[TRUST_ROOTS_FILE]);
208
+ }
209
+ catch {
210
+ return denied(`corrupt ${TRUST_ROOTS_FILE}`);
211
+ }
212
+ const trustedHumanFingerprints = extractHumanFingerprints(trustRootsDoc);
213
+ // Parse the (now-trusted) breakpoint policy. Absent → default: every breakpoint
214
+ // requires a signature (fail-closed default when the anchor is active).
215
+ let policyDoc = { requireSignatureForAllBreakpoints: true };
216
+ if (files[BREAKPOINT_POLICY_FILE] !== undefined) {
217
+ try {
218
+ policyDoc = JSON.parse(files[BREAKPOINT_POLICY_FILE]);
219
+ }
220
+ catch {
221
+ return denied(`corrupt ${BREAKPOINT_POLICY_FILE}`);
222
+ }
223
+ }
224
+ const requiresSignature = (breakpointId) => {
225
+ const signed = new Set(policyDoc.signedBreakpointIds ?? []);
226
+ const exempt = new Set(policyDoc.exemptBreakpointIds ?? []);
227
+ if (policyDoc.requireSignatureForAllBreakpoints === true) {
228
+ return breakpointId === undefined ? true : !exempt.has(breakpointId);
229
+ }
230
+ return breakpointId !== undefined && signed.has(breakpointId);
231
+ };
232
+ return {
233
+ enforcementActive: true,
234
+ requiresSignature,
235
+ trustedHumanFingerprints,
236
+ // proven verifyAnswer resolves trusted keys from `<baseDir>/.keys/trusted`; anchor
237
+ // it at the manifest-verified config dir so the key store is trust-anchored, not
238
+ // task.json-supplied. The gate ALSO checks the resolved fingerprint membership.
239
+ trustedKeysBaseDir: configDir,
240
+ };
241
+ }
242
+ catch (err) {
243
+ return denied(`enforcement config error: ${err?.message ?? String(err)}`);
244
+ }
245
+ }
246
+ /** Extract the fingerprints of every non-revoked `human` trust root. */
247
+ function extractHumanFingerprints(doc) {
248
+ const roots = extractTrustRoots(doc);
249
+ return roots
250
+ .filter((r) => r.kind === "human" && r.revoked !== true && typeof r.fingerprint === "string")
251
+ .map((r) => r.fingerprint);
252
+ }
253
+ function extractTrustRoots(doc) {
254
+ if (Array.isArray(doc))
255
+ return doc;
256
+ if (doc && typeof doc === "object") {
257
+ const d = doc;
258
+ if (Array.isArray(d.trustRoots))
259
+ return d.trustRoots;
260
+ if (Array.isArray(d.roots))
261
+ return d.roots;
262
+ }
263
+ return [];
264
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"commitEffectResult.d.ts","sourceRoot":"","sources":["../../src/runtime/commitEffectResult.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EAG1B,MAAM,SAAS,CAAC;AAYjB,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAsGjH;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,SAAS,EAAE,aAAa,CAAC;CAC1C;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDhC"}
1
+ {"version":3,"file":"commitEffectResult.d.ts","sourceRoot":"","sources":["../../src/runtime/commitEffectResult.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EAG1B,MAAM,SAAS,CAAC;AAcjB,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAuGjH;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,SAAS,EAAE,aAAa,CAAC;CAC1C;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDhC"}
@@ -19,6 +19,8 @@ const stateCache_1 = require("./replay/stateCache");
19
19
  const workDirLeak_1 = require("./workDirLeak");
20
20
  const runtime_1 = require("./hooks/runtime");
21
21
  const schemaValidator_1 = require("./schemaValidator");
22
+ const enforce_signed_breakpoint_1 = require("../breakpoints/enforce-signed-breakpoint");
23
+ const trusted_breakpoint_policy_1 = require("../breakpoints/trusted-breakpoint-policy");
22
24
  async function commitEffectResult(options) {
23
25
  return await (0, lock_1.withRunLock)(options.runDir, "runtime:commitEffectResult", async () => {
24
26
  guardResultPayload(options);
@@ -34,6 +36,7 @@ async function commitEffectResult(options) {
34
36
  }
35
37
  ensureInvocationKeyMatches(options, record);
36
38
  await validateTaskResultOutputSchema(options, record);
39
+ await enforceSignedBreakpointGate(options, record);
37
40
  const resultPayload = buildResultPayload(options);
38
41
  const taskCompletedHookResult = await (0, runtime_1.callRuntimeHook)("task.completed", {
39
42
  runId: node_path_1.default.basename(options.runDir),
@@ -157,6 +160,133 @@ async function commitEffectCancellation(options) {
157
160
  return { resultRef };
158
161
  });
159
162
  }
163
+ /**
164
+ * Milestone C (AC-4 / AC-7 / AC-28 / AC-35 / AC-36 / AC-48) — enforced signed human
165
+ * breakpoints, anchored to the INTEGRITY-PROTECTED trust config (never task.json).
166
+ *
167
+ * TRUST SOURCE. The signature REQUIREMENT and the set of trusted `human` keys are
168
+ * sourced ONLY from the out-of-agent trust anchor the design §10 defines — the pinned
169
+ * config root (`POLICY_CONFIG_ROOT_FP`, off-workspace) → the signed
170
+ * `.policy/config-manifest.json` (monotonic-epoch, AC-46) → `.policy/trust-roots.json`
171
+ * + `.policy/breakpoint-policy.json` — via `loadTrustedBreakpointPolicy`
172
+ * (breakpoints/trusted-breakpoint-policy.ts). The per-effect `tasks/<id>/task.json` —
173
+ * which the workspace-writable agent can delete/corrupt/rewrite — is NEVER the source
174
+ * of the requirement or the trusted keys. The breakpoint IDENTITY is taken from the
175
+ * append-only JOURNAL (the effect's `invocationKey`, recorded at breakpoint-creation),
176
+ * not task.json.
177
+ *
178
+ * FAIL CLOSED (defect 1). When the config anchor is pinned but cannot be trustworthily
179
+ * established (missing/edited/corrupt/untrusted manifest or config, below-floor epoch),
180
+ * a breakpoint the trusted policy marks as requiring a signature is REJECTED — never
181
+ * skipped. When NO anchor is pinned (pre-Milestone-C default), enforcement is inactive
182
+ * and unsigned answers are unaffected (back-compat).
183
+ *
184
+ * DEFECT 2. The trusted human root set is passed to the enforcement decision, which
185
+ * additionally requires the raw-verified signer fingerprint to be a member — so a key
186
+ * the attacker drops into a run-dir directory is not trusted even if proven's raw
187
+ * verify succeeds.
188
+ *
189
+ * This runs BEFORE the result is written / EFFECT_RESOLVED is emitted, so a rejected
190
+ * answer leaves the effect PENDING. Non-breakpoint effects / non-`ok` statuses are
191
+ * unaffected.
192
+ */
193
+ async function enforceSignedBreakpointGate(options, record) {
194
+ if (record.kind !== "breakpoint" && record.taskId !== "__sdk.breakpoint")
195
+ return;
196
+ if (options.result.status !== "ok")
197
+ return;
198
+ // Load the trust-anchored policy. projectRoot is the base for the default `.policy`
199
+ // location; the real off-workspace pin is POLICY_CONFIG_DIR / the env pins. This is
200
+ // NOT the per-effect task dir.
201
+ const policy = await (0, trusted_breakpoint_policy_1.loadTrustedBreakpointPolicy)(process.cwd());
202
+ // No anchor pinned → enforcement inactive → back-compat pass-through.
203
+ if (!policy.enforcementActive)
204
+ return;
205
+ // The breakpoint id comes from the append-only journal (the effect's invocationKey),
206
+ // NOT task.json — so the adversary cannot erase the requirement by editing task.json.
207
+ const breakpointId = deriveBreakpointIdFromRecord(record);
208
+ const requiresSignature = policy.requiresSignature(breakpointId);
209
+ if (!requiresSignature)
210
+ return;
211
+ // A signature IS required for this breakpoint. If the trust anchor could not be
212
+ // established, we fail CLOSED (deny) — the requirement cannot be trustworthily
213
+ // determined, so the post is rejected and the effect stays pending.
214
+ if (policy.configError) {
215
+ logCommitFailure(options, "signed_breakpoint_rejected", {
216
+ taskId: record.taskId,
217
+ kind: record.kind,
218
+ reason: `trusted config unavailable: ${policy.configError}`,
219
+ });
220
+ throw new exceptions_1.RunFailedError(`Signed-breakpoint enforcement could not establish trust for effect ${options.effectId}: ${policy.configError}`, {
221
+ details: {
222
+ reason: "signed_breakpoint_rejected",
223
+ effectId: options.effectId,
224
+ taskId: record.taskId,
225
+ kind: record.kind,
226
+ },
227
+ });
228
+ }
229
+ // Extract the answer object from the committed value and enforce, anchoring WHICH
230
+ // keys are trusted to the manifest-verified trust-roots (defect 2).
231
+ const answer = extractBreakpointAnswer(options.result.value);
232
+ const decision = await (0, enforce_signed_breakpoint_1.enforceSignedBreakpointAnswer)(answer, {
233
+ signatureRequired: true,
234
+ trustedKeysDir: policy.trustedKeysBaseDir,
235
+ trustedHumanFingerprints: policy.trustedHumanFingerprints,
236
+ });
237
+ if (!decision.accepted) {
238
+ logCommitFailure(options, "signed_breakpoint_rejected", {
239
+ taskId: record.taskId,
240
+ kind: record.kind,
241
+ reason: decision.reason,
242
+ });
243
+ throw new exceptions_1.RunFailedError(`Signed-breakpoint enforcement rejected the answer for effect ${options.effectId}: ${decision.reason ?? "unsigned or invalid"}`, {
244
+ details: {
245
+ reason: "signed_breakpoint_rejected",
246
+ effectId: options.effectId,
247
+ taskId: record.taskId,
248
+ kind: record.kind,
249
+ },
250
+ });
251
+ }
252
+ }
253
+ /**
254
+ * Derive the breakpointId from the effect's JOURNAL-recorded invocation key (never
255
+ * task.json). Breakpoint effects use `__sdk.breakpoint.<breakpointId>` (with an optional
256
+ * `.<n>` occurrence suffix for repeated breakpoints, see intrinsics/breakpoint.ts).
257
+ * Returns undefined when the shape does not match — the trusted policy then applies its
258
+ * fail-closed default (an unidentifiable breakpoint under an active anchor requires a
259
+ * signature).
260
+ */
261
+ function deriveBreakpointIdFromRecord(record) {
262
+ const key = record.invocationKey;
263
+ const prefix = "__sdk.breakpoint.";
264
+ if (typeof key !== "string" || !key.startsWith(prefix))
265
+ return undefined;
266
+ let rest = key.slice(prefix.length);
267
+ // Strip a trailing numeric occurrence suffix (".1", ".2", …) added for repeats.
268
+ const suffixMatch = rest.match(/\.(\d+)$/);
269
+ if (suffixMatch)
270
+ rest = rest.slice(0, rest.length - suffixMatch[0].length);
271
+ return rest.length > 0 ? rest : undefined;
272
+ }
273
+ /**
274
+ * Extract the breakpoint answer record from a committed effect value. The value may
275
+ * BE the answer, or wrap it under `answer` / `result`.
276
+ */
277
+ function extractBreakpointAnswer(value) {
278
+ if (value && typeof value === "object" && !Array.isArray(value)) {
279
+ const v = value;
280
+ if (v.answer && typeof v.answer === "object" && !Array.isArray(v.answer)) {
281
+ return v.answer;
282
+ }
283
+ if (v.result && typeof v.result === "object" && !Array.isArray(v.result) && ("approved" in v.result || "signature" in v.result)) {
284
+ return v.result;
285
+ }
286
+ return v;
287
+ }
288
+ return {};
289
+ }
160
290
  async function validateTaskResultOutputSchema(options, record) {
161
291
  if (record.kind !== "shell" || options.result.status !== "ok") {
162
292
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/session/parse.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGzD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,YASnC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAuC3G;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAgDnF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4B5E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAgB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9E"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/session/parse.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGzD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,YASnC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAuC3G;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAgDnF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4B5E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAgB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9E"}
@@ -3,6 +3,9 @@
3
3
  * Session state file parsing utilities.
4
4
  * Parses markdown files with YAML frontmatter for session state.
5
5
  */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
6
9
  Object.defineProperty(exports, "__esModule", { value: true });
7
10
  exports.DEFAULT_SESSION_STATE = void 0;
8
11
  exports.parseYamlFrontmatter = parseYamlFrontmatter;
@@ -12,6 +15,7 @@ exports.sessionFileExists = sessionFileExists;
12
15
  exports.validateSessionState = validateSessionState;
13
16
  exports.getSessionFilePath = getSessionFilePath;
14
17
  const node_fs_1 = require("node:fs");
18
+ const node_path_1 = __importDefault(require("node:path"));
15
19
  const types_1 = require("./types");
16
20
  /**
17
21
  * Default session state values.
@@ -172,5 +176,5 @@ function validateSessionState(state) {
172
176
  * Get the state file path for a session.
173
177
  */
174
178
  function getSessionFilePath(stateDir, sessionId) {
175
- return `${stateDir}/${sessionId}.md`;
179
+ return node_path_1.default.join(stateDir, `${sessionId}.md`);
176
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a5c-ai/babysitter-sdk",
3
- "version": "6.0.2",
3
+ "version": "6.0.3-staging.032e07a1ffa0",
4
4
  "description": "Storage and run-registry primitives for event-sourced babysitter workflows.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -42,9 +42,9 @@
42
42
  "verify:package-health": "node scripts/verify-package-health.cjs"
43
43
  },
44
44
  "dependencies": {
45
- "@a5c-ai/atlas": "6.0.2",
46
- "@a5c-ai/adapters": "6.0.2",
47
- "@a5c-ai/hooks-adapter-cli": "6.0.2",
45
+ "@a5c-ai/atlas": "6.0.3-staging.032e07a1ffa0",
46
+ "@a5c-ai/adapters": "6.0.3-staging.032e07a1ffa0",
47
+ "@a5c-ai/hooks-adapter-cli": "6.0.3-staging.032e07a1ffa0",
48
48
  "@modelcontextprotocol/sdk": "^1.12.1",
49
49
  "@sinclair/typebox": "^0.34.48",
50
50
  "fs-extra": "^11.2.0",
@@ -57,7 +57,7 @@
57
57
  "puppeteer": "^24.0.0"
58
58
  },
59
59
  "peerDependencies": {
60
- "@a5c-ai/tasks-adapter": "6.0.2"
60
+ "@a5c-ai/tasks-adapter": "6.0.3-staging.032e07a1ffa0"
61
61
  },
62
62
  "peerDependenciesMeta": {
63
63
  "@a5c-ai/tasks-adapter": {
@@ -65,7 +65,7 @@
65
65
  }
66
66
  },
67
67
  "devDependencies": {
68
- "@a5c-ai/tasks-adapter": "6.0.2",
68
+ "@a5c-ai/tasks-adapter": "6.0.3-staging.032e07a1ffa0",
69
69
  "@eslint/js": "^9.39.4",
70
70
  "@types/fs-extra": "^11.0.4",
71
71
  "@types/node": "^20.11.30",