@hyperdreamer/pi-webui 1.10.6 → 1.11.0-beta.1
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/dist/client/assets/{CodeViewer-BJJZQ999.js → CodeViewer-DLZCHlMU.js} +1 -1
- package/dist/client/assets/{UnifiedDiffViewer-jpQy9-p7.js → UnifiedDiffViewer-XCb8NlrO.js} +1 -1
- package/dist/client/assets/{index-DWWtrUuQ.js → index-BaDGxg8F.js} +850 -422
- package/dist/client/index.html +1 -1
- package/dist/config.js +88 -14
- package/dist/config.js.map +1 -1
- package/dist/pi-webui-plugins/workspace-memory/pi-webui-plugin.js +3 -2
- package/dist/plugin-api.d.ts +28 -0
- package/dist/server/configRoutes.js +8 -1
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionProxyRoutes.js +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond.js +21 -3
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/modelTierRegistry.js +88 -0
- package/dist/server/sessions/modelTierRegistry.js.map +1 -0
- package/dist/server/sessions/modelTierSettingsRoutes.js +36 -0
- package/dist/server/sessions/modelTierSettingsRoutes.js.map +1 -0
- package/dist/server/sessions/modelTierSettingsService.js +100 -0
- package/dist/server/sessions/modelTierSettingsService.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +49 -4
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +12 -0
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/shared/apiTypes.d.ts +33 -0
- package/dist/shared/apiTypes.js +2 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +3 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +2 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/docs/config.md +25 -2
- package/docs/plugins.md +93 -16
- package/package.json +2 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
2
|
+
import { isKnownThinkingLevel } from "../../shared/thinkingLevels.js";
|
|
3
|
+
import { MODEL_TIERS } from "../../shared/apiTypes.js";
|
|
4
|
+
export { MODEL_TIERS } from "../../shared/apiTypes.js";
|
|
5
|
+
export function isModelTier(value) {
|
|
6
|
+
return MODEL_TIERS.some((tier) => tier === value);
|
|
7
|
+
}
|
|
8
|
+
/** Default thinking-level lookup, delegating to Pi rather than a local table. */
|
|
9
|
+
export function runtimeThinkingLevels(model) {
|
|
10
|
+
return model === undefined ? [] : getSupportedThinkingLevels(model);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Bind config intent to one currently available runtime model. This wrapper is
|
|
14
|
+
* deliberately synchronous: the session daemon's model runtime publishes a
|
|
15
|
+
* current catalog snapshot, and a request must fail closed against that
|
|
16
|
+
* snapshot rather than inventing a fallback while it refreshes.
|
|
17
|
+
*/
|
|
18
|
+
export function createModelTierRegistry(deps) {
|
|
19
|
+
return {
|
|
20
|
+
resolve(tier) {
|
|
21
|
+
const config = deps.loadConfig();
|
|
22
|
+
if (config.modelTiersError !== undefined) {
|
|
23
|
+
throw new Error(`model tier configuration is invalid: ${config.modelTiersError}`);
|
|
24
|
+
}
|
|
25
|
+
if (config.modelTiers === undefined) {
|
|
26
|
+
throw new Error("model tier configuration is missing");
|
|
27
|
+
}
|
|
28
|
+
const models = deps.models();
|
|
29
|
+
const resolved = resolveTier(tier, config.modelTiers, {
|
|
30
|
+
models,
|
|
31
|
+
supportedThinkingLevels: (model) => deps.supportedThinkingLevels(model),
|
|
32
|
+
});
|
|
33
|
+
const model = models.find((candidate) => candidate.provider === resolved.model.provider && candidate.id === resolved.model.id);
|
|
34
|
+
if (model === undefined) {
|
|
35
|
+
// This is defensive against a catalog changing between the resolution
|
|
36
|
+
// lookup and the runtime handoff. It is still a terminal resolution
|
|
37
|
+
// failure, never a neighbouring-tier substitution.
|
|
38
|
+
throw new Error(`tier ${tier} names unavailable model ${resolved.model.provider}/${resolved.model.id}`);
|
|
39
|
+
}
|
|
40
|
+
return { tier: resolved.tier, model, thinkingLevel: resolved.thinkingLevel };
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function describeModel(model) {
|
|
45
|
+
return `${model.provider}/${model.id}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve one tier, or throw explaining why not.
|
|
49
|
+
*
|
|
50
|
+
* Every failure is terminal by design. Substituting a neighbouring tier would
|
|
51
|
+
* silently run weaker or stronger work than the caller asked for, which is the
|
|
52
|
+
* exact failure the tier system exists to prevent.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveTier(tier, ladder, deps) {
|
|
55
|
+
if (!isModelTier(tier))
|
|
56
|
+
throw new Error(`unknown tier: ${tier}`);
|
|
57
|
+
const entry = ladder[tier];
|
|
58
|
+
if (entry === undefined)
|
|
59
|
+
throw new Error(`tier ${tier} has no ladder entry`);
|
|
60
|
+
const available = deps.models.find((candidate) => candidate.provider === entry.model.provider && candidate.id === entry.model.id);
|
|
61
|
+
if (available === undefined) {
|
|
62
|
+
throw new Error(`tier ${tier} names unavailable model ${describeModel(entry.model)}`);
|
|
63
|
+
}
|
|
64
|
+
if (!isKnownThinkingLevel(entry.thinkingLevel)) {
|
|
65
|
+
throw new Error(`tier ${tier} names unknown thinking level ${entry.thinkingLevel}`);
|
|
66
|
+
}
|
|
67
|
+
const supported = deps.supportedThinkingLevels(available);
|
|
68
|
+
if (!supported.includes(entry.thinkingLevel)) {
|
|
69
|
+
throw new Error(`tier ${tier} names thinking level ${entry.thinkingLevel}, unsupported by ${describeModel(entry.model)}`);
|
|
70
|
+
}
|
|
71
|
+
return { tier, model: { ...entry.model }, thinkingLevel: entry.thinkingLevel };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate a complete ladder. Returns a reason rather than throwing, because a
|
|
75
|
+
* broken ladder is a reportable configuration state, not an exception path.
|
|
76
|
+
*/
|
|
77
|
+
export function validateLadder(ladder, deps) {
|
|
78
|
+
for (const tier of MODEL_TIERS) {
|
|
79
|
+
try {
|
|
80
|
+
resolveTier(tier, ladder, deps);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return { valid: false, reason: error instanceof Error ? error.message : String(error) };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { valid: true };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=modelTierRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelTierRegistry.js","sourceRoot":"","sources":["../../../src/server/sessions/modelTierRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { parseModelTiersConfig } from "../../config.js";
|
|
2
|
+
export function registerModelTierSettingsRoutes(app, service, prefix = "") {
|
|
3
|
+
app.get(`${prefix}/model-tiers`, async (_request, reply) => {
|
|
4
|
+
try {
|
|
5
|
+
return await service.inspect();
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
app.put(`${prefix}/model-tiers`, async (request, reply) => {
|
|
12
|
+
try {
|
|
13
|
+
return await service.replace(parseReplacement(request.body));
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function parseReplacement(value) {
|
|
21
|
+
if (!isRecord(value))
|
|
22
|
+
throw new Error("Expected object body");
|
|
23
|
+
const unknownField = Object.keys(value).find((key) => key !== "ladder");
|
|
24
|
+
if (unknownField !== undefined)
|
|
25
|
+
throw new Error(`unknown field ${JSON.stringify(unknownField)}; expected exactly ladder`);
|
|
26
|
+
if (!Object.prototype.hasOwnProperty.call(value, "ladder"))
|
|
27
|
+
throw new Error("ladder is required");
|
|
28
|
+
return parseModelTiersConfig(value["ladder"], "request body ladder");
|
|
29
|
+
}
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function errorMessage(error) {
|
|
34
|
+
return error instanceof Error ? error.message : String(error);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=modelTierSettingsRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelTierSettingsRoutes.js","sourceRoot":"","sources":["../../../src/server/sessions/modelTierSettingsRoutes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AASxD,MAAM,UAAU,+BAA+B,CAAC,GAAoB,EAAE,OAAsC,EAAE,MAAM,GAAG,EAAE;IACvH,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACzD,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAoB,GAAG,MAAM,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC3E,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC9D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC;IACxE,IAAI,YAAY,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,2BAA2B,CAAC,CAAC;IAC1H,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAClG,OAAO,qBAAqB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { MODEL_TIERS, } from "../../shared/apiTypes.js";
|
|
2
|
+
import { resolveTier } from "./modelTierRegistry.js";
|
|
3
|
+
/**
|
|
4
|
+
* Inspect and atomically replace the machine-global model-tier ladder against
|
|
5
|
+
* the daemon's latest authenticated model catalog.
|
|
6
|
+
*
|
|
7
|
+
* File access and runtime refresh remain dependency-injected so this boundary
|
|
8
|
+
* can be tested without starting the daemon or making network requests.
|
|
9
|
+
*/
|
|
10
|
+
export function createModelTierSettingsService(deps) {
|
|
11
|
+
const inspect = async () => {
|
|
12
|
+
const snapshot = await refreshedSnapshot();
|
|
13
|
+
return responseFor(deps.loadConfig(), snapshot);
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
inspect,
|
|
17
|
+
replace: async (ladder) => {
|
|
18
|
+
const snapshot = await refreshedSnapshot();
|
|
19
|
+
const rows = validationRows(ladder, snapshot);
|
|
20
|
+
const invalidRows = MODEL_TIERS.filter((tier) => !rows[tier].valid);
|
|
21
|
+
if (invalidRows.length > 0) {
|
|
22
|
+
throw new Error(invalidRows.map((tier) => rows[tier].reason ?? `tier ${tier} is invalid`).join("; "));
|
|
23
|
+
}
|
|
24
|
+
await deps.saveConfig({ modelTiers: ladder });
|
|
25
|
+
return await inspect();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
async function refreshedSnapshot() {
|
|
29
|
+
await deps.modelRuntime.refresh({ allowNetwork: false });
|
|
30
|
+
return deps.modelRuntime.getAvailableSnapshot();
|
|
31
|
+
}
|
|
32
|
+
function responseFor(config, models) {
|
|
33
|
+
const modelOptions = models.map((model) => modelOptionFor(model));
|
|
34
|
+
if (config.modelTiersError !== undefined) {
|
|
35
|
+
return {
|
|
36
|
+
contractVersion: 1,
|
|
37
|
+
configError: config.modelTiersError,
|
|
38
|
+
models: modelOptions,
|
|
39
|
+
rows: invalidRows(config.modelTiersError),
|
|
40
|
+
valid: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (config.modelTiers === undefined) {
|
|
44
|
+
return {
|
|
45
|
+
contractVersion: 1,
|
|
46
|
+
models: modelOptions,
|
|
47
|
+
rows: invalidRows("model tier configuration is missing"),
|
|
48
|
+
valid: false,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const rows = validationRows(config.modelTiers, models);
|
|
52
|
+
return {
|
|
53
|
+
contractVersion: 1,
|
|
54
|
+
ladder: config.modelTiers,
|
|
55
|
+
models: modelOptions,
|
|
56
|
+
rows,
|
|
57
|
+
valid: MODEL_TIERS.every((tier) => rows[tier].valid),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function modelOptionFor(model) {
|
|
61
|
+
return {
|
|
62
|
+
model: { provider: model.provider, id: model.id },
|
|
63
|
+
...(model.name === undefined ? {} : { name: model.name }),
|
|
64
|
+
thinkingLevels: [...deps.thinkingLevelsForModel(model)],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function validationRows(ladder, models) {
|
|
68
|
+
return {
|
|
69
|
+
economy: validationFor("economy", ladder, models),
|
|
70
|
+
fast: validationFor("fast", ladder, models),
|
|
71
|
+
standard: validationFor("standard", ladder, models),
|
|
72
|
+
advanced: validationFor("advanced", ladder, models),
|
|
73
|
+
capable: validationFor("capable", ladder, models),
|
|
74
|
+
frontier: validationFor("frontier", ladder, models),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function validationFor(tier, ladder, models) {
|
|
78
|
+
try {
|
|
79
|
+
resolveTier(tier, ladder, {
|
|
80
|
+
models,
|
|
81
|
+
supportedThinkingLevels: (model) => deps.thinkingLevelsForModel(model),
|
|
82
|
+
});
|
|
83
|
+
return { valid: true };
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return { valid: false, reason: error instanceof Error ? error.message : String(error) };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function invalidRows(reason) {
|
|
90
|
+
return {
|
|
91
|
+
economy: { valid: false, reason },
|
|
92
|
+
fast: { valid: false, reason },
|
|
93
|
+
standard: { valid: false, reason },
|
|
94
|
+
advanced: { valid: false, reason },
|
|
95
|
+
capable: { valid: false, reason },
|
|
96
|
+
frontier: { valid: false, reason },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=modelTierSettingsService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelTierSettingsService.js","sourceRoot":"","sources":["../../../src/server/sessions/modelTierSettingsService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { loadPiWebUiConfig } from "../../config.js";
|
|
3
4
|
import { open, readFile, writeFile } from "node:fs/promises";
|
|
4
5
|
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEditToolDefinition, defineTool, readStoredCredential, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
5
6
|
import { projectBrowserMessage } from "../browserMessageProjection.js";
|
|
@@ -15,6 +16,7 @@ import { deterministicSessionName, fallbackSessionName, generateShortSessionName
|
|
|
15
16
|
import { computeEditPreview } from "./editPreview.js";
|
|
16
17
|
import { attachmentsToInlineImages, saveAttachmentsToWorkspace } from "./attachmentService.js";
|
|
17
18
|
import { parsePromptAttachments } from "../../shared/promptAttachments.js";
|
|
19
|
+
import { isKnownThinkingLevel } from "../../shared/thinkingLevels.js";
|
|
18
20
|
import { SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH, SESSION_UNREAD_LIMIT } from "../../shared/apiTypes.js";
|
|
19
21
|
import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
|
|
20
22
|
import { createSpawnSessionToolDefinition } from "./spawnSessionTool.js";
|
|
@@ -24,6 +26,7 @@ import { planForceCleanup, planSessionCleanup, summarizeForceCleanupExecution, s
|
|
|
24
26
|
import { SessionNotificationStore, } from "./sessionNotificationStore.js";
|
|
25
27
|
import { plainTextTheme } from "./plainTextTheme.js";
|
|
26
28
|
import { SessionUnreadStore } from "./sessionUnreadStore.js";
|
|
29
|
+
import { createModelTierRegistry, isModelTier, runtimeThinkingLevels } from "./modelTierRegistry.js";
|
|
27
30
|
const noopLogger = { info() { } };
|
|
28
31
|
const DEFAULT_UNREAD_PUBLICATION_RETRY_MS = 1_000;
|
|
29
32
|
const MAX_UNREAD_PUBLICATION_RETRY_MS = 30_000;
|
|
@@ -86,6 +89,15 @@ function requirePromptText(value) {
|
|
|
86
89
|
throw new Error("Prompt text is required");
|
|
87
90
|
return value;
|
|
88
91
|
}
|
|
92
|
+
function leadingTierDirective(prompt) {
|
|
93
|
+
const match = /^(?:\uFEFF)?\/tier-([^\s\r\n]+)(?:[ \t]*(?:\r?\n|$))/u.exec(prompt);
|
|
94
|
+
if (match === null)
|
|
95
|
+
return undefined;
|
|
96
|
+
const value = match[1];
|
|
97
|
+
if (value === undefined || !isModelTier(value))
|
|
98
|
+
throw new Error(`Unknown leading tier directive: /tier-${value ?? ""}`);
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
89
101
|
function parsePromptStreamingBehavior(value) {
|
|
90
102
|
if (value === undefined)
|
|
91
103
|
return undefined;
|
|
@@ -199,26 +211,30 @@ export function dismissSessionWarning(session, dismissId) {
|
|
|
199
211
|
function defaultCreateAgentRuntime(createRuntime, options) {
|
|
200
212
|
if (!(options.sessionManager instanceof SessionManager))
|
|
201
213
|
throw new Error("Default runtime creation requires an SDK SessionManager");
|
|
202
|
-
const runtimeFactory = createRuntimeWithOneShotSessionOptions(createRuntime, options.initialModel, options.delegationToolsEnabled);
|
|
214
|
+
const runtimeFactory = createRuntimeWithOneShotSessionOptions(createRuntime, options.initialModel, options.initialThinkingLevel, options.delegationToolsEnabled);
|
|
203
215
|
return createAgentSessionRuntime(runtimeFactory, {
|
|
204
216
|
cwd: options.cwd,
|
|
205
217
|
agentDir: options.agentDir,
|
|
206
218
|
sessionManager: options.sessionManager,
|
|
207
219
|
});
|
|
208
220
|
}
|
|
209
|
-
function createRuntimeWithOneShotSessionOptions(createRuntime, initialModel, delegationToolsEnabled) {
|
|
221
|
+
function createRuntimeWithOneShotSessionOptions(createRuntime, initialModel, initialThinkingLevel, delegationToolsEnabled) {
|
|
210
222
|
// These inputs belong only to the session being opened. A later runtime
|
|
211
223
|
// replacement resolves its own model and delegation capability.
|
|
212
224
|
let pendingInitialModel = initialModel;
|
|
225
|
+
let pendingInitialThinkingLevel = initialThinkingLevel;
|
|
213
226
|
let pendingDelegationToolsEnabled = delegationToolsEnabled;
|
|
214
227
|
return async (options) => {
|
|
215
228
|
const model = pendingInitialModel;
|
|
229
|
+
const thinkingLevel = pendingInitialThinkingLevel;
|
|
216
230
|
const toolsEnabled = pendingDelegationToolsEnabled;
|
|
217
231
|
pendingInitialModel = undefined;
|
|
232
|
+
pendingInitialThinkingLevel = undefined;
|
|
218
233
|
pendingDelegationToolsEnabled = undefined;
|
|
219
234
|
return createRuntime({
|
|
220
235
|
...options,
|
|
221
236
|
...(model === undefined ? {} : { initialModel: model }),
|
|
237
|
+
...(thinkingLevel === undefined ? {} : { initialThinkingLevel: thinkingLevel }),
|
|
222
238
|
...(toolsEnabled === undefined ? {} : { delegationToolsEnabled: toolsEnabled }),
|
|
223
239
|
});
|
|
224
240
|
};
|
|
@@ -231,7 +247,7 @@ export function createPiWebUiCustomToolDefinitions(cwd, delegationEnabled, spawn
|
|
|
231
247
|
];
|
|
232
248
|
}
|
|
233
249
|
function createDefaultRuntimeFactory(modelRuntime, sessionManagers, spawn, subsessions) {
|
|
234
|
-
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel, delegationToolsEnabled }) => {
|
|
250
|
+
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel, initialThinkingLevel, delegationToolsEnabled }) => {
|
|
235
251
|
const services = await createAgentSessionServices({ cwd, agentDir, modelRuntime });
|
|
236
252
|
const resolvedDelegationToolsEnabled = delegationToolsEnabled
|
|
237
253
|
?? await sessionAllowsDelegationTools(sessionManager, sessionManagers);
|
|
@@ -242,6 +258,7 @@ function createDefaultRuntimeFactory(modelRuntime, sessionManagers, spawn, subse
|
|
|
242
258
|
customTools,
|
|
243
259
|
...(sessionStartEvent === undefined ? {} : { sessionStartEvent }),
|
|
244
260
|
...(initialModel === undefined ? {} : { model: initialModel }),
|
|
261
|
+
...(initialThinkingLevel === undefined ? {} : { thinkingLevel: initialThinkingLevel }),
|
|
245
262
|
});
|
|
246
263
|
return { ...result, services, diagnostics: services.diagnostics };
|
|
247
264
|
};
|
|
@@ -326,6 +343,17 @@ export class PiSessionService {
|
|
|
326
343
|
read: (parentSessionId, sessionId, query, parentSessionFile) => this.readSubsession(parentSessionId, sessionId, query, parentSessionFile),
|
|
327
344
|
});
|
|
328
345
|
this.createAgentRuntime = deps.createAgentRuntime ?? defaultCreateAgentRuntime;
|
|
346
|
+
this.modelTierRegistry = deps.modelTierRegistry ?? createModelTierRegistry({
|
|
347
|
+
loadConfig: () => {
|
|
348
|
+
const loaded = loadPiWebUiConfig();
|
|
349
|
+
return {
|
|
350
|
+
...(loaded.config.modelTiers === undefined ? {} : { modelTiers: loaded.config.modelTiers }),
|
|
351
|
+
...(loaded.modelTiersError === undefined ? {} : { modelTiersError: loaded.modelTiersError }),
|
|
352
|
+
};
|
|
353
|
+
},
|
|
354
|
+
models: () => this.modelRuntime.getAvailableSnapshot(),
|
|
355
|
+
supportedThinkingLevels: runtimeThinkingLevels,
|
|
356
|
+
});
|
|
329
357
|
this.workspaceActivity = deps.workspaceActivity;
|
|
330
358
|
this.heartbeat = setInterval(() => { this.publishHeartbeats(); }, deps.heartbeatIntervalMs ?? 2000);
|
|
331
359
|
this.commandService = new SessionCommandService((sessionId) => this.getActive(sessionId), (sessionId, text) => this.prompt(sessionId, text, undefined, undefined, { echoUserMessage: false }), events, {
|
|
@@ -522,6 +550,7 @@ export class PiSessionService {
|
|
|
522
550
|
async startSession(cwd, options) {
|
|
523
551
|
const active = await this.create(this.sessionManager.create(cwd, options.parentSession === undefined ? undefined : { parentSession: options.parentSession }), cwd, {
|
|
524
552
|
...(options.initialModel === undefined ? {} : { initialModel: options.initialModel }),
|
|
553
|
+
...(options.initialThinkingLevel === undefined ? {} : { initialThinkingLevel: options.initialThinkingLevel }),
|
|
525
554
|
...(options.creationProvenance === undefined ? {} : { creationProvenance: options.creationProvenance }),
|
|
526
555
|
});
|
|
527
556
|
const { session } = active.runtime;
|
|
@@ -571,9 +600,24 @@ export class PiSessionService {
|
|
|
571
600
|
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd);
|
|
572
601
|
if (!decision.allowed)
|
|
573
602
|
throw spawnTargetError(decision);
|
|
603
|
+
let initialModel = input.model;
|
|
604
|
+
let initialThinkingLevel;
|
|
605
|
+
if (input.tier !== undefined) {
|
|
606
|
+
const echoedTier = leadingTierDirective(input.prompt);
|
|
607
|
+
if (echoedTier !== undefined && echoedTier !== input.tier) {
|
|
608
|
+
throw new Error(`Leading tier directive /tier-${echoedTier} disagrees with typed tier ${input.tier}`);
|
|
609
|
+
}
|
|
610
|
+
const resolved = this.modelTierRegistry.resolve(input.tier);
|
|
611
|
+
if (!isKnownThinkingLevel(resolved.thinkingLevel)) {
|
|
612
|
+
throw new Error(`tier ${input.tier} resolved to unknown thinking level ${resolved.thinkingLevel}`);
|
|
613
|
+
}
|
|
614
|
+
initialModel = resolved.model;
|
|
615
|
+
initialThinkingLevel = resolved.thinkingLevel;
|
|
616
|
+
}
|
|
574
617
|
const created = await this.startSession(decision.cwd, {
|
|
575
618
|
...(input.parentSessionFile === undefined ? {} : { parentSession: input.parentSessionFile }),
|
|
576
|
-
...(
|
|
619
|
+
...(initialModel === undefined ? {} : { initialModel }),
|
|
620
|
+
...(initialThinkingLevel === undefined ? {} : { initialThinkingLevel }),
|
|
577
621
|
creationProvenance: "tracked-subsession",
|
|
578
622
|
});
|
|
579
623
|
const parentSessionFile = nonEmptyString(input.parentSessionFile);
|
|
@@ -1899,6 +1943,7 @@ export class PiSessionService {
|
|
|
1899
1943
|
sessionManager,
|
|
1900
1944
|
delegationToolsEnabled,
|
|
1901
1945
|
...(options.initialModel === undefined ? {} : { initialModel: options.initialModel }),
|
|
1946
|
+
...(options.initialThinkingLevel === undefined ? {} : { initialThinkingLevel: options.initialThinkingLevel }),
|
|
1902
1947
|
});
|
|
1903
1948
|
const active = { runtime, unsubscribe: noop };
|
|
1904
1949
|
let boundSession = runtime.session;
|