@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.
Files changed (122) hide show
  1. package/README.md +20 -6
  2. package/compute-image/Dockerfile +47 -0
  3. package/compute-image/Dockerfile.dockerignore +5 -0
  4. package/dist/agent-runtime/context-build.js +26 -2
  5. package/dist/agent-runtime/contract.js +78 -28
  6. package/dist/agent-runtime/portable-history.js +19 -9
  7. package/dist/agent-runtime/registry.js +8 -2
  8. package/dist/agent-runtime/routed-session.js +120 -15
  9. package/dist/agent-runtimes/codex-native/adapter.js +12 -0
  10. package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
  11. package/dist/agent-runtimes/omp/adapter.js +3 -7
  12. package/dist/agent-runtimes/pi/adapter.js +5 -3
  13. package/dist/agent-runtimes/pi/routed-session.js +59 -12
  14. package/dist/agent-runtimes/pi/runtime.js +6 -0
  15. package/dist/apps/chat/agent-profiles.js +31 -2
  16. package/dist/apps/chat/agent-store.js +98 -6
  17. package/dist/apps/chat/chat-request-normalizers.js +31 -0
  18. package/dist/apps/chat/data/chat-data-mappers.js +61 -11
  19. package/dist/apps/chat/data/project-service.js +119 -15
  20. package/dist/apps/chat/data/room-service.js +21 -0
  21. package/dist/apps/chat/data/session-query-service.js +33 -10
  22. package/dist/apps/chat/data/timeline-query-service.js +60 -35
  23. package/dist/apps/chat/loop-api.js +10 -2
  24. package/dist/apps/chat/output-compactor.js +144 -21
  25. package/dist/apps/chat/output-event-policy.js +179 -2
  26. package/dist/apps/chat/stream.js +16 -7
  27. package/dist/apps/chat/trace-v2.js +1 -0
  28. package/dist/apps/chat/web-app.js +317 -86
  29. package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CUQJqggN.js} +1 -1
  30. package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-ClUlQWYN.js} +1 -1
  31. package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-Djul9BmZ.js} +1 -1
  32. package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-GD0JGdCi.js} +1 -1
  33. package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-W5HOqHym.js} +1 -1
  34. package/dist/apps/chat-ui/assets/index-BcjkX-iP.js +228 -0
  35. package/dist/apps/chat-ui/assets/index-CmqRSbBU.css +1 -0
  36. package/dist/apps/chat-ui/index.html +2 -2
  37. package/dist/apps/chat-vscode-web/assets/index-JvrPUGvI.js +43 -0
  38. package/dist/apps/chat-vscode-web/index.html +1 -1
  39. package/dist/apps/cli-ui/InkSessionApp.js +111 -12
  40. package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
  41. package/dist/cli-session/localSessionSource.js +153 -161
  42. package/dist/cli.js +7 -11
  43. package/dist/compute/cli.js +35 -16
  44. package/dist/compute/docker.js +341 -94
  45. package/dist/config/config.js +3 -0
  46. package/dist/core/context-build.js +7 -16
  47. package/dist/core/output-persistence-retry.js +484 -0
  48. package/dist/core/output-render-sequence.js +331 -0
  49. package/dist/core/profiles.js +24 -0
  50. package/dist/core/provider-recovery.js +7 -1
  51. package/dist/core/session-model.js +37 -1
  52. package/dist/core/session-router.js +78 -110
  53. package/dist/data/ingest-service.js +91 -9
  54. package/dist/data/navigation-store.js +5 -5
  55. package/dist/data/pibo-store.js +8 -1
  56. package/dist/data/schema.js +243 -3
  57. package/dist/data/session-store.js +3 -3
  58. package/dist/debug/agents.js +91 -89
  59. package/dist/debug/index.js +46 -2
  60. package/dist/debug/pty.js +127 -48
  61. package/dist/gateway/cli.js +4 -0
  62. package/dist/gateway/web.js +8 -8
  63. package/dist/local/client.js +3 -2
  64. package/dist/loops/channel.js +3 -1
  65. package/dist/loops/cli.js +2 -1
  66. package/dist/loops/service.js +90 -11
  67. package/dist/loops/store.js +100 -74
  68. package/dist/mcp/agent-context.js +13 -26
  69. package/dist/mcp/commands/info.js +3 -1
  70. package/dist/mcp/config-command.js +46 -2
  71. package/dist/mcp/config.js +18 -4
  72. package/dist/plugins/context-files-store.js +36 -6
  73. package/dist/plugins/context-files.js +52 -1
  74. package/dist/plugins/registry.js +2 -0
  75. package/dist/plugins/user-profile-resources.js +22 -0
  76. package/dist/previews/base-url.js +34 -0
  77. package/dist/previews/config.js +2 -32
  78. package/dist/ralph/store.js +5 -0
  79. package/dist/reliability/store.js +378 -106
  80. package/dist/session-ui/terminalRows.js +8 -4
  81. package/dist/sessions/pibo-data-store.js +145 -0
  82. package/dist/sessions/store.js +42 -0
  83. package/dist/setup/cli.js +422 -6
  84. package/dist/setup/installation-profiles.js +464 -0
  85. package/dist/shared/deterministic-digest.js +94 -0
  86. package/dist/shared/trace-engine.js +54 -0
  87. package/dist/shared/trace-event-projection.js +124 -67
  88. package/dist/shared/trace-history.js +47 -12
  89. package/dist/shared/trace-live-reducer.js +23 -4
  90. package/dist/shared/trace-nodes.js +17 -28
  91. package/dist/shared/trace-order.js +41 -5
  92. package/dist/shared/trace-page-merge.js +44 -14
  93. package/dist/shared/trace-patch-nodes.js +2 -0
  94. package/dist/shared/trace-subagent-links.js +4 -1
  95. package/dist/shared/trace-tool-identity.js +15 -8
  96. package/dist/signals/projector.js +28 -6
  97. package/dist/subagents/observation-query.js +121 -0
  98. package/dist/subagents/tool.js +7 -3
  99. package/dist/tools/agent-browser-leases.js +58 -23
  100. package/dist/tools/agent-browser-wrapper.js +1 -1
  101. package/dist/tools/browser-use-leases.js +129 -16
  102. package/dist/tools/browser-use-wrapper.js +1 -1
  103. package/dist/tools/index.js +27 -11
  104. package/dist/tools/python-runtime.js +10 -3
  105. package/dist/tools/registry.js +1 -1
  106. package/dist/tools/runtime/node-backend.js +16 -5
  107. package/dist/tools/runtime/node-worker-source.js +112 -33
  108. package/dist/tools/runtime/python-backend.js +16 -5
  109. package/dist/tools/runtime/python-worker-source.js +167 -16
  110. package/dist/tools/runtime/registry.js +23 -10
  111. package/dist/user-skills/store.js +0 -1
  112. package/docs/ops/vscode-extension-release.md +9 -1
  113. package/npm-shrinkwrap.json +9 -2
  114. package/package.json +9 -2
  115. package/scripts/docker-entrypoint.sh +46 -0
  116. package/scripts/prepare-agent-browser-wrapper.sh +70 -0
  117. package/scripts/prepare-browser-use-wrapper.sh +255 -0
  118. package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
  119. package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
  120. package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +0 -43
  121. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  122. package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.1.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-JvrPUGvI.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-SluZr_-r.css">
10
10
  </head>
11
11
  <body>
@@ -5,10 +5,23 @@ import { buildCompactTerminalRows, buildSlashCommandCatalog, buildTerminalStatus
5
5
  import { InkTerminalView } from "./InkTerminalView.js";
6
6
  import { isExpandableTerminalRow } from "./InkTerminalRow.js";
7
7
  const IDENTIFIER_PATTERN = /^(?:user|room|ps|pi|entry|evt|run|create|provider|model|agent)[_:]/i;
8
+ export async function runInkPickerActivation(guard, picker, activate) {
9
+ if (guard.inFlight || guard.lastPicker === picker)
10
+ return undefined;
11
+ guard.inFlight = true;
12
+ guard.lastPicker = picker;
13
+ try {
14
+ return await activate();
15
+ }
16
+ finally {
17
+ guard.inFlight = false;
18
+ }
19
+ }
8
20
  const INITIAL_STATE = {
9
21
  loading: true,
10
22
  rows: [],
11
23
  input: "",
24
+ inputCursor: 0,
12
25
  mode: "transcript",
13
26
  };
14
27
  export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars, onExit }) {
@@ -18,6 +31,7 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
18
31
  const unsubscribeRef = useRef(undefined);
19
32
  const closedRef = useRef(false);
20
33
  const stateRef = useRef(state);
34
+ const pickerActivationRef = useRef({ inFlight: false });
21
35
  useEffect(() => {
22
36
  stateRef.current = state;
23
37
  }, [state]);
@@ -120,7 +134,7 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
120
134
  error: undefined,
121
135
  }));
122
136
  }, [source]);
123
- const selectPickerItem = useCallback(async () => {
137
+ const activatePickerItem = useCallback(async () => {
124
138
  const picker = stateRef.current.picker;
125
139
  const item = picker?.items[picker.selectedIndex];
126
140
  if (!picker || !item) {
@@ -136,8 +150,12 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
136
150
  return;
137
151
  }
138
152
  if (picker.action === "select-room") {
139
- const status = await source.getStatus({ sessionId: stateRef.current.session?.id });
140
- setState((current) => ({ ...current, status, activeRoom: room, mode: "transcript", picker: undefined, overlayStack: undefined, message: `Selected room ${room.title}.`, error: undefined }));
153
+ const currentSession = stateRef.current.session;
154
+ const preserveSession = currentSession?.roomId === room.id;
155
+ const status = await source.getStatus({ sessionId: preserveSession ? currentSession.id : undefined });
156
+ if (!preserveSession)
157
+ closeOpenSession();
158
+ setState((current) => selectInkSessionRoom(current, room, status));
141
159
  return;
142
160
  }
143
161
  await openSessionPickerForRoom(room, picker);
@@ -174,7 +192,13 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
174
192
  catch (error) {
175
193
  setState((current) => ({ ...current, mode: "transcript", picker: undefined, error: formatCliSessionError(error), message: undefined }));
176
194
  }
177
- }, [openSession, openSessionPickerForRoom, source]);
195
+ }, [closeOpenSession, openSession, openSessionPickerForRoom, source]);
196
+ const selectPickerItem = useCallback(async () => {
197
+ const picker = stateRef.current.picker;
198
+ if (!picker)
199
+ return;
200
+ await runInkPickerActivation(pickerActivationRef.current, picker, activatePickerItem);
201
+ }, [activatePickerItem]);
178
202
  useEffect(() => {
179
203
  closedRef.current = false;
180
204
  void (async () => {
@@ -215,6 +239,22 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
215
239
  setState((current) => reduceInkSessionInputState(current, { type: "down" }));
216
240
  return;
217
241
  }
242
+ if (key.leftArrow) {
243
+ setState((current) => reduceInkSessionInputState(current, { type: "left" }));
244
+ return;
245
+ }
246
+ if (key.rightArrow) {
247
+ setState((current) => reduceInkSessionInputState(current, { type: "right" }));
248
+ return;
249
+ }
250
+ if (key.home) {
251
+ setState((current) => reduceInkSessionInputState(current, { type: "home" }));
252
+ return;
253
+ }
254
+ if (key.end) {
255
+ setState((current) => reduceInkSessionInputState(current, { type: "end" }));
256
+ return;
257
+ }
218
258
  if (key.return) {
219
259
  const submitted = stateRef.current.input;
220
260
  if (submitted.length === 0 && !stateRef.current.picker && !stateRef.current.slashSuggestions && canToggleSelectedRowDetails(stateRef.current)) {
@@ -224,11 +264,11 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
224
264
  if (stateRef.current.slashSuggestions && !stateRef.current.picker) {
225
265
  const accepted = acceptSlashSuggestion(stateRef.current);
226
266
  if (accepted.runInput) {
227
- setState((current) => ({ ...current, input: "", slashSuggestions: undefined, message: undefined, error: undefined }));
267
+ setState((current) => ({ ...current, input: "", inputCursor: 0, slashSuggestions: undefined, message: undefined, error: undefined }));
228
268
  void submitCommandOrMessage(accepted.runInput);
229
269
  }
230
270
  else {
231
- setState((current) => ({ ...current, input: accepted.input, slashSuggestions: undefined, message: `Accepted ${accepted.input.trim()}. Press Enter to run or add arguments.`, error: undefined }));
271
+ setState((current) => ({ ...current, input: accepted.input, inputCursor: accepted.input.length, slashSuggestions: undefined, message: `Accepted ${accepted.input.trim()}. Press Enter to run or add arguments.`, error: undefined }));
232
272
  }
233
273
  return;
234
274
  }
@@ -238,6 +278,7 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
238
278
  return;
239
279
  }
240
280
  if (stateRef.current.picker) {
281
+ setState((current) => reduceInkSessionInputState(current, { type: "enter" }));
241
282
  void selectPickerItem();
242
283
  return;
243
284
  }
@@ -292,19 +333,44 @@ export function InkSessionPickerView({ picker, maxLineChars }) {
292
333
  return React.createElement(Box, { flexDirection: "column" }, React.createElement(Text, { color: "yellow" }, title), ...picker.items.map((item, index) => React.createElement(Text, { key: item.id, color: overlayItemColor(index === picker.selectedIndex, item.disabled === true) }, formatOverlayItemLine(item, index === picker.selectedIndex, lineLimit))), React.createElement(Text, { color: "gray" }, "↑↓ select · enter confirm · esc back/cancel · ctrl-c exit"));
293
334
  }
294
335
  export function reduceInkSessionInputState(state, action) {
295
- if (action.type === "text")
296
- return withSlashSuggestions({ ...state, input: state.input + action.value, message: undefined, error: undefined });
297
- if (action.type === "backspace")
298
- return withSlashSuggestions({ ...state, input: state.input.slice(0, -1) });
336
+ if (action.type === "text") {
337
+ const inputCursor = normalizeInputCursor(state.input, state.inputCursor);
338
+ return withSlashSuggestions({
339
+ ...state,
340
+ input: state.input.slice(0, inputCursor) + action.value + state.input.slice(inputCursor),
341
+ inputCursor: inputCursor + action.value.length,
342
+ message: undefined,
343
+ error: undefined,
344
+ });
345
+ }
346
+ if (action.type === "backspace") {
347
+ const inputCursor = normalizeInputCursor(state.input, state.inputCursor);
348
+ const previousCursor = adjacentInputCursor(state.input, inputCursor, -1);
349
+ if (previousCursor === inputCursor)
350
+ return state;
351
+ return withSlashSuggestions({
352
+ ...state,
353
+ input: state.input.slice(0, previousCursor) + state.input.slice(inputCursor),
354
+ inputCursor: previousCursor,
355
+ });
356
+ }
357
+ if (action.type === "left" || action.type === "right") {
358
+ const inputCursor = normalizeInputCursor(state.input, state.inputCursor);
359
+ return { ...state, inputCursor: adjacentInputCursor(state.input, inputCursor, action.type === "left" ? -1 : 1) };
360
+ }
361
+ if (action.type === "home")
362
+ return { ...state, inputCursor: 0 };
363
+ if (action.type === "end")
364
+ return { ...state, inputCursor: state.input.length };
299
365
  if (action.type === "toggle-details")
300
366
  return toggleSelectedRowDetails(state);
301
367
  if (action.type === "escape") {
302
368
  if (state.slashSuggestions)
303
369
  return { ...state, slashSuggestions: undefined, overlayStack: popInkSessionOverlay(state.overlayStack), message: "Closed slash suggestions." };
304
370
  if (state.picker?.parent) {
305
- return withPickerOverlay({ ...state, picker: state.picker.parent, mode: pickerMode(state.picker.parent), message: "Back." }, state.picker.parent);
371
+ return withPickerOverlay({ ...state, input: "", picker: state.picker.parent, mode: pickerMode(state.picker.parent), message: "Back." }, state.picker.parent);
306
372
  }
307
- return { ...state, input: "", mode: "transcript", picker: undefined, overlayStack: popInkSessionOverlay(state.overlayStack), message: "Canceled." };
373
+ return { ...state, input: "", inputCursor: 0, mode: "transcript", picker: undefined, overlayStack: popInkSessionOverlay(state.overlayStack), message: "Canceled." };
308
374
  }
309
375
  if (action.type === "up" || action.type === "down") {
310
376
  if (state.slashSuggestions && !state.picker && state.slashSuggestions.items.length > 0) {
@@ -323,11 +389,30 @@ export function reduceInkSessionInputState(state, action) {
323
389
  return {
324
390
  ...state,
325
391
  input: "",
392
+ inputCursor: 0,
326
393
  slashSuggestions: undefined,
327
394
  message: undefined,
328
395
  error: undefined,
329
396
  };
330
397
  }
398
+ const INPUT_GRAPHEME_SEGMENTER = new Intl.Segmenter(undefined, { granularity: "grapheme" });
399
+ function normalizeInputCursor(input, inputCursor) {
400
+ if (inputCursor === undefined)
401
+ return input.length;
402
+ const clamped = Math.max(0, Math.min(input.length, inputCursor));
403
+ return inputGraphemeBoundaries(input).findLast((boundary) => boundary <= clamped) ?? 0;
404
+ }
405
+ function adjacentInputCursor(input, inputCursor, direction) {
406
+ const boundaries = inputGraphemeBoundaries(input);
407
+ const index = boundaries.indexOf(inputCursor);
408
+ return boundaries[Math.max(0, Math.min(boundaries.length - 1, index + direction))] ?? inputCursor;
409
+ }
410
+ function inputGraphemeBoundaries(input) {
411
+ const boundaries = [0];
412
+ for (const segment of INPUT_GRAPHEME_SEGMENTER.segment(input))
413
+ boundaries.push(segment.index + segment.segment.length);
414
+ return boundaries;
415
+ }
331
416
  export function normalizeInkRowSelection(state) {
332
417
  const expandableIds = expandableRowIds(state.rows);
333
418
  const expandedRowIds = (state.expandedRowIds ?? []).filter((id) => expandableIds.includes(id));
@@ -338,6 +423,20 @@ export function normalizeInkRowSelection(state) {
338
423
  : expandableIds[expandableIds.length - 1];
339
424
  return { ...state, selectedRowId, expandedRowIds };
340
425
  }
426
+ export function selectInkSessionRoom(state, room, status) {
427
+ const preserveSession = state.session?.roomId === room.id;
428
+ return {
429
+ ...state,
430
+ status,
431
+ activeRoom: room,
432
+ ...(preserveSession ? {} : { session: undefined, rows: [], selectedRowId: undefined, expandedRowIds: [] }),
433
+ mode: "transcript",
434
+ picker: undefined,
435
+ overlayStack: undefined,
436
+ message: `Selected room ${room.title}.`,
437
+ error: undefined,
438
+ };
439
+ }
341
440
  function selectExpandableRow(state, direction) {
342
441
  const normalized = normalizeInkRowSelection(state);
343
442
  const ids = expandableRowIds(normalized.rows);
@@ -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 };