immune-brain 3.2.2 → 3.4.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/README.md +2 -2
- package/README.zh-CN.md +3 -3
- package/package.json +5 -3
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +19 -73
- package/plugins/immune-brain/.pi-extension/pi-canary-interaction.ts +1 -2
- package/plugins/immune-brain/.pi-extension/runtime-stub.ts +2 -2
- package/plugins/immune-brain/dist/BASELINE.md +1 -1
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +317 -316
- package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +1 -1
- package/plugins/immune-brain/dist/imm-brainstorm.md +1 -1
- package/plugins/immune-brain/dist/imm-loop.md +15 -10
- package/plugins/immune-brain/dist/imm-planner.md +22 -17
- package/plugins/immune-brain/hooks/hooks.json +0 -10
- package/plugins/immune-brain/runtime/assurance/coordinator.ts +0 -8
- package/plugins/immune-brain/runtime/claude/capability.ts +4 -10
- package/plugins/immune-brain/runtime/claude/interaction.ts +55 -22
- package/plugins/immune-brain/runtime/claude/kernel_ports.ts +63 -73
- package/plugins/immune-brain/runtime/claude/mcp_server.ts +233 -55
- package/plugins/immune-brain/runtime/claude/review_host.ts +2 -138
- package/plugins/immune-brain/runtime/kernel/application.ts +0 -1
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +1 -7
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +0 -7
- package/plugins/immune-brain/runtime/kernel/completion.ts +1 -3
- package/plugins/immune-brain/runtime/kernel/reducer.ts +0 -31
- package/plugins/immune-brain/runtime/kernel/types.ts +0 -2
- package/plugins/immune-brain/runtime/kernel/validation.ts +1 -3
- package/plugins/immune-brain/runtime/plugin_version.ts +2 -0
- package/plugins/immune-brain/skills/BASELINE.md +1 -1
- package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +4 -0
- package/plugins/immune-brain/skills/imm-loop/SKILL.md +4 -0
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +6 -0
|
@@ -90,7 +90,7 @@ Pi `Agent` has no `readonly` parameter; the empty tool policy, child type, and p
|
|
|
90
90
|
4. Kernel Assurance chat uses native Tool rendering only. It does not publish completion messages or wake a later parent turn; the direct Tool result is the continuation boundary. `awaiting_user` remains valid until the host-built authorization operation is confirmed and the immutable snapshot is revalidated.
|
|
91
91
|
5. Child 不得再次派发 child,nested delegation 一律禁止。Parent 保留综合与最终判断责任。
|
|
92
92
|
6. Kernel authority Review 对每个 immutable snapshot 恰好一个 primary reviewer,turn 预算按 workload 缩放(Quick 12 / Standard 16 / Heavy 24),并使用该 snapshot 对应的 Quick/Standard/Heavy 执行档位;不存在从 initial dispatch 起算的单一端到端总预算。Reviewer 必须先验证 immutable v5 manifest 的 `base_head`、`review_commit`、单一 parent、`review_tree` 与 `manifest_digest`,再用只读 Git 命令从 synthetic revision 获取源码;manifest 只有 metadata,不复制 source bytes,也不枚举 neighborhood files。审查只围绕 acceptance assertions 与 `changed_paths`,未变更路径只有在 acceptance、changed caller 或同一 state machine 直接需要时按需读取并注明理由;Reviewer 不探索无关 repository paths。对 settlement-class change,Reviewer 必须先枚举 immutable revision 内每条 terminal、cancellation、timeout 与 race path,再对全部路径给出判断;finding summary 必须以受影响路径开头。每个 acceptance 的执行结果已由 deterministic QA 在 review 前验证并内嵌于 manifest 的 `outcomes` 字段(acceptance_id -> {status, summary});Reviewer 不得重跑 descriptor,也不得把本地没有测试运行当作 finding——Review 只审 revision provenance、代码正确性、回归、安全与缺失测试。除该 reviewer 外,同一触发点最多两个相互独立的 advisory/discovery children;它们只能并行读,不能写 workflow state、关闭 QA 或产生 authority。
|
|
93
|
-
7. Foreground assurance never sleeps, polls, or schedules a completion callback. One Tool call owns QA; one Parent turn obtains and submits the reviewer verdict
|
|
93
|
+
7. Foreground assurance never sleeps, polls, or schedules a completion callback. One Tool call owns QA; one Parent turn obtains and submits the reviewer verdict. User authorization is reserved for unresolved decisions, explicit stop, and breaking Intent revisions; no risk tier adds a generic final confirmation.
|
|
94
94
|
8. `submit_review` validates the verdict contract, task identity, immutable snapshot digest, and fresh record/Intent/workspace/diff revisions before applying Review authority. Malformed verdicts are retryable without rebuilding evidence. Stale verdicts leave the TaskRecord unchanged and release the stale reservation. Local execution trusts the Parent to relay the reviewer verdict and does not require Agent lifecycle receipts.
|
|
95
95
|
9. The assurance Tool checks the immutable snapshot before each phase and returns a terminal structured state directly: `cancelled`, `rework`, `review_ready`, `awaiting_user`, `blocked`, or `settlement_unknown`. The commit boundary is non-cancellable. No silent task, status timer, completion notification, or result retrieval path is permitted.
|
|
96
96
|
|
|
@@ -103,7 +103,7 @@ Runtime helpers: `imm_core.buildBrainstormEnsembleRequest`, `imm_core.buildBrain
|
|
|
103
103
|
|
|
104
104
|
A Brainstorm ensemble is optional advisory-only framing input, not a vote and not a child-owned decision. The default roles are clarify scope, divergent options, minimal solution, and risk review.
|
|
105
105
|
|
|
106
|
-
All Brainstorm ensemble children are advisory-only with `tool_policy: no tools`; they do not edit code, write Specs, write Plans, mutate workflow state, or close QA. The parent `imm-brainstorm` owns final framing synthesis, Brainstorm manifest IDs, and decision-delta confirmation. Final Spec and Plan authority stays with `imm-planner`. Routine Managed enrollment uses the
|
|
106
|
+
All Brainstorm ensemble children are advisory-only with `tool_policy: no tools`; they do not edit code, write Specs, write Plans, mutate workflow state, or close QA. The parent `imm-brainstorm` owns final framing synthesis, Brainstorm manifest IDs, and decision-delta confirmation. Final Spec and Plan authority stays with `imm-planner`. Routine Managed enrollment uses the current Host's native confirmation bound to the TaskIntent revision, content hash, and preparation digest as the single authority gate; Enrollment validates descriptor structure without executing acceptance descriptors, deterministic QA executes them after implementation, and the routine task proceeds without a second human stop. A later authority-gate failure stays fail-closed in the current Host and reports exactly one same-Host recovery action; Brainstorm must never recommend another Host, worktree, or unmanaged implementation as a fallback.
|
|
107
107
|
|
|
108
108
|
Pi's adapter may consume `brainstorm_ensemble` dispatch JSON to prepare advisory Pi subagent envelopes, but envelope construction is not child execution and does not transfer framing authority. Pi launches one foreground Agent at a time, consumes its direct result, and re-evaluates the remaining dispatch budget before launching another candidate. Runtime does not call any agent, poll or recover background work, mutate state, or own final Spec/Plan authority. Pi subagent children remain no-tools advisory candidates; the parent `imm-brainstorm` collects outputs before synthesis.
|
|
109
109
|
|
|
@@ -36,7 +36,7 @@ before any workflow mutation.
|
|
|
36
36
|
|
|
37
37
|
## Core Responsibilities
|
|
38
38
|
|
|
39
|
-
- **Main-context completion loop**: Drive the enrolled Kernel task in the current
|
|
39
|
+
- **Main-context completion loop**: Drive the enrolled Kernel task in the current Host conversation until completion or a safe stop.
|
|
40
40
|
- **Context-preserving execution**: Call `imm_loop_action` with `op: route`, then follow the returned `executor` context in the current Parent conversation. Implement only the active Step or pending same-boundary `follow_up`, then record structured execution evidence through the Loop runtime action. A bounded test failure uses the returned internal `test-fixer` dispatch with its explicit delegated test-file list; PR feedback or CI repair uses the returned internal `pr-fix` dispatch inside the current Plan boundary.
|
|
41
41
|
- **Independent authority isolation**: Use the host `Agent` subagent primitive for `awaiting_qa_decision` and for the exact runtime-reported review gate. Standard Plan Steps close from accepted passing evidence before an internal QA boundary exists; Strict Steps and all follow-ups retain isolated QA. The parent records accepted child decisions through the Loop runtime action.
|
|
42
42
|
- **Observable progress**: Update only at major phase changes: Step start, execution evidence recorded, QA/review result, or terminal stop. Always emit a terminal summary.
|
|
@@ -52,7 +52,7 @@ Repeat this sequence; do not silently stop while a valid action remains:
|
|
|
52
52
|
1. Call `imm_loop_action` with `op: route` (or `dispatch_role` at a QA/review boundary) and follow the projected `next` authority.
|
|
53
53
|
2. Emit one progress line: `[target][phase] result | next: action`.
|
|
54
54
|
3. Execute exactly one allowed action:
|
|
55
|
-
- Kernel ownership: call `imm_kernel_canary` for that owned task. Freeze the completed artifacts, then call `advance_assurance`; when it returns `review_ready`, invoke the foreground reviewer and pass its structured verdict to `submit_review`. When the projection calls for `request_authorization
|
|
55
|
+
- Kernel ownership: call `imm_kernel_canary` for that owned task. Freeze the completed artifacts, then call `advance_assurance`; when it returns `review_ready`, invoke the foreground reviewer and pass its structured verdict to `submit_review`. When the projection calls for `request_authorization` or `approve_breaking_intent_revision`, invoke the exact Tool operation directly without asking the user for chat pre-confirmation; the native host interaction is the single authority decision. Invoke `repair_authority_state` directly for a proven stale claim; Kernel revalidation removes only the redundant claim without user interaction.
|
|
56
56
|
- Active Step / `rework_needed`: follow the returned `executor` context in the current conversation, implement only the active Step or pending same-boundary `follow_up`, verify, record structured execution evidence through the Loop runtime action, and continue. A bounded test-only repair may request internal `test-fixer` with `focus_delta.specific_changes`; PR review or CI repair may request internal `pr-fix` with the current `plan_id`, changed-file boundary, and verification. Both return child evidence to the Parent and cannot widen scope.
|
|
57
57
|
- `awaiting_qa_decision`: call `imm_loop_action` with `op: dispatch_role`, role `qa`, the current projection, Plan verification, recorded evidence, and current target identity. Invoke the returned foreground Agent envelope exactly. A `rework` or `replan` must carry validated `notes`.
|
|
58
58
|
- `review_required`: map the exact `pending_review_gate` (`imm-code-review` or `imm-ui-review`) to the internal `code-review` or `ui-review` role and call `imm_loop_action` with `op: dispatch_role`, passing `pending_review_gate`, `review_changed_files`, and `review_changed_files_signature`. Invoke the returned foreground Agent envelope exactly. Record a validated pass, or open a same-boundary follow-up through the Loop runtime action.
|
|
@@ -67,6 +67,7 @@ Use Pi native `Agent` subagents. Do not spawn Pi child processes or invoke a sep
|
|
|
67
67
|
- The parent may implement but must not issue its own QA or review pass.
|
|
68
68
|
- QA and reviewer children must not edit files, write Plans, mutate Kernel state, or close decisions directly.
|
|
69
69
|
- Missing `Agent` support, failed or malformed child output, stale child target, runtime write failure, invalid projection, missing credentials, unclear verification, repeated unchanged failure, or user cancellation stops fail-closed with an explicit reason and no decision write.
|
|
70
|
+
- A Managed native authority failure reports its stable reason and exactly one same-Host recovery action. Never recommend another Host, worktree, Direct Path, unmanaged implementation, or automatic retry as a fallback.
|
|
70
71
|
- `replan_needed` stops at `imm-planner`; do not widen scope or rewrite the active Plan. A replacement must use a new sequential Plan path after the current Plan reaches `completed`, or after a literal user explicitly marks it `cancelled` or `superseded`.
|
|
71
72
|
- Plans never suspend, resume, queue, or execute in parallel. Do not insert a repair Plan ahead of the current Plan.
|
|
72
73
|
- Same-boundary review `follow_up` repeats execution, independent QA, and the originating review gate.
|
|
@@ -139,18 +140,22 @@ Default user-facing shape: checkpoint progress lines, then `Conclusion -> Eviden
|
|
|
139
140
|
## Kernel Canary Routing
|
|
140
141
|
|
|
141
142
|
When the Kernel projection reports an active/draining backend claim, keep
|
|
142
|
-
`imm-loop` as the user-facing entry and call the
|
|
143
|
-
that owned task. Enrollment
|
|
144
|
-
|
|
145
|
-
`
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
`imm-loop` as the user-facing entry and call the current Host's Kernel integration
|
|
144
|
+
for that owned task. Enrollment and Review authorization use the current Host's
|
|
145
|
+
native gates. When the projection calls for `request_authorization` or
|
|
146
|
+
`approve_breaking_intent_revision`, invoke the exact Tool operation directly
|
|
147
|
+
without asking the user for chat pre-confirmation; the native Host interaction is
|
|
148
|
+
the single authority decision. Invoke
|
|
149
|
+
`repair_authority_state` directly for a proven stale claim; Kernel revalidation
|
|
150
|
+
removes only the redundant claim without user interaction. Do not invoke the
|
|
151
|
+
removed `imm-canary-work` Skill as
|
|
149
152
|
a separate entry point. Invalid or contradictory projections fail closed. After
|
|
150
153
|
implementation and focused verification, freeze the artifacts and call
|
|
151
154
|
`advance_assurance`. If it returns `review_ready`, invoke the foreground
|
|
152
155
|
reviewer and pass its structured verdict to `submit_review`;
|
|
153
|
-
`request_authorization`
|
|
156
|
+
`request_authorization` is reserved for an unresolved user decision or an
|
|
157
|
+
explicit stop. Critical work completes after fresh QA and Review without a
|
|
158
|
+
second user confirmation.
|
|
154
159
|
Every QA/Review operation stays foreground and returns its next projected
|
|
155
160
|
obligation directly to the Parent. The host performs any opted-in GitHub Issue
|
|
156
161
|
projection only after the corresponding authority mutation: only a fresh
|
|
@@ -22,9 +22,12 @@ planning artifacts. A later literal-user request to start Enrollment is a
|
|
|
22
22
|
non-authoritative execution trigger: invoke the native Enrollment gate directly,
|
|
23
23
|
without asking for chat pre-confirmation. For a clear mutation request that
|
|
24
24
|
already includes execution, invoke that gate as soon as the candidate is
|
|
25
|
-
validated and Git-tracked. Literal-user confirmation in the
|
|
26
|
-
the authority boundary.
|
|
27
|
-
|
|
25
|
+
validated and Git-tracked. Literal-user confirmation in the current Host's
|
|
26
|
+
native gate remains the authority boundary. A gate failure preserves candidate
|
|
27
|
+
artifacts and reports its reason plus exactly one same-Host recovery action; it
|
|
28
|
+
must never recommend another Host, worktree, Direct Path, unmanaged
|
|
29
|
+
implementation, or automatic retry. Fast-Track may compress the same phases but
|
|
30
|
+
cannot bypass that boundary, QA, Review, authorization, or completion.
|
|
28
31
|
|
|
29
32
|
## Clarification supplement
|
|
30
33
|
|
|
@@ -73,7 +76,7 @@ Then route deterministically:
|
|
|
73
76
|
- an active or otherwise nonterminal v3 Plan remains on its existing v3 route;
|
|
74
77
|
- no routing policy preserves the legacy v3 Planner behavior;
|
|
75
78
|
- a valid `kernel_task_intent` retirement policy produces one TaskIntent draft
|
|
76
|
-
through
|
|
79
|
+
through the current Host's explicit `imm-planner`;
|
|
77
80
|
- an invalid, unreadable, untracked, or tracked-deleted policy rejects new
|
|
78
81
|
planning authority with `routing_policy_invalid`;
|
|
79
82
|
- no Planner path enrolls a task or falls back to v3 after retirement.
|
|
@@ -90,17 +93,18 @@ The routing projection selects the planning route; explicit
|
|
|
90
93
|
of that Plan artifact. A valid Plan never proves Managed authority. Under an
|
|
91
94
|
active `kernel_task_intent` policy, authority still requires a Git-tracked
|
|
92
95
|
TaskIntent whose `imm-kernel intent validate <path> --json` projection is
|
|
93
|
-
`valid: true` and `enrollment_ready: true`, followed by
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
`valid: true` and `enrollment_ready: true`, followed by current-Host native
|
|
97
|
+
Enrollment.
|
|
98
|
+
|
|
99
|
+
Host identity is implicit and never a planning input. The production boundary
|
|
100
|
+
that turns a Git-tracked TaskIntent draft into managed execution authority is
|
|
101
|
+
the current Host's native Enrollment gate. Invoke that Host integration
|
|
102
|
+
directly when the route is ready; do not ask for chat pre-confirmation. The
|
|
103
|
+
single literal-user decision is bound to the TaskIntent revision, content hash,
|
|
104
|
+
and preparation digest. Enrollment validates the intent, Git ownership, scope,
|
|
105
|
+
workspace claim, and final authority preconditions without executing acceptance
|
|
106
|
+
descriptors. A routine task proceeds from that single confirmation through
|
|
107
|
+
enrollment, execution and QA without a second human stop.
|
|
104
108
|
|
|
105
109
|
Before authoring a TaskIntent, trace each expected behavior from its public or
|
|
106
110
|
runtime entry point through existing imports and callers to the highest focused
|
|
@@ -387,10 +391,11 @@ Iteration plan under `docs/plans/` and spec under `docs/specs/`. Includes: `Summ
|
|
|
387
391
|
- When a project explicitly expects Chinese document prose, `imm-plan <plan-path> --json` includes an `output_language` warning if target Plan or referenced Spec prose appears mostly English.
|
|
388
392
|
- Spec references align with steps: each step’s `Verification` is copy-paste-checkable against repo commands or files.
|
|
389
393
|
- For brainstorm-origin Plans with a manifest, `imm-plan <plan-path> --json` reports `origin_coverage` totals with no `unmapped_items` and no reason-required trace rows without reasons.
|
|
390
|
-
- Managed execution handoff is Git-tracked TaskIntent author/validate plus
|
|
394
|
+
- Managed execution handoff is Git-tracked TaskIntent author/validate plus current-Host native Enrollment. Do not sync a v3 State Ledger or invoke a missing dispatcher.
|
|
391
395
|
|
|
392
396
|
## Next Action
|
|
393
397
|
|
|
394
398
|
- Gate: Reference closure and the clarification supplement are complete; every upstream `BR-*` item is represented; no unresolved user-owned decision remains; any Planner-introduced decision delta is confirmed; the Plan passes `imm-plan --json` validation; and no step has a hypothetical-only verification path.
|
|
395
|
-
- If gates pass: for Kernel-managed work, invoke the
|
|
399
|
+
- If gates pass: for Kernel-managed work, invoke the current Host's native Enrollment Tool directly without chat pre-confirmation. Its single literal-user gate binds the TaskIntent revision, content hash, and preparation digest, validates Enrollment preconditions without executing acceptance descriptors, and enrolls the task to continue through `imm-loop`.
|
|
400
|
+
- If the native gate fails: preserve candidate artifacts and report the stable reason plus exactly one same-Host recovery action. Do not suggest another Host, worktree, Direct Path, unmanaged implementation, or automatic retry.
|
|
396
401
|
- If gates are not met: state which validation failures, unresolved verification paths, or material decision deltas remain; do not name a next skill.
|
|
@@ -133,7 +133,6 @@ export interface ForegroundToolUpdate {
|
|
|
133
133
|
|
|
134
134
|
export type AssuranceAdvanceResult =
|
|
135
135
|
| { state: "review_ready"; operation: "review"; operation_id: string; snapshot_digest: string; review_bundle_digest: string; agent_params: unknown }
|
|
136
|
-
| { state: "awaiting_user"; operation: "record-user-approval"; operation_id: string }
|
|
137
136
|
| { state: "rework"; operation: "qa"; operation_id: string; summary: string }
|
|
138
137
|
| { state: "cancelled"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
139
138
|
| { state: "failed"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
@@ -150,7 +149,6 @@ export type AssuranceAdvanceResult =
|
|
|
150
149
|
|
|
151
150
|
export type AssuranceSubmitReviewResult =
|
|
152
151
|
| { state: "rework"; operation: "review"; operation_id: string; summary: string }
|
|
153
|
-
| { state: "awaiting_user"; operation: "record-user-approval"; operation_id: string }
|
|
154
152
|
| { state: "review_preparation_failed"; operation: "review"; operation_id: string; reason: string }
|
|
155
153
|
| { state: "completed" }
|
|
156
154
|
| { state: "settlement_unknown"; operation: "qa" | "review"; operation_id: string; reason: string }
|
|
@@ -528,8 +526,6 @@ export class AssuranceCoordinator {
|
|
|
528
526
|
return this.unknownAfterCommit(taskId, "qa", operationId, boundedAssuranceError(error));
|
|
529
527
|
}
|
|
530
528
|
}
|
|
531
|
-
if (projection.projection.next_obligation === "authorize_user")
|
|
532
|
-
return { state: "awaiting_user", operation: "record-user-approval", operation_id: operationId };
|
|
533
529
|
if (projection.projection.next_obligation !== "run_qa" && projection.projection.next_obligation !== "run_review")
|
|
534
530
|
return { state: "blocked", reason: `Kernel requires ${projection.projection.next_obligation}` };
|
|
535
531
|
const qaAlreadySettled = projection.projection.next_obligation === "run_review";
|
|
@@ -623,8 +619,6 @@ export class AssuranceCoordinator {
|
|
|
623
619
|
return this.unknownAfterCommit(taskId, "qa", operationId, boundedAssuranceError(error));
|
|
624
620
|
}
|
|
625
621
|
}
|
|
626
|
-
if (fresh.projection.next_obligation === "authorize_user")
|
|
627
|
-
return { state: "awaiting_user", operation: "record-user-approval", operation_id: operationId };
|
|
628
622
|
if (fresh.projection.next_obligation !== "run_review") {
|
|
629
623
|
if (authorityCommitted && aborted()) return this.unknownAfterCommit(taskId, "qa", operationId, "QA settlement projection did not require Review after cancellation");
|
|
630
624
|
return { state: "blocked", reason: `Kernel requires ${fresh.projection.next_obligation} after QA` };
|
|
@@ -777,8 +771,6 @@ export class AssuranceCoordinator {
|
|
|
777
771
|
return this.unknownAfterCommit(taskId, "review", reservation.operationId, boundedAssuranceError(error));
|
|
778
772
|
}
|
|
779
773
|
}
|
|
780
|
-
if (settled.projection.next_obligation === "authorize_user")
|
|
781
|
-
return { state: "awaiting_user", operation: "record-user-approval", operation_id: reservation.operationId };
|
|
782
774
|
return { state: "blocked", reason: `Kernel requires ${settled.projection.next_obligation} after Review` };
|
|
783
775
|
}
|
|
784
776
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const MIN_CLAUDE_CODE_VERSION = "2.1.
|
|
1
|
+
export const MIN_CLAUDE_CODE_VERSION = "2.1.236";
|
|
2
2
|
export const HOST_ID = "claude-code" as const;
|
|
3
3
|
export const CORE_CONTRACT = "assurance_kernel/host_adapter/claude-code/v1";
|
|
4
4
|
export const SUPPORTED_PLATFORMS = ["darwin", "linux"] as const;
|
|
@@ -6,12 +6,12 @@ export const SUPPORTED_PLATFORMS = ["darwin", "linux"] as const;
|
|
|
6
6
|
export type PermissionMode = "manual" | "acceptEdits" | "auto" | "bypassPermissions" | "dontAsk";
|
|
7
7
|
|
|
8
8
|
export type HostProbe =
|
|
9
|
-
| { ok: true; version: string;
|
|
9
|
+
| { ok: true; version: string; platform: string }
|
|
10
10
|
| { ok: false; reason: string };
|
|
11
11
|
|
|
12
12
|
function parseSemver(value: string): [number, number, number] | null {
|
|
13
13
|
// Prerelease and build suffixes are rejected: an unstable build such as
|
|
14
|
-
//
|
|
14
|
+
// Prerelease builds never satisfy the stable minimum.
|
|
15
15
|
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(value.trim());
|
|
16
16
|
if (!match) return null;
|
|
17
17
|
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
@@ -57,11 +57,5 @@ export function probeHost(
|
|
|
57
57
|
if (platform !== "darwin" && platform !== "linux") {
|
|
58
58
|
return { ok: false, reason: `unsupported platform ${platform}; native Windows is out of scope` };
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
if (rawPermissionMode !== undefined && rawPermissionMode !== "") {
|
|
62
|
-
const permissionMode = parsePermissionMode(rawPermissionMode);
|
|
63
|
-
if (!permissionMode) return { ok: false, reason: `unsupported permission mode ${rawPermissionMode}` };
|
|
64
|
-
return { ok: true, version, permissionMode, platform };
|
|
65
|
-
}
|
|
66
|
-
return { ok: true, version, permissionMode: "manual", platform };
|
|
60
|
+
return { ok: true, version, platform };
|
|
67
61
|
}
|
|
@@ -1,58 +1,91 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { parsePermissionMode, type PermissionMode } from "./capability";
|
|
3
2
|
|
|
4
3
|
export const PRIVILEGED_OPERATIONS = [
|
|
5
4
|
"enroll",
|
|
6
5
|
"request_authorization",
|
|
7
6
|
"approve_breaking_intent_revision",
|
|
8
7
|
"stop",
|
|
9
|
-
"repair_authority_state",
|
|
10
8
|
] as const;
|
|
11
9
|
|
|
12
10
|
export type PrivilegedOperation = (typeof PRIVILEGED_OPERATIONS)[number];
|
|
13
|
-
export type NativeDecision = "accept" | "
|
|
11
|
+
export type NativeDecision = "accept" | "decline" | "cancel";
|
|
12
|
+
export type NativeFailureCode =
|
|
13
|
+
| "interaction_not_opened"
|
|
14
|
+
| "user_denied"
|
|
15
|
+
| "user_cancelled"
|
|
16
|
+
| "correlation_missing"
|
|
17
|
+
| "unsupported_host"
|
|
18
|
+
| "workspace_changed";
|
|
19
|
+
|
|
20
|
+
const RECOVERY_ACTIONS: Record<NativeFailureCode, string> = {
|
|
21
|
+
interaction_not_opened: "retry through a fresh native gate in the current Host",
|
|
22
|
+
user_denied: "wait for a fresh literal-user request",
|
|
23
|
+
user_cancelled: "wait for a fresh literal-user request",
|
|
24
|
+
correlation_missing: "retry through a fresh native gate in the current Host",
|
|
25
|
+
unsupported_host: "upgrade to a supported Claude Code version and retry in the current Host",
|
|
26
|
+
workspace_changed: "review the current workspace and retry through a fresh native gate",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export class NativeAuthorityError extends Error {
|
|
30
|
+
constructor(
|
|
31
|
+
readonly reasonCode: NativeFailureCode,
|
|
32
|
+
detail: string,
|
|
33
|
+
readonly recoveryAction = RECOVERY_ACTIONS[reasonCode],
|
|
34
|
+
) {
|
|
35
|
+
super(`${reasonCode}: ${detail}; recovery: ${recoveryAction}`);
|
|
36
|
+
this.name = "NativeAuthorityError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
14
39
|
|
|
15
40
|
export function isPrivilegedOperation(operation: string): operation is PrivilegedOperation {
|
|
16
41
|
return (PRIVILEGED_OPERATIONS as readonly string[]).includes(operation);
|
|
17
42
|
}
|
|
18
43
|
|
|
19
44
|
export function privilegedAnnotations(): Record<string, unknown> {
|
|
20
|
-
return {
|
|
21
|
-
destructiveHint: true,
|
|
22
|
-
"anthropic/requiresUserInteraction": true,
|
|
23
|
-
};
|
|
45
|
+
return { destructiveHint: true };
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
export interface NativeGateInput {
|
|
27
49
|
operation: string;
|
|
28
|
-
permissionMode: PermissionMode | string;
|
|
29
|
-
requiresUserInteraction: boolean;
|
|
30
50
|
interactive: boolean;
|
|
31
51
|
decision?: NativeDecision;
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
export type NativeGateResult =
|
|
35
55
|
| { ok: true }
|
|
36
|
-
| { ok: false;
|
|
56
|
+
| { ok: false; error: NativeAuthorityError };
|
|
37
57
|
|
|
38
58
|
export function evaluateNativeGate(input: NativeGateInput): NativeGateResult {
|
|
39
59
|
if (!isPrivilegedOperation(input.operation)) return { ok: true };
|
|
40
|
-
if (!input.interactive) return { ok: false,
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
if (
|
|
44
|
-
if (!input.requiresUserInteraction) {
|
|
45
|
-
return { ok: false, reason: "privileged operation requires anthropic/requiresUserInteraction" };
|
|
46
|
-
}
|
|
47
|
-
if (input.decision === "deny") return { ok: false, reason: "native interaction denied" };
|
|
48
|
-
if (input.decision === "cancel") return { ok: false, reason: "native interaction cancelled" };
|
|
49
|
-
if (input.decision !== "accept") return { ok: false, reason: "native interaction missing" };
|
|
60
|
+
if (!input.interactive) return { ok: false, error: new NativeAuthorityError("unsupported_host", "interactive MCP elicitation is unavailable") };
|
|
61
|
+
if (input.decision === "decline") return { ok: false, error: new NativeAuthorityError("user_denied", "native interaction declined") };
|
|
62
|
+
if (input.decision === "cancel") return { ok: false, error: new NativeAuthorityError("user_cancelled", "native interaction cancelled") };
|
|
63
|
+
if (input.decision !== "accept") return { ok: false, error: new NativeAuthorityError("interaction_not_opened", "native interaction returned no decision") };
|
|
50
64
|
return { ok: true };
|
|
51
65
|
}
|
|
52
66
|
|
|
67
|
+
export interface NativeConfirmationInput {
|
|
68
|
+
operation: PrivilegedOperation;
|
|
69
|
+
taskId: string;
|
|
70
|
+
toolCallId: string;
|
|
71
|
+
risk?: string;
|
|
72
|
+
intentRevision?: number;
|
|
73
|
+
intentContentHash?: string;
|
|
74
|
+
bindingDigest?: string;
|
|
75
|
+
signal?: AbortSignal;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface NativeConfirmationResult {
|
|
79
|
+
decision: NativeDecision;
|
|
80
|
+
requestId: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type NativeConfirmationPort = (input: NativeConfirmationInput) => Promise<NativeConfirmationResult>;
|
|
84
|
+
|
|
53
85
|
export function confirmationRef(input: {
|
|
54
|
-
|
|
86
|
+
connectionId: string;
|
|
55
87
|
toolCallId: string;
|
|
88
|
+
requestId: string;
|
|
56
89
|
operation: string;
|
|
57
90
|
taskId: string;
|
|
58
91
|
intentRevision?: number;
|
|
@@ -60,7 +93,7 @@ export function confirmationRef(input: {
|
|
|
60
93
|
bindingDigest?: string;
|
|
61
94
|
}): string {
|
|
62
95
|
return `claude-confirm-${createHash("sha256")
|
|
63
|
-
.update(`${input.
|
|
96
|
+
.update(`${input.connectionId}\0${input.toolCallId}\0${input.requestId}\0${input.operation}\0${input.taskId}\0${input.intentRevision ?? ""}\0${input.intentContentHash ?? ""}\0${input.bindingDigest ?? ""}`)
|
|
64
97
|
.digest("hex")
|
|
65
98
|
.slice(0, 16)}`;
|
|
66
99
|
}
|