Skip to content

Commit 5aaefec

Browse files
authored
Merge pull request #254 from CambrianTech/fix/widget-routing-infinite-loop
Fix widget routing infinite loop and room switching
2 parents 25fdb26 + 2afa284 commit 5aaefec

File tree

230 files changed

+35825
-1295
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

230 files changed

+35825
-1295
lines changed

src/debug/jtag/browser/generated.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Browser Structure Registry - Auto-generated
33
*
4-
* Contains 11 daemons and 133 commands and 2 adapters and 26 widgets.
4+
* Contains 11 daemons and 139 commands and 2 adapters and 26 widgets.
55
* Generated by scripts/generate-structure.ts - DO NOT EDIT MANUALLY
66
*/
77

@@ -19,6 +19,9 @@ import { UserDaemonBrowser } from './../daemons/user-daemon/browser/UserDaemonBr
1919
import { WidgetDaemonBrowser } from './../daemons/widget-daemon/browser/WidgetDaemonBrowser';
2020

2121
// Browser commands Imports
22+
import { AdapterAdoptBrowserCommand } from './../commands/adapter/adopt/browser/AdapterAdoptBrowserCommand';
23+
import { AdapterSearchBrowserCommand } from './../commands/adapter/search/browser/AdapterSearchBrowserCommand';
24+
import { AdapterTryBrowserCommand } from './../commands/adapter/try/browser/AdapterTryBrowserCommand';
2225
import { BagOfWordsBrowserCommand } from './../commands/ai/bag-of-words/browser/BagOfWordsBrowserCommand';
2326
import { AICostBrowserCommand } from './../commands/ai/cost/browser/AICostBrowserCommand';
2427
import { AiDetectSemanticLoopBrowserCommand } from './../commands/ai/detect-semantic-loop/browser/AiDetectSemanticLoopBrowserCommand';
@@ -51,6 +54,7 @@ import { DecisionProposeBrowserCommand } from './../commands/collaboration/decis
5154
import { DecisionRankBrowserCommand } from './../commands/collaboration/decision/rank/browser/DecisionRankBrowserCommand';
5255
import { DecisionViewBrowserCommand } from './../commands/collaboration/decision/view/browser/DecisionViewBrowserCommand';
5356
import { DecisionVoteBrowserCommand } from './../commands/collaboration/decision/vote/browser/DecisionVoteBrowserCommand';
57+
import { DmBrowserCommand } from './../commands/collaboration/dm/browser/DmBrowserCommand';
5458
import { WallListBrowserCommand } from './../commands/collaboration/wall/list/browser/WallListBrowserCommand';
5559
import { WallReadBrowserCommand } from './../commands/collaboration/wall/read/browser/WallReadBrowserCommand';
5660
import { WallWriteBrowserCommand } from './../commands/collaboration/wall/write/browser/WallWriteBrowserCommand';
@@ -99,6 +103,7 @@ import { GenomeJobCreateBrowserCommand } from './../commands/genome/job-create/b
99103
import { GenomeJobStatusBrowserCommand } from './../commands/genome/job-status/browser/GenomeJobStatusBrowserCommand';
100104
import { HelpBrowserCommand } from './../commands/help/browser/HelpBrowserCommand';
101105
import { IndicatorBrowserCommand } from './../commands/indicator/browser/IndicatorBrowserCommand';
106+
import { InferenceGenerateBrowserCommand } from './../commands/inference/generate/browser/InferenceGenerateBrowserCommand';
102107
import { ClickBrowserCommand } from './../commands/interface/click/browser/ClickBrowserCommand';
103108
import { GetTextBrowserCommand } from './../commands/interface/get-text/browser/GetTextBrowserCommand';
104109
import { NavigateBrowserCommand } from './../commands/interface/navigate/browser/NavigateBrowserCommand';
@@ -116,6 +121,7 @@ import { LogsSearchBrowserCommand } from './../commands/logs/search/browser/Logs
116121
import { LogsStatsBrowserCommand } from './../commands/logs/stats/browser/LogsStatsBrowserCommand';
117122
import { MediaProcessBrowserCommand } from './../commands/media/process/browser/MediaProcessBrowserCommand';
118123
import { MediaResizeBrowserCommand } from './../commands/media/resize/browser/MediaResizeBrowserCommand';
124+
import { PersonaGenomeBrowserCommand } from './../commands/persona/genome/browser/PersonaGenomeBrowserCommand';
119125
import { GenomeCaptureFeedbackBrowserCommand } from './../commands/persona/learning/capture-feedback/browser/GenomeCaptureFeedbackBrowserCommand';
120126
import { GenomeCaptureInteractionBrowserCommand } from './../commands/persona/learning/capture-interaction/browser/GenomeCaptureInteractionBrowserCommand';
121127
import { GenomeMultiAgentLearnBrowserCommand } from './../commands/persona/learning/multi-agent-learn/browser/GenomeMultiAgentLearnBrowserCommand';
@@ -253,6 +259,21 @@ export const BROWSER_DAEMONS: DaemonEntry[] = [
253259
];
254260

255261
export const BROWSER_COMMANDS: CommandEntry[] = [
262+
{
263+
name: 'adapter/adopt',
264+
className: 'AdapterAdoptBrowserCommand',
265+
commandClass: AdapterAdoptBrowserCommand
266+
},
267+
{
268+
name: 'adapter/search',
269+
className: 'AdapterSearchBrowserCommand',
270+
commandClass: AdapterSearchBrowserCommand
271+
},
272+
{
273+
name: 'adapter/try',
274+
className: 'AdapterTryBrowserCommand',
275+
commandClass: AdapterTryBrowserCommand
276+
},
256277
{
257278
name: 'ai/bag-of-words',
258279
className: 'BagOfWordsBrowserCommand',
@@ -413,6 +434,11 @@ export const BROWSER_COMMANDS: CommandEntry[] = [
413434
className: 'DecisionVoteBrowserCommand',
414435
commandClass: DecisionVoteBrowserCommand
415436
},
437+
{
438+
name: 'collaboration/dm',
439+
className: 'DmBrowserCommand',
440+
commandClass: DmBrowserCommand
441+
},
416442
{
417443
name: 'collaboration/wall/list',
418444
className: 'WallListBrowserCommand',
@@ -653,6 +679,11 @@ export const BROWSER_COMMANDS: CommandEntry[] = [
653679
className: 'IndicatorBrowserCommand',
654680
commandClass: IndicatorBrowserCommand
655681
},
682+
{
683+
name: 'inference/generate',
684+
className: 'InferenceGenerateBrowserCommand',
685+
commandClass: InferenceGenerateBrowserCommand
686+
},
656687
{
657688
name: 'interface/click',
658689
className: 'ClickBrowserCommand',
@@ -738,6 +769,11 @@ export const BROWSER_COMMANDS: CommandEntry[] = [
738769
className: 'MediaResizeBrowserCommand',
739770
commandClass: MediaResizeBrowserCommand
740771
},
772+
{
773+
name: 'persona/genome',
774+
className: 'PersonaGenomeBrowserCommand',
775+
commandClass: PersonaGenomeBrowserCommand
776+
},
741777
{
742778
name: 'persona/learning/capture-feedback',
743779
className: 'GenomeCaptureFeedbackBrowserCommand',

src/debug/jtag/cli.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ async function main() {
108108
const [command, ...rawParams] = commandArgs;
109109

110110
// Parse parameters into object format
111-
const params: Record<string, string | boolean | number> = {};
111+
// ParsedValue supports primitives, arrays (for repeated flags), and objects (for complex params)
112+
type ParsedValue = string | boolean | number | null | ParsedValue[] | { [key: string]: ParsedValue };
113+
const params: Record<string, ParsedValue> = {};
112114
let i = 0;
113115
while (i < rawParams.length) {
114116
const arg = rawParams[i];
@@ -209,17 +211,18 @@ async function main() {
209211
i++;
210212
} else {
211213
// Handle positional arguments - add them to a general array
212-
if (!params._positional) {
214+
if (!params._positional || !Array.isArray(params._positional)) {
213215
params._positional = [];
214216
}
215-
params._positional.push(arg);
217+
(params._positional as ParsedValue[]).push(arg);
216218
i++;
217219
}
218220
}
219221

220222
// Handle positional arguments for single-parameter commands
221223
// This allows `./jtag help screenshot` instead of `./jtag help commandName=screenshot`
222-
if (params._positional && params._positional.length > 0) {
224+
const positional = params._positional;
225+
if (Array.isArray(positional) && positional.length > 0) {
223226
// Map of commands to their primary parameter name
224227
const singleParamCommands: Record<string, string> = {
225228
'help': 'commandName',
@@ -236,20 +239,27 @@ async function main() {
236239
const primaryParam = singleParamCommands[command];
237240
if (primaryParam && !params[primaryParam]) {
238241
// Use first positional arg as the primary parameter
239-
params[primaryParam] = params._positional[0];
242+
params[primaryParam] = positional[0] as ParsedValue;
240243
// Remove from positional array
241-
params._positional = params._positional.slice(1);
242-
if (params._positional.length === 0) {
244+
params._positional = positional.slice(1);
245+
if ((params._positional as ParsedValue[]).length === 0) {
243246
delete params._positional;
244247
}
245248
}
246249
}
247250

248251
// INTELLIGENT ENTRY POINT: Adapts behavior based on detected agent type
252+
type OutputFormat = 'json' | 'human' | 'auto' | 'compact' | 'ai-friendly';
253+
const formatValue = params.format as string | undefined;
254+
const validFormats: OutputFormat[] = ['json', 'human', 'auto', 'compact', 'ai-friendly'];
255+
const format: OutputFormat = (formatValue && validFormats.includes(formatValue as OutputFormat))
256+
? formatValue as OutputFormat
257+
: 'auto';
258+
249259
const entryPoint = new EntryPointAdapter({
250-
verbose: params.verbose,
251-
quiet: params.quiet,
252-
format: params.format || 'auto',
260+
verbose: params.verbose as boolean | undefined,
261+
quiet: params.quiet as boolean | undefined,
262+
format,
253263
showAgentInfo: !params.quiet
254264
});
255265

@@ -290,7 +300,7 @@ async function main() {
290300
}
291301

292302
const clientOptions: JTAGClientConnectOptions = {
293-
targetEnvironment: 'server',
303+
targetEnvironment: 'server',
294304
transportType: 'websocket',
295305
serverUrl: `ws://localhost:${instanceConfig.ports.websocket_server}`,
296306
enableFallback: false,
@@ -300,8 +310,7 @@ async function main() {
300310
cli: {
301311
command,
302312
args: commandArgs,
303-
timestamp: new Date().toISOString(),
304-
sessionPersistence: sessionId ? 'reused' : 'new'
313+
timestamp: new Date().toISOString()
305314
}
306315
}
307316
};
@@ -326,7 +335,7 @@ async function main() {
326335

327336
const isConnectionRefused = connectionError.message.includes('ECONNREFUSED') ||
328337
connectionError.message.includes('connect') ||
329-
(err && typeof err === 'object' && 'code' in err && err.code === 'ECONNREFUSED');
338+
(err && typeof err === 'object' && 'code' in err && (err as {code?: string}).code === 'ECONNREFUSED');
330339

331340
if (behavior.logLevel === 'verbose') {
332341
console.log('='.repeat(60));
@@ -337,7 +346,7 @@ async function main() {
337346
console.error('✅ IMMEDIATE ACTION: Run "npm start" and wait 60 seconds');
338347
} else {
339348
console.error('🔍 Connection details:', connectionError.message);
340-
console.error('🔍 Error code:', connectionError.code || 'unknown');
349+
console.error('🔍 Error code:', (connectionError as Error & {code?: string}).code || 'unknown');
341350
}
342351
} else {
343352
// Clean JSON error for connection failures - send to stderr
@@ -373,10 +382,12 @@ async function main() {
373382
// AI commands need longer timeout due to queue + generation time
374383
// Genome commands can take longer for training operations
375384
// Interface commands (screenshot) may need to wait for html2canvas rendering
385+
// Inference commands (inference/generate) need time for local model generation
376386
const isAICommand = command.startsWith('ai/');
377387
const isGenomeCommand = command.startsWith('genome/');
378388
const isInterfaceCommand = command.startsWith('interface/');
379-
const timeoutMs = isGenomeCommand ? 300000 : isAICommand ? 60000 : isInterfaceCommand ? 60000 : 10000; // 5min for genome, 60s for AI/interface, 10s for others
389+
const isInferenceCommand = command.startsWith('inference/');
390+
const timeoutMs = isGenomeCommand ? 300000 : (isAICommand || isInferenceCommand) ? 60000 : isInterfaceCommand ? 60000 : 10000; // 5min for genome, 60s for AI/inference/interface, 10s for others
380391
const timeoutSeconds = timeoutMs / 1000;
381392

382393
const commandTimeout = new Promise((_, reject) =>
@@ -407,18 +418,22 @@ async function main() {
407418

408419
// Special parameter transformation for screenshot command
409420
if (command === 'screenshot') {
421+
// Ensure options is an object
422+
if (!params.options || typeof params.options !== 'object' || Array.isArray(params.options)) {
423+
params.options = {};
424+
}
425+
const options = params.options as { [key: string]: ParsedValue };
426+
410427
// Convert comma-separated presets to array
411428
if (params.presets && typeof params.presets === 'string') {
412-
params.options = params.options || {};
413-
params.options.presets = params.presets.split(',').map((p: string) => p.trim());
429+
options.presets = params.presets.split(',').map((p: string) => p.trim());
414430
delete params.presets;
415431
}
416-
432+
417433
// Convert comma-separated resolutions to array (if provided as JSON string)
418434
if (params.resolutions && typeof params.resolutions === 'string') {
419435
try {
420-
params.options = params.options || {};
421-
params.options.resolutions = JSON.parse(params.resolutions);
436+
options.resolutions = JSON.parse(params.resolutions);
422437
delete params.resolutions;
423438
} catch (e) {
424439
console.warn(`⚠️ Invalid resolutions JSON: ${params.resolutions}`);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Development files
2+
.eslintrc*
3+
tsconfig*.json
4+
vitest.config.ts
5+
6+
# Build artifacts
7+
*.js.map
8+
*.d.ts.map
9+
10+
# IDE
11+
.vscode/
12+
.idea/
13+
14+
# Logs
15+
*.log
16+
npm-debug.log*
17+
18+
# OS files
19+
.DS_Store
20+
Thumbs.db

0 commit comments

Comments
 (0)