@pasko70/pibo 3.3.0 → 3.4.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 (237) hide show
  1. package/dist/agent-runtime/capabilities.js +5 -0
  2. package/dist/agent-runtime/contract.js +2 -0
  3. package/dist/agent-runtime/routed-session.js +68 -11
  4. package/dist/agent-runtimes/codex-native/adapter.js +3 -0
  5. package/dist/agent-runtimes/codex-native/thread.js +26 -4
  6. package/dist/agent-runtimes/omp/adapter.js +1 -0
  7. package/dist/agent-runtimes/pi/adapter.js +4 -0
  8. package/dist/agent-runtimes/pi/routed-session.js +53 -5
  9. package/dist/apps/chat/chat-api-routes.js +11 -37
  10. package/dist/apps/chat/chat-request-normalizers.js +8 -48
  11. package/dist/apps/chat/data/legacy-project-migration.js +492 -0
  12. package/dist/apps/chat/data/room-service.js +2 -2
  13. package/dist/apps/chat/data/workflow-session-model.js +1 -0
  14. package/dist/apps/chat/data/workflow-session-service.js +352 -0
  15. package/dist/apps/chat/rooms.js +1 -1
  16. package/dist/apps/chat/types/rooms.js +30 -0
  17. package/dist/apps/chat/web-app.js +264 -647
  18. package/dist/apps/chat/workflow-catalog.js +18 -18
  19. package/dist/apps/chat/{project-workflow-human-actions.js → workflow-human-actions.js} +14 -14
  20. package/dist/apps/chat/workflow-manual-trigger-runtime.js +5 -1
  21. package/dist/apps/chat/workflow-persistence.js +20 -28
  22. package/dist/apps/chat/{project-workflow-sessions.js → workflow-sessions.js} +18 -17
  23. package/dist/apps/chat-ui/assets/{dist-Kc3zbvQi.js → dist-AhtdAETR.js} +1 -1
  24. package/dist/apps/chat-ui/assets/{dist-CcAkJ8iv.js → dist-Bk_L1qbJ.js} +1 -1
  25. package/dist/apps/chat-ui/assets/{dist-BbCjOLiw.js → dist-Dyp6sLQn.js} +1 -1
  26. package/dist/apps/chat-ui/assets/{dist-Bd9gWCbk.js → dist-jVaiGXFD.js} +1 -1
  27. package/dist/apps/chat-ui/assets/{dist-C9a0_446.js → dist-t25moyJS.js} +1 -1
  28. package/dist/apps/chat-ui/assets/index-CZjNw-aK.js +228 -0
  29. package/dist/apps/chat-ui/assets/index-CcEjFITM.css +1 -0
  30. package/dist/apps/chat-ui/index.html +2 -2
  31. package/dist/apps/chat-vscode-web/assets/index-Spj6M0tn.js +43 -0
  32. package/dist/apps/chat-vscode-web/index.html +1 -1
  33. package/dist/compute/pool/cli.js +1 -1
  34. package/dist/compute/pool/seeds.js +2 -3
  35. package/dist/config/config.js +1 -1
  36. package/dist/core/session-router.js +7 -4
  37. package/dist/previews/cli.js +154 -79
  38. package/dist/previews/compute-worker.js +37 -0
  39. package/dist/previews/config.js +2 -1
  40. package/dist/previews/migrations/session-ownership.js +7 -0
  41. package/dist/previews/proxy.js +4 -2
  42. package/dist/previews/public-setup.js +108 -0
  43. package/dist/previews/store.js +20 -13
  44. package/dist/previews/web-app.js +12 -2
  45. package/dist/sessions/workflow-session-kind.js +0 -3
  46. package/npm-shrinkwrap.json +7 -6
  47. package/package.json +6 -2
  48. package/packages/workflows/README.md +21 -0
  49. package/packages/workflows/dist/api/index.d.ts +48 -0
  50. package/packages/workflows/dist/api/index.d.ts.map +1 -0
  51. package/packages/workflows/dist/api/index.js +96 -0
  52. package/packages/workflows/dist/api/index.js.map +1 -0
  53. package/packages/workflows/dist/definition-hash.d.ts +4 -0
  54. package/packages/workflows/dist/definition-hash.d.ts.map +1 -0
  55. package/packages/workflows/dist/definition-hash.js +27 -0
  56. package/packages/workflows/dist/definition-hash.js.map +1 -0
  57. package/packages/workflows/dist/fixtures/index.d.ts +39 -0
  58. package/packages/workflows/dist/fixtures/index.d.ts.map +1 -0
  59. package/packages/workflows/dist/fixtures/index.js +608 -0
  60. package/packages/workflows/dist/fixtures/index.js.map +1 -0
  61. package/packages/workflows/dist/index.d.ts +16 -0
  62. package/packages/workflows/dist/index.d.ts.map +1 -0
  63. package/packages/workflows/dist/index.js +10 -0
  64. package/packages/workflows/dist/index.js.map +1 -0
  65. package/packages/workflows/dist/inspection/index.d.ts +47 -0
  66. package/packages/workflows/dist/inspection/index.d.ts.map +1 -0
  67. package/packages/workflows/dist/inspection/index.js +131 -0
  68. package/packages/workflows/dist/inspection/index.js.map +1 -0
  69. package/packages/workflows/dist/registry/index.d.ts +30 -0
  70. package/packages/workflows/dist/registry/index.d.ts.map +1 -0
  71. package/packages/workflows/dist/registry/index.js +161 -0
  72. package/packages/workflows/dist/registry/index.js.map +1 -0
  73. package/packages/workflows/dist/runtime/adapter-node.d.ts +28 -0
  74. package/packages/workflows/dist/runtime/adapter-node.d.ts.map +1 -0
  75. package/packages/workflows/dist/runtime/adapter-node.js +172 -0
  76. package/packages/workflows/dist/runtime/adapter-node.js.map +1 -0
  77. package/packages/workflows/dist/runtime/agent-node.d.ts +32 -0
  78. package/packages/workflows/dist/runtime/agent-node.d.ts.map +1 -0
  79. package/packages/workflows/dist/runtime/agent-node.js +231 -0
  80. package/packages/workflows/dist/runtime/agent-node.js.map +1 -0
  81. package/packages/workflows/dist/runtime/agent-runtime.d.ts +25 -0
  82. package/packages/workflows/dist/runtime/agent-runtime.d.ts.map +1 -0
  83. package/packages/workflows/dist/runtime/agent-runtime.js +111 -0
  84. package/packages/workflows/dist/runtime/agent-runtime.js.map +1 -0
  85. package/packages/workflows/dist/runtime/code-node.d.ts +31 -0
  86. package/packages/workflows/dist/runtime/code-node.d.ts.map +1 -0
  87. package/packages/workflows/dist/runtime/code-node.js +223 -0
  88. package/packages/workflows/dist/runtime/code-node.js.map +1 -0
  89. package/packages/workflows/dist/runtime/dispatch-failures.d.ts +36 -0
  90. package/packages/workflows/dist/runtime/dispatch-failures.d.ts.map +1 -0
  91. package/packages/workflows/dist/runtime/dispatch-failures.js +80 -0
  92. package/packages/workflows/dist/runtime/dispatch-failures.js.map +1 -0
  93. package/packages/workflows/dist/runtime/edge-payloads.d.ts +3 -0
  94. package/packages/workflows/dist/runtime/edge-payloads.d.ts.map +1 -0
  95. package/packages/workflows/dist/runtime/edge-payloads.js +11 -0
  96. package/packages/workflows/dist/runtime/edge-payloads.js.map +1 -0
  97. package/packages/workflows/dist/runtime/edge-transfer.d.ts +39 -0
  98. package/packages/workflows/dist/runtime/edge-transfer.d.ts.map +1 -0
  99. package/packages/workflows/dist/runtime/edge-transfer.js +356 -0
  100. package/packages/workflows/dist/runtime/edge-transfer.js.map +1 -0
  101. package/packages/workflows/dist/runtime/human-action.d.ts +43 -0
  102. package/packages/workflows/dist/runtime/human-action.d.ts.map +1 -0
  103. package/packages/workflows/dist/runtime/human-action.js +231 -0
  104. package/packages/workflows/dist/runtime/human-action.js.map +1 -0
  105. package/packages/workflows/dist/runtime/human-node.d.ts +27 -0
  106. package/packages/workflows/dist/runtime/human-node.d.ts.map +1 -0
  107. package/packages/workflows/dist/runtime/human-node.js +126 -0
  108. package/packages/workflows/dist/runtime/human-node.js.map +1 -0
  109. package/packages/workflows/dist/runtime/ids.d.ts +2 -0
  110. package/packages/workflows/dist/runtime/ids.d.ts.map +1 -0
  111. package/packages/workflows/dist/runtime/ids.js +4 -0
  112. package/packages/workflows/dist/runtime/ids.js.map +1 -0
  113. package/packages/workflows/dist/runtime/index.d.ts +23 -0
  114. package/packages/workflows/dist/runtime/index.d.ts.map +1 -0
  115. package/packages/workflows/dist/runtime/index.js +12 -0
  116. package/packages/workflows/dist/runtime/index.js.map +1 -0
  117. package/packages/workflows/dist/runtime/manual-trigger.d.ts +51 -0
  118. package/packages/workflows/dist/runtime/manual-trigger.d.ts.map +1 -0
  119. package/packages/workflows/dist/runtime/manual-trigger.js +403 -0
  120. package/packages/workflows/dist/runtime/manual-trigger.js.map +1 -0
  121. package/packages/workflows/dist/runtime/nested-workflow-node.d.ts +48 -0
  122. package/packages/workflows/dist/runtime/nested-workflow-node.d.ts.map +1 -0
  123. package/packages/workflows/dist/runtime/nested-workflow-node.js +246 -0
  124. package/packages/workflows/dist/runtime/nested-workflow-node.js.map +1 -0
  125. package/packages/workflows/dist/runtime/one-node-agent.d.ts +34 -0
  126. package/packages/workflows/dist/runtime/one-node-agent.d.ts.map +1 -0
  127. package/packages/workflows/dist/runtime/one-node-agent.js +334 -0
  128. package/packages/workflows/dist/runtime/one-node-agent.js.map +1 -0
  129. package/packages/workflows/dist/runtime/persistence.d.ts +9 -0
  130. package/packages/workflows/dist/runtime/persistence.d.ts.map +1 -0
  131. package/packages/workflows/dist/runtime/persistence.js +67 -0
  132. package/packages/workflows/dist/runtime/persistence.js.map +1 -0
  133. package/packages/workflows/dist/runtime/pibo-routing.d.ts +126 -0
  134. package/packages/workflows/dist/runtime/pibo-routing.d.ts.map +1 -0
  135. package/packages/workflows/dist/runtime/pibo-routing.js +96 -0
  136. package/packages/workflows/dist/runtime/pibo-routing.js.map +1 -0
  137. package/packages/workflows/dist/runtime/prompts.d.ts +20 -0
  138. package/packages/workflows/dist/runtime/prompts.d.ts.map +1 -0
  139. package/packages/workflows/dist/runtime/prompts.js +235 -0
  140. package/packages/workflows/dist/runtime/prompts.js.map +1 -0
  141. package/packages/workflows/dist/runtime/retry.d.ts +35 -0
  142. package/packages/workflows/dist/runtime/retry.d.ts.map +1 -0
  143. package/packages/workflows/dist/runtime/retry.js +80 -0
  144. package/packages/workflows/dist/runtime/retry.js.map +1 -0
  145. package/packages/workflows/dist/runtime/state.d.ts +14 -0
  146. package/packages/workflows/dist/runtime/state.d.ts.map +1 -0
  147. package/packages/workflows/dist/runtime/state.js +139 -0
  148. package/packages/workflows/dist/runtime/state.js.map +1 -0
  149. package/packages/workflows/dist/runtime/time.d.ts +6 -0
  150. package/packages/workflows/dist/runtime/time.d.ts.map +1 -0
  151. package/packages/workflows/dist/runtime/time.js +42 -0
  152. package/packages/workflows/dist/runtime/time.js.map +1 -0
  153. package/packages/workflows/dist/store/catalog-records.d.ts +18 -0
  154. package/packages/workflows/dist/store/catalog-records.d.ts.map +1 -0
  155. package/packages/workflows/dist/store/catalog-records.js +59 -0
  156. package/packages/workflows/dist/store/catalog-records.js.map +1 -0
  157. package/packages/workflows/dist/store/contracts.d.ts +149 -0
  158. package/packages/workflows/dist/store/contracts.d.ts.map +1 -0
  159. package/packages/workflows/dist/store/contracts.js +2 -0
  160. package/packages/workflows/dist/store/contracts.js.map +1 -0
  161. package/packages/workflows/dist/store/draft-writes.d.ts +7 -0
  162. package/packages/workflows/dist/store/draft-writes.d.ts.map +1 -0
  163. package/packages/workflows/dist/store/draft-writes.js +18 -0
  164. package/packages/workflows/dist/store/draft-writes.js.map +1 -0
  165. package/packages/workflows/dist/store/index.d.ts +55 -0
  166. package/packages/workflows/dist/store/index.d.ts.map +1 -0
  167. package/packages/workflows/dist/store/index.js +650 -0
  168. package/packages/workflows/dist/store/index.js.map +1 -0
  169. package/packages/workflows/dist/store/list-query.d.ts +13 -0
  170. package/packages/workflows/dist/store/list-query.d.ts.map +1 -0
  171. package/packages/workflows/dist/store/list-query.js +20 -0
  172. package/packages/workflows/dist/store/list-query.js.map +1 -0
  173. package/packages/workflows/dist/store/row-mappers.d.ts +206 -0
  174. package/packages/workflows/dist/store/row-mappers.d.ts.map +1 -0
  175. package/packages/workflows/dist/store/row-mappers.js +262 -0
  176. package/packages/workflows/dist/store/row-mappers.js.map +1 -0
  177. package/packages/workflows/dist/store/schema.d.ts +13 -0
  178. package/packages/workflows/dist/store/schema.d.ts.map +1 -0
  179. package/packages/workflows/dist/store/schema.js +458 -0
  180. package/packages/workflows/dist/store/schema.js.map +1 -0
  181. package/packages/workflows/dist/store/write-values.d.ts +18 -0
  182. package/packages/workflows/dist/store/write-values.d.ts.map +1 -0
  183. package/packages/workflows/dist/store/write-values.js +227 -0
  184. package/packages/workflows/dist/store/write-values.js.map +1 -0
  185. package/packages/workflows/dist/types/index.d.ts +1202 -0
  186. package/packages/workflows/dist/types/index.d.ts.map +1 -0
  187. package/packages/workflows/dist/types/index.js +8 -0
  188. package/packages/workflows/dist/types/index.js.map +1 -0
  189. package/packages/workflows/dist/validation/diagnostics.d.ts +27 -0
  190. package/packages/workflows/dist/validation/diagnostics.d.ts.map +1 -0
  191. package/packages/workflows/dist/validation/diagnostics.js +104 -0
  192. package/packages/workflows/dist/validation/diagnostics.js.map +1 -0
  193. package/packages/workflows/dist/validation/graph-cycles.d.ts +4 -0
  194. package/packages/workflows/dist/validation/graph-cycles.d.ts.map +1 -0
  195. package/packages/workflows/dist/validation/graph-cycles.js +130 -0
  196. package/packages/workflows/dist/validation/graph-cycles.js.map +1 -0
  197. package/packages/workflows/dist/validation/graph-edges.d.ts +5 -0
  198. package/packages/workflows/dist/validation/graph-edges.d.ts.map +1 -0
  199. package/packages/workflows/dist/validation/graph-edges.js +48 -0
  200. package/packages/workflows/dist/validation/graph-edges.js.map +1 -0
  201. package/packages/workflows/dist/validation/graph-ports.d.ts +5 -0
  202. package/packages/workflows/dist/validation/graph-ports.d.ts.map +1 -0
  203. package/packages/workflows/dist/validation/graph-ports.js +55 -0
  204. package/packages/workflows/dist/validation/graph-ports.js.map +1 -0
  205. package/packages/workflows/dist/validation/index.d.ts +18 -0
  206. package/packages/workflows/dist/validation/index.d.ts.map +1 -0
  207. package/packages/workflows/dist/validation/index.js +175 -0
  208. package/packages/workflows/dist/validation/index.js.map +1 -0
  209. package/packages/workflows/dist/validation/json-schema.d.ts +12 -0
  210. package/packages/workflows/dist/validation/json-schema.d.ts.map +1 -0
  211. package/packages/workflows/dist/validation/json-schema.js +556 -0
  212. package/packages/workflows/dist/validation/json-schema.js.map +1 -0
  213. package/packages/workflows/dist/validation/registry-refs.d.ts +21 -0
  214. package/packages/workflows/dist/validation/registry-refs.d.ts.map +1 -0
  215. package/packages/workflows/dist/validation/registry-refs.js +283 -0
  216. package/packages/workflows/dist/validation/registry-refs.js.map +1 -0
  217. package/packages/workflows/dist/validation/retry-policy.d.ts +3 -0
  218. package/packages/workflows/dist/validation/retry-policy.d.ts.map +1 -0
  219. package/packages/workflows/dist/validation/retry-policy.js +114 -0
  220. package/packages/workflows/dist/validation/retry-policy.js.map +1 -0
  221. package/packages/workflows/dist/validation/schema-declarations.d.ts +5 -0
  222. package/packages/workflows/dist/validation/schema-declarations.d.ts.map +1 -0
  223. package/packages/workflows/dist/validation/schema-declarations.js +36 -0
  224. package/packages/workflows/dist/validation/schema-declarations.js.map +1 -0
  225. package/packages/workflows/dist/validation/state-access.d.ts +4 -0
  226. package/packages/workflows/dist/validation/state-access.d.ts.map +1 -0
  227. package/packages/workflows/dist/validation/state-access.js +121 -0
  228. package/packages/workflows/dist/validation/state-access.js.map +1 -0
  229. package/packages/workflows/dist/xstate/index.d.ts +73 -0
  230. package/packages/workflows/dist/xstate/index.d.ts.map +1 -0
  231. package/packages/workflows/dist/xstate/index.js +728 -0
  232. package/packages/workflows/dist/xstate/index.js.map +1 -0
  233. package/packages/workflows/package.json +23 -0
  234. package/dist/apps/chat/data/project-service.js +0 -1029
  235. package/dist/apps/chat-ui/assets/index-DWJnUye8.css +0 -1
  236. package/dist/apps/chat-ui/assets/index-exohEOUE.js +0 -228
  237. package/dist/apps/chat-vscode-web/assets/index-D1bpAUkR.js +0 -43
@@ -6,6 +6,7 @@ const BOOLEAN_CAPABILITY_PATHS = [
6
6
  "lifecycle.attach",
7
7
  "lifecycle.listNativeSessions",
8
8
  "lifecycle.fork",
9
+ "lifecycle.forkWhileRunning",
9
10
  "lifecycle.clone",
10
11
  "lifecycle.tree",
11
12
  "input.text",
@@ -107,6 +108,9 @@ export function validateAgentRuntimeCapabilities(value) {
107
108
  for (const path of DELIVERY_CAPABILITY_PATHS)
108
109
  validateDelivery(path, readPath(value, path), errors);
109
110
  const persistent = readPath(value, "lifecycle.persistent");
111
+ if (readPath(value, "lifecycle.forkWhileRunning") === true && readPath(value, "lifecycle.fork") !== true) {
112
+ errors.push("lifecycle.forkWhileRunning requires lifecycle.fork");
113
+ }
110
114
  for (const path of ["lifecycle.resume", "lifecycle.attach"]) {
111
115
  if (readPath(value, path) === true && persistent !== true)
112
116
  errors.push(`${path} requires lifecycle.persistent`);
@@ -223,6 +227,7 @@ export function createMinimalAgentRuntimeCapabilities() {
223
227
  attach: false,
224
228
  listNativeSessions: false,
225
229
  fork: false,
230
+ forkWhileRunning: false,
226
231
  clone: false,
227
232
  tree: false,
228
233
  },
@@ -43,6 +43,8 @@ export function validateAgentRuntimeSessionContract(session) {
43
43
  if (capabilities) {
44
44
  requireMethod(capabilities.lifecycle.listNativeSessions, "lifecycle.listNativeSessions", "listSessions");
45
45
  requireMethod(capabilities.lifecycle.fork, "lifecycle.fork", "forkSession");
46
+ requireMethod(capabilities.lifecycle.forkWhileRunning, "lifecycle.forkWhileRunning", "getForkCandidatesWhileRunning");
47
+ requireMethod(capabilities.lifecycle.forkWhileRunning, "lifecycle.forkWhileRunning", "forkSessionWhileRunning");
46
48
  requireMethod(capabilities.lifecycle.clone, "lifecycle.clone", "cloneSession");
47
49
  requireMethod(capabilities.lifecycle.tree, "lifecycle.tree", "getSessionTree");
48
50
  requireMethod(capabilities.lifecycle.tree, "lifecycle.tree", "navigateSessionTree");
@@ -87,6 +87,7 @@ function nativeOperationToPiCompatibility(session, piboSessionId, result) {
87
87
  selectedText: result.selectedText,
88
88
  editorText: result.editorText,
89
89
  summaryEntryId: result.summaryEntryId,
90
+ sourceSessionUnchanged: result.sourceSessionUnchanged,
90
91
  };
91
92
  }
92
93
  function nativeSessionInfoToPiCompatibility(session, info) {
@@ -137,6 +138,7 @@ export class RuntimeRoutedSession {
137
138
  activeThinkingIndex;
138
139
  nextThinkingIndex = 0;
139
140
  sessionIdentityOperationInFlight = false;
141
+ forkWhileRunningOperationInFlight = false;
140
142
  forkCandidatesRequest;
141
143
  primaryModel;
142
144
  modelFallbacks;
@@ -215,10 +217,23 @@ export class RuntimeRoutedSession {
215
217
  : event.action === "session.switch"
216
218
  ? "switch"
217
219
  : undefined;
220
+ const status = this.getStatus();
221
+ const sourceHasWork = status.processing || status.streaming || status.queuedMessages > 0;
222
+ const forkWhileRunning = event.action === "session.fork"
223
+ && sourceHasWork
224
+ && Boolean(this.runtimeSession.controls?.forkSessionWhileRunning);
218
225
  if (sessionIdentityOperation) {
219
- this.assertSessionIdentityOperationIdle(sessionIdentityOperation);
220
- this.sessionIdentityOperationInFlight = true;
221
- this.notifyState();
226
+ if (forkWhileRunning) {
227
+ if (this.forkWhileRunningOperationInFlight) {
228
+ throw new Error("Pibo session already has a running-safe fork in progress.");
229
+ }
230
+ this.forkWhileRunningOperationInFlight = true;
231
+ }
232
+ else {
233
+ this.assertSessionIdentityOperationIdle(sessionIdentityOperation);
234
+ this.sessionIdentityOperationInFlight = true;
235
+ this.notifyState();
236
+ }
222
237
  }
223
238
  try {
224
239
  if (sessionIdentityOperation)
@@ -237,7 +252,10 @@ export class RuntimeRoutedSession {
237
252
  return output;
238
253
  }
239
254
  finally {
240
- if (sessionIdentityOperation) {
255
+ if (forkWhileRunning) {
256
+ this.forkWhileRunningOperationInFlight = false;
257
+ }
258
+ else if (sessionIdentityOperation) {
241
259
  this.sessionIdentityOperationInFlight = false;
242
260
  this.notifyState();
243
261
  this.startDrain();
@@ -368,7 +386,25 @@ export class RuntimeRoutedSession {
368
386
  async getForkCandidates() {
369
387
  if (this.forkCandidatesRequest)
370
388
  return await this.forkCandidatesRequest;
371
- this.assertSessionIdentityOperationIdle("inspect fork candidates");
389
+ if (this.sessionIdentityOperationInFlight) {
390
+ throw new Error("Pibo session already has a session identity operation in progress; cannot inspect fork candidates.");
391
+ }
392
+ const status = this.getStatus();
393
+ const sourceHasWork = status.processing || status.streaming || status.queuedMessages > 0;
394
+ if (sourceHasWork) {
395
+ const getForkCandidatesWhileRunning = this.runtimeSession.controls?.getForkCandidatesWhileRunning;
396
+ if (!getForkCandidatesWhileRunning)
397
+ this.assertSessionWorkIdle("inspect fork candidates");
398
+ const request = Promise.resolve().then(async () => await getForkCandidatesWhileRunning());
399
+ this.forkCandidatesRequest = request;
400
+ try {
401
+ return await request;
402
+ }
403
+ finally {
404
+ if (this.forkCandidatesRequest === request)
405
+ this.forkCandidatesRequest = undefined;
406
+ }
407
+ }
372
408
  const getForkCandidates = this.runtimeSession.controls?.getForkCandidates;
373
409
  if (!getForkCandidates)
374
410
  throw runtimeCapabilityError(this.runtimeSession, "native session fork candidates");
@@ -388,6 +424,12 @@ export class RuntimeRoutedSession {
388
424
  }
389
425
  }
390
426
  async forkSession(entryId) {
427
+ if (this.forkWhileRunningOperationInFlight) {
428
+ const forkSessionWhileRunning = this.runtimeSession.controls?.forkSessionWhileRunning;
429
+ if (!forkSessionWhileRunning)
430
+ throw runtimeCapabilityError(this.runtimeSession, "forking completed history while running");
431
+ return nativeOperationToPiCompatibility(this.runtimeSession, this.piboSessionId, await forkSessionWhileRunning(entryId));
432
+ }
391
433
  this.assertSessionWorkIdle("fork");
392
434
  const forkSession = this.runtimeSession.controls?.forkSession;
393
435
  if (!forkSession)
@@ -436,11 +478,26 @@ export class RuntimeRoutedSession {
436
478
  return nativeOperationToPiCompatibility(this.runtimeSession, this.piboSessionId, await switchSession(params));
437
479
  }
438
480
  async setModel(model) {
439
- const setModel = this.runtimeSession.controls?.setModel;
481
+ const selected = await this.setRuntimeModelPreservingReasoning(model);
482
+ this.primaryModel = { ...selected };
483
+ return selected;
484
+ }
485
+ async setRuntimeModelPreservingReasoning(model) {
486
+ const controls = this.runtimeSession.controls;
487
+ const setModel = controls?.setModel;
440
488
  if (!setModel)
441
489
  throw runtimeCapabilityError(this.runtimeSession, "in-session model switching");
490
+ const priorReasoning = controls.getReasoning?.() ?? this.runtimeSession.getStatus().reasoning;
442
491
  const selected = await setModel(model);
443
- this.primaryModel = { ...selected };
492
+ if (!priorReasoning?.supported || !priorReasoning.value || !controls.setReasoning)
493
+ return selected;
494
+ const currentReasoning = controls.getReasoning?.() ?? this.runtimeSession.getStatus().reasoning;
495
+ if (!currentReasoning?.supported || currentReasoning.value === priorReasoning.value)
496
+ return selected;
497
+ if (currentReasoning.availableValues?.length && !currentReasoning.availableValues.includes(priorReasoning.value)) {
498
+ return selected;
499
+ }
500
+ controls.setReasoning(priorReasoning.value);
444
501
  return selected;
445
502
  }
446
503
  setThinkingLevel(level) {
@@ -751,9 +808,9 @@ export class RuntimeRoutedSession {
751
808
  }
752
809
  }
753
810
  async promptWithModelFallbacks(event, inFlight) {
754
- const setModel = this.runtimeSession.controls?.setModel;
811
+ const canSetModel = Boolean(this.runtimeSession.controls?.setModel);
755
812
  const fallbackModels = this.modelFallbacks.filter((model) => !sameModel(model, this.primaryModel));
756
- if (!setModel || fallbackModels.length === 0) {
813
+ if (!canSetModel || fallbackModels.length === 0) {
757
814
  await this.runtimeSession.prompt({
758
815
  text: event.text,
759
816
  source: promptSource(event.source),
@@ -770,7 +827,7 @@ export class RuntimeRoutedSession {
770
827
  return;
771
828
  if (attempt > 0) {
772
829
  try {
773
- await setModel(fallbackModels[attempt - 1]);
830
+ await this.setRuntimeModelPreservingReasoning(fallbackModels[attempt - 1]);
774
831
  switchedModel = true;
775
832
  }
776
833
  catch {
@@ -807,7 +864,7 @@ export class RuntimeRoutedSession {
807
864
  this.pendingProviderFailure = undefined;
808
865
  if (switchedModel && this.primaryModel && !this.disposed) {
809
866
  try {
810
- await setModel(this.primaryModel);
867
+ await this.setRuntimeModelPreservingReasoning(this.primaryModel);
811
868
  }
812
869
  catch (error) {
813
870
  console.warn(`Failed to restore primary model for Pibo session "${this.piboSessionId}": ${errorMessage(error)}`);
@@ -88,6 +88,7 @@ function codexNativeCapabilities(structuredUserInput) {
88
88
  attach: true,
89
89
  listNativeSessions: true,
90
90
  fork: true,
91
+ forkWhileRunning: true,
91
92
  clone: true,
92
93
  tree: false,
93
94
  },
@@ -313,6 +314,7 @@ export class CodexNativeThreadSession {
313
314
  getCurrentSession: () => this.threads.getSnapshot(this.runtimeInstanceId),
314
315
  listSessions: () => this.threads.list(this.runtimeInstanceId, this.cwd),
315
316
  getForkCandidates: () => this.threads.getForkCandidates(),
317
+ getForkCandidatesWhileRunning: () => this.threads.getForkCandidatesWhileRunning(),
316
318
  forkSession: async (entryId) => await this.runIdleOperation(async () => {
317
319
  const result = await this.threads.fork(this.runtimeInstanceId, this.cwd, entryId, async (threadId) => await this.resourceDelivery.verifyThread(this.process.client, threadId));
318
320
  if (result.current.nativeSessionId) {
@@ -321,6 +323,7 @@ export class CodexNativeThreadSession {
321
323
  }
322
324
  return result;
323
325
  }),
326
+ forkSessionWhileRunning: async (entryId) => await this.threads.forkWhileRunning(this.runtimeInstanceId, this.cwd, entryId, async (threadId) => await this.resourceDelivery.verifyThread(this.process.client, threadId)),
324
327
  cloneSession: async () => await this.runIdleOperation(async () => {
325
328
  const result = await this.threads.clone(this.runtimeInstanceId, this.cwd, async (threadId) => await this.resourceDelivery.verifyThread(this.process.client, threadId));
326
329
  this.settings.attachThread(this.threads.thread.id, this.threads.configuration);
@@ -303,6 +303,12 @@ export class CodexNativeThreadController {
303
303
  getForkCandidates() {
304
304
  return codexThreadForkCandidates(this.currentThread);
305
305
  }
306
+ getForkCandidatesWhileRunning() {
307
+ return codexThreadForkCandidates({
308
+ ...this.currentThread,
309
+ turns: this.currentThread.turns.filter((turn) => turn.status !== "inProgress"),
310
+ });
311
+ }
306
312
  setStatus(status) {
307
313
  this.currentThread = { ...this.currentThread, status: structuredClone(status) };
308
314
  this.knownThreads.set(this.currentThread.id, structuredClone(this.currentThread));
@@ -372,10 +378,24 @@ export class CodexNativeThreadController {
372
378
  text: legacyText ?? `Codex turn ${entryId}`,
373
379
  });
374
380
  }
381
+ async forkWhileRunning(runtimeInstanceId, workspace, entryId, validateThread) {
382
+ if (!entryId.trim())
383
+ throw new Error("Codex thread fork requires a native user-message id.");
384
+ const target = codexThreadForkTargets({
385
+ ...this.currentThread,
386
+ turns: this.currentThread.turns.filter((turn) => turn.status !== "inProgress"),
387
+ }).find((candidate) => candidate.entryId === entryId);
388
+ if (!target)
389
+ throw new Error("Fork target is not a completed user message.");
390
+ const result = !target.previousTurnId
391
+ ? this.branchBeforeFirstTurn(runtimeInstanceId, workspace, target)
392
+ : await this.forkAt(runtimeInstanceId, workspace, target.previousTurnId, validateThread, target, false);
393
+ return { ...result, sourceSessionUnchanged: true };
394
+ }
375
395
  async clone(runtimeInstanceId, workspace, validateThread) {
376
396
  return await this.forkAt(runtimeInstanceId, workspace, undefined, validateThread);
377
397
  }
378
- async forkAt(runtimeInstanceId, workspace, lastTurnId, validateThread, selectedTarget) {
398
+ async forkAt(runtimeInstanceId, workspace, lastTurnId, validateThread, selectedTarget, adoptFork = true) {
379
399
  const previousThread = this.currentThread;
380
400
  const previous = codexThreadSnapshot(runtimeInstanceId, previousThread);
381
401
  const params = {
@@ -398,10 +418,12 @@ export class CodexNativeThreadController {
398
418
  throw new CodexNativeThreadProtocolError("Codex thread/fork returned the source thread id.");
399
419
  }
400
420
  await validateThread?.(forked.id);
401
- this.knownThreads.set(previousThread.id, structuredClone(previousThread));
421
+ if (adoptFork) {
422
+ this.knownThreads.set(previousThread.id, structuredClone(previousThread));
423
+ this.currentThread = forked;
424
+ this.currentConfiguration = selected.configuration;
425
+ }
402
426
  this.knownThreads.set(forked.id, structuredClone(forked));
403
- this.currentThread = forked;
404
- this.currentConfiguration = selected.configuration;
405
427
  return {
406
428
  previous,
407
429
  current: codexThreadSnapshot(runtimeInstanceId, forked),
@@ -50,6 +50,7 @@ function ompCapabilities() {
50
50
  attach: true,
51
51
  listNativeSessions: true,
52
52
  fork: true,
53
+ forkWhileRunning: false,
53
54
  clone: false,
54
55
  tree: false,
55
56
  },
@@ -46,6 +46,7 @@ export const PI_AGENT_RUNTIME_CAPABILITIES = {
46
46
  attach: true,
47
47
  listNativeSessions: true,
48
48
  fork: true,
49
+ forkWhileRunning: true,
49
50
  clone: true,
50
51
  tree: true,
51
52
  },
@@ -176,6 +177,7 @@ function nativeOperationFromPi(runtimeInstanceId, result) {
176
177
  previous: nativeSnapshotFromPi(runtimeInstanceId, result.previous),
177
178
  current: nativeSnapshotFromPi(runtimeInstanceId, result.current),
178
179
  cancelled: result.cancelled,
180
+ sourceSessionUnchanged: result.sourceSessionUnchanged,
179
181
  selectedText: result.selectedText,
180
182
  editorText: result.editorText,
181
183
  summaryEntryId: result.summaryEntryId,
@@ -439,7 +441,9 @@ class PiAgentRuntimeSession {
439
441
  getCurrentSession: () => nativeSnapshotFromPi(this.runtimeInstanceId, this.routed.getCurrentSession()),
440
442
  listSessions: async () => (await this.routed.listSessions()).map((info) => nativeSessionInfoFromPi(this.runtimeInstanceId, info)),
441
443
  getForkCandidates: () => this.routed.getForkCandidates(),
444
+ getForkCandidatesWhileRunning: () => this.routed.getForkCandidatesWhileRunning(),
442
445
  forkSession: async (entryId) => nativeOperationFromPi(this.runtimeInstanceId, await this.routed.forkSession(entryId)),
446
+ forkSessionWhileRunning: async (entryId) => nativeOperationFromPi(this.runtimeInstanceId, await this.routed.forkSessionWhileRunning(entryId)),
443
447
  cloneSession: async () => nativeOperationFromPi(this.runtimeInstanceId, await this.routed.cloneSession()),
444
448
  getSessionTree: () => {
445
449
  const result = this.routed.getSessionTree();
@@ -1239,6 +1239,52 @@ export class RoutedSession {
1239
1239
  this.assertSessionIdentityOperationIdle("inspect fork candidates");
1240
1240
  return this.runtime.session.getUserMessagesForForking();
1241
1241
  }
1242
+ getForkCandidatesWhileRunning() {
1243
+ const candidates = this.runtime.session.getUserMessagesForForking();
1244
+ return this.getStatus().processing || this.getStatus().streaming
1245
+ ? candidates.slice(0, -1)
1246
+ : candidates;
1247
+ }
1248
+ async forkSessionWhileRunning(entryId) {
1249
+ this.assertActive();
1250
+ const sourceManager = this.runtime.session.sessionManager;
1251
+ const sourceFile = sourceManager.getSessionFile();
1252
+ if (!sourceManager.isPersisted() || !sourceFile || !existsSync(sourceFile)) {
1253
+ throw new Error("A running Pi session can only fork from persisted history.");
1254
+ }
1255
+ const selected = this.getForkCandidatesWhileRunning().find((candidate) => candidate.entryId === entryId);
1256
+ if (!selected)
1257
+ throw new Error("Fork target is not a completed user message.");
1258
+ const selectedEntry = sourceManager.getEntry(entryId);
1259
+ if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
1260
+ throw new Error("Invalid entry ID for forking");
1261
+ }
1262
+ const previous = this.createSessionSnapshot();
1263
+ const targetLeafId = selectedEntry.parentId;
1264
+ let forkedManager;
1265
+ if (!targetLeafId) {
1266
+ forkedManager = SessionManager.create(this.runtime.cwd, sourceManager.getSessionDir());
1267
+ forkedManager.newSession({ parentSession: sourceFile });
1268
+ }
1269
+ else {
1270
+ forkedManager = SessionManager.open(sourceFile, sourceManager.getSessionDir(), this.runtime.cwd);
1271
+ if (!forkedManager.getEntry(targetLeafId)) {
1272
+ throw new Error("Fork target is not available in persisted history.");
1273
+ }
1274
+ const forkedSessionPath = forkedManager.createBranchedSession(targetLeafId);
1275
+ if (!forkedSessionPath)
1276
+ throw new Error("Failed to create forked session");
1277
+ }
1278
+ materializeForkedSession(forkedManager);
1279
+ return {
1280
+ piboSessionId: this.piboSessionId,
1281
+ previous,
1282
+ current: this.createSessionManagerSnapshot(forkedManager),
1283
+ cancelled: false,
1284
+ sourceSessionUnchanged: true,
1285
+ selectedText: selected.text,
1286
+ };
1287
+ }
1242
1288
  async forkSession(entryId) {
1243
1289
  this.assertActive();
1244
1290
  this.assertSessionWorkIdle("fork");
@@ -1809,13 +1855,15 @@ export class RoutedSession {
1809
1855
  }
1810
1856
  createSessionSnapshot() {
1811
1857
  const session = this.runtime.session;
1812
- const manager = session.sessionManager;
1858
+ return this.createSessionManagerSnapshot(session.sessionManager, session.sessionName);
1859
+ }
1860
+ createSessionManagerSnapshot(manager, sessionName = manager.getSessionName()) {
1813
1861
  return {
1814
- piSessionId: session.sessionId,
1815
- sessionFile: session.sessionFile,
1862
+ piSessionId: manager.getSessionId(),
1863
+ sessionFile: manager.getSessionFile(),
1816
1864
  leafId: manager.getLeafId(),
1817
- cwd: this.runtime.cwd,
1818
- sessionName: session.sessionName,
1865
+ cwd: manager.getCwd(),
1866
+ sessionName,
1819
1867
  parentSessionFile: manager.getHeader()?.parentSession,
1820
1868
  };
1821
1869
  }
@@ -13,7 +13,7 @@ export function roomResourcePath(pathname) {
13
13
  try {
14
14
  const roomId = decodeURIComponent(parts[0]);
15
15
  const child = parts[1] ? decodeURIComponent(parts[1]) : undefined;
16
- if (child && child !== "events" && child !== "messages" && child !== "read")
16
+ if (child && child !== "events" && child !== "messages" && child !== "read" && child !== "order")
17
17
  return undefined;
18
18
  return { roomId, child };
19
19
  }
@@ -246,52 +246,26 @@ export function userSkillResourceId(pathname) {
246
246
  throw new PiboWebHttpError("Invalid user skill id", 400);
247
247
  }
248
248
  }
249
- export function projectResourcePath(pathname) {
250
- const prefix = `${CHAT_WEB_API_PREFIX}/projects/`;
251
- if (!pathname.startsWith(prefix))
252
- return undefined;
253
- const parts = pathname.slice(prefix.length).split("/").filter(Boolean).map((part) => decodeURIComponent(part));
254
- if (!parts[0])
255
- return undefined;
256
- return { projectId: parts[0], ...(parts[1] ? { child: parts[1] } : {}) };
257
- }
258
- export function projectWorkflowSessionStartResource(pathname) {
259
- const prefix = `${CHAT_WEB_API_PREFIX}/projects/`;
249
+ export function sessionWorkflowResource(pathname) {
250
+ const prefix = `${CHAT_WEB_API_PREFIX}/sessions/`;
260
251
  if (!pathname.startsWith(prefix))
261
252
  return undefined;
262
253
  const parts = pathname.slice(prefix.length).split("/");
263
- if (parts.length !== 4 || !parts[0] || parts[1] !== "workflow-sessions" || !parts[2] || parts[3] !== "start")
254
+ if (parts.length < 2 || parts.length > 3 || !parts[0] || parts[1] !== "workflow")
264
255
  return undefined;
265
- try {
266
- return { projectId: decodeURIComponent(parts[0]), piboSessionId: decodeURIComponent(parts[2]) };
267
- }
268
- catch {
269
- throw new PiboWebHttpError("Invalid Project workflow session start path", 400);
270
- }
271
- }
272
- export function projectWorkflowHumanActionsResource(pathname) {
273
- const prefix = `${CHAT_WEB_API_PREFIX}/projects/`;
274
- if (!pathname.startsWith(prefix))
275
- return undefined;
276
- const parts = pathname.slice(prefix.length).split("/");
277
- if (parts.length !== 4 || !parts[0] || parts[1] !== "workflow-sessions" || !parts[2] || parts[3] !== "human-actions")
256
+ const action = parts[2];
257
+ if (action !== undefined && action !== "start" && action !== "human-actions")
278
258
  return undefined;
279
259
  try {
280
- return { projectId: decodeURIComponent(parts[0]), piboSessionId: decodeURIComponent(parts[2]) };
260
+ return {
261
+ piboSessionId: decodeURIComponent(parts[0]),
262
+ ...(action ? { action } : {}),
263
+ };
281
264
  }
282
265
  catch {
283
- throw new PiboWebHttpError("Invalid Project workflow human-action path", 400);
266
+ throw new PiboWebHttpError("Invalid Workflow session path", 400);
284
267
  }
285
268
  }
286
- export function projectSessionResourceId(pathname) {
287
- const prefix = `${CHAT_WEB_API_PREFIX}/project-sessions/`;
288
- if (!pathname.startsWith(prefix))
289
- return undefined;
290
- const encodedId = pathname.slice(prefix.length);
291
- if (!encodedId || encodedId.includes("/"))
292
- return undefined;
293
- return decodeURIComponent(encodedId);
294
- }
295
269
  export function sessionResourceId(pathname) {
296
270
  const prefix = `${CHAT_WEB_API_PREFIX}/sessions/`;
297
271
  if (!pathname.startsWith(prefix))
@@ -1,12 +1,12 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
- import { isAbsolute, resolve } from "node:path";
2
+ import { isAbsolute } from "node:path";
3
3
  import { savePiboModelDefaults } from "../../core/model-defaults.js";
4
4
  import { DEFAULT_AGENT_RUNTIME_INSTANCE_ID } from "../../core/profiles.js";
5
5
  import { isPiboThinkingLevel } from "../../core/thinking.js";
6
6
  import { PiboWebHttpError } from "../../web/http.js";
7
7
  import { listPiPackages } from "../../pi-packages/store.js";
8
8
  import { withChatWebArchived, withChatWebSessionPinned } from "./session-metadata.js";
9
- import { isDefaultPiboRoom, withPiboRoomArchived, withPiboRoomWorkspace } from "./types/rooms.js";
9
+ import { isDefaultPiboRoom, withPiboRoomArchived, withPiboRoomPinned, withPiboRoomWorkspace } from "./types/rooms.js";
10
10
  import { isValidCustomAgentName } from "./agent-store.js";
11
11
  export function normalizeRoomName(value, fallback = "New Chat") {
12
12
  if (value === undefined)
@@ -595,52 +595,6 @@ export function updateChatModelDefaults(body, cwd = process.cwd()) {
595
595
  subagentFast: normalizeOptionalBoolean(body.subagentFast, "subagentFast"),
596
596
  }, cwd);
597
597
  }
598
- const PROJECT_SESSION_PATCH_FIELDS = new Set([
599
- "title",
600
- "archived",
601
- ]);
602
- export function assertProjectSessionPatchFields(body) {
603
- if (!body || typeof body !== "object" || Array.isArray(body))
604
- throw new PiboWebHttpError("Invalid JSON body", 400);
605
- for (const key of Object.keys(body)) {
606
- if (!PROJECT_SESSION_PATCH_FIELDS.has(key)) {
607
- throw new PiboWebHttpError(`Unsupported project session update field: ${key}. Project workflow selection and configuration are immutable; create a new configured session to change workflow, input, prompt, model, thinking, or fast-mode values.`, 400);
608
- }
609
- }
610
- }
611
- export function normalizeProjectPath(value) {
612
- if (typeof value !== "string" || !value.trim())
613
- throw new PiboWebHttpError("Project folder is required", 400);
614
- let projectPath = value.trim();
615
- if (projectPath === "~")
616
- projectPath = process.env.HOME ?? projectPath;
617
- else if (projectPath.startsWith("~/"))
618
- projectPath = `${process.env.HOME ?? ""}${projectPath.slice(1)}`;
619
- if (!isAbsolute(projectPath))
620
- throw new PiboWebHttpError("Project folder must be an absolute path, e.g. ~/code/my-project or /home/me/code/my-project", 400);
621
- return resolve(projectPath);
622
- }
623
- export function normalizeProjectDescription(value) {
624
- if (value === undefined || value === null)
625
- return undefined;
626
- if (typeof value !== "string")
627
- throw new PiboWebHttpError("Project description must be a string", 400);
628
- return value.trim() || undefined;
629
- }
630
- export function normalizeProjectArchived(value) {
631
- if (value === undefined)
632
- return undefined;
633
- if (typeof value !== "boolean")
634
- throw new PiboWebHttpError("Project archived flag must be boolean", 400);
635
- return value;
636
- }
637
- export function normalizeProjectSessionArchived(value) {
638
- if (value === undefined)
639
- return undefined;
640
- if (typeof value !== "boolean")
641
- throw new PiboWebHttpError("Project session archived flag must be boolean", 400);
642
- return value;
643
- }
644
598
  export function normalizeSessionTitle(value) {
645
599
  if (value === undefined)
646
600
  return undefined;
@@ -712,6 +666,12 @@ export function createRoomUpdate(room, body) {
712
666
  metadata = withPiboRoomWorkspace(metadata, workspace ?? undefined);
713
667
  metadataChanged = true;
714
668
  }
669
+ if (body.pinned !== undefined) {
670
+ if (typeof body.pinned !== "boolean")
671
+ throw new PiboWebHttpError("Room pinned flag must be boolean", 400);
672
+ metadata = withPiboRoomPinned(metadata, body.pinned);
673
+ metadataChanged = true;
674
+ }
715
675
  if (metadataChanged)
716
676
  update.metadata = metadata;
717
677
  if (Object.keys(update).length === 0) {