@tt-a1i/openpi 0.1.1 → 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/README.md +37 -22
- package/SETUP.md +8 -6
- package/extensions/ask-user/handoff.ts +5 -1
- package/extensions/ask-user/index.ts +44 -0
- package/extensions/background-terminals/index.ts +118 -29
- package/extensions/background-terminals/src/domain.ts +5 -1
- package/extensions/background-terminals/src/manager.ts +2 -1
- package/extensions/background-terminals/src/prompt.ts +35 -0
- package/extensions/background-terminals/src/result-delivery.ts +76 -3
- package/extensions/background-terminals/src/ui/tool-result.ts +52 -1
- package/extensions/capabilities/index.ts +198 -0
- package/extensions/context-pivot/index.ts +21 -0
- package/extensions/cron/index.ts +42 -15
- package/extensions/execution-convergence/active-evidence.ts +129 -0
- package/extensions/execution-convergence/index.ts +442 -0
- package/extensions/execution-convergence/workspace-provenance.ts +338 -0
- package/extensions/file-search/index.ts +8 -1
- package/extensions/file-search/src/binaries.ts +2 -1
- package/extensions/git-info/src/runtime.ts +1 -1
- package/extensions/goal/controller.ts +2 -1
- package/extensions/goal/index.ts +20 -1
- package/extensions/plan-mode/index.ts +12 -0
- package/extensions/setup/index.ts +93 -7
- package/extensions/shared/child-session.ts +40 -4
- package/extensions/shared/setup-config.ts +22 -0
- package/extensions/shared/setup-episode-state.ts +7 -0
- package/extensions/shared/tool-surface.ts +435 -0
- package/extensions/subagents/index.ts +15 -0
- package/extensions/subagents/src/manager.ts +13 -11
- package/extensions/subagents/src/prompt.ts +1 -1
- package/extensions/tasks/index.ts +39 -12
- package/extensions/ui-customization/footer.ts +6 -1
- package/extensions/workflows/graph-projection.ts +6 -4
- package/extensions/workflows/index.ts +16 -1
- package/extensions/workflows/invocation-ledger.ts +8 -2
- package/extensions/workflows/model.ts +5 -1
- package/extensions/workflows/prompt.ts +10 -40
- package/extensions/workflows/replay-safety.ts +9 -8
- package/package.json +10 -10
- package/skills/subagents/SKILL.md +6 -0
- package/skills/workflows/EXAMPLES.md +58 -0
- package/skills/workflows/REFERENCE.md +44 -0
- package/skills/workflows/SKILL.md +39 -0
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
type SubagentRoleModels,
|
|
11
11
|
} from "../shared/subagent-roles.ts";
|
|
12
12
|
import { sanitizeTerminalText } from "../shared/terminal-text.ts";
|
|
13
|
+
import {
|
|
14
|
+
OPENPI_SETUP_EPISODE_CHANNEL,
|
|
15
|
+
type OpenPiSetupEpisodeState,
|
|
16
|
+
} from "../shared/setup-episode-state.ts";
|
|
17
|
+
import { patchOwnedTools } from "../shared/tool-surface.ts";
|
|
13
18
|
import {
|
|
14
19
|
formatPiIntercomStatus,
|
|
15
20
|
inspectPiIntercom,
|
|
@@ -18,6 +23,7 @@ import {
|
|
|
18
23
|
} from "./intercom.ts";
|
|
19
24
|
import {
|
|
20
25
|
applyFooterConfig,
|
|
26
|
+
CAPABILITY_DISCOVERY_MODES,
|
|
21
27
|
DETAIL_DISPLAYS,
|
|
22
28
|
FOOTER_ITEMS,
|
|
23
29
|
FOOTER_LAYOUT_ITEMS,
|
|
@@ -33,6 +39,7 @@ import {
|
|
|
33
39
|
REASONING_LEVELS,
|
|
34
40
|
SETUP_CONFIG_CHANGED_CHANNEL,
|
|
35
41
|
type FooterLayoutItem,
|
|
42
|
+
type CapabilityDiscoveryMode,
|
|
36
43
|
type FooterPreset,
|
|
37
44
|
type FooterStyle,
|
|
38
45
|
type MyPiSetupConfig,
|
|
@@ -102,12 +109,12 @@ export function buildInteractiveSetupPrompt(options: {
|
|
|
102
109
|
}) {
|
|
103
110
|
const configurationState = options.savedConfigExists
|
|
104
111
|
? [
|
|
105
|
-
"This package has already been configured. Explain the current settings in the user's language, then ask whether they want to keep them or change Next-action suggestions, Workflow limits, UI/Footer, result detail display, Post-edit, Agent role models, or review everything.",
|
|
112
|
+
"This package has already been configured. Explain the current settings in the user's language, then ask whether they want to keep them or change Capability discovery, Next-action suggestions, Workflow limits, UI/Footer, result detail display, Post-edit, Agent role models, or review everything.",
|
|
106
113
|
"If the user keeps the current settings, do not call configure_my_pi_setup. If they choose a category, ask only the follow-up needed for that category.",
|
|
107
114
|
]
|
|
108
115
|
: [
|
|
109
116
|
"This is the first setup. Explain the available choices and their impact in the user's language, then collect the initial preferences.",
|
|
110
|
-
"Prefer one ask_user call with up to three independent questions covering Next-action suggestions,
|
|
117
|
+
"Prefer one ask_user call with up to three independent questions covering Capability discovery plus Workflow limits, Next-action suggestions, and UI/Footer/result display. Explain that Post-edit defaults off; keep it off unless the user opts in, then ask only for the command. Explain that built-in Agent roles used by subagent_spawn and workflow agent_type inherit the parent model unless the user assigns an available model to a role.",
|
|
111
118
|
];
|
|
112
119
|
|
|
113
120
|
return [
|
|
@@ -122,6 +129,7 @@ export function buildInteractiveSetupPrompt(options: {
|
|
|
122
129
|
...configurationState,
|
|
123
130
|
"",
|
|
124
131
|
"Before asking, briefly explain what can be configured and the practical impact:",
|
|
132
|
+
"- Capability discovery: explicit is the safe default and keeps OpenPI model tools absent until the user asks for a capability. adaptive is opt-in and keeps only the small openpi_load_tools gateway visible, allowing the model to load Subagents, Workflows, background terminals, structured search, or Session tracking when it judges them useful. Loaded groups remain session-stable, and normal permission, concurrency, and workflow limits still apply.",
|
|
125
133
|
"- Next-action suggestions: disabled, or model-generated after a fully settled main-agent run. A suggestion appears as dim inline text on the first row of an empty editor; reserved cells at the row end keep CJK IME preedit from overwriting it. Right accepts it without submitting, and any other editor input dismisses it. Enabling requires an available provider/model and reasoning level and adds one small model call per settled run.",
|
|
126
134
|
"- Workflow fan-out: concurrency controls simultaneous agents and resource pressure; max agent calls controls the total capacity of one workflow. Valid ranges are 1-64 and 1-1024.",
|
|
127
135
|
"- UI: the large header costs vertical space; the custom footer is a declarative dashboard. Presets: powerline (default one-line ANSI256 blocks), powerline-mono (one-line high-contrast gray powerline), and compact (one-line plain text). Style can also be set independently: plain, powerline, powerline-mono. Custom lines are a 2D layout of cwd/model/thinking/context/cache/cost/throughput/git/pr plus at most one flex per line for left/right alignment. Nerd Font only affects powerline separator glyphs; text stays readable without it. Changes apply immediately in the active TUI session.",
|
|
@@ -132,6 +140,8 @@ export function buildInteractiveSetupPrompt(options: {
|
|
|
132
140
|
"- Intercom: optional cross-session messaging is installed only after a native setup confirmation. It stays parent-only; Direct/Workflow children and Replay cannot use it. The status above is informational for this model-guided step—do not install packages or edit its config yourself.",
|
|
133
141
|
"",
|
|
134
142
|
"Natural-language configuration examples the user might ask for:",
|
|
143
|
+
'- "let the model discover OpenPI capabilities when useful" → capability_discovery=adaptive',
|
|
144
|
+
'- "only use OpenPI capabilities when I ask" → capability_discovery=explicit',
|
|
135
145
|
'- "switch footer to powerline" → ui_footer_preset=powerline',
|
|
136
146
|
'- "use mono powerline" → ui_footer_preset=powerline-mono',
|
|
137
147
|
'- "compact footer" → ui_footer_preset=compact',
|
|
@@ -151,6 +161,16 @@ const safeSetupNotice = (value: unknown, maximum = 500) =>
|
|
|
151
161
|
.trim()
|
|
152
162
|
.slice(0, maximum);
|
|
153
163
|
|
|
164
|
+
export function buildSetupSuccessText(
|
|
165
|
+
currentConfiguration: string,
|
|
166
|
+
normalizationNote = "",
|
|
167
|
+
) {
|
|
168
|
+
return [
|
|
169
|
+
`Updated OpenPI setup. ${currentConfiguration}${normalizationNote}`,
|
|
170
|
+
"This setup episode is complete; configure_my_pi_setup is now hidden. Do not call it again. Do not edit configuration files directly. If the user requests another configuration change, tell them to run /openpi-setup <request> to start a new setup episode.",
|
|
171
|
+
].join(" ");
|
|
172
|
+
}
|
|
173
|
+
|
|
154
174
|
export function shouldOfferPiIntercom(options: {
|
|
155
175
|
readonly request: string;
|
|
156
176
|
readonly status: PiIntercomStatus;
|
|
@@ -231,13 +251,72 @@ async function maybeOfferPiIntercom(
|
|
|
231
251
|
}
|
|
232
252
|
}
|
|
233
253
|
|
|
254
|
+
export const CONFIGURE_MY_PI_SETUP_TOOL_NAME = "configure_my_pi_setup";
|
|
255
|
+
|
|
256
|
+
type SetupEpisode = "idle" | "armed" | "active";
|
|
257
|
+
|
|
258
|
+
function showConfigureTool(pi: ExtensionAPI) {
|
|
259
|
+
patchOwnedTools(pi, "setup", {
|
|
260
|
+
enable: [CONFIGURE_MY_PI_SETUP_TOOL_NAME],
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function hideConfigureTool(pi: ExtensionAPI) {
|
|
265
|
+
patchOwnedTools(pi, "setup", {
|
|
266
|
+
disable: [CONFIGURE_MY_PI_SETUP_TOOL_NAME],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
234
270
|
export default function openPiSetup(pi: ExtensionAPI) {
|
|
271
|
+
let episode: SetupEpisode = "idle";
|
|
272
|
+
const publishEpisode = () =>
|
|
273
|
+
pi.events.emit(OPENPI_SETUP_EPISODE_CHANNEL, {
|
|
274
|
+
active: episode !== "idle",
|
|
275
|
+
} satisfies OpenPiSetupEpisodeState);
|
|
276
|
+
|
|
277
|
+
const endEpisode = () => {
|
|
278
|
+
episode = "idle";
|
|
279
|
+
hideConfigureTool(pi);
|
|
280
|
+
publishEpisode();
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
pi.on("session_start", () => {
|
|
284
|
+
endEpisode();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
pi.on("agent_start", () => {
|
|
288
|
+
if (episode === "armed") {
|
|
289
|
+
episode = "active";
|
|
290
|
+
publishEpisode();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
pi.on("tool_execution_end", (event) => {
|
|
295
|
+
if (
|
|
296
|
+
episode === "active" &&
|
|
297
|
+
event.toolName === CONFIGURE_MY_PI_SETUP_TOOL_NAME &&
|
|
298
|
+
!event.isError
|
|
299
|
+
) {
|
|
300
|
+
endEpisode();
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
pi.on("agent_settled", () => {
|
|
305
|
+
if (episode === "active") endEpisode();
|
|
306
|
+
});
|
|
307
|
+
|
|
235
308
|
pi.registerTool({
|
|
236
309
|
name: "configure_my_pi_setup",
|
|
237
310
|
label: "Configure OpenPI",
|
|
238
311
|
description:
|
|
239
|
-
"Apply a user-requested configuration change for this Pi setup. Configures next-action suggestions, workflow fan-out, UI/Footer (presets, style, multi-line layout), result detail display, optional Post-edit, and built-in Agent-role model assignments shared by subagent_spawn and workflow agent_type. Role models must be available in the Pi registry; null clears a role back to parent-model inheritance. Footer examples: powerline preset, powerline-mono, compact, or custom ui_footer_lines with flex. Preserve current values for settings the user did not ask to change. Changes apply immediately to
|
|
312
|
+
"Apply a user-requested configuration change for this Pi setup. Configures capability discovery (explicit or opt-in adaptive), next-action suggestions, workflow fan-out, UI/Footer (presets, style, multi-line layout), result detail display, optional Post-edit, and built-in Agent-role model assignments shared by subagent_spawn and workflow agent_type. Role models must be available in the Pi registry; null clears a role back to parent-model inheritance. Footer examples: powerline preset, powerline-mono, compact, or custom ui_footer_lines with flex. Preserve current values for settings the user did not ask to change. Changes apply immediately to the capability gateway and active TUI footer.",
|
|
240
313
|
parameters: Type.Object({
|
|
314
|
+
capability_discovery: Type.Optional(
|
|
315
|
+
StringEnum(CAPABILITY_DISCOVERY_MODES, {
|
|
316
|
+
description:
|
|
317
|
+
"Capability adoption policy. explicit keeps OpenPI tools absent until the user asks for a capability; adaptive keeps only the small openpi_load_tools gateway visible so the model may load a useful group on its own. Adaptive can start expensive work such as Subagents or Workflows, so it is opt-in. Omit to preserve the current value.",
|
|
318
|
+
}),
|
|
319
|
+
),
|
|
241
320
|
suggestions_enabled: Type.Optional(
|
|
242
321
|
Type.Boolean({
|
|
243
322
|
description:
|
|
@@ -406,6 +485,12 @@ export default function openPiSetup(pi: ExtensionAPI) {
|
|
|
406
485
|
);
|
|
407
486
|
|
|
408
487
|
const config: MyPiSetupConfig = {
|
|
488
|
+
capabilities: {
|
|
489
|
+
discovery:
|
|
490
|
+
(params.capability_discovery as
|
|
491
|
+
| CapabilityDiscoveryMode
|
|
492
|
+
| undefined) ?? current.capabilities.discovery,
|
|
493
|
+
},
|
|
409
494
|
suggestions: {
|
|
410
495
|
enabled: suggestionsEnabled,
|
|
411
496
|
...(model ? { model } : {}),
|
|
@@ -457,9 +542,7 @@ export default function openPiSetup(pi: ExtensionAPI) {
|
|
|
457
542
|
: "";
|
|
458
543
|
if (ctx.hasUI) ctx.ui.notify(`${text}${note}`, "info");
|
|
459
544
|
return {
|
|
460
|
-
content: [
|
|
461
|
-
{ type: "text", text: `Updated OpenPI setup. ${text}${note}` },
|
|
462
|
-
],
|
|
545
|
+
content: [{ type: "text", text: buildSetupSuccessText(text, note) }],
|
|
463
546
|
details: config,
|
|
464
547
|
};
|
|
465
548
|
},
|
|
@@ -490,7 +573,7 @@ export default function openPiSetup(pi: ExtensionAPI) {
|
|
|
490
573
|
"Current configuration:",
|
|
491
574
|
currentConfiguration,
|
|
492
575
|
"",
|
|
493
|
-
"Footer tips: presets are powerline, powerline-mono, compact; style is plain/powerline/powerline-mono; custom layouts use ui_footer_lines (2D enum arrays with optional flex). Do not use ui_footer_items together with ui_footer_lines. Built-in Agent role models (explorer, implementer, reviewer, advisor) are shared by subagent_spawn and workflow agent_type; they inherit the parent unless assigned an available registry model, and clearing an assignment restores inheritance. Custom agent-type files still override built-in role definitions. Nerd Font only affects powerline separator glyphs. Changes apply immediately in the active TUI session. Intercom installation is handled only by the native setup confirmation; do not install packages or edit its config yourself.",
|
|
576
|
+
"Capability discovery is explicit by default; adaptive is an opt-in that keeps only openpi_load_tools visible so the model may load useful groups. Footer tips: presets are powerline, powerline-mono, compact; style is plain/powerline/powerline-mono; custom layouts use ui_footer_lines (2D enum arrays with optional flex). Do not use ui_footer_items together with ui_footer_lines. Built-in Agent role models (explorer, implementer, reviewer, advisor) are shared by subagent_spawn and workflow agent_type; they inherit the parent unless assigned an available registry model, and clearing an assignment restores inheritance. Custom agent-type files still override built-in role definitions. Nerd Font only affects powerline separator glyphs. Changes apply immediately in the active TUI session. Intercom installation is handled only by the native setup confirmation; do not install packages or edit its config yourself.",
|
|
494
577
|
"",
|
|
495
578
|
"Use configure_my_pi_setup to apply only the requested OpenPI-owned changes and preserve everything else. Interpret model names from the available Pi registry. Do not edit configuration files directly.",
|
|
496
579
|
]
|
|
@@ -501,6 +584,9 @@ export default function openPiSetup(pi: ExtensionAPI) {
|
|
|
501
584
|
savedConfigExists,
|
|
502
585
|
});
|
|
503
586
|
|
|
587
|
+
episode = "armed";
|
|
588
|
+
showConfigureTool(pi);
|
|
589
|
+
publishEpisode();
|
|
504
590
|
pi.sendUserMessage(
|
|
505
591
|
prompt.join("\n"),
|
|
506
592
|
ctx.isIdle() ? undefined : { deliverAs: "followUp" },
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import {
|
|
3
|
+
type AgentSession,
|
|
3
4
|
DefaultResourceLoader,
|
|
4
5
|
getAgentDir,
|
|
5
6
|
ProjectTrustStore,
|
|
6
|
-
SettingsManager,
|
|
7
|
-
type AgentSession,
|
|
8
7
|
type SessionShutdownEvent,
|
|
8
|
+
SettingsManager,
|
|
9
9
|
} from "@earendil-works/pi-coding-agent";
|
|
10
10
|
|
|
11
11
|
export const CHILD_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
@@ -51,6 +51,8 @@ function isPiIntercomNpmResource(resource: {
|
|
|
51
51
|
* drift test in child-session.test.ts).
|
|
52
52
|
*/
|
|
53
53
|
export const CHILD_EXCLUDED_TOOL_NAMES = [
|
|
54
|
+
// capability discovery mutates the parent model-facing tool surface
|
|
55
|
+
"openpi_load_tools",
|
|
54
56
|
// subagents — children cannot spawn/observe more agents
|
|
55
57
|
"subagent_spawn",
|
|
56
58
|
"subagent_wait",
|
|
@@ -185,7 +187,9 @@ export function resolveStandaloneChildProjectTrust(options: {
|
|
|
185
187
|
|
|
186
188
|
interface ChildSessionStartup {
|
|
187
189
|
bindExtensions(bindings: { mode: "print" }): Promise<void>;
|
|
188
|
-
getActiveToolNames(): string[];
|
|
190
|
+
getActiveToolNames?(): string[];
|
|
191
|
+
getAllTools?(): { name: string }[];
|
|
192
|
+
setActiveToolsByName?(toolNames: string[]): void;
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
function boundedToolNames(names: readonly string[]) {
|
|
@@ -208,9 +212,41 @@ export async function bindChildSessionExtensions(
|
|
|
208
212
|
) {
|
|
209
213
|
await session.bindExtensions({ mode: "print" });
|
|
210
214
|
const requested = effectiveChildToolAllowlist(requestedTools);
|
|
215
|
+
let active: Set<string> | undefined;
|
|
216
|
+
if (
|
|
217
|
+
session.getActiveToolNames &&
|
|
218
|
+
session.getAllTools &&
|
|
219
|
+
session.setActiveToolsByName
|
|
220
|
+
) {
|
|
221
|
+
const requestedSet = requested ? new Set(requested) : undefined;
|
|
222
|
+
const available = new Set(session.getAllTools().map(({ name }) => name));
|
|
223
|
+
const activeNames = session.getActiveToolNames();
|
|
224
|
+
active = new Set(activeNames);
|
|
225
|
+
for (const name of CHILD_SAFE_PACKAGE_TOOL_NAMES) {
|
|
226
|
+
if (
|
|
227
|
+
available.has(name) &&
|
|
228
|
+
!active.has(name) &&
|
|
229
|
+
(requestedSet === undefined || requestedSet.has(name))
|
|
230
|
+
) {
|
|
231
|
+
activeNames.push(name);
|
|
232
|
+
active.add(name);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (activeNames.length !== session.getActiveToolNames().length) {
|
|
236
|
+
session.setActiveToolsByName(activeNames);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
211
239
|
if (!requested) return;
|
|
212
240
|
|
|
213
|
-
|
|
241
|
+
if (!active && session.getActiveToolNames) {
|
|
242
|
+
active = new Set(session.getActiveToolNames());
|
|
243
|
+
}
|
|
244
|
+
if (!active) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
"Child tool preflight failed: the bound child session does not expose active-tool introspection.",
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
214
250
|
const missing = [...new Set(requested)].filter((name) => !active.has(name));
|
|
215
251
|
if (missing.length === 0) return;
|
|
216
252
|
|
|
@@ -63,6 +63,10 @@ export type FooterLines = readonly (readonly FooterLayoutItem[])[];
|
|
|
63
63
|
export const DETAIL_DISPLAYS = ["full", "compact"] as const;
|
|
64
64
|
export type DetailDisplay = (typeof DETAIL_DISPLAYS)[number];
|
|
65
65
|
|
|
66
|
+
export const CAPABILITY_DISCOVERY_MODES = ["explicit", "adaptive"] as const;
|
|
67
|
+
export type CapabilityDiscoveryMode =
|
|
68
|
+
(typeof CAPABILITY_DISCOVERY_MODES)[number];
|
|
69
|
+
|
|
66
70
|
/** Canonical default layout: one-line Powerline dashboard with flex alignment. */
|
|
67
71
|
export const DEFAULT_FOOTER_LINES: FooterLines = [
|
|
68
72
|
[
|
|
@@ -123,6 +127,9 @@ export const POST_EDIT_COMMAND_MAX_CHARS = 500;
|
|
|
123
127
|
export const SETUP_CONFIG_CHANGED_CHANNEL = "my-pi-setup:config-changed";
|
|
124
128
|
|
|
125
129
|
export interface MyPiSetupConfig {
|
|
130
|
+
readonly capabilities: {
|
|
131
|
+
readonly discovery: CapabilityDiscoveryMode;
|
|
132
|
+
};
|
|
126
133
|
readonly suggestions: {
|
|
127
134
|
readonly enabled: boolean;
|
|
128
135
|
readonly model?: SuggestionModelConfig;
|
|
@@ -157,6 +164,7 @@ export interface MyPiSetupConfig {
|
|
|
157
164
|
}
|
|
158
165
|
|
|
159
166
|
export const DEFAULT_SETUP_CONFIG: MyPiSetupConfig = {
|
|
167
|
+
capabilities: { discovery: "explicit" },
|
|
160
168
|
suggestions: { enabled: false },
|
|
161
169
|
workflows: {
|
|
162
170
|
concurrency: DEFAULT_WORKFLOW_CONCURRENCY,
|
|
@@ -205,6 +213,12 @@ const isFooterStyle = (value: unknown): value is FooterStyle =>
|
|
|
205
213
|
const isFooterPreset = (value: unknown): value is FooterPreset =>
|
|
206
214
|
typeof value === "string" && FOOTER_PRESETS.includes(value as FooterPreset);
|
|
207
215
|
|
|
216
|
+
const isCapabilityDiscoveryMode = (
|
|
217
|
+
value: unknown,
|
|
218
|
+
): value is CapabilityDiscoveryMode =>
|
|
219
|
+
typeof value === "string" &&
|
|
220
|
+
CAPABILITY_DISCOVERY_MODES.includes(value as CapabilityDiscoveryMode);
|
|
221
|
+
|
|
208
222
|
export function flattenFooterItems(lines: FooterLines): readonly FooterItem[] {
|
|
209
223
|
const items: FooterItem[] = [];
|
|
210
224
|
const seen = new Set<FooterItem>();
|
|
@@ -401,6 +415,8 @@ function boundedInteger(value: unknown, fallback: number, maximum: number) {
|
|
|
401
415
|
export function parseSetupConfig(value: unknown): MyPiSetupConfig {
|
|
402
416
|
if (!isRecord(value)) return DEFAULT_SETUP_CONFIG;
|
|
403
417
|
|
|
418
|
+
const capabilities = isRecord(value.capabilities) ? value.capabilities : {};
|
|
419
|
+
|
|
404
420
|
// `summaries` is the pre-suggestion config key. Read it once as a migration
|
|
405
421
|
// source; every subsequent save writes only the canonical `suggestions` key.
|
|
406
422
|
const suggestions = isRecord(value.suggestions)
|
|
@@ -430,6 +446,11 @@ export function parseSetupConfig(value: unknown): MyPiSetupConfig {
|
|
|
430
446
|
const subagents = isRecord(value.subagents) ? value.subagents : {};
|
|
431
447
|
const footer = parseUiFooter(ui);
|
|
432
448
|
return {
|
|
449
|
+
capabilities: {
|
|
450
|
+
discovery: isCapabilityDiscoveryMode(capabilities.discovery)
|
|
451
|
+
? capabilities.discovery
|
|
452
|
+
: "explicit",
|
|
453
|
+
},
|
|
433
454
|
suggestions: {
|
|
434
455
|
enabled: requestedEnabled && Boolean(model),
|
|
435
456
|
...(model ? { model } : {}),
|
|
@@ -942,6 +963,7 @@ export function formatSetupConfig(
|
|
|
942
963
|
? `on · ${config.ui.footerStyle} · ${formatFooterLines(config.ui.footerLines)}`
|
|
943
964
|
: "off";
|
|
944
965
|
return [
|
|
966
|
+
`Capability discovery: ${config.capabilities.discovery}`,
|
|
945
967
|
suggestions,
|
|
946
968
|
`Workflows: ${config.workflows.concurrency} concurrent agents · ${config.workflows.maxAgentCalls} total calls`,
|
|
947
969
|
`UI: large header ${config.ui.showHeader ? "on" : "off"} · custom footer ${footer}`,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Broadcast whenever the package-owned setup episode becomes usable or ends. */
|
|
2
|
+
export const OPENPI_SETUP_EPISODE_CHANNEL = "openpi:setup-episode";
|
|
3
|
+
|
|
4
|
+
export interface OpenPiSetupEpisodeState {
|
|
5
|
+
/** True for both armed and actively running setup episodes. */
|
|
6
|
+
readonly active: boolean;
|
|
7
|
+
}
|