codemini-cli 0.4.2 → 0.4.3

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.
@@ -110,6 +110,7 @@ function getCompletionCopy(language = 'zh') {
110
110
  'context.tool_result_max_chars': '工具结果字符上限',
111
111
  'context.read_file_default_lines': 'read_file 默认行数窗口',
112
112
  'context.read_file_max_chars': 'read_file 字符上限',
113
+ 'context.prompt_budget_audit': 'Prompt 预算审计开关',
113
114
  'sessions.max_sessions': '会话保留上限',
114
115
  'sessions.retention_days': '会话保留天数',
115
116
  'shell.default': '默认 shell',
@@ -127,7 +128,8 @@ function getCompletionCopy(language = 'zh') {
127
128
  'execution.mode': '可选:auto | normal | plan',
128
129
  'shell.default': '常用:bash | powershell',
129
130
  'policy.safe_mode': '可选:true | false',
130
- 'policy.allow_dangerous_commands': '可选:true | false'
131
+ 'policy.allow_dangerous_commands': '可选:true | false',
132
+ 'context.prompt_budget_audit': '可选:true | false'
131
133
  },
132
134
  describeSet: (label, hint) => `设置${label}${hint ? `(${hint})` : ''}`,
133
135
  describeGet: (label, hint) => `查看${label}${hint ? `(${hint})` : ''}`,
@@ -210,6 +212,7 @@ function getCompletionCopy(language = 'zh') {
210
212
  'context.tool_result_max_chars': 'tool result character limit',
211
213
  'context.read_file_default_lines': 'default read_file line window',
212
214
  'context.read_file_max_chars': 'read_file character limit',
215
+ 'context.prompt_budget_audit': 'prompt budget audit switch',
213
216
  'sessions.max_sessions': 'stored session limit',
214
217
  'sessions.retention_days': 'session retention days',
215
218
  'shell.default': 'default shell',
@@ -227,7 +230,8 @@ function getCompletionCopy(language = 'zh') {
227
230
  'execution.mode': 'options: auto | normal | plan',
228
231
  'shell.default': 'common: bash | powershell',
229
232
  'policy.safe_mode': 'options: true | false',
230
- 'policy.allow_dangerous_commands': 'options: true | false'
233
+ 'policy.allow_dangerous_commands': 'options: true | false',
234
+ 'context.prompt_budget_audit': 'options: true | false'
231
235
  },
232
236
  describeSet: (label, hint) => `set the ${label}${hint ? ` (${hint})` : ''}`,
233
237
  describeGet: (label, hint) => `show the ${label}${hint ? ` (${hint})` : ''}`,
@@ -301,9 +305,10 @@ function describeConfigKey(key, mode = 'set', language = 'zh') {
301
305
  return mode === 'get' ? copy.describeGet(label, hint) : copy.describeSet(label, hint);
302
306
  }
303
307
 
304
- const SUB_AGENT_ROLES = ['planner', 'coder', 'reviewer', 'tester', 'summarizer'];
308
+ const SUB_AGENT_ROLES = ['planner', 'advisor', 'coder', 'reviewer', 'tester', 'summarizer'];
305
309
  export const ROLE_TOOL_POLICY = {
306
310
  planner: ['read', 'grep', 'list', 'query_project_index', 'tool_search', 'glob', 'ast_query', 'read_ast_node', 'web_fetch', 'web_search', 'read_plan', 'update_plan'],
311
+ advisor: ['read', 'grep', 'list', 'query_project_index', 'tool_search', 'read_plan'],
307
312
  coder: ['read', 'grep', 'list', 'edit', 'write', 'delete', 'run', 'ast_query', 'read_ast_node', 'glob', 'tool_search', 'web_fetch', 'web_search', 'update_todos', 'read_plan', 'update_plan'],
308
313
  reviewer: ['read', 'grep', 'list', 'glob', 'tool_search', 'ast_query', 'read_ast_node', 'read_plan'],
309
314
  tester: ['read', 'grep', 'list', 'run', 'glob', 'tool_search', 'read_plan'],
@@ -351,6 +356,25 @@ export function getSubAgentRolePrompt(role) {
351
356
  'Do not add a closing summary or "Next Action" — the pipeline handles what comes next.'
352
357
  ].join('\n');
353
358
  }
359
+ if (role === 'advisor') {
360
+ return [
361
+ 'You are the advisor in a multi-step agent pipeline.',
362
+ 'Your job: analyze the handed-off context and produce recommendations, tradeoffs, and evidence.',
363
+ 'Do not edit files, write code, delete files, or run commands.',
364
+ 'Output format — keep it short and direct:',
365
+ 'Findings:',
366
+ '- <important observation, constraint, or "none">',
367
+ 'Recommendations:',
368
+ '- <prioritized recommendation or "none">',
369
+ 'Tradeoffs:',
370
+ '- <important tradeoff or "none">',
371
+ 'Evidence:',
372
+ '- <files, docs, or observations checked>',
373
+ 'Open Questions:',
374
+ '- <blocking uncertainty or "none">',
375
+ 'Do not summarize your own work or add closing remarks — just deliver the structured advisory handoff and stop.'
376
+ ].join('\n');
377
+ }
354
378
  if (role === 'tester') {
355
379
  return [
356
380
  'You are the tester in a multi-step agent pipeline.',
@@ -720,6 +744,9 @@ function buildGoalRequirementPacket(goal, role) {
720
744
  lines.push('Verify the implementation against the original goal, not just syntax or smoke checks.');
721
745
  lines.push('Check each acceptance item explicitly before calling the work verified.');
722
746
  lines.push('If any requested behavior is unverified or contradicted by evidence, list it under Not Verified or Failures.');
747
+ } else if (role === 'advisor') {
748
+ lines.push('Advise against the acceptance checklist and original goal, not generic best practices.');
749
+ lines.push('Prioritize concrete recommendations, evidence, and tradeoffs. Do not implement changes.');
723
750
  } else if (role === 'coder') {
724
751
  lines.push('Implement against the acceptance checklist, not only the broad wording of the goal.');
725
752
  }
@@ -735,11 +762,11 @@ function buildAutoPlanPlannerGuidance() {
735
762
  '- Prefer the smallest local approach that satisfies the goal.',
736
763
  '- Do not output multiple alternative branches in the final plan.',
737
764
  '- Do not assume implementation should begin before the plan is coherent.',
738
- '- Available sub-agent roles are planner, coder, reviewer, tester, and summarizer. Use only the non-summary roles the task actually needs.',
765
+ '- Available sub-agent roles are planner, advisor, coder, reviewer, tester, and summarizer. Use only the non-summary roles the task actually needs.',
739
766
  '- Always include a summarizer as the final step. The summarizer reads accumulated step results from the plan file and synthesizes the final summary. It does NOT re-analyze the codebase.',
740
- '- Do not ask planner, coder, reviewer, or tester steps to produce the final summary. They should write detailed step results for the summarizer.',
767
+ '- Do not ask planner, advisor, coder, reviewer, or tester steps to produce the final summary. They should write detailed step results for the summarizer.',
741
768
  '- For implementation-heavy or risky changes, prefer adding review and/or verification steps.',
742
- '- For analysis, recommendation, or planning-only goals, you may omit reviewer/tester if they do not add value.',
769
+ '- For analysis, recommendation, optimization, architecture feedback, or planning-only goals, prefer advisor over coder and omit reviewer/tester if they do not add value.',
743
770
  '- Prefer 3-5 steps total unless the task is clearly larger.',
744
771
  '- Keep the plan ordered, implementation-oriented, and easy for small sub-agents to follow.'
745
772
  ].join('\n');
@@ -757,6 +784,9 @@ function buildAutoPlanExecutionGuidance(role) {
757
784
  if (role === 'coder') {
758
785
  common.push('- Keep edits tightly scoped to the chosen plan direction.');
759
786
  common.push('- Avoid speculative cleanup or unrelated improvements.');
787
+ } else if (role === 'advisor') {
788
+ common.push('- Produce advisory findings and recommendations only; do not modify files or run commands.');
789
+ common.push('- Ground every recommendation in inspected evidence or mark it as an assumption.');
760
790
  } else if (role === 'reviewer') {
761
791
  common.push('- Review against the chosen plan direction and the acceptance checklist.');
762
792
  common.push('- Call out missing requested behavior, regression risk, and unverified claims.');
@@ -1070,6 +1100,10 @@ function selectAutoSkillNames(text = '') {
1070
1100
  const input = String(text || '').toLowerCase();
1071
1101
  const selected = ['superpowers-lite'];
1072
1102
 
1103
+ const explicitGrillMe =
1104
+ /\bgr+ill\s+me\b|\bpressure[- ]?test\b|\bstress[- ]?test\b|\bchallenge\s+(?:this|my|me)\b|\btear\s+(?:this|my)\s+.*apart\b/i.test(
1105
+ input
1106
+ ) || /(拷问|质询|压力测试|挑战一下|挑刺|狠狠审|喷一下|怼一下)/i.test(input);
1073
1107
  const explicitBrainstorm =
1074
1108
  /(brainstorm|头脑风暴|方案|思路|设计一下|设计方案|怎么做|如何做|approach|options?)/i.test(input);
1075
1109
  const ambiguitySignals =
@@ -1088,6 +1122,9 @@ function selectAutoSkillNames(text = '') {
1088
1122
  if (explicitBrainstorm || (ambiguitySignals && featureRequest) || greenfieldBuildRequest) {
1089
1123
  selected.push('brainstorm');
1090
1124
  }
1125
+ if (explicitGrillMe) {
1126
+ selected.push('grill-me');
1127
+ }
1091
1128
  return selected;
1092
1129
  }
1093
1130
 
@@ -1266,12 +1303,32 @@ function summarizeGoalForStepTitle(goal, fallback = 'requested change') {
1266
1303
  function buildFallbackAutoPlan(goal) {
1267
1304
  const requirements = deriveGoalRequirements(goal);
1268
1305
  const lightweightGoal = isLightweightAutoPlanGoal(goal, requirements);
1306
+ const taskClass = classifyPlanTaskClass(goal);
1269
1307
  const focus = summarizeGoalForStepTitle(goal);
1270
1308
  const summary =
1271
1309
  requirements.length > 0
1272
1310
  ? `Auto fallback plan for: ${requirements.join('; ')}`
1273
1311
  : `Auto fallback plan for: ${goal}`;
1274
1312
 
1313
+ if (taskClass === 'advisory') {
1314
+ return {
1315
+ summary,
1316
+ steps: [
1317
+ {
1318
+ title: 'Inspect the target area',
1319
+ role: 'planner',
1320
+ task: `Inspect the relevant project context for: ${goal}. Identify constraints, evidence, and likely high-value advisory areas.`
1321
+ },
1322
+ {
1323
+ title: `Advise on ${focus}`,
1324
+ role: 'advisor',
1325
+ task: `Analyze the findings for: ${goal}. Produce prioritized recommendations, tradeoffs, evidence, and open questions without implementing changes.`
1326
+ },
1327
+ buildDefaultSummarizerStep(goal)
1328
+ ]
1329
+ };
1330
+ }
1331
+
1275
1332
  if (lightweightGoal) {
1276
1333
  const summarizerStep = buildDefaultSummarizerStep(goal);
1277
1334
  return {
@@ -1332,6 +1389,13 @@ function buildFallbackAutoPlan(goal) {
1332
1389
  function buildDefaultSummarizerStep(goal, source = []) {
1333
1390
  const existing = (Array.isArray(source) ? source : []).find((step) => step.role === 'summarizer');
1334
1391
  if (existing?.title && existing?.task) return existing;
1392
+ if (classifyPlanTaskClass(goal) === 'advisory') {
1393
+ return {
1394
+ title: 'Synthesize final findings',
1395
+ role: 'summarizer',
1396
+ task: `Synthesize the advisory findings for: ${goal}. Read the accumulated observations, recommendations, tradeoffs, evidence, and open questions from earlier steps, then produce a concise final summary with the single best next action.`
1397
+ };
1398
+ }
1335
1399
  return {
1336
1400
  title: 'Synthesize final implementation status',
1337
1401
  role: 'summarizer',
@@ -1344,7 +1408,7 @@ function enforceAutoPlanGuardrailSteps(plan, goal) {
1344
1408
  const requirements = deriveGoalRequirements(goal);
1345
1409
  const lightweightGoal = isLightweightAutoPlanGoal(goal, requirements);
1346
1410
  const taskClass = classifyPlanTaskClass(goal);
1347
- const implementationSteps = source.filter((step) => step.role !== 'reviewer' && step.role !== 'tester' && step.role !== 'summarizer');
1411
+ const implementationSteps = source.filter((step) => step.role !== 'advisor' && step.role !== 'reviewer' && step.role !== 'tester' && step.role !== 'summarizer');
1348
1412
  const primaryImplementationStep =
1349
1413
  implementationSteps.find((step) => step.role === 'coder') ||
1350
1414
  implementationSteps[0] || {
@@ -1367,11 +1431,41 @@ function enforceAutoPlanGuardrailSteps(plan, goal) {
1367
1431
  const hasTester = source.some((step) => step.role === 'tester');
1368
1432
 
1369
1433
  if (taskClass === 'advisory') {
1370
- const advisorySteps = source.filter((step) => step.role === 'planner' || step.role === 'coder');
1371
- const baseSteps = advisorySteps.length > 0 ? advisorySteps.slice(0, 6) : [primaryImplementationStep];
1434
+ const advisorySteps = source
1435
+ .filter((step) => step.role === 'planner' || step.role === 'advisor' || step.role === 'coder')
1436
+ .map((step) =>
1437
+ step.role === 'coder'
1438
+ ? {
1439
+ ...step,
1440
+ role: 'advisor',
1441
+ title: /^implement\b/i.test(String(step.title || '')) ? 'Advise on requested goal' : step.title
1442
+ }
1443
+ : step
1444
+ );
1445
+ const hasAdvisor = advisorySteps.some((step) => step.role === 'advisor');
1446
+ const baseSteps =
1447
+ advisorySteps.length > 0
1448
+ ? advisorySteps.slice(0, 6)
1449
+ : [
1450
+ {
1451
+ title: 'Advise on requested goal',
1452
+ role: 'advisor',
1453
+ task: `Analyze the goal and recommend the highest-value next steps for: ${goal}`
1454
+ }
1455
+ ];
1456
+ const finalSteps = hasAdvisor
1457
+ ? baseSteps
1458
+ : [
1459
+ ...baseSteps,
1460
+ {
1461
+ title: 'Advise on requested goal',
1462
+ role: 'advisor',
1463
+ task: `Analyze the goal and recommend the highest-value next steps for: ${goal}`
1464
+ }
1465
+ ];
1372
1466
  return {
1373
1467
  summary: String(plan?.summary || `Auto plan for: ${goal}`).trim(),
1374
- steps: [...baseSteps, summarizerStep]
1468
+ steps: [...finalSteps, summarizerStep]
1375
1469
  };
1376
1470
  }
1377
1471
 
@@ -1642,7 +1736,7 @@ async function buildAutoPlanFinalSummary({
1642
1736
  content: buildAutoPlanFinalSummaryUserPrompt({ goal, autoPlan, runItems, planningError })
1643
1737
  }
1644
1738
  ],
1645
- timeoutMs: config.gateway.timeout_ms || 90000,
1739
+ timeoutMs: config.gateway.timeout_ms || 1800000,
1646
1740
  maxRetries: config.gateway.max_retries ?? 2
1647
1741
  });
1648
1742
  return trimInline(result.text || '', 600) || fallbackSummary;
@@ -1768,7 +1862,7 @@ async function buildSpecWithModel({
1768
1862
  { role: 'system', content: `${systemPrompt}\n${prompt}` },
1769
1863
  { role: 'user', content: `Topic: ${topic}` }
1770
1864
  ],
1771
- timeoutMs: config.gateway.timeout_ms || 90000,
1865
+ timeoutMs: config.gateway.timeout_ms || 1800000,
1772
1866
  maxRetries: config.gateway.max_retries ?? 2
1773
1867
  });
1774
1868
  return String(result.text || '').trim();
@@ -1831,7 +1925,7 @@ async function buildPlanFromSpecWithModel({
1831
1925
  content: `Spec path: ${specPath || '(inline)'}\n\nProject implementation constraints:\n${projectConstraints}\n\n${specText}`
1832
1926
  }
1833
1927
  ],
1834
- timeoutMs: config.gateway.timeout_ms || 90000,
1928
+ timeoutMs: config.gateway.timeout_ms || 1800000,
1835
1929
  maxRetries: config.gateway.max_retries ?? 2
1836
1930
  });
1837
1931
  return String(result.text || '').trim();
@@ -1929,6 +2023,72 @@ function effectiveMaxContextTokens(config) {
1929
2023
  return 32000;
1930
2024
  }
1931
2025
 
2026
+ function estimateTextTokens(role, content) {
2027
+ const text = String(content || '');
2028
+ if (!text) return 0;
2029
+ return estimateMessagesTokens([{ role, content: text }]);
2030
+ }
2031
+
2032
+ function makePromptBudgetComponent(name, role, content) {
2033
+ const text = String(content || '');
2034
+ return {
2035
+ name,
2036
+ chars: text.length,
2037
+ estimated_tokens: estimateTextTokens(role, text)
2038
+ };
2039
+ }
2040
+
2041
+ function buildPromptBudgetAudit({
2042
+ systemPrompt = '',
2043
+ projectContextSnippet = '',
2044
+ projectContextGuidance = '',
2045
+ messages = [],
2046
+ toolDefinitions = [],
2047
+ config = {}
2048
+ }) {
2049
+ const toolSchemaText = JSON.stringify(toolDefinitions || []);
2050
+ const messageTexts = (Array.isArray(messages) ? messages : []).map((message) => ({
2051
+ role: message?.role || 'user',
2052
+ content: message?.content || ''
2053
+ }));
2054
+ const components = [
2055
+ makePromptBudgetComponent('system_prompt', 'system', systemPrompt),
2056
+ makePromptBudgetComponent('project_context', 'system', projectContextSnippet),
2057
+ makePromptBudgetComponent('project_context_guidance', 'system', projectContextGuidance),
2058
+ {
2059
+ name: 'message_history',
2060
+ chars: messageTexts.reduce((total, message) => total + String(message.content || '').length, 0),
2061
+ estimated_tokens: estimateMessagesTokens(messageTexts)
2062
+ },
2063
+ makePromptBudgetComponent('tool_schemas', 'system', toolSchemaText)
2064
+ ];
2065
+ const totalChars = components.reduce((total, component) => total + component.chars, 0);
2066
+ const totalTokens = components.reduce((total, component) => total + component.estimated_tokens, 0);
2067
+ const maxContextTokens = effectiveMaxContextTokens(config);
2068
+ const contextUsagePct =
2069
+ maxContextTokens > 0 ? Math.min(100, Math.max(0, (totalTokens / maxContextTokens) * 100)) : 0;
2070
+ return {
2071
+ components,
2072
+ total: {
2073
+ chars: totalChars,
2074
+ estimated_tokens: totalTokens
2075
+ },
2076
+ max_context_tokens: maxContextTokens,
2077
+ context_usage_pct: contextUsagePct
2078
+ };
2079
+ }
2080
+
2081
+ function summarizePromptBudgetAudit(audit) {
2082
+ const totalTokens = audit?.total?.estimated_tokens || 0;
2083
+ const maxContextTokens = audit?.max_context_tokens || 0;
2084
+ const pct = Number(audit?.context_usage_pct || 0).toFixed(1);
2085
+ const components = (audit?.components || [])
2086
+ .filter((component) => component.estimated_tokens > 0)
2087
+ .map((component) => `${component.name}=${component.estimated_tokens}`)
2088
+ .join(', ');
2089
+ return `prompt budget: ${totalTokens}/${maxContextTokens} est tokens (${pct}%)${components ? `; ${components}` : ''}`;
2090
+ }
2091
+
1932
2092
  function buildRuntimeStateSnapshot({ currentSession, config, model, executionMode, extraSession }) {
1933
2093
  const parentTokens = estimateMessagesTokens(currentSession?.messages || []);
1934
2094
  const subTokens = extraSession ? estimateMessagesTokens(extraSession.messages || []) : 0;
@@ -1939,6 +2099,7 @@ function buildRuntimeStateSnapshot({ currentSession, config, model, executionMod
1939
2099
  sessionId: currentSession?.id || '',
1940
2100
  mode: executionMode || config.execution?.mode || 'auto',
1941
2101
  sdkProvider: config.sdk?.provider || 'openai-compatible',
2102
+ agentRole: 'general',
1942
2103
  model: model || config.model?.name || '',
1943
2104
  maxContextTokens
1944
2105
  };
@@ -2241,8 +2402,10 @@ async function askModel({
2241
2402
  }
2242
2403
 
2243
2404
  const projectContextSnippet = await buildProjectContextSnippet(process.cwd(), text).catch(() => '');
2405
+ const projectContextGuidance =
2406
+ 'Use this project context as lightweight guidance and verify important details with fresh reads when needed.';
2244
2407
  const effectiveSystemPrompt = projectContextSnippet
2245
- ? `${systemPrompt}\n\n${projectContextSnippet}\n\nUse this project context as lightweight guidance and verify important details with fresh reads when needed.`
2408
+ ? `${systemPrompt}\n\n${projectContextSnippet}\n\n${projectContextGuidance}`
2246
2409
  : systemPrompt;
2247
2410
 
2248
2411
  const { definitions, handlers, formatters, deferredDefinitions, dispose: disposeTools } = getBuiltinTools({
@@ -2272,6 +2435,31 @@ async function askModel({
2272
2435
  ? Object.fromEntries(Object.entries(deferredDefinitions).filter(([name]) => allowedTools.includes(name)))
2273
2436
  : deferredDefinitions;
2274
2437
 
2438
+ if (config.context?.prompt_budget_audit === true && onAgentEvent) {
2439
+ const auditId = `prompt-budget-${Date.now()}`;
2440
+ const audit = buildPromptBudgetAudit({
2441
+ systemPrompt,
2442
+ projectContextSnippet,
2443
+ projectContextGuidance: projectContextSnippet ? projectContextGuidance : '',
2444
+ messages: session.messages.filter((m) => m.role !== 'system'),
2445
+ toolDefinitions: filteredDefinitions,
2446
+ config
2447
+ });
2448
+ onAgentEvent({
2449
+ type: 'system_tool:start',
2450
+ id: auditId,
2451
+ name: 'prompt_budget',
2452
+ summary: 'calculating prompt budget'
2453
+ });
2454
+ onAgentEvent({
2455
+ type: 'system_tool:end',
2456
+ id: auditId,
2457
+ name: 'prompt_budget',
2458
+ summary: summarizePromptBudgetAudit(audit),
2459
+ details: audit
2460
+ });
2461
+ }
2462
+
2275
2463
  let activeAssistantIndex = -1;
2276
2464
  const wrappedAgentEvent = (event) => {
2277
2465
  // Always accumulate messages in session (for token tracking), only save when persisting
@@ -2351,7 +2539,7 @@ async function askModel({
2351
2539
  model: selectedModel,
2352
2540
  messages,
2353
2541
  tools,
2354
- timeoutMs: config.gateway.timeout_ms || 90000,
2542
+ timeoutMs: config.gateway.timeout_ms || 1800000,
2355
2543
  maxRetries: config.gateway.max_retries ?? 2,
2356
2544
  signal,
2357
2545
  onTextDelta: (delta) => {
@@ -2644,14 +2832,15 @@ async function buildAutoPlanAndRun({
2644
2832
  '- advisory = analysis, review, audit, optimization suggestions, architecture feedback, brainstorming, planning, or recommendation requests.',
2645
2833
  '- implementation = add/build/create/implement/refactor/fix/update/change behavior in code or files.',
2646
2834
  '- verification-heavy = the user explicitly asks to run tests, verify findings, reproduce a bug, prove a claim, or validate a result.',
2647
- '- For advisory goals, prefer only planner and coder roles. Do not use reviewer or tester unless the user explicitly asks for verification or review as a separate deliverable.',
2835
+ '- For advisory goals, prefer planner and advisor roles. Do not use coder unless the plan will actually modify code or files.',
2836
+ '- For advisory goals, do not use reviewer or tester unless the user explicitly asks for verification or review as a separate deliverable.',
2648
2837
  '- For advisory goals, do not emit generic filler steps such as "Test and verify", "Review recommendations", or other template-only steps.',
2649
2838
  '- For implementation goals, reviewer and tester are optional support roles, not defaults. Only include them when they clearly add value.',
2650
2839
  '- Every step title must be concrete and tied to the goal. Avoid vague titles like "Initial analysis", "Review recommendations", or "Test and verify" unless the user explicitly requested those activities.',
2651
2840
  '- If the task is purely to inspect the current project and suggest improvements, a lean 2-step or 3-step plan is preferred.',
2652
- '- Example advisory roles: planner -> inspect project shape, coder -> synthesize findings and prioritized recommendations.',
2841
+ '- Example advisory roles: planner -> inspect project shape, advisor -> synthesize findings and prioritized recommendations.',
2653
2842
  '- Example implementation roles: planner -> inspect target area, coder -> implement change, tester -> verify changed behavior.',
2654
- 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.'
2843
+ 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|advisor|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.'
2655
2844
  ].join('\n');
2656
2845
  let autoPlan = {
2657
2846
  summary: `Auto plan for: ${goal}`,
@@ -2676,15 +2865,15 @@ async function buildAutoPlanAndRun({
2676
2865
  role: 'user',
2677
2866
  content: [
2678
2867
  'Create an execution plan and assign best sub-agent role for each step.',
2679
- 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.',
2680
- 'The available roles are planner, coder, reviewer, tester, and summarizer. Use only the non-summary roles the task actually needs.',
2868
+ 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|advisor|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.',
2869
+ 'The available roles are planner, advisor, coder, reviewer, tester, and summarizer. Use only the non-summary roles the task actually needs.',
2681
2870
  'Always include a summarizer as the final step. The summarizer synthesizes prior step results without re-analyzing.',
2682
- 'Planner, coder, reviewer, and tester steps should write detailed step results, not final summaries.',
2871
+ 'Planner, advisor, coder, reviewer, and tester steps should write detailed step results, not final summaries.',
2683
2872
  `Task class: ${normalizedTaskClass}`,
2684
2873
  'Before choosing roles, decide whether the request is advisory, implementation, or verification-heavy.',
2685
2874
  requirementPacket,
2686
2875
  'The first step should usually inspect or clarify the target area before implementation.',
2687
- 'For analysis, recommendation, optimization, audit, or project-review goals, keep the plan lean and usually limit it to planner/coder.',
2876
+ 'For analysis, recommendation, optimization, audit, or project-review goals, keep the plan lean and usually limit it to planner/advisor.',
2688
2877
  'Do not include reviewer/tester for advisory goals unless the user explicitly asks to validate, verify, or independently review the findings.',
2689
2878
  'Avoid template-only titles like "Initial analysis", "Review recommendations", or "Test and verify" for advisory goals.',
2690
2879
  'For implementation-heavy changes, prefer review and/or testing steps near the end only when they materially improve confidence.',
@@ -2694,7 +2883,7 @@ async function buildAutoPlanAndRun({
2694
2883
  .join('\n')
2695
2884
  }
2696
2885
  ],
2697
- timeoutMs: config.gateway.timeout_ms || 90000,
2886
+ timeoutMs: config.gateway.timeout_ms || 1800000,
2698
2887
  maxRetries: config.gateway.max_retries ?? 2
2699
2888
  });
2700
2889
  const parsed = extractJsonBlock(planning.text || '');
@@ -2795,7 +2984,7 @@ async function revisePendingPlanWithModel({
2795
2984
  const prompt = [
2796
2985
  buildAutoPlanPlannerGuidance(),
2797
2986
  'You are revising an existing plan based on explicit user feedback.',
2798
- 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.',
2987
+ 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|advisor|coder|reviewer|tester|summarizer","task":"..."}]}. No markdown.',
2799
2988
  'Keep roles minimal and only include steps that materially help the goal.',
2800
2989
  'Always keep a summarizer as the final step.'
2801
2990
  ].join('\n');
@@ -2819,7 +3008,7 @@ async function revisePendingPlanWithModel({
2819
3008
  ].join('\n')
2820
3009
  }
2821
3010
  ],
2822
- timeoutMs: config.gateway.timeout_ms || 90000,
3011
+ timeoutMs: config.gateway.timeout_ms || 1800000,
2823
3012
  maxRetries: config.gateway.max_retries ?? 2
2824
3013
  });
2825
3014
  const parsed = extractJsonBlock(result.text || '');
@@ -3123,7 +3312,7 @@ export async function createChatRuntime({
3123
3312
  ];
3124
3313
  const specTemplates = ['/spec <topic>'];
3125
3314
  const planTemplates = ['/plan <goal>', '/plan auto <goal>', '/plan approve', '/plan from-spec <spec-path?>'];
3126
- const agentTemplates = ['/agents list', '/agents run planner <task>', '/agents run coder <task>', '/agents run reviewer <task>', '/agents run tester <task>', '/agents run summarizer <task>'];
3315
+ const agentTemplates = ['/agents list', '/agents run planner <task>', '/agents run advisor <task>', '/agents run coder <task>', '/agents run reviewer <task>', '/agents run tester <task>', '/agents run summarizer <task>'];
3127
3316
  const debugTemplates = ['/debug keys on', '/debug keys off', '/debug keys status'];
3128
3317
  const dreamTemplates = ['/dream', '/dream --dry-run', '/dream --scope=project', '/dream --scope=global'];
3129
3318
  const reflectTemplates = ['/reflect', '/reflect --scope=global <request>', '/reflect <request>'];
@@ -3353,7 +3542,7 @@ export async function createChatRuntime({
3353
3542
  if (tokens.length === 1 || (tokens.length === 2 && !hasTrailingSpace)) {
3354
3543
  const sub = tokens[1] || '';
3355
3544
  if (sub === 'run') {
3356
- return ['planner', 'coder', 'reviewer', 'tester', 'summarizer']
3545
+ return SUB_AGENT_ROLES
3357
3546
  .map((r) => registerSuggestion(`/agents run ${r} `, completionCopy.generic.agentCommand));
3358
3547
  }
3359
3548
  return ['list', 'run']
@@ -3362,7 +3551,7 @@ export async function createChatRuntime({
3362
3551
  }
3363
3552
  if (tokens[1] === 'run') {
3364
3553
  const rolePrefix = tokens[2] || '';
3365
- return ['planner', 'coder', 'reviewer', 'tester']
3554
+ return SUB_AGENT_ROLES
3366
3555
  .filter((r) => r.startsWith(rolePrefix))
3367
3556
  .map((r) => registerSuggestion(`/agents run ${r} `, completionCopy.generic.agentCommand));
3368
3557
  }
@@ -3661,7 +3850,7 @@ export async function createChatRuntime({
3661
3850
  const todoCount = countActiveTodos(currentSession.todos);
3662
3851
  return {
3663
3852
  type: 'system',
3664
- text: `mode=${executionMode} | model=${model || config.model.name} | max_ctx=${effectiveMaxContextTokens(config)} | session=${currentSession.id} | todos=${todoCount}`
3853
+ text: `mode=${executionMode} | role=general | model=${model || config.model.name} | max_ctx=${effectiveMaxContextTokens(config)} | session=${currentSession.id} | todos=${todoCount}`
3665
3854
  };
3666
3855
  }
3667
3856
  if (parsedInput.command === 'mode') {
@@ -4002,7 +4191,7 @@ export async function createChatRuntime({
4002
4191
  if (sub === 'list') {
4003
4192
  return {
4004
4193
  type: 'system',
4005
- text: 'Sub-agent roles: planner, coder, reviewer, tester, summarizer\nUse: /agents run <role> <task>'
4194
+ text: 'Sub-agent roles: planner, advisor, coder, reviewer, tester, summarizer\nUse: /agents run <role> <task>'
4006
4195
  };
4007
4196
  }
4008
4197
  if (sub === 'run') {
@@ -4010,7 +4199,7 @@ export async function createChatRuntime({
4010
4199
  const task = parsedInput.args.slice(2).join(' ').trim();
4011
4200
  if (!role || !task) return { type: 'system', text: 'Usage: /agents run <role> <task>' };
4012
4201
  if (!SUB_AGENT_ROLES.includes(role)) {
4013
- return { type: 'system', text: 'Unknown role. Allowed: planner|coder|reviewer|tester|summarizer' };
4202
+ return { type: 'system', text: 'Unknown role. Allowed: planner|advisor|coder|reviewer|tester|summarizer' };
4014
4203
  }
4015
4204
  const output = await runSubAgentTask({
4016
4205
  role,
@@ -19,7 +19,7 @@ const DEFAULT_CONFIG = {
19
19
  gateway: {
20
20
  base_url: 'http://127.0.0.1:8000/v1',
21
21
  api_key: '',
22
- timeout_ms: 90000,
22
+ timeout_ms: 1800000,
23
23
  max_retries: 2
24
24
  },
25
25
  model: {
@@ -32,7 +32,8 @@ const DEFAULT_CONFIG = {
32
32
  hard_limit_pct: 98,
33
33
  tool_result_max_chars: 12000,
34
34
  read_file_default_lines: 220,
35
- read_file_max_chars: 24000
35
+ read_file_max_chars: 24000,
36
+ prompt_budget_audit: false
36
37
  },
37
38
  execution: {
38
39
  mode: 'auto',
@@ -56,7 +57,7 @@ const DEFAULT_CONFIG = {
56
57
  },
57
58
  shell: {
58
59
  default: normalizeShellName(process.platform === 'win32' ? 'powershell' : 'bash'),
59
- timeout_ms: 120000
60
+ timeout_ms: 1800000
60
61
  },
61
62
  ui: {
62
63
  language: 'zh',
@@ -177,6 +178,8 @@ function normalizePolicyLists(config) {
177
178
  next.memory.project_binding = ['path', 'alias', 'path-or-alias'].includes(String(next.memory.project_binding || ''))
178
179
  ? String(next.memory.project_binding)
179
180
  : 'path-or-alias';
181
+ next.context = next.context || {};
182
+ next.context.prompt_budget_audit = next.context.prompt_budget_audit === true;
180
183
  next.web = next.web || {};
181
184
  next.web.search_enabled = next.web.search_enabled !== false;
182
185
  next.policy = next.policy || {};
@@ -111,7 +111,7 @@ class FffMcpClient {
111
111
  capabilities: {},
112
112
  clientInfo: {
113
113
  name: 'codemini-cli',
114
- version: '0.4.2'
114
+ version: '0.4.3'
115
115
  }
116
116
  });
117
117
  this.sendNotification('notifications/initialized', {});
@@ -294,7 +294,7 @@ export async function createChatCompletion({
294
294
  messages,
295
295
  temperature = 0.2,
296
296
  tools,
297
- timeoutMs = 90000,
297
+ timeoutMs = 1800000,
298
298
  maxTokens = 4096
299
299
  }) {
300
300
  const payload = buildPayload({ model, temperature, messages, tools, maxTokens });
@@ -317,7 +317,7 @@ export async function createChatCompletionStream({
317
317
  tools,
318
318
  onTextDelta,
319
319
  onToolCallDelta,
320
- timeoutMs = 90000,
320
+ timeoutMs = 1800000,
321
321
  maxTokens = 4096,
322
322
  signal: externalSignal
323
323
  }) {
@@ -344,7 +344,7 @@ export async function createChatCompletion({
344
344
  messages,
345
345
  temperature = 0.2,
346
346
  tools,
347
- timeoutMs = 90000,
347
+ timeoutMs = 1800000,
348
348
  maxRetries = 2
349
349
  }) {
350
350
  const payload = buildPayload({ model, temperature, messages, tools });
@@ -399,7 +399,7 @@ export async function createChatCompletionStream({
399
399
  tools,
400
400
  onTextDelta,
401
401
  onToolCallDelta,
402
- timeoutMs = 90000,
402
+ timeoutMs = 1800000,
403
403
  maxRetries = 2,
404
404
  signal: externalSignal
405
405
  }) {
package/src/core/shell.js CHANGED
@@ -220,7 +220,7 @@ export function runShellCommand({
220
220
  command,
221
221
  cwd = process.cwd(),
222
222
  shell = 'powershell',
223
- timeoutMs = 120000
223
+ timeoutMs = 1800000
224
224
  }) {
225
225
  const shellSpec = resolveShell(shell);
226
226
  const shellCommand =