@pasko70/pibo 2.5.1 → 3.0.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 (104) hide show
  1. package/context/pibo-native-tooling.md +1 -0
  2. package/dist/agent-runtime/contract.js +2 -1
  3. package/dist/agent-runtime/history.js +53 -1
  4. package/dist/agent-runtime/routed-session.js +100 -26
  5. package/dist/agent-runtimes/codex-native/adapter.js +307 -31
  6. package/dist/agent-runtimes/codex-native/client.js +22 -3
  7. package/dist/agent-runtimes/codex-native/first-use.js +294 -0
  8. package/dist/agent-runtimes/codex-native/history.js +26 -7
  9. package/dist/agent-runtimes/codex-native/models.js +48 -1
  10. package/dist/agent-runtimes/codex-native/process.js +15 -1
  11. package/dist/agent-runtimes/codex-native/thread.js +75 -31
  12. package/dist/agent-runtimes/codex-native/turn.js +30 -0
  13. package/dist/agent-runtimes/history-proof.js +8 -0
  14. package/dist/agent-runtimes/omp/adapter.js +61 -14
  15. package/dist/agent-runtimes/omp/history.js +189 -34
  16. package/dist/agent-runtimes/omp/thread.js +82 -36
  17. package/dist/agent-runtimes/pi/adapter.js +30 -3
  18. package/dist/agent-runtimes/pi/history.js +66 -5
  19. package/dist/agent-runtimes/pi/openai-response-observer.js +102 -0
  20. package/dist/agent-runtimes/pi/routed-session.js +344 -37
  21. package/dist/apps/chat/agent-store.js +3 -0
  22. package/dist/apps/chat/chat-api-routes.js +1 -1
  23. package/dist/apps/chat/chat-files.js +130 -9
  24. package/dist/apps/chat/chat-request-normalizers.js +6 -0
  25. package/dist/apps/chat/chat-settings-routes.js +27 -2
  26. package/dist/apps/chat/chat-speech.js +100 -0
  27. package/dist/apps/chat/data/chat-data-mappers.js +25 -5
  28. package/dist/apps/chat/data/timeline-query-service.js +89 -1
  29. package/dist/apps/chat/trace-v2.js +210 -8
  30. package/dist/apps/chat/trace.js +3 -0
  31. package/dist/apps/chat/web-app.js +108 -3
  32. package/dist/apps/chat-ui/assets/{dist-GS8xUdfg.js → dist-BBDMeU5-.js} +1 -1
  33. package/dist/apps/chat-ui/assets/{dist-BpVSf6ZV.js → dist-BDIATCQt.js} +1 -1
  34. package/dist/apps/chat-ui/assets/{dist-CRv7fzi3.js → dist-JnW4v8UE.js} +1 -1
  35. package/dist/apps/chat-ui/assets/{dist-CbBXbGVi.js → dist-lRiLP9rr.js} +1 -1
  36. package/dist/apps/chat-ui/assets/{dist-HC0xQ92C.js → dist-mqCviI-c.js} +1 -1
  37. package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +228 -0
  38. package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +1 -0
  39. package/dist/apps/chat-ui/index.html +2 -2
  40. package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +43 -0
  41. package/dist/apps/chat-vscode-web/assets/{index-CF1-9PKU.css → index-SluZr_-r.css} +1 -1
  42. package/dist/apps/chat-vscode-web/index.html +2 -2
  43. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  44. package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
  45. package/dist/cli.js +49 -5
  46. package/dist/config/config.js +59 -8
  47. package/dist/core/preview-server-settings.js +20 -0
  48. package/dist/core/profiles.js +1 -0
  49. package/dist/core/session-router.js +476 -44
  50. package/dist/core/user-settings.js +10 -0
  51. package/dist/data/payload-store.js +57 -1
  52. package/dist/data/schema.js +18 -1
  53. package/dist/debug/trace.js +5 -0
  54. package/dist/gateway/client.js +368 -65
  55. package/dist/gateway/protocol.js +2 -0
  56. package/dist/gateway/server.js +8 -0
  57. package/dist/gateway/web.js +2 -0
  58. package/dist/index.js +1 -1
  59. package/dist/loops/store.js +6 -4
  60. package/dist/mcp/config.js +89 -17
  61. package/dist/mcp/daemon-client.js +473 -153
  62. package/dist/mcp/daemon.js +401 -37
  63. package/dist/plugins/builtin.js +2 -2
  64. package/dist/plugins/codex-native.js +2 -0
  65. package/dist/plugins/registry.js +260 -1
  66. package/dist/previews/cli.js +345 -0
  67. package/dist/previews/config.js +61 -0
  68. package/dist/previews/manager.js +644 -0
  69. package/dist/previews/network.js +198 -0
  70. package/dist/previews/plugin.js +11 -0
  71. package/dist/previews/process-supervisor.js +38 -0
  72. package/dist/previews/proxy.js +387 -0
  73. package/dist/previews/store.js +750 -0
  74. package/dist/previews/types.js +1 -0
  75. package/dist/previews/web-app.js +446 -0
  76. package/dist/runs/registry.js +22 -0
  77. package/dist/runs/tools.js +46 -7
  78. package/dist/session-ui/terminalRows.js +187 -13
  79. package/dist/sessions/pibo-data-store.js +37 -0
  80. package/dist/sessions/runtime-binding-persistence.js +34 -0
  81. package/dist/sessions/runtime-binding.js +6 -0
  82. package/dist/sessions/sqlite-store.js +37 -0
  83. package/dist/shared/trace-engine.js +12 -64
  84. package/dist/shared/trace-event-projection.js +16 -0
  85. package/dist/shared/trace-history.js +684 -184
  86. package/dist/shared/trace-limits.js +7 -0
  87. package/dist/shared/trace-live-patch.js +61 -0
  88. package/dist/shared/trace-tool-identity.js +43 -0
  89. package/dist/speech/openai-codex-realtime-call-proxy.js +248 -0
  90. package/dist/speech/openai-codex.js +407 -0
  91. package/dist/speech/types.js +8 -0
  92. package/dist/subagents/context.js +5 -3
  93. package/dist/subagents/observations.js +94 -6
  94. package/dist/subagents/tool.js +103 -20
  95. package/dist/tools/codex-image-artifacts.js +24 -0
  96. package/dist/tools/codex-image-generation.js +4 -22
  97. package/dist/web/channel.js +88 -4
  98. package/dist/web/http.js +2 -1
  99. package/npm-shrinkwrap.json +2 -2
  100. package/package.json +1 -1
  101. package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +0 -1
  102. package/dist/apps/chat-ui/assets/index-DT5jCQBP.js +0 -228
  103. package/dist/apps/chat-vscode-web/assets/index-DvTSSvzN.js +0 -43
  104. package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.5.1.vsix +0 -0
@@ -165,17 +165,14 @@ function secondsToIso(seconds) {
165
165
  const date = new Date(Number(seconds) * 1_000);
166
166
  return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
167
167
  }
168
- function itemText(item) {
169
- if (item.type === "agentMessage" && typeof item.text === "string")
170
- return redactCodexNativeSensitiveText(item.text);
171
- if (item.type === "userMessage" && Array.isArray(item.content)) {
172
- const text = item.content
173
- .filter((part) => isRecord(part) && part.type === "text" && typeof part.text === "string")
174
- .map((part) => redactCodexNativeSensitiveText(part.text))
175
- .join("\n");
176
- return text || undefined;
177
- }
178
- return undefined;
168
+ function userMessageText(item) {
169
+ if (item.type !== "userMessage" || !Array.isArray(item.content))
170
+ return undefined;
171
+ const text = item.content
172
+ .filter((part) => isRecord(part) && part.type === "text" && typeof part.text === "string")
173
+ .map((part) => redactCodexNativeSensitiveText(part.text))
174
+ .join("\n");
175
+ return text || undefined;
179
176
  }
180
177
  function threadLeafId(thread) {
181
178
  return thread.turns.at(-1)?.id ?? null;
@@ -204,23 +201,32 @@ export function codexThreadInfo(runtimeInstanceId, thread) {
204
201
  firstMessage: thread.preview ? redactCodexNativeSensitiveText(thread.preview) : undefined,
205
202
  };
206
203
  }
207
- export function codexThreadForkCandidates(thread) {
208
- return thread.turns.map((turn) => {
209
- const text = [...turn.items].reverse().map(itemText).find((candidate) => candidate?.trim()) ?? `Codex turn ${turn.id}`;
210
- return { entryId: turn.id, text };
204
+ function codexThreadForkTargets(thread) {
205
+ return thread.turns.flatMap((turn, index) => {
206
+ const userMessage = turn.items.find((item) => item.type === "userMessage");
207
+ if (!userMessage)
208
+ return [];
209
+ return [{
210
+ entryId: userMessage.id,
211
+ text: userMessageText(userMessage) ?? `Codex turn ${turn.id}`,
212
+ ...(index > 0 ? { previousTurnId: thread.turns[index - 1].id } : {}),
213
+ }];
211
214
  });
212
215
  }
216
+ export function codexThreadForkCandidates(thread) {
217
+ return codexThreadForkTargets(thread).map(({ entryId, text }) => ({ entryId, text }));
218
+ }
213
219
  export class CodexNativeThreadController {
214
220
  client;
215
221
  currentThread;
216
222
  currentConfiguration;
217
- resourceSelection;
223
+ inheritedSelection;
218
224
  knownThreads = new Map();
219
- constructor(client, currentThread, currentConfiguration, resourceSelection) {
225
+ constructor(client, currentThread, currentConfiguration, inheritedSelection) {
220
226
  this.client = client;
221
227
  this.currentThread = currentThread;
222
228
  this.currentConfiguration = currentConfiguration;
223
- this.resourceSelection = resourceSelection;
229
+ this.inheritedSelection = inheritedSelection;
224
230
  this.knownThreads.set(currentThread.id, structuredClone(currentThread));
225
231
  }
226
232
  static async start(client, workspace, selection = {}) {
@@ -230,13 +236,18 @@ export class CodexNativeThreadController {
230
236
  ...(selection.model ? { model: selection.model } : {}),
231
237
  ...(selection.serviceTier !== undefined ? { serviceTier: selection.serviceTier } : {}),
232
238
  ...(selection.personality !== undefined ? { personality: selection.personality } : {}),
239
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
240
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
233
241
  ...(selection.config ? { config: selection.config } : {}),
234
242
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
235
243
  });
236
244
  const selected = threadSessionFromResponse(response, "thread/start");
237
245
  return new CodexNativeThreadController(client, selected.thread, selected.configuration, {
246
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
247
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
238
248
  ...(selection.config ? { config: structuredClone(selection.config) } : {}),
239
249
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
250
+ ...(selection.personality !== undefined ? { personality: selection.personality } : {}),
240
251
  });
241
252
  }
242
253
  static async resume(client, threadId, workspace, selection = {}) {
@@ -247,6 +258,8 @@ export class CodexNativeThreadController {
247
258
  ...(selection.model ? { model: selection.model } : {}),
248
259
  ...(selection.serviceTier !== undefined ? { serviceTier: selection.serviceTier } : {}),
249
260
  ...(selection.personality !== undefined ? { personality: selection.personality } : {}),
261
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
262
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
250
263
  ...(selection.config ? { config: selection.config } : {}),
251
264
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
252
265
  });
@@ -255,8 +268,11 @@ export class CodexNativeThreadController {
255
268
  if (thread.id !== threadId)
256
269
  throw new CodexNativeThreadProtocolError("Codex resumed a different thread than requested.");
257
270
  return new CodexNativeThreadController(client, thread, selected.configuration, {
271
+ ...(selection.approvalPolicy ? { approvalPolicy: selection.approvalPolicy } : {}),
272
+ ...(selection.sandbox ? { sandbox: selection.sandbox } : {}),
258
273
  ...(selection.config ? { config: structuredClone(selection.config) } : {}),
259
274
  ...(selection.developerInstructions ? { developerInstructions: selection.developerInstructions } : {}),
275
+ ...(selection.personality !== undefined ? { personality: selection.personality } : {}),
260
276
  });
261
277
  }
262
278
  catch (error) {
@@ -337,15 +353,29 @@ export class CodexNativeThreadController {
337
353
  .sort((left, right) => right.updatedAt - left.updatedAt || right.createdAt - left.createdAt)
338
354
  .map((thread) => codexThreadInfo(runtimeInstanceId, thread));
339
355
  }
340
- async fork(runtimeInstanceId, workspace, lastTurnId, validateThread) {
341
- if (!lastTurnId.trim())
342
- throw new Error("Codex thread fork requires a native turn id.");
343
- return await this.forkAt(runtimeInstanceId, workspace, lastTurnId, validateThread);
356
+ async fork(runtimeInstanceId, workspace, entryId, validateThread) {
357
+ if (!entryId.trim())
358
+ throw new Error("Codex thread fork requires a native user-message id.");
359
+ const target = codexThreadForkTargets(this.currentThread).find((candidate) => candidate.entryId === entryId);
360
+ if (target) {
361
+ if (!target.previousTurnId) {
362
+ return this.branchBeforeFirstTurn(runtimeInstanceId, workspace, target);
363
+ }
364
+ return await this.forkAt(runtimeInstanceId, workspace, target.previousTurnId, validateThread, target);
365
+ }
366
+ const legacyTurn = this.currentThread.turns.find((turn) => turn.id === entryId);
367
+ if (!legacyTurn)
368
+ throw new Error(`Codex fork target "${entryId}" was not found.`);
369
+ const legacyText = legacyTurn.items.map(userMessageText).find((text) => text?.trim());
370
+ return await this.forkAt(runtimeInstanceId, workspace, entryId, validateThread, {
371
+ entryId,
372
+ text: legacyText ?? `Codex turn ${entryId}`,
373
+ });
344
374
  }
345
375
  async clone(runtimeInstanceId, workspace, validateThread) {
346
376
  return await this.forkAt(runtimeInstanceId, workspace, undefined, validateThread);
347
377
  }
348
- async forkAt(runtimeInstanceId, workspace, lastTurnId, validateThread) {
378
+ async forkAt(runtimeInstanceId, workspace, lastTurnId, validateThread, selectedTarget) {
349
379
  const previousThread = this.currentThread;
350
380
  const previous = codexThreadSnapshot(runtimeInstanceId, previousThread);
351
381
  const params = {
@@ -353,9 +383,11 @@ export class CodexNativeThreadController {
353
383
  ...(lastTurnId ? { lastTurnId } : {}),
354
384
  cwd: workspace,
355
385
  ephemeral: false,
356
- ...(this.resourceSelection.config ? { config: structuredClone(this.resourceSelection.config) } : {}),
357
- ...(this.resourceSelection.developerInstructions
358
- ? { developerInstructions: this.resourceSelection.developerInstructions }
386
+ ...(this.inheritedSelection.approvalPolicy ? { approvalPolicy: this.inheritedSelection.approvalPolicy } : {}),
387
+ ...(this.inheritedSelection.sandbox ? { sandbox: this.inheritedSelection.sandbox } : {}),
388
+ ...(this.inheritedSelection.config ? { config: structuredClone(this.inheritedSelection.config) } : {}),
389
+ ...(this.inheritedSelection.developerInstructions
390
+ ? { developerInstructions: this.inheritedSelection.developerInstructions }
359
391
  : {}),
360
392
  };
361
393
  try {
@@ -366,9 +398,6 @@ export class CodexNativeThreadController {
366
398
  throw new CodexNativeThreadProtocolError("Codex thread/fork returned the source thread id.");
367
399
  }
368
400
  await validateThread?.(forked.id);
369
- const selectedText = lastTurnId
370
- ? codexThreadForkCandidates(previousThread).find((candidate) => candidate.entryId === lastTurnId)?.text
371
- : undefined;
372
401
  this.knownThreads.set(previousThread.id, structuredClone(previousThread));
373
402
  this.knownThreads.set(forked.id, structuredClone(forked));
374
403
  this.currentThread = forked;
@@ -377,12 +406,27 @@ export class CodexNativeThreadController {
377
406
  previous,
378
407
  current: codexThreadSnapshot(runtimeInstanceId, forked),
379
408
  cancelled: false,
380
- ...(selectedText ? { selectedText } : {}),
381
- ...(lastTurnId ? { summaryEntryId: lastTurnId } : {}),
409
+ ...(selectedTarget?.text ? { selectedText: selectedTarget.text } : {}),
410
+ ...(selectedTarget ? { summaryEntryId: selectedTarget.entryId } : {}),
382
411
  };
383
412
  }
384
413
  catch (error) {
385
414
  return normalizeThreadError(error, previousThread.id);
386
415
  }
387
416
  }
417
+ branchBeforeFirstTurn(runtimeInstanceId, workspace, target) {
418
+ const previousThread = this.currentThread;
419
+ return {
420
+ previous: codexThreadSnapshot(runtimeInstanceId, previousThread),
421
+ current: {
422
+ adapterId: CODEX_NATIVE_ADAPTER_ID,
423
+ runtimeInstanceId,
424
+ cwd: workspace,
425
+ leafId: null,
426
+ },
427
+ cancelled: false,
428
+ selectedText: target.text,
429
+ summaryEntryId: target.entryId,
430
+ };
431
+ }
388
432
  }
@@ -291,6 +291,9 @@ export class CodexNativeTurnController {
291
291
  model: modelOptions.model,
292
292
  effort: modelOptions.effort,
293
293
  serviceTier: modelOptions.serviceTier,
294
+ approvalPolicy: modelOptions.approvalPolicy,
295
+ sandboxPolicy: modelOptions.sandboxPolicy,
296
+ ...(modelOptions.collaborationMode ? { collaborationMode: modelOptions.collaborationMode } : {}),
294
297
  ...(modelOptions.summary !== undefined ? { summary: modelOptions.summary } : {}),
295
298
  ...(modelOptions.personality !== undefined ? { personality: modelOptions.personality } : {}),
296
299
  };
@@ -316,6 +319,33 @@ export class CodexNativeTurnController {
316
319
  throw error;
317
320
  }
318
321
  }
322
+ replayTerminalTurn(turn) {
323
+ if (this.disposed)
324
+ throw new Error("Codex native turn controller is disposed.");
325
+ if (this.pending)
326
+ throw new Error("Codex native session already has an active turn.");
327
+ if (turn.status === "inProgress") {
328
+ throw new CodexNativeTurnProtocolError("Codex cannot replay a non-terminal native turn.");
329
+ }
330
+ const replay = newPendingTurn();
331
+ this.assignTurnId(replay, turn.id);
332
+ replay.started = true;
333
+ for (const item of turn.items)
334
+ this.completeItem(replay, item);
335
+ this.finishIncompleteItems(replay, turn.status);
336
+ if (turn.status === "failed") {
337
+ const message = errorMessage(turn.error);
338
+ this.emit({
339
+ type: "turn_failed",
340
+ message,
341
+ details: providerErrorDetails(turn.error, message, false),
342
+ turnId: turn.id,
343
+ });
344
+ }
345
+ else {
346
+ this.emit({ type: "turn_completed", status: turn.status, turnId: turn.id });
347
+ }
348
+ }
319
349
  async compact() {
320
350
  if (this.disposed)
321
351
  throw new Error("Codex native turn controller is disposed.");
@@ -0,0 +1,8 @@
1
+ import { isCodexNativeBuiltInHistoryReconciliationProof } from "./codex-native/adapter.js";
2
+ import { isOmpBuiltInHistoryReconciliationProof } from "./omp/adapter.js";
3
+ import { isPiBuiltInHistoryReconciliationProof } from "./pi/adapter.js";
4
+ export function isBuiltInHistoryReconciliationProof(proof) {
5
+ return proof !== undefined && (isPiBuiltInHistoryReconciliationProof(proof)
6
+ || isOmpBuiltInHistoryReconciliationProof(proof)
7
+ || isCodexNativeBuiltInHistoryReconciliationProof(proof));
8
+ }
@@ -6,6 +6,7 @@ import { OmpRpcClient, OmpRpcResponseError } from "./client.js";
6
6
  import { defaultOmpRuntimeConfig, OMP_RUNTIME_CONFIG_SCHEMA, parseOmpRuntimeConfig } from "./config.js";
7
7
  import { OmpHostToolBridge } from "./host-tools.js";
8
8
  import { emptyOmpHistoryPage, inspectOmpHistory, readOmpHistory } from "./history.js";
9
+ import { historyReconciliationDigest, } from "../../agent-runtime/history.js";
9
10
  import { OMP_MODEL_OPTIONS_SCHEMA, OMP_MODEL_PROVIDER_ID, OMP_REASONING_VALUES, parseOmpReasoning, readOmpModelCatalog, setOmpModel, } from "./models.js";
10
11
  import { buildOmpProcessEnvironment, diagnoseOmpRuntime, disposeOmpSessionPaths, prepareOmpSessionPaths, resetOmpNativeSession, resolveOmpCommand, } from "./process.js";
11
12
  import { OmpResourceDelivery } from "./resource-delivery.js";
@@ -14,6 +15,32 @@ import { OmpRpcTurnController } from "./turn.js";
14
15
  export { OMP_ADAPTER_ID } from "./thread.js";
15
16
  export const OMP_RUNTIME_PROTOCOL_NAME = "omp-rpc";
16
17
  export const OMP_RUNTIME_SUPPORTED_RANGE = "2";
18
+ const ompCompleteHistoryProofs = new WeakMap();
19
+ const ompRpcRequestImplementation = OmpRpcClient.prototype.request;
20
+ const ompBuiltInHistoryClients = new WeakMap();
21
+ const ompBuiltInAdapterHistoryClients = new WeakMap();
22
+ function bindOmpCompleteHistoryProof(page) {
23
+ const claim = page.reconciliationProof;
24
+ if (!claim?.complete)
25
+ return page;
26
+ const entries = Object.freeze([...claim.entries]);
27
+ const fullScope = Object.freeze({ entryCount: entries.length, digest: historyReconciliationDigest(entries) });
28
+ const proof = Object.freeze({
29
+ complete: true,
30
+ ...(claim.scopeId ? { scopeId: claim.scopeId } : {}),
31
+ fullScope,
32
+ entries,
33
+ });
34
+ ompCompleteHistoryProofs.set(proof, { ...(claim.scopeId ? { scopeId: claim.scopeId } : {}), fullScope });
35
+ return { ...page, reconciliationProof: proof };
36
+ }
37
+ export function isOmpBuiltInHistoryReconciliationProof(proof) {
38
+ const bound = ompCompleteHistoryProofs.get(proof);
39
+ return proof.complete
40
+ && bound !== undefined
41
+ && proof.scopeId === bound.scopeId
42
+ && proof.fullScope === bound.fullScope;
43
+ }
17
44
  function ompCapabilities() {
18
45
  return {
19
46
  lifecycle: {
@@ -179,6 +206,7 @@ export class OmpSession {
179
206
  thread;
180
207
  hostTools;
181
208
  resourceDelivery;
209
+ forkCandidatesRequest;
182
210
  constructor(runtimeInstanceId, bundle, config, emitWarning, adapter) {
183
211
  this.runtimeInstanceId = runtimeInstanceId;
184
212
  this.bundle = bundle;
@@ -197,12 +225,11 @@ export class OmpSession {
197
225
  this.controls = {
198
226
  getCurrentSession: () => this.thread.getSessionSnapshot(this.runtimeInstanceId),
199
227
  listSessions: () => this.thread.listSessions(this.runtimeInstanceId),
200
- getForkCandidates: () => this.forkCandidates(),
228
+ getForkCandidates: () => this.getForkCandidates(),
201
229
  forkSession: async (entryId) => await this.runIdleOperation(async () => {
202
- const previous = this.thread.getSessionSnapshot(this.runtimeInstanceId);
203
230
  const result = await this.thread.forkSession(this.runtimeInstanceId, entryId);
204
231
  this.updateBinding();
205
- return { previous, current: result.current, cancelled: result.cancelled };
232
+ return result;
206
233
  }),
207
234
  getReasoning: () => parseOmpReasoning(undefined),
208
235
  setReasoning: (value) => {
@@ -245,9 +272,6 @@ export class OmpSession {
245
272
  adapterId: OMP_ADAPTER_ID,
246
273
  };
247
274
  }
248
- forkCandidates() {
249
- return this.thread.cachedForkCandidates();
250
- }
251
275
  async setOmpModel(provider, modelId) {
252
276
  const info = await setOmpModel(this.client, provider, modelId);
253
277
  return info;
@@ -315,7 +339,7 @@ export class OmpSession {
315
339
  listener(event);
316
340
  }
317
341
  async prompt(input) {
318
- this.assertActive();
342
+ this.assertIdle();
319
343
  this.operationInFlight = true;
320
344
  try {
321
345
  await this.turn.prompt(input.text);
@@ -369,6 +393,19 @@ export class OmpSession {
369
393
  if (this.disposed)
370
394
  throw new Error("OMP session is disposed.");
371
395
  }
396
+ async getForkCandidates() {
397
+ if (this.forkCandidatesRequest)
398
+ return await this.forkCandidatesRequest;
399
+ const request = this.runIdleOperation(async () => await this.thread.loadForkCandidates(this.runtimeInstanceId));
400
+ this.forkCandidatesRequest = request;
401
+ try {
402
+ return await request;
403
+ }
404
+ finally {
405
+ if (this.forkCandidatesRequest === request)
406
+ this.forkCandidatesRequest = undefined;
407
+ }
408
+ }
372
409
  async runIdleOperation(operation) {
373
410
  this.assertIdle();
374
411
  this.operationInFlight = true;
@@ -380,7 +417,7 @@ export class OmpSession {
380
417
  }
381
418
  }
382
419
  }
383
- export class OmpAgentRuntimeAdapter {
420
+ class OmpAgentRuntimeAdapter {
384
421
  instanceId;
385
422
  descriptor;
386
423
  config;
@@ -473,6 +510,10 @@ export class OmpAgentRuntimeAdapter {
473
510
  const session = new OmpSession(this.instanceId, bundle, this.parsed, (m) => {
474
511
  // Warning surfaced via session events is delivered by the turn controller.
475
512
  }, this);
513
+ const historyClient = Object.freeze({
514
+ request: ((...args) => Reflect.apply(ompRpcRequestImplementation, client, args)),
515
+ });
516
+ ompBuiltInHistoryClients.set(session, { owner: this, client: historyClient });
476
517
  session.setPiboSessionId(input.piboSession.id);
477
518
  // Wire host tools after session construction so the executor is available.
478
519
  const portableTools = input.services?.portableTools;
@@ -521,8 +562,6 @@ export class OmpAgentRuntimeAdapter {
521
562
  // (bindNativeSessionId below still sets the binding.)
522
563
  }
523
564
  }
524
- // Prime fork candidates (get_branch_messages) for the sync SPI.
525
- void threads.loadForkCandidates(this.instanceId);
526
565
  }
527
566
  catch (error) {
528
567
  await client.dispose();
@@ -537,11 +576,18 @@ export class OmpAgentRuntimeAdapter {
537
576
  }
538
577
  /** Record the live session so adapter-level reads can route to it. */
539
578
  attachLiveSession(session) {
579
+ const history = ompBuiltInHistoryClients.get(session);
580
+ if (!history || history.owner !== this) {
581
+ throw new AgentRuntimeUnavailableError(this.instanceId, "OMP history requires a session created by this built-in adapter instance.");
582
+ }
540
583
  this.live = session;
584
+ ompBuiltInAdapterHistoryClients.set(this, history.client);
541
585
  }
542
586
  detachLiveSession(session) {
543
- if (this.live === session)
544
- this.live = undefined;
587
+ if (this.live !== session)
588
+ return;
589
+ this.live = undefined;
590
+ ompBuiltInAdapterHistoryClients.delete(this);
545
591
  }
546
592
  async listModels() {
547
593
  if (this.live) {
@@ -579,8 +625,9 @@ export class OmpAgentRuntimeAdapter {
579
625
  return inspectOmpHistory(input, this.instanceId);
580
626
  }
581
627
  async readHistory(input) {
582
- if (this.live) {
583
- return await readOmpHistory(this.live.getClient(), input, this.instanceId, input.binding);
628
+ const historyClient = ompBuiltInAdapterHistoryClients.get(this);
629
+ if (historyClient) {
630
+ return bindOmpCompleteHistoryProof(await readOmpHistory(historyClient, input, this.instanceId, input.binding));
584
631
  }
585
632
  return emptyOmpHistoryPage(this.instanceId);
586
633
  }