laohuang 0.9.4 → 0.9.6

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/bin.js CHANGED
@@ -9264,8 +9264,12 @@ var ContextGovernor = class {
9264
9264
  retainTokens: input.reserveTokens === void 0 ? calculated.retainTokens : 1,
9265
9265
  estimator: this.#estimator
9266
9266
  });
9267
- if (input.trigger === "manual" && plan.summarizedEntries.length === 0) {
9268
- throw new Error("No messages to compact in current history.");
9267
+ if (plan.summarizedEntries.length === 0) {
9268
+ if (input.trigger === "manual")
9269
+ throw new Error("No messages to compact in current history.");
9270
+ if (input.trigger === "provider_overflow") {
9271
+ throw new ContextBudgetError("No earlier messages to compact after provider overflow.");
9272
+ }
9269
9273
  }
9270
9274
  const summarizedFromSeq = plan.summarizedEntries[0]?.seq ?? 1;
9271
9275
  const summarizedThroughSeq = plan.summarizedEntries.at(-1)?.seq ?? Math.max(0, summarizedFromSeq - 1);
@@ -9306,10 +9310,13 @@ var ContextGovernor = class {
9306
9310
  throw error;
9307
9311
  }
9308
9312
  }
9309
- const compaction = await this.compact({ ...input, trigger: "provider_overflow" });
9310
- prepared = await this.prepare({ ...input, entries: [...input.entries, compaction.entry] });
9313
+ prepared = await this.recoverAfterOverflow(input);
9311
9314
  return await send(prepared);
9312
9315
  }
9316
+ async recoverAfterOverflow(input) {
9317
+ const compaction = await this.compact({ ...input, trigger: "provider_overflow" });
9318
+ return this.prepare({ ...input, entries: [...input.entries, compaction.entry] });
9319
+ }
9313
9320
  measure(input, built) {
9314
9321
  const tools = input.projectTools?.(built.messages) ?? input.tools;
9315
9322
  return this.#estimator.measure({
@@ -32495,7 +32502,7 @@ var AgentStepRunner = class {
32495
32502
  }
32496
32503
  modelRound += 1;
32497
32504
  modelRequests += 1;
32498
- const requestId = modelRound === 1 && this.options.requestId !== null ? this.options.requestId : randomUUID10();
32505
+ let requestId = modelRound === 1 && this.options.requestId !== null ? this.options.requestId : randomUUID10();
32499
32506
  this.options.onRequestId(requestId);
32500
32507
  const registry = this.options.getTools();
32501
32508
  const selection = this.options.selection ?? new ToolSelection();
@@ -32533,96 +32540,137 @@ var AgentStepRunner = class {
32533
32540
  throw error;
32534
32541
  }
32535
32542
  }, () => history.snapshot().map((message) => message.role === "system" || message.role === "user" ? message : {}));
32536
- let selected = select(history.snapshot());
32537
- if (selected.announcement)
32538
- history.commitToolContext({ role: "user", ...selected.announcement });
32539
- let prepared = { messages: history.snapshot() };
32540
- for (let attempt = 0; attempt < 3; attempt++) {
32541
- prepared = await this.options.contextGovernor?.prepare({
32542
- messages: history.snapshot(),
32543
- tools: selected.view.definitions,
32544
- provider: this.options.provider,
32545
- model: this.options.model,
32546
- projectTools: (messages) => select(messages).view.definitions
32547
- }) ?? { messages: history.snapshot() };
32548
- history.retain(prepared.messages);
32549
- selected = select(prepared.messages);
32550
- const resources = await this.options.resources?.prepareContext(prepared.messages, cancelToken?.signal) ?? [];
32551
- if (!selected.announcement && resources.length === 0)
32552
- break;
32553
- if (attempt === 2)
32554
- throw this.options.createError("Resource catalog cannot fit the retained context.");
32555
- if (selected.announcement)
32556
- history.commitToolContext({ role: "user", ...selected.announcement });
32557
- for (const message of resources) {
32558
- history.commitSkillContext(message);
32559
- this.options.resources?.committed([message]);
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;
32543
+ const prepareRequest = async () => {
32544
+ let selected2 = select(history.snapshot());
32545
+ if (selected2.announcement)
32546
+ history.commitToolContext({ role: "user", ...selected2.announcement });
32547
+ let prepared2 = { messages: history.snapshot() };
32548
+ for (let attempt = 0; attempt < 3; attempt++) {
32549
+ prepared2 = await this.options.contextGovernor?.prepare({
32550
+ messages: history.snapshot(),
32551
+ tools: selected2.view.definitions,
32552
+ provider: this.options.provider,
32553
+ model: this.options.model,
32554
+ projectTools: (messages) => select(messages).view.definitions
32555
+ }) ?? { messages: history.snapshot() };
32556
+ history.retain(prepared2.messages);
32557
+ selected2 = select(prepared2.messages);
32558
+ const resources = await this.options.resources?.prepareContext(prepared2.messages, cancelToken?.signal) ?? [];
32559
+ if (!selected2.announcement && resources.length === 0)
32560
+ break;
32561
+ if (attempt === 2)
32562
+ throw this.options.createError("Resource catalog cannot fit the retained context.");
32563
+ if (selected2.announcement)
32564
+ history.commitToolContext({ role: "user", ...selected2.announcement });
32565
+ for (const message of resources) {
32566
+ history.commitSkillContext(message);
32567
+ this.options.resources?.committed([message]);
32598
32568
  }
32569
+ }
32570
+ remainingTokens = prepared2.hardInputLimit === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, prepared2.hardInputLimit - (prepared2.contextTokens ?? 0) - 1024);
32571
+ return { selected: selected2, prepared: prepared2 };
32572
+ };
32573
+ let { selected, prepared } = await prepareRequest();
32574
+ let tools = selected.view;
32575
+ let definitions = tools.definitions;
32576
+ let requestMessages = [...prepared.messages];
32577
+ let result;
32578
+ for (let overflowAttempt = 0; ; overflowAttempt++) {
32579
+ const attemptRequestId = requestId;
32580
+ const attemptRound = modelRound;
32581
+ this.emit("model_request", {
32582
+ round: attemptRound,
32583
+ request_id: attemptRequestId,
32584
+ message_count: requestMessages.length,
32585
+ tool_rounds: toolRounds,
32586
+ model_requests: modelRequests,
32587
+ total_tokens: totalTokens,
32588
+ ...prepared.contextTokens === void 0 ? {} : { context_tokens: prepared.contextTokens },
32589
+ ...prepared.contextWindow === void 0 ? {} : { context_window: prepared.contextWindow }
32599
32590
  });
32600
- } catch (error) {
32601
- if (error instanceof ModelStreamCancelled) {
32602
- this.emit("model_response_aborted", {
32603
- round: modelRound,
32604
- request_id: requestId,
32605
- reason: errorMessage10(error)
32591
+ try {
32592
+ const modelRequestOpened = context?.modelRequestOpened;
32593
+ result = await this.options.modelRuntime.complete({
32594
+ provider: this.options.provider,
32595
+ model: this.options.model,
32596
+ ...this.options.baseUrl === null ? {} : { baseUrl: this.options.baseUrl },
32597
+ messages: requestMessages,
32598
+ tools: definitions,
32599
+ reasoningEffort: this.options.getReasoningEffort(),
32600
+ requestId: attemptRequestId,
32601
+ cancelToken,
32602
+ isRequestActive: this.options.isRequestActive,
32603
+ onDelta: (kind, payload) => {
32604
+ this.emit(kind, { round: attemptRound, request_id: attemptRequestId, ...payload });
32605
+ },
32606
+ onRequestOpened: () => {
32607
+ if (modelRequestOpened?.call(context) === false)
32608
+ return false;
32609
+ this.options.resources?.committed(pendingResourceInputs);
32610
+ pendingResourceInputs = [];
32611
+ return true;
32612
+ }
32606
32613
  });
32607
- throw this.options.createCancelled(errorMessage10(error), error);
32608
- }
32609
- const errorPayload = {
32610
- round: modelRound,
32611
- request_id: requestId,
32612
- error: errorMessage10(error)
32613
- };
32614
- if (error instanceof ModelStreamError && error.hadDelta) {
32615
- this.emit("model_response_aborted", errorPayload);
32616
- this.options.emitLegacy("model_error", errorPayload);
32617
- } else {
32618
- this.emit("model_error", errorPayload);
32619
- }
32620
- let message = `Model request failed: ${errorMessage10(error)}`;
32621
- if (this.options.provider && modelErrorKind(error) === "authentication") {
32622
- message += `
32614
+ break;
32615
+ } catch (error) {
32616
+ if (error instanceof ModelStreamCancelled) {
32617
+ this.emit("model_response_aborted", {
32618
+ round: attemptRound,
32619
+ request_id: attemptRequestId,
32620
+ reason: errorMessage10(error)
32621
+ });
32622
+ throw this.options.createCancelled(errorMessage10(error), error);
32623
+ }
32624
+ const errorPayload = {
32625
+ round: attemptRound,
32626
+ request_id: attemptRequestId,
32627
+ error: errorMessage10(error)
32628
+ };
32629
+ if (error instanceof ModelStreamError && error.hadDelta) {
32630
+ this.emit("model_response_aborted", errorPayload);
32631
+ this.options.emitLegacy("model_error", errorPayload);
32632
+ } else {
32633
+ this.emit("model_error", errorPayload);
32634
+ }
32635
+ if (overflowAttempt === 0 && error instanceof ModelError && error.kind === "context_overflow" && !error.hadDelta && this.options.contextGovernor?.recoverAfterOverflow !== void 0) {
32636
+ this.raiseIfCancelled();
32637
+ const recovered = await this.options.contextGovernor.recoverAfterOverflow({
32638
+ messages: history.snapshot(),
32639
+ tools: definitions,
32640
+ provider: this.options.provider,
32641
+ model: this.options.model,
32642
+ projectTools: (messages) => select(messages).view.definitions
32643
+ });
32644
+ this.raiseIfCancelled();
32645
+ history.retain(recovered.messages);
32646
+ remainingTokens = Number.POSITIVE_INFINITY;
32647
+ preflightCompacted = false;
32648
+ ({ selected, prepared } = await prepareRequest());
32649
+ this.raiseIfCancelled();
32650
+ tools = selected.view;
32651
+ definitions = tools.definitions;
32652
+ requestMessages = [...prepared.messages];
32653
+ this.emit("model_retry_scheduled", {
32654
+ round: attemptRound,
32655
+ request_id: attemptRequestId,
32656
+ attempt: 2,
32657
+ max_attempts: 2,
32658
+ delay_ms: 0,
32659
+ error_kind: "context_overflow"
32660
+ });
32661
+ modelRound += 1;
32662
+ modelRequests += 1;
32663
+ requestId = randomUUID10();
32664
+ this.options.onRequestId(requestId);
32665
+ continue;
32666
+ }
32667
+ let message = `Model request failed: ${errorMessage10(error)}`;
32668
+ if (this.options.provider && modelErrorKind(error) === "authentication") {
32669
+ message += `
32623
32670
  Authentication failed for ${this.options.provider}. Run /login ${this.options.provider} to update your API key.`;
32671
+ }
32672
+ throw this.options.createError(message, error);
32624
32673
  }
32625
- throw this.options.createError(message, error);
32626
32674
  }
32627
32675
  const toolCalls = toolCallsOf(result.message);
32628
32676
  let requestTokens = usageTotalTokens(result.usage);
@@ -33520,6 +33568,25 @@ function createSessionRuntime(options) {
33520
33568
  contextWindow: selectedModel.contextWindow,
33521
33569
  hardInputLimit: selectedModel.contextWindow - selectedModel.maxTokens
33522
33570
  };
33571
+ },
33572
+ recoverAfterOverflow: async ({ tools, projectTools: projectTools2 }) => {
33573
+ const history = activeHistory(options.sessionController);
33574
+ const prepared = await createGovernor(history).recoverAfterOverflow({
33575
+ protectedFromSeq: attachmentTurnStart,
33576
+ entries: history.entries(),
33577
+ currentProvider: route.provider,
33578
+ currentModel: route.model,
33579
+ tools,
33580
+ projectTools: projectTools2,
33581
+ budget: modelBudget(selectedModel),
33582
+ policy: defaultContextPolicy()
33583
+ });
33584
+ return {
33585
+ messages: prepared.messages,
33586
+ contextTokens: prepared.tokens,
33587
+ contextWindow: selectedModel.contextWindow,
33588
+ hardInputLimit: selectedModel.contextWindow - selectedModel.maxTokens
33589
+ };
33523
33590
  }
33524
33591
  }
33525
33592
  });
@@ -38703,64 +38770,7 @@ async function handleCommandResult(presenter, suggestCommand, result) {
38703
38770
  return true;
38704
38771
  }
38705
38772
  async function runSessionRepl(session, options) {
38706
- if (options.runUi !== void 0 || typeof options.ui.run === "function") {
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;
38773
+ return runPersistentSessionRepl(session, options);
38764
38774
  }
38765
38775
  function startCoordinator(session, commandHandler, presenter, suggestCommand) {
38766
38776
  const queue = [];
@@ -39767,6 +39777,7 @@ async function main(argv, options = {}) {
39767
39777
  let attachmentRuntime;
39768
39778
  try {
39769
39779
  modelPlatform = await createPiAiPlatform({
39780
+ // 附件运行时稍后才创建,通过回调取得届时可用的存储实例。
39770
39781
  attachments: { store: () => attachmentRuntime?.store },
39771
39782
  credentials,
39772
39783
  modelCatalogStore,
@@ -39903,14 +39914,41 @@ async function main(argv, options = {}) {
39903
39914
  model: args.model,
39904
39915
  baseUrl: args.baseUrl
39905
39916
  });
39906
- if (!await validateConfiguredSelection({
39917
+ const validation = await validateConfiguredSelection({
39907
39918
  config,
39908
39919
  catalog: modelPlatform.catalog,
39909
39920
  providerAuth,
39910
39921
  presenter: startupPresenter
39911
- })) {
39922
+ });
39923
+ if (validation === false) {
39912
39924
  return 2;
39913
39925
  }
39926
+ if (typeof validation === "string") {
39927
+ const canRecover = (options.stdin ?? process.stdin).isTTY === true && (options.stdout ?? process.stdout).isTTY === true && !args.model && !environ["LAOHUANG_MODEL"];
39928
+ if (!canRecover) throw new Error(validation);
39929
+ outputFn(`${validation}. Please reconfigure profile '${config.profile}'.`);
39930
+ let selection;
39931
+ try {
39932
+ selection = await runInitialModelSelection({ selector, presenter: startupPresenter, providerAuth });
39933
+ } catch (error) {
39934
+ if (!(error instanceof PromptCancelledError) && !(error instanceof PromptEofError)) throw error;
39935
+ selection = null;
39936
+ }
39937
+ if (selection === null) {
39938
+ outputFn("Configuration cancelled.");
39939
+ return 0;
39940
+ }
39941
+ const saved = manager.resolve({ environ: {}, profile: config.profile });
39942
+ const baseUrl = selection.config.provider === saved.provider ? saved.baseUrl : selection.config.baseUrl;
39943
+ manager.configure({
39944
+ name: config.profile,
39945
+ provider: selection.config.provider,
39946
+ model: selection.config.model,
39947
+ baseUrl
39948
+ });
39949
+ config = { ...config, ...selection.config, baseUrl: args.baseUrl ?? environ["LAOHUANG_BASE_URL"] ?? baseUrl };
39950
+ outputFn(`Saved profile '${config.profile}' to ${configPath}`);
39951
+ }
39914
39952
  } else {
39915
39953
  const selection = await runInitialModelSelection({
39916
39954
  selector,
@@ -40328,7 +40366,7 @@ async function runInitialModelSelection(options) {
40328
40366
  async function validateConfiguredSelection(options) {
40329
40367
  const provider2 = options.catalog.getProvider(options.config.provider);
40330
40368
  if (provider2 === void 0) {
40331
- throw new Error(`Unknown provider: ${options.config.provider}`);
40369
+ return `Unknown provider: ${options.config.provider}`;
40332
40370
  }
40333
40371
  if (!await options.providerAuth.ensureConfigured(options.config.provider, {
40334
40372
  promptIfMissing: true,
@@ -40341,9 +40379,7 @@ async function validateConfiguredSelection(options) {
40341
40379
  }
40342
40380
  await options.catalog.refresh(options.config.provider);
40343
40381
  if (options.catalog.getModel(options.config.provider, options.config.model) === void 0) {
40344
- throw new Error(
40345
- `Unknown model: ${options.config.provider}/${options.config.model}`
40346
- );
40382
+ return `Unknown model: ${options.config.provider}/${options.config.model}`;
40347
40383
  }
40348
40384
  return true;
40349
40385
  }