@pasko70/pibo 3.0.1 → 3.1.0
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/compute-image/Dockerfile +47 -0
- package/compute-image/Dockerfile.dockerignore +5 -0
- package/dist/agent-runtime/context-build.js +26 -2
- package/dist/agent-runtime/contract.js +78 -28
- package/dist/agent-runtime/portable-history.js +19 -9
- package/dist/agent-runtime/registry.js +8 -2
- package/dist/agent-runtime/routed-session.js +120 -15
- 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 +59 -12
- 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 +98 -6
- 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/project-service.js +119 -15
- package/dist/apps/chat/data/room-service.js +21 -0
- 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/loop-api.js +10 -2
- 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 +317 -86
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CUQJqggN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-ClUlQWYN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-Djul9BmZ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-GD0JGdCi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-W5HOqHym.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BcjkX-iP.js +228 -0
- package/dist/apps/chat-ui/assets/index-CmqRSbBU.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-JvrPUGvI.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +111 -12
- 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/compute/cli.js +35 -16
- package/dist/compute/docker.js +341 -94
- package/dist/config/config.js +3 -0
- 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 +78 -110
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/pibo-store.js +8 -1
- package/dist/data/schema.js +243 -3
- package/dist/data/session-store.js +3 -3
- package/dist/debug/agents.js +91 -89
- package/dist/debug/index.js +46 -2
- package/dist/debug/pty.js +127 -48
- package/dist/gateway/cli.js +4 -0
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- package/dist/loops/channel.js +3 -1
- package/dist/loops/cli.js +2 -1
- package/dist/loops/service.js +90 -11
- package/dist/loops/store.js +100 -74
- package/dist/mcp/agent-context.js +13 -26
- package/dist/mcp/commands/info.js +3 -1
- package/dist/mcp/config-command.js +46 -2
- package/dist/mcp/config.js +18 -4
- package/dist/plugins/context-files-store.js +36 -6
- 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/previews/base-url.js +34 -0
- package/dist/previews/config.js +2 -32
- package/dist/ralph/store.js +5 -0
- package/dist/reliability/store.js +378 -106
- package/dist/session-ui/terminalRows.js +8 -4
- 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/observation-query.js +121 -0
- package/dist/subagents/tool.js +7 -3
- package/dist/tools/agent-browser-leases.js +58 -23
- package/dist/tools/agent-browser-wrapper.js +1 -1
- package/dist/tools/browser-use-leases.js +129 -16
- package/dist/tools/browser-use-wrapper.js +1 -1
- package/dist/tools/index.js +27 -11
- package/dist/tools/python-runtime.js +10 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/runtime/node-backend.js +16 -5
- package/dist/tools/runtime/node-worker-source.js +112 -33
- package/dist/tools/runtime/python-backend.js +16 -5
- package/dist/tools/runtime/python-worker-source.js +167 -16
- package/dist/tools/runtime/registry.js +23 -10
- package/dist/user-skills/store.js +0 -1
- package/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +9 -2
- package/package.json +9 -2
- package/scripts/docker-entrypoint.sh +46 -0
- package/scripts/prepare-agent-browser-wrapper.sh +70 -0
- package/scripts/prepare-browser-use-wrapper.sh +255 -0
- 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/chat-vscode-web/assets/index-Dtw2Z7jz.js +0 -43
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.1.vsix +0 -0
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { DEFAULT_PIBO_PROFILE_NAME,
|
|
2
|
+
import { DEFAULT_PIBO_PROFILE_NAME, resolvePiboProfileNameFromRegistryOrDefault, } from "../plugins/builtin.js";
|
|
3
|
+
import { createDefaultPiboUserProfileRegistry } from "../plugins/user-profile-resources.js";
|
|
3
4
|
import { createDefaultPiboDataSessionStore } from "../sessions/pibo-data-store.js";
|
|
5
|
+
import { OutputRenderSequencer, outputRenderHighWaterStore, validRenderSequence } from "../core/output-render-sequence.js";
|
|
6
|
+
import { OutputPersistenceRetryQueue, } from "../core/output-persistence-retry.js";
|
|
4
7
|
import { getDefaultPiboWorkspace } from "../core/workspace.js";
|
|
5
8
|
import { buildTraceView } from "../apps/chat/trace.js";
|
|
6
9
|
import { storedPiboEventFromV2Row, } from "../apps/chat/data/chat-data-mappers.js";
|
|
7
|
-
import { ChatDataIngestService } from "../data/ingest-service.js";
|
|
10
|
+
import { ChatDataIngestService, outputIdempotencyKey, outputPersistenceDeliveryKey } from "../data/ingest-service.js";
|
|
11
|
+
import { OutputCompactor } from "../apps/chat/output-compactor.js";
|
|
12
|
+
import { isPiboOutputEvent } from "../apps/chat/output-event-policy.js";
|
|
8
13
|
import { ChatRoomService } from "../apps/chat/data/room-service.js";
|
|
9
14
|
import { ChatHistoryQueryService } from "../apps/chat/data/history-query-service.js";
|
|
15
|
+
import { createDefaultPiboReliabilityStore, } from "../reliability/store.js";
|
|
10
16
|
import { buildSlashCommandCatalog, normalizeCommandErrorDescriptor, normalizeCommandResultDescriptor, } from "../session-ui/index.js";
|
|
17
|
+
import { patchTraceViewWithEvent } from "../shared/trace-engine.js";
|
|
11
18
|
import { CliSourceError, } from "./sessionSource.js";
|
|
12
19
|
export const CLI_LOCAL_RECOVERY_SOURCE_ID = "local:root";
|
|
13
20
|
export const CLI_LOCAL_RECOVERY_SOURCE_LABEL = "Local recovery";
|
|
@@ -20,9 +27,14 @@ export class LocalCliSessionSource {
|
|
|
20
27
|
ownsRouter;
|
|
21
28
|
unsubscribeRouter;
|
|
22
29
|
now;
|
|
30
|
+
outputRenderSequencer;
|
|
31
|
+
outputCompactor = new OutputCompactor();
|
|
32
|
+
outputPersistenceRetries;
|
|
23
33
|
statusMessage;
|
|
24
34
|
dataStore;
|
|
25
35
|
ownsDataStore;
|
|
36
|
+
reliabilityStore;
|
|
37
|
+
ownsReliabilityStore;
|
|
26
38
|
ingestService;
|
|
27
39
|
roomService;
|
|
28
40
|
agentSummaries;
|
|
@@ -39,14 +51,28 @@ export class LocalCliSessionSource {
|
|
|
39
51
|
options.ownsSessionStore ?? options.sessionStore === undefined;
|
|
40
52
|
this.roomProvider = options.roomProvider;
|
|
41
53
|
this.pluginRegistry =
|
|
42
|
-
options.pluginRegistry ??
|
|
54
|
+
options.pluginRegistry ?? createDefaultPiboUserProfileRegistry();
|
|
43
55
|
this.router = options.router;
|
|
44
56
|
this.ownsRouter = options.ownsRouter ?? false;
|
|
45
|
-
this.unsubscribeRouter = this.router?.subscribe((event) => this.handleRouterEvent(event));
|
|
46
57
|
this.now = options.now ?? (() => new Date().toISOString());
|
|
58
|
+
this.outputRenderSequencer = new OutputRenderSequencer({
|
|
59
|
+
now: () => {
|
|
60
|
+
const timestamp = Date.parse(this.now());
|
|
61
|
+
return Number.isFinite(timestamp) ? timestamp : Date.now();
|
|
62
|
+
},
|
|
63
|
+
highWaterStore: outputRenderHighWaterStore(this.sessionStore),
|
|
64
|
+
});
|
|
47
65
|
this.statusMessage = options.statusMessage;
|
|
48
66
|
this.dataStore = options.dataStore;
|
|
49
67
|
this.ownsDataStore = options.ownsDataStore === true;
|
|
68
|
+
this.reliabilityStore = options.reliabilityStore
|
|
69
|
+
?? (this.ownsDataStore ? createDefaultPiboReliabilityStore() : undefined);
|
|
70
|
+
this.ownsReliabilityStore = options.ownsReliabilityStore
|
|
71
|
+
?? (this.ownsDataStore && options.reliabilityStore === undefined);
|
|
72
|
+
this.outputPersistenceRetries = new OutputPersistenceRetryQueue({
|
|
73
|
+
durableStore: this.reliabilityStore,
|
|
74
|
+
queueName: "output-persistence-cli",
|
|
75
|
+
});
|
|
50
76
|
this.ingestService = options.dataStore
|
|
51
77
|
? new ChatDataIngestService(options.dataStore)
|
|
52
78
|
: undefined;
|
|
@@ -56,6 +82,13 @@ export class LocalCliSessionSource {
|
|
|
56
82
|
this.agentSummaries = options.agentSummaries
|
|
57
83
|
? [...options.agentSummaries]
|
|
58
84
|
: undefined;
|
|
85
|
+
this.outputPersistenceRetries.recover((job) => {
|
|
86
|
+
const persistenceState = parseCliOutputPersistenceState(job.payload);
|
|
87
|
+
if (!persistenceState)
|
|
88
|
+
throw new Error(`Invalid durable CLI output persistence job ${job.key}`);
|
|
89
|
+
return this.createOutputPersistenceJob(persistenceState);
|
|
90
|
+
});
|
|
91
|
+
this.unsubscribeRouter = this.router?.subscribe((event) => this.handleRouterEvent(event));
|
|
59
92
|
}
|
|
60
93
|
async listRooms() {
|
|
61
94
|
this.assertOpen();
|
|
@@ -179,6 +212,14 @@ export class LocalCliSessionSource {
|
|
|
179
212
|
const session = this.resolveSession(sessionId);
|
|
180
213
|
const eventId = randomUUID();
|
|
181
214
|
this.ingestUserMessage(session, eventId, trimmed);
|
|
215
|
+
this.recordOutputEvent({
|
|
216
|
+
type: "message_queued",
|
|
217
|
+
piboSessionId: sessionId,
|
|
218
|
+
eventId,
|
|
219
|
+
text: trimmed,
|
|
220
|
+
source: "ui",
|
|
221
|
+
queuedMessages: 0,
|
|
222
|
+
});
|
|
182
223
|
if (this.router) {
|
|
183
224
|
try {
|
|
184
225
|
await this.router.emit({
|
|
@@ -195,7 +236,6 @@ export class LocalCliSessionSource {
|
|
|
195
236
|
}
|
|
196
237
|
return;
|
|
197
238
|
}
|
|
198
|
-
this.recordLocalUserMessage(sessionId, eventId, trimmed, "done");
|
|
199
239
|
this.updateSessionStatus(sessionId, "idle");
|
|
200
240
|
}
|
|
201
241
|
async listAgents() {
|
|
@@ -371,14 +411,20 @@ export class LocalCliSessionSource {
|
|
|
371
411
|
}
|
|
372
412
|
try {
|
|
373
413
|
this.closed = true;
|
|
414
|
+
await this.outputPersistenceRetries.drain();
|
|
415
|
+
this.outputPersistenceRetries.dispose();
|
|
374
416
|
for (const handle of [...this.openHandles])
|
|
375
417
|
handle.close();
|
|
376
418
|
this.listeners.clear();
|
|
419
|
+
this.outputRenderSequencer.disposeAll();
|
|
420
|
+
this.outputCompactor.disposeAll();
|
|
377
421
|
this.unsubscribeRouter?.();
|
|
378
422
|
if (this.ownsSessionStore)
|
|
379
423
|
this.sessionStore.close?.();
|
|
380
424
|
if (this.ownsDataStore)
|
|
381
425
|
this.dataStore?.close();
|
|
426
|
+
if (this.ownsReliabilityStore)
|
|
427
|
+
this.reliabilityStore?.close();
|
|
382
428
|
}
|
|
383
429
|
finally {
|
|
384
430
|
this.closing = false;
|
|
@@ -544,170 +590,95 @@ export class LocalCliSessionSource {
|
|
|
544
590
|
handleRouterEvent(event) {
|
|
545
591
|
if (this.closed)
|
|
546
592
|
return;
|
|
593
|
+
if (!isPiboOutputEvent(event)) {
|
|
594
|
+
this.outputPersistenceRetries.quarantine("runtime_output_event_invalid");
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
547
597
|
const session = this.sessionStore.get(event.piboSessionId);
|
|
548
598
|
if (!session)
|
|
549
599
|
return;
|
|
550
|
-
this.
|
|
551
|
-
this.
|
|
600
|
+
const positionedEvent = this.outputRenderSequencer.position(event);
|
|
601
|
+
const compacted = this.outputCompactor.prepare(positionedEvent);
|
|
602
|
+
if (compacted.persistedEvents.length === 0) {
|
|
603
|
+
compacted.ack();
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const persistenceState = {
|
|
607
|
+
version: 1,
|
|
608
|
+
piboSessionId: session.id,
|
|
609
|
+
deliveries: compacted.persistedEvents.map((persistedEvent) => ({ event: persistedEvent })),
|
|
610
|
+
};
|
|
611
|
+
this.outputPersistenceRetries.enqueue(this.createOutputPersistenceJob(persistenceState, () => compacted.ack(), () => compacted.rollback()));
|
|
612
|
+
}
|
|
613
|
+
for (const liveEvent of compacted.liveEvents)
|
|
614
|
+
this.recordOutputEvent(liveEvent);
|
|
615
|
+
}
|
|
616
|
+
createOutputPersistenceJob(persistenceState, onSuccess, onDeadLetter) {
|
|
617
|
+
const firstEvent = persistenceState.deliveries[0]?.event;
|
|
618
|
+
return {
|
|
619
|
+
key: JSON.stringify(persistenceState.deliveries.map((delivery) => outputPersistenceDeliveryKey(delivery.event))),
|
|
620
|
+
piboSessionId: persistenceState.piboSessionId,
|
|
621
|
+
eventId: firstEvent ? outputPersistenceDeliveryKey(firstEvent) : undefined,
|
|
622
|
+
payload: persistenceState,
|
|
623
|
+
run: (retryContext) => this.deliverOutputPersistenceState(retryContext),
|
|
624
|
+
onSuccess,
|
|
625
|
+
onDeadLetter,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
deliverOutputPersistenceState(retryContext) {
|
|
629
|
+
const persistenceState = parseCliOutputPersistenceState(retryContext.payload);
|
|
630
|
+
if (!persistenceState)
|
|
631
|
+
throw new Error("Invalid durable CLI output persistence state");
|
|
632
|
+
const session = this.sessionStore.get(persistenceState.piboSessionId);
|
|
633
|
+
if (!session)
|
|
634
|
+
throw new Error(`No session available for ${persistenceState.piboSessionId}`);
|
|
635
|
+
for (const delivery of persistenceState.deliveries) {
|
|
636
|
+
if (delivery.persisted)
|
|
637
|
+
continue;
|
|
638
|
+
if (!this.ingestOutputEvent(session, delivery.event)) {
|
|
639
|
+
throw new Error(`Failed to persist ${outputPersistenceDeliveryKey(delivery.event)}`);
|
|
640
|
+
}
|
|
641
|
+
delivery.persisted = true;
|
|
642
|
+
retryContext.updatePayload(persistenceState);
|
|
643
|
+
}
|
|
552
644
|
}
|
|
553
645
|
recordOutputEvent(event) {
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
646
|
+
const positionedEvent = validRenderSequence(event.renderSequence)
|
|
647
|
+
? event
|
|
648
|
+
: this.outputRenderSequencer.position(event);
|
|
649
|
+
const sessionId = positionedEvent.piboSessionId;
|
|
650
|
+
const session = this.resolveSession(sessionId);
|
|
651
|
+
const current = this.traceViews.get(sessionId) ?? emptyTraceView(session);
|
|
652
|
+
const status = positionedEvent.type === "session_error"
|
|
653
|
+
? "error"
|
|
654
|
+
: positionedEvent.type === "message_finished"
|
|
655
|
+
? "idle"
|
|
656
|
+
: "running";
|
|
657
|
+
const next = patchTraceViewWithEvent(current, storedEvent(positionedEvent, this.now(), current.rawEvents.length + 1), status === "idle" ? "idle" : status);
|
|
658
|
+
next.eventCount = next.rawEvents.length;
|
|
659
|
+
next.version = `local-${next.rawEvents.length}-${session.updatedAt}`;
|
|
660
|
+
this.traceViews.set(sessionId, next);
|
|
661
|
+
this.emit(sessionId, {
|
|
662
|
+
type: "trace",
|
|
663
|
+
session: sessionToSummary(session, this.ensureDefaultRoom().id),
|
|
664
|
+
traceView: next,
|
|
665
|
+
});
|
|
666
|
+
if (positionedEvent.type === "message_queued" || positionedEvent.type === "message_started") {
|
|
558
667
|
this.updateSessionStatus(sessionId, "running");
|
|
559
|
-
return;
|
|
560
668
|
}
|
|
561
|
-
if (
|
|
669
|
+
else if (positionedEvent.type === "message_finished") {
|
|
562
670
|
this.updateSessionStatus(sessionId, "idle");
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
if (event.type === "assistant_delta") {
|
|
566
|
-
this.upsertTraceNode(sessionId, {
|
|
567
|
-
id: traceNodeId("assistant", eventId, event.assistantIndex ?? event.contentIndex),
|
|
568
|
-
piboSessionId: sessionId,
|
|
569
|
-
type: "assistant.message",
|
|
570
|
-
title: "Agent Message",
|
|
571
|
-
status: "running",
|
|
572
|
-
startedAt: this.now(),
|
|
573
|
-
output: event.text,
|
|
574
|
-
children: [],
|
|
575
|
-
}, { appendOutput: true, rawEvent: event });
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (event.type === "assistant_message") {
|
|
579
|
-
this.upsertTraceNode(sessionId, {
|
|
580
|
-
id: traceNodeId("assistant", eventId, event.assistantIndex ?? event.contentIndex),
|
|
581
|
-
piboSessionId: sessionId,
|
|
582
|
-
type: "assistant.message",
|
|
583
|
-
title: "Agent Message",
|
|
584
|
-
status: "done",
|
|
585
|
-
startedAt: this.now(),
|
|
586
|
-
completedAt: this.now(),
|
|
587
|
-
output: event.text,
|
|
588
|
-
children: [],
|
|
589
|
-
}, { rawEvent: event });
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
if (event.type === "tool_call" ||
|
|
593
|
-
event.type === "tool_execution_started" ||
|
|
594
|
-
event.type === "tool_execution_updated" ||
|
|
595
|
-
event.type === "tool_execution_finished") {
|
|
596
|
-
const finished = event.type === "tool_execution_finished";
|
|
597
|
-
this.upsertTraceNode(sessionId, {
|
|
598
|
-
id: traceNodeId("tool", event.toolCallId),
|
|
599
|
-
piboSessionId: sessionId,
|
|
600
|
-
eventId,
|
|
601
|
-
toolCallId: event.toolCallId,
|
|
602
|
-
type: finished ? "tool.result" : "tool.call",
|
|
603
|
-
title: event.toolName,
|
|
604
|
-
status: finished ? (event.isError ? "error" : "done") : "running",
|
|
605
|
-
startedAt: this.now(),
|
|
606
|
-
completedAt: finished ? this.now() : undefined,
|
|
607
|
-
input: "args" in event ? event.args : undefined,
|
|
608
|
-
output: finished
|
|
609
|
-
? event.result
|
|
610
|
-
: "partialResult" in event
|
|
611
|
-
? event.partialResult
|
|
612
|
-
: undefined,
|
|
613
|
-
children: [],
|
|
614
|
-
}, { rawEvent: event });
|
|
615
|
-
return;
|
|
616
671
|
}
|
|
617
|
-
if (
|
|
618
|
-
this.
|
|
619
|
-
return;
|
|
672
|
+
else if (positionedEvent.type === "session_error") {
|
|
673
|
+
this.updateSessionStatus(sessionId, "error");
|
|
620
674
|
}
|
|
621
|
-
this.appendRawEvent(sessionId, event);
|
|
622
|
-
}
|
|
623
|
-
recordLocalUserMessage(sessionId, eventId, text, status) {
|
|
624
|
-
this.upsertTraceNode(sessionId, {
|
|
625
|
-
id: traceNodeId("user", eventId),
|
|
626
|
-
piboSessionId: sessionId,
|
|
627
|
-
eventId,
|
|
628
|
-
type: "user.message",
|
|
629
|
-
title: "User Message",
|
|
630
|
-
status,
|
|
631
|
-
startedAt: this.now(),
|
|
632
|
-
completedAt: status === "done" ? this.now() : undefined,
|
|
633
|
-
output: text,
|
|
634
|
-
children: [],
|
|
635
|
-
}, {
|
|
636
|
-
rawEvent: {
|
|
637
|
-
type: "message_queued",
|
|
638
|
-
piboSessionId: sessionId,
|
|
639
|
-
eventId,
|
|
640
|
-
queuedMessages: 0,
|
|
641
|
-
text,
|
|
642
|
-
source: "ui",
|
|
643
|
-
},
|
|
644
|
-
});
|
|
645
675
|
}
|
|
646
676
|
recordLocalError(sessionId, eventId, error) {
|
|
647
|
-
this.
|
|
648
|
-
|
|
677
|
+
this.recordOutputEvent({
|
|
678
|
+
type: "session_error",
|
|
649
679
|
piboSessionId: sessionId,
|
|
650
680
|
eventId,
|
|
651
|
-
type: "error",
|
|
652
|
-
title: "CLI source error",
|
|
653
|
-
status: "error",
|
|
654
|
-
startedAt: this.now(),
|
|
655
|
-
completedAt: this.now(),
|
|
656
681
|
error: error instanceof Error ? error.message : String(error),
|
|
657
|
-
children: [],
|
|
658
|
-
}, {
|
|
659
|
-
rawEvent: {
|
|
660
|
-
type: "session_error",
|
|
661
|
-
piboSessionId: sessionId,
|
|
662
|
-
eventId,
|
|
663
|
-
error: error instanceof Error ? error.message : String(error),
|
|
664
|
-
},
|
|
665
|
-
});
|
|
666
|
-
this.updateSessionStatus(sessionId, "error");
|
|
667
|
-
}
|
|
668
|
-
upsertTraceNode(sessionId, node, options = {}) {
|
|
669
|
-
const session = this.resolveSession(sessionId);
|
|
670
|
-
const traceView = cloneJson(this.traceViews.get(sessionId) ?? emptyTraceView(session));
|
|
671
|
-
const index = traceView.nodes.findIndex((candidate) => candidate.id === node.id);
|
|
672
|
-
if (index >= 0) {
|
|
673
|
-
const previous = traceView.nodes[index];
|
|
674
|
-
const output = options.appendOutput &&
|
|
675
|
-
typeof previous.output === "string" &&
|
|
676
|
-
typeof node.output === "string"
|
|
677
|
-
? previous.output + node.output
|
|
678
|
-
: node.output;
|
|
679
|
-
traceView.nodes[index] = {
|
|
680
|
-
...previous,
|
|
681
|
-
...node,
|
|
682
|
-
output,
|
|
683
|
-
children: node.children ?? previous.children,
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
else {
|
|
687
|
-
traceView.nodes.push(node);
|
|
688
|
-
}
|
|
689
|
-
if (options.rawEvent)
|
|
690
|
-
traceView.rawEvents.push(storedEvent(options.rawEvent, this.now(), traceView.rawEvents.length + 1));
|
|
691
|
-
traceView.eventCount = traceView.rawEvents.length;
|
|
692
|
-
traceView.version = `local-${traceView.rawEvents.length}-${session.updatedAt}`;
|
|
693
|
-
this.traceViews.set(sessionId, traceView);
|
|
694
|
-
this.emit(sessionId, {
|
|
695
|
-
type: "trace",
|
|
696
|
-
session: sessionToSummary(session, this.ensureDefaultRoom().id),
|
|
697
|
-
traceView: cloneJson(traceView),
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
appendRawEvent(sessionId, event) {
|
|
701
|
-
const session = this.resolveSession(sessionId);
|
|
702
|
-
const traceView = cloneJson(this.traceViews.get(sessionId) ?? emptyTraceView(session));
|
|
703
|
-
traceView.rawEvents.push(storedEvent(event, this.now(), traceView.rawEvents.length + 1));
|
|
704
|
-
traceView.eventCount = traceView.rawEvents.length;
|
|
705
|
-
traceView.version = `local-${traceView.rawEvents.length}-${session.updatedAt}`;
|
|
706
|
-
this.traceViews.set(sessionId, traceView);
|
|
707
|
-
this.emit(sessionId, {
|
|
708
|
-
type: "trace",
|
|
709
|
-
session: sessionToSummary(session, this.ensureDefaultRoom().id),
|
|
710
|
-
traceView: cloneJson(traceView),
|
|
711
682
|
});
|
|
712
683
|
}
|
|
713
684
|
updateSessionStatus(sessionId, status) {
|
|
@@ -770,7 +741,7 @@ export class LocalCliSessionSource {
|
|
|
770
741
|
}
|
|
771
742
|
ingestOutputEvent(session, event) {
|
|
772
743
|
if (!this.ingestService)
|
|
773
|
-
return;
|
|
744
|
+
return true;
|
|
774
745
|
try {
|
|
775
746
|
this.ingestService.ingestOutputEvent({
|
|
776
747
|
session,
|
|
@@ -779,9 +750,11 @@ export class LocalCliSessionSource {
|
|
|
779
750
|
event,
|
|
780
751
|
createdAt: this.now(),
|
|
781
752
|
});
|
|
753
|
+
return true;
|
|
782
754
|
}
|
|
783
755
|
catch {
|
|
784
|
-
//
|
|
756
|
+
// The caller retains the prepared compaction and retries this exact event identity.
|
|
757
|
+
return false;
|
|
785
758
|
}
|
|
786
759
|
}
|
|
787
760
|
buildStatus(session) {
|
|
@@ -860,6 +833,29 @@ export class LocalCliSessionSource {
|
|
|
860
833
|
export function createLocalCliSessionSource(options = {}) {
|
|
861
834
|
return new LocalCliSessionSource(options);
|
|
862
835
|
}
|
|
836
|
+
function parseCliOutputPersistenceState(value) {
|
|
837
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
838
|
+
return undefined;
|
|
839
|
+
const candidate = value;
|
|
840
|
+
if (candidate.version !== 1 || typeof candidate.piboSessionId !== "string" || !Array.isArray(candidate.deliveries))
|
|
841
|
+
return undefined;
|
|
842
|
+
const deliveries = [];
|
|
843
|
+
for (const rawDelivery of candidate.deliveries) {
|
|
844
|
+
if (!rawDelivery || typeof rawDelivery !== "object" || Array.isArray(rawDelivery))
|
|
845
|
+
return undefined;
|
|
846
|
+
const delivery = rawDelivery;
|
|
847
|
+
const event = delivery.event;
|
|
848
|
+
if (!isPiboOutputEvent(event) || !outputIdempotencyKey(event))
|
|
849
|
+
return undefined;
|
|
850
|
+
deliveries.push({
|
|
851
|
+
event: event,
|
|
852
|
+
...(delivery.persisted === true ? { persisted: true } : {}),
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
if (!deliveries.length)
|
|
856
|
+
return undefined;
|
|
857
|
+
return { version: 1, piboSessionId: candidate.piboSessionId, deliveries };
|
|
858
|
+
}
|
|
863
859
|
export function redactCliSecretText(text) {
|
|
864
860
|
return text
|
|
865
861
|
.replace(/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD)[A-Z0-9_]*)\s*=\s*([^\s]+)/gi, "$1=[redacted]")
|
|
@@ -989,17 +985,13 @@ function storedEvent(event, createdAt, eventSequence) {
|
|
|
989
985
|
id: eventId ?? `${event.piboSessionId}-${event.type}-${eventSequence}`,
|
|
990
986
|
piboSessionId: event.piboSessionId,
|
|
991
987
|
eventSequence,
|
|
988
|
+
renderSequence: event.renderSequence,
|
|
992
989
|
eventId,
|
|
993
990
|
type: event.type,
|
|
994
991
|
createdAt,
|
|
995
992
|
payload: cloneJson(event),
|
|
996
993
|
};
|
|
997
994
|
}
|
|
998
|
-
function traceNodeId(kind, eventId, index) {
|
|
999
|
-
return ["local", kind, eventId ?? "event", index]
|
|
1000
|
-
.filter((part) => part !== undefined)
|
|
1001
|
-
.join(":");
|
|
1002
|
-
}
|
|
1003
995
|
function toCliSourceError(code, message, cause) {
|
|
1004
996
|
return cause instanceof CliSourceError
|
|
1005
997
|
? cause
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { PIBO_CONFIG_KEYS, getDefaultPiboConfigPath, deletePiboConfigValue, getDisplayPiboConfigValue, loadPiboConfig, redactPiboConfig, savePiboConfig, setPiboConfigValue, } from "./config/config.js";
|
|
4
4
|
import { parsePiboThinkingLevel } from "./core/thinking.js";
|
|
5
|
-
import { ensurePrivatePiboHome
|
|
5
|
+
import { ensurePrivatePiboHome } from "./core/pibo-home.js";
|
|
6
6
|
async function resolveCliProfile(profileName) {
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
if (existsSync(chatAgentStorePath)) {
|
|
11
|
-
const { createPiboChatCustomAgentProfilesPlugin } = await import("./plugins/chat-custom-agents.js");
|
|
12
|
-
registry.registerPlugin(createPiboChatCustomAgentProfilesPlugin({ agentStorePath: chatAgentStorePath }));
|
|
13
|
-
}
|
|
7
|
+
const { createGatewayProducerPiboProfile, createPiboProfileFromRegistryOrDefault } = await import("./plugins/builtin.js");
|
|
8
|
+
const { createDefaultPiboUserProfileRegistry } = await import("./plugins/user-profile-resources.js");
|
|
9
|
+
const registry = createDefaultPiboUserProfileRegistry();
|
|
14
10
|
const profile = profileName === "gateway-producer" || profileName === "pibo-gateway-producer"
|
|
15
11
|
? createGatewayProducerPiboProfile()
|
|
16
12
|
: createPiboProfileFromRegistryOrDefault(registry, profileName);
|
|
@@ -281,7 +277,7 @@ export async function runPiboCli(argv = process.argv) {
|
|
|
281
277
|
});
|
|
282
278
|
program
|
|
283
279
|
.command("setup")
|
|
284
|
-
.description("Plan
|
|
280
|
+
.description("Plan and manage supported host installation profiles")
|
|
285
281
|
.helpOption(false)
|
|
286
282
|
.allowUnknownOption(true)
|
|
287
283
|
.allowExcessArguments(true)
|
|
@@ -533,7 +529,7 @@ Commands:
|
|
|
533
529
|
compute Manage Pibo Docker compute workers
|
|
534
530
|
resources Inspect and safely reap managed compute and browser resources
|
|
535
531
|
preview Expose session-linked live development previews
|
|
536
|
-
setup Plan
|
|
532
|
+
setup Plan and manage supported host installation profiles
|
|
537
533
|
skills Manage Pibo user skills
|
|
538
534
|
cron Manage scheduled Pibo jobs
|
|
539
535
|
loop Manage continuous agent loops (goal mode by default)
|
package/dist/compute/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import {
|
|
1
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
2
|
+
import { imageExists, shouldRebuild, shouldRebuildDeps, dockerBuild, resolveComputeImageBuildConfig, saveHash, saveDepHash, spawnWorker, spawnDevWorker, listWorkers, releaseWorker, planReapWorkers, applyComputeWorkerReapPlan, getComputeDiskDiagnostics, } from "./docker.js";
|
|
3
3
|
import { getComputeResourceHealth } from "./resource-health.js";
|
|
4
4
|
import { mkdir } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
@@ -7,6 +7,9 @@ import os from "node:os";
|
|
|
7
7
|
const WORKSPACE_DIR = process.env.PIBO_COMPUTE_WORKSPACE || process.cwd();
|
|
8
8
|
const HASH_FILE = path.join(os.homedir(), ".pibo", "compute-image-hash");
|
|
9
9
|
const DEP_HASH_FILE = path.join(os.homedir(), ".pibo", "compute-dep-hash");
|
|
10
|
+
function computeImageBuildConfig() {
|
|
11
|
+
return resolveComputeImageBuildConfig(WORKSPACE_DIR);
|
|
12
|
+
}
|
|
10
13
|
function printJson(value) {
|
|
11
14
|
console.log(JSON.stringify(value, null, 2));
|
|
12
15
|
}
|
|
@@ -16,6 +19,12 @@ function parsePositiveIntegerOption(value) {
|
|
|
16
19
|
const parsed = Number(value);
|
|
17
20
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
18
21
|
}
|
|
22
|
+
function parseNonNegativeNumber(value) {
|
|
23
|
+
const parsed = Number(value);
|
|
24
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
25
|
+
throw new InvalidArgumentError("Value must be a non-negative number");
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
19
28
|
function hostPort(value) {
|
|
20
29
|
if (!value || value === "-")
|
|
21
30
|
return undefined;
|
|
@@ -233,18 +242,24 @@ Creates a worker container and starts the gateway. Prints JSON with the containe
|
|
|
233
242
|
|
|
234
243
|
Use this for quick isolated checks. For code changes, prefer:
|
|
235
244
|
$ pibo compute dev spawn --worktree my-fix
|
|
245
|
+
|
|
246
|
+
Environment:
|
|
247
|
+
PIBO_COMPUTE_IMAGE Override the Docker image name.
|
|
248
|
+
PIBO_COMPUTE_WORKSPACE Use an explicit workspace or source checkout.
|
|
236
249
|
`)
|
|
237
250
|
.action(async (options) => {
|
|
238
251
|
await mkdir(path.dirname(HASH_FILE), { recursive: true });
|
|
239
|
-
const
|
|
252
|
+
const image = computeImageBuildConfig();
|
|
253
|
+
const needsBuild = !(await imageExists(image.imageName)) || (await shouldRebuild(image.buildContext, HASH_FILE, image.dockerfile));
|
|
240
254
|
if (needsBuild) {
|
|
241
|
-
console.error(`Building ${
|
|
242
|
-
await dockerBuild(
|
|
243
|
-
await saveHash(
|
|
255
|
+
console.error(`Building ${image.imageName} from ${image.buildContext}...`);
|
|
256
|
+
await dockerBuild(image);
|
|
257
|
+
await saveHash(image.buildContext, HASH_FILE, image.dockerfile);
|
|
244
258
|
console.error("Build complete.");
|
|
245
259
|
}
|
|
246
260
|
const worker = await spawnWorker({
|
|
247
261
|
workspaceDir: WORKSPACE_DIR,
|
|
262
|
+
imageName: image.imageName,
|
|
248
263
|
name: options.name,
|
|
249
264
|
holder: options.holder,
|
|
250
265
|
ttlSeconds: parsePositiveIntegerOption(options.ttlSeconds),
|
|
@@ -282,12 +297,13 @@ Example:
|
|
|
282
297
|
`)
|
|
283
298
|
.action(async (options) => {
|
|
284
299
|
await mkdir(path.dirname(DEP_HASH_FILE), { recursive: true });
|
|
300
|
+
const image = resolveComputeImageBuildConfig(options.repo, { packageRoot: options.repo });
|
|
285
301
|
console.error("[pibo compute] Checking Docker image status...");
|
|
286
|
-
const needsBuild = !(await imageExists(
|
|
302
|
+
const needsBuild = !(await imageExists(image.imageName)) || (await shouldRebuildDeps(options.repo, DEP_HASH_FILE));
|
|
287
303
|
if (needsBuild) {
|
|
288
304
|
console.error(`[pibo compute] Dependencies changed (package.json, package-lock.json, or Dockerfile).`);
|
|
289
|
-
console.error(`[pibo compute] Rebuilding Docker image ${
|
|
290
|
-
await dockerBuild(
|
|
305
|
+
console.error(`[pibo compute] Rebuilding Docker image ${image.imageName} — this takes 1-2 minutes...`);
|
|
306
|
+
await dockerBuild(image);
|
|
291
307
|
await saveDepHash(options.repo, DEP_HASH_FILE);
|
|
292
308
|
console.error("[pibo compute] Docker image build complete.");
|
|
293
309
|
}
|
|
@@ -298,6 +314,7 @@ Example:
|
|
|
298
314
|
const worker = await spawnDevWorker({
|
|
299
315
|
repoDir: options.repo,
|
|
300
316
|
worktreeName: options.worktree,
|
|
317
|
+
imageName: image.imageName,
|
|
301
318
|
holder: options.holder,
|
|
302
319
|
ttlSeconds: parsePositiveIntegerOption(options.ttlSeconds),
|
|
303
320
|
idleSeconds: parsePositiveIntegerOption(options.idleSeconds),
|
|
@@ -317,10 +334,11 @@ Example:
|
|
|
317
334
|
Rebuilds from the current workspace and refreshes compute image hashes.
|
|
318
335
|
`)
|
|
319
336
|
.action(async () => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
await
|
|
323
|
-
await
|
|
337
|
+
const image = computeImageBuildConfig();
|
|
338
|
+
console.error(`Rebuilding ${image.imageName} from ${image.buildContext}...`);
|
|
339
|
+
await dockerBuild(image);
|
|
340
|
+
await saveHash(image.buildContext, HASH_FILE, image.dockerfile);
|
|
341
|
+
await saveDepHash(image.buildContext, DEP_HASH_FILE);
|
|
324
342
|
console.error("Build complete.");
|
|
325
343
|
});
|
|
326
344
|
program
|
|
@@ -403,7 +421,7 @@ Use the name or id shown by:
|
|
|
403
421
|
program
|
|
404
422
|
.command("reap")
|
|
405
423
|
.description("Preview or apply compute worker container cleanup")
|
|
406
|
-
.option("--max-age-minutes <n>", "Select workers older than this many minutes",
|
|
424
|
+
.option("--max-age-minutes <n>", "Select workers older than this many minutes", parseNonNegativeNumber, 60)
|
|
407
425
|
.option("--include-dev", "Also select dev-worker containers; dev workers are skipped by default")
|
|
408
426
|
.option("--no-stopped", "Do not select stopped, dead, created, or restarting containers")
|
|
409
427
|
.option("--no-dirty", "Do not select dirty or OOM-killed containers")
|
|
@@ -416,12 +434,13 @@ Stopped, dirty/OOM, and old workers are selected unless disabled by selector fla
|
|
|
416
434
|
Worktrees are preserved; container cleanup never deletes Git worktrees.
|
|
417
435
|
`)
|
|
418
436
|
.action(async (options) => {
|
|
419
|
-
|
|
437
|
+
if (options.apply && options.dryRun)
|
|
438
|
+
throw new Error("Use either --apply or --dry-run, not both");
|
|
420
439
|
const plan = await planReapWorkers({
|
|
421
440
|
includeDev: options.includeDev === true,
|
|
422
441
|
includeStopped: options.stopped !== false,
|
|
423
442
|
includeDirty: options.dirty !== false,
|
|
424
|
-
maxAgeMinutes:
|
|
443
|
+
maxAgeMinutes: options.maxAgeMinutes,
|
|
425
444
|
});
|
|
426
445
|
const shouldApply = options.apply === true;
|
|
427
446
|
const removed = shouldApply ? await applyComputeWorkerReapPlan(plan) : [];
|