shariq-pi-extensions 0.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/LICENSE +21 -0
- package/README.md +94 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/docs/ARCHITECTURE.md +54 -0
- package/docs/DEVELOPMENT.md +88 -0
- package/docs/EXTENSIONS.md +89 -0
- package/extensions/antigravity-provider/LICENSE +21 -0
- package/extensions/antigravity-provider/README.md +23 -0
- package/extensions/antigravity-provider/antigravity/cloud-code-assist.ts +519 -0
- package/extensions/antigravity-provider/antigravity/index.ts +67 -0
- package/extensions/antigravity-provider/antigravity/models.ts +184 -0
- package/extensions/antigravity-provider/antigravity/oauth.ts +499 -0
- package/extensions/antigravity-provider/index.ts +3 -0
- package/extensions/ask-user/README.md +21 -0
- package/extensions/ask-user/index.ts +417 -0
- package/extensions/background-terminals/README.md +43 -0
- package/extensions/background-terminals/index.ts +400 -0
- package/extensions/background-terminals/src/manager.ts +480 -0
- package/extensions/background-terminals/src/output-buffer.ts +110 -0
- package/extensions/background-terminals/src/presentation.ts +74 -0
- package/extensions/background-terminals/src/types.ts +47 -0
- package/extensions/background-terminals/src/ui.ts +478 -0
- package/extensions/context-usage/LICENSE +21 -0
- package/extensions/context-usage/README.md +20 -0
- package/extensions/context-usage/index.ts +2006 -0
- package/extensions/context-usage/lib/ansi.ts +60 -0
- package/extensions/context-usage/lib/boundary.ts +27 -0
- package/extensions/context-usage/lib/chat.ts +190 -0
- package/extensions/context-usage/lib/config.ts +30 -0
- package/extensions/context-usage/lib/fmt.ts +59 -0
- package/extensions/context-usage/lib/style.ts +260 -0
- package/extensions/copy-all/README.md +15 -0
- package/extensions/copy-all/index.ts +72 -0
- package/extensions/factory-provider/API_KEYS.md +17 -0
- package/extensions/factory-provider/AUTH.md +15 -0
- package/extensions/factory-provider/README.md +90 -0
- package/extensions/factory-provider/REFRESH_MODELS.md +185 -0
- package/extensions/factory-provider/UPDATING.md +97 -0
- package/extensions/factory-provider/factory/api-keys.ts +407 -0
- package/extensions/factory-provider/factory/auth.ts +132 -0
- package/extensions/factory-provider/factory/constants.ts +15 -0
- package/extensions/factory-provider/factory/dashboard.ts +363 -0
- package/extensions/factory-provider/factory/droid.ts +84 -0
- package/extensions/factory-provider/factory/limits.ts +342 -0
- package/extensions/factory-provider/factory/models.ts +283 -0
- package/extensions/factory-provider/factory/responses.ts +331 -0
- package/extensions/factory-provider/factory/warm-theme.ts +53 -0
- package/extensions/factory-provider/factory/websocket.ts +130 -0
- package/extensions/factory-provider/index.ts +204 -0
- package/extensions/factory-provider/scripts/audit-reasoning-efforts.py +84 -0
- package/extensions/firecrawl-web/README.md +31 -0
- package/extensions/firecrawl-web/auth.ts +92 -0
- package/extensions/firecrawl-web/client.ts +238 -0
- package/extensions/firecrawl-web/index.ts +96 -0
- package/extensions/firecrawl-web/output.ts +125 -0
- package/extensions/git-info/README.md +25 -0
- package/extensions/git-info/index.ts +162 -0
- package/extensions/git-info/src/git.ts +206 -0
- package/extensions/git-info/src/ui.ts +291 -0
- package/extensions/goal/README.md +44 -0
- package/extensions/goal/constants.ts +4 -0
- package/extensions/goal/extension.ts +1064 -0
- package/extensions/goal/index.ts +2 -0
- package/extensions/goal/types.ts +56 -0
- package/extensions/goal/ui.ts +334 -0
- package/extensions/orchestration/README.md +58 -0
- package/extensions/orchestration/dashboard.ts +192 -0
- package/extensions/orchestration/engine.ts +706 -0
- package/extensions/orchestration/index.ts +243 -0
- package/extensions/orchestration/prompts.ts +124 -0
- package/extensions/orchestration/protocol.ts +116 -0
- package/extensions/orchestration/settings-ui.ts +41 -0
- package/extensions/orchestration/settings.ts +59 -0
- package/extensions/orchestration/storage.ts +49 -0
- package/extensions/orchestration/types.ts +105 -0
- package/extensions/performance-status/README.md +5 -0
- package/extensions/performance-status/index.ts +171 -0
- package/extensions/pi-memory/README.md +53 -0
- package/extensions/pi-memory/index.ts +326 -0
- package/extensions/pi-memory/src/config.ts +75 -0
- package/extensions/pi-memory/src/database.ts +617 -0
- package/extensions/pi-memory/src/extraction.ts +178 -0
- package/extensions/pi-memory/src/project.ts +80 -0
- package/extensions/pi-memory/src/projection.ts +59 -0
- package/extensions/pi-memory/src/redaction.ts +60 -0
- package/extensions/pi-memory/src/retrieval.ts +30 -0
- package/extensions/pi-memory/src/service.ts +194 -0
- package/extensions/pi-memory/src/session.ts +146 -0
- package/extensions/pi-memory/src/types.ts +82 -0
- package/extensions/shared/README.md +18 -0
- package/extensions/shared/activity-status.ts +31 -0
- package/extensions/shared/child-session.ts +150 -0
- package/extensions/shared/context-utilization.ts +47 -0
- package/extensions/shared/dashboard-state.ts +99 -0
- package/extensions/shared/tool-call-timeout.ts +104 -0
- package/extensions/shared/tui-dashboard.ts +139 -0
- package/extensions/shell-shortcuts/README.md +17 -0
- package/extensions/shell-shortcuts/features/exit-alias.ts +10 -0
- package/extensions/shell-shortcuts/index.ts +6 -0
- package/extensions/subagents/README.md +89 -0
- package/extensions/subagents/index.ts +1386 -0
- package/extensions/subagents/src/backend.ts +69 -0
- package/extensions/subagents/src/backends/pi.ts +781 -0
- package/extensions/subagents/src/catalog.ts +85 -0
- package/extensions/subagents/src/config.ts +241 -0
- package/extensions/subagents/src/context.ts +57 -0
- package/extensions/subagents/src/coordinator.ts +64 -0
- package/extensions/subagents/src/domain.ts +294 -0
- package/extensions/subagents/src/format.ts +74 -0
- package/extensions/subagents/src/manager.ts +813 -0
- package/extensions/subagents/src/prompt.ts +94 -0
- package/extensions/subagents/src/result-delivery.ts +20 -0
- package/extensions/subagents/src/runtime.ts +48 -0
- package/extensions/subagents/src/ui/takeover.ts +695 -0
- package/extensions/subagents/src/ui/transcript.ts +182 -0
- package/extensions/subagents/src/worktree.ts +175 -0
- package/extensions/web-fetch/README.md +13 -0
- package/extensions/web-fetch/index.ts +441 -0
- package/package.json +117 -0
- package/skills/background-terminals/SKILL.md +42 -0
- package/skills/orchestration/SKILL.md +33 -0
- package/skills/subagents/SKILL.md +89 -0
- package/themes/ember-warm-dark.json +85 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
|
|
4
|
+
export type SemanticState =
|
|
5
|
+
| "active"
|
|
6
|
+
| "success"
|
|
7
|
+
| "warning"
|
|
8
|
+
| "error"
|
|
9
|
+
| "muted";
|
|
10
|
+
|
|
11
|
+
const STATE_COLORS = {
|
|
12
|
+
active: "accent",
|
|
13
|
+
success: "success",
|
|
14
|
+
warning: "warning",
|
|
15
|
+
error: "error",
|
|
16
|
+
muted: "muted",
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
export function stateDot(theme: Theme, state: SemanticState): string {
|
|
20
|
+
return theme.fg(STATE_COLORS[state], "●");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function stateLabel(
|
|
24
|
+
theme: Theme,
|
|
25
|
+
state: SemanticState,
|
|
26
|
+
label: string,
|
|
27
|
+
): string {
|
|
28
|
+
return `${stateDot(theme, state)} ${theme.fg(STATE_COLORS[state], label)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function sanitizeTerminalText(text: string): string {
|
|
32
|
+
return text
|
|
33
|
+
.replace(/(?:\u001b\]|\u009d)(?:[^\u0007\u001b\u009c]|\u001b(?!\\))*(?:\u0007|\u001b\\|\u009c)/g, "")
|
|
34
|
+
.replace(/(?:\u001b\[|\u009b)[0-?]*[ -/]*[@-~]/g, "")
|
|
35
|
+
.replace(/\u001b(?:[()][0-2A-Z]|[ -/]*[@-~])/g, "")
|
|
36
|
+
.replaceAll("\t", " ")
|
|
37
|
+
.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g, "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function oneLine(text: string): string {
|
|
41
|
+
return sanitizeTerminalText(text).replace(/\s+/g, " ").trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function padLine(text: string, width: number): string {
|
|
45
|
+
const safeWidth = Math.max(0, width);
|
|
46
|
+
const truncated = truncateToWidth(text, safeWidth, "");
|
|
47
|
+
return truncated + " ".repeat(Math.max(0, safeWidth - visibleWidth(truncated)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function joinSides(left: string, right: string, width: number): string {
|
|
51
|
+
const safeWidth = Math.max(0, width);
|
|
52
|
+
const rightWidth = visibleWidth(right);
|
|
53
|
+
if (rightWidth >= safeWidth) return truncateToWidth(right, safeWidth);
|
|
54
|
+
const leftWidth = Math.max(0, safeWidth - rightWidth - 1);
|
|
55
|
+
const fittedLeft = truncateToWidth(left, leftWidth, "…");
|
|
56
|
+
return truncateToWidth(
|
|
57
|
+
fittedLeft + " ".repeat(Math.max(1, safeWidth - visibleWidth(fittedLeft) - rightWidth)) + right,
|
|
58
|
+
safeWidth,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function rule(theme: Theme, width: number, accent = false): string {
|
|
63
|
+
return theme.fg(accent ? "borderAccent" : "border", "─".repeat(Math.max(0, width)));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function framedRow(
|
|
67
|
+
theme: Theme,
|
|
68
|
+
content: string,
|
|
69
|
+
width: number,
|
|
70
|
+
selected = false,
|
|
71
|
+
): string {
|
|
72
|
+
const innerWidth = Math.max(0, width - 2);
|
|
73
|
+
const body = padLine(content, innerWidth);
|
|
74
|
+
const row = `${theme.fg(selected ? "borderAccent" : "border", "│")}${body}${theme.fg(selected ? "borderAccent" : "border", "│")}`;
|
|
75
|
+
return selected ? theme.bg("selectedBg", row) : row;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function frameTop(theme: Theme, width: number, title = ""): string {
|
|
79
|
+
const inner = Math.max(0, width - 2);
|
|
80
|
+
const label = title ? ` ${oneLine(title)} ` : "";
|
|
81
|
+
const fitted = truncateToWidth(label, Math.max(0, inner - 1), "…");
|
|
82
|
+
return (
|
|
83
|
+
theme.fg("border", "╭") +
|
|
84
|
+
(fitted ? theme.fg("muted", fitted) : "") +
|
|
85
|
+
theme.fg("border", "─".repeat(Math.max(0, inner - visibleWidth(fitted)))) +
|
|
86
|
+
theme.fg("border", "╮")
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function frameBottom(theme: Theme, width: number): string {
|
|
91
|
+
return (
|
|
92
|
+
theme.fg("border", "╰") +
|
|
93
|
+
theme.fg("border", "─".repeat(Math.max(0, width - 2))) +
|
|
94
|
+
theme.fg("border", "╯")
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function meter(
|
|
99
|
+
theme: Theme,
|
|
100
|
+
value: number,
|
|
101
|
+
total: number,
|
|
102
|
+
width: number,
|
|
103
|
+
state: SemanticState = "active",
|
|
104
|
+
): string {
|
|
105
|
+
const safeWidth = Math.max(3, width);
|
|
106
|
+
const ratio = total > 0 ? Math.min(1, Math.max(0, value / total)) : 0;
|
|
107
|
+
const percent = Math.round(ratio * 100);
|
|
108
|
+
const percentText = `${percent}%`;
|
|
109
|
+
const barWidth = Math.max(1, safeWidth - percentText.length - 1);
|
|
110
|
+
const filled = Math.min(barWidth, Math.round(barWidth * ratio));
|
|
111
|
+
const bar =
|
|
112
|
+
theme.fg(STATE_COLORS[state], "━".repeat(filled)) +
|
|
113
|
+
theme.fg("borderMuted", "─".repeat(Math.max(0, barWidth - filled)));
|
|
114
|
+
return `${bar} ${theme.fg("muted", percentText)}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function formatDurationMs(milliseconds: number): string {
|
|
118
|
+
const seconds = Math.max(0, Math.floor(milliseconds / 1000));
|
|
119
|
+
const hours = Math.floor(seconds / 3600);
|
|
120
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
121
|
+
const remainder = seconds % 60;
|
|
122
|
+
if (hours > 0) return `${hours}h ${minutes.toString().padStart(2, "0")}m`;
|
|
123
|
+
if (minutes > 0) return `${minutes}m ${remainder.toString().padStart(2, "0")}s`;
|
|
124
|
+
return `${remainder}s`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function viewportSlice<T>(
|
|
128
|
+
items: ReadonlyArray<T>,
|
|
129
|
+
selectedIndex: number,
|
|
130
|
+
height: number,
|
|
131
|
+
): { start: number; items: ReadonlyArray<T> } {
|
|
132
|
+
const safeHeight = Math.max(0, height);
|
|
133
|
+
if (items.length <= safeHeight) return { start: 0, items };
|
|
134
|
+
const start = Math.min(
|
|
135
|
+
Math.max(0, selectedIndex - Math.floor(safeHeight / 2)),
|
|
136
|
+
Math.max(0, items.length - safeHeight),
|
|
137
|
+
);
|
|
138
|
+
return { start, items: items.slice(start, start + safeHeight) };
|
|
139
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Pi shell shortcuts
|
|
2
|
+
|
|
3
|
+
Small always-on quality-of-life features for local Pi.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- `features/exit-alias.ts` — adds `/exit` as an alias for `/quit`.
|
|
8
|
+
|
|
9
|
+
## Removed
|
|
10
|
+
|
|
11
|
+
- `features/native-max-thinking.ts` was removed because Pi 0.80.7 supports `--thinking max` natively.
|
|
12
|
+
|
|
13
|
+
## When to add here
|
|
14
|
+
|
|
15
|
+
Add tiny, low-risk workflow conveniences here when they do not deserve a standalone extension.
|
|
16
|
+
|
|
17
|
+
Keep larger or security-sensitive systems separate, such as provider auth, web search, subagents, MCP, and persistent goals.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export default function exitAliasExtension(pi: ExtensionAPI) {
|
|
4
|
+
pi.registerCommand("exit", {
|
|
5
|
+
description: "Alias for /quit",
|
|
6
|
+
handler: async (_args, ctx) => {
|
|
7
|
+
ctx.shutdown();
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Pi subagents
|
|
2
|
+
|
|
3
|
+
A Pi-only subagent system with an Effect-managed lifecycle, persistent child sessions, configurable profiles, context forks, resumability, worktree isolation, and a live takeover dashboard.
|
|
4
|
+
|
|
5
|
+
## Topology
|
|
6
|
+
|
|
7
|
+
The system is deliberately flat. Only the main Pi thread can spawn subagents. Children may list and message existing peers through the main-thread manager, but they do not receive spawn, agent-management, or workflow tools. This permits collaboration without recursive fan-out or runaway agent trees.
|
|
8
|
+
|
|
9
|
+
## Parent tools
|
|
10
|
+
|
|
11
|
+
- `spawn_agent` — start a background child with an optional profile, persona, capability mode, context fork, model override, or isolated worktree
|
|
12
|
+
- `wait_agent` — collect results already available and report running children as pending without blocking
|
|
13
|
+
- `close_agent` — interrupt children while retaining their transcripts
|
|
14
|
+
- `check_agent` — inspect live activity without waiting
|
|
15
|
+
- `list_agent_profiles` — discover active profiles, personas, defaults, and concurrency
|
|
16
|
+
- `list_agents` — list live and globally archived/resumable children
|
|
17
|
+
- `send_message` — steer a running child or start its next turn
|
|
18
|
+
- `apply_agent_changes` — inspect, patch, cherry-pick, merge, or discard an isolated child worktree
|
|
19
|
+
- `reply_question` — answer a child’s blocking `ask_parent` request
|
|
20
|
+
- `task` — atomically reserve capacity for up to the configured limit (maximum 50), start the fan-out in the background, and return child ids immediately
|
|
21
|
+
|
|
22
|
+
Child sessions receive `message_parent`, `ask_parent`, `list_peers`, and `message_peer`. Peer messages are routed through the main-thread manager and can steer a running child or continue a settled one; they cannot create agents. Background completions wake the parent automatically, so the main turn can continue independent work or end and remain available to the user.
|
|
23
|
+
|
|
24
|
+
## Profiles and capabilities
|
|
25
|
+
|
|
26
|
+
Built-in profiles:
|
|
27
|
+
|
|
28
|
+
- `general-purpose` — unrestricted coding worker
|
|
29
|
+
- `explore` — investigation with read and command execution, but no file edits
|
|
30
|
+
- `plan` — investigation followed by a concrete implementation plan, with no file edits
|
|
31
|
+
|
|
32
|
+
Capability modes:
|
|
33
|
+
|
|
34
|
+
- `read-only` — allowlisted read/search and parent-communication tools only
|
|
35
|
+
- `read-write` — the read-only allowlist plus direct file write/edit tools, without shell execution
|
|
36
|
+
- `execute` — the read-only allowlist plus shell and background-terminal execution, without direct file edit tools
|
|
37
|
+
- `all` — full child tool access
|
|
38
|
+
|
|
39
|
+
Restrictive modes fail closed: newly registered extension tools remain unavailable until they are explicitly classified. This prevents another extension from silently bypassing the selected capability.
|
|
40
|
+
|
|
41
|
+
Optional user profiles and personas can be defined in `~/.pi/agent/subagents.json`. Trusted projects may override them in `.pi/subagents.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"maxConcurrent": 6,
|
|
46
|
+
"profiles": {
|
|
47
|
+
"reviewer": {
|
|
48
|
+
"description": "Review changes without editing",
|
|
49
|
+
"instructions": "Find correctness and regression risks. Cite files and lines.",
|
|
50
|
+
"capability": "execute"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"personas": {
|
|
54
|
+
"concise": {
|
|
55
|
+
"instructions": "Return only findings, evidence, and the recommended action."
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Project configuration is ignored when the project is not trusted. Concurrency is bounded to 1–50; this private package defaults to 50. `/subagents profiles` provides discovery, while `/subagents config` opens a validated editor for global or trusted-project configuration.
|
|
62
|
+
|
|
63
|
+
## Context and continuation
|
|
64
|
+
|
|
65
|
+
New children start with independent context by default. `fork_turns` may be `all` or a positive number of recent user turns. Forking keeps user messages and final assistant text while removing thinking, tool calls, and tool results so the child never inherits an unresolved tool protocol.
|
|
66
|
+
|
|
67
|
+
Every child uses a persistent Pi session file. `resume_from` continues a completed child with its full transcript, tool state, and logical agent id, including after a parent `/reload` or resume. Non-secret metadata is stored both in the parent session and in `~/.pi/agent/subagents/catalog.json`, so children created by an ephemeral or different parent process remain discoverable. Existing legacy ids remain valid catalog keys.
|
|
68
|
+
|
|
69
|
+
## Worktree isolation
|
|
70
|
+
|
|
71
|
+
Children share the requested workspace by default. Use `isolation: "worktree"` only when the user requests it, a configured profile requires it, or concurrent write tasks are likely to overlap or interfere. Read-only work and edits to clearly separate areas should stay in the shared workspace.
|
|
72
|
+
|
|
73
|
+
`isolation: "worktree"` requires a clean source checkout, then creates a dedicated branch and persistent git worktree from `HEAD` under `~/.pi/agent/subagent-worktrees/`. A dirty source is rejected rather than silently giving the child stale code; commit or stash first, or use the shared workspace. `apply_agent_changes` supports `inspect`, checked patch application, commit-preserving cherry-pick, merge, and permanent discard. Cherry-pick and merge first run in a temporary worktree; conflicts leave the source checkout unchanged. Discard requires confirmation in interactive mode. A failed spawn removes the worktree it created; completed worktrees remain available for inspection or continuation.
|
|
74
|
+
|
|
75
|
+
## UI
|
|
76
|
+
|
|
77
|
+
`/subagents` or `/subagents agents` opens the live operations dashboard and takeover UI. `/btw <question>` starts a read-only, context-aware side investigation owned by the user; it opens directly in takeover view and records its answer without waking the parent model. Wide terminals use a split-pane agent list and selected-agent inspector with running/completed/failed counts, model/profile/access metadata, a context meter, current tool activity, queue state, elapsed time, turns, cwd, and latest output. The takeover view adds a live transcript, context meter, active-tool state, scrolling, child interruption, and follow-up input. `/subagents peers` shows the persistent peer-message audit trail. `/subagents profiles` browses profiles and personas, and `/subagents config` edits validated configuration.
|
|
78
|
+
|
|
79
|
+
## Architecture
|
|
80
|
+
|
|
81
|
+
- `src/manager.ts` — Effect service, bounded lifecycle, normalized snapshots, cancellation, and retention
|
|
82
|
+
- `src/backends/pi.ts` — in-process Pi sessions, trust-gated resources, capability filtering, context seeding, resume, and parent bridge
|
|
83
|
+
- `src/config.ts` — built-in and user/project profiles, personas, validation, and limits
|
|
84
|
+
- `src/catalog.ts` — global non-secret resumable-agent catalog
|
|
85
|
+
- `src/context.ts` — safe context-fork selection and profile prompt assembly
|
|
86
|
+
- `src/worktree.ts` — isolated worktree creation, inspection, conflict preflight, integration, and cleanup
|
|
87
|
+
- `src/runtime.ts` — managed runtime and Pi backend registry
|
|
88
|
+
- `src/ui/` — dashboard, transcript, and takeover components
|
|
89
|
+
- `src/result-delivery.ts` — deferred automatic result delivery
|