monomind 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -5
- package/packages/@monomind/cli/.claude/helpers/control-start.cjs +13 -20
- package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +109 -6
- package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +19 -17
- package/packages/@monomind/cli/.claude/helpers/handlers/agent-start-handler.cjs +60 -28
- package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +75 -18
- package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +16 -7
- package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
- package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +4 -4
- package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +40 -22
- package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +0 -12
- package/packages/@monomind/cli/.claude/helpers/handlers/task-handler.cjs +53 -22
- package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +54 -4
- package/packages/@monomind/cli/.claude/helpers/intelligence.cjs +94 -15
- package/packages/@monomind/cli/.claude/helpers/session.cjs +6 -1
- package/packages/@monomind/cli/.claude/helpers/statusline.cjs +10 -10
- package/packages/@monomind/cli/.claude/helpers/token-tracker.cjs +3 -3
- package/packages/@monomind/cli/.claude/helpers/utils/agent-registrations.cjs +41 -0
- package/packages/@monomind/cli/.claude/helpers/utils/fs-helpers.cjs +183 -0
- package/packages/@monomind/cli/.claude/helpers/utils/micro-agents.cjs +10 -6
- package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
- package/packages/@monomind/cli/bin/cli.js +10 -1
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
- package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
- package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
- package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
- package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
- package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
- package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
- package/packages/@monomind/cli/dist/src/commands/org.js +306 -129
- package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
- package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
- package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
- package/packages/@monomind/cli/dist/src/index.js +64 -28
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
- package/packages/@monomind/cli/dist/src/init/executor.js +63 -49
- package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
- package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
- package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +51 -34
- package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +65 -43
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +24 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.js +68 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +30 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +96 -7
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +105 -8
- package/packages/@monomind/cli/dist/src/orgrt/live.html +56 -0
- package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
- package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
- package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.js +72 -0
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
- package/packages/@monomind/cli/dist/src/output.js +12 -5
- package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
- package/packages/@monomind/cli/dist/src/parser.js +130 -5
- package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
- package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
- package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +40 -40
- package/packages/@monomind/cli/dist/src/ui/orgs.html +110 -11
- package/packages/@monomind/cli/dist/src/ui/server.mjs +304 -133
- package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
- package/packages/@monomind/cli/package.json +6 -2
|
@@ -56,6 +56,35 @@ function runWorker(task) {
|
|
|
56
56
|
reject(new Error('embed worker timed out'));
|
|
57
57
|
}, WORKER_TIMEOUT_MS);
|
|
58
58
|
timer.unref?.();
|
|
59
|
+
// Resolve as soon as the result marker is *seen*, rather than waiting for
|
|
60
|
+
// the child's `close` event. This is a robustness net: onnxruntime's
|
|
61
|
+
// native thread pool can keep the child's event loop alive after it has
|
|
62
|
+
// already written its result, so `close` may never fire (or only fires
|
|
63
|
+
// after a slow OS-level teardown). embed-worker.ts explicitly
|
|
64
|
+
// process.exit()s right after writing the marker, but resolving here too
|
|
65
|
+
// means a future regression in that exit call degrades to "resolves
|
|
66
|
+
// immediately, child lingers" instead of "silently discards a correct
|
|
67
|
+
// result after a 90s timeout".
|
|
68
|
+
const tryResolveFromMarker = () => {
|
|
69
|
+
if (settled)
|
|
70
|
+
return;
|
|
71
|
+
const markedLine = stdout
|
|
72
|
+
.split('\n')
|
|
73
|
+
.reverse()
|
|
74
|
+
.find((l) => l.startsWith(RESULT_MARKER));
|
|
75
|
+
if (!markedLine)
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(markedLine.slice(RESULT_MARKER.length));
|
|
79
|
+
settled = true;
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
resolve(parsed);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Marker line present but not yet valid JSON (unlikely — it's written
|
|
85
|
+
// in one shot) — fall through and let `close`/timeout handle it.
|
|
86
|
+
}
|
|
87
|
+
};
|
|
59
88
|
child.stdout?.on('data', (d) => {
|
|
60
89
|
stdout += d.toString();
|
|
61
90
|
if (stdout.length > MAX_WORKER_OUTPUT) {
|
|
@@ -68,7 +97,9 @@ function runWorker(task) {
|
|
|
68
97
|
}
|
|
69
98
|
catch { /* already dead */ }
|
|
70
99
|
reject(new Error('embed worker produced excessive output'));
|
|
100
|
+
return;
|
|
71
101
|
}
|
|
102
|
+
tryResolveFromMarker();
|
|
72
103
|
});
|
|
73
104
|
child.stderr?.on('data', (d) => { if (stderr.length < MAX_WORKER_OUTPUT)
|
|
74
105
|
stderr += d.toString(); });
|
|
@@ -2,11 +2,32 @@
|
|
|
2
2
|
* Config File Manager
|
|
3
3
|
* Shared JSON config file persistence with atomic writes and Zod validation
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* Default config values.
|
|
7
|
+
*
|
|
8
|
+
* IMPORTANT: never hand this object (or a shallow `{ ...DEFAULT_CONFIG }`
|
|
9
|
+
* copy) out directly — its nested sections (`agents`, `swarm`, `memory`, ...)
|
|
10
|
+
* would be shared-by-reference with every caller, so one caller mutating
|
|
11
|
+
* `config.swarm.maxAgents` would silently corrupt this module-level constant
|
|
12
|
+
* for the rest of the process. Always hand out `cloneDefaultConfig()`.
|
|
13
|
+
*/
|
|
14
|
+
declare const DEFAULT_CONFIG: Record<string, unknown>;
|
|
15
|
+
/** Exposed read-only for callers (e.g. config-adapter.ts) that need to
|
|
16
|
+
* agree with this module's defaults instead of hardcoding their own. */
|
|
17
|
+
export { DEFAULT_CONFIG };
|
|
5
18
|
export declare class ConfigFileManager {
|
|
6
19
|
private configPath;
|
|
7
20
|
private config;
|
|
8
21
|
/** Find config file in search paths starting from cwd */
|
|
9
22
|
findConfig(cwd: string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Load config from an EXACT file path (used by `--config <file>` / `-c`).
|
|
25
|
+
* Unlike load()/findConfig(), this never falls back to directory search —
|
|
26
|
+
* an explicitly-named config file that doesn't exist or fails to parse is
|
|
27
|
+
* an error, not a silent fallback to defaults or an unrelated file in the
|
|
28
|
+
* same directory.
|
|
29
|
+
*/
|
|
30
|
+
loadExact(filePath: string): Record<string, unknown>;
|
|
10
31
|
/** Load config from file, returns null if not found */
|
|
11
32
|
load(cwd: string): Record<string, unknown> | null;
|
|
12
33
|
/** Get the current config, loading if needed */
|
|
@@ -10,7 +10,15 @@ const CONFIG_FILENAMES = [
|
|
|
10
10
|
'monomind.config.json',
|
|
11
11
|
'.monomind/config.json',
|
|
12
12
|
];
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Default config values.
|
|
15
|
+
*
|
|
16
|
+
* IMPORTANT: never hand this object (or a shallow `{ ...DEFAULT_CONFIG }`
|
|
17
|
+
* copy) out directly — its nested sections (`agents`, `swarm`, `memory`, ...)
|
|
18
|
+
* would be shared-by-reference with every caller, so one caller mutating
|
|
19
|
+
* `config.swarm.maxAgents` would silently corrupt this module-level constant
|
|
20
|
+
* for the rest of the process. Always hand out `cloneDefaultConfig()`.
|
|
21
|
+
*/
|
|
14
22
|
const DEFAULT_CONFIG = {
|
|
15
23
|
version: '3.5',
|
|
16
24
|
agents: {
|
|
@@ -61,6 +69,20 @@ const DEFAULT_CONFIG = {
|
|
|
61
69
|
},
|
|
62
70
|
},
|
|
63
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Deep-clone the default config so every caller gets its own independent
|
|
74
|
+
* object graph. `{ ...DEFAULT_CONFIG }` is only a shallow copy — its nested
|
|
75
|
+
* sub-configs (`agents`, `swarm`, `memory`, ...) would remain the SAME
|
|
76
|
+
* object references as the module-level constant, so mutating a nested
|
|
77
|
+
* field on one caller's copy would corrupt the shared default for every
|
|
78
|
+
* subsequent caller in the process.
|
|
79
|
+
*/
|
|
80
|
+
function cloneDefaultConfig() {
|
|
81
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
82
|
+
}
|
|
83
|
+
/** Exposed read-only for callers (e.g. config-adapter.ts) that need to
|
|
84
|
+
* agree with this module's defaults instead of hardcoding their own. */
|
|
85
|
+
export { DEFAULT_CONFIG };
|
|
64
86
|
export class ConfigFileManager {
|
|
65
87
|
configPath = null;
|
|
66
88
|
config = null;
|
|
@@ -83,6 +105,36 @@ export class ConfigFileManager {
|
|
|
83
105
|
}
|
|
84
106
|
return null;
|
|
85
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Load config from an EXACT file path (used by `--config <file>` / `-c`).
|
|
110
|
+
* Unlike load()/findConfig(), this never falls back to directory search —
|
|
111
|
+
* an explicitly-named config file that doesn't exist or fails to parse is
|
|
112
|
+
* an error, not a silent fallback to defaults or an unrelated file in the
|
|
113
|
+
* same directory.
|
|
114
|
+
*/
|
|
115
|
+
loadExact(filePath) {
|
|
116
|
+
const resolved = path.resolve(filePath);
|
|
117
|
+
if (!fs.existsSync(resolved)) {
|
|
118
|
+
throw new Error(`Config file not found: ${resolved}`);
|
|
119
|
+
}
|
|
120
|
+
let content;
|
|
121
|
+
try {
|
|
122
|
+
content = fs.readFileSync(resolved, 'utf-8');
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
throw new Error(`Failed to read config file ${resolved}: ${err instanceof Error ? err.message : String(err)}`);
|
|
126
|
+
}
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(content);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
throw new Error(`Failed to parse config file ${resolved}: ${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
}
|
|
134
|
+
this.configPath = resolved;
|
|
135
|
+
this.config = sanitizeConfigObject(parsed);
|
|
136
|
+
return this.config;
|
|
137
|
+
}
|
|
86
138
|
/** Load config from file, returns null if not found */
|
|
87
139
|
load(cwd) {
|
|
88
140
|
this.configPath = this.findConfig(cwd);
|
|
@@ -106,7 +158,7 @@ export class ConfigFileManager {
|
|
|
106
158
|
if (this.config === null) {
|
|
107
159
|
this.load(cwd);
|
|
108
160
|
}
|
|
109
|
-
return this.config ??
|
|
161
|
+
return this.config ?? cloneDefaultConfig();
|
|
110
162
|
}
|
|
111
163
|
/** Get a nested config value by dot-separated key */
|
|
112
164
|
get(cwd, key) {
|
|
@@ -136,7 +188,7 @@ export class ConfigFileManager {
|
|
|
136
188
|
// cross-process atomic without an OS-level flock, but combined with the
|
|
137
189
|
// O_EXCL atomic rename it prevents the most common credential-clobber
|
|
138
190
|
// window where two CLIs interleave their getConfig→set→write cycles.
|
|
139
|
-
let onDisk =
|
|
191
|
+
let onDisk = cloneDefaultConfig();
|
|
140
192
|
if (fs.existsSync(targetPath)) {
|
|
141
193
|
try {
|
|
142
194
|
const parsed = JSON.parse(fs.readFileSync(targetPath, 'utf-8'));
|
|
@@ -155,7 +207,7 @@ export class ConfigFileManager {
|
|
|
155
207
|
if (fs.existsSync(targetPath) && !force) {
|
|
156
208
|
throw new Error(`Config file already exists: ${targetPath}. Use --force to overwrite.`);
|
|
157
209
|
}
|
|
158
|
-
const config = { ...
|
|
210
|
+
const config = { ...cloneDefaultConfig(), ...(overrides ? sanitizeConfigObject(overrides) : {}) };
|
|
159
211
|
this.writeAtomic(targetPath, config);
|
|
160
212
|
this.config = config;
|
|
161
213
|
this.configPath = targetPath;
|
|
@@ -164,8 +216,9 @@ export class ConfigFileManager {
|
|
|
164
216
|
/** Reset config to defaults */
|
|
165
217
|
reset(cwd) {
|
|
166
218
|
const targetPath = this.configPath ?? path.resolve(cwd, CONFIG_FILENAMES[0]);
|
|
167
|
-
|
|
168
|
-
this.
|
|
219
|
+
const fresh = cloneDefaultConfig();
|
|
220
|
+
this.writeAtomic(targetPath, fresh);
|
|
221
|
+
this.config = fresh;
|
|
169
222
|
this.configPath = targetPath;
|
|
170
223
|
return targetPath;
|
|
171
224
|
}
|
|
@@ -228,7 +281,7 @@ export class ConfigFileManager {
|
|
|
228
281
|
}
|
|
229
282
|
/** Get default config */
|
|
230
283
|
getDefaults() {
|
|
231
|
-
return
|
|
284
|
+
return cloneDefaultConfig();
|
|
232
285
|
}
|
|
233
286
|
/** Atomic write with restrictive 0o600 mode.
|
|
234
287
|
* SECURITY: this config file may contain API keys (per `commands/providers.ts`).
|
|
@@ -84,7 +84,8 @@ export function redact(text) {
|
|
|
84
84
|
/(?:secret|password|passwd|pwd)\s*[:=]\s*['"]?[^\s'"]{8,}['"]?/gi,
|
|
85
85
|
/(?:token|bearer)\s*[:=]\s*['"]?[^\s'"]{10,}['"]?/gi,
|
|
86
86
|
/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
|
|
87
|
-
/sk-[a-zA-Z0-
|
|
87
|
+
/sk-ant-[a-zA-Z0-9_-]{20,}/g,
|
|
88
|
+
/sk-[a-zA-Z0-9_-]{20,}/g,
|
|
88
89
|
/ghp_[a-zA-Z0-9]{36}/g,
|
|
89
90
|
/gho_[a-zA-Z0-9]{36}/g,
|
|
90
91
|
/npm_[a-zA-Z0-9]{36}/g,
|
|
@@ -4448,9 +4448,9 @@ function appendChatViewEvent(ev, animate) {
|
|
|
4448
4448
|
const atBottom = !animate || (feed.scrollHeight - feed.scrollTop - feed.clientHeight < 80);
|
|
4449
4449
|
const ts = ev.ts ? new Date(ev.ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'}) : '';
|
|
4450
4450
|
if (ev.type === 'run:start') {
|
|
4451
|
-
el = mkCVSys('▶ Run started' + (ev.goal ? ': ' +
|
|
4451
|
+
el = mkCVSys('▶ Run started' + (ev.goal ? ': ' + (ev.goal.slice(0,80)) : '') + (ev.bossRole ? ' · boss: ' + (ev.bossRole) : ''), ts);
|
|
4452
4452
|
} else if (ev.type === 'run:cycle:complete') {
|
|
4453
|
-
el = mkCVSys('◎ Cycle complete' + (ev.pending != null ? ' · ' +
|
|
4453
|
+
el = mkCVSys('◎ Cycle complete' + (ev.pending != null ? ' · ' + (String(ev.pending)) + ' tasks pending' : ''), ts);
|
|
4454
4454
|
} else if (ev.type === 'intercom') {
|
|
4455
4455
|
el = mkCVIntercom(ev.from, ev.to, ev.msg || '', ts);
|
|
4456
4456
|
} else if (ev.type === 'org:comms') {
|
|
@@ -4459,15 +4459,15 @@ function appendChatViewEvent(ev, animate) {
|
|
|
4459
4459
|
if (ev.to && ev.to !== 'all') el = mkCVIntercom(_cvCommsFrom, ev.to, ev.msg || '', ts);
|
|
4460
4460
|
else el = mkCVAgent(_cvCommsFrom, ev.msg || '', ts, 'org:comms');
|
|
4461
4461
|
} else if (ev.type === 'org:agent:online') {
|
|
4462
|
-
el = mkCVSys('[' +
|
|
4462
|
+
el = mkCVSys('[' + (ev.org || '') + '] ' + (ev.role || ev.title || '?') + ' online' + (ev.title && ev.role && ev.title !== ev.role ? ' (' + (ev.title) + ')' : ''), ts);
|
|
4463
4463
|
} else if (ev.type === 'org:checkpoint') {
|
|
4464
|
-
el = mkCVSys('[checkpoint] ' +
|
|
4464
|
+
el = mkCVSys('[checkpoint] ' + (ev.summary || ev.progress || ev.msg || ''), ts);
|
|
4465
4465
|
} else if (ev.type === 'org:start') {
|
|
4466
|
-
el = mkCVSys('[org:start] ' +
|
|
4466
|
+
el = mkCVSys('[org:start] ' + (ev.org || ''), ts);
|
|
4467
4467
|
} else if (ev.type === 'run:complete') {
|
|
4468
|
-
el = mkCVSys('■ Run complete' + (ev.status ? ' [' +
|
|
4468
|
+
el = mkCVSys('■ Run complete' + (ev.status ? ' [' + (ev.status) + ']' : ''), ts);
|
|
4469
4469
|
} else if (ev.type === 'org:complete') {
|
|
4470
|
-
el = mkCVSys('■ Org complete' + (ev.org ? ' — ' +
|
|
4470
|
+
el = mkCVSys('■ Org complete' + (ev.org ? ' — ' + (ev.org) : ''), ts);
|
|
4471
4471
|
} else if (ev.type === 'agent:result') {
|
|
4472
4472
|
el = mkCVResult(ev.agent || ev.from || '?', ev.result || ev.msg || ev.message || ev.summary || '', ts);
|
|
4473
4473
|
} else if (ev.type === 'agent:spawn') {
|
|
@@ -4487,36 +4487,36 @@ function appendChatViewEvent(ev, animate) {
|
|
|
4487
4487
|
const _auAgent = ev.role || ev.agentType || '?';
|
|
4488
4488
|
const _auCost = ev.cost_usd != null ? '$' + Number(ev.cost_usd).toFixed(4) : '';
|
|
4489
4489
|
const _auTok = (ev.tokens_in || ev.tokens_out) ? ((ev.tokens_in||0)).toLocaleString() + '↑ ' + ((ev.tokens_out||0)).toLocaleString() + '↓' : '';
|
|
4490
|
-
el = mkCVSys('◈ ' +
|
|
4490
|
+
el = mkCVSys('◈ ' + (_auAgent) + (_auTok ? ' · ' + _auTok : '') + (_auCost ? ' · ' + _auCost : ''), ts);
|
|
4491
4491
|
} else if (ev.type === 'agent:message') {
|
|
4492
4492
|
el = mkCVAgent(ev.agent || ev.name || ev.role || '?', ev.msg || ev.message || ev.content || '', ts, ev.type);
|
|
4493
4493
|
} else if (ev.type === 'session:start') {
|
|
4494
|
-
el = mkCVSys('Session started' + (ev.prompt ? ': ' +
|
|
4494
|
+
el = mkCVSys('Session started' + (ev.prompt ? ': ' + (ev.prompt.slice(0,80)) : ''), ts);
|
|
4495
4495
|
} else if (ev.type === 'session:complete') {
|
|
4496
|
-
el = mkCVSys('Session complete' + (ev.status ? ' — ' +
|
|
4496
|
+
el = mkCVSys('Session complete' + (ev.status ? ' — ' + (ev.status) : ''), ts);
|
|
4497
4497
|
} else if (ev.type === 'domain:dispatch') {
|
|
4498
|
-
el = mkCVSys('→ ' +
|
|
4498
|
+
el = mkCVSys('→ ' + (ev.domain || '') + (ev.cmd ? ': ' + (ev.cmd.slice(0,80)) : ''), ts);
|
|
4499
4499
|
} else if (ev.type === 'domain:complete') {
|
|
4500
|
-
el = mkCVSys('✓ ' +
|
|
4500
|
+
el = mkCVSys('✓ ' + (ev.domain || '') + (ev.status ? ' [' + (ev.status) + ']' : ''), ts);
|
|
4501
4501
|
} else if (ev.type === 'loop:start') {
|
|
4502
|
-
el = mkCVSys('◎ Loop: ' +
|
|
4502
|
+
el = mkCVSys('◎ Loop: ' + (ev.command || '') + (ev.repeat ? ' ×' + (String(ev.repeat)) : ''), ts);
|
|
4503
4503
|
if (currentView === 'loops') renderLoops();
|
|
4504
4504
|
} else if (ev.type === 'loop:complete') {
|
|
4505
|
-
el = mkCVSys('■ Loop done: ' +
|
|
4505
|
+
el = mkCVSys('■ Loop done: ' + (ev.command || '') + (ev.ranReps ? ' (' + (String(ev.ranReps)) + ' runs)' : ''), ts);
|
|
4506
4506
|
if (currentView === 'loops') renderLoops();
|
|
4507
4507
|
} else if (ev.type === 'loop:tick') {
|
|
4508
4508
|
const _tickLbl = ev.rep != null
|
|
4509
|
-
? 'rep ' +
|
|
4510
|
-
:
|
|
4509
|
+
? 'rep ' + (String(ev.rep)) + ' done' + (ev.wait != null ? ' → next in ' + (String(ev.wait)) + 's' : '')
|
|
4510
|
+
: (ev.command || ev.loopId || ev.id || '');
|
|
4511
4511
|
el = mkCVSys('◷ ' + _tickLbl, ts);
|
|
4512
4512
|
el.dataset.evType = 'loop:tick';
|
|
4513
4513
|
if (feed.lastElementChild?.dataset?.evType === 'loop:tick') feed.lastElementChild.remove();
|
|
4514
4514
|
if (currentView === 'loops') renderLoops();
|
|
4515
4515
|
} else if (ev.type === 'loop:hil' || ev.type === 'loop:hil:waiting') {
|
|
4516
|
-
el = mkCVSys('⚠ Loop HIL: ' +
|
|
4516
|
+
el = mkCVSys('⚠ Loop HIL: ' + (ev.command || ev.loopId || ev.id || ''), ts);
|
|
4517
4517
|
if (currentView === 'loops') renderLoops();
|
|
4518
4518
|
} else if (ev.type === 'loop:hil:resolved') {
|
|
4519
|
-
el = mkCVSys('✓ Loop HIL resolved: ' +
|
|
4519
|
+
el = mkCVSys('✓ Loop HIL resolved: ' + (ev.loopId || ev.command || ''), ts);
|
|
4520
4520
|
if (currentView === 'loops') renderLoops();
|
|
4521
4521
|
} else if (ev.type === 'file:write') {
|
|
4522
4522
|
el = mkCVFileCard(ev.name || ev.path || '?', ev.path || '', ev.size || 0, ts);
|
|
@@ -4527,12 +4527,12 @@ function appendChatViewEvent(ev, animate) {
|
|
|
4527
4527
|
} else if (ev.type === 'agent:browse') {
|
|
4528
4528
|
el = mkCVTool('BROWSE', ev.payload || ev.url || '', '', 'oklch(65% 0.15 190)', ts);
|
|
4529
4529
|
} else if (ev.type === 'org:agent:offline') {
|
|
4530
|
-
el = mkCVSys('◌ ' +
|
|
4530
|
+
el = mkCVSys('◌ ' + (ev.title || ev.role || '?') + ' offline' + (ev.org ? ' [' + (ev.org) + ']' : ''), ts);
|
|
4531
4531
|
} else if (ev.type === 'org:error') {
|
|
4532
|
-
el = mkCVSys('⚠ Error: ' +
|
|
4532
|
+
el = mkCVSys('⚠ Error: ' + (ev.msg || ev.error || ev.message || ''), ts);
|
|
4533
4533
|
el.classList.add('cv-err');
|
|
4534
4534
|
} else {
|
|
4535
|
-
el = mkCVSys(
|
|
4535
|
+
el = mkCVSys((ev.type || 'event'), ts);
|
|
4536
4536
|
}
|
|
4537
4537
|
if (animate) el.classList.add('cv-new');
|
|
4538
4538
|
feed.appendChild(el);
|
|
@@ -4671,10 +4671,10 @@ function mkCVTool(tag, cmd, output, color, ts) {
|
|
|
4671
4671
|
return d;
|
|
4672
4672
|
}
|
|
4673
4673
|
|
|
4674
|
-
function mkCVSys(
|
|
4674
|
+
function mkCVSys(text, ts) {
|
|
4675
4675
|
const d = document.createElement('div');
|
|
4676
4676
|
d.className = 'cv-msg cv-sys';
|
|
4677
|
-
d.innerHTML = `<div class="cv-bub"><span class="cv-etype">SYS</span><span class="cv-text">${
|
|
4677
|
+
d.innerHTML = `<div class="cv-bub"><span class="cv-etype">SYS</span><span class="cv-text">${esc(text)}</span><span class="cv-ts">${ts}</span></div>`;
|
|
4678
4678
|
return d;
|
|
4679
4679
|
}
|
|
4680
4680
|
|
|
@@ -8014,18 +8014,18 @@ function _odtAppendEvent(ev, animate) {
|
|
|
8014
8014
|
const ts = ev.ts ? new Date(ev.ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'}) : '';
|
|
8015
8015
|
let el;
|
|
8016
8016
|
if (ev.type === 'run:start') {
|
|
8017
|
-
el = mkCVSys('▶ Run started' + (ev.goal ? ': ' +
|
|
8017
|
+
el = mkCVSys('▶ Run started' + (ev.goal ? ': ' + (ev.goal.slice(0,80)) : '') + (ev.bossRole ? ' · boss: ' + (ev.bossRole) : ''), ts);
|
|
8018
8018
|
} else if (ev.type === 'run:cycle:complete') {
|
|
8019
|
-
el = mkCVSys('◎ Cycle complete' + (ev.pending != null ? ' · ' +
|
|
8019
|
+
el = mkCVSys('◎ Cycle complete' + (ev.pending != null ? ' · ' + (String(ev.pending)) + ' tasks pending' : ''), ts);
|
|
8020
8020
|
} else if (ev.type === 'run:complete') {
|
|
8021
|
-
el = mkCVSys('■ Run complete' + (ev.status ? ' [' +
|
|
8021
|
+
el = mkCVSys('■ Run complete' + (ev.status ? ' [' + (ev.status) + ']' : ''), ts);
|
|
8022
8022
|
} else if (ev.type === 'org:comms') {
|
|
8023
8023
|
// Some older runs use ev.role instead of ev.from — normalise before rendering
|
|
8024
8024
|
const _commsFrom = ev.from || ev.role || 'boss';
|
|
8025
8025
|
if (ev.to && ev.to !== 'all') el = mkCVIntercom(_commsFrom, ev.to, ev.msg || '', ts);
|
|
8026
8026
|
else el = mkCVAgent(_commsFrom, ev.msg || '', ts, 'org:comms');
|
|
8027
8027
|
} else if (ev.type === 'org:agent:online') {
|
|
8028
|
-
el = mkCVSys('◉ ' +
|
|
8028
|
+
el = mkCVSys('◉ ' + (ev.title || ev.role || '?') + ' online', ts);
|
|
8029
8029
|
} else if (ev.type === 'org:checkpoint') {
|
|
8030
8030
|
el = mkCVAgent('boss', ev.summary || ev.progress || ev.msg || '', ts, 'org:checkpoint');
|
|
8031
8031
|
} else if (ev.type === 'org:start' || ev.type === 'org:complete') {
|
|
@@ -8041,32 +8041,32 @@ function _odtAppendEvent(ev, animate) {
|
|
|
8041
8041
|
} else if (ev.type === 'agent:message') {
|
|
8042
8042
|
el = mkCVAgent(ev.agent || ev.name || ev.role || '?', ev.msg || ev.message || ev.content || '', ts, ev.type);
|
|
8043
8043
|
} else if (ev.type === 'session:start') {
|
|
8044
|
-
el = mkCVSys('▶ ' +
|
|
8044
|
+
el = mkCVSys('▶ ' + ((ev.prompt || '').replace(/^running org:\s*/i,'').slice(0,80) || 'Session started'), ts);
|
|
8045
8045
|
} else if (ev.type === 'session:complete') {
|
|
8046
|
-
el = mkCVSys('■ Complete' + (ev.status ? ' — ' +
|
|
8046
|
+
el = mkCVSys('■ Complete' + (ev.status ? ' — ' + (ev.status) : ''), ts);
|
|
8047
8047
|
} else if (ev.type === 'domain:dispatch') {
|
|
8048
|
-
el = mkCVSys('→ ' +
|
|
8048
|
+
el = mkCVSys('→ ' + (ev.domain || '') + (ev.cmd ? ': ' + (ev.cmd.slice(0,80)) : ''), ts);
|
|
8049
8049
|
} else if (ev.type === 'domain:complete') {
|
|
8050
|
-
el = mkCVSys('✓ ' +
|
|
8050
|
+
el = mkCVSys('✓ ' + (ev.domain || '') + (ev.status ? ' [' + (ev.status) + ']' : ''), ts);
|
|
8051
8051
|
} else if (ev.type === 'loop:start') {
|
|
8052
|
-
el = mkCVSys('Loop: ' +
|
|
8052
|
+
el = mkCVSys('Loop: ' + (ev.command || ''), ts);
|
|
8053
8053
|
} else if (ev.type === 'loop:complete') {
|
|
8054
|
-
el = mkCVSys('Loop done: ' +
|
|
8054
|
+
el = mkCVSys('Loop done: ' + (ev.command || '') + (ev.ranReps ? ' (' + ev.ranReps + ' runs)' : ''), ts);
|
|
8055
8055
|
} else if (ev.type === 'loop:tick') {
|
|
8056
8056
|
const _trep = ev.rep ?? ev.fromRep;
|
|
8057
8057
|
const _tnext = ev.nextRep ?? ev.toRep;
|
|
8058
8058
|
const tickLabel = _trep != null
|
|
8059
|
-
? 'rep ' +
|
|
8060
|
-
:
|
|
8059
|
+
? 'rep ' + (String(_trep)) + ' done' + (_tnext != null ? ' → rep ' + (String(_tnext)) + (ev.wait != null ? ' in ' + (String(ev.wait)) + 's' : '') : '')
|
|
8060
|
+
: (ev.command || ev.loopId || '');
|
|
8061
8061
|
el = mkCVSys('◷ ' + tickLabel, ts);
|
|
8062
8062
|
el.dataset.evType = 'loop:tick';
|
|
8063
8063
|
// Collapse: replace the previous loop:tick if it's the last element — prevents the
|
|
8064
8064
|
// feed from flooding with one entry per rep in long-running loops (e.g. --repeat 9999).
|
|
8065
8065
|
if (feed.lastElementChild?.dataset?.evType === 'loop:tick') feed.lastElementChild.remove();
|
|
8066
8066
|
} else if (ev.type === 'loop:hil' || ev.type === 'loop:hil:waiting') {
|
|
8067
|
-
el = mkCVSys('⚠ Loop HIL: ' +
|
|
8067
|
+
el = mkCVSys('⚠ Loop HIL: ' + (ev.command || ev.loopId || ''), ts);
|
|
8068
8068
|
} else if (ev.type === 'loop:hil:resolved') {
|
|
8069
|
-
el = mkCVSys('✓ Loop HIL resolved: ' +
|
|
8069
|
+
el = mkCVSys('✓ Loop HIL resolved: ' + (ev.loopId || ''), ts);
|
|
8070
8070
|
} else if (ev.type === 'agent:edit' || ev.type === 'agent:write') {
|
|
8071
8071
|
el = mkCVTool('EDIT', ev.payload || ev.file || ev.path || '', '', 'oklch(65% 0.20 270)', ts);
|
|
8072
8072
|
} else if (ev.type === 'agent:bash') {
|
|
@@ -8074,15 +8074,15 @@ function _odtAppendEvent(ev, animate) {
|
|
|
8074
8074
|
} else if (ev.type === 'agent:browse') {
|
|
8075
8075
|
el = mkCVTool('BROWSE', ev.payload || ev.url || '', '', 'oklch(65% 0.15 190)', ts);
|
|
8076
8076
|
} else if (ev.type === 'org:error') {
|
|
8077
|
-
el = mkCVSys('⚠ Error: ' +
|
|
8077
|
+
el = mkCVSys('⚠ Error: ' + (ev.msg || ev.error || ev.message || ''), ts);
|
|
8078
8078
|
el.classList.add('cv-err');
|
|
8079
8079
|
} else if (ev.type === 'org:agent:offline') {
|
|
8080
|
-
el = mkCVSys('◌ ' +
|
|
8080
|
+
el = mkCVSys('◌ ' + (ev.title || ev.role || '?') + ' offline', ts);
|
|
8081
8081
|
} else if (ev.type === 'user:message') {
|
|
8082
8082
|
el = mkCVAgent('you', ev.text || ev.msg || ev.message || '', ts, 'user:message');
|
|
8083
8083
|
el.classList.add('cv-user-msg');
|
|
8084
8084
|
} else {
|
|
8085
|
-
el = mkCVSys(
|
|
8085
|
+
el = mkCVSys((ev.type || 'event'), ts);
|
|
8086
8086
|
}
|
|
8087
8087
|
if (animate) el.classList.add('cv-new');
|
|
8088
8088
|
feed.appendChild(el);
|
|
@@ -383,6 +383,37 @@ html, body {
|
|
|
383
383
|
content: '—'; color: oklch(36% 0.008 186 / 0.5); flex-shrink: 0; margin-top: 1px;
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
+
/* ── Files tab ──────────────────────────────────────────────────── */
|
|
387
|
+
#files-pane { padding: 20px; }
|
|
388
|
+
.files-grid {
|
|
389
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
390
|
+
gap: 10px;
|
|
391
|
+
}
|
|
392
|
+
.file-card {
|
|
393
|
+
background: var(--bg-panel);
|
|
394
|
+
border: 1px solid var(--border); border-radius: 3px;
|
|
395
|
+
padding: 10px 12px;
|
|
396
|
+
display: flex; align-items: center; gap: 10px;
|
|
397
|
+
transition: border-color 0.12s;
|
|
398
|
+
}
|
|
399
|
+
.file-card:hover { border-color: var(--teal-dim); }
|
|
400
|
+
.fc-icon { font-size: 18px; flex-shrink: 0; }
|
|
401
|
+
.fc-body { flex: 1; min-width: 0; }
|
|
402
|
+
.fc-name {
|
|
403
|
+
font-size: 12px; color: var(--text); font-weight: 500;
|
|
404
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
405
|
+
}
|
|
406
|
+
.fc-meta {
|
|
407
|
+
font-size: 9px; color: var(--dim); margin-top: 2px;
|
|
408
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
409
|
+
}
|
|
410
|
+
.fc-view {
|
|
411
|
+
flex-shrink: 0; background: #1a3a5a; color: #5d9fd9; border: 1px solid #2980b944;
|
|
412
|
+
border-radius: 4px; padding: 4px 10px; font-size: 10px; font-weight: 700; cursor: pointer;
|
|
413
|
+
}
|
|
414
|
+
.fc-view:hover { background: #204a70; }
|
|
415
|
+
.fc-binary { flex-shrink: 0; font-size: 9px; color: var(--dim); }
|
|
416
|
+
|
|
386
417
|
/* ── Activity / event log ───────────────────────────────────────── */
|
|
387
418
|
#activity-pane { padding: 20px; }
|
|
388
419
|
#activity-log { display: flex; flex-direction: column; gap: 0; }
|
|
@@ -819,6 +850,7 @@ html, body {
|
|
|
819
850
|
<button class="tab-btn" onclick="switchTab('roles')">ROLES</button>
|
|
820
851
|
<button class="tab-btn" onclick="switchTab('activity')">ACTIVITY</button>
|
|
821
852
|
<button class="tab-btn" onclick="switchTab('health')">HEALTH</button>
|
|
853
|
+
<button class="tab-btn" onclick="switchTab('files')">FILES</button>
|
|
822
854
|
<button class="tab-btn" onclick="switchTab('chat')">CHAT</button>
|
|
823
855
|
</div>
|
|
824
856
|
|
|
@@ -937,6 +969,14 @@ html, body {
|
|
|
937
969
|
</div>
|
|
938
970
|
</div>
|
|
939
971
|
|
|
972
|
+
<!-- Files tab: every asset any agent in this org has produced, across all runs -->
|
|
973
|
+
<div class="tab-pane" id="tab-files">
|
|
974
|
+
<div id="files-pane">
|
|
975
|
+
<div id="files-empty" class="act-empty" style="display:none">No files produced yet.</div>
|
|
976
|
+
<div id="files-grid" class="files-grid"></div>
|
|
977
|
+
</div>
|
|
978
|
+
</div>
|
|
979
|
+
|
|
940
980
|
<!-- Chat tab -->
|
|
941
981
|
<div class="tab-pane" id="tab-chat">
|
|
942
982
|
<div id="chat-pane">
|
|
@@ -1081,9 +1121,61 @@ window.switchTab = function(tab) {
|
|
|
1081
1121
|
const mainBody = document.getElementById('main-body');
|
|
1082
1122
|
if (mainBody) mainBody.classList.toggle('chat-active', tab === 'chat');
|
|
1083
1123
|
if (tab === 'chat') renderChatTab();
|
|
1124
|
+
else if (tab === 'files') renderFilesTab();
|
|
1084
1125
|
else if (orgDetailData) renderTab(tab);
|
|
1085
1126
|
};
|
|
1086
1127
|
|
|
1128
|
+
// ── Files tab: every asset produced by this org, deduped by path, latest first ──
|
|
1129
|
+
function collectOrgArtifacts() {
|
|
1130
|
+
const bySessions = chatSessions.filter(orgSessionMatch);
|
|
1131
|
+
const seen = new Map(); // path -> { art, ts, from }
|
|
1132
|
+
bySessions.forEach(s => (s.events || []).forEach(ev => {
|
|
1133
|
+
if (ev.type !== 'org:artifact') return;
|
|
1134
|
+
const art = ev.artifact || (ev.path ? { path: ev.path, label: ev.label, mimeType: ev.mimeType } : null);
|
|
1135
|
+
if (!art || !art.path) return;
|
|
1136
|
+
const prev = seen.get(art.path);
|
|
1137
|
+
if (!prev || (ev.ts || 0) >= prev.ts) seen.set(art.path, { art, ts: ev.ts || 0, from: ev.from || '' });
|
|
1138
|
+
}));
|
|
1139
|
+
return Array.from(seen.values()).sort((a, b) => b.ts - a.ts);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function mkFileCard(entry) {
|
|
1143
|
+
const { art, ts, from } = entry;
|
|
1144
|
+
const el = document.createElement('div');
|
|
1145
|
+
el.className = 'file-card';
|
|
1146
|
+
const isText = (art.mimeType || '').startsWith('text/') || (art.mimeType || '') === 'application/json';
|
|
1147
|
+
const labelRaw = art.label || (art.path || 'file').split('/').pop();
|
|
1148
|
+
const timeStr = ts ? new Date(ts).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '';
|
|
1149
|
+
const meta = [from, art.path ? art.path.split('/').slice(-3).join('/') : null, timeStr].filter(Boolean).join(' · ');
|
|
1150
|
+
el.innerHTML = `
|
|
1151
|
+
<div class="fc-icon">📄</div>
|
|
1152
|
+
<div class="fc-body">
|
|
1153
|
+
<div class="fc-name">${esc(labelRaw)}</div>
|
|
1154
|
+
<div class="fc-meta">${esc(meta)}</div>
|
|
1155
|
+
</div>
|
|
1156
|
+
${isText && art.path
|
|
1157
|
+
? `<button class="fc-view" onclick="viewArtifact(${JSON.stringify(art.path)},${JSON.stringify(labelRaw)})">View</button>`
|
|
1158
|
+
: `<span class="fc-binary">Binary</span>`}
|
|
1159
|
+
`;
|
|
1160
|
+
return el;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function paintFilesGrid() {
|
|
1164
|
+
const grid = document.getElementById('files-grid');
|
|
1165
|
+
const empty = document.getElementById('files-empty');
|
|
1166
|
+
if (!grid) return;
|
|
1167
|
+
const files = collectOrgArtifacts();
|
|
1168
|
+
grid.innerHTML = '';
|
|
1169
|
+
if (!files.length) { if (empty) empty.style.display = 'block'; return; }
|
|
1170
|
+
if (empty) empty.style.display = 'none';
|
|
1171
|
+
files.forEach(entry => grid.appendChild(mkFileCard(entry)));
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function renderFilesTab() {
|
|
1175
|
+
paintFilesGrid(); // paint immediately with whatever's cached
|
|
1176
|
+
loadChatSessions().then(() => { if (currentTab === 'files') paintFilesGrid(); });
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1087
1179
|
// ── Render all orgs in sidebar ────────────────────────────────────
|
|
1088
1180
|
function renderSidebar() {
|
|
1089
1181
|
const list = document.getElementById('sb-list');
|
|
@@ -1689,8 +1781,13 @@ function orgSessionMatch(s) {
|
|
|
1689
1781
|
const orgLc = selectedOrg.toLowerCase();
|
|
1690
1782
|
const prompt = (s.prompt || '').toLowerCase();
|
|
1691
1783
|
if (prompt.includes('running org: ' + orgLc) || prompt.includes('org: ' + orgLc)) return true;
|
|
1692
|
-
//
|
|
1693
|
-
|
|
1784
|
+
// Only genuine org-run events (org:comms, org:artifact, org:start, ...) count.
|
|
1785
|
+
// agent:bash / agent:edit / etc. also carry an `org` field (ambient "which
|
|
1786
|
+
// org was active while this Claude Code session ran" tagging) — matching on
|
|
1787
|
+
// those too would pull an unrelated dev session's entire tool-call history
|
|
1788
|
+
// into this org's Chat/Files tabs whenever that org happened to be running
|
|
1789
|
+
// at the same time.
|
|
1790
|
+
return (s.events || []).some(ev => (ev.type || '').startsWith('org:') && (ev.org || '').toLowerCase() === orgLc);
|
|
1694
1791
|
}
|
|
1695
1792
|
|
|
1696
1793
|
function populateChatSelect() {
|
|
@@ -1834,21 +1931,21 @@ function appendChatEvent(ev, animate) {
|
|
|
1834
1931
|
} else if (ev.type === 'agent:spawn') {
|
|
1835
1932
|
const col = dc(ev.domain);
|
|
1836
1933
|
const name = ev.agentType || ev.from || ev.agent || 'agent';
|
|
1837
|
-
el = mkAgent(name, `spawned: ${
|
|
1934
|
+
el = mkAgent(name, `spawned: ${ev.task||'task'}`, ts, col, 'spawn');
|
|
1838
1935
|
} else if (ev.type === 'agent:complete') {
|
|
1839
1936
|
const name = ev.agentType || ev.role || ev.from || 'agent';
|
|
1840
1937
|
const result = (ev.result || '').slice(0, 300);
|
|
1841
1938
|
const cost = ev.cost_usd ? ` · $${parseFloat(ev.cost_usd).toFixed(4)}` : '';
|
|
1842
1939
|
const tools = ev.toolCalls?.length ? ` · ${ev.toolCalls.slice(0,4).join(', ')}` : '';
|
|
1843
|
-
el = mkAgent(name, `${
|
|
1940
|
+
el = mkAgent(name, `${result || 'done'}${tools}${cost}`, ts, dc(ev.domain), 'complete');
|
|
1844
1941
|
} else if (ev.type === 'org:comms') {
|
|
1845
|
-
el = mkIntercom(ev.from || 'boss', ev.to || 'agent',
|
|
1942
|
+
el = mkIntercom(ev.from || 'boss', ev.to || 'agent', ev.msg || ev.message || '', ts);
|
|
1846
1943
|
} else if (ev.type === 'agent:message') {
|
|
1847
|
-
el = mkAgent(ev.agent||'agent',
|
|
1944
|
+
el = mkAgent(ev.agent||'agent', ev.text||'', ts, dc(ev.domain), null);
|
|
1848
1945
|
} else if (ev.type === 'intercom') {
|
|
1849
|
-
el = mkIntercom(ev.from, ev.to,
|
|
1946
|
+
el = mkIntercom(ev.from, ev.to, ev.msg||'', ts);
|
|
1850
1947
|
} else if (ev.type === 'user:message') {
|
|
1851
|
-
el = mkUser(
|
|
1948
|
+
el = mkUser(ev.text||ev.msg||'', ts);
|
|
1852
1949
|
} else if (ev.type === 'loop:start') {
|
|
1853
1950
|
el = mkSys(`🔁 Loop: ${esc(ev.command||'')} (${ev.repeat||'?'} runs)`, ts);
|
|
1854
1951
|
} else if (ev.type === 'loop:complete') {
|
|
@@ -1876,13 +1973,13 @@ function mkAgent(name, text, ts, color, typeTag) {
|
|
|
1876
1973
|
el.className = 'cmsg cagent';
|
|
1877
1974
|
const tagStyle = color ? `style="border-color:${color}44;color:${color}"` : '';
|
|
1878
1975
|
const ttag = typeTag ? `<span class="cevtype">${esc(typeTag)}</span>` : '';
|
|
1879
|
-
el.innerHTML = `<div class="cmsg-meta"><span class="ctag" ${tagStyle}>${esc(name)}</span>${ttag}<span class="cts">${ts}</span></div><div class="cbubble">${text}</div>`;
|
|
1976
|
+
el.innerHTML = `<div class="cmsg-meta"><span class="ctag" ${tagStyle}>${esc(name)}</span>${ttag}<span class="cts">${ts}</span></div><div class="cbubble">${esc(text)}</div>`;
|
|
1880
1977
|
return el;
|
|
1881
1978
|
}
|
|
1882
1979
|
function mkIntercom(from, to, text, ts) {
|
|
1883
1980
|
const el = document.createElement('div');
|
|
1884
1981
|
el.className = 'cmsg cic';
|
|
1885
|
-
el.innerHTML = `<div class="cmsg-meta"><span class="ctag sender">${esc(from||'?')}</span><span class="cic-arrow">→</span><span class="ctag receiver">${esc(to||'?')}</span><span class="cevtype">intercom</span><span class="cts">${ts}</span></div><div class="cbubble">${text}</div>`;
|
|
1982
|
+
el.innerHTML = `<div class="cmsg-meta"><span class="ctag sender">${esc(from||'?')}</span><span class="cic-arrow">→</span><span class="ctag receiver">${esc(to||'?')}</span><span class="cevtype">intercom</span><span class="cts">${ts}</span></div><div class="cbubble">${esc(text)}</div>`;
|
|
1886
1983
|
return el;
|
|
1887
1984
|
}
|
|
1888
1985
|
function mkArtifact(art, ts) {
|
|
@@ -1901,7 +1998,7 @@ function mkArtifact(art, ts) {
|
|
|
1901
1998
|
function mkUser(text, ts) {
|
|
1902
1999
|
const el = document.createElement('div');
|
|
1903
2000
|
el.className = 'cmsg cuser';
|
|
1904
|
-
el.innerHTML = `<div class="cmsg-meta" style="justify-content:flex-end"><span class="cts">${ts}</span><span class="ctag" style="border-color:oklch(68% 0.18 250 / 0.35);color:oklch(68% 0.14 250)">you</span></div><div class="cbubble">${text}</div>`;
|
|
2001
|
+
el.innerHTML = `<div class="cmsg-meta" style="justify-content:flex-end"><span class="cts">${ts}</span><span class="ctag" style="border-color:oklch(68% 0.18 250 / 0.35);color:oklch(68% 0.14 250)">you</span></div><div class="cbubble">${esc(text)}</div>`;
|
|
1905
2002
|
return el;
|
|
1906
2003
|
}
|
|
1907
2004
|
|
|
@@ -2042,6 +2139,8 @@ function handleMmEvent(ev) {
|
|
|
2042
2139
|
appendChatEvent(ev, true);
|
|
2043
2140
|
chatScrollFeed();
|
|
2044
2141
|
}
|
|
2142
|
+
// Live-update the Files tab when a new asset lands while it's open
|
|
2143
|
+
if (ev.type === 'org:artifact' && currentTab === 'files') paintFilesGrid();
|
|
2045
2144
|
}
|
|
2046
2145
|
}
|
|
2047
2146
|
|