@fragno-dev/pi-fragment 0.0.1 → 0.0.3

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 (98) hide show
  1. package/README.md +39 -3
  2. package/dist/browser/client/react.d.ts +44 -36
  3. package/dist/browser/client/react.d.ts.map +1 -1
  4. package/dist/browser/client/react.js +105 -22
  5. package/dist/browser/client/react.js.map +1 -1
  6. package/dist/browser/client/solid.d.ts +42 -36
  7. package/dist/browser/client/solid.d.ts.map +1 -1
  8. package/dist/browser/client/solid.js +27 -13
  9. package/dist/browser/client/solid.js.map +1 -1
  10. package/dist/browser/client/svelte.d.ts +42 -36
  11. package/dist/browser/client/svelte.d.ts.map +1 -1
  12. package/dist/browser/client/svelte.js +14 -6
  13. package/dist/browser/client/svelte.js.map +1 -1
  14. package/dist/browser/client/vanilla.d.ts +99 -39
  15. package/dist/browser/client/vanilla.d.ts.map +1 -1
  16. package/dist/browser/client/vanilla.js +151 -3
  17. package/dist/browser/client/vanilla.js.map +1 -1
  18. package/dist/browser/client/vue.d.ts +54 -38
  19. package/dist/browser/client/vue.d.ts.map +1 -1
  20. package/dist/browser/client/vue.js +25 -17
  21. package/dist/browser/client/vue.js.map +1 -1
  22. package/dist/browser/{factory-DKoO_lRA.js → clients-BscY_HVe.js} +1051 -799
  23. package/dist/browser/clients-BscY_HVe.js.map +1 -0
  24. package/dist/browser/index.d.ts +3 -776
  25. package/dist/browser/index.js +801 -2
  26. package/dist/browser/index.js.map +1 -0
  27. package/dist/browser/routes-CpL_YGWK.d.ts +1560 -0
  28. package/dist/browser/routes-CpL_YGWK.d.ts.map +1 -0
  29. package/dist/cli/mod.d.ts.map +1 -1
  30. package/dist/cli/mod.js +245 -7
  31. package/dist/cli/mod.js.map +1 -1
  32. package/dist/node/{pi → client}/clients.d.ts +46 -36
  33. package/dist/node/client/clients.d.ts.map +1 -0
  34. package/dist/node/client/clients.js +54 -0
  35. package/dist/node/client/clients.js.map +1 -0
  36. package/dist/node/client/session-controller.d.ts +31 -0
  37. package/dist/node/client/session-controller.d.ts.map +1 -0
  38. package/dist/node/client/session-controller.js +33 -0
  39. package/dist/node/client/session-controller.js.map +1 -0
  40. package/dist/node/client/session-store.d.ts +71 -0
  41. package/dist/node/client/session-store.d.ts.map +1 -0
  42. package/dist/node/client/session-store.js +637 -0
  43. package/dist/node/client/session-store.js.map +1 -0
  44. package/dist/node/debug-log.d.ts +9 -0
  45. package/dist/node/debug-log.d.ts.map +1 -0
  46. package/dist/node/debug-log.js +58 -0
  47. package/dist/node/debug-log.js.map +1 -0
  48. package/dist/node/index.d.ts +5 -4
  49. package/dist/node/index.js +5 -3
  50. package/dist/node/pi/definition.d.ts +1 -1
  51. package/dist/node/pi/definition.d.ts.map +1 -1
  52. package/dist/node/pi/dsl.d.ts +5 -2
  53. package/dist/node/pi/dsl.d.ts.map +1 -1
  54. package/dist/node/pi/dsl.js +22 -3
  55. package/dist/node/pi/dsl.js.map +1 -1
  56. package/dist/node/pi/factory.d.ts +37 -34
  57. package/dist/node/pi/factory.d.ts.map +1 -1
  58. package/dist/node/pi/factory.js.map +1 -1
  59. package/dist/node/pi/mappers.js +0 -1
  60. package/dist/node/pi/mappers.js.map +1 -1
  61. package/dist/node/pi/route-schemas.js +42 -10
  62. package/dist/node/pi/route-schemas.js.map +1 -1
  63. package/dist/node/pi/types.d.ts +155 -7
  64. package/dist/node/pi/types.d.ts.map +1 -1
  65. package/dist/node/pi/types.js +6 -0
  66. package/dist/node/pi/types.js.map +1 -0
  67. package/dist/node/pi/workflow/active-session.d.ts +2 -0
  68. package/dist/node/pi/workflow/active-session.js +107 -0
  69. package/dist/node/pi/workflow/active-session.js.map +1 -0
  70. package/dist/node/pi/workflow/agent-runner.d.ts +13 -0
  71. package/dist/node/pi/workflow/agent-runner.d.ts.map +1 -0
  72. package/dist/node/pi/workflow/agent-runner.js +228 -0
  73. package/dist/node/pi/workflow/agent-runner.js.map +1 -0
  74. package/dist/node/pi/workflow/tool-journal.js +157 -0
  75. package/dist/node/pi/workflow/tool-journal.js.map +1 -0
  76. package/dist/node/pi/workflow/workflow.d.ts +29 -0
  77. package/dist/node/pi/workflow/workflow.d.ts.map +1 -0
  78. package/dist/node/pi/workflow/workflow.js +219 -0
  79. package/dist/node/pi/workflow/workflow.js.map +1 -0
  80. package/dist/node/routes.d.ts +38 -35
  81. package/dist/node/routes.d.ts.map +1 -1
  82. package/dist/node/routes.js +203 -132
  83. package/dist/node/routes.js.map +1 -1
  84. package/dist/node/schema.js +1 -1
  85. package/dist/node/schema.js.map +1 -1
  86. package/package.json +30 -29
  87. package/dist/browser/client-Bk-J98pf.d.ts +0 -679
  88. package/dist/browser/client-Bk-J98pf.d.ts.map +0 -1
  89. package/dist/browser/factory-DKoO_lRA.js.map +0 -1
  90. package/dist/browser/index.d.ts.map +0 -1
  91. package/dist/node/pi/clients.d.ts.map +0 -1
  92. package/dist/node/pi/clients.js +0 -18
  93. package/dist/node/pi/clients.js.map +0 -1
  94. package/dist/node/pi/workflow.d.ts +0 -31
  95. package/dist/node/pi/workflow.d.ts.map +0 -1
  96. package/dist/node/pi/workflow.js +0 -242
  97. package/dist/node/pi/workflow.js.map +0 -1
  98. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,74 +1,66 @@
1
1
  import { piSchema } from "./schema.js";
2
2
  import { piFragmentDefinition } from "./pi/definition.js";
3
- import { messageAckSchema, sessionBaseSchema, sessionDetailSchema } from "./pi/route-schemas.js";
4
- import { extractAssistantTextFromMessage, normalizeSteeringMode, toSessionOutput } from "./pi/mappers.js";
5
- import { PI_WORKFLOW_NAME } from "./pi/workflow.js";
3
+ import { PiLogger } from "./debug-log.js";
4
+ import { normalizeSteeringMode, toSessionOutput } from "./pi/mappers.js";
5
+ import { activeSessionStreamItemSchema, messageAckSchema, sessionBaseSchema, sessionDetailSchema } from "./pi/route-schemas.js";
6
+ import { createInitialPiAgentLoopState, ensurePiActiveSessionState } from "./pi/workflow/active-session.js";
7
+ import { PI_WORKFLOW_NAME } from "./pi/workflow/workflow.js";
6
8
  import { defineRoutes } from "@fragno-dev/core";
9
+ import { serviceCalls } from "@fragno-dev/db";
7
10
  import { createId } from "@fragno-dev/db/id";
8
11
  import { z } from "zod";
9
12
 
10
13
  //#region src/routes.ts
11
14
  const DEFAULT_PAGE_SIZE = 50;
12
15
  const MAX_PAGE_SIZE = 200;
13
- const MAX_HISTORY_RUNS = 5;
14
16
  const createRouteError = (code, message, status) => {
15
17
  const error = new Error(message);
16
18
  error.code = code;
17
19
  error.status = status;
18
20
  return error;
19
21
  };
20
- const isSessionRouteErrorCode = (value) => value === "SESSION_NOT_FOUND" || value === "WORKFLOWS_REQUIRED" || value === "WORKFLOW_INSTANCE_MISSING";
21
- const isRecord = (value) => typeof value === "object" && value !== null;
22
- const getArrayFromResult = (result, key) => {
23
- if (!isRecord(result)) return null;
24
- const value = result[key];
25
- return Array.isArray(value) ? value : null;
22
+ const parseBooleanQueryValue = (value, defaultValue) => {
23
+ if (value === null) return defaultValue;
24
+ const normalized = value.trim().toLowerCase();
25
+ if (normalized === "1" || normalized === "true" || normalized === "yes") return true;
26
+ if (normalized === "0" || normalized === "false" || normalized === "no") return false;
27
+ return defaultValue;
26
28
  };
27
- const getAssistantFromResult = (result) => {
28
- if (!isRecord(result)) return null;
29
- const assistant = result["assistant"];
30
- if (!assistant || typeof assistant !== "object") return null;
31
- return assistant;
29
+ const projectSessionDetailState = (state) => {
30
+ const nextState = state ?? createInitialPiAgentLoopState();
31
+ return {
32
+ messages: nextState.messages,
33
+ events: nextState.events,
34
+ trace: nextState.trace,
35
+ summaries: nextState.summaries,
36
+ turn: nextState.turn,
37
+ phase: nextState.phase,
38
+ waitingFor: nextState.waitingFor
39
+ };
32
40
  };
33
- const parseAssistantTurn = (name) => {
34
- const match = /^assistant-(\d+)$/.exec(name);
35
- return match ? Number.parseInt(match[1], 10) : null;
41
+ const isSessionStreamable = (state) => state.phase !== "complete";
42
+ const ensureLiveSessionActiveState = (state) => {
43
+ if (!isSessionStreamable(state)) return null;
44
+ return ensurePiActiveSessionState(state);
36
45
  };
37
- const deriveHistory = (steps, output) => {
38
- let messages = [];
39
- const trace = [];
40
- const summaries = [];
41
- let lastAssistant = null;
42
- const sortedSteps = [...steps].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
43
- for (const step of sortedSteps) {
44
- if (!step.result) continue;
45
- const stepMessages = getArrayFromResult(step.result, "messages");
46
- if (stepMessages) messages = stepMessages;
47
- const stepTrace = getArrayFromResult(step.result, "trace");
48
- if (stepTrace) trace.push(...stepTrace);
49
- const assistant = getAssistantFromResult(step.result);
50
- const turn = parseAssistantTurn(step.name);
51
- if (assistant && turn !== null) {
52
- lastAssistant = assistant;
53
- summaries.push({
54
- turn,
55
- assistant,
56
- summary: extractAssistantTextFromMessage(assistant) || null
57
- });
58
- }
59
- }
60
- if (messages.length === 0 && isRecord(output) && Array.isArray(output["messages"])) messages = output["messages"];
61
- if (lastAssistant && !messages.some((message) => message?.role === "assistant")) messages = [...messages, lastAssistant];
46
+ const toActiveSessionProtocolMessage = (update, source) => {
47
+ if (update.type === "settled") return {
48
+ layer: "system",
49
+ type: "settled",
50
+ turn: update.turn,
51
+ status: update.status
52
+ };
62
53
  return {
63
- messages,
64
- trace,
65
- summaries
54
+ layer: "pi",
55
+ type: "event",
56
+ turn: update.turn,
57
+ source,
58
+ event: update.event
66
59
  };
67
60
  };
68
- const collectHistorySteps = (pages, maxRunNumber) => {
69
- return pages.filter((page) => page.runNumber <= maxRunNumber).flatMap((page) => page.steps);
70
- };
71
61
  const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, defineRoute, serviceDeps }) => {
62
+ PiLogger.reset();
63
+ if (config.logging) PiLogger.configure(config.logging);
72
64
  return [
73
65
  defineRoute({
74
66
  method: "POST",
@@ -76,23 +68,16 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
76
68
  inputSchema: z.object({
77
69
  agent: z.string(),
78
70
  name: z.string().optional(),
71
+ systemMessage: z.string().optional(),
79
72
  metadata: z.any().optional(),
80
73
  tags: z.array(z.string()).optional(),
81
74
  steeringMode: z.enum(["all", "one-at-a-time"]).optional()
82
75
  }),
83
76
  outputSchema: sessionBaseSchema,
84
- errorCodes: [
85
- "AGENT_NOT_FOUND",
86
- "WORKFLOWS_REQUIRED",
87
- "WORKFLOW_CREATE_FAILED"
88
- ],
77
+ errorCodes: ["AGENT_NOT_FOUND", "WORKFLOW_CREATE_FAILED"],
89
78
  handler: async function({ input }, { json, error }) {
90
79
  const values = await input.valid();
91
80
  const workflowsService = serviceDeps.workflows;
92
- if (!workflowsService) return error({
93
- message: "Workflows service is required.",
94
- code: "WORKFLOWS_REQUIRED"
95
- }, { status: 500 });
96
81
  const agentName = values.agent;
97
82
  const agent = config.agents?.[agentName];
98
83
  if (!agent) return error({
@@ -103,12 +88,13 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
103
88
  const steeringMode = normalizeSteeringMode(values.steeringMode ?? config.defaultSteeringMode);
104
89
  const sessionId = createId();
105
90
  try {
106
- const created = await this.handlerTx().withServiceCalls(() => [workflowsService.createInstance(PI_WORKFLOW_NAME, {
91
+ const systemPrompt = [agent.systemPrompt, values.systemMessage].filter((value) => typeof value === "string" && value.trim() !== "").join("\n\n");
92
+ await this.handlerTx().withServiceCalls(() => [workflowsService.createInstance(PI_WORKFLOW_NAME, {
107
93
  id: sessionId,
108
94
  params: {
109
95
  sessionId,
110
96
  agentName,
111
- systemPrompt: agent.systemPrompt,
97
+ systemPrompt,
112
98
  initialMessages: []
113
99
  }
114
100
  })]).mutate(({ forSchema }) => {
@@ -117,22 +103,18 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
117
103
  name: values.name ?? null,
118
104
  agent: agentName,
119
105
  status: "active",
120
- workflowInstanceId: sessionId,
121
106
  steeringMode,
122
107
  metadata: values.metadata ?? null,
123
108
  tags: values.tags ?? null,
124
109
  createdAt: now,
125
110
  updatedAt: now
126
111
  });
127
- }).transform(({ serviceResult }) => serviceResult[0]).execute();
128
- const workflowInstanceId = created.id;
129
- const workflowStatus = created.details;
112
+ }).execute();
130
113
  return json({
131
114
  id: sessionId,
132
115
  name: values.name ?? null,
133
- status: workflowStatus.status,
116
+ status: "active",
134
117
  agent: agentName,
135
- workflowInstanceId,
136
118
  steeringMode,
137
119
  metadata: values.metadata ?? null,
138
120
  tags: values.tags ?? [],
@@ -164,68 +146,55 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
164
146
  defineRoute({
165
147
  method: "GET",
166
148
  path: "/sessions/:sessionId",
167
- outputSchema: sessionDetailSchema,
168
- errorCodes: [
169
- "SESSION_NOT_FOUND",
170
- "WORKFLOWS_REQUIRED",
171
- "WORKFLOW_INSTANCE_MISSING"
149
+ queryParameters: [
150
+ "events",
151
+ "trace",
152
+ "summaries"
172
153
  ],
173
- handler: async function({ pathParams }, { json, error }) {
154
+ outputSchema: sessionDetailSchema,
155
+ errorCodes: ["SESSION_NOT_FOUND", "WORKFLOW_INSTANCE_MISSING"],
156
+ handler: async function({ pathParams, query }, { json, error }) {
174
157
  const sessionId = pathParams.sessionId;
158
+ const includeEvents = parseBooleanQueryValue(query.get("events"), true);
159
+ const includeTrace = parseBooleanQueryValue(query.get("trace"), true);
160
+ const includeSummaries = parseBooleanQueryValue(query.get("summaries"), true);
175
161
  const workflowsService = serviceDeps.workflows;
176
- if (!workflowsService) return error({
177
- message: "Workflows service is required.",
178
- code: "WORKFLOWS_REQUIRED"
179
- }, { status: 500 });
180
162
  const workflowName = PI_WORKFLOW_NAME;
181
163
  try {
182
- const [sessionRow] = await this.handlerTx().retrieve(({ forSchema }) => {
164
+ const liveSessionSnapshot = workflowsService.getLiveInstanceState(workflowName, sessionId);
165
+ const result = await this.handlerTx().retrieve(({ forSchema }) => {
183
166
  return forSchema(piSchema).findFirst("session", (b) => b.whereIndex("primary", (eb) => eb("id", "=", sessionId)));
184
- }).execute();
185
- if (!sessionRow) throw createRouteError("SESSION_NOT_FOUND", `Session ${sessionId} not found.`, 404);
186
- const workflowInstanceId = sessionRow.workflowInstanceId;
187
- if (!workflowInstanceId) throw createRouteError("SESSION_NOT_FOUND", `Session ${sessionId} not found.`, 404);
188
- const result = await this.handlerTx().withServiceCalls(() => {
189
- const historyCalls = Array.from({ length: MAX_HISTORY_RUNS + 1 }, (_, runNumber) => workflowsService.listHistory({
190
- workflowName,
191
- instanceId: workflowInstanceId,
192
- runNumber
193
- }));
194
- return [
195
- workflowsService.getInstanceStatus(workflowName, workflowInstanceId),
196
- workflowsService.getInstanceRunNumber(workflowName, workflowInstanceId),
197
- ...historyCalls
198
- ];
199
- }).mutate(({ forSchema, serviceIntermediateResult }) => {
200
- const [workflowStatus] = serviceIntermediateResult;
201
- forSchema(piSchema).update("session", sessionRow.id, (b) => b.set({
202
- status: workflowStatus.status,
203
- updatedAt: /* @__PURE__ */ new Date()
204
- }).check());
205
- }).transform(({ serviceResult }) => {
206
- const [workflowStatus, runNumber, ...historyPages] = serviceResult;
207
- const maxRunNumber = Number.isFinite(runNumber) ? Math.max(0, Math.min(MAX_HISTORY_RUNS, runNumber)) : 0;
167
+ }).withServiceCalls(() => serviceCalls(workflowsService.getInstanceStatus(workflowName, sessionId), liveSessionSnapshot ? void 0 : workflowsService.restoreInstanceState(workflowName, sessionId))).transform(({ retrieveResult, serviceResult }) => {
168
+ const [sessionRow] = retrieveResult;
169
+ if (!sessionRow) throw createRouteError("SESSION_NOT_FOUND", `Session ${sessionId} not found.`, 404);
170
+ const [workflowStatus, restoredState] = serviceResult;
171
+ const detailState = projectSessionDetailState(liveSessionSnapshot?.state ?? restoredState);
208
172
  return {
173
+ session: toSessionOutput(sessionRow),
209
174
  workflowStatus,
210
- history: deriveHistory(collectHistorySteps(historyPages.slice(0, maxRunNumber + 1), maxRunNumber), workflowStatus.output)
175
+ detailState
211
176
  };
212
177
  }).execute();
213
178
  return json({
214
- ...toSessionOutput(sessionRow),
179
+ ...result.session,
215
180
  status: result.workflowStatus.status,
216
181
  workflow: {
217
182
  status: result.workflowStatus.status,
218
183
  error: result.workflowStatus.error,
219
184
  output: result.workflowStatus.output
220
185
  },
221
- messages: result.history.messages,
222
- trace: result.history.trace,
223
- summaries: result.history.summaries
186
+ messages: result.detailState.messages,
187
+ events: includeEvents ? result.detailState.events : [],
188
+ trace: includeTrace ? result.detailState.trace : [],
189
+ turn: result.detailState.turn,
190
+ phase: result.detailState.phase,
191
+ waitingFor: result.detailState.waitingFor,
192
+ summaries: includeSummaries ? result.detailState.summaries : []
224
193
  });
225
194
  } catch (err) {
226
195
  if (err && typeof err === "object" && "code" in err && "status" in err) {
227
196
  const routeError = err;
228
- const code = isSessionRouteErrorCode(routeError.code) ? routeError.code : "WORKFLOW_INSTANCE_MISSING";
197
+ const code = routeError.code === "SESSION_NOT_FOUND" ? "SESSION_NOT_FOUND" : "WORKFLOW_INSTANCE_MISSING";
229
198
  const status = code === "SESSION_NOT_FOUND" ? 404 : 500;
230
199
  return error({
231
200
  message: routeError.message,
@@ -237,7 +206,124 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
237
206
  code: "SESSION_NOT_FOUND"
238
207
  }, { status: 404 });
239
208
  return error({
240
- message: err instanceof Error ? err.message : "Failed to load workflow history.",
209
+ message: err instanceof Error ? err.message : "Failed to load workflow detail.",
210
+ code: "WORKFLOW_INSTANCE_MISSING"
211
+ }, { status: 500 });
212
+ }
213
+ }
214
+ }),
215
+ defineRoute({
216
+ method: "GET",
217
+ path: "/sessions/:sessionId/active",
218
+ outputSchema: z.array(activeSessionStreamItemSchema),
219
+ errorCodes: ["SESSION_NOT_FOUND", "WORKFLOW_INSTANCE_MISSING"],
220
+ handler: async function({ pathParams }, { error, jsonStream }) {
221
+ const sessionId = pathParams.sessionId;
222
+ const workflowsService = serviceDeps.workflows;
223
+ const workflowName = PI_WORKFLOW_NAME;
224
+ try {
225
+ const liveSessionSnapshot = workflowsService.getLiveInstanceState(workflowName, sessionId);
226
+ const [restoredState] = liveSessionSnapshot ? [void 0] : await this.handlerTx().withServiceCalls(() => [workflowsService.restoreInstanceState(workflowName, sessionId)]).transform(({ serviceResult }) => serviceResult).execute();
227
+ const detailState = liveSessionSnapshot?.state ?? restoredState;
228
+ if (!detailState) throw createRouteError("WORKFLOW_INSTANCE_MISSING", `Session ${sessionId} could not load live workflow state.`, 500);
229
+ const activeSession = ensureLiveSessionActiveState(detailState);
230
+ const targetTurn = detailState.turn;
231
+ const replayUpdates = activeSession?.replayTurn(targetTurn) ?? [];
232
+ const snapshotMessage = {
233
+ layer: "system",
234
+ type: "snapshot",
235
+ turn: targetTurn,
236
+ phase: detailState.phase,
237
+ waitingFor: detailState.waitingFor,
238
+ replayCount: replayUpdates.filter((update) => update.type === "event").length
239
+ };
240
+ if (!activeSession) {
241
+ const inactiveMessage = {
242
+ layer: "system",
243
+ type: "inactive",
244
+ reason: detailState.phase === "complete" ? "session-complete" : "session-idle",
245
+ turn: detailState.turn,
246
+ phase: detailState.phase,
247
+ waitingFor: detailState.waitingFor
248
+ };
249
+ return jsonStream(async (stream) => {
250
+ await stream.write(snapshotMessage);
251
+ await stream.write(inactiveMessage);
252
+ });
253
+ }
254
+ const pendingMessages = replayUpdates.map((update) => toActiveSessionProtocolMessage(update, "replay"));
255
+ if (replayUpdates.some((update) => update.type === "settled")) return jsonStream(async (stream) => {
256
+ await stream.write(snapshotMessage);
257
+ for (const message of pendingMessages) await stream.write(message);
258
+ });
259
+ let cleanupDone = false;
260
+ let streamWriter = null;
261
+ let flushPromise = null;
262
+ let resolveSettled = null;
263
+ const settledPromise = new Promise((resolve) => {
264
+ resolveSettled = resolve;
265
+ });
266
+ const flushPendingMessages = async () => {
267
+ if (streamWriter === null || flushPromise) return flushPromise ?? Promise.resolve();
268
+ flushPromise = (async () => {
269
+ while (pendingMessages.length > 0 && streamWriter) {
270
+ const nextMessage = pendingMessages.shift();
271
+ if (!nextMessage) continue;
272
+ await streamWriter(nextMessage);
273
+ }
274
+ })();
275
+ try {
276
+ await flushPromise;
277
+ } finally {
278
+ flushPromise = null;
279
+ if (pendingMessages.length > 0 && streamWriter !== null) flushPendingMessages();
280
+ }
281
+ };
282
+ const unsubscribe = activeSession.subscribe((update) => {
283
+ if (update.turn !== targetTurn) return;
284
+ pendingMessages.push(toActiveSessionProtocolMessage(update, "live"));
285
+ if (update.type === "settled") resolveSettled?.();
286
+ if (streamWriter) flushPendingMessages();
287
+ });
288
+ const cleanup = () => {
289
+ if (cleanupDone) return;
290
+ cleanupDone = true;
291
+ unsubscribe();
292
+ streamWriter = null;
293
+ resolveSettled?.();
294
+ };
295
+ return jsonStream(async (stream) => {
296
+ streamWriter = (message) => stream.write(message);
297
+ stream.onAbort(() => {
298
+ cleanup();
299
+ });
300
+ try {
301
+ await stream.write(snapshotMessage);
302
+ await flushPendingMessages();
303
+ await settledPromise;
304
+ await flushPendingMessages();
305
+ } catch (streamError) {
306
+ cleanup();
307
+ throw streamError;
308
+ }
309
+ cleanup();
310
+ });
311
+ } catch (err) {
312
+ if (err && typeof err === "object" && "code" in err && "status" in err) {
313
+ const routeError = err;
314
+ const code = routeError.code === "SESSION_NOT_FOUND" ? "SESSION_NOT_FOUND" : "WORKFLOW_INSTANCE_MISSING";
315
+ const status = code === "SESSION_NOT_FOUND" ? 404 : 500;
316
+ return error({
317
+ message: routeError.message,
318
+ code
319
+ }, { status });
320
+ }
321
+ if (err instanceof Error && err.message === "INSTANCE_NOT_FOUND") return error({
322
+ message: `Session ${sessionId} not found.`,
323
+ code: "SESSION_NOT_FOUND"
324
+ }, { status: 404 });
325
+ return error({
326
+ message: err instanceof Error ? err.message : "Failed to stream the active session.",
241
327
  code: "WORKFLOW_INSTANCE_MISSING"
242
328
  }, { status: 500 });
243
329
  }
@@ -254,17 +340,13 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
254
340
  outputSchema: messageAckSchema,
255
341
  errorCodes: [
256
342
  "SESSION_NOT_FOUND",
257
- "WORKFLOWS_REQUIRED",
343
+ "SESSION_NOT_READY",
258
344
  "WORKFLOW_INSTANCE_MISSING"
259
345
  ],
260
346
  handler: async function({ input, pathParams }, { json, error }) {
261
347
  const values = await input.valid();
262
348
  const sessionId = pathParams.sessionId;
263
349
  const workflowsService = serviceDeps.workflows;
264
- if (!workflowsService) return error({
265
- message: "Workflows service is required.",
266
- code: "WORKFLOWS_REQUIRED"
267
- }, { status: 500 });
268
350
  const workflowName = PI_WORKFLOW_NAME;
269
351
  const payload = {
270
352
  text: values.text,
@@ -276,34 +358,23 @@ const piRoutesFactory = defineRoutes(piFragmentDefinition).create(({ config, def
276
358
  return forSchema(piSchema).findFirst("session", (b) => b.whereIndex("primary", (eb) => eb("id", "=", sessionId)));
277
359
  }).execute();
278
360
  if (!sessionRow) throw createRouteError("SESSION_NOT_FOUND", `Session ${sessionId} not found.`, 404);
279
- const workflowInstanceId = sessionRow.workflowInstanceId;
280
- if (!workflowInstanceId) throw createRouteError("SESSION_NOT_FOUND", `Session ${sessionId} not found.`, 404);
281
361
  if (!payload.steeringMode) payload.steeringMode = normalizeSteeringMode(sessionRow.steeringMode ?? config.defaultSteeringMode);
282
- return json({ status: (await this.handlerTx().withServiceCalls(() => [
283
- workflowsService.sendEvent(workflowName, workflowInstanceId, {
284
- type: "user_message",
285
- payload
286
- }),
287
- workflowsService.getInstanceStatus(workflowName, workflowInstanceId),
288
- workflowsService.getInstanceRunNumber(workflowName, workflowInstanceId)
289
- ]).mutate(({ forSchema, serviceIntermediateResult }) => {
290
- const [, workflowStatus] = serviceIntermediateResult;
362
+ return json({ status: (await this.handlerTx().withServiceCalls(() => serviceCalls(workflowsService.sendEvent(workflowName, sessionId, {
363
+ type: "user_message",
364
+ payload
365
+ }), workflowsService.getInstanceStatus(workflowName, sessionId))).mutate(({ forSchema }) => {
291
366
  const updates = { updatedAt: /* @__PURE__ */ new Date() };
292
367
  if (values.steeringMode) updates.steeringMode = values.steeringMode;
293
- updates.status = workflowStatus.status;
294
368
  forSchema(piSchema).update("session", sessionRow.id, (b) => b.set(updates).check());
295
369
  }).transform(({ serviceResult }) => {
296
- const [, workflowStatus, runNumber] = serviceResult;
297
- return {
298
- workflowStatus,
299
- runNumber
300
- };
370
+ const [, workflowStatus] = serviceResult;
371
+ return { workflowStatus };
301
372
  }).execute()).workflowStatus.status }, 202);
302
373
  } catch (err) {
303
374
  if (err && typeof err === "object" && "code" in err && "status" in err) {
304
375
  const routeError = err;
305
- const code = isSessionRouteErrorCode(routeError.code) ? routeError.code : "WORKFLOW_INSTANCE_MISSING";
306
- const status = code === "SESSION_NOT_FOUND" ? 404 : 500;
376
+ const code = routeError.code === "SESSION_NOT_FOUND" ? "SESSION_NOT_FOUND" : routeError.code === "SESSION_NOT_READY" ? "SESSION_NOT_READY" : "WORKFLOW_INSTANCE_MISSING";
377
+ const status = code === "SESSION_NOT_FOUND" ? 404 : code === "SESSION_NOT_READY" ? 409 : 500;
307
378
  return error({
308
379
  message: routeError.message,
309
380
  code
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","names":[],"sources":["../../src/routes.ts"],"sourcesContent":["import { defineRoutes } from \"@fragno-dev/core\";\nimport { createId } from \"@fragno-dev/db/id\";\nimport type { AgentEvent, AgentMessage } from \"@mariozechner/pi-agent-core\";\nimport { z } from \"zod\";\n\nimport { piSchema } from \"./schema\";\nimport { piFragmentDefinition } from \"./pi/definition\";\nimport { messageAckSchema, sessionBaseSchema, sessionDetailSchema } from \"./pi/route-schemas\";\nimport {\n extractAssistantTextFromMessage,\n normalizeSteeringMode,\n toSessionOutput,\n} from \"./pi/mappers\";\nimport { PI_WORKFLOW_NAME } from \"./pi/workflow\";\nimport type {\n PiSession,\n PiTurnSummary,\n PiWorkflowHistoryStep,\n PiWorkflowsHistoryPage,\n PiWorkflowsInstanceStatus,\n} from \"./pi/types\";\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 200;\nconst MAX_HISTORY_RUNS = 5;\n\ntype RouteError = Error & { code: string; status: number };\n\nconst createRouteError = (code: string, message: string, status: number): RouteError => {\n const error = new Error(message) as RouteError;\n error.code = code;\n error.status = status;\n return error;\n};\n\ntype SessionRouteErrorCode =\n | \"SESSION_NOT_FOUND\"\n | \"WORKFLOWS_REQUIRED\"\n | \"WORKFLOW_INSTANCE_MISSING\";\n\nconst isSessionRouteErrorCode = (value: unknown): value is SessionRouteErrorCode =>\n value === \"SESSION_NOT_FOUND\" ||\n value === \"WORKFLOWS_REQUIRED\" ||\n value === \"WORKFLOW_INSTANCE_MISSING\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst getArrayFromResult = <T>(result: unknown, key: string): T[] | null => {\n if (!isRecord(result)) {\n return null;\n }\n const value = result[key];\n return Array.isArray(value) ? (value as T[]) : null;\n};\n\nconst getAssistantFromResult = (result: unknown): AgentMessage | null => {\n if (!isRecord(result)) {\n return null;\n }\n const assistant = result[\"assistant\"];\n if (!assistant || typeof assistant !== \"object\") {\n return null;\n }\n return assistant as AgentMessage;\n};\n\nconst parseAssistantTurn = (name: string): number | null => {\n const match = /^assistant-(\\d+)$/.exec(name);\n return match ? Number.parseInt(match[1], 10) : null;\n};\n\nconst deriveHistory = (steps: PiWorkflowHistoryStep[], output: unknown) => {\n let messages: AgentMessage[] = [];\n const trace: AgentEvent[] = [];\n const summaries: PiTurnSummary[] = [];\n let lastAssistant: AgentMessage | null = null;\n\n const sortedSteps = [...steps].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());\n\n for (const step of sortedSteps) {\n if (!step.result) {\n continue;\n }\n\n const stepMessages = getArrayFromResult<AgentMessage>(step.result, \"messages\");\n if (stepMessages) {\n messages = stepMessages;\n }\n\n const stepTrace = getArrayFromResult<AgentEvent>(step.result, \"trace\");\n if (stepTrace) {\n trace.push(...stepTrace);\n }\n\n const assistant = getAssistantFromResult(step.result);\n const turn = parseAssistantTurn(step.name);\n if (assistant && turn !== null) {\n lastAssistant = assistant;\n summaries.push({\n turn,\n assistant,\n summary: extractAssistantTextFromMessage(assistant) || null,\n });\n }\n }\n\n if (messages.length === 0 && isRecord(output) && Array.isArray(output[\"messages\"])) {\n messages = output[\"messages\"] as AgentMessage[];\n }\n if (lastAssistant && !messages.some((message) => message?.role === \"assistant\")) {\n messages = [...messages, lastAssistant];\n }\n\n return { messages, trace, summaries };\n};\n\nconst collectHistorySteps = (\n pages: PiWorkflowsHistoryPage[],\n maxRunNumber: number,\n): PiWorkflowHistoryStep[] => {\n return pages.filter((page) => page.runNumber <= maxRunNumber).flatMap((page) => page.steps);\n};\n\nexport const piRoutesFactory = defineRoutes(piFragmentDefinition).create(\n ({ config, defineRoute, serviceDeps }) => {\n return [\n defineRoute({\n method: \"POST\",\n path: \"/sessions\",\n inputSchema: z.object({\n agent: z.string(),\n name: z.string().optional(),\n metadata: z.any().optional(),\n tags: z.array(z.string()).optional(),\n steeringMode: z.enum([\"all\", \"one-at-a-time\"]).optional(),\n }),\n outputSchema: sessionBaseSchema,\n errorCodes: [\"AGENT_NOT_FOUND\", \"WORKFLOWS_REQUIRED\", \"WORKFLOW_CREATE_FAILED\"],\n handler: async function ({ input }, { json, error }) {\n const values = await input.valid();\n\n const workflowsService = serviceDeps.workflows;\n if (!workflowsService) {\n return error(\n { message: \"Workflows service is required.\", code: \"WORKFLOWS_REQUIRED\" },\n { status: 500 },\n );\n }\n\n const agentName = values.agent;\n const agent = config.agents?.[agentName];\n if (!agent) {\n return error(\n { message: `Agent ${agentName} not found.`, code: \"AGENT_NOT_FOUND\" },\n { status: 404 },\n );\n }\n\n const now = new Date();\n const steeringMode = normalizeSteeringMode(\n values.steeringMode ?? config.defaultSteeringMode,\n );\n const sessionId = createId();\n\n try {\n const created = await this.handlerTx()\n .withServiceCalls(\n () =>\n [\n workflowsService.createInstance(PI_WORKFLOW_NAME, {\n id: sessionId,\n params: {\n sessionId,\n agentName,\n systemPrompt: agent.systemPrompt,\n initialMessages: [],\n },\n }),\n ] as const,\n )\n .mutate(({ forSchema }) => {\n const uow = forSchema(piSchema);\n uow.create(\"session\", {\n id: sessionId,\n name: values.name ?? null,\n agent: agentName,\n status: \"active\",\n workflowInstanceId: sessionId,\n steeringMode,\n metadata: values.metadata ?? null,\n tags: values.tags ?? null,\n createdAt: now,\n updatedAt: now,\n });\n })\n .transform(({ serviceResult }) => serviceResult[0])\n .execute();\n\n const workflowInstanceId = created.id;\n const workflowStatus = created.details;\n\n const session: PiSession = {\n id: sessionId,\n name: values.name ?? null,\n status: workflowStatus.status,\n agent: agentName,\n workflowInstanceId,\n steeringMode,\n metadata: values.metadata ?? null,\n tags: values.tags ?? [],\n createdAt: now,\n updatedAt: now,\n };\n\n return json(session);\n } catch (err) {\n // TODO: cleanup workflow/session if createInstance or session persist fails.\n const message =\n err instanceof Error ? err.message : \"Failed to create workflow instance.\";\n return error({ message, code: \"WORKFLOW_CREATE_FAILED\" }, { status: 500 });\n }\n },\n }),\n defineRoute({\n method: \"GET\",\n path: \"/sessions\",\n queryParameters: [\"limit\"],\n outputSchema: z.array(sessionBaseSchema),\n handler: async function ({ query }, { json }) {\n const limit = Number.parseInt(query.get(\"limit\") ?? `${DEFAULT_PAGE_SIZE}`, 10);\n const normalizedLimit = Number.isFinite(limit)\n ? Math.max(1, Math.min(MAX_PAGE_SIZE, limit))\n : DEFAULT_PAGE_SIZE;\n\n const [sessions] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(piSchema);\n return uow.find(\"session\", (b) =>\n b\n .whereIndex(\"idx_session_created\")\n .orderByIndex(\"idx_session_created\", \"desc\")\n .pageSize(normalizedLimit),\n );\n })\n .execute();\n\n // TODO: hydrate workflow status without additional handlerTx calls.\n const outputs = sessions.map(toSessionOutput);\n return json(outputs);\n },\n }),\n defineRoute({\n method: \"GET\",\n path: \"/sessions/:sessionId\",\n outputSchema: sessionDetailSchema,\n errorCodes: [\"SESSION_NOT_FOUND\", \"WORKFLOWS_REQUIRED\", \"WORKFLOW_INSTANCE_MISSING\"],\n handler: async function ({ pathParams }, { json, error }) {\n const sessionId = pathParams.sessionId;\n\n const workflowsService = serviceDeps.workflows;\n if (!workflowsService) {\n return error(\n { message: \"Workflows service is required.\", code: \"WORKFLOWS_REQUIRED\" },\n { status: 500 },\n );\n }\n\n const workflowName = PI_WORKFLOW_NAME;\n\n try {\n const [sessionRow] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(piSchema);\n return uow.findFirst(\"session\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId)),\n );\n })\n .execute();\n\n if (!sessionRow) {\n throw createRouteError(\"SESSION_NOT_FOUND\", `Session ${sessionId} not found.`, 404);\n }\n\n const workflowInstanceId = sessionRow.workflowInstanceId;\n if (!workflowInstanceId) {\n throw createRouteError(\"SESSION_NOT_FOUND\", `Session ${sessionId} not found.`, 404);\n }\n\n const result = await this.handlerTx()\n .withServiceCalls(() => {\n const historyCalls = Array.from({ length: MAX_HISTORY_RUNS + 1 }, (_, runNumber) =>\n workflowsService.listHistory({\n workflowName,\n instanceId: workflowInstanceId,\n runNumber,\n }),\n );\n return [\n workflowsService.getInstanceStatus(workflowName, workflowInstanceId),\n workflowsService.getInstanceRunNumber(workflowName, workflowInstanceId),\n ...historyCalls,\n ];\n })\n .mutate(({ forSchema, serviceIntermediateResult }) => {\n const [workflowStatus] = serviceIntermediateResult as [\n PiWorkflowsInstanceStatus,\n number,\n ...PiWorkflowsHistoryPage[],\n ];\n const uow = forSchema(piSchema);\n uow.update(\"session\", sessionRow.id, (b) =>\n b\n .set({\n status: workflowStatus.status,\n updatedAt: new Date(),\n })\n .check(),\n );\n })\n .transform(({ serviceResult }) => {\n const [workflowStatus, runNumber, ...historyPages] = serviceResult as [\n PiWorkflowsInstanceStatus,\n number,\n ...PiWorkflowsHistoryPage[],\n ];\n\n const maxRunNumber = Number.isFinite(runNumber)\n ? Math.max(0, Math.min(MAX_HISTORY_RUNS, runNumber))\n : 0;\n const pages = historyPages.slice(0, maxRunNumber + 1);\n const steps = collectHistorySteps(pages, maxRunNumber);\n const history = deriveHistory(steps, workflowStatus.output);\n\n return { workflowStatus, history };\n })\n .execute();\n\n const session = toSessionOutput(sessionRow);\n\n return json({\n ...session,\n status: result.workflowStatus.status,\n workflow: {\n status: result.workflowStatus.status,\n error: result.workflowStatus.error,\n output: result.workflowStatus.output,\n },\n messages: result.history.messages,\n trace: result.history.trace,\n summaries: result.history.summaries,\n });\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && \"status\" in err) {\n const routeError = err as RouteError;\n const code = isSessionRouteErrorCode(routeError.code)\n ? routeError.code\n : \"WORKFLOW_INSTANCE_MISSING\";\n const status = code === \"SESSION_NOT_FOUND\" ? 404 : 500;\n return error({ message: routeError.message, code }, { status });\n }\n if (err instanceof Error && err.message === \"INSTANCE_NOT_FOUND\") {\n return error(\n { message: `Session ${sessionId} not found.`, code: \"SESSION_NOT_FOUND\" },\n { status: 404 },\n );\n }\n const message = err instanceof Error ? err.message : \"Failed to load workflow history.\";\n return error({ message, code: \"WORKFLOW_INSTANCE_MISSING\" }, { status: 500 });\n }\n },\n }),\n defineRoute({\n method: \"POST\",\n path: \"/sessions/:sessionId/messages\",\n inputSchema: z.object({\n text: z.string(),\n done: z.boolean().optional(),\n steeringMode: z.enum([\"all\", \"one-at-a-time\"]).optional(),\n }),\n outputSchema: messageAckSchema,\n errorCodes: [\"SESSION_NOT_FOUND\", \"WORKFLOWS_REQUIRED\", \"WORKFLOW_INSTANCE_MISSING\"],\n handler: async function ({ input, pathParams }, { json, error }) {\n const values = await input.valid();\n const sessionId = pathParams.sessionId;\n\n const workflowsService = serviceDeps.workflows;\n if (!workflowsService) {\n return error(\n { message: \"Workflows service is required.\", code: \"WORKFLOWS_REQUIRED\" },\n { status: 500 },\n );\n }\n\n const workflowName = PI_WORKFLOW_NAME;\n const payload: {\n text: string;\n done?: boolean;\n steeringMode?: \"all\" | \"one-at-a-time\";\n } = {\n text: values.text,\n done: values.done,\n };\n if (values.steeringMode) {\n payload.steeringMode = values.steeringMode;\n }\n\n try {\n const [sessionRow] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(piSchema);\n return uow.findFirst(\"session\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId)),\n );\n })\n .execute();\n\n if (!sessionRow) {\n throw createRouteError(\"SESSION_NOT_FOUND\", `Session ${sessionId} not found.`, 404);\n }\n\n const workflowInstanceId = sessionRow.workflowInstanceId;\n if (!workflowInstanceId) {\n throw createRouteError(\"SESSION_NOT_FOUND\", `Session ${sessionId} not found.`, 404);\n }\n\n if (!payload.steeringMode) {\n // Ensure workflow events use the session's steering mode when not overridden.\n payload.steeringMode = normalizeSteeringMode(\n sessionRow.steeringMode ?? config.defaultSteeringMode,\n );\n }\n\n const result = await this.handlerTx()\n .withServiceCalls(() => [\n workflowsService.sendEvent(workflowName, workflowInstanceId, {\n type: \"user_message\",\n payload,\n }),\n workflowsService.getInstanceStatus(workflowName, workflowInstanceId),\n workflowsService.getInstanceRunNumber(workflowName, workflowInstanceId),\n ])\n .mutate(({ forSchema, serviceIntermediateResult }) => {\n const [, workflowStatus] = serviceIntermediateResult as [\n unknown,\n PiWorkflowsInstanceStatus,\n number,\n ];\n const updates: {\n updatedAt: Date;\n steeringMode?: \"all\" | \"one-at-a-time\";\n status?: string;\n } = {\n updatedAt: new Date(),\n };\n if (values.steeringMode) {\n updates.steeringMode = values.steeringMode;\n }\n updates.status = workflowStatus.status;\n const uow = forSchema(piSchema);\n uow.update(\"session\", sessionRow.id, (b) => b.set(updates).check());\n })\n .transform(({ serviceResult }) => {\n const [, workflowStatus, runNumber] = serviceResult as [\n unknown,\n PiWorkflowsInstanceStatus,\n number,\n ];\n return { workflowStatus, runNumber };\n })\n .execute();\n\n return json(\n {\n status: result.workflowStatus.status,\n },\n 202,\n );\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && \"status\" in err) {\n const routeError = err as RouteError;\n const code = isSessionRouteErrorCode(routeError.code)\n ? routeError.code\n : \"WORKFLOW_INSTANCE_MISSING\";\n const status = code === \"SESSION_NOT_FOUND\" ? 404 : 500;\n return error({ message: routeError.message, code }, { status });\n }\n if (err instanceof Error && err.message === \"INSTANCE_NOT_FOUND\") {\n return error(\n { message: `Session ${sessionId} not found.`, code: \"SESSION_NOT_FOUND\" },\n { status: 404 },\n );\n }\n const message = err instanceof Error ? err.message : \"Failed to deliver message.\";\n return error({ message, code: \"WORKFLOW_INSTANCE_MISSING\" }, { status: 500 });\n }\n },\n }),\n ];\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../src/routes.ts"],"sourcesContent":["import { createId } from \"@fragno-dev/db/id\";\nimport { z } from \"zod\";\n\nimport { defineRoutes } from \"@fragno-dev/core\";\nimport { serviceCalls } from \"@fragno-dev/db\";\n\nimport { PiLogger } from \"./debug-log\";\nimport { piFragmentDefinition } from \"./pi/definition\";\nimport { normalizeSteeringMode, toSessionOutput } from \"./pi/mappers\";\nimport {\n activeSessionStreamItemSchema,\n messageAckSchema,\n sessionBaseSchema,\n sessionDetailSchema,\n} from \"./pi/route-schemas\";\nimport type {\n PiActiveSessionProtocolMessage,\n PiActiveSessionUpdate,\n PiAgentLoopState,\n PiAgentLoopSerializableState,\n PiSession,\n} from \"./pi/types\";\nimport {\n createInitialPiAgentLoopState,\n ensurePiActiveSessionState,\n PI_WORKFLOW_NAME,\n} from \"./pi/workflow/workflow\";\nimport { piSchema } from \"./schema\";\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 200;\n\ntype RouteError = Error & { code: string; status: number };\n\nconst createRouteError = (code: string, message: string, status: number): RouteError => {\n const error = new Error(message) as RouteError;\n error.code = code;\n error.status = status;\n return error;\n};\n\nconst parseBooleanQueryValue = (value: string | null, defaultValue: boolean): boolean => {\n if (value === null) {\n return defaultValue;\n }\n const normalized = value.trim().toLowerCase();\n if (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\") {\n return true;\n }\n if (normalized === \"0\" || normalized === \"false\" || normalized === \"no\") {\n return false;\n }\n return defaultValue;\n};\n\nconst projectSessionDetailState = (\n state: PiAgentLoopState | null | undefined,\n): PiAgentLoopSerializableState => {\n const nextState = state ?? createInitialPiAgentLoopState();\n\n return {\n messages: nextState.messages,\n events: nextState.events,\n trace: nextState.trace,\n summaries: nextState.summaries,\n turn: nextState.turn,\n phase: nextState.phase,\n waitingFor: nextState.waitingFor,\n };\n};\n\nconst isSessionStreamable = (state: PiAgentLoopState) => state.phase !== \"complete\";\n\nconst ensureLiveSessionActiveState = (state: PiAgentLoopState) => {\n if (!isSessionStreamable(state)) {\n return null;\n }\n return ensurePiActiveSessionState(state);\n};\n\nconst toActiveSessionProtocolMessage = (\n update: PiActiveSessionUpdate,\n source: \"replay\" | \"live\",\n): PiActiveSessionProtocolMessage => {\n if (update.type === \"settled\") {\n return {\n layer: \"system\",\n type: \"settled\",\n turn: update.turn,\n status: update.status,\n };\n }\n\n return {\n layer: \"pi\",\n type: \"event\",\n turn: update.turn,\n source,\n event: update.event,\n };\n};\n\nexport const piRoutesFactory = defineRoutes(piFragmentDefinition).create(\n ({ config, defineRoute, serviceDeps }) => {\n PiLogger.reset();\n if (config.logging) {\n PiLogger.configure(config.logging);\n }\n\n return [\n defineRoute({\n method: \"POST\",\n path: \"/sessions\",\n inputSchema: z.object({\n agent: z.string(),\n name: z.string().optional(),\n systemMessage: z.string().optional(),\n metadata: z.any().optional(),\n tags: z.array(z.string()).optional(),\n steeringMode: z.enum([\"all\", \"one-at-a-time\"]).optional(),\n }),\n outputSchema: sessionBaseSchema,\n errorCodes: [\"AGENT_NOT_FOUND\", \"WORKFLOW_CREATE_FAILED\"],\n handler: async function ({ input }, { json, error }) {\n const values = await input.valid();\n\n const workflowsService = serviceDeps.workflows;\n\n const agentName = values.agent;\n const agent = config.agents?.[agentName];\n if (!agent) {\n return error(\n { message: `Agent ${agentName} not found.`, code: \"AGENT_NOT_FOUND\" },\n { status: 404 },\n );\n }\n\n const now = new Date();\n const steeringMode = normalizeSteeringMode(\n values.steeringMode ?? config.defaultSteeringMode,\n );\n const sessionId = createId();\n\n try {\n const systemPrompt = [agent.systemPrompt, values.systemMessage]\n .filter((value): value is string => typeof value === \"string\" && value.trim() !== \"\")\n .join(\"\\n\\n\");\n\n await this.handlerTx()\n .withServiceCalls(\n () =>\n [\n workflowsService.createInstance(PI_WORKFLOW_NAME, {\n id: sessionId,\n params: {\n sessionId,\n agentName,\n systemPrompt,\n initialMessages: [],\n },\n }),\n ] as const,\n )\n .mutate(({ forSchema }) => {\n const uow = forSchema(piSchema);\n uow.create(\"session\", {\n id: sessionId,\n name: values.name ?? null,\n agent: agentName,\n status: \"active\",\n steeringMode,\n metadata: values.metadata ?? null,\n tags: values.tags ?? null,\n createdAt: now,\n updatedAt: now,\n });\n })\n .execute();\n\n const session: PiSession = {\n id: sessionId,\n name: values.name ?? null,\n status: \"active\",\n agent: agentName,\n steeringMode,\n metadata: values.metadata ?? null,\n tags: values.tags ?? [],\n createdAt: now,\n updatedAt: now,\n };\n\n return json(session);\n } catch (err) {\n // TODO: cleanup workflow/session if createInstance or session persist fails.\n const message =\n err instanceof Error ? err.message : \"Failed to create workflow instance.\";\n return error({ message, code: \"WORKFLOW_CREATE_FAILED\" }, { status: 500 });\n }\n },\n }),\n defineRoute({\n method: \"GET\",\n path: \"/sessions\",\n queryParameters: [\"limit\"],\n outputSchema: z.array(sessionBaseSchema),\n handler: async function ({ query }, { json }) {\n const limit = Number.parseInt(query.get(\"limit\") ?? `${DEFAULT_PAGE_SIZE}`, 10);\n const normalizedLimit = Number.isFinite(limit)\n ? Math.max(1, Math.min(MAX_PAGE_SIZE, limit))\n : DEFAULT_PAGE_SIZE;\n\n const [sessions] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(piSchema);\n return uow.find(\"session\", (b) =>\n b\n .whereIndex(\"idx_session_created\")\n .orderByIndex(\"idx_session_created\", \"desc\")\n .pageSize(normalizedLimit),\n );\n })\n .execute();\n\n const outputs = sessions.map(toSessionOutput);\n return json(outputs);\n },\n }),\n defineRoute({\n method: \"GET\",\n path: \"/sessions/:sessionId\",\n queryParameters: [\"events\", \"trace\", \"summaries\"],\n outputSchema: sessionDetailSchema,\n errorCodes: [\"SESSION_NOT_FOUND\", \"WORKFLOW_INSTANCE_MISSING\"],\n handler: async function ({ pathParams, query }, { json, error }) {\n const sessionId = pathParams.sessionId;\n const includeEvents = parseBooleanQueryValue(query.get(\"events\"), true);\n const includeTrace = parseBooleanQueryValue(query.get(\"trace\"), true);\n const includeSummaries = parseBooleanQueryValue(query.get(\"summaries\"), true);\n\n const workflowsService = serviceDeps.workflows;\n\n const workflowName = PI_WORKFLOW_NAME;\n\n try {\n const liveSessionSnapshot = workflowsService.getLiveInstanceState(\n workflowName,\n sessionId,\n );\n\n const result = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(piSchema);\n return uow.findFirst(\"session\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId)),\n );\n })\n .withServiceCalls(() =>\n serviceCalls(\n workflowsService.getInstanceStatus(workflowName, sessionId),\n liveSessionSnapshot\n ? undefined\n : workflowsService.restoreInstanceState(workflowName, sessionId),\n ),\n )\n .transform(({ retrieveResult, serviceResult }) => {\n const [sessionRow] = retrieveResult;\n if (!sessionRow) {\n throw createRouteError(\n \"SESSION_NOT_FOUND\",\n `Session ${sessionId} not found.`,\n 404,\n );\n }\n\n const [workflowStatus, restoredState] = serviceResult;\n const detailState = projectSessionDetailState(\n liveSessionSnapshot?.state ?? restoredState,\n );\n\n return {\n session: toSessionOutput(sessionRow),\n workflowStatus,\n detailState,\n };\n })\n .execute();\n\n return json({\n ...result.session,\n status: result.workflowStatus.status,\n workflow: {\n status: result.workflowStatus.status,\n error: result.workflowStatus.error,\n output: result.workflowStatus.output,\n },\n messages: result.detailState.messages,\n events: includeEvents ? result.detailState.events : [],\n trace: includeTrace ? result.detailState.trace : [],\n turn: result.detailState.turn,\n phase: result.detailState.phase,\n waitingFor: result.detailState.waitingFor,\n summaries: includeSummaries ? result.detailState.summaries : [],\n });\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && \"status\" in err) {\n const routeError = err as RouteError;\n const code =\n routeError.code === \"SESSION_NOT_FOUND\"\n ? \"SESSION_NOT_FOUND\"\n : \"WORKFLOW_INSTANCE_MISSING\";\n const status = code === \"SESSION_NOT_FOUND\" ? 404 : 500;\n return error({ message: routeError.message, code }, { status });\n }\n if (err instanceof Error && err.message === \"INSTANCE_NOT_FOUND\") {\n return error(\n { message: `Session ${sessionId} not found.`, code: \"SESSION_NOT_FOUND\" },\n { status: 404 },\n );\n }\n const message = err instanceof Error ? err.message : \"Failed to load workflow detail.\";\n return error({ message, code: \"WORKFLOW_INSTANCE_MISSING\" }, { status: 500 });\n }\n },\n }),\n defineRoute({\n method: \"GET\",\n path: \"/sessions/:sessionId/active\",\n outputSchema: z.array(activeSessionStreamItemSchema),\n errorCodes: [\"SESSION_NOT_FOUND\", \"WORKFLOW_INSTANCE_MISSING\"],\n handler: async function ({ pathParams }, { error, jsonStream }) {\n const sessionId = pathParams.sessionId;\n\n const workflowsService = serviceDeps.workflows;\n\n const workflowName = PI_WORKFLOW_NAME;\n\n try {\n const liveSessionSnapshot = workflowsService.getLiveInstanceState(\n workflowName,\n sessionId,\n );\n const [restoredState] = liveSessionSnapshot\n ? [undefined]\n : await this.handlerTx()\n .withServiceCalls(() => [\n workflowsService.restoreInstanceState(workflowName, sessionId),\n ])\n .transform(({ serviceResult }) => serviceResult)\n .execute();\n\n const detailState = liveSessionSnapshot?.state ?? restoredState;\n if (!detailState) {\n throw createRouteError(\n \"WORKFLOW_INSTANCE_MISSING\",\n `Session ${sessionId} could not load live workflow state.`,\n 500,\n );\n }\n\n const activeSession = ensureLiveSessionActiveState(detailState);\n const targetTurn = detailState.turn;\n const replayUpdates = activeSession?.replayTurn(targetTurn) ?? [];\n const snapshotMessage: PiActiveSessionProtocolMessage = {\n layer: \"system\",\n type: \"snapshot\",\n turn: targetTurn,\n phase: detailState.phase,\n waitingFor: detailState.waitingFor,\n replayCount: replayUpdates.filter((update) => update.type === \"event\").length,\n };\n\n if (!activeSession) {\n const inactiveMessage: PiActiveSessionProtocolMessage = {\n layer: \"system\",\n type: \"inactive\",\n reason: detailState.phase === \"complete\" ? \"session-complete\" : \"session-idle\",\n turn: detailState.turn,\n phase: detailState.phase,\n waitingFor: detailState.waitingFor,\n };\n\n return jsonStream(async (stream) => {\n await stream.write(snapshotMessage);\n await stream.write(inactiveMessage);\n });\n }\n\n const pendingMessages = replayUpdates.map((update) =>\n toActiveSessionProtocolMessage(update, \"replay\"),\n );\n const replayAlreadySettled = replayUpdates.some((update) => update.type === \"settled\");\n\n if (replayAlreadySettled) {\n return jsonStream(async (stream) => {\n await stream.write(snapshotMessage);\n for (const message of pendingMessages) {\n await stream.write(message);\n }\n });\n }\n\n let cleanupDone = false;\n let streamWriter: ((message: PiActiveSessionProtocolMessage) => Promise<void>) | null =\n null;\n let flushPromise: Promise<void> | null = null;\n let resolveSettled: (() => void) | null = null;\n const settledPromise = new Promise<void>((resolve) => {\n resolveSettled = resolve;\n });\n\n const flushPendingMessages = async () => {\n if (streamWriter === null || flushPromise) {\n return flushPromise ?? Promise.resolve();\n }\n\n flushPromise = (async () => {\n while (pendingMessages.length > 0 && streamWriter) {\n const nextMessage = pendingMessages.shift();\n if (!nextMessage) {\n continue;\n }\n await streamWriter(nextMessage);\n }\n })();\n\n try {\n await flushPromise;\n } finally {\n flushPromise = null;\n if (pendingMessages.length > 0 && streamWriter !== null) {\n void flushPendingMessages();\n }\n }\n };\n\n const unsubscribe = activeSession.subscribe((update: PiActiveSessionUpdate) => {\n if (update.turn !== targetTurn) {\n return;\n }\n\n pendingMessages.push(toActiveSessionProtocolMessage(update, \"live\"));\n if (update.type === \"settled\") {\n resolveSettled?.();\n }\n if (streamWriter) {\n void flushPendingMessages();\n }\n });\n\n const cleanup = () => {\n if (cleanupDone) {\n return;\n }\n cleanupDone = true;\n unsubscribe();\n streamWriter = null;\n resolveSettled?.();\n };\n\n return jsonStream(async (stream) => {\n streamWriter = (message) => stream.write(message);\n stream.onAbort(() => {\n cleanup();\n });\n\n try {\n await stream.write(snapshotMessage);\n await flushPendingMessages();\n await settledPromise;\n await flushPendingMessages();\n } catch (streamError) {\n cleanup();\n throw streamError;\n }\n cleanup();\n });\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && \"status\" in err) {\n const routeError = err as RouteError;\n const code =\n routeError.code === \"SESSION_NOT_FOUND\"\n ? \"SESSION_NOT_FOUND\"\n : \"WORKFLOW_INSTANCE_MISSING\";\n const status = code === \"SESSION_NOT_FOUND\" ? 404 : 500;\n return error({ message: routeError.message, code }, { status });\n }\n if (err instanceof Error && err.message === \"INSTANCE_NOT_FOUND\") {\n return error(\n { message: `Session ${sessionId} not found.`, code: \"SESSION_NOT_FOUND\" },\n { status: 404 },\n );\n }\n const message =\n err instanceof Error ? err.message : \"Failed to stream the active session.\";\n return error({ message, code: \"WORKFLOW_INSTANCE_MISSING\" }, { status: 500 });\n }\n },\n }),\n defineRoute({\n method: \"POST\",\n path: \"/sessions/:sessionId/messages\",\n inputSchema: z.object({\n text: z.string(),\n done: z.boolean().optional(),\n steeringMode: z.enum([\"all\", \"one-at-a-time\"]).optional(),\n }),\n outputSchema: messageAckSchema,\n errorCodes: [\"SESSION_NOT_FOUND\", \"SESSION_NOT_READY\", \"WORKFLOW_INSTANCE_MISSING\"],\n handler: async function ({ input, pathParams }, { json, error }) {\n const values = await input.valid();\n const sessionId = pathParams.sessionId;\n\n const workflowsService = serviceDeps.workflows;\n\n const workflowName = PI_WORKFLOW_NAME;\n const payload: {\n text: string;\n done?: boolean;\n steeringMode?: \"all\" | \"one-at-a-time\";\n } = {\n text: values.text,\n done: values.done,\n };\n if (values.steeringMode) {\n payload.steeringMode = values.steeringMode;\n }\n\n try {\n const [sessionRow] = await this.handlerTx()\n .retrieve(({ forSchema }) => {\n const uow = forSchema(piSchema);\n return uow.findFirst(\"session\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", sessionId)),\n );\n })\n .execute();\n\n if (!sessionRow) {\n throw createRouteError(\"SESSION_NOT_FOUND\", `Session ${sessionId} not found.`, 404);\n }\n\n if (!payload.steeringMode) {\n // This route still needs the persisted session row before sending the event so the\n // workflow receives the session's effective steering mode when the caller omits it.\n payload.steeringMode = normalizeSteeringMode(\n sessionRow.steeringMode ?? config.defaultSteeringMode,\n );\n }\n\n const result = await this.handlerTx()\n .withServiceCalls(() =>\n serviceCalls(\n workflowsService.sendEvent(workflowName, sessionId, {\n type: \"user_message\",\n payload,\n }),\n workflowsService.getInstanceStatus(workflowName, sessionId),\n ),\n )\n .mutate(({ forSchema }) => {\n const updates: {\n updatedAt: Date;\n steeringMode?: \"all\" | \"one-at-a-time\";\n } = {\n updatedAt: new Date(),\n };\n if (values.steeringMode) {\n updates.steeringMode = values.steeringMode;\n }\n const uow = forSchema(piSchema);\n uow.update(\"session\", sessionRow.id, (b) => b.set(updates).check());\n })\n .transform(({ serviceResult }) => {\n const [, workflowStatus] = serviceResult;\n return { workflowStatus };\n })\n .execute();\n\n return json(\n {\n status: result.workflowStatus.status,\n },\n 202,\n );\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && \"status\" in err) {\n const routeError = err as RouteError;\n const code =\n routeError.code === \"SESSION_NOT_FOUND\"\n ? \"SESSION_NOT_FOUND\"\n : routeError.code === \"SESSION_NOT_READY\"\n ? \"SESSION_NOT_READY\"\n : \"WORKFLOW_INSTANCE_MISSING\";\n const status =\n code === \"SESSION_NOT_FOUND\" ? 404 : code === \"SESSION_NOT_READY\" ? 409 : 500;\n return error({ message: routeError.message, code }, { status });\n }\n if (err instanceof Error && err.message === \"INSTANCE_NOT_FOUND\") {\n return error(\n { message: `Session ${sessionId} not found.`, code: \"SESSION_NOT_FOUND\" },\n { status: 404 },\n );\n }\n const message = err instanceof Error ? err.message : \"Failed to deliver message.\";\n return error({ message, code: \"WORKFLOW_INSTANCE_MISSING\" }, { status: 500 });\n }\n },\n }),\n ];\n },\n);\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { column, idColumn, schema } from "@fragno-dev/db/schema";
3
3
  //#region src/schema.ts
4
4
  const piSchema = schema("pi-fragment", (s) => {
5
5
  return s.addTable("session", (t) => {
6
- return t.addColumn("id", idColumn()).addColumn("name", column("string").nullable()).addColumn("agent", column("string")).addColumn("status", column("string")).addColumn("workflowInstanceId", column("string").nullable()).addColumn("steeringMode", column("string")).addColumn("metadata", column("json").nullable()).addColumn("tags", column("json").nullable()).addColumn("createdAt", column("timestamp").defaultTo((b) => b.now())).addColumn("updatedAt", column("timestamp").defaultTo((b) => b.now())).createIndex("idx_session_status", ["status"]).createIndex("idx_session_created", ["createdAt"]);
6
+ return t.addColumn("id", idColumn()).addColumn("name", column("string").nullable()).addColumn("agent", column("string")).addColumn("status", column("string")).addColumn("steeringMode", column("string")).addColumn("metadata", column("json").nullable()).addColumn("tags", column("json").nullable()).addColumn("createdAt", column("timestamp").defaultTo((b) => b.now())).addColumn("updatedAt", column("timestamp").defaultTo((b) => b.now())).createIndex("idx_session_status", ["status"]).createIndex("idx_session_created", ["createdAt"]);
7
7
  });
8
8
  });
9
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../src/schema.ts"],"sourcesContent":["import { column, idColumn, schema } from \"@fragno-dev/db/schema\";\n\nexport const piSchema = schema(\"pi-fragment\", (s) => {\n return s.addTable(\"session\", (t) => {\n return (\n t\n .addColumn(\"id\", idColumn())\n .addColumn(\"name\", column(\"string\").nullable())\n .addColumn(\"agent\", column(\"string\"))\n // Possible statuses: active, paused, errored, terminated, complete, waiting.\n .addColumn(\"status\", column(\"string\"))\n .addColumn(\"workflowInstanceId\", column(\"string\").nullable())\n .addColumn(\"steeringMode\", column(\"string\"))\n .addColumn(\"metadata\", column(\"json\").nullable())\n .addColumn(\"tags\", column(\"json\").nullable())\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .addColumn(\n \"updatedAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .createIndex(\"idx_session_status\", [\"status\"])\n .createIndex(\"idx_session_created\", [\"createdAt\"])\n );\n });\n});\n"],"mappings":";;;AAEA,MAAa,WAAW,OAAO,gBAAgB,MAAM;AACnD,QAAO,EAAE,SAAS,YAAY,MAAM;AAClC,SACE,EACG,UAAU,MAAM,UAAU,CAAC,CAC3B,UAAU,QAAQ,OAAO,SAAS,CAAC,UAAU,CAAC,CAC9C,UAAU,SAAS,OAAO,SAAS,CAAC,CAEpC,UAAU,UAAU,OAAO,SAAS,CAAC,CACrC,UAAU,sBAAsB,OAAO,SAAS,CAAC,UAAU,CAAC,CAC5D,UAAU,gBAAgB,OAAO,SAAS,CAAC,CAC3C,UAAU,YAAY,OAAO,OAAO,CAAC,UAAU,CAAC,CAChD,UAAU,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC,CAC5C,UACC,aACA,OAAO,YAAY,CAAC,WAAW,MAAM,EAAE,KAAK,CAAC,CAC9C,CACA,UACC,aACA,OAAO,YAAY,CAAC,WAAW,MAAM,EAAE,KAAK,CAAC,CAC9C,CACA,YAAY,sBAAsB,CAAC,SAAS,CAAC,CAC7C,YAAY,uBAAuB,CAAC,YAAY,CAAC;GAEtD;EACF"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../src/schema.ts"],"sourcesContent":["import { column, idColumn, schema } from \"@fragno-dev/db/schema\";\n\nexport const piSchema = schema(\"pi-fragment\", (s) => {\n return s.addTable(\"session\", (t) => {\n return (\n t\n // id is equal to the workflow's workflowInstanceId\n .addColumn(\"id\", idColumn())\n .addColumn(\"name\", column(\"string\").nullable())\n .addColumn(\"agent\", column(\"string\"))\n // Possible statuses: active, paused, errored, terminated, complete, waiting.\n .addColumn(\"status\", column(\"string\"))\n .addColumn(\"steeringMode\", column(\"string\"))\n .addColumn(\"metadata\", column(\"json\").nullable())\n .addColumn(\"tags\", column(\"json\").nullable())\n .addColumn(\n \"createdAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .addColumn(\n \"updatedAt\",\n column(\"timestamp\").defaultTo((b) => b.now()),\n )\n .createIndex(\"idx_session_status\", [\"status\"])\n .createIndex(\"idx_session_created\", [\"createdAt\"])\n );\n });\n});\n"],"mappings":";;;AAEA,MAAa,WAAW,OAAO,gBAAgB,MAAM;AACnD,QAAO,EAAE,SAAS,YAAY,MAAM;AAClC,SACE,EAEG,UAAU,MAAM,UAAU,CAAC,CAC3B,UAAU,QAAQ,OAAO,SAAS,CAAC,UAAU,CAAC,CAC9C,UAAU,SAAS,OAAO,SAAS,CAAC,CAEpC,UAAU,UAAU,OAAO,SAAS,CAAC,CACrC,UAAU,gBAAgB,OAAO,SAAS,CAAC,CAC3C,UAAU,YAAY,OAAO,OAAO,CAAC,UAAU,CAAC,CAChD,UAAU,QAAQ,OAAO,OAAO,CAAC,UAAU,CAAC,CAC5C,UACC,aACA,OAAO,YAAY,CAAC,WAAW,MAAM,EAAE,KAAK,CAAC,CAC9C,CACA,UACC,aACA,OAAO,YAAY,CAAC,WAAW,MAAM,EAAE,KAAK,CAAC,CAC9C,CACA,YAAY,sBAAsB,CAAC,SAAS,CAAC,CAC7C,YAAY,uBAAuB,CAAC,YAAY,CAAC;GAEtD;EACF"}