@signal9/era-ui 4.7.2 → 4.9.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/dist/ai/swarm/index.d.ts +2 -1
- package/dist/ai/swarm/index.js +2 -1
- package/dist/ai/swarm/swarm-actions.svelte +86 -0
- package/dist/ai/swarm/swarm-actions.svelte.d.ts +31 -0
- package/dist/ai/swarm/swarm-agent.svelte +9 -43
- package/dist/ai/swarm/swarm-artifact.svelte +2 -6
- package/dist/ai/swarm/swarm-artifacts.svelte +1 -10
- package/dist/ai/swarm/swarm-artifacts.svelte.d.ts +0 -2
- package/dist/ai/swarm/swarm-barriers.svelte +39 -0
- package/dist/ai/swarm/swarm-barriers.svelte.d.ts +13 -0
- package/dist/ai/swarm/swarm-counts.svelte +1 -4
- package/dist/ai/swarm/swarm-counts.svelte.d.ts +0 -2
- package/dist/ai/swarm/swarm-footer.svelte +20 -13
- package/dist/ai/swarm/swarm-header.svelte +3 -5
- package/dist/ai/swarm/swarm-node.svelte +8 -64
- package/dist/ai/swarm/swarm-role.svelte +2 -9
- package/dist/ai/swarm/swarm-sandbox.svelte +45 -0
- package/dist/ai/swarm/swarm-sandbox.svelte.d.ts +10 -0
- package/dist/ai/swarm/swarm-tree.svelte +22 -8
- package/dist/ai/swarm/swarm.d.ts +8 -0
- package/dist/ai/swarm/swarm.js +11 -2
- package/dist/ai/swarm/swarm.svelte.d.ts +0 -1
- package/dist/ai/swarm/swarm.svelte.js +10 -12
- package/dist/apps/llm-shell/llm-shell.svelte +2 -36
- package/dist/apps/llm-shell/llm-swarm.svelte +64 -0
- package/dist/apps/llm-shell/llm-swarm.svelte.d.ts +23 -0
- package/dist/apps/llm-shell/types.d.ts +2 -0
- package/dist/apps/notes/editor/bubble-toolbar.svelte +2 -1
- package/dist/apps/notes/notes.svelte +13 -10
- package/dist/dev/audit/audits/clipped-glyphs.js +33 -11
- package/dist/docs/llms-txt.js +1 -1
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/bar.md +3 -0
- package/dist/generated-docs/input.md +3 -1
- package/dist/generated-docs/llms-full.txt +15 -6
- package/dist/generated-docs/llms.txt +1 -1
- package/dist/generated-docs/manifest.json +5 -5
- package/dist/generated-docs/utilities.json +2 -2
- package/dist/generated-docs/utilities.md +8 -4
- package/dist/styles/density.css +42 -9
- package/dist/styles/index.css +8 -4
- package/dist/ui/bar/bar.svelte +37 -3
- package/dist/ui/bar/bar.svelte.d.ts +61 -1
- package/dist/ui/input/input.svelte +24 -4
- package/dist/ui/input/input.svelte.d.ts +6 -1
- package/dist/ui/input/variants.d.ts +99 -30
- package/dist/ui/input/variants.js +45 -19
- package/package.json +1 -1
package/dist/ai/swarm/index.d.ts
CHANGED
|
@@ -7,10 +7,11 @@ export { default as Grid } from './swarm-grid.svelte';
|
|
|
7
7
|
export { default as Agent } from './swarm-agent.svelte';
|
|
8
8
|
export { default as Artifacts } from './swarm-artifacts.svelte';
|
|
9
9
|
export { default as Artifact } from './swarm-artifact.svelte';
|
|
10
|
+
export { default as Barriers } from './swarm-barriers.svelte';
|
|
10
11
|
export { default as Barrier } from './swarm-barrier.svelte';
|
|
11
12
|
export { default as Footer } from './swarm-footer.svelte';
|
|
12
13
|
export { default as Dot, SWARM_DOT } from './swarm-dot.svelte';
|
|
13
14
|
export { default as Role, SWARM_ROLE } from './swarm-role.svelte';
|
|
14
15
|
export { SwarmController } from './swarm.svelte.js';
|
|
15
|
-
export { buildSwarmTree, flattenSwarmTree, summarizeSwarm, summarizeBarrier, swarmElapsed, formatSwarmAge, formatSwarmDuration, type SwarmAgent, type SwarmAgentStatus, type SwarmArtifact, type SwarmBarrier, type SwarmBarrierStatus, type SwarmConnection, type SwarmCounts, type SwarmEvent, type SwarmNode, type SwarmPlan, type SwarmPlanStatus, type SwarmRole, type SwarmSandbox, type SwarmSnapshot } from './swarm.js';
|
|
16
|
+
export { buildSwarmTree, isSwarmAgentActive, flattenSwarmTree, summarizeSwarm, summarizeBarrier, swarmElapsed, formatSwarmAge, formatSwarmDuration, type SwarmAgent, type SwarmAgentStatus, type SwarmArtifact, type SwarmBarrier, type SwarmBarrierStatus, type SwarmConnection, type SwarmCounts, type SwarmEvent, type SwarmNode, type SwarmPlan, type SwarmPlanStatus, type SwarmRole, type SwarmSandbox, type SwarmSnapshot } from './swarm.js';
|
|
16
17
|
export type { SwarmContext } from './context.js';
|
package/dist/ai/swarm/index.js
CHANGED
|
@@ -7,9 +7,10 @@ export { default as Grid } from './swarm-grid.svelte';
|
|
|
7
7
|
export { default as Agent } from './swarm-agent.svelte';
|
|
8
8
|
export { default as Artifacts } from './swarm-artifacts.svelte';
|
|
9
9
|
export { default as Artifact } from './swarm-artifact.svelte';
|
|
10
|
+
export { default as Barriers } from './swarm-barriers.svelte';
|
|
10
11
|
export { default as Barrier } from './swarm-barrier.svelte';
|
|
11
12
|
export { default as Footer } from './swarm-footer.svelte';
|
|
12
13
|
export { default as Dot, SWARM_DOT } from './swarm-dot.svelte';
|
|
13
14
|
export { default as Role, SWARM_ROLE } from './swarm-role.svelte';
|
|
14
15
|
export { SwarmController } from './swarm.svelte.js';
|
|
15
|
-
export { buildSwarmTree, flattenSwarmTree, summarizeSwarm, summarizeBarrier, swarmElapsed, formatSwarmAge, formatSwarmDuration } from './swarm.js';
|
|
16
|
+
export { buildSwarmTree, isSwarmAgentActive, flattenSwarmTree, summarizeSwarm, summarizeBarrier, swarmElapsed, formatSwarmAge, formatSwarmDuration } from './swarm.js';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { isSwarmAgentActive, type SwarmAgent } from './swarm.js';
|
|
3
|
+
import type { SwarmContext } from './context.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Which affordances an agent actually gets, resolved once.
|
|
7
|
+
*
|
|
8
|
+
* Both the row and the card need the ANSWER before they render — the row pays
|
|
9
|
+
* a different trailing inset when it holds buttons — so the decision is a
|
|
10
|
+
* function they call, not something each of them re-derives. It used to be
|
|
11
|
+
* re-derived, and the two copies had already drifted on which sandbox states
|
|
12
|
+
* counted as cancellable.
|
|
13
|
+
*
|
|
14
|
+
* `null` means "no affordance": no handler, or nothing for it to act on (an
|
|
15
|
+
* agent with no transcript has nowhere to open, a landed agent has nothing
|
|
16
|
+
* left to stop).
|
|
17
|
+
*/
|
|
18
|
+
export function swarmActions(
|
|
19
|
+
agent: SwarmAgent,
|
|
20
|
+
ctx: SwarmContext,
|
|
21
|
+
overrides?: {
|
|
22
|
+
onOpen?: (agent: SwarmAgent) => void;
|
|
23
|
+
onCancel?: (agent: SwarmAgent) => void;
|
|
24
|
+
}
|
|
25
|
+
): { open: ((agent: SwarmAgent) => void) | null; cancel: ((agent: SwarmAgent) => void) | null } {
|
|
26
|
+
const open = overrides?.onOpen ?? ctx.onOpen;
|
|
27
|
+
const cancel = overrides?.onCancel ?? ctx.onCancel;
|
|
28
|
+
return {
|
|
29
|
+
open: open && agent.conversationId != null ? open : null,
|
|
30
|
+
cancel: cancel && isSwarmAgentActive(agent) ? cancel : null
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import X from '@lucide/svelte/icons/x';
|
|
37
|
+
import PanelRightOpen from '@lucide/svelte/icons/panel-right-open';
|
|
38
|
+
import { Button } from '../../ui/button/index.js';
|
|
39
|
+
|
|
40
|
+
let {
|
|
41
|
+
agent,
|
|
42
|
+
open = null,
|
|
43
|
+
cancel = null
|
|
44
|
+
}: {
|
|
45
|
+
agent: SwarmAgent;
|
|
46
|
+
/** Resolved handlers from `swarmActions` — `null` renders no button. */
|
|
47
|
+
open?: ((agent: SwarmAgent) => void) | null;
|
|
48
|
+
cancel?: ((agent: SwarmAgent) => void) | null;
|
|
49
|
+
} = $props();
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<!-- The two things you can do to one agent, wherever it is drawn.
|
|
53
|
+
stopPropagation unconditionally: in the tree these sit inside a treeitem
|
|
54
|
+
whose own click toggles the row, and a button that also collapsed its
|
|
55
|
+
parent would be a trap. On a card there is nothing above to stop, so one
|
|
56
|
+
behaviour covers both.
|
|
57
|
+
The glyphs carry no size class — Button's icon variant sizes them from the
|
|
58
|
+
tier via [&_svg], and that selector outranks a class here (see
|
|
59
|
+
button/variants.ts), so one would be inert. -->
|
|
60
|
+
{#if cancel}
|
|
61
|
+
<Button
|
|
62
|
+
icon
|
|
63
|
+
size="xxs"
|
|
64
|
+
tone="destructive"
|
|
65
|
+
aria-label="Cancel {agent.task}"
|
|
66
|
+
onclick={(event: MouseEvent) => {
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
cancel?.(agent);
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<X />
|
|
72
|
+
</Button>
|
|
73
|
+
{/if}
|
|
74
|
+
{#if open}
|
|
75
|
+
<Button
|
|
76
|
+
icon
|
|
77
|
+
size="xxs"
|
|
78
|
+
aria-label="Open {agent.task}"
|
|
79
|
+
onclick={(event: MouseEvent) => {
|
|
80
|
+
event.stopPropagation();
|
|
81
|
+
open?.(agent);
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
<PanelRightOpen />
|
|
85
|
+
</Button>
|
|
86
|
+
{/if}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type SwarmAgent } from './swarm.js';
|
|
2
|
+
import type { SwarmContext } from './context.js';
|
|
3
|
+
/**
|
|
4
|
+
* Which affordances an agent actually gets, resolved once.
|
|
5
|
+
*
|
|
6
|
+
* Both the row and the card need the ANSWER before they render — the row pays
|
|
7
|
+
* a different trailing inset when it holds buttons — so the decision is a
|
|
8
|
+
* function they call, not something each of them re-derives. It used to be
|
|
9
|
+
* re-derived, and the two copies had already drifted on which sandbox states
|
|
10
|
+
* counted as cancellable.
|
|
11
|
+
*
|
|
12
|
+
* `null` means "no affordance": no handler, or nothing for it to act on (an
|
|
13
|
+
* agent with no transcript has nowhere to open, a landed agent has nothing
|
|
14
|
+
* left to stop).
|
|
15
|
+
*/
|
|
16
|
+
export declare function swarmActions(agent: SwarmAgent, ctx: SwarmContext, overrides?: {
|
|
17
|
+
onOpen?: (agent: SwarmAgent) => void;
|
|
18
|
+
onCancel?: (agent: SwarmAgent) => void;
|
|
19
|
+
}): {
|
|
20
|
+
open: ((agent: SwarmAgent) => void) | null;
|
|
21
|
+
cancel: ((agent: SwarmAgent) => void) | null;
|
|
22
|
+
};
|
|
23
|
+
type $$ComponentProps = {
|
|
24
|
+
agent: SwarmAgent;
|
|
25
|
+
/** Resolved handlers from `swarmActions` — `null` renders no button. */
|
|
26
|
+
open?: ((agent: SwarmAgent) => void) | null;
|
|
27
|
+
cancel?: ((agent: SwarmAgent) => void) | null;
|
|
28
|
+
};
|
|
29
|
+
declare const SwarmActions: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
30
|
+
type SwarmActions = ReturnType<typeof SwarmActions>;
|
|
31
|
+
export default SwarmActions;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
-
import Box from '@lucide/svelte/icons/box';
|
|
5
|
-
import X from '@lucide/svelte/icons/x';
|
|
6
|
-
import PanelRightOpen from '@lucide/svelte/icons/panel-right-open';
|
|
7
|
-
import { Button } from '../../ui/button/index.js';
|
|
8
4
|
import { Card } from '../../ui/card/index.js';
|
|
9
5
|
import { cn } from '../../utils/index.js';
|
|
10
6
|
import { getSwarmContext } from './context.js';
|
|
11
7
|
import Dot from './swarm-dot.svelte';
|
|
12
8
|
import Role from './swarm-role.svelte';
|
|
9
|
+
import Sandbox from './swarm-sandbox.svelte';
|
|
10
|
+
import Actions, { swarmActions } from './swarm-actions.svelte';
|
|
13
11
|
import { formatSwarmDuration, swarmElapsed, type SwarmAgent } from './swarm.js';
|
|
14
12
|
|
|
15
13
|
let {
|
|
@@ -32,21 +30,17 @@
|
|
|
32
30
|
} = $props();
|
|
33
31
|
|
|
34
32
|
const ctx = getSwarmContext();
|
|
35
|
-
const
|
|
36
|
-
const cancelAgent = $derived(onCancel ?? ctx.onCancel);
|
|
37
|
-
const cancellable = $derived(
|
|
38
|
-
cancelAgent != null &&
|
|
39
|
-
(agent.status === 'running' || agent.status === 'queued' || agent.status === 'blocked')
|
|
40
|
-
);
|
|
41
|
-
// See `Swarm.Node`: the open affordance is gated on the agent actually having
|
|
42
|
-
// a transcript to open.
|
|
43
|
-
const openable = $derived(openAgent != null && agent.conversationId != null);
|
|
33
|
+
const actions = $derived(swarmActions(agent, ctx, { onOpen, onCancel }));
|
|
44
34
|
const elapsed = $derived(swarmElapsed(agent, ctx.now));
|
|
45
35
|
</script>
|
|
46
36
|
|
|
47
37
|
<!-- One agent as a card, for the console grid — the same agent the tree renders
|
|
48
38
|
as a row, sized for scanning forty of them at once.
|
|
49
39
|
|
|
40
|
+
The row and the card are two drawings of one agent, so everything that
|
|
41
|
+
decides WHAT to draw — which affordances it gets, how its VM reads — is
|
|
42
|
+
shared with the row rather than restated here.
|
|
43
|
+
|
|
50
44
|
A well, not an elevated card: the grid sits INSIDE the swarm panel, and on
|
|
51
45
|
the flat surface `elevated` resolves to the same fill the panel already has
|
|
52
46
|
— forty invisible boxes. The recessed tier is the one that reads as content
|
|
@@ -77,22 +71,7 @@
|
|
|
77
71
|
{#if depth != null}
|
|
78
72
|
<span class="shrink-0 font-mono text-[0.625rem] text-muted tabular-nums">d{depth}</span>
|
|
79
73
|
{/if}
|
|
80
|
-
{
|
|
81
|
-
<Button
|
|
82
|
-
icon
|
|
83
|
-
size="xxs"
|
|
84
|
-
tone="destructive"
|
|
85
|
-
aria-label="Cancel {agent.task}"
|
|
86
|
-
onclick={() => cancelAgent?.(agent)}
|
|
87
|
-
>
|
|
88
|
-
<X class="size-(--era-h-xs)" />
|
|
89
|
-
</Button>
|
|
90
|
-
{/if}
|
|
91
|
-
{#if openable}
|
|
92
|
-
<Button icon size="xxs" aria-label="Open {agent.task}" onclick={() => openAgent?.(agent)}>
|
|
93
|
-
<PanelRightOpen class="size-(--era-h-xs)" />
|
|
94
|
-
</Button>
|
|
95
|
-
{/if}
|
|
74
|
+
<Actions {agent} open={actions.open} cancel={actions.cancel} />
|
|
96
75
|
</div>
|
|
97
76
|
|
|
98
77
|
<!-- Two lines of task, one of result: enough to tell two workers apart,
|
|
@@ -108,20 +87,7 @@
|
|
|
108
87
|
{#if agent.sandbox || elapsed != null}
|
|
109
88
|
<div class="flex min-w-0 items-center gap-(--era-gap) text-body era-text-trim text-muted">
|
|
110
89
|
{#if agent.sandbox}
|
|
111
|
-
<
|
|
112
|
-
class="flex min-w-0 items-center gap-(--era-gap) font-mono"
|
|
113
|
-
title="sandbox {agent.sandbox.id ?? ''}"
|
|
114
|
-
>
|
|
115
|
-
<Box
|
|
116
|
-
class={cn(
|
|
117
|
-
'size-(--era-h-xs) shrink-0',
|
|
118
|
-
agent.sandbox.status === 'running' && 'text-success',
|
|
119
|
-
agent.sandbox.status === 'paused' && 'text-warning'
|
|
120
|
-
)}
|
|
121
|
-
aria-hidden="true"
|
|
122
|
-
/>
|
|
123
|
-
<span class="truncate">{agent.sandbox.status}</span>
|
|
124
|
-
</span>
|
|
90
|
+
<Sandbox sandbox={agent.sandbox} label />
|
|
125
91
|
{/if}
|
|
126
92
|
<span class="flex-1"></span>
|
|
127
93
|
{#if elapsed != null}
|
|
@@ -29,14 +29,10 @@
|
|
|
29
29
|
{...restProps}
|
|
30
30
|
>
|
|
31
31
|
<div class="flex min-w-0 items-center gap-(--era-gap) text-body era-text-trim">
|
|
32
|
-
<span class="shrink-0 font-mono text-[0.625rem] tracking-[0.
|
|
32
|
+
<span class="shrink-0 font-mono text-[0.625rem] tracking-[0.15em] text-muted uppercase">
|
|
33
33
|
{artifact.kind}
|
|
34
34
|
</span>
|
|
35
|
-
{
|
|
36
|
-
<span class="min-w-0 flex-1 truncate text-bright">{artifact.title}</span>
|
|
37
|
-
{:else}
|
|
38
|
-
<span class="flex-1"></span>
|
|
39
|
-
{/if}
|
|
35
|
+
<span class="min-w-0 flex-1 truncate text-bright">{artifact.title ?? ''}</span>
|
|
40
36
|
{#if age}
|
|
41
37
|
<span class="shrink-0 font-mono text-body text-muted tabular-nums">{age}</span>
|
|
42
38
|
{/if}
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
let {
|
|
10
10
|
artifacts = [],
|
|
11
11
|
label = 'artifacts',
|
|
12
|
-
limit,
|
|
13
12
|
children,
|
|
14
13
|
class: className,
|
|
15
14
|
...restProps
|
|
@@ -17,14 +16,9 @@
|
|
|
17
16
|
artifacts?: SwarmArtifact[];
|
|
18
17
|
/** Section heading. Set to `undefined` for a bare list. */
|
|
19
18
|
label?: string;
|
|
20
|
-
/** Show at most this many, newest first by position. Omit for all of them. */
|
|
21
|
-
limit?: number;
|
|
22
19
|
/** Replaces the default `Swarm.Artifact` rendering for each item. */
|
|
23
20
|
children?: Snippet<[SwarmArtifact]>;
|
|
24
21
|
} = $props();
|
|
25
|
-
|
|
26
|
-
const shown = $derived(limit == null ? artifacts : artifacts.slice(0, limit));
|
|
27
|
-
const hidden = $derived(artifacts.length - shown.length);
|
|
28
22
|
</script>
|
|
29
23
|
|
|
30
24
|
<!-- Renders nothing at all when there is nothing to show: an empty "ARTIFACTS"
|
|
@@ -32,11 +26,8 @@
|
|
|
32
26
|
that are mostly empty until a branch lands. -->
|
|
33
27
|
{#if artifacts.length > 0}
|
|
34
28
|
<Step.Section {label} class={cn('min-w-0', className)} {...restProps}>
|
|
35
|
-
{#each
|
|
29
|
+
{#each artifacts as artifact (artifact.id)}
|
|
36
30
|
{#if children}{@render children(artifact)}{:else}<Artifact {artifact} />{/if}
|
|
37
31
|
{/each}
|
|
38
|
-
{#if hidden > 0}
|
|
39
|
-
<span class="text-body text-muted">+{hidden} more</span>
|
|
40
|
-
{/if}
|
|
41
32
|
</Step.Section>
|
|
42
33
|
{/if}
|
|
@@ -5,8 +5,6 @@ type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
|
5
5
|
artifacts?: SwarmArtifact[];
|
|
6
6
|
/** Section heading. Set to `undefined` for a bare list. */
|
|
7
7
|
label?: string;
|
|
8
|
-
/** Show at most this many, newest first by position. Omit for all of them. */
|
|
9
|
-
limit?: number;
|
|
10
8
|
/** Replaces the default `Swarm.Artifact` rendering for each item. */
|
|
11
9
|
children?: Snippet<[SwarmArtifact]>;
|
|
12
10
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn } from '../../utils/index.js';
|
|
5
|
+
import Barrier from './swarm-barrier.svelte';
|
|
6
|
+
import type { SwarmAgent, SwarmBarrier } from './swarm.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
barriers = [],
|
|
10
|
+
agents = [],
|
|
11
|
+
children,
|
|
12
|
+
class: className,
|
|
13
|
+
...restProps
|
|
14
|
+
}: Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
15
|
+
barriers?: SwarmBarrier[];
|
|
16
|
+
/** The swarm's agents — each barrier's progress is derived from its members. */
|
|
17
|
+
agents?: SwarmAgent[];
|
|
18
|
+
/** Replaces the default `Swarm.Barrier` rendering for each gate. */
|
|
19
|
+
children?: Snippet<[SwarmBarrier]>;
|
|
20
|
+
} = $props();
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<!-- The plural container, so a host does not have to know that the fan-in
|
|
24
|
+
section is divided off from the tree above it (Artifacts already had one;
|
|
25
|
+
the asymmetry meant every host re-invented this chrome). Renders nothing
|
|
26
|
+
when a swarm declares no gates, which most do not. -->
|
|
27
|
+
{#if barriers.length > 0}
|
|
28
|
+
<div
|
|
29
|
+
class={cn(
|
|
30
|
+
'flex min-w-0 flex-col gap-(--era-pad-sm) border-t border-divider-faded p-(--era-pad-sm)',
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
{...restProps}
|
|
34
|
+
>
|
|
35
|
+
{#each barriers as barrier (barrier.id)}
|
|
36
|
+
{#if children}{@render children(barrier)}{:else}<Barrier {barrier} {agents} />{/if}
|
|
37
|
+
{/each}
|
|
38
|
+
</div>
|
|
39
|
+
{/if}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { SwarmAgent, SwarmBarrier } from './swarm.js';
|
|
4
|
+
type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
5
|
+
barriers?: SwarmBarrier[];
|
|
6
|
+
/** The swarm's agents — each barrier's progress is derived from its members. */
|
|
7
|
+
agents?: SwarmAgent[];
|
|
8
|
+
/** Replaces the default `Swarm.Barrier` rendering for each gate. */
|
|
9
|
+
children?: Snippet<[SwarmBarrier]>;
|
|
10
|
+
};
|
|
11
|
+
declare const SwarmBarriers: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type SwarmBarriers = ReturnType<typeof SwarmBarriers>;
|
|
13
|
+
export default SwarmBarriers;
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
let {
|
|
9
9
|
agents = [],
|
|
10
10
|
counts,
|
|
11
|
-
sandboxes = true,
|
|
12
11
|
class: className,
|
|
13
12
|
...restProps
|
|
14
13
|
}: HTMLAttributes<HTMLDivElement> & {
|
|
@@ -16,8 +15,6 @@
|
|
|
16
15
|
agents?: SwarmAgent[];
|
|
17
16
|
/** Pre-computed counts (a controller's `counts`), used instead of `agents`. */
|
|
18
17
|
counts?: SwarmCounts;
|
|
19
|
-
/** Show the live-VM tally. Off for swarms that do not run sandboxes. */
|
|
20
|
-
sandboxes?: boolean;
|
|
21
18
|
} = $props();
|
|
22
19
|
|
|
23
20
|
const tally = $derived(counts ?? summarizeSwarm(agents));
|
|
@@ -43,7 +40,7 @@
|
|
|
43
40
|
{#if tally.done > 0}<Badge tone="success">{tally.done} done</Badge>{/if}
|
|
44
41
|
{#if tally.failed > 0}<Badge tone="destructive">{tally.failed} failed</Badge>{/if}
|
|
45
42
|
{/if}
|
|
46
|
-
{#if
|
|
43
|
+
{#if tally.sandboxes > 0}
|
|
47
44
|
<Badge>
|
|
48
45
|
<Box class="size-(--era-h-xs)" aria-hidden="true" />
|
|
49
46
|
{tally.sandboxes}
|
|
@@ -5,8 +5,6 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
5
5
|
agents?: SwarmAgent[];
|
|
6
6
|
/** Pre-computed counts (a controller's `counts`), used instead of `agents`. */
|
|
7
7
|
counts?: SwarmCounts;
|
|
8
|
-
/** Show the live-VM tally. Off for swarms that do not run sandboxes. */
|
|
9
|
-
sandboxes?: boolean;
|
|
10
8
|
};
|
|
11
9
|
declare const SwarmCounts: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
10
|
type SwarmCounts = ReturnType<typeof SwarmCounts>;
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
import type { SwarmConnection } from './swarm.js';
|
|
2
|
+
import type { SwarmAgentStatus, SwarmConnection } from './swarm.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Stream health, which is NOT swarm health — the two are shown apart on
|
|
6
6
|
* purpose (the header has the plan's dot, this has the socket's). A swarm can
|
|
7
7
|
* be running perfectly behind a dead connection, and a console that paints
|
|
8
8
|
* one dot for both tells you the run stalled when only your view did.
|
|
9
|
+
*
|
|
10
|
+
* Borrows the agent dot's vocabulary the same way the header does, so one tone
|
|
11
|
+
* system covers every state in the family and the dot itself is drawn once.
|
|
9
12
|
*/
|
|
10
|
-
const CONNECTION: Record<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
const CONNECTION: Record<
|
|
14
|
+
SwarmConnection,
|
|
15
|
+
{ status: SwarmAgentStatus; class: string; label: string; pulse?: boolean }
|
|
16
|
+
> = {
|
|
17
|
+
idle: { status: 'queued', class: 'text-muted', label: 'idle' },
|
|
18
|
+
loading: { status: 'queued', class: 'text-muted', label: 'loading', pulse: true },
|
|
19
|
+
live: { status: 'done', class: 'text-success', label: 'live' },
|
|
20
|
+
reconnecting: { status: 'blocked', class: 'text-warning', label: 'reconnecting', pulse: true },
|
|
21
|
+
error: { status: 'failed', class: 'text-destructive', label: 'disconnected' }
|
|
16
22
|
};
|
|
17
23
|
</script>
|
|
18
24
|
|
|
19
25
|
<script lang="ts">
|
|
20
26
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
21
27
|
import { cn } from '../../utils/index.js';
|
|
28
|
+
import Dot from './swarm-dot.svelte';
|
|
22
29
|
|
|
23
30
|
let {
|
|
24
31
|
connection = 'idle',
|
|
@@ -49,14 +56,14 @@
|
|
|
49
56
|
{...restProps}
|
|
50
57
|
>
|
|
51
58
|
<span class={cn('flex shrink-0 items-center gap-(--era-gap)', tone.class)}>
|
|
52
|
-
<
|
|
59
|
+
<Dot status={tone.status} label={tone.label} class={tone.pulse ? 'animate-pulse' : undefined} />
|
|
53
60
|
{tone.label}
|
|
54
61
|
</span>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
<!-- Always rendered, empty when there is nothing to say: the span is also the
|
|
63
|
+
spacer that pushes the watermark to the trailing edge. -->
|
|
64
|
+
<span class="min-w-0 flex-1 truncate text-destructive" title={error ?? undefined}>
|
|
65
|
+
{error ?? ''}
|
|
66
|
+
</span>
|
|
60
67
|
{#if seq != null && seq >= 0}
|
|
61
68
|
<span class="shrink-0 text-muted tabular-nums">seq {seq}</span>
|
|
62
69
|
{/if}
|
|
@@ -51,11 +51,9 @@
|
|
|
51
51
|
{#if plan}
|
|
52
52
|
<Dot status={plan.status} label={plan.label} />
|
|
53
53
|
{/if}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
<span class="flex-1"></span>
|
|
58
|
-
{/if}
|
|
54
|
+
<!-- Always rendered: it is the title AND the spacer that pushes the trailing
|
|
55
|
+
group to the edge, so there is no empty-title branch to keep in sync. -->
|
|
56
|
+
<span class="min-w-0 flex-1 truncate text-bright">{title ?? ''}</span>
|
|
59
57
|
{#if children}
|
|
60
58
|
<div class="flex shrink-0 items-center gap-(--era-gap)">
|
|
61
59
|
{@render children()}
|
|
@@ -1,24 +1,13 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
/** How a sandbox's health tints the VM glyph on a row. */
|
|
3
|
-
const SANDBOX_TONE: Record<string, string> = {
|
|
4
|
-
running: 'text-success',
|
|
5
|
-
paused: 'text-warning',
|
|
6
|
-
terminated: 'text-muted'
|
|
7
|
-
};
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
1
|
<script lang="ts">
|
|
11
2
|
import type { Snippet } from 'svelte';
|
|
12
3
|
import { Collapsible } from 'bits-ui';
|
|
13
4
|
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
|
14
|
-
import Box from '@lucide/svelte/icons/box';
|
|
15
|
-
import X from '@lucide/svelte/icons/x';
|
|
16
|
-
import PanelRightOpen from '@lucide/svelte/icons/panel-right-open';
|
|
17
|
-
import { Button } from '../../ui/button/index.js';
|
|
18
5
|
import { cn } from '../../utils/index.js';
|
|
19
6
|
import { getSwarmContext } from './context.js';
|
|
20
7
|
import Dot from './swarm-dot.svelte';
|
|
21
8
|
import Role from './swarm-role.svelte';
|
|
9
|
+
import Sandbox from './swarm-sandbox.svelte';
|
|
10
|
+
import Actions, { swarmActions } from './swarm-actions.svelte';
|
|
22
11
|
import { formatSwarmDuration, swarmElapsed, type SwarmAgent } from './swarm.js';
|
|
23
12
|
|
|
24
13
|
let {
|
|
@@ -63,24 +52,11 @@
|
|
|
63
52
|
} = $props();
|
|
64
53
|
|
|
65
54
|
const ctx = getSwarmContext();
|
|
66
|
-
const
|
|
67
|
-
const
|
|
55
|
+
const actions = $derived(swarmActions(agent, ctx, { onOpen, onCancel }));
|
|
56
|
+
const hasActions = $derived(actions.open != null || actions.cancel != null);
|
|
68
57
|
|
|
69
58
|
const canExpand = $derived(expandable ?? (children != null || detail != null));
|
|
70
59
|
const running = $derived(agent.status === 'running');
|
|
71
|
-
// Cancel is offered for work that has not landed — a done or failed agent has
|
|
72
|
-
// nothing left to stop, and showing a dead button on every finished row is
|
|
73
|
-
// how a long run turns into a wall of noise.
|
|
74
|
-
const cancellable = $derived(
|
|
75
|
-
cancelAgent != null &&
|
|
76
|
-
(agent.status === 'running' || agent.status === 'queued' || agent.status === 'blocked')
|
|
77
|
-
);
|
|
78
|
-
// No transcript, no button: `conversationId` is the host's way of saying this
|
|
79
|
-
// agent has something to open, and a row full of buttons that open nothing is
|
|
80
|
-
// worse than a row without them.
|
|
81
|
-
const openable = $derived(openAgent != null && agent.conversationId != null);
|
|
82
|
-
const hasActions = $derived(cancellable || openable);
|
|
83
|
-
|
|
84
60
|
const elapsed = $derived(swarmElapsed(agent, ctx.now));
|
|
85
61
|
|
|
86
62
|
function toggle() {
|
|
@@ -99,7 +75,7 @@
|
|
|
99
75
|
if (event.target !== event.currentTarget) return;
|
|
100
76
|
if (event.key === 'Enter') {
|
|
101
77
|
event.preventDefault();
|
|
102
|
-
if (
|
|
78
|
+
if (actions.open) actions.open(agent);
|
|
103
79
|
else toggle();
|
|
104
80
|
} else if (event.key === ' ') {
|
|
105
81
|
event.preventDefault();
|
|
@@ -137,7 +113,7 @@
|
|
|
137
113
|
aria-expanded={canExpand ? open : undefined}
|
|
138
114
|
data-agent-id={agent.id}
|
|
139
115
|
data-active={active || undefined}
|
|
140
|
-
tabindex={-1}
|
|
116
|
+
tabindex={active ? 0 : -1}
|
|
141
117
|
{onkeydown}
|
|
142
118
|
{onclick}
|
|
143
119
|
class={cn('group/node min-w-0 outline-none', className)}
|
|
@@ -188,12 +164,7 @@
|
|
|
188
164
|
{@render meta?.(agent)}
|
|
189
165
|
|
|
190
166
|
{#if agent.sandbox}
|
|
191
|
-
<
|
|
192
|
-
class={cn('flex shrink-0 items-center', SANDBOX_TONE[agent.sandbox.status] ?? 'text-muted')}
|
|
193
|
-
title="sandbox {agent.sandbox.id ?? ''} ({agent.sandbox.status})"
|
|
194
|
-
>
|
|
195
|
-
<Box class="size-(--era-h-xs)" aria-hidden="true" />
|
|
196
|
-
</span>
|
|
167
|
+
<Sandbox sandbox={agent.sandbox} />
|
|
197
168
|
{/if}
|
|
198
169
|
|
|
199
170
|
{#if elapsed != null}
|
|
@@ -202,34 +173,7 @@
|
|
|
202
173
|
</span>
|
|
203
174
|
{/if}
|
|
204
175
|
|
|
205
|
-
{
|
|
206
|
-
<Button
|
|
207
|
-
icon
|
|
208
|
-
size="xxs"
|
|
209
|
-
tone="destructive"
|
|
210
|
-
aria-label="Cancel {agent.task}"
|
|
211
|
-
onclick={(event: MouseEvent) => {
|
|
212
|
-
event.stopPropagation();
|
|
213
|
-
cancelAgent?.(agent);
|
|
214
|
-
}}
|
|
215
|
-
>
|
|
216
|
-
<X class="size-(--era-h-xs)" />
|
|
217
|
-
</Button>
|
|
218
|
-
{/if}
|
|
219
|
-
|
|
220
|
-
{#if openable}
|
|
221
|
-
<Button
|
|
222
|
-
icon
|
|
223
|
-
size="xxs"
|
|
224
|
-
aria-label="Open {agent.task}"
|
|
225
|
-
onclick={(event: MouseEvent) => {
|
|
226
|
-
event.stopPropagation();
|
|
227
|
-
openAgent?.(agent);
|
|
228
|
-
}}
|
|
229
|
-
>
|
|
230
|
-
<PanelRightOpen class="size-(--era-h-xs)" />
|
|
231
|
-
</Button>
|
|
232
|
-
{/if}
|
|
176
|
+
<Actions {agent} open={actions.open} cancel={actions.cancel} />
|
|
233
177
|
</div>
|
|
234
178
|
|
|
235
179
|
{#if canExpand}
|
|
@@ -31,15 +31,12 @@
|
|
|
31
31
|
let {
|
|
32
32
|
role = 'worker',
|
|
33
33
|
label,
|
|
34
|
-
short = false,
|
|
35
34
|
class: className,
|
|
36
35
|
...restProps
|
|
37
36
|
}: HTMLAttributes<HTMLSpanElement> & {
|
|
38
37
|
role?: SwarmRole;
|
|
39
38
|
/** Specialization shown after the role ("api", "docs") — the agent's `label`. */
|
|
40
39
|
label?: string;
|
|
41
|
-
/** Glyph only, with the role kept as the hover/announced name. For dense rows. */
|
|
42
|
-
short?: boolean;
|
|
43
40
|
} = $props();
|
|
44
41
|
|
|
45
42
|
const spec = $derived(SWARM_ROLE[role]);
|
|
@@ -56,16 +53,12 @@
|
|
|
56
53
|
data-role={role}
|
|
57
54
|
title={name}
|
|
58
55
|
class={cn(
|
|
59
|
-
'inline-flex shrink-0 items-center gap-(--era-gap) font-mono text-[0.625rem] era-text-trim-caps tracking-[0.
|
|
56
|
+
'inline-flex shrink-0 items-center gap-(--era-gap) font-mono text-[0.625rem] era-text-trim-caps tracking-[0.15em] uppercase',
|
|
60
57
|
role === 'orchestrator' ? 'text-fg' : 'text-muted',
|
|
61
58
|
className
|
|
62
59
|
)}
|
|
63
60
|
{...restProps}
|
|
64
61
|
>
|
|
65
62
|
<Icon class="size-(--era-h-xs) shrink-0" aria-hidden="true" />
|
|
66
|
-
{
|
|
67
|
-
<span class="sr-only">{name}</span>
|
|
68
|
-
{:else}
|
|
69
|
-
{name}
|
|
70
|
-
{/if}
|
|
63
|
+
{name}
|
|
71
64
|
</span>
|