@timurproko/a1 0.1.8-dev.546 → 0.1.8-dev.558

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.
@@ -42,7 +42,7 @@ export function createConsoleProjectTrustPrompt(options = {}) {
42
42
  optionRow("Trust", selected === 0),
43
43
  optionRow("Do not trust", selected === 1),
44
44
  "",
45
- `${DIM} ↑/↓${MUTED} to navigate ${DIM}Enter${MUTED} to select ${DIM}Esc${MUTED} to cancel${RESET_FG}`,
45
+ `${DIM}↑/↓${MUTED} to navigate ${DIM}Enter${MUTED} to select ${DIM}Esc${MUTED} to cancel${RESET_FG}`,
46
46
  ];
47
47
  output.write(`${CLEAR_HOME}${lines.map(line => clipAnsiSafe(line, width)).join("\n")}`);
48
48
  };
@@ -134,11 +134,11 @@ export class ReferenceScreenApp {
134
134
  : null;
135
135
  const withRail = withScrollbarRail(body.slice(0, bodyHeight), geometry, contentWidth, theme, { presentation });
136
136
  const hint = this.#interruptArmed
137
- ? theme.fg("dim", "press ctrl+c again to exit a1")
138
- : renderShortcutHints(REFERENCE_SCREEN_SHORTCUTS.hintEntries(SCOPE), theme);
137
+ ? theme.fg("dim", " press ctrl+c again to exit a1")
138
+ : renderShortcutHints(REFERENCE_SCREEN_SHORTCUTS.hintEntries(SCOPE), theme, 1);
139
139
  // Compatibility: the v2 reference screen frames its document between two border-coloured rules.
140
140
  const rule = theme.fg("border", "─".repeat(rect.width));
141
- // Compatibility: the footer remains against the left edge, unlike the settings status line.
141
+ // Invariant: dialog chrome shares the title's one-cell inset; document content keeps its own layout.
142
142
  const frame = [rule, ...withRail, rule, padToWidth(hint, rect.width)];
143
143
  // Invariant: a rectangle too small for the chrome still gets exactly its rows, top first.
144
144
  return frame.slice(0, rect.height).concat(Array(Math.max(0, rect.height - frame.length)).fill(""));
@@ -20,8 +20,8 @@ export { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDoc
20
20
  export type { RestartSeal, RestartValidationEvent } from "./restart-certification.js";
21
21
  export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistry } from "./stable-release.js";
22
22
  export type { StableRegistryState, StableRegistryVerificationOptions, StableReleaseEvidence, StableReleaseEvidenceInput } from "./stable-release.js";
23
- export { PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
24
- export type { ProcessRequest, ProcessResult, SelfUpdateOptions, UpdateActivationPhase, UpdateChannel, UpdateFileMetadata, UpdateFileSystem, UpdateLifecycleCoordinator, UpdateMaterializationProgress, UpdateMeasuredPhase, UpdateOutput, UpdateOwnershipAction, UpdatePackageReplacementInput, UpdatePerformanceEvidence, UpdatePhaseTimingEvent, UpdateProcessRunner, UpdateTransactionJournal, } from "./update.js";
23
+ export { PACKAGE_UNLOCK_PATIENCE_MS, PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, isPackageLockError, lockedPackageDiagnostic, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
24
+ export type { ProcessRequest, ProcessResult, SelfUpdateOptions, UpdateActivationPhase, UpdateChannel, UpdateFileMetadata, UpdateFileSystem, UpdateLifecycleCoordinator, UpdateMaterializationProgress, UpdateMeasuredPhase, UpdateOutput, UpdateOwnershipAction, UpdatePackageReplacementInput, UpdatePerformanceEvidence, UpdatePhaseTimingEvent, UpdateProcessRunner, UpdateTransactionJournal, UpdateUnlockPatience, } from "./update.js";
25
25
  export { UPDATE_ACTIVATION_CONTRACT, UPDATE_ACTIVATION_ENTRY, UPDATE_ACTIVATION_MANIFEST_FIELD, activateInstalledRelease, delegateActivation, readActivationContracts, runActivationEntry, } from "./update-activation.js";
26
26
  export type { UpdateActivationCallbacks, UpdateActivationEvent, UpdateActivationRequest } from "./update-activation.js";
27
27
  export { selectSupervisorLaunchReleaseId, selectUpdateLaunchRelease } from "./update-launch.js";
@@ -9,7 +9,7 @@ export { collectRelease, prepareReleaseCleanup, runBoundedReleaseCleanup, runRel
9
9
  export { RELEASE_MANIFEST_FILENAME, assertImmutableExecutionRoot, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease, } from "./release-store.js";
10
10
  export { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument, restartSealDigest } from "./restart-certification.js";
11
11
  export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistry } from "./stable-release.js";
12
- export { PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
12
+ export { PACKAGE_UNLOCK_PATIENCE_MS, PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, isPackageLockError, lockedPackageDiagnostic, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
13
13
  export { UPDATE_ACTIVATION_CONTRACT, UPDATE_ACTIVATION_ENTRY, UPDATE_ACTIVATION_MANIFEST_FIELD, activateInstalledRelease, delegateActivation, readActivationContracts, runActivationEntry, } from "./update-activation.js";
14
14
  export { selectSupervisorLaunchReleaseId, selectUpdateLaunchRelease } from "./update-launch.js";
15
15
  export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, npmPrefixForGlobalRoot, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, updateNpmInstallArguments, } from "./update-recovery.js";
@@ -23,6 +23,16 @@ export interface UpdateFileSystem {
23
23
  realpath(path: string): Promise<string>;
24
24
  lstat?(path: string): Promise<UpdateFileMetadata>;
25
25
  access?(path: string): Promise<void>;
26
+ rename?(from: string, to: string): Promise<void>;
27
+ }
28
+ /**
29
+ * How long the update keeps checking that nothing holds the package tree, and the clock it
30
+ * checks with. Tests inject a clock; the product waits in real time.
31
+ */
32
+ export interface UpdateUnlockPatience {
33
+ readonly windowMs?: number;
34
+ readonly sleep?: (durationMs: number) => Promise<void>;
35
+ readonly now?: () => number;
26
36
  }
27
37
  export interface UpdateOutput {
28
38
  stdout(message: string): void;
@@ -103,6 +113,17 @@ export interface UpdatePackageReplacementInput {
103
113
  readonly environment: NodeJS.ProcessEnv;
104
114
  readonly onRecoveryState: (state: UpdateRecoveryState) => Promise<void>;
105
115
  }
116
+ /**
117
+ * The package tree is checked for holders by renaming it and renaming it back, because on
118
+ * Windows that is the one operation that fails while any file under it is open. A holder is
119
+ * usually brief: a scanner, an indexer, a file browser, or a session still shutting down.
120
+ * The update therefore keeps checking for this long before it calls the package locked.
121
+ */
122
+ export declare const PACKAGE_UNLOCK_PATIENCE_MS = 15000;
123
+ /** Whether a rename failure means something holds the tree, as opposed to the tree being wrong. */
124
+ export declare function isPackageLockError(error: unknown): boolean;
125
+ /** What the user reads when the package stayed held for the whole patience window. */
126
+ export declare function lockedPackageDiagnostic(packageRoot: string, waitedMs: number, attempts: number, error: unknown): string;
106
127
  /** Bound on the child diagnostics an update keeps: enough for npm's failure reason, never a log. */
107
128
  export declare const CHILD_DIAGNOSTIC_LIMIT = 8000;
108
129
  export declare function createNpmProcessRunner(platform?: NodeJS.Platform): UpdateProcessRunner;
@@ -114,7 +135,7 @@ export type UpdateOwnershipAction = "clean-dead-record" | "leave-running" | "end
114
135
  * because that installation is exactly what is about to be replaced under it.
115
136
  */
116
137
  export declare function planUpdateOwnership(ownership: "live-verified" | "dead", runsFromRetainedRelease: boolean): UpdateOwnershipAction;
117
- export declare function createUpdateLifecycleCoordinator(environment?: NodeJS.ProcessEnv, fileSystem?: UpdateFileSystem, output?: UpdateOutput): UpdateLifecycleCoordinator;
138
+ export declare function createUpdateLifecycleCoordinator(environment?: NodeJS.ProcessEnv, fileSystem?: UpdateFileSystem, output?: UpdateOutput, patience?: UpdateUnlockPatience): UpdateLifecycleCoordinator;
118
139
  /** Renders the terminal-only update meter; the visual preview shares this exact frame. */
119
140
  export declare function renderUpdateProgressBar(percent: number): string;
120
141
  export declare function runSelfUpdate(options: SelfUpdateOptions): Promise<number>;
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
- import { access, lstat, readFile, realpath } from "node:fs/promises";
2
+ import { access, lstat, readFile, realpath, rename } from "node:fs/promises";
3
3
  import { connect } from "node:net";
4
4
  import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
5
5
  import crossSpawn from "cross-spawn";
@@ -23,7 +23,32 @@ const defaultFileSystem = {
23
23
  realpath,
24
24
  lstat,
25
25
  access,
26
+ rename,
26
27
  };
28
+ /**
29
+ * The package tree is checked for holders by renaming it and renaming it back, because on
30
+ * Windows that is the one operation that fails while any file under it is open. A holder is
31
+ * usually brief: a scanner, an indexer, a file browser, or a session still shutting down.
32
+ * The update therefore keeps checking for this long before it calls the package locked.
33
+ */
34
+ export const PACKAGE_UNLOCK_PATIENCE_MS = 15_000;
35
+ const PACKAGE_UNLOCK_INITIAL_DELAY_MS = 100;
36
+ const PACKAGE_UNLOCK_MAX_DELAY_MS = 1_000;
37
+ // Platform: Windows reports a held directory as EPERM or EBUSY; EACCES and ENOTEMPTY are the other
38
+ // spellings a rename gives to a tree something is using. Anything else is not a lock.
39
+ const PACKAGE_LOCK_CODES = new Set(["EPERM", "EACCES", "EBUSY", "ENOTEMPTY"]);
40
+ const defaultSleep = async (durationMs) => await new Promise(resolvePromise => setTimeout(resolvePromise, durationMs));
41
+ /** Whether a rename failure means something holds the tree, as opposed to the tree being wrong. */
42
+ export function isPackageLockError(error) {
43
+ return error instanceof Error && "code" in error && typeof error.code === "string" && PACKAGE_LOCK_CODES.has(error.code);
44
+ }
45
+ /** What the user reads when the package stayed held for the whole patience window. */
46
+ export function lockedPackageDiagnostic(packageRoot, waitedMs, attempts, error) {
47
+ const seconds = Math.max(1, Math.round(waitedMs / 1_000));
48
+ return PRODUCT_TEXT.diagnostic(`package remained locked for ${seconds}s after verified shutdown (${attempts} checks): ${errorMessage(error)}. `
49
+ + `Another program is holding a file under ${packageRoot}: an antivirus scan, a file browser or terminal open inside it, `
50
+ + `or a session still shutting down. Close it and run the update again; nothing was changed.`);
51
+ }
27
52
  const defaultOutput = {
28
53
  stdout(message) { process.stdout.write(message); },
29
54
  stderr(message) { process.stderr.write(message); },
@@ -79,7 +104,7 @@ async function readActiveEndpoint(paths, activeReleaseId, environment) {
79
104
  const legacy = await readEndpointMetadata(legacyPaths.endpointMetadataPath);
80
105
  return legacy ? { metadata: legacy, paths: legacyPaths } : null;
81
106
  }
82
- export function createUpdateLifecycleCoordinator(environment = process.env, fileSystem = defaultFileSystem, output = defaultOutput) {
107
+ export function createUpdateLifecycleCoordinator(environment = process.env, fileSystem = defaultFileSystem, output = defaultOutput, patience = {}) {
83
108
  const paths = resolveProductPaths(environment);
84
109
  const stateStore = new CohortStateStore(paths.dataDir);
85
110
  return {
@@ -139,16 +164,40 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
139
164
  if (fileSystem.access)
140
165
  await fileSystem.access(packageRoot);
141
166
  const probe = `${packageRoot}.${PRODUCT_IDENTITY.filesystem.slug}-unlock-probe`;
142
- const { rename } = await import("node:fs/promises");
143
- try {
144
- await rename(packageRoot, probe);
145
- await rename(probe, packageRoot);
146
- }
147
- catch (error) {
148
- // Invariant: best-effort rollback runs if the first rename succeeded and the second did not.
149
- await rename(probe, packageRoot).catch(() => { });
150
- throw new Error(PRODUCT_TEXT.diagnostic(`package remains locked after verified shutdown: ${errorMessage(error)}`));
151
- }
167
+ const renameTree = fileSystem.rename ?? rename;
168
+ const windowMs = patience.windowMs ?? PACKAGE_UNLOCK_PATIENCE_MS;
169
+ const now = patience.now ?? Date.now;
170
+ const sleep = patience.sleep ?? defaultSleep;
171
+ const startedAt = now();
172
+ let delay = PACKAGE_UNLOCK_INITIAL_DELAY_MS;
173
+ let attempts = 0;
174
+ // Rationale: a holder found right after ownership was released is almost always transient,
175
+ // so one failed rename is a reason to look again, not to abandon an update that has not
176
+ // yet changed anything. The wait is bounded so a genuine lock still fails clearly.
177
+ const withPatience = async (from, to, describeFailure) => {
178
+ while (true) {
179
+ attempts += 1;
180
+ try {
181
+ await renameTree(from, to);
182
+ return;
183
+ }
184
+ catch (error) {
185
+ const elapsed = now() - startedAt;
186
+ if (!isPackageLockError(error) || elapsed >= windowMs)
187
+ throw describeFailure(error);
188
+ // Rationale: the last pause is cut to the window's edge so the wait ends when promised.
189
+ await sleep(Math.min(delay, windowMs - elapsed));
190
+ delay = Math.min(PACKAGE_UNLOCK_MAX_DELAY_MS, delay * 2);
191
+ }
192
+ }
193
+ };
194
+ await withPatience(packageRoot, probe, error => isPackageLockError(error)
195
+ ? new Error(lockedPackageDiagnostic(packageRoot, now() - startedAt, attempts, error))
196
+ : new Error(PRODUCT_TEXT.diagnostic(`could not verify that the package is unlocked: ${errorMessage(error)}`)));
197
+ // Invariant: the package tree is put back under its own name before this returns; a holder
198
+ // that appears while it wears the probe name is waited out the same way, and if it never
199
+ // lets go the diagnostic names where the tree is so the user can restore it.
200
+ await withPatience(probe, packageRoot, error => new Error(PRODUCT_TEXT.diagnostic(`could not restore the package after checking it was unlocked: ${errorMessage(error)}. The package is at ${probe}; rename it back to ${packageRoot} before running the update again.`)));
152
201
  },
153
202
  async activateInstalled(packageRoot, targetVersion, phase, onMaterializing, onWarmup) {
154
203
  const request = { packageRoot, dataDir: paths.dataDir, targetVersion, environment };
@@ -228,7 +228,7 @@ export class ModelsDialogComponent {
228
228
  push();
229
229
  if (this.#refreshStatus !== undefined)
230
230
  push(theme.fg(this.#refreshStatus.kind, ` ${this.#refreshStatus.message}`));
231
- push(renderPiModalShortcutHints(this.#hints(), 2));
231
+ push(renderPiModalShortcutHints(this.#hints()));
232
232
  push(border);
233
233
  return lines;
234
234
  }
@@ -16,14 +16,8 @@ export declare const PINNED_PI_BUILTIN_SLASH_COMMANDS: ({
16
16
  export declare const OWNED_BUILTIN_SLASH_COMMANDS: ({
17
17
  readonly name: "thinking";
18
18
  readonly description: "Set thinking level";
19
- readonly argumentHint: "<level>";
20
- } | {
21
- name: string;
22
- description: string;
23
- argumentHint?: never;
24
19
  } | {
25
20
  name: string;
26
21
  description: string;
27
- argumentHint: string;
28
22
  })[];
29
23
  export declare function createPiShellEditor(options: PiShellEditorOptions): PiShellEditorPort;
@@ -9,12 +9,14 @@ import { createTuiFacade, ensureTheme, isAutocompleteProvider, } from "./shell-s
9
9
  import { SKILLS_COMMAND_NAME, collapseSkillCommands, createSkillsTunnelProvider, } from "./skills-command.js";
10
10
  /** A selected autocomplete row: the accent primary column, then the aligned description. */
11
11
  const SELECTED_DESCRIBED_ROW = /^(→ .*?\S)(\s{2,}.*)$/u;
12
- const THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level", argumentHint: "<level>" };
12
+ const PINNED_THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level", argumentHint: "<level>" };
13
+ const OWNED_THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level" };
14
+ const OWNED_LOGIN_SLASH_COMMAND = { name: "login", description: "Configure provider authentication" };
13
15
  export const PINNED_PI_BUILTIN_SLASH_COMMANDS = [
14
16
  { name: "settings", description: "Open settings menu" },
15
17
  { name: "model", description: "Select model (opens selector UI)", argumentHint: "<provider/model>" },
16
18
  { name: "tree", description: "Navigate session tree (switch branches)" },
17
- THINKING_SLASH_COMMAND,
19
+ PINNED_THINKING_SLASH_COMMAND,
18
20
  { name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },
19
21
  { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
20
22
  { name: "import", description: "Import and resume a session from a JSONL file" },
@@ -36,9 +38,16 @@ export const PINNED_PI_BUILTIN_SLASH_COMMANDS = [
36
38
  { name: "quit", description: "Quit" },
37
39
  ];
38
40
  /** Bare A1's built-in catalog: `models` replaces the pinned pair and is followed by `thinking`. */
39
- export const OWNED_BUILTIN_SLASH_COMMANDS = PINNED_PI_BUILTIN_SLASH_COMMANDS.flatMap(command => command.name === "model"
40
- ? [{ name: "models", description: "Switch models and manage scoped model cycling" }, THINKING_SLASH_COMMAND]
41
- : command.name === "thinking" || command.name === "scoped-models" ? [] : [command]);
41
+ export const OWNED_BUILTIN_SLASH_COMMANDS = PINNED_PI_BUILTIN_SLASH_COMMANDS.flatMap(command => {
42
+ if (command.name === "model") {
43
+ return [{ name: "models", description: "Switch models and manage scoped model cycling" }, OWNED_THINKING_SLASH_COMMAND];
44
+ }
45
+ if (command.name === "thinking" || command.name === "scoped-models")
46
+ return [];
47
+ if (command.name === "login")
48
+ return [OWNED_LOGIN_SLASH_COMMAND];
49
+ return [command];
50
+ });
42
51
  export function createPiShellEditor(options) {
43
52
  ensureTheme();
44
53
  const tui = createTuiFacade(options);
@@ -49,7 +49,7 @@ class SkillsSelectorComponent extends Container {
49
49
  { key: "↑↓", action: "navigate" },
50
50
  { key: bindings.getKeys("tui.select.confirm").join("/"), action: "select" },
51
51
  { key: bindings.getKeys("tui.select.cancel").join("/"), action: "cancel" },
52
- ], 2), 0, 0));
52
+ ]), 0, 0));
53
53
  this.addChild(new DynamicBorder());
54
54
  this.#updateList();
55
55
  }
@@ -179,7 +179,7 @@ export class ScopedModelsSelectorComponent extends Container {
179
179
  { key: reorderKeys, action: "reorder" },
180
180
  { key: keyDisplayText("app.models.save"), action: "save" },
181
181
  { action: countText },
182
- ], 2);
182
+ ]);
183
183
  return this.isDirty ? `${hint} ${theme.fg("warning", "(unsaved)")}` : hint;
184
184
  }
185
185
  refresh() {
@@ -74,7 +74,7 @@ export class OwnedThinkingSelectorComponent extends Container {
74
74
  { key: this.keyDisplayText("tui.select.confirm"), action: "to select" },
75
75
  { key: this.keyDisplayText("app.thinking.save"), action: "to set as default" },
76
76
  { key: this.keyDisplayText("tui.select.cancel"), action: "to cancel" },
77
- ], 2), 0, 0));
77
+ ]), 0, 0));
78
78
  this.addChild(new DynamicBorder((text) => piTheme().fg("border", text)));
79
79
  }
80
80
  keyDisplayText(keybinding) {
@@ -1080,7 +1080,8 @@ class TreeHelp {
1080
1080
  return renderPiModalShortcutHints([key ? { key, action: label, ...(labelFirst === undefined ? {} : { actionFirst: labelFirst }) } : { action: label }]);
1081
1081
  });
1082
1082
  const availableWidth = Math.max(1, width);
1083
- const indent = " ";
1083
+ // Match the title's Text padding plus its two-cell authored inset.
1084
+ const indent = " ";
1084
1085
  const separator = " ";
1085
1086
  const lines = [];
1086
1087
  let currentLine = "";
@@ -178,7 +178,7 @@ export class PiResourceCatalog {
178
178
  {
179
179
  name: "login",
180
180
  description: "Configure provider authentication",
181
- argumentHint: "<provider>",
181
+ ...(this.#productMode === "comparison" ? { argumentHint: "<provider>" } : {}),
182
182
  argumentOptions: this.#contexts.loginOptions().map(option => ({ ...option, id: option.id.split(":").at(-1) ?? option.id })),
183
183
  source: "builtin",
184
184
  },
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-09-22T23:31:15.773Z",
8
+ "builtAt": "2026-09-23T10:26:02.976Z",
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-22T23:30:53.377Z",
8
+ "builtAt": "2026-09-23T10:25:44.532Z",
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-22T23:31:43.770Z",
8
+ "builtAt": "2026-09-23T10:26:28.697Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "57ed4829927f81b1cb638ffafa7a5b3832537284aa343c0eb0ef70592ed671d3",
11
+ "sha256": "3350a8cef128cf5a794ff0f9bb46eb6c99dd10f390f40a9083f4b5c13f0e7d62",
12
12
  "size": 177664
13
13
  },
14
14
  "provenance": {
@@ -27,9 +27,9 @@ export function renderDialogPanel(state, width, theme) {
27
27
  });
28
28
  const description = state.rows[state.index]?.description ?? "";
29
29
  const hint = typeof state.hint === "string"
30
- ? theme.fg("dim", ` ${state.hint}`)
31
- : renderShortcutHints(state.hint, theme, 2);
32
- const plainHint = typeof state.hint === "string" ? ` ${state.hint}` : shortcutHintsText(state.hint, 2);
30
+ ? theme.fg("dim", ` ${state.hint}`)
31
+ : renderShortcutHints(state.hint, theme, 1);
32
+ const plainHint = typeof state.hint === "string" ? ` ${state.hint}` : shortcutHintsText(state.hint, 1);
33
33
  return [
34
34
  rule,
35
35
  ...rows,
@@ -92,15 +92,18 @@ Build output mirrors the production namespaces directly under ignored `dist/`, w
92
92
 
93
93
  Every task worktree must be created at `{working-dir}/.worktrees/<task-id>`, where `{working-dir}` is the agent session's initial working directory. The `.worktrees` directory is inside that working directory, not beside it. For working directory `D:/Git/a1`, `D:/Git/a1/.worktrees/<task-id>` is correct and `D:/Git/a1-<task-id>` is forbidden.
94
94
 
95
- From the initial working directory, create and address a task worktree explicitly:
95
+ From the initial working directory, create the task branch and worktree, run `a1 session link-worktree <absolute-worktree>` from the owning A1 session, and continue to address it explicitly:
96
96
 
97
97
  ```sh
98
98
  git fetch origin develop
99
- git worktree add --detach .worktrees/<task-id> origin/develop
99
+ git worktree add -b <type>/<short-description> .worktrees/<task-id> origin/develop
100
+ a1 session link-worktree <absolute-worktree>
100
101
  git -C .worktrees/<task-id> status
101
102
  ```
102
103
 
103
- The primary worktree stays on `develop` for integration and must not be used for task edits. Do not edit, move, or remove another session's worktree.
104
+ A successful link response confirming the exact canonical worktree is required before planning, implementation, test, or delivery-documentation edits. If linking fails or confirms another path, stop task edits and report the blocker instead of continuing with primary-checkout footer metadata. A session resuming an existing delivery or switching streams links the exact owned worktree before editing it.
105
+
106
+ The association switches bare A1's footer repository context and pull-request discovery; it does not change process or tool cwd and does not register, claim, release, or authorize cleanup of the worktree. Repository commands therefore keep an explicit worktree path. The primary worktree stays on `develop` for integration and must not be used for task edits. Do not edit, adopt, move, or remove another session's worktree.
104
107
 
105
108
  ## Documentation and comments
106
109
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.1.8-dev.546",
3
+ "version": "0.1.8-dev.558",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "privateLaunchContract": "neutral-launch-v1",