immune-brain 4.0.0 → 4.1.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/package.json +1 -1
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/.pi-extension/imm-canary-enroll.ts +12 -8
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +13 -9
- package/plugins/immune-brain/.pi-extension/ux-language.ts +28 -0
- package/plugins/immune-brain/dist/BASELINE.md +11 -0
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +12 -1
- package/plugins/immune-brain/dist/imm-loop.md +15 -1
- package/plugins/immune-brain/dist/imm-planner.md +7 -0
- package/plugins/immune-brain/dist/role-prompts/code-review.md +8 -0
- package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
- package/plugins/immune-brain/runtime/prompts/code-review.md +8 -0
- package/plugins/immune-brain/runtime/role_prompt_bridge.ts +21 -0
- package/plugins/immune-brain/skills/BASELINE.md +11 -0
package/package.json
CHANGED
|
@@ -37,6 +37,10 @@ import {
|
|
|
37
37
|
type UserAttentionReason,
|
|
38
38
|
} from "./pi-canary-interaction";
|
|
39
39
|
import { isToolFailureState, throwToolFailure } from "./pi-canary-tool-failure";
|
|
40
|
+
import { resolveUxLanguage, uxText } from "./ux-language";
|
|
41
|
+
|
|
42
|
+
/** Host-native UI language; see ux-language.ts. Resolved once per process. */
|
|
43
|
+
const UX_LANG = resolveUxLanguage();
|
|
40
44
|
|
|
41
45
|
const TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
42
46
|
|
|
@@ -62,7 +66,7 @@ async function requestEnrollmentConfirmation(
|
|
|
62
66
|
task_id: taskId,
|
|
63
67
|
state: "Approval required",
|
|
64
68
|
result: title,
|
|
65
|
-
next: "Review enrollment evidence",
|
|
69
|
+
next: uxText(UX_LANG, "Review enrollment evidence", "请审查 Enrollment 证据"),
|
|
66
70
|
});
|
|
67
71
|
const selected = await requestAuthorityDialog(pi, ctx, {
|
|
68
72
|
attention_id: randomUUID(),
|
|
@@ -75,8 +79,8 @@ async function requestEnrollmentConfirmation(
|
|
|
75
79
|
details,
|
|
76
80
|
signal,
|
|
77
81
|
actions: [
|
|
78
|
-
{ value: "confirm", label: "Confirm enrollment", description: "Create the Kernel-managed task" },
|
|
79
|
-
{ value: "cancel", label: "Cancel", description: "Leave planning artifacts and authority unchanged" },
|
|
82
|
+
{ value: "confirm", label: uxText(UX_LANG, "Confirm enrollment", "确认 Enrollment"), description: uxText(UX_LANG, "Create the Kernel-managed task", "创建 Kernel 托管任务") },
|
|
83
|
+
{ value: "cancel", label: uxText(UX_LANG, "Cancel", "取消"), description: uxText(UX_LANG, "Leave planning artifacts and authority unchanged", "不改动规划产物与权限状态") },
|
|
80
84
|
],
|
|
81
85
|
});
|
|
82
86
|
return selected === "confirm";
|
|
@@ -305,7 +309,7 @@ async function executeForegroundEnrollment(
|
|
|
305
309
|
|
|
306
310
|
try {
|
|
307
311
|
signal.throwIfAborted();
|
|
308
|
-
progress("preparing", `Preparing immutable Kernel owners for ${taskId}
|
|
312
|
+
progress("preparing", uxText(UX_LANG, `Preparing immutable Kernel owners for ${taskId}`, `正在为 ${taskId} 准备不可变 Kernel 所有者`));
|
|
309
313
|
const now = new Date().toISOString();
|
|
310
314
|
let taskIntent: Awaited<ReturnType<typeof readTaskIntent>>;
|
|
311
315
|
|
|
@@ -489,13 +493,13 @@ async function executeForegroundEnrollment(
|
|
|
489
493
|
`Owners: intent+workspace+claim+record checked`,
|
|
490
494
|
`Route: Kernel enrollment`,
|
|
491
495
|
].join("\n");
|
|
492
|
-
progress("awaiting_confirmation", "Waiting for exact literal-user confirmation");
|
|
496
|
+
progress("awaiting_confirmation", uxText(UX_LANG, "Waiting for exact literal-user confirmation", "等待本人确认"));
|
|
493
497
|
const confirmed = await requestEnrollmentConfirmation(
|
|
494
498
|
pi,
|
|
495
499
|
ctx,
|
|
496
500
|
taskId,
|
|
497
501
|
"enrollment",
|
|
498
|
-
"Create Kernel-managed task?",
|
|
502
|
+
uxText(UX_LANG, "Create Kernel-managed task?", "创建 Kernel 托管任务?"),
|
|
499
503
|
confirmationSummary,
|
|
500
504
|
confirmationDetails,
|
|
501
505
|
signal,
|
|
@@ -526,7 +530,7 @@ async function executeForegroundEnrollment(
|
|
|
526
530
|
return terminal(action, taskId, "blocked", stage, "Intent changed after confirmation; enrollment aborted before authority", "restore the intended snapshot and rerun enrollment");
|
|
527
531
|
}
|
|
528
532
|
|
|
529
|
-
progress("revalidating", "Revalidating immutable owners and the confirmed TaskIntent");
|
|
533
|
+
progress("revalidating", uxText(UX_LANG, "Revalidating immutable owners and the confirmed TaskIntent", "正在重新校验不可变所有者与已确认的 TaskIntent"));
|
|
530
534
|
const { unchanged } = await revalidatePiCanary(root, { task_id: taskId, now }, preparation);
|
|
531
535
|
if (!unchanged)
|
|
532
536
|
return terminal(action, taskId, "blocked", stage, "Workspace changed after confirmation; enrollment aborted before authority", "restore the intended snapshot and rerun enrollment");
|
|
@@ -555,7 +559,7 @@ async function executeForegroundEnrollment(
|
|
|
555
559
|
now,
|
|
556
560
|
};
|
|
557
561
|
|
|
558
|
-
progress("rehearsing", "Running the zero-write Kernel owner rehearsal");
|
|
562
|
+
progress("rehearsing", uxText(UX_LANG, "Running the zero-write Kernel owner rehearsal", "正在执行 Kernel 所有者零写入预演"));
|
|
559
563
|
const rehearsal = await runEnrollmentRehearsal(root, input, capability, registry);
|
|
560
564
|
if (!rehearsal.rehearsed || rehearsal.evidence.outcome !== "ready")
|
|
561
565
|
return terminal(action, taskId, "failed", stage, `Kernel enrollment rehearsal failed: ${rehearsal.evidence.blockers.join("; ")}`, "resolve the final-lock preconditions and retry");
|
|
@@ -65,6 +65,10 @@ import {
|
|
|
65
65
|
type UserAttentionReason,
|
|
66
66
|
} from "./pi-canary-interaction";
|
|
67
67
|
import { isToolFailureState, throwToolFailure, type ToolFailureV1 } from "./pi-canary-tool-failure";
|
|
68
|
+
import { resolveUxLanguage, uxText } from "./ux-language";
|
|
69
|
+
|
|
70
|
+
/** Host-native UI language; see ux-language.ts. Resolved once per process. */
|
|
71
|
+
const UX_LANG = resolveUxLanguage();
|
|
68
72
|
import { taskDiffIdentity, taskRevisionIdentity, captureGitTaskSnapshot } from "../runtime/workspace_scope";
|
|
69
73
|
import { reviewAdvisoryRecords, reviewReworkFindings } from "../runtime/assurance/coordinator";
|
|
70
74
|
import {
|
|
@@ -323,7 +327,7 @@ export default function (
|
|
|
323
327
|
task_id: claim.task_id,
|
|
324
328
|
state: "Blocked",
|
|
325
329
|
result: projection.error,
|
|
326
|
-
next: "Inspect authority state",
|
|
330
|
+
next: uxText(UX_LANG, "Inspect authority state", "检查权限状态"),
|
|
327
331
|
});
|
|
328
332
|
return;
|
|
329
333
|
}
|
|
@@ -395,8 +399,8 @@ export default function (
|
|
|
395
399
|
if (input?.task_id) presentTaskRail(ctx, {
|
|
396
400
|
task_id: input.task_id,
|
|
397
401
|
state: "Planning",
|
|
398
|
-
result: "Preparing enrollment",
|
|
399
|
-
next: "Review the native enrollment decision",
|
|
402
|
+
result: uxText(UX_LANG, "Preparing enrollment", "正在准备 Enrollment"),
|
|
403
|
+
next: uxText(UX_LANG, "Review the native enrollment decision", "请审查原生 Enrollment 决策"),
|
|
400
404
|
});
|
|
401
405
|
}
|
|
402
406
|
});
|
|
@@ -871,24 +875,24 @@ export default function (
|
|
|
871
875
|
presentTaskRail(ctx, {
|
|
872
876
|
task_id: taskId,
|
|
873
877
|
state: "Approval required",
|
|
874
|
-
result: `${operation} requires your confirmation`,
|
|
875
|
-
next: `Decide ${operation}`,
|
|
878
|
+
result: uxText(UX_LANG, `${operation} requires your confirmation`, `${operation} 需要您的确认`),
|
|
879
|
+
next: uxText(UX_LANG, `Decide ${operation}`, `请决策 ${operation}`),
|
|
876
880
|
});
|
|
877
881
|
const attention = {
|
|
878
882
|
attention_id: randomUUID(),
|
|
879
883
|
task_id: taskId,
|
|
880
884
|
reason: attentionReason,
|
|
881
|
-
label: `${operation} approval required`,
|
|
885
|
+
label: uxText(UX_LANG, `${operation} approval required`, `${operation} 待您批准`),
|
|
882
886
|
};
|
|
883
887
|
try {
|
|
884
888
|
const selected = await requestAuthorityDialog(pi, ctx, attention, {
|
|
885
|
-
title: `Authorize ${operation}?`,
|
|
889
|
+
title: uxText(UX_LANG, `Authorize ${operation}?`, `是否批准 ${operation}?`),
|
|
886
890
|
summary: dialogSummary,
|
|
887
891
|
details: dialogDetails,
|
|
888
892
|
signal: ctx.signal,
|
|
889
893
|
actions: [
|
|
890
|
-
{ value: "authorize", label: "Authorize", description: `Apply ${operation} after re-checking state` },
|
|
891
|
-
{ value: "cancel", label: "Cancel", description: "Leave managed authority unchanged" },
|
|
894
|
+
{ value: "authorize", label: uxText(UX_LANG, "Authorize", "批准"), description: uxText(UX_LANG, `Apply ${operation} after re-checking state`, `重新校验状态后应用 ${operation}`) },
|
|
895
|
+
{ value: "cancel", label: uxText(UX_LANG, "Cancel", "取消"), description: uxText(UX_LANG, "Leave managed authority unchanged", "保持托管权限状态不变") },
|
|
892
896
|
],
|
|
893
897
|
});
|
|
894
898
|
confirmed = selected === "authorize";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-native UI language for Immune-Brain TUI text (Task Rail sentences,
|
|
3
|
+
* authority dialog titles/actions, progress summaries). This layer is
|
|
4
|
+
* deterministic extension code, so conversational AGENTS.md reply-language
|
|
5
|
+
* rules cannot reach it; users opt in with IMM_UX_LANGUAGE (for example
|
|
6
|
+
* `zh`). Anything unrecognized keeps English, the published default.
|
|
7
|
+
*
|
|
8
|
+
* Boundary: only sentence-level interaction text follows this setting.
|
|
9
|
+
* Machine contracts and domain terms (state enums, operation ids, Task /
|
|
10
|
+
* Intent / Claim / Acceptance field labels, hashes, paths, CLI commands)
|
|
11
|
+
* always stay literal, as do agent-facing Tool result reasons and
|
|
12
|
+
* diagnostic notifications.
|
|
13
|
+
*/
|
|
14
|
+
export type UxLanguage = "en" | "zh";
|
|
15
|
+
|
|
16
|
+
export function resolveUxLanguage(
|
|
17
|
+
env: Readonly<Record<string, string | undefined>> = process.env,
|
|
18
|
+
): UxLanguage {
|
|
19
|
+
const raw = env.IMM_UX_LANGUAGE?.trim().toLowerCase();
|
|
20
|
+
if (raw === "zh" || raw === "zh-cn" || raw === "zh-tw" || raw === "chinese" || raw === "中文")
|
|
21
|
+
return "zh";
|
|
22
|
+
return "en";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Pick the interaction sentence for the resolved language. */
|
|
26
|
+
export function uxText(lang: UxLanguage, en: string, zh: string): string {
|
|
27
|
+
return lang === "zh" ? zh : en;
|
|
28
|
+
}
|
|
@@ -124,6 +124,17 @@ always run sequentially.
|
|
|
124
124
|
- Do not translate or rename machine contracts: schema fields, enum values,
|
|
125
125
|
CLI flags, JSON keys, file paths, tool names, API names,
|
|
126
126
|
and code identifiers stay literal.
|
|
127
|
+
- Host-native UI text (Task Rail sentences, authority dialog titles and
|
|
128
|
+
actions, enrollment progress summaries) is deterministic extension code and
|
|
129
|
+
cannot follow conversational rules; users localize it with the
|
|
130
|
+
`IMM_UX_LANGUAGE` environment variable (for example `IMM_UX_LANGUAGE=zh`).
|
|
131
|
+
Only sentence-level interaction text follows it; machine contracts, domain
|
|
132
|
+
field labels, and agent-facing Tool result reasons stay literal English.
|
|
133
|
+
- Internal role dispatches (`dispatch_role` and routed role contexts) carry
|
|
134
|
+
the user-facing interaction language as `interaction_language` in the
|
|
135
|
+
delegation context, so QA/Review/Explorer roles report in the user's
|
|
136
|
+
language while keeping machine contracts literal. Omit it to keep English
|
|
137
|
+
role output.
|
|
127
138
|
- Preserve `CONTEXT.md` canonical terms such as `Step`, `Plan`, `Spec`,
|
|
128
139
|
`Skill`, `Brainstorm`, `Executor`, `QA`, `Compounder`, `Learning`, and `ADR`;
|
|
129
140
|
add local-language explanations around them when helpful.
|
|
@@ -39,7 +39,7 @@ function probeHost(env = process.env, platform = process.platform, hostVersion)
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// plugins/immune-brain/runtime/plugin_version.ts
|
|
42
|
-
var PLUGIN_VERSION = "4.
|
|
42
|
+
var PLUGIN_VERSION = "4.1.0";
|
|
43
43
|
|
|
44
44
|
// plugins/immune-brain/runtime/claude/interaction.ts
|
|
45
45
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -1263,6 +1263,13 @@ function loadRolePrompt(role) {
|
|
|
1263
1263
|
}
|
|
1264
1264
|
throw new Error(`internal role prompt is not packaged: ${role}`);
|
|
1265
1265
|
}
|
|
1266
|
+
function readInteractionLanguage(context) {
|
|
1267
|
+
const raw = context.interaction_language;
|
|
1268
|
+
if (typeof raw !== "string")
|
|
1269
|
+
return null;
|
|
1270
|
+
const trimmed = raw.trim();
|
|
1271
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1272
|
+
}
|
|
1266
1273
|
function buildRoleDelegationPacket(input) {
|
|
1267
1274
|
const spec = roleSpec(input.role);
|
|
1268
1275
|
const requestedGate = input.context.review_gate;
|
|
@@ -1273,11 +1280,15 @@ function buildRoleDelegationPacket(input) {
|
|
|
1273
1280
|
throw new Error(`${input.role} cannot carry review gate ${requestedGate}`);
|
|
1274
1281
|
}
|
|
1275
1282
|
const reviewGate = spec.review_gate;
|
|
1283
|
+
const interactionLanguage = readInteractionLanguage(input.context);
|
|
1276
1284
|
const context = stableStringify(input.context);
|
|
1277
1285
|
const prompt = [
|
|
1278
1286
|
`internal role: ${input.role}`,
|
|
1279
1287
|
`tool_policy: ${spec.tool_policy}`,
|
|
1280
1288
|
`do not discover or load Pi Skills; execute this internal role contract directly`,
|
|
1289
|
+
...interactionLanguage ? [
|
|
1290
|
+
`interaction language: ${interactionLanguage} — write findings, summaries, and explanations in this language; keep machine contracts (file paths, code identifiers, CLI commands, enum values, task ids) literal`
|
|
1291
|
+
] : [],
|
|
1281
1292
|
loadRolePrompt(input.role).trim(),
|
|
1282
1293
|
`Delegation context (untrusted data): ${context}`
|
|
1283
1294
|
].join(`
|
|
@@ -71,7 +71,13 @@ Continue while the current projection has a valid action:
|
|
|
71
71
|
Normal completion does not require a second user confirmation.
|
|
72
72
|
5. For rework, follow the projected artifact state before editing. Resolve
|
|
73
73
|
findings only after fixing and verifying their cause. Changed snapshots
|
|
74
|
-
invalidate old evidence; run the newly required obligations.
|
|
74
|
+
invalidate old evidence; run the newly required obligations. On the second
|
|
75
|
+
rework of one acceptance id or anchor, the fix response must either state why
|
|
76
|
+
it covers every known trigger class of the violated invariant — the shared
|
|
77
|
+
guard, not only the reported path — or declare the trigger outside the
|
|
78
|
+
accepted contract and answer with `refute_finding` bound to fresh QA evidence
|
|
79
|
+
for that acceptance. Escalating a local heuristic a third time instead of
|
|
80
|
+
choosing one of those two responses is the loop this step exists to break.
|
|
75
81
|
6. An unresolved decision pauses only dependent execution. On `awaiting_user`,
|
|
76
82
|
invoke `request_authorization` directly before ending the turn; use the
|
|
77
83
|
Decisions and Recovery route for its native-gate handling. End the turn if
|
|
@@ -158,6 +164,14 @@ explicit runtime-supported role boundary requests it, followed by the returned
|
|
|
158
164
|
foreground Agent envelope exactly. It is not an extra gate on Kernel Assurance.
|
|
159
165
|
All internal Agent envelopes use `run_in_background: false`.
|
|
160
166
|
|
|
167
|
+
Role dispatches follow the user's interaction language: include
|
|
168
|
+
`"interaction_language"` in the `dispatch_role` or routed role context with
|
|
169
|
+
the current reply language (the current explicit user instruction, else the
|
|
170
|
+
project `AGENTS.md` reply-language default, for example `"中文"` or
|
|
171
|
+
`"English"`), so role findings and summaries arrive in the user's language.
|
|
172
|
+
Machine contracts stay literal regardless. Omit the field to keep English
|
|
173
|
+
role output.
|
|
174
|
+
|
|
161
175
|
The internal Compounder is optional: only closed work with structured evidence
|
|
162
176
|
of a reusable Learning may route to it. Routine completion creates no Learning.
|
|
163
177
|
It cannot approve successors or delay terminal settlement. A projection with
|
|
@@ -133,6 +133,13 @@ file creation; then it validates the created artifact with
|
|
|
133
133
|
continue through Kernel `revise_intent` authority and are not a Planner
|
|
134
134
|
overwrite path.
|
|
135
135
|
|
|
136
|
+
Before authoring a TaskIntent that adds a field or verdict branch to a state
|
|
137
|
+
machine, enumerate every consumer of that value and of the version gates around
|
|
138
|
+
it: the producing side, each branch or switch that reads it, and any migration or
|
|
139
|
+
replay path that carries historical copies. Name all of them in `scope_hint`. An
|
|
140
|
+
authoring pass that traces only the producer is the defect this enumeration
|
|
141
|
+
exists to prevent.
|
|
142
|
+
|
|
136
143
|
### Initiative Carrier Preference
|
|
137
144
|
|
|
138
145
|
For a large proposal split across multiple TaskIntents, exactly one planning
|
|
@@ -26,6 +26,14 @@ material task-local maintenance risks. Pure naming, length, complexity
|
|
|
26
26
|
thresholds, formatting, and design preference are not findings and must not
|
|
27
27
|
cause style-only rework.
|
|
28
28
|
|
|
29
|
+
When a finding repeats an acceptance id or anchor that already produced rework in
|
|
30
|
+
this task, do not file it as a fresh single-trigger defect. Name the invariant
|
|
31
|
+
class it belongs to and state in `evidence.trigger` why the previously accepted
|
|
32
|
+
fix does not cover that class: a second variant of the same trigger is a claim
|
|
33
|
+
about the fix's scope, not a new bug. A trigger the accepted contract explicitly
|
|
34
|
+
excludes is reported as an advisory note against that boundary, never as blocking
|
|
35
|
+
rework.
|
|
36
|
+
|
|
29
37
|
Return exactly one JSON object with the fields required by the Loop review
|
|
30
38
|
contract: `contract`, `role`, `task_id`, `snapshot_digest`, `decision` (`pass`
|
|
31
39
|
or `rework`), and for `pass` include `approval` (`kind`, `authority_role`,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/plugin_versioning.ts from the root package.json.
|
|
2
|
-
export const PLUGIN_VERSION = "4.
|
|
2
|
+
export const PLUGIN_VERSION = "4.1.0" as const;
|
|
@@ -26,6 +26,14 @@ material task-local maintenance risks. Pure naming, length, complexity
|
|
|
26
26
|
thresholds, formatting, and design preference are not findings and must not
|
|
27
27
|
cause style-only rework.
|
|
28
28
|
|
|
29
|
+
When a finding repeats an acceptance id or anchor that already produced rework in
|
|
30
|
+
this task, do not file it as a fresh single-trigger defect. Name the invariant
|
|
31
|
+
class it belongs to and state in `evidence.trigger` why the previously accepted
|
|
32
|
+
fix does not cover that class: a second variant of the same trigger is a claim
|
|
33
|
+
about the fix's scope, not a new bug. A trigger the accepted contract explicitly
|
|
34
|
+
excludes is reported as an advisory note against that boundary, never as blocking
|
|
35
|
+
rework.
|
|
36
|
+
|
|
29
37
|
Return exactly one JSON object with the fields required by the Loop review
|
|
30
38
|
contract: `contract`, `role`, `task_id`, `snapshot_digest`, `decision` (`pass`
|
|
31
39
|
or `rework`), and for `pass` include `approval` (`kind`, `authority_role`,
|
|
@@ -89,6 +89,14 @@ export interface RoleDelegationContext {
|
|
|
89
89
|
target_id?: string;
|
|
90
90
|
review_gate?: StableReviewGate;
|
|
91
91
|
changed_files_signature?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Optional user-facing interaction language (for example "中文" or
|
|
94
|
+
* "English"). The Parent sets it from the current explicit user
|
|
95
|
+
* instruction or the project reply-language default; when present the
|
|
96
|
+
* delegation prompt asks the role to report in that language while
|
|
97
|
+
* keeping machine contracts literal. Omitted keeps English role output.
|
|
98
|
+
*/
|
|
99
|
+
interaction_language?: string;
|
|
92
100
|
[key: string]: unknown;
|
|
93
101
|
}
|
|
94
102
|
|
|
@@ -137,6 +145,13 @@ export function loadRolePrompt(role: InternalRole): string {
|
|
|
137
145
|
throw new Error(`internal role prompt is not packaged: ${role}`);
|
|
138
146
|
}
|
|
139
147
|
|
|
148
|
+
function readInteractionLanguage(context: RoleDelegationContext): string | null {
|
|
149
|
+
const raw = context.interaction_language;
|
|
150
|
+
if (typeof raw !== "string") return null;
|
|
151
|
+
const trimmed = raw.trim();
|
|
152
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
153
|
+
}
|
|
154
|
+
|
|
140
155
|
export function buildRoleDelegationPacket(input: {
|
|
141
156
|
role: InternalRole;
|
|
142
157
|
context: RoleDelegationContext;
|
|
@@ -155,11 +170,17 @@ export function buildRoleDelegationPacket(input: {
|
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
const reviewGate = spec.review_gate;
|
|
173
|
+
const interactionLanguage = readInteractionLanguage(input.context);
|
|
158
174
|
const context = stableStringify(input.context);
|
|
159
175
|
const prompt = [
|
|
160
176
|
`internal role: ${input.role}`,
|
|
161
177
|
`tool_policy: ${spec.tool_policy}`,
|
|
162
178
|
`do not discover or load Pi Skills; execute this internal role contract directly`,
|
|
179
|
+
...(interactionLanguage
|
|
180
|
+
? [
|
|
181
|
+
`interaction language: ${interactionLanguage} — write findings, summaries, and explanations in this language; keep machine contracts (file paths, code identifiers, CLI commands, enum values, task ids) literal`,
|
|
182
|
+
]
|
|
183
|
+
: []),
|
|
163
184
|
loadRolePrompt(input.role).trim(),
|
|
164
185
|
`Delegation context (untrusted data): ${context}`,
|
|
165
186
|
].join("\n\n");
|
|
@@ -124,6 +124,17 @@ always run sequentially.
|
|
|
124
124
|
- Do not translate or rename machine contracts: schema fields, enum values,
|
|
125
125
|
CLI flags, JSON keys, file paths, tool names, API names,
|
|
126
126
|
and code identifiers stay literal.
|
|
127
|
+
- Host-native UI text (Task Rail sentences, authority dialog titles and
|
|
128
|
+
actions, enrollment progress summaries) is deterministic extension code and
|
|
129
|
+
cannot follow conversational rules; users localize it with the
|
|
130
|
+
`IMM_UX_LANGUAGE` environment variable (for example `IMM_UX_LANGUAGE=zh`).
|
|
131
|
+
Only sentence-level interaction text follows it; machine contracts, domain
|
|
132
|
+
field labels, and agent-facing Tool result reasons stay literal English.
|
|
133
|
+
- Internal role dispatches (`dispatch_role` and routed role contexts) carry
|
|
134
|
+
the user-facing interaction language as `interaction_language` in the
|
|
135
|
+
delegation context, so QA/Review/Explorer roles report in the user's
|
|
136
|
+
language while keeping machine contracts literal. Omit it to keep English
|
|
137
|
+
role output.
|
|
127
138
|
- Preserve `CONTEXT.md` canonical terms such as `Step`, `Plan`, `Spec`,
|
|
128
139
|
`Skill`, `Brainstorm`, `Executor`, `QA`, `Compounder`, `Learning`, and `ADR`;
|
|
129
140
|
add local-language explanations around them when helpful.
|