immune-brain 2.8.3 → 3.0.2
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/.claude-plugin/marketplace.json +16 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/package.json +9 -2
- package/plugins/immune-brain/.claude-plugin/plugin.json +8 -0
- package/plugins/immune-brain/.mcp.json +8 -0
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +354 -64
- package/plugins/immune-brain/.pi-extension/pi-canary-assurance-progression.ts +74 -706
- package/plugins/immune-brain/.pi-extension/pi-canary-invocations.ts +1 -90
- package/plugins/immune-brain/.pi-extension/pi-canary-native-review.ts +13 -160
- package/plugins/immune-brain/.pi-extension/pi-canary-qa-findings.ts +1 -50
- package/plugins/immune-brain/.pi-extension/pi-canary-review-bundle.ts +1 -262
- package/plugins/immune-brain/.pi-extension/pi-canary-tool-failure.ts +3 -2
- package/plugins/immune-brain/.pi-extension/pi-canary-verification.ts +8 -229
- package/plugins/immune-brain/.pi-extension/runtime-stub.ts +23 -5
- package/plugins/immune-brain/agents/immune-brain-reviewer.md +11 -0
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +7514 -0
- package/plugins/immune-brain/dist/docs/reference/code-quality-guard.md +58 -0
- package/plugins/immune-brain/dist/docs/reference/immune-brain-config.md +1 -1
- package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +1 -1
- package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +19 -13
- package/plugins/immune-brain/dist/imm-loop.md +6 -7
- package/plugins/immune-brain/dist/imm-planner.md +1 -1
- package/plugins/immune-brain/dist/imm-pr-fix.md +9 -0
- package/plugins/immune-brain/dist/role-prompts/code-review.md +33 -13
- package/plugins/immune-brain/dist/role-prompts/executor.md +14 -0
- package/plugins/immune-brain/dist/role-prompts/pr-fix.md +9 -0
- package/plugins/immune-brain/dist/role-prompts/test-fixer.md +7 -0
- package/plugins/immune-brain/hooks/hooks.json +55 -0
- package/plugins/immune-brain/runtime/assurance/coordinator.ts +836 -0
- package/plugins/immune-brain/runtime/assurance/enrollment.ts +6 -0
- package/plugins/immune-brain/runtime/assurance/host_port.ts +18 -0
- package/plugins/immune-brain/runtime/assurance/invocations.ts +90 -0
- package/plugins/immune-brain/runtime/assurance/qa_findings.ts +50 -0
- package/plugins/immune-brain/runtime/assurance/review_evidence.ts +596 -0
- package/plugins/immune-brain/runtime/assurance/verification.ts +233 -0
- package/plugins/immune-brain/runtime/claude/capability.ts +67 -0
- package/plugins/immune-brain/runtime/claude/interaction.ts +70 -0
- package/plugins/immune-brain/runtime/claude/kernel_ports.ts +789 -0
- package/plugins/immune-brain/runtime/claude/mcp_server.ts +363 -0
- package/plugins/immune-brain/runtime/claude/review_host.ts +645 -0
- package/plugins/immune-brain/runtime/commands/kernel.ts +221 -3
- package/plugins/immune-brain/runtime/github_issue_tracker.ts +2 -2
- package/plugins/immune-brain/runtime/kernel/application.ts +8 -5
- package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +24 -13
- package/plugins/immune-brain/runtime/kernel/authority_port.ts +78 -115
- package/plugins/immune-brain/runtime/kernel/canary_application.ts +6 -4
- package/plugins/immune-brain/runtime/kernel/capability_registry.ts +89 -0
- package/plugins/immune-brain/runtime/kernel/completion.ts +64 -6
- package/plugins/immune-brain/runtime/kernel/enrollment.ts +189 -100
- package/plugins/immune-brain/runtime/kernel/enrollment_authority.ts +37 -80
- package/plugins/immune-brain/runtime/kernel/intent.ts +24 -0
- package/plugins/immune-brain/runtime/kernel/pi_canary_prepare.ts +31 -0
- package/plugins/immune-brain/runtime/kernel/reducer.ts +36 -13
- package/plugins/immune-brain/runtime/kernel/storage.ts +16 -16
- package/plugins/immune-brain/runtime/kernel/types.ts +32 -2
- package/plugins/immune-brain/runtime/kernel/validation.ts +107 -16
- package/plugins/immune-brain/runtime/loop_contract.ts +17 -2
- package/plugins/immune-brain/runtime/prompts/code-review.md +33 -13
- package/plugins/immune-brain/runtime/prompts/executor.md +14 -0
- package/plugins/immune-brain/runtime/prompts/pr-fix.md +9 -0
- package/plugins/immune-brain/runtime/prompts/test-fixer.md +7 -0
- package/plugins/immune-brain/runtime/v4_runtime.ts +5 -2
- package/plugins/immune-brain/runtime/workspace_scope.ts +191 -5
- package/plugins/immune-brain/skills/imm-loop/SKILL.md +3 -4
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +2 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ReviewRequest {
|
|
2
|
+
taskId: string;
|
|
3
|
+
operationId: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
evidencePath: string;
|
|
6
|
+
maxTurns: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface HostReviewReservation {
|
|
10
|
+
id: string;
|
|
11
|
+
dispatch: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AssuranceHostPort {
|
|
15
|
+
readonly host: "pi" | "claude-code" | "fake";
|
|
16
|
+
prepareReview(request: ReviewRequest): HostReviewReservation;
|
|
17
|
+
releaseReview(reservation: HostReviewReservation): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// P2B2 task-scoped invocation registry. NOT part of the Kernel runtime graph.
|
|
2
|
+
// One linear state transition per task: `open -> committed | cancelled`.
|
|
3
|
+
// Concurrent assure/authorize operations for the same task are rejected;
|
|
4
|
+
// timeout/cancel wins `open -> cancelled` first; a successful continuation
|
|
5
|
+
// must win `open -> committed` immediately before mint/apply; application
|
|
6
|
+
// failure leaves the invocation closed so retry requires a new invocation.
|
|
7
|
+
// No memory/file cross-transaction atomicity is claimed.
|
|
8
|
+
|
|
9
|
+
export type InvocationState = "open" | "committed" | "cancelled";
|
|
10
|
+
|
|
11
|
+
export interface InvocationToken {
|
|
12
|
+
readonly task_id: string;
|
|
13
|
+
readonly nonce: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface InvocationRegistry {
|
|
17
|
+
/** Open a new invocation for one task; rejects if one is already open. */
|
|
18
|
+
open(taskId: string): InvocationToken;
|
|
19
|
+
/**
|
|
20
|
+
* Linearization point: wins `open -> committed` for exactly this token.
|
|
21
|
+
* Throws for a foreign token or a token whose invocation already ended.
|
|
22
|
+
* Only the winner may mint/apply a capability.
|
|
23
|
+
*/
|
|
24
|
+
commit(token: InvocationToken): void;
|
|
25
|
+
/** Wins `open -> cancelled` (timeout/cancel/abort path). */
|
|
26
|
+
cancel(token: InvocationToken): void;
|
|
27
|
+
/** Inspect the current state of the caller's token. */
|
|
28
|
+
stateOf(token: InvocationToken): InvocationState;
|
|
29
|
+
/** Whether any invocation for the task is currently open. */
|
|
30
|
+
isOpen(taskId: string): boolean;
|
|
31
|
+
/** Internal task state (for tests). */
|
|
32
|
+
states(): Record<string, InvocationState>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createInvocationRegistry(): InvocationRegistry {
|
|
36
|
+
const states = new Map<string, { token: InvocationToken; state: InvocationState }>();
|
|
37
|
+
|
|
38
|
+
function tokenOf(taskId: string, nonce: string): InvocationToken {
|
|
39
|
+
return Object.freeze({ task_id: taskId, nonce });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function entryOf(token: InvocationToken): { token: InvocationToken; state: InvocationState } {
|
|
43
|
+
const entry = states.get(token.task_id);
|
|
44
|
+
if (!entry || entry.token.nonce !== token.nonce)
|
|
45
|
+
throw new Error("invocation token is not recognized for this task");
|
|
46
|
+
return entry;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
open(taskId: string): InvocationToken {
|
|
51
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(taskId))
|
|
52
|
+
throw new Error("task id is not a safe file identity");
|
|
53
|
+
const existing = states.get(taskId);
|
|
54
|
+
if (existing && existing.state === "open") {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`task ${taskId} already has an open invocation; concurrent assure/authorize is rejected`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
// A closed (committed/cancelled) invocation is replaced by a fresh
|
|
60
|
+
// one: retry requires a new invocation.
|
|
61
|
+
const token = tokenOf(taskId, `${taskId}:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`);
|
|
62
|
+
states.set(taskId, { token, state: "open" });
|
|
63
|
+
return token;
|
|
64
|
+
},
|
|
65
|
+
commit(token: InvocationToken): void {
|
|
66
|
+
const entry = entryOf(token);
|
|
67
|
+
if (entry.state !== "open")
|
|
68
|
+
throw new Error(
|
|
69
|
+
`invocation for task ${token.task_id} is already ${entry.state}; a new invocation is required`,
|
|
70
|
+
);
|
|
71
|
+
entry.state = "committed";
|
|
72
|
+
},
|
|
73
|
+
cancel(token: InvocationToken): void {
|
|
74
|
+
const entry = entryOf(token);
|
|
75
|
+
if (entry.state === "open") entry.state = "cancelled";
|
|
76
|
+
// Cancel on an already-closed invocation is a no-op (idempotent).
|
|
77
|
+
},
|
|
78
|
+
stateOf(token: InvocationToken): InvocationState {
|
|
79
|
+
return entryOf(token).state;
|
|
80
|
+
},
|
|
81
|
+
isOpen(taskId: string): boolean {
|
|
82
|
+
return states.get(taskId)?.state === "open";
|
|
83
|
+
},
|
|
84
|
+
states(): Record<string, InvocationState> {
|
|
85
|
+
const out: Record<string, InvocationState> = {};
|
|
86
|
+
for (const [taskId, entry] of states) out[taskId] = entry.state;
|
|
87
|
+
return out;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QA finding ids must stay globally unique for the lifetime of a task: the
|
|
5
|
+
* Kernel reducer rejects any finding id that was ever recorded, including
|
|
6
|
+
* resolved ones. Scoping each id to the snapshot digest plus a per-invocation
|
|
7
|
+
* suffix guarantees repeated rework attempts (even on an identical snapshot)
|
|
8
|
+
* never collide, while keeping the acceptance id traceable in the id itself.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Digest-scoped id for a per-acceptance QA rework finding. */
|
|
12
|
+
export function qaFindingId(acceptanceId: string, snapshotDigest: string): string {
|
|
13
|
+
return `qa-${acceptanceId}-${attemptRef(snapshotDigest)}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Digest-scoped id for an evidence-freshness QA rework finding. */
|
|
17
|
+
export function qaEvidenceFreshnessId(snapshotDigest: string): string {
|
|
18
|
+
const digest16 = snapshotDigest.slice("sha256:".length, "sha256:".length + 16);
|
|
19
|
+
return `qa-evidence-freshness-${digest16}-${attemptRef(snapshotDigest)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function attemptRef(snapshotDigest: string): string {
|
|
23
|
+
const digest8 = snapshotDigest.slice("sha256:".length, "sha256:".length + 8);
|
|
24
|
+
return `${digest8}-${randomUUID().slice(0, 6)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Preserve the original QA verdict details (which acceptance failed and why)
|
|
29
|
+
* when applying an authority verdict fails. The raw reducer error alone masks
|
|
30
|
+
* the failure reason that matters; append the verdict findings when present.
|
|
31
|
+
*/
|
|
32
|
+
export function describeQaFailure(
|
|
33
|
+
boundedBase: string,
|
|
34
|
+
findings?: Array<{ id: string; summary: string }>,
|
|
35
|
+
): string {
|
|
36
|
+
if (!findings?.length) return boundedBase;
|
|
37
|
+
const detail = findings.map((finding) => `${finding.id}: ${finding.summary}`).join(" | ");
|
|
38
|
+
const marker = "; verdict: ";
|
|
39
|
+
const totalBudget = 300;
|
|
40
|
+
// Reserve room for at least the first 160 chars of verdict detail.
|
|
41
|
+
const baseBudget = Math.max(0, totalBudget - marker.length - Math.min(detail.length, 160));
|
|
42
|
+
const trimmedBase =
|
|
43
|
+
boundedBase.length <= baseBudget
|
|
44
|
+
? boundedBase
|
|
45
|
+
: `${boundedBase.slice(0, Math.max(0, baseBudget - 1))}…`;
|
|
46
|
+
const detailBudget = Math.max(0, totalBudget - trimmedBase.length - marker.length);
|
|
47
|
+
const bounded =
|
|
48
|
+
detail.length <= detailBudget ? detail : `${detail.slice(0, Math.max(0, detailBudget - 3))}...`;
|
|
49
|
+
return `${trimmedBase}${marker}${bounded}`;
|
|
50
|
+
}
|