@timurproko/a1 0.1.8-dev.0b6d8cc → 0.1.8-dev.235df0e

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
@@ -9,11 +9,10 @@ npm install --global @timurproko/a1@latest
9
9
  ## Commands
10
10
 
11
11
  ```sh
12
- a1 # A1-owned UI and profile: ~/.a1/agent
13
- a1 version # show Installed, Release (latest), and Next versions
14
- a1 update # update to npm latest
15
- a1 update:next # update to npm next (or a1 update:<commit> for a specific preview)
16
- a1 update --models # refresh A1's model catalogs
12
+ a1 # A1-owned UI and profile: ~/.a1/agent
13
+ a1 version # show Installed, Release (latest), and Next versions
14
+ a1 update # update to npm latest
15
+ a1 update:next # update to npm next (or a1 update:<commit> for a specific preview)
17
16
  ```
18
17
 
19
18
  Prerelease builds — what `a1 update:next` installs — add two development profiles
@@ -36,6 +35,7 @@ a1 pi remove npm:pi-mcp-adapter # remove it again (alias: a1 pi uninstall)
36
35
  a1 pi list # list packages installed for a1
37
36
  a1 pi update --extensions # update every installed package
38
37
  a1 pi update npm:pi-mcp-adapter # update one of them
38
+ a1 update --models # refresh A1's model catalogs
39
39
  ```
40
40
 
41
41
  A running session loads a newly installed package after a restart. Pi's own
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "unsupported",
8
- "builtAt": "2026-08-25T08:02:28.988Z",
8
+ "builtAt": "2026-08-25T07:40:28.168Z",
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-25T08:02:31.153Z",
8
+ "builtAt": "2026-08-25T07:40:28.525Z",
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-25T08:02:58.479Z",
8
+ "builtAt": "2026-08-25T07:40:52.318Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "fdbde2408643ffb897f5107d96ed40e8c8467505ef7daa64f86f9c2497c304ca",
11
+ "sha256": "9a82f7885d1b3375ea0b78174e68461c176c26504ed5fb6740fcd4cb96516432",
12
12
  "size": 172544
13
13
  },
14
14
  "provenance": {
@@ -3,7 +3,7 @@ export * from "./conformance.js";
3
3
  export * from "./shell-components.js";
4
4
  export * from "./theme.js";
5
5
  export * from "./upstream/theme/theme-controller.js";
6
- export { applyConfiguredPiTheme, getAvailablePiThemes, onPiThemeChange } from "./upstream/theme/theme.js";
6
+ export { applyConfiguredPiTheme, getAvailablePiThemes } from "./upstream/theme/theme.js";
7
7
  export * from "./upstream/components/countdown-timer.js";
8
8
  export * from "./upstream/components/extension-editor.js";
9
9
  export * from "./upstream/components/session-selector.js";
@@ -3,7 +3,7 @@ export * from "./conformance.js";
3
3
  export * from "./shell-components.js";
4
4
  export * from "./theme.js";
5
5
  export * from "./upstream/theme/theme-controller.js";
6
- export { applyConfiguredPiTheme, getAvailablePiThemes, onPiThemeChange } from "./upstream/theme/theme.js";
6
+ export { applyConfiguredPiTheme, getAvailablePiThemes } from "./upstream/theme/theme.js";
7
7
  export * from "./upstream/components/countdown-timer.js";
8
8
  export * from "./upstream/components/extension-editor.js";
9
9
  export * from "./upstream/components/session-selector.js";
@@ -59,8 +59,6 @@ export class PiEngineAdapter {
59
59
  #activeCommandIds = [];
60
60
  #completedCommands = new Map();
61
61
  #transcript = [];
62
- #transcriptIndex = new Map();
63
- #transcriptSnapshot;
64
62
  #messageBlockIds = new WeakMap();
65
63
  #messageFallbackIds = new Map();
66
64
  #toolBlockIds = new Map();
@@ -736,7 +734,7 @@ export class PiEngineAdapter {
736
734
  sessionId: this.#sessionId,
737
735
  revision: this.#viewRevision,
738
736
  lifecycle: this.#lifecycle,
739
- transcript: this.#transcriptSnapshot ??= Object.freeze([...this.#transcript]),
737
+ transcript: this.#transcript.map(block => ({ ...block })),
740
738
  editor: { ...this.#editor, queuedSubmissions: [...this.#editor.queuedSubmissions] },
741
739
  status: {
742
740
  ...this.#status,
@@ -1416,22 +1414,6 @@ export class PiEngineAdapter {
1416
1414
  this.#emitView();
1417
1415
  }
1418
1416
  }
1419
- /**
1420
- * Replaces the transcript and the index that finds a block by its identifier. Every
1421
- * lookup goes through the index, so streaming a chunk costs the same in a long session
1422
- * as in a new one.
1423
- */
1424
- #setTranscript(blocks) {
1425
- this.#transcript = blocks;
1426
- this.#transcriptIndex.clear();
1427
- for (const [index, block] of blocks.entries())
1428
- this.#transcriptIndex.set(block.id, index);
1429
- this.#transcriptSnapshot = undefined;
1430
- }
1431
- #transcriptBlock(id) {
1432
- const index = this.#transcriptIndex.get(id);
1433
- return index === undefined ? undefined : this.#transcript[index];
1434
- }
1435
1417
  /** Shows the state named by `kind`, which becomes the state a later end can clear. */
1436
1418
  #enterWorkState(kind, message) {
1437
1419
  const wasBusy = this.#lifecycle === "busy";
@@ -1512,7 +1494,7 @@ export class PiEngineAdapter {
1512
1494
  if (finalMessages.length > 0)
1513
1495
  this.#rebuildTranscript(finalMessages, "finalized");
1514
1496
  else
1515
- this.#setTranscript(this.#transcript.map(block => block.status === "live" ? { ...block, status: "finalized" } : block));
1497
+ this.#transcript = this.#transcript.map(block => block.status === "live" ? { ...block, status: "finalized" } : block);
1516
1498
  // Ending a turn leaves the working state, as the recorded pinned baseline does, but
1517
1499
  // it leaves only that state: a compaction or retry being shown outlives the turn
1518
1500
  // that ended under it. Settlement ends the run, and with it every state — the
@@ -1638,7 +1620,7 @@ export class PiEngineAdapter {
1638
1620
  }
1639
1621
  }
1640
1622
  }
1641
- this.#setTranscript(blocks);
1623
+ this.#transcript = blocks;
1642
1624
  }
1643
1625
  #upsertMessageBlock(message, status) {
1644
1626
  const blocks = this.#messageBlocks(message, status, this.#transcript.length);
@@ -1723,7 +1705,7 @@ export class PiEngineAdapter {
1723
1705
  : this.#toolBlockIds.get(toolCallId) ?? `tool-${toolCallId}`;
1724
1706
  if (toolCallId !== undefined)
1725
1707
  this.#toolBlockIds.set(toolCallId, blockId);
1726
- const existing = this.#transcriptBlock(blockId);
1708
+ const existing = this.#transcript.find(block => block.id === blockId);
1727
1709
  const existingPayload = isRecord(existing?.payload) ? existing.payload : undefined;
1728
1710
  return [{
1729
1711
  id: blockId,
@@ -1814,14 +1796,11 @@ export class PiEngineAdapter {
1814
1796
  });
1815
1797
  }
1816
1798
  #upsertTranscriptBlock(block) {
1817
- const index = this.#transcriptIndex.get(block.id);
1818
- if (index !== undefined)
1799
+ const index = this.#transcript.findIndex(existing => existing.id === block.id);
1800
+ if (index >= 0)
1819
1801
  this.#transcript[index] = block;
1820
- else {
1821
- this.#transcriptIndex.set(block.id, this.#transcript.length);
1802
+ else
1822
1803
  this.#transcript.push(block);
1823
- }
1824
- this.#transcriptSnapshot = undefined;
1825
1804
  this.#emitEvent({ type: "transcript-block", block });
1826
1805
  }
1827
1806
  #messageBlockId(message, fallbackIndex, status, occurrence) {
@@ -1839,7 +1818,7 @@ export class PiEngineAdapter {
1839
1818
  }
1840
1819
  }
1841
1820
  else {
1842
- id = [...cached].reverse().find(candidate => this.#transcriptBlock(candidate)?.status === "live");
1821
+ id = [...cached].reverse().find(candidate => this.#transcript.some(block => block.id === candidate && block.status === "live"));
1843
1822
  if (id === undefined && status === "finalized")
1844
1823
  id = cached.at(-1);
1845
1824
  if (id === undefined) {
@@ -1852,7 +1831,7 @@ export class PiEngineAdapter {
1852
1831
  return id;
1853
1832
  }
1854
1833
  #nextBlockRevision(id) {
1855
- const existing = this.#transcriptBlock(id);
1834
+ const existing = this.#transcript.find(block => block.id === id);
1856
1835
  if (existing)
1857
1836
  return existing.revision + 1;
1858
1837
  this.#nextBlockSequence += 1;
@@ -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, onPiThemeChange, piTheme, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, 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;
@@ -8,9 +8,6 @@ export class OwnedUiSessionShellRoot {
8
8
  resources;
9
9
  #cwd;
10
10
  #transcript = new Map();
11
- #blocksById = new Map();
12
- #renderedRows = new Map();
13
- #themeUnsubscribe;
14
11
  #transcriptOrder = [];
15
12
  #view;
16
13
  #status;
@@ -57,9 +54,6 @@ export class OwnedUiSessionShellRoot {
57
54
  this.editor.addToHistory(block.text);
58
55
  }
59
56
  this.#syncTranscript(view.transcript);
60
- // Colours come from the active theme, so rendered rows outlive their revision only
61
- // until the theme under them changes.
62
- this.#themeUnsubscribe = onPiThemeChange(() => this.#renderedRows.clear());
63
57
  }
64
58
  update(view) {
65
59
  this.#view = view;
@@ -134,10 +128,10 @@ export class OwnedUiSessionShellRoot {
134
128
  }
135
129
  #renderDocument(width) {
136
130
  const transcript = this.#transcriptOrder.flatMap((id, index) => {
137
- const block = this.#blocksById.get(id);
131
+ const block = this.#view.transcript.find(item => item.id === id);
138
132
  if (!this.#thinkingVisible && block?.kind === "thinking")
139
133
  return [];
140
- const rows = this.#blockRows(id, block, width);
134
+ const rows = this.#transcript.get(id)?.render(width) ?? [];
141
135
  if (index > 0 && block?.kind === "user")
142
136
  return ["", ...rows];
143
137
  return rows;
@@ -173,25 +167,6 @@ export class OwnedUiSessionShellRoot {
173
167
  ...diagnosticRows,
174
168
  ];
175
169
  }
176
- /**
177
- * Rows for one transcript block. A finalized block renders once for a given revision
178
- * and width and is reused after that, so a frame costs what changed rather than what
179
- * the session has accumulated. A live block, and anything the shell drives itself, is
180
- * rendered every time because its content is still moving.
181
- */
182
- #blockRows(id, block, width) {
183
- const component = this.#transcript.get(id);
184
- if (component === undefined)
185
- return [];
186
- if (block === undefined || block.status !== "finalized")
187
- return component.render(width);
188
- const cached = this.#renderedRows.get(id);
189
- if (cached && cached.width === width && cached.revision === block.revision)
190
- return cached.rows;
191
- const rows = component.render(width);
192
- this.#renderedRows.set(id, { width, revision: block.revision, rows });
193
- return rows;
194
- }
195
170
  transcriptComponent(id) {
196
171
  return this.#transcript.get(id);
197
172
  }
@@ -359,8 +334,6 @@ export class OwnedUiSessionShellRoot {
359
334
  this.#extensionWorkingMessage = undefined;
360
335
  this.#status.setWorkingOverride(undefined);
361
336
  this.#footer.update(this.#viewWithExtensionStatuses(this.#view));
362
- // An extension renderer may have drawn transcript blocks that are now unrendered by it.
363
- this.#renderedRows.clear();
364
337
  this.invalidate();
365
338
  }
366
339
  addExtensionNotification(message, type) {
@@ -411,13 +384,11 @@ export class OwnedUiSessionShellRoot {
411
384
  this.#inputSurface.setFocused?.(focused);
412
385
  }
413
386
  dispose() {
414
- this.#themeUnsubscribe();
415
387
  this.header.dispose?.();
416
388
  this.resources.dispose?.();
417
389
  for (const component of this.#transcript.values())
418
390
  component.dispose?.();
419
391
  this.#transcript.clear();
420
- this.#renderedRows.clear();
421
392
  if (this.#inputSurface !== this.editor)
422
393
  this.#inputSurface.dispose?.();
423
394
  this.#extensionHeader?.dispose?.();
@@ -431,16 +402,12 @@ export class OwnedUiSessionShellRoot {
431
402
  this.#queued.dispose?.();
432
403
  }
433
404
  #syncTranscript(blocks) {
434
- this.#blocksById.clear();
435
- for (const block of blocks)
436
- this.#blocksById.set(block.id, block);
437
405
  const nextIds = new Set(blocks.map(block => block.id));
438
406
  for (const [id, component] of this.#transcript) {
439
407
  if (id.startsWith("workflow-status-") || nextIds.has(id))
440
408
  continue;
441
409
  component.dispose?.();
442
410
  this.#transcript.delete(id);
443
- this.#renderedRows.delete(id);
444
411
  }
445
412
  for (const block of blocks) {
446
413
  const component = this.#transcript.get(block.id);
@@ -464,9 +431,8 @@ export class OwnedUiSessionShellRoot {
464
431
  if (block !== undefined)
465
432
  order.push(block.id);
466
433
  }
467
- const placed = new Set(order);
468
434
  for (const statusId of statusIds) {
469
- if (!placed.has(statusId))
435
+ if (!order.includes(statusId))
470
436
  order.push(statusId);
471
437
  }
472
438
  this.#transcriptOrder = order;
@@ -532,8 +498,6 @@ export class OwnedUiSessionShellRoot {
532
498
  this.resources.setExpanded(expanded);
533
499
  for (const component of this.#transcript.values())
534
500
  component.setExpanded(expanded);
535
- // Expansion changes what a block draws without changing its revision.
536
- this.#renderedRows.clear();
537
501
  this.invalidate();
538
502
  }
539
503
  }
@@ -715,7 +679,6 @@ export class OwnedUiSessionShell {
715
679
  #sequence = 0;
716
680
  #started = false;
717
681
  #disposed = false;
718
- #pointerReporting = false;
719
682
  #compactionQueue = [];
720
683
  #lastClearTime = 0;
721
684
  #activeLoginDialog;
@@ -1390,25 +1353,10 @@ export class OwnedUiSessionShell {
1390
1353
  this.root.appendDaxnuts();
1391
1354
  }
1392
1355
  }
1393
- /**
1394
- * Turns terminal pointer reporting on for a screen that reads the pointer, and off for
1395
- * every path that ends it. While it is on the terminal hands A1 the wheel and the
1396
- * button instead of scrolling and selecting itself, so leaving it on outlives the
1397
- * screen that wanted it and takes the terminal's own scrolling and selection with it.
1398
- */
1399
- #setPointerReporting(enabled) {
1400
- if (this.#pointerReporting === enabled)
1401
- return;
1402
- this.#pointerReporting = enabled;
1403
- if (!this.runtime.active)
1404
- return;
1405
- this.runtime.writeControl(enabled ? MOUSE_TRACKING_ON : MOUSE_TRACKING_OFF);
1406
- }
1407
1356
  async dispose() {
1408
1357
  if (this.#disposed)
1409
1358
  return;
1410
1359
  this.#disposed = true;
1411
- this.#setPointerReporting(false);
1412
1360
  this.#unsubscribe();
1413
1361
  this.#dialogHandle?.hide();
1414
1362
  await this.backend.unbindExtensionUi();
@@ -1421,8 +1369,6 @@ export class OwnedUiSessionShell {
1421
1369
  this.#sessionGeneration = this.backend.sessionGeneration;
1422
1370
  this.#activeLoginDialog = undefined;
1423
1371
  this.#extensionBridge.reset();
1424
- // A replaced session takes its screens with it, pointer reporting included.
1425
- this.#setPointerReporting(false);
1426
1372
  this.root.setInputSurface(null);
1427
1373
  this.root.resetExtensionUi();
1428
1374
  this.root.resetWorkflowPresentation();
@@ -1443,7 +1389,7 @@ export class OwnedUiSessionShell {
1443
1389
  this.#dialogHandle?.hide();
1444
1390
  // Any-event reporting: hover and drag are what the screen is driven by, and
1445
1391
  // it also stops the terminal treating a drag as a text selection.
1446
- this.#setPointerReporting(true);
1392
+ this.runtime.writeControl(MOUSE_TRACKING_ON);
1447
1393
  // The interrupt chord is global, so it is watched on raw input rather than
1448
1394
  // through the overlay: the pinned shell handles that key before an overlay
1449
1395
  // ever sees it, which is why an owned screen must not rely on being asked.
@@ -1466,7 +1412,7 @@ export class OwnedUiSessionShell {
1466
1412
  });
1467
1413
  const closeSurface = () => {
1468
1414
  removeInterruptWatch();
1469
- this.#setPointerReporting(false);
1415
+ this.runtime.writeControl(MOUSE_TRACKING_OFF);
1470
1416
  this.#dialogHandle?.hide();
1471
1417
  this.#dialogHandle = undefined;
1472
1418
  this.#dialogId = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.8-dev.0b6d8cc",
3
+ "version": "0.1.8-dev.235df0e",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.13.0",