agent-tempo 1.3.1 → 1.4.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/CLAUDE.md +39 -5
- package/README.md +6 -2
- package/dashboard/dist/assets/{index-D6Xyje_n.js → index-jmYe6rmS.js} +2 -2
- package/dashboard/dist/assets/index-jmYe6rmS.js.map +1 -0
- package/dashboard/dist/index.html +1 -1
- package/dashboard/package.json +1 -1
- package/dist/activities/outbox.d.ts +30 -1
- package/dist/activities/outbox.js +96 -3
- package/dist/adapters/base.js +5 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/pi/adapter.d.ts +2 -0
- package/dist/adapters/pi/adapter.js +43 -0
- package/dist/adapters/pi/index.d.ts +16 -0
- package/dist/adapters/pi/index.js +10 -0
- package/dist/client/core.js +9 -2
- package/dist/client/interface.d.ts +6 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +74 -0
- package/dist/daemon.js +32 -1
- package/dist/http/aggregate.d.ts +22 -1
- package/dist/http/aggregate.js +41 -0
- package/dist/http/auth.d.ts +94 -8
- package/dist/http/auth.js +93 -9
- package/dist/http/body.d.ts +4 -1
- package/dist/http/body.js +6 -3
- package/dist/http/event-bus.js +1 -0
- package/dist/http/event-types.d.ts +34 -2
- package/dist/http/event-types.js +1 -0
- package/dist/http/gate-audit.d.ts +12 -0
- package/dist/http/gate-audit.js +95 -0
- package/dist/http/gate-registry.d.ts +167 -0
- package/dist/http/gate-registry.js +163 -0
- package/dist/http/gate-routes.d.ts +48 -0
- package/dist/http/gate-routes.js +102 -0
- package/dist/http/ingest-registry.d.ts +30 -0
- package/dist/http/ingest-registry.js +108 -0
- package/dist/http/inner-loop-routes.d.ts +66 -0
- package/dist/http/inner-loop-routes.js +182 -0
- package/dist/http/inner-loop.d.ts +92 -0
- package/dist/http/inner-loop.js +155 -0
- package/dist/http/server.d.ts +38 -3
- package/dist/http/server.js +211 -6
- package/dist/http/snapshot.d.ts +6 -0
- package/dist/http/snapshot.js +6 -0
- package/dist/pi/cue-pump.d.ts +61 -0
- package/dist/pi/cue-pump.js +95 -0
- package/dist/pi/extension.d.ts +45 -0
- package/dist/pi/extension.js +407 -0
- package/dist/pi/gate-client.d.ts +54 -0
- package/dist/pi/gate-client.js +136 -0
- package/dist/pi/headless.d.ts +85 -0
- package/dist/pi/headless.js +224 -0
- package/dist/pi/index.d.ts +28 -0
- package/dist/pi/index.js +43 -0
- package/dist/pi/inner-loop-client.d.ts +67 -0
- package/dist/pi/inner-loop-client.js +164 -0
- package/dist/pi/inner-loop-publisher.d.ts +187 -0
- package/dist/pi/inner-loop-publisher.js +236 -0
- package/dist/pi/lazy-proxy.d.ts +37 -0
- package/dist/pi/lazy-proxy.js +55 -0
- package/dist/pi/mission-control/actions.d.ts +48 -0
- package/dist/pi/mission-control/actions.js +98 -0
- package/dist/pi/mission-control/board.d.ts +53 -0
- package/dist/pi/mission-control/board.js +104 -0
- package/dist/pi/mission-control/extension.d.ts +44 -0
- package/dist/pi/mission-control/extension.js +251 -0
- package/dist/pi/mission-control/index.d.ts +15 -0
- package/dist/pi/mission-control/index.js +32 -0
- package/dist/pi/mission-control/inner-tail.d.ts +48 -0
- package/dist/pi/mission-control/inner-tail.js +76 -0
- package/dist/pi/mission-control/pi-ui.d.ts +43 -0
- package/dist/pi/mission-control/pi-ui.js +10 -0
- package/dist/pi/mission-control/render.d.ts +6 -0
- package/dist/pi/mission-control/render.js +95 -0
- package/dist/pi/phase-driver.d.ts +74 -0
- package/dist/pi/phase-driver.js +122 -0
- package/dist/pi/pi-types.d.ts +208 -0
- package/dist/pi/pi-types.js +21 -0
- package/dist/pi/probe.d.ts +80 -0
- package/dist/pi/probe.js +154 -0
- package/dist/pi/render-tools.d.ts +17 -0
- package/dist/pi/render-tools.js +51 -0
- package/dist/pi/reset-pump.d.ts +47 -0
- package/dist/pi/reset-pump.js +85 -0
- package/dist/pi/tool-capability.d.ts +60 -0
- package/dist/pi/tool-capability.js +156 -0
- package/dist/pi/workflow-client.d.ts +158 -0
- package/dist/pi/workflow-client.js +289 -0
- package/dist/pi/zod-to-typebox.d.ts +74 -0
- package/dist/pi/zod-to-typebox.js +191 -0
- package/dist/server-tools.d.ts +2 -0
- package/dist/server-tools.js +50 -46
- package/dist/spawn.d.ts +55 -0
- package/dist/spawn.js +72 -0
- package/dist/tools/agent-types.d.ts +2 -2
- package/dist/tools/agent-types.js +22 -17
- package/dist/tools/attachment-info.d.ts +2 -2
- package/dist/tools/attachment-info.js +38 -33
- package/dist/tools/broadcast.d.ts +2 -2
- package/dist/tools/broadcast.js +69 -64
- package/dist/tools/cancel-stage.d.ts +2 -2
- package/dist/tools/cancel-stage.js +20 -15
- package/dist/tools/clear-state.d.ts +2 -2
- package/dist/tools/clear-state.js +25 -20
- package/dist/tools/coat-check-evict.d.ts +2 -2
- package/dist/tools/coat-check-evict.js +29 -24
- package/dist/tools/coat-check-get.d.ts +2 -2
- package/dist/tools/coat-check-get.js +38 -33
- package/dist/tools/coat-check-list.d.ts +2 -2
- package/dist/tools/coat-check-list.js +48 -43
- package/dist/tools/coat-check-put.d.ts +2 -2
- package/dist/tools/coat-check-put.js +38 -33
- package/dist/tools/cue.d.ts +2 -2
- package/dist/tools/cue.js +57 -52
- package/dist/tools/descriptor.d.ts +72 -0
- package/dist/tools/descriptor.js +39 -0
- package/dist/tools/destroy.d.ts +2 -2
- package/dist/tools/destroy.js +153 -148
- package/dist/tools/ensemble.d.ts +2 -2
- package/dist/tools/ensemble.js +71 -66
- package/dist/tools/evaluate-gate.d.ts +2 -2
- package/dist/tools/evaluate-gate.js +33 -27
- package/dist/tools/fetch-state.d.ts +2 -2
- package/dist/tools/fetch-state.js +42 -37
- package/dist/tools/gates.d.ts +2 -2
- package/dist/tools/gates.js +39 -34
- package/dist/tools/hosts.d.ts +2 -2
- package/dist/tools/hosts.js +25 -20
- package/dist/tools/listen.d.ts +2 -2
- package/dist/tools/listen.js +23 -18
- package/dist/tools/load-lineup.d.ts +2 -2
- package/dist/tools/load-lineup.js +324 -319
- package/dist/tools/migrate.d.ts +2 -2
- package/dist/tools/migrate.js +45 -40
- package/dist/tools/pause.d.ts +2 -2
- package/dist/tools/pause.js +34 -29
- package/dist/tools/play.d.ts +2 -2
- package/dist/tools/play.js +53 -48
- package/dist/tools/quality-gate.d.ts +2 -2
- package/dist/tools/quality-gate.js +26 -21
- package/dist/tools/recall.d.ts +2 -2
- package/dist/tools/recall.js +32 -27
- package/dist/tools/recruit.d.ts +2 -2
- package/dist/tools/recruit.js +325 -256
- package/dist/tools/release.d.ts +2 -2
- package/dist/tools/release.js +85 -80
- package/dist/tools/report.d.ts +2 -2
- package/dist/tools/report.js +28 -23
- package/dist/tools/reset.d.ts +3 -0
- package/dist/tools/reset.js +51 -0
- package/dist/tools/restart.d.ts +2 -2
- package/dist/tools/restart.js +51 -46
- package/dist/tools/restore.d.ts +2 -2
- package/dist/tools/restore.js +76 -71
- package/dist/tools/save-lineup.d.ts +2 -2
- package/dist/tools/save-lineup.js +32 -27
- package/dist/tools/save-state.d.ts +2 -2
- package/dist/tools/save-state.js +31 -26
- package/dist/tools/schedule.d.ts +2 -2
- package/dist/tools/schedule.js +133 -128
- package/dist/tools/schedules.d.ts +2 -2
- package/dist/tools/schedules.js +41 -36
- package/dist/tools/set-ensemble-description.d.ts +2 -2
- package/dist/tools/set-ensemble-description.js +26 -21
- package/dist/tools/set-name.d.ts +2 -2
- package/dist/tools/set-name.js +38 -33
- package/dist/tools/set-part.d.ts +2 -2
- package/dist/tools/set-part.js +20 -15
- package/dist/tools/shutdown.d.ts +2 -2
- package/dist/tools/shutdown.js +39 -34
- package/dist/tools/stage.d.ts +2 -2
- package/dist/tools/stage.js +28 -23
- package/dist/tools/stages.d.ts +2 -2
- package/dist/tools/stages.js +36 -31
- package/dist/tools/unschedule.d.ts +2 -2
- package/dist/tools/unschedule.js +30 -25
- package/dist/tools/who-am-i.d.ts +2 -2
- package/dist/tools/who-am-i.js +36 -31
- package/dist/tools/worktree.d.ts +2 -2
- package/dist/tools/worktree.js +134 -129
- package/dist/tui/index.js +6 -6
- package/dist/types.d.ts +47 -2
- package/dist/types.js +1 -1
- package/dist/utils/default-part.js +1 -0
- package/dist/utils/sdk-probe.d.ts +23 -0
- package/dist/utils/sdk-probe.js +46 -7
- package/dist/worker.d.ts +3 -1
- package/dist/worker.js +6 -2
- package/dist/workflows/session.js +70 -2
- package/dist/workflows/signals.d.ts +32 -2
- package/dist/workflows/signals.js +25 -2
- package/package.json +4 -1
- package/workflow-bundle.js +97 -6
- package/dashboard/dist/assets/index-D6Xyje_n.js.map +0 -1
- package/dist/tools/helpers.d.ts +0 -21
- package/dist/tools/helpers.js +0 -25
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hand-written structural slice of Pi's extension UI + command API that
|
|
4
|
+
* mission-control consumes (3f). Kept LOCAL (not in the shared `src/pi/pi-types.ts`)
|
|
5
|
+
* so this feature is self-contained and `tsc` stays green WITHOUT the optional
|
|
6
|
+
* `@earendil-works/pi-coding-agent` dep installed. Mirrors the installed SDK's
|
|
7
|
+
* `dist/core/extensions/types.d.ts` (verified 0.78.0): `ctx.ui.setWidget` /
|
|
8
|
+
* `select` / `confirm` / `input` / `notify`, `registerCommand`, `registerShortcut`.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type BoardModel } from './board';
|
|
2
|
+
/**
|
|
3
|
+
* Render the full board. Header + player rows (conductor first), then — when a
|
|
4
|
+
* player is selected — a fine inner-loop tail (last {@link TAIL_RENDER_LINES}).
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderBoard(model: BoardModel): string[];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderBoard = renderBoard;
|
|
4
|
+
const board_1 = require("./board");
|
|
5
|
+
/** How many recent fine-tail frames to show under the selected player. */
|
|
6
|
+
const TAIL_RENDER_LINES = 12;
|
|
7
|
+
/** Compact phase glyph — ASCII-safe for the TUI. */
|
|
8
|
+
function phaseGlyph(phase) {
|
|
9
|
+
switch (phase) {
|
|
10
|
+
case 'processing': return '*'; // working
|
|
11
|
+
case 'awaiting': return 'o'; // idle, attached
|
|
12
|
+
case 'attached': return '+';
|
|
13
|
+
case 'booting': return '.';
|
|
14
|
+
case 'draining': return '~';
|
|
15
|
+
case 'detached': return 'x';
|
|
16
|
+
case 'gone': return '#';
|
|
17
|
+
default: return '?';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function pct(contextPercent) {
|
|
21
|
+
if (contextPercent === undefined)
|
|
22
|
+
return '';
|
|
23
|
+
// contextPercent may be a 0..1 fraction or a 0..100 number — normalize to %.
|
|
24
|
+
const p = contextPercent <= 1 ? contextPercent * 100 : contextPercent;
|
|
25
|
+
return `${Math.round(p)}%`;
|
|
26
|
+
}
|
|
27
|
+
function renderRow(row, selected) {
|
|
28
|
+
const sel = selected ? '>' : ' ';
|
|
29
|
+
const glyph = phaseGlyph(row.phase);
|
|
30
|
+
const tool = row.currentTool ? `[${row.currentTool}]` : '';
|
|
31
|
+
const ctx = pct(row.contextPercent);
|
|
32
|
+
const part = row.part ? ` ${row.part}` : '';
|
|
33
|
+
// sel glyph id part tool ctx
|
|
34
|
+
return [`${sel}${glyph} ${row.playerId}`, part, tool, ctx]
|
|
35
|
+
.filter((s) => s !== '')
|
|
36
|
+
.join(' ')
|
|
37
|
+
.trimEnd();
|
|
38
|
+
}
|
|
39
|
+
/** One-line summary of a fine inner-loop frame for the tail. */
|
|
40
|
+
function renderInnerFrame(f) {
|
|
41
|
+
switch (f.type) {
|
|
42
|
+
case 'inner.thinking':
|
|
43
|
+
return ` ${f.kind === 'thinking' ? '~' : '"'} ${oneLine(f.delta, 80)}`;
|
|
44
|
+
case 'inner.tool_call':
|
|
45
|
+
return ` -> ${f.tool}(${oneLine(f.argsSummary, 60)})`;
|
|
46
|
+
case 'inner.tool_result':
|
|
47
|
+
return ` <- ${f.tool}${f.isError ? ' ERR' : ''}: ${oneLine(f.resultSummary, 60)}`;
|
|
48
|
+
case 'inner.token':
|
|
49
|
+
return ` · ctx ${f.contextTokens ?? '?'} tok${f.contextPercent !== undefined ? ` (${pct(f.contextPercent)})` : ''}`;
|
|
50
|
+
case 'inner.turn':
|
|
51
|
+
return ` -- turn ${f.phase} #${f.turnIndex}`;
|
|
52
|
+
case 'inner.gate_pending':
|
|
53
|
+
// requestId front-and-center — the operator types it into `/gate <requestId> allow|deny`.
|
|
54
|
+
return ` [GATE ${f.requestId} ${f.tool} (${f.classification}, ${Math.round(f.timeoutMs / 1000)}s)]`;
|
|
55
|
+
case 'inner.gate_resolved':
|
|
56
|
+
return ` [GATE ${f.requestId} -> ${f.decision}]`;
|
|
57
|
+
default:
|
|
58
|
+
return ' ·';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Collapse whitespace + truncate for a single tail line. */
|
|
62
|
+
function oneLine(s, max) {
|
|
63
|
+
const flat = s.replace(/\s+/g, ' ').trim();
|
|
64
|
+
return flat.length <= max ? flat : flat.slice(0, max - 1) + '…';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Render the full board. Header + player rows (conductor first), then — when a
|
|
68
|
+
* player is selected — a fine inner-loop tail (last {@link TAIL_RENDER_LINES}).
|
|
69
|
+
*/
|
|
70
|
+
function renderBoard(model) {
|
|
71
|
+
const ids = (0, board_1.sortedPlayerIds)(model);
|
|
72
|
+
const lines = [];
|
|
73
|
+
lines.push(`MISSION CONTROL · ${model.ensemble} · ${ids.length} player${ids.length === 1 ? '' : 's'}`);
|
|
74
|
+
if (ids.length === 0) {
|
|
75
|
+
lines.push(' (no players — waiting for the ensemble…)');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
for (const id of ids) {
|
|
79
|
+
const row = model.players.get(id);
|
|
80
|
+
lines.push(renderRow(row, id === model.selected));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (model.selected) {
|
|
84
|
+
lines.push(`── tail: ${model.selected} ──`);
|
|
85
|
+
const recent = model.innerTail.slice(-TAIL_RENDER_LINES);
|
|
86
|
+
if (recent.length === 0) {
|
|
87
|
+
lines.push(' (no inner-loop activity yet)');
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
for (const f of recent)
|
|
91
|
+
lines.push(renderInnerFrame(f));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return lines;
|
|
95
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi-event → attachment-phase state machine (PURE — no Temporal, no Pi imports).
|
|
3
|
+
*
|
|
4
|
+
* This is the de-risking core of the Phase 0 PoC and the primary unit-test
|
|
5
|
+
* target. It translates Pi lifecycle events into (a) the next attachment phase
|
|
6
|
+
* and (b) the workflow action the extension must perform to publish that phase
|
|
7
|
+
* through the EXISTING session-workflow wire surface (claimAttachment /
|
|
8
|
+
* processingStart / processingEnd / requestDetach + adapterExited).
|
|
9
|
+
*
|
|
10
|
+
* Architect's EXACT mapping (do not "improve" without an architect sign-off):
|
|
11
|
+
*
|
|
12
|
+
* session_start → claim → phase `attached`
|
|
13
|
+
* agent_start → processingStart → phase `processing`
|
|
14
|
+
* agent_end → processingEnd → phase `awaiting` (NOT detached!)
|
|
15
|
+
* session_shutdown → detach → phase `draining` (→ `detached`
|
|
16
|
+
* confirmed workflow-side after
|
|
17
|
+
* adapterExited; see note below)
|
|
18
|
+
* turn_start / turn_end → NONE → phase UNCHANGED, stamp activity
|
|
19
|
+
* tool_execution_start / _end → NONE → phase UNCHANGED, stamp activity
|
|
20
|
+
*
|
|
21
|
+
* CRITICAL INVARIANT: `turn_*` and `tool_execution_*` fire multiple times mid
|
|
22
|
+
* agent run. If they drove the phase, the session would oscillate
|
|
23
|
+
* processing↔awaiting many times per turn. They are ACTIVITY signals only.
|
|
24
|
+
*/
|
|
25
|
+
/** Attachment phases — mirrors `AttachmentPhase` in src/types.ts (kept local to stay import-free). */
|
|
26
|
+
export type PiPhase = 'booting' | 'attached' | 'processing' | 'awaiting' | 'draining' | 'detached' | 'gone';
|
|
27
|
+
/**
|
|
28
|
+
* The workflow-side action the extension should perform in response to an event.
|
|
29
|
+
* `none` means "no phase-affecting workflow call" (activity-only events).
|
|
30
|
+
*/
|
|
31
|
+
export type WorkflowAction = {
|
|
32
|
+
kind: 'claim';
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'processingStart';
|
|
35
|
+
messageId: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'processingEnd';
|
|
38
|
+
messageId: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'detach';
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'none';
|
|
43
|
+
};
|
|
44
|
+
export interface PhaseDriverResult {
|
|
45
|
+
/** The workflow call the extension must make (or `none`). */
|
|
46
|
+
action: WorkflowAction;
|
|
47
|
+
/** The phase AFTER applying this event (the driver's local view). */
|
|
48
|
+
phase: PiPhase;
|
|
49
|
+
/** True when this event bumped the last-activity timestamp. */
|
|
50
|
+
activityStamped: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Stateful (but side-effect-free) translator. One instance per attached Pi
|
|
54
|
+
* session. Re-instantiate on each `session_start` — never reuse across session
|
|
55
|
+
* switches (D11).
|
|
56
|
+
*/
|
|
57
|
+
export declare class PhaseDriver {
|
|
58
|
+
private _phase;
|
|
59
|
+
private _lastActivityAt;
|
|
60
|
+
/** The in-flight message id between agent_start and agent_end (idempotency). */
|
|
61
|
+
private _currentMessageId;
|
|
62
|
+
get phase(): PiPhase;
|
|
63
|
+
get lastActivityAt(): string | null;
|
|
64
|
+
/**
|
|
65
|
+
* Translate a Pi lifecycle event into a phase transition + workflow action.
|
|
66
|
+
*
|
|
67
|
+
* @param event Pi lifecycle event name.
|
|
68
|
+
* @param payload Optional `{ messageId }` carried by the event.
|
|
69
|
+
* @param now ISO timestamp injected by the caller (keeps this pure/testable).
|
|
70
|
+
*/
|
|
71
|
+
handle(event: string, payload: {
|
|
72
|
+
messageId?: string;
|
|
73
|
+
} | undefined, now: string): PhaseDriverResult;
|
|
74
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pi-event → attachment-phase state machine (PURE — no Temporal, no Pi imports).
|
|
4
|
+
*
|
|
5
|
+
* This is the de-risking core of the Phase 0 PoC and the primary unit-test
|
|
6
|
+
* target. It translates Pi lifecycle events into (a) the next attachment phase
|
|
7
|
+
* and (b) the workflow action the extension must perform to publish that phase
|
|
8
|
+
* through the EXISTING session-workflow wire surface (claimAttachment /
|
|
9
|
+
* processingStart / processingEnd / requestDetach + adapterExited).
|
|
10
|
+
*
|
|
11
|
+
* Architect's EXACT mapping (do not "improve" without an architect sign-off):
|
|
12
|
+
*
|
|
13
|
+
* session_start → claim → phase `attached`
|
|
14
|
+
* agent_start → processingStart → phase `processing`
|
|
15
|
+
* agent_end → processingEnd → phase `awaiting` (NOT detached!)
|
|
16
|
+
* session_shutdown → detach → phase `draining` (→ `detached`
|
|
17
|
+
* confirmed workflow-side after
|
|
18
|
+
* adapterExited; see note below)
|
|
19
|
+
* turn_start / turn_end → NONE → phase UNCHANGED, stamp activity
|
|
20
|
+
* tool_execution_start / _end → NONE → phase UNCHANGED, stamp activity
|
|
21
|
+
*
|
|
22
|
+
* CRITICAL INVARIANT: `turn_*` and `tool_execution_*` fire multiple times mid
|
|
23
|
+
* agent run. If they drove the phase, the session would oscillate
|
|
24
|
+
* processing↔awaiting many times per turn. They are ACTIVITY signals only.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.PhaseDriver = void 0;
|
|
28
|
+
/** Events that stamp last-activity but MUST NOT drive the phase. */
|
|
29
|
+
const ACTIVITY_ONLY_EVENTS = new Set([
|
|
30
|
+
'turn_start',
|
|
31
|
+
'turn_end',
|
|
32
|
+
'tool_execution_start',
|
|
33
|
+
'tool_execution_end',
|
|
34
|
+
]);
|
|
35
|
+
/**
|
|
36
|
+
* Stateful (but side-effect-free) translator. One instance per attached Pi
|
|
37
|
+
* session. Re-instantiate on each `session_start` — never reuse across session
|
|
38
|
+
* switches (D11).
|
|
39
|
+
*/
|
|
40
|
+
class PhaseDriver {
|
|
41
|
+
_phase = 'booting';
|
|
42
|
+
_lastActivityAt = null;
|
|
43
|
+
/** The in-flight message id between agent_start and agent_end (idempotency). */
|
|
44
|
+
_currentMessageId = null;
|
|
45
|
+
get phase() {
|
|
46
|
+
return this._phase;
|
|
47
|
+
}
|
|
48
|
+
get lastActivityAt() {
|
|
49
|
+
return this._lastActivityAt;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Translate a Pi lifecycle event into a phase transition + workflow action.
|
|
53
|
+
*
|
|
54
|
+
* @param event Pi lifecycle event name.
|
|
55
|
+
* @param payload Optional `{ messageId }` carried by the event.
|
|
56
|
+
* @param now ISO timestamp injected by the caller (keeps this pure/testable).
|
|
57
|
+
*/
|
|
58
|
+
handle(event, payload = {}, now) {
|
|
59
|
+
if (ACTIVITY_ONLY_EVENTS.has(event)) {
|
|
60
|
+
// Activity-only: stamp, but DO NOT touch the phase.
|
|
61
|
+
this._lastActivityAt = now;
|
|
62
|
+
return { action: { kind: 'none' }, phase: this._phase, activityStamped: true };
|
|
63
|
+
}
|
|
64
|
+
switch (event) {
|
|
65
|
+
case 'session_start': {
|
|
66
|
+
this._currentMessageId = null; // defensive: fresh attach has no in-flight turn.
|
|
67
|
+
this._phase = 'attached';
|
|
68
|
+
return { action: { kind: 'claim' }, phase: this._phase, activityStamped: false };
|
|
69
|
+
}
|
|
70
|
+
case 'agent_start': {
|
|
71
|
+
// processingStart requires a stable messageId for idempotency. Prefer the
|
|
72
|
+
// event-supplied id; fall back to a now-derived id when Pi omits one.
|
|
73
|
+
const messageId = payload.messageId ?? `pi-turn-${now}`;
|
|
74
|
+
this._currentMessageId = messageId;
|
|
75
|
+
this._phase = 'processing';
|
|
76
|
+
this._lastActivityAt = now;
|
|
77
|
+
return {
|
|
78
|
+
action: { kind: 'processingStart', messageId },
|
|
79
|
+
phase: this._phase,
|
|
80
|
+
activityStamped: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
case 'agent_end': {
|
|
84
|
+
// End the IN-FLIGHT turn. agent_end means the agent finished THIS run and
|
|
85
|
+
// is now waiting for input → `awaiting`, NOT `detached`.
|
|
86
|
+
//
|
|
87
|
+
// Hardening (P2-4): end with the id we STARTED the turn with — NOT the
|
|
88
|
+
// payload — so processingStart/End pair EXACTLY (the workflow keys its
|
|
89
|
+
// in-flight set on that id; a mismatched end would orphan the entry). And
|
|
90
|
+
// a spurious / duplicate agent_end with no turn in flight is INERT: it
|
|
91
|
+
// must not synthesize a bogus processingEnd nor flip the phase (which
|
|
92
|
+
// could mask a genuine `processing` state or spuriously leave `awaiting`).
|
|
93
|
+
const inFlightId = this._currentMessageId;
|
|
94
|
+
if (inFlightId === null) {
|
|
95
|
+
return { action: { kind: 'none' }, phase: this._phase, activityStamped: false };
|
|
96
|
+
}
|
|
97
|
+
this._currentMessageId = null;
|
|
98
|
+
this._phase = 'awaiting';
|
|
99
|
+
this._lastActivityAt = now;
|
|
100
|
+
return {
|
|
101
|
+
action: { kind: 'processingEnd', messageId: inFlightId },
|
|
102
|
+
phase: this._phase,
|
|
103
|
+
activityStamped: true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
case 'session_shutdown': {
|
|
107
|
+
// Graceful teardown: phase → draining; the extension performs
|
|
108
|
+
// requestDetach (→ draining) then adapterExited (→ detached). The
|
|
109
|
+
// `detached` collapse is authoritative WORKFLOW-side; the driver's
|
|
110
|
+
// local view rests at `draining` because the process is exiting.
|
|
111
|
+
// Clear any in-flight turn so a late stray event after shutdown is inert.
|
|
112
|
+
this._currentMessageId = null;
|
|
113
|
+
this._phase = 'draining';
|
|
114
|
+
return { action: { kind: 'detach' }, phase: this._phase, activityStamped: false };
|
|
115
|
+
}
|
|
116
|
+
default:
|
|
117
|
+
// Unknown / unhandled events are inert: no phase change, no activity stamp.
|
|
118
|
+
return { action: { kind: 'none' }, phase: this._phase, activityStamped: false };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.PhaseDriver = PhaseDriver;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local, hand-written structural declarations of the slice of Pi's
|
|
3
|
+
* `ExtensionAPI` surface that the agent-tempo Phase 0 PoC consumes.
|
|
4
|
+
*
|
|
5
|
+
* WHY HAND-WRITTEN (Phase 0 shortcut — see src/pi/README.md "Known limitations"):
|
|
6
|
+
* The real types live in `@earendil-works/pi-coding-agent`, which is an
|
|
7
|
+
* OPTIONAL dependency requiring Node 22.19+. Declaring these locally keeps
|
|
8
|
+
* `tsc` (root build + test build) green WITHOUT Pi installed, so the unit
|
|
9
|
+
* suite runs on any CI node. The cost is drift risk: if Pi's real
|
|
10
|
+
* `ExtensionAPI` changes, these decls won't catch it at compile time.
|
|
11
|
+
*
|
|
12
|
+
* Phase 1 should switch to importing the real Pi types at type-check time
|
|
13
|
+
* (Pi as a true optional/peer dep) so we get compile-time API-drift
|
|
14
|
+
* detection. These decls are a temporary stand-in, NOT the end state.
|
|
15
|
+
*
|
|
16
|
+
* Surface mirrored against `badlogic/pi-mono` @ 564ad70 (packages 0.78.0):
|
|
17
|
+
* - `core/extensions/types.ts` (ExtensionAPI, ToolDefinition, events)
|
|
18
|
+
* - `core/agent-session.ts` (sendCustomMessage / steer / followUp)
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Pi lifecycle events. Phase 0 only wires the lifecycle-relevant subset; the
|
|
22
|
+
* real `ExtensionAPI.on` accepts many more event names.
|
|
23
|
+
*
|
|
24
|
+
* IMPORTANT (architect's phase spec): `turn_start` / `turn_end` /
|
|
25
|
+
* `tool_execution_start` / `tool_execution_end` fire MULTIPLE times within a
|
|
26
|
+
* single agent run and therefore MUST NOT drive the attachment phase — they
|
|
27
|
+
* only stamp a last-activity timestamp. See `phase-driver.ts`.
|
|
28
|
+
*/
|
|
29
|
+
export type PiLifecycleEvent = 'session_start' | 'agent_start' | 'agent_end' | 'turn_start' | 'turn_end' | 'tool_call' | 'tool_execution_start' | 'tool_execution_end' | 'message_update' | 'session_shutdown';
|
|
30
|
+
/** Options for `sendCustomMessage` — D10 (FLIPPED): default `steer` + `triggerTurn`. */
|
|
31
|
+
export interface PiCustomMessageOptions {
|
|
32
|
+
/** Start a new agent turn after delivery. */
|
|
33
|
+
triggerTurn?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* `steer` — interrupt an in-flight turn and inject immediately (priority).
|
|
36
|
+
* `followUp` — queue behind the current turn.
|
|
37
|
+
*/
|
|
38
|
+
deliverAs?: 'steer' | 'followUp';
|
|
39
|
+
}
|
|
40
|
+
/** A message injected into a live Pi session. */
|
|
41
|
+
export interface PiOutboundMessage {
|
|
42
|
+
/** Free-form tag Pi surfaces to the agent (we use `'cue'`). */
|
|
43
|
+
customType?: string;
|
|
44
|
+
content: string;
|
|
45
|
+
/** Render the injected content in the human-visible transcript. */
|
|
46
|
+
display?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The live, human-attached agent session. `sendCustomMessage` is bound in the
|
|
50
|
+
* `AgentSession` constructor (no mode gate) — confirmed by the spike — so a
|
|
51
|
+
* cue can be injected into a running interactive session.
|
|
52
|
+
*/
|
|
53
|
+
export interface PiAgentSession {
|
|
54
|
+
/**
|
|
55
|
+
* Inject a message into the live session. Pi's `AgentSession` exposes this as
|
|
56
|
+
* `sendCustomMessage` (NOT `sendMessage` — verified against the installed SDK's
|
|
57
|
+
* `agent-session.d.ts` during the 3a live smoke). The earlier spike modeled it
|
|
58
|
+
* as `sendMessage`; the real method name is `sendCustomMessage`.
|
|
59
|
+
*/
|
|
60
|
+
sendCustomMessage(msg: PiOutboundMessage, opts?: PiCustomMessageOptions): void | Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* 3d D14 — wipe the conversation and start a FRESH context (no replay). The
|
|
63
|
+
* reset handler calls this on a clean-wipe reset. Optional in the slice (Pi
|
|
64
|
+
* provides it; older Pi / a fake session in tests may not).
|
|
65
|
+
*/
|
|
66
|
+
newSession?(): void | Promise<void>;
|
|
67
|
+
/** Pi's stable session identifier (reconciled with workflow metadata — D11). */
|
|
68
|
+
readonly id?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Payload delivered to a `pi.on(event, handler)` callback. Kept structural and
|
|
72
|
+
* open: Pi passes an event-specific object; we read only what Phase 0 needs and
|
|
73
|
+
* RE-ACQUIRE the session from each payload (never cache across switches — D11).
|
|
74
|
+
*/
|
|
75
|
+
export interface PiEventPayload {
|
|
76
|
+
/** The live session, present on most lifecycle events. */
|
|
77
|
+
session?: PiAgentSession;
|
|
78
|
+
/** A per-message/per-turn identifier when the event carries one. */
|
|
79
|
+
messageId?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Discriminator on `session_start` / `session_shutdown`:
|
|
82
|
+
* `{ new | resume | fork | reload | quit }`. Drives Option-C teardown — only
|
|
83
|
+
* `quit` detaches; switch reasons rebind. Unknown/missing → treated as a
|
|
84
|
+
* switch (no detach), so a future Pi value can't cause a flap.
|
|
85
|
+
*/
|
|
86
|
+
reason?: string;
|
|
87
|
+
/** Open for forward-compat with Pi event fields we don't consume yet. */
|
|
88
|
+
[key: string]: unknown;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Pi context-window usage — the PULL-only token signal (3c). There is NO token
|
|
92
|
+
* event in Pi 0.78; usage is queried on demand via `ExtensionContext.getContextUsage()`.
|
|
93
|
+
* Mirrors pi-ai's `ContextUsage` (verified against the installed 0.78 `.d.ts`).
|
|
94
|
+
*/
|
|
95
|
+
export interface PiContextUsage {
|
|
96
|
+
/** Estimated context tokens in use, or `null` when unknown. */
|
|
97
|
+
tokens: number | null;
|
|
98
|
+
/** Model context-window size. */
|
|
99
|
+
contextWindow: number;
|
|
100
|
+
/** Usage as a fraction/percent of the window, or `null` when unknown. */
|
|
101
|
+
percent: number | null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Second argument Pi passes to every `pi.on(event, handler)` callback
|
|
105
|
+
* (`ExtensionHandler<E,R> = (event, ctx) => …`). Minimal structural slice — only
|
|
106
|
+
* the members 3c consumes. `getContextUsage()` is the sole source of token/usage
|
|
107
|
+
* data (pull-only — see {@link PiContextUsage}).
|
|
108
|
+
*/
|
|
109
|
+
export interface PiExtensionContext {
|
|
110
|
+
getContextUsage(): PiContextUsage | undefined;
|
|
111
|
+
isIdle(): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* 3d — the in-flight turn's AbortSignal (Esc / cancel). Pi passes it to handlers
|
|
114
|
+
* (the shipped `permission-gate.ts` precedent threads it into an awaited gate);
|
|
115
|
+
* the operator-gate await uses it to cancel a pending poll so a cancelled turn
|
|
116
|
+
* never hangs on an unresolved decision (Pi #2381). Optional — absent on older
|
|
117
|
+
* Pi / non-turn handlers.
|
|
118
|
+
*/
|
|
119
|
+
signal?: AbortSignal;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Handlers may take an optional second `ctx` arg (3c — needed for
|
|
123
|
+
* `getContextUsage()`). Optional so existing one-arg handlers (Phase 0-2) still
|
|
124
|
+
* satisfy the type — widening is additive/backward-compatible.
|
|
125
|
+
*/
|
|
126
|
+
export type PiEventHandler = (payload: PiEventPayload, ctx?: PiExtensionContext) => void | Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* One streaming delta inside a `message_update` event's `assistantMessageEvent`
|
|
129
|
+
* (a discriminated union in pi-ai). 3c forwards `thinking_delta` / `text_delta`
|
|
130
|
+
* as `inner.thinking` frames; `.delta` is the incremental string.
|
|
131
|
+
*/
|
|
132
|
+
export interface PiAssistantDelta {
|
|
133
|
+
/** `'text_delta'` | `'thinking_delta'` | `'toolcall_delta'` | … */
|
|
134
|
+
type?: string;
|
|
135
|
+
/** Orders multi-block content within a turn. */
|
|
136
|
+
contentIndex?: number;
|
|
137
|
+
/** The incremental text fragment. */
|
|
138
|
+
delta?: string;
|
|
139
|
+
/** Cumulative `AssistantMessage` so far (unused by 3c). */
|
|
140
|
+
partial?: unknown;
|
|
141
|
+
}
|
|
142
|
+
/** `message_update` payload — carries the streaming `assistantMessageEvent`. */
|
|
143
|
+
export interface PiMessageUpdatePayload extends PiEventPayload {
|
|
144
|
+
assistantMessageEvent?: PiAssistantDelta;
|
|
145
|
+
}
|
|
146
|
+
/** `tool_execution_start` payload. `toolName` is a bare string (NOT a literal union). */
|
|
147
|
+
export interface PiToolExecutionStartPayload extends PiEventPayload {
|
|
148
|
+
toolCallId?: string;
|
|
149
|
+
toolName?: string;
|
|
150
|
+
args?: unknown;
|
|
151
|
+
}
|
|
152
|
+
/** `tool_execution_end` payload — structured result + error flag. */
|
|
153
|
+
export interface PiToolExecutionEndPayload extends PiEventPayload {
|
|
154
|
+
toolCallId?: string;
|
|
155
|
+
toolName?: string;
|
|
156
|
+
result?: unknown;
|
|
157
|
+
isError?: boolean;
|
|
158
|
+
}
|
|
159
|
+
/** `turn_start` / `turn_end` payload. No phase field, no token counts (3c finding). */
|
|
160
|
+
export interface PiTurnPayload extends PiEventPayload {
|
|
161
|
+
turnIndex?: number;
|
|
162
|
+
timestamp?: number;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* `tool_call` pre-execution event — Pi fires this before running a tool, letting
|
|
166
|
+
* an extension allow/deny it. `toolName` is Pi's built-in or registered tool id
|
|
167
|
+
* (`bash` | `read` | `edit` | `write` | `grep` | …). The MD-C headless gate reads
|
|
168
|
+
* it to hard-block the shell/exec class at `toolAccess='restricted'`.
|
|
169
|
+
*/
|
|
170
|
+
export interface PiToolCallEvent {
|
|
171
|
+
type?: 'tool_call';
|
|
172
|
+
toolCallId?: string;
|
|
173
|
+
toolName: string;
|
|
174
|
+
input?: Record<string, unknown>;
|
|
175
|
+
}
|
|
176
|
+
/** Result of a `tool_call` handler: `block:true` denies the tool (with a reason). */
|
|
177
|
+
export interface PiToolCallResult {
|
|
178
|
+
block?: boolean;
|
|
179
|
+
reason?: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Pi tool result (`AgentToolResult`). The exact streaming shape is UNCONFIRMED
|
|
183
|
+
* (spike gap D12b) — Phase 0 uses the minimal `{ output, isError }` form, which
|
|
184
|
+
* is sufficient for a non-streaming tool like `report`.
|
|
185
|
+
*/
|
|
186
|
+
export interface PiToolResult {
|
|
187
|
+
output?: string;
|
|
188
|
+
isError?: boolean;
|
|
189
|
+
[key: string]: unknown;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Pi native tool definition. `parameters` is a TypeBox schema (NOT zod) — see
|
|
193
|
+
* `render-tools.ts` (derives it from the zod descriptor via the converter).
|
|
194
|
+
*/
|
|
195
|
+
export interface PiToolDefinition {
|
|
196
|
+
name: string;
|
|
197
|
+
description?: string;
|
|
198
|
+
/** TypeBox schema object. Typed `unknown` to avoid coupling pi-types to typebox. */
|
|
199
|
+
parameters: unknown;
|
|
200
|
+
execute: (args: Record<string, unknown>) => Promise<PiToolResult> | PiToolResult;
|
|
201
|
+
}
|
|
202
|
+
/** The `pi` object passed to `export default function(pi: ExtensionAPI) {}`. */
|
|
203
|
+
export interface ExtensionAPI {
|
|
204
|
+
on(event: PiLifecycleEvent | string, handler: PiEventHandler): void;
|
|
205
|
+
registerTool(def: PiToolDefinition): void;
|
|
206
|
+
}
|
|
207
|
+
/** An extension is a default-exported function receiving the `ExtensionAPI`. */
|
|
208
|
+
export type PiExtension = (pi: ExtensionAPI) => void | Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Local, hand-written structural declarations of the slice of Pi's
|
|
4
|
+
* `ExtensionAPI` surface that the agent-tempo Phase 0 PoC consumes.
|
|
5
|
+
*
|
|
6
|
+
* WHY HAND-WRITTEN (Phase 0 shortcut — see src/pi/README.md "Known limitations"):
|
|
7
|
+
* The real types live in `@earendil-works/pi-coding-agent`, which is an
|
|
8
|
+
* OPTIONAL dependency requiring Node 22.19+. Declaring these locally keeps
|
|
9
|
+
* `tsc` (root build + test build) green WITHOUT Pi installed, so the unit
|
|
10
|
+
* suite runs on any CI node. The cost is drift risk: if Pi's real
|
|
11
|
+
* `ExtensionAPI` changes, these decls won't catch it at compile time.
|
|
12
|
+
*
|
|
13
|
+
* Phase 1 should switch to importing the real Pi types at type-check time
|
|
14
|
+
* (Pi as a true optional/peer dep) so we get compile-time API-drift
|
|
15
|
+
* detection. These decls are a temporary stand-in, NOT the end state.
|
|
16
|
+
*
|
|
17
|
+
* Surface mirrored against `badlogic/pi-mono` @ 564ad70 (packages 0.78.0):
|
|
18
|
+
* - `core/extensions/types.ts` (ExtensionAPI, ToolDefinition, events)
|
|
19
|
+
* - `core/agent-session.ts` (sendCustomMessage / steer / followUp)
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Canonical Pi package (the npm `@mariozechner/...` name is an alias). */
|
|
2
|
+
export declare const PI_PACKAGE = "@earendil-works/pi-coding-agent";
|
|
3
|
+
/** Pi's model/typing helpers (`StringEnum`, providers). */
|
|
4
|
+
export declare const PI_AI_PACKAGE = "@earendil-works/pi-ai";
|
|
5
|
+
/** Tested-pinned Pi version — drift is a maintainer decision (D6). */
|
|
6
|
+
export declare const TESTED_PI_VERSION = "~0.78";
|
|
7
|
+
/**
|
|
8
|
+
* Minimum Pi version the integration requires. 0.78.0 covers the Pi fixes the
|
|
9
|
+
* cue-pump + headless paths rely on (#2860 / #5080 / #5115). Bumping this is a
|
|
10
|
+
* D6 maintainer decision; the headless/Copilot pre-flight hard-fails below it.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PI_VERSION_FLOOR = "0.78.0";
|
|
13
|
+
/** Node floor imposed by the Pi packages (NOT by typebox or agent-tempo core). */
|
|
14
|
+
export declare const PI_NODE_FLOOR = "22.19.0";
|
|
15
|
+
export interface PiProbeResult {
|
|
16
|
+
available: boolean;
|
|
17
|
+
/** Human-readable, actionable reason when unavailable. */
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check whether the Pi runtime packages are installed. Returns a structured
|
|
22
|
+
* result so callers choose whether to warn or hard-fail (the extension warns;
|
|
23
|
+
* a headless spawner would hard-fail).
|
|
24
|
+
*/
|
|
25
|
+
export declare function probePi(): PiProbeResult;
|
|
26
|
+
/**
|
|
27
|
+
* Pure semver FLOOR check: is `installed` >= `floor`? Compares major, then
|
|
28
|
+
* minor, then patch (a missing patch defaults to 0). Any pre-release/build
|
|
29
|
+
* suffix (`-beta`, `+sha`) is ignored — a pre-release of a version at/above the
|
|
30
|
+
* floor counts as meeting it. An unparseable `installed` returns `false`
|
|
31
|
+
* (conservative: unknown version is treated as below the floor).
|
|
32
|
+
*/
|
|
33
|
+
export declare function meetsVersionFloor(installed: string, floor?: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Injectable collaborators for {@link probeCopilotPiPreflight}. All default to
|
|
36
|
+
* real implementations; tests override them to exercise each branch without a
|
|
37
|
+
* live Pi install or real `~/.pi` / env.
|
|
38
|
+
*/
|
|
39
|
+
export interface CopilotPiPreflightDeps {
|
|
40
|
+
/** Whether a package is installed. Default: filesystem-walk {@link probeSdkInstall}. */
|
|
41
|
+
isInstalled?: (pkg: string) => boolean;
|
|
42
|
+
/** Installed version of a package, or null. Default: {@link readSdkPackageVersion}. */
|
|
43
|
+
installedVersion?: (pkg: string) => string | null;
|
|
44
|
+
/** Environment to read `COPILOT_GITHUB_TOKEN` from. Default: `process.env`. */
|
|
45
|
+
env?: NodeJS.ProcessEnv;
|
|
46
|
+
/** Whether the mounted `~/.pi/agent/auth.json` exists. Default: real `existsSync`. */
|
|
47
|
+
authFileExists?: () => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The parsed recruit selector to validate against Pi's model index (the
|
|
50
|
+
* `{ provider, model }` from {@link parsePiProviderModel}). Omit it — e.g. no
|
|
51
|
+
* `model` recruit arg, the Pi-default path — to SKIP the model-index gate.
|
|
52
|
+
*/
|
|
53
|
+
requestedModel?: {
|
|
54
|
+
provider: string;
|
|
55
|
+
model: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a `(provider, modelName)` against Pi's build-time model index,
|
|
59
|
+
* returning a truthy Model when indexed and `undefined` when not (the
|
|
60
|
+
* empirically-confirmed contract of pi-ai's `getModel`). The recruit caller
|
|
61
|
+
* (A4) injects pi-ai's `getModel` here — B2 deliberately does NOT import the
|
|
62
|
+
* ESM-only `@earendil-works/pi-ai` from the CJS recruit context. Omit it to
|
|
63
|
+
* SKIP the model-index gate (e.g. unit tests, or callers without pi-ai); the
|
|
64
|
+
* A4 `getModel` backstop then covers an unindexed model.
|
|
65
|
+
*/
|
|
66
|
+
resolveModel?: (provider: string, modelName: string) => unknown;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Hard pre-flight for recruiting a Copilot-backed Pi player (headless). Three
|
|
70
|
+
* gates, each with an actionable, `force: true`-bypassable error:
|
|
71
|
+
* 1. The Pi optional deps (`@earendil-works/pi-coding-agent` + `pi-ai`) are
|
|
72
|
+
* installed.
|
|
73
|
+
* 2. The Pi SDK version meets {@link PI_VERSION_FLOOR}.
|
|
74
|
+
* 3. Copilot auth is present — either `COPILOT_GITHUB_TOKEN` in the env or a
|
|
75
|
+
* mounted `~/.pi/agent/auth.json`.
|
|
76
|
+
*
|
|
77
|
+
* Mirrors the opencode / claude-code-headless recruit pre-flights. Returns a
|
|
78
|
+
* {@link PiProbeResult} so the caller (recruit) chooses warn vs hard-fail.
|
|
79
|
+
*/
|
|
80
|
+
export declare function probeCopilotPiPreflight(deps?: CopilotPiPreflightDeps): PiProbeResult;
|