@timurproko/a1 0.2.7-dev.808 → 0.2.7-dev.810
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 +4 -2
- package/dist/app/session-shell/dialog-coordinator.d.ts +42 -0
- package/dist/app/session-shell/dialog-coordinator.js +133 -0
- package/dist/app/session-shell/dock-chrome.d.ts +34 -0
- package/dist/app/session-shell/dock-chrome.js +109 -0
- package/dist/app/session-shell/exit-notice.d.ts +1 -1
- package/dist/app/session-shell/exit-notice.js +1 -1
- package/dist/app/session-shell/extension-surface.d.ts +30 -0
- package/dist/app/session-shell/extension-surface.js +105 -0
- package/dist/app/session-shell/input-surface-switch.d.ts +36 -0
- package/dist/app/session-shell/input-surface-switch.js +85 -0
- package/dist/app/session-shell/login-interaction.d.ts +27 -0
- package/dist/app/session-shell/login-interaction.js +91 -0
- package/dist/app/session-shell/prompt-submission.d.ts +48 -0
- package/dist/app/session-shell/prompt-submission.js +276 -0
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +41 -0
- package/dist/app/session-shell/resident-tab-coordinator.js +120 -0
- package/dist/app/session-shell/response-copy-transport.d.ts +13 -0
- package/dist/app/session-shell/response-copy-transport.js +16 -7
- package/dist/app/session-shell/session-concern.d.ts +22 -0
- package/dist/app/session-shell/session-concern.js +29 -0
- package/dist/app/session-shell/session-lifecycle.d.ts +29 -0
- package/dist/app/session-shell/session-lifecycle.js +49 -0
- package/dist/app/session-shell/session-shell-root.d.ts +1 -1
- package/dist/app/session-shell/session-shell-root.js +182 -931
- package/dist/app/session-shell/session-shell.d.ts +2 -8
- package/dist/app/session-shell/session-shell.js +202 -901
- package/dist/app/session-shell/settings-bindings.d.ts +17 -0
- package/dist/app/session-shell/settings-bindings.js +91 -0
- package/dist/app/session-shell/shell-editor-factory.d.ts +27 -0
- package/dist/app/session-shell/shell-editor-factory.js +102 -0
- package/dist/app/session-shell/shell-host.d.ts +6 -0
- package/dist/app/session-shell/shell-host.js +8 -0
- package/dist/app/session-shell/shell-layout.d.ts +64 -0
- package/dist/app/session-shell/shell-layout.js +18 -0
- package/dist/app/session-shell/suggestion-surface.d.ts +25 -0
- package/dist/app/session-shell/suggestion-surface.js +63 -0
- package/dist/app/session-shell/transcript-document.d.ts +78 -0
- package/dist/app/session-shell/transcript-document.js +365 -0
- package/dist/app/session-shell/viewport-layout.d.ts +31 -0
- package/dist/app/session-shell/viewport-layout.js +160 -0
- package/dist/app/session-shell/workflow-output.d.ts +33 -0
- package/dist/app/session-shell/workflow-output.js +174 -0
- package/dist/app/session-shell/workflow-router.d.ts +25 -0
- package/dist/app/session-shell/workflow-router.js +39 -0
- package/dist/composition/owned-ui.d.ts +4 -25
- package/dist/composition/owned-ui.js +26 -270
- package/dist/composition/resident-tab-bridge.d.ts +16 -0
- package/dist/composition/resident-tab-bridge.js +159 -0
- package/dist/composition/session-backend.d.ts +19 -0
- package/dist/composition/session-backend.js +1 -0
- package/dist/composition/session-runtime-registry.d.ts +20 -0
- package/dist/composition/session-runtime-registry.js +85 -0
- package/dist/composition/startup-route.d.ts +32 -0
- package/dist/composition/startup-route.js +72 -0
- package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
- package/dist/foundation/release/progress-palette.generated.js +2 -2
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
- package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
- package/dist/integrations/pi/components/shell-extension-ui.js +79 -60
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +1 -1
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +2 -4
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +77 -98
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.js +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.js +1 -1
- package/dist/integrations/pi/engine/adapter.d.ts +4 -77
- package/dist/integrations/pi/engine/adapter.js +85 -393
- package/dist/integrations/pi/engine/command-dispatch.d.ts +13 -0
- package/dist/integrations/pi/engine/command-dispatch.js +57 -0
- package/dist/integrations/pi/engine/host.js +2 -2
- package/dist/integrations/pi/engine/ports.d.ts +56 -0
- package/dist/integrations/pi/engine/ports.js +89 -0
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/view-state.d.ts +42 -0
- package/dist/integrations/pi/engine/view-state.js +126 -0
- package/dist/integrations/pi/startup-public.js +164 -150
- package/dist/integrations/pi/startup-public.manifest.json +351 -310
- package/dist/integrations/pi/tui-runtime/adapter.js +4 -0
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +5 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
- 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/runtime-payload-inventory.json +5181 -5369
- package/dist/ui/components/transcript-viewport.js +10 -19
- package/docs/architecture/boundaries.md +14 -0
- package/docs/architecture/resident-tabs.md +8 -8
- package/docs/architecture/response-copy-delivery.md +1 -1
- package/docs/architecture/toolchain.md +2 -2
- package/package.json +5 -5
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { unwatchFile, watchFile } from "node:fs";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
2
|
import { SuggestionDiagnosticCapture } from "../features/prompt-suggestions/diagnostics.js";
|
|
4
3
|
import { PRODUCT_IDENTITY } from "../product-identity.js";
|
|
5
4
|
import { PromptHistoryService } from "../features/prompt-history/service.js";
|
|
@@ -13,8 +12,6 @@ import { createPiSessionPresenters } from "../integrations/pi/session-presenters
|
|
|
13
12
|
import { ClipboardDiagnosticCapture } from "../app/session-shell/clipboard-diagnostics.js";
|
|
14
13
|
import { createOwnedUiClipboardServices } from "../app/session-shell/clipboard-services.js";
|
|
15
14
|
import { createPiKeybindingsHost } from "../integrations/pi/components/shell-shared-facade.js";
|
|
16
|
-
import { projectResidentTabShortcuts } from "../features/resident-tab/shortcut-projection.js";
|
|
17
|
-
import { readShortcutBindingsFile } from "../features/resident-tab/shortcut-bindings-file.js";
|
|
18
15
|
import { OwnedUiSessionPresenter, OwnedUiTerminalHost, sessionTerminalHostOptions, } from "../app/session-shell/session-shell.js";
|
|
19
16
|
import { OwnedSettingsManager } from "../ui/settings/manager.js";
|
|
20
17
|
import { createPiTerminalBridge } from "../integrations/pi/tui-runtime/presentation-adapter.js";
|
|
@@ -22,8 +19,10 @@ import { createOwnedRouteHost } from "./settings-route-host.js";
|
|
|
22
19
|
import { renderPiShellChangelogLines } from "../integrations/pi/components/shell-presenters-info.js";
|
|
23
20
|
import { nativeHyperlinkStyle } from "../ui/components/spans.js";
|
|
24
21
|
import { ResidentTabRecovery } from "../foundation/resident-tabs/recovery.js";
|
|
25
|
-
import {
|
|
26
|
-
|
|
22
|
+
import { bindResidentTabBridge, correlateResidentTabSubmission, currentBackendSessionFile, residentTabKeybindings } from "./resident-tab-bridge.js";
|
|
23
|
+
import { SessionRuntimeRegistry } from "./session-runtime-registry.js";
|
|
24
|
+
import { readPackageVersion, StartupReleaseNote } from "./startup-route.js";
|
|
25
|
+
export { bindResidentTabBridge, correlateResidentTabSubmission } from "./resident-tab-bridge.js";
|
|
27
26
|
export function residentStartupPresentation(resident, fallbackNotice) {
|
|
28
27
|
if (resident)
|
|
29
28
|
return { startup: { quiet: true } };
|
|
@@ -35,62 +34,10 @@ export async function composeOwnedUiApplication(options = {}) {
|
|
|
35
34
|
export async function composeOwnedUi(options = {}) {
|
|
36
35
|
const cwd = options.cwd ?? process.cwd();
|
|
37
36
|
const ownedSurfaces = options.ownedSurfaces !== "off";
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const activeSessionIdentities = new Map();
|
|
43
|
-
let sessionRuntimeRefreshTimer = null;
|
|
44
|
-
let sessionRuntimeRefresh = Promise.resolve();
|
|
45
|
-
let sessionRuntimeDisposed = false;
|
|
46
|
-
const runtimeIdentity = async () => {
|
|
47
|
-
if (sessionRuntimeId === undefined || inspectSessionProcess === undefined)
|
|
48
|
-
return null;
|
|
49
|
-
sessionRuntimeIdentity ??= inspectSessionProcess(process.pid).catch(() => null);
|
|
50
|
-
return await sessionRuntimeIdentity;
|
|
51
|
-
};
|
|
52
|
-
const queueSessionRuntimeRefresh = () => {
|
|
53
|
-
if (sessionRuntimeDisposed || activeSessionIdentities.size === 0 || sessionRuntimeId === undefined || inspectSessionProcess === undefined)
|
|
54
|
-
return;
|
|
55
|
-
sessionRuntimeRefresh = sessionRuntimeRefresh.then(async () => {
|
|
56
|
-
if (sessionRuntimeDisposed)
|
|
57
|
-
return;
|
|
58
|
-
const owner = await runtimeIdentity();
|
|
59
|
-
if (owner === null)
|
|
60
|
-
return;
|
|
61
|
-
const { registerSessionRepositoryRuntime } = await import("../foundation/lifecycle/session-repository-context.js");
|
|
62
|
-
for (const [agentId, identity] of activeSessionIdentities) {
|
|
63
|
-
if (sessionRuntimeDisposed)
|
|
64
|
-
return;
|
|
65
|
-
await registerSessionRepositoryRuntime(identity, sessionRuntimeId, owner, {
|
|
66
|
-
inspectProcess: inspectSessionProcess,
|
|
67
|
-
...(agentId === undefined ? {} : { agentId }),
|
|
68
|
-
}).catch(() => undefined);
|
|
69
|
-
}
|
|
70
|
-
}).catch(() => undefined);
|
|
71
|
-
};
|
|
72
|
-
/** The repository context reader for one agent; undefined names the primary agent, the only one composed today. */
|
|
73
|
-
const repositoryContextReaderFor = (agentId) => async (sessionId, sessionFile, signal) => {
|
|
74
|
-
const owner = await runtimeIdentity();
|
|
75
|
-
if (owner === null || sessionRuntimeId === undefined || inspectSessionProcess === undefined)
|
|
76
|
-
return null;
|
|
77
|
-
const identity = { sessionId, sessionFile };
|
|
78
|
-
activeSessionIdentities.set(agentId, identity);
|
|
79
|
-
const { activateSessionRepositoryContext } = await import("../foundation/lifecycle/session-repository-context.js");
|
|
80
|
-
const context = await activateSessionRepositoryContext(identity, sessionRuntimeId, owner, {
|
|
81
|
-
signal,
|
|
82
|
-
inspectProcess: inspectSessionProcess,
|
|
83
|
-
...(agentId === undefined ? {} : { agentId }),
|
|
84
|
-
});
|
|
85
|
-
armSessionRuntimeRefresh();
|
|
86
|
-
return context;
|
|
87
|
-
};
|
|
88
|
-
const armSessionRuntimeRefresh = () => {
|
|
89
|
-
if (sessionRuntimeRefreshTimer !== null)
|
|
90
|
-
return;
|
|
91
|
-
sessionRuntimeRefreshTimer = setInterval(queueSessionRuntimeRefresh, SESSION_RUNTIME_REFRESH_MS);
|
|
92
|
-
sessionRuntimeRefreshTimer.unref();
|
|
93
|
-
};
|
|
37
|
+
const sessionRuntimes = new SessionRuntimeRegistry({
|
|
38
|
+
runtimeId: ownedSurfaces ? process.env[PRODUCT_IDENTITY.environment.sessionRuntimeId] : undefined,
|
|
39
|
+
inspectProcess: options.inspectProcess,
|
|
40
|
+
});
|
|
94
41
|
const productPaths = resolveProductPaths();
|
|
95
42
|
const profilePaths = resolveLaunchProfilePaths();
|
|
96
43
|
// Rationale: the manager reads the engine's settings port lazily, after the session exists.
|
|
@@ -145,7 +92,7 @@ export async function composeOwnedUi(options = {}) {
|
|
|
145
92
|
try {
|
|
146
93
|
backend = await host.create({
|
|
147
94
|
cwd,
|
|
148
|
-
...(ownedSurfaces ? { repositoryContextReader:
|
|
95
|
+
...(ownedSurfaces ? { repositoryContextReader: sessionRuntimes.readerFor(undefined) } : {}),
|
|
149
96
|
...(options.sessionPath === undefined ? {} : { sessionPath: options.sessionPath }),
|
|
150
97
|
...(options.sessionSelection === undefined ? {} : { sessionSelection: options.sessionSelection }),
|
|
151
98
|
...(sessionForkPrompt === undefined ? {} : { sessionForkPrompt }),
|
|
@@ -169,26 +116,13 @@ export async function composeOwnedUi(options = {}) {
|
|
|
169
116
|
tabBridge.accent(settings.value("accentColor"));
|
|
170
117
|
let removeControllerBoundaryInput = () => { };
|
|
171
118
|
let unsubscribeTabBridge = () => { };
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
catch (error) {
|
|
180
|
-
releaseNotesFailure = error;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
const packageVersion = ownedSurfaces
|
|
184
|
-
? options.packageVersion ?? await readPackageVersion()
|
|
185
|
-
: null;
|
|
186
|
-
const currentReleaseNote = packageVersion === null ? null : releaseNotes?.current(packageVersion) ?? null;
|
|
187
|
-
const releaseNoteClaim = currentReleaseNote === null || options.profileId === undefined
|
|
188
|
-
? null
|
|
189
|
-
: await import("../features/owned-ui/release-note-state.js").then(module => module.claimReleaseNote({
|
|
190
|
-
configDir: productPaths.configDir, profileId: options.profileId, version: currentReleaseNote.version,
|
|
191
|
-
}));
|
|
119
|
+
const releaseNote = await StartupReleaseNote.load({
|
|
120
|
+
ownedSurfaces,
|
|
121
|
+
configDir: productPaths.configDir,
|
|
122
|
+
...(options.releaseNotes === undefined ? {} : { releaseNotes: options.releaseNotes }),
|
|
123
|
+
...(options.packageVersion === undefined ? {} : { packageVersion: options.packageVersion }),
|
|
124
|
+
...(options.profileId === undefined ? {} : { profileId: options.profileId }),
|
|
125
|
+
});
|
|
192
126
|
const unsubscribeAccentChange = settings === null || !ownedSurfaces
|
|
193
127
|
? () => { }
|
|
194
128
|
: settings.onChange(current => {
|
|
@@ -205,9 +139,9 @@ export async function composeOwnedUi(options = {}) {
|
|
|
205
139
|
// Rationale: Routes open only after shell construction.
|
|
206
140
|
const references = {
|
|
207
141
|
changelog: async (input) => {
|
|
208
|
-
if (input?.document === undefined &&
|
|
209
|
-
throw
|
|
210
|
-
const markdown = input?.document ??
|
|
142
|
+
if (input?.document === undefined && releaseNote.failure !== undefined)
|
|
143
|
+
throw releaseNote.failure;
|
|
144
|
+
const markdown = input?.document ?? releaseNote.catalog?.completeMarkdown ?? "No A1 release notes found.";
|
|
211
145
|
return { rows: width => renderPiShellChangelogLines(markdown, width).map(row => nativeHyperlinkStyle(row)) };
|
|
212
146
|
},
|
|
213
147
|
hotkeys: async () => {
|
|
@@ -266,22 +200,11 @@ export async function composeOwnedUi(options = {}) {
|
|
|
266
200
|
// Invariant: process-wide services are created once here and borrowed by every session shell. The
|
|
267
201
|
// keybinding registry is applied before any presenter is built, so none of them reads Pi's defaults.
|
|
268
202
|
const keybindingsHost = createPiKeybindingsHost({ profile: ownedSurfaces ? "a1" : "pi", agentDir: backend.identity.agentDir });
|
|
269
|
-
|
|
270
|
-
// from its effective declarations, so a customization reload republishes the projection.
|
|
271
|
-
const publishTabShortcuts = () => tabBridge?.shortcuts(projectResidentTabShortcuts({
|
|
272
|
-
configured: readShortcutBindingsFile(backend.identity.agentDir),
|
|
273
|
-
reserved: keybindingsHost.manager.getEffectiveConfig(),
|
|
274
|
-
}).assignments);
|
|
275
|
-
const keybindings = tabBridge === null ? keybindingsHost : {
|
|
276
|
-
...keybindingsHost,
|
|
277
|
-
reload: () => { keybindingsHost.reload(); publishTabShortcuts(); },
|
|
278
|
-
};
|
|
279
|
-
publishTabShortcuts();
|
|
203
|
+
const keybindings = residentTabKeybindings(tabBridge, backend.identity.agentDir, keybindingsHost);
|
|
280
204
|
const clipboardServices = ownedSurfaces ? createOwnedUiClipboardServices() : null;
|
|
281
205
|
let createdTerminalHost;
|
|
282
206
|
let terminalHost;
|
|
283
207
|
let presenter;
|
|
284
|
-
let releaseNoteAcknowledgement = null;
|
|
285
208
|
try {
|
|
286
209
|
const presenterOptions = {
|
|
287
210
|
presenters: createPiSessionPresenters(backend),
|
|
@@ -289,16 +212,7 @@ export async function composeOwnedUi(options = {}) {
|
|
|
289
212
|
backend,
|
|
290
213
|
cwd: backend.identity.cwd,
|
|
291
214
|
...(routeHost === null ? {} : { routeHost }),
|
|
292
|
-
...(
|
|
293
|
-
startupRoute: {
|
|
294
|
-
route: "changelog",
|
|
295
|
-
input: { document: currentReleaseNote.markdown },
|
|
296
|
-
onClosed: () => {
|
|
297
|
-
releaseNoteAcknowledgement ??= releaseNoteClaim.acknowledge();
|
|
298
|
-
return releaseNoteAcknowledgement;
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
}),
|
|
215
|
+
...(releaseNote.startupRoute === undefined ? {} : { startupRoute: releaseNote.startupRoute }),
|
|
302
216
|
...(ownedSurfaces ? { sessionLayout: "custom-viewport" } : {}),
|
|
303
217
|
...(tabBridge === null || tabRecovery === null ? {} : { residentTab: {
|
|
304
218
|
requestDetach: () => tabBridge.requestDetach(),
|
|
@@ -367,7 +281,7 @@ export async function composeOwnedUi(options = {}) {
|
|
|
367
281
|
catch (error) {
|
|
368
282
|
await createdTerminalHost?.dispose().catch(() => undefined);
|
|
369
283
|
unsubscribeAccent();
|
|
370
|
-
await
|
|
284
|
+
await releaseNote.releaseClaim();
|
|
371
285
|
clipboardDiagnostics?.dispose();
|
|
372
286
|
suggestionDiagnostics?.dispose();
|
|
373
287
|
clipboardServices?.dispose();
|
|
@@ -381,7 +295,7 @@ export async function composeOwnedUi(options = {}) {
|
|
|
381
295
|
const check = options.checkForNewerRelease
|
|
382
296
|
?? (await import("../foundation/release/latest-release.js")).checkForNewerRelease;
|
|
383
297
|
const release = await check({
|
|
384
|
-
runningVersion: packageVersion ?? await readPackageVersion(),
|
|
298
|
+
runningVersion: releaseNote.packageVersion ?? await readPackageVersion(),
|
|
385
299
|
configDir: productPaths.configDir,
|
|
386
300
|
interactive: process.stdout.isTTY === true,
|
|
387
301
|
...(settings === null || !ownedSurfaces ? {} : { settingEnabled: settings.value("updateCheck") }),
|
|
@@ -410,25 +324,15 @@ export async function composeOwnedUi(options = {}) {
|
|
|
410
324
|
await terminalHost.dispose();
|
|
411
325
|
// Invariant: cleared only once the terminal is restored; a failed dispose leaves it armed.
|
|
412
326
|
(await exitNotice)?.clear();
|
|
413
|
-
await
|
|
327
|
+
await releaseNote.settleAcknowledgement();
|
|
414
328
|
}
|
|
415
329
|
finally {
|
|
416
330
|
// Invariant: the shared spares outlive every shell and stop only after the last one is disposed.
|
|
417
331
|
clipboardServices?.dispose();
|
|
418
332
|
removeControllerBoundaryInput();
|
|
419
333
|
unsubscribeAccent();
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
clearInterval(sessionRuntimeRefreshTimer);
|
|
423
|
-
sessionRuntimeRefreshTimer = null;
|
|
424
|
-
await sessionRuntimeRefresh;
|
|
425
|
-
const owner = await runtimeIdentity();
|
|
426
|
-
if (sessionRuntimeId !== undefined && owner !== null) {
|
|
427
|
-
await import("../foundation/lifecycle/session-repository-context.js")
|
|
428
|
-
.then(module => module.releaseSessionRepositoryRuntime(sessionRuntimeId, owner))
|
|
429
|
-
.catch(() => undefined);
|
|
430
|
-
}
|
|
431
|
-
await releaseNoteClaim?.release();
|
|
334
|
+
await sessionRuntimes.dispose();
|
|
335
|
+
await releaseNote.releaseClaim();
|
|
432
336
|
suggestionDiagnostics?.dispose();
|
|
433
337
|
clipboardDiagnostics?.dispose();
|
|
434
338
|
unsubscribeTabBridge();
|
|
@@ -440,147 +344,6 @@ export async function composeOwnedUi(options = {}) {
|
|
|
440
344
|
};
|
|
441
345
|
return { application, settings };
|
|
442
346
|
}
|
|
443
|
-
export async function bindResidentTabBridge(tabBridge, backend, recovery = null, options = {}) {
|
|
444
|
-
await tabBridge.start().catch(() => false);
|
|
445
|
-
tabBridge.status("starting");
|
|
446
|
-
const currentSessionFile = () => currentBackendSessionFile(backend);
|
|
447
|
-
let pendingAutoName = null;
|
|
448
|
-
// Invariant: while a generated name is being applied, the session name it produces is automatic
|
|
449
|
-
// even if the session normalizes it; reporting it as `user` would block the strip's title
|
|
450
|
-
// animation and every later automatic rename.
|
|
451
|
-
let applyingAutoName = false;
|
|
452
|
-
const isDefaultName = (name) => name == null || /^agent(?: \d+)?$/i.test(name);
|
|
453
|
-
const currentSessionName = () => backend.session.view().status.footer?.sessionName ?? null;
|
|
454
|
-
const announceSession = () => {
|
|
455
|
-
const name = currentSessionName();
|
|
456
|
-
// Invariant: the session may announce the applied name after the workflow resolves; the
|
|
457
|
-
// first non-default name announced for a pending auto-name is that automatic name.
|
|
458
|
-
if (applyingAutoName && !isDefaultName(name)) {
|
|
459
|
-
pendingAutoName = name;
|
|
460
|
-
applyingAutoName = false;
|
|
461
|
-
}
|
|
462
|
-
const automatic = name === pendingAutoName;
|
|
463
|
-
const nameSource = automatic ? "auto" : isDefaultName(name) ? "default" : "user";
|
|
464
|
-
tabBridge.session(currentSessionFile(), name, nameSource);
|
|
465
|
-
};
|
|
466
|
-
let working = false;
|
|
467
|
-
// Invariant: the last turn's settlement stays reported until the next turn starts.
|
|
468
|
-
let lastTurn;
|
|
469
|
-
let autoNameAttempted = false;
|
|
470
|
-
let autoNameArmed = false;
|
|
471
|
-
// Invariant: naming launches once per tab during an agent run, as soon as its prompt is projected,
|
|
472
|
-
// and runs beside the turn instead of waiting for it to settle.
|
|
473
|
-
const launchAutoName = () => {
|
|
474
|
-
if (!autoNameArmed || autoNameAttempted || options.autoName !== true || !isDefaultName(currentSessionName()))
|
|
475
|
-
return;
|
|
476
|
-
const prompt = backend.session.view().transcript.find(block => block.kind === "user")?.text.trim();
|
|
477
|
-
if (!prompt)
|
|
478
|
-
return;
|
|
479
|
-
autoNameAttempted = true;
|
|
480
|
-
void import("../features/resident-tab/index.js").then(({ generateAutoName }) => generateAutoName(prompt, request => backend.generateResidentTabName(request.prompt, request.maxTokens, request.signal))).then(async (name) => {
|
|
481
|
-
// Invariant: a manual rename made while generation was pending owns both the session and the tab.
|
|
482
|
-
if (!isDefaultName(currentSessionName()))
|
|
483
|
-
return;
|
|
484
|
-
pendingAutoName = name;
|
|
485
|
-
applyingAutoName = true;
|
|
486
|
-
const result = await backend.workflows.executeWorkflow({ command: "name", argument: name });
|
|
487
|
-
if (result.outcome !== "completed") {
|
|
488
|
-
applyingAutoName = false;
|
|
489
|
-
if (pendingAutoName === name)
|
|
490
|
-
pendingAutoName = null;
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
const current = currentSessionName();
|
|
494
|
-
if (current !== null && !isDefaultName(current)) {
|
|
495
|
-
// Invariant: The session already holds the applied name; report it as automatic now.
|
|
496
|
-
pendingAutoName = current;
|
|
497
|
-
applyingAutoName = false;
|
|
498
|
-
tabBridge.session(currentSessionFile(), current, "auto");
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
// Invariant: The session announces later; show the generated name meanwhile, still automatic.
|
|
502
|
-
tabBridge.session(currentSessionFile(), name, "auto");
|
|
503
|
-
}
|
|
504
|
-
}).catch(() => { pendingAutoName = null; applyingAutoName = false; });
|
|
505
|
-
};
|
|
506
|
-
const reportStatus = (lifecycle) => {
|
|
507
|
-
const view = backend.session.view();
|
|
508
|
-
const pendingRequests = view.dialog == null ? 0 : 1;
|
|
509
|
-
const queuedInput = view.editor?.queuedSubmissions.length ?? 0;
|
|
510
|
-
tabBridge.status(deriveResidentTabStatus({
|
|
511
|
-
lifecycle: lifecycle ?? view.lifecycle,
|
|
512
|
-
pendingRequests,
|
|
513
|
-
queuedInput,
|
|
514
|
-
...(lastTurn === undefined ? {} : { lastTurn }),
|
|
515
|
-
}), false, pendingRequests, queuedInput);
|
|
516
|
-
};
|
|
517
|
-
announceSession();
|
|
518
|
-
const unsubscribeCommands = typeof tabBridge.onCommand === "function" ? tabBridge.onCommand(command => {
|
|
519
|
-
// Invariant: visibility stays decoded for generation-1 peers but never pauses rendering; a hidden tab
|
|
520
|
-
// keeps its resident terminal current while the holder withholds only its client surfaces.
|
|
521
|
-
if (command.type === "rename") {
|
|
522
|
-
void backend.workflows.executeWorkflow({ command: "name", argument: command.name }).catch(() => { });
|
|
523
|
-
}
|
|
524
|
-
else if (command.type === "route") {
|
|
525
|
-
if (options.openRoute !== undefined)
|
|
526
|
-
options.openRoute(command.route);
|
|
527
|
-
else
|
|
528
|
-
void backend.workflows.executeWorkflow({ command: command.route, argument: "" }).catch(() => { });
|
|
529
|
-
}
|
|
530
|
-
else if (command.type === "stop") {
|
|
531
|
-
void backend.session.dispose().catch(() => { });
|
|
532
|
-
}
|
|
533
|
-
}) : () => { };
|
|
534
|
-
const unsubscribeEvents = backend.session.onEvent(event => {
|
|
535
|
-
if (event.type === "session-lifecycle") {
|
|
536
|
-
const wasWorking = working;
|
|
537
|
-
working = event.lifecycle === "busy";
|
|
538
|
-
if (working)
|
|
539
|
-
lastTurn = undefined;
|
|
540
|
-
reportStatus(event.lifecycle);
|
|
541
|
-
if (wasWorking && !working && recovery !== null) {
|
|
542
|
-
void recovery.settled(currentSessionFile()).catch(() => tabBridge.degraded(true));
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
if (event.type === "agent-run-started") {
|
|
546
|
-
autoNameArmed = true;
|
|
547
|
-
launchAutoName();
|
|
548
|
-
}
|
|
549
|
-
// Rationale: Pi starts the run before it projects the user message, so the prompt may arrive later.
|
|
550
|
-
if (event.type === "transcript-block" || event.type === "session-view")
|
|
551
|
-
launchAutoName();
|
|
552
|
-
if (event.type === "agent-run-settled") {
|
|
553
|
-
launchAutoName();
|
|
554
|
-
autoNameArmed = false;
|
|
555
|
-
lastTurn = event.aborted ? "aborted" : event.successful ? "done" : "error";
|
|
556
|
-
tabBridge.prompt(event.aborted);
|
|
557
|
-
reportStatus();
|
|
558
|
-
}
|
|
559
|
-
if (event.type === "session-view") {
|
|
560
|
-
announceSession();
|
|
561
|
-
reportStatus();
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
return () => {
|
|
565
|
-
unsubscribeCommands();
|
|
566
|
-
unsubscribeEvents();
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
export async function correlateResidentTabSubmission(recovery, backend, submissionId, entryId) {
|
|
570
|
-
await recovery.correlated(submissionId, entryId);
|
|
571
|
-
// Concurrency: a normal prompt command can resolve after the ready transition already fired.
|
|
572
|
-
// Re-read lifecycle only after correlation is active; a concurrent ready observer and this path
|
|
573
|
-
// safely race because recovery consumes the active entry once and retirement is idempotent.
|
|
574
|
-
if (backend.session.view().lifecycle !== "busy")
|
|
575
|
-
await recovery.settled(currentBackendSessionFile(backend));
|
|
576
|
-
}
|
|
577
|
-
function currentBackendSessionFile(backend) {
|
|
578
|
-
const direct = backend.identity.currentSessionFile?.();
|
|
579
|
-
if (direct !== undefined && direct !== null)
|
|
580
|
-
return direct;
|
|
581
|
-
const resume = backend.identity.currentSessionResumeMetadata();
|
|
582
|
-
return resume === null ? null : `${resume.sessionDir}/${resume.sessionId}`;
|
|
583
|
-
}
|
|
584
347
|
const SHARED_SETTINGS_POLL_MS = 250;
|
|
585
348
|
/** Rereads the shared settings document whenever its stat changes; returns the stop. */
|
|
586
349
|
export function watchSharedSettings(settings) {
|
|
@@ -596,13 +359,6 @@ export function watchSharedSettings(settings) {
|
|
|
596
359
|
watchFile(file, { interval: SHARED_SETTINGS_POLL_MS, persistent: false }, listener);
|
|
597
360
|
return () => unwatchFile(file, listener);
|
|
598
361
|
}
|
|
599
|
-
async function readPackageVersion() {
|
|
600
|
-
const manifest = JSON.parse(await readFile(new URL("../../package.json", import.meta.url), "utf8"));
|
|
601
|
-
const version = manifest?.version;
|
|
602
|
-
if (typeof version !== "string")
|
|
603
|
-
throw new Error("A1 package version is unavailable");
|
|
604
|
-
return version;
|
|
605
|
-
}
|
|
606
362
|
function quitOutroSettingsSnapshot(settings) {
|
|
607
363
|
return {
|
|
608
364
|
enabled: settings.value("quitAnimation"),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TabBridgeClient } from "../foundation/resident-tabs/bridge-client.js";
|
|
2
|
+
import type { ResidentTabRecovery } from "../foundation/resident-tabs/recovery.js";
|
|
3
|
+
import type { PiKeybindingsHost } from "../integrations/pi/components/shell-shared-facade.js";
|
|
4
|
+
import type { ComposedSessionBackend } from "./session-backend.js";
|
|
5
|
+
/**
|
|
6
|
+
* The keybindings the shells borrow. Inside a resident tab the host dispatches and labels the tab
|
|
7
|
+
* shortcuts this child projects from its effective declarations, so the projection is published now
|
|
8
|
+
* and again after every customization reload.
|
|
9
|
+
*/
|
|
10
|
+
export declare function residentTabKeybindings(tabBridge: TabBridgeClient | null, agentDir: string, keybindingsHost: PiKeybindingsHost): PiKeybindingsHost;
|
|
11
|
+
export declare function bindResidentTabBridge(tabBridge: TabBridgeClient, backend: Pick<ComposedSessionBackend, "identity" | "session" | "workflows" | "generateResidentTabName">, recovery?: Pick<ResidentTabRecovery, "settled"> | null, options?: {
|
|
12
|
+
readonly autoName?: boolean;
|
|
13
|
+
readonly openRoute?: (route: "settings") => void;
|
|
14
|
+
}): Promise<() => void>;
|
|
15
|
+
export declare function correlateResidentTabSubmission(recovery: Pick<ResidentTabRecovery, "correlated" | "settled">, backend: Pick<ComposedSessionBackend, "identity" | "session">, submissionId: string, entryId: string): Promise<void>;
|
|
16
|
+
export declare function currentBackendSessionFile(backend: Pick<ComposedSessionBackend, "identity">): string | null;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { deriveResidentTabStatus } from "../features/resident-tab/status-deriver.js";
|
|
2
|
+
import { projectResidentTabShortcuts } from "../features/resident-tab/shortcut-projection.js";
|
|
3
|
+
import { readShortcutBindingsFile } from "../features/resident-tab/shortcut-bindings-file.js";
|
|
4
|
+
/**
|
|
5
|
+
* The keybindings the shells borrow. Inside a resident tab the host dispatches and labels the tab
|
|
6
|
+
* shortcuts this child projects from its effective declarations, so the projection is published now
|
|
7
|
+
* and again after every customization reload.
|
|
8
|
+
*/
|
|
9
|
+
export function residentTabKeybindings(tabBridge, agentDir, keybindingsHost) {
|
|
10
|
+
if (tabBridge === null)
|
|
11
|
+
return keybindingsHost;
|
|
12
|
+
const publish = () => tabBridge.shortcuts(projectResidentTabShortcuts({
|
|
13
|
+
configured: readShortcutBindingsFile(agentDir),
|
|
14
|
+
reserved: keybindingsHost.manager.getEffectiveConfig(),
|
|
15
|
+
}).assignments);
|
|
16
|
+
publish();
|
|
17
|
+
return { ...keybindingsHost, reload: () => { keybindingsHost.reload(); publish(); } };
|
|
18
|
+
}
|
|
19
|
+
export async function bindResidentTabBridge(tabBridge, backend, recovery = null, options = {}) {
|
|
20
|
+
await tabBridge.start().catch(() => false);
|
|
21
|
+
tabBridge.status("starting");
|
|
22
|
+
const currentSessionFile = () => currentBackendSessionFile(backend);
|
|
23
|
+
let pendingAutoName = null;
|
|
24
|
+
// Invariant: while a generated name is being applied, the session name it produces is automatic
|
|
25
|
+
// even if the session normalizes it; reporting it as `user` would block the strip's title
|
|
26
|
+
// animation and every later automatic rename.
|
|
27
|
+
let applyingAutoName = false;
|
|
28
|
+
const isDefaultName = (name) => name == null || /^agent(?: \d+)?$/i.test(name);
|
|
29
|
+
const currentSessionName = () => backend.session.view().status.footer?.sessionName ?? null;
|
|
30
|
+
const announceSession = () => {
|
|
31
|
+
const name = currentSessionName();
|
|
32
|
+
// Invariant: the session may announce the applied name after the workflow resolves; the
|
|
33
|
+
// first non-default name announced for a pending auto-name is that automatic name.
|
|
34
|
+
if (applyingAutoName && !isDefaultName(name)) {
|
|
35
|
+
pendingAutoName = name;
|
|
36
|
+
applyingAutoName = false;
|
|
37
|
+
}
|
|
38
|
+
const automatic = name === pendingAutoName;
|
|
39
|
+
const nameSource = automatic ? "auto" : isDefaultName(name) ? "default" : "user";
|
|
40
|
+
tabBridge.session(currentSessionFile(), name, nameSource);
|
|
41
|
+
};
|
|
42
|
+
let working = false;
|
|
43
|
+
// Invariant: the last turn's settlement stays reported until the next turn starts.
|
|
44
|
+
let lastTurn;
|
|
45
|
+
let autoNameAttempted = false;
|
|
46
|
+
let autoNameArmed = false;
|
|
47
|
+
// Invariant: naming launches once per tab during an agent run, as soon as its prompt is projected,
|
|
48
|
+
// and runs beside the turn instead of waiting for it to settle.
|
|
49
|
+
const launchAutoName = () => {
|
|
50
|
+
if (!autoNameArmed || autoNameAttempted || options.autoName !== true || !isDefaultName(currentSessionName()))
|
|
51
|
+
return;
|
|
52
|
+
const prompt = backend.session.view().transcript.find(block => block.kind === "user")?.text.trim();
|
|
53
|
+
if (!prompt)
|
|
54
|
+
return;
|
|
55
|
+
autoNameAttempted = true;
|
|
56
|
+
void import("../features/resident-tab/index.js").then(({ generateAutoName }) => generateAutoName(prompt, request => backend.generateResidentTabName(request.prompt, request.maxTokens, request.signal))).then(async (name) => {
|
|
57
|
+
// Invariant: a manual rename made while generation was pending owns both the session and the tab.
|
|
58
|
+
if (!isDefaultName(currentSessionName()))
|
|
59
|
+
return;
|
|
60
|
+
pendingAutoName = name;
|
|
61
|
+
applyingAutoName = true;
|
|
62
|
+
const result = await backend.workflows.executeWorkflow({ command: "name", argument: name });
|
|
63
|
+
if (result.outcome !== "completed") {
|
|
64
|
+
applyingAutoName = false;
|
|
65
|
+
if (pendingAutoName === name)
|
|
66
|
+
pendingAutoName = null;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const current = currentSessionName();
|
|
70
|
+
if (current !== null && !isDefaultName(current)) {
|
|
71
|
+
// Invariant: The session already holds the applied name; report it as automatic now.
|
|
72
|
+
pendingAutoName = current;
|
|
73
|
+
applyingAutoName = false;
|
|
74
|
+
tabBridge.session(currentSessionFile(), current, "auto");
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Invariant: The session announces later; show the generated name meanwhile, still automatic.
|
|
78
|
+
tabBridge.session(currentSessionFile(), name, "auto");
|
|
79
|
+
}
|
|
80
|
+
}).catch(() => { pendingAutoName = null; applyingAutoName = false; });
|
|
81
|
+
};
|
|
82
|
+
const reportStatus = (lifecycle) => {
|
|
83
|
+
const view = backend.session.view();
|
|
84
|
+
const pendingRequests = view.dialog == null ? 0 : 1;
|
|
85
|
+
const queuedInput = view.editor?.queuedSubmissions.length ?? 0;
|
|
86
|
+
tabBridge.status(deriveResidentTabStatus({
|
|
87
|
+
lifecycle: lifecycle ?? view.lifecycle,
|
|
88
|
+
pendingRequests,
|
|
89
|
+
queuedInput,
|
|
90
|
+
...(lastTurn === undefined ? {} : { lastTurn }),
|
|
91
|
+
}), false, pendingRequests, queuedInput);
|
|
92
|
+
};
|
|
93
|
+
announceSession();
|
|
94
|
+
const unsubscribeCommands = typeof tabBridge.onCommand === "function" ? tabBridge.onCommand(command => {
|
|
95
|
+
// Invariant: visibility stays decoded for generation-1 peers but never pauses rendering; a hidden tab
|
|
96
|
+
// keeps its resident terminal current while the holder withholds only its client surfaces.
|
|
97
|
+
if (command.type === "rename") {
|
|
98
|
+
void backend.workflows.executeWorkflow({ command: "name", argument: command.name }).catch(() => { });
|
|
99
|
+
}
|
|
100
|
+
else if (command.type === "route") {
|
|
101
|
+
if (options.openRoute !== undefined)
|
|
102
|
+
options.openRoute(command.route);
|
|
103
|
+
else
|
|
104
|
+
void backend.workflows.executeWorkflow({ command: command.route, argument: "" }).catch(() => { });
|
|
105
|
+
}
|
|
106
|
+
else if (command.type === "stop") {
|
|
107
|
+
void backend.session.dispose().catch(() => { });
|
|
108
|
+
}
|
|
109
|
+
}) : () => { };
|
|
110
|
+
const unsubscribeEvents = backend.session.onEvent(event => {
|
|
111
|
+
if (event.type === "session-lifecycle") {
|
|
112
|
+
const wasWorking = working;
|
|
113
|
+
working = event.lifecycle === "busy";
|
|
114
|
+
if (working)
|
|
115
|
+
lastTurn = undefined;
|
|
116
|
+
reportStatus(event.lifecycle);
|
|
117
|
+
if (wasWorking && !working && recovery !== null) {
|
|
118
|
+
void recovery.settled(currentSessionFile()).catch(() => tabBridge.degraded(true));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (event.type === "agent-run-started") {
|
|
122
|
+
autoNameArmed = true;
|
|
123
|
+
launchAutoName();
|
|
124
|
+
}
|
|
125
|
+
// Rationale: Pi starts the run before it projects the user message, so the prompt may arrive later.
|
|
126
|
+
if (event.type === "transcript-block" || event.type === "session-view")
|
|
127
|
+
launchAutoName();
|
|
128
|
+
if (event.type === "agent-run-settled") {
|
|
129
|
+
launchAutoName();
|
|
130
|
+
autoNameArmed = false;
|
|
131
|
+
lastTurn = event.aborted ? "aborted" : event.successful ? "done" : "error";
|
|
132
|
+
tabBridge.prompt(event.aborted);
|
|
133
|
+
reportStatus();
|
|
134
|
+
}
|
|
135
|
+
if (event.type === "session-view") {
|
|
136
|
+
announceSession();
|
|
137
|
+
reportStatus();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return () => {
|
|
141
|
+
unsubscribeCommands();
|
|
142
|
+
unsubscribeEvents();
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export async function correlateResidentTabSubmission(recovery, backend, submissionId, entryId) {
|
|
146
|
+
await recovery.correlated(submissionId, entryId);
|
|
147
|
+
// Concurrency: a normal prompt command can resolve after the ready transition already fired.
|
|
148
|
+
// Re-read lifecycle only after correlation is active; a concurrent ready observer and this path
|
|
149
|
+
// safely race because recovery consumes the active entry once and retirement is idempotent.
|
|
150
|
+
if (backend.session.view().lifecycle !== "busy")
|
|
151
|
+
await recovery.settled(currentBackendSessionFile(backend));
|
|
152
|
+
}
|
|
153
|
+
export function currentBackendSessionFile(backend) {
|
|
154
|
+
const direct = backend.identity.currentSessionFile?.();
|
|
155
|
+
if (direct !== undefined && direct !== null)
|
|
156
|
+
return direct;
|
|
157
|
+
const resume = backend.identity.currentSessionResumeMetadata();
|
|
158
|
+
return resume === null ? null : `${resume.sessionDir}/${resume.sessionId}`;
|
|
159
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AgentSettingsPort } from "../contracts/agent-engine/index.js";
|
|
2
|
+
import type { OwnedUiPromptSuggestionGeneratorPort, UiAccentColor } from "../contracts/owned-ui/index.js";
|
|
3
|
+
import type { PiEngineSessionRequest } from "../integrations/pi/engine/host.js";
|
|
4
|
+
import type { PiSessionPresenterBackend } from "../integrations/pi/session-presenters/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* The session backend composition wires: the owned-UI port, the prompt-suggestion generator, and
|
|
7
|
+
* the agent settings port. `settingsPort` stays outside the owned-UI contract because its type
|
|
8
|
+
* belongs to the agent-engine contracts, which the owned-UI contract may not import.
|
|
9
|
+
*/
|
|
10
|
+
export interface ComposedSessionBackend extends PiSessionPresenterBackend, OwnedUiPromptSuggestionGeneratorPort {
|
|
11
|
+
settingsPort(): AgentSettingsPort | null;
|
|
12
|
+
generateResidentTabName(instruction: string, maxTokens: number, signal: AbortSignal): Promise<string | null>;
|
|
13
|
+
}
|
|
14
|
+
/** The process-level engine host as composition drives it: sessions, the live accent, and disposal. */
|
|
15
|
+
export interface ComposedEngineHost {
|
|
16
|
+
create(request: PiEngineSessionRequest): Promise<ComposedSessionBackend>;
|
|
17
|
+
setAccentColor(color: UiAccentColor): void;
|
|
18
|
+
dispose(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NativeProcessIdentity } from "../foundation/lifecycle/model.js";
|
|
2
|
+
export interface SessionRuntimeRegistryOptions {
|
|
3
|
+
/** This runtime's id from the environment; absent outside a runtime that claims worktrees. */
|
|
4
|
+
readonly runtimeId: string | undefined;
|
|
5
|
+
/** Exact native process inspection; absent SDK seams publish no claims. */
|
|
6
|
+
readonly inspectProcess: ((pid: number) => Promise<NativeProcessIdentity | null>) | undefined;
|
|
7
|
+
readonly refreshMs?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The live worktree claims this runtime's agents hold: each agent's session identity, the heartbeat
|
|
11
|
+
* that keeps the claims fresh, and their release when the runtime is disposed.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SessionRuntimeRegistry {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(options: SessionRuntimeRegistryOptions);
|
|
16
|
+
/** The repository context reader for one agent; undefined names the primary agent. */
|
|
17
|
+
readerFor(agentId: string | undefined): (sessionId: string, sessionFile: string, signal: AbortSignal) => Promise<import("../foundation/lifecycle/session-repository-context.js").SessionRepositoryContext | null>;
|
|
18
|
+
/** Stops the heartbeat, waits for an in-flight refresh, and releases every claim this runtime holds. */
|
|
19
|
+
dispose(): Promise<void>;
|
|
20
|
+
}
|