Skip to content

Commit 04c655a

Browse files
author
X
committed
.
1 parent f634c1e commit 04c655a

File tree

9 files changed

+247
-95
lines changed

9 files changed

+247
-95
lines changed

core/agent-loop.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,17 @@ const AgentLoop = {
246246

247247
if (_abortController.signal.aborted) break;
248248

249+
// Check for parse errors before executing
250+
if (call.error) {
251+
logger.warn(`[Agent] Tool ${call.name} has parse error: ${call.error}`);
252+
const result = `Error: ${call.error}`;
253+
context.push({ role: 'user', content: `TOOL_RESULT (${call.name}):\n${result}` });
254+
EventBus.emit('agent:history', { type: 'tool_result', cycle: iteration, tool: call.name, args: {}, result });
255+
_pushActivity({ kind: 'tool_error', cycle: iteration, tool: call.name, error: call.error });
256+
executedTools++;
257+
continue;
258+
}
259+
249260
// Check circuit breaker before executing
250261
if (_isCircuitOpen(call.name)) {
251262
const circuitRecord = _toolCircuits.get(call.name);
@@ -414,7 +425,9 @@ ARGS: { "path": "/core/agent-loop.js" }
414425
- Act autonomously. Do not ask for permission.
415426
- Every response must use at least one tool unless declaring DONE.
416427
- Iterate: analyze results, identify improvements, apply them, repeat.
417-
- Say DONE only when the goal is fully achieved.
428+
- When you modify code, check write_file output for syntax warnings. Fix any errors before proceeding.
429+
- For recursive self-improvement: after achieving a goal, consider what blockers or inefficiencies you encountered and refactor them.
430+
- Say DONE only when the goal is fully achieved AND all written code is verified (no syntax errors).
418431
419432
## Goal
420433
${goal}

core/llm-client.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -559,29 +559,17 @@ const LLMClient = {
559559
const requestId = Utils.generateId('req');
560560
const isCloudProvider = ['gemini', 'openai', 'anthropic'].includes(modelConfig.provider);
561561

562-
// Debug: Log routing decision
563-
logger.info(`[LLM] Routing decision:`, {
564-
provider: modelConfig.provider,
565-
hostType: modelConfig.hostType,
566-
queryMethod: modelConfig.queryMethod,
567-
isCloudProvider,
568-
model: modelConfig.id
569-
});
570-
571562
// Route to appropriate backend
572563
if (modelConfig.provider === 'transformers' ||
573564
(TransformersClient && TransformersClient.isTransformersModel && TransformersClient.isTransformersModel(modelConfig.id))) {
574-
logger.info(`[LLM] Using Transformers.js backend`);
575565
return await _chatTransformers(messages, modelConfig, onUpdate, requestId);
576566
} else if (modelConfig.hostType === 'browser-cloud' && isCloudProvider) {
577567
// Direct browser-to-cloud API call (no proxy)
578-
logger.info(`[LLM] Using direct cloud API (browser-cloud) for ${modelConfig.provider}/${modelConfig.id}`);
568+
logger.info(`[LLM] Direct API call to ${modelConfig.provider}/${modelConfig.id}`);
579569
return await _chatCloudDirect(messages, modelConfig, onUpdate, requestId);
580570
} else if (modelConfig.queryMethod === 'browser' || modelConfig.provider === 'webllm') {
581-
logger.info(`[LLM] Using WebLLM browser backend`);
582571
return await _chatBrowser(messages, modelConfig, onUpdate, requestId);
583572
} else {
584-
logger.info(`[LLM] Using proxy backend (hostType: ${modelConfig.hostType})`);
585573
return await _chatProxy(messages, modelConfig, onUpdate, requestId);
586574
}
587575
};

core/response-parser.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ const ResponseParser = {
8585
rawPreview: rawArgs.slice(0, 200),
8686
rawEnd: rawArgs.length > 200 ? rawArgs.slice(-50) : ''
8787
});
88-
calls.push({ name, args: {}, error: `JSON Parse Error: ${e.message}` });
88+
// Provide actionable error message
89+
const hint = rawArgs.includes('\n') && !rawArgs.includes('\\n')
90+
? ' Hint: Content has literal newlines - use \\n escapes instead.'
91+
: '';
92+
calls.push({ name, args: {}, error: `JSON Parse Error: ${e.message}.${hint}` });
8993
}
9094
}
9195

core/tool-runner.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,23 @@ const ToolRunner = {
3131
write_file: async (args) => {
3232
const path = args.path || args.file;
3333
const content = args.content;
34-
if (!path || content === undefined) throw new Errors.ValidationError('Missing args');
34+
if (!path) throw new Errors.ValidationError('Missing path argument');
35+
if (content === undefined) throw new Errors.ValidationError('Missing content argument');
36+
3537
await VFS.write(path, content);
36-
return `Wrote ${path} (${content.length} bytes)`;
38+
let result = `Wrote ${path} (${content.length} bytes)`;
39+
40+
// Syntax check for JS files
41+
if (path.endsWith('.js')) {
42+
try {
43+
new Function(content);
44+
} catch (e) {
45+
result += `\n⚠️ WARNING: Syntax error detected - ${e.message}`;
46+
logger.warn(`[ToolRunner] Syntax error in ${path}: ${e.message}`);
47+
}
48+
}
49+
50+
return result;
3751
},
3852
list_files: async (args) => {
3953
const path = args.path || args.directory || args.dir;

index.html

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -104,46 +104,48 @@ <h4 class="model-cards-title">Active Models</h4>
104104
</div>
105105
</div>
106106

107-
<!-- Inline Add/Edit Model Form (shown when adding/editing) -->
108-
<div id="model-form-inline" class="model-form-inline hidden">
109-
<div class="model-form-header">
110-
<h5 id="model-form-title">Add Model</h5>
111-
<button class="close-btn-small" id="close-model-form">×</button>
112-
</div>
113-
114-
<!-- Provider Selection -->
115-
<div class="inline-form-row">
116-
<label>Provider</label>
117-
<select id="provider-select" class="inline-select">
118-
<option value="">Select provider...</option>
119-
</select>
120-
</div>
121-
122-
<!-- Model Selection -->
123-
<div class="inline-form-row hidden" id="model-select-group">
124-
<label>Model</label>
125-
<select id="model-select-dropdown" class="inline-select">
126-
<option value="">Select model...</option>
127-
</select>
128-
</div>
129-
130-
<!-- API Key (if needed) -->
131-
<div class="inline-form-row hidden" id="api-key-group">
132-
<label>API Key</label>
133-
<input type="password" id="model-api-key" class="inline-input" placeholder="Enter API key..." />
134-
</div>
135-
136-
<!-- Connection Type Selection -->
137-
<div class="inline-form-row hidden" id="connection-type-group">
138-
<label>Connection</label>
139-
<select id="connection-type-select" class="inline-select">
140-
<option value="">Select connection type...</option>
141-
</select>
142-
</div>
143-
144-
<div class="inline-form-actions">
145-
<button class="inline-btn-cancel" id="cancel-model-btn">Cancel</button>
146-
<button class="inline-btn-save" id="save-model-btn" disabled>Add Model</button>
107+
<!-- Model Form Modal (moved to overlay) -->
108+
<div id="model-form-overlay" class="model-form-overlay">
109+
<div id="model-form-dialog" class="model-form-dialog">
110+
<div class="model-form-header">
111+
<h5 id="model-form-title">Add Model</h5>
112+
<button class="close-btn-small" id="close-model-form">×</button>
113+
</div>
114+
115+
<!-- Provider Selection -->
116+
<div class="inline-form-row">
117+
<label>Provider</label>
118+
<select id="provider-select" class="inline-select">
119+
<option value="">Select provider...</option>
120+
</select>
121+
</div>
122+
123+
<!-- Model Selection -->
124+
<div class="inline-form-row hidden" id="model-select-group">
125+
<label>Model</label>
126+
<select id="model-select-dropdown" class="inline-select">
127+
<option value="">Select model...</option>
128+
</select>
129+
</div>
130+
131+
<!-- API Key (if needed) -->
132+
<div class="inline-form-row hidden" id="api-key-group">
133+
<label>API Key</label>
134+
<input type="password" id="model-api-key" class="inline-input" placeholder="Enter API key..." />
135+
</div>
136+
137+
<!-- Connection Type Selection -->
138+
<div class="inline-form-row hidden" id="connection-type-group">
139+
<label>Connection</label>
140+
<select id="connection-type-select" class="inline-select">
141+
<option value="">Select connection type...</option>
142+
</select>
143+
</div>
144+
145+
<div class="inline-form-actions">
146+
<button class="inline-btn-cancel" id="cancel-model-btn">Cancel</button>
147+
<button class="inline-btn-save" id="save-model-btn" disabled>Add Model</button>
148+
</div>
147149
</div>
148150
</div>
149151

styles/boot.css

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,60 @@ body::after {
193193
gap: 8px;
194194
}
195195

196-
/* Inline Add/Edit Model Form */
197-
.model-form-inline {
196+
/* Model Form Modal Overlay */
197+
.model-form-overlay {
198+
position: fixed;
199+
inset: 0;
200+
background: rgba(0, 0, 0, 0);
201+
display: flex;
202+
align-items: center;
203+
justify-content: center;
204+
z-index: 10000;
205+
pointer-events: none;
206+
opacity: 0;
207+
transition: background 0.3s ease, opacity 0.2s ease;
208+
}
209+
210+
.model-form-overlay.open {
211+
background: rgba(0, 0, 0, 0.75);
212+
pointer-events: auto;
213+
opacity: 1;
214+
}
215+
216+
/* Model Form Dialog with Hero Animation */
217+
.model-form-dialog {
198218
background: var(--bg-panel);
199-
border: 1px solid var(--border-default);
219+
border: 1px solid var(--primary-dim);
200220
padding: 24px;
201-
margin-top: 15px;
202-
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.3);
221+
width: 420px;
222+
max-width: 90vw;
223+
max-height: 85vh;
224+
overflow-y: auto;
225+
box-shadow:
226+
0 0 40px rgba(0, 255, 255, 0.15),
227+
inset 0 0 20px rgba(0, 0, 0, 0.3);
228+
transform: scale(0.8) translateY(20px);
229+
opacity: 0;
230+
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1),
231+
opacity 0.25s ease;
232+
transform-origin: center center;
233+
}
234+
235+
.model-form-overlay.open .model-form-dialog {
236+
transform: scale(1) translateY(0);
237+
opacity: 1;
238+
}
239+
240+
/* Closing animation */
241+
.model-form-overlay.closing .model-form-dialog {
242+
transform: scale(0.9) translateY(10px);
243+
opacity: 0;
244+
transition: transform 0.2s ease-in, opacity 0.15s ease-in;
245+
}
246+
247+
.model-form-overlay.closing {
248+
background: rgba(0, 0, 0, 0);
249+
transition: background 0.2s ease-in;
203250
}
204251

205252
.model-form-header {
@@ -248,6 +295,19 @@ body::after {
248295
border-color: var(--secondary);
249296
}
250297

298+
/* Mobile responsive for modal */
299+
@media (max-width: 480px) {
300+
.model-form-dialog {
301+
width: 95vw;
302+
padding: 20px 16px;
303+
max-height: 90vh;
304+
}
305+
306+
.model-form-overlay.open .model-form-dialog {
307+
transform: scale(1) translateY(0);
308+
}
309+
}
310+
251311
.inline-form-row {
252312
margin-bottom: 16px;
253313
}
@@ -435,10 +495,44 @@ body::after {
435495
/* Goal Input */
436496
.goal-container-top { margin-bottom: 20px; }
437497
.goal-input-group { display: flex; gap: 10px; }
498+
499+
#goal-input {
500+
flex: 1;
501+
padding: 14px 16px;
502+
background: var(--bg-element);
503+
border: 1px solid var(--border-default);
504+
color: var(--text-primary);
505+
font-family: var(--font-mono);
506+
font-size: 13px;
507+
transition: all 0.2s ease;
508+
}
509+
510+
#goal-input::placeholder {
511+
color: var(--text-muted);
512+
font-style: italic;
513+
}
514+
515+
#goal-input:hover:not(:disabled) {
516+
border-color: var(--border-active);
517+
}
518+
519+
#goal-input:focus {
520+
outline: none;
521+
border-color: var(--primary);
522+
box-shadow: 0 0 10px rgba(0, 255, 255, 0.15), inset 0 0 8px rgba(0, 255, 255, 0.05);
523+
}
524+
525+
#goal-input:disabled {
526+
opacity: 0.5;
527+
cursor: not-allowed;
528+
background: var(--bg-panel);
529+
}
530+
438531
.goal-example-chips { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; justify-content: center; }
439-
.goal-chip {
440-
background: var(--bg-element); border: 1px solid var(--border-default);
532+
.goal-chip {
533+
background: var(--bg-element); border: 1px solid var(--border-default);
441534
color: var(--text-muted); padding: 5px 10px; font-size: 11px; cursor: pointer;
535+
transition: all 0.2s ease;
442536
}
443537
.goal-chip:hover { border-color: var(--primary); color: var(--primary); }
444538

0 commit comments

Comments
 (0)