erosolar-cli 1.7.80 → 1.7.82

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.
Files changed (109) hide show
  1. package/agents/erosolar-code.rules.json +5 -0
  2. package/agents/general.rules.json +5 -0
  3. package/dist/bin/erosolar.js +0 -2
  4. package/dist/bin/erosolar.js.map +1 -1
  5. package/dist/contracts/agent-schemas.json +20 -12
  6. package/dist/contracts/unified-schema.json +1 -1
  7. package/dist/core/agent.d.ts +36 -3
  8. package/dist/core/agent.d.ts.map +1 -1
  9. package/dist/core/agent.js +223 -8
  10. package/dist/core/agent.js.map +1 -1
  11. package/dist/core/cliTestHarness.d.ts +200 -0
  12. package/dist/core/cliTestHarness.d.ts.map +1 -0
  13. package/dist/core/cliTestHarness.js +549 -0
  14. package/dist/core/cliTestHarness.js.map +1 -0
  15. package/dist/core/errors/apiKeyErrors.js +1 -1
  16. package/dist/core/errors/apiKeyErrors.js.map +1 -1
  17. package/dist/core/isolatedVerifier.js +274 -22
  18. package/dist/core/isolatedVerifier.js.map +1 -1
  19. package/dist/core/modelDiscovery.d.ts.map +1 -1
  20. package/dist/core/modelDiscovery.js +23 -28
  21. package/dist/core/modelDiscovery.js.map +1 -1
  22. package/dist/core/multilinePasteHandler.d.ts +35 -0
  23. package/dist/core/multilinePasteHandler.d.ts.map +1 -0
  24. package/dist/core/multilinePasteHandler.js +80 -0
  25. package/dist/core/multilinePasteHandler.js.map +1 -0
  26. package/dist/core/secretStore.d.ts +9 -0
  27. package/dist/core/secretStore.d.ts.map +1 -1
  28. package/dist/core/secretStore.js +52 -2
  29. package/dist/core/secretStore.js.map +1 -1
  30. package/dist/core/types.d.ts +6 -0
  31. package/dist/core/types.d.ts.map +1 -1
  32. package/dist/headless/headlessApp.d.ts.map +1 -1
  33. package/dist/headless/headlessApp.js +16 -0
  34. package/dist/headless/headlessApp.js.map +1 -1
  35. package/dist/plugins/providers/google/index.js +3 -2
  36. package/dist/plugins/providers/google/index.js.map +1 -1
  37. package/dist/providers/anthropicProvider.d.ts.map +1 -1
  38. package/dist/providers/anthropicProvider.js +27 -1
  39. package/dist/providers/anthropicProvider.js.map +1 -1
  40. package/dist/providers/googleProvider.d.ts.map +1 -1
  41. package/dist/providers/googleProvider.js +23 -1
  42. package/dist/providers/googleProvider.js.map +1 -1
  43. package/dist/providers/openaiChatCompletionsProvider.d.ts +2 -1
  44. package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
  45. package/dist/providers/openaiChatCompletionsProvider.js +111 -4
  46. package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
  47. package/dist/providers/openaiResponsesProvider.d.ts.map +1 -1
  48. package/dist/providers/openaiResponsesProvider.js +39 -18
  49. package/dist/providers/openaiResponsesProvider.js.map +1 -1
  50. package/dist/runtime/agentController.d.ts +4 -0
  51. package/dist/runtime/agentController.d.ts.map +1 -1
  52. package/dist/runtime/agentController.js +29 -3
  53. package/dist/runtime/agentController.js.map +1 -1
  54. package/dist/security/persistence-research.d.ts +0 -2
  55. package/dist/security/persistence-research.d.ts.map +1 -1
  56. package/dist/security/persistence-research.js +0 -2
  57. package/dist/security/persistence-research.js.map +1 -1
  58. package/dist/security/security-testing-framework.d.ts +0 -2
  59. package/dist/security/security-testing-framework.d.ts.map +1 -1
  60. package/dist/security/security-testing-framework.js +0 -2
  61. package/dist/security/security-testing-framework.js.map +1 -1
  62. package/dist/shell/bracketedPasteManager.d.ts +8 -5
  63. package/dist/shell/bracketedPasteManager.d.ts.map +1 -1
  64. package/dist/shell/bracketedPasteManager.js +27 -43
  65. package/dist/shell/bracketedPasteManager.js.map +1 -1
  66. package/dist/shell/composableMessage.d.ts +1 -1
  67. package/dist/shell/composableMessage.js +2 -2
  68. package/dist/shell/composableMessage.js.map +1 -1
  69. package/dist/shell/interactiveShell.d.ts +7 -48
  70. package/dist/shell/interactiveShell.d.ts.map +1 -1
  71. package/dist/shell/interactiveShell.js +144 -340
  72. package/dist/shell/interactiveShell.js.map +1 -1
  73. package/dist/shell/shellApp.d.ts.map +1 -1
  74. package/dist/shell/shellApp.js +54 -3
  75. package/dist/shell/shellApp.js.map +1 -1
  76. package/dist/shell/systemPrompt.d.ts +1 -1
  77. package/dist/shell/systemPrompt.d.ts.map +1 -1
  78. package/dist/shell/systemPrompt.js +10 -3
  79. package/dist/shell/systemPrompt.js.map +1 -1
  80. package/dist/shell/updateManager.js +4 -2
  81. package/dist/shell/updateManager.js.map +1 -1
  82. package/dist/subagents/taskRunner.js +2 -2
  83. package/dist/subagents/taskRunner.js.map +1 -1
  84. package/dist/tools/cloudTools.d.ts +0 -2
  85. package/dist/tools/cloudTools.d.ts.map +1 -1
  86. package/dist/tools/cloudTools.js +0 -2
  87. package/dist/tools/cloudTools.js.map +1 -1
  88. package/dist/tools/fileTools.d.ts.map +1 -1
  89. package/dist/tools/fileTools.js +31 -3
  90. package/dist/tools/fileTools.js.map +1 -1
  91. package/dist/ui/ShellUIAdapter.d.ts +10 -2
  92. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  93. package/dist/ui/ShellUIAdapter.js +123 -11
  94. package/dist/ui/ShellUIAdapter.js.map +1 -1
  95. package/dist/ui/keyboardShortcuts.d.ts.map +1 -1
  96. package/dist/ui/keyboardShortcuts.js +12 -2
  97. package/dist/ui/keyboardShortcuts.js.map +1 -1
  98. package/dist/ui/persistentPrompt.d.ts +24 -0
  99. package/dist/ui/persistentPrompt.d.ts.map +1 -1
  100. package/dist/ui/persistentPrompt.js +86 -4
  101. package/dist/ui/persistentPrompt.js.map +1 -1
  102. package/dist/ui/toolDisplay.d.ts.map +1 -1
  103. package/dist/ui/toolDisplay.js +652 -0
  104. package/dist/ui/toolDisplay.js.map +1 -1
  105. package/package.json +10 -10
  106. package/dist/shell/inputProcessor.d.ts +0 -55
  107. package/dist/shell/inputProcessor.d.ts.map +0 -1
  108. package/dist/shell/inputProcessor.js +0 -171
  109. package/dist/shell/inputProcessor.js.map +0 -1
@@ -43,6 +43,11 @@
43
43
  "summary": "Narrate intent before running tools/commands and summarize output afterward.",
44
44
  "severity": "required"
45
45
  },
46
+ {
47
+ "id": "guardrail.continuous_execution",
48
+ "summary": "CRITICAL: Continue using tools until the task is complete. Do NOT stop after reading files—immediately proceed to make the requested edits. Do NOT describe what you WILL do—just DO it by calling the appropriate tools. After each tool call, continue with the next step until the task is fully accomplished.",
49
+ "severity": "critical"
50
+ },
46
51
  {
47
52
  "id": "guardrail.manual_loop_supervision",
48
53
  "summary": "Stop if you lack evidence—surface blockers instead of guessing or editing blindly.",
@@ -42,6 +42,11 @@
42
42
  "detail": "Keep tool usage auditable by narrating the intent before running them and summarizing their output afterward.",
43
43
  "severity": "required"
44
44
  },
45
+ {
46
+ "id": "guardrail.continuous_execution",
47
+ "summary": "CRITICAL: Continue using tools until the task is complete. Do NOT stop after reading files—immediately proceed to make the requested edits. Do NOT describe what you WILL do—just DO it by calling the appropriate tools. After each tool call, continue with the next step until the task is fully accomplished.",
48
+ "severity": "critical"
49
+ },
45
50
  {
46
51
  "id": "guardrail.manual_loop_supervision",
47
52
  "summary": "Humans supervise the loop manually—escalate when you lack evidence or stall.",
@@ -4,8 +4,6 @@
4
4
  *
5
5
  * @license MIT
6
6
  * @author Bo Shang
7
- *
8
- * Thank you to Anthropic for allowing me to use Claude Code to build erosolar-cli.
9
7
  */
10
8
  import { launchShell } from '../shell/shellApp.js';
11
9
  import { runHeadlessApp } from '../headless/headlessApp.js';
@@ -1 +1 @@
1
- {"version":3,"file":"erosolar.js","sourceRoot":"","sources":["../../src/bin/erosolar.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACvC,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;KAAM,CAAC;IACN,WAAW,CAAC,eAAe,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7E,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"erosolar.js","sourceRoot":"","sources":["../../src/bin/erosolar.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACvC,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;KAAM,CAAC;IACN,WAAW,CAAC,eAAe,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7E,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -61,6 +61,14 @@
61
61
  ],
62
62
 
63
63
  "models": [
64
+ {
65
+ "id": "gpt-5.1-codex-mini",
66
+ "label": "gpt-5.1-codex-mini",
67
+ "provider": "openai",
68
+ "description": "Lightweight GPT-5.1 Codex for fast iteration and quick tasks.",
69
+ "reasoningEffort": "low",
70
+ "capabilities": ["chat", "reasoning", "tools", "streaming"]
71
+ },
64
72
  {
65
73
  "id": "gpt-5.1-codex",
66
74
  "label": "gpt-5.1-codex",
@@ -111,8 +119,8 @@
111
119
  "capabilities": ["chat", "reasoning", "tools", "streaming"]
112
120
  },
113
121
  {
114
- "id": "claude-opus-4.1",
115
- "label": "opus-4.1",
122
+ "id": "claude-opus-4-20250514",
123
+ "label": "opus-4",
116
124
  "provider": "anthropic",
117
125
  "description": "Anthropic Opus 4.1 for the richest Claude reasoning runs.",
118
126
  "temperature": 0.7,
@@ -120,7 +128,7 @@
120
128
  "capabilities": ["chat", "reasoning", "tools", "streaming"]
121
129
  },
122
130
  {
123
- "id": "claude-haiku-4.5",
131
+ "id": "claude-haiku-4-5-20251001",
124
132
  "label": "haiku-4.5",
125
133
  "provider": "anthropic",
126
134
  "description": "Anthropic Haiku 4.5 focused on latency-sensitive workflows.",
@@ -143,12 +151,19 @@
143
151
  "capabilities": ["chat", "tools", "streaming"]
144
152
  },
145
153
  {
146
- "id": "grok-4.1-fast-reasoning",
147
- "label": "grok-4.1-fast-reasoning",
154
+ "id": "grok-4-1-fast-reasoning",
155
+ "label": "grok-4-1-fast-reasoning",
148
156
  "provider": "xai",
149
157
  "description": "Grok-4.1 fast reasoning mode with improved performance and enhanced thinking capabilities.",
150
158
  "capabilities": ["chat", "reasoning", "tools", "streaming"]
151
159
  },
160
+ {
161
+ "id": "grok-4-1-fast-non-reasoning",
162
+ "label": "grok-4-1-fast-non-reasoning",
163
+ "provider": "xai",
164
+ "description": "Grok-4.1 fast non-reasoning mode for lower latency without thinking.",
165
+ "capabilities": ["chat", "tools", "streaming"]
166
+ },
152
167
  {
153
168
  "id": "grok-4",
154
169
  "label": "grok-4",
@@ -191,13 +206,6 @@
191
206
  "description": "Google Gemini 2.5 Flash for lower-latency edits and iterative coding.",
192
207
  "capabilities": ["chat", "tools", "streaming", "multimodal"]
193
208
  },
194
- {
195
- "id": "gemini-3.0-pro-preview",
196
- "label": "gemini-3.0-pro-preview",
197
- "provider": "google",
198
- "description": "Google Gemini 3.0 Pro Preview for next-generation multimodal reasoning and advanced capabilities.",
199
- "capabilities": ["chat", "reasoning", "tools", "streaming", "multimodal"]
200
- },
201
209
  {
202
210
  "id": "llama3.1:8b",
203
211
  "label": "llama3.1:8b",
@@ -22,7 +22,7 @@
22
22
  "maxDelayMs": 40000,
23
23
  "backoffMultiplier": 2
24
24
  },
25
- "models": ["claude-opus-4-5-20251101", "claude-sonnet-4-5-20250929", "claude-haiku-4-5"],
25
+ "models": ["claude-opus-4-5-20251101", "claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001"],
26
26
  "defaultModel": "claude-sonnet-4-5-20250929",
27
27
  "status": "production"
28
28
  },
@@ -1,6 +1,7 @@
1
1
  import type { ToolRuntime } from './toolRuntime.js';
2
2
  import { type ConversationMessage, type LLMProvider, type ProviderUsage } from './types.js';
3
3
  import { ContextManager } from './contextManager.js';
4
+ import { type PasteSummary } from './multilinePasteHandler.js';
4
5
  export interface AgentCallbacks {
5
6
  onAssistantMessage?(content: string, metadata: AssistantMessageMetadata): void;
6
7
  onStreamChunk?(chunk: string): void;
@@ -11,12 +12,30 @@ export interface AgentCallbacks {
11
12
  onContextRecovery?(attempt: number, maxAttempts: number, message: string): void;
12
13
  /** Called when agent continues after context recovery - useful for updating UI */
13
14
  onContinueAfterRecovery?(): void;
15
+ /** Called when auto-continuing because model expressed intent but didn't act */
16
+ onAutoContinue?(attempt: number, maxAttempts: number, message: string): void;
17
+ /** Called when multi-line paste is detected - displays summary instead of full content */
18
+ onMultilinePaste?(summary: string, metadata: PasteSummary): void;
19
+ /** Called when verification should be triggered for a final response */
20
+ onVerificationNeeded?(response: string, context: VerificationCallbackContext): void;
21
+ }
22
+ export interface VerificationCallbackContext {
23
+ /** Working directory for verification */
24
+ workingDirectory: string;
25
+ /** Recent conversation history for context */
26
+ conversationHistory: string[];
27
+ /** Provider ID */
28
+ provider: string;
29
+ /** Model ID */
30
+ model: string;
14
31
  }
15
32
  export interface AssistantMessageMetadata {
16
33
  isFinal: boolean;
17
34
  elapsedMs?: number;
18
35
  usage?: ProviderUsage | null;
19
36
  contextStats?: Record<string, unknown> | null;
37
+ /** True if content was already displayed via streaming chunks */
38
+ wasStreamed?: boolean;
20
39
  }
21
40
  interface AgentOptions {
22
41
  provider: LLMProvider;
@@ -24,6 +43,12 @@ interface AgentOptions {
24
43
  systemPrompt: string;
25
44
  callbacks?: AgentCallbacks;
26
45
  contextManager?: ContextManager;
46
+ /** Provider ID for verification context */
47
+ providerId?: string;
48
+ /** Model ID for verification context */
49
+ modelId?: string;
50
+ /** Working directory for verification */
51
+ workingDirectory?: string;
27
52
  }
28
53
  export declare class AgentRuntime {
29
54
  private readonly messages;
@@ -33,6 +58,9 @@ export declare class AgentRuntime {
33
58
  private readonly contextManager;
34
59
  private activeRun;
35
60
  private readonly baseSystemPrompt;
61
+ private readonly providerId;
62
+ private readonly modelId;
63
+ private readonly workingDirectory;
36
64
  constructor(options: AgentOptions);
37
65
  send(text: string, useStreaming?: boolean): Promise<string>;
38
66
  private processConversation;
@@ -48,6 +76,11 @@ export declare class AgentRuntime {
48
76
  private resolveToolCalls;
49
77
  private get providerTools();
50
78
  private emitAssistantMessage;
79
+ /**
80
+ * Trigger verification for a final response if callback is registered
81
+ * and response contains verifiable claims (implementation, build success, etc.)
82
+ */
83
+ private triggerVerificationIfNeeded;
51
84
  getHistory(): ConversationMessage[];
52
85
  loadHistory(history: ConversationMessage[]): void;
53
86
  clearHistory(): void;
@@ -72,9 +105,9 @@ export declare class AgentRuntime {
72
105
  *
73
106
  * This is called when an API call fails due to context length exceeding limits.
74
107
  * It performs increasingly aggressive pruning on each attempt:
75
- * - Attempt 1: Remove 30% of oldest messages
76
- * - Attempt 2: Remove 50% of oldest messages
77
- * - Attempt 3: Remove 70% of oldest messages (keep only recent)
108
+ * - Attempt 1: Remove 30% of oldest messages + truncate tool outputs to 5k
109
+ * - Attempt 2: Remove 50% of oldest messages + truncate tool outputs to 2k
110
+ * - Attempt 3: Remove 70% of oldest messages + truncate tool outputs to 500 chars
78
111
  *
79
112
  * @returns true if recovery was successful (context was reduced)
80
113
  */
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/core/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAGhB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAuBrD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC/E,aAAa,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,eAAe,CAAC,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7E,8DAA8D;IAC9D,kBAAkB,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,8DAA8D;IAC9D,iBAAiB,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChF,kFAAkF;IAClF,uBAAuB,CAAC,IAAI,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC/C;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgB;gBAErC,OAAO,EAAE,YAAY;IAa3B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;YAqBjD,mBAAmB;YAwDnB,4BAA4B;IA2E1C;;;;;;;OAOG;YACW,gBAAgB;IAgE9B,OAAO,KAAK,aAAa,GAExB;IAED,OAAO,CAAC,oBAAoB;IAY5B,UAAU,IAAI,mBAAmB,EAAE;IAInC,WAAW,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,IAAI;IAajD,YAAY,IAAI,IAAI;IAOpB;;;;;;OAMG;YACW,qBAAqB;IAiCnC;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,iBAAiB,IAAI,cAAc,GAAG,IAAI;IAI1C;;;;;;;;;;OAUG;YACW,0BAA0B;CAmJzC"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/core/agent.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,54 @@
1
+ import { isMultilinePaste, processPaste } from './multilinePasteHandler.js';
1
2
  /**
2
3
  * Maximum number of context overflow recovery attempts
3
4
  */
4
5
  const MAX_CONTEXT_RECOVERY_ATTEMPTS = 3;
6
+ /**
7
+ * Maximum number of auto-continuation attempts when model expresses intent but doesn't act
8
+ */
9
+ const MAX_AUTO_CONTINUE_ATTEMPTS = 3;
10
+ /**
11
+ * Patterns that indicate the model intends to take action but hasn't yet
12
+ * These suggest the model should be prompted to continue
13
+ */
14
+ const INTENT_WITHOUT_ACTION_PATTERNS = [
15
+ // "Let me X" patterns - model is stating what it will do
16
+ /\blet me\s+(create|write|implement|add|update|edit|modify|fix|build|make|refactor|read|check|look|search|find|analyze|examine|review)/i,
17
+ // "I'll X" / "I will X" patterns
18
+ /\bi['']ll\s+(create|write|implement|add|update|edit|modify|fix|build|make|refactor|read|check|look|search|find|analyze|start|begin|now)/i,
19
+ /\bi will\s+(create|write|implement|add|update|edit|modify|fix|build|make|refactor|read|check|look|search|find|analyze|start|begin|now)/i,
20
+ // "I'm going to X" patterns
21
+ /\bi['']m going to\s+(create|write|implement|add|update|edit|modify|fix|build|make|refactor|read|check|look|search|find|analyze)/i,
22
+ // "Now I'll X" / "First, I'll X" patterns
23
+ /\b(now|first|next)\s*(,)?\s*i['']ll\s+/i,
24
+ // Explicit continuation signals
25
+ /\bhere['']s (the|my) (plan|approach|solution|implementation)/i,
26
+ // Numbered steps suggesting action to come
27
+ /^\s*\d+\.\s+(create|write|implement|add|update|edit|modify|fix|show|read|check)/im,
28
+ // Bullet points suggesting planned actions
29
+ /^[\s•\-\*]+\s*(create|write|implement|add|update|edit|modify|fix|shows?|reads?|checks?)\s/im,
30
+ ];
31
+ /**
32
+ * Check if response indicates intent to act without actually acting
33
+ * This detects when the model says "let me do X" but doesn't call any tools
34
+ */
35
+ function shouldAutoContinue(content, hasToolCalls) {
36
+ // If there are tool calls, no need to auto-continue
37
+ if (hasToolCalls) {
38
+ return false;
39
+ }
40
+ // If content is very short, likely not an incomplete intent
41
+ if (content.length < 50) {
42
+ return false;
43
+ }
44
+ // Check for intent patterns
45
+ for (const pattern of INTENT_WITHOUT_ACTION_PATTERNS) {
46
+ if (pattern.test(content)) {
47
+ return true;
48
+ }
49
+ }
50
+ return false;
51
+ }
5
52
  /**
6
53
  * Check if an error is a context overflow error
7
54
  */
@@ -24,11 +71,17 @@ export class AgentRuntime {
24
71
  contextManager;
25
72
  activeRun = null;
26
73
  baseSystemPrompt;
74
+ providerId;
75
+ modelId;
76
+ workingDirectory;
27
77
  constructor(options) {
28
78
  this.provider = options.provider;
29
79
  this.toolRuntime = options.toolRuntime;
30
80
  this.callbacks = options.callbacks ?? {};
31
81
  this.contextManager = options.contextManager ?? null;
82
+ this.providerId = options.providerId ?? 'unknown';
83
+ this.modelId = options.modelId ?? 'unknown';
84
+ this.workingDirectory = options.workingDirectory ?? process.cwd();
32
85
  const trimmedPrompt = options.systemPrompt.trim();
33
86
  this.baseSystemPrompt = trimmedPrompt || null;
34
87
  if (trimmedPrompt) {
@@ -40,7 +93,18 @@ export class AgentRuntime {
40
93
  if (!prompt) {
41
94
  return '';
42
95
  }
43
- this.messages.push({ role: 'user', content: prompt });
96
+ // Handle multi-line paste: show summary to user, send full content to AI
97
+ if (isMultilinePaste(prompt)) {
98
+ const processed = processPaste(prompt);
99
+ // Notify UI about the paste summary
100
+ this.callbacks.onMultilinePaste?.(processed.displaySummary, processed.metadata);
101
+ // But send the full content to the AI
102
+ this.messages.push({ role: 'user', content: processed.fullContent });
103
+ }
104
+ else {
105
+ // Single-line or short text: send as-is
106
+ this.messages.push({ role: 'user', content: prompt });
107
+ }
44
108
  const run = { startedAt: Date.now() };
45
109
  this.activeRun = run;
46
110
  try {
@@ -57,6 +121,7 @@ export class AgentRuntime {
57
121
  }
58
122
  async processConversation() {
59
123
  let contextRecoveryAttempts = 0;
124
+ let autoContinueAttempts = 0;
60
125
  while (true) {
61
126
  // Prune messages if approaching context limit (BEFORE generation)
62
127
  await this.pruneMessagesIfNeeded();
@@ -80,13 +145,40 @@ export class AgentRuntime {
80
145
  }
81
146
  this.messages.push(assistantMessage);
82
147
  await this.resolveToolCalls(response.toolCalls);
148
+ // Reset auto-continue counter since model is actively working
149
+ autoContinueAttempts = 0;
83
150
  continue;
84
151
  }
85
152
  const reply = response.content?.trim() ?? '';
153
+ // Check if model expressed intent to act but didn't call tools
154
+ // This catches "Let me create..." without actual tool calls
155
+ if (shouldAutoContinue(reply, false) && autoContinueAttempts < MAX_AUTO_CONTINUE_ATTEMPTS) {
156
+ autoContinueAttempts++;
157
+ // Emit the planning content but mark as non-final
158
+ if (reply) {
159
+ this.emitAssistantMessage(reply, { isFinal: false, usage, contextStats });
160
+ }
161
+ this.messages.push({ role: 'assistant', content: reply });
162
+ // Auto-prompt with increasingly direct instructions
163
+ const prompts = [
164
+ 'Continue. Execute the actions you described using the available tools.',
165
+ 'You MUST use tools NOW. Call write_file or edit_file immediately. Do not explain - just call the tool.',
166
+ 'CRITICAL: Call a tool right now. Use write_file with file_path and content parameters. No more text - only tool calls.',
167
+ ];
168
+ this.messages.push({
169
+ role: 'user',
170
+ content: prompts[Math.min(autoContinueAttempts - 1, prompts.length - 1)],
171
+ });
172
+ const autoContinueMessage = `Model expressed intent but didn't use tools. Auto-prompting to continue...`;
173
+ this.callbacks.onAutoContinue?.(autoContinueAttempts, MAX_AUTO_CONTINUE_ATTEMPTS, autoContinueMessage);
174
+ continue;
175
+ }
86
176
  if (reply) {
87
177
  this.emitAssistantMessage(reply, { isFinal: true, usage, contextStats });
88
178
  }
89
179
  this.messages.push({ role: 'assistant', content: reply });
180
+ // Trigger verification for final responses with verifiable claims
181
+ this.triggerVerificationIfNeeded(reply);
90
182
  return reply;
91
183
  }
92
184
  catch (error) {
@@ -111,6 +203,7 @@ export class AgentRuntime {
111
203
  return this.processConversation();
112
204
  }
113
205
  let contextRecoveryAttempts = 0;
206
+ let autoContinueAttempts = 0;
114
207
  while (true) {
115
208
  // Prune messages if approaching context limit (BEFORE generation)
116
209
  await this.pruneMessagesIfNeeded();
@@ -138,7 +231,8 @@ export class AgentRuntime {
138
231
  if (toolCalls.length > 0) {
139
232
  const narration = fullContent.trim();
140
233
  if (narration) {
141
- this.emitAssistantMessage(narration, { isFinal: false, usage, contextStats });
234
+ // Mark as wasStreamed since content was already output via onStreamChunk
235
+ this.emitAssistantMessage(narration, { isFinal: false, usage, contextStats, wasStreamed: true });
142
236
  }
143
237
  const assistantMessage = {
144
238
  role: 'assistant',
@@ -147,14 +241,42 @@ export class AgentRuntime {
147
241
  };
148
242
  this.messages.push(assistantMessage);
149
243
  await this.resolveToolCalls(toolCalls);
244
+ // Reset auto-continue counter since model is actively working
245
+ autoContinueAttempts = 0;
150
246
  continue;
151
247
  }
152
- // Final message
248
+ // Check if model expressed intent to act but didn't call tools
249
+ // This catches "Let me create..." without actual tool calls
153
250
  const reply = fullContent.trim();
251
+ if (shouldAutoContinue(reply, false) && autoContinueAttempts < MAX_AUTO_CONTINUE_ATTEMPTS) {
252
+ autoContinueAttempts++;
253
+ // Emit the planning content but mark as non-final
254
+ // Mark as wasStreamed since content was already output via onStreamChunk
255
+ if (reply) {
256
+ this.emitAssistantMessage(reply, { isFinal: false, usage, contextStats, wasStreamed: true });
257
+ }
258
+ this.messages.push({ role: 'assistant', content: reply });
259
+ // Auto-prompt with increasingly direct instructions
260
+ const prompts = [
261
+ 'Continue. Execute the actions you described using the available tools.',
262
+ 'You MUST use tools NOW. Call write_file or edit_file immediately. Do not explain - just call the tool.',
263
+ 'CRITICAL: Call a tool right now. Use write_file with file_path and content parameters. No more text - only tool calls.',
264
+ ];
265
+ this.messages.push({
266
+ role: 'user',
267
+ content: prompts[Math.min(autoContinueAttempts - 1, prompts.length - 1)],
268
+ });
269
+ const autoContinueMessage = `Model expressed intent but didn't use tools. Auto-prompting to continue...`;
270
+ this.callbacks.onAutoContinue?.(autoContinueAttempts, MAX_AUTO_CONTINUE_ATTEMPTS, autoContinueMessage);
271
+ continue;
272
+ }
273
+ // Final message - mark as streamed to avoid double-display in UI
154
274
  if (reply) {
155
- this.emitAssistantMessage(reply, { isFinal: true, usage, contextStats });
275
+ this.emitAssistantMessage(reply, { isFinal: true, usage, contextStats, wasStreamed: true });
156
276
  }
157
277
  this.messages.push({ role: 'assistant', content: reply });
278
+ // Trigger verification for final responses with verifiable claims
279
+ this.triggerVerificationIfNeeded(reply);
158
280
  return reply;
159
281
  }
160
282
  catch (error) {
@@ -249,6 +371,46 @@ export class AgentRuntime {
249
371
  }
250
372
  this.callbacks.onAssistantMessage?.(content, payload);
251
373
  }
374
+ /**
375
+ * Trigger verification for a final response if callback is registered
376
+ * and response contains verifiable claims (implementation, build success, etc.)
377
+ */
378
+ triggerVerificationIfNeeded(response) {
379
+ if (!this.callbacks.onVerificationNeeded) {
380
+ return;
381
+ }
382
+ // Only trigger verification for responses that likely contain verifiable claims
383
+ // These patterns indicate the model is claiming to have completed work
384
+ const verifiablePatterns = [
385
+ /\b(implemented|created|wrote|added|fixed|built|deployed|completed|refactored)\b/i,
386
+ /\b(tests?\s+(are\s+)?pass(ing)?|build\s+succeed)/i,
387
+ /\b(file|function|class|module|component)\s+(has been|is now|was)\s+(created|updated|modified)/i,
388
+ /✅|✓|\[done\]|\[complete\]/i,
389
+ /\bcommit(ted)?\b.*\b(success|done)\b/i,
390
+ ];
391
+ const hasVerifiableClaims = verifiablePatterns.some(pattern => pattern.test(response));
392
+ if (!hasVerifiableClaims) {
393
+ return;
394
+ }
395
+ // Build conversation history for context (last 5 user/assistant exchanges)
396
+ const conversationHistory = [];
397
+ const recentMessages = this.messages.slice(-10);
398
+ for (const msg of recentMessages) {
399
+ if (msg.role === 'user' || msg.role === 'assistant') {
400
+ const content = typeof msg.content === 'string' ? msg.content : '';
401
+ if (content.length > 0) {
402
+ conversationHistory.push(`${msg.role}: ${content.slice(0, 500)}`);
403
+ }
404
+ }
405
+ }
406
+ // Trigger verification callback
407
+ this.callbacks.onVerificationNeeded(response, {
408
+ workingDirectory: this.workingDirectory,
409
+ conversationHistory,
410
+ provider: this.providerId,
411
+ model: this.modelId,
412
+ });
413
+ }
252
414
  getHistory() {
253
415
  return this.messages.map(cloneMessage);
254
416
  }
@@ -323,9 +485,9 @@ export class AgentRuntime {
323
485
  *
324
486
  * This is called when an API call fails due to context length exceeding limits.
325
487
  * It performs increasingly aggressive pruning on each attempt:
326
- * - Attempt 1: Remove 30% of oldest messages
327
- * - Attempt 2: Remove 50% of oldest messages
328
- * - Attempt 3: Remove 70% of oldest messages (keep only recent)
488
+ * - Attempt 1: Remove 30% of oldest messages + truncate tool outputs to 5k
489
+ * - Attempt 2: Remove 50% of oldest messages + truncate tool outputs to 2k
490
+ * - Attempt 3: Remove 70% of oldest messages + truncate tool outputs to 500 chars
329
491
  *
330
492
  * @returns true if recovery was successful (context was reduced)
331
493
  */
@@ -333,6 +495,9 @@ export class AgentRuntime {
333
495
  // Calculate reduction percentage based on attempt
334
496
  const reductionPercentages = [0.3, 0.5, 0.7];
335
497
  const reductionPercent = reductionPercentages[attempt - 1] ?? 0.7;
498
+ // Increasingly aggressive tool output truncation limits
499
+ const toolOutputLimits = [5000, 2000, 500];
500
+ const toolOutputLimit = toolOutputLimits[attempt - 1] ?? 500;
336
501
  // Notify UI about recovery attempt
337
502
  const message = `Context overflow detected. Auto-squishing context (attempt ${attempt}/${MAX_CONTEXT_RECOVERY_ATTEMPTS}, removing ${Math.round(reductionPercent * 100)}% of history)...`;
338
503
  this.callbacks.onContextRecovery?.(attempt, MAX_CONTEXT_RECOVERY_ATTEMPTS, message);
@@ -433,6 +598,39 @@ export class AgentRuntime {
433
598
  // Flatten valid turns back to messages
434
599
  const keepMessages = validTurns.flat();
435
600
  const actualRemoveCount = conversationMessages.length - keepMessages.length;
601
+ // Aggressively truncate tool outputs in remaining messages
602
+ let truncatedCount = 0;
603
+ for (const msg of keepMessages) {
604
+ if (msg.role === 'tool' && msg.content) {
605
+ const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
606
+ if (content.length > toolOutputLimit) {
607
+ // Truncate with smart ending
608
+ const truncated = content.slice(0, toolOutputLimit);
609
+ const lastNewline = truncated.lastIndexOf('\n');
610
+ const cutPoint = lastNewline > toolOutputLimit * 0.7 ? lastNewline : toolOutputLimit;
611
+ msg.content = truncated.slice(0, cutPoint) + `\n\n[... truncated ${content.length - cutPoint} chars for context recovery ...]`;
612
+ truncatedCount++;
613
+ }
614
+ }
615
+ // Also truncate very long assistant messages
616
+ if (msg.role === 'assistant' && msg.content && msg.content.length > toolOutputLimit * 2) {
617
+ const content = msg.content;
618
+ const limit = toolOutputLimit * 2;
619
+ const truncated = content.slice(0, limit);
620
+ const lastNewline = truncated.lastIndexOf('\n');
621
+ const cutPoint = lastNewline > limit * 0.8 ? lastNewline : limit;
622
+ msg.content = truncated.slice(0, cutPoint) + `\n\n[... truncated for context recovery ...]`;
623
+ truncatedCount++;
624
+ }
625
+ }
626
+ // Also truncate system messages if they're huge (except first system prompt)
627
+ for (let i = 1; i < systemMessages.length; i++) {
628
+ const sys = systemMessages[i];
629
+ if (sys && sys.content && sys.content.length > toolOutputLimit) {
630
+ sys.content = sys.content.slice(0, toolOutputLimit) + '\n[... truncated ...]';
631
+ truncatedCount++;
632
+ }
633
+ }
436
634
  // Rebuild message array
437
635
  this.messages.length = 0;
438
636
  // Add system messages
@@ -442,7 +640,7 @@ export class AgentRuntime {
442
640
  // Add summary notice
443
641
  this.messages.push({
444
642
  role: 'system',
445
- content: `[Auto Context Recovery] Removed ${actualRemoveCount} earlier messages to stay within token limits. Conversation context has been compressed.`,
643
+ content: `[Auto Context Recovery] Removed ${actualRemoveCount} messages and truncated ${truncatedCount} large outputs to stay within token limits.`,
446
644
  });
447
645
  // Add remaining conversation (maintaining tool call/result pairing)
448
646
  for (const msg of keepMessages) {
@@ -456,7 +654,24 @@ export class AgentRuntime {
456
654
  attempt,
457
655
  removedPercent: reductionPercent * 100,
458
656
  turnsRemoved: targetTurnsToRemove + startIndex,
657
+ truncatedOutputs: truncatedCount,
658
+ toolOutputLimit,
459
659
  });
660
+ // Check if we're still over limit after all reductions
661
+ const newStats = this.contextManager?.getStats(this.messages);
662
+ if (newStats && newStats.percentage > 100) {
663
+ // Still over limit - do one more aggressive pass
664
+ // Truncate ALL tool outputs to absolute minimum
665
+ const minLimit = 200;
666
+ for (const msg of this.messages) {
667
+ if (msg.role === 'tool' && msg.content) {
668
+ const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
669
+ if (content.length > minLimit) {
670
+ msg.content = content.slice(0, minLimit) + '\n[... severely truncated ...]';
671
+ }
672
+ }
673
+ }
674
+ }
460
675
  return true;
461
676
  }
462
677
  }