@timurproko/a1 0.1.1-dev.11 → 0.1.1-dev.12

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 CHANGED
@@ -20,12 +20,14 @@ a1 update:next # update to npm next
20
20
  ## Develop
21
21
 
22
22
  ```sh
23
- npm ci # install exact locked dependencies
24
- npm run build # compile TypeScript and the process guardian into dist
25
- npm start # build and launch an isolated development instance
26
- npm run test:fast # typecheck + fast suite, no build needed
27
- npm test # same as test:fast
28
- npm run test:full # complete non-physical suite
23
+ npm ci # install exact locked dependencies
24
+ npm run build # compile TypeScript and the process guardian into dist
25
+ npm start # build and launch an isolated development `a1`
26
+ npm run start:pi # build and launch an isolated development `a1 pi`
27
+ npm run start:sandbox # build and launch an isolated development `a1 sandbox`
28
+ npm run test:fast # typecheck + fast suite, no build needed
29
+ npm test # same as test:fast
30
+ npm run test:full # complete non-physical suite
29
31
  ```
30
32
 
31
33
  ## Release
@@ -0,0 +1,75 @@
1
+ /**
2
+ * One pi-tui module identity per process.
3
+ *
4
+ * npm can materialize @earendil-works/pi-tui twice under A1's package root:
5
+ * once as A1's direct dependency at the node_modules root, and once nested
6
+ * inside @earendil-works/pi-coding-agent's isolated dependency tree. A1's
7
+ * owned UI imports the root copy while pinned Pi's extension loader hands
8
+ * extensions the nested copy, so every TUI class exists twice: `instanceof`
9
+ * checks and prototype patches made by extensions land on classes the
10
+ * renderer never uses — extension chrome silently disappears and routed
11
+ * input dead-ends.
12
+ *
13
+ * Repair runs at launch, not at install: npm's `prepare` hook is skipped for
14
+ * registry installs, so an installed A1 must self-heal the same way a source
15
+ * checkout does. The root copy is replaced with a junction (Windows) or
16
+ * directory symlink to the nested copy so every loader resolves the same
17
+ * files and therefore the same module instances.
18
+ *
19
+ * This lives in bin/ (shipped, plain JS) rather than src/ deliberately: its
20
+ * whole job is repairing the node_modules layout, which the Pi API boundary
21
+ * policy rightly forbids ordinary production code from touching.
22
+ *
23
+ * The repair is idempotent and fail-open: a hoisted tree (single copy), an
24
+ * already-linked root, a version mismatch, or a filesystem that refuses the
25
+ * link all leave the tree as it was — launch proceeds with a warning rather
26
+ * than failing, because a degraded UI beats no UI.
27
+ */
28
+ import { existsSync, lstatSync, readFileSync, renameSync, rmSync, symlinkSync } from "node:fs";
29
+ import { join } from "node:path";
30
+
31
+ function packageVersion(directory) {
32
+ return JSON.parse(readFileSync(join(directory, "package.json"), "utf8")).version;
33
+ }
34
+
35
+ /**
36
+ * Collapse a duplicated @earendil-works/pi-tui under `packageRoot` onto the
37
+ * copy pinned Pi resolves, so extensions and the owned UI share one module
38
+ * instance. Must run before anything in the process imports pi-tui.
39
+ * Returns a discriminated outcome; never throws.
40
+ */
41
+ export function ensureSinglePiTuiModule(packageRoot) {
42
+ const rootCopy = join(packageRoot, "node_modules", "@earendil-works", "pi-tui");
43
+ const nestedCopy = join(
44
+ packageRoot,
45
+ "node_modules", "@earendil-works", "pi-coding-agent",
46
+ "node_modules", "@earendil-works", "pi-tui",
47
+ );
48
+ try {
49
+ if (!existsSync(nestedCopy)) return { kind: "single-copy" };
50
+ if (existsSync(rootCopy) && lstatSync(rootCopy).isSymbolicLink()) return { kind: "already-linked" };
51
+ if (existsSync(rootCopy)) {
52
+ const rootVersion = packageVersion(rootCopy);
53
+ const nestedVersion = packageVersion(nestedCopy);
54
+ if (rootVersion !== nestedVersion) return { kind: "version-mismatch", rootVersion, nestedVersion };
55
+ const retired = `${rootCopy}.duplicate`;
56
+ rmSync(retired, { recursive: true, force: true });
57
+ renameSync(rootCopy, retired);
58
+ rmSync(retired, { recursive: true, force: true });
59
+ }
60
+ symlinkSync(nestedCopy, rootCopy, "junction");
61
+ return { kind: "linked" };
62
+ } catch (error) {
63
+ return { kind: "failed", message: error instanceof Error ? error.message : String(error) };
64
+ }
65
+ }
66
+
67
+ /** Launch-entry wrapper: repair, and warn on stderr when the tree stays split. */
68
+ export function ensureSinglePiTuiModuleAtLaunch(packageRoot, warn) {
69
+ const outcome = ensureSinglePiTuiModule(packageRoot);
70
+ if (outcome.kind === "version-mismatch") {
71
+ warn(`a1: pi-tui is duplicated at incompatible versions (${outcome.rootVersion} vs ${outcome.nestedVersion}); extension UI may not render.\n`);
72
+ } else if (outcome.kind === "failed") {
73
+ warn(`a1: could not unify the duplicated pi-tui module (${outcome.message}); extension UI may not render.\n`);
74
+ }
75
+ }
package/bin/ui.js CHANGED
@@ -1,5 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const { ensureSinglePiTuiModuleAtLaunch } = await import("./module-identity.js");
4
+ const { fileURLToPath } = await import("node:url");
5
+
6
+ // Before the composition loads pinned Pi's terminal stack: collapse npm's
7
+ // duplicated copies of it so extensions and the owned UI share one module
8
+ // identity (see bin/module-identity.js for the full story).
9
+ ensureSinglePiTuiModuleAtLaunch(fileURLToPath(new URL("..", import.meta.url)), message => process.stderr.write(message));
10
+
3
11
  const { runSelectedInteractiveRuntime } = await import("../dist/src/features/launch/index.js");
4
12
 
5
13
  runSelectedInteractiveRuntime(process.env.A1_LAUNCH_PROFILE ?? "a1", {
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "unsupported",
8
- "builtAt": "2026-08-23T09:25:46.770Z",
8
+ "builtAt": "2026-08-23T13:01:43.157Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "7524bf568992517f50ed53196c861c5edeba0d7275633bb4735ab45bd5963a28",
@@ -5,7 +5,7 @@
5
5
  "platform": "linux",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-23T09:25:43.327Z",
8
+ "builtAt": "2026-08-23T13:01:56.341Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "29e22fe29de2828982bc67ef418c4adcaab1490281477b7bea592ec6fe621bcd",
@@ -5,10 +5,10 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-23T09:26:36.967Z",
8
+ "builtAt": "2026-08-23T13:02:22.268Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "1fd994f340bfa8423eee47ab6b7c3a19a5b1a19dea88814af27d8a989cfa0da0",
11
+ "sha256": "f36ba90394ff9d7bd32612d8b89f96f23afa394e22260466271938ae27ee5c9f",
12
12
  "size": 172544
13
13
  },
14
14
  "provenance": {
@@ -37,6 +37,21 @@ export declare function createPiShellChangelog(markdown: string): PiShellCompone
37
37
  export declare function createPiShellHotkeys(): PiShellComponentPort;
38
38
  export declare function createPiShellTranscriptComponent(initial: OwnedUiTranscriptBlock, cwd: string, extensions?: PiShellExtensionRendererResolver): PiShellTranscriptComponentPort;
39
39
  export declare function renderPiShellTranscriptBlock(block: OwnedUiTranscriptBlock, width: number, cwd: string): readonly string[];
40
+ /**
41
+ * Pinned Pi's CLI prints startup diagnostics with `reportDiagnostics` before
42
+ * the banner: the whole line, prefix included, in chalk's basic ANSI severity
43
+ * colour — not the theme's tokens — with info lines dim and unprefixed.
44
+ */
45
+ export declare function renderPiShellStartupDiagnostic(diagnostic: {
46
+ readonly severity: "info" | "warning" | "error";
47
+ readonly message: string;
48
+ }, width: number): readonly string[];
49
+ /**
50
+ * Pinned Pi's `showPackageUpdateNotification` banner: warning-coloured dynamic
51
+ * borders around a bold warning title, the muted update instruction with the
52
+ * accent command, and the package list.
53
+ */
54
+ export declare function renderPiShellPackageUpdateNotice(packages: readonly string[], width: number): readonly string[];
40
55
  type PiAssistantMessage = NonNullable<ConstructorParameters<typeof AssistantMessageComponent>[0]>;
41
56
  export declare function validatedAssistantMessage(block: OwnedUiTranscriptBlock): PiAssistantMessage;
42
57
  export {};
@@ -141,6 +141,39 @@ export function renderPiShellTranscriptBlock(block, width, cwd) {
141
141
  ensureTheme();
142
142
  return transcriptComponent(block, cwd, true).render(width);
143
143
  }
144
+ /**
145
+ * Pinned Pi's CLI prints startup diagnostics with `reportDiagnostics` before
146
+ * the banner: the whole line, prefix included, in chalk's basic ANSI severity
147
+ * colour — not the theme's tokens — with info lines dim and unprefixed.
148
+ */
149
+ export function renderPiShellStartupDiagnostic(diagnostic, width) {
150
+ ensureTheme();
151
+ const escape = String.fromCharCode(27);
152
+ const chalk = diagnostic.severity === "error"
153
+ ? { open: `${escape}[31m`, close: `${escape}[39m`, prefix: "Error: " }
154
+ : diagnostic.severity === "warning"
155
+ ? { open: `${escape}[33m`, close: `${escape}[39m`, prefix: "Warning: " }
156
+ : { open: `${escape}[2m`, close: `${escape}[22m`, prefix: "" };
157
+ return new Text(`${chalk.open}${chalk.prefix}${diagnostic.message}${chalk.close}`, 0, 0).render(width);
158
+ }
159
+ /**
160
+ * Pinned Pi's `showPackageUpdateNotification` banner: warning-coloured dynamic
161
+ * borders around a bold warning title, the muted update instruction with the
162
+ * accent command, and the package list.
163
+ */
164
+ export function renderPiShellPackageUpdateNotice(packages, width) {
165
+ ensureTheme();
166
+ const theme = piTheme();
167
+ const container = new Container();
168
+ container.addChild(new Spacer(1));
169
+ container.addChild(new DynamicBorder(text => theme.fg("warning", text)));
170
+ container.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n`
171
+ + `${theme.fg("muted", "Package updates are available. Run ")}${theme.fg("accent", "pi update --extensions")}\n`
172
+ + `${theme.fg("muted", "Packages:")}\n`
173
+ + packages.map(name => `- ${name}`).join("\n"), 1, 0));
174
+ container.addChild(new DynamicBorder(text => theme.fg("warning", text)));
175
+ return container.render(width);
176
+ }
144
177
  function transcriptComponent(block, cwd, expanded, extensions) {
145
178
  switch (block.kind) {
146
179
  case "user": {
@@ -1,4 +1,4 @@
1
- import { type AgentSessionRuntime, type SessionInfo } from "@earendil-works/pi-coding-agent";
1
+ import { type AgentSessionRuntime, type AgentSessionServices, type SessionInfo } from "@earendil-works/pi-coding-agent";
2
2
  import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, type OwnedUiCommand, type OwnedUiCommandOutcome, type OwnedUiEvent, type OwnedUiSessionViewModel, type OwnedUiSnapshot } from "../owned-ui-contracts/index.js";
3
3
  import { type PiAuthenticationProviderOption, type PiBashWorkflowResult, type PiPinnedSettingsCallback, type PiPinnedSettingsSnapshot, type PiWorkflowAutocompleteCommand, type PiWorkflowHost, type PiWorkflowInteractionHost, type PiWorkflowOption, type PiWorkflowRequest, type PiWorkflowResult } from "./workflows.js";
4
4
  import type { AgentSettingsPort } from "../agent-engine-contracts/index.js";
@@ -51,6 +51,7 @@ export interface PiScopedModelsRefreshResult extends PiScopedModelsContext {
51
51
  readonly status: string;
52
52
  readonly statusKind: "success" | "warning";
53
53
  }
54
+ type PiServicesApi = AgentSessionServices;
54
55
  export type PiEngineRuntimeFactory = (input: PiEngineRuntimeFactoryInput) => Promise<AgentSessionRuntime>;
55
56
  export interface OwnedPiResourceSummary {
56
57
  readonly kind: "skill" | "prompt-template" | "agent-context" | "system-prompt" | "theme";
@@ -90,6 +91,11 @@ export interface PiEngineAdapterOptions {
90
91
  * offering stay here.
91
92
  */
92
93
  readonly availableThemes?: () => readonly string[];
94
+ /**
95
+ * Startup extension-package update probe, mirroring pinned Pi's interactive
96
+ * mode. Returns display names of packages with updates available.
97
+ */
98
+ readonly checkPackageUpdates?: (settingsManager: PiServicesApi["settingsManager"]) => Promise<readonly string[]>;
93
99
  }
94
100
  export interface AdapterCommandResult {
95
101
  readonly outcome: OwnedUiCommandOutcome;
@@ -155,3 +161,4 @@ export declare class PiEngineAdapter {
155
161
  dispose(): Promise<void>;
156
162
  }
157
163
  export declare function createPiEngineAdapter(options?: PiEngineAdapterOptions): Promise<PiEngineAdapter>;
164
+ export {};
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import { PRODUCT_IDENTITY } from "../../product-identity.js";
7
- import { copyToClipboard, getAgentDir, ProjectTrustStore, SessionManager, } from "@earendil-works/pi-coding-agent";
7
+ import { copyToClipboard, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, } from "@earendil-works/pi-coding-agent";
8
8
  import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiSnapshot, } from "../owned-ui-contracts/index.js";
9
9
  import { PINNED_PI_SETTINGS_CALLBACKS, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "./workflows.js";
10
10
  import { createPiRuntimeIntegration } from "./runtime-integration.js";
@@ -19,6 +19,7 @@ const DEFAULT_SURFACE = {
19
19
  };
20
20
  export class PiEngineAdapter {
21
21
  #runtimeFactory;
22
+ #checkPackageUpdates;
22
23
  #cwd;
23
24
  #agentDir;
24
25
  #sessionId;
@@ -73,6 +74,10 @@ export class PiEngineAdapter {
73
74
  this.#agentDir = options.agentDir ?? getAgentDir();
74
75
  this.#sessionId = options.sessionId ?? "owned-session-1";
75
76
  this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
77
+ this.#checkPackageUpdates = options.checkPackageUpdates
78
+ ?? (options.createRuntime
79
+ ? async () => []
80
+ : settingsManager => checkDefaultPiPackageUpdates(this.#cwd, this.#agentDir, settingsManager));
76
81
  this.#workflowHost = options.workflowHost ?? defaultWorkflowHost();
77
82
  this.#availableThemes = options.availableThemes ?? null;
78
83
  this.#workflowInteraction = { prompt: async () => null, notify() { } };
@@ -122,8 +127,25 @@ export class PiEngineAdapter {
122
127
  this.#editor = { ...this.#editor, submitEnabled: true };
123
128
  this.#emitEvent({ type: "session-lifecycle", lifecycle: "ready", reason: null });
124
129
  this.#emitView();
130
+ void this.#announcePackageUpdates(runtime.services.settingsManager);
125
131
  return this.view();
126
132
  }
133
+ async #announcePackageUpdates(settingsManager) {
134
+ if (process.env.PI_OFFLINE)
135
+ return;
136
+ let updates;
137
+ try {
138
+ updates = await this.#checkPackageUpdates(settingsManager);
139
+ }
140
+ catch {
141
+ return;
142
+ }
143
+ if (this.#disposed || updates.length === 0)
144
+ return;
145
+ const packages = updates.map(name => `- ${name}`).join("\n");
146
+ this.#addDiagnostic("info", "package-updates", `Package updates are available. Run pi update --extensions\nPackages:\n${packages}`, true);
147
+ this.#emitView();
148
+ }
127
149
  onEvent(listener) {
128
150
  this.#listeners.add(listener);
129
151
  listener(this.#event({ type: "session-view", view: this.view() }));
@@ -1876,6 +1898,11 @@ export async function createPiEngineAdapter(options = {}) {
1876
1898
  async function createDefaultPiRuntime(input) {
1877
1899
  return createPiRuntimeIntegration({ cwd: input.cwd, agentDir: input.agentDir });
1878
1900
  }
1901
+ async function checkDefaultPiPackageUpdates(cwd, agentDir, settingsManager) {
1902
+ const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
1903
+ const updates = await packageManager.checkForAvailableUpdates();
1904
+ return updates.map(update => update.displayName);
1905
+ }
1879
1906
  function pinnedSessionInfoPresentation(value, sessionName, entries, modelRuntime) {
1880
1907
  const stats = isRecord(value) ? value : {};
1881
1908
  const tokens = dynamicObject(stats, "tokens");
@@ -1,4 +1,4 @@
1
- import { type AgentSession, type AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
1
+ import { type AgentSession, type AgentSessionRuntime, type AgentSessionServices, type ScopedModel } from "@earendil-works/pi-coding-agent";
2
2
  export interface PiRuntimeIntegrationOptions {
3
3
  readonly cwd: string;
4
4
  readonly agentDir: string;
@@ -10,9 +10,26 @@ export type PiSessionReplacement = {
10
10
  readonly kind: "resume";
11
11
  readonly sessionPath: string;
12
12
  };
13
+ interface ConfiguredModelScope {
14
+ readonly scopedModels: readonly ScopedModel[];
15
+ readonly model: ScopedModel["model"] | undefined;
16
+ readonly thinkingLevel: ScopedModel["thinkingLevel"];
17
+ readonly diagnostics: readonly {
18
+ type: "info" | "warning" | "error";
19
+ message: string;
20
+ }[];
21
+ }
22
+ /**
23
+ * Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
24
+ * into a scoped model list, keep the resolver's warnings (e.g. "No models match
25
+ * pattern ..."), and pick the same initial model pinned Pi would pick — the
26
+ * saved default when it is in scope, otherwise the first scoped model.
27
+ */
28
+ export declare function resolveConfiguredModelScope(services: Pick<AgentSessionServices, "settingsManager" | "modelRuntime">): Promise<ConfiguredModelScope>;
13
29
  export declare function createPiRuntimeIntegration(options: PiRuntimeIntegrationOptions): Promise<AgentSessionRuntime>;
14
30
  export declare function bindPiRuntimeSession(runtime: AgentSessionRuntime, rebind: (session: AgentSession) => Promise<void>): () => void;
15
31
  export declare function replacePiRuntimeSession(runtime: AgentSessionRuntime, replacement: PiSessionReplacement): Promise<{
16
32
  readonly cancelled: boolean;
17
33
  }>;
18
34
  export declare function disposePiRuntimeIntegration(runtime: AgentSessionRuntime): Promise<void>;
35
+ export {};
@@ -1,14 +1,49 @@
1
- import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, SessionManager, } from "@earendil-works/pi-coding-agent";
1
+ import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, resolveModelScopeWithDiagnostics, SessionManager, } from "@earendil-works/pi-coding-agent";
2
+ /**
3
+ * Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
4
+ * into a scoped model list, keep the resolver's warnings (e.g. "No models match
5
+ * pattern ..."), and pick the same initial model pinned Pi would pick — the
6
+ * saved default when it is in scope, otherwise the first scoped model.
7
+ */
8
+ export async function resolveConfiguredModelScope(services) {
9
+ const patterns = services.settingsManager.getEnabledModels();
10
+ if (!patterns || patterns.length === 0) {
11
+ return { scopedModels: [], model: undefined, thinkingLevel: undefined, diagnostics: [] };
12
+ }
13
+ const { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics([...patterns], services.modelRuntime, { signal: AbortSignal.timeout(15_000) });
14
+ let selected;
15
+ if (scopedModels.length > 0) {
16
+ const savedProvider = services.settingsManager.getDefaultProvider();
17
+ const savedModelId = services.settingsManager.getDefaultModel();
18
+ const savedModel = savedProvider && savedModelId
19
+ ? services.modelRuntime.getModel(savedProvider, savedModelId)
20
+ : undefined;
21
+ selected = (savedModel
22
+ ? scopedModels.find(scoped => scoped.model.provider === savedModel.provider && scoped.model.id === savedModel.id)
23
+ : undefined) ?? scopedModels[0];
24
+ }
25
+ return {
26
+ scopedModels,
27
+ model: selected?.model,
28
+ thinkingLevel: selected?.thinkingLevel,
29
+ diagnostics: diagnostics.map(diagnostic => ({ type: diagnostic.type, message: diagnostic.message })),
30
+ };
31
+ }
2
32
  export async function createPiRuntimeIntegration(options) {
3
33
  const sessionManager = SessionManager.create(options.cwd, options.sessionDir ?? process.env.PI_CODING_AGENT_SESSION_DIR);
4
34
  const createRuntime = async ({ cwd, sessionManager: targetSessionManager, sessionStartEvent, }) => {
5
35
  const services = await createAgentSessionServices({ cwd, agentDir: options.agentDir });
36
+ const modelScope = await resolveConfiguredModelScope(services);
37
+ const hasExistingSession = targetSessionManager.buildSessionContext().messages.length > 0;
6
38
  const created = await createAgentSessionFromServices({
7
39
  services,
8
40
  sessionManager: targetSessionManager,
9
41
  ...(sessionStartEvent ? { sessionStartEvent } : {}),
42
+ ...(modelScope.model && !hasExistingSession ? { model: modelScope.model } : {}),
43
+ ...(modelScope.thinkingLevel && !hasExistingSession ? { thinkingLevel: modelScope.thinkingLevel } : {}),
44
+ ...(modelScope.scopedModels.length > 0 ? { scopedModels: [...modelScope.scopedModels] } : {}),
10
45
  });
11
- return { ...created, services, diagnostics: services.diagnostics };
46
+ return { ...created, services, diagnostics: [...modelScope.diagnostics] };
12
47
  };
13
48
  return createAgentSessionRuntime(createRuntime, {
14
49
  cwd: options.cwd,
@@ -1,6 +1,6 @@
1
1
  import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../ui-components/index.js";
2
2
  import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "../pi-engine-adapter/index.js";
3
- import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, piTheme, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../pi-component-adapter/index.js";
3
+ import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, piTheme, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../pi-component-adapter/index.js";
4
4
  import { PiTuiRuntimeAdapter, } from "../pi-tui-runtime-adapter/index.js";
5
5
  export class OwnedUiSessionShellRoot {
6
6
  editor;
@@ -136,7 +136,17 @@ export class OwnedUiSessionShellRoot {
136
136
  return ["", ...rows];
137
137
  return rows;
138
138
  });
139
- const diagnosticRows = this.#view.diagnostics.slice(-3).flatMap(diagnostic => renderPiShellTranscriptBlock({
139
+ const diagnostics = this.#view.diagnostics;
140
+ const startupRows = diagnostics
141
+ .filter(diagnostic => diagnostic.code === "engine-startup")
142
+ .flatMap(diagnostic => renderPiShellStartupDiagnostic(diagnostic, width));
143
+ const packageUpdateRows = diagnostics
144
+ .filter(diagnostic => diagnostic.code === "package-updates")
145
+ .flatMap(diagnostic => renderPiShellPackageUpdateNotice(diagnostic.message.split("\n").filter(line => line.startsWith("- ")).map(line => line.slice(2)), width));
146
+ const diagnosticRows = diagnostics
147
+ .filter(diagnostic => diagnostic.code !== "engine-startup" && diagnostic.code !== "package-updates")
148
+ .slice(-3)
149
+ .flatMap(diagnostic => renderPiShellTranscriptBlock({
140
150
  id: `diagnostic-${diagnostic.sequence}`,
141
151
  kind: diagnostic.severity === "error" ? "error" : "system",
142
152
  status: "finalized",
@@ -149,9 +159,11 @@ export class OwnedUiSessionShellRoot {
149
159
  if (resourceRows.at(-1) === "")
150
160
  resourceRows.pop();
151
161
  return [
162
+ ...startupRows,
152
163
  ...(this.#extensionHeader ?? this.header).render(width),
153
164
  ...resourceRows,
154
165
  ...transcript,
166
+ ...packageUpdateRows,
155
167
  ...diagnosticRows,
156
168
  ];
157
169
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.1-dev.11",
3
+ "version": "0.1.1-dev.12",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",
@@ -46,7 +46,7 @@
46
46
  "start:sandbox": "npm run build && node scripts/dev-launch.mjs sandbox",
47
47
  "prepack": "node scripts/prepack-gate.mjs",
48
48
  "prepublishOnly": "npm run test:release",
49
- "prepare": "npm run build",
49
+ "prepare": "node scripts/unify-pi-tui.mjs && npm run build",
50
50
  "update:pi-settings-metadata": "node scripts/update-pi-settings-metadata.mjs"
51
51
  },
52
52
  "dependencies": {