@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
@@ -0,0 +1,341 @@
1
+ import { createPiShellModelSelector, createPiShellModelsDialog, createPiShellScopedModelsSelector, createPiShellSessionSelector, createPiShellSettingsSelector, } from "../components/shell-selectors-dialogs.js";
2
+ const MODEL_REFRESH_TIMEOUT_MS = 15_000;
3
+ const MODEL_REFRESH_TIMED_OUT = "Model refresh timed out; showing cached models.";
4
+ /**
5
+ * Builds the selectors and transcript renderers whose inputs are Pi engine objects. This owner is the one place
6
+ * that sees both the engine adapter and the Pi components, so the shell only ever holds the neutral port.
7
+ */
8
+ export function createPiSessionPresenters(adapter, options = {}) {
9
+ const source = () => adapter.presentationSource();
10
+ let lazySelectors = options.lazySelectors;
11
+ let lazySelectorsPromise;
12
+ // Performance: the tree selector module stays out of the startup graph and loads on first use.
13
+ const loadLazySelectors = () => {
14
+ if (lazySelectors !== undefined)
15
+ return Promise.resolve(lazySelectors);
16
+ return lazySelectorsPromise ??= import("../components/lazy-selectors.js").then(module => {
17
+ lazySelectors = module.piShellLazySelectors;
18
+ return module.piShellLazySelectors;
19
+ });
20
+ };
21
+ const renderers = {
22
+ getMessageRenderer: customType => opaqueRenderer(source().pinnedMessageRenderer(customType)),
23
+ getToolRenderers: toolName => opaqueRenderer(source().pinnedToolRenderers(toolName)),
24
+ // Compatibility: the component adapter passes Pi's own keybinding config through the neutral port.
25
+ getShortcuts: bindings => source().pinnedShortcutDescriptions(bindings),
26
+ };
27
+ return {
28
+ transcriptRenderers: () => renderers,
29
+ openModelSelector(host, initialSearchInput) {
30
+ const context = source().pinnedModelSelectorContext();
31
+ const close = closeSurface(host);
32
+ const component = createPiShellModelSelector({
33
+ ...context,
34
+ runtime: {
35
+ getColumns: () => host.viewport().columns,
36
+ getRows: () => host.viewport().rows,
37
+ requestRender: () => host.requestRender(),
38
+ },
39
+ ...(initialSearchInput === undefined ? {} : { initialSearchInput }),
40
+ onSelect: model => {
41
+ close();
42
+ void host.runWorkflow({ command: "model", argument: "", selection: modelReference(model) });
43
+ },
44
+ onSelectAsDefault: model => {
45
+ close();
46
+ void host.runWorkflow({ command: "model", argument: "", selection: modelReference(model), persist: true });
47
+ },
48
+ onCancel: close,
49
+ });
50
+ host.setInputSurface(component);
51
+ host.requestRender();
52
+ },
53
+ async openSessionSelector(host, onExit) {
54
+ const context = source().pinnedSessionSelectorContext();
55
+ const close = closeSurface(host);
56
+ const component = await createPiShellSessionSelector({
57
+ currentSessionsLoader: context.loadCurrentSessions,
58
+ allSessionsLoader: context.loadAllSessions,
59
+ currentSessionFilePath: context.currentSessionFilePath,
60
+ renameSession: context.renameSession,
61
+ requestRender: () => host.requestRender(),
62
+ onSelect: sessionPath => {
63
+ close();
64
+ void host.runWorkflow({ command: "resume", argument: sessionPath });
65
+ },
66
+ onCancel: close,
67
+ onExit: () => {
68
+ close();
69
+ onExit();
70
+ },
71
+ });
72
+ host.setInputSurface(component);
73
+ host.requestRender();
74
+ },
75
+ async openTreeSelector(host, navigate, initialSelectedId) {
76
+ const context = source().pinnedTreeSelectorContext();
77
+ if (context.tree.length === 0) {
78
+ host.appendWorkflowStatus("No entries in session");
79
+ host.requestRender();
80
+ return;
81
+ }
82
+ const close = closeSurface(host);
83
+ const releasePresentation = host.beginPresentationHold?.();
84
+ try {
85
+ const selectors = await loadLazySelectors();
86
+ const component = await selectors.createTree({
87
+ tree: context.tree,
88
+ currentLeafId: context.currentLeafId,
89
+ terminalHeight: host.viewport().rows,
90
+ initialFilterMode: context.filterMode,
91
+ ...(initialSelectedId === undefined ? {} : { initialSelectedId }),
92
+ onLabelChange: context.appendLabelChange,
93
+ onCopy: text => {
94
+ if (!text) {
95
+ host.appendWorkflowResult({ command: "tree", outcome: "failed", message: "Selected entry has no text to copy" });
96
+ host.requestRender();
97
+ return;
98
+ }
99
+ const generation = adapter.identity.sessionBindingGeneration;
100
+ const stale = () => host.disposed || generation !== adapter.identity.sessionBindingGeneration;
101
+ void adapter.workflows.copyWorkflowText(text).then(acknowledged => {
102
+ if (stale())
103
+ return;
104
+ host.appendWorkflowStatus(acknowledged ? "Copied selected message to clipboard" : "Submitted selected message to clipboard");
105
+ host.requestRender();
106
+ }).catch(error => {
107
+ if (stale())
108
+ return;
109
+ host.appendWorkflowResult({ command: "tree", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
110
+ host.requestRender();
111
+ });
112
+ },
113
+ onCancel: close,
114
+ onSelect: entryId => {
115
+ if (entryId === context.currentLeafId) {
116
+ close();
117
+ host.appendWorkflowStatus("Already at this point");
118
+ host.requestRender();
119
+ return;
120
+ }
121
+ if (context.skipSummaryPrompt)
122
+ close();
123
+ navigate(entryId, context.skipSummaryPrompt);
124
+ },
125
+ });
126
+ if (host.disposed)
127
+ return;
128
+ host.setInputSurface(component);
129
+ host.requestRender();
130
+ }
131
+ finally {
132
+ releasePresentation?.();
133
+ }
134
+ },
135
+ openSettingsSelector(host) {
136
+ const snapshot = adapter.settings.snapshot();
137
+ const { currentModel, availableDefaultModels } = source().pinnedSettingsModels();
138
+ const close = closeSurface(host);
139
+ const component = createPiShellSettingsSelector({
140
+ config: {
141
+ ...snapshot,
142
+ availableThinkingLevels: [...snapshot.availableThinkingLevels],
143
+ availableThemes: [...snapshot.availableThemes],
144
+ warnings: { ...snapshot.warnings },
145
+ modelThinkingLevels: { ...snapshot.modelThinkingLevels },
146
+ ...(currentModel === undefined ? {} : { currentModel: currentModel }),
147
+ availableDefaultModels: availableDefaultModels,
148
+ },
149
+ onChange: (callback, value) => {
150
+ if (callback === "onCancel") {
151
+ close();
152
+ return;
153
+ }
154
+ if (callback === "onTuiModeChange") {
155
+ // Invariant: a surface that cannot change TUI mode (bare A1 is permanently fullscreen) ignores this callback.
156
+ if (host.switchTuiMode === undefined)
157
+ return;
158
+ if (value !== "regular" && value !== "fullscreen")
159
+ return;
160
+ if (!host.switchTuiMode(value)) {
161
+ host.appendWorkflowStatus("Close active overlays before changing TUI mode");
162
+ host.requestRender();
163
+ return;
164
+ }
165
+ }
166
+ void source().applyPinnedSettingValue(callback, value).then(result => {
167
+ if (result.outcome === "failed")
168
+ host.appendWorkflowResult(result);
169
+ else if (callback === "onTuiModeChange")
170
+ host.appendWorkflowStatus(`TUI mode: ${value}`);
171
+ host.requestRender();
172
+ });
173
+ },
174
+ onCancel: close,
175
+ });
176
+ host.setInputSurface(component);
177
+ host.requestRender();
178
+ },
179
+ /** The bare-A1 unified Models dialog: switch on Enter, scope on Space, persist on Ctrl+S, all through the engine. */
180
+ openModelsDialog(host, initialQuery) {
181
+ const catalog = adapter.catalog;
182
+ const context = catalog.modelsContext();
183
+ const available = new Set(context.models.map(model => `${model.provider}/${model.id}`));
184
+ const savedScopeIds = context.persistedScopeIds.filter(id => available.has(id));
185
+ // Invariant: an explicit session scope wins; otherwise the dialog starts from what is persisted, never from "all rows scoped".
186
+ const scopeIds = context.sessionScopeIds.length > 0 ? context.sessionScopeIds : savedScopeIds;
187
+ const refresh = new ModelCatalogRefresh();
188
+ const close = () => {
189
+ refresh.stop();
190
+ host.setInputSurface(null);
191
+ host.requestRender();
192
+ };
193
+ const dialog = createPiShellModelsDialog({
194
+ models: context.models,
195
+ activeModelId: context.activeModelId,
196
+ scopeIds,
197
+ savedScopeIds,
198
+ ...(initialQuery === undefined ? {} : { initialQuery }),
199
+ refreshStatus: "Refreshing model catalogs…",
200
+ requestRender: () => host.requestRender(),
201
+ onSelect: modelId => {
202
+ void host.runWorkflow({ command: "models", argument: "", selection: modelId }).then(result => {
203
+ if (!refresh.stopped && result.outcome === "completed")
204
+ close();
205
+ });
206
+ },
207
+ onScopeChange: ids => {
208
+ catalog.setSessionModelScope(ids);
209
+ host.requestRender();
210
+ },
211
+ onSave: ids => {
212
+ try {
213
+ catalog.persistModelScope(ids);
214
+ }
215
+ catch (error) {
216
+ host.appendWorkflowResult({ command: "models", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
217
+ host.requestRender();
218
+ throw error;
219
+ }
220
+ host.appendWorkflowStatus("Model selection saved to settings");
221
+ host.requestRender();
222
+ },
223
+ onCancel: close,
224
+ });
225
+ const component = {
226
+ ...dialog,
227
+ dispose: () => {
228
+ refresh.stop();
229
+ dialog.dispose?.();
230
+ },
231
+ };
232
+ host.setInputSurface(component);
233
+ host.requestRender();
234
+ refresh.run(host, component, signal => catalog.refreshModels(signal), refreshed => component.updateModels(refreshed.models));
235
+ },
236
+ openScopedModelsSelector(host) {
237
+ const catalog = adapter.catalog;
238
+ const initial = catalog.pinnedScopedModelsContext();
239
+ let currentEnabledIds = initial.enabledModelIds === null ? null : [...initial.enabledModelIds];
240
+ let selectionChanged = false;
241
+ const refresh = new ModelCatalogRefresh();
242
+ const close = () => {
243
+ refresh.stop();
244
+ host.setInputSurface(null);
245
+ host.requestRender();
246
+ };
247
+ const selector = createPiShellScopedModelsSelector({
248
+ models: initial.models,
249
+ enabledModelIds: currentEnabledIds,
250
+ refreshStatus: "Refreshing model catalogs…",
251
+ onChange: enabledIds => {
252
+ selectionChanged = true;
253
+ currentEnabledIds = enabledIds === null ? null : [...enabledIds];
254
+ catalog.updateScopedModels(currentEnabledIds);
255
+ host.requestRender();
256
+ },
257
+ onPersist: enabledIds => {
258
+ currentEnabledIds = enabledIds === null ? null : [...enabledIds];
259
+ catalog.persistScopedModels(currentEnabledIds);
260
+ host.appendWorkflowStatus("Model selection saved to settings");
261
+ host.requestRender();
262
+ },
263
+ onCancel: close,
264
+ });
265
+ const component = {
266
+ ...selector,
267
+ dispose: () => {
268
+ refresh.stop();
269
+ selector.dispose?.();
270
+ },
271
+ };
272
+ host.setInputSurface(component);
273
+ host.requestRender();
274
+ refresh.run(host, component, signal => catalog.refreshScopedModels(signal), refreshed => {
275
+ if (!selectionChanged) {
276
+ currentEnabledIds = refreshed.enabledModelIds === null ? null : [...refreshed.enabledModelIds];
277
+ component.updateModels(refreshed.models, currentEnabledIds);
278
+ }
279
+ else {
280
+ component.updateModels(refreshed.models);
281
+ catalog.updateScopedModels(currentEnabledIds);
282
+ }
283
+ });
284
+ },
285
+ };
286
+ }
287
+ /**
288
+ * One model-catalog refresh owned by an open dialog: it aborts after fifteen seconds or when the dialog
289
+ * closes, and a result that arrives after the dialog closed is dropped.
290
+ */
291
+ class ModelCatalogRefresh {
292
+ #controller = new AbortController();
293
+ #timeout;
294
+ #stopped = false;
295
+ #timedOut = false;
296
+ constructor() {
297
+ this.#timeout = setTimeout(() => {
298
+ this.#timedOut = true;
299
+ this.#controller.abort();
300
+ }, MODEL_REFRESH_TIMEOUT_MS);
301
+ }
302
+ get stopped() { return this.#stopped; }
303
+ stop() {
304
+ this.#stopped = true;
305
+ clearTimeout(this.#timeout);
306
+ this.#controller.abort();
307
+ }
308
+ run(host, dialog, refresh, apply) {
309
+ void refresh(this.#controller.signal).then(refreshed => {
310
+ if (this.#stopped)
311
+ return;
312
+ apply(refreshed);
313
+ dialog.setRefreshStatus(this.#timedOut ? MODEL_REFRESH_TIMED_OUT : refreshed.status, this.#timedOut ? "warning" : refreshed.statusKind);
314
+ host.requestRender();
315
+ }).catch(error => {
316
+ if (this.#stopped)
317
+ return;
318
+ dialog.setRefreshStatus(this.#timedOut
319
+ ? MODEL_REFRESH_TIMED_OUT
320
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`, "warning");
321
+ host.requestRender();
322
+ }).finally(() => clearTimeout(this.#timeout));
323
+ }
324
+ }
325
+ function closeSurface(host) {
326
+ return () => {
327
+ host.setInputSurface(null);
328
+ host.requestRender();
329
+ };
330
+ }
331
+ function opaqueRenderer(value) {
332
+ return (typeof value === "object" && value !== null) || typeof value === "function" ? value : undefined;
333
+ }
334
+ function modelReference(model) {
335
+ if (typeof model !== "object" || model === null)
336
+ return "";
337
+ const value = model;
338
+ const provider = typeof value.provider === "string" ? value.provider : "";
339
+ const id = typeof value.id === "string" ? value.id : typeof value.modelId === "string" ? value.modelId : "";
340
+ return provider && id ? `${provider}/${id}` : "";
341
+ }