@pasko70/pibo 3.0.0 → 3.0.2
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 +20 -6
- package/dist/agent-runtime/context-build.js +26 -2
- package/dist/agent-runtime/routed-session.js +108 -9
- package/dist/agent-runtimes/codex-native/adapter.js +12 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
- package/dist/agent-runtimes/omp/adapter.js +3 -7
- package/dist/agent-runtimes/pi/adapter.js +5 -3
- package/dist/agent-runtimes/pi/routed-session.js +49 -8
- package/dist/agent-runtimes/pi/runtime.js +6 -0
- package/dist/apps/chat/agent-profiles.js +31 -2
- package/dist/apps/chat/agent-store.js +43 -3
- package/dist/apps/chat/chat-request-normalizers.js +31 -0
- package/dist/apps/chat/data/chat-data-mappers.js +61 -11
- package/dist/apps/chat/data/session-query-service.js +33 -10
- package/dist/apps/chat/data/timeline-query-service.js +60 -35
- package/dist/apps/chat/output-compactor.js +144 -21
- package/dist/apps/chat/output-event-policy.js +179 -2
- package/dist/apps/chat/stream.js +16 -7
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +236 -64
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-B6GZgWSj.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-BPotHecb.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-BzqQKeVO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CvaPTBTN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-paagejNj.js} +1 -1
- package/dist/apps/chat-ui/assets/index-6JJV-eic.js +228 -0
- package/dist/apps/chat-ui/assets/index-CeL9JPP5.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/{index-Dtw2Z7jz.js → index-U-MSErGa.js} +4 -4
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
- package/dist/cli-session/localSessionSource.js +153 -161
- package/dist/cli.js +7 -11
- package/dist/core/context-build.js +7 -16
- package/dist/core/output-persistence-retry.js +484 -0
- package/dist/core/output-render-sequence.js +331 -0
- package/dist/core/profiles.js +24 -0
- package/dist/core/provider-recovery.js +7 -1
- package/dist/core/session-model.js +37 -1
- package/dist/core/session-router.js +67 -20
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/schema.js +122 -2
- package/dist/data/session-store.js +3 -3
- package/dist/debug/index.js +44 -2
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- package/dist/plugins/context-files-store.js +30 -0
- package/dist/plugins/context-files.js +52 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/user-profile-resources.js +22 -0
- package/dist/reliability/store.js +246 -37
- package/dist/session-ui/terminalRows.js +6 -3
- package/dist/sessions/pibo-data-store.js +145 -0
- package/dist/sessions/store.js +42 -0
- package/dist/setup/cli.js +422 -6
- package/dist/setup/installation-profiles.js +464 -0
- package/dist/shared/deterministic-digest.js +94 -0
- package/dist/shared/trace-engine.js +54 -0
- package/dist/shared/trace-event-projection.js +124 -67
- package/dist/shared/trace-history.js +47 -12
- package/dist/shared/trace-live-reducer.js +23 -4
- package/dist/shared/trace-nodes.js +17 -28
- package/dist/shared/trace-order.js +41 -5
- package/dist/shared/trace-page-merge.js +44 -14
- package/dist/shared/trace-patch-nodes.js +2 -0
- package/dist/shared/trace-subagent-links.js +4 -1
- package/dist/shared/trace-tool-identity.js +15 -8
- package/dist/signals/projector.js +28 -6
- package/dist/subagents/tool.js +7 -3
- package/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
package/README.md
CHANGED
|
@@ -24,6 +24,19 @@ pibo --help
|
|
|
24
24
|
|
|
25
25
|
The current package declares Node.js 24+. Some Linux distributions still ship Node 22 through `apt`, so install a recent Node release first when needed. A user-local Node/npm setup through `nvm`, `fnm`, or a user-writable npm prefix works well.
|
|
26
26
|
|
|
27
|
+
Package installation has no host-level side effects. For a self-hosted server, choose a supported setup profile after installing the CLI:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Recommended complete workstation
|
|
31
|
+
pibo setup plan --profile batteries-included --domain pibo.example.com
|
|
32
|
+
sudo pibo setup install --profile batteries-included --domain pibo.example.com --apply --yes
|
|
33
|
+
|
|
34
|
+
# Minimal gateway and Chat Web only
|
|
35
|
+
pibo setup plan --profile vanilla --domain pibo.example.com
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
See [Installation profiles](./docs/project/installation-profiles.md) for components, resource and security costs, staging, upgrades, and rollback.
|
|
39
|
+
|
|
27
40
|
For a user-local global npm prefix:
|
|
28
41
|
|
|
29
42
|
```bash
|
|
@@ -67,17 +80,18 @@ A fresh server install usually needs only three decisions:
|
|
|
67
80
|
2. Install Node.js 24+ for that user or system-wide.
|
|
68
81
|
3. Install Pibo through npm.
|
|
69
82
|
|
|
70
|
-
Use
|
|
83
|
+
Use an installation profile for the normal first run. Batteries Included is recommended; Vanilla is the minimal controlled base:
|
|
71
84
|
|
|
72
85
|
```bash
|
|
73
86
|
pibo setup doctor --domain pibo.example.com --expected-ip <server-ip>
|
|
74
|
-
pibo setup
|
|
75
|
-
pibo setup
|
|
87
|
+
pibo setup plan --profile batteries-included --domain pibo.example.com
|
|
88
|
+
pibo setup install --profile batteries-included --domain pibo.example.com --write-to /tmp/pibo-setup
|
|
89
|
+
sudo pibo setup install --profile batteries-included --domain pibo.example.com --apply --yes
|
|
76
90
|
```
|
|
77
91
|
|
|
78
|
-
|
|
92
|
+
The package install remains side-effect free. The explicit setup command records ownership, checks pinned downloads, configures the selected components, and exposes status, upgrade, and uninstall plans. See [Installation profiles](./docs/project/installation-profiles.md).
|
|
79
93
|
|
|
80
|
-
|
|
94
|
+
Core developers can still use the topology-specific developer-host planner to add production/dev separation plus Docker compute workers:
|
|
81
95
|
|
|
82
96
|
```bash
|
|
83
97
|
pibo setup developer-host \
|
|
@@ -159,7 +173,7 @@ pibo mcp # discover and call configured MCP servers
|
|
|
159
173
|
pibo tools # install and inspect curated external CLI tools
|
|
160
174
|
pibo pi-packages # register Pi Coding Agent packages
|
|
161
175
|
pibo debug # inspect local Pibo data stores
|
|
162
|
-
pibo setup # plan
|
|
176
|
+
pibo setup # plan and manage supported host installation profiles
|
|
163
177
|
pibo profile # inspect runtime profiles
|
|
164
178
|
pibo tui # start the direct Pi TUI
|
|
165
179
|
pibo tui:routed # start the routed Pibo TUI
|
|
@@ -17,6 +17,7 @@ export function profileWithRuntimeInstance(profile, runtimeInstanceId) {
|
|
|
17
17
|
parentSessionId: profile.parentSessionId,
|
|
18
18
|
model: undefined,
|
|
19
19
|
mainModel: undefined,
|
|
20
|
+
mainModelFallbacks: [],
|
|
20
21
|
subagentModel: undefined,
|
|
21
22
|
thinkingLevel: profile.thinkingLevel,
|
|
22
23
|
mainThinkingLevel: profile.mainThinkingLevel,
|
|
@@ -30,6 +31,7 @@ export function profileWithRuntimeInstance(profile, runtimeInstanceId) {
|
|
|
30
31
|
mcpServers: profile.mcpServers,
|
|
31
32
|
piPackages: profile.piPackages,
|
|
32
33
|
contextFiles: profile.contextFiles,
|
|
34
|
+
diagnostics: profile.diagnostics,
|
|
33
35
|
builtinTools: profile.builtinTools,
|
|
34
36
|
builtinToolNames: profile.builtinToolNames,
|
|
35
37
|
autoContextFiles: profile.autoContextFiles,
|
|
@@ -229,6 +231,7 @@ export function buildPortableRuntimeContextSnapshot(input) {
|
|
|
229
231
|
metadata: {
|
|
230
232
|
activeModel: runtimeManifest.effectiveModel,
|
|
231
233
|
mainModel: profile.mainModel,
|
|
234
|
+
mainModelFallbacks: profile.mainModelFallbacks,
|
|
232
235
|
subagentModel: profile.subagentModel,
|
|
233
236
|
mainThinkingLevel: profile.mainThinkingLevel ?? profile.thinkingLevel,
|
|
234
237
|
subagentThinkingLevel: profile.subagentThinkingLevel ?? profile.thinkingLevel,
|
|
@@ -240,6 +243,21 @@ export function buildPortableRuntimeContextSnapshot(input) {
|
|
|
240
243
|
?? input.runtime.capabilities.nativeSubagents.enabledByDefault,
|
|
241
244
|
},
|
|
242
245
|
});
|
|
246
|
+
for (const [index, diagnostic] of profile.diagnostics.entries()) {
|
|
247
|
+
addNode({
|
|
248
|
+
id: `profile-diagnostic-${index}`,
|
|
249
|
+
kind: "diagnostic",
|
|
250
|
+
title: diagnostic.code,
|
|
251
|
+
source: "profile",
|
|
252
|
+
state: diagnostic.severity === "error" ? "error" : "warning",
|
|
253
|
+
badges: [diagnostic.severity.toUpperCase(), "PROFILE"],
|
|
254
|
+
hydratedText: diagnostic.message,
|
|
255
|
+
metadata: diagnostic.resourceName ? {
|
|
256
|
+
resourceKind: diagnostic.resourceKind,
|
|
257
|
+
resourceName: diagnostic.resourceName,
|
|
258
|
+
} : undefined,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
243
261
|
for (const [index, diagnostic] of input.runtime.diagnostics.entries()) {
|
|
244
262
|
addNode({
|
|
245
263
|
id: `runtime-diagnostic-${index}`,
|
|
@@ -265,9 +283,11 @@ export function buildPortableRuntimeContextSnapshot(input) {
|
|
|
265
283
|
});
|
|
266
284
|
}
|
|
267
285
|
const resourceDiagnostics = input.resources?.diagnostics ?? [];
|
|
268
|
-
const warnings =
|
|
286
|
+
const warnings = profile.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length
|
|
287
|
+
+ input.runtime.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length
|
|
269
288
|
+ resourceDiagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
|
|
270
|
-
const errors =
|
|
289
|
+
const errors = profile.diagnostics.filter((diagnostic) => diagnostic.severity === "error").length
|
|
290
|
+
+ input.runtime.diagnostics.filter((diagnostic) => diagnostic.severity === "error").length
|
|
271
291
|
+ resourceDiagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
272
292
|
return {
|
|
273
293
|
version: 1,
|
|
@@ -287,6 +307,10 @@ export function buildPortableRuntimeContextSnapshot(input) {
|
|
|
287
307
|
},
|
|
288
308
|
nodes,
|
|
289
309
|
diagnostics: [
|
|
310
|
+
...profile.diagnostics.map((diagnostic) => ({
|
|
311
|
+
type: diagnostic.severity,
|
|
312
|
+
message: `[${diagnostic.code}] ${diagnostic.message}`,
|
|
313
|
+
})),
|
|
290
314
|
...input.runtime.diagnostics.map((diagnostic) => ({ type: diagnostic.severity, message: diagnostic.message })),
|
|
291
315
|
...resourceDiagnostics.map((diagnostic) => ({ type: diagnostic.severity, message: diagnostic.message })),
|
|
292
316
|
],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PiboSteeringUnavailableError, } from "../core/events.js";
|
|
2
|
-
import {
|
|
2
|
+
import { PIBO_PROVIDER_RECOVERY_PROMPT, isPiboProviderFallbackError } from "../core/provider-recovery.js";
|
|
3
|
+
import { normalizeSessionErrorDetails, runtimeSessionErrorDetails } from "../core/session-errors.js";
|
|
3
4
|
import { isPiboThinkingLevel } from "../core/thinking.js";
|
|
4
5
|
import { AgentRuntimeCapabilityUnavailableError } from "./errors.js";
|
|
5
6
|
const RUN_REMINDER_CAPABILITY_TOOLS = new Set([
|
|
@@ -29,6 +30,23 @@ function errorMessage(error) {
|
|
|
29
30
|
function promptSource(source) {
|
|
30
31
|
return source === "user" || source === "ui" ? "interactive" : "rpc";
|
|
31
32
|
}
|
|
33
|
+
function sameModel(left, right) {
|
|
34
|
+
return Boolean(left && right && left.provider === right.provider && left.id === right.id);
|
|
35
|
+
}
|
|
36
|
+
function uniqueModels(models) {
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
return models.flatMap((model) => {
|
|
39
|
+
const provider = model.provider.trim();
|
|
40
|
+
const id = model.id.trim();
|
|
41
|
+
if (!provider || !id)
|
|
42
|
+
return [];
|
|
43
|
+
const key = `${provider}\u0000${id}`;
|
|
44
|
+
if (seen.has(key))
|
|
45
|
+
return [];
|
|
46
|
+
seen.add(key);
|
|
47
|
+
return [{ provider, id }];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
32
50
|
function runtimeCapabilityError(session, capability) {
|
|
33
51
|
return new AgentRuntimeCapabilityUnavailableError(capability, session.runtimeInstanceId, `Runtime instance "${session.runtimeInstanceId}" does not support ${capability}.`);
|
|
34
52
|
}
|
|
@@ -120,6 +138,10 @@ export class RuntimeRoutedSession {
|
|
|
120
138
|
nextThinkingIndex = 0;
|
|
121
139
|
sessionIdentityOperationInFlight = false;
|
|
122
140
|
forkCandidatesRequest;
|
|
141
|
+
primaryModel;
|
|
142
|
+
modelFallbacks;
|
|
143
|
+
suppressProviderFailures = false;
|
|
144
|
+
pendingProviderFailure;
|
|
123
145
|
unsubscribe;
|
|
124
146
|
constructor(piboSessionId, runtimeSession, emit, pluginRegistry, options = {}) {
|
|
125
147
|
this.piboSessionId = piboSessionId;
|
|
@@ -128,6 +150,10 @@ export class RuntimeRoutedSession {
|
|
|
128
150
|
this.pluginRegistry = pluginRegistry;
|
|
129
151
|
this.options = options;
|
|
130
152
|
this.runtime = runtimeSession.getNativeCompatibilityHandle?.() ?? runtimeSession;
|
|
153
|
+
this.primaryModel = runtimeSession.getStatus().activeModel
|
|
154
|
+
? { ...runtimeSession.getStatus().activeModel }
|
|
155
|
+
: undefined;
|
|
156
|
+
this.modelFallbacks = uniqueModels(options.modelFallbacks ?? []);
|
|
131
157
|
this.unsubscribe = runtimeSession.subscribe((event) => this.handleRuntimeEvent(event));
|
|
132
158
|
}
|
|
133
159
|
enqueueMessage(event, onAccepted = () => { }) {
|
|
@@ -407,7 +433,9 @@ export class RuntimeRoutedSession {
|
|
|
407
433
|
const setModel = this.runtimeSession.controls?.setModel;
|
|
408
434
|
if (!setModel)
|
|
409
435
|
throw runtimeCapabilityError(this.runtimeSession, "in-session model switching");
|
|
410
|
-
|
|
436
|
+
const selected = await setModel(model);
|
|
437
|
+
this.primaryModel = { ...selected };
|
|
438
|
+
return selected;
|
|
411
439
|
}
|
|
412
440
|
setThinkingLevel(level) {
|
|
413
441
|
const setReasoning = this.runtimeSession.controls?.setReasoning;
|
|
@@ -642,10 +670,10 @@ export class RuntimeRoutedSession {
|
|
|
642
670
|
}));
|
|
643
671
|
return;
|
|
644
672
|
case "error":
|
|
645
|
-
this.
|
|
673
|
+
this.handleRuntimeFailure(event.message, event.details);
|
|
646
674
|
return;
|
|
647
675
|
case "turn_failed":
|
|
648
|
-
this.
|
|
676
|
+
this.handleRuntimeFailure(event.message, event.details);
|
|
649
677
|
return;
|
|
650
678
|
case "native_event":
|
|
651
679
|
this.options.onNativeEventTelemetry?.(this.piboSessionId, event.event, {
|
|
@@ -661,6 +689,14 @@ export class RuntimeRoutedSession {
|
|
|
661
689
|
return;
|
|
662
690
|
}
|
|
663
691
|
}
|
|
692
|
+
handleRuntimeFailure(message, details) {
|
|
693
|
+
const normalized = normalizeSessionErrorDetails(message, details);
|
|
694
|
+
if (this.suppressProviderFailures && isPiboProviderFallbackError(message, normalized)) {
|
|
695
|
+
this.pendingProviderFailure = { message, details: normalized };
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
this.emitRuntimeError(message, normalized);
|
|
699
|
+
}
|
|
664
700
|
emitRuntimeError(message, details) {
|
|
665
701
|
if (this.activeMessageFailed)
|
|
666
702
|
return;
|
|
@@ -706,6 +742,71 @@ export class RuntimeRoutedSession {
|
|
|
706
742
|
this.notifyState();
|
|
707
743
|
}
|
|
708
744
|
}
|
|
745
|
+
async promptWithModelFallbacks(event, inFlight) {
|
|
746
|
+
const setModel = this.runtimeSession.controls?.setModel;
|
|
747
|
+
const fallbackModels = this.modelFallbacks.filter((model) => !sameModel(model, this.primaryModel));
|
|
748
|
+
if (!setModel || fallbackModels.length === 0) {
|
|
749
|
+
await this.runtimeSession.prompt({
|
|
750
|
+
text: event.text,
|
|
751
|
+
source: promptSource(event.source),
|
|
752
|
+
capabilityScope: event.capabilityScope,
|
|
753
|
+
});
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
this.suppressProviderFailures = true;
|
|
757
|
+
let switchedModel = false;
|
|
758
|
+
let finalFailure;
|
|
759
|
+
try {
|
|
760
|
+
for (let attempt = 0; attempt <= fallbackModels.length; attempt += 1) {
|
|
761
|
+
if (this.disposed || inFlight.cancelled)
|
|
762
|
+
return;
|
|
763
|
+
if (attempt > 0) {
|
|
764
|
+
try {
|
|
765
|
+
await setModel(fallbackModels[attempt - 1]);
|
|
766
|
+
switchedModel = true;
|
|
767
|
+
}
|
|
768
|
+
catch {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
this.pendingProviderFailure = undefined;
|
|
773
|
+
let promptError;
|
|
774
|
+
try {
|
|
775
|
+
await this.runtimeSession.prompt({
|
|
776
|
+
text: attempt === 0 ? event.text : PIBO_PROVIDER_RECOVERY_PROMPT,
|
|
777
|
+
source: attempt === 0 ? promptSource(event.source) : "rpc",
|
|
778
|
+
capabilityScope: event.capabilityScope,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
promptError = error;
|
|
783
|
+
}
|
|
784
|
+
if (this.disposed || inFlight.cancelled)
|
|
785
|
+
return;
|
|
786
|
+
const failure = this.pendingProviderFailure;
|
|
787
|
+
if (!failure) {
|
|
788
|
+
if (promptError)
|
|
789
|
+
throw promptError;
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
finalFailure = failure;
|
|
793
|
+
}
|
|
794
|
+
if (finalFailure)
|
|
795
|
+
this.emitRuntimeError(finalFailure.message, finalFailure.details);
|
|
796
|
+
}
|
|
797
|
+
finally {
|
|
798
|
+
this.suppressProviderFailures = false;
|
|
799
|
+
this.pendingProviderFailure = undefined;
|
|
800
|
+
if (switchedModel && this.primaryModel && !this.disposed) {
|
|
801
|
+
try {
|
|
802
|
+
await setModel(this.primaryModel);
|
|
803
|
+
}
|
|
804
|
+
catch (error) {
|
|
805
|
+
console.warn(`Failed to restore primary model for Pibo session "${this.piboSessionId}": ${errorMessage(error)}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
709
810
|
async processQueuedMessage(event) {
|
|
710
811
|
const inFlight = this.beginInFlightMessage(event);
|
|
711
812
|
try {
|
|
@@ -743,11 +844,7 @@ export class RuntimeRoutedSession {
|
|
|
743
844
|
});
|
|
744
845
|
if (inFlight.cancelled)
|
|
745
846
|
return;
|
|
746
|
-
await this.
|
|
747
|
-
text: event.text,
|
|
748
|
-
source: promptSource(event.source),
|
|
749
|
-
capabilityScope: event.capabilityScope,
|
|
750
|
-
});
|
|
847
|
+
await this.promptWithModelFallbacks(event, inFlight);
|
|
751
848
|
if (this.disposed || inFlight.cancelled)
|
|
752
849
|
return;
|
|
753
850
|
if (!this.activeMessageFailed) {
|
|
@@ -776,6 +873,8 @@ export class RuntimeRoutedSession {
|
|
|
776
873
|
}
|
|
777
874
|
}
|
|
778
875
|
finally {
|
|
876
|
+
this.suppressProviderFailures = false;
|
|
877
|
+
this.pendingProviderFailure = undefined;
|
|
779
878
|
this.activeMessage = undefined;
|
|
780
879
|
this.activeMessageFailed = false;
|
|
781
880
|
this.resetContentIndices();
|
|
@@ -290,6 +290,7 @@ export class CodexNativeThreadSession {
|
|
|
290
290
|
recycleProcessAfterInterruptedTurn = false;
|
|
291
291
|
selectedToolNames = new Set();
|
|
292
292
|
observedToolNames = new Set();
|
|
293
|
+
emittedResourceWarnings = new Set();
|
|
293
294
|
toolInventoryWarning;
|
|
294
295
|
constructor(runtimeInstanceId, process, threads, settings, resourceDelivery, binding, structuredUserInput, reloadProcess, productContext, bindingPersistence, firstUseTestFailpoints) {
|
|
295
296
|
this.runtimeInstanceId = runtimeInstanceId;
|
|
@@ -390,6 +391,7 @@ export class CodexNativeThreadSession {
|
|
|
390
391
|
this.assertActive();
|
|
391
392
|
await this.ensureFreshResourcesForTurn();
|
|
392
393
|
this.assertIdle();
|
|
394
|
+
this.emitPendingResourceWarnings();
|
|
393
395
|
this.operationInFlight = true;
|
|
394
396
|
let ownedFirstUseAttempt;
|
|
395
397
|
try {
|
|
@@ -473,6 +475,7 @@ export class CodexNativeThreadSession {
|
|
|
473
475
|
contextUsage: this.settings.currentContextUsage,
|
|
474
476
|
warnings: [
|
|
475
477
|
...diagnostics.filter((entry) => entry.level === "warning").map((entry) => entry.message),
|
|
478
|
+
...this.resourceDelivery.warnings.map((entry) => entry.message),
|
|
476
479
|
...(this.resourceWarning ? [this.resourceWarning] : []),
|
|
477
480
|
...(this.toolInventoryWarning ? [this.toolInventoryWarning] : []),
|
|
478
481
|
],
|
|
@@ -659,6 +662,15 @@ export class CodexNativeThreadSession {
|
|
|
659
662
|
throw new Error(`Native Codex portable-resource credential refresh failed while ${phase}; Pibo will retry while the session remains idle.`);
|
|
660
663
|
}
|
|
661
664
|
}
|
|
665
|
+
emitPendingResourceWarnings() {
|
|
666
|
+
for (const warning of this.resourceDelivery.warnings) {
|
|
667
|
+
const key = `${warning.code}\0${warning.message}`;
|
|
668
|
+
if (this.emittedResourceWarnings.has(key))
|
|
669
|
+
continue;
|
|
670
|
+
this.emittedResourceWarnings.add(key);
|
|
671
|
+
this.emit({ type: "warning", message: warning.message, details: { code: warning.code } });
|
|
672
|
+
}
|
|
673
|
+
}
|
|
662
674
|
updateSelectedToolNames(resourceDelivery) {
|
|
663
675
|
const names = [...new Set(resourceDelivery.enabledToolNames)].sort();
|
|
664
676
|
this.selectedToolNames = new Set(names.slice(0, MAX_INSPECTED_SELECTED_TOOL_NAMES));
|
|
@@ -256,21 +256,25 @@ export class CodexNativeResourceDelivery {
|
|
|
256
256
|
skillRoots;
|
|
257
257
|
selectedSkills;
|
|
258
258
|
expectedMcpServers;
|
|
259
|
+
resources;
|
|
259
260
|
portableTools;
|
|
260
261
|
mcpAccess;
|
|
261
262
|
credentialIssuedAtMs;
|
|
262
263
|
credentialExpiresAtMs;
|
|
263
|
-
|
|
264
|
+
resourceWarnings;
|
|
265
|
+
constructor(environment, threadConfig, developerInstructions, skillRoots, selectedSkills, expectedMcpServers, resources, portableTools, mcpAccess, credentialIssuedAtMs, credentialExpiresAtMs, resourceWarnings = []) {
|
|
264
266
|
this.environment = environment;
|
|
265
267
|
this.threadConfig = threadConfig;
|
|
266
268
|
this.developerInstructions = developerInstructions;
|
|
267
269
|
this.skillRoots = skillRoots;
|
|
268
270
|
this.selectedSkills = selectedSkills;
|
|
269
271
|
this.expectedMcpServers = expectedMcpServers;
|
|
272
|
+
this.resources = resources;
|
|
270
273
|
this.portableTools = portableTools;
|
|
271
274
|
this.mcpAccess = mcpAccess;
|
|
272
275
|
this.credentialIssuedAtMs = credentialIssuedAtMs;
|
|
273
276
|
this.credentialExpiresAtMs = credentialExpiresAtMs;
|
|
277
|
+
this.resourceWarnings = resourceWarnings;
|
|
274
278
|
}
|
|
275
279
|
static async prepare(input) {
|
|
276
280
|
const resourceEnvironment = { ...(input.resources?.getAdapterEnvironment() ?? {}) };
|
|
@@ -323,7 +327,7 @@ export class CodexNativeResourceDelivery {
|
|
|
323
327
|
const selectedSkills = input.resources
|
|
324
328
|
? await Promise.all(input.resources.getInspection().skills.flatMap((skill) => {
|
|
325
329
|
const path = skill.materializedPath;
|
|
326
|
-
return path ? [{ name: skill.name, path }] : [];
|
|
330
|
+
return path ? [{ contributionId: skill.contributionId, name: skill.name, path }] : [];
|
|
327
331
|
}).map(async (skill) => ({ ...skill, path: await realpath(skill.path) })))
|
|
328
332
|
: [];
|
|
329
333
|
const materializedSkillsRoot = input.resources?.getInspection().paths?.skills;
|
|
@@ -339,7 +343,7 @@ export class CodexNativeResourceDelivery {
|
|
|
339
343
|
: { multi_agent: false, multi_agent_v2: false };
|
|
340
344
|
threadConfig.agents = { enabled: input.nativeSubagentsEnabled };
|
|
341
345
|
}
|
|
342
|
-
return new CodexNativeResourceDelivery(resourceEnvironment, Object.keys(threadConfig).length > 0 ? threadConfig : undefined, buildDeveloperInstructions(input.resources), skillRoots, selectedSkills, expectedMcpServers, input.portableTools, mcpAccess, credentialIssuedAtMs, credentialExpiresAtMs);
|
|
346
|
+
return new CodexNativeResourceDelivery(resourceEnvironment, Object.keys(threadConfig).length > 0 ? threadConfig : undefined, buildDeveloperInstructions(input.resources), skillRoots, selectedSkills, expectedMcpServers, input.resources, input.portableTools, mcpAccess, credentialIssuedAtMs, credentialExpiresAtMs);
|
|
343
347
|
}
|
|
344
348
|
catch (error) {
|
|
345
349
|
if (mcpAccess && input.portableTools)
|
|
@@ -350,6 +354,9 @@ export class CodexNativeResourceDelivery {
|
|
|
350
354
|
get hasMcpServers() {
|
|
351
355
|
return this.expectedMcpServers.length > 0;
|
|
352
356
|
}
|
|
357
|
+
get warnings() {
|
|
358
|
+
return this.resourceWarnings;
|
|
359
|
+
}
|
|
353
360
|
get enabledToolNames() {
|
|
354
361
|
return this.expectedMcpServers
|
|
355
362
|
.flatMap((server) => server.toolNames.map((toolName) => `${server.name}/${toolName}`))
|
|
@@ -379,6 +386,7 @@ export class CodexNativeResourceDelivery {
|
|
|
379
386
|
this.credentialExpiresAtMs = expiresAt;
|
|
380
387
|
}
|
|
381
388
|
async configureProcess(client, workspace) {
|
|
389
|
+
this.resourceWarnings = [];
|
|
382
390
|
if (this.skillRoots.length === 0)
|
|
383
391
|
return;
|
|
384
392
|
let response;
|
|
@@ -411,15 +419,35 @@ export class CodexNativeResourceDelivery {
|
|
|
411
419
|
}
|
|
412
420
|
}
|
|
413
421
|
}
|
|
422
|
+
const collisions = new Set();
|
|
414
423
|
for (const skill of this.selectedSkills) {
|
|
415
424
|
const sameNamePaths = loadedSkills.get(skill.name) ?? new Set();
|
|
416
425
|
const conflictingPath = [...sameNamePaths].find((path) => path !== skill.path);
|
|
417
|
-
if (conflictingPath)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (!loadedPaths.has(skill.path))
|
|
426
|
+
if (conflictingPath)
|
|
427
|
+
collisions.add(skill.name);
|
|
428
|
+
if (!loadedPaths.has(skill.path) && !conflictingPath)
|
|
421
429
|
throw new Error(`Codex did not load selected Pibo skill "${skill.name}".`);
|
|
422
430
|
}
|
|
431
|
+
let collisionMessage;
|
|
432
|
+
if (collisions.size > 0) {
|
|
433
|
+
const names = [...collisions].sort().map((name) => `"${name}"`).join(", ");
|
|
434
|
+
collisionMessage = `Codex native skill name collision for ${names}: the selected Pibo skill remains materialized, but precedence is ambiguous. Rename or disable one source to remove the collision.`;
|
|
435
|
+
this.resourceWarnings = [{
|
|
436
|
+
code: "codex_native_skill_name_collision",
|
|
437
|
+
message: collisionMessage,
|
|
438
|
+
}];
|
|
439
|
+
}
|
|
440
|
+
this.resources?.recordAdapterDelivery?.(this.selectedSkills.map((skill) => {
|
|
441
|
+
const collided = collisions.has(skill.name);
|
|
442
|
+
return {
|
|
443
|
+
contributionId: skill.contributionId,
|
|
444
|
+
status: collided ? "degraded" : "delivered",
|
|
445
|
+
mode: "codex-extra-roots",
|
|
446
|
+
fidelity: collided ? "lossy" : "exact",
|
|
447
|
+
target: skill.path,
|
|
448
|
+
...(collided && collisionMessage ? { diagnostic: collisionMessage } : {}),
|
|
449
|
+
};
|
|
450
|
+
}));
|
|
423
451
|
}
|
|
424
452
|
async verifyThread(client, threadId) {
|
|
425
453
|
if (this.expectedMcpServers.length === 0)
|
|
@@ -631,13 +631,9 @@ class OmpAgentRuntimeAdapter {
|
|
|
631
631
|
}
|
|
632
632
|
return emptyOmpHistoryPage(this.instanceId);
|
|
633
633
|
}
|
|
634
|
-
async resolveBinding() {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
runtimeInstanceId: this.instanceId,
|
|
638
|
-
adapterId: OMP_ADAPTER_ID,
|
|
639
|
-
state: "unbound",
|
|
640
|
-
};
|
|
634
|
+
async resolveBinding(input) {
|
|
635
|
+
// OMP can verify and resume the transcript only after startup via switch_session.
|
|
636
|
+
return structuredClone(input.binding);
|
|
641
637
|
}
|
|
642
638
|
}
|
|
643
639
|
export const OMP_AGENT_RUNTIME_DRIVER = {
|
|
@@ -135,6 +135,7 @@ function cloneProfileForPiSession(input) {
|
|
|
135
135
|
parentSessionId: profile.parentSessionId,
|
|
136
136
|
model: profile.model,
|
|
137
137
|
mainModel: profile.mainModel,
|
|
138
|
+
mainModelFallbacks: profile.mainModelFallbacks,
|
|
138
139
|
subagentModel: profile.subagentModel,
|
|
139
140
|
thinkingLevel: profile.thinkingLevel,
|
|
140
141
|
mainThinkingLevel: profile.mainThinkingLevel,
|
|
@@ -148,6 +149,7 @@ function cloneProfileForPiSession(input) {
|
|
|
148
149
|
mcpServers: profile.mcpServers,
|
|
149
150
|
piPackages: profile.piPackages,
|
|
150
151
|
contextFiles: profile.contextFiles,
|
|
152
|
+
diagnostics: profile.diagnostics,
|
|
151
153
|
builtinTools: profile.builtinTools,
|
|
152
154
|
builtinToolNames: profile.builtinToolNames,
|
|
153
155
|
autoContextFiles: profile.autoContextFiles,
|
|
@@ -276,7 +278,7 @@ class PiAgentRuntimeSession {
|
|
|
276
278
|
bindingNativeSessionId;
|
|
277
279
|
nativePresenceExpected;
|
|
278
280
|
disposed = false;
|
|
279
|
-
constructor(runtimeInstanceId, piboSessionId, runtime, binding, initialFastMode, providerWebSearchEnabled) {
|
|
281
|
+
constructor(runtimeInstanceId, piboSessionId, runtime, binding, initialFastMode, providerWebSearchEnabled, providerFallbacksEnabled) {
|
|
280
282
|
this.runtimeInstanceId = runtimeInstanceId;
|
|
281
283
|
this.piboSessionId = piboSessionId;
|
|
282
284
|
this.runtime = runtime;
|
|
@@ -284,7 +286,7 @@ class PiAgentRuntimeSession {
|
|
|
284
286
|
this.cwd = runtime.cwd;
|
|
285
287
|
this.bindingNativeSessionId = runtime.session.sessionId;
|
|
286
288
|
this.nativePresenceExpected = runtime.session.sessionManager.buildSessionContext().messages.length > 0;
|
|
287
|
-
this.routed = new PiRoutedSession(piboSessionId, runtime, (event) => this.handlePiboEvent(event), PiboPluginRegistry.create(), true, undefined, initialFastMode, undefined, undefined, (state) => this.handleEngineState(state));
|
|
289
|
+
this.routed = new PiRoutedSession(piboSessionId, runtime, (event) => this.handlePiboEvent(event), PiboPluginRegistry.create(), true, undefined, initialFastMode, undefined, undefined, (state) => this.handleEngineState(state), undefined, undefined, providerFallbacksEnabled);
|
|
288
290
|
if (providerWebSearchEnabled)
|
|
289
291
|
this.routed.enableProviderWebSearchObservation();
|
|
290
292
|
this.controls = this.createControls();
|
|
@@ -685,7 +687,7 @@ class PiAgentRuntimeAdapter {
|
|
|
685
687
|
&& runtime.session.sessionManager.buildSessionContext().messages.length > 0,
|
|
686
688
|
},
|
|
687
689
|
};
|
|
688
|
-
return new PiAgentRuntimeSession(this.instanceId, input.piboSession.id, runtime, binding, compatibility?.initialFastMode ?? false, profile.tools.some((tool) => tool.enabled !== false && isWebSearchProviderTool(tool)));
|
|
690
|
+
return new PiAgentRuntimeSession(this.instanceId, input.piboSession.id, runtime, binding, compatibility?.initialFastMode ?? false, profile.tools.some((tool) => tool.enabled !== false && isWebSearchProviderTool(tool)), compatibility?.providerFallbacksEnabled ?? false);
|
|
689
691
|
}
|
|
690
692
|
loadModelCatalog() {
|
|
691
693
|
const now = Date.now();
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
1
3
|
import { SessionManager, shouldCompact } from "@earendil-works/pi-coding-agent";
|
|
2
4
|
import { PiboSteeringUnavailableError } from "../../core/events.js";
|
|
3
5
|
import { getOpenAiCodexProviderUsageForActiveModel } from "../../auth/openai-codex-usage.js";
|
|
@@ -18,6 +20,16 @@ const RUN_REMINDER_CAPABILITY_TOOLS = new Set([
|
|
|
18
20
|
"pibo_run_cancel",
|
|
19
21
|
"pibo_run_ack",
|
|
20
22
|
]);
|
|
23
|
+
function materializeForkedSession(sessionManager) {
|
|
24
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
25
|
+
const header = sessionManager.getHeader();
|
|
26
|
+
if (!sessionManager.isPersisted() || !sessionFile || !header || existsSync(sessionFile))
|
|
27
|
+
return;
|
|
28
|
+
mkdirSync(dirname(sessionFile), { recursive: true });
|
|
29
|
+
writeFileSync(sessionFile, `${[header, ...sessionManager.getEntries()].map((entry) => JSON.stringify(entry)).join("\n")}\n`, { encoding: "utf8", flag: "wx" });
|
|
30
|
+
// Reload through the public API so Pi also records that the file is flushed.
|
|
31
|
+
sessionManager.setSessionFile(sessionFile);
|
|
32
|
+
}
|
|
21
33
|
function modelSupportsFastServiceTier(model) {
|
|
22
34
|
if (!model)
|
|
23
35
|
return false;
|
|
@@ -587,6 +599,7 @@ export class RoutedSession {
|
|
|
587
599
|
onStateChange;
|
|
588
600
|
onMessagesInterrupted;
|
|
589
601
|
messagePreflight;
|
|
602
|
+
providerFallbacksEnabled;
|
|
590
603
|
queue = [];
|
|
591
604
|
processing = false;
|
|
592
605
|
disposed = false;
|
|
@@ -605,6 +618,9 @@ export class RoutedSession {
|
|
|
605
618
|
nextAssistantIndex = 0;
|
|
606
619
|
activeThinkingIndex;
|
|
607
620
|
nextThinkingIndex = 0;
|
|
621
|
+
nextUsageIndex = 0;
|
|
622
|
+
activeCompactionIndex;
|
|
623
|
+
nextCompactionIndex = 0;
|
|
608
624
|
sessionIdentityOperationInFlight = false;
|
|
609
625
|
pendingAssistantError;
|
|
610
626
|
pendingAssistantErrorRetryable = false;
|
|
@@ -616,7 +632,7 @@ export class RoutedSession {
|
|
|
616
632
|
recoverySession;
|
|
617
633
|
isContinuePatched = false;
|
|
618
634
|
compatibilityAuthController;
|
|
619
|
-
constructor(piboSessionId, runtime, emit, pluginRegistry, forwardPiEvents, onPiEventTelemetry, initialFastMode, onSessionOperation, onKillChildren, onStateChange, onMessagesInterrupted, messagePreflight) {
|
|
635
|
+
constructor(piboSessionId, runtime, emit, pluginRegistry, forwardPiEvents, onPiEventTelemetry, initialFastMode, onSessionOperation, onKillChildren, onStateChange, onMessagesInterrupted, messagePreflight, providerFallbacksEnabled = false) {
|
|
620
636
|
this.piboSessionId = piboSessionId;
|
|
621
637
|
this.runtime = runtime;
|
|
622
638
|
this.emit = emit;
|
|
@@ -628,6 +644,7 @@ export class RoutedSession {
|
|
|
628
644
|
this.onStateChange = onStateChange;
|
|
629
645
|
this.onMessagesInterrupted = onMessagesInterrupted;
|
|
630
646
|
this.messagePreflight = messagePreflight;
|
|
647
|
+
this.providerFallbacksEnabled = providerFallbacksEnabled;
|
|
631
648
|
this.fastMode = initialFastMode && this.fastModeSupported();
|
|
632
649
|
this.bindRuntimeSession();
|
|
633
650
|
this.patchProviderRequest();
|
|
@@ -1005,26 +1022,34 @@ export class RoutedSession {
|
|
|
1005
1022
|
const candidate = event;
|
|
1006
1023
|
const eventId = this.activeMessage?.id ?? this.activeExecutionEvent?.id;
|
|
1007
1024
|
if (candidate.type === "compaction_start") {
|
|
1025
|
+
const compactionIndex = this.activeCompactionIndex ?? this.nextCompactionIndex;
|
|
1026
|
+
if (this.activeCompactionIndex === undefined)
|
|
1027
|
+
this.nextCompactionIndex += 1;
|
|
1028
|
+
this.activeCompactionIndex = compactionIndex;
|
|
1008
1029
|
this.emit(this.withActiveMessage({
|
|
1009
1030
|
type: "compaction_start",
|
|
1010
1031
|
piboSessionId: this.piboSessionId,
|
|
1011
1032
|
eventId,
|
|
1033
|
+
compactionIndex,
|
|
1012
1034
|
reason: typeof candidate.reason === "string" ? candidate.reason : "unknown",
|
|
1013
1035
|
}));
|
|
1014
1036
|
}
|
|
1015
1037
|
if (candidate.type === "compaction_end") {
|
|
1038
|
+
const compactionIndex = this.activeCompactionIndex ?? this.nextCompactionIndex;
|
|
1039
|
+
if (this.activeCompactionIndex === undefined)
|
|
1040
|
+
this.nextCompactionIndex += 1;
|
|
1041
|
+
this.activeCompactionIndex = undefined;
|
|
1016
1042
|
if (candidate.result && candidate.aborted !== true) {
|
|
1017
|
-
//
|
|
1018
|
-
//
|
|
1043
|
+
// End any open output block, but keep durable part indices monotonic for
|
|
1044
|
+
// the lifetime of the routed event across context-guard continuation.
|
|
1019
1045
|
this.activeAssistantIndex = undefined;
|
|
1020
|
-
this.nextAssistantIndex = 0;
|
|
1021
1046
|
this.activeThinkingIndex = undefined;
|
|
1022
|
-
this.nextThinkingIndex = 0;
|
|
1023
1047
|
}
|
|
1024
1048
|
this.emit(this.withActiveMessage({
|
|
1025
1049
|
type: "compaction_end",
|
|
1026
1050
|
piboSessionId: this.piboSessionId,
|
|
1027
1051
|
eventId,
|
|
1052
|
+
compactionIndex,
|
|
1028
1053
|
reason: typeof candidate.reason === "string" ? candidate.reason : "unknown",
|
|
1029
1054
|
result: candidate.result,
|
|
1030
1055
|
aborted: candidate.aborted === true,
|
|
@@ -1213,6 +1238,11 @@ export class RoutedSession {
|
|
|
1213
1238
|
this.assertSessionWorkIdle("fork");
|
|
1214
1239
|
const previous = this.createSessionSnapshot();
|
|
1215
1240
|
const result = await this.runtime.fork(entryId);
|
|
1241
|
+
if (!result.cancelled) {
|
|
1242
|
+
// Pi defers persistence until an assistant message. Materialize an empty
|
|
1243
|
+
// first-message branch so history readers can use its advertised path.
|
|
1244
|
+
materializeForkedSession(this.runtime.session.sessionManager);
|
|
1245
|
+
}
|
|
1216
1246
|
return {
|
|
1217
1247
|
piboSessionId: this.piboSessionId,
|
|
1218
1248
|
previous,
|
|
@@ -1518,6 +1548,9 @@ export class RoutedSession {
|
|
|
1518
1548
|
this.nextAssistantIndex = 0;
|
|
1519
1549
|
this.activeThinkingIndex = undefined;
|
|
1520
1550
|
this.nextThinkingIndex = 0;
|
|
1551
|
+
this.nextUsageIndex = 0;
|
|
1552
|
+
this.activeCompactionIndex = undefined;
|
|
1553
|
+
this.nextCompactionIndex = 0;
|
|
1521
1554
|
this.emit({
|
|
1522
1555
|
type: "message_started",
|
|
1523
1556
|
piboSessionId: this.piboSessionId,
|
|
@@ -1539,7 +1572,8 @@ export class RoutedSession {
|
|
|
1539
1572
|
await this.resumeContextGuardRecovery(session);
|
|
1540
1573
|
if (this.disposed || inFlight.cancelled)
|
|
1541
1574
|
return;
|
|
1542
|
-
|
|
1575
|
+
if (!this.providerFallbacksEnabled)
|
|
1576
|
+
await this.recoverTransientProviderErrors(session);
|
|
1543
1577
|
if (this.disposed || inFlight.cancelled)
|
|
1544
1578
|
return;
|
|
1545
1579
|
this.flushPendingAssistantError();
|
|
@@ -1578,6 +1612,9 @@ export class RoutedSession {
|
|
|
1578
1612
|
this.nextAssistantIndex = 0;
|
|
1579
1613
|
this.activeThinkingIndex = undefined;
|
|
1580
1614
|
this.nextThinkingIndex = 0;
|
|
1615
|
+
this.nextUsageIndex = 0;
|
|
1616
|
+
this.activeCompactionIndex = undefined;
|
|
1617
|
+
this.nextCompactionIndex = 0;
|
|
1581
1618
|
this.providerWebSearchLifecycle.clear();
|
|
1582
1619
|
if (this.inFlightMessage === inFlight)
|
|
1583
1620
|
this.inFlightMessage = undefined;
|
|
@@ -1817,8 +1854,12 @@ export class RoutedSession {
|
|
|
1817
1854
|
this.activeThinkingIndex = undefined;
|
|
1818
1855
|
return output;
|
|
1819
1856
|
}
|
|
1820
|
-
if (event.type === "assistant_usage"
|
|
1821
|
-
|
|
1857
|
+
if (event.type === "assistant_usage") {
|
|
1858
|
+
const usageIndex = this.nextUsageIndex;
|
|
1859
|
+
this.nextUsageIndex += 1;
|
|
1860
|
+
return { ...event, ...correlation, usageIndex };
|
|
1861
|
+
}
|
|
1862
|
+
if (event.type === "compaction_start" ||
|
|
1822
1863
|
event.type === "compaction_end" ||
|
|
1823
1864
|
event.type === "tool_call" ||
|
|
1824
1865
|
event.type === "tool_execution_started" ||
|