@timurproko/a1 0.2.1-dev.590 → 0.2.1-dev.600
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 +15 -9
- package/dist/app/session-shell/response-copy-helper.js +0 -4
- package/dist/app/session-shell/session-shell.js +3 -3
- package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +9 -8
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +29 -28
- package/dist/integrations/pi/engine/adapter.d.ts +2 -0
- package/dist/integrations/pi/engine/adapter.js +4 -0
- package/dist/integrations/pi/engine/session-runtime.js +15 -6
- package/dist/integrations/pi/engine/settings-port.d.ts +2 -0
- package/dist/integrations/pi/engine/settings-port.js +10 -0
- package/dist/integrations/pi/engine/workflow-runner.js +1 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/ui/components/transcript-viewport.d.ts +1 -1
- package/dist/ui/components/transcript-viewport.js +3 -2
- package/docs/architecture/toolchain.md +1 -1
- package/docs/ci-release-runbook.md +33 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,20 +3,26 @@
|
|
|
3
3
|
## Install
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
|
|
6
|
+
# stable release
|
|
7
|
+
npx -y @timurproko/a1-install
|
|
8
|
+
|
|
9
|
+
# development channel
|
|
10
|
+
npx -y @timurproko/a1-install --develop
|
|
11
|
+
|
|
12
|
+
# exact development version
|
|
13
|
+
npx -y @timurproko/a1-install --version 0.1.8-dev.107
|
|
7
14
|
```
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
The installer keeps npm's dependency transcript out of the normal terminal output,
|
|
17
|
+
shows one progress row, activates the installed release, and verifies the command
|
|
18
|
+
before reporting success. The shorter `npx @timurproko/a1-install` form also works,
|
|
19
|
+
but npm may ask for first-use confirmation; `-y` avoids that prompt.
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
# dev channel (next tag)
|
|
14
|
-
npm install -g @timurproko/a1@next
|
|
21
|
+
Direct npm installation remains available as a fallback or manual recovery path:
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
```sh
|
|
17
24
|
npm install -g @timurproko/a1@latest
|
|
18
|
-
|
|
19
|
-
# exact version
|
|
25
|
+
npm install -g @timurproko/a1@next
|
|
20
26
|
npm install -g @timurproko/a1@0.1.8-dev.107
|
|
21
27
|
```
|
|
22
28
|
|
|
@@ -70,10 +70,6 @@ async function receive(message) {
|
|
|
70
70
|
send({ kind: "phase", phase: "extracted", bytes });
|
|
71
71
|
const text = parts.join("\n");
|
|
72
72
|
parts = [];
|
|
73
|
-
if (text.length === 0) {
|
|
74
|
-
finish({ outcome: "failed", failure: "size" });
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
73
|
if (header.mode === "prepare") {
|
|
78
74
|
// Protocol: this success acknowledges prepared IPC only; the owner must still await its injected writer.
|
|
79
75
|
output = text;
|
|
@@ -866,12 +866,12 @@ export class OwnedUiSessionShell {
|
|
|
866
866
|
this.root.setInputSurface(null);
|
|
867
867
|
this.runtime.requestRender();
|
|
868
868
|
};
|
|
869
|
-
const select = (level
|
|
869
|
+
const select = (level) => {
|
|
870
870
|
close();
|
|
871
|
-
void this.runWorkflow({ command: "thinking", argument: "", selection: level
|
|
871
|
+
void this.runWorkflow({ command: "thinking", argument: "", selection: level });
|
|
872
872
|
};
|
|
873
873
|
const { createPiShellThinkingSelector } = await import("../../integrations/pi/components/thinking-selector-dialog.js");
|
|
874
|
-
const component = createPiShellThinkingSelector(snapshot.thinkingLevel, snapshot.availableThinkingLevels,
|
|
874
|
+
const component = createPiShellThinkingSelector(snapshot.thinkingLevel, snapshot.availableThinkingLevels, select, close, level => this.backend.setDefaultThinkingLevel(level), snapshot.defaultThinkingLevel, this.#customViewport ? {
|
|
875
875
|
profile: "bare",
|
|
876
876
|
cycleBinding: this.root.editor.keybindingConfig()["app.thinking.cycle"] ?? [],
|
|
877
877
|
} : undefined);
|
|
@@ -5,4 +5,4 @@ export interface PiShellThinkingSelectorPresentation {
|
|
|
5
5
|
readonly cycleBinding: string | readonly string[];
|
|
6
6
|
}
|
|
7
7
|
/** Lazily loaded because thinking-selector code does not belong to the startup graph before `/thinking` opens. */
|
|
8
|
-
export declare function createPiShellThinkingSelector(currentLevel: ThinkingSelectorLevel, availableLevels: readonly ThinkingSelectorLevel[], onSelect: (level:
|
|
8
|
+
export declare function createPiShellThinkingSelector(currentLevel: ThinkingSelectorLevel, availableLevels: readonly ThinkingSelectorLevel[], onSelect: (level: ThinkingSelectorLevel) => void, onCancel: () => void, onSelectAsDefault?: (level: ThinkingSelectorLevel) => void, defaultLevel?: ThinkingSelectorLevel, presentation?: PiShellThinkingSelectorPresentation): PiShellComponentPort;
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
* Provenance: @earendil-works/pi-coding-agent 0.87.1 (MIT), commit f07218c4d4bbc12bef056a7058c3dd49dfe41abe,
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/thinking-selector.ts.
|
|
4
4
|
* Modifications: Preserve the searchable thinking-level selector, current/default semantics,
|
|
5
|
-
* selection,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* public pinned component.
|
|
12
|
-
* Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading
|
|
5
|
+
* selection, and focus while accepting the active bare-A1 cycle-key label from the shell, styling the
|
|
6
|
+
* title with the established bold semantic accent treatment, placing it directly below the top rule
|
|
7
|
+
* through the shared compact padded modal frame and its muted hint directly below it, deduplicating
|
|
8
|
+
* levels, and rendering aligned muted descriptions after fixed active and bracketed default columns.
|
|
9
|
+
* Bare A1 persists defaults immediately on Space, closes only on Escape, and uses the shared compact
|
|
10
|
+
* semantic shortcut row. All list and border colors use the owned theme and its explicit color mode.
|
|
11
|
+
* The comparison profile retains the public pinned component.
|
|
12
|
+
* Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading,
|
|
13
|
+
* owned-thinking-selector-controls.
|
|
13
14
|
*/
|
|
14
15
|
import { Container, type Focusable, SelectList } from "@earendil-works/pi-tui";
|
|
15
16
|
export type ThinkingSelectorLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
* Provenance: @earendil-works/pi-coding-agent 0.87.1 (MIT), commit f07218c4d4bbc12bef056a7058c3dd49dfe41abe,
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/thinking-selector.ts.
|
|
4
4
|
* Modifications: Preserve the searchable thinking-level selector, current/default semantics,
|
|
5
|
-
* selection,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* public pinned component.
|
|
12
|
-
* Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading
|
|
5
|
+
* selection, and focus while accepting the active bare-A1 cycle-key label from the shell, styling the
|
|
6
|
+
* title with the established bold semantic accent treatment, placing it directly below the top rule
|
|
7
|
+
* through the shared compact padded modal frame and its muted hint directly below it, deduplicating
|
|
8
|
+
* levels, and rendering aligned muted descriptions after fixed active and bracketed default columns.
|
|
9
|
+
* Bare A1 persists defaults immediately on Space, closes only on Escape, and uses the shared compact
|
|
10
|
+
* semantic shortcut row. All list and border colors use the owned theme and its explicit color mode.
|
|
11
|
+
* The comparison profile retains the public pinned component.
|
|
12
|
+
* Deviations: owned-modal-shortcut-hints, owned-level-cycle-shortcut, owned-thinking-selector-heading,
|
|
13
|
+
* owned-thinking-selector-controls.
|
|
13
14
|
*/
|
|
14
|
-
import { Container, fuzzyFilter, getKeybindings, Input, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
15
|
+
import { Container, fuzzyFilter, getKeybindings, Input, Key, matchesKey, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
15
16
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
16
17
|
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
17
18
|
import { piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
@@ -72,9 +73,9 @@ export class OwnedThinkingSelectorComponent extends Container {
|
|
|
72
73
|
this.addChild(this.selectListContainer);
|
|
73
74
|
this.addChild(new Spacer(1));
|
|
74
75
|
this.addChild(new Text(renderPiModalShortcutHints([
|
|
75
|
-
{ key: this.keyDisplayText("tui.select.confirm"), action: "
|
|
76
|
-
{ key:
|
|
77
|
-
{ key:
|
|
76
|
+
{ key: this.keyDisplayText("tui.select.confirm"), action: "select" },
|
|
77
|
+
{ key: "Space", action: "default" },
|
|
78
|
+
{ key: "Esc", action: "close" },
|
|
78
79
|
]), 0, 0));
|
|
79
80
|
this.addChild(new DynamicBorder((text) => piTheme().fg("border", text)));
|
|
80
81
|
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1, header });
|
|
@@ -88,20 +89,15 @@ export class OwnedThinkingSelectorComponent extends Container {
|
|
|
88
89
|
.join("/");
|
|
89
90
|
}
|
|
90
91
|
buildSelectList(items, preselect) {
|
|
91
|
-
const
|
|
92
|
-
const primaryWidth = this.allItems.reduce((widest, item) => {
|
|
93
|
-
const level = item.label ?? item.value;
|
|
94
|
-
return Math.max(widest, level.length + markerWidth(item));
|
|
95
|
-
}, 0);
|
|
92
|
+
const levelWidth = this.allItems.reduce((widest, item) => Math.max(widest, (item.label ?? item.value).length), 0);
|
|
96
93
|
const themedItems = items.map((item) => {
|
|
97
|
-
const level = item.label ?? item.value;
|
|
98
|
-
const currentMarker = item.value === this.currentLevel ? ` ${piTheme().fg("success", "✓")}` : "";
|
|
94
|
+
const level = (item.label ?? item.value).padEnd(levelWidth);
|
|
95
|
+
const currentMarker = item.value === this.currentLevel ? ` ${piTheme().fg("success", "✓")}` : " ";
|
|
99
96
|
const defaultMarker = item.value === this.defaultThinkingLevel
|
|
100
97
|
? ` ${piTheme().fg("muted", "[default]")}`
|
|
101
|
-
: "";
|
|
102
|
-
const separator = " ".repeat(Math.max(1, primaryWidth - level.length - markerWidth(item) + 1));
|
|
98
|
+
: " ";
|
|
103
99
|
const description = item.description ? piTheme().fg("muted", item.description) : "";
|
|
104
|
-
return { value: item.value, label: `${level}${currentMarker}${defaultMarker}${
|
|
100
|
+
return { value: item.value, label: `${level}${currentMarker}${defaultMarker} ${description}` };
|
|
105
101
|
});
|
|
106
102
|
// Invariant: the bare selector uses the owned theme's explicit color mode, not upstream's host detection.
|
|
107
103
|
const list = new SelectList(themedItems, Math.max(1, themedItems.length), {
|
|
@@ -115,7 +111,6 @@ export class OwnedThinkingSelectorComponent extends Container {
|
|
|
115
111
|
if (currentIndex !== -1)
|
|
116
112
|
list.setSelectedIndex(currentIndex);
|
|
117
113
|
list.onSelect = (item) => this.onSelect(item.value);
|
|
118
|
-
list.onCancel = () => this.onCancel();
|
|
119
114
|
return list;
|
|
120
115
|
}
|
|
121
116
|
applyFilter(query) {
|
|
@@ -130,16 +125,22 @@ export class OwnedThinkingSelectorComponent extends Container {
|
|
|
130
125
|
}
|
|
131
126
|
handleInput(keyData) {
|
|
132
127
|
const kb = getKeybindings();
|
|
133
|
-
if (
|
|
128
|
+
if (keyData === " ") {
|
|
134
129
|
const item = this.selectList.getSelectedItem();
|
|
135
|
-
if (item)
|
|
136
|
-
this.
|
|
130
|
+
if (item) {
|
|
131
|
+
this.defaultThinkingLevel = item.value;
|
|
132
|
+
this.applyFilter(this.searchInput.getValue());
|
|
133
|
+
this.onSelectAsDefault?.(this.defaultThinkingLevel);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (matchesKey(keyData, Key.escape)) {
|
|
138
|
+
this.onCancel();
|
|
137
139
|
return;
|
|
138
140
|
}
|
|
139
141
|
const isNav = kb.matches(keyData, "tui.select.up") ||
|
|
140
142
|
kb.matches(keyData, "tui.select.down") ||
|
|
141
|
-
kb.matches(keyData, "tui.select.confirm")
|
|
142
|
-
kb.matches(keyData, "tui.select.cancel");
|
|
143
|
+
kb.matches(keyData, "tui.select.confirm");
|
|
143
144
|
if (isNav) {
|
|
144
145
|
this.selectList.handleInput(keyData);
|
|
145
146
|
return;
|
|
@@ -118,6 +118,8 @@ export declare class PiEngineAdapter implements OwnedUiPromptSuggestionGenerator
|
|
|
118
118
|
* theme's name, or a `light/dark` pair meaning "follow the terminal".
|
|
119
119
|
*/
|
|
120
120
|
configuredTheme(): string | undefined;
|
|
121
|
+
/** Persist the global thinking default without changing the live session level. */
|
|
122
|
+
setDefaultThinkingLevel(level: PiPinnedSettingsSnapshot["defaultThinkingLevel"]): void;
|
|
121
123
|
/** Settings port for the live runtime, or null before the runtime is available. */
|
|
122
124
|
settingsPort(): PiSettingsBridge | null;
|
|
123
125
|
/** Which settings surface this adapter serves; hidden-in-bare effects are unbindable in bare mode. */
|
|
@@ -379,6 +379,10 @@ export class PiEngineAdapter {
|
|
|
379
379
|
configuredTheme() {
|
|
380
380
|
return this.#settings.configuredTheme();
|
|
381
381
|
}
|
|
382
|
+
/** Persist the global thinking default without changing the live session level. */
|
|
383
|
+
setDefaultThinkingLevel(level) {
|
|
384
|
+
this.#settings.setDefaultThinkingLevel(level);
|
|
385
|
+
}
|
|
382
386
|
/** Settings port for the live runtime, or null before the runtime is available. */
|
|
383
387
|
settingsPort() {
|
|
384
388
|
return this.#settings.settingsPort();
|
|
@@ -170,11 +170,7 @@ export class PiEngineRuntime {
|
|
|
170
170
|
this.#sessionBindingGeneration += 1;
|
|
171
171
|
this.#session = session;
|
|
172
172
|
this.#sessionCommands = new PiSessionCommandIntegration(session);
|
|
173
|
-
this.#
|
|
174
|
-
this.#compactionProgress = observeCompactionProgress(session, percent => {
|
|
175
|
-
if (this.#session === session && !this.#ports.disposed())
|
|
176
|
-
this.#ports.compactionProgress(percent);
|
|
177
|
-
});
|
|
173
|
+
this.#attachCompactionProgress(session);
|
|
178
174
|
// Invariant: the subscription is live before the adapter rebuilds its state so the extension
|
|
179
175
|
// rebind that finishes the rebuild cannot emit an event nobody is listening to.
|
|
180
176
|
this.#subscribe();
|
|
@@ -193,8 +189,14 @@ export class PiEngineRuntime {
|
|
|
193
189
|
}
|
|
194
190
|
/** Forward the current session's events again under the current generation. */
|
|
195
191
|
resume() {
|
|
196
|
-
|
|
192
|
+
const session = this.#session;
|
|
193
|
+
if (session === undefined)
|
|
197
194
|
return;
|
|
195
|
+
// Invariant: resumed events cannot enter before observation is restored, and repeated resume
|
|
196
|
+
// owns only one subscription and one stream-function wrapper.
|
|
197
|
+
this.#unsubscribe?.();
|
|
198
|
+
this.#unsubscribe = undefined;
|
|
199
|
+
this.#attachCompactionProgress(session);
|
|
198
200
|
this.#subscribe();
|
|
199
201
|
}
|
|
200
202
|
compactionStarted() {
|
|
@@ -342,6 +344,13 @@ export class PiEngineRuntime {
|
|
|
342
344
|
}, this.#repositoryContextPollMs);
|
|
343
345
|
this.#repositoryRefreshTimer.unref?.();
|
|
344
346
|
}
|
|
347
|
+
#attachCompactionProgress(session) {
|
|
348
|
+
this.#compactionProgress?.dispose();
|
|
349
|
+
this.#compactionProgress = observeCompactionProgress(session, percent => {
|
|
350
|
+
if (this.#session === session && !this.#ports.disposed())
|
|
351
|
+
this.#ports.compactionProgress(percent);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
345
354
|
#subscribe() {
|
|
346
355
|
const session = this.#session;
|
|
347
356
|
if (session === undefined)
|
|
@@ -33,6 +33,8 @@ export declare class PiEngineSettings {
|
|
|
33
33
|
* theme's name, or a `light/dark` pair meaning "follow the terminal".
|
|
34
34
|
*/
|
|
35
35
|
configuredTheme(): string | undefined;
|
|
36
|
+
/** Persist the global thinking default without changing the live session level. */
|
|
37
|
+
setDefaultThinkingLevel(level: PiPinnedSettingsSnapshot["defaultThinkingLevel"]): void;
|
|
36
38
|
/** Settings port for the live runtime, or null before the runtime is available. */
|
|
37
39
|
settingsPort(): PiSettingsBridge | null;
|
|
38
40
|
bindSettingsOwner(owner: AgentSettingOwner, handlers: PiSettingOwnerHandlers): () => void;
|
|
@@ -32,6 +32,16 @@ export class PiEngineSettings {
|
|
|
32
32
|
configuredTheme() {
|
|
33
33
|
return this.#ports.runtime()?.services.settingsManager.getThemeSetting();
|
|
34
34
|
}
|
|
35
|
+
/** Persist the global thinking default without changing the live session level. */
|
|
36
|
+
setDefaultThinkingLevel(level) {
|
|
37
|
+
if (!isThinkingLevel(level))
|
|
38
|
+
throw new TypeError("Thinking level is invalid");
|
|
39
|
+
const settings = this.#ports.runtime()?.services.settingsManager;
|
|
40
|
+
if (!settings)
|
|
41
|
+
throw new Error("Settings are unavailable");
|
|
42
|
+
settings.setDefaultThinkingLevel(level);
|
|
43
|
+
this.#ports.emitView();
|
|
44
|
+
}
|
|
35
45
|
/** Settings port for the live runtime, or null before the runtime is available. */
|
|
36
46
|
settingsPort() {
|
|
37
47
|
const settings = this.#ports.runtime()?.services.settingsManager;
|
|
@@ -474,7 +474,7 @@ export class PiWorkflowRunner {
|
|
|
474
474
|
return workflowResult(request.command, "completed", "Compaction requested", undefined, "silent");
|
|
475
475
|
}
|
|
476
476
|
catch (error) {
|
|
477
|
-
return workflowResult(request.command, "failed", error instanceof Error ? error.message : String(error)
|
|
477
|
+
return workflowResult(request.command, "failed", error instanceof Error ? error.message : String(error));
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
480
|
case "resume": {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-27T16:04:54.789Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "9db1726bbe3fc2e8292f2ead1e7e9d0fd4d7d0dc9217b372582bf354b0f565dc",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-27T16:04:46.965Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "d8cda6b0c7cb36c0cc41e90802aceebf6c02eaebbc08a83d7d963d2e918dbd7a",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-27T16:05:39.736Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "60cd9e414acb62dc39d2a00856aa93daa614b37c88784aa36cd4c84eda23bfaf",
|
|
12
12
|
"size": 178176
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -169,7 +169,7 @@ export declare class TranscriptViewport {
|
|
|
169
169
|
releaseSelection(): boolean;
|
|
170
170
|
clearSelection(): boolean;
|
|
171
171
|
selectedText(): string | null;
|
|
172
|
-
/** Captures
|
|
172
|
+
/** Captures visible selected text, not off-screen history. */
|
|
173
173
|
captureSelectedText(): SelectionCopySnapshot | null;
|
|
174
174
|
scrollBy(lines: number, now?: number): boolean;
|
|
175
175
|
scrollTo(position: number, now?: number): boolean;
|
|
@@ -179,14 +179,15 @@ export class TranscriptViewport {
|
|
|
179
179
|
const text = textSelectionText(selection, this.#selectionRows, line => usefulTextLineContent(this.#selectionRows[line] ?? ""));
|
|
180
180
|
return text.length === 0 ? null : text;
|
|
181
181
|
}
|
|
182
|
-
/** Captures
|
|
182
|
+
/** Captures visible selected text, not off-screen history. */
|
|
183
183
|
captureSelectedText() {
|
|
184
184
|
if (!this.#copyableSelection)
|
|
185
185
|
return null;
|
|
186
186
|
const started = performance.now();
|
|
187
|
-
|
|
187
|
+
let text = this.selectedText();
|
|
188
188
|
if (text === null)
|
|
189
189
|
return null;
|
|
190
|
+
text = text.trim();
|
|
190
191
|
const normalized = {
|
|
191
192
|
start: { line: 0, column: 0 },
|
|
192
193
|
end: { line: 0, column: Number.MAX_SAFE_INTEGER },
|
|
@@ -81,7 +81,7 @@ Presentation acceptance is the reader comparing `a1 pi` with pinned Pi. `node sc
|
|
|
81
81
|
|
|
82
82
|
## Publication
|
|
83
83
|
|
|
84
|
-
One workflow publishes both channels: `.github/workflows/release.yml`. Pushes do not publish. Nightly development verification runs at `03:17 UTC`; `npm run develop` explicitly requests a numbered preview and `npm run release -- patch` explicitly requests stable publication after its version pull request is manually merged; it promotes a development version to its stable core. The following development-version pull request also requires manual merge. A preview is stamped as `-dev.<merged pull-request number>` and uses npm `next` only as an internal dist-tag. New candidates pack once and
|
|
84
|
+
One workflow publishes both channels: `.github/workflows/release.yml`. Pushes do not publish. Nightly development verification runs at `03:17 UTC`; `npm run develop` explicitly requests a numbered preview and `npm run release -- patch` explicitly requests stable publication after its version pull request is manually merged; it promotes a development version to its stable core. The following development-version pull request also requires manual merge. A preview is stamped as `-dev.<merged pull-request number>` and uses npm `next` only as an internal dist-tag. New candidates pack the application and dependency-free installer once each and validate their exact bytes on Windows, Linux, and macOS; a repeated nightly verifies both immutable registry tarballs. All publication uses provenance from the `npm-publish` environment, a preview never changes `latest`, and the stable tag, GitHub Release, and `master` are written only after npm has both packages. One global non-cancelling concurrency group serializes the final registry check.
|
|
85
85
|
|
|
86
86
|
`docs/ci-release-runbook.md` is the operational reference.
|
|
87
87
|
|
|
@@ -166,14 +166,17 @@ The command fetches authoritative `origin/develop`, resolves the unique merged p
|
|
|
166
166
|
request associated with that exact commit through GitHub, and derives
|
|
167
167
|
`<major.minor.patch>-dev.<pull-request number>`. Thus GitHub's `develop (#107)`
|
|
168
168
|
source produces `0.1.8-dev.107`. It first checks npm; if the immutable version
|
|
169
|
-
already exists
|
|
170
|
-
|
|
169
|
+
already exists for both the application and installer packages it reports that
|
|
170
|
+
version without dispatching package work. If either immutable artifact is missing,
|
|
171
|
+
it dispatches GitHub Actions, waits, and verifies both published versions. It never
|
|
171
172
|
builds or uploads npm bytes from the workstation.
|
|
172
173
|
|
|
173
174
|
Nightly resolves the same current `origin/develop` source. It runs one platform-independent full documentation review before the platform matrix, and the matrix records that prerequisite instead of repeating the scan four times. It always runs complete verification even if source has not changed. For a new number it packs once and
|
|
174
175
|
runs the suite against that final-version tarball before publication. For an
|
|
175
|
-
existing number it downloads
|
|
176
|
-
against those registry bytes; publication is then a successful
|
|
176
|
+
existing number it downloads both exact npm tarballs and runs package/update and
|
|
177
|
+
installer gates against those registry bytes; publication is then a successful
|
|
178
|
+
no-op. A new or missing installer artifact is dependency-free, packed once, and
|
|
179
|
+
validated on every publication platform before the publish job receives it.
|
|
177
180
|
|
|
178
181
|
Manual and nightly runs share one non-cancelling concurrency group. Their final
|
|
179
182
|
registry check is serialized, so overlapping requests can produce only one publish
|
|
@@ -183,7 +186,9 @@ internal `next` dist-tag and never moves `latest`.
|
|
|
183
186
|
Users install previews with public `develop` terminology:
|
|
184
187
|
|
|
185
188
|
```sh
|
|
186
|
-
a1
|
|
189
|
+
npx -y @timurproko/a1-install --develop
|
|
190
|
+
npx -y @timurproko/a1-install --version 0.1.8-dev.107
|
|
191
|
+
a1 update --develop # update an existing installation
|
|
187
192
|
a1 update --develop 107 # numbered preview
|
|
188
193
|
a1 update --develop 0.1.8-dev.107 # exact full preview version
|
|
189
194
|
```
|
|
@@ -214,13 +219,13 @@ touching anything, and the stable version is never committed to `develop`.
|
|
|
214
219
|
publication for that exact source with the stable version in the request. A changed source is
|
|
215
220
|
an error, not permission to substitute a newer commit.
|
|
216
221
|
2. The workflow stamps the requested version on the checked-out source
|
|
217
|
-
(`npm version <x.y.z> --no-git-tag-version`) before packing
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
3. Only after verified publication of `0.1.8` does the helper commit
|
|
222
|
-
in an owned detached worktree beneath `.worktrees/` (
|
|
223
|
-
|
|
222
|
+
(`npm version <x.y.z> --no-git-tag-version`) before packing the application and
|
|
223
|
+
stages the same exact version into the dependency-free installer before packing
|
|
224
|
+
it. Both tarballs therefore declare `0.1.8` while the tagged commit still
|
|
225
|
+
declares `0.1.8-dev`; their recorded digests bind them to the same source tree.
|
|
226
|
+
3. Only after verified publication of both `0.1.8` packages does the helper commit
|
|
227
|
+
`0.1.9-dev` in an owned detached worktree beneath `.worktrees/` (the application
|
|
228
|
+
manifest, root lockfile, and installer manifest versions change), open the one version PR, and wait for you to
|
|
224
229
|
**merge it manually** with a bounded 30-minute poll. The helper never merges
|
|
225
230
|
PRs or enables auto-merge. Until that merge the helper reports development
|
|
226
231
|
reopening as incomplete, and previews cannot be published from a `develop`
|
|
@@ -236,12 +241,23 @@ only when its branch, original HEAD, and cleanliness remain unchanged. Otherwise
|
|
|
236
241
|
preserve local work and synchronize manually with the reported remote state.
|
|
237
242
|
|
|
238
243
|
Stable publication builds the process guardian on all supported platforms, stamps
|
|
239
|
-
the version, packs once, runs the complete suite
|
|
240
|
-
Windows, Linux, and macOS, publishes to npm
|
|
241
|
-
`npm-publish` environment, and
|
|
242
|
-
|
|
244
|
+
the version, packs each package once, runs the complete suite and installer package
|
|
245
|
+
check against those exact bytes on Windows, Linux, and macOS, publishes both to npm
|
|
246
|
+
`latest` with provenance from the `npm-publish` environment, and exercises the exact
|
|
247
|
+
published installer/application pair in isolated prefixes on every release lane.
|
|
248
|
+
Only after those post-publication checks pass does it write `vx.y.z` on the source commit, record
|
|
249
|
+
the GitHub Release, and fast-forward `master`. A push of the stable version does
|
|
243
250
|
not publish it, and no automation ever pushes one.
|
|
244
251
|
|
|
252
|
+
The installer package does not yet exist on npm, so its first publication needs a
|
|
253
|
+
one-time granular token in the `npm-publish` environment secret
|
|
254
|
+
`NPM_BOOTSTRAP_TOKEN`; the workflow still publishes the exact validated tarball
|
|
255
|
+
with provenance. Immediately after that first successful publication, configure
|
|
256
|
+
`.github/workflows/release.yml` and environment `npm-publish` as the package's npm
|
|
257
|
+
trusted publisher, delete the bootstrap secret, and let npm 11 authenticate future
|
|
258
|
+
publications through GitHub OIDC. The application package continues using its
|
|
259
|
+
existing trusted-publisher configuration throughout.
|
|
260
|
+
|
|
245
261
|
Rules that do not bend:
|
|
246
262
|
|
|
247
263
|
- **Never upload locally rebuilt bytes.** The publisher uploads the artifact the validation ran against and checks its digest before and after.
|
|
@@ -264,7 +280,7 @@ Rules that do not bend:
|
|
|
264
280
|
- **A Pi upgrade proposal needs a re-run:** the sync never force-pushes over `chore/pi-<version>` once it carries a commit the bot did not author; a scheduled re-run posts its fresh verdicts as a comment headed with the version and date and rewrites only the report between the `<!-- pi-upgrade-report -->` markers of the description. To re-run the derived steps and gates on the reviewer's head, dispatch the workflow with `refresh` and the `version`; it checks out the proposal branch, skips bump, evaluation, install, and merge, and reports without pushing. A branch whose commits are all the bot's is recreated from `develop` as before.
|
|
265
281
|
- **A Pi version should not be adopted:** close its proposal pull request with the `pi-upgrade-skipped` label (declared in `config/github-repository-governance.json`) and say why in the closing comment; the next run proposes the newest unskipped version newer than the pin, or nothing, and names the skipped versions in its log. A proposal closed without the label is proposed again the next night. Dispatching the workflow with a `version` ignores skips, which is how a skipped version is reconsidered. To silence the schedule through a release window, set the repository variable `PI_UPGRADE_FREEZE_UNTIL` to a date (`2026-10-01`); scheduled runs exit before proposing until it passes, manual dispatches still run.
|
|
266
282
|
- **Nightly documentation review fails:** inspect the reported paths and rules, identify the introducing merge from the nightly interval, and repair the invariant before unrelated work proceeds.
|
|
267
|
-
- **Development publication fails:** fix the cause and rerun `npm run develop`; an npm version that already exists is never overwritten.
|
|
283
|
+
- **Development publication fails:** inspect both package versions and digests, fix the cause, and rerun `npm run develop`; an npm version that already exists is verified and never overwritten.
|
|
268
284
|
- **Registry verification times out:** a `has not propagated` failure after a successful `npm publish` means npm is still ingesting the upload; it warns that a provenance-signed package "may take a few minutes" and the publisher polls for ten minutes. Confirm the version and its shasum on `https://registry.npmjs.org/<name>/<version>`, then rerun the failed jobs: the final registry check finds the exact bytes, skips `npm publish`, and verification passes. A digest or tag mismatch is not a timeout and is never repaired by rerunning.
|
|
269
285
|
- **Stable publication fails or is uncertain:** no reopening PR was prepared and `develop` still declares the open `-dev` version. Inspect the workflow run, npm, and the `v<version>` tag. When nothing was published, fix the cause and rerun the same target; the registry and tag guards refuse a version that already exists.
|
|
270
286
|
- **Stable is published but reopening stopped:** the helper reports the reopening PR or retained worktree. Merge the pending `chore/release-<x.y.z>-dev` PR by hand once its CI passes, or repair the branch and open the PR yourself; never rerun the release for the published version.
|
package/package.json
CHANGED