@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.
Files changed (77) hide show
  1. package/README.md +20 -6
  2. package/dist/agent-runtime/context-build.js +26 -2
  3. package/dist/agent-runtime/routed-session.js +108 -9
  4. package/dist/agent-runtimes/codex-native/adapter.js +12 -0
  5. package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
  6. package/dist/agent-runtimes/omp/adapter.js +3 -7
  7. package/dist/agent-runtimes/pi/adapter.js +5 -3
  8. package/dist/agent-runtimes/pi/routed-session.js +49 -8
  9. package/dist/agent-runtimes/pi/runtime.js +6 -0
  10. package/dist/apps/chat/agent-profiles.js +31 -2
  11. package/dist/apps/chat/agent-store.js +43 -3
  12. package/dist/apps/chat/chat-request-normalizers.js +31 -0
  13. package/dist/apps/chat/data/chat-data-mappers.js +61 -11
  14. package/dist/apps/chat/data/session-query-service.js +33 -10
  15. package/dist/apps/chat/data/timeline-query-service.js +60 -35
  16. package/dist/apps/chat/output-compactor.js +144 -21
  17. package/dist/apps/chat/output-event-policy.js +179 -2
  18. package/dist/apps/chat/stream.js +16 -7
  19. package/dist/apps/chat/trace-v2.js +1 -0
  20. package/dist/apps/chat/web-app.js +236 -64
  21. package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-B6GZgWSj.js} +1 -1
  22. package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-BPotHecb.js} +1 -1
  23. package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-BzqQKeVO.js} +1 -1
  24. package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CvaPTBTN.js} +1 -1
  25. package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-paagejNj.js} +1 -1
  26. package/dist/apps/chat-ui/assets/index-6JJV-eic.js +228 -0
  27. package/dist/apps/chat-ui/assets/index-CeL9JPP5.css +1 -0
  28. package/dist/apps/chat-ui/index.html +2 -2
  29. package/dist/apps/chat-vscode-web/assets/{index-Dtw2Z7jz.js → index-U-MSErGa.js} +4 -4
  30. package/dist/apps/chat-vscode-web/index.html +1 -1
  31. package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
  32. package/dist/cli-session/localSessionSource.js +153 -161
  33. package/dist/cli.js +7 -11
  34. package/dist/core/context-build.js +7 -16
  35. package/dist/core/output-persistence-retry.js +484 -0
  36. package/dist/core/output-render-sequence.js +331 -0
  37. package/dist/core/profiles.js +24 -0
  38. package/dist/core/provider-recovery.js +7 -1
  39. package/dist/core/session-model.js +37 -1
  40. package/dist/core/session-router.js +67 -20
  41. package/dist/data/ingest-service.js +91 -9
  42. package/dist/data/navigation-store.js +5 -5
  43. package/dist/data/schema.js +122 -2
  44. package/dist/data/session-store.js +3 -3
  45. package/dist/debug/index.js +44 -2
  46. package/dist/gateway/web.js +8 -8
  47. package/dist/local/client.js +3 -2
  48. package/dist/plugins/context-files-store.js +30 -0
  49. package/dist/plugins/context-files.js +52 -1
  50. package/dist/plugins/registry.js +2 -0
  51. package/dist/plugins/user-profile-resources.js +22 -0
  52. package/dist/reliability/store.js +246 -37
  53. package/dist/session-ui/terminalRows.js +6 -3
  54. package/dist/sessions/pibo-data-store.js +145 -0
  55. package/dist/sessions/store.js +42 -0
  56. package/dist/setup/cli.js +422 -6
  57. package/dist/setup/installation-profiles.js +464 -0
  58. package/dist/shared/deterministic-digest.js +94 -0
  59. package/dist/shared/trace-engine.js +54 -0
  60. package/dist/shared/trace-event-projection.js +124 -67
  61. package/dist/shared/trace-history.js +47 -12
  62. package/dist/shared/trace-live-reducer.js +23 -4
  63. package/dist/shared/trace-nodes.js +17 -28
  64. package/dist/shared/trace-order.js +41 -5
  65. package/dist/shared/trace-page-merge.js +44 -14
  66. package/dist/shared/trace-patch-nodes.js +2 -0
  67. package/dist/shared/trace-subagent-links.js +4 -1
  68. package/dist/shared/trace-tool-identity.js +15 -8
  69. package/dist/signals/projector.js +28 -6
  70. package/dist/subagents/tool.js +7 -3
  71. package/docs/ops/vscode-extension-release.md +9 -1
  72. package/npm-shrinkwrap.json +2 -2
  73. package/package.json +2 -1
  74. package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
  75. package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
  76. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  77. package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#101d22" />
7
7
  <title>Pibo</title>
8
- <script type="module" crossorigin src="/apps/chat-vscode/assets/index-Dtw2Z7jz.js"></script>
8
+ <script type="module" crossorigin src="/apps/chat-vscode/assets/index-U-MSErGa.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-SluZr_-r.css">
10
10
  </head>
11
11
  <body>
@@ -1,12 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import React from "react";
3
3
  import { render } from "ink";
4
- import { createCustomAgentProfileDefinition } from "../../apps/chat/agent-profiles.js";
5
4
  import { createDefaultCustomAgentStore } from "../../apps/chat/agent-store.js";
6
5
  import { createDefaultFakeCliSessionSource, LocalCliSessionSource } from "../../cli-session/index.js";
7
6
  import { PiboSessionRouter } from "../../core/session-router.js";
8
7
  import { PiboDataStore } from "../../data/pibo-store.js";
9
8
  import { createDefaultPiboPluginRegistry } from "../../plugins/builtin.js";
9
+ import { registerPiboUserProfileResources } from "../../plugins/user-profile-resources.js";
10
10
  import { PiboDataSessionStore } from "../../sessions/pibo-data-store.js";
11
11
  import { cliCommandSummaryText, InkSessionApp } from "./InkSessionApp.js";
12
12
  export async function runCliSessionsUi(options = {}) {
@@ -43,6 +43,18 @@ export function createDefaultLocalCliSessionSource() {
43
43
  }
44
44
  function createDebugMockedLocalCliSessionSource(options) {
45
45
  const context = createLocalCliSessionSourceContext();
46
+ if (process.env.PIBO_DEBUG_PTY_STREAM_REVIEW === "1") {
47
+ const appendEvent = context.dataStore.eventLog.appendEvent.bind(context.dataStore.eventLog);
48
+ let failFirstAssistantFinal = true;
49
+ context.dataStore.eventLog.appendEvent = (input) => {
50
+ if (failFirstAssistantFinal && input.type === "assistant_message") {
51
+ failFirstAssistantFinal = false;
52
+ process.stderr.write("[debug-pty] injected once-only assistant persistence failure; automatic retry scheduled\n");
53
+ throw new Error("Injected once-only PTY persistence failure");
54
+ }
55
+ return appendEvent(input);
56
+ };
57
+ }
46
58
  const debugRooms = debugRoomSummariesFromEnv();
47
59
  const router = new DebugMockCliSessionRouter(options.assistantReply);
48
60
  return createLocalCliSessionSourceFromContext({
@@ -71,15 +83,10 @@ function createLocalCliSessionSourceContext() {
71
83
  const sessionStore = new PiboDataSessionStore(dataStore);
72
84
  const pluginRegistry = createDefaultPiboPluginRegistry();
73
85
  const builtInAgentSummaries = pluginRegistry.getProfileInfos().map((profile) => ({ id: profile.name, name: profile.name, description: profile.description, profileName: profile.name }));
86
+ registerPiboUserProfileResources(pluginRegistry, { contextFilesMode: "catalog" });
74
87
  const customAgentStore = createDefaultCustomAgentStore();
75
88
  const customAgents = customAgentStore.list();
76
- try {
77
- for (const agent of customAgents)
78
- pluginRegistry.upsertProfile(createCustomAgentProfileDefinition(agent, { missingReferenceMode: "silent" }));
79
- }
80
- finally {
81
- customAgentStore.close();
82
- }
89
+ customAgentStore.close();
83
90
  const agentSummaries = [
84
91
  ...builtInAgentSummaries,
85
92
  ...customAgents.map((agent) => ({ id: agent.profileName, name: agent.profileName, description: agent.description || agent.displayName, profileName: agent.profileName })),
@@ -122,6 +129,25 @@ class DebugMockCliSessionRouter {
122
129
  this.publish({ type: "message_queued", piboSessionId: event.piboSessionId, eventId, queuedMessages: 1, text: event.text, source: event.source });
123
130
  this.statuses.set(event.piboSessionId, this.status(event.piboSessionId, { processing: true, queuedMessages: 0 }));
124
131
  this.publish({ type: "message_started", piboSessionId: event.piboSessionId, eventId, text: event.text, source: event.source });
132
+ if (process.env.PIBO_DEBUG_PTY_STREAM_REVIEW === "1") {
133
+ const tool = { piboSessionId: event.piboSessionId, eventId, toolCallId: "pty-reused", toolName: "read" };
134
+ this.publish({ type: "thinking_started", piboSessionId: event.piboSessionId, eventId, thinkingIndex: 0 });
135
+ this.publish({ type: "thinking_delta", piboSessionId: event.piboSessionId, eventId, thinkingIndex: 0, text: "PTY retained thought" });
136
+ this.publish({ ...tool, type: "tool_execution_finished", result: "PTY tool result", isError: false });
137
+ this.publish({ ...tool, type: "tool_execution_updated", args: { path: "README.md" }, partialResult: "PTY partial tool result" });
138
+ this.publish({ ...tool, type: "tool_execution_started", args: { path: "README.md" } });
139
+ this.publish({ ...tool, type: "tool_call", args: { path: "README.md" }, argsComplete: true });
140
+ this.publish({ ...tool, type: "tool_execution_finished", result: "PTY tool result", isError: false });
141
+ this.publish({ type: "assistant_delta", piboSessionId: event.piboSessionId, eventId, assistantIndex: 0, contentIndex: 0, text: "PTY retained " });
142
+ this.publish({ type: "assistant_delta", piboSessionId: event.piboSessionId, eventId, assistantIndex: 0, contentIndex: 0, text: "answer" });
143
+ this.publish({ type: "thinking_finished", piboSessionId: event.piboSessionId, eventId, thinkingIndex: 0, text: "" });
144
+ const assistant = { type: "assistant_message", piboSessionId: event.piboSessionId, eventId, assistantIndex: 0, contentIndex: 0, text: "" };
145
+ this.publish(assistant);
146
+ const finished = { type: "message_finished", piboSessionId: event.piboSessionId, eventId, source: event.source };
147
+ this.statuses.set(event.piboSessionId, this.status(event.piboSessionId, { processing: false, queuedMessages: 0 }));
148
+ this.publish(finished);
149
+ return assistant;
150
+ }
125
151
  const assistant = { type: "assistant_message", piboSessionId: event.piboSessionId, eventId, assistantIndex: 0, contentIndex: 0, text: this.assistantReply };
126
152
  this.publish(assistant);
127
153
  const finished = { type: "message_finished", piboSessionId: event.piboSessionId, eventId, source: event.source };
@@ -1,13 +1,20 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { DEFAULT_PIBO_PROFILE_NAME, createDefaultPiboPluginRegistry, resolvePiboProfileNameFromRegistryOrDefault, } from "../plugins/builtin.js";
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 ?? createDefaultPiboPluginRegistry();
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.ingestOutputEvent(session, event);
551
- this.recordOutputEvent(event);
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 sessionId = event.piboSessionId;
555
- const eventId = "eventId" in event ? event.eventId : undefined;
556
- if (event.type === "message_queued" || event.type === "message_started") {
557
- this.recordLocalUserMessage(sessionId, eventId ?? randomUUID(), event.text, event.type === "message_started" ? "running" : "done");
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 (event.type === "message_finished") {
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 (event.type === "session_error") {
618
- this.recordLocalError(sessionId, eventId ?? randomUUID(), event.error);
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.upsertTraceNode(sessionId, {
648
- id: traceNodeId("error", eventId),
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
- // Persistence is best-effort; live local rendering still proceeds through in-memory trace updates.
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 { existsSync, readFileSync } from "node:fs";
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, piboHomePath } from "./core/pibo-home.js";
5
+ import { ensurePrivatePiboHome } from "./core/pibo-home.js";
6
6
  async function resolveCliProfile(profileName) {
7
- const { createDefaultPiboPluginRegistry, createGatewayProducerPiboProfile, createPiboProfileFromRegistryOrDefault } = await import("./plugins/builtin.js");
8
- const registry = createDefaultPiboPluginRegistry();
9
- const chatAgentStorePath = piboHomePath("chat-agents.sqlite");
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 user-host installs and developer-host upgrades")
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 user-host installs and developer-host upgrades
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)
@@ -214,6 +214,7 @@ function withoutRequestedModels(profile) {
214
214
  mcpServers: profile.mcpServers,
215
215
  piPackages: profile.piPackages,
216
216
  contextFiles: profile.contextFiles,
217
+ diagnostics: profile.diagnostics,
217
218
  builtinTools: profile.builtinTools,
218
219
  builtinToolNames: profile.builtinToolNames,
219
220
  autoContextFiles: profile.autoContextFiles,
@@ -229,15 +230,15 @@ function toolDefinitionSchema(definition, toolInfo) {
229
230
  }
230
231
  function generatedOriginForTool(name, profile) {
231
232
  if (name === "runtime")
232
- return "Generated Pibo runtime tool selected by the profile.";
233
+ return "pibo-runtime";
233
234
  if (name.startsWith("pibo_agents_"))
234
- return "Generated shared agent-management tool from the profile's delegated-agent list.";
235
+ return "pibo-subagents";
235
236
  if (name.startsWith("pibo_run_"))
236
- return "Generated run-control tool from the pibo-run-control capability package.";
237
+ return "pibo-run-control";
237
238
  if (PIBO_GOAL_TOOL_NAMES.includes(name))
238
- return "Generated goal-control tool from the pibo-goal-control capability package.";
239
+ return "pibo-goal-control";
239
240
  if (name === "apply_patch" || name === "view_image")
240
- return "Generated Codex-compatible tool from the codex-compat package.";
241
+ return "codex-compat";
241
242
  if (profile.builtinToolNames.includes(name) || DEFAULT_BUILTIN_TOOL_NAMES.includes(name))
242
243
  return undefined;
243
244
  return undefined;
@@ -502,17 +503,6 @@ export async function inspectPiboContextBuild(options = {}) {
502
503
  },
503
504
  });
504
505
  }
505
- if (generatedOrigin) {
506
- children.push({
507
- id: `tools/${name}/generated-origin`,
508
- kind: "metadata",
509
- title: "Generated Origin",
510
- source: "generated",
511
- state: "active",
512
- badges: ["GENERATED", "PIBO"],
513
- hydratedText: generatedOrigin,
514
- });
515
- }
516
506
  if (providerTool?.providerTool.kind === "web_search") {
517
507
  children.push({
518
508
  id: `tools/${name}/provider-payload`,
@@ -568,6 +558,7 @@ export async function inspectPiboContextBuild(options = {}) {
568
558
  hasDefinition: Boolean(definition || info),
569
559
  description: definition?.description ?? info?.description ?? providerTool?.description,
570
560
  ...(toolSource === "pibo" || toolSource === "generated" ? { owner: "pibo", deliveryMode: "direct" } : {}),
561
+ ...(generatedOrigin ? { inspectorOrigin: { label: generatedOrigin, modelVisible: false } } : {}),
571
562
  },
572
563
  children,
573
564
  };