@timurproko/a1 0.1.8-dev.512 → 0.1.8-dev.518
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -1
- package/dist/app/session-shell/index.d.ts +1 -1
- package/dist/app/session-shell/prompt-suggestion-controller.d.ts +2 -0
- package/dist/app/session-shell/prompt-suggestion-controller.js +12 -0
- package/dist/app/session-shell/session-shell-root.d.ts +10 -0
- package/dist/app/session-shell/session-shell-root.js +12 -2
- package/dist/app/session-shell/session-shell.d.ts +8 -1
- package/dist/app/session-shell/session-shell.js +217 -18
- package/dist/composition/index.d.ts +1 -1
- package/dist/composition/owned-ui.js +21 -3
- package/dist/composition/settings-route-host.d.ts +17 -4
- package/dist/composition/settings-route-host.js +47 -16
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/model.d.ts +1 -5
- package/dist/features/owned-ui/index.d.ts +3 -0
- package/dist/features/owned-ui/index.js +2 -0
- package/dist/features/owned-ui/reference-routes.d.ts +6 -0
- package/dist/features/owned-ui/reference-routes.js +6 -0
- package/dist/features/owned-ui/reference-screen-app.d.ts +37 -0
- package/dist/features/owned-ui/reference-screen-app.js +232 -0
- package/dist/features/owned-ui/settings-app.js +2 -7
- package/dist/integrations/pi/components/index.d.ts +4 -2
- package/dist/integrations/pi/components/index.js +2 -1
- package/dist/integrations/pi/components/models-dialog.d.ts +55 -0
- package/dist/integrations/pi/components/models-dialog.js +310 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +10 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +29 -5
- package/dist/integrations/pi/components/shell-footer-status.js +2 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +10 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +31 -11
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +11 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +13 -0
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +8 -0
- package/dist/integrations/pi/components/skills-command.d.ts +47 -0
- package/dist/integrations/pi/components/skills-command.js +118 -0
- package/dist/integrations/pi/components/skills-dialog.d.ts +9 -0
- package/dist/integrations/pi/components/skills-dialog.js +113 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +42 -7
- package/dist/integrations/pi/engine/adapter.d.ts +5 -1
- package/dist/integrations/pi/engine/adapter.js +13 -1
- package/dist/integrations/pi/engine/index.d.ts +2 -2
- package/dist/integrations/pi/engine/index.js +1 -1
- package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
- package/dist/integrations/pi/engine/resource-catalog.js +19 -9
- package/dist/integrations/pi/engine/workflow-contexts.d.ts +8 -1
- package/dist/integrations/pi/engine/workflow-contexts.js +52 -8
- package/dist/integrations/pi/engine/workflow-runner.js +98 -74
- package/dist/integrations/pi/engine/workflows.d.ts +26 -2
- package/dist/integrations/pi/engine/workflows.js +8 -0
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/ui/apps/contracts.d.ts +9 -1
- package/dist/ui/apps/index.d.ts +1 -1
- package/dist/ui/settings/declarations.d.ts +15 -29
- package/dist/ui/settings/declarations.js +14 -25
- package/dist/ui/settings/index.d.ts +1 -1
- package/dist/ui/settings/index.js +1 -1
- package/dist/ui/settings/migrations.js +18 -0
- package/docs/architecture/prompt-suggestions.md +1 -1
- package/docs/architecture/ui-reference-provenance.md +1 -0
- package/docs/features/modal-content-interaction.md +3 -2
- package/package.json +1 -1
|
@@ -156,23 +156,67 @@ export class PiWorkflowContexts {
|
|
|
156
156
|
requireCapability(runtime.services.settingsManager?.setEnabledModels, "setEnabledModels").call(runtime.services.settingsManager, patterns === undefined ? undefined : [...patterns]);
|
|
157
157
|
}
|
|
158
158
|
async refreshScopedModels(signal) {
|
|
159
|
+
const outcome = await this.#refreshCatalogs(signal);
|
|
160
|
+
return { ...this.pinnedScopedModelsContext(), ...outcome };
|
|
161
|
+
}
|
|
162
|
+
/** The bare-A1 Models dialog's read: catalog, active model, and the session and persisted scopes kept separate. */
|
|
163
|
+
modelsContext() {
|
|
164
|
+
const session = this.#requireSession();
|
|
165
|
+
const runtime = this.#ports.runtime();
|
|
166
|
+
if (!runtime)
|
|
167
|
+
throw new Error("engine runtime is unavailable");
|
|
168
|
+
const models = scopedModelRecords(runtime.services.modelRuntime);
|
|
169
|
+
const scoped = session.scopedModels;
|
|
170
|
+
const active = this.#ports.activeModel();
|
|
171
|
+
const patterns = runtime.services.settingsManager?.getEnabledModels?.();
|
|
172
|
+
return {
|
|
173
|
+
models: models.map(item => item.descriptor),
|
|
174
|
+
activeModelId: active === null ? null : `${active.providerId}/${active.modelId}`,
|
|
175
|
+
sessionScopeIds: Array.isArray(scoped) ? scoped.map(scopedModelReference).filter((id) => id !== undefined) : [],
|
|
176
|
+
persistedScopeIds: Array.isArray(patterns)
|
|
177
|
+
? resolveConfiguredModelIds(patterns.filter((value) => typeof value === "string"), models)
|
|
178
|
+
: [],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Replace the session's cycling scope with exactly these available models, in order; empty restores the all-model fallback. */
|
|
182
|
+
setSessionModelScope(scopeIds) {
|
|
183
|
+
const session = this.#requireSession();
|
|
184
|
+
const runtime = this.#ports.runtime();
|
|
185
|
+
if (!runtime)
|
|
186
|
+
throw new Error("engine runtime is unavailable");
|
|
187
|
+
const models = scopedModelRecords(runtime.services.modelRuntime);
|
|
188
|
+
const scoped = scopeIds.flatMap(id => {
|
|
189
|
+
const item = models.find(candidate => `${candidate.descriptor.provider}/${candidate.descriptor.id}` === id);
|
|
190
|
+
return item === undefined ? [] : [{ model: item.model }];
|
|
191
|
+
});
|
|
192
|
+
requireCapability(session.setScopedModels, "setScopedModels").call(session, scoped);
|
|
193
|
+
this.#ports.emitView();
|
|
194
|
+
}
|
|
195
|
+
/** Persist the explicit scope as `enabledModels`; an empty scope removes the setting so cycling falls back to every model. */
|
|
196
|
+
persistModelScope(scopeIds) {
|
|
197
|
+
const runtime = this.#ports.runtime();
|
|
198
|
+
if (!runtime)
|
|
199
|
+
throw new Error("engine runtime is unavailable");
|
|
200
|
+
requireCapability(runtime.services.settingsManager?.setEnabledModels, "setEnabledModels")
|
|
201
|
+
.call(runtime.services.settingsManager, scopeIds.length === 0 ? undefined : [...scopeIds]);
|
|
202
|
+
}
|
|
203
|
+
async refreshModels(signal) {
|
|
204
|
+
const outcome = await this.#refreshCatalogs(signal);
|
|
205
|
+
return { models: this.modelsContext().models, ...outcome };
|
|
206
|
+
}
|
|
207
|
+
async #refreshCatalogs(signal) {
|
|
159
208
|
const runtime = this.#ports.runtime();
|
|
160
209
|
if (!runtime)
|
|
161
210
|
throw new Error("engine runtime is unavailable");
|
|
162
211
|
const result = await runtime.services.modelRuntime.refresh?.({ signal });
|
|
163
|
-
const context = this.pinnedScopedModelsContext();
|
|
164
212
|
if (isRecord(result) && result.aborted === true) {
|
|
165
|
-
return {
|
|
213
|
+
return { status: "Model refresh timed out; showing cached models.", statusKind: "warning" };
|
|
166
214
|
}
|
|
167
215
|
const errors = isRecord(result) ? result.errors : undefined;
|
|
168
216
|
if (errors instanceof Map && errors.size > 0) {
|
|
169
|
-
return {
|
|
170
|
-
...context,
|
|
171
|
-
status: `Could not refresh ${[...errors.keys()].join(", ")}; showing cached models.`,
|
|
172
|
-
statusKind: "warning",
|
|
173
|
-
};
|
|
217
|
+
return { status: `Could not refresh ${[...errors.keys()].join(", ")}; showing cached models.`, statusKind: "warning" };
|
|
174
218
|
}
|
|
175
|
-
return {
|
|
219
|
+
return { status: "Model catalogs refreshed.", statusKind: "success" };
|
|
176
220
|
}
|
|
177
221
|
pinnedLoginMethodOptions(providerReference) {
|
|
178
222
|
const runtime = this.#ports.runtime();
|
|
@@ -150,6 +150,86 @@ export class PiWorkflowRunner {
|
|
|
150
150
|
return workflowResult(request.command, "failed", contextualMessage);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
async #selectModel(request, reference, options) {
|
|
154
|
+
const session = this.#requireSession();
|
|
155
|
+
const runtime = this.#ports.runtime();
|
|
156
|
+
if (!runtime)
|
|
157
|
+
throw new Error("engine runtime is unavailable");
|
|
158
|
+
let model = findExactWorkflowModel(reference, options.pool);
|
|
159
|
+
const messages = [];
|
|
160
|
+
const generation = this.#ports.sessionGeneration();
|
|
161
|
+
const current = () => !this.#ports.disposed() && generation === this.#ports.sessionGeneration();
|
|
162
|
+
const publish = (message) => {
|
|
163
|
+
if (!current())
|
|
164
|
+
return;
|
|
165
|
+
const interaction = this.#ports.interaction();
|
|
166
|
+
if (interaction.publish)
|
|
167
|
+
interaction.publish(message);
|
|
168
|
+
else
|
|
169
|
+
messages.push(message);
|
|
170
|
+
};
|
|
171
|
+
if (model === undefined && options.refreshOnMiss) {
|
|
172
|
+
publish({ kind: "status", message: "Refreshing model catalogs…" });
|
|
173
|
+
const controller = new AbortController();
|
|
174
|
+
let timedOut = false;
|
|
175
|
+
const timeout = setTimeout(() => {
|
|
176
|
+
timedOut = true;
|
|
177
|
+
controller.abort();
|
|
178
|
+
}, AUTH_REFRESH_TIMEOUT_MS);
|
|
179
|
+
try {
|
|
180
|
+
const refreshed = await runtime.services.modelRuntime.refresh?.({ signal: controller.signal });
|
|
181
|
+
if (isRecord(refreshed) && refreshed.aborted === true && timedOut) {
|
|
182
|
+
publish({ kind: "warning", message: "Model refresh timed out; searching cached models." });
|
|
183
|
+
}
|
|
184
|
+
else if (isRecord(refreshed) && refreshed.errors instanceof Map && refreshed.errors.size > 0) {
|
|
185
|
+
publish({ kind: "warning", message: `Could not refresh ${[...refreshed.errors.keys()].join(", ")}; searching cached models.` });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
publish({
|
|
190
|
+
kind: "warning",
|
|
191
|
+
message: timedOut
|
|
192
|
+
? "Model refresh timed out; searching cached models."
|
|
193
|
+
: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
}
|
|
199
|
+
model = findExactWorkflowModel(reference, [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])]);
|
|
200
|
+
}
|
|
201
|
+
if (!current())
|
|
202
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
203
|
+
if (model === undefined) {
|
|
204
|
+
if (options.onMiss === "failed") {
|
|
205
|
+
const failure = { kind: "error", message: `Model not found: ${reference}` };
|
|
206
|
+
return workflowResult(request.command, "failed", failure.message, undefined, "error", messages.length === 0 ? undefined : [...messages, failure]);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
...workflowResult(request.command, "requires-selection", "Select a model", reference, "silent"),
|
|
210
|
+
...(messages.length === 0 ? {} : { messages: Object.freeze(messages) }),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
await session.setModel(model, { persist: options.persist });
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
if (!current())
|
|
218
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
219
|
+
const failure = { kind: "error", message: error instanceof Error ? error.message : String(error) };
|
|
220
|
+
return workflowResult(request.command, "failed", failure.message, undefined, "error", messages.length === 0 ? undefined : [...messages, failure]);
|
|
221
|
+
}
|
|
222
|
+
if (!current())
|
|
223
|
+
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
224
|
+
const providerId = stringProperty(model, "provider") ?? "unknown";
|
|
225
|
+
const modelId = stringProperty(model, "id") ?? reference;
|
|
226
|
+
this.#ports.setActiveModel({ providerId, modelId, displayName: stringProperty(model, "name") ?? modelId });
|
|
227
|
+
this.#ports.emitView();
|
|
228
|
+
const resultMessage = { kind: "status", message: options.persist ? `Default model: ${providerId}/${modelId}` : `Model: ${modelId}` };
|
|
229
|
+
return messages.length === 0
|
|
230
|
+
? workflowResult(request.command, "completed", resultMessage.message)
|
|
231
|
+
: workflowResult(request.command, "completed", resultMessage.message, undefined, "status", [...messages, resultMessage]);
|
|
232
|
+
}
|
|
153
233
|
async #performWorkflow(request) {
|
|
154
234
|
const session = this.#requireSession();
|
|
155
235
|
const runtime = this.#ports.runtime();
|
|
@@ -191,80 +271,24 @@ export class PiWorkflowRunner {
|
|
|
191
271
|
const scopedModels = Array.isArray(session.scopedModels)
|
|
192
272
|
? session.scopedModels.map(item => item.model)
|
|
193
273
|
: [];
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
: [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])]
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const controller = new AbortController();
|
|
213
|
-
let timedOut = false;
|
|
214
|
-
const timeout = setTimeout(() => {
|
|
215
|
-
timedOut = true;
|
|
216
|
-
controller.abort();
|
|
217
|
-
}, AUTH_REFRESH_TIMEOUT_MS);
|
|
218
|
-
try {
|
|
219
|
-
const refreshed = await runtime.services.modelRuntime.refresh?.({ signal: controller.signal });
|
|
220
|
-
if (isRecord(refreshed) && refreshed.aborted === true && timedOut) {
|
|
221
|
-
publish({ kind: "warning", message: "Model refresh timed out; searching cached models." });
|
|
222
|
-
}
|
|
223
|
-
else if (isRecord(refreshed) && refreshed.errors instanceof Map && refreshed.errors.size > 0) {
|
|
224
|
-
publish({ kind: "warning", message: `Could not refresh ${[...refreshed.errors.keys()].join(", ")}; searching cached models.` });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
publish({
|
|
229
|
-
kind: "warning",
|
|
230
|
-
message: timedOut
|
|
231
|
-
? "Model refresh timed out; searching cached models."
|
|
232
|
-
: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
finally {
|
|
236
|
-
clearTimeout(timeout);
|
|
237
|
-
}
|
|
238
|
-
availableModels = [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
|
|
239
|
-
model = findExactWorkflowModel(reference, availableModels);
|
|
240
|
-
}
|
|
241
|
-
if (!current())
|
|
242
|
-
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
243
|
-
if (model === undefined) {
|
|
244
|
-
return {
|
|
245
|
-
...workflowResult(request.command, "requires-selection", "Select a model", reference, "silent"),
|
|
246
|
-
...(messages.length === 0 ? {} : { messages: Object.freeze(messages) }),
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
try {
|
|
250
|
-
await session.setModel(model, { persist: request.persist === true });
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
if (!current())
|
|
254
|
-
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
255
|
-
const failure = { kind: "error", message: error instanceof Error ? error.message : String(error) };
|
|
256
|
-
return workflowResult(request.command, "failed", failure.message, undefined, "error", messages.length === 0 ? undefined : [...messages, failure]);
|
|
257
|
-
}
|
|
258
|
-
if (!current())
|
|
259
|
-
return workflowResult(request.command, "cancelled", "Model selection cancelled", undefined, "silent");
|
|
260
|
-
const providerId = stringProperty(model, "provider") ?? "unknown";
|
|
261
|
-
const modelId = stringProperty(model, "id") ?? reference;
|
|
262
|
-
this.#ports.setActiveModel({ providerId, modelId, displayName: stringProperty(model, "name") ?? modelId });
|
|
263
|
-
this.#ports.emitView();
|
|
264
|
-
const resultMessage = { kind: "status", message: request.persist === true ? `Default model: ${providerId}/${modelId}` : `Model: ${modelId}` };
|
|
265
|
-
return messages.length === 0
|
|
266
|
-
? workflowResult(request.command, "completed", resultMessage.message)
|
|
267
|
-
: workflowResult(request.command, "completed", resultMessage.message, undefined, "status", [...messages, resultMessage]);
|
|
274
|
+
// Compatibility: pinned Pi searches the session scope when one exists and refreshes catalogs only for an unscoped miss.
|
|
275
|
+
return this.#selectModel(request, reference, {
|
|
276
|
+
pool: scopedModels.length > 0 ? scopedModels : [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])],
|
|
277
|
+
refreshOnMiss: scopedModels.length === 0,
|
|
278
|
+
persist: request.persist === true,
|
|
279
|
+
onMiss: "requires-selection",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
case "models": {
|
|
283
|
+
// Invariant: the bare dialog seeds its query from the argument; only an explicit selection switches models.
|
|
284
|
+
if (!selection)
|
|
285
|
+
return workflowResult(request.command, "requires-selection", "Select a model", argument || undefined, "silent");
|
|
286
|
+
return this.#selectModel(request, selection, {
|
|
287
|
+
pool: [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])],
|
|
288
|
+
refreshOnMiss: true,
|
|
289
|
+
persist: true,
|
|
290
|
+
onMiss: "failed",
|
|
291
|
+
});
|
|
268
292
|
}
|
|
269
293
|
case "scoped-models": {
|
|
270
294
|
if (!selection)
|
|
@@ -3,7 +3,16 @@ export declare const PINNED_PI_WORKFLOW_COMMAND_NAMES: readonly ["settings", "mo
|
|
|
3
3
|
export declare const PINNED_PI_HIDDEN_COMMAND_NAMES: readonly ["debug", "arminsayshi", "dementedelves"];
|
|
4
4
|
export type PiWorkflowCommandName = typeof PINNED_PI_WORKFLOW_COMMAND_NAMES[number];
|
|
5
5
|
export type PiHiddenWorkflowCommandName = typeof PINNED_PI_HIDDEN_COMMAND_NAMES[number];
|
|
6
|
-
|
|
6
|
+
/** The one bare-A1 model command; it replaces the pinned `model` and `scoped-models` routes there. */
|
|
7
|
+
export declare const OWNED_MODELS_COMMAND_NAME: "models";
|
|
8
|
+
export type OwnedModelsCommandName = typeof OWNED_MODELS_COMMAND_NAME;
|
|
9
|
+
export type OwnedWorkflowCommandName = Exclude<PiWorkflowCommandName, "model" | "scoped-models"> | OwnedModelsCommandName;
|
|
10
|
+
/** Bare A1's advertised command catalog: the pinned catalog with `models` in place of `model` and `scoped-models`. */
|
|
11
|
+
export declare const OWNED_WORKFLOW_COMMAND_NAMES: readonly OwnedWorkflowCommandName[];
|
|
12
|
+
export type PiProductMode = "bare" | "comparison";
|
|
13
|
+
/** The advertised command catalog for a product mode; hidden routes are shared by both. */
|
|
14
|
+
export declare function workflowCommandNames(productMode: PiProductMode): readonly (PiWorkflowCommandName | OwnedModelsCommandName)[];
|
|
15
|
+
export type PiWorkflowRoute = PiWorkflowCommandName | PiHiddenWorkflowCommandName | OwnedModelsCommandName;
|
|
7
16
|
export declare const PINNED_PI_SETTINGS_CALLBACKS: readonly ["onAutoCompactChange", "onShowImagesChange", "onImageWidthCellsChange", "onAutoResizeImagesChange", "onBlockImagesChange", "onEnableSkillCommandsChange", "onSteeringModeChange", "onFollowUpModeChange", "onTransportChange", "onHttpIdleTimeoutMsChange", "onModelThinkingLevelChange", "onModelThinkingLevelRemove", "onThemeChange", "onThemePreview", "onHideThinkingBlockChange", "onMermaidRenderingModeChange", "onShowCacheMissNoticesChange", "onCollapseChangelogChange", "onEnableInstallTelemetryChange", "onQuietStartupChange", "onDefaultProjectTrustChange", "onDoubleEscapeActionChange", "onTreeFilterModeChange", "onShowHardwareCursorChange", "onEditorPaddingXChange", "onOutputPadChange", "onAutocompleteMaxVisibleChange", "onClearOnShrinkChange", "onShowTerminalProgressChange", "onTuiModeChange", "onFullscreenExitOutputChange", "onFullscreenScrollbarChange", "onFullscreenCopyOnSelectChange", "onWarningsChange", "onCancel"];
|
|
8
17
|
export type PiPinnedSettingsCallback = typeof PINNED_PI_SETTINGS_CALLBACKS[number];
|
|
9
18
|
export interface PiPinnedSettingsSnapshot {
|
|
@@ -71,7 +80,7 @@ export interface PiWorkflowRequest {
|
|
|
71
80
|
readonly command: PiWorkflowRoute;
|
|
72
81
|
readonly argument: string;
|
|
73
82
|
readonly selection?: string;
|
|
74
|
-
/** For the model route: also persist the selection as the default model. */
|
|
83
|
+
/** For the model route: also persist the selection as the default model. The bare `models` route always persists. */
|
|
75
84
|
readonly persist?: boolean;
|
|
76
85
|
readonly confirmed?: boolean;
|
|
77
86
|
/** Recovery cwd selected after an import/resume source cwd is unavailable. */
|
|
@@ -249,3 +258,18 @@ export interface PiScopedModelsRefreshResult extends PiScopedModelsContext {
|
|
|
249
258
|
readonly status: string;
|
|
250
259
|
readonly statusKind: "success" | "warning";
|
|
251
260
|
}
|
|
261
|
+
/** What the bare-A1 Models dialog reads: the authenticated catalog and the explicit session and persisted scopes, kept apart. */
|
|
262
|
+
export interface PiModelsContext {
|
|
263
|
+
readonly models: readonly PiScopedModelDescriptor[];
|
|
264
|
+
/** `provider/id` of the active model, or null before one is set. */
|
|
265
|
+
readonly activeModelId: string | null;
|
|
266
|
+
/** Ordered `provider/id` references the session cycles; empty is the all-model fallback, not an explicit full scope. */
|
|
267
|
+
readonly sessionScopeIds: readonly string[];
|
|
268
|
+
/** The persisted `enabledModels` patterns resolved against the catalog, in order; unmatched patterns stay verbatim. */
|
|
269
|
+
readonly persistedScopeIds: readonly string[];
|
|
270
|
+
}
|
|
271
|
+
export interface PiModelsRefreshResult {
|
|
272
|
+
readonly models: readonly PiScopedModelDescriptor[];
|
|
273
|
+
readonly status: string;
|
|
274
|
+
readonly statusKind: "success" | "warning";
|
|
275
|
+
}
|
|
@@ -24,6 +24,14 @@ export const PINNED_PI_WORKFLOW_COMMAND_NAMES = [
|
|
|
24
24
|
"quit",
|
|
25
25
|
];
|
|
26
26
|
export const PINNED_PI_HIDDEN_COMMAND_NAMES = ["debug", "arminsayshi", "dementedelves"];
|
|
27
|
+
/** The one bare-A1 model command; it replaces the pinned `model` and `scoped-models` routes there. */
|
|
28
|
+
export const OWNED_MODELS_COMMAND_NAME = "models";
|
|
29
|
+
/** Bare A1's advertised command catalog: the pinned catalog with `models` in place of `model` and `scoped-models`. */
|
|
30
|
+
export const OWNED_WORKFLOW_COMMAND_NAMES = Object.freeze(PINNED_PI_WORKFLOW_COMMAND_NAMES.flatMap((name) => name === "model" ? [OWNED_MODELS_COMMAND_NAME] : name === "scoped-models" ? [] : [name]));
|
|
31
|
+
/** The advertised command catalog for a product mode; hidden routes are shared by both. */
|
|
32
|
+
export function workflowCommandNames(productMode) {
|
|
33
|
+
return productMode === "bare" ? OWNED_WORKFLOW_COMMAND_NAMES : PINNED_PI_WORKFLOW_COMMAND_NAMES;
|
|
34
|
+
}
|
|
27
35
|
export const PINNED_PI_SETTINGS_CALLBACKS = [
|
|
28
36
|
"onAutoCompactChange",
|
|
29
37
|
"onShowImagesChange",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "darwin",
|
|
6
6
|
"architecture": "arm64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-19T16:47:03.520Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "9db1726bbe3fc2e8292f2ead1e7e9d0fd4d7d0dc9217b372582bf354b0f565dc",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"platform": "linux",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-19T16:46:51.393Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian",
|
|
11
11
|
"sha256": "d8cda6b0c7cb36c0cc41e90802aceebf6c02eaebbc08a83d7d963d2e918dbd7a",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"platform": "win32",
|
|
6
6
|
"architecture": "x64",
|
|
7
7
|
"capability": "supported",
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-19T16:47:27.951Z",
|
|
9
9
|
"artifact": {
|
|
10
10
|
"filename": "process-guardian.exe",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "760dc53aa17d5e0df1c0fa5cbd3be56f73b6cf662b0cef5994edffff68ca9d8f",
|
|
12
12
|
"size": 177664
|
|
13
13
|
},
|
|
14
14
|
"provenance": {
|
|
Binary file
|
|
@@ -56,9 +56,17 @@ export interface UiRouteSurface {
|
|
|
56
56
|
/** The surface asks to leave A1 entirely, not merely to close itself. */
|
|
57
57
|
onExitRequested(listener: () => void): void;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* What a shell may hand a route when it opens it. A route that does not use the
|
|
61
|
+
* input opens exactly as it does without one.
|
|
62
|
+
*/
|
|
63
|
+
export interface UiRouteInput {
|
|
64
|
+
/** A caller-supplied document for a route that presents documents. */
|
|
65
|
+
readonly document?: string;
|
|
66
|
+
}
|
|
59
67
|
/** Route lookup seam shared by owned apps and any presentation runtime hosting them. */
|
|
60
68
|
export interface UiRouteHost {
|
|
61
69
|
claims(route: string): boolean;
|
|
62
|
-
open(route: string): UiRouteSurface | null;
|
|
70
|
+
open(route: string, input?: UiRouteInput): UiRouteSurface | null;
|
|
63
71
|
}
|
|
64
72
|
export declare function assertAppRegistration(registration: AppRegistration): void;
|
package/dist/ui/apps/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { assertAppRegistration } from "./contracts.js";
|
|
2
|
-
export type { AppHostServices, AppRegistration, AppSize, UiApp, UiRouteHost, UiRouteSurface } from "./contracts.js";
|
|
2
|
+
export type { AppHostServices, AppRegistration, AppSize, UiApp, UiRouteHost, UiRouteInput, UiRouteSurface } from "./contracts.js";
|
|
3
3
|
export { UiAppHost } from "./host.js";
|
|
4
4
|
export type { AppHostOptions, AppHostSurface } from "./host.js";
|
|
5
5
|
export { UiAppRegistry } from "./registry.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export declare const OWNED_UI_SETTINGS_VERSION = 8;
|
|
2
2
|
export type OwnedUiSettingValue = string | number | boolean;
|
|
3
3
|
export type OwnedUiSettingApplication = "live" | "restart";
|
|
4
4
|
export interface OwnedUiSettingDeclaration {
|
|
@@ -15,8 +15,6 @@ export interface OwnedUiSettingDeclaration {
|
|
|
15
15
|
readonly defaultValue: OwnedUiSettingValue;
|
|
16
16
|
readonly allowedValues: readonly OwnedUiSettingValue[];
|
|
17
17
|
}
|
|
18
|
-
/** Playback lengths the quit outro offers, in milliseconds. */
|
|
19
|
-
export declare const QUIT_EFFECT_DURATIONS_MS: readonly number[];
|
|
20
18
|
/**
|
|
21
19
|
* Every A1 setting, keyed by its id. The table is the one declaration: ids, defaults, and allowed
|
|
22
20
|
* values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
|
|
@@ -24,12 +22,12 @@ export declare const QUIT_EFFECT_DURATIONS_MS: readonly number[];
|
|
|
24
22
|
export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
25
23
|
readonly quitAnimation: Readonly<{
|
|
26
24
|
id: "quitAnimation";
|
|
27
|
-
label: "
|
|
25
|
+
label: "Quit animation";
|
|
28
26
|
section: Readonly<{
|
|
29
27
|
id: "generic";
|
|
30
28
|
title: "Generic";
|
|
31
29
|
}>;
|
|
32
|
-
description: "Play the
|
|
30
|
+
description: "Play the fall effect over the last screen when the session quits. Off returns to the terminal immediately.";
|
|
33
31
|
application: "live";
|
|
34
32
|
defaultValue: true;
|
|
35
33
|
allowedValues: readonly [true, false];
|
|
@@ -94,30 +92,6 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
94
92
|
defaultValue: 100;
|
|
95
93
|
allowedValues: readonly [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
|
|
96
94
|
}>;
|
|
97
|
-
readonly quitEffect: Readonly<{
|
|
98
|
-
id: "quitEffect";
|
|
99
|
-
label: "Effect";
|
|
100
|
-
section: Readonly<{
|
|
101
|
-
id: "quit";
|
|
102
|
-
title: "Quit";
|
|
103
|
-
}>;
|
|
104
|
-
description: "Animation played over the last screen when the session quits.";
|
|
105
|
-
application: "live";
|
|
106
|
-
defaultValue: "fall";
|
|
107
|
-
allowedValues: readonly ["fall", "dissolve", "starburst", "waves"];
|
|
108
|
-
}>;
|
|
109
|
-
readonly quitEffectDurationMs: Readonly<{
|
|
110
|
-
id: "quitEffectDurationMs";
|
|
111
|
-
label: "Duration";
|
|
112
|
-
section: Readonly<{
|
|
113
|
-
id: "quit";
|
|
114
|
-
title: "Quit";
|
|
115
|
-
}>;
|
|
116
|
-
description: "Milliseconds the quit animation plays before the terminal is restored.";
|
|
117
|
-
application: "live";
|
|
118
|
-
defaultValue: 800;
|
|
119
|
-
allowedValues: readonly number[];
|
|
120
|
-
}>;
|
|
121
95
|
readonly promptSuggestions: Readonly<{
|
|
122
96
|
id: "promptSuggestions";
|
|
123
97
|
label: "Prompt suggestions";
|
|
@@ -130,6 +104,18 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
130
104
|
defaultValue: true;
|
|
131
105
|
allowedValues: readonly [true, false];
|
|
132
106
|
}>;
|
|
107
|
+
readonly skillsPresentation: Readonly<{
|
|
108
|
+
id: "skillsPresentation";
|
|
109
|
+
label: "Skills";
|
|
110
|
+
section: Readonly<{
|
|
111
|
+
id: "agent";
|
|
112
|
+
title: "Agent";
|
|
113
|
+
}>;
|
|
114
|
+
description: "Collapse offers one /skills command with a searchable dialog and the /skills: shortcut; expand lists every /skill:<name> command directly.";
|
|
115
|
+
application: "live";
|
|
116
|
+
defaultValue: "collapse";
|
|
117
|
+
allowedValues: readonly ["collapse", "expand"];
|
|
118
|
+
}>;
|
|
133
119
|
}>;
|
|
134
120
|
export type OwnedSettingId = keyof typeof OWNED_SETTING_DECLARATIONS;
|
|
135
121
|
/** The value type a setting's declaration allows: a literal union for a choice, `number` for a stepped range. */
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
export const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export const OWNED_UI_SETTINGS_VERSION = 8;
|
|
2
2
|
const MAX_ID_LENGTH = 64;
|
|
3
3
|
const ID_PATTERN = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
|
|
4
4
|
/** Declared first so the settings screen opens on it; sections follow first-declaration order. */
|
|
5
5
|
const GENERIC_SECTION = Object.freeze({ id: "generic", title: "Generic" });
|
|
6
6
|
const SCROLL_SECTION = Object.freeze({ id: "scroll", title: "Scroll" });
|
|
7
|
-
const
|
|
8
|
-
/** Playback lengths the quit outro offers, in milliseconds. */
|
|
9
|
-
export const QUIT_EFFECT_DURATIONS_MS = Object.freeze(Array.from({ length: 18 }, (_, index) => 300 + index * 100));
|
|
7
|
+
const AGENT_SECTION = Object.freeze({ id: "agent", title: "Agent" });
|
|
10
8
|
/**
|
|
11
9
|
* Every A1 setting, keyed by its id. The table is the one declaration: ids, defaults, and allowed
|
|
12
10
|
* values are read from it, and `OwnedSettingValueOf` derives each getter's type from it.
|
|
@@ -14,9 +12,9 @@ export const QUIT_EFFECT_DURATIONS_MS = Object.freeze(Array.from({ length: 18 },
|
|
|
14
12
|
export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
15
13
|
quitAnimation: Object.freeze({
|
|
16
14
|
id: "quitAnimation",
|
|
17
|
-
label: "
|
|
15
|
+
label: "Quit animation",
|
|
18
16
|
section: GENERIC_SECTION,
|
|
19
|
-
description: "Play the
|
|
17
|
+
description: "Play the fall effect over the last screen when the session quits. Off returns to the terminal immediately.",
|
|
20
18
|
application: "live",
|
|
21
19
|
defaultValue: true,
|
|
22
20
|
allowedValues: Object.freeze([true, false]),
|
|
@@ -66,33 +64,24 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
66
64
|
defaultValue: 100,
|
|
67
65
|
allowedValues: Object.freeze([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]),
|
|
68
66
|
}),
|
|
69
|
-
quitEffect: Object.freeze({
|
|
70
|
-
id: "quitEffect",
|
|
71
|
-
label: "Effect",
|
|
72
|
-
section: QUIT_SECTION,
|
|
73
|
-
description: "Animation played over the last screen when the session quits.",
|
|
74
|
-
application: "live",
|
|
75
|
-
defaultValue: "fall",
|
|
76
|
-
allowedValues: Object.freeze(["fall", "dissolve", "starburst", "waves"]),
|
|
77
|
-
}),
|
|
78
|
-
quitEffectDurationMs: Object.freeze({
|
|
79
|
-
id: "quitEffectDurationMs",
|
|
80
|
-
label: "Duration",
|
|
81
|
-
section: QUIT_SECTION,
|
|
82
|
-
description: "Milliseconds the quit animation plays before the terminal is restored.",
|
|
83
|
-
application: "live",
|
|
84
|
-
defaultValue: 800,
|
|
85
|
-
allowedValues: QUIT_EFFECT_DURATIONS_MS,
|
|
86
|
-
}),
|
|
87
67
|
promptSuggestions: Object.freeze({
|
|
88
68
|
id: "promptSuggestions",
|
|
89
69
|
label: "Prompt suggestions",
|
|
90
|
-
section:
|
|
70
|
+
section: AGENT_SECTION,
|
|
91
71
|
description: "Predict likely next prompts with one additional background request using the selected model.",
|
|
92
72
|
application: "live",
|
|
93
73
|
defaultValue: true,
|
|
94
74
|
allowedValues: Object.freeze([true, false]),
|
|
95
75
|
}),
|
|
76
|
+
skillsPresentation: Object.freeze({
|
|
77
|
+
id: "skillsPresentation",
|
|
78
|
+
label: "Skills",
|
|
79
|
+
section: AGENT_SECTION,
|
|
80
|
+
description: "Collapse offers one /skills command with a searchable dialog and the /skills: shortcut; expand lists every /skill:<name> command directly.",
|
|
81
|
+
application: "live",
|
|
82
|
+
defaultValue: "collapse",
|
|
83
|
+
allowedValues: Object.freeze(["collapse", "expand"]),
|
|
84
|
+
}),
|
|
96
85
|
});
|
|
97
86
|
/** The declarations in declaration order, for resolution, persistence, and the settings screen. */
|
|
98
87
|
export const OWNED_UI_SETTING_DECLARATIONS = Object.freeze(Object.values(OWNED_SETTING_DECLARATIONS));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING_DECLARATIONS,
|
|
1
|
+
export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING_DECLARATIONS, assertOwnedUiSettingDeclarations, findOwnedUiSettingDeclaration, isOwnedSettingId, } from "./declarations.js";
|
|
2
2
|
export type { OwnedSettingId, OwnedSettingValueOf, OwnedUiSettingApplication, OwnedUiSettingDeclaration, OwnedUiSettingValue } from "./declarations.js";
|
|
3
3
|
export { OWNED_UI_SETTINGS_MIGRATIONS, assertOwnedUiSettingsMigrations, migrationsFrom } from "./migrations.js";
|
|
4
4
|
export type { OwnedUiSettingsMigration } from "./migrations.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING_DECLARATIONS,
|
|
1
|
+
export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING_DECLARATIONS, assertOwnedUiSettingDeclarations, findOwnedUiSettingDeclaration, isOwnedSettingId, } from "./declarations.js";
|
|
2
2
|
export { OWNED_UI_SETTINGS_MIGRATIONS, assertOwnedUiSettingsMigrations, migrationsFrom } from "./migrations.js";
|
|
3
3
|
export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, settingValue } from "./resolution.js";
|
|
4
4
|
export { AGENT_SECTION_ID, buildOwnedUiSettingsSections, findOwnedUiSettingsEntry } from "./sections.js";
|
|
@@ -49,6 +49,24 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
|
|
|
49
49
|
return migrated;
|
|
50
50
|
},
|
|
51
51
|
}),
|
|
52
|
+
Object.freeze({
|
|
53
|
+
to: 7,
|
|
54
|
+
description: "Drop the quit effect and duration; the outro always plays fall for 800 ms.",
|
|
55
|
+
migrate(values) {
|
|
56
|
+
// Invariant: the manager keeps unknown keys, so the retired ids are removed here rather than lingering.
|
|
57
|
+
const migrated = { ...values };
|
|
58
|
+
delete migrated.quitEffect;
|
|
59
|
+
delete migrated.quitEffectDurationMs;
|
|
60
|
+
return migrated;
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
Object.freeze({
|
|
64
|
+
to: 8,
|
|
65
|
+
description: "Introduce the collapsed skills presentation with the collapse default.",
|
|
66
|
+
migrate(values) {
|
|
67
|
+
return { ...values };
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
52
70
|
]);
|
|
53
71
|
export function assertOwnedUiSettingsMigrations(migrations, currentVersion = OWNED_UI_SETTINGS_VERSION) {
|
|
54
72
|
const firstProduced = currentVersion - migrations.length + 1;
|
|
@@ -6,7 +6,7 @@ Bare A1 predicts a short next user input with one isolated request using the sel
|
|
|
6
6
|
|
|
7
7
|
The adapter resolves the lowest supported effort from the selected session model's available thinking levels, without changing the main thinking setting. Non-reasoning models omit reasoning controls; supported `off` also omits the optional reasoning argument, matching the model runtime's ordinary completion contract. Other supported efforts are supplied explicitly. Missing capabilities produce `unavailable`, not a guessed provider option. Provider mapping/clamping remains owned by the pinned model runtime; the diagnostic policy describes the applied request option, not inferred server-side computation.
|
|
8
8
|
|
|
9
|
-
The controller retains the 15-second deadline, does not retry or switch models, and retires a timeout before aborting. A provider that ignores abort cannot revive retired text. A timely candidate remains private until matching settlement. Ghost text is not editor content, approval, or a submission: Tab accepts it into the editor, and Enter must be pressed separately to submit it. Typing, session/model replacement, continuation, or disabling suggestions invalidates unaccepted work. `a1 pi` does not install this feature.
|
|
9
|
+
The controller retains the 15-second deadline, does not retry or switch models, and retires a timeout before aborting. A provider that ignores abort cannot revive retired text. A timely candidate remains private until matching settlement. Ghost text is not editor content, approval, or a submission: Tab accepts it into the editor, and Enter must be pressed separately to submit it. Typing aborts pending generation but keeps a delivered suggestion: the ghost text is hidden while the editor holds a draft and reappears when the draft is cleared. Submission, session/model replacement, continuation, interruption, or disabling suggestions invalidates unaccepted work. `a1 pi` does not install this feature.
|
|
10
10
|
|
|
11
11
|
## Opt-in local diagnostics
|
|
12
12
|
|
|
@@ -30,6 +30,7 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
|
|
|
30
30
|
| `ui-components/mouse.ts` | `core/panes/sgr-mouse.ts` | Same SGR decoding and per-call regex reset; A1 emits its own event shape. |
|
|
31
31
|
| `ui-components/mouse.ts` — tracking sequences | `core/host/pi/providers/host-bridge-surface.ts` | Mouse modes only. A1 does not take the alternate screen, because the Pi TUI owns the screen A1 renders through. |
|
|
32
32
|
| `features/owned-ui/settings-app.ts` — section layout, pointer controls, and scrolling | `settings/impl.ts` — `settingsValueColumn`, block navigation, sticky sections, and pointer hit regions | Setting discovery is A1's own A1/Agent section model. Section navigation and pointer-only numeric controls follow the A1 reference; explicit `/` search, ruled shared input, shortcut-derived hints, hidden description rows, configured wheel cadence, a rail presented through the shared scrollbar settings, `Ctrl+Home`/`Ctrl+End` boundary jumps, and distinct floating scalar menus are reviewed owned interactions. |
|
|
33
|
+
| `pi-components/models-dialog.ts` — the bare-A1 `/models` dialog | `models/index.ts` and `core/editor/components.ts` — `EditorModal` | Keeps the `all`/`scoped` filters, substring search over id/name/provider, provider-then-id ordering, Enter to switch, Space to scope, Ctrl+S to save, the `(unsaved)` marker, and the `●`/`○` scope markers. A1 renders the marker before the id and the active `✓` after `[provider]`, lists the scoped filter in cycling order (saved-but-removed members last), keeps the pinned bulk/provider/reorder bindings, and hands every switch, scope write, save, and catalog refresh to the engine adapter instead of reading settings files itself. |
|
|
33
34
|
|
|
34
35
|
## Ported from the pinned engine
|
|
35
36
|
|
|
@@ -14,6 +14,7 @@ The existing [modal transition inventory](../../config/baselines/modal-surface-i
|
|
|
14
14
|
| --- | --- | --- |
|
|
15
15
|
| `editor.root` | Ordinary editor and viewport | Existing viewport/controller and shell regression cases |
|
|
16
16
|
| `settings.*`, `models.*`, `trust.project` | Shell replacement input, including nested component states | Real settings/thinking, Model Configuration and model selection; generic replacement geometry and gesture tests |
|
|
17
|
+
| A1-owned Skills dialog (`/skills`, not a pinned node) | Shell replacement input, presented like `models.select` | `test/app/session-shell/session-shell-skills.test.ts` apply/cancel/resize cases; shared replacement tests |
|
|
17
18
|
| `session.*`, `tree.*` | Shell replacement input and nested replacement/confirmation controllers | Real fork plus existing resume/tree/missing-directory lifecycle cases; shared replacement tests |
|
|
18
19
|
| `auth.*` | Shell replacement input, including provider and nested prompts | Real login/logout plus existing OAuth/API-key/nested restoration cases; shared replacement tests |
|
|
19
20
|
| `command.import-confirm`, `operation.*` | Shell replacement confirmation/loader | Existing confirmation/loader lifecycle cases; shared replacement tests |
|
|
@@ -30,8 +31,8 @@ Focused files:
|
|
|
30
31
|
|
|
31
32
|
## Manual review
|
|
32
33
|
|
|
33
|
-
Build the exact candidate, then launch it through `./scripts/dev` from its worktree in Windows Terminal/Git Bash.
|
|
34
|
+
Build the exact candidate, then launch it through `./scripts/dev` from its worktree in Windows Terminal/Git Bash. Populate a long transcript (for example with a few prompts, or `/hotkeys` under `a1 pi`; in bare A1 `/hotkeys` opens a full-screen reference screen and appends nothing), then open `/scoped-models`. Compare scrolling and drag/word/line selection with the dialog closed and open. Try the rail, copy, and jump-to-bottom; the model dialog must not navigate or close as a side effect of transcript gestures. Copy must not show Pi's `Copied` notification.
|
|
34
35
|
|
|
35
|
-
Repeat with `/
|
|
36
|
+
Repeat with `/models`, settings and a nested settings dialog, and extension-provided docked and floating surfaces. Check that typing, modal buttons and wheel handling, save, and cancel still work. Resize during a drag, close/reopen the modal, and repeat while output streams. A full-cover screen must not allow click-through into hidden transcript cells. `auto` scrollbar visibility remains activity-dependent; `hidden` remains hidden.
|
|
36
37
|
|
|
37
38
|
User confirmation of the exact built candidate is still required before merging the code PR. The baseline's pre-existing selection boundaries and speed are intentionally preserved, as clarified by the user; this change does not attempt to reconcile older specification wording by changing those behaviors.
|