@huseynsnmz/pi-suite 0.0.0-stage → 0.2.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/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +88 -2
- package/SECURITY.md +5 -0
- package/package.json +112 -4
- package/packages/board/CHANGELOG.md +340 -0
- package/packages/board/CONTRIBUTING.md +7 -0
- package/packages/board/LICENSE +21 -0
- package/packages/board/README.md +88 -0
- package/packages/board/SECURITY.md +19 -0
- package/packages/board/docs/how-it-works.md +31 -0
- package/packages/board/docs/reference.md +278 -0
- package/packages/board/package.json +81 -0
- package/packages/board/runner/job-runner-legacy.mjs +68 -0
- package/packages/board/runner/job-runner.mjs +834 -0
- package/packages/board/runner/pty-runner-legacy.mjs +50 -0
- package/packages/board/runner/pty-runner.mjs +2175 -0
- package/packages/board/runner/state-coordinator.mjs +673 -0
- package/packages/board/runner/state-runner.mjs +203 -0
- package/packages/board/runner/title-runner.mjs +80 -0
- package/packages/board/src/commands/agent-board.ts +394 -0
- package/packages/board/src/commands/attach-decision.mjs +66 -0
- package/packages/board/src/commands/attach-flow.ts +251 -0
- package/packages/board/src/commands/bg.ts +103 -0
- package/packages/board/src/commands/board-model.ts +21 -0
- package/packages/board/src/core/atomic.mjs +184 -0
- package/packages/board/src/core/attach-delete.mjs +24 -0
- package/packages/board/src/core/attach-frame-proof.mjs +870 -0
- package/packages/board/src/core/attach-frame-reporter.mjs +648 -0
- package/packages/board/src/core/attach-frame-stream.mjs +266 -0
- package/packages/board/src/core/attach-view-proof.mjs +709 -0
- package/packages/board/src/core/auto-state.mjs +377 -0
- package/packages/board/src/core/background-runs.mjs +273 -0
- package/packages/board/src/core/code-refs-store.mjs +315 -0
- package/packages/board/src/core/code-refs.mjs +913 -0
- package/packages/board/src/core/config.mjs +77 -0
- package/packages/board/src/core/control-protocol.mjs +482 -0
- package/packages/board/src/core/coordinator-client.mjs +633 -0
- package/packages/board/src/core/coordinator-journal.mjs +282 -0
- package/packages/board/src/core/coordinator-protocol.mjs +14 -0
- package/packages/board/src/core/cwd-stats.mjs +114 -0
- package/packages/board/src/core/dashboard-render.mjs +10 -0
- package/packages/board/src/core/derive.mjs +117 -0
- package/packages/board/src/core/diagnostics.mjs +133 -0
- package/packages/board/src/core/double-press.mjs +26 -0
- package/packages/board/src/core/editor-state-reporter.mjs +112 -0
- package/packages/board/src/core/events.mjs +286 -0
- package/packages/board/src/core/evidence.mjs +245 -0
- package/packages/board/src/core/follow-up-queue.mjs +211 -0
- package/packages/board/src/core/foreground-preview-cache.mjs +117 -0
- package/packages/board/src/core/foreground-projection-cache.mjs +99 -0
- package/packages/board/src/core/frame-witness-update.mjs +35 -0
- package/packages/board/src/core/heuristics.mjs +313 -0
- package/packages/board/src/core/hold-input.mjs +27 -0
- package/packages/board/src/core/host-coordination.mjs +300 -0
- package/packages/board/src/core/host-crash.mjs +79 -0
- package/packages/board/src/core/host-probe.mjs +207 -0
- package/packages/board/src/core/host-protocol.mjs +73 -0
- package/packages/board/src/core/ids.mjs +35 -0
- package/packages/board/src/core/ime-cursor-coalesce.mjs +193 -0
- package/packages/board/src/core/invocation.mjs +43 -0
- package/packages/board/src/core/launch-options.mjs +418 -0
- package/packages/board/src/core/launch.mjs +157 -0
- package/packages/board/src/core/load-config.mjs +4 -0
- package/packages/board/src/core/locks.mjs +530 -0
- package/packages/board/src/core/paths.mjs +183 -0
- package/packages/board/src/core/pid.mjs +75 -0
- package/packages/board/src/core/prewarm-schedule.mjs +41 -0
- package/packages/board/src/core/process-identity-probe.mjs +228 -0
- package/packages/board/src/core/prompt-transport.mjs +13 -0
- package/packages/board/src/core/pty-attach-jiggle-controller.mjs +356 -0
- package/packages/board/src/core/pty-attach-jiggle-retry.mjs +109 -0
- package/packages/board/src/core/pty-attach-reconnect.mjs +61 -0
- package/packages/board/src/core/pty-attach-render.mjs +229 -0
- package/packages/board/src/core/pty-links.mjs +81 -0
- package/packages/board/src/core/pty-scroll.mjs +522 -0
- package/packages/board/src/core/pty-support.mjs +327 -0
- package/packages/board/src/core/repo.mjs +103 -0
- package/packages/board/src/core/rows.mjs +414 -0
- package/packages/board/src/core/screen-log-gc.mjs +198 -0
- package/packages/board/src/core/screen-log.mjs +160 -0
- package/packages/board/src/core/session-view.mjs +174 -0
- package/packages/board/src/core/slash-commands.mjs +52 -0
- package/packages/board/src/core/state-commands.mjs +757 -0
- package/packages/board/src/core/status-consistency.mjs +98 -0
- package/packages/board/src/core/steering-prompts.mjs +34 -0
- package/packages/board/src/core/steering.mjs +133 -0
- package/packages/board/src/core/stop-latch.mjs +41 -0
- package/packages/board/src/core/store.mjs +523 -0
- package/packages/board/src/core/suspend.mjs +36 -0
- package/packages/board/src/core/terminal-attach-client.mjs +981 -0
- package/packages/board/src/core/terminal-attach-protocol.mjs +460 -0
- package/packages/board/src/core/terminal-model.mjs +222 -0
- package/packages/board/src/core/terminal-query-sequences.mjs +239 -0
- package/packages/board/src/core/terminal-reply-guard.mjs +222 -0
- package/packages/board/src/core/terminal-snapshot.mjs +440 -0
- package/packages/board/src/core/test-knobs.mjs +28 -0
- package/packages/board/src/core/title.mjs +43 -0
- package/packages/board/src/core/types.mjs +414 -0
- package/packages/board/src/core/warm-host-sweeper.mjs +168 -0
- package/packages/board/src/core/worktree.mjs +65 -0
- package/packages/board/src/index.ts +303 -0
- package/packages/board/src/runtime/service.mjs +3222 -0
- package/packages/board/src/ui/chrome.ts +20 -0
- package/packages/board/src/ui/dashboard-decisions.mjs +55 -0
- package/packages/board/src/ui/dashboard-evidence.mjs +85 -0
- package/packages/board/src/ui/dashboard.ts +2237 -0
- package/packages/board/src/ui/double-left.ts +30 -0
- package/packages/board/src/ui/external-editor.ts +25 -0
- package/packages/board/src/ui/focus.ts +32 -0
- package/packages/board/src/ui/pty-attach.ts +3184 -0
- package/packages/board/src/ui/waiting.ts +30 -0
- package/packages/commands/LICENSE +21 -0
- package/packages/commands/README.md +19 -0
- package/packages/commands/package.json +22 -0
- package/packages/commands/src/index.ts +43 -0
- package/packages/commands/src/security-review.ts +109 -0
- package/packages/frame/CHANGELOG.md +113 -0
- package/packages/frame/CONTRIBUTING.md +7 -0
- package/packages/frame/LICENSE +22 -0
- package/packages/frame/README.md +105 -0
- package/packages/frame/SECURITY.md +16 -0
- package/packages/frame/docs/how-it-works.md +53 -0
- package/packages/frame/docs/reference.md +78 -0
- package/packages/frame/package.json +72 -0
- package/packages/frame/src/data/git.ts +90 -0
- package/packages/frame/src/data/github.ts +140 -0
- package/packages/frame/src/data/gitlab.ts +63 -0
- package/packages/frame/src/data/mode.ts +49 -0
- package/packages/frame/src/data/past-prompts.ts +57 -0
- package/packages/frame/src/data/runner.ts +32 -0
- package/packages/frame/src/data/snapshot.ts +223 -0
- package/packages/frame/src/data/start-notes.ts +104 -0
- package/packages/frame/src/data/telemetry.ts +337 -0
- package/packages/frame/src/data/todos.ts +162 -0
- package/packages/frame/src/data/totals.ts +94 -0
- package/packages/frame/src/data/visits.ts +45 -0
- package/packages/frame/src/index.ts +696 -0
- package/packages/frame/src/kit/LICENSE +21 -0
- package/packages/frame/src/kit/app/attribution.ts +30 -0
- package/packages/frame/src/kit/app/builder.ts +186 -0
- package/packages/frame/src/kit/app/claim.ts +27 -0
- package/packages/frame/src/kit/app/feature.ts +91 -0
- package/packages/frame/src/kit/app/host.ts +214 -0
- package/packages/frame/src/kit/app/renderers.ts +62 -0
- package/packages/frame/src/kit/app/scope.ts +125 -0
- package/packages/frame/src/kit/board/model.ts +337 -0
- package/packages/frame/src/kit/contract.ts +79 -0
- package/packages/frame/src/kit/contracts/accounts.ts +33 -0
- package/packages/frame/src/kit/contracts/ask.ts +120 -0
- package/packages/frame/src/kit/contracts/board.ts +6 -0
- package/packages/frame/src/kit/contracts/by.ts +22 -0
- package/packages/frame/src/kit/contracts/calls.ts +46 -0
- package/packages/frame/src/kit/contracts/events.ts +89 -0
- package/packages/frame/src/kit/contracts/mode.ts +26 -0
- package/packages/frame/src/kit/contracts/names.ts +12 -0
- package/packages/frame/src/kit/contracts/peers.ts +77 -0
- package/packages/frame/src/kit/contracts/prompts.ts +8 -0
- package/packages/frame/src/kit/contracts/screen.ts +75 -0
- package/packages/frame/src/kit/contracts/skills.ts +81 -0
- package/packages/frame/src/kit/contracts/sources.ts +81 -0
- package/packages/frame/src/kit/contracts/views.ts +42 -0
- package/packages/frame/src/kit/control.ts +59 -0
- package/packages/frame/src/kit/dark-theme.ts +41 -0
- package/packages/frame/src/kit/decode.ts +210 -0
- package/packages/frame/src/kit/events/log.ts +313 -0
- package/packages/frame/src/kit/format.ts +25 -0
- package/packages/frame/src/kit/host.ts +4 -0
- package/packages/frame/src/kit/index.ts +25 -0
- package/packages/frame/src/kit/legacy-tasks.ts +39 -0
- package/packages/frame/src/kit/overlay-images.ts +36 -0
- package/packages/frame/src/kit/paths.ts +16 -0
- package/packages/frame/src/kit/screen/client.ts +74 -0
- package/packages/frame/src/kit/screen/feature.ts +64 -0
- package/packages/frame/src/kit/screen/model.ts +182 -0
- package/packages/frame/src/kit/screen/view.ts +838 -0
- package/packages/frame/src/kit/settings/files.ts +90 -0
- package/packages/frame/src/kit/settings/setting.ts +57 -0
- package/packages/frame/src/kit/settings/store.ts +192 -0
- package/packages/frame/src/kit/sources/file.ts +129 -0
- package/packages/frame/src/kit/sources/follow.ts +155 -0
- package/packages/frame/src/kit/testing.ts +260 -0
- package/packages/frame/src/kit/totals.ts +72 -0
- package/packages/frame/src/kit/ui/entry.ts +75 -0
- package/packages/frame/src/kit/ui/frame.ts +212 -0
- package/packages/frame/src/kit/ui/glyphs.ts +132 -0
- package/packages/frame/src/kit/ui/scroll.ts +14 -0
- package/packages/frame/src/render/diff.ts +157 -0
- package/packages/frame/src/render/fit.ts +86 -0
- package/packages/frame/src/render/format.ts +108 -0
- package/packages/frame/src/render/frame.ts +106 -0
- package/packages/frame/src/render/glyphs.ts +189 -0
- package/packages/frame/src/render/keys.ts +43 -0
- package/packages/frame/src/render/lead.ts +37 -0
- package/packages/frame/src/render/paint.ts +135 -0
- package/packages/frame/src/render/safe.ts +19 -0
- package/packages/frame/src/render/segments.ts +735 -0
- package/packages/frame/src/render/streaming.ts +223 -0
- package/packages/frame/src/render/timing.ts +106 -0
- package/packages/frame/src/render/tools.ts +167 -0
- package/packages/frame/src/settings.ts +105 -0
- package/packages/frame/src/ui/editor.ts +272 -0
- package/packages/frame/src/ui/effort.ts +36 -0
- package/packages/frame/src/ui/footer.ts +165 -0
- package/packages/frame/src/ui/header.ts +362 -0
- package/packages/frame/src/ui/history-search.ts +23 -0
- package/packages/frame/src/ui/interrupt.ts +21 -0
- package/packages/frame/src/ui/outputs.ts +170 -0
- package/packages/frame/src/ui/pi-entries.ts +298 -0
- package/packages/frame/src/ui/previews.ts +72 -0
- package/packages/frame/src/ui/queue.ts +75 -0
- package/packages/frame/src/ui/screen.ts +59 -0
- package/packages/frame/src/ui/timings.ts +48 -0
- package/packages/frame/src/ui/todos.ts +205 -0
- package/packages/frame/src/ui/tool-frame.ts +595 -0
- package/packages/frame/src/ui/working.ts +25 -0
- package/packages/frame/src/view/state.ts +97 -0
- package/packages/frame/src/view/transcript.ts +175 -0
- package/packages/frame/src/view/view.ts +162 -0
- package/packages/memory/CONTRIBUTING.md +7 -0
- package/packages/memory/LICENSE +21 -0
- package/packages/memory/README.md +44 -0
- package/packages/memory/SECURITY.md +20 -0
- package/packages/memory/package.json +55 -0
- package/packages/memory/skills/review-memory/SKILL.md +48 -0
- package/packages/memory/src/index.ts +163 -0
- package/packages/memory/src/prompt.ts +25 -0
- package/packages/memory/src/review.ts +150 -0
- package/packages/memory/src/secrets.ts +22 -0
- package/packages/memory/src/store.ts +173 -0
- package/packages/modes/CHANGELOG.md +104 -0
- package/packages/modes/CONTRIBUTING.md +7 -0
- package/packages/modes/LICENSE +21 -0
- package/packages/modes/README.md +174 -0
- package/packages/modes/SECURITY.md +17 -0
- package/packages/modes/package.json +55 -0
- package/packages/modes/skills/review-permissions/SKILL.md +54 -0
- package/packages/modes/src/bash.ts +218 -0
- package/packages/modes/src/deny-off.ts +62 -0
- package/packages/modes/src/git-safety.ts +72 -0
- package/packages/modes/src/index.ts +338 -0
- package/packages/modes/src/interview.ts +206 -0
- package/packages/modes/src/judge-gate.ts +113 -0
- package/packages/modes/src/modes.ts +121 -0
- package/packages/modes/src/review/apply.mjs +156 -0
- package/packages/modes/src/review/collect.mjs +208 -0
- package/packages/modes/src/review/tools.ts +71 -0
- package/packages/modes/src/sandbox.ts +37 -0
- package/packages/router/CHANGELOG.md +8 -0
- package/packages/router/LICENSE +21 -0
- package/packages/router/README.md +90 -0
- package/packages/router/package.json +51 -0
- package/packages/router/src/config.ts +65 -0
- package/packages/router/src/index.ts +172 -0
- package/packages/router/src/judge.ts +51 -0
- package/packages/router/src/route.ts +67 -0
- package/packages/web/CHANGELOG.md +36 -0
- package/packages/web/LICENSE +21 -0
- package/packages/web/README.md +87 -0
- package/packages/web/SECURITY.md +13 -0
- package/packages/web/package.json +59 -0
- package/packages/web/src/config.ts +109 -0
- package/packages/web/src/dnsfilter.ts +97 -0
- package/packages/web/src/extract.ts +65 -0
- package/packages/web/src/index.ts +217 -0
- package/packages/web/src/markdown.ts +122 -0
- package/packages/web/src/net.ts +109 -0
- package/packages/web/src/reader.ts +73 -0
- package/packages/web/src/sanitize.ts +50 -0
- package/packages/web/src/search.ts +76 -0
- package/packages/web/src/secrets.ts +22 -0
- package/packages/web/src/validate.ts +186 -0
- package/src/index.ts +156 -0
- package/src/settings.ts +79 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Agent Board — extension entry point.
|
|
3
|
+
*
|
|
4
|
+
* Registers the `/agent-board` dashboard command, resolves how to
|
|
5
|
+
* launch background workers and the detached runner, and keeps a small footer status with
|
|
6
|
+
* the count of sessions needing attention.
|
|
7
|
+
*/
|
|
8
|
+
import { chmodSync, mkdirSync } from "node:fs";
|
|
9
|
+
import "./core/load-config.mjs";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { createConnection } from "node:net";
|
|
12
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { resolvePiInvocation } from "./core/invocation.mjs";
|
|
14
|
+
import { defaultRoot, resolveControlEndpoint } from "./core/paths.mjs";
|
|
15
|
+
import { listRows } from "./core/store.mjs";
|
|
16
|
+
import { createService, envInt } from "./runtime/service.mjs";
|
|
17
|
+
import { attachWarmHostSweeper } from "./core/warm-host-sweeper.mjs";
|
|
18
|
+
import { openDashboard, registerAgentBoardCommand } from "./commands/agent-board.js";
|
|
19
|
+
import { registerBgCommand } from "./commands/bg.js";
|
|
20
|
+
import { registerBoardModelCommand } from "./commands/board-model.ts";
|
|
21
|
+
import { announceBoardClosedOn } from "./ui/chrome.ts";
|
|
22
|
+
import { watchDoubleLeft } from "./ui/double-left.ts";
|
|
23
|
+
import { watchWaiting } from "./ui/waiting.ts";
|
|
24
|
+
|
|
25
|
+
const RUNNER_SCRIPT = fileURLToPath(new URL("../runner/job-runner.mjs", import.meta.url));
|
|
26
|
+
const PTY_RUNNER_SCRIPT = fileURLToPath(new URL("../runner/pty-runner.mjs", import.meta.url));
|
|
27
|
+
const TITLE_RUNNER_SCRIPT = fileURLToPath(new URL("../runner/title-runner.mjs", import.meta.url));
|
|
28
|
+
const AUTO_STATE_RUNNER_SCRIPT = fileURLToPath(new URL("../runner/state-runner.mjs", import.meta.url));
|
|
29
|
+
|
|
30
|
+
let hostedEditorReporter: { start(): void; stop(): void } | null = null;
|
|
31
|
+
/**
|
|
32
|
+
* Issue #222 (23b1, FW-03/FW-04): hosted-child frame-witness reporter. It is
|
|
33
|
+
* capability-gated by the runner (`frame_witness_enable`) and only exists for hosted
|
|
34
|
+
* children with a view id and a TUI; it publishes NO attach readiness or proof — the
|
|
35
|
+
* runner only consumes the markers for its own bookkeeping until 23b2.
|
|
36
|
+
*/
|
|
37
|
+
let hostedFrameWitness: { stop(): void } | null = null;
|
|
38
|
+
// F4: truthful session epoch source. A hosted runner child currently hosts exactly ONE
|
|
39
|
+
// interactive session, so the first session_start is epoch 1; any additional actual
|
|
40
|
+
// session_start increments it (and the runner-side intent lifecycle invalidates the old
|
|
41
|
+
// candidates). No other topology is used here.
|
|
42
|
+
let hostedSessionEpoch = 0;
|
|
43
|
+
|
|
44
|
+
/** Transcripts and screen logs live under here, and can hold whatever a session printed. */
|
|
45
|
+
function keepRootPrivate(root: string): void {
|
|
46
|
+
try {
|
|
47
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
48
|
+
chmodSync(root, 0o700);
|
|
49
|
+
} catch {
|
|
50
|
+
/* The board still works, as it did before it had this. */
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default function piAgentBoard(pi: ExtensionAPI): void {
|
|
55
|
+
announceBoardClosedOn(pi.events);
|
|
56
|
+
let stopDoubleLeft: (() => void) | null = null;
|
|
57
|
+
let stopWaiting: (() => void) | null = null;
|
|
58
|
+
const root = defaultRoot();
|
|
59
|
+
keepRootPrivate(root);
|
|
60
|
+
const { piCommand, piArgsPrefix } = resolvePiInvocation();
|
|
61
|
+
|
|
62
|
+
const isHostedChild = process.env.AGENT_BOARD_CHILD === "1" || process.env.AGENT_VIEW_CHILD === "1";
|
|
63
|
+
const hostedViewId = process.env.AGENT_BOARD_VIEW_ID ?? process.env.AGENT_VIEW_VIEW_ID;
|
|
64
|
+
|
|
65
|
+
const commandOpts = {
|
|
66
|
+
root,
|
|
67
|
+
runnerScript: RUNNER_SCRIPT,
|
|
68
|
+
titleRunnerScript: TITLE_RUNNER_SCRIPT,
|
|
69
|
+
autoStateRunnerScript: AUTO_STATE_RUNNER_SCRIPT,
|
|
70
|
+
ptyRunnerScript: PTY_RUNNER_SCRIPT,
|
|
71
|
+
piCommand,
|
|
72
|
+
piArgsPrefix,
|
|
73
|
+
getThinkingLevel: () => pi.getThinkingLevel(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Warm-host reclaim (issue #75): idle PTY hosts must not leak forever after
|
|
77
|
+
// detach. Sweep once now (reclaims hosts orphaned by a previous host pi that
|
|
78
|
+
// exited without cleanup), then periodically; sweep again on shutdown.
|
|
79
|
+
// Child pi processes skip entirely (they share the board root and would
|
|
80
|
+
// terminate their own runner). serviceForContext is defined before the attach
|
|
81
|
+
// call because the sweeper sweeps synchronously on attach and the sweep
|
|
82
|
+
// closure references it (no TDZ).
|
|
83
|
+
const serviceForContext = () =>
|
|
84
|
+
createService({ root, runnerScript: RUNNER_SCRIPT, ptyRunnerScript: PTY_RUNNER_SCRIPT, titleRunnerScript: TITLE_RUNNER_SCRIPT, autoStateRunnerScript: AUTO_STATE_RUNNER_SCRIPT, piCommand, piArgsPrefix, defaultCwd: process.cwd() });
|
|
85
|
+
let sweepService: ReturnType<typeof createService> | null = null;
|
|
86
|
+
attachWarmHostSweeper(pi, {
|
|
87
|
+
isHostedChild,
|
|
88
|
+
sweep: () => {
|
|
89
|
+
try {
|
|
90
|
+
// Reuse one service instance per extension lifetime: createService
|
|
91
|
+
// schedules a screen-log GC on construction, so per-tick creation
|
|
92
|
+
// would turn an event-driven cost into a timed one.
|
|
93
|
+
sweepService ??= serviceForContext();
|
|
94
|
+
sweepService.pruneWarmHosts();
|
|
95
|
+
} catch {
|
|
96
|
+
/* best-effort: never break the session over a sweep */
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
intervalMs: envInt("AGENT_BOARD_SWEEP_INTERVAL_MS", 60_000, 0, 24 * 60 * 60 * 1000, "AGENT_VIEW_SWEEP_INTERVAL_MS"),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
registerAgentBoardCommand(pi, commandOpts);
|
|
103
|
+
registerBgCommand(pi, commandOpts);
|
|
104
|
+
registerBoardModelCommand(pi);
|
|
105
|
+
pi.registerFlag("agent-board", {
|
|
106
|
+
description: "Open the agent-board dashboard on startup",
|
|
107
|
+
type: "boolean",
|
|
108
|
+
default: false,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Footer status: reconcile stale rows and surface how many need attention.
|
|
112
|
+
// Live model list for the stale-defaultModel launch guard (issue #90); the
|
|
113
|
+
// service calls it per validation, and undefined conservatively skips.
|
|
114
|
+
const availableModelsFor = (ctx: ExtensionContext) => () => {
|
|
115
|
+
try {
|
|
116
|
+
return ctx.modelRegistry.getAvailable();
|
|
117
|
+
} catch {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const serviceFor = (ctx: ExtensionContext) =>
|
|
122
|
+
createService({ root, runnerScript: RUNNER_SCRIPT, ptyRunnerScript: PTY_RUNNER_SCRIPT, titleRunnerScript: TITLE_RUNNER_SCRIPT, autoStateRunnerScript: AUTO_STATE_RUNNER_SCRIPT, piCommand, piArgsPrefix, defaultCwd: ctx.cwd, availableModels: availableModelsFor(ctx) });
|
|
123
|
+
|
|
124
|
+
const updateStatus = (ctx: ExtensionContext) => {
|
|
125
|
+
try {
|
|
126
|
+
void serviceFor(ctx).reconcile().catch(() => {});
|
|
127
|
+
const rows = listRows(root);
|
|
128
|
+
const needs = rows.filter((r) => r.state?.semanticState === "needs_input").length;
|
|
129
|
+
const working = rows.filter((r) => r.alive).length;
|
|
130
|
+
const queued = rows.reduce((sum, r) => sum + (r.state?.followUps?.queuedCount ?? 0), 0);
|
|
131
|
+
if (isHostedChild) return;
|
|
132
|
+
if (rows.length === 0) {
|
|
133
|
+
ctx.ui.setStatus("agent-board", undefined);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const parts: string[] = [];
|
|
137
|
+
if (working > 0) parts.push(ctx.ui.theme.fg("accent", `●${working}`));
|
|
138
|
+
if (needs > 0) parts.push(ctx.ui.theme.fg("warning", `◆${needs}`));
|
|
139
|
+
if (queued > 0) parts.push(ctx.ui.theme.fg("muted", `q${queued}`));
|
|
140
|
+
ctx.ui.setStatus("agent-board", parts.length ? `${ctx.ui.theme.fg("muted", "agents")} ${parts.join(" ")}` : undefined);
|
|
141
|
+
} catch {
|
|
142
|
+
/* never break the session over a status update */
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
async function startHostedFrameWitness(ctx: ExtensionContext): Promise<void> {
|
|
147
|
+
if (hostedFrameWitness !== null || !hostedViewId || !ctx.hasUI || !ctx.ui) return;
|
|
148
|
+
const { AttachFrameReporter } = await import("./core/attach-frame-reporter.mjs");
|
|
149
|
+
const socket = createConnection(
|
|
150
|
+
resolveControlEndpoint({
|
|
151
|
+
envSocketPath: process.env.AGENT_BOARD_CONTROL_SOCKET,
|
|
152
|
+
platform: process.platform as "win32" | "linux" | "darwin",
|
|
153
|
+
root,
|
|
154
|
+
viewId: hostedViewId,
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
const reporter = new AttachFrameReporter({
|
|
158
|
+
ui: ctx.ui as never,
|
|
159
|
+
mode: "fullscreen", // F6: the sentinel factory rebinds from the ACTUAL public tui.mode
|
|
160
|
+
sessionEpoch: hostedSessionEpoch,
|
|
161
|
+
send: (message: unknown) => {
|
|
162
|
+
try {
|
|
163
|
+
socket.write(`${JSON.stringify(message)}\n`);
|
|
164
|
+
} catch {
|
|
165
|
+
/* socket errors surface through the error handler */
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
let installed = false;
|
|
170
|
+
let buffer = "";
|
|
171
|
+
socket.on("connect", () => reporter.announce());
|
|
172
|
+
socket.on("data", (chunk: Buffer) => {
|
|
173
|
+
buffer += chunk.toString("utf8");
|
|
174
|
+
let newline = buffer.indexOf("\n");
|
|
175
|
+
while (newline >= 0) {
|
|
176
|
+
const line = buffer.slice(0, newline);
|
|
177
|
+
buffer = buffer.slice(newline + 1);
|
|
178
|
+
try {
|
|
179
|
+
const message = JSON.parse(line) as { type?: string };
|
|
180
|
+
if (message.type === "frame_witness_enable") {
|
|
181
|
+
const negotiation = reporter.handleMessage(message);
|
|
182
|
+
if (negotiation.status === "enabled" && !installed) installed = reporter.install();
|
|
183
|
+
} else if (message.type === "frame_witness_intent") {
|
|
184
|
+
reporter.applyIntent(message);
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
/* malformed runner message: ignore, the capability stays as it is */
|
|
188
|
+
}
|
|
189
|
+
newline = buffer.indexOf("\n");
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
let cleaned = false;
|
|
193
|
+
const cleanup = (reason: string) => {
|
|
194
|
+
if (cleaned) return;
|
|
195
|
+
cleaned = true;
|
|
196
|
+
try {
|
|
197
|
+
reporter.dispose(reason);
|
|
198
|
+
} catch {
|
|
199
|
+
/* never break the session over the frame witness */
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
socket.destroy();
|
|
203
|
+
} catch {
|
|
204
|
+
/* already closed */
|
|
205
|
+
}
|
|
206
|
+
hostedFrameWitness = null;
|
|
207
|
+
};
|
|
208
|
+
socket.on("error", () => cleanup("socket-error"));
|
|
209
|
+
socket.on("close", () => cleanup("socket-close"));
|
|
210
|
+
hostedFrameWitness = { stop: () => cleanup("session-shutdown") };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// After this the context they hold is stale, and using one takes Pi down.
|
|
214
|
+
pi.on("session_shutdown", () => {
|
|
215
|
+
stopDoubleLeft?.();
|
|
216
|
+
stopDoubleLeft = null;
|
|
217
|
+
stopWaiting?.();
|
|
218
|
+
stopWaiting = null;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
pi.on("session_start", async (event, ctx) => {
|
|
222
|
+
if (!isHostedChild && ctx.mode === "tui") {
|
|
223
|
+
stopDoubleLeft?.();
|
|
224
|
+
stopDoubleLeft = watchDoubleLeft(ctx);
|
|
225
|
+
}
|
|
226
|
+
if (ctx.mode === "tui") {
|
|
227
|
+
stopWaiting?.();
|
|
228
|
+
stopWaiting = watchWaiting(ctx, (waiting) => syncForeground({ type: "agent_board_waiting", waiting }, ctx));
|
|
229
|
+
}
|
|
230
|
+
if (isHostedChild && !hostedEditorReporter && typeof ctx.ui?.getEditorText === "function" && hostedViewId) {
|
|
231
|
+
const { createEditorStateReporter } = await import("./core/editor-state-reporter.mjs");
|
|
232
|
+
hostedEditorReporter = createEditorStateReporter({
|
|
233
|
+
getEditorText: () => ctx.ui.getEditorText(),
|
|
234
|
+
connect: () => createConnection(resolveControlEndpoint({
|
|
235
|
+
// The runner injects the endpoint it actually bound (issue #103);
|
|
236
|
+
// falling back to the stable per-view address keeps older runners
|
|
237
|
+
// and legacy hosts working.
|
|
238
|
+
envSocketPath: process.env.AGENT_BOARD_CONTROL_SOCKET,
|
|
239
|
+
platform: process.platform as "win32" | "linux" | "darwin",
|
|
240
|
+
root,
|
|
241
|
+
viewId: hostedViewId,
|
|
242
|
+
})),
|
|
243
|
+
});
|
|
244
|
+
hostedEditorReporter.start();
|
|
245
|
+
}
|
|
246
|
+
if (isHostedChild) {
|
|
247
|
+
hostedSessionEpoch += 1;
|
|
248
|
+
try {
|
|
249
|
+
// P2: an actual additional session_start invalidates the old witness (its
|
|
250
|
+
// epoch is stale) and starts a new one with the new epoch.
|
|
251
|
+
if (hostedFrameWitness !== null) {
|
|
252
|
+
hostedFrameWitness.stop();
|
|
253
|
+
hostedFrameWitness = null;
|
|
254
|
+
}
|
|
255
|
+
await startHostedFrameWitness(ctx);
|
|
256
|
+
} catch {
|
|
257
|
+
/* the frame witness must never break a hosted session */
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
updateStatus(ctx);
|
|
261
|
+
if (event.reason === "startup" && !isHostedChild && pi.getFlag("agent-board") === true && ctx.hasUI) {
|
|
262
|
+
const service = createService({ root, runnerScript: RUNNER_SCRIPT, ptyRunnerScript: PTY_RUNNER_SCRIPT, titleRunnerScript: TITLE_RUNNER_SCRIPT, autoStateRunnerScript: AUTO_STATE_RUNNER_SCRIPT, piCommand, piArgsPrefix, defaultCwd: ctx.cwd, availableModels: availableModelsFor(ctx) });
|
|
263
|
+
void service.reconcile().catch(() => {});
|
|
264
|
+
ctx.ui.setWorkingVisible(false);
|
|
265
|
+
ctx.ui.setHeader(() => ({ render: () => [], invalidate() {} }));
|
|
266
|
+
ctx.ui.setFooter(() => ({ render: () => [], invalidate() {} }));
|
|
267
|
+
ctx.ui.setTitle("agent board");
|
|
268
|
+
const result = await openDashboard(ctx, service, { currentThinkingLevel: pi.getThinkingLevel() });
|
|
269
|
+
if (result.action === "attach") {
|
|
270
|
+
ctx.ui.notify("Attach requires the /agent-board command path; launch from a normal Pi session for now.", "warning");
|
|
271
|
+
} else {
|
|
272
|
+
ctx.shutdown();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
const syncForeground = (event: unknown, ctx: ExtensionContext) => {
|
|
277
|
+
try {
|
|
278
|
+
const service = serviceFor(ctx);
|
|
279
|
+
if (hostedViewId) service.syncHostedEvent(hostedViewId, event);
|
|
280
|
+
else service.syncForegroundEvent(ctx.sessionManager.getSessionFile(), event);
|
|
281
|
+
updateStatus(ctx);
|
|
282
|
+
} catch {
|
|
283
|
+
/* never break the session over dashboard mirroring */
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
pi.on("session_shutdown", () => {
|
|
288
|
+
try {
|
|
289
|
+
hostedFrameWitness?.stop();
|
|
290
|
+
} catch {
|
|
291
|
+
/* best effort */
|
|
292
|
+
}
|
|
293
|
+
hostedFrameWitness = null;
|
|
294
|
+
});
|
|
295
|
+
pi.on("input", async (event, ctx) => syncForeground(event, ctx));
|
|
296
|
+
pi.on("before_agent_start", async (event, ctx) => syncForeground(event, ctx));
|
|
297
|
+
pi.on("agent_start", async (event, ctx) => syncForeground(event, ctx));
|
|
298
|
+
pi.on("tool_execution_start", async (event, ctx) => syncForeground(event, ctx));
|
|
299
|
+
pi.on("tool_execution_end", async (event, ctx) => syncForeground(event, ctx));
|
|
300
|
+
pi.on("message_start", async (event, ctx) => syncForeground(event, ctx));
|
|
301
|
+
pi.on("message_end", async (event, ctx) => syncForeground(event, ctx));
|
|
302
|
+
pi.on("agent_end", async (event, ctx) => syncForeground(event, ctx));
|
|
303
|
+
}
|