takomi 2.1.45 → 2.5.1

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/.pi/README.md CHANGED
@@ -33,7 +33,7 @@ Inside Pi, use:
33
33
  - `/takomi design [prompt]` to run UI/UX design against the agreed project direction
34
34
  - `/takomi build [prompt]` to implement while cross-checking against approved UI/design artifacts
35
35
  - `/takomi plan [title]` to create a Genesis-first orchestration session that can expand through Design and Build
36
- - `/takomi mode direct`, `/takomi mode orchestrate`, or `/takomi mode review` to choose the session operating mode
36
+ - `/takomi mode idle|code|review|orchestrate` to choose the main-agent mode; Genesis, Design, and Build remain separate lifecycle stages
37
37
  - `/takomi gate auto` to continue approved plans automatically and explicitly authorize project-local agents for this session
38
38
  - `/takomi gate review` (or `/takomi gate manual`) to return to the user after each task with results and verification guidance and revoke that authorization
39
39
  - `/takomi subagents on` or `/takomi subagents off` to allow or disable delegated subagents
@@ -100,12 +100,12 @@ So when working on packaging, agents should distinguish between:
100
100
  - Design stage includes a Gemini-oriented hint for model selection.
101
101
  - Build is treated as a workflow/stage, not as a separate specialist agent.
102
102
  - A fresh orchestration session starts with a Genesis foundation task, then expands Design and Build only when the scope justifies it.
103
- - The main execution roles remain things like `orchestrator`, `coder`, `designer`, `architect`, and `reviewer`.
103
+ - The only public Takomi personas are `architect`, `designer`, `coder`, `worker`, `reviewer`, and `orchestrator`. Overlapping upstream built-ins remain hidden behind the execution engine.
104
104
  - Agent discovery prefers `project/.pi/agents/`, also supports legacy `project/.agents/`, and falls back to Pi's configured user agent directory so new projects can reuse the global Takomi agent pack without hard-coding `~/.pi` assumptions.
105
105
  - Orchestrator sessions run in hybrid mode:
106
106
  - human-readable docs live under `docs/tasks/orchestrator-sessions/<sessionId>/`
107
107
  - machine state lives under `.pi/takomi/orchestrator/<sessionId>.json`
108
- - Task packets can carry `workflow`, `skills`, `preferredModel`, `fallbackModels`, `preferredThinking`, `executionHint`, `conversationId`, and `checklist` metadata.
108
+ - Task packets use canonical persona names and can carry `workflow`, `skills`, confirmed `preferredModel`, explicit `fallbackModels`, `requiredCapabilities`, `preferredThinking`, `executionHint`, `conversationId`, and `checklist` metadata.
109
109
  - Direct `takomi_subagent` calls build a `TakomiDelegationPlan` before launch. In auto mode the plan launches immediately; in manual mode the plan is returned for review until `confirmLaunch=true` is supplied.
110
110
  - The subagent tool supports `conversationId`, so reviewed work can be sent back to the same agent for continuation instead of restarting from scratch.
111
111
  - The subagent tool supports Pi-style single, parallel `tasks`, sequential `chain`, explicit `context=fork|fresh`, `async=true`, and native read/control actions such as `action=list`, `action=status`, `action=doctor`, `action=interrupt`, and `action=resume`.
@@ -119,9 +119,9 @@ So when working on packaging, agents should distinguish between:
119
119
  - Use Pi's native result expansion, `Alt+T`, or `/takomi subagents expand` to inspect detailed subagent output.
120
120
  - Takomi still tracks active runs internally for status and review continuity, but board synchronization is explicit: run with `takomi_subagent`, then update the board.
121
121
  - `takomi-context-manager` reduces prompt bloat by replacing the always-on skill description dump with a names-only Skill Index plus progressive `skill_manifest`/`skill_load` tools.
122
- - `takomi-context-manager` treats `/takomi routing` as the source of truth for model-routing policy via `.pi/settings.json -> takomi.modelRoutingPolicyFile`.
122
+ - `model-routing.md` is advisory model-facing guidance. Executable provider allowlists and persona defaults live only under `takomi.routing` in global/project settings.
123
123
  - `takomi-context-manager` gates `takomi_subagent` when model-routing context has not been loaded, provides the routing policy, and tells the agent to retry.
124
- - `takomi-context-manager` can correct safe wrong-provider model requests, block or pause on policy violations, and ask the user whether to retry with an approved model or stop.
124
+ - `takomi-context-manager` never performs provider-family substitution. It blocks unapproved exact IDs and changes models only after explicit user selection.
125
125
  - `takomi-context-manager` detects known duplicate global/project Takomi extension paths in `context_report` to help diagnose tool registration conflicts.
126
126
  - `context_report` restores context-manager snapshots and Pi tool-result history after reload/restart and explicitly labels gaps; it is not a replacement for Pi's Alt-C token/session stats.
127
127
  - `context_report` defaults to compact `mode: "summary"`; use `mode: "verbose"` for full diagnostics or `mode: "problems"` for attention-only output. `verbose: true` remains a compatibility alias. `/context-report` exposes `summary`, `verbose`, and `problems` as slash-command argument completions.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: architect
3
3
  description: Gather requirements, design architecture, and create implementation-ready plans.
4
- tools: read,bash,grep,find,ls
4
+ tools: read,bash,write,grep,find,ls
5
5
  ---
6
6
  You are the Takomi Architect.
7
7
 
@@ -46,7 +46,7 @@ Produce implementation-ready structure when relevant:
46
46
 
47
47
  This is technical planning, not the Takomi Design lifecycle stage. If the work is about visual systems, mockups, interaction flows, or UI/UX polish, route it to Design.
48
48
 
49
- Stay at architecture level; do not write product code.
49
+ Stay at architecture level; do not write product code. You may create or replace planning Markdown artifacts when the task requires them.
50
50
 
51
51
  ## Phase 4: Planning
52
52
  Create a practical plan with:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: designer
3
3
  description: Translate requirements into build-ready UI, UX, visual systems, and interaction direction.
4
- tools: read,bash,grep,find,ls
4
+ tools: read,bash,write,grep,find,ls
5
5
  ---
6
6
  You are the Takomi Design Specialist.
7
7
 
@@ -64,7 +64,7 @@ Report:
64
64
  - pages/components ready for implementation
65
65
  - open design risks or follow-up
66
66
 
67
- Update builder guidance when mockups should be treated as implementation source of truth.
67
+ Update builder guidance when mockups should be treated as implementation source of truth. You may create or replace UI/UX Markdown artifacts when the task requires them.
68
68
 
69
69
  ## Anti-Patterns
70
70
  - do not stay vague or inspirational only
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: worker
3
+ description: Execute write-capable non-coding tasks and produce repository artifacts with controlled scope.
4
+ tools: read,bash,edit,write,grep,find,ls
5
+ ---
6
+ You are the Takomi Worker.
7
+
8
+ Your mode pattern is:
9
+ READ -> UNDERSTAND -> EXECUTE -> VERIFY -> HANDOFF.
10
+
11
+ ## Role Scope
12
+ - documentation and repository artifact creation
13
+ - configuration and content updates
14
+ - mechanical or operational tasks that do not require the Coder persona
15
+ - execution of an approved, well-scoped direction
16
+
17
+ Use Coder for application implementation, debugging, refactoring, and tests. Use Architect for requirements, architecture, and planning decisions. Use Designer for UI/UX work. Worker executes a clear non-coding task; it does not invent product or architecture decisions.
18
+
19
+ ## Working Rules
20
+ - Read the task, relevant files, and project instructions before editing.
21
+ - Make the smallest complete change within scope.
22
+ - Create or modify the requested artifacts directly.
23
+ - Escalate missing product or architecture decisions instead of guessing.
24
+ - Verify outputs with the strongest practical checks.
25
+ - Do not expand scope or perform unrelated cleanup.
26
+
27
+ ## Handoff
28
+ Report:
29
+ - artifacts created or changed
30
+ - validation performed
31
+ - anything left incomplete
32
+ - decisions still needed
@@ -1,115 +1,27 @@
1
- import { readFile } from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
2
  import type { ContextManagerState } from "./state";
6
3
  import { recordBlocked } from "./state";
7
- import { resolveTakomiRoutingPolicy } from "../takomi-runtime/routing-policy";
8
- import { approvedModelEquivalent, isTakomiModelApproved } from "../takomi-runtime/model-routing-defaults";
4
+ import { isTakomiModelApproved, loadTakomiModelRoutingSnapshot } from "../takomi-runtime/model-routing-defaults";
9
5
  import { persistReportSnapshot } from "./session-state";
10
6
  import { sanitizePresentation } from "./tool-renderers";
11
7
 
12
- type Settings = {
13
- takomi?: { modelRoutingPolicyFile?: string };
14
- subagents?: { agentOverrides?: Record<string, unknown> };
15
- };
16
-
17
8
  type ModelPolicySnapshot = {
18
9
  approvedModels: string[];
19
10
  preferredModels: string[];
20
11
  sourceFiles: string[];
12
+ policyConflicts?: string[];
21
13
  };
22
14
 
23
- function asRecord(value: unknown): Record<string, unknown> {
24
- return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
25
- }
26
-
27
- async function readSettingsFile(filePath: string): Promise<Settings> {
28
- try {
29
- return JSON.parse(await readFile(filePath, "utf8")) as Settings;
30
- } catch {
31
- return {};
32
- }
33
- }
34
-
35
- function mergeSettings(globalSettings: Settings, projectSettings: Settings): Settings {
36
- const globalOverrides = asRecord(globalSettings.subagents?.agentOverrides);
37
- const projectOverrides = asRecord(projectSettings.subagents?.agentOverrides);
15
+ async function loadSnapshot(cwd: string): Promise<ModelPolicySnapshot> {
16
+ const snapshot = await loadTakomiModelRoutingSnapshot(cwd);
38
17
  return {
39
- ...globalSettings,
40
- ...projectSettings,
41
- takomi: { ...(globalSettings.takomi ?? {}), ...(projectSettings.takomi ?? {}) },
42
- subagents: {
43
- ...(globalSettings.subagents ?? {}),
44
- ...(projectSettings.subagents ?? {}),
45
- agentOverrides: { ...globalOverrides, ...projectOverrides },
46
- },
18
+ approvedModels: snapshot.approvedModels,
19
+ preferredModels: snapshot.preferredModels,
20
+ sourceFiles: snapshot.sourceFiles,
21
+ policyConflicts: snapshot.policyConflicts,
47
22
  };
48
23
  }
49
24
 
50
- async function readSettings(cwd: string): Promise<Settings> {
51
- const globalSettings = await readSettingsFile(path.join(os.homedir(), ".pi", "agent", "settings.json"));
52
- const projectSettings = await readSettingsFile(path.resolve(cwd, ".pi/settings.json"));
53
- return mergeSettings(globalSettings, projectSettings);
54
- }
55
-
56
- function modelFamily(model: string): string {
57
- return model.split("/").at(-1)?.toLowerCase() ?? model.toLowerCase();
58
- }
59
-
60
- function unique(values: string[]): string[] {
61
- return [...new Set(values.filter(Boolean))];
62
- }
63
-
64
- function collectModelsFromSettings(settings: Settings): string[] {
65
- const overrides = asRecord(settings.subagents?.agentOverrides);
66
- const models: string[] = [];
67
- for (const value of Object.values(overrides)) {
68
- const record = asRecord(value);
69
- if (typeof record.model === "string") models.push(record.model);
70
- if (Array.isArray(record.fallbackModels)) {
71
- for (const fallback of record.fallbackModels) if (typeof fallback === "string") models.push(fallback.split(":")[0]);
72
- }
73
- }
74
- return models;
75
- }
76
-
77
- function isModelLike(value: string): boolean {
78
- const lower = value.toLowerCase();
79
- return /(^|\/)(gpt|claude|gemini|o[0-9]|qwen|deepseek|llama|mistral|kimi|grok|sonnet|haiku|opus|codex|mini|max)/i.test(lower)
80
- || lower.includes("oauth-router/")
81
- || lower.includes("openai-codex/")
82
- || lower.includes("lmstudio/");
83
- }
84
-
85
- function extractPreferredProvider(text: string): string | undefined {
86
- const match = text.match(/(?:preferred|default)\s+(?:provider|router)(?:\s*\/\s*(?:provider|router))?\s*:\s*([a-z0-9-]+)/i)
87
- ?? text.match(/use\s+([a-z0-9-]+)\s+as\s+(?:the\s+)?(?:provider|router)/i);
88
- return match?.[1];
89
- }
90
-
91
- function collectModelsFromPolicy(text: string): string[] {
92
- // Providerless names such as "GPT-5.5" are intent labels unless the policy
93
- // declares a preferred provider/router header.
94
- const explicit = (text.match(/[a-z0-9-]+\/[a-z0-9._-]+/gi) ?? []).filter(isModelLike);
95
- const preferredProvider = extractPreferredProvider(text);
96
- const inferred: string[] = [];
97
- if (preferredProvider && /gpt[- ]?5\.5/i.test(text)) inferred.push(`${preferredProvider}/gpt-5.5`);
98
- if (preferredProvider && /gpt[- ]?5\.4(?!\s*mini)/i.test(text)) inferred.push(`${preferredProvider}/gpt-5.4`);
99
- if (preferredProvider && /gpt[- ]?5\.4\s*mini/i.test(text)) inferred.push(`${preferredProvider}/gpt-5.4-mini`);
100
- return unique([...explicit, ...inferred]);
101
- }
102
-
103
- async function loadSnapshot(cwd: string): Promise<ModelPolicySnapshot> {
104
- const settings = await readSettings(cwd);
105
- const settingsModels = collectModelsFromSettings(settings);
106
- const resolvedPolicy = await resolveTakomiRoutingPolicy(cwd);
107
- const sourceFiles = resolvedPolicy.policyPath ? [resolvedPolicy.policyPath] : [];
108
- const policyModels = resolvedPolicy.text ? collectModelsFromPolicy(resolvedPolicy.text) : [];
109
- const approvedModels = unique([...settingsModels, ...policyModels]);
110
- return { approvedModels, preferredModels: settingsModels.length ? unique(settingsModels) : approvedModels, sourceFiles };
111
- }
112
-
113
25
  function collectRequestedModelRefs(input: unknown): Array<{ holder: Record<string, unknown>; key: string; value: string; index?: number }> {
114
26
  const refs: Array<{ holder: Record<string, unknown>; key: string; value: string; index?: number }> = [];
115
27
  function visit(value: unknown): void {
@@ -204,18 +116,18 @@ export function installModelPolicyGate(pi: ExtensionAPI, state: ContextManagerSt
204
116
  if (event.toolName !== "takomi_subagent") return;
205
117
  const snapshot = await loadSnapshot(ctx.cwd);
206
118
  const approved = snapshot.approvedModels;
119
+ if (snapshot.policyConflicts?.length) {
120
+ const reason = ["Blocked by conflicting Takomi routing guidance and executable settings.", "", ...snapshot.policyConflicts.map((item) => `- ${item}`)].join("\n");
121
+ recordBlocked(state, event.toolName, reason);
122
+ persistReportSnapshot(pi, state, "model-policy-conflict");
123
+ return { block: true, reason };
124
+ }
207
125
  if (approved.length === 0) return;
208
126
 
209
127
  const refs = collectRequestedModelRefs(event.input);
210
128
  const corrections: Array<{ from: string; to: string; recovery?: string }> = [];
211
129
  for (const ref of refs) {
212
130
  if (isTakomiModelApproved(ref.value, approved)) continue;
213
- const equivalent = approvedModelEquivalent(ref.value, approved);
214
- if (equivalent) {
215
- setModelRef(ref, equivalent);
216
- corrections.push({ from: ref.value, to: equivalent });
217
- continue;
218
- }
219
131
  const recovery = await askForInvalidModelRecovery(ctx, ref.value, approved);
220
132
  if (recovery.action === "retry") {
221
133
  setModelRef(ref, recovery.model);
@@ -244,7 +156,7 @@ export function installModelPolicyGate(pi: ExtensionAPI, state: ContextManagerSt
244
156
  timestamp,
245
157
  })));
246
158
  persistReportSnapshot(pi, state, "model-policy-correction");
247
- const notification = `Takomi context manager corrected subagent model routing:\n- ${corrections.map((correction) => `${sanitizePresentation(correction.from)} -> ${sanitizePresentation(correction.to)}${correction.recovery ? ` (${sanitizePresentation(correction.recovery)})` : ""}`).join("\n- ")}\n\nBe careful to follow /takomi routing policy next time.`;
159
+ const notification = `Takomi model routing changed only after explicit user selection:\n- ${corrections.map((correction) => `${sanitizePresentation(correction.from)} -> ${sanitizePresentation(correction.to)}${correction.recovery ? ` (${sanitizePresentation(correction.recovery)})` : ""}`).join("\n- ")}`;
248
160
  ctx.ui.notify(notification, "warning");
249
161
  }
250
162
  });
@@ -14,7 +14,7 @@ const ROOT_COMPLETIONS: TakomiCompletion[] = [
14
14
  { value: "design", label: "design", description: "Run UI/UX design from approved scope" },
15
15
  { value: "build", label: "build", description: "Implement against the agreed UI" },
16
16
  { value: "plan", label: "plan", description: "Create or update the orchestration plan" },
17
- { value: "mode", label: "mode", description: "Set direct, orchestrate, or review mode" },
17
+ { value: "mode", label: "mode", description: "Set idle, code, review, or orchestrate main-agent mode" },
18
18
  { value: "gate", label: "gate", description: "Set auto or review-gated execution" },
19
19
  { value: "subagents", label: "subagents", description: "Control subagent usage and view" },
20
20
  { value: "stats", label: "stats", description: "Show token, model, project, session, tool, and subagent usage stats" },
@@ -23,9 +23,10 @@ const ROOT_COMPLETIONS: TakomiCompletion[] = [
23
23
 
24
24
  const SUBCOMMAND_COMPLETIONS: Record<string, TakomiCompletion[]> = {
25
25
  mode: [
26
- { value: "direct", label: "direct", description: "Handle work directly unless delegation is explicit" },
27
- { value: "orchestrate", label: "orchestrate", description: "Plan and delegate broad work" },
26
+ { value: "idle", label: "idle", description: "Clear active Takomi main-agent behavior" },
27
+ { value: "code", label: "code", description: "Implement directly in the main session" },
28
28
  { value: "review", label: "review", description: "Inspect outputs and route fixes" },
29
+ { value: "orchestrate", label: "orchestrate", description: "Coordinate lifecycle work and specialist personas" },
29
30
  ],
30
31
  gate: [
31
32
  { value: "review", label: "review", description: "Return to the user after each task" },
@@ -94,7 +95,7 @@ export function commandHelp(): string {
94
95
  "/takomi design [prompt]",
95
96
  "/takomi build [prompt]",
96
97
  "/takomi plan [title]",
97
- "/takomi mode <direct|orchestrate|review>",
98
+ "/takomi mode <idle|code|review|orchestrate>",
98
99
  "/takomi gate <auto|review>",
99
100
  "/takomi subagents <list|on|off|status>",
100
101
  "/takomi stats [overview|daily|models|projects|projects-full|sessions|sessions-full|tasks|tasks-full|tools|subagents|sources] [since 7d]",
@@ -55,28 +55,30 @@ export function registerTakomiCommands(pi: ExtensionAPI, options: RegisterTakomi
55
55
  }
56
56
 
57
57
  async function handleMode(ctx: ExtensionCommandContext, mode?: string): Promise<void> {
58
- if (mode !== "direct" && mode !== "orchestrate" && mode !== "review") {
59
- ctx.ui.notify("Usage: /takomi mode <direct|orchestrate|review>", "warning");
58
+ if (mode === "direct") mode = "code";
59
+ if (mode !== "idle" && mode !== "code" && mode !== "orchestrate" && mode !== "review") {
60
+ ctx.ui.notify("Usage: /takomi mode <idle|code|review|orchestrate>", "warning");
60
61
  return;
61
62
  }
62
- const hasGenesis = await options.hasGenesisArtifacts(ctx.cwd);
63
63
  await options.updateState(ctx, () => {
64
64
  const state = options.getState();
65
65
  state.enabled = true;
66
- if (mode === "direct") {
66
+ if (mode === "idle") {
67
67
  state.autoOrch = false;
68
68
  state.planMode = false;
69
69
  state.role = "general";
70
- state.stage = undefined;
71
- state.workflow = undefined;
72
70
  state.modeSource = "idle";
73
71
  state.modeReason = undefined;
72
+ } else if (mode === "code") {
73
+ state.autoOrch = false;
74
+ state.planMode = false;
75
+ state.role = "coder";
76
+ state.modeSource = "manual";
77
+ state.modeReason = "/takomi mode code";
74
78
  } else if (mode === "orchestrate") {
75
79
  state.autoOrch = true;
76
80
  state.planMode = true;
77
81
  state.role = "orchestrator";
78
- state.stage = hasGenesis ? "build" : "genesis";
79
- state.workflow = hasGenesis ? "vibe-build" : "vibe-genesis";
80
82
  state.modeSource = "manual";
81
83
  state.modeReason = "/takomi mode orchestrate";
82
84
  } else {
@@ -84,9 +86,7 @@ export function registerTakomiCommands(pi: ExtensionAPI, options: RegisterTakomi
84
86
  state.planMode = true;
85
87
  state.launchMode = "manual";
86
88
  options.recordUserGateAutoProvenance(false);
87
- state.role = "review";
88
- state.stage = undefined;
89
- state.workflow = undefined;
89
+ state.role = "reviewer";
90
90
  state.modeSource = "manual";
91
91
  state.modeReason = "/takomi mode review";
92
92
  }
@@ -201,14 +201,14 @@ export function registerTakomiCommands(pi: ExtensionAPI, options: RegisterTakomi
201
201
  ? `WARNING: This input is much shorter than the active policy (${preview.policy.length} vs ${activePolicy.text.length} characters). It will replace the file, not merge into it. If the user referred to a full source file or prior policy, inspect and use that complete text instead.`
202
202
  : "This input replaces the selected policy file exactly; it is not merged with the active policy.";
203
203
  const reviewPrompt = [
204
- "Review this Takomi routing policy extraction before it is saved.",
204
+ "Review this advisory Takomi model-routing guidance before it is saved.",
205
205
  "",
206
206
  "Rules:",
207
207
  "- Do not invent providers or model IDs not grounded in the policy.",
208
- "- Providerless names are valid routing intent. Require a provider only when writing an executable model override.",
209
- "- Check the available Pi model registry below before asking the user whether a named model exists or what its exact ID is.",
210
- "- Conditional task-shape routes do not need to be forced into role-wide defaults or agentOverrides.",
211
- "- Valid role-wide Takomi overrides are: general, orchestrator, architect, designer, coder, reviewer. Other headings may still be policy concepts or execution routes.",
208
+ "- Providerless names such as Sol, Terra, and Luna are valid advisory routing concepts.",
209
+ "- Do not infer executable providers, allowlists, fallbacks, or persona defaults from this prose.",
210
+ "- Executable changes belong in takomi.routing settings through takomi_config_routing.",
211
+ "- Canonical Takomi personas are: architect, designer, coder, worker, reviewer, orchestrator.",
212
212
  "- Preserve the user's full authored policy. If this looks like a summary/excerpt and a richer referenced source exists, inspect that source and apply the complete intended text instead of overwriting it with the excerpt.",
213
213
  "- If the extraction is correct and safe, call takomi_apply_routing_policy with the complete intended policyText and scope below.",
214
214
  "- Ask only for unresolved provider/account choices or genuine policy ambiguity; do not ask for facts available from the registry or files.",
@@ -227,7 +227,7 @@ export function registerTakomiCommands(pi: ExtensionAPI, options: RegisterTakomi
227
227
  "",
228
228
  `Tool call to apply if safe: takomi_apply_routing_policy({ scope: ${JSON.stringify(scope)}, policyText: <original policy text> })`,
229
229
  ].join("\n");
230
- ctx.ui.notify("Takomi routing extraction prepared. Sending it to the active model for review before saving.", "info");
230
+ ctx.ui.notify("Takomi routing guidance prepared. Sending it to the active model for preservation review before saving.", "info");
231
231
  pi.sendUserMessage(reviewPrompt);
232
232
  } catch (error) {
233
233
  ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");