@timurproko/a1 0.2.7-dev.754 → 0.2.7-dev.767

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.
Files changed (149) hide show
  1. package/bin/guardian.js +5 -0
  2. package/bin/tab-launch-context.d.ts +23 -0
  3. package/bin/tab-launch-context.js +45 -0
  4. package/bin/ui.js +20 -5
  5. package/dist/app/session-shell/helper-pool.d.ts +3 -5
  6. package/dist/app/session-shell/helper-pool.js +3 -5
  7. package/dist/app/session-shell/session-shell-root.d.ts +29 -1
  8. package/dist/app/session-shell/session-shell-root.js +3 -2
  9. package/dist/app/session-shell/session-shell.js +153 -30
  10. package/dist/app/session-shell/session-viewport-controller.d.ts +1 -1
  11. package/dist/app/session-shell/session-viewport-controller.js +3 -3
  12. package/dist/composition/owned-ui.d.ts +20 -0
  13. package/dist/composition/owned-ui.js +76 -3
  14. package/dist/contracts/owned-ui/session-backend.d.ts +1 -0
  15. package/dist/features/owned-ui/settings-app.js +8 -35
  16. package/dist/foundation/launch-context/index.d.ts +6 -0
  17. package/dist/foundation/launch-context/index.js +15 -0
  18. package/dist/foundation/launch-guardian/main.d.ts +5 -0
  19. package/dist/foundation/launch-guardian/main.js +2 -1
  20. package/dist/foundation/release/bootstrap.d.ts +4 -0
  21. package/dist/foundation/release/bootstrap.js +44 -6
  22. package/dist/foundation/release/dependency-certification.js +54 -19
  23. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  24. package/dist/foundation/release/progress-palette.generated.js +2 -2
  25. package/dist/foundation/resident-tabs/bridge-client.d.ts +41 -0
  26. package/dist/foundation/resident-tabs/bridge-client.js +199 -0
  27. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
  28. package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
  29. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +30 -0
  30. package/dist/foundation/resident-tabs/ensure-terminal-host.js +121 -0
  31. package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
  32. package/dist/foundation/resident-tabs/fault-injection.js +26 -0
  33. package/dist/foundation/resident-tabs/index.d.ts +6 -0
  34. package/dist/foundation/resident-tabs/index.js +3 -0
  35. package/dist/foundation/resident-tabs/prompt-journal.d.ts +65 -0
  36. package/dist/foundation/resident-tabs/prompt-journal.js +214 -0
  37. package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
  38. package/dist/foundation/resident-tabs/protocol.js +195 -0
  39. package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
  40. package/dist/foundation/resident-tabs/recovery.js +205 -0
  41. package/dist/foundation/resident-tabs/routing.d.ts +10 -0
  42. package/dist/foundation/resident-tabs/routing.js +8 -0
  43. package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
  44. package/dist/foundation/resident-tabs/session-sync.js +37 -0
  45. package/dist/foundation/resident-tabs/settings.d.ts +9 -0
  46. package/dist/foundation/resident-tabs/settings.js +65 -0
  47. package/dist/foundation/session-writer-lock/index.d.ts +27 -0
  48. package/dist/foundation/session-writer-lock/index.js +197 -0
  49. package/dist/foundation/startup/startup-descriptor.js +2 -2
  50. package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
  51. package/dist/foundation/terminal-cleanup/index.js +1 -1
  52. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +1 -0
  53. package/dist/foundation/terminal-cleanup/terminal-reset.js +1 -0
  54. package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -1
  55. package/dist/integrations/pi/components/owned-editor-ux.js +2 -2
  56. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
  57. package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
  58. package/dist/integrations/pi/components/shell-shared-facade.d.ts +1 -1
  59. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  60. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
  61. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  62. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  63. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  64. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  65. package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
  66. package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
  67. package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
  68. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  69. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  70. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  71. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  72. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  73. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  74. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  75. package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
  76. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
  77. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
  78. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
  79. package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
  80. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  81. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  82. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  83. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
  84. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +0 -2
  85. package/dist/integrations/pi/components/upstream/components/tool-execution.js +41 -20
  86. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  87. package/dist/integrations/pi/components/upstream/components/tree-selector.js +1 -1
  88. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  89. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  90. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
  91. package/dist/integrations/pi/components/upstream/history/editor-core.js +5 -5
  92. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  93. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  94. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  95. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  96. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  97. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  98. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  99. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  100. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
  101. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  102. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +3 -0
  103. package/dist/integrations/pi/components/upstream/theme/theme.js +24 -1
  104. package/dist/integrations/pi/engine/adapter.js +1 -0
  105. package/dist/integrations/pi/engine/pi-settings-metadata.json +2 -2
  106. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  107. package/dist/integrations/pi/engine/runtime-integration.js +102 -29
  108. package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
  109. package/dist/integrations/pi/engine/session-runtime.js +8 -3
  110. package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
  111. package/dist/integrations/pi/engine/session-selection.js +45 -9
  112. package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
  113. package/dist/integrations/pi/startup-public.js +152 -222
  114. package/dist/integrations/pi/startup-public.manifest.json +266 -177
  115. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  116. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  117. package/dist/native/darwin-arm64/manifest.json +1 -1
  118. package/dist/native/linux-x64/manifest.json +1 -1
  119. package/dist/native/win32-x64/manifest.json +2 -2
  120. package/dist/native/win32-x64/process-guardian.exe +0 -0
  121. package/dist/product-identity.d.ts +1 -1
  122. package/dist/product-identity.js +1 -1
  123. package/dist/product-identity.json +5 -0
  124. package/dist/runtime-payload-inventory.json +5349 -6035
  125. package/dist/ui/components/line-input.d.ts +1 -1
  126. package/dist/ui/components/prompt-input.d.ts +7 -5
  127. package/dist/ui/components/prompt-input.js +7 -7
  128. package/dist/ui/components/transcript-viewport.d.ts +6 -3
  129. package/dist/ui/components/transcript-viewport.js +13 -15
  130. package/dist/ui/settings/declarations.d.ts +49 -1
  131. package/dist/ui/settings/declarations.js +37 -1
  132. package/dist/ui/settings/index.d.ts +5 -3
  133. package/dist/ui/settings/index.js +3 -2
  134. package/dist/ui/settings/manager.d.ts +9 -3
  135. package/dist/ui/settings/manager.js +81 -29
  136. package/dist/ui/settings/migrations.d.ts +6 -0
  137. package/dist/ui/settings/migrations.js +32 -0
  138. package/dist/ui/settings/profile-document.d.ts +26 -0
  139. package/dist/ui/settings/profile-document.js +164 -0
  140. package/dist/ui/settings/resolution.d.ts +2 -0
  141. package/dist/ui/settings/resolution.js +13 -1
  142. package/dist/ui/settings/sections.js +4 -0
  143. package/docs/architecture/internal-naming.md +1 -1
  144. package/docs/architecture/resident-tabs-requirements.md +580 -0
  145. package/docs/architecture/resident-tabs.md +522 -0
  146. package/docs/architecture/terminal-host-proof-gate.md +2 -0
  147. package/docs/openspec-archive-automation.md +17 -16
  148. package/docs/validation.md +1 -1
  149. package/package.json +6 -4
@@ -0,0 +1,14 @@
1
+ import type { PromptJournal } from "./prompt-journal.js";
2
+ import { ResidentTabFaultInjection } from "./fault-injection.js";
3
+ export declare function synchronizeSessionFile(path: string, faults?: ResidentTabFaultInjection): Promise<void>;
4
+ /** Serializes settled-turn sync and journal ordering off the input path. */
5
+ export declare class SettledTurnSynchronizer {
6
+ #private;
7
+ constructor(journal: PromptJournal, faults?: ResidentTabFaultInjection);
8
+ settle(sessionFile: string, submissionId: string, entryId: string): Promise<void>;
9
+ settleMany(sessionFile: string, entries: readonly {
10
+ readonly id: string;
11
+ readonly entryId: string;
12
+ }[]): Promise<void>;
13
+ flush(): Promise<void>;
14
+ }
@@ -0,0 +1,37 @@
1
+ import { open } from "node:fs/promises";
2
+ import { ResidentTabFaultInjection } from "./fault-injection.js";
3
+ export async function synchronizeSessionFile(path, faults = new ResidentTabFaultInjection()) {
4
+ const handle = await open(path, "r+");
5
+ try {
6
+ faults.at("session-before-sync");
7
+ await handle.sync();
8
+ faults.at("session-after-sync");
9
+ }
10
+ finally {
11
+ await handle.close();
12
+ }
13
+ }
14
+ /** Serializes settled-turn sync and journal ordering off the input path. */
15
+ export class SettledTurnSynchronizer {
16
+ #journal;
17
+ #faults;
18
+ #queue = Promise.resolve();
19
+ constructor(journal, faults = new ResidentTabFaultInjection()) {
20
+ this.#journal = journal;
21
+ this.#faults = faults;
22
+ }
23
+ settle(sessionFile, submissionId, entryId) {
24
+ return this.settleMany(sessionFile, [{ id: submissionId, entryId }]);
25
+ }
26
+ settleMany(sessionFile, entries) {
27
+ const operation = this.#queue.then(async () => {
28
+ await synchronizeSessionFile(sessionFile, this.#faults);
29
+ this.#faults.at("journal-after-settle-sync");
30
+ for (const entry of entries)
31
+ await this.#journal.settle(entry.id, entry.entryId);
32
+ });
33
+ this.#queue = operation.catch(() => undefined);
34
+ return operation;
35
+ }
36
+ flush() { return this.#queue; }
37
+ }
@@ -0,0 +1,9 @@
1
+ export interface ResidentTabLaunchSettings {
2
+ readonly residentTabs: boolean;
3
+ readonly maxConcurrentStarts: number;
4
+ readonly unresponsiveRestartSeconds: number;
5
+ readonly stallNoticeSeconds: number;
6
+ }
7
+ /** Read only resident-launch values before any UI or extension module is loaded. */
8
+ export declare function readResidentTabLaunchSettings(profileDir: string, namespace?: string, legacyConfigDir?: string, profileId?: string): Promise<ResidentTabLaunchSettings>;
9
+ export declare function readResidentTabsSetting(profileDir: string, namespace?: string, legacyConfigDir?: string, profileId?: string): Promise<boolean>;
@@ -0,0 +1,65 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_MIGRATIONS, OWNED_UI_SETTINGS_VERSION, normalizeOwnedUiSettingsValues, parseOwnedUiSettingsDocument, readProfileSettingsNamespace, repairProfileSettingsDocument, resolveOwnedUiSettings, settingValue, } from "../../ui/settings/index.js";
4
+ const MAX_SETTINGS_BYTES = 256 * 1024;
5
+ const DEFAULTS = Object.freeze({
6
+ residentTabs: false,
7
+ maxConcurrentStarts: 2,
8
+ unresponsiveRestartSeconds: 120,
9
+ stallNoticeSeconds: 300,
10
+ });
11
+ /** Read only resident-launch values before any UI or extension module is loaded. */
12
+ export async function readResidentTabLaunchSettings(profileDir, namespace = "a1", legacyConfigDir, profileId = namespace) {
13
+ const sharedFile = resolve(profileDir, "settings.json");
14
+ try {
15
+ repairProfileSettingsDocument(sharedFile);
16
+ }
17
+ catch {
18
+ // Compatibility: recovery is opportunistic; resident launch falls back when bytes stay invalid.
19
+ }
20
+ const shared = readProfileSettingsNamespace(sharedFile, namespace);
21
+ if (shared.status === "invalid")
22
+ return DEFAULTS;
23
+ if (shared.status === "available") {
24
+ const compatibility = normalizeOwnedUiSettingsValues(shared.value);
25
+ return compatibility.values === null
26
+ ? DEFAULTS
27
+ : resolveLaunchValues({ version: OWNED_UI_SETTINGS_VERSION, values: compatibility.values }, []);
28
+ }
29
+ const file = resolve(legacyConfigDir ?? profileDir, "settings", `${profileId}.json`);
30
+ try {
31
+ const raw = await readFile(file, "utf8");
32
+ if (Buffer.byteLength(raw, "utf8") > MAX_SETTINGS_BYTES)
33
+ return DEFAULTS;
34
+ const document = parseOwnedUiSettingsDocument(raw);
35
+ return document === null ? DEFAULTS : resolveLaunchValues(document, OWNED_UI_SETTINGS_MIGRATIONS);
36
+ }
37
+ catch {
38
+ return DEFAULTS;
39
+ }
40
+ }
41
+ export async function readResidentTabsSetting(profileDir, namespace = "a1", legacyConfigDir, profileId = namespace) {
42
+ return (await readResidentTabLaunchSettings(profileDir, namespace, legacyConfigDir, profileId)).residentTabs;
43
+ }
44
+ function resolveLaunchValues(document, migrations) {
45
+ const resolution = resolveOwnedUiSettings({
46
+ declarations: [
47
+ OWNED_SETTING_DECLARATIONS.residentTabs,
48
+ OWNED_SETTING_DECLARATIONS.tabsMaxConcurrentStarts,
49
+ OWNED_SETTING_DECLARATIONS.tabsUnresponsiveRestartSeconds,
50
+ OWNED_SETTING_DECLARATIONS.tabsStallNoticeSeconds,
51
+ ],
52
+ migrations,
53
+ document,
54
+ currentVersion: OWNED_UI_SETTINGS_VERSION,
55
+ });
56
+ const maximum = settingValue(resolution, "tabsMaxConcurrentStarts");
57
+ const unresponsive = settingValue(resolution, "tabsUnresponsiveRestartSeconds");
58
+ const stall = settingValue(resolution, "tabsStallNoticeSeconds");
59
+ return {
60
+ residentTabs: settingValue(resolution, "residentTabs") === true,
61
+ maxConcurrentStarts: typeof maximum === "number" ? maximum : DEFAULTS.maxConcurrentStarts,
62
+ unresponsiveRestartSeconds: typeof unresponsive === "number" ? unresponsive : DEFAULTS.unresponsiveRestartSeconds,
63
+ stallNoticeSeconds: typeof stall === "number" ? stall : DEFAULTS.stallNoticeSeconds,
64
+ };
65
+ }
@@ -0,0 +1,27 @@
1
+ export interface NativeLock {
2
+ readonly bound: boolean;
3
+ bindCreated(path: string): void;
4
+ release(): void;
5
+ }
6
+ export interface LockBackend {
7
+ acquire(path: string): NativeLock;
8
+ }
9
+ /** Reports that a canonical session identity already has a live writer. */
10
+ export declare class SessionWriterConflict extends Error {
11
+ readonly path: string;
12
+ readonly holderKind: string;
13
+ constructor(path: string, holderKind?: string);
14
+ }
15
+ export interface SessionWriterHandle {
16
+ readonly path: string;
17
+ readonly bound: boolean;
18
+ bind(createdPath?: string): void;
19
+ release(): void;
20
+ }
21
+ export declare function acquireExisting(path: string): SessionWriterHandle;
22
+ export declare function reserveNew(parent: string, name: string): SessionWriterHandle;
23
+ export declare function acquireSessionWriter(path: string): SessionWriterHandle;
24
+ export declare function bind(handle: SessionWriterHandle, createdPath?: string): void;
25
+ export declare function release(handle: SessionWriterHandle): void;
26
+ /** Test-only backend seam; passing undefined restores native discovery. */
27
+ export declare function setSessionWriterLockBackendForTest(value: LockBackend | undefined): void;
@@ -0,0 +1,197 @@
1
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { tmpdir } from "node:os";
4
+ import { basename, dirname, join, resolve } from "node:path";
5
+ import { createHash } from "node:crypto";
6
+ /** Reports that a canonical session identity already has a live writer. */
7
+ export class SessionWriterConflict extends Error {
8
+ path;
9
+ holderKind;
10
+ constructor(path, holderKind = "another A1 process") {
11
+ super(`session is open in ${holderKind}`);
12
+ this.name = "SessionWriterConflict";
13
+ this.path = path;
14
+ this.holderKind = holderKind;
15
+ }
16
+ }
17
+ let backendOverride;
18
+ let cachedBackend;
19
+ function nativeCandidates() {
20
+ const suffix = process.platform === "win32" ? "win32" : process.platform;
21
+ const target = `${suffix}-${process.arch}`;
22
+ return [
23
+ process.env.A1_SESSION_LOCK_PATH,
24
+ resolve(process.cwd(), "dist", "native", target, "a1_session_lock.node"),
25
+ resolve(process.cwd(), ".artifacts", "terminal-host", "a1_session_lock.node"),
26
+ ].filter((value) => typeof value === "string" && value.length > 0);
27
+ }
28
+ function loadNativeBackend() {
29
+ const require = createRequire(import.meta.url);
30
+ for (const candidate of nativeCandidates()) {
31
+ if (!existsSync(candidate))
32
+ continue;
33
+ try {
34
+ const loaded = require(candidate);
35
+ if (loaded.SessionLock?.acquire)
36
+ return loaded.SessionLock;
37
+ }
38
+ catch {
39
+ // Compatibility: an addon for another Node ABI is unusable; try the next location.
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ function processIsLive(pid) {
45
+ try {
46
+ process.kill(pid, 0);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ /** Development fallback. Packaged builds use the Node-API lock above. */
54
+ function portableBackend() {
55
+ const root = join(tmpdir(), "a1-session-writer-locks");
56
+ mkdirSync(root, { recursive: true, mode: 0o700 });
57
+ const authorityPath = (kind, key) => join(root, `${createHash("sha256").update(`${kind}\0${key}`).digest("hex")}.lock`);
58
+ const locationKey = (path) => `${realpathSync.native(dirname(path))}\0${process.platform === "win32" ? basename(path).toLowerCase() : basename(path)}`;
59
+ const identityKey = (path) => {
60
+ const physical = realpathSync.native(path);
61
+ const metadata = statSync(physical, { bigint: true });
62
+ return `${metadata.dev}:${metadata.ino}`;
63
+ };
64
+ const acquireAuthority = (path) => {
65
+ for (let attempt = 0; attempt < 2; attempt += 1) {
66
+ try {
67
+ const descriptor = openSync(path, "wx", 0o600);
68
+ writeFileSync(descriptor, JSON.stringify({ pid: process.pid, kind: process.env.A1_TAB_ID ? "resident tab" : "another A1 process" }));
69
+ closeSync(descriptor);
70
+ return;
71
+ }
72
+ catch (error) {
73
+ if (attempt === 0) {
74
+ try {
75
+ const record = JSON.parse(readFileSync(path, "utf8"));
76
+ if (typeof record.pid === "number" && !processIsLive(record.pid)) {
77
+ rmSync(path, { force: true });
78
+ continue;
79
+ }
80
+ }
81
+ catch {
82
+ // Security: malformed or racing lock evidence remains authoritative and fails closed.
83
+ }
84
+ }
85
+ throw error;
86
+ }
87
+ }
88
+ };
89
+ return {
90
+ acquire(requested) {
91
+ const absolute = resolve(requested);
92
+ const location = authorityPath("location", locationKey(absolute));
93
+ acquireAuthority(location);
94
+ let identity;
95
+ let identityAcquired = false;
96
+ let bound = false;
97
+ try {
98
+ if (existsSync(absolute)) {
99
+ const key = identityKey(absolute);
100
+ identity = authorityPath("identity", key);
101
+ acquireAuthority(identity);
102
+ identityAcquired = true;
103
+ if (identityKey(absolute) !== key) {
104
+ throw new Error("session identity changed while its writer lock was acquired");
105
+ }
106
+ rmSync(location, { force: true });
107
+ bound = true;
108
+ }
109
+ }
110
+ catch (error) {
111
+ rmSync(location, { force: true });
112
+ if (identityAcquired && identity !== undefined)
113
+ rmSync(identity, { force: true });
114
+ throw error;
115
+ }
116
+ let released = false;
117
+ return {
118
+ get bound() { return bound; },
119
+ bindCreated(created) {
120
+ if (bound || resolve(created) !== absolute || !existsSync(absolute)) {
121
+ throw new Error("created session does not match its reservation");
122
+ }
123
+ const key = identityKey(absolute);
124
+ identity = authorityPath("identity", key);
125
+ acquireAuthority(identity);
126
+ if (identityKey(absolute) !== key) {
127
+ rmSync(identity, { force: true });
128
+ identity = undefined;
129
+ throw new Error("created session identity changed during bind");
130
+ }
131
+ rmSync(location, { force: true });
132
+ bound = true;
133
+ },
134
+ release() {
135
+ if (released)
136
+ throw new Error("session lock was already released");
137
+ released = true;
138
+ rmSync(bound ? identity : location, { force: true });
139
+ },
140
+ };
141
+ },
142
+ };
143
+ }
144
+ function backend() {
145
+ return backendOverride ?? (cachedBackend ??= loadNativeBackend() ?? portableBackend());
146
+ }
147
+ function acquire(path) {
148
+ const absolute = resolve(path);
149
+ let lock;
150
+ try {
151
+ lock = backend().acquire(absolute);
152
+ }
153
+ catch {
154
+ throw new SessionWriterConflict(absolute, process.env.A1_TAB_ID ? "a resident tab" : "another A1 process");
155
+ }
156
+ let released = false;
157
+ return {
158
+ path: absolute,
159
+ get bound() { return lock.bound; },
160
+ bind(createdPath = absolute) {
161
+ if (released)
162
+ throw new Error("session writer lock was released");
163
+ if (!lock.bound)
164
+ lock.bindCreated(resolve(createdPath));
165
+ },
166
+ release() {
167
+ if (released)
168
+ return;
169
+ released = true;
170
+ lock.release();
171
+ },
172
+ };
173
+ }
174
+ export function acquireExisting(path) {
175
+ if (!existsSync(path))
176
+ throw new Error(`session file does not exist: ${path}`);
177
+ return acquire(path);
178
+ }
179
+ export function reserveNew(parent, name) {
180
+ if (!name || basename(name) !== name)
181
+ throw new Error("session reservation requires one file name");
182
+ return acquire(join(parent, name));
183
+ }
184
+ export function acquireSessionWriter(path) {
185
+ return acquire(path);
186
+ }
187
+ export function bind(handle, createdPath) {
188
+ handle.bind(createdPath);
189
+ }
190
+ export function release(handle) {
191
+ handle.release();
192
+ }
193
+ /** Test-only backend seam; passing undefined restores native discovery. */
194
+ export function setSessionWriterLockBackendForTest(value) {
195
+ backendOverride = value;
196
+ cachedBackend = undefined;
197
+ }
@@ -10,7 +10,7 @@ export const STARTUP_DESCRIPTOR = Object.freeze({
10
10
  ],
11
11
  "generatedArtifact": {
12
12
  "path": "dist/integrations/pi/startup-public.js",
13
- "sha256": "62906f5056fabed7a83b97843e3a407c955103ab2bb7849e6713026fe7c760a9"
13
+ "sha256": "2e5c23263868c689b0b76648a7565fe4384f8144f4c11b64569a08fc4d3502c2"
14
14
  },
15
15
  "dependencyLayerIdentity": {
16
16
  "source": "A1_RELEASE_LAYERS",
@@ -19,7 +19,7 @@ export const STARTUP_DESCRIPTOR = Object.freeze({
19
19
  "compileCacheNamespace": {
20
20
  "strategy": "node-abi-version-and-ordered-layer-sha256-v1"
21
21
  },
22
- "identity": "089bcfdc784e7d61cd028ede3b5546864ab49e6329e1296ed55afacb16bcb077"
22
+ "identity": "5d1fea810b3c76b1560dd3a743dd4db0b8d03935d9f5079fd4b2da55d4f1084a"
23
23
  });
24
24
 
25
25
  export function loadDeclaredStartupGraph() {
@@ -1,3 +1,3 @@
1
- export { EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
1
+ export { CLEAR_EXIT_INTERVAL_MS, EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
2
2
  export { installFatalExit, writeFatalDiagnostic } from "./fatal-exit.js";
3
3
  export type { FatalExitOptions } from "./fatal-exit.js";
@@ -1,2 +1,2 @@
1
- export { EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
1
+ export { CLEAR_EXIT_INTERVAL_MS, EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
2
2
  export { installFatalExit, writeFatalDiagnostic } from "./fatal-exit.js";
@@ -1,3 +1,4 @@
1
+ export declare const CLEAR_EXIT_INTERVAL_MS = 500;
1
2
  export declare const EMERGENCY_TERMINAL_RESET: string;
2
3
  export declare function restoreProcessTerminal(): void;
3
4
  export declare function restoreAfterOwnedExit(ownsTerminal: boolean, code: number | null, signal: string | null, restore?: () => void): number;
@@ -1,4 +1,5 @@
1
1
  import { writeSync } from "node:fs";
2
+ export const CLEAR_EXIT_INTERVAL_MS = 500;
2
3
  // Protocol: reset modes on both screens; setting keyboard flags to zero is idempotent,
3
4
  // unlike repeatedly popping a keyboard-protocol stack owned by a parent application.
4
5
  // Resetting parent-screen margins homes the cursor, so preserve its restored position.
@@ -44,7 +44,7 @@ export declare class OwnedEditorUxInterception {
44
44
  pasteClipboard(): boolean;
45
45
  }
46
46
  export interface PromptSelectionUxOptions {
47
- readonly copyText: (text: string) => void;
47
+ readonly copyText: (text: string, cut: boolean) => void;
48
48
  readonly readClipboardContent: () => Promise<PiShellClipboardContent | null>;
49
49
  readonly beginClipboardPaste?: () => PiShellPasteReservation;
50
50
  readonly beginTextPaste?: (text: string) => PiShellPasteReservation;
@@ -88,12 +88,12 @@ class PromptSelectionInterceptor {
88
88
  return;
89
89
  }
90
90
  if (this.keybindings.matches(data, "tui.input.copy") && this.hasSelection()) {
91
- this.options.copyText(this.options.expandCopiedText(this.#selectedText()));
91
+ this.options.copyText(this.options.expandCopiedText(this.#selectedText()), false);
92
92
  this.#collapseCopiedSelection();
93
93
  return;
94
94
  }
95
95
  if (this.keybindings.matches(data, "owned.editor.cut") && this.hasSelection()) {
96
- this.options.copyText(this.options.expandCopiedText(this.#selectedText()));
96
+ this.options.copyText(this.options.expandCopiedText(this.#selectedText()), true);
97
97
  if (!this.#deleteSelection())
98
98
  this.#deleteAtomicFocus();
99
99
  return;
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Attributed owned theme resources adapted from @earendil-works/pi-coding-agent 1.1.0 (MIT),
3
- * commit abe508e1b89912adde45528136c3221eb69acdd7.
4
- * Upstream dark.json SHA-256: c11a588b714d35300293079b425fb09a3693d4b2d453585d211b08163c648b75
5
- * Upstream light.json SHA-256: f590c51c2bc8b238891efd0e983473ed2e2a6487b6692cc1d4b2845165e703f7
2
+ * Attributed owned theme resources adapted from @earendil-works/pi-coding-agent 1.2.0 (MIT),
3
+ * commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff.
4
+ * Upstream dark.json SHA-256: e1f96982f8cb7d2a48eca7db03c9cb282cb1e14a243680cd855638ecd6a251b6
5
+ * Upstream light.json SHA-256: 2dee6a4be7f765e7fd7728166b7ebf8c29e4ce313f8ec6b38ae50699e3abc2f3
6
6
  * Modifications: removed upstream schema/export metadata; runtime color data is unchanged.
7
7
  */
8
8
  export declare const BUILTIN_THEME_RESOURCES: Readonly<{
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Attributed owned theme resources adapted from @earendil-works/pi-coding-agent 1.1.0 (MIT),
3
- * commit abe508e1b89912adde45528136c3221eb69acdd7.
4
- * Upstream dark.json SHA-256: c11a588b714d35300293079b425fb09a3693d4b2d453585d211b08163c648b75
5
- * Upstream light.json SHA-256: f590c51c2bc8b238891efd0e983473ed2e2a6487b6692cc1d4b2845165e703f7
2
+ * Attributed owned theme resources adapted from @earendil-works/pi-coding-agent 1.2.0 (MIT),
3
+ * commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff.
4
+ * Upstream dark.json SHA-256: e1f96982f8cb7d2a48eca7db03c9cb282cb1e14a243680cd855638ecd6a251b6
5
+ * Upstream light.json SHA-256: 2dee6a4be7f765e7fd7728166b7ebf8c29e4ce313f8ec6b38ae50699e3abc2f3
6
6
  * Modifications: removed upstream schema/export metadata; runtime color data is unchanged.
7
7
  */
8
8
  export const BUILTIN_THEME_RESOURCES = Object.freeze({
@@ -185,7 +185,7 @@ export interface PiShellEditorOptions {
185
185
  readonly onFollowUp?: (() => void) | undefined;
186
186
  readonly onDequeue?: (() => void) | undefined;
187
187
  readonly onPromptSuggestionAccepted?: (text: string) => void;
188
- readonly onCopyText?: (text: string) => void;
188
+ readonly onCopyText?: (text: string, cut: boolean) => void;
189
189
  readonly readClipboardContent?: (signal?: AbortSignal) => Promise<PiShellClipboardContent | null>;
190
190
  readonly beginClipboardPaste?: () => PiShellPasteReservation;
191
191
  readonly beginTextPaste?: (text: string) => PiShellPasteReservation;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/core/keybindings.ts.
4
4
  * Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
5
5
  * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/core/keybindings.ts.
4
4
  * Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
5
5
  * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
@@ -9,7 +9,7 @@
9
9
  * Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut,
10
10
  * owned-input-keybinding-aliases, owned-session-tree-dialog, owned-dialog-ctrl-c-cancel.
11
11
  */
12
- import { matchesKey, TUI_KEYBINDINGS, KeybindingsManager as TuiKeybindingsManager, } from "@earendil-works/pi-tui";
12
+ import { Key, matchesKey, TUI_KEYBINDINGS, KeybindingsManager as TuiKeybindingsManager, } from "@earendil-works/pi-tui";
13
13
  import { existsSync, readFileSync } from "node:fs";
14
14
  import { join } from "node:path";
15
15
  import { getAgentDir } from "../../../../startup-public.js";
@@ -278,14 +278,27 @@ const KEYBINDING_NAME_MIGRATIONS = {
278
278
  function isLegacyKeybindingName(key) {
279
279
  return key in KEYBINDING_NAME_MIGRATIONS;
280
280
  }
281
+ const KEYBINDING_MODIFIERS = ["ctrl", "shift", "alt", "super"];
282
+ const BASE_KEY_PATTERN = `(?:[a-z0-9]|${Object.values(Key)
283
+ .flatMap((value) => (typeof value === "string" ? [value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&")] : []))
284
+ .join("|")})`;
285
+ const DUPLICATE_MODIFIER_PATTERN = KEYBINDING_MODIFIERS
286
+ .map((modifier) => `${modifier}\\+.*${modifier}\\+`)
287
+ .join("|");
288
+ const KEY_ID_PATTERN = new RegExp(`^(?!.*(?:${DUPLICATE_MODIFIER_PATTERN}))(?:(?:${KEYBINDING_MODIFIERS.join("|")})\\+){0,4}${BASE_KEY_PATTERN}$`);
289
+ function isKeyId(value) {
290
+ return typeof value === "string" && KEY_ID_PATTERN.test(value);
291
+ }
281
292
  function toKeybindingsConfig(value) {
282
293
  const config = {};
283
294
  for (const [key, binding] of Object.entries(value)) {
284
- if (typeof binding === "string") {
295
+ if (key === "$schema")
296
+ continue;
297
+ if (isKeyId(binding)) {
285
298
  config[key] = binding;
286
299
  continue;
287
300
  }
288
- if (Array.isArray(binding) && binding.every((entry) => typeof entry === "string")) {
301
+ if (Array.isArray(binding) && binding.every(isKeyId)) {
289
302
  config[key] = binding;
290
303
  }
291
304
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "sourcePackage": "@earendil-works/pi-coding-agent",
3
- "sourceVersion": "1.1.0",
3
+ "sourceVersion": "1.2.0",
4
4
  "upstreamRepository": "https://github.com/earendil-works/pi.git",
5
- "upstreamCommit": "abe508e1b89912adde45528136c3221eb69acdd7",
5
+ "upstreamCommit": "06a466935536f8c9bcacb2d5efc0fc021be1bdff",
6
6
  "sourcePath": "dist/modes/interactive/assets/clankolas.png",
7
7
  "sourceSha256": "169acd0dfe6fbb8d8742ed24a3fc654fd0b2e2d4223c733249c5493723f1b72d",
8
8
  "license": "MIT",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/countdown-timer.ts.
4
4
  * Modifications: Mechanical source port with ECMAScript private fields; imports remain on the public
5
5
  * Pi TUI package root.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/earendil-announcement.ts.
4
4
  * Modifications: Mechanical pinned source port with private imports remapped to public package-root
5
5
  * types/APIs and A1-owned theme boundaries. The unchanged image is loaded lazily from its attributed,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/extension-editor.ts.
4
4
  * Modifications: Mechanical port: remap pi-tui to the root public singleton, use owned
5
5
  * keybindings/theme and external-editor seams, preserve editor layout, hints, focus, submission,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/extension-editor.ts.
4
4
  * Modifications: Mechanical port: remap pi-tui to the root public singleton, use owned
5
5
  * keybindings/theme and external-editor seams, preserve editor layout, hints, focus, submission,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/extension-input.ts.
4
4
  * Modifications: Mechanical port: remap public imports, use ECMAScript private fields, and route the
5
5
  * bare-A1 instruction row and chrome through the shared semantic shortcut and compact padded
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/extension-selector.ts.
4
4
  * Modifications: Mechanical port: remap public imports, use ECMAScript private fields, and route the
5
5
  * bare-A1 instruction row and chrome through the shared semantic shortcut and compact padded
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/mermaid.ts.
4
4
  * Modifications: Mechanical pinned source port with private imports remapped to public package-root
5
5
  * types/APIs and A1-owned theme boundaries; behavior remains acceptance-tested.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/custom-editor.ts.
4
4
  * Modifications: A1-owned class name and synchronized A1 keybinding contract replace the nominal
5
5
  * private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/custom-editor.ts.
4
4
  * Modifications: A1-owned class name and synchronized A1 keybinding contract replace the nominal
5
5
  * private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/pi-logo.ts.
4
4
  * Modifications: Remapped the private theme import to A1's public-backed adapter and composed the
5
5
  * fixed-color logo into the owned startup header.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/scoped-models-selector.ts.
4
4
  * Modifications: Source-synchronized scoped-model selector port: preserve session-only toggles,
5
5
  * search, bulk/provider/reorder actions, dirty state, Ctrl+S persistence, refresh status,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/footer.ts.
4
4
  * Modifications: Consumes neutral owned-UI view data instead of a fabricated concrete AgentSession;
5
5
  * preserves the latest physical route for virtual models; an explicit bare-A1 profile colors the
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.1.0 (MIT), commit abe508e1b89912adde45528136c3221eb69acdd7,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.2.0 (MIT), commit 06a466935536f8c9bcacb2d5efc0fc021be1bdff,
3
3
  * packages/coding-agent/src/modes/interactive/components/session-selector-search.ts.
4
4
  * Modifications: Source-synchronized session search port with only the public SessionInfo type import
5
5
  * remapped.