pi-orchestrator-config 4.3.9 → 4.3.10
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.
|
@@ -810,6 +810,15 @@ function bodyAlreadySigned(text: string): boolean {
|
|
|
810
810
|
return /\*Assisted-by:/.test(text) || /(?:^|\n|\\n)Assisted-by:\s/.test(text);
|
|
811
811
|
}
|
|
812
812
|
|
|
813
|
+
/** Unresolved model placeholders are stale template footers, not signatures. */
|
|
814
|
+
function replaceUnresolvedModelFooters(payload: string): string {
|
|
815
|
+
const unresolvedFooter = /(?:\n|\\n){2}---(?:\n|\\n)\*Assisted-by: PI \((?:\$PI_MODEL|\$\{PI_MODEL\})\)\*/g;
|
|
816
|
+
const replaced = payload.replace(unresolvedFooter, "");
|
|
817
|
+
if (replaced !== payload)
|
|
818
|
+
enfLog.debug("injectGhBodySignature replaced unresolved PI_MODEL footer");
|
|
819
|
+
return replaced;
|
|
820
|
+
}
|
|
821
|
+
|
|
813
822
|
/** Index of the matching closer for a quote at `openIdx`, honoring POSIX escapes. */
|
|
814
823
|
function matchingQuoteIndex(s: string, openIdx: number): number | null {
|
|
815
824
|
const q = s[openIdx];
|
|
@@ -888,10 +897,6 @@ export function injectGhBodySignature(command: string, signature: string): strin
|
|
|
888
897
|
enfLog.debug("injectGhBodySignature skip not a gh body command");
|
|
889
898
|
return command;
|
|
890
899
|
}
|
|
891
|
-
if (bodyAlreadySigned(command)) {
|
|
892
|
-
enfLog.debug("injectGhBodySignature skip already present");
|
|
893
|
-
return command;
|
|
894
|
-
}
|
|
895
900
|
const footer = commentSignatureFooter(signature);
|
|
896
901
|
const span = findLastQuotedBodySpan(command);
|
|
897
902
|
if (!span) {
|
|
@@ -899,6 +904,19 @@ export function injectGhBodySignature(command: string, signature: string): strin
|
|
|
899
904
|
return command;
|
|
900
905
|
}
|
|
901
906
|
|
|
907
|
+
const payload = command.slice(span.open + 1, span.close);
|
|
908
|
+
const normalizedPayload = replaceUnresolvedModelFooters(payload);
|
|
909
|
+
if (normalizedPayload !== payload) {
|
|
910
|
+
// Re-parse positions after removal so command-substitution heredocs still
|
|
911
|
+
// receive their footer before the closing delimiter.
|
|
912
|
+
const normalizedCommand = command.slice(0, span.open + 1) + normalizedPayload + command.slice(span.close);
|
|
913
|
+
return injectGhBodySignature(normalizedCommand, signature);
|
|
914
|
+
}
|
|
915
|
+
if (bodyAlreadySigned(payload)) {
|
|
916
|
+
enfLog.debug("injectGhBodySignature skip already present in body");
|
|
917
|
+
return command;
|
|
918
|
+
}
|
|
919
|
+
|
|
902
920
|
const last = lastHeredocInSpan(command, span.open, span.close);
|
|
903
921
|
if (last) {
|
|
904
922
|
const delim = last[2];
|
|
@@ -911,7 +929,6 @@ export function injectGhBodySignature(command: string, signature: string): strin
|
|
|
911
929
|
}
|
|
912
930
|
}
|
|
913
931
|
|
|
914
|
-
const payload = command.slice(span.open + 1, span.close);
|
|
915
932
|
if (payload.startsWith("$(")) {
|
|
916
933
|
enfLog.warn("injectGhBodySignature --body command subst without heredoc match");
|
|
917
934
|
return command;
|
|
@@ -27,7 +27,6 @@ import type {
|
|
|
27
27
|
StreamOptions,
|
|
28
28
|
} from "@earendil-works/pi-ai";
|
|
29
29
|
import { createLogger } from "../shared/logger.js";
|
|
30
|
-
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
31
30
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
32
31
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
33
32
|
import { asStringArray, getSetting } from "../orchestrator/project-settings.js";
|
|
@@ -80,12 +79,35 @@ const DISCOVERY_TIMEOUT_MS = 30_000;
|
|
|
80
79
|
|
|
81
80
|
const registry = new ProviderDriverRegistry();
|
|
82
81
|
|
|
82
|
+
let createAssistantMessageEventStream: () => AssistantMessageEventStream;
|
|
83
|
+
|
|
83
84
|
/** Track which instances are CLI vs ACPX for configured gates. */
|
|
84
85
|
const cliInstances = new Map<string, ProviderInstance>();
|
|
85
86
|
const acpxInstances = new Map<string, ProviderInstance>();
|
|
86
87
|
|
|
87
88
|
let projectCwd = "";
|
|
88
89
|
|
|
90
|
+
interface ProviderDiscoverySummary {
|
|
91
|
+
summary: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Render durable discovery entries without adding a TUI package dependency. */
|
|
95
|
+
function providerDiscoverySummaryComponent(
|
|
96
|
+
summary: string,
|
|
97
|
+
theme: { fg: (color: "muted", text: string) => string },
|
|
98
|
+
) {
|
|
99
|
+
log.debug("rendering CLI/ACPX discovery summary", { summary });
|
|
100
|
+
return {
|
|
101
|
+
render: (_width: number): string[] => {
|
|
102
|
+
log.debug("rendered CLI/ACPX discovery summary");
|
|
103
|
+
return [theme.fg("muted", summary)];
|
|
104
|
+
},
|
|
105
|
+
invalidate: (): void => {
|
|
106
|
+
log.debug("invalidated CLI/ACPX discovery summary");
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
89
111
|
// ---------------------------------------------------------------------------
|
|
90
112
|
// Configured Gates
|
|
91
113
|
// ---------------------------------------------------------------------------
|
|
@@ -294,7 +316,17 @@ log.debug("providers module loaded");
|
|
|
294
316
|
|
|
295
317
|
const cwdHookBound = new WeakSet<object>();
|
|
296
318
|
|
|
297
|
-
export default async function (
|
|
319
|
+
export default async function (
|
|
320
|
+
pi: ExtensionAPI,
|
|
321
|
+
testOptions?: { providerSummaryParts?: readonly string[] },
|
|
322
|
+
) {
|
|
323
|
+
if (typeof pi.registerEntryRenderer === "function") {
|
|
324
|
+
pi.registerEntryRenderer<ProviderDiscoverySummary>(
|
|
325
|
+
"provider-discovery-summary",
|
|
326
|
+
(entry, _options, theme) => providerDiscoverySummaryComponent(entry.data?.summary ?? "", theme),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
298
330
|
// pi --help / --version — skip discovery
|
|
299
331
|
if (isPiMetaInvocation()) return;
|
|
300
332
|
|
|
@@ -319,6 +351,9 @@ export default async function (pi: ExtensionAPI) {
|
|
|
319
351
|
return;
|
|
320
352
|
}
|
|
321
353
|
markProvidersInitialized();
|
|
354
|
+
if (!testOptions?.providerSummaryParts) {
|
|
355
|
+
({ createAssistantMessageEventStream } = await import("@earendil-works/pi-ai"));
|
|
356
|
+
}
|
|
322
357
|
log.debug(
|
|
323
358
|
"providers factory: proceeding after reset / first load — re-entering discovery",
|
|
324
359
|
);
|
|
@@ -548,15 +583,23 @@ export default async function (pi: ExtensionAPI) {
|
|
|
548
583
|
|
|
549
584
|
// Show discovery summary on session start; restore saved default model when
|
|
550
585
|
// findInitialModel raced hasConfiguredAuth false → wrong initial model (#753).
|
|
551
|
-
const providerSummaryParts
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
586
|
+
const providerSummaryParts = testOptions?.providerSummaryParts
|
|
587
|
+
? [...testOptions.providerSummaryParts]
|
|
588
|
+
: (() => {
|
|
589
|
+
const parts: string[] = [];
|
|
590
|
+
for (const [agent, inst] of cliInstances) {
|
|
591
|
+
const count = inst.snapshot.getSnapshot().models.length;
|
|
592
|
+
parts.push(`cli-${agent} (${count})`);
|
|
593
|
+
}
|
|
594
|
+
for (const [agent, inst] of acpxInstances) {
|
|
595
|
+
const count = inst.snapshot.getSnapshot().models.length;
|
|
596
|
+
parts.push(`acpx-${agent} (${count})`);
|
|
597
|
+
}
|
|
598
|
+
log.debug("built CLI/ACPX provider discovery summary", {
|
|
599
|
+
providerCount: parts.length,
|
|
600
|
+
});
|
|
601
|
+
return parts;
|
|
602
|
+
})();
|
|
560
603
|
|
|
561
604
|
// Fire-and-forget restore so session_start is not blocked by retries (#753).
|
|
562
605
|
// Omit registeredProviders: cli/acpx-only lists falsely fail-fast native defaults.
|
|
@@ -578,15 +621,18 @@ export default async function (pi: ExtensionAPI) {
|
|
|
578
621
|
|
|
579
622
|
pi.on("session_start", (event, ctx) => {
|
|
580
623
|
const reason = typeof event?.reason === "string" ? event.reason : "";
|
|
581
|
-
|
|
624
|
+
const summary = `Providers: ${providerSummaryParts.join(", ")}`;
|
|
625
|
+
// Entries survive reload/resume. Only append for a newly started session;
|
|
626
|
+
// its persisted entry renders on later lifecycle events without duplication.
|
|
627
|
+
if (providerSummaryParts.length > 0 && (reason === "startup" || reason === "new")) {
|
|
582
628
|
try {
|
|
583
|
-
|
|
629
|
+
pi.appendEntry<ProviderDiscoverySummary>("provider-discovery-summary", { summary });
|
|
630
|
+
log.info("persisted CLI/ACPX discovery summary", { reason, summary });
|
|
584
631
|
} catch (err) {
|
|
585
|
-
log.
|
|
586
|
-
"providers notify skipped",
|
|
587
|
-
err instanceof Error ? err.message : String(err),
|
|
588
|
-
);
|
|
632
|
+
log.warn("provider discovery summary append failed", err instanceof Error ? err.name : typeof err);
|
|
589
633
|
}
|
|
634
|
+
} else if (providerSummaryParts.length > 0) {
|
|
635
|
+
log.debug("provider discovery summary retained", { reason: reason || "unknown" });
|
|
590
636
|
}
|
|
591
637
|
applyCliAcpxThinking(
|
|
592
638
|
ctx.model
|
|
@@ -30,6 +30,10 @@ export interface OpenAiCompatibleModelRecord {
|
|
|
30
30
|
cost?: unknown;
|
|
31
31
|
contextWindow?: unknown;
|
|
32
32
|
maxTokens?: unknown;
|
|
33
|
+
/** LiteLLM's generic input capacity alias. */
|
|
34
|
+
max_input_tokens?: unknown;
|
|
35
|
+
/** LiteLLM's generic output capacity alias. */
|
|
36
|
+
max_output_tokens?: unknown;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/** Pi's models.json defaults for omitted static-model metadata. */
|
|
@@ -461,6 +465,7 @@ export function materializeOpenAiCompatibleModels(
|
|
|
461
465
|
baseUrl: string,
|
|
462
466
|
providerId: string,
|
|
463
467
|
): Model<Api>[] {
|
|
468
|
+
log.debug("materializing OpenAI-compatible models", { providerId, recordCount: records.length });
|
|
464
469
|
const models: Model<Api>[] = [];
|
|
465
470
|
const seenIds = new Set<string>();
|
|
466
471
|
for (const record of records) {
|
|
@@ -477,8 +482,21 @@ export function materializeOpenAiCompatibleModels(
|
|
|
477
482
|
: PI_STATIC_MODEL_DEFAULTS.reasoning,
|
|
478
483
|
input: materializeInput(record.input),
|
|
479
484
|
cost: materializeCost(record.cost),
|
|
480
|
-
|
|
481
|
-
|
|
485
|
+
// Native Pi metadata wins. LiteLLM commonly exposes only generic input
|
|
486
|
+
// and output capacities, whose combined capacity is Pi's context window.
|
|
487
|
+
contextWindow: positiveFiniteNumber(
|
|
488
|
+
record.contextWindow,
|
|
489
|
+
(() => {
|
|
490
|
+
const input = positiveFiniteNumber(record.max_input_tokens, 0);
|
|
491
|
+
const output = positiveFiniteNumber(record.max_output_tokens, 0);
|
|
492
|
+
const capacity = input > 0 && output > 0 ? input + output : 0;
|
|
493
|
+
return positiveFiniteNumber(capacity, PI_STATIC_MODEL_DEFAULTS.contextWindow);
|
|
494
|
+
})(),
|
|
495
|
+
),
|
|
496
|
+
maxTokens: positiveFiniteNumber(
|
|
497
|
+
record.maxTokens,
|
|
498
|
+
positiveFiniteNumber(record.max_output_tokens, PI_STATIC_MODEL_DEFAULTS.maxTokens),
|
|
499
|
+
),
|
|
482
500
|
});
|
|
483
501
|
}
|
|
484
502
|
return models;
|