omni-pi 0.8.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.2 - 2026-04-07
4
+
5
+ ### Single-brain runtime
6
+
7
+ - removed the legacy multi-agent execution path and deleted the dormant subagent runtime
8
+ - simplified task execution to a single-brain retry flow with recovery notes instead of worker/expert escalation
9
+ - removed outdated worker, expert, and planner role/config concepts so runtime, config, and tests now match the current brain-only architecture
10
+
11
+ ### Startup
12
+
13
+ - defaulted first-run `omni` launcher installs to quiet startup so package resource listings do not appear unless the user opts in
14
+
15
+ ### Model setup and config
16
+
17
+ - simplified Omni model configuration to a single `brain` model assignment
18
+ - stopped offering automatic model discovery for `google-generative-ai`, which was not implemented
19
+ - stopped persisting fake placeholder API keys for local or unauthenticated custom providers
20
+
21
+ ### Dependencies
22
+
23
+ - removed the unused `pi-subagents` dependency
24
+ - added npm overrides for `@mozilla/readability`, `brace-expansion`, `picomatch`, and `vite`
25
+
3
26
  ## 0.8.0 - 2026-04-06
4
27
 
5
28
  ### Runtime and UX
package/PROVIDERS.md CHANGED
@@ -30,6 +30,8 @@ If a bundled provider already has valid auth in the Pi runtime, Omni-Pi may use
30
30
 
31
31
  Use `/manage-providers` to list bundled providers that currently have stored auth and remove that auth when needed.
32
32
 
33
+ Anthropic is API-key-only in Omni-Pi. Anthropic OAuth login is intentionally disabled.
34
+
33
35
  The bundled-provider list below is expected to stay in sync with the exported provider setup list in `src/model-setup.ts`. The test suite checks that this section matches the code list.
34
36
 
35
37
  ### Bundled provider list
package/README.md CHANGED
@@ -10,7 +10,7 @@ Requires Node.js 22 or newer.
10
10
 
11
11
  ## What It Does
12
12
 
13
- - Starts in normal Pi behavior by default while keeping Omni-Pi branding and UI.
13
+ - Starts in normal Pi behavior with an opinionated setup.
14
14
  - `/omni-mode` turns on Omni's specialized interview, plan, build, and verify workflow for the current project.
15
15
  - Keeps durable standards and project context in `.omni/`, even when Omni mode is off.
16
16
  - Writes specs, tasks, and progress into `.omni/` once Omni mode is enabled.
@@ -96,6 +96,8 @@ Use it when you want to configure:
96
96
 
97
97
  Use `/manage-providers` to remove stored auth for bundled Pi providers.
98
98
 
99
+ Anthropic is intentionally API-key-only in Omni-Pi. Anthropic OAuth login is disabled.
100
+
99
101
  See [PROVIDERS.md](PROVIDERS.md) for the current supported-provider list and auth-management split.
100
102
 
101
103
  ## Omni Mode
package/bin/omni.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawn } from "node:child_process";
4
- import { realpathSync } from "node:fs";
4
+ import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
5
+ import os from "node:os";
5
6
  import path from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
 
@@ -26,6 +27,60 @@ export function buildOmniEnvironment(baseEnv = process.env) {
26
27
  };
27
28
  }
28
29
 
30
+ function resolveAgentDir(baseEnv = process.env) {
31
+ const envDir = baseEnv.PI_CODING_AGENT_DIR;
32
+ if (!envDir) {
33
+ return path.join(os.homedir(), ".pi", "agent");
34
+ }
35
+
36
+ if (envDir === "~") {
37
+ return os.homedir();
38
+ }
39
+
40
+ if (envDir.startsWith("~/")) {
41
+ return path.join(os.homedir(), envDir.slice(2));
42
+ }
43
+
44
+ return envDir;
45
+ }
46
+
47
+ export function ensureQuietStartupDefault(baseEnv = process.env) {
48
+ const agentDir = resolveAgentDir(baseEnv);
49
+ const settingsFile = path.join(agentDir, "settings.json");
50
+
51
+ try {
52
+ const raw = readFileSync(settingsFile, "utf8");
53
+ const parsed = JSON.parse(raw);
54
+ if (
55
+ parsed &&
56
+ typeof parsed === "object" &&
57
+ parsed.quietStartup === undefined
58
+ ) {
59
+ writeFileSync(
60
+ settingsFile,
61
+ `${JSON.stringify({ ...parsed, quietStartup: true }, null, 2)}\n`,
62
+ "utf8",
63
+ );
64
+ }
65
+ } catch (error) {
66
+ const code =
67
+ error && typeof error === "object" && "code" in error
68
+ ? error.code
69
+ : undefined;
70
+
71
+ if (code !== "ENOENT") {
72
+ return;
73
+ }
74
+
75
+ mkdirSync(agentDir, { recursive: true });
76
+ writeFileSync(
77
+ settingsFile,
78
+ `${JSON.stringify({ quietStartup: true }, null, 2)}\n`,
79
+ "utf8",
80
+ );
81
+ }
82
+ }
83
+
29
84
  export function buildPiProcessSpec(
30
85
  argv = process.argv.slice(2),
31
86
  baseEnv = process.env,
@@ -38,6 +93,7 @@ export function buildPiProcessSpec(
38
93
  }
39
94
 
40
95
  export async function runOmni(argv = process.argv.slice(2), options = {}) {
96
+ ensureQuietStartupDefault(options.env);
41
97
  const spec = buildPiProcessSpec(argv, options.env);
42
98
 
43
99
  await new Promise((resolve, reject) => {
@@ -1,5 +1,6 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
 
3
+ import { disableAnthropicOAuth } from "../../src/anthropic-auth-guard.js";
3
4
  import { refreshAuthenticatedProviderModels } from "../../src/model-setup.js";
4
5
  import { registerOmniProviders } from "../../src/providers.js";
5
6
 
@@ -9,6 +10,7 @@ export default async function omniProvidersExtension(
9
10
  await registerOmniProviders(api);
10
11
 
11
12
  api.on("session_start", async (_event, ctx) => {
13
+ disableAnthropicOAuth(ctx.modelRegistry);
12
14
  await refreshAuthenticatedProviderModels(ctx.modelRegistry);
13
15
  });
14
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-pi",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Single-agent Pi package that interviews the user, documents the spec, and implements work in bounded slices.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -83,8 +83,13 @@
83
83
  "@mariozechner/pi-coding-agent": "^0.65.2",
84
84
  "glimpseui": "^0.6.2",
85
85
  "pi-interview": "^0.5.5",
86
- "pi-subagents": "^0.11.11",
87
86
  "pi-web-access": "^0.10.3",
88
87
  "zod": "^4.3.6"
88
+ },
89
+ "overrides": {
90
+ "@mozilla/readability": "0.6.0",
91
+ "brace-expansion": "5.0.5",
92
+ "picomatch": "4.0.4",
93
+ "vite": "7.3.2"
89
94
  }
90
95
  }
@@ -0,0 +1,95 @@
1
+ interface AnthropicCredential {
2
+ type: "api_key";
3
+ key: string;
4
+ }
5
+
6
+ interface AuthStorageLike {
7
+ get?(provider: string): AnthropicCredential | { type: "oauth" } | undefined;
8
+ getApiKey?(
9
+ provider: string,
10
+ options?: { includeFallback?: boolean },
11
+ ): Promise<string | undefined>;
12
+ hasAuth?(provider: string): boolean;
13
+ getOAuthProviders?(): Array<{ id: string; name?: string }>;
14
+ login?(providerId: string, callbacks: unknown): Promise<void>;
15
+ }
16
+
17
+ interface ModelRegistryLike {
18
+ authStorage: AuthStorageLike;
19
+ refresh(): void;
20
+ __omniAnthropicAuthGuardInstalled?: boolean;
21
+ }
22
+
23
+ function getAnthropicApiKeyFromStorage(
24
+ authStorage: AuthStorageLike,
25
+ ): string | undefined {
26
+ const envApiKey = process.env.ANTHROPIC_API_KEY?.trim();
27
+ if (envApiKey) {
28
+ return envApiKey;
29
+ }
30
+
31
+ const stored = authStorage.get?.("anthropic");
32
+ if (stored?.type === "api_key" && stored.key.trim()) {
33
+ return stored.key.trim();
34
+ }
35
+
36
+ return undefined;
37
+ }
38
+
39
+ export function disableAnthropicOAuthInAuthStorage(
40
+ authStorage: AuthStorageLike,
41
+ ): void {
42
+ const originalGetApiKey = authStorage.getApiKey?.bind(authStorage);
43
+ const originalGetOAuthProviders =
44
+ authStorage.getOAuthProviders?.bind(authStorage);
45
+ authStorage.getApiKey = async (
46
+ provider: string,
47
+ options?: { includeFallback?: boolean },
48
+ ) => {
49
+ if (provider !== "anthropic") {
50
+ return originalGetApiKey?.(provider, options);
51
+ }
52
+
53
+ return getAnthropicApiKeyFromStorage(authStorage);
54
+ };
55
+
56
+ const originalHasAuth = authStorage.hasAuth?.bind(authStorage);
57
+ authStorage.hasAuth = (provider: string) => {
58
+ if (provider !== "anthropic") {
59
+ return originalHasAuth?.(provider) ?? false;
60
+ }
61
+
62
+ return getAnthropicApiKeyFromStorage(authStorage) !== undefined;
63
+ };
64
+
65
+ authStorage.getOAuthProviders = () =>
66
+ (originalGetOAuthProviders?.() ?? []).filter(
67
+ (provider) => provider.id !== "anthropic",
68
+ );
69
+
70
+ const originalLogin = authStorage.login?.bind(authStorage);
71
+ authStorage.login = async (providerId: string, callbacks: unknown) => {
72
+ if (providerId === "anthropic") {
73
+ throw new Error(
74
+ "Anthropic OAuth login is disabled in Omni-Pi. Use an Anthropic API key instead.",
75
+ );
76
+ }
77
+
78
+ await originalLogin?.(providerId, callbacks);
79
+ };
80
+ }
81
+
82
+ export function disableAnthropicOAuth(modelRegistry: ModelRegistryLike): void {
83
+ disableAnthropicOAuthInAuthStorage(modelRegistry.authStorage);
84
+
85
+ if (modelRegistry.__omniAnthropicAuthGuardInstalled) {
86
+ return;
87
+ }
88
+
89
+ const originalRefresh = modelRegistry.refresh.bind(modelRegistry);
90
+ modelRegistry.refresh = () => {
91
+ originalRefresh();
92
+ disableAnthropicOAuthInAuthStorage(modelRegistry.authStorage);
93
+ };
94
+ modelRegistry.__omniAnthropicAuthGuardInstalled = true;
95
+ }
package/src/brain.ts CHANGED
@@ -33,7 +33,7 @@ Your workflow is mandatory:
33
33
 
34
34
  Behavior rules:
35
35
  - Stay friendly, plain-spoken, direct, and efficient with tokens/context.
36
- - Do not expose planner/worker/expert role handoffs. Everything happens behind the scenes.
36
+ - Do not expose internal handoffs or legacy role concepts. Everything happens behind the scenes.
37
37
  - If the request is not fully clear enough to implement safely without guessing, use the interview tool to ask targeted clarification questions instead of asking them in chat.
38
38
  - Do not start editing code until the spec is explicit enough to avoid guessing.
39
39
  - In this repo, treat direct user instructions as requested Omni app/product behavior by default unless the user explicitly marks them as meta instructions for the agent/session.
package/src/config.ts CHANGED
@@ -6,13 +6,8 @@ import { AVAILABLE_MODELS } from "./providers.js";
6
6
 
7
7
  export const DEFAULT_CONFIG: OmniConfig = {
8
8
  models: {
9
- worker: "anthropic/claude-sonnet-4-6",
10
- expert: "openai/gpt-5.4",
11
- planner: "openai/gpt-5.4",
12
9
  brain: "anthropic/claude-opus-4-6",
13
10
  },
14
- retryLimit: 2,
15
- chainEnabled: false,
16
11
  cleanupCompletedPlans: false,
17
12
  };
18
13
 
@@ -38,24 +33,14 @@ function parseModelTable(
38
33
  if (rowMatch) {
39
34
  const agent = rowMatch[1].trim().toLowerCase();
40
35
  const model = rowMatch[2].trim();
41
- models[agent] = model;
36
+ if (model.length > 0) {
37
+ models[agent] = model;
38
+ }
42
39
  }
43
40
  }
44
41
  return models;
45
42
  }
46
43
 
47
- function parseRetryLimit(content: string): number {
48
- const match = content.match(
49
- /(?:Implementation retries before the plan must be tightened|Worker retries before expert takeover):\s*(\d+)/u,
50
- );
51
- return match ? Number.parseInt(match[1], 10) : DEFAULT_CONFIG.retryLimit;
52
- }
53
-
54
- function parseChainEnabled(content: string): boolean {
55
- const match = content.match(/Chain execution enabled:\s*(true|false)/u);
56
- return match ? match[1] === "true" : DEFAULT_CONFIG.chainEnabled;
57
- }
58
-
59
44
  function parseCleanupCompletedPlans(content: string): boolean {
60
45
  const match = content.match(/Delete completed plan files:\s*(true|false)/u);
61
46
  return match ? match[1] === "true" : DEFAULT_CONFIG.cleanupCompletedPlans;
@@ -66,17 +51,11 @@ export async function readConfig(rootDir: string): Promise<OmniConfig> {
66
51
  try {
67
52
  const content = await readFile(configPath, "utf8");
68
53
  const models = parseModelTable(content, "## Models");
69
- const retryLimit = parseRetryLimit(content);
70
54
 
71
55
  return {
72
56
  models: {
73
- worker: models.worker ?? DEFAULT_CONFIG.models.worker,
74
- expert: models.expert ?? DEFAULT_CONFIG.models.expert,
75
- planner: models.planner ?? DEFAULT_CONFIG.models.planner,
76
57
  brain: models.brain ?? DEFAULT_CONFIG.models.brain,
77
58
  },
78
- retryLimit,
79
- chainEnabled: parseChainEnabled(content),
80
59
  cleanupCompletedPlans: parseCleanupCompletedPlans(content),
81
60
  };
82
61
  } catch {
@@ -91,19 +70,8 @@ function renderConfigContent(config: OmniConfig): string {
91
70
 
92
71
  | Agent | Model |
93
72
  |-------|-------|
94
- | worker | ${config.models.worker} |
95
- | expert | ${config.models.expert} |
96
- | planner | ${config.models.planner} |
97
73
  | brain | ${config.models.brain} |
98
74
 
99
- ## Retry Policy
100
-
101
- Implementation retries before the plan must be tightened: ${config.retryLimit}
102
-
103
- ## Execution
104
-
105
- Chain execution enabled: ${config.chainEnabled}
106
-
107
75
  ## Memory
108
76
 
109
77
  Delete completed plan files: ${config.cleanupCompletedPlans}
@@ -125,7 +93,7 @@ export async function updateModelConfig(
125
93
  model: string,
126
94
  ): Promise<OmniConfig> {
127
95
  const config = await readConfig(rootDir);
128
- const validAgents = ["worker", "expert", "planner", "brain"] as const;
96
+ const validAgents = ["brain"] as const;
129
97
  const normalizedAgent = agent.toLowerCase() as (typeof validAgents)[number];
130
98
 
131
99
  if (!validAgents.includes(normalizedAgent)) {
package/src/contracts.ts CHANGED
@@ -32,7 +32,6 @@ export interface TaskBrief {
32
32
  contextFiles: string[];
33
33
  skills: string[];
34
34
  doneCriteria: string[];
35
- role: "worker" | "expert";
36
35
  status: TaskStatus;
37
36
  dependsOn: string[];
38
37
  }
@@ -51,20 +50,6 @@ export interface TaskAttemptResult {
51
50
  modifiedFiles?: string[];
52
51
  }
53
52
 
54
- export interface EscalationBrief {
55
- taskId: string;
56
- priorAttempts: number;
57
- failureLogs: string[];
58
- expertObjective: string;
59
- verificationResults?: Array<{
60
- command: string;
61
- passed: boolean;
62
- stdout: string;
63
- stderr: string;
64
- }>;
65
- modifiedFiles?: string[];
66
- }
67
-
68
53
  export interface SkillCandidate {
69
54
  name: string;
70
55
  reason: string;
@@ -94,7 +79,7 @@ export interface PresetConfig {
94
79
  maxTasks: number;
95
80
  skipInterview: boolean;
96
81
  requireVerification: boolean;
97
- workerHint: string;
82
+ executionHint: string;
98
83
  }
99
84
 
100
85
  export const WORKFLOW_PRESETS: Record<WorkflowPreset, PresetConfig> = {
@@ -105,7 +90,7 @@ export const WORKFLOW_PRESETS: Record<WorkflowPreset, PresetConfig> = {
105
90
  maxTasks: 2,
106
91
  skipInterview: true,
107
92
  requireVerification: true,
108
- workerHint:
93
+ executionHint:
109
94
  "Focus on the root cause. Write a regression test before fixing.",
110
95
  },
111
96
  feature: {
@@ -115,7 +100,7 @@ export const WORKFLOW_PRESETS: Record<WorkflowPreset, PresetConfig> = {
115
100
  maxTasks: 8,
116
101
  skipInterview: false,
117
102
  requireVerification: true,
118
- workerHint: "Follow the spec. Keep tasks bounded and verifiable.",
103
+ executionHint: "Follow the spec. Keep tasks bounded and verifiable.",
119
104
  },
120
105
  refactor: {
121
106
  name: "refactor",
@@ -123,7 +108,7 @@ export const WORKFLOW_PRESETS: Record<WorkflowPreset, PresetConfig> = {
123
108
  maxTasks: 5,
124
109
  skipInterview: true,
125
110
  requireVerification: true,
126
- workerHint:
111
+ executionHint:
127
112
  "Preserve all existing behavior. Run the full test suite after each change.",
128
113
  },
129
114
  spike: {
@@ -133,7 +118,7 @@ export const WORKFLOW_PRESETS: Record<WorkflowPreset, PresetConfig> = {
133
118
  maxTasks: 1,
134
119
  skipInterview: true,
135
120
  requireVerification: false,
136
- workerHint: "Explore freely. Document findings in .omni/research/.",
121
+ executionHint: "Explore freely. Document findings in .omni/research/.",
137
122
  },
138
123
  "security-audit": {
139
124
  name: "security-audit",
@@ -141,7 +126,7 @@ export const WORKFLOW_PRESETS: Record<WorkflowPreset, PresetConfig> = {
141
126
  maxTasks: 3,
142
127
  skipInterview: true,
143
128
  requireVerification: false,
144
- workerHint:
129
+ executionHint:
145
130
  "Analyze for OWASP Top 10, secrets in code, dependency vulnerabilities. Do not modify source code.",
146
131
  },
147
132
  };
@@ -162,13 +147,8 @@ export function detectPreset(
162
147
 
163
148
  export interface OmniConfig {
164
149
  models: {
165
- worker: string;
166
- expert: string;
167
- planner: string;
168
150
  brain: string;
169
151
  };
170
- retryLimit: number;
171
- chainEnabled: boolean;
172
152
  cleanupCompletedPlans: boolean;
173
153
  }
174
154
 
package/src/doctor.ts CHANGED
@@ -50,15 +50,12 @@ async function checkConfigParseable(
50
50
  ): Promise<DiagnosticResult> {
51
51
  try {
52
52
  const config = await readConfig(rootDir);
53
- const roles = ["worker", "expert", "planner", "brain"] as const;
54
- for (const role of roles) {
55
- if (!config.models[role]) {
56
- return {
57
- name: "config",
58
- level: "yellow",
59
- message: `Model for ${role} is empty in CONFIG.md.`,
60
- };
61
- }
53
+ if (!config.models.brain) {
54
+ return {
55
+ name: "config",
56
+ level: "yellow",
57
+ message: "Model for brain is empty in CONFIG.md.",
58
+ };
62
59
  }
63
60
  return { name: "config", level: "green", message: "Config is valid." };
64
61
  } catch {
@@ -473,21 +473,32 @@ export async function setupCustomProviderModel(
473
473
  apiKey,
474
474
  };
475
475
 
476
+ const setupOptions =
477
+ apiChoice === "google-generative-ai"
478
+ ? [
479
+ {
480
+ label: "Add a single model manually",
481
+ value: "manual",
482
+ searchText: "manual single model",
483
+ },
484
+ ]
485
+ : [
486
+ {
487
+ label: "Discover models automatically",
488
+ value: "discover",
489
+ searchText: "discover automatic provider models",
490
+ },
491
+ {
492
+ label: "Add a single model manually",
493
+ value: "manual",
494
+ searchText: "manual single model",
495
+ },
496
+ ];
497
+
476
498
  const setupMode = await searchableSelect(
477
499
  ui,
478
500
  `How should Omni-Pi configure ${provider}?`,
479
- [
480
- {
481
- label: "Discover models automatically",
482
- value: "discover",
483
- searchText: "discover automatic provider models",
484
- },
485
- {
486
- label: "Add a single model manually",
487
- value: "manual",
488
- searchText: "manual single model",
489
- },
490
- ],
501
+ setupOptions,
491
502
  );
492
503
  if (!setupMode) {
493
504
  return { summary: "Custom provider setup cancelled." };
@@ -612,7 +623,7 @@ async function setupDiscoveredProvider(
612
623
 
613
624
  export function buildCustomProviderConfigUpdate(
614
625
  current: ModelsJsonProviderConfig,
615
- provider: string,
626
+ _provider: string,
616
627
  submission: BrowserCustomModelSubmission,
617
628
  ): ModelsJsonProviderConfig {
618
629
  const modelId = submission.modelId.trim();
@@ -624,8 +635,11 @@ export function buildCustomProviderConfigUpdate(
624
635
  ...current,
625
636
  baseUrl: normalizeBaseUrl(submission.baseUrl),
626
637
  api: submission.api,
627
- apiKey:
628
- submission.apiKey?.trim() || current.apiKey || `${provider}-local-key`,
638
+ ...(submission.apiKey?.trim()
639
+ ? { apiKey: submission.apiKey.trim() }
640
+ : current.apiKey?.trim()
641
+ ? { apiKey: current.apiKey.trim() }
642
+ : {}),
629
643
  authHeader:
630
644
  submission.api === "openai-completions" ||
631
645
  submission.api === "openai-responses",
@@ -643,7 +657,7 @@ export function buildCustomProviderConfigUpdate(
643
657
 
644
658
  export function buildDiscoveredProviderConfigUpdate(
645
659
  current: ModelsJsonProviderConfig,
646
- provider: string,
660
+ _provider: string,
647
661
  submission: ProviderConnectionSubmission,
648
662
  discovered: OmniProviderModel[],
649
663
  ): ModelsJsonProviderConfig {
@@ -656,8 +670,11 @@ export function buildDiscoveredProviderConfigUpdate(
656
670
  ...current,
657
671
  baseUrl: normalizeBaseUrl(submission.baseUrl),
658
672
  api: submission.api,
659
- apiKey:
660
- submission.apiKey?.trim() || current.apiKey || `${provider}-local-key`,
673
+ ...(submission.apiKey?.trim()
674
+ ? { apiKey: submission.apiKey.trim() }
675
+ : current.apiKey?.trim()
676
+ ? { apiKey: current.apiKey.trim() }
677
+ : {}),
661
678
  authHeader:
662
679
  submission.api === "openai-completions" ||
663
680
  submission.api === "openai-responses",
@@ -803,26 +820,8 @@ function renderBrowserModelSelectionPage(
803
820
  <main>
804
821
  <section class="panel">
805
822
  <h1>Configure Omni-Pi models</h1>
806
- <p>Search Pi's available models for each role, then save all assignments in one pass.</p>
823
+ <p>Search Pi's available models, then save the single brain assignment.</p>
807
824
  <div class="grid">
808
- <section class="role" data-role="worker">
809
- <h2>worker</h2>
810
- <label>Search<input id="search-worker" placeholder="Filter models" /></label>
811
- <label>Selected model<input id="selected-worker" value="${escapeHtml(currentModels.worker)}" /></label>
812
- <div id="results-worker" class="list"></div>
813
- </section>
814
- <section class="role" data-role="expert">
815
- <h2>expert</h2>
816
- <label>Search<input id="search-expert" placeholder="Filter models" /></label>
817
- <label>Selected model<input id="selected-expert" value="${escapeHtml(currentModels.expert)}" /></label>
818
- <div id="results-expert" class="list"></div>
819
- </section>
820
- <section class="role" data-role="planner">
821
- <h2>planner</h2>
822
- <label>Search<input id="search-planner" placeholder="Filter models" /></label>
823
- <label>Selected model<input id="selected-planner" value="${escapeHtml(currentModels.planner)}" /></label>
824
- <div id="results-planner" class="list"></div>
825
- </section>
826
825
  <section class="role" data-role="brain">
827
826
  <h2>brain</h2>
828
827
  <label>Search<input id="search-brain" placeholder="Filter models" /></label>
@@ -830,13 +829,13 @@ function renderBrowserModelSelectionPage(
830
829
  <div id="results-brain" class="list"></div>
831
830
  </section>
832
831
  </div>
833
- <button id="save">Save Model Assignments</button>
832
+ <button id="save">Save Brain Model</button>
834
833
  </section>
835
834
  </main>
836
835
  <script>
837
836
  const models = ${modelsJson};
838
837
  const currentModels = ${currentModelsJson};
839
- const roles = ['worker', 'expert', 'planner', 'brain'];
838
+ const roles = ['brain'];
840
839
 
841
840
  const renderRole = (role) => {
842
841
  const searchEl = document.getElementById('search-' + role);
@@ -871,9 +870,6 @@ function renderBrowserModelSelectionPage(
871
870
  headers: { 'Content-Type': 'application/json' },
872
871
  body: JSON.stringify({
873
872
  selectedModels: {
874
- worker: document.getElementById('selected-worker').value,
875
- expert: document.getElementById('selected-expert').value,
876
- planner: document.getElementById('selected-planner').value,
877
873
  brain: document.getElementById('selected-brain').value
878
874
  }
879
875
  })
package/src/planning.ts CHANGED
@@ -119,7 +119,6 @@ function buildBootstrapTasks(repoSignals: RepoSignals): TaskBrief[] {
119
119
  "Constraints are captured.",
120
120
  "Success criteria are explicit.",
121
121
  ],
122
- role: "worker",
123
122
  status: "todo",
124
123
  dependsOn: [],
125
124
  },
@@ -135,7 +134,6 @@ function buildBootstrapTasks(repoSignals: RepoSignals): TaskBrief[] {
135
134
  "Each task has explicit done criteria.",
136
135
  "Verification requirements are listed.",
137
136
  ],
138
- role: "worker",
139
137
  status: "todo",
140
138
  dependsOn: ["T01"],
141
139
  },
@@ -156,7 +154,6 @@ function buildBootstrapTasks(repoSignals: RepoSignals): TaskBrief[] {
156
154
  "Browser testing expectations are documented.",
157
155
  "The verification plan names the browser toolchain.",
158
156
  ],
159
- role: "worker",
160
157
  status: "todo",
161
158
  dependsOn: ["T02"],
162
159
  });
@@ -289,7 +286,7 @@ export function createInitialSpec(
289
286
  ];
290
287
 
291
288
  if (presetConfig) {
292
- architecture.push(`Implementation hint: ${presetConfig.workerHint}`);
289
+ architecture.push(`Implementation hint: ${presetConfig.executionHint}`);
293
290
  }
294
291
 
295
292
  if (planningCtx?.existingDecisions.length) {
@@ -383,10 +380,6 @@ ${rows.join("\n")}
383
380
  export function renderTestsMarkdown(repoSignals: RepoSignals): string {
384
381
  const projectChecks = ["npm test"];
385
382
 
386
- if (repoSignals.tools.includes("vitest")) {
387
- projectChecks.push("npm run test");
388
- }
389
-
390
383
  if (repoSignals.tools.includes("playwright")) {
391
384
  projectChecks.push("npx playwright test");
392
385
  }