laohuang 0.9.5 → 0.9.7
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 +5 -4
- package/dist/bin.js +259 -171
- package/dist/bin.js.map +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npm install -g laohuang
|
|
|
33
33
|
```bash
|
|
34
34
|
laohuang --help
|
|
35
35
|
laohuang --version
|
|
36
|
-
laohuang config set --provider deepseek --model deepseek-
|
|
36
|
+
laohuang config set --provider deepseek --model deepseek-flash
|
|
37
37
|
laohuang doctor
|
|
38
38
|
laohuang update
|
|
39
39
|
```
|
|
@@ -42,9 +42,10 @@ Run `laohuang` in a project directory to start an interactive session. The CLI
|
|
|
42
42
|
loads local project instructions, uses saved provider profiles, and keeps file
|
|
43
43
|
and shell tool execution rooted in the current project.
|
|
44
44
|
|
|
45
|
-
Model providers come from the installed pi-ai API-key catalog. In pi-ai 0.
|
|
46
|
-
the product exposes
|
|
47
|
-
Google Vertex, OAuth-only providers,
|
|
45
|
+
Model providers come from the installed pi-ai API-key catalog. In pi-ai 1.0.0
|
|
46
|
+
the product exposes 38 available chat API-key providers, excluding Amazon Bedrock,
|
|
47
|
+
Google Vertex, OAuth-only providers, OpenAI Codex, and operation-only catalogs.
|
|
48
|
+
`deepseek-flash` is the built-in DeepSeek V4.1 Flash model. Use `/providers` to see
|
|
48
49
|
available, configured, and verified status independently.
|
|
49
50
|
|
|
50
51
|
`/login <provider>` stores API-key credentials, `/logout <provider>` removes
|
package/dist/bin.js
CHANGED
|
@@ -9105,7 +9105,7 @@ function validateToolPairs(selected) {
|
|
|
9105
9105
|
}
|
|
9106
9106
|
|
|
9107
9107
|
// ../../packages/session/session-context/dist/summary-prompt.js
|
|
9108
|
-
var COMPACTION_SYSTEM_PROMPT = "Summarize the conversation for continuation. Preserve user goals, explicit constraints, completed work, modified files, architectural decisions, tool outcomes, failures, current TODOs, and concrete state needed to continue. Do not include credentials. Do not invent results.";
|
|
9108
|
+
var COMPACTION_SYSTEM_PROMPT = "Summarize the conversation for continuation. If the input contains <previous_summary>, merge and update it with the newer conversation instead of discarding it. Preserve user goals, explicit constraints, completed work, modified files, architectural decisions, tool outcomes, failures, current TODOs, and concrete state needed to continue. Do not include credentials. Do not invent results.";
|
|
9109
9109
|
|
|
9110
9110
|
// ../../packages/session/session-context/dist/compaction.js
|
|
9111
9111
|
function selectCompactionPlan(input) {
|
|
@@ -9225,6 +9225,8 @@ var ContextBudgetError = class extends Error {
|
|
|
9225
9225
|
this.name = "ContextBudgetError";
|
|
9226
9226
|
}
|
|
9227
9227
|
};
|
|
9228
|
+
var NoCompactionAvailableError = class extends Error {
|
|
9229
|
+
};
|
|
9228
9230
|
var ContextGovernor = class {
|
|
9229
9231
|
#builder;
|
|
9230
9232
|
#estimator;
|
|
@@ -9245,7 +9247,17 @@ var ContextGovernor = class {
|
|
|
9245
9247
|
throw new ContextBudgetError("context exceeds hard input limit");
|
|
9246
9248
|
return { built, messages: built.messages, compacted: false, tokens };
|
|
9247
9249
|
}
|
|
9248
|
-
|
|
9250
|
+
let result;
|
|
9251
|
+
try {
|
|
9252
|
+
result = await this.compact({ ...input, trigger: "automatic" });
|
|
9253
|
+
} catch (error) {
|
|
9254
|
+
if (!(error instanceof NoCompactionAvailableError))
|
|
9255
|
+
throw error;
|
|
9256
|
+
if (tokens > calculated.hardInputLimit) {
|
|
9257
|
+
throw new ContextBudgetError("context exceeds hard input limit with no earlier messages to compact");
|
|
9258
|
+
}
|
|
9259
|
+
return { built, messages: built.messages, compacted: false, tokens };
|
|
9260
|
+
}
|
|
9249
9261
|
const entries = [...input.entries, result.entry];
|
|
9250
9262
|
built = this.#builder.build({ ...input, entries });
|
|
9251
9263
|
tokens = this.measure({ ...input, entries }, built);
|
|
@@ -9256,29 +9268,32 @@ var ContextGovernor = class {
|
|
|
9256
9268
|
}
|
|
9257
9269
|
async compact(input) {
|
|
9258
9270
|
const calculated = calculateModelBudget({ budget: input.budget, policy: input.policy });
|
|
9271
|
+
const built = this.#builder.build(input);
|
|
9272
|
+
const activeCompaction = input.entries.find((entry) => entry.id === built.activeCompactionId && entry.entryType === "compaction");
|
|
9273
|
+
const activeSourceEntryIds = new Set(built.sourceEntryIds);
|
|
9274
|
+
const projectedEntries = activeCompaction === void 0 ? input.entries : input.entries.filter((entry) => entry.id !== activeCompaction.id && activeSourceEntryIds.has(entry.id));
|
|
9259
9275
|
const plan = selectCompactionPlan({
|
|
9260
9276
|
protectedFromSeq: input.protectedFromSeq,
|
|
9261
|
-
entries:
|
|
9277
|
+
entries: projectedEntries,
|
|
9262
9278
|
// A search preflight reserves the pending call/result/definition unit.
|
|
9263
9279
|
// Keep the latest completed unit and summarize older units to make room.
|
|
9264
9280
|
retainTokens: input.reserveTokens === void 0 ? calculated.retainTokens : 1,
|
|
9265
9281
|
estimator: this.#estimator
|
|
9266
9282
|
});
|
|
9267
|
-
if (
|
|
9268
|
-
|
|
9283
|
+
if (plan.summarizedEntries.length === 0) {
|
|
9284
|
+
if (input.trigger === "manual")
|
|
9285
|
+
throw new Error("No messages to compact in current history.");
|
|
9286
|
+
if (input.trigger === "provider_overflow") {
|
|
9287
|
+
throw new ContextBudgetError("No earlier messages to compact after provider overflow.");
|
|
9288
|
+
}
|
|
9289
|
+
throw new NoCompactionAvailableError();
|
|
9269
9290
|
}
|
|
9270
|
-
const summarizedFromSeq = plan.summarizedEntries[0]?.seq ?? 1;
|
|
9271
|
-
const summarizedThroughSeq = plan.summarizedEntries.at(-1)?.seq ?? Math.max(0, summarizedFromSeq - 1);
|
|
9272
|
-
const built = this.#builder.build(input);
|
|
9273
|
-
const activeCompaction = input.entries.find((entry) => entry.id === built.activeCompactionId);
|
|
9291
|
+
const summarizedFromSeq = activeCompaction?.payload.summarizedFromSeq ?? plan.summarizedEntries[0]?.seq ?? 1;
|
|
9292
|
+
const summarizedThroughSeq = plan.summarizedEntries.at(-1)?.seq ?? activeCompaction?.payload.summarizedThroughSeq ?? Math.max(0, summarizedFromSeq - 1);
|
|
9274
9293
|
const messagesBefore = built.messages;
|
|
9275
9294
|
const tokensBefore = this.#estimator.estimateMessages(messagesBefore) + this.#estimator.estimateTools(input.projectTools?.(messagesBefore) ?? input.tools);
|
|
9276
|
-
const summary =
|
|
9277
|
-
|
|
9278
|
-
inputTokens: 0,
|
|
9279
|
-
outputTokens: 0
|
|
9280
|
-
} : await this.#summarize({
|
|
9281
|
-
serialized: serializeConversation(plan.summarizedEntries),
|
|
9295
|
+
const summary = await this.#summarize({
|
|
9296
|
+
serialized: serializeCompactionInput(activeCompaction?.payload.summary, plan.summarizedEntries),
|
|
9282
9297
|
maxSummaryTokens: input.policy.maxSummaryTokens
|
|
9283
9298
|
});
|
|
9284
9299
|
const payload = {
|
|
@@ -9306,10 +9321,13 @@ var ContextGovernor = class {
|
|
|
9306
9321
|
throw error;
|
|
9307
9322
|
}
|
|
9308
9323
|
}
|
|
9309
|
-
|
|
9310
|
-
prepared = await this.prepare({ ...input, entries: [...input.entries, compaction.entry] });
|
|
9324
|
+
prepared = await this.recoverAfterOverflow(input);
|
|
9311
9325
|
return await send(prepared);
|
|
9312
9326
|
}
|
|
9327
|
+
async recoverAfterOverflow(input) {
|
|
9328
|
+
const compaction = await this.compact({ ...input, trigger: "provider_overflow" });
|
|
9329
|
+
return this.prepare({ ...input, entries: [...input.entries, compaction.entry] });
|
|
9330
|
+
}
|
|
9313
9331
|
measure(input, built) {
|
|
9314
9332
|
const tools = input.projectTools?.(built.messages) ?? input.tools;
|
|
9315
9333
|
return this.#estimator.measure({
|
|
@@ -9325,6 +9343,15 @@ var ContextGovernor = class {
|
|
|
9325
9343
|
}).totalTokens + (input.reserveTokens ?? 0);
|
|
9326
9344
|
}
|
|
9327
9345
|
};
|
|
9346
|
+
function serializeCompactionInput(previousSummary, entries) {
|
|
9347
|
+
const conversation = serializeConversation(entries);
|
|
9348
|
+
if (previousSummary === void 0)
|
|
9349
|
+
return conversation;
|
|
9350
|
+
return `<previous_summary>
|
|
9351
|
+
${previousSummary}
|
|
9352
|
+
</previous_summary>
|
|
9353
|
+
${conversation}`;
|
|
9354
|
+
}
|
|
9328
9355
|
function calculateModelBudget(input) {
|
|
9329
9356
|
const hardInputLimit = input.budget.contextWindow - input.budget.maxOutputTokens;
|
|
9330
9357
|
const safetyTokens = Math.ceil(input.budget.contextWindow * input.policy.safetyRatio);
|
|
@@ -9513,8 +9540,22 @@ function parseToolArguments(raw) {
|
|
|
9513
9540
|
kind: "protocol"
|
|
9514
9541
|
});
|
|
9515
9542
|
}
|
|
9543
|
+
if (!Object.values(parsed).every(isJsonValue)) {
|
|
9544
|
+
throw new ModelError("tool-call arguments contain invalid JSON values", {
|
|
9545
|
+
kind: "protocol"
|
|
9546
|
+
});
|
|
9547
|
+
}
|
|
9516
9548
|
return parsed;
|
|
9517
9549
|
}
|
|
9550
|
+
function isJsonValue(value) {
|
|
9551
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
9552
|
+
return true;
|
|
9553
|
+
if (typeof value === "number")
|
|
9554
|
+
return Number.isFinite(value);
|
|
9555
|
+
if (Array.isArray(value))
|
|
9556
|
+
return value.every(isJsonValue);
|
|
9557
|
+
return isRecord(value) && Object.values(value).every(isJsonValue);
|
|
9558
|
+
}
|
|
9518
9559
|
function zeroUsage() {
|
|
9519
9560
|
return {
|
|
9520
9561
|
input: 0,
|
|
@@ -9952,7 +9993,10 @@ var PiModelsStore = class {
|
|
|
9952
9993
|
return void 0;
|
|
9953
9994
|
}
|
|
9954
9995
|
return {
|
|
9955
|
-
models: entry.models.
|
|
9996
|
+
models: entry.models.flatMap((model2) => {
|
|
9997
|
+
const restored = restoreModel(providerId, model2);
|
|
9998
|
+
return restored === void 0 ? [] : [restored];
|
|
9999
|
+
}),
|
|
9956
10000
|
...entry.lastModified === void 0 ? {} : { lastModified: entry.lastModified },
|
|
9957
10001
|
...entry.checkedAt === void 0 ? {} : { checkedAt: entry.checkedAt },
|
|
9958
10002
|
...entry.etag === void 0 ? {} : { etag: entry.etag }
|
|
@@ -9971,12 +10015,15 @@ var PiModelsStore = class {
|
|
|
9971
10015
|
}
|
|
9972
10016
|
};
|
|
9973
10017
|
function restoreModel(providerId, model2) {
|
|
10018
|
+
const type = model2["type"] ?? "chat";
|
|
10019
|
+
if (type !== "chat" && type !== "image" && type !== "classifier")
|
|
10020
|
+
return void 0;
|
|
9974
10021
|
const id = readString(model2, "id", providerId);
|
|
9975
10022
|
const name = readString(model2, "name", providerId);
|
|
9976
10023
|
const api2 = readString(model2, "api", providerId);
|
|
9977
10024
|
const provider2 = readString(model2, "provider", providerId);
|
|
9978
10025
|
const input = model2["input"];
|
|
9979
|
-
if (!Array.isArray(input) || !input.every((item) =>
|
|
10026
|
+
if (!Array.isArray(input) || !input.every((item) => item === "text" || item === "image")) {
|
|
9980
10027
|
throw new ModelError(`stored model for '${providerId}' has invalid input`, {
|
|
9981
10028
|
kind: "protocol"
|
|
9982
10029
|
});
|
|
@@ -9987,6 +10034,27 @@ function restoreModel(providerId, model2) {
|
|
|
9987
10034
|
kind: "protocol"
|
|
9988
10035
|
});
|
|
9989
10036
|
}
|
|
10037
|
+
const common = {
|
|
10038
|
+
...model2,
|
|
10039
|
+
id,
|
|
10040
|
+
name,
|
|
10041
|
+
api: api2,
|
|
10042
|
+
provider: provider2,
|
|
10043
|
+
input,
|
|
10044
|
+
cost: cost2,
|
|
10045
|
+
baseUrl: readString(model2, "baseUrl", providerId)
|
|
10046
|
+
};
|
|
10047
|
+
if (type === "image") {
|
|
10048
|
+
const output = model2["output"];
|
|
10049
|
+
if (!Array.isArray(output) || !output.includes("image") || !output.every((item) => item === "text" || item === "image")) {
|
|
10050
|
+
throw new ModelError(`stored model for '${providerId}' has invalid output`, { kind: "protocol" });
|
|
10051
|
+
}
|
|
10052
|
+
return { ...common, type, api: api2, output };
|
|
10053
|
+
}
|
|
10054
|
+
const contextWindow = readPositiveNumber(model2, "contextWindow", providerId);
|
|
10055
|
+
if (type === "classifier") {
|
|
10056
|
+
return { ...common, type, api: api2, contextWindow };
|
|
10057
|
+
}
|
|
9990
10058
|
const reasoning = model2["reasoning"];
|
|
9991
10059
|
if (typeof reasoning !== "boolean") {
|
|
9992
10060
|
throw new ModelError(`stored model for '${providerId}' has invalid reasoning`, {
|
|
@@ -9994,15 +10062,13 @@ function restoreModel(providerId, model2) {
|
|
|
9994
10062
|
});
|
|
9995
10063
|
}
|
|
9996
10064
|
return {
|
|
9997
|
-
...
|
|
9998
|
-
|
|
9999
|
-
name,
|
|
10065
|
+
...common,
|
|
10066
|
+
type,
|
|
10000
10067
|
api: api2,
|
|
10001
|
-
provider: provider2,
|
|
10002
10068
|
reasoning,
|
|
10003
10069
|
input,
|
|
10004
10070
|
cost: cost2,
|
|
10005
|
-
contextWindow
|
|
10071
|
+
contextWindow,
|
|
10006
10072
|
maxTokens: readPositiveNumber(model2, "maxTokens", providerId)
|
|
10007
10073
|
};
|
|
10008
10074
|
}
|
|
@@ -10036,11 +10102,11 @@ function isCost(value) {
|
|
|
10036
10102
|
}
|
|
10037
10103
|
|
|
10038
10104
|
// ../../packages/llm/llm-pi-ai/dist/platform.js
|
|
10039
|
-
import { defaultProviderAuthContext, getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
10105
|
+
import { defaultProviderAuthContext, getSupportedThinkingLevels, isModelType as isModelType2 } from "@earendil-works/pi-ai";
|
|
10040
10106
|
import { builtinModels as builtinModels2 } from "@earendil-works/pi-ai/providers/all";
|
|
10041
10107
|
|
|
10042
10108
|
// ../../packages/llm/llm-pi-ai/dist/custom-models.js
|
|
10043
|
-
import { createProvider, envApiKeyAuth } from "@earendil-works/pi-ai";
|
|
10109
|
+
import { createProvider, envApiKeyAuth, isModelType } from "@earendil-works/pi-ai";
|
|
10044
10110
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
10045
10111
|
import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
|
|
10046
10112
|
import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
|
|
@@ -17866,6 +17932,8 @@ var completionsCompat = partial(typebox_exports.Object({
|
|
|
17866
17932
|
supportsOpenAIGrammarTools: typebox_exports.Boolean(),
|
|
17867
17933
|
supportsStrictMode: typebox_exports.Boolean(),
|
|
17868
17934
|
cacheControlFormat: typebox_exports.Literal("anthropic"),
|
|
17935
|
+
supportsMidConvoSystemMessages: typebox_exports.Boolean(),
|
|
17936
|
+
supportsMidConvoToolAdditions: typebox_exports.Boolean(),
|
|
17869
17937
|
sendSessionAffinityHeaders: typebox_exports.Boolean(),
|
|
17870
17938
|
deferredToolsMode: typebox_exports.Literal("kimi"),
|
|
17871
17939
|
sessionAffinityFormat: affinity,
|
|
@@ -17875,6 +17943,7 @@ var completionsCompat = partial(typebox_exports.Object({
|
|
|
17875
17943
|
var responsesCompat = partial(typebox_exports.Object({
|
|
17876
17944
|
supportsDeveloperRole: typebox_exports.Boolean(),
|
|
17877
17945
|
sessionAffinityFormat: affinity,
|
|
17946
|
+
supportsMidConvoSystemMessages: typebox_exports.Boolean(),
|
|
17878
17947
|
supportsLongCacheRetention: typebox_exports.Boolean(),
|
|
17879
17948
|
supportsStrictMode: typebox_exports.Boolean(),
|
|
17880
17949
|
supportsOpenAIGrammarTools: typebox_exports.Boolean(),
|
|
@@ -17894,6 +17963,9 @@ var anthropicCompat = partial(typebox_exports.Object({
|
|
|
17894
17963
|
supportsStrictTools: typebox_exports.Boolean(),
|
|
17895
17964
|
supportsMidConvoEffort: typebox_exports.Boolean(),
|
|
17896
17965
|
supportsToolReferences: typebox_exports.Boolean(),
|
|
17966
|
+
supportsMidConvoSystemMessages: typebox_exports.Boolean(),
|
|
17967
|
+
supportsMidConvoToolChanges: typebox_exports.Boolean(),
|
|
17968
|
+
sessionAffinityFormat: typebox_exports.Literal("openrouter"),
|
|
17897
17969
|
allowedFallbackModels: typebox_exports.Array(typebox_exports.Object({ provider: text, model: text, cost }, { additionalProperties: false }))
|
|
17898
17970
|
}, { additionalProperties: false }));
|
|
17899
17971
|
var api = typebox_exports.Union([
|
|
@@ -18066,6 +18138,10 @@ function customProvider(id, config, base) {
|
|
|
18066
18138
|
baseUrl: config.baseUrl ?? base?.baseUrl,
|
|
18067
18139
|
headers: { ...base?.headers, ...config.headers },
|
|
18068
18140
|
getModels,
|
|
18141
|
+
getAllModels: () => [
|
|
18142
|
+
...getModels(),
|
|
18143
|
+
...(base?.getAllModels?.() ?? []).filter((model2) => !isModelType(model2, "chat"))
|
|
18144
|
+
],
|
|
18069
18145
|
// Keep provider-owned refresh/cache logic; getModels reapplies user overrides afterwards.
|
|
18070
18146
|
...base?.filterModels === void 0 ? {} : {
|
|
18071
18147
|
filterModels: (models, credential) => {
|
|
@@ -18073,6 +18149,12 @@ function customProvider(id, config, base) {
|
|
|
18073
18149
|
return models.filter((model2) => available.has(model2.id) || additions.has(model2.id));
|
|
18074
18150
|
}
|
|
18075
18151
|
},
|
|
18152
|
+
...base?.filterAllModels === void 0 ? {} : {
|
|
18153
|
+
filterAllModels: (models, credential) => {
|
|
18154
|
+
const available = new Set(base.filterAllModels(models, credential).map((model2) => `${model2.type ?? "chat"}:${model2.id}`));
|
|
18155
|
+
return models.filter((model2) => available.has(`${model2.type ?? "chat"}:${model2.id}`) || isModelType(model2, "chat") && additions.has(model2.id));
|
|
18156
|
+
}
|
|
18157
|
+
},
|
|
18076
18158
|
stream: (model2, context, options) => dispatch(model2).stream(model2, context, options),
|
|
18077
18159
|
streamSimple: (model2, context, options) => dispatch(model2).streamSimple(model2, context, options)
|
|
18078
18160
|
};
|
|
@@ -18183,7 +18265,9 @@ var PiAiPlatform = class {
|
|
|
18183
18265
|
}
|
|
18184
18266
|
};
|
|
18185
18267
|
function isEligibleProvider(provider2, excludedProviderIds) {
|
|
18186
|
-
|
|
18268
|
+
const allModels = provider2.getAllModels?.() ?? provider2.getModels();
|
|
18269
|
+
return provider2.auth.apiKey?.login !== void 0 && !excludedProviderIds.has(provider2.id) && // Empty dynamic catalogs remain eligible; operation-only catalogs do not.
|
|
18270
|
+
(allModels.length === 0 || allModels.some((model2) => isModelType2(model2, "chat")));
|
|
18187
18271
|
}
|
|
18188
18272
|
var PiAiCatalog = class {
|
|
18189
18273
|
models;
|
|
@@ -32495,7 +32579,7 @@ var AgentStepRunner = class {
|
|
|
32495
32579
|
}
|
|
32496
32580
|
modelRound += 1;
|
|
32497
32581
|
modelRequests += 1;
|
|
32498
|
-
|
|
32582
|
+
let requestId = modelRound === 1 && this.options.requestId !== null ? this.options.requestId : randomUUID10();
|
|
32499
32583
|
this.options.onRequestId(requestId);
|
|
32500
32584
|
const registry = this.options.getTools();
|
|
32501
32585
|
const selection = this.options.selection ?? new ToolSelection();
|
|
@@ -32533,96 +32617,137 @@ var AgentStepRunner = class {
|
|
|
32533
32617
|
throw error;
|
|
32534
32618
|
}
|
|
32535
32619
|
}, () => history.snapshot().map((message) => message.role === "system" || message.role === "user" ? message : {}));
|
|
32536
|
-
|
|
32537
|
-
|
|
32538
|
-
|
|
32539
|
-
|
|
32540
|
-
|
|
32541
|
-
|
|
32542
|
-
|
|
32543
|
-
|
|
32544
|
-
|
|
32545
|
-
|
|
32546
|
-
|
|
32547
|
-
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
|
|
32551
|
-
|
|
32552
|
-
|
|
32553
|
-
|
|
32554
|
-
|
|
32555
|
-
|
|
32556
|
-
|
|
32557
|
-
|
|
32558
|
-
|
|
32559
|
-
|
|
32560
|
-
|
|
32561
|
-
}
|
|
32562
|
-
remainingTokens = prepared.hardInputLimit === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, prepared.hardInputLimit - (prepared.contextTokens ?? 0) - 1024);
|
|
32563
|
-
const tools = selected.view;
|
|
32564
|
-
const definitions = tools.definitions;
|
|
32565
|
-
const requestMessages = [...prepared.messages];
|
|
32566
|
-
this.emit("model_request", {
|
|
32567
|
-
round: modelRound,
|
|
32568
|
-
request_id: requestId,
|
|
32569
|
-
message_count: requestMessages.length,
|
|
32570
|
-
tool_rounds: toolRounds,
|
|
32571
|
-
model_requests: modelRequests,
|
|
32572
|
-
total_tokens: totalTokens,
|
|
32573
|
-
...prepared.contextTokens === void 0 ? {} : { context_tokens: prepared.contextTokens },
|
|
32574
|
-
...prepared.contextWindow === void 0 ? {} : { context_window: prepared.contextWindow }
|
|
32575
|
-
});
|
|
32576
|
-
let result;
|
|
32577
|
-
try {
|
|
32578
|
-
const modelRequestOpened = context?.modelRequestOpened;
|
|
32579
|
-
result = await this.options.modelRuntime.complete({
|
|
32580
|
-
provider: this.options.provider,
|
|
32581
|
-
model: this.options.model,
|
|
32582
|
-
...this.options.baseUrl === null ? {} : { baseUrl: this.options.baseUrl },
|
|
32583
|
-
messages: requestMessages,
|
|
32584
|
-
tools: definitions,
|
|
32585
|
-
reasoningEffort: this.options.getReasoningEffort(),
|
|
32586
|
-
requestId,
|
|
32587
|
-
cancelToken,
|
|
32588
|
-
isRequestActive: this.options.isRequestActive,
|
|
32589
|
-
onDelta: (kind, payload) => {
|
|
32590
|
-
this.emit(kind, { round: modelRound, request_id: requestId, ...payload });
|
|
32591
|
-
},
|
|
32592
|
-
onRequestOpened: () => {
|
|
32593
|
-
if (modelRequestOpened?.call(context) === false)
|
|
32594
|
-
return false;
|
|
32595
|
-
this.options.resources?.committed(pendingResourceInputs);
|
|
32596
|
-
pendingResourceInputs = [];
|
|
32597
|
-
return true;
|
|
32620
|
+
const prepareRequest = async () => {
|
|
32621
|
+
let selected2 = select(history.snapshot());
|
|
32622
|
+
if (selected2.announcement)
|
|
32623
|
+
history.commitToolContext({ role: "user", ...selected2.announcement });
|
|
32624
|
+
let prepared2 = { messages: history.snapshot() };
|
|
32625
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
32626
|
+
prepared2 = await this.options.contextGovernor?.prepare({
|
|
32627
|
+
messages: history.snapshot(),
|
|
32628
|
+
tools: selected2.view.definitions,
|
|
32629
|
+
provider: this.options.provider,
|
|
32630
|
+
model: this.options.model,
|
|
32631
|
+
projectTools: (messages) => select(messages).view.definitions
|
|
32632
|
+
}) ?? { messages: history.snapshot() };
|
|
32633
|
+
history.retain(prepared2.messages);
|
|
32634
|
+
selected2 = select(prepared2.messages);
|
|
32635
|
+
const resources = await this.options.resources?.prepareContext(prepared2.messages, cancelToken?.signal) ?? [];
|
|
32636
|
+
if (!selected2.announcement && resources.length === 0)
|
|
32637
|
+
break;
|
|
32638
|
+
if (attempt === 2)
|
|
32639
|
+
throw this.options.createError("Resource catalog cannot fit the retained context.");
|
|
32640
|
+
if (selected2.announcement)
|
|
32641
|
+
history.commitToolContext({ role: "user", ...selected2.announcement });
|
|
32642
|
+
for (const message of resources) {
|
|
32643
|
+
history.commitSkillContext(message);
|
|
32644
|
+
this.options.resources?.committed([message]);
|
|
32598
32645
|
}
|
|
32646
|
+
}
|
|
32647
|
+
remainingTokens = prepared2.hardInputLimit === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, prepared2.hardInputLimit - (prepared2.contextTokens ?? 0) - 1024);
|
|
32648
|
+
return { selected: selected2, prepared: prepared2 };
|
|
32649
|
+
};
|
|
32650
|
+
let { selected, prepared } = await prepareRequest();
|
|
32651
|
+
let tools = selected.view;
|
|
32652
|
+
let definitions = tools.definitions;
|
|
32653
|
+
let requestMessages = [...prepared.messages];
|
|
32654
|
+
let result;
|
|
32655
|
+
for (let overflowAttempt = 0; ; overflowAttempt++) {
|
|
32656
|
+
const attemptRequestId = requestId;
|
|
32657
|
+
const attemptRound = modelRound;
|
|
32658
|
+
this.emit("model_request", {
|
|
32659
|
+
round: attemptRound,
|
|
32660
|
+
request_id: attemptRequestId,
|
|
32661
|
+
message_count: requestMessages.length,
|
|
32662
|
+
tool_rounds: toolRounds,
|
|
32663
|
+
model_requests: modelRequests,
|
|
32664
|
+
total_tokens: totalTokens,
|
|
32665
|
+
...prepared.contextTokens === void 0 ? {} : { context_tokens: prepared.contextTokens },
|
|
32666
|
+
...prepared.contextWindow === void 0 ? {} : { context_window: prepared.contextWindow }
|
|
32599
32667
|
});
|
|
32600
|
-
|
|
32601
|
-
|
|
32602
|
-
this.
|
|
32603
|
-
|
|
32604
|
-
|
|
32605
|
-
|
|
32668
|
+
try {
|
|
32669
|
+
const modelRequestOpened = context?.modelRequestOpened;
|
|
32670
|
+
result = await this.options.modelRuntime.complete({
|
|
32671
|
+
provider: this.options.provider,
|
|
32672
|
+
model: this.options.model,
|
|
32673
|
+
...this.options.baseUrl === null ? {} : { baseUrl: this.options.baseUrl },
|
|
32674
|
+
messages: requestMessages,
|
|
32675
|
+
tools: definitions,
|
|
32676
|
+
reasoningEffort: this.options.getReasoningEffort(),
|
|
32677
|
+
requestId: attemptRequestId,
|
|
32678
|
+
cancelToken,
|
|
32679
|
+
isRequestActive: this.options.isRequestActive,
|
|
32680
|
+
onDelta: (kind, payload) => {
|
|
32681
|
+
this.emit(kind, { round: attemptRound, request_id: attemptRequestId, ...payload });
|
|
32682
|
+
},
|
|
32683
|
+
onRequestOpened: () => {
|
|
32684
|
+
if (modelRequestOpened?.call(context) === false)
|
|
32685
|
+
return false;
|
|
32686
|
+
this.options.resources?.committed(pendingResourceInputs);
|
|
32687
|
+
pendingResourceInputs = [];
|
|
32688
|
+
return true;
|
|
32689
|
+
}
|
|
32606
32690
|
});
|
|
32607
|
-
|
|
32608
|
-
}
|
|
32609
|
-
|
|
32610
|
-
|
|
32611
|
-
|
|
32612
|
-
|
|
32613
|
-
|
|
32614
|
-
|
|
32615
|
-
|
|
32616
|
-
|
|
32617
|
-
|
|
32618
|
-
|
|
32619
|
-
|
|
32620
|
-
|
|
32621
|
-
|
|
32622
|
-
|
|
32691
|
+
break;
|
|
32692
|
+
} catch (error) {
|
|
32693
|
+
if (error instanceof ModelStreamCancelled) {
|
|
32694
|
+
this.emit("model_response_aborted", {
|
|
32695
|
+
round: attemptRound,
|
|
32696
|
+
request_id: attemptRequestId,
|
|
32697
|
+
reason: errorMessage10(error)
|
|
32698
|
+
});
|
|
32699
|
+
throw this.options.createCancelled(errorMessage10(error), error);
|
|
32700
|
+
}
|
|
32701
|
+
const errorPayload = {
|
|
32702
|
+
round: attemptRound,
|
|
32703
|
+
request_id: attemptRequestId,
|
|
32704
|
+
error: errorMessage10(error)
|
|
32705
|
+
};
|
|
32706
|
+
if (error instanceof ModelStreamError && error.hadDelta) {
|
|
32707
|
+
this.emit("model_response_aborted", errorPayload);
|
|
32708
|
+
this.options.emitLegacy("model_error", errorPayload);
|
|
32709
|
+
} else {
|
|
32710
|
+
this.emit("model_error", errorPayload);
|
|
32711
|
+
}
|
|
32712
|
+
if (overflowAttempt === 0 && error instanceof ModelError && error.kind === "context_overflow" && !error.hadDelta && this.options.contextGovernor?.recoverAfterOverflow !== void 0) {
|
|
32713
|
+
this.raiseIfCancelled();
|
|
32714
|
+
const recovered = await this.options.contextGovernor.recoverAfterOverflow({
|
|
32715
|
+
messages: history.snapshot(),
|
|
32716
|
+
tools: definitions,
|
|
32717
|
+
provider: this.options.provider,
|
|
32718
|
+
model: this.options.model,
|
|
32719
|
+
projectTools: (messages) => select(messages).view.definitions
|
|
32720
|
+
});
|
|
32721
|
+
this.raiseIfCancelled();
|
|
32722
|
+
history.retain(recovered.messages);
|
|
32723
|
+
remainingTokens = Number.POSITIVE_INFINITY;
|
|
32724
|
+
preflightCompacted = false;
|
|
32725
|
+
({ selected, prepared } = await prepareRequest());
|
|
32726
|
+
this.raiseIfCancelled();
|
|
32727
|
+
tools = selected.view;
|
|
32728
|
+
definitions = tools.definitions;
|
|
32729
|
+
requestMessages = [...prepared.messages];
|
|
32730
|
+
this.emit("model_retry_scheduled", {
|
|
32731
|
+
round: attemptRound,
|
|
32732
|
+
request_id: attemptRequestId,
|
|
32733
|
+
attempt: 2,
|
|
32734
|
+
max_attempts: 2,
|
|
32735
|
+
delay_ms: 0,
|
|
32736
|
+
error_kind: "context_overflow"
|
|
32737
|
+
});
|
|
32738
|
+
modelRound += 1;
|
|
32739
|
+
modelRequests += 1;
|
|
32740
|
+
requestId = randomUUID10();
|
|
32741
|
+
this.options.onRequestId(requestId);
|
|
32742
|
+
continue;
|
|
32743
|
+
}
|
|
32744
|
+
let message = `Model request failed: ${errorMessage10(error)}`;
|
|
32745
|
+
if (this.options.provider && modelErrorKind(error) === "authentication") {
|
|
32746
|
+
message += `
|
|
32623
32747
|
Authentication failed for ${this.options.provider}. Run /login ${this.options.provider} to update your API key.`;
|
|
32748
|
+
}
|
|
32749
|
+
throw this.options.createError(message, error);
|
|
32624
32750
|
}
|
|
32625
|
-
throw this.options.createError(message, error);
|
|
32626
32751
|
}
|
|
32627
32752
|
const toolCalls = toolCallsOf(result.message);
|
|
32628
32753
|
let requestTokens = usageTotalTokens(result.usage);
|
|
@@ -33520,6 +33645,25 @@ function createSessionRuntime(options) {
|
|
|
33520
33645
|
contextWindow: selectedModel.contextWindow,
|
|
33521
33646
|
hardInputLimit: selectedModel.contextWindow - selectedModel.maxTokens
|
|
33522
33647
|
};
|
|
33648
|
+
},
|
|
33649
|
+
recoverAfterOverflow: async ({ tools, projectTools: projectTools2 }) => {
|
|
33650
|
+
const history = activeHistory(options.sessionController);
|
|
33651
|
+
const prepared = await createGovernor(history).recoverAfterOverflow({
|
|
33652
|
+
protectedFromSeq: attachmentTurnStart,
|
|
33653
|
+
entries: history.entries(),
|
|
33654
|
+
currentProvider: route.provider,
|
|
33655
|
+
currentModel: route.model,
|
|
33656
|
+
tools,
|
|
33657
|
+
projectTools: projectTools2,
|
|
33658
|
+
budget: modelBudget(selectedModel),
|
|
33659
|
+
policy: defaultContextPolicy()
|
|
33660
|
+
});
|
|
33661
|
+
return {
|
|
33662
|
+
messages: prepared.messages,
|
|
33663
|
+
contextTokens: prepared.tokens,
|
|
33664
|
+
contextWindow: selectedModel.contextWindow,
|
|
33665
|
+
hardInputLimit: selectedModel.contextWindow - selectedModel.maxTokens
|
|
33666
|
+
};
|
|
33523
33667
|
}
|
|
33524
33668
|
}
|
|
33525
33669
|
});
|
|
@@ -38703,64 +38847,7 @@ async function handleCommandResult(presenter, suggestCommand, result) {
|
|
|
38703
38847
|
return true;
|
|
38704
38848
|
}
|
|
38705
38849
|
async function runSessionRepl(session, options) {
|
|
38706
|
-
|
|
38707
|
-
return runPersistentSessionRepl(session, options);
|
|
38708
|
-
}
|
|
38709
|
-
return runClassicSessionRepl(session, options);
|
|
38710
|
-
}
|
|
38711
|
-
async function runClassicSessionRepl(session, options) {
|
|
38712
|
-
const { commandHandler, presenter, suggestCommand, ui } = options;
|
|
38713
|
-
ui.startEventRenderer?.();
|
|
38714
|
-
ui.showWelcome?.();
|
|
38715
|
-
let cleanShutdown = false;
|
|
38716
|
-
try {
|
|
38717
|
-
for (; ; ) {
|
|
38718
|
-
let userInput;
|
|
38719
|
-
try {
|
|
38720
|
-
const prompt = ui.prompt;
|
|
38721
|
-
if (typeof prompt !== "function") {
|
|
38722
|
-
throw new Error("Classic session UI requires a prompt function.");
|
|
38723
|
-
}
|
|
38724
|
-
userInput = await prompt.call(ui);
|
|
38725
|
-
if (!/^\s*\/skill:/.test(userInput)) userInput = userInput.trim();
|
|
38726
|
-
} catch (error) {
|
|
38727
|
-
if (isEofError(error)) {
|
|
38728
|
-
break;
|
|
38729
|
-
}
|
|
38730
|
-
if (isInterruptedError(error)) {
|
|
38731
|
-
presentNotice(presenter, "Interrupted.", "warning");
|
|
38732
|
-
continue;
|
|
38733
|
-
}
|
|
38734
|
-
throw error;
|
|
38735
|
-
}
|
|
38736
|
-
const keepGoing = await handleSessionInput(
|
|
38737
|
-
session,
|
|
38738
|
-
commandHandler,
|
|
38739
|
-
presenter,
|
|
38740
|
-
suggestCommand,
|
|
38741
|
-
userInput
|
|
38742
|
-
);
|
|
38743
|
-
if (!keepGoing) {
|
|
38744
|
-
break;
|
|
38745
|
-
}
|
|
38746
|
-
}
|
|
38747
|
-
} finally {
|
|
38748
|
-
cleanShutdown = await session.close({ wait: true, timeoutMs: 1e4 });
|
|
38749
|
-
if (cleanShutdown) {
|
|
38750
|
-
await session.eventBus.flush();
|
|
38751
|
-
ui.flushEventRenderer?.();
|
|
38752
|
-
}
|
|
38753
|
-
ui.stopEventRenderer?.();
|
|
38754
|
-
}
|
|
38755
|
-
if (cleanShutdown) {
|
|
38756
|
-
ui.showGoodbye?.();
|
|
38757
|
-
} else {
|
|
38758
|
-
presenter.notice({
|
|
38759
|
-
text: "Task worker did not stop before the shutdown timeout.",
|
|
38760
|
-
tone: "error"
|
|
38761
|
-
});
|
|
38762
|
-
}
|
|
38763
|
-
return cleanShutdown;
|
|
38850
|
+
return runPersistentSessionRepl(session, options);
|
|
38764
38851
|
}
|
|
38765
38852
|
function startCoordinator(session, commandHandler, presenter, suggestCommand) {
|
|
38766
38853
|
const queue = [];
|
|
@@ -39767,6 +39854,7 @@ async function main(argv, options = {}) {
|
|
|
39767
39854
|
let attachmentRuntime;
|
|
39768
39855
|
try {
|
|
39769
39856
|
modelPlatform = await createPiAiPlatform({
|
|
39857
|
+
// 附件运行时稍后才创建,通过回调取得届时可用的存储实例。
|
|
39770
39858
|
attachments: { store: () => attachmentRuntime?.store },
|
|
39771
39859
|
credentials,
|
|
39772
39860
|
modelCatalogStore,
|