@animalabs/connectome-host 0.7.3 → 0.8.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/.env.example +12 -5
- package/.github/PULL_REQUEST_TEMPLATE.md +3 -2
- package/.github/workflows/changelog.yml +9 -4
- package/.github/workflows/ci.yml +5 -3
- package/.github/workflows/publish.yml +12 -6
- package/CHANGELOG.md +401 -10
- package/CONTRIBUTING.md +47 -19
- package/HEADLESS-FLEET-PLAN.md +22 -0
- package/README.md +39 -1
- package/bun.lock +27 -31
- package/changelog.d/README.md +28 -0
- package/docs/AGENT-ONBOARDING.md +1 -1
- package/docs/debug-context-api.md +2 -2
- package/docs/retrieval-traces.md +173 -0
- package/docs/webui-deployment.md +2 -1
- package/package.json +6 -6
- package/recipes/SETUP.md +11 -5
- package/recipes/TRIUMVIRATE-SETUP.md +68 -14
- package/recipes/knowledge-miner.json +0 -30
- package/recipes/mock-test.json +19 -0
- package/recipes/triumvirate.json +6 -1
- package/scripts/audit-module-optins.ts +288 -0
- package/scripts/release-changelog.ts +210 -21
- package/src/cache-keepalive-log.ts +41 -0
- package/src/commands.ts +96 -0
- package/src/framework-strategy.ts +50 -4
- package/src/gate-telemetry.ts +106 -0
- package/src/headless.ts +24 -0
- package/src/index.ts +179 -64
- package/src/mcpl-config.ts +99 -1
- package/src/modules/fleet-module.ts +60 -1
- package/src/modules/fleet-types.ts +30 -1
- package/src/modules/identity-module.ts +310 -2
- package/src/modules/instructions-module.ts +265 -0
- package/src/modules/mcpl-admin-module.ts +89 -13
- package/src/modules/retrieval-module.ts +249 -51
- package/src/modules/retrieval-trace-page.ts +254 -0
- package/src/modules/retrieval-trace.ts +904 -0
- package/src/modules/subagent-module.ts +18 -0
- package/src/modules/tts-relay-module.ts +33 -18
- package/src/modules/web-ui-module.ts +445 -894
- package/src/recipe.ts +787 -29
- package/src/retrieval-config.ts +39 -0
- package/src/strategies/frontdesk-strategy.ts +34 -125
- package/src/tui.ts +325 -54
- package/src/web/panel-data.ts +1206 -0
- package/src/web/protocol.ts +75 -10
- package/src/workspace-mounts.ts +73 -0
- package/test/audit-module-optins.test.ts +174 -0
- package/test/cache-keepalive-log.test.ts +83 -0
- package/test/conversations-recipe.test.ts +142 -0
- package/test/fleet-panel-request.test.ts +90 -0
- package/test/framework-fkm-composition.test.ts +35 -3
- package/test/framework-strategy-defaults.test.ts +41 -0
- package/test/frontdesk-strategy.test.ts +25 -37
- package/test/gate-telemetry-adapter.test.ts +84 -0
- package/test/gate-telemetry.test.ts +91 -0
- package/test/headless-panel-request.test.ts +201 -0
- package/test/identity-and-surfaces.test.ts +212 -1
- package/test/instructions-module.test.ts +258 -0
- package/test/mcpl-admin-module.test.ts +64 -0
- package/test/mcpl-agent-overlay.test.ts +51 -3
- package/test/mcpl-child-env.test.ts +64 -0
- package/test/mock-headless-child.ts +14 -0
- package/test/nudge-command.test.ts +47 -0
- package/test/recipe-cache-keepalive.test.ts +59 -0
- package/test/recipe-compression-fallback.test.ts +19 -0
- package/test/recipe-hybrid-prose-routing.test.ts +12 -0
- package/test/recipe-instructions.test.ts +176 -0
- package/test/recipe-kv-unified.test.ts +87 -0
- package/test/recipe-mcp-source.test.ts +54 -0
- package/test/recipe-openai-compatible.test.ts +54 -0
- package/test/recipe-path-resolution.test.ts +19 -8
- package/test/recipe-provider.test.ts +14 -0
- package/test/recipe-save-unresolved.test.ts +244 -0
- package/test/recipe-source-only.test.ts +38 -0
- package/test/release-changelog.test.ts +202 -0
- package/test/retrieval-auth-loopback.test.ts +49 -0
- package/test/retrieval-config.test.ts +74 -0
- package/test/retrieval-module.test.ts +821 -0
- package/test/subagent-prose-routing.test.ts +109 -0
- package/test/tui-format.test.ts +106 -0
- package/test/web-ui-context-coverage.test.ts +1 -1
- package/test/web-ui-module.test.ts +189 -3
- package/test/web-ui-observers.test.ts +8 -5
- package/test/web-ui-protocol.test.ts +0 -0
- package/test/workspace-mounts.test.ts +68 -0
- package/web/src/App.tsx +160 -44
- package/web/src/Context.tsx +35 -8
- package/web/src/ContextDocument.tsx +20 -5
- package/web/src/Files.tsx +2 -8
- package/web/src/Health.tsx +61 -1
- package/web/src/Lessons.tsx +2 -38
- package/web/src/Mcpl.tsx +80 -14
- package/web/src/Pins.tsx +5 -0
- package/web/src/Settings.tsx +5 -0
- package/web/vite.config.ts +8 -2
package/web/src/Health.tsx
CHANGED
|
@@ -72,6 +72,36 @@ export interface HealthSnapshot {
|
|
|
72
72
|
} | null;
|
|
73
73
|
}>;
|
|
74
74
|
compressionQuarantine?: Record<string, { count?: number; keys?: string[] }>;
|
|
75
|
+
|
|
76
|
+
/** cm's single-authority debt reduction (getCompressionDebt) — the QUEUE
|
|
77
|
+
|
|
78
|
+
* of closed-but-uncompressed chunks. Distinct from contextComposition:
|
|
79
|
+
|
|
80
|
+
* composition says what the last compile RENDERED (summaries L1 = 0 there
|
|
81
|
+
|
|
82
|
+
* means "no L1 tokens in the window", often healthy consolidation);
|
|
83
|
+
|
|
84
|
+
* this block says what the memory organ still OWES. Absent per-agent
|
|
85
|
+
|
|
86
|
+
* entry = the stack predates the reduction — render that as
|
|
87
|
+
|
|
88
|
+
* "not reported", never as zero (misread twice on 2026-08-29). */
|
|
89
|
+
|
|
90
|
+
compressionDebt?: Record<string, {
|
|
91
|
+
|
|
92
|
+
state?: 'healthy' | 'degraded' | 'critical';
|
|
93
|
+
|
|
94
|
+
pendingChunks?: number;
|
|
95
|
+
|
|
96
|
+
oldestPendingAgeMs?: number | null;
|
|
97
|
+
|
|
98
|
+
mergeQueueDepth?: number;
|
|
99
|
+
|
|
100
|
+
mergeQuarantineCount?: number;
|
|
101
|
+
|
|
102
|
+
compressionQuarantineCount?: number;
|
|
103
|
+
|
|
104
|
+
}>;
|
|
75
105
|
runtimeSettings?: Record<string, {
|
|
76
106
|
contextBudgetTokens?: number;
|
|
77
107
|
tailTokens?: number;
|
|
@@ -301,6 +331,7 @@ export function HealthPanel(props: {
|
|
|
301
331
|
}) {
|
|
302
332
|
const agents = () => props.health?.agents ?? [];
|
|
303
333
|
const quarantine = (name: string) => props.health?.compressionQuarantine?.[name];
|
|
334
|
+
const debt = (name: string) => props.health?.compressionDebt?.[name];
|
|
304
335
|
const settings = (name: string) => props.health?.runtimeSettings?.[name];
|
|
305
336
|
const composition = (name: string) => props.health?.contextComposition?.[name];
|
|
306
337
|
|
|
@@ -343,7 +374,10 @@ export function HealthPanel(props: {
|
|
|
343
374
|
<span>up {fmtUptime(props.health!.uptimeSec!)}</span>
|
|
344
375
|
</Show>
|
|
345
376
|
<Show when={props.health!.pendingRequests !== undefined}>
|
|
346
|
-
|
|
377
|
+
{/* the INFERENCE queue (requests waiting to run) — not compression
|
|
378
|
+
debt; that lives per-agent below. A bare "queued" invited the
|
|
379
|
+
misread, hence the explicit label. */}
|
|
380
|
+
<span>{props.health!.pendingRequests} inference queued</span>
|
|
347
381
|
</Show>
|
|
348
382
|
<Show when={props.health!.activeStreams !== undefined}>
|
|
349
383
|
<span>{props.health!.activeStreams!.length} streaming</span>
|
|
@@ -388,6 +422,32 @@ export function HealthPanel(props: {
|
|
|
388
422
|
</div>
|
|
389
423
|
</Show>
|
|
390
424
|
|
|
425
|
+
{/* Compression debt: the organ's QUEUE, not the render composition.
|
|
426
|
+
An old stack that reports nothing must say so — absence
|
|
427
|
+
rendered as zero invites misreads. */}
|
|
428
|
+
<Show when={debt(a.name)} fallback={
|
|
429
|
+
<div class="text-neutral-600">compression debt: not reported by this stack</div>
|
|
430
|
+
}>
|
|
431
|
+
<div class={
|
|
432
|
+
debt(a.name)!.state === 'critical' ? 'text-rose-300'
|
|
433
|
+
: debt(a.name)!.state === 'degraded' ? 'text-amber-300/90'
|
|
434
|
+
: 'text-neutral-500'
|
|
435
|
+
}>
|
|
436
|
+
compression debt: {debt(a.name)!.state ?? '?'}
|
|
437
|
+
{' · '}{debt(a.name)!.pendingChunks ?? 0} chunk(s) pending
|
|
438
|
+
<Show when={(debt(a.name)!.pendingChunks ?? 0) > 0}>
|
|
439
|
+
<span>
|
|
440
|
+
{debt(a.name)!.oldestPendingAgeMs != null
|
|
441
|
+
? ` · oldest ${Math.round(debt(a.name)!.oldestPendingAgeMs! / 60000)}m`
|
|
442
|
+
: ' · age unknown'}
|
|
443
|
+
</span>
|
|
444
|
+
</Show>
|
|
445
|
+
<Show when={(debt(a.name)!.mergeQueueDepth ?? 0) > 0}>
|
|
446
|
+
<span> · merge queue {debt(a.name)!.mergeQueueDepth}</span>
|
|
447
|
+
</Show>
|
|
448
|
+
</div>
|
|
449
|
+
</Show>
|
|
450
|
+
|
|
391
451
|
<Show when={a.refusalStats && (a.refusalStats.total ?? 0) > 0}>
|
|
392
452
|
<div class="text-amber-300/90">
|
|
393
453
|
refusals: {a.refusalStats!.total}
|
package/web/src/Lessons.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lessons panel — read-only view of
|
|
2
|
+
* Lessons panel — read-only view of one process's lesson library (the host
|
|
3
|
+
* or a fleet child; the sidebar's shared scope selector decides which).
|
|
3
4
|
*
|
|
4
5
|
* Shows active lessons sorted by confidence; deprecated entries collapse to a
|
|
5
6
|
* dim line with the reason. Manual refresh button — lessons mutate slowly
|
|
@@ -27,12 +28,6 @@ export function LessonsPanel(props: {
|
|
|
27
28
|
/** True if the LessonsModule is mounted in the recipe. */
|
|
28
29
|
moduleLoaded: boolean;
|
|
29
30
|
lessons: LessonRow[];
|
|
30
|
-
/** Currently-selected scope ('local' for the parent process, or a fleet
|
|
31
|
-
* child name). Drives which process's lessons are shown. */
|
|
32
|
-
scope: string;
|
|
33
|
-
/** Selectable scopes — always includes 'local', plus every fleet child. */
|
|
34
|
-
scopes: Array<{ id: string; label: string }>;
|
|
35
|
-
onScopeChange(scope: string): void;
|
|
36
31
|
onRefresh(): void;
|
|
37
32
|
}) {
|
|
38
33
|
const active = (): LessonRow[] => props.lessons.filter(l => !l.deprecated)
|
|
@@ -60,7 +55,6 @@ export function LessonsPanel(props: {
|
|
|
60
55
|
refresh
|
|
61
56
|
</button>
|
|
62
57
|
</div>
|
|
63
|
-
<ScopePicker scope={props.scope} scopes={props.scopes} onChange={props.onScopeChange} />
|
|
64
58
|
|
|
65
59
|
<Show when={!props.loaded} fallback={null}>
|
|
66
60
|
<div class="text-neutral-600 italic">Loading…</div>
|
|
@@ -95,36 +89,6 @@ export function LessonsPanel(props: {
|
|
|
95
89
|
);
|
|
96
90
|
}
|
|
97
91
|
|
|
98
|
-
/** Compact scope chooser shown at the top of scoped panels. The current
|
|
99
|
-
* scope is rendered as a pill; alternative scopes appear as siblings.
|
|
100
|
-
* Hidden when there's only one scope (no fleet children). */
|
|
101
|
-
export function ScopePicker(props: {
|
|
102
|
-
scope: string;
|
|
103
|
-
scopes: Array<{ id: string; label: string }>;
|
|
104
|
-
onChange(scope: string): void;
|
|
105
|
-
}) {
|
|
106
|
-
return (
|
|
107
|
-
<Show when={props.scopes.length > 1}>
|
|
108
|
-
<div class="flex flex-wrap items-center gap-1 mb-2 text-[10px] font-mono">
|
|
109
|
-
<span class="text-neutral-600 uppercase tracking-wider mr-1">scope</span>
|
|
110
|
-
<For each={props.scopes}>{(s) => (
|
|
111
|
-
<button
|
|
112
|
-
type="button"
|
|
113
|
-
class={`px-1.5 py-0.5 rounded ${
|
|
114
|
-
props.scope === s.id
|
|
115
|
-
? 'bg-cyan-900/60 text-cyan-100'
|
|
116
|
-
: 'bg-neutral-800 hover:bg-neutral-700 text-neutral-300'
|
|
117
|
-
}`}
|
|
118
|
-
onClick={() => props.onChange(s.id)}
|
|
119
|
-
>
|
|
120
|
-
{s.label}
|
|
121
|
-
</button>
|
|
122
|
-
)}</For>
|
|
123
|
-
</div>
|
|
124
|
-
</Show>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
92
|
function LessonCard(props: { lesson: LessonRow }) {
|
|
129
93
|
const conf = (): number => Math.round(props.lesson.confidence * 100);
|
|
130
94
|
const confColor = (): string => {
|
package/web/src/Mcpl.tsx
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCPL
|
|
3
|
-
* `/mcp list|add|remove|env` slash commands but with structured controls.
|
|
2
|
+
* MCPL panel — two views of one scope's MCPL world:
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* live what the scoped process actually LOADED (recipe opt-in +
|
|
5
|
+
* agent overlay), with connection status and tool counts. This
|
|
6
|
+
* is the per-scope truth: a conductor with zero MCPLs and a
|
|
7
|
+
* clerk with five share the same registry file.
|
|
8
|
+
* registry the shared mcpl-servers.json (editable). Mutations are
|
|
9
|
+
* file-only — a restart applies them — and happen on the HOST
|
|
10
|
+
* scope: the file is one cwd-shared registry for the whole
|
|
11
|
+
* fleet, so the panel goes read-only on child scopes rather
|
|
12
|
+
* than pretending a "child-local" edit exists.
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
15
|
import { createSignal, For, Show } from 'solid-js';
|
|
@@ -20,10 +25,25 @@ export interface McplServerRow {
|
|
|
20
25
|
disabledFeatureSets?: string[];
|
|
21
26
|
}
|
|
22
27
|
|
|
28
|
+
/** A server the scoped process has actually loaded, with live status. */
|
|
29
|
+
export interface McplLiveRow {
|
|
30
|
+
id: string;
|
|
31
|
+
connected: boolean;
|
|
32
|
+
toolCount: number;
|
|
33
|
+
toolPrefix?: string;
|
|
34
|
+
target?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
export function McplPanel(props: {
|
|
24
38
|
loaded: boolean;
|
|
25
39
|
configPath: string;
|
|
26
40
|
servers: McplServerRow[];
|
|
41
|
+
/** Servers the scoped process actually loaded, with connection status.
|
|
42
|
+
* Empty array is meaningful ("this process runs no MCPLs"); undefined
|
|
43
|
+
* means an older host that doesn't report it. */
|
|
44
|
+
live?: McplLiveRow[];
|
|
45
|
+
/** True on fleet-child scopes: registry edits stay on the host scope. */
|
|
46
|
+
readOnly?: boolean;
|
|
27
47
|
onRefresh(): void;
|
|
28
48
|
onAdd(input: { id: string; command: string; args?: string[]; env?: Record<string, string>; toolPrefix?: string }): void;
|
|
29
49
|
onRemove(id: string): void;
|
|
@@ -45,13 +65,15 @@ export function McplPanel(props: {
|
|
|
45
65
|
>
|
|
46
66
|
refresh
|
|
47
67
|
</button>
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
<Show when={!props.readOnly}>
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
class="px-2 py-0.5 text-[10px] bg-cyan-900/40 hover:bg-cyan-900/60 text-cyan-200 rounded font-mono"
|
|
72
|
+
onClick={() => setShowAdd(s => !s)}
|
|
73
|
+
>
|
|
74
|
+
{showAdd() ? 'cancel add' : '+ add'}
|
|
75
|
+
</button>
|
|
76
|
+
</Show>
|
|
55
77
|
</div>
|
|
56
78
|
|
|
57
79
|
<Show when={!props.loaded}>
|
|
@@ -59,11 +81,51 @@ export function McplPanel(props: {
|
|
|
59
81
|
</Show>
|
|
60
82
|
|
|
61
83
|
<Show when={props.loaded}>
|
|
84
|
+
{/* ---- live: what this process actually runs ---- */}
|
|
85
|
+
<Show when={props.live !== undefined}>
|
|
86
|
+
<div class="text-neutral-500 uppercase tracking-wider text-[10px] font-semibold mb-1">
|
|
87
|
+
loaded by this process
|
|
88
|
+
</div>
|
|
89
|
+
<Show when={props.live!.length === 0}>
|
|
90
|
+
<div class="text-neutral-600 italic mb-2">
|
|
91
|
+
No MCPL servers loaded — the recipe opts into none.
|
|
92
|
+
</div>
|
|
93
|
+
</Show>
|
|
94
|
+
<div class="space-y-1 mb-3">
|
|
95
|
+
<For each={props.live}>{(s) => (
|
|
96
|
+
<div class="border border-neutral-800 rounded px-2 py-1 bg-neutral-950 flex items-baseline gap-2">
|
|
97
|
+
<span class={`inline-block w-2 h-2 rounded-full self-center shrink-0 ${s.connected ? 'bg-emerald-500' : 'bg-rose-500'}`}
|
|
98
|
+
title={s.connected ? 'connected' : 'disconnected'} />
|
|
99
|
+
<span class="font-mono text-cyan-300 truncate">{s.id}</span>
|
|
100
|
+
<span class="text-[10px] text-neutral-500 shrink-0">{s.toolCount} tools</span>
|
|
101
|
+
<Show when={s.toolPrefix}>
|
|
102
|
+
<span class="text-[10px] text-neutral-600 shrink-0">prefix={s.toolPrefix}</span>
|
|
103
|
+
</Show>
|
|
104
|
+
<Show when={s.target}>
|
|
105
|
+
<span class="text-[10px] font-mono text-neutral-600 truncate ml-auto" title={s.target}>
|
|
106
|
+
{s.target}
|
|
107
|
+
</span>
|
|
108
|
+
</Show>
|
|
109
|
+
</div>
|
|
110
|
+
)}</For>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="text-neutral-500 uppercase tracking-wider text-[10px] font-semibold mb-1">
|
|
113
|
+
shared registry
|
|
114
|
+
</div>
|
|
115
|
+
</Show>
|
|
116
|
+
|
|
62
117
|
<div class="text-[10px] text-neutral-600 italic mb-2 break-all" title={props.configPath}>
|
|
63
118
|
{props.configPath}
|
|
64
119
|
</div>
|
|
65
120
|
|
|
66
|
-
<Show when={
|
|
121
|
+
<Show when={props.readOnly}>
|
|
122
|
+
<div class="text-[10px] text-neutral-500 italic mb-2">
|
|
123
|
+
The registry file is shared fleet-wide — edit it from the host
|
|
124
|
+
scope. Which entries this child loads is its recipe's opt-in.
|
|
125
|
+
</div>
|
|
126
|
+
</Show>
|
|
127
|
+
|
|
128
|
+
<Show when={showAdd() && !props.readOnly}>
|
|
67
129
|
<AddServerForm
|
|
68
130
|
onSubmit={(input) => {
|
|
69
131
|
props.onAdd(input);
|
|
@@ -81,13 +143,14 @@ export function McplPanel(props: {
|
|
|
81
143
|
<For each={props.servers}>{(server) => (
|
|
82
144
|
<ServerCard
|
|
83
145
|
server={server}
|
|
146
|
+
readOnly={props.readOnly ?? false}
|
|
84
147
|
onRemove={() => props.onRemove(server.id)}
|
|
85
148
|
onSetEnv={(env) => props.onSetEnv(server.id, env)}
|
|
86
149
|
/>
|
|
87
150
|
)}</For>
|
|
88
151
|
</div>
|
|
89
152
|
|
|
90
|
-
<Show when={props.servers.length > 0}>
|
|
153
|
+
<Show when={props.servers.length > 0 && !props.readOnly}>
|
|
91
154
|
<div class="mt-3 text-[10px] text-amber-300/80 italic">
|
|
92
155
|
Changes are written to disk; restart the host process to apply.
|
|
93
156
|
</div>
|
|
@@ -99,6 +162,7 @@ export function McplPanel(props: {
|
|
|
99
162
|
|
|
100
163
|
function ServerCard(props: {
|
|
101
164
|
server: McplServerRow;
|
|
165
|
+
readOnly: boolean;
|
|
102
166
|
onRemove(): void;
|
|
103
167
|
onSetEnv(env: Record<string, string>): void;
|
|
104
168
|
}) {
|
|
@@ -117,6 +181,7 @@ function ServerCard(props: {
|
|
|
117
181
|
<Show when={props.server.reconnect}>
|
|
118
182
|
<span class="text-[10px] text-emerald-400">↻ reconnect</span>
|
|
119
183
|
</Show>
|
|
184
|
+
<Show when={!props.readOnly}>
|
|
120
185
|
<button
|
|
121
186
|
type="button"
|
|
122
187
|
class="ml-auto text-[10px] px-1 py-0.5 bg-neutral-800 hover:bg-neutral-700 text-neutral-300 rounded font-mono"
|
|
@@ -150,6 +215,7 @@ function ServerCard(props: {
|
|
|
150
215
|
remove
|
|
151
216
|
</button>
|
|
152
217
|
</Show>
|
|
218
|
+
</Show>
|
|
153
219
|
</div>
|
|
154
220
|
<div class="font-mono text-neutral-300 text-[11px] break-all leading-tight">
|
|
155
221
|
{cmdLine()}
|
package/web/src/Pins.tsx
CHANGED
|
@@ -35,6 +35,11 @@ export interface PinsState {
|
|
|
35
35
|
pinsSupported: boolean;
|
|
36
36
|
levelHonored: boolean;
|
|
37
37
|
deepestLevel?: number;
|
|
38
|
+
/** Picker candidates shipped by a fleet-child scope's snapshot — the
|
|
39
|
+
* client has no window into a child's message store, so the child sends
|
|
40
|
+
* recent real store ids along. Absent on local scope (the client builds
|
|
41
|
+
* its candidate list from its own message window). */
|
|
42
|
+
candidates?: PinCandidate[];
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
package/web/src/Settings.tsx
CHANGED
|
@@ -91,6 +91,10 @@ const TRANSITION_HELP: Record<string, string> = {
|
|
|
91
91
|
export function SettingsPanel(props: {
|
|
92
92
|
loaded: boolean;
|
|
93
93
|
state: SettingsState | null;
|
|
94
|
+
/** Inspection scope ('local' or fleet-child name) — routes the preview
|
|
95
|
+
* fetch through the host's ?scope= proxy so the DRY RUN compiles in the
|
|
96
|
+
* same process whose settings this panel is editing. */
|
|
97
|
+
scope?: string;
|
|
94
98
|
onRefresh(): void;
|
|
95
99
|
onApply(patch: {
|
|
96
100
|
contextBudgetTokens?: number;
|
|
@@ -157,6 +161,7 @@ export function SettingsPanel(props: {
|
|
|
157
161
|
setElapsed(null);
|
|
158
162
|
try {
|
|
159
163
|
const qs = new URLSearchParams({ budget: String(b), agent: props.state!.agent });
|
|
164
|
+
if (props.scope && props.scope !== 'local') qs.set('scope', props.scope);
|
|
160
165
|
const t = num(tail());
|
|
161
166
|
if (t !== undefined) qs.set('tail', String(t));
|
|
162
167
|
if (render) qs.set('render', '1');
|
package/web/vite.config.ts
CHANGED
|
@@ -19,14 +19,20 @@ export default defineConfig({
|
|
|
19
19
|
emptyOutDir: true,
|
|
20
20
|
},
|
|
21
21
|
server: {
|
|
22
|
-
// Dev server proxies WS to the running conhost so
|
|
23
|
-
// talks to a real backend. Port matches
|
|
22
|
+
// Dev server proxies WS + HTTP surfaces to the running conhost so
|
|
23
|
+
// `bun run dev` in web/ talks to a real backend. Port matches the
|
|
24
|
+
// WebUiModule default. Without the HTTP entries every /debug and
|
|
25
|
+
// /healthz fetch 404s against Vite itself in dev.
|
|
24
26
|
proxy: {
|
|
25
27
|
'/ws': {
|
|
26
28
|
target: 'ws://127.0.0.1:7340',
|
|
27
29
|
ws: true,
|
|
28
30
|
changeOrigin: true,
|
|
29
31
|
},
|
|
32
|
+
'/debug': { target: 'http://127.0.0.1:7340', changeOrigin: true },
|
|
33
|
+
'/healthz': { target: 'http://127.0.0.1:7340', changeOrigin: true },
|
|
34
|
+
'/curve': { target: 'http://127.0.0.1:7340', changeOrigin: true },
|
|
35
|
+
'/files': { target: 'http://127.0.0.1:7340', changeOrigin: true },
|
|
30
36
|
},
|
|
31
37
|
},
|
|
32
38
|
});
|