@jmfederico/pi-web 1.202607.1 → 1.202607.3
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 +8 -8
- package/dist/cli.js +17 -5
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{CodeViewer-uASb28Y1.js → CodeViewer-CDVbMiN9.js} +1 -1
- package/dist/client/assets/{TerminalPanel-DfTmykFw.js → TerminalPanel-CTS1CgqF.js} +2 -2
- package/dist/client/assets/{UnifiedDiffViewer-laQN1x7P.js → UnifiedDiffViewer-Cqke12ZX.js} +1 -1
- package/dist/client/assets/index-LME0LfPb.js +4068 -0
- package/dist/client/index.html +1 -1
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-web-plugins/info/infoInternals.js +208 -0
- package/dist/pi-web-plugins/info/pi-web-plugin.js +12 -15
- package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
- package/dist/pi-web-plugins/relays/package.json +9 -0
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
- package/dist/pi-web-plugins/relays/relayDiscovery.js +96 -0
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +393 -0
- package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
- package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
- package/dist/plugin-api.d.ts +7 -1
- package/dist/server/configRoutes.js +11 -0
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/diagnostics/nodePtyNativeModule.js +36 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -0
- package/dist/server/git/gitService.js +176 -5
- package/dist/server/git/gitService.js.map +1 -1
- package/dist/server/piWebStatus.js +6 -3
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +4 -0
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +32 -0
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
- package/dist/server/sessiond.js +127 -79
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserTool.js +105 -0
- package/dist/server/sessions/askUserTool.js.map +1 -0
- package/dist/server/sessions/authService.js +58 -7
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/extensionDialogWaiters.js +76 -0
- package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +167 -0
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -0
- package/dist/server/sessions/modelCatalogRefresher.js +192 -0
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -0
- package/dist/server/sessions/parentSessionLocator.js +75 -0
- package/dist/server/sessions/parentSessionLocator.js.map +1 -0
- package/dist/server/sessions/pendingAskStore.js +278 -0
- package/dist/server/sessions/pendingAskStore.js.map +1 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +1226 -130
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/plainTextTheme.js +112 -0
- package/dist/server/sessions/plainTextTheme.js.map +1 -0
- package/dist/server/sessions/sessionCommandService.js +68 -9
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionFileHeader.js +45 -0
- package/dist/server/sessions/sessionFileHeader.js.map +1 -0
- package/dist/server/sessions/sessionNotificationStore.js +484 -0
- package/dist/server/sessions/sessionNotificationStore.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +273 -1
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionTreeProjection.js +250 -0
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -0
- package/dist/server/sessions/sessionUnreadStore.js +520 -0
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -0
- package/dist/server/sessions/spawnSessionTool.js +2 -2
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +10 -12
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +3 -16
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
- package/dist/server/workspaces/gitWorktreeDiscovery.js +9 -0
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -1
- package/dist/server/workspaces/projectWorkspaceCwds.js +22 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
- package/dist/server/workspaces/workspaceService.js +15 -2
- package/dist/server/workspaces/workspaceService.js.map +1 -1
- package/dist/shared/activity.js +9 -1
- package/dist/shared/activity.js.map +1 -1
- package/dist/shared/apiTypes.d.ts +452 -2
- package/dist/shared/apiTypes.js +34 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +9 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +12 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/docs/config.md +84 -7
- package/docs/plugins.md +87 -17
- package/install.sh +1 -1
- package/package.json +9 -7
- package/dist/client/assets/index-Dhapi0oR.js +0 -3093
- package/dist/server/sessiond/sessionDaemonStartup.js +0 -49
- package/dist/server/sessiond/sessionDaemonStartup.js.map +0 -1
- package/dist/server/sessions/sessionArchiveMigration.js +0 -590
- package/dist/server/sessions/sessionArchiveMigration.js.map +0 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { ASK_USER_ID_MAX_LENGTH, ASK_USER_OPTION_LIMIT, ASK_USER_QUESTION_LIMIT, ASK_USER_TEXT_MAX_LENGTH, } from "../../shared/apiTypes.js";
|
|
4
|
+
import { renderSupersededAskText } from "./pendingAskStore.js";
|
|
5
|
+
const AskUserOptionParams = Type.Object({
|
|
6
|
+
value: Type.String({
|
|
7
|
+
maxLength: ASK_USER_ID_MAX_LENGTH,
|
|
8
|
+
description: "Stable machine value reported back to you when the user picks this option.",
|
|
9
|
+
}),
|
|
10
|
+
label: Type.String({
|
|
11
|
+
maxLength: ASK_USER_TEXT_MAX_LENGTH,
|
|
12
|
+
description: "Short label the user reads, ideally a few words.",
|
|
13
|
+
}),
|
|
14
|
+
detail: Type.Optional(Type.String({
|
|
15
|
+
maxLength: ASK_USER_TEXT_MAX_LENGTH,
|
|
16
|
+
description: "Optional clarification shown under the label.",
|
|
17
|
+
})),
|
|
18
|
+
});
|
|
19
|
+
const AskUserQuestionParams = Type.Object({
|
|
20
|
+
id: Type.String({
|
|
21
|
+
maxLength: ASK_USER_ID_MAX_LENGTH,
|
|
22
|
+
description: "Unique within this call; used as the answer key reported back to you.",
|
|
23
|
+
}),
|
|
24
|
+
question: Type.String({
|
|
25
|
+
maxLength: ASK_USER_TEXT_MAX_LENGTH,
|
|
26
|
+
description: "The question itself, as one plain-text line.",
|
|
27
|
+
}),
|
|
28
|
+
detail: Type.Optional(Type.String({
|
|
29
|
+
maxLength: ASK_USER_TEXT_MAX_LENGTH,
|
|
30
|
+
description: "Optional supporting context shown under the question.",
|
|
31
|
+
})),
|
|
32
|
+
options: Type.Optional(Type.Array(AskUserOptionParams, {
|
|
33
|
+
maxItems: ASK_USER_OPTION_LIMIT,
|
|
34
|
+
description: "Options to choose from. Omit when free text is the whole answer; the browser always adds a Custom choice.",
|
|
35
|
+
})),
|
|
36
|
+
multiple: Type.Optional(Type.Boolean({
|
|
37
|
+
description: "Allow several options at once. Default: one answer per question.",
|
|
38
|
+
})),
|
|
39
|
+
});
|
|
40
|
+
const AskUserParams = Type.Object({
|
|
41
|
+
questions: Type.Array(AskUserQuestionParams, {
|
|
42
|
+
minItems: 1,
|
|
43
|
+
maxItems: ASK_USER_QUESTION_LIMIT,
|
|
44
|
+
description: "The questions to post, in the order the user should read them. Every question may be left unanswered.",
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
/** Shapes one schema question into the domain question; the store owns validation. */
|
|
48
|
+
function toQuestion(param) {
|
|
49
|
+
const { detail, options, multiple } = param;
|
|
50
|
+
return {
|
|
51
|
+
id: param.id,
|
|
52
|
+
question: param.question,
|
|
53
|
+
...(detail === undefined ? {} : { detail }),
|
|
54
|
+
options: (options ?? []).map(toOption),
|
|
55
|
+
// Keep the compatibility marker on the daemon wire even though the model no
|
|
56
|
+
// longer chooses whether a question accepts a custom answer.
|
|
57
|
+
allowOther: true,
|
|
58
|
+
...(multiple === undefined ? {} : { multiple }),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function toOption(param) {
|
|
62
|
+
const { detail } = param;
|
|
63
|
+
return { value: param.value, label: param.label, ...(detail === undefined ? {} : { detail }) };
|
|
64
|
+
}
|
|
65
|
+
function postedText(result) {
|
|
66
|
+
const count = result.ask.questions.length;
|
|
67
|
+
const posted = `Posted ${count.toString()} question${count === 1 ? "" : "s"} to the user as ask ${result.ask.askId}. Ending this run; the answers arrive as a follow-up message that wakes you, naming every question the user left unanswered. Do not repost these questions.`;
|
|
68
|
+
return result.superseded === undefined ? posted : `${posted}\n\n${renderSupersededAskText(result.superseded)}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Custom tool that posts a question set to the user's browser as interactive UI.
|
|
72
|
+
*
|
|
73
|
+
* It deliberately does **not** await the user. Awaiting would pin the agent run
|
|
74
|
+
* for an unbounded human-scale wait, keep the session streaming, and leave a
|
|
75
|
+
* dangling tool call if the runtime were replaced meanwhile. Instead the ask
|
|
76
|
+
* becomes daemon-owned state, the tool terminates the run, and the submitted
|
|
77
|
+
* answers return later as a follow-up message that wakes the session.
|
|
78
|
+
*
|
|
79
|
+
* Rejected question sets throw: the agent loop turns the thrown message into an
|
|
80
|
+
* error tool result, so the model can fix the ask and post it again.
|
|
81
|
+
*/
|
|
82
|
+
export function createAskUserToolDefinition(deps) {
|
|
83
|
+
return defineTool({
|
|
84
|
+
name: "ask_user",
|
|
85
|
+
label: "Ask user",
|
|
86
|
+
description: "Post a set of questions to the user as a browser form and end this run. Answers arrive later as a follow-up message; the user may leave any question unanswered.",
|
|
87
|
+
promptSnippet: "ask_user: post a question set to the user; ends the run, answers return as a follow-up",
|
|
88
|
+
promptGuidelines: [
|
|
89
|
+
"When you need decisions from the user, post them together with ask_user instead of asking in prose one at a time. It ends the run and the answers, including the questions the user left unanswered, come back as a follow-up message that wakes you. Call it alone and last, and do not repost the same questions or poll for answers.",
|
|
90
|
+
],
|
|
91
|
+
parameters: AskUserParams,
|
|
92
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
93
|
+
const result = await deps.open({
|
|
94
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
95
|
+
questions: params.questions.map(toQuestion),
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: postedText(result) }],
|
|
99
|
+
details: result,
|
|
100
|
+
terminate: true,
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=askUserTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"askUserTool.js","sourceRoot":"","sources":["../../../src/server/sessions/askUserTool.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,11 +3,62 @@ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { getLoginProviderOptions, getLogoutProviderOptions } from "./authProviderOptions.js";
|
|
4
4
|
import { OAuthLoginFlowService } from "./oauthLoginFlowService.js";
|
|
5
5
|
const noopLogger = { error() { } };
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Serializes the `PI_OFFLINE` windows below, which is what keeps their
|
|
8
|
+
* save/restore pairs properly nested. Two overlapping calls would otherwise both
|
|
9
|
+
* capture the forced `"1"` and restore it, leaving the whole process offline
|
|
10
|
+
* permanently. Owned by this module only; nothing else may mutate it.
|
|
11
|
+
*/
|
|
12
|
+
let offlineRuntimeCreations = Promise.resolve();
|
|
13
|
+
/**
|
|
14
|
+
* Create the shared model runtime with runtime-owned network refreshes disabled.
|
|
15
|
+
*
|
|
16
|
+
* Upstream `ModelRuntime.refresh()`, `login()`, and `logout()` always refresh
|
|
17
|
+
* with `allowNetwork: modelNetworkEnabled` by default, and `login()`/`logout()`
|
|
18
|
+
* accept no abort signal. With the default (`PI_OFFLINE` unset) a single stalled
|
|
19
|
+
* provider-catalog fetch can block those call paths for minutes — and, because
|
|
20
|
+
* pi-web shares one runtime and pi coalesces per-provider refreshes, session
|
|
21
|
+
* creation joins the same stalled fetch. Forcing `PI_OFFLINE` during construction makes every runtime-driven
|
|
22
|
+
* refresh local-only; pi-web performs its own bounded catalog refreshes in the
|
|
23
|
+
* background instead (see modelCatalogRefresher.ts).
|
|
24
|
+
*
|
|
25
|
+
* `modelNetworkEnabled` is computed once from the environment inside
|
|
26
|
+
* `ModelRuntime.create()`, so the env var is the only lever upstream exposes.
|
|
27
|
+
* Calls are queued so their env windows never overlap.
|
|
28
|
+
*/
|
|
29
|
+
function createOfflineModelRuntime(options) {
|
|
30
|
+
const created = offlineRuntimeCreations.then(() => forceOfflineWhile(() => ModelRuntime.create(options)));
|
|
31
|
+
// A failed creation must not poison the queue; the caller still sees the rejection.
|
|
32
|
+
offlineRuntimeCreations = created.then(() => undefined, () => undefined);
|
|
33
|
+
return created;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Force `PI_OFFLINE` for the duration of `create`, then restore what was there.
|
|
37
|
+
*
|
|
38
|
+
* `process.env` is process-wide, so this window is a real global side effect:
|
|
39
|
+
* anything reading `PI_OFFLINE` while `create` awaits observes offline mode,
|
|
40
|
+
* including upstream's package manager, tools manager, and version check. That
|
|
41
|
+
* is acceptable because pi-web only builds runtimes during daemon startup and in
|
|
42
|
+
* tests, and the window is one `ModelRuntime.create()` call — but it is why
|
|
43
|
+
* callers must stay serialized rather than run concurrently.
|
|
44
|
+
*/
|
|
45
|
+
async function forceOfflineWhile(create) {
|
|
46
|
+
const previous = process.env["PI_OFFLINE"];
|
|
47
|
+
process.env["PI_OFFLINE"] = "1";
|
|
48
|
+
try {
|
|
49
|
+
return await create();
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
if (previous === undefined)
|
|
53
|
+
delete process.env["PI_OFFLINE"];
|
|
54
|
+
else
|
|
55
|
+
process.env["PI_OFFLINE"] = previous;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function createModelRuntimeForAgentDir(agentDir) {
|
|
59
|
+
return createOfflineModelRuntime({
|
|
8
60
|
authPath: join(agentDir, "auth.json"),
|
|
9
61
|
modelsPath: join(agentDir, "models.json"),
|
|
10
|
-
...(allowModelNetwork === undefined ? {} : { allowModelNetwork }),
|
|
11
62
|
});
|
|
12
63
|
}
|
|
13
64
|
export class AuthService {
|
|
@@ -18,7 +69,7 @@ export class AuthService {
|
|
|
18
69
|
this.logger = logger;
|
|
19
70
|
}
|
|
20
71
|
static async create(deps = {}) {
|
|
21
|
-
const runtime = deps.runtime ?? (deps.agentDir === undefined ? await
|
|
72
|
+
const runtime = deps.runtime ?? (deps.agentDir === undefined ? await createOfflineModelRuntime({}) : await createModelRuntimeForAgentDir(deps.agentDir));
|
|
22
73
|
const logger = deps.logger ?? noopLogger;
|
|
23
74
|
const authFlows = deps.authFlows ?? new OAuthLoginFlowService({ logger });
|
|
24
75
|
return new AuthService(runtime, authFlows, logger);
|
|
@@ -34,7 +85,7 @@ export class AuthService {
|
|
|
34
85
|
this.listeners.clear();
|
|
35
86
|
}
|
|
36
87
|
async authProviders(mode, authType) {
|
|
37
|
-
await this.runtime.
|
|
88
|
+
await this.runtime.refresh();
|
|
38
89
|
const providers = mode === "logout" ? await getLogoutProviderOptions(this.runtime) : getLoginProviderOptions(this.runtime, authType);
|
|
39
90
|
return { providers };
|
|
40
91
|
}
|
|
@@ -113,7 +164,7 @@ export class AuthService {
|
|
|
113
164
|
}
|
|
114
165
|
}
|
|
115
166
|
async requireApiKeyLoginProvider(providerId) {
|
|
116
|
-
await this.runtime.
|
|
167
|
+
await this.runtime.refresh();
|
|
117
168
|
const provider = getLoginProviderOptions(this.runtime, "api_key").find((option) => option.id === providerId);
|
|
118
169
|
if (provider !== undefined)
|
|
119
170
|
return provider;
|
|
@@ -124,7 +175,7 @@ export class AuthService {
|
|
|
124
175
|
throw new Error(`API key provider not found: ${providerId}`);
|
|
125
176
|
}
|
|
126
177
|
async requireOAuthLoginProvider(providerId) {
|
|
127
|
-
await this.runtime.
|
|
178
|
+
await this.runtime.refresh();
|
|
128
179
|
const provider = getLoginProviderOptions(this.runtime, "oauth").find((option) => option.id === providerId);
|
|
129
180
|
if (provider === undefined)
|
|
130
181
|
throw new Error(`OAuth provider not found: ${providerId}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/server/sessions/authService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/server/sessions/authService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** The value an extension's dialog Promise settles with on any close without an answer. */
|
|
2
|
+
export function extensionDialogCancelValue(kind) {
|
|
3
|
+
return kind === "confirm" ? false : undefined;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The auto-cancel delay of one dialog: the sooner of the extension's own
|
|
7
|
+
* `timeout` and the daemon's `extensionDialogsTimeoutMs` default, where `0`
|
|
8
|
+
* (or an invalid extension value, defensively ignored) means "waits forever".
|
|
9
|
+
*/
|
|
10
|
+
export function effectiveExtensionDialogTimeoutMs(extensionTimeoutMs, daemonDefaultMs) {
|
|
11
|
+
const fromExtension = typeof extensionTimeoutMs === "number" && Number.isFinite(extensionTimeoutMs) && extensionTimeoutMs > 0 ? extensionTimeoutMs : undefined;
|
|
12
|
+
const fromDaemon = daemonDefaultMs > 0 ? daemonDefaultMs : undefined;
|
|
13
|
+
if (fromExtension === undefined)
|
|
14
|
+
return fromDaemon;
|
|
15
|
+
if (fromDaemon === undefined)
|
|
16
|
+
return fromExtension;
|
|
17
|
+
return Math.min(fromExtension, fromDaemon);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The parked Promise resolvers behind open extension dialogs, plus the timers
|
|
21
|
+
* and signal subscriptions that can end a wait without the browser. Timers use
|
|
22
|
+
* the global `setTimeout`/`clearTimeout` looked up per call, the same seam the
|
|
23
|
+
* rest of the service's timer tests fake.
|
|
24
|
+
*
|
|
25
|
+
* Kept deliberately separate from {@link PendingExtensionDialogStore}: the
|
|
26
|
+
* store owns the domain state every browser sees, the waiters own the one
|
|
27
|
+
* in-memory resolver each open dialog parks inside extension code — state no
|
|
28
|
+
* browser ever observes and that must not survive the runtime. The pairing is
|
|
29
|
+
* the wiring's invariant: every open store record has exactly one parked
|
|
30
|
+
* waiter, and whoever closes the record settles the waiter exactly once.
|
|
31
|
+
*/
|
|
32
|
+
export class ExtensionDialogWaiters {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.parked = new Map();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Park the extension-facing Promise for a dialog the store just opened. Arms
|
|
38
|
+
* the timeout and signal triggers; both are disarmed when the wait settles,
|
|
39
|
+
* so a settled wait can never be triggered (nor trigger twice).
|
|
40
|
+
*/
|
|
41
|
+
park(dialog, triggers = {}) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const parked = { cancelValue: extensionDialogCancelValue(dialog.kind), resolve };
|
|
44
|
+
if (triggers.timeoutMs !== undefined) {
|
|
45
|
+
const handle = setTimeout(() => { triggers.onTrigger?.("timeout"); }, triggers.timeoutMs);
|
|
46
|
+
parked.cancelArmedTimeout = () => { clearTimeout(handle); };
|
|
47
|
+
}
|
|
48
|
+
if (triggers.signal !== undefined) {
|
|
49
|
+
const signal = triggers.signal;
|
|
50
|
+
const onAbort = () => { triggers.onTrigger?.("cancelled"); };
|
|
51
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
52
|
+
parked.removeSignalListener = () => { signal.removeEventListener("abort", onAbort); };
|
|
53
|
+
}
|
|
54
|
+
this.parked.set(dialog.dialogId, parked);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** Resolve the parked wait with the user's answer, which the store has already validated and recorded. */
|
|
58
|
+
settleWithAnswer(dialogId, answer) {
|
|
59
|
+
return this.settle(dialogId, (parked) => { parked.resolve(answer); });
|
|
60
|
+
}
|
|
61
|
+
/** Resolve the parked wait with the dialog kind's cancel value after a close without an answer. */
|
|
62
|
+
settleWithCancelValue(dialogId) {
|
|
63
|
+
return this.settle(dialogId, (parked) => { parked.resolve(parked.cancelValue); });
|
|
64
|
+
}
|
|
65
|
+
settle(dialogId, resolveParked) {
|
|
66
|
+
const parked = this.parked.get(dialogId);
|
|
67
|
+
if (parked === undefined)
|
|
68
|
+
return false;
|
|
69
|
+
this.parked.delete(dialogId);
|
|
70
|
+
parked.cancelArmedTimeout?.();
|
|
71
|
+
parked.removeSignalListener?.();
|
|
72
|
+
resolveParked(parked);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=extensionDialogWaiters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionDialogWaiters.js","sourceRoot":"","sources":["../../../src/server/sessions/extensionDialogWaiters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
import { createAgentSessionServices, } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
const LOG_CONTEXT = "global-provider-bootstrap";
|
|
7
|
+
const MODELS_FIELD = "models";
|
|
8
|
+
/**
|
|
9
|
+
* Snapshot the merged config Pi holds for every config-registered provider.
|
|
10
|
+
* Pi merges defined values over the previous registration, so the runtime's
|
|
11
|
+
* own record — not the extension's last argument — is the accurate baseline.
|
|
12
|
+
* Native providers have no comparable config and are deliberately absent.
|
|
13
|
+
*/
|
|
14
|
+
function captureProviderConfigBaseline(runtime) {
|
|
15
|
+
const baseline = new Map();
|
|
16
|
+
for (const providerId of runtime.getRegisteredProviderIds()) {
|
|
17
|
+
const config = runtime.getRegisteredProviderConfig(providerId);
|
|
18
|
+
if (config)
|
|
19
|
+
baseline.set(providerId, config);
|
|
20
|
+
}
|
|
21
|
+
return baseline;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* True when `incoming` would change the model catalog of `baseline` and nothing
|
|
25
|
+
* else.
|
|
26
|
+
*
|
|
27
|
+
* Extensions that refresh their catalog re-send a complete provider config, so
|
|
28
|
+
* the test is "equal to the baseline except for models", not "contains only
|
|
29
|
+
* models". Omitted fields are not changes: Pi's merge keeps the previous value,
|
|
30
|
+
* which also makes an unchanged catalog a plain replay rather than an update.
|
|
31
|
+
* Function-valued fields (`streamSimple`, `refreshModels`, `oauth` methods)
|
|
32
|
+
* compare by reference under deep strict equality, so a freshly created closure
|
|
33
|
+
* reads as a mismatch. That conservative direction is intentional: an unclear
|
|
34
|
+
* comparison must fall back to the frozen no-op.
|
|
35
|
+
*/
|
|
36
|
+
function isModelsOnlyProviderUpdate(baseline, incoming) {
|
|
37
|
+
const baselineFields = new Map(Object.entries(baseline));
|
|
38
|
+
const otherFieldsMatch = Object.entries(incoming).every(([field, value]) => {
|
|
39
|
+
if (field === MODELS_FIELD || value === undefined)
|
|
40
|
+
return true;
|
|
41
|
+
return isDeepStrictEqual(value, baselineFields.get(field));
|
|
42
|
+
});
|
|
43
|
+
if (!otherFieldsMatch)
|
|
44
|
+
return false;
|
|
45
|
+
return incoming.models !== undefined && !isDeepStrictEqual(incoming.models, baseline.models);
|
|
46
|
+
}
|
|
47
|
+
async function loadGlobalExtensionServices(runtime, agentDir) {
|
|
48
|
+
const scratchCwd = await mkdtemp(join(tmpdir(), "pi-web-global-ext-"));
|
|
49
|
+
try {
|
|
50
|
+
return await createAgentSessionServices({ cwd: scratchCwd, agentDir, modelRuntime: runtime });
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
await rm(scratchCwd, { recursive: true, force: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function logBootstrapDiagnostic(logger, diagnostic) {
|
|
57
|
+
const details = {
|
|
58
|
+
context: LOG_CONTEXT,
|
|
59
|
+
diagnosticType: diagnostic.type,
|
|
60
|
+
diagnostic: diagnostic.message,
|
|
61
|
+
};
|
|
62
|
+
if (diagnostic.type === "error") {
|
|
63
|
+
logger.error(details, "global extension provider bootstrap diagnostic");
|
|
64
|
+
}
|
|
65
|
+
else if (diagnostic.type === "warning") {
|
|
66
|
+
logger.warn(details, "global extension provider bootstrap diagnostic");
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
logger.info(details, "global extension provider bootstrap diagnostic");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function freezeProviderMutations(runtime, logger, configBaseline) {
|
|
73
|
+
const originalMethods = {
|
|
74
|
+
registerNativeProvider: runtime.registerNativeProvider.bind(runtime),
|
|
75
|
+
registerProvider: runtime.registerProvider.bind(runtime),
|
|
76
|
+
unregisterProvider: runtime.unregisterProvider.bind(runtime),
|
|
77
|
+
};
|
|
78
|
+
const loggedProviderIds = {
|
|
79
|
+
registerNativeProvider: new Set(),
|
|
80
|
+
registerProvider: new Set(),
|
|
81
|
+
unregisterProvider: new Set(),
|
|
82
|
+
};
|
|
83
|
+
// Logging must never turn a provider mutation into an extension failure.
|
|
84
|
+
const logQuietly = (details, message) => {
|
|
85
|
+
try {
|
|
86
|
+
logger.info(details, message);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Intentionally ignored; the mutation decision already stands.
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const logIgnoredMutation = (operation, providerId) => {
|
|
93
|
+
const loggedIds = loggedProviderIds[operation];
|
|
94
|
+
if (loggedIds.has(providerId))
|
|
95
|
+
return;
|
|
96
|
+
loggedIds.add(providerId);
|
|
97
|
+
logQuietly({ context: LOG_CONTEXT, operation, providerId }, "ignored provider mutation after global bootstrap");
|
|
98
|
+
};
|
|
99
|
+
const frozenMethods = {
|
|
100
|
+
registerProvider(providerId, config) {
|
|
101
|
+
const baseline = configBaseline.get(providerId);
|
|
102
|
+
if (!baseline || !isModelsOnlyProviderUpdate(baseline, config)) {
|
|
103
|
+
logIgnoredMutation("registerProvider", providerId);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Pi validates the registration and ends in a fire-and-forget local
|
|
107
|
+
// refresh, so this stays synchronous and never reaches the network.
|
|
108
|
+
originalMethods.registerProvider(providerId, config);
|
|
109
|
+
const accepted = runtime.getRegisteredProviderConfig(providerId);
|
|
110
|
+
// Re-read the merged record so the next comparison uses what Pi stored.
|
|
111
|
+
if (accepted)
|
|
112
|
+
configBaseline.set(providerId, accepted);
|
|
113
|
+
logQuietly({
|
|
114
|
+
context: LOG_CONTEXT,
|
|
115
|
+
operation: "registerProvider",
|
|
116
|
+
providerId,
|
|
117
|
+
modelCount: accepted?.models?.length ?? 0,
|
|
118
|
+
}, "applied models-only provider update after global bootstrap");
|
|
119
|
+
},
|
|
120
|
+
registerNativeProvider(provider) {
|
|
121
|
+
logIgnoredMutation("registerNativeProvider", provider.id);
|
|
122
|
+
},
|
|
123
|
+
unregisterProvider(providerId) {
|
|
124
|
+
logIgnoredMutation("unregisterProvider", providerId);
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
Object.assign(runtime, frozenMethods);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
Object.assign(runtime, originalMethods);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Load global extensions once against the shared model runtime, then make its
|
|
137
|
+
* extension-provider baseline immutable for the rest of the daemon lifetime.
|
|
138
|
+
* All sessions share this runtime, so accepting project-dependent mutations
|
|
139
|
+
* would leak provider configuration across workspaces. This is an accidental
|
|
140
|
+
* contamination guard, not a sandbox for otherwise trusted extensions.
|
|
141
|
+
*
|
|
142
|
+
* The temporary cwd is guaranteed to be empty, so Pi discovers agent-dir
|
|
143
|
+
* extensions without loading project resources. Documented initialization-time
|
|
144
|
+
* config and native registrations therefore reach the runtime through Pi's
|
|
145
|
+
* public service factory. Pi exposes no provider-freeze hook, so the daemon
|
|
146
|
+
* deliberately shadows the three public instance mutation methods afterward;
|
|
147
|
+
* every registration replay or later call is then a logged no-op.
|
|
148
|
+
*
|
|
149
|
+
* The one exception is a known config provider refreshing its own model
|
|
150
|
+
* catalog: a `registerProvider` call whose config matches the recorded
|
|
151
|
+
* baseline in every field except `models` is applied, because a catalog is a
|
|
152
|
+
* property of the provider rather than of the project. Native registration
|
|
153
|
+
* stays fully frozen — it passes a whole `Provider` object with no comparable
|
|
154
|
+
* config — as does unregistration.
|
|
155
|
+
*/
|
|
156
|
+
export async function bootstrapAndFreezeGlobalExtensionProviders(runtime, agentDir, logger) {
|
|
157
|
+
const services = await loadGlobalExtensionServices(runtime, agentDir);
|
|
158
|
+
const providerIds = Object.freeze([...runtime.getRegisteredProviderIds()].sort());
|
|
159
|
+
freezeProviderMutations(runtime, logger, captureProviderConfigBaseline(runtime));
|
|
160
|
+
for (const diagnostic of services.diagnostics)
|
|
161
|
+
logBootstrapDiagnostic(logger, diagnostic);
|
|
162
|
+
for (const extensionError of services.resourceLoader.getExtensions().errors) {
|
|
163
|
+
logger.error({ context: LOG_CONTEXT, error: extensionError.error }, "global extension failed during provider bootstrap");
|
|
164
|
+
}
|
|
165
|
+
logger.info({ context: LOG_CONTEXT, providerIds }, "global extension provider baseline bootstrapped and frozen");
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=globalProviderPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"globalProviderPolicy.js","sourceRoot":"","sources":["../../../src/server/sessions/globalProviderPolicy.ts"],"names":[],"mappings":"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"}
|