@timurproko/a1 0.2.6-dev.717 → 0.2.6-dev.734

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 (213) hide show
  1. package/bin/cli.js +13 -2
  2. package/bin/ui.js +13 -0
  3. package/dist/app/session-shell/clipboard-services.d.ts +19 -0
  4. package/dist/app/session-shell/clipboard-services.js +7 -0
  5. package/dist/app/session-shell/exit-notice.d.ts +9 -5
  6. package/dist/app/session-shell/exit-notice.js +5 -5
  7. package/dist/app/session-shell/index.d.ts +3 -1
  8. package/dist/app/session-shell/index.js +1 -0
  9. package/dist/app/session-shell/paste-preparation-client.d.ts +3 -0
  10. package/dist/app/session-shell/paste-preparation-client.js +5 -2
  11. package/dist/app/session-shell/path-chip-presentation.d.ts +10 -3
  12. package/dist/app/session-shell/path-chip-presentation.js +48 -6
  13. package/dist/app/session-shell/prompt-chips.js +30 -4
  14. package/dist/app/session-shell/prompt-suggestion-controller.d.ts +5 -4
  15. package/dist/app/session-shell/prompt-suggestion-controller.js +148 -58
  16. package/dist/app/session-shell/quit-outro.d.ts +3 -1
  17. package/dist/app/session-shell/quit-outro.js +23 -8
  18. package/dist/app/session-shell/response-copy-transport.d.ts +6 -1
  19. package/dist/app/session-shell/response-copy-transport.js +8 -2
  20. package/dist/app/session-shell/session-shell-root.d.ts +22 -11
  21. package/dist/app/session-shell/session-shell-root.js +19 -12
  22. package/dist/app/session-shell/session-shell.d.ts +19 -21
  23. package/dist/app/session-shell/session-shell.js +678 -840
  24. package/dist/app/session-shell/session-viewport-controller.js +33 -21
  25. package/dist/cli/dispatch.js +5 -3
  26. package/dist/cli/session-context.d.ts +5 -1
  27. package/dist/cli/session-context.js +25 -8
  28. package/dist/composition/owned-ui.d.ts +23 -2
  29. package/dist/composition/owned-ui.js +124 -33
  30. package/dist/composition/session-info-reference.d.ts +2 -2
  31. package/dist/composition/session-info-reference.js +2 -2
  32. package/dist/composition/settings-route-host.js +2 -1
  33. package/dist/contracts/agent-engine/capability-ports.d.ts +1 -85
  34. package/dist/contracts/agent-engine/capability-ports.js +1 -35
  35. package/dist/contracts/agent-engine/index.d.ts +1 -7
  36. package/dist/contracts/agent-engine/index.js +0 -4
  37. package/dist/contracts/owned-ui/index.d.ts +9 -3
  38. package/dist/contracts/owned-ui/index.js +2 -1
  39. package/dist/contracts/owned-ui/model.d.ts +10 -0
  40. package/dist/contracts/owned-ui/session-backend.d.ts +119 -0
  41. package/dist/contracts/owned-ui/session-presenters.d.ts +55 -0
  42. package/dist/contracts/owned-ui/session-presenters.js +1 -0
  43. package/dist/contracts/owned-ui/session-workflows.d.ts +342 -0
  44. package/dist/contracts/owned-ui/session-workflows.js +30 -0
  45. package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +5 -1
  46. package/dist/contracts/owned-ui/suggestion-diagnostics.js +3 -1
  47. package/dist/contracts/owned-ui/validation.js +7 -0
  48. package/dist/features/launch/index.d.ts +1 -1
  49. package/dist/features/launch/session-context.d.ts +17 -2
  50. package/dist/features/launch/session-context.js +9 -5
  51. package/dist/features/owned-ui/settings-app.js +56 -54
  52. package/dist/features/prompt-suggestions/diagnostics.js +4 -1
  53. package/dist/foundation/lifecycle/index.d.ts +2 -2
  54. package/dist/foundation/lifecycle/index.js +1 -1
  55. package/dist/foundation/lifecycle/session-repository-context.d.ts +19 -2
  56. package/dist/foundation/lifecycle/session-repository-context.js +434 -66
  57. package/dist/foundation/process-containment/index.d.ts +1 -0
  58. package/dist/foundation/process-containment/index.js +1 -0
  59. package/dist/foundation/process-containment/native-process-inspector.d.ts +3 -0
  60. package/dist/foundation/process-containment/native-process-inspector.js +21 -0
  61. package/dist/foundation/release/bootstrap.js +1 -2
  62. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  63. package/dist/foundation/release/progress-palette.generated.js +2 -2
  64. package/dist/foundation/release/release-gc.js +1 -1
  65. package/dist/foundation/release/update.js +0 -4
  66. package/dist/foundation/startup/startup-descriptor.js +2 -2
  67. package/dist/foundation/storage/control-store.js +0 -12
  68. package/dist/integrations/pi/components/components.js +2 -18
  69. package/dist/integrations/pi/components/index.d.ts +4 -4
  70. package/dist/integrations/pi/components/index.js +2 -2
  71. package/dist/integrations/pi/components/modal-frame.js +3 -4
  72. package/dist/integrations/pi/components/models-dialog.js +6 -35
  73. package/dist/integrations/pi/components/prompt-input-port.d.ts +1 -0
  74. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
  75. package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
  76. package/dist/integrations/pi/components/share-operation-dialog.js +3 -3
  77. package/dist/integrations/pi/components/shell-editor-autocomplete.js +19 -16
  78. package/dist/integrations/pi/components/shell-extension-ui.d.ts +5 -3
  79. package/dist/integrations/pi/components/shell-extension-ui.js +26 -12
  80. package/dist/integrations/pi/components/shell-hotkey-sections.js +2 -2
  81. package/dist/integrations/pi/components/shell-presenters-info.js +10 -10
  82. package/dist/integrations/pi/components/shell-presenters-transcript.js +13 -13
  83. package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -6
  84. package/dist/integrations/pi/components/shell-shared-facade.d.ts +21 -1
  85. package/dist/integrations/pi/components/shell-shared-facade.js +19 -3
  86. package/dist/integrations/pi/components/skills-dialog.js +3 -3
  87. package/dist/integrations/pi/components/submitted-prompt-adapter.js +2 -3
  88. package/dist/integrations/pi/components/theme.d.ts +9 -0
  89. package/dist/integrations/pi/components/theme.js +30 -0
  90. package/dist/integrations/pi/components/tool-result-adapter.js +6 -1
  91. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  92. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +1 -1
  93. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  94. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  95. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  96. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  97. package/dist/integrations/pi/components/upstream/components/extension-editor.js +6 -6
  98. package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -4
  99. package/dist/integrations/pi/components/upstream/components/extension-selector.js +4 -4
  100. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  101. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +5 -5
  102. package/dist/integrations/pi/components/upstream/components/owned-editor.js +8 -5
  103. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  104. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  105. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -3
  106. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  107. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  108. package/dist/integrations/pi/components/upstream/components/session-selector.js +9 -9
  109. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +3 -2
  110. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +6 -3
  111. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  112. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  113. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  114. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  115. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  116. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +2 -2
  117. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +4 -0
  118. package/dist/integrations/pi/components/upstream/components/tool-execution.js +11 -1
  119. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +2 -2
  120. package/dist/integrations/pi/components/upstream/components/tree-selector.js +27 -18
  121. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  122. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  123. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +4 -4
  124. package/dist/integrations/pi/components/upstream/history/editor-core.js +11 -7
  125. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  126. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  127. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  128. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  129. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  130. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  131. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  132. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  133. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +31 -0
  134. package/dist/integrations/pi/components/upstream/program-status-reporter.js +89 -0
  135. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  136. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +22 -1
  137. package/dist/integrations/pi/components/upstream/theme/theme.js +225 -10
  138. package/dist/integrations/pi/engine/adapter.d.ts +44 -24
  139. package/dist/integrations/pi/engine/adapter.js +130 -22
  140. package/dist/integrations/pi/engine/command-dispatch.d.ts +2 -5
  141. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -11
  142. package/dist/integrations/pi/engine/host-ports.d.ts +20 -0
  143. package/dist/integrations/pi/engine/host-ports.js +13 -0
  144. package/dist/integrations/pi/engine/host.d.ts +70 -0
  145. package/dist/integrations/pi/engine/host.js +147 -0
  146. package/dist/integrations/pi/engine/index.d.ts +8 -4
  147. package/dist/integrations/pi/engine/index.js +3 -1
  148. package/dist/integrations/pi/engine/pi-settings-metadata.json +1 -1
  149. package/dist/integrations/pi/engine/resource-catalog.d.ts +4 -23
  150. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  151. package/dist/integrations/pi/engine/runtime-integration.d.ts +8 -2
  152. package/dist/integrations/pi/engine/runtime-integration.js +12 -7
  153. package/dist/integrations/pi/engine/session-events.js +10 -2
  154. package/dist/integrations/pi/engine/session-integration.d.ts +3 -8
  155. package/dist/integrations/pi/engine/session-integration.js +0 -49
  156. package/dist/integrations/pi/engine/session-runtime.d.ts +18 -3
  157. package/dist/integrations/pi/engine/session-runtime.js +18 -7
  158. package/dist/integrations/pi/engine/settings-bridge.d.ts +2 -2
  159. package/dist/integrations/pi/engine/settings-bridge.js +0 -2
  160. package/dist/integrations/pi/engine/settings-port.d.ts +2 -0
  161. package/dist/integrations/pi/engine/settings-port.js +3 -5
  162. package/dist/integrations/pi/engine/transcript-projection.js +2 -1
  163. package/dist/integrations/pi/engine/workflows.d.ts +33 -260
  164. package/dist/integrations/pi/engine/workflows.js +6 -28
  165. package/dist/integrations/pi/session-presenters/index.d.ts +2 -0
  166. package/dist/integrations/pi/session-presenters/index.js +1 -0
  167. package/dist/integrations/pi/session-presenters/presenters.d.ts +16 -0
  168. package/dist/integrations/pi/session-presenters/presenters.js +341 -0
  169. package/dist/integrations/pi/startup-public.js +306 -290
  170. package/dist/integrations/pi/startup-public.manifest.json +289 -265
  171. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -1
  172. package/dist/integrations/pi/tui-runtime/adapter.js +7 -2
  173. package/dist/integrations/pi/tui-runtime/contracts.d.ts +3 -1
  174. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +8 -1
  175. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +67 -2
  176. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
  177. package/dist/integrations/pi/tui-runtime/index.js +1 -1
  178. package/dist/native/darwin-arm64/manifest.json +1 -1
  179. package/dist/native/linux-x64/manifest.json +1 -1
  180. package/dist/native/win32-x64/manifest.json +2 -2
  181. package/dist/native/win32-x64/process-guardian.exe +0 -0
  182. package/dist/product-identity.d.ts +2 -2
  183. package/dist/product-identity.js +2 -2
  184. package/dist/product-identity.json +3 -1
  185. package/dist/runtime-payload-inventory.json +3092 -2878
  186. package/dist/ui/components/prompt-input.d.ts +2 -0
  187. package/dist/ui/components/prompt-input.js +5 -1
  188. package/dist/ui/components/text-selection.d.ts +1 -1
  189. package/dist/ui/components/text-selection.js +3 -1
  190. package/dist/ui/components/theme.d.ts +2 -0
  191. package/dist/ui/components/transcript-viewport.d.ts +6 -4
  192. package/dist/ui/components/transcript-viewport.js +94 -16
  193. package/dist/ui/components/value-menu.d.ts +2 -0
  194. package/dist/ui/components/value-menu.js +13 -8
  195. package/dist/ui/settings/declarations.d.ts +38 -14
  196. package/dist/ui/settings/declarations.js +35 -11
  197. package/dist/ui/settings/migrations.js +23 -0
  198. package/docs/architecture/boundaries.md +9 -1
  199. package/docs/architecture/project-structure.md +7 -5
  200. package/docs/architecture/prompt-suggestions.md +14 -10
  201. package/docs/local-worktree-cleanup.md +11 -5
  202. package/docs/openspec-archive-automation.md +6 -5
  203. package/package.json +3 -3
  204. package/dist/contracts/agent-engine/model.d.ts +0 -78
  205. package/dist/contracts/agent-engine/model.js +0 -1
  206. package/dist/contracts/agent-engine/ports.d.ts +0 -18
  207. package/dist/contracts/agent-engine/serialization.d.ts +0 -7
  208. package/dist/contracts/agent-engine/serialization.js +0 -58
  209. package/dist/contracts/agent-engine/validation.d.ts +0 -5
  210. package/dist/contracts/agent-engine/validation.js +0 -128
  211. package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +0 -61
  212. package/dist/integrations/pi/components/upstream/theme/system-theme.js +0 -709
  213. /package/dist/contracts/{agent-engine/ports.js → owned-ui/session-backend.js} +0 -0
package/bin/cli.js CHANGED
@@ -55,8 +55,19 @@ process.exitCode = await dispatchCli(process.argv.slice(2), {
55
55
  return await runPackageCommand(request, { createPort: createPiPackagesPort });
56
56
  },
57
57
  sessionContext: async request => {
58
- const { runSessionContextCommand } = await import("../dist/cli/session-context.js");
59
- return await runSessionContextCommand(request);
58
+ const [{ runSessionContextCommand }, { createNativeProcessInspector }, { readLaunchContext }] = await Promise.all([
59
+ import("../dist/cli/session-context.js"),
60
+ import("../dist/foundation/process-containment/native-process-inspector.js"),
61
+ import("../dist/foundation/launch-context/index.js"),
62
+ ]);
63
+ const launch = readLaunchContext(process.env);
64
+ let inspector;
65
+ return await runSessionContextCommand(request, {
66
+ inspectProcess: async pid => {
67
+ inspector ??= createNativeProcessInspector(process.env, process.platform, launch.releaseRoot);
68
+ return (await inspector).observe(pid);
69
+ },
70
+ });
60
71
  },
61
72
  }, {
62
73
  stdout: message => process.stdout.write(message),
package/bin/ui.js CHANGED
@@ -9,6 +9,13 @@ const { readLaunchContext } = await import("../dist/foundation/launch-context/in
9
9
  const launchContext = readLaunchContext(process.env, "profile");
10
10
  const profile = launchContext.launchProfile;
11
11
  if (profile === undefined) throw new Error("A1 launch profile is missing");
12
+ if (profile === "a1") {
13
+ const [{ randomUUID }, { PRODUCT_IDENTITY }] = await Promise.all([
14
+ import("node:crypto"),
15
+ import("../dist/product-identity.js"),
16
+ ]);
17
+ process.env[PRODUCT_IDENTITY.environment.sessionRuntimeId] = randomUUID();
18
+ }
12
19
  const { installFatalExit } = await import("../dist/foundation/terminal-cleanup/fatal-exit.js");
13
20
  const { resolveProductPaths } = await import("../dist/foundation/lifecycle/paths.js");
14
21
  const { join } = await import("node:path");
@@ -49,10 +56,16 @@ Promise.resolve().then(() => {
49
56
  if (sessionSelection && profile !== "a1") throw new Error("session selection requires the normal A1 profile");
50
57
  return runSelectedInteractiveRuntime(profile, {
51
58
  ownedUi: async (profileId, ownedSurfaces) => {
59
+ const processInspector = ownedSurfaces
60
+ ? await import("../dist/foundation/process-containment/native-process-inspector.js")
61
+ .then(module => module.createNativeProcessInspector(process.env, process.platform, launchContext.releaseRoot))
62
+ .catch(() => null)
63
+ : null;
52
64
  const { application, settings } = await composeOwnedUi({
53
65
  cwd: process.cwd(),
54
66
  profileId,
55
67
  ownedSurfaces,
68
+ ...(processInspector === null ? {} : { inspectProcess: pid => processInspector.observe(pid) }),
56
69
  projectTrustPrompt: createConsoleProjectTrustPrompt({ presentation: profile === "a1" ? "bare" : "comparison" }),
57
70
  sessionForkPrompt: createConsoleSessionForkPrompt(),
58
71
  ...(sessionSelection === undefined ? {} : { sessionSelection }),
@@ -0,0 +1,19 @@
1
+ import type { HelperPool } from "./helper-pool.js";
2
+ /**
3
+ * Process-wide clipboard helper spares. Composition creates one and every session shell borrows it, so N
4
+ * sessions keep at most one spare paste helper and one spare copy helper instead of 2N. Shells warm the
5
+ * pools after their first frame; only the creator disposes them.
6
+ */
7
+ export interface OwnedUiClipboardServices {
8
+ readonly paste: HelperPool;
9
+ readonly copy: HelperPool;
10
+ dispose(): void;
11
+ }
12
+ export interface OwnedUiClipboardServicesOptions {
13
+ /** Package/fault-test seams for the forked helpers' entries. */
14
+ readonly pasteHelper?: URL;
15
+ readonly copyHelper?: URL;
16
+ /** Idle bound for both spares; production keeps the default. */
17
+ readonly spareIdleMs?: number;
18
+ }
19
+ export declare function createOwnedUiClipboardServices(options?: OwnedUiClipboardServicesOptions): OwnedUiClipboardServices;
@@ -0,0 +1,7 @@
1
+ import { createPasteHelperPool } from "./paste-executor.js";
2
+ import { createCopyHelperPool } from "./response-copy-transport.js";
3
+ export function createOwnedUiClipboardServices(options = {}) {
4
+ const paste = createPasteHelperPool(options.pasteHelper, options.spareIdleMs);
5
+ const copy = createCopyHelperPool(options.copyHelper, options.spareIdleMs);
6
+ return { paste, copy, dispose: () => { paste.dispose(); copy.dispose(); } };
7
+ }
@@ -1,10 +1,14 @@
1
1
  import { type SessionResumeCommandMetadata } from "./session-shell.js";
2
2
  export interface ExitNoticeSource {
3
- readonly sessionGeneration: number;
4
- currentSessionResumeMetadata(): SessionResumeCommandMetadata | null;
5
- onEvent(listener: (event: {
6
- readonly type: string;
7
- }) => void): () => void;
3
+ readonly identity: {
4
+ readonly sessionGeneration: number;
5
+ currentSessionResumeMetadata(): SessionResumeCommandMetadata | null;
6
+ };
7
+ readonly session: {
8
+ onEvent(listener: (event: {
9
+ readonly type: string;
10
+ }) => void): () => void;
11
+ };
8
12
  }
9
13
  export interface ArmedExitNotice {
10
14
  clear(): void;
@@ -17,12 +17,12 @@ export function uncleanExitNoticeLines(resume) {
17
17
  export function armExitNotice(source, path) {
18
18
  let current = null;
19
19
  let cleared = false;
20
- let generation = source.sessionGeneration;
20
+ let generation = source.identity.sessionGeneration;
21
21
  const write = () => {
22
22
  if (cleared)
23
23
  return;
24
24
  // Protocol: plain text only; the guardian strips control characters, so styling would be lost.
25
- const text = `${uncleanExitNoticeLines(source.currentSessionResumeMetadata()).join("\n")}\n`;
25
+ const text = `${uncleanExitNoticeLines(source.identity.currentSessionResumeMetadata()).join("\n")}\n`;
26
26
  if (text === current)
27
27
  return;
28
28
  try {
@@ -35,13 +35,13 @@ export function armExitNotice(source, path) {
35
35
  catch { }
36
36
  };
37
37
  write();
38
- const unsubscribe = source.onEvent(event => {
38
+ const unsubscribe = source.session.onEvent(event => {
39
39
  // Rationale: a session is persisted by its first response and replaced by /new or /resume;
40
40
  // these events cover both without touching the per-chunk stream.
41
- const replaced = source.sessionGeneration !== generation;
41
+ const replaced = source.identity.sessionGeneration !== generation;
42
42
  if (!replaced && event.type !== "session-lifecycle" && event.type !== "assistant-message-completed")
43
43
  return;
44
- generation = source.sessionGeneration;
44
+ generation = source.identity.sessionGeneration;
45
45
  write();
46
46
  });
47
47
  return {
@@ -1,7 +1,9 @@
1
1
  export { ClipboardDiagnosticCapture } from "./clipboard-diagnostics.js";
2
+ export { createOwnedUiClipboardServices } from "./clipboard-services.js";
3
+ export type { OwnedUiClipboardServices, OwnedUiClipboardServicesOptions } from "./clipboard-services.js";
2
4
  export { ContextualPromptSuggestionController, samePromptSuggestionIdentity } from "./prompt-suggestion-controller.js";
3
5
  export type { ContextualPromptSuggestionControllerOptions, ContextualPromptSuggestionSurface } from "./prompt-suggestion-controller.js";
4
6
  export { OwnedUiSessionShell, OwnedUiSessionShellRoot, formatSessionResumeCommand, quoteCommandArgument } from "./session-shell.js";
5
- export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, OwnedUiShellPromptImagesOptions, OwnedUiShellSkillsOptions, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
7
+ export type { OwnedUiSessionShellOptions, OwnedUiShellDiagnosticOptions, OwnedUiShellEngineOptions, OwnedUiShellHistoryOptions, OwnedUiShellPresentationOptions, OwnedUiShellPromptImagesOptions, OwnedUiShellSharedServices, OwnedUiShellSkillsOptions, OwnedUiShellSuggestionOptions, SessionResumeCommandMetadata, } from "./session-shell.js";
6
8
  export { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer } from "./stream-presentation-coalescer.js";
7
9
  export type { StreamPresentationScheduler } from "./stream-presentation-coalescer.js";
@@ -1,4 +1,5 @@
1
1
  export { ClipboardDiagnosticCapture } from "./clipboard-diagnostics.js";
2
+ export { createOwnedUiClipboardServices } from "./clipboard-services.js";
2
3
  export { ContextualPromptSuggestionController, samePromptSuggestionIdentity } from "./prompt-suggestion-controller.js";
3
4
  export { OwnedUiSessionShell, OwnedUiSessionShellRoot, formatSessionResumeCommand, quoteCommandArgument } from "./session-shell.js";
4
5
  export { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer } from "./stream-presentation-coalescer.js";
@@ -1,4 +1,5 @@
1
1
  import { startPasteExecutor } from "./paste-executor.js";
2
+ import type { HelperPool } from "./helper-pool.js";
2
3
  import { type PasteEvent, type PasteSource, type PreparedPaste } from "./paste-protocol.js";
3
4
  export interface PreparedPasteJob<T> {
4
5
  readonly result: Promise<T>;
@@ -16,6 +17,8 @@ export interface PastePreparationClientOptions {
16
17
  readonly helper?: URL;
17
18
  /** Idle bound for the spare helper; production keeps the default. */
18
19
  readonly spareIdleMs?: number;
20
+ /** Process-wide spare pool shared across sessions; the client warms it but never disposes it. */
21
+ readonly pool?: HelperPool;
19
22
  }
20
23
  /** Keeps eight distinct paste transactions alive through insertion; canceled executors retain capacity until fenced. */
21
24
  export declare class PastePreparationClient {
@@ -8,13 +8,15 @@ export class PastePreparationClient {
8
8
  #execute;
9
9
  #helper;
10
10
  #pool;
11
+ #ownsPool;
11
12
  #sequence = 0;
12
13
  #disposed = false;
13
14
  constructor(options = {}) {
14
15
  this.#onEvent = options.onEvent;
15
16
  this.#execute = options.execute ?? startPasteExecutor;
16
17
  this.#helper = options.helper;
17
- this.#pool = options.execute === undefined ? createPasteHelperPool(options.helper, options.spareIdleMs) : undefined;
18
+ this.#pool = options.execute === undefined ? options.pool ?? createPasteHelperPool(options.helper, options.spareIdleMs) : undefined;
19
+ this.#ownsPool = options.pool === undefined;
18
20
  }
19
21
  /** Forks the spare helper now so the first paste takes a live child; later pastes rewarm it themselves. */
20
22
  warm() { this.#pool?.warm(); }
@@ -160,7 +162,8 @@ export class PastePreparationClient {
160
162
  async dispose() {
161
163
  this.#disposed = true;
162
164
  this.reset();
163
- this.#pool?.dispose();
165
+ if (this.#ownsPool)
166
+ this.#pool?.dispose();
164
167
  let timer;
165
168
  await Promise.race([Promise.all([...this.#active].map(active => active.stopped)),
166
169
  new Promise(resolve => { timer = setTimeout(resolve, PASTE_STOP_MS); timer.unref(); })]);
@@ -1,7 +1,14 @@
1
1
  import type { ClipboardPath, PreparedPasteText } from "./paste-types.js";
2
2
  export declare const PATH_CHIP_PRESENTATION_UNITS = 4096;
3
- export declare const PATH_CHIP_SUFFIX_UNITS = 20;
4
- /** Shared base tag for presentation budgeting and actual chip adoption, including image-file spacing. */
5
- export declare function pathChipTag(item: ClipboardPath): string;
3
+ /** Normalizes a path for host-filesystem identity comparisons. */
4
+ export declare function normalizedPathIdentity(fullPath: string): string;
5
+ /** Yields labels from the basename through the normalized rooted path, shortest first. */
6
+ export declare function pathChipLabelCandidates(fullPath: string): Iterable<string>;
7
+ /** Formats one path label with the established folder/file/image-file icon. */
8
+ export declare function pathChipTag(item: ClipboardPath, label?: string): string;
9
+ /** Yields complete deterministic tags lazily for path collision adoption. */
10
+ export declare function pathChipTagCandidates(item: ClipboardPath): Iterable<string>;
11
+ /** Returns the conservative UTF-16 presentation cost for one path occurrence. */
12
+ export declare function pathChipPresentationUnits(item: ClipboardPath): number;
6
13
  /** Run in the isolated preparer: compact before path arrays become an oversized editor presentation. */
7
14
  export declare function preparePathPresentation(paths: readonly ClipboardPath[]): PreparedPasteText;
@@ -1,19 +1,52 @@
1
1
  import path from "node:path";
2
2
  export const PATH_CHIP_PRESENTATION_UNITS = 4096;
3
- // Invariant: a collision suffix containing any safe-integer index fits this per-occurrence reserve.
4
- export const PATH_CHIP_SUFFIX_UNITS = 20;
5
3
  const IMAGE_EXTENSION = /\.(?:jpe?g|png|webp|gif|bmp|tiff?)$/iu;
6
- /** Shared base tag for presentation budgeting and actual chip adoption, including image-file spacing. */
7
- export function pathChipTag(item) {
8
- const label = path.basename(item.fullPath) || path.parse(item.fullPath).root.replace(/[\\/]+$/u, "") || item.fullPath;
4
+ /** Normalizes a path for host-filesystem identity comparisons. */
5
+ export function normalizedPathIdentity(fullPath) {
6
+ const normalized = path.normalize(fullPath.trim());
7
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
8
+ }
9
+ /** Yields labels from the basename through the normalized rooted path, shortest first. */
10
+ export function* pathChipLabelCandidates(fullPath) {
11
+ const normalized = path.normalize(fullPath.trim());
12
+ const parsed = path.parse(normalized);
13
+ const relative = normalized.slice(parsed.root.length);
14
+ const segments = relative.split(path.sep).filter(segment => segment.length > 0);
15
+ let previous = "";
16
+ for (let depth = 1; depth <= segments.length; depth++) {
17
+ previous = segments.slice(-depth).join("/");
18
+ yield previous;
19
+ }
20
+ const rooted = displayPath(normalized);
21
+ if (segments.length === 0) {
22
+ const root = displayPath(parsed.root).replace(/\/$/u, "");
23
+ if (root.length > 0) {
24
+ previous = root;
25
+ yield root;
26
+ }
27
+ }
28
+ if (rooted.length > 0 && rooted !== previous)
29
+ yield rooted;
30
+ }
31
+ /** Formats one path label with the established folder/file/image-file icon. */
32
+ export function pathChipTag(item, label = basePathLabel(item.fullPath)) {
9
33
  const icon = item.kind === "folder" ? "📁" : IMAGE_EXTENSION.test(item.fullPath) ? "🖼 " : "📄";
10
34
  return `[${icon} ${label}]`;
11
35
  }
36
+ /** Yields complete deterministic tags lazily for path collision adoption. */
37
+ export function* pathChipTagCandidates(item) {
38
+ for (const label of pathChipLabelCandidates(item.fullPath))
39
+ yield pathChipTag(item, label);
40
+ }
41
+ /** Returns the conservative UTF-16 presentation cost for one path occurrence. */
42
+ export function pathChipPresentationUnits(item) {
43
+ return pathChipTag(item, displayPath(path.normalize(item.fullPath.trim()))).length;
44
+ }
12
45
  /** Run in the isolated preparer: compact before path arrays become an oversized editor presentation. */
13
46
  export function preparePathPresentation(paths) {
14
47
  let units = 0;
15
48
  for (const item of paths) {
16
- units += pathChipTag(item).length + PATH_CHIP_SUFFIX_UNITS;
49
+ units += pathChipPresentationUnits(item);
17
50
  if (units > PATH_CHIP_PRESENTATION_UNITS) {
18
51
  // Compatibility: individual adjacent chips expand without inserted separators or text re-normalization.
19
52
  const text = paths.map(value => value.fullPath).join("");
@@ -22,3 +55,12 @@ export function preparePathPresentation(paths) {
22
55
  }
23
56
  return { kind: "paths", paths };
24
57
  }
58
+ function basePathLabel(fullPath) {
59
+ const normalized = path.normalize(fullPath.trim());
60
+ return path.basename(normalized)
61
+ || displayPath(path.parse(normalized).root).replace(/\/$/u, "")
62
+ || displayPath(normalized);
63
+ }
64
+ function displayPath(value) {
65
+ return path.sep === "\\" ? value.replaceAll("\\", "/") : value;
66
+ }
@@ -1,10 +1,9 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import path from "node:path";
3
2
  import { canonicalizeClipboardImage } from "./clipboard-image.js";
4
3
  import { assertImageEncodedSize, assertPromptImages, canonicalPromptChipMatches, DEFAULT_PROMPT_IMAGE_LIMIT, ImageAttachmentError, replaceCanonicalPromptChips, } from "../../contracts/owned-ui/index.js";
5
4
  import { ImagePreparationClient } from "./image-preparation-client.js";
6
5
  import { preparePasteText } from "./paste-text-preparation.js";
7
- import { pathChipTag } from "./path-chip-presentation.js";
6
+ import { normalizedPathIdentity, pathChipTagCandidates } from "./path-chip-presentation.js";
8
7
  import { PastePreparationClient } from "./paste-preparation-client.js";
9
8
  const URL_SUBSTRING_PATTERN = /https?:\/\/[^\s\u0000-\u001f\u007f\]]+/giu;
10
9
  const IMAGE_CHIP_IDENTIFIER_PATTERN = /^\[📷 screenshot-([a-f0-9]+)(?:-resized)?\]$/u;
@@ -23,6 +22,7 @@ function referencesPasteMarker(text, entry) {
23
22
  /** Owns semantic chips and bounded pending paste references; cancels background work on reset or disposal. */
24
23
  export class PromptChipStore {
25
24
  #chips = new Map();
25
+ #pathTags = new Map();
26
26
  // Invariant: clearing the editor never recycles a recoverable text chip's identity.
27
27
  #textCounter = 0;
28
28
  #pending = new Map();
@@ -199,6 +199,7 @@ export class PromptChipStore {
199
199
  async dispose() {
200
200
  await Promise.all([this.#preparation.dispose(), this.#isolated?.dispose(), ...this.#stopping]);
201
201
  this.#chips.clear();
202
+ this.#pathTags.clear();
202
203
  this.#pending.clear();
203
204
  this.#countRejectedImages.clear();
204
205
  this.#provisionalOwners.clear();
@@ -269,7 +270,26 @@ export class PromptChipStore {
269
270
  this.#claimChip(tag, owner, true);
270
271
  return tag;
271
272
  }
272
- return paste.paths.map(item => this.#recordUnique({ kind: item.kind, tag: pathChipTag(item), path: item.fullPath }, owner)).join("");
273
+ return paste.paths.map(item => this.#recordPath(item, owner)).join("");
274
+ }
275
+ #recordPath(item, owner) {
276
+ const key = pathChipKey(item);
277
+ const establishedTag = this.#pathTags.get(key);
278
+ if (establishedTag !== undefined && this.#chips.has(establishedTag)) {
279
+ this.#claimChip(establishedTag, owner, false);
280
+ return establishedTag;
281
+ }
282
+ for (const tag of pathChipTagCandidates(item)) {
283
+ const chip = { kind: item.kind, tag, path: item.fullPath };
284
+ const existing = this.#chips.get(tag);
285
+ if (existing !== undefined && !sameChipValue(existing, chip))
286
+ continue;
287
+ this.#chips.set(tag, chip);
288
+ this.#pathTags.set(key, tag);
289
+ this.#claimChip(tag, owner, existing === undefined);
290
+ return tag;
291
+ }
292
+ throw new Error("Path chip labels could not distinguish normalized paths");
273
293
  }
274
294
  /** Hide provisional clipboard identities until the read identifies an actual image. */
275
295
  hiddenRanges(line) {
@@ -501,7 +521,10 @@ export class PromptChipStore {
501
521
  owners.delete(owner);
502
522
  if (owners.size === 0) {
503
523
  this.#provisionalOwners.delete(tag);
524
+ const chip = this.#chips.get(tag);
504
525
  this.#chips.delete(tag);
526
+ if (chip?.kind === "file" || chip?.kind === "folder")
527
+ this.#pathTags.delete(pathChipKey(chip));
505
528
  }
506
529
  }
507
530
  }
@@ -522,13 +545,16 @@ export class PromptChipStore {
522
545
  return uniqueTag;
523
546
  }
524
547
  }
548
+ function pathChipKey(item) {
549
+ return `${item.kind}\0${normalizedPathIdentity("fullPath" in item ? item.fullPath : item.path)}`;
550
+ }
525
551
  function sameChipValue(left, right) {
526
552
  if (left.kind !== right.kind)
527
553
  return false;
528
554
  if (left.kind === "url" && right.kind === "url")
529
555
  return left.url === right.url;
530
556
  if ((left.kind === "file" || left.kind === "folder") && (right.kind === "file" || right.kind === "folder")) {
531
- return path.normalize(left.path) === path.normalize(right.path);
557
+ return normalizedPathIdentity(left.path) === normalizedPathIdentity(right.path);
532
558
  }
533
559
  if (left.kind === "image" && right.kind === "image")
534
560
  return left.image.data === right.image.data;
@@ -17,7 +17,7 @@ export interface ContextualPromptSuggestionControllerOptions {
17
17
  type DecisionIdentity = Omit<OwnedUiPromptSuggestionIdentity, "model"> & {
18
18
  readonly model: OwnedUiPromptSuggestionIdentity["model"] | null;
19
19
  };
20
- /** Prefetches one inert candidate; owns retirement even when a provider ignores abort. */
20
+ /** Prefetches one inert candidate and owns one bounded settlement/retry recovery lifecycle. */
21
21
  export declare class ContextualPromptSuggestionController {
22
22
  #private;
23
23
  readonly options: ContextualPromptSuggestionControllerOptions;
@@ -26,12 +26,13 @@ export declare class ContextualPromptSuggestionController {
26
26
  setEnabled(enabled: boolean): void;
27
27
  skip(identity: DecisionIdentity, reason: SuggestionDecisionReason): void;
28
28
  consider(identity: OwnedUiPromptSuggestionIdentity, decision: boolean | SuggestionDecision): void;
29
- settle(identity: OwnedUiPromptSuggestionIdentity): void;
29
+ /** Settles an existing prefetch or starts the first request when that eligible prefetch was missed. */
30
+ settle(identity: OwnedUiPromptSuggestionIdentity, decision?: boolean | SuggestionDecision): void;
30
31
  accept(): void;
31
32
  invalidate(): void;
32
- /** Retires only a generating request; a prepared or available suggestion survives the draft edit. */
33
+ /** Retires generating or scheduled recovery; a prepared or available suggestion survives a draft edit. */
33
34
  abortPending(): void;
34
- /** Reasserts retained ghost text after an editor mutation makes the prompt eligible again. */
35
+ /** Reasserts retained ghost text or retries a deferred presentation when the editor is eligible. */
35
36
  restoreAvailable(): void;
36
37
  dispose(): void;
37
38
  }