@timurproko/a1 0.2.7-dev.810 → 0.2.7-dev.828
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 +7 -1
- package/bin/automatic-update.js +7 -0
- package/bin/tab-launch-context.d.ts +1 -0
- package/bin/tab-launch-context.js +3 -3
- package/bin/ui.js +96 -66
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +6 -9
- package/dist/app/session-shell/resident-tab-coordinator.js +9 -52
- package/dist/app/session-shell/session-shell-root.d.ts +14 -8
- package/dist/app/session-shell/session-shell-root.js +9 -5
- package/dist/app/session-shell/session-shell.js +70 -20
- package/dist/app/session-shell/session-viewport-controller.js +6 -4
- package/dist/app/session-shell/shell-host.js +6 -3
- package/dist/app/session-shell/transcript-document.js +1 -1
- package/dist/cli/tabs.d.ts +1 -1
- package/dist/cli/tabs.js +10 -14
- package/dist/composition/owned-settings.d.ts +12 -0
- package/dist/composition/owned-settings.js +36 -0
- package/dist/composition/owned-ui.d.ts +5 -14
- package/dist/composition/owned-ui.js +93 -64
- package/dist/composition/resident-tab-bridge.d.ts +10 -1
- package/dist/composition/resident-tab-bridge.js +21 -0
- package/dist/composition/settings-route-host.d.ts +12 -2
- package/dist/composition/settings-route-host.js +27 -9
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -0
- package/dist/composition/utility-tab.d.ts +28 -0
- package/dist/composition/utility-tab.js +242 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
- package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
- package/dist/features/owned-ui/project-trust-dialog.js +6 -8
- package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
- package/dist/features/owned-ui/project-trust-prompt.js +7 -3
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +12 -2
- package/dist/features/owned-ui/settings-app.d.ts +10 -3
- package/dist/features/owned-ui/settings-app.js +98 -45
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
- package/dist/features/resident-tab/shortcut-declarations.js +2 -0
- package/dist/foundation/launch-context/index.d.ts +2 -0
- package/dist/foundation/launch-context/index.js +1 -0
- package/dist/foundation/lifecycle/session-repository-context.js +16 -2
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +1 -9
- package/dist/foundation/release/bootstrap.js +7 -3
- package/dist/foundation/release/early-accent.d.ts +12 -0
- package/dist/foundation/release/early-accent.js +59 -0
- package/dist/foundation/release/index.d.ts +1 -1
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/latest-release.d.ts +3 -2
- package/dist/foundation/release/latest-release.js +1 -1
- package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
- package/dist/foundation/release/progress-palette.generated.js +137 -3
- package/dist/foundation/release/release-gc.js +19 -4
- package/dist/foundation/release/update-execution-lease.d.ts +36 -0
- package/dist/foundation/release/update-execution-lease.js +145 -0
- package/dist/foundation/release/update.d.ts +9 -7
- package/dist/foundation/release/update.js +244 -175
- package/dist/foundation/resident-tabs/bridge-client.d.ts +10 -3
- package/dist/foundation/resident-tabs/bridge-client.js +20 -8
- package/dist/foundation/resident-tabs/maintenance.d.ts +2 -6
- package/dist/foundation/resident-tabs/maintenance.js +5 -23
- package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
- package/dist/foundation/resident-tabs/progress-observation.d.ts +3 -6
- package/dist/foundation/resident-tabs/progress-observation.js +43 -12
- package/dist/foundation/resident-tabs/release-holds.js +3 -14
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/settings.d.ts +0 -1
- package/dist/foundation/resident-tabs/settings.js +0 -2
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +3 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
- package/dist/integrations/pi/components/shell-footer-status.js +19 -3
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
- package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +2 -0
- package/dist/integrations/pi/components/upstream/theme/theme.js +5 -1
- package/dist/integrations/pi/engine/adapter.js +7 -2
- package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
- package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
- package/dist/integrations/pi/engine/ports.d.ts +1 -1
- package/dist/integrations/pi/engine/ports.js +4 -0
- package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
- package/dist/integrations/pi/engine/settings-bridge.js +13 -0
- package/dist/integrations/pi/engine/settings-port.js +1 -14
- package/dist/integrations/pi/engine/workflow-runner.js +9 -2
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +2 -2
- package/dist/product-identity.json +2 -0
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.js +35 -30
- package/dist/ui/components/line-input.d.ts +3 -0
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +5 -4
- package/dist/ui/components/prompt-input.js +8 -6
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/submitted-prompt.d.ts +2 -2
- package/dist/ui/components/submitted-prompt.js +3 -3
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/text.d.ts +3 -6
- package/dist/ui/components/text.js +6 -8
- package/dist/ui/components/transcript-viewport.d.ts +1 -1
- package/dist/ui/components/transcript-viewport.js +141 -24
- package/dist/ui/settings/declarations.d.ts +67 -17
- package/dist/ui/settings/declarations.js +47 -15
- package/dist/ui/settings/early-accent.d.ts +81 -0
- package/dist/ui/settings/early-accent.js +50 -0
- package/dist/ui/settings/manager.js +7 -3
- package/dist/ui/settings/migrations.js +22 -0
- package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
- package/dist/ui/settings/progress-palette.generated.js +139 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/resident-tabs.md +52 -21
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/features/prompt-history.md +41 -20
- package/docs/validation.md +25 -5
- package/package.json +1 -1
|
@@ -322,8 +322,8 @@ export class SessionViewportController {
|
|
|
322
322
|
this.#requestRender();
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
-
const previousPrompt =
|
|
326
|
-
if (editorActive && allowWheel && (previousPrompt ||
|
|
325
|
+
const previousPrompt = PROMPT_UP_INPUTS.has(data) || this.#editor.matchesTerminalKey(data, "alt+home");
|
|
326
|
+
if (editorActive && allowWheel && (previousPrompt || PROMPT_DOWN_INPUTS.has(data))) {
|
|
327
327
|
const scrolled = previousPrompt
|
|
328
328
|
? this.#viewport.scrollToPreviousPrompt(now)
|
|
329
329
|
: this.#viewport.scrollToNextPrompt(now);
|
|
@@ -670,5 +670,7 @@ export class SessionViewportController {
|
|
|
670
670
|
}
|
|
671
671
|
const SELECTION_AUTO_SCROLL_INTERVAL_MS = 30;
|
|
672
672
|
const EDITOR_LINK_DELETION_INPUTS = new Set(["\b", "\u007f", "\u001b[3~"]);
|
|
673
|
-
|
|
674
|
-
|
|
673
|
+
// Platform: Windows Terminal may consume Shift+Arrow for native selection. Ctrl+Arrow matches
|
|
674
|
+
// solo A1's Windows binding while retaining Shift+Arrow whenever the terminal delivers it.
|
|
675
|
+
const PROMPT_UP_INPUTS = new Set(["\u001b[1;2A", "\u001b[1;5A"]);
|
|
676
|
+
const PROMPT_DOWN_INPUTS = new Set(["\u001b[1;2B", "\u001b[1;5B"]);
|
|
@@ -102,10 +102,13 @@ export class OwnedUiTerminalHost {
|
|
|
102
102
|
},
|
|
103
103
|
}),
|
|
104
104
|
} : { layoutRoot: pinnedLayoutRoot(this.#delegatingLayoutParts()) }),
|
|
105
|
-
...(input?.onEvent === undefined ? {} : {
|
|
105
|
+
...(input?.onEvent === undefined && progressObserver === null ? {} : {
|
|
106
106
|
inputDiagnostics: {
|
|
107
|
-
onEvent:
|
|
108
|
-
|
|
107
|
+
onEvent: event => {
|
|
108
|
+
input?.onEvent?.(event);
|
|
109
|
+
progressObserver?.inputPhase?.(event.phase, event.revision);
|
|
110
|
+
},
|
|
111
|
+
...(input?.now === undefined ? {} : { now: input.now }),
|
|
109
112
|
},
|
|
110
113
|
}),
|
|
111
114
|
...(options.terminal === undefined ? {} : { terminal: options.terminal }),
|
|
@@ -260,7 +260,7 @@ export class TranscriptDocument {
|
|
|
260
260
|
: createPiShellChangelog(diagnostic.message).render(width)));
|
|
261
261
|
rows.push(...diagnostics
|
|
262
262
|
.filter(diagnostic => diagnostic.code !== "engine-startup" && diagnostic.code !== "project-trust"
|
|
263
|
-
&& diagnostic.code !== "release-update"
|
|
263
|
+
&& diagnostic.code !== "release-update" && diagnostic.code !== "release-update-installed"
|
|
264
264
|
&& diagnostic.code !== "package-updates" && diagnostic.code !== "changelog-collapsed"
|
|
265
265
|
&& diagnostic.code !== "changelog-expanded")
|
|
266
266
|
.slice(-3)
|
package/dist/cli/tabs.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { runTerminalHostMaintenance, writeDoctorBundle } from "../foundation/res
|
|
|
2
2
|
import type { TabsRequest } from "./tabs-request.js";
|
|
3
3
|
export type { TabsRequest } from "./tabs-request.js";
|
|
4
4
|
export interface TabsCommandOptions {
|
|
5
|
-
/** The
|
|
5
|
+
/** The invoking package, never a different active or retained release. */
|
|
6
6
|
readonly packageRoot?: string;
|
|
7
7
|
readonly environment?: NodeJS.ProcessEnv;
|
|
8
8
|
readonly stdout?: (message: string) => void;
|
package/dist/cli/tabs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveProductPaths } from "../foundation/lifecycle/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { materializeRelease } from "../foundation/release/index.js";
|
|
3
3
|
import { readResidentTabLaunchSettings, runTerminalHostMaintenance, writeDoctorBundle, } from "../foundation/resident-tabs/index.js";
|
|
4
4
|
import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../product-identity.js";
|
|
5
5
|
/** `a1 tabs`: inspect or stop resident tabs without attaching, and never start a host to report state. */
|
|
@@ -9,7 +9,7 @@ export async function runTabsCommand(request, options = {}) {
|
|
|
9
9
|
const stderr = options.stderr ?? (message => process.stderr.write(message));
|
|
10
10
|
const paths = resolveProductPaths(environment);
|
|
11
11
|
const profileRoot = environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir;
|
|
12
|
-
const releaseRoots = await
|
|
12
|
+
const releaseRoots = await invokingReleaseRoots(paths.dataDir, options.packageRoot, environment);
|
|
13
13
|
const maintenance = options.maintenance ?? runTerminalHostMaintenance;
|
|
14
14
|
const report = await maintenance({
|
|
15
15
|
action: request.action === "doctor" ? "host-status" : request.action,
|
|
@@ -51,22 +51,18 @@ export async function runTabsCommand(request, options = {}) {
|
|
|
51
51
|
}
|
|
52
52
|
return report.exitCode === 0 ? 0 : 1;
|
|
53
53
|
}
|
|
54
|
-
async function
|
|
55
|
-
|
|
54
|
+
async function invokingReleaseRoots(dataDir, packageRoot, environment) {
|
|
55
|
+
if (!packageRoot)
|
|
56
|
+
return [];
|
|
57
|
+
if (environment[PRODUCT_IDENTITY.environment.developmentInstanceId])
|
|
58
|
+
return [packageRoot];
|
|
56
59
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (id && state.releases[id])
|
|
60
|
-
roots.push(state.releases[id].releaseRoot);
|
|
61
|
-
}
|
|
62
|
-
roots.push(...Object.values(state.releases).map(record => record.releaseRoot));
|
|
60
|
+
// Invariant: immutable materialization selects exact payload bytes without activating or starting a host.
|
|
61
|
+
return [(await materializeRelease(packageRoot, dataDir)).releaseRoot];
|
|
63
62
|
}
|
|
64
63
|
catch {
|
|
65
|
-
|
|
64
|
+
return [];
|
|
66
65
|
}
|
|
67
|
-
if (packageRoot)
|
|
68
|
-
roots.push(packageRoot);
|
|
69
|
-
return [...new Set(roots)];
|
|
70
66
|
}
|
|
71
67
|
const NO_TABS = "No resident tabs are running.\n";
|
|
72
68
|
function describe(request, report) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentSettingsPort } from "../contracts/agent-engine/index.js";
|
|
2
|
+
import { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
3
|
+
export interface OwnedSettingsManagerFactoryOptions {
|
|
4
|
+
readonly profileId: string;
|
|
5
|
+
readonly hideTabSettings?: boolean;
|
|
6
|
+
readonly agent?: AgentSettingsPort | null;
|
|
7
|
+
readonly agentProvider?: () => AgentSettingsPort | null;
|
|
8
|
+
}
|
|
9
|
+
/** Builds the profile-global settings manager shared by the Agent and slim utility compositions. */
|
|
10
|
+
export declare function createOwnedSettingsManager(options: OwnedSettingsManagerFactoryOptions): OwnedSettingsManager;
|
|
11
|
+
/** Rereads the shared settings document whenever its stat changes; returns the stop. */
|
|
12
|
+
export declare function watchSharedSettings(settings: Pick<OwnedSettingsManager, "file" | "refresh">): () => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { unwatchFile, watchFile } from "node:fs";
|
|
2
|
+
import { resolveLaunchProfilePaths } from "../features/launch/profile-paths.js";
|
|
3
|
+
import { resolveProductPaths } from "../foundation/lifecycle/paths.js";
|
|
4
|
+
import { PRODUCT_IDENTITY } from "../product-identity.js";
|
|
5
|
+
import { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
6
|
+
/** Builds the profile-global settings manager shared by the Agent and slim utility compositions. */
|
|
7
|
+
export function createOwnedSettingsManager(options) {
|
|
8
|
+
const profilePaths = resolveLaunchProfilePaths();
|
|
9
|
+
const productPaths = resolveProductPaths();
|
|
10
|
+
return new OwnedSettingsManager({
|
|
11
|
+
profileDir: profilePaths.agentProfile,
|
|
12
|
+
profileId: options.profileId,
|
|
13
|
+
namespace: PRODUCT_IDENTITY.commandName,
|
|
14
|
+
legacyConfigDir: productPaths.configDir,
|
|
15
|
+
...(options.agent === undefined ? {} : { agent: options.agent }),
|
|
16
|
+
...(options.agentProvider === undefined ? {} : { agentProvider: options.agentProvider }),
|
|
17
|
+
...(options.hideTabSettings ? { hiddenOwnedSettingIds: ["tabsAutoName", "tabsPosition", "tabBarPlusMenu"] } : {}),
|
|
18
|
+
hiddenAgentSettingIds: ["fullscreenWheelScrollLines"],
|
|
19
|
+
agentSettingLabelOverrides: { fullscreenCopyOnSelect: "Copy on select" },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const SHARED_SETTINGS_POLL_MS = 250;
|
|
23
|
+
/** Rereads the shared settings document whenever its stat changes; returns the stop. */
|
|
24
|
+
export function watchSharedSettings(settings) {
|
|
25
|
+
const file = settings.file;
|
|
26
|
+
const listener = () => {
|
|
27
|
+
try {
|
|
28
|
+
settings.refresh();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Compatibility: an unreadable peer write resolves on the next stat change.
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
watchFile(file, { interval: SHARED_SETTINGS_POLL_MS, persistent: false }, listener);
|
|
35
|
+
return () => unwatchFile(file, listener);
|
|
36
|
+
}
|
|
@@ -4,7 +4,7 @@ import type { SessionSelection } from "../foundation/lifecycle/session-selection
|
|
|
4
4
|
import { type PiEngineHostOptions } from "../integrations/pi/engine/host.js";
|
|
5
5
|
import type { PiProjectTrustPreflightPrompt } from "../integrations/pi/engine/project-trust-preflight.js";
|
|
6
6
|
import type { PiSessionForkPrompt } from "../integrations/pi/engine/session-selection.js";
|
|
7
|
-
import { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
7
|
+
import type { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
8
8
|
import type { OwnedUiApplicationPort, PresentationTerminalPort } from "../contracts/presentation/index.js";
|
|
9
9
|
import type { ReleaseNoteCatalog } from "../features/owned-ui/release-notes.js";
|
|
10
10
|
import type { TabBridgeClient } from "../foundation/resident-tabs/bridge-client.js";
|
|
@@ -20,17 +20,11 @@ export interface OwnedUiCompositionOptions {
|
|
|
20
20
|
readonly sessionPath?: string;
|
|
21
21
|
readonly sessionSelection?: SessionSelection;
|
|
22
22
|
readonly sessionForkPrompt?: PiSessionForkPrompt;
|
|
23
|
-
/**
|
|
24
|
-
* A1 profile whose settings this session reads and writes. Omitted keeps the
|
|
25
|
-
* session settings-free, which is what the pinned comparison paths use.
|
|
26
|
-
*/
|
|
23
|
+
/** A1 settings profile; comparison paths omit it. */
|
|
27
24
|
readonly profileId?: string;
|
|
28
25
|
/** Hide resident-only tab controls for the explicit direct single-agent launch. */
|
|
29
26
|
readonly hideTabSettings?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Whether A1's product-specific surfaces are reachable. Comparison profiles
|
|
32
|
-
* use the same composition with those surfaces withheld.
|
|
33
|
-
*/
|
|
27
|
+
/** `off` withholds A1-specific surfaces from comparison profiles. */
|
|
34
28
|
readonly ownedSurfaces?: "on" | "off";
|
|
35
29
|
readonly projectTrustPrompt?: PiProjectTrustPreflightPrompt;
|
|
36
30
|
/** Explicit local diagnostic destination; ignored by comparison/settings-free compositions. */
|
|
@@ -39,18 +33,16 @@ export interface OwnedUiCompositionOptions {
|
|
|
39
33
|
readonly clipboardDiagnosticsPath?: string;
|
|
40
34
|
/** Deterministic release-note seams for composition tests. */
|
|
41
35
|
readonly packageVersion?: string;
|
|
36
|
+
readonly notificationVersion?: string | undefined;
|
|
42
37
|
readonly releaseNotes?: ReleaseNoteCatalog;
|
|
43
38
|
/** Startup release-availability seam; defaults to the throttled registry check. */
|
|
44
39
|
readonly checkForNewerRelease?: (options: StartupReleaseCheckOptions) => Promise<AvailableRelease | null>;
|
|
45
|
-
/** Exact native process inspection injected by the shipped entry; absent SDK seams do not publish claims. */
|
|
46
40
|
readonly inspectProcess?: (pid: number) => Promise<NativeProcessIdentity | null>;
|
|
47
|
-
/** Private credentials were consumed by the entry before composition or extensions loaded. */
|
|
48
41
|
readonly tab?: {
|
|
49
42
|
readonly bridge: TabBridgeClient;
|
|
50
43
|
readonly dataDir: string;
|
|
51
44
|
readonly incarnation: number;
|
|
52
45
|
};
|
|
53
|
-
/** One prompt-adjacent notice explaining why a resident launch fell back. */
|
|
54
46
|
readonly residentFallbackNotice?: string;
|
|
55
47
|
}
|
|
56
48
|
export interface OwnedUiComposition {
|
|
@@ -69,5 +61,4 @@ export declare function residentStartupPresentation(resident: boolean, fallbackN
|
|
|
69
61
|
};
|
|
70
62
|
export declare function composeOwnedUiApplication(options?: OwnedUiCompositionOptions): Promise<OwnedUiApplicationPort>;
|
|
71
63
|
export declare function composeOwnedUi(options?: OwnedUiCompositionOptions): Promise<OwnedUiComposition>;
|
|
72
|
-
|
|
73
|
-
export declare function watchSharedSettings(settings: Pick<OwnedSettingsManager, "file" | "refresh">): () => void;
|
|
64
|
+
export { watchSharedSettings } from "./owned-settings.js";
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { unwatchFile, watchFile } from "node:fs";
|
|
2
1
|
import { SuggestionDiagnosticCapture } from "../features/prompt-suggestions/diagnostics.js";
|
|
3
2
|
import { PRODUCT_IDENTITY } from "../product-identity.js";
|
|
4
3
|
import { PromptHistoryService } from "../features/prompt-history/service.js";
|
|
5
4
|
import { PromptImageSidecar } from "../features/prompt-history/image-sidecar.js";
|
|
6
5
|
import { resolvePromptHistoryPath } from "../features/prompt-history/paths.js";
|
|
7
|
-
import {
|
|
6
|
+
import { resolvePromptHistoryDataDir } from "../features/launch/profile-paths.js";
|
|
8
7
|
import { resolveProductPaths } from "../foundation/lifecycle/paths.js";
|
|
8
|
+
import { MAX_CLIPBOARD_TEXT_BYTES } from "../contracts/owned-ui/index.js";
|
|
9
9
|
import { applyConfiguredPiTheme, getAvailablePiThemes, setPiAccentColor, setPiPackageBorderProjectionEnabled } from "../integrations/pi/components/upstream/theme/theme.js";
|
|
10
10
|
import { createPiEngineHost } from "../integrations/pi/engine/host.js";
|
|
11
11
|
import { createPiSessionPresenters } from "../integrations/pi/session-presenters/index.js";
|
|
12
12
|
import { ClipboardDiagnosticCapture } from "../app/session-shell/clipboard-diagnostics.js";
|
|
13
13
|
import { createOwnedUiClipboardServices } from "../app/session-shell/clipboard-services.js";
|
|
14
|
+
import { writeSystemClipboardText } from "../app/session-shell/system-clipboard.js";
|
|
14
15
|
import { createPiKeybindingsHost } from "../integrations/pi/components/shell-shared-facade.js";
|
|
15
16
|
import { OwnedUiSessionPresenter, OwnedUiTerminalHost, sessionTerminalHostOptions, } from "../app/session-shell/session-shell.js";
|
|
16
|
-
import { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
17
17
|
import { createPiTerminalBridge } from "../integrations/pi/tui-runtime/presentation-adapter.js";
|
|
18
18
|
import { createOwnedRouteHost } from "./settings-route-host.js";
|
|
19
19
|
import { renderPiShellChangelogLines } from "../integrations/pi/components/shell-presenters-info.js";
|
|
20
20
|
import { nativeHyperlinkStyle } from "../ui/components/spans.js";
|
|
21
21
|
import { ResidentTabRecovery } from "../foundation/resident-tabs/recovery.js";
|
|
22
|
-
import { bindResidentTabBridge, correlateResidentTabSubmission, currentBackendSessionFile, residentTabKeybindings } from "./resident-tab-bridge.js";
|
|
22
|
+
import { bindResidentTabBridge, closeResidentUtilityRoute, correlateResidentTabSubmission, currentBackendSessionFile, residentRouteHostOptions, residentTabKeybindings } from "./resident-tab-bridge.js";
|
|
23
23
|
import { SessionRuntimeRegistry } from "./session-runtime-registry.js";
|
|
24
24
|
import { readPackageVersion, StartupReleaseNote } from "./startup-route.js";
|
|
25
|
+
import { createOwnedSettingsManager, watchSharedSettings } from "./owned-settings.js";
|
|
25
26
|
export { bindResidentTabBridge, correlateResidentTabSubmission } from "./resident-tab-bridge.js";
|
|
26
27
|
export function residentStartupPresentation(resident, fallbackNotice) {
|
|
27
28
|
if (resident)
|
|
@@ -39,20 +40,14 @@ export async function composeOwnedUi(options = {}) {
|
|
|
39
40
|
inspectProcess: options.inspectProcess,
|
|
40
41
|
});
|
|
41
42
|
const productPaths = resolveProductPaths();
|
|
42
|
-
const profilePaths = resolveLaunchProfilePaths();
|
|
43
43
|
// Rationale: the manager reads the engine's settings port lazily, after the session exists.
|
|
44
44
|
let backend;
|
|
45
45
|
const settings = options.profileId === undefined
|
|
46
46
|
? null
|
|
47
|
-
:
|
|
48
|
-
profileDir: profilePaths.agentProfile,
|
|
47
|
+
: createOwnedSettingsManager({
|
|
49
48
|
profileId: options.profileId,
|
|
50
|
-
namespace: PRODUCT_IDENTITY.commandName,
|
|
51
|
-
legacyConfigDir: productPaths.configDir,
|
|
52
49
|
agentProvider: () => backend.settingsPort(),
|
|
53
|
-
...(options.hideTabSettings ? {
|
|
54
|
-
hiddenAgentSettingIds: ["fullscreenWheelScrollLines"],
|
|
55
|
-
agentSettingLabelOverrides: { fullscreenCopyOnSelect: "Copy on select" },
|
|
50
|
+
...(options.hideTabSettings ? { hideTabSettings: true } : {}),
|
|
56
51
|
});
|
|
57
52
|
const tabBridge = options.tab?.bridge ?? null;
|
|
58
53
|
if (tabBridge !== null)
|
|
@@ -130,12 +125,15 @@ export async function composeOwnedUi(options = {}) {
|
|
|
130
125
|
host.setAccentColor(accent);
|
|
131
126
|
tabBridge?.accent(accent);
|
|
132
127
|
});
|
|
133
|
-
// Rationale:
|
|
134
|
-
// change saved in
|
|
135
|
-
const unwatchSharedSettings = settings === null || !ownedSurfaces
|
|
128
|
+
// Rationale: resident tabs and separate solo agents share a settings document; a live scope
|
|
129
|
+
// change saved in either reaches the others only by rereading that document.
|
|
130
|
+
const unwatchSharedSettings = settings === null || !ownedSurfaces
|
|
136
131
|
? () => { }
|
|
137
132
|
: watchSharedSettings(settings);
|
|
138
133
|
const unsubscribeAccent = () => { unwatchSharedSettings(); unsubscribeAccentChange(); };
|
|
134
|
+
// Invariant: the Settings search and agent prompt borrow the same resolved Pi keybindings.
|
|
135
|
+
const keybindingsHost = createPiKeybindingsHost({ profile: ownedSurfaces ? "a1" : "pi", agentDir: backend.identity.agentDir });
|
|
136
|
+
const keybindings = residentTabKeybindings(tabBridge, backend.identity.agentDir, keybindingsHost);
|
|
139
137
|
// Rationale: Routes open only after shell construction.
|
|
140
138
|
const references = {
|
|
141
139
|
changelog: async (input) => {
|
|
@@ -151,9 +149,8 @@ export async function composeOwnedUi(options = {}) {
|
|
|
151
149
|
},
|
|
152
150
|
session: () => import("./session-info-reference.js").then(m => m.loadSessionInfoReference(backend)),
|
|
153
151
|
};
|
|
154
|
-
const routeHost = settings === null || !ownedSurfaces ? null
|
|
155
|
-
|
|
156
|
-
});
|
|
152
|
+
const routeHost = settings === null || !ownedSurfaces ? null
|
|
153
|
+
: createOwnedRouteHost(settings, references, settingsRouteOptions(tabBridge, keybindings, backend.identity.agentDir, cwd));
|
|
157
154
|
const viewportSettings = settings === null || !ownedSurfaces ? null : {
|
|
158
155
|
snapshot: () => viewportSettingsSnapshot(settings),
|
|
159
156
|
onChange: listener => settings.onChange(() => listener(viewportSettingsSnapshot(settings))),
|
|
@@ -181,26 +178,11 @@ export async function composeOwnedUi(options = {}) {
|
|
|
181
178
|
// Rationale: one listener covers both the A1 presentation choice and the engine skill-command toggle.
|
|
182
179
|
onChange: (listener) => settings.onChange(() => listener()),
|
|
183
180
|
};
|
|
184
|
-
const
|
|
185
|
-
const historyProfileLocation = settings === null || !ownedSurfaces || !settings.value("promptHistoryEnabled")
|
|
186
|
-
? null
|
|
187
|
-
: resolvePromptHistoryPath(resolvePromptHistoryDataDir(), backend.identity.agentDir);
|
|
188
|
-
const promptHistory = historyProfileLocation === null ? null : {
|
|
189
|
-
limit: typeof historyLimit === "number" ? historyLimit : 100,
|
|
190
|
-
store: new PromptHistoryService({
|
|
191
|
-
dataDir: resolvePromptHistoryDataDir(),
|
|
192
|
-
profileRoot: backend.identity.agentDir,
|
|
193
|
-
limit: typeof historyLimit === "number" ? historyLimit : 100,
|
|
194
|
-
}),
|
|
195
|
-
imageSidecar: new PromptImageSidecar(historyProfileLocation.imagesDir),
|
|
196
|
-
};
|
|
181
|
+
const promptHistory = createPromptHistoryConfiguration(settings, backend, ownedSurfaces);
|
|
197
182
|
const clipboardDestination = options.clipboardDiagnosticsPath ?? process.env[PRODUCT_IDENTITY.environment.clipboardDiagnostics];
|
|
198
183
|
const clipboardDiagnostics = settings !== null && ownedSurfaces && clipboardDestination?.trim()
|
|
199
184
|
? new ClipboardDiagnosticCapture(clipboardDestination) : null;
|
|
200
|
-
// Invariant: process-wide services are created once here and borrowed by every session shell.
|
|
201
|
-
// keybinding registry is applied before any presenter is built, so none of them reads Pi's defaults.
|
|
202
|
-
const keybindingsHost = createPiKeybindingsHost({ profile: ownedSurfaces ? "a1" : "pi", agentDir: backend.identity.agentDir });
|
|
203
|
-
const keybindings = residentTabKeybindings(tabBridge, backend.identity.agentDir, keybindingsHost);
|
|
185
|
+
// Invariant: process-wide services are created once here and borrowed by every session shell.
|
|
204
186
|
const clipboardServices = ownedSurfaces ? createOwnedUiClipboardServices() : null;
|
|
205
187
|
let createdTerminalHost;
|
|
206
188
|
let terminalHost;
|
|
@@ -216,12 +198,8 @@ export async function composeOwnedUi(options = {}) {
|
|
|
216
198
|
...(ownedSurfaces ? { sessionLayout: "custom-viewport" } : {}),
|
|
217
199
|
...(tabBridge === null || tabRecovery === null ? {} : { residentTab: {
|
|
218
200
|
requestDetach: () => tabBridge.requestDetach(),
|
|
219
|
-
closeOwnedRoute: route =>
|
|
220
|
-
if (tabBridge.tabId.startsWith("settings-") && route === "settings")
|
|
221
|
-
void tabBridge.requestClose();
|
|
222
|
-
},
|
|
201
|
+
closeOwnedRoute: route => closeResidentUtilityRoute(tabBridge, route),
|
|
223
202
|
reportStatus: status => tabBridge.status(status),
|
|
224
|
-
stallNoticeSeconds: 300,
|
|
225
203
|
admit: input => tabRecovery.admit({ ...input, incarnation: options.tab.incarnation }),
|
|
226
204
|
dispatched: () => tabRecovery.dispatched(),
|
|
227
205
|
correlated: (submissionId, entryId) => correlateResidentTabSubmission(tabRecovery, backend, submissionId, entryId),
|
|
@@ -291,18 +269,13 @@ export async function composeOwnedUi(options = {}) {
|
|
|
291
269
|
// Rationale: pinned Pi's version check is unreachable because the owned shell never runs its
|
|
292
270
|
// InteractiveMode, so A1 checks its own channel. The result is never awaited on the startup path;
|
|
293
271
|
// settings are read here because the runner resolves them only just before start.
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
...(settings === null || !ownedSurfaces ? {} : { settingEnabled: settings.value("updateCheck") }),
|
|
302
|
-
});
|
|
303
|
-
if (release !== null)
|
|
304
|
-
backend.extensions.announceReleaseUpdate(release);
|
|
305
|
-
};
|
|
272
|
+
const releaseUpdates = createStartupReleaseUpdateObservation({
|
|
273
|
+
backend,
|
|
274
|
+
configDir: productPaths.configDir,
|
|
275
|
+
mode: settings === null || !ownedSurfaces ? "auto" : settings.value("updateCheck"),
|
|
276
|
+
packageVersion: releaseNote.packageVersion, notificationVersion: options.notificationVersion,
|
|
277
|
+
checkForNewerRelease: options.checkForNewerRelease,
|
|
278
|
+
});
|
|
306
279
|
const exitNoticePath = process.env[PRODUCT_IDENTITY.environment.exitNoticePath];
|
|
307
280
|
// Security: tools the agent runs must never see, or rewrite, this instance's notice.
|
|
308
281
|
delete process.env[PRODUCT_IDENTITY.environment.exitNoticePath];
|
|
@@ -315,11 +288,12 @@ export async function composeOwnedUi(options = {}) {
|
|
|
315
288
|
exitNotice = import("../app/session-shell/exit-notice.js")
|
|
316
289
|
.then(module => module.armExitNotice(backend, exitNoticePath), () => null);
|
|
317
290
|
presenter.start();
|
|
318
|
-
|
|
291
|
+
releaseUpdates.start();
|
|
319
292
|
},
|
|
320
293
|
flush: () => backend.session.flushEvents(),
|
|
321
294
|
waitUntilStopped: () => presenter.waitUntilStopped(),
|
|
322
295
|
dispose: async () => {
|
|
296
|
+
releaseUpdates.stop();
|
|
323
297
|
try {
|
|
324
298
|
await terminalHost.dispose();
|
|
325
299
|
// Invariant: cleared only once the terminal is restored; a failed dispose leaves it armed.
|
|
@@ -344,20 +318,75 @@ export async function composeOwnedUi(options = {}) {
|
|
|
344
318
|
};
|
|
345
319
|
return { application, settings };
|
|
346
320
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
321
|
+
function createPromptHistoryConfiguration(settings, backend, ownedSurfaces) {
|
|
322
|
+
const limit = settings?.value("promptHistoryMaxItems") ?? 100;
|
|
323
|
+
const scope = settings?.value("promptHistoryScope") ?? "unique";
|
|
324
|
+
const enabled = settings !== null && ownedSurfaces && settings.value("promptHistoryEnabled");
|
|
325
|
+
const sessionId = enabled ? backend.identity.currentHistorySessionId() : null;
|
|
326
|
+
if (settings === null || !enabled || (scope === "unique" && sessionId === null))
|
|
327
|
+
return null;
|
|
328
|
+
const dataDir = resolvePromptHistoryDataDir();
|
|
329
|
+
const forScope = (selected, id) => {
|
|
330
|
+
if (selected === "unique" && id === null)
|
|
331
|
+
return null;
|
|
332
|
+
const location = resolvePromptHistoryPath(dataDir, backend.identity.agentDir, process.platform, selected, id ?? "");
|
|
333
|
+
return {
|
|
334
|
+
store: new PromptHistoryService({ dataDir, profileRoot: backend.identity.agentDir, limit, scope: selected, sessionId: id ?? "" }),
|
|
335
|
+
imageSidecar: new PromptImageSidecar(location.imagesDir),
|
|
336
|
+
};
|
|
337
|
+
};
|
|
338
|
+
return {
|
|
339
|
+
limit, scope,
|
|
340
|
+
...(sessionId === null ? {} : { sessionId }),
|
|
341
|
+
forScope,
|
|
342
|
+
onScopeChange: (listener) => settings.onChange(() => listener(settings.value("promptHistoryScope"))),
|
|
343
|
+
...forScope(scope, sessionId),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function createStartupReleaseUpdateObservation(input) {
|
|
347
|
+
const controller = new AbortController();
|
|
348
|
+
const observe = async () => {
|
|
349
|
+
const check = input.checkForNewerRelease
|
|
350
|
+
?? (await import("../foundation/release/latest-release.js")).checkForNewerRelease;
|
|
351
|
+
const runningVersion = input.packageVersion ?? await readPackageVersion();
|
|
352
|
+
const discoveryVersion = input.mode === "notify" ? input.notificationVersion ?? runningVersion : runningVersion;
|
|
353
|
+
const release = await check({
|
|
354
|
+
runningVersion: discoveryVersion,
|
|
355
|
+
configDir: input.configDir,
|
|
356
|
+
interactive: process.stdout.isTTY === true,
|
|
357
|
+
settingMode: input.mode,
|
|
358
|
+
});
|
|
359
|
+
if (release === null || controller.signal.aborted)
|
|
360
|
+
return;
|
|
361
|
+
if (input.mode === "notify") {
|
|
362
|
+
input.backend.extensions.announceReleaseUpdate(release);
|
|
363
|
+
return;
|
|
354
364
|
}
|
|
355
|
-
|
|
356
|
-
|
|
365
|
+
if (input.mode === "auto") {
|
|
366
|
+
const installed = await import("../foundation/release/automatic-update.js")
|
|
367
|
+
.then(module => module.requestAutomaticUpdate({ release, runningVersion, signal: controller.signal }));
|
|
368
|
+
if (installed !== null && !controller.signal.aborted) {
|
|
369
|
+
input.backend.extensions.announceReleaseUpdate({ status: "installed", version: installed });
|
|
370
|
+
}
|
|
357
371
|
}
|
|
358
372
|
};
|
|
359
|
-
|
|
360
|
-
|
|
373
|
+
return {
|
|
374
|
+
start() { void observe().catch(() => undefined); },
|
|
375
|
+
stop() { controller.abort(); },
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
export { watchSharedSettings } from "./owned-settings.js";
|
|
379
|
+
function settingsRouteOptions(tabBridge, keybindings, agentDir, cwd) {
|
|
380
|
+
return {
|
|
381
|
+
...residentRouteHostOptions(tabBridge),
|
|
382
|
+
keybindings,
|
|
383
|
+
agentDir,
|
|
384
|
+
cwd,
|
|
385
|
+
copyText: text => {
|
|
386
|
+
if (Buffer.byteLength(text) <= MAX_CLIPBOARD_TEXT_BYTES)
|
|
387
|
+
void writeSystemClipboardText(text).catch(() => { });
|
|
388
|
+
},
|
|
389
|
+
};
|
|
361
390
|
}
|
|
362
391
|
function quitOutroSettingsSnapshot(settings) {
|
|
363
392
|
return {
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import type { TabBridgeClient } from "../foundation/resident-tabs/bridge-client.js";
|
|
2
|
+
import { type ResidentTabRoute } from "../foundation/resident-tabs/routes.js";
|
|
2
3
|
import type { ResidentTabRecovery } from "../foundation/resident-tabs/recovery.js";
|
|
3
4
|
import type { PiKeybindingsHost } from "../integrations/pi/components/shell-shared-facade.js";
|
|
4
5
|
import type { ComposedSessionBackend } from "./session-backend.js";
|
|
6
|
+
import type { OwnedRouteHostOptions } from "./settings-route-host.js";
|
|
5
7
|
/**
|
|
6
8
|
* The keybindings the shells borrow. Inside a resident tab the host dispatches and labels the tab
|
|
7
9
|
* shortcuts this child projects from its effective declarations, so the projection is published now
|
|
8
10
|
* and again after every customization reload.
|
|
9
11
|
*/
|
|
10
12
|
export declare function residentTabKeybindings(tabBridge: TabBridgeClient | null, agentDir: string, keybindingsHost: PiKeybindingsHost): PiKeybindingsHost;
|
|
13
|
+
/**
|
|
14
|
+
* How owned screens are presented in a resident tab: a Settings or Changelog tab's screen is the tab, and the Plus menu entry opens
|
|
15
|
+
* the tab bar's own Plus Menu dialog while this tab's controlling client can show it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function residentRouteHostOptions(tabBridge: TabBridgeClient | null): OwnedRouteHostOptions;
|
|
18
|
+
/** Closes a utility tab when its own screen closes. Invariant: a utility tab's screen is the tab. */
|
|
19
|
+
export declare function closeResidentUtilityRoute(tabBridge: Pick<TabBridgeClient, "tabId" | "requestClose">, route: string): void;
|
|
11
20
|
export declare function bindResidentTabBridge(tabBridge: TabBridgeClient, backend: Pick<ComposedSessionBackend, "identity" | "session" | "workflows" | "generateResidentTabName">, recovery?: Pick<ResidentTabRecovery, "settled"> | null, options?: {
|
|
12
21
|
readonly autoName?: boolean;
|
|
13
|
-
readonly openRoute?: (route:
|
|
22
|
+
readonly openRoute?: (route: ResidentTabRoute) => void;
|
|
14
23
|
}): Promise<() => void>;
|
|
15
24
|
export declare function correlateResidentTabSubmission(recovery: Pick<ResidentTabRecovery, "correlated" | "settled">, backend: Pick<ComposedSessionBackend, "identity" | "session">, submissionId: string, entryId: string): Promise<void>;
|
|
16
25
|
export declare function currentBackendSessionFile(backend: Pick<ComposedSessionBackend, "identity">): string | null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { residentUtilityRoute } from "../foundation/resident-tabs/routes.js";
|
|
1
2
|
import { deriveResidentTabStatus } from "../features/resident-tab/status-deriver.js";
|
|
2
3
|
import { projectResidentTabShortcuts } from "../features/resident-tab/shortcut-projection.js";
|
|
3
4
|
import { readShortcutBindingsFile } from "../features/resident-tab/shortcut-bindings-file.js";
|
|
@@ -16,6 +17,26 @@ export function residentTabKeybindings(tabBridge, agentDir, keybindingsHost) {
|
|
|
16
17
|
publish();
|
|
17
18
|
return { ...keybindingsHost, reload: () => { keybindingsHost.reload(); publish(); } };
|
|
18
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* How owned screens are presented in a resident tab: a Settings or Changelog tab's screen is the tab, and the Plus menu entry opens
|
|
22
|
+
* the tab bar's own Plus Menu dialog while this tab's controlling client can show it.
|
|
23
|
+
*/
|
|
24
|
+
export function residentRouteHostOptions(tabBridge) {
|
|
25
|
+
return {
|
|
26
|
+
...(tabBridge === null ? {} : { tabRoute: residentUtilityRoute(tabBridge.tabId) }),
|
|
27
|
+
configurePlusMenu: () => {
|
|
28
|
+
if (tabBridge === null || !tabBridge.available || tabBridge.captureAttachment() === null)
|
|
29
|
+
return false;
|
|
30
|
+
void tabBridge.requestAction("configure-plus");
|
|
31
|
+
return true;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Closes a utility tab when its own screen closes. Invariant: a utility tab's screen is the tab. */
|
|
36
|
+
export function closeResidentUtilityRoute(tabBridge, route) {
|
|
37
|
+
if (residentUtilityRoute(tabBridge.tabId) === route)
|
|
38
|
+
void tabBridge.requestClose();
|
|
39
|
+
}
|
|
19
40
|
export async function bindResidentTabBridge(tabBridge, backend, recovery = null, options = {}) {
|
|
20
41
|
await tabBridge.start().catch(() => false);
|
|
21
42
|
tabBridge.status("starting");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReferenceDocumentProvider } from "../features/owned-ui/reference-screen-app.js";
|
|
2
2
|
import type { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
3
3
|
import type { UiRouteHost, UiRouteInput } from "../ui/apps/contracts.js";
|
|
4
|
+
import type { PiKeybindingsHost } from "../integrations/pi/components/shell-shared-facade.js";
|
|
4
5
|
/** Styled document rows for one content width. */
|
|
5
6
|
export type OwnedReferenceRows = NonNullable<ReferenceDocumentProvider["rows"]>;
|
|
6
7
|
/** Documents supplied by composition so the route host never reads shell data itself. */
|
|
@@ -11,8 +12,17 @@ export interface OwnedReferenceProviders {
|
|
|
11
12
|
}
|
|
12
13
|
/** How the settings route is presented. */
|
|
13
14
|
export interface OwnedRouteHostOptions {
|
|
14
|
-
/**
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The route whose screen is this resident utility tab. That screen is the tab, so it offers no
|
|
17
|
+
* screen-level close: neither Esc nor one Ctrl+C dismisses it, and it shows no close hint.
|
|
18
|
+
*/
|
|
19
|
+
readonly tabRoute?: string | null;
|
|
20
|
+
/** Opens the tab bar's own Plus Menu dialog; false when no tab bar can show it. */
|
|
21
|
+
readonly configurePlusMenu?: () => boolean;
|
|
22
|
+
readonly keybindings?: PiKeybindingsHost;
|
|
23
|
+
readonly agentDir?: string;
|
|
24
|
+
readonly cwd?: string;
|
|
25
|
+
readonly copyText?: (text: string) => void;
|
|
16
26
|
}
|
|
17
27
|
/** Declares settings and lazy document routes. */
|
|
18
28
|
export declare function createOwnedRouteHost(settings: OwnedSettingsManager, references?: OwnedReferenceProviders, options?: OwnedRouteHostOptions): UiRouteHost;
|
|
@@ -11,13 +11,14 @@ export function createOwnedRouteHost(settings, references, options = {}) {
|
|
|
11
11
|
if (!routes.has(route))
|
|
12
12
|
return null;
|
|
13
13
|
if (route === SETTINGS_ROUTE) {
|
|
14
|
-
return deferredSurface(SETTINGS_APP_ID, "settings", () => loadSettingsSurface(settings, options
|
|
14
|
+
return deferredSurface(SETTINGS_APP_ID, "settings", () => loadSettingsSurface(settings, options));
|
|
15
15
|
}
|
|
16
16
|
const changelog = route === CHANGELOG_ROUTE;
|
|
17
17
|
const session = route === "session";
|
|
18
18
|
const title = session ? "Session Info" : changelog ? input ? "What's New" : "Changelog" : HOTKEYS_TITLE;
|
|
19
19
|
const document = session ? references.session() : changelog ? references.changelog(input) : references.hotkeys();
|
|
20
|
-
|
|
20
|
+
const pinned = options.tabRoute === route;
|
|
21
|
+
return deferredSurface(route, title, () => loadReferenceSurface(settings, route, title, document, pinned));
|
|
21
22
|
},
|
|
22
23
|
};
|
|
23
24
|
}
|
|
@@ -61,12 +62,27 @@ function deferredSurface(id, label, load) {
|
|
|
61
62
|
onExitRequested: listener => { onExit = listener; },
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
|
-
async function loadSettingsSurface(settings,
|
|
65
|
-
const { SettingsApp } = await
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
async function loadSettingsSurface(settings, options) {
|
|
66
|
+
const [{ SettingsApp }, { createSettingsSearchInputFactory }] = await Promise.all([
|
|
67
|
+
import("../features/owned-ui/settings-app.js"),
|
|
68
|
+
import("./settings-search-input.js"),
|
|
69
|
+
]);
|
|
70
|
+
const pinned = options.tabRoute === SETTINGS_ROUTE;
|
|
71
|
+
const searchInputFactory = createSettingsSearchInputFactory({
|
|
72
|
+
...(options.keybindings === undefined ? {} : { keybindings: options.keybindings }),
|
|
73
|
+
...(options.agentDir === undefined ? {} : { agentDir: options.agentDir }),
|
|
74
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
75
|
+
});
|
|
76
|
+
// Invariant: a pinned Settings tab closes as a tab; neither Esc nor one unclaimed Ctrl+C dismisses its screen.
|
|
77
|
+
return hostApp(SETTINGS_APP_ID, SETTINGS_ROUTE, () => new SettingsApp(settings, {
|
|
78
|
+
pinned,
|
|
79
|
+
searchInputFactory,
|
|
80
|
+
configurePlusMenu: options.configurePlusMenu ?? null,
|
|
81
|
+
...(options.copyText === undefined ? {} : { copyText: options.copyText }),
|
|
82
|
+
}), !pinned, true);
|
|
68
83
|
}
|
|
69
|
-
async function loadReferenceSurface(settings,
|
|
84
|
+
async function loadReferenceSurface(settings, route, title, document, pinned) {
|
|
85
|
+
const id = route;
|
|
70
86
|
// Rationale: the app module and the document load together, so a document that fails
|
|
71
87
|
// to render is reported by the placeholder rather than by a screen that never fills.
|
|
72
88
|
const [{ ReferenceScreenApp }, content] = await Promise.all([import("../features/owned-ui/reference-screen-app.js"), document]);
|
|
@@ -79,9 +95,10 @@ async function loadReferenceSurface(settings, id, route, title, document) {
|
|
|
79
95
|
scrollbarStyle: settings.value("scrollbarStyle"),
|
|
80
96
|
scrollbarSpeed: settings.value("scrollbarSpeed"),
|
|
81
97
|
}),
|
|
82
|
-
|
|
98
|
+
pinned,
|
|
99
|
+
}), !pinned);
|
|
83
100
|
}
|
|
84
|
-
async function hostApp(id, route, create, closeOnInterrupt = true) {
|
|
101
|
+
async function hostApp(id, route, create, closeOnInterrupt = true, appInputBeforeInterrupt = false) {
|
|
85
102
|
const [{ UiAppHost }, { UiAppRegistry }] = await Promise.all([
|
|
86
103
|
import("../ui/apps/host.js"),
|
|
87
104
|
import("../ui/apps/registry.js"),
|
|
@@ -96,6 +113,7 @@ async function hostApp(id, route, create, closeOnInterrupt = true) {
|
|
|
96
113
|
const host = new UiAppHost({
|
|
97
114
|
registry,
|
|
98
115
|
closeOnInterrupt,
|
|
116
|
+
appInputBeforeInterrupt,
|
|
99
117
|
theme: pinnedTheme(),
|
|
100
118
|
surface: {
|
|
101
119
|
size: () => size,
|