agent-relay 4.0.19 → 4.0.21

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 (130) hide show
  1. package/dist/index.cjs +1407 -537
  2. package/dist/src/cli/commands/messaging.d.ts +44 -0
  3. package/dist/src/cli/commands/messaging.d.ts.map +1 -1
  4. package/dist/src/cli/commands/messaging.js +195 -8
  5. package/dist/src/cli/commands/messaging.js.map +1 -1
  6. package/dist/src/cli/commands/setup.d.ts +30 -1
  7. package/dist/src/cli/commands/setup.d.ts.map +1 -1
  8. package/dist/src/cli/commands/setup.js +102 -85
  9. package/dist/src/cli/commands/setup.js.map +1 -1
  10. package/node_modules/@agent-relay/cloud/dist/auth.d.ts +2 -2
  11. package/node_modules/@agent-relay/cloud/dist/auth.d.ts.map +1 -1
  12. package/node_modules/@agent-relay/cloud/dist/auth.js +108 -62
  13. package/node_modules/@agent-relay/cloud/dist/auth.js.map +1 -1
  14. package/node_modules/@agent-relay/cloud/package.json +2 -2
  15. package/node_modules/@agent-relay/config/package.json +1 -1
  16. package/node_modules/@agent-relay/hooks/package.json +4 -4
  17. package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.d.ts +5 -35
  18. package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.d.ts.map +1 -1
  19. package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.js +158 -292
  20. package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.js.map +1 -1
  21. package/node_modules/@agent-relay/sdk/package.json +3 -2
  22. package/node_modules/@agent-relay/telemetry/package.json +1 -1
  23. package/node_modules/@agent-relay/trajectory/package.json +2 -2
  24. package/node_modules/@agent-relay/user-directory/package.json +2 -2
  25. package/node_modules/@agent-relay/utils/package.json +2 -2
  26. package/node_modules/@clack/core/CHANGELOG.md +200 -0
  27. package/node_modules/@clack/core/LICENSE +9 -0
  28. package/node_modules/@clack/core/README.md +22 -0
  29. package/node_modules/@clack/core/dist/index.cjs +15 -0
  30. package/node_modules/@clack/core/dist/index.cjs.map +1 -0
  31. package/node_modules/@clack/core/dist/index.d.cts +151 -0
  32. package/node_modules/@clack/core/dist/index.d.mts +151 -0
  33. package/node_modules/@clack/core/dist/index.d.ts +151 -0
  34. package/node_modules/@clack/core/dist/index.mjs +15 -0
  35. package/node_modules/@clack/core/dist/index.mjs.map +1 -0
  36. package/node_modules/@clack/core/package.json +62 -0
  37. package/node_modules/@clack/prompts/CHANGELOG.md +256 -0
  38. package/node_modules/@clack/prompts/LICENSE +23 -0
  39. package/node_modules/@clack/prompts/README.md +158 -0
  40. package/node_modules/@clack/prompts/dist/index.cjs +77 -0
  41. package/node_modules/@clack/prompts/dist/index.d.ts +106 -0
  42. package/node_modules/@clack/prompts/dist/index.mjs +77 -0
  43. package/node_modules/@clack/prompts/node_modules/is-unicode-supported/index.d.ts +12 -0
  44. package/node_modules/@clack/prompts/node_modules/is-unicode-supported/index.js +17 -0
  45. package/node_modules/@clack/prompts/node_modules/is-unicode-supported/license +9 -0
  46. package/node_modules/@clack/prompts/node_modules/is-unicode-supported/package.json +43 -0
  47. package/node_modules/@clack/prompts/node_modules/is-unicode-supported/readme.md +35 -0
  48. package/node_modules/@clack/prompts/package.json +65 -0
  49. package/node_modules/@smithy/config-resolver/package.json +2 -2
  50. package/node_modules/@smithy/util-defaults-mode-node/package.json +2 -2
  51. package/node_modules/@smithy/util-endpoints/dist-cjs/index.js +154 -61
  52. package/node_modules/@smithy/util-endpoints/dist-es/bdd/BinaryDecisionDiagram.js +15 -0
  53. package/node_modules/@smithy/util-endpoints/dist-es/decideEndpoint.js +41 -0
  54. package/node_modules/@smithy/util-endpoints/dist-es/index.js +2 -0
  55. package/node_modules/@smithy/util-endpoints/dist-es/lib/coalesce.js +8 -0
  56. package/node_modules/@smithy/util-endpoints/dist-es/lib/index.js +3 -0
  57. package/node_modules/@smithy/util-endpoints/dist-es/lib/ite.js +3 -0
  58. package/node_modules/@smithy/util-endpoints/dist-es/lib/split.js +13 -0
  59. package/node_modules/@smithy/util-endpoints/dist-es/lib/substring.js +1 -1
  60. package/node_modules/@smithy/util-endpoints/dist-es/utils/endpointFunctions.js +4 -1
  61. package/node_modules/@smithy/util-endpoints/dist-es/utils/evaluateExpression.js +20 -5
  62. package/node_modules/@smithy/util-endpoints/dist-es/utils/evaluateTemplate.js +3 -6
  63. package/node_modules/@smithy/util-endpoints/dist-es/utils/getReferenceValue.js +1 -5
  64. package/node_modules/@smithy/util-endpoints/dist-types/bdd/BinaryDecisionDiagram.d.ts +22 -0
  65. package/node_modules/@smithy/util-endpoints/dist-types/decideEndpoint.d.ts +7 -0
  66. package/node_modules/@smithy/util-endpoints/dist-types/index.d.ts +2 -0
  67. package/node_modules/@smithy/util-endpoints/dist-types/lib/coalesce.d.ts +7 -0
  68. package/node_modules/@smithy/util-endpoints/dist-types/lib/index.d.ts +3 -0
  69. package/node_modules/@smithy/util-endpoints/dist-types/lib/ite.d.ts +6 -0
  70. package/node_modules/@smithy/util-endpoints/dist-types/lib/split.d.ts +11 -0
  71. package/node_modules/@smithy/util-endpoints/dist-types/utils/endpointFunctions.d.ts +4 -0
  72. package/node_modules/@smithy/util-endpoints/dist-types/utils/getReferenceValue.d.ts +3 -1
  73. package/node_modules/@smithy/util-endpoints/package.json +1 -1
  74. package/node_modules/agent-trajectories/README.md +562 -0
  75. package/node_modules/agent-trajectories/dist/chunk-W222QB6V.js +2036 -0
  76. package/node_modules/agent-trajectories/dist/chunk-W222QB6V.js.map +1 -0
  77. package/node_modules/agent-trajectories/dist/cli/index.d.ts +2 -0
  78. package/node_modules/agent-trajectories/dist/cli/index.js +4592 -0
  79. package/node_modules/agent-trajectories/dist/cli/index.js.map +1 -0
  80. package/node_modules/agent-trajectories/dist/index-7tzw_CMS.d.ts +1777 -0
  81. package/node_modules/agent-trajectories/dist/index.d.ts +90 -0
  82. package/node_modules/agent-trajectories/dist/index.js +73 -0
  83. package/node_modules/agent-trajectories/dist/index.js.map +1 -0
  84. package/node_modules/agent-trajectories/dist/sdk/index.d.ts +2 -0
  85. package/node_modules/agent-trajectories/dist/sdk/index.js +39 -0
  86. package/node_modules/agent-trajectories/dist/sdk/index.js.map +1 -0
  87. package/node_modules/agent-trajectories/package.json +72 -0
  88. package/node_modules/commander/LICENSE +22 -0
  89. package/node_modules/commander/Readme.md +1157 -0
  90. package/node_modules/commander/esm.mjs +16 -0
  91. package/node_modules/commander/index.js +24 -0
  92. package/node_modules/commander/lib/argument.js +149 -0
  93. package/node_modules/commander/lib/command.js +2509 -0
  94. package/node_modules/commander/lib/error.js +39 -0
  95. package/node_modules/commander/lib/help.js +520 -0
  96. package/node_modules/commander/lib/option.js +330 -0
  97. package/node_modules/commander/lib/suggestSimilar.js +101 -0
  98. package/node_modules/commander/package-support.json +16 -0
  99. package/node_modules/commander/package.json +84 -0
  100. package/node_modules/commander/typings/esm.d.mts +3 -0
  101. package/node_modules/commander/typings/index.d.ts +969 -0
  102. package/node_modules/picocolors/LICENSE +15 -0
  103. package/node_modules/picocolors/README.md +21 -0
  104. package/node_modules/picocolors/package.json +25 -0
  105. package/node_modules/picocolors/picocolors.browser.js +4 -0
  106. package/node_modules/picocolors/picocolors.d.ts +5 -0
  107. package/node_modules/picocolors/picocolors.js +75 -0
  108. package/node_modules/picocolors/types.d.ts +51 -0
  109. package/node_modules/sisteransi/license +21 -0
  110. package/node_modules/sisteransi/package.json +34 -0
  111. package/node_modules/sisteransi/readme.md +113 -0
  112. package/node_modules/sisteransi/src/index.js +58 -0
  113. package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
  114. package/package.json +10 -10
  115. package/packages/cloud/dist/auth.d.ts +2 -2
  116. package/packages/cloud/dist/auth.d.ts.map +1 -1
  117. package/packages/cloud/dist/auth.js +108 -62
  118. package/packages/cloud/dist/auth.js.map +1 -1
  119. package/packages/cloud/package.json +2 -2
  120. package/packages/config/package.json +1 -1
  121. package/packages/hooks/package.json +4 -4
  122. package/packages/sdk/dist/workflows/trajectory.d.ts +5 -35
  123. package/packages/sdk/dist/workflows/trajectory.d.ts.map +1 -1
  124. package/packages/sdk/dist/workflows/trajectory.js +158 -292
  125. package/packages/sdk/dist/workflows/trajectory.js.map +1 -1
  126. package/packages/sdk/package.json +3 -2
  127. package/packages/telemetry/package.json +1 -1
  128. package/packages/trajectory/package.json +2 -2
  129. package/packages/user-directory/package.json +2 -2
  130. package/packages/utils/package.json +2 -2
@@ -0,0 +1,2036 @@
1
+ // src/sdk/client.ts
2
+ import { spawn } from "child_process";
3
+ import { existsSync as existsSync2, readFileSync } from "fs";
4
+ import { createRequire } from "module";
5
+ import { dirname, resolve as resolvePath } from "path";
6
+
7
+ // src/core/id.ts
8
+ import { webcrypto } from "crypto";
9
+ var ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
10
+ var ID_LENGTH = 12;
11
+ function generateRandomId(length = ID_LENGTH) {
12
+ let id = "";
13
+ const randomValues = new Uint8Array(length);
14
+ webcrypto.getRandomValues(randomValues);
15
+ for (let i = 0; i < length; i++) {
16
+ id += ALPHABET[randomValues[i] % ALPHABET.length];
17
+ }
18
+ return id;
19
+ }
20
+ function generateTrajectoryId() {
21
+ return `traj_${generateRandomId()}`;
22
+ }
23
+ function generateChapterId() {
24
+ return `chap_${generateRandomId()}`;
25
+ }
26
+ function isValidTrajectoryId(id) {
27
+ return /^traj_[a-z0-9_]+$/.test(id);
28
+ }
29
+ function isValidChapterId(id) {
30
+ return /^chap_[a-z0-9]{12}$/.test(id);
31
+ }
32
+
33
+ // src/core/schema.ts
34
+ import { z } from "zod";
35
+ var TaskSourceSystemSchema = z.union([
36
+ z.literal("beads"),
37
+ z.literal("github"),
38
+ z.literal("linear"),
39
+ z.literal("jira"),
40
+ z.literal("plain"),
41
+ z.string()
42
+ // Allow custom systems
43
+ ]);
44
+ var TaskSourceSchema = z.object({
45
+ system: TaskSourceSystemSchema,
46
+ id: z.string().min(1, "Task ID is required"),
47
+ url: z.string().url().optional()
48
+ });
49
+ var TaskReferenceSchema = z.object({
50
+ title: z.string().min(1, "Trajectory title is required").max(500, "Trajectory title must be 500 characters or less"),
51
+ description: z.string().optional(),
52
+ source: TaskSourceSchema.optional()
53
+ });
54
+ var TrajectoryStatusSchema = z.enum([
55
+ "active",
56
+ "completed",
57
+ "abandoned"
58
+ ]);
59
+ var TrajectoryEventTypeSchema = z.union([
60
+ z.literal("prompt"),
61
+ z.literal("thinking"),
62
+ z.literal("tool_call"),
63
+ z.literal("tool_result"),
64
+ z.literal("message_sent"),
65
+ z.literal("message_received"),
66
+ z.literal("decision"),
67
+ z.literal("finding"),
68
+ z.literal("reflection"),
69
+ z.literal("note"),
70
+ z.literal("error"),
71
+ z.string()
72
+ // Allow event types emitted by other tools (e.g. agent-relay's completion-evidence / completion-marker). Downstream code filters to known types.
73
+ ]);
74
+ var EventSignificanceSchema = z.enum([
75
+ "low",
76
+ "medium",
77
+ "high",
78
+ "critical"
79
+ ]);
80
+ var TrajectoryEventSchema = z.object({
81
+ ts: z.number().int().positive(),
82
+ type: TrajectoryEventTypeSchema,
83
+ content: z.string().min(1, "Event content is required"),
84
+ raw: z.unknown().optional(),
85
+ significance: EventSignificanceSchema.optional(),
86
+ tags: z.array(z.string()).optional(),
87
+ confidence: z.number().min(0, "Confidence must be between 0 and 1").max(1, "Confidence must be between 0 and 1").optional()
88
+ });
89
+ var AlternativeSchema = z.object({
90
+ option: z.string().min(1, "Alternative option is required"),
91
+ reason: z.string().optional()
92
+ });
93
+ var DecisionSchema = z.object({
94
+ question: z.string().min(1, "Decision question is required"),
95
+ chosen: z.string().min(1, "Chosen option is required"),
96
+ alternatives: z.array(z.union([z.string(), AlternativeSchema])),
97
+ reasoning: z.string().min(1, "Decision reasoning is required"),
98
+ confidence: z.number().min(0, "Confidence must be between 0 and 1").max(1, "Confidence must be between 0 and 1").optional()
99
+ });
100
+ var AgentParticipationSchema = z.object({
101
+ name: z.string().min(1, "Agent name is required"),
102
+ role: z.string().min(1, "Agent role is required"),
103
+ joinedAt: z.string().datetime(),
104
+ leftAt: z.string().datetime().optional()
105
+ });
106
+ var ChapterSchema = z.object({
107
+ id: z.string().min(1),
108
+ title: z.string().min(1, "Chapter title is required"),
109
+ agentName: z.string().min(1, "Agent name is required"),
110
+ startedAt: z.string().datetime(),
111
+ endedAt: z.string().datetime().optional(),
112
+ events: z.array(TrajectoryEventSchema)
113
+ });
114
+ var RetrospectiveSchema = z.object({
115
+ summary: z.string().min(1, "Retrospective summary is required"),
116
+ approach: z.string().min(1, "Approach description is required"),
117
+ decisions: z.array(DecisionSchema).optional(),
118
+ challenges: z.array(z.string()).optional(),
119
+ learnings: z.array(z.string()).optional(),
120
+ suggestions: z.array(z.string()).optional(),
121
+ confidence: z.number().min(0, "Confidence must be between 0 and 1").max(1, "Confidence must be between 0 and 1"),
122
+ timeSpent: z.string().optional()
123
+ });
124
+ var TraceRangeSchema = z.object({
125
+ start_line: z.number().int().positive("Start line must be positive"),
126
+ end_line: z.number().int().positive("End line must be positive"),
127
+ revision: z.string().optional(),
128
+ content_hash: z.string().optional()
129
+ });
130
+ var ContributorTypeSchema = z.enum([
131
+ "human",
132
+ "ai",
133
+ "mixed",
134
+ "unknown"
135
+ ]);
136
+ var TraceContributorSchema = z.object({
137
+ type: ContributorTypeSchema,
138
+ model_id: z.string().max(250).optional()
139
+ });
140
+ var TraceConversationSchema = z.object({
141
+ contributor: TraceContributorSchema,
142
+ url: z.string().url().optional(),
143
+ ranges: z.array(TraceRangeSchema)
144
+ });
145
+ var TraceFileSchema = z.object({
146
+ path: z.string().min(1, "File path is required"),
147
+ conversations: z.array(TraceConversationSchema)
148
+ });
149
+ var TraceRecordSchema = z.object({
150
+ version: z.string().min(1, "Version is required"),
151
+ id: z.string().min(1, "Trace ID is required"),
152
+ timestamp: z.string().datetime(),
153
+ trajectory: z.string().optional(),
154
+ files: z.array(TraceFileSchema)
155
+ });
156
+ var TrajectoryTraceRefSchema = z.object({
157
+ startRef: z.string().min(1, "Start ref is required"),
158
+ endRef: z.string().optional(),
159
+ traceId: z.string().optional()
160
+ });
161
+ var TrajectorySchema = z.object({
162
+ id: z.string().regex(/^traj_[a-z0-9_]+$/, "Invalid trajectory ID format"),
163
+ version: z.literal(1),
164
+ task: TaskReferenceSchema,
165
+ status: TrajectoryStatusSchema,
166
+ startedAt: z.string().datetime(),
167
+ completedAt: z.string().datetime().optional(),
168
+ agents: z.array(AgentParticipationSchema),
169
+ chapters: z.array(ChapterSchema),
170
+ retrospective: RetrospectiveSchema.optional(),
171
+ commits: z.array(z.string()).default([]),
172
+ filesChanged: z.array(z.string()).default([]),
173
+ projectId: z.string().optional(),
174
+ workflowId: z.string().optional(),
175
+ tags: z.array(z.string()).default([]),
176
+ _trace: TrajectoryTraceRefSchema.optional()
177
+ });
178
+ var CreateTrajectoryInputSchema = z.object({
179
+ title: z.string().min(1, "Trajectory title is required").max(500, "Trajectory title must be 500 characters or less"),
180
+ description: z.string().optional(),
181
+ source: TaskSourceSchema.optional(),
182
+ projectId: z.string().optional(),
183
+ tags: z.array(z.string()).optional()
184
+ });
185
+ var AddChapterInputSchema = z.object({
186
+ title: z.string().min(1, "Chapter title is required"),
187
+ agentName: z.string().min(1, "Agent name is required")
188
+ });
189
+ var AddEventInputSchema = z.object({
190
+ type: TrajectoryEventTypeSchema,
191
+ content: z.string().min(1, "Event content is required"),
192
+ raw: z.unknown().optional(),
193
+ significance: EventSignificanceSchema.optional(),
194
+ tags: z.array(z.string()).optional()
195
+ });
196
+ var CompleteTrajectoryInputSchema = z.object({
197
+ summary: z.string().min(1, "Retrospective summary is required"),
198
+ approach: z.string().min(1, "Approach description is required"),
199
+ decisions: z.array(DecisionSchema).optional(),
200
+ challenges: z.array(z.string()).optional(),
201
+ learnings: z.array(z.string()).optional(),
202
+ suggestions: z.array(z.string()).optional(),
203
+ confidence: z.number().min(0, "Confidence must be between 0 and 1").max(1, "Confidence must be between 0 and 1")
204
+ });
205
+ var TrajectoryQuerySchema = z.object({
206
+ status: TrajectoryStatusSchema.optional(),
207
+ since: z.string().datetime().optional(),
208
+ until: z.string().datetime().optional(),
209
+ limit: z.number().int().positive().max(100).optional(),
210
+ offset: z.number().int().nonnegative().optional(),
211
+ sortBy: z.enum(["startedAt", "completedAt", "title"]).optional(),
212
+ sortOrder: z.enum(["asc", "desc"]).optional()
213
+ });
214
+ function validateTrajectory(data) {
215
+ const result = TrajectorySchema.safeParse(data);
216
+ if (result.success) {
217
+ return { success: true, data: result.data };
218
+ }
219
+ return { success: false, errors: result.error };
220
+ }
221
+ function validateCreateInput(data) {
222
+ const result = CreateTrajectoryInputSchema.safeParse(data);
223
+ if (result.success) {
224
+ return { success: true, data: result.data };
225
+ }
226
+ return { success: false, errors: result.error };
227
+ }
228
+ function validateCompleteInput(data) {
229
+ const result = CompleteTrajectoryInputSchema.safeParse(data);
230
+ if (result.success) {
231
+ return { success: true, data: result.data };
232
+ }
233
+ return { success: false, errors: result.error };
234
+ }
235
+
236
+ // src/core/trajectory.ts
237
+ var TrajectoryError = class extends Error {
238
+ constructor(message, code, suggestion) {
239
+ super(message);
240
+ this.code = code;
241
+ this.suggestion = suggestion;
242
+ this.name = "TrajectoryError";
243
+ }
244
+ };
245
+ function createTrajectory(input) {
246
+ const validation = CreateTrajectoryInputSchema.safeParse(input);
247
+ if (!validation.success) {
248
+ const firstError = validation.error.errors[0];
249
+ throw new TrajectoryError(
250
+ firstError.message,
251
+ "VALIDATION_ERROR",
252
+ "Check your input and try again"
253
+ );
254
+ }
255
+ const now = (/* @__PURE__ */ new Date()).toISOString();
256
+ return {
257
+ id: generateTrajectoryId(),
258
+ version: 1,
259
+ task: {
260
+ title: input.title,
261
+ description: input.description,
262
+ source: input.source
263
+ },
264
+ status: "active",
265
+ startedAt: now,
266
+ agents: [],
267
+ chapters: [],
268
+ commits: [],
269
+ filesChanged: [],
270
+ projectId: input.projectId ?? process.cwd(),
271
+ tags: input.tags ?? []
272
+ };
273
+ }
274
+ function addChapter(trajectory2, input) {
275
+ if (trajectory2.status === "completed") {
276
+ throw new TrajectoryError(
277
+ "Cannot add chapter to completed trajectory",
278
+ "TRAJECTORY_ALREADY_COMPLETED",
279
+ "Start a new trajectory instead"
280
+ );
281
+ }
282
+ const now = (/* @__PURE__ */ new Date()).toISOString();
283
+ const updatedChapters = trajectory2.chapters.map((chapter, index) => {
284
+ if (index === trajectory2.chapters.length - 1 && !chapter.endedAt) {
285
+ return { ...chapter, endedAt: now };
286
+ }
287
+ return chapter;
288
+ });
289
+ const newChapter = {
290
+ id: generateChapterId(),
291
+ title: input.title,
292
+ agentName: input.agentName,
293
+ startedAt: now,
294
+ events: []
295
+ };
296
+ let updatedAgents = trajectory2.agents;
297
+ const agentExists = trajectory2.agents.some((a) => a.name === input.agentName);
298
+ if (!agentExists) {
299
+ const isFirstAgent = trajectory2.agents.length === 0;
300
+ updatedAgents = [
301
+ ...trajectory2.agents,
302
+ {
303
+ name: input.agentName,
304
+ role: isFirstAgent ? "lead" : "contributor",
305
+ joinedAt: now
306
+ }
307
+ ];
308
+ }
309
+ return {
310
+ ...trajectory2,
311
+ agents: updatedAgents,
312
+ chapters: [...updatedChapters, newChapter]
313
+ };
314
+ }
315
+ function addEvent(trajectory2, input) {
316
+ let updatedTrajectory = trajectory2;
317
+ if (trajectory2.chapters.length === 0) {
318
+ updatedTrajectory = addChapter(trajectory2, {
319
+ title: "Work",
320
+ agentName: "default"
321
+ });
322
+ }
323
+ const event = {
324
+ ts: Date.now(),
325
+ type: input.type,
326
+ content: input.content,
327
+ raw: input.raw,
328
+ significance: input.significance,
329
+ tags: input.tags
330
+ };
331
+ const chapters = [...updatedTrajectory.chapters];
332
+ const lastChapter = chapters[chapters.length - 1];
333
+ chapters[chapters.length - 1] = {
334
+ ...lastChapter,
335
+ events: [...lastChapter.events, event]
336
+ };
337
+ return {
338
+ ...updatedTrajectory,
339
+ chapters
340
+ };
341
+ }
342
+ function addDecision(trajectory2, decision) {
343
+ return addEvent(trajectory2, {
344
+ type: "decision",
345
+ content: `${decision.question}: ${decision.chosen}`,
346
+ raw: decision,
347
+ significance: "high"
348
+ });
349
+ }
350
+ function completeTrajectory(trajectory2, input) {
351
+ if (trajectory2.status === "completed") {
352
+ throw new TrajectoryError(
353
+ "Trajectory is already completed",
354
+ "TRAJECTORY_ALREADY_COMPLETED",
355
+ "Start a new trajectory instead"
356
+ );
357
+ }
358
+ const validation = CompleteTrajectoryInputSchema.safeParse(input);
359
+ if (!validation.success) {
360
+ const firstError = validation.error.errors[0];
361
+ throw new TrajectoryError(
362
+ firstError.message,
363
+ "VALIDATION_ERROR",
364
+ "Check your input and try again"
365
+ );
366
+ }
367
+ const now = (/* @__PURE__ */ new Date()).toISOString();
368
+ const chapters = trajectory2.chapters.map((chapter, index) => {
369
+ if (index === trajectory2.chapters.length - 1 && !chapter.endedAt) {
370
+ return { ...chapter, endedAt: now };
371
+ }
372
+ return chapter;
373
+ });
374
+ return {
375
+ ...trajectory2,
376
+ status: "completed",
377
+ completedAt: now,
378
+ chapters,
379
+ retrospective: {
380
+ summary: input.summary,
381
+ approach: input.approach,
382
+ decisions: input.decisions,
383
+ challenges: input.challenges,
384
+ learnings: input.learnings,
385
+ suggestions: input.suggestions,
386
+ confidence: input.confidence
387
+ }
388
+ };
389
+ }
390
+ function abandonTrajectory(trajectory2, reason) {
391
+ const now = (/* @__PURE__ */ new Date()).toISOString();
392
+ const chapters = trajectory2.chapters.map((chapter, index) => {
393
+ if (index === trajectory2.chapters.length - 1 && !chapter.endedAt) {
394
+ return { ...chapter, endedAt: now };
395
+ }
396
+ return chapter;
397
+ });
398
+ let updatedChapters = chapters;
399
+ if (reason && chapters.length > 0) {
400
+ const lastChapter = chapters[chapters.length - 1];
401
+ updatedChapters = [
402
+ ...chapters.slice(0, -1),
403
+ {
404
+ ...lastChapter,
405
+ events: [
406
+ ...lastChapter.events,
407
+ {
408
+ ts: Date.now(),
409
+ type: "note",
410
+ content: `Abandoned: ${reason}`,
411
+ significance: "high"
412
+ }
413
+ ]
414
+ }
415
+ ];
416
+ }
417
+ return {
418
+ ...trajectory2,
419
+ status: "abandoned",
420
+ completedAt: now,
421
+ chapters: updatedChapters
422
+ };
423
+ }
424
+
425
+ // src/export/json.ts
426
+ function exportToJSON(trajectory2, options) {
427
+ if (options?.compact) {
428
+ return JSON.stringify(trajectory2);
429
+ }
430
+ return JSON.stringify(trajectory2, null, 2);
431
+ }
432
+
433
+ // src/export/markdown.ts
434
+ function exportToMarkdown(trajectory2) {
435
+ const lines = [];
436
+ lines.push(`# Trajectory: ${trajectory2.task.title}`);
437
+ lines.push("");
438
+ lines.push(`> **Status:** ${formatStatus(trajectory2.status)}`);
439
+ if (trajectory2.task.source) {
440
+ const linkText = trajectory2.task.source.url ? `[${trajectory2.task.source.id}](${trajectory2.task.source.url})` : trajectory2.task.source.id;
441
+ lines.push(`> **Task:** ${linkText}`);
442
+ }
443
+ if (trajectory2.retrospective?.confidence !== void 0) {
444
+ lines.push(
445
+ `> **Confidence:** ${Math.round(trajectory2.retrospective.confidence * 100)}%`
446
+ );
447
+ }
448
+ lines.push(`> **Started:** ${formatDate(trajectory2.startedAt)}`);
449
+ if (trajectory2.completedAt) {
450
+ lines.push(`> **Completed:** ${formatDate(trajectory2.completedAt)}`);
451
+ }
452
+ lines.push("");
453
+ if (trajectory2.retrospective) {
454
+ lines.push("---");
455
+ lines.push("");
456
+ lines.push("## Summary");
457
+ lines.push("");
458
+ lines.push(trajectory2.retrospective.summary);
459
+ lines.push("");
460
+ if (trajectory2.retrospective.approach) {
461
+ lines.push(`**Approach:** ${trajectory2.retrospective.approach}`);
462
+ lines.push("");
463
+ }
464
+ }
465
+ const decisions = extractDecisions(trajectory2);
466
+ if (decisions.length > 0) {
467
+ lines.push("---");
468
+ lines.push("");
469
+ lines.push("## Key Decisions");
470
+ lines.push("");
471
+ for (const decision of decisions) {
472
+ lines.push(`### ${decision.question}`);
473
+ lines.push(`- **Chose:** ${decision.chosen}`);
474
+ if (decision.alternatives.length > 0) {
475
+ const altStrings = decision.alternatives.map(
476
+ (a) => typeof a === "string" ? a : a.option
477
+ );
478
+ lines.push(`- **Rejected:** ${altStrings.join(", ")}`);
479
+ }
480
+ lines.push(`- **Reasoning:** ${decision.reasoning}`);
481
+ lines.push("");
482
+ }
483
+ }
484
+ if (trajectory2.chapters.length > 0) {
485
+ lines.push("---");
486
+ lines.push("");
487
+ lines.push("## Chapters");
488
+ lines.push("");
489
+ trajectory2.chapters.forEach((chapter, index) => {
490
+ lines.push(`### ${index + 1}. ${chapter.title}`);
491
+ lines.push(`*Agent: ${chapter.agentName}*`);
492
+ lines.push("");
493
+ if (chapter.events.length > 0) {
494
+ const significantEvents = chapter.events.filter(
495
+ (e) => e.significance === "high" || e.significance === "critical" || e.type === "decision"
496
+ );
497
+ if (significantEvents.length > 0) {
498
+ for (const event of significantEvents) {
499
+ lines.push(`- ${event.content}`);
500
+ }
501
+ lines.push("");
502
+ }
503
+ }
504
+ });
505
+ }
506
+ if (trajectory2.retrospective?.challenges && trajectory2.retrospective.challenges.length > 0) {
507
+ lines.push("---");
508
+ lines.push("");
509
+ lines.push("## Challenges");
510
+ lines.push("");
511
+ for (const challenge of trajectory2.retrospective.challenges) {
512
+ lines.push(`- ${challenge}`);
513
+ }
514
+ lines.push("");
515
+ }
516
+ if (trajectory2.retrospective?.learnings && trajectory2.retrospective.learnings.length > 0) {
517
+ lines.push("---");
518
+ lines.push("");
519
+ lines.push("## Learnings");
520
+ lines.push("");
521
+ for (const learning of trajectory2.retrospective.learnings) {
522
+ lines.push(`- ${learning}`);
523
+ }
524
+ lines.push("");
525
+ }
526
+ if (trajectory2.retrospective?.suggestions && trajectory2.retrospective.suggestions.length > 0) {
527
+ lines.push("---");
528
+ lines.push("");
529
+ lines.push("## Suggestions");
530
+ lines.push("");
531
+ for (const suggestion of trajectory2.retrospective.suggestions) {
532
+ lines.push(`- ${suggestion}`);
533
+ }
534
+ lines.push("");
535
+ }
536
+ if (trajectory2.commits.length > 0 || trajectory2.filesChanged.length > 0) {
537
+ lines.push("---");
538
+ lines.push("");
539
+ lines.push("## Artifacts");
540
+ lines.push("");
541
+ if (trajectory2.commits.length > 0) {
542
+ lines.push(`**Commits:** ${trajectory2.commits.join(", ")}`);
543
+ }
544
+ if (trajectory2.filesChanged.length > 0) {
545
+ lines.push(`**Files changed:** ${trajectory2.filesChanged.length}`);
546
+ }
547
+ lines.push("");
548
+ }
549
+ return lines.join("\n");
550
+ }
551
+ function formatStatus(status) {
552
+ switch (status) {
553
+ case "active":
554
+ return "\u{1F504} Active";
555
+ case "completed":
556
+ return "\u2705 Completed";
557
+ case "abandoned":
558
+ return "\u274C Abandoned";
559
+ default:
560
+ return status;
561
+ }
562
+ }
563
+ function formatDate(isoString) {
564
+ const date = new Date(isoString);
565
+ return date.toLocaleDateString("en-US", {
566
+ year: "numeric",
567
+ month: "long",
568
+ day: "numeric",
569
+ hour: "2-digit",
570
+ minute: "2-digit"
571
+ });
572
+ }
573
+ function extractDecisions(trajectory2) {
574
+ const decisions = [];
575
+ if (trajectory2.retrospective?.decisions) {
576
+ decisions.push(...trajectory2.retrospective.decisions);
577
+ }
578
+ for (const chapter of trajectory2.chapters) {
579
+ for (const event of chapter.events) {
580
+ if (event.type === "decision" && event.raw) {
581
+ const raw = event.raw;
582
+ if (raw.question && raw.chosen && raw.reasoning) {
583
+ if (!decisions.some((d) => d.question === raw.question)) {
584
+ decisions.push(raw);
585
+ }
586
+ }
587
+ }
588
+ }
589
+ }
590
+ return decisions;
591
+ }
592
+
593
+ // src/export/pr-summary.ts
594
+ function exportToPRSummary(trajectory2, options) {
595
+ const lines = [];
596
+ lines.push("## Trajectory Summary");
597
+ lines.push("");
598
+ if (trajectory2.retrospective?.summary) {
599
+ lines.push(trajectory2.retrospective.summary);
600
+ lines.push("");
601
+ }
602
+ const decisionCount = trajectory2.chapters.reduce(
603
+ (count, chapter) => count + chapter.events.filter((e) => e.type === "decision").length,
604
+ 0
605
+ );
606
+ lines.push(`**Key decisions:** ${decisionCount}`);
607
+ if (trajectory2.retrospective?.confidence !== void 0) {
608
+ lines.push(
609
+ `**Confidence:** ${Math.round(trajectory2.retrospective.confidence * 100)}%`
610
+ );
611
+ }
612
+ if (options?.trajectoryPath) {
613
+ lines.push("");
614
+ lines.push(`[Full trajectory](${options.trajectoryPath})`);
615
+ }
616
+ return lines.join("\n");
617
+ }
618
+
619
+ // src/export/timeline.ts
620
+ function exportToTimeline(trajectory2) {
621
+ const lines = [];
622
+ lines.push(
623
+ `\u25CF ${formatTime(trajectory2.startedAt)} Started: ${trajectory2.task.title}`
624
+ );
625
+ lines.push("\u2502");
626
+ for (const chapter of trajectory2.chapters) {
627
+ lines.push(
628
+ `\u251C\u2500 ${formatTime(chapter.startedAt)} Chapter: ${chapter.title}`
629
+ );
630
+ lines.push(`\u2502 Agent: ${chapter.agentName}`);
631
+ lines.push("\u2502");
632
+ for (const event of chapter.events) {
633
+ const prefix = event.type === "decision" ? "\u251C\u2500 Decision: " : "\u251C\u2500 ";
634
+ const timeStr = formatTime(new Date(event.ts).toISOString());
635
+ if (event.type === "decision") {
636
+ lines.push(`\u2502 ${timeStr} ${prefix}${event.content}`);
637
+ } else if (event.significance === "high" || event.significance === "critical") {
638
+ lines.push(`\u2502 ${timeStr} ${prefix}${event.content}`);
639
+ }
640
+ }
641
+ if (chapter.endedAt) {
642
+ lines.push("\u2502");
643
+ }
644
+ }
645
+ if (trajectory2.completedAt) {
646
+ const status = trajectory2.status === "completed" ? "Completed" : "Abandoned";
647
+ lines.push(`\u25CB ${formatTime(trajectory2.completedAt)} ${status}`);
648
+ if (trajectory2.retrospective) {
649
+ lines.push("");
650
+ lines.push(` Summary: ${trajectory2.retrospective.summary}`);
651
+ lines.push(
652
+ ` Confidence: ${Math.round(trajectory2.retrospective.confidence * 100)}%`
653
+ );
654
+ }
655
+ }
656
+ return lines.join("\n");
657
+ }
658
+ function formatTime(isoString) {
659
+ const date = new Date(isoString);
660
+ return date.toLocaleTimeString("en-US", {
661
+ hour: "2-digit",
662
+ minute: "2-digit"
663
+ });
664
+ }
665
+
666
+ // src/storage/file.ts
667
+ import { existsSync } from "fs";
668
+ import { mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
669
+ import { join } from "path";
670
+ function expandPath(path) {
671
+ if (path.startsWith("~")) {
672
+ return join(process.env.HOME ?? "", path.slice(1));
673
+ }
674
+ return path;
675
+ }
676
+ var FileStorage = class {
677
+ baseDir;
678
+ trajectoriesDir;
679
+ activeDir;
680
+ completedDir;
681
+ indexPath;
682
+ constructor(baseDir) {
683
+ this.baseDir = baseDir ?? process.cwd();
684
+ const dataDir = process.env.TRAJECTORIES_DATA_DIR;
685
+ if (dataDir) {
686
+ this.trajectoriesDir = expandPath(dataDir);
687
+ } else {
688
+ this.trajectoriesDir = join(this.baseDir, ".trajectories");
689
+ }
690
+ this.activeDir = join(this.trajectoriesDir, "active");
691
+ this.completedDir = join(this.trajectoriesDir, "completed");
692
+ this.indexPath = join(this.trajectoriesDir, "index.json");
693
+ }
694
+ /**
695
+ * Initialize storage directories
696
+ */
697
+ async initialize() {
698
+ await mkdir(this.trajectoriesDir, { recursive: true });
699
+ await mkdir(this.activeDir, { recursive: true });
700
+ await mkdir(this.completedDir, { recursive: true });
701
+ if (!existsSync(this.indexPath)) {
702
+ await this.saveIndex({
703
+ version: 1,
704
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
705
+ trajectories: {}
706
+ });
707
+ }
708
+ await this.reconcileIndex();
709
+ }
710
+ /**
711
+ * Scan active/ and completed/ recursively and add any trajectory files
712
+ * missing from the index. Existing entries are preserved — reconcile
713
+ * only adds, never removes.
714
+ *
715
+ * Handles three on-disk layouts in completed/:
716
+ * - flat: completed/{id}.json (legacy workforce data)
717
+ * - monthly: completed/YYYY-MM/{id}.json (current save() writes)
718
+ * - nested: completed/.../{id}.json (defensive — any depth)
719
+ *
720
+ * Returns a ReconcileSummary so tests and CLI wrappers can observe
721
+ * outcomes without parsing logs. Only writes the index if anything was
722
+ * added.
723
+ */
724
+ async reconcileIndex() {
725
+ const summary = {
726
+ scanned: 0,
727
+ added: 0,
728
+ alreadyIndexed: 0,
729
+ skippedMalformedJson: 0,
730
+ skippedSchemaViolation: 0,
731
+ skippedIoError: 0
732
+ };
733
+ const index = await this.loadIndex();
734
+ const before = Object.keys(index.trajectories).length;
735
+ const discovered = [];
736
+ try {
737
+ const activeFiles = await readdir(this.activeDir);
738
+ for (const file of activeFiles) {
739
+ if (!file.endsWith(".json")) continue;
740
+ discovered.push(join(this.activeDir, file));
741
+ }
742
+ } catch (error) {
743
+ if (error.code !== "ENOENT") throw error;
744
+ }
745
+ await this.walkJsonFilesInto(this.completedDir, discovered);
746
+ for (const filePath of discovered) {
747
+ summary.scanned += 1;
748
+ const result = await this.readTrajectoryFile(filePath);
749
+ if (!result.ok) {
750
+ if (result.reason === "malformed_json") {
751
+ summary.skippedMalformedJson += 1;
752
+ } else if (result.reason === "schema_violation") {
753
+ summary.skippedSchemaViolation += 1;
754
+ } else {
755
+ summary.skippedIoError += 1;
756
+ }
757
+ continue;
758
+ }
759
+ const trajectory2 = result.trajectory;
760
+ if (index.trajectories[trajectory2.id]) {
761
+ summary.alreadyIndexed += 1;
762
+ continue;
763
+ }
764
+ index.trajectories[trajectory2.id] = {
765
+ title: trajectory2.task.title,
766
+ status: trajectory2.status,
767
+ startedAt: trajectory2.startedAt,
768
+ completedAt: trajectory2.completedAt,
769
+ path: filePath
770
+ };
771
+ summary.added += 1;
772
+ }
773
+ if (Object.keys(index.trajectories).length !== before) {
774
+ await this.saveIndex(index);
775
+ }
776
+ const hadSkips = summary.skippedMalformedJson + summary.skippedSchemaViolation + summary.skippedIoError > 0;
777
+ if (summary.added > 0 || hadSkips) {
778
+ const parts = [`reconciled ${summary.added}/${summary.scanned}`];
779
+ if (summary.skippedMalformedJson > 0) {
780
+ parts.push(`malformed: ${summary.skippedMalformedJson}`);
781
+ }
782
+ if (summary.skippedSchemaViolation > 0) {
783
+ parts.push(`invalid: ${summary.skippedSchemaViolation}`);
784
+ }
785
+ if (summary.skippedIoError > 0) {
786
+ parts.push(`io: ${summary.skippedIoError}`);
787
+ }
788
+ console.warn(`[trajectories] ${parts.join(", ")}`);
789
+ }
790
+ return summary;
791
+ }
792
+ /**
793
+ * Recursively collect all .json file paths under `dir` into `out`.
794
+ * Silently treats a missing directory as empty.
795
+ */
796
+ async walkJsonFilesInto(dir, out) {
797
+ let entries;
798
+ try {
799
+ entries = await readdir(dir, { withFileTypes: true });
800
+ } catch (error) {
801
+ if (error.code === "ENOENT") return;
802
+ throw error;
803
+ }
804
+ for (const entry of entries) {
805
+ const entryPath = join(dir, entry.name);
806
+ if (entry.isDirectory()) {
807
+ await this.walkJsonFilesInto(entryPath, out);
808
+ } else if (entry.isFile() && entry.name.endsWith(".json")) {
809
+ out.push(entryPath);
810
+ }
811
+ }
812
+ }
813
+ /**
814
+ * Save a trajectory.
815
+ *
816
+ * Validates the input against the trajectory schema before touching
817
+ * disk. Closes the historical read/write asymmetry where save() would
818
+ * happily write data that the reader then rejected, producing files
819
+ * that could never be loaded back.
820
+ */
821
+ async save(input) {
822
+ const validation = validateTrajectory(input);
823
+ if (!validation.success) {
824
+ const issues = validation.errors?.issues.map((issue) => {
825
+ const path = issue.path.length > 0 ? issue.path.join(".") : "root";
826
+ return `${path}: ${issue.message}`;
827
+ }).join("; ") ?? "unknown validation error";
828
+ throw new Error(`Cannot save invalid trajectory: ${issues}`);
829
+ }
830
+ const trajectory2 = validation.data;
831
+ const isCompleted = trajectory2.status === "completed" || trajectory2.status === "abandoned";
832
+ let filePath;
833
+ if (isCompleted) {
834
+ const date = new Date(trajectory2.completedAt ?? trajectory2.startedAt);
835
+ const monthDir = join(
836
+ this.completedDir,
837
+ `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`
838
+ );
839
+ await mkdir(monthDir, { recursive: true });
840
+ filePath = join(monthDir, `${trajectory2.id}.json`);
841
+ const activePath = join(this.activeDir, `${trajectory2.id}.json`);
842
+ if (existsSync(activePath)) {
843
+ await unlink(activePath);
844
+ }
845
+ const mdPath = join(monthDir, `${trajectory2.id}.md`);
846
+ const markdown = exportToMarkdown(trajectory2);
847
+ await writeFile(mdPath, markdown, "utf-8");
848
+ } else {
849
+ filePath = join(this.activeDir, `${trajectory2.id}.json`);
850
+ }
851
+ await writeFile(filePath, JSON.stringify(trajectory2, null, 2), "utf-8");
852
+ await this.updateIndex(trajectory2, filePath);
853
+ }
854
+ /**
855
+ * Get a trajectory by ID
856
+ */
857
+ async get(id) {
858
+ const activePath = join(this.activeDir, `${id}.json`);
859
+ if (existsSync(activePath)) {
860
+ return this.readTrajectoryOrNull(activePath);
861
+ }
862
+ const index = await this.loadIndex();
863
+ const entry = index.trajectories[id];
864
+ if (entry?.path && existsSync(entry.path)) {
865
+ return this.readTrajectoryOrNull(entry.path);
866
+ }
867
+ try {
868
+ const flatPath = join(this.completedDir, `${id}.json`);
869
+ if (existsSync(flatPath)) {
870
+ return this.readTrajectoryOrNull(flatPath);
871
+ }
872
+ const months = await readdir(this.completedDir);
873
+ for (const month of months) {
874
+ const filePath = join(this.completedDir, month, `${id}.json`);
875
+ if (existsSync(filePath)) {
876
+ return this.readTrajectoryOrNull(filePath);
877
+ }
878
+ }
879
+ } catch (error) {
880
+ if (error.code !== "ENOENT") {
881
+ console.error("Error searching completed trajectories:", error);
882
+ }
883
+ }
884
+ return null;
885
+ }
886
+ /**
887
+ * Get the currently active trajectory
888
+ */
889
+ async getActive() {
890
+ try {
891
+ const files = await readdir(this.activeDir);
892
+ const jsonFiles = files.filter((f) => f.endsWith(".json"));
893
+ if (jsonFiles.length === 0) {
894
+ return null;
895
+ }
896
+ let mostRecent = null;
897
+ let mostRecentTime = 0;
898
+ for (const file of jsonFiles) {
899
+ const trajectory2 = await this.readTrajectoryOrNull(
900
+ join(this.activeDir, file)
901
+ );
902
+ if (trajectory2) {
903
+ const startTime = new Date(trajectory2.startedAt).getTime();
904
+ if (startTime > mostRecentTime) {
905
+ mostRecentTime = startTime;
906
+ mostRecent = trajectory2;
907
+ }
908
+ }
909
+ }
910
+ return mostRecent;
911
+ } catch (error) {
912
+ if (error.code === "ENOENT") {
913
+ return null;
914
+ }
915
+ console.error("Error reading active trajectories:", error);
916
+ return null;
917
+ }
918
+ }
919
+ /**
920
+ * List trajectories with optional filtering
921
+ */
922
+ async list(query) {
923
+ const index = await this.loadIndex();
924
+ let entries = Object.entries(index.trajectories);
925
+ if (query.status) {
926
+ entries = entries.filter(([, entry]) => entry.status === query.status);
927
+ }
928
+ if (query.since) {
929
+ const sinceTime = new Date(query.since).getTime();
930
+ entries = entries.filter(
931
+ ([, entry]) => new Date(entry.startedAt).getTime() >= sinceTime
932
+ );
933
+ }
934
+ if (query.until) {
935
+ const untilTime = new Date(query.until).getTime();
936
+ entries = entries.filter(
937
+ ([, entry]) => new Date(entry.startedAt).getTime() <= untilTime
938
+ );
939
+ }
940
+ const sortBy = query.sortBy ?? "startedAt";
941
+ const sortOrder = query.sortOrder ?? "desc";
942
+ entries.sort((a, b) => {
943
+ const aVal = a[1][sortBy] ?? "";
944
+ const bVal = b[1][sortBy] ?? "";
945
+ const cmp = String(aVal).localeCompare(String(bVal));
946
+ return sortOrder === "asc" ? cmp : -cmp;
947
+ });
948
+ const offset = query.offset ?? 0;
949
+ const limit = query.limit ?? 50;
950
+ entries = entries.slice(offset, offset + limit);
951
+ return Promise.all(
952
+ entries.map(async ([id, entry]) => {
953
+ const trajectory2 = await this.get(id);
954
+ return {
955
+ id,
956
+ title: entry.title,
957
+ status: entry.status,
958
+ startedAt: entry.startedAt,
959
+ completedAt: entry.completedAt,
960
+ confidence: trajectory2?.retrospective?.confidence,
961
+ chapterCount: trajectory2?.chapters.length ?? 0,
962
+ decisionCount: trajectory2?.chapters.reduce(
963
+ (count, chapter) => count + chapter.events.filter((e) => e.type === "decision").length,
964
+ 0
965
+ ) ?? 0
966
+ };
967
+ })
968
+ );
969
+ }
970
+ /**
971
+ * Delete a trajectory
972
+ */
973
+ async delete(id) {
974
+ const activePath = join(this.activeDir, `${id}.json`);
975
+ if (existsSync(activePath)) {
976
+ await unlink(activePath);
977
+ }
978
+ const index = await this.loadIndex();
979
+ const entry = index.trajectories[id];
980
+ if (entry?.path && existsSync(entry.path)) {
981
+ await unlink(entry.path);
982
+ const mdPath = entry.path.replace(".json", ".md");
983
+ if (existsSync(mdPath)) {
984
+ await unlink(mdPath);
985
+ }
986
+ }
987
+ delete index.trajectories[id];
988
+ await this.saveIndex(index);
989
+ }
990
+ /**
991
+ * Search trajectories by text
992
+ */
993
+ async search(text, options) {
994
+ const allTrajectories = await this.list({});
995
+ const searchLower = text.toLowerCase();
996
+ const limit = options?.limit ?? 20;
997
+ const matches = [];
998
+ for (const summary of allTrajectories) {
999
+ if (matches.length >= limit) break;
1000
+ if (summary.title.toLowerCase().includes(searchLower)) {
1001
+ matches.push(summary);
1002
+ continue;
1003
+ }
1004
+ const trajectory2 = await this.get(summary.id);
1005
+ if (!trajectory2) continue;
1006
+ if (trajectory2.retrospective?.summary.toLowerCase().includes(searchLower)) {
1007
+ matches.push(summary);
1008
+ continue;
1009
+ }
1010
+ const hasMatchingDecision = trajectory2.chapters.some(
1011
+ (chapter) => chapter.events.some(
1012
+ (event) => event.type === "decision" && event.content.toLowerCase().includes(searchLower)
1013
+ )
1014
+ );
1015
+ if (hasMatchingDecision) {
1016
+ matches.push(summary);
1017
+ }
1018
+ }
1019
+ return matches;
1020
+ }
1021
+ /**
1022
+ * Close storage (no-op for file storage)
1023
+ */
1024
+ async close() {
1025
+ }
1026
+ // Private helpers
1027
+ /**
1028
+ * Read a trajectory file and return a tagged result so callers can
1029
+ * distinguish missing files, malformed JSON, and schema violations.
1030
+ *
1031
+ * Does NOT log. Callers choose whether to warn, swallow, or throw.
1032
+ */
1033
+ async readTrajectoryFile(path) {
1034
+ let content;
1035
+ try {
1036
+ content = await readFile(path, "utf-8");
1037
+ } catch (error) {
1038
+ return { ok: false, reason: "io_error", path, error };
1039
+ }
1040
+ let data;
1041
+ try {
1042
+ data = JSON.parse(content);
1043
+ } catch (error) {
1044
+ return { ok: false, reason: "malformed_json", path, error };
1045
+ }
1046
+ const validation = validateTrajectory(data);
1047
+ if (validation.success) {
1048
+ return { ok: true, trajectory: validation.data };
1049
+ }
1050
+ return {
1051
+ ok: false,
1052
+ reason: "schema_violation",
1053
+ path,
1054
+ error: validation.errors
1055
+ };
1056
+ }
1057
+ /**
1058
+ * Convenience wrapper for callers that only care whether they got a
1059
+ * trajectory. Returns null for any failure and writes nothing to the
1060
+ * console — so nothing leaks into test output or the CLI spinner.
1061
+ */
1062
+ async readTrajectoryOrNull(path) {
1063
+ const result = await this.readTrajectoryFile(path);
1064
+ return result.ok ? result.trajectory : null;
1065
+ }
1066
+ async loadIndex() {
1067
+ try {
1068
+ const content = await readFile(this.indexPath, "utf-8");
1069
+ return JSON.parse(content);
1070
+ } catch (error) {
1071
+ if (error.code !== "ENOENT") {
1072
+ console.error(
1073
+ "Error loading trajectory index, using empty index:",
1074
+ error
1075
+ );
1076
+ }
1077
+ return {
1078
+ version: 1,
1079
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
1080
+ trajectories: {}
1081
+ };
1082
+ }
1083
+ }
1084
+ async saveIndex(index) {
1085
+ index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
1086
+ await writeFile(this.indexPath, JSON.stringify(index, null, 2), "utf-8");
1087
+ }
1088
+ async updateIndex(trajectory2, filePath) {
1089
+ const index = await this.loadIndex();
1090
+ index.trajectories[trajectory2.id] = {
1091
+ title: trajectory2.task.title,
1092
+ status: trajectory2.status,
1093
+ startedAt: trajectory2.startedAt,
1094
+ completedAt: trajectory2.completedAt,
1095
+ path: filePath
1096
+ };
1097
+ await this.saveIndex(index);
1098
+ }
1099
+ };
1100
+
1101
+ // src/sdk/client.ts
1102
+ var require2 = createRequire(import.meta.url);
1103
+ function normalizeOptionalString(value) {
1104
+ const normalized = value?.trim();
1105
+ return normalized ? normalized : void 0;
1106
+ }
1107
+ function normalizeAutoCompactOptions(autoCompact) {
1108
+ if (!autoCompact) {
1109
+ return false;
1110
+ }
1111
+ if (autoCompact === true) {
1112
+ return { mechanical: false, markdown: true };
1113
+ }
1114
+ return {
1115
+ mechanical: autoCompact.mechanical ?? false,
1116
+ markdown: autoCompact.markdown ?? true
1117
+ };
1118
+ }
1119
+ function resolveStartWorkflowId(options) {
1120
+ if (options !== void 0 && Object.prototype.hasOwnProperty.call(options, "workflowId")) {
1121
+ return normalizeOptionalString(options.workflowId);
1122
+ }
1123
+ return normalizeOptionalString(process.env.TRAJECTORIES_WORKFLOW_ID);
1124
+ }
1125
+ function resolveTrajectoryCliInvocation() {
1126
+ const explicitCli = normalizeOptionalString(process.env.TRAJECTORIES_CLI);
1127
+ if (explicitCli) {
1128
+ if (/\.(?:cjs|mjs|js)$/i.test(explicitCli)) {
1129
+ return { command: process.execPath, args: [explicitCli] };
1130
+ }
1131
+ return { command: explicitCli, args: [] };
1132
+ }
1133
+ try {
1134
+ const packageJsonPath = require2.resolve("agent-trajectories/package.json");
1135
+ const pkg = JSON.parse(
1136
+ readFileSync(packageJsonPath, "utf-8")
1137
+ );
1138
+ const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.trail ?? (pkg.name ? pkg.bin?.[pkg.name] : void 0);
1139
+ if (binEntry) {
1140
+ const cliPath = resolvePath(dirname(packageJsonPath), binEntry);
1141
+ if (existsSync2(cliPath)) {
1142
+ return { command: process.execPath, args: [cliPath] };
1143
+ }
1144
+ }
1145
+ } catch {
1146
+ }
1147
+ return { command: "trail", args: [] };
1148
+ }
1149
+ function parseCompactWorkflowOutput(stdout) {
1150
+ const compactedPath = stdout.match(
1151
+ /^\s*Compacted trajectory saved to:\s*(.+)$/m
1152
+ )?.[1];
1153
+ const markdownPath = stdout.match(
1154
+ /^\s*Markdown summary saved to:\s*(.+)$/m
1155
+ )?.[1];
1156
+ if (!compactedPath) {
1157
+ throw new Error("compactWorkflow failed: unable to parse compacted path");
1158
+ }
1159
+ return {
1160
+ compactedPath: compactedPath.trim(),
1161
+ ...markdownPath ? { markdownPath: markdownPath.trim() } : {}
1162
+ };
1163
+ }
1164
+ async function compactWorkflow(workflowId, options) {
1165
+ const normalizedWorkflowId = normalizeOptionalString(workflowId);
1166
+ if (!normalizedWorkflowId) {
1167
+ throw new Error("compactWorkflow failed: workflowId is required");
1168
+ }
1169
+ const cli = resolveTrajectoryCliInvocation();
1170
+ const args = [
1171
+ ...cli.args,
1172
+ "compact",
1173
+ "--workflow",
1174
+ normalizedWorkflowId,
1175
+ "--all"
1176
+ ];
1177
+ if (options?.markdown) {
1178
+ args.push("--markdown");
1179
+ }
1180
+ if (options?.mechanical) {
1181
+ args.push("--mechanical");
1182
+ }
1183
+ return new Promise((resolve, reject) => {
1184
+ const child = spawn(cli.command, args, {
1185
+ cwd: options?.cwd,
1186
+ stdio: ["ignore", "pipe", "pipe"]
1187
+ });
1188
+ const stdoutChunks = [];
1189
+ let stderr = "";
1190
+ child.stdout.on("data", (chunk) => {
1191
+ stdoutChunks.push(Buffer.from(chunk));
1192
+ });
1193
+ child.stderr.on("data", (chunk) => {
1194
+ stderr += chunk.toString();
1195
+ process.stderr.write(chunk);
1196
+ });
1197
+ child.on("error", (error) => {
1198
+ reject(new Error(`compactWorkflow failed: ${error.message}`));
1199
+ });
1200
+ child.on("close", (code) => {
1201
+ if (code !== 0) {
1202
+ reject(
1203
+ new Error(
1204
+ `compactWorkflow failed: ${stderr.trim() || `exit code ${code}`}`
1205
+ )
1206
+ );
1207
+ return;
1208
+ }
1209
+ try {
1210
+ const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
1211
+ resolve(parseCompactWorkflowOutput(stdout));
1212
+ } catch (error) {
1213
+ reject(
1214
+ error instanceof Error ? error : new Error("compactWorkflow failed: unable to parse CLI output")
1215
+ );
1216
+ }
1217
+ });
1218
+ });
1219
+ }
1220
+ var TrajectorySession = class {
1221
+ trajectory;
1222
+ client;
1223
+ autoSave;
1224
+ constructor(trajectory2, client, autoSave) {
1225
+ this.trajectory = trajectory2;
1226
+ this.client = client;
1227
+ this.autoSave = autoSave;
1228
+ }
1229
+ /**
1230
+ * Get the current trajectory data
1231
+ */
1232
+ get data() {
1233
+ return this.trajectory;
1234
+ }
1235
+ /**
1236
+ * Get the trajectory ID
1237
+ */
1238
+ get id() {
1239
+ return this.trajectory.id;
1240
+ }
1241
+ async autoCompactIfConfigured() {
1242
+ const autoCompact = this.client.getAutoCompactOptions();
1243
+ if (!autoCompact || !this.trajectory.workflowId) {
1244
+ return;
1245
+ }
1246
+ try {
1247
+ await compactWorkflow(this.trajectory.workflowId, {
1248
+ ...autoCompact,
1249
+ cwd: this.client.getAutoCompactCwd()
1250
+ });
1251
+ } catch (error) {
1252
+ const message = error instanceof Error ? error.message : String(error);
1253
+ console.error(
1254
+ `Warning: autoCompact failed for workflow ${this.trajectory.workflowId}: ${message}`
1255
+ );
1256
+ }
1257
+ }
1258
+ /**
1259
+ * Start a new chapter
1260
+ * @param title - Chapter title
1261
+ * @param agentName - Agent name (uses default if not specified)
1262
+ */
1263
+ async chapter(title, agentName) {
1264
+ const agent = agentName ?? this.client.defaultAgent ?? "default";
1265
+ this.trajectory = addChapter(this.trajectory, { title, agentName: agent });
1266
+ if (this.autoSave) {
1267
+ await this.client.save(this.trajectory);
1268
+ }
1269
+ return this;
1270
+ }
1271
+ /**
1272
+ * Record an event
1273
+ * @param type - Event type
1274
+ * @param content - Human-readable content
1275
+ * @param options - Additional event options
1276
+ */
1277
+ async event(type, content, options) {
1278
+ this.trajectory = addEvent(this.trajectory, {
1279
+ type,
1280
+ content,
1281
+ ...options
1282
+ });
1283
+ if (this.autoSave) {
1284
+ await this.client.save(this.trajectory);
1285
+ }
1286
+ return this;
1287
+ }
1288
+ /**
1289
+ * Record a note
1290
+ */
1291
+ async note(content, significance) {
1292
+ return this.event("note", content, { significance });
1293
+ }
1294
+ /**
1295
+ * Record a finding
1296
+ */
1297
+ async finding(content, significance) {
1298
+ return this.event("finding", content, {
1299
+ significance: significance ?? "medium"
1300
+ });
1301
+ }
1302
+ /**
1303
+ * Record a reflection — a higher-level synthesis of recent observations.
1304
+ * Used by workflow orchestrators and lead agents to periodically
1305
+ * synthesize worker progress and course-correct.
1306
+ */
1307
+ async reflect(content, confidence) {
1308
+ return this.event("reflection", content, {
1309
+ significance: "high",
1310
+ ...confidence !== void 0 ? { tags: [`confidence:${confidence}`] } : {}
1311
+ });
1312
+ }
1313
+ /**
1314
+ * Record an error
1315
+ */
1316
+ async error(content) {
1317
+ return this.event("error", content, { significance: "high" });
1318
+ }
1319
+ /**
1320
+ * Record a decision
1321
+ * @param decision - Structured decision record
1322
+ */
1323
+ async decision(decision) {
1324
+ this.trajectory = addDecision(this.trajectory, decision);
1325
+ if (this.autoSave) {
1326
+ await this.client.save(this.trajectory);
1327
+ }
1328
+ return this;
1329
+ }
1330
+ /**
1331
+ * Quick decision helper for simple choices
1332
+ * @param question - What was the question/choice?
1333
+ * @param chosen - What was chosen
1334
+ * @param reasoning - Why this choice was made
1335
+ * @param alternatives - Optional list of alternatives considered
1336
+ */
1337
+ async decide(question, chosen, reasoning, alternatives) {
1338
+ return this.decision({
1339
+ question,
1340
+ chosen,
1341
+ reasoning,
1342
+ alternatives: alternatives ?? []
1343
+ });
1344
+ }
1345
+ /**
1346
+ * Add a tag to the trajectory
1347
+ */
1348
+ async tag(tag) {
1349
+ if (!this.trajectory.tags.includes(tag)) {
1350
+ this.trajectory = {
1351
+ ...this.trajectory,
1352
+ tags: [...this.trajectory.tags, tag]
1353
+ };
1354
+ if (this.autoSave) {
1355
+ await this.client.save(this.trajectory);
1356
+ }
1357
+ }
1358
+ return this;
1359
+ }
1360
+ /**
1361
+ * Complete the trajectory with a retrospective
1362
+ * @param input - Retrospective details
1363
+ */
1364
+ async complete(input) {
1365
+ this.trajectory = completeTrajectory(this.trajectory, input);
1366
+ await this.client.save(this.trajectory);
1367
+ await this.autoCompactIfConfigured();
1368
+ return this.trajectory;
1369
+ }
1370
+ /**
1371
+ * Quick complete with minimal required fields
1372
+ * @param summary - What was accomplished
1373
+ * @param confidence - Confidence level (0-1)
1374
+ * @param options - Additional optional fields
1375
+ */
1376
+ async done(summary, confidence, options) {
1377
+ return this.complete({
1378
+ summary,
1379
+ confidence,
1380
+ approach: options?.approach ?? "Standard approach",
1381
+ decisions: options?.decisions,
1382
+ challenges: options?.challenges,
1383
+ learnings: options?.learnings,
1384
+ suggestions: options?.suggestions
1385
+ });
1386
+ }
1387
+ /**
1388
+ * Abandon the trajectory
1389
+ * @param reason - Reason for abandonment
1390
+ */
1391
+ async abandon(reason) {
1392
+ this.trajectory = abandonTrajectory(this.trajectory, reason);
1393
+ await this.client.save(this.trajectory);
1394
+ return this.trajectory;
1395
+ }
1396
+ /**
1397
+ * Force save the current state
1398
+ */
1399
+ async save() {
1400
+ await this.client.save(this.trajectory);
1401
+ }
1402
+ /**
1403
+ * Export to markdown
1404
+ */
1405
+ toMarkdown() {
1406
+ return exportToMarkdown(this.trajectory);
1407
+ }
1408
+ /**
1409
+ * Export to JSON
1410
+ */
1411
+ toJSON(compact) {
1412
+ return exportToJSON(this.trajectory, { compact });
1413
+ }
1414
+ /**
1415
+ * Export to timeline format
1416
+ */
1417
+ toTimeline() {
1418
+ return exportToTimeline(this.trajectory);
1419
+ }
1420
+ /**
1421
+ * Export to PR summary format
1422
+ */
1423
+ toPRSummary() {
1424
+ return exportToPRSummary(this.trajectory);
1425
+ }
1426
+ };
1427
+ var TrajectoryClient = class {
1428
+ storage;
1429
+ initialized = false;
1430
+ defaultAgent;
1431
+ projectId;
1432
+ autoSave;
1433
+ autoCompactCwd;
1434
+ autoCompact;
1435
+ constructor(options = {}) {
1436
+ this.storage = options.storage ?? new FileStorage(options.dataDir);
1437
+ this.defaultAgent = options.defaultAgent ?? process.env.TRAJECTORIES_AGENT;
1438
+ this.projectId = options.projectId ?? process.env.TRAJECTORIES_PROJECT;
1439
+ this.autoSave = options.autoSave ?? true;
1440
+ this.autoCompact = normalizeAutoCompactOptions(options.autoCompact);
1441
+ this.autoCompactCwd = options.storage ? void 0 : options.dataDir;
1442
+ }
1443
+ getAutoCompactOptions() {
1444
+ return this.autoCompact;
1445
+ }
1446
+ getAutoCompactCwd() {
1447
+ return this.autoCompactCwd;
1448
+ }
1449
+ /**
1450
+ * Initialize the client (creates storage directories, etc.)
1451
+ * Must be called before using other methods.
1452
+ */
1453
+ async init() {
1454
+ if (!this.initialized) {
1455
+ await this.storage.initialize();
1456
+ this.initialized = true;
1457
+ }
1458
+ }
1459
+ /**
1460
+ * Ensure the client is initialized
1461
+ */
1462
+ ensureInitialized() {
1463
+ if (!this.initialized) {
1464
+ throw new TrajectoryError(
1465
+ "Client not initialized. Call init() first.",
1466
+ "NOT_INITIALIZED",
1467
+ "Add 'await client.init()' before using the client"
1468
+ );
1469
+ }
1470
+ }
1471
+ /**
1472
+ * Start a new trajectory
1473
+ * @param title - Task title
1474
+ * @param options - Additional creation options
1475
+ * @returns A session for the new trajectory
1476
+ */
1477
+ async start(title, options) {
1478
+ this.ensureInitialized();
1479
+ const active = await this.storage.getActive();
1480
+ if (active) {
1481
+ throw new TrajectoryError(
1482
+ `Active trajectory already exists: ${active.id}`,
1483
+ "ACTIVE_TRAJECTORY_EXISTS",
1484
+ "Complete or abandon the active trajectory first"
1485
+ );
1486
+ }
1487
+ const workflowId = resolveStartWorkflowId(options);
1488
+ const { workflowId: _workflowId, ...createOptions } = options ?? {};
1489
+ const trajectory2 = createTrajectory({
1490
+ title,
1491
+ projectId: this.projectId,
1492
+ ...createOptions
1493
+ });
1494
+ const stampedTrajectory = workflowId ? { ...trajectory2, workflowId } : trajectory2;
1495
+ await this.storage.save(stampedTrajectory);
1496
+ return new TrajectorySession(stampedTrajectory, this, this.autoSave);
1497
+ }
1498
+ /**
1499
+ * Resume the currently active trajectory
1500
+ * @returns Session for active trajectory, or null if none active
1501
+ */
1502
+ async resume() {
1503
+ this.ensureInitialized();
1504
+ const active = await this.storage.getActive();
1505
+ if (!active) {
1506
+ return null;
1507
+ }
1508
+ return new TrajectorySession(active, this, this.autoSave);
1509
+ }
1510
+ /**
1511
+ * Get the active trajectory (without creating a session)
1512
+ */
1513
+ async getActive() {
1514
+ this.ensureInitialized();
1515
+ return this.storage.getActive();
1516
+ }
1517
+ /**
1518
+ * Get a trajectory by ID
1519
+ * @param id - Trajectory ID
1520
+ */
1521
+ async get(id) {
1522
+ this.ensureInitialized();
1523
+ return this.storage.get(id);
1524
+ }
1525
+ /**
1526
+ * Open a trajectory session by ID
1527
+ * @param id - Trajectory ID
1528
+ */
1529
+ async open(id) {
1530
+ this.ensureInitialized();
1531
+ const trajectory2 = await this.storage.get(id);
1532
+ if (!trajectory2) {
1533
+ return null;
1534
+ }
1535
+ return new TrajectorySession(trajectory2, this, this.autoSave);
1536
+ }
1537
+ /**
1538
+ * List trajectories with optional filtering
1539
+ * @param query - Query options
1540
+ */
1541
+ async list(query) {
1542
+ this.ensureInitialized();
1543
+ return this.storage.list(query ?? {});
1544
+ }
1545
+ /**
1546
+ * Search trajectories by text
1547
+ * @param text - Search text
1548
+ * @param limit - Maximum results
1549
+ */
1550
+ async search(text, limit) {
1551
+ this.ensureInitialized();
1552
+ return this.storage.search(
1553
+ text,
1554
+ limit !== void 0 ? { limit } : void 0
1555
+ );
1556
+ }
1557
+ /**
1558
+ * Delete a trajectory
1559
+ * @param id - Trajectory ID
1560
+ */
1561
+ async delete(id) {
1562
+ this.ensureInitialized();
1563
+ await this.storage.delete(id);
1564
+ }
1565
+ /**
1566
+ * Save a trajectory to storage
1567
+ * @param trajectory - Trajectory to save
1568
+ */
1569
+ async save(trajectory2) {
1570
+ this.ensureInitialized();
1571
+ await this.storage.save(trajectory2);
1572
+ }
1573
+ /**
1574
+ * Close the client and release resources
1575
+ */
1576
+ async close() {
1577
+ if (this.initialized) {
1578
+ await this.storage.close();
1579
+ this.initialized = false;
1580
+ }
1581
+ }
1582
+ /**
1583
+ * Export a trajectory to markdown
1584
+ * @param id - Trajectory ID
1585
+ */
1586
+ async exportMarkdown(id) {
1587
+ const trajectory2 = await this.get(id);
1588
+ if (!trajectory2) return null;
1589
+ return exportToMarkdown(trajectory2);
1590
+ }
1591
+ /**
1592
+ * Export a trajectory to JSON
1593
+ * @param id - Trajectory ID
1594
+ * @param compact - Whether to use compact format
1595
+ */
1596
+ async exportJSON(id, compact) {
1597
+ const trajectory2 = await this.get(id);
1598
+ if (!trajectory2) return null;
1599
+ return exportToJSON(trajectory2, { compact });
1600
+ }
1601
+ /**
1602
+ * Export a trajectory to timeline format
1603
+ * @param id - Trajectory ID
1604
+ */
1605
+ async exportTimeline(id) {
1606
+ const trajectory2 = await this.get(id);
1607
+ if (!trajectory2) return null;
1608
+ return exportToTimeline(trajectory2);
1609
+ }
1610
+ /**
1611
+ * Export a trajectory to PR summary format
1612
+ * @param id - Trajectory ID
1613
+ */
1614
+ async exportPRSummary(id) {
1615
+ const trajectory2 = await this.get(id);
1616
+ if (!trajectory2) return null;
1617
+ return exportToPRSummary(trajectory2);
1618
+ }
1619
+ };
1620
+
1621
+ // src/sdk/builder.ts
1622
+ var TrajectoryBuilder = class _TrajectoryBuilder {
1623
+ trajectory;
1624
+ constructor(input) {
1625
+ this.trajectory = createTrajectory(input);
1626
+ }
1627
+ /**
1628
+ * Create a new trajectory builder
1629
+ * @param title - Task title
1630
+ */
1631
+ static create(title) {
1632
+ return new _TrajectoryBuilder({ title });
1633
+ }
1634
+ /**
1635
+ * Set the task description
1636
+ */
1637
+ withDescription(description) {
1638
+ this.trajectory = {
1639
+ ...this.trajectory,
1640
+ task: {
1641
+ ...this.trajectory.task,
1642
+ description
1643
+ }
1644
+ };
1645
+ return this;
1646
+ }
1647
+ /**
1648
+ * Set the external task source
1649
+ */
1650
+ withSource(source) {
1651
+ this.trajectory = {
1652
+ ...this.trajectory,
1653
+ task: {
1654
+ ...this.trajectory.task,
1655
+ source
1656
+ }
1657
+ };
1658
+ return this;
1659
+ }
1660
+ /**
1661
+ * Set the project ID
1662
+ */
1663
+ withProject(projectId) {
1664
+ this.trajectory = {
1665
+ ...this.trajectory,
1666
+ projectId
1667
+ };
1668
+ return this;
1669
+ }
1670
+ /**
1671
+ * Add tags
1672
+ */
1673
+ withTags(...tags) {
1674
+ this.trajectory = {
1675
+ ...this.trajectory,
1676
+ tags: [...this.trajectory.tags, ...tags]
1677
+ };
1678
+ return this;
1679
+ }
1680
+ /**
1681
+ * Add a single tag
1682
+ */
1683
+ tag(tag) {
1684
+ if (!this.trajectory.tags.includes(tag)) {
1685
+ this.trajectory = {
1686
+ ...this.trajectory,
1687
+ tags: [...this.trajectory.tags, tag]
1688
+ };
1689
+ }
1690
+ return this;
1691
+ }
1692
+ /**
1693
+ * Start a new chapter
1694
+ * @param title - Chapter title
1695
+ * @param agentName - Agent name
1696
+ */
1697
+ chapter(title, agentName) {
1698
+ this.trajectory = addChapter(this.trajectory, { title, agentName });
1699
+ return this;
1700
+ }
1701
+ /**
1702
+ * Add a generic event
1703
+ */
1704
+ event(type, content, options) {
1705
+ this.trajectory = addEvent(this.trajectory, {
1706
+ type,
1707
+ content,
1708
+ ...options
1709
+ });
1710
+ return this;
1711
+ }
1712
+ /**
1713
+ * Add a note event
1714
+ */
1715
+ note(content, significance) {
1716
+ return this.event("note", content, { significance });
1717
+ }
1718
+ /**
1719
+ * Add a finding event
1720
+ */
1721
+ finding(content, significance) {
1722
+ return this.event("finding", content, {
1723
+ significance: significance ?? "medium"
1724
+ });
1725
+ }
1726
+ /**
1727
+ * Add a reflection event — a higher-level synthesis of recent observations.
1728
+ * Reflections are always high significance since they represent
1729
+ * periodic course-correction insights.
1730
+ */
1731
+ reflect(content, confidence) {
1732
+ return this.event("reflection", content, {
1733
+ significance: "high",
1734
+ ...confidence !== void 0 ? { tags: [`confidence:${confidence}`] } : {}
1735
+ });
1736
+ }
1737
+ /**
1738
+ * Add an error event
1739
+ */
1740
+ error(content) {
1741
+ return this.event("error", content, { significance: "high" });
1742
+ }
1743
+ /**
1744
+ * Add a thinking event
1745
+ */
1746
+ thinking(content) {
1747
+ return this.event("thinking", content, { significance: "low" });
1748
+ }
1749
+ /**
1750
+ * Add a tool call event
1751
+ */
1752
+ toolCall(content, raw) {
1753
+ return this.event("tool_call", content, { raw });
1754
+ }
1755
+ /**
1756
+ * Add a tool result event
1757
+ */
1758
+ toolResult(content, raw) {
1759
+ return this.event("tool_result", content, { raw });
1760
+ }
1761
+ /**
1762
+ * Add a prompt event
1763
+ */
1764
+ prompt(content) {
1765
+ return this.event("prompt", content, { significance: "medium" });
1766
+ }
1767
+ /**
1768
+ * Add a message sent event
1769
+ */
1770
+ messageSent(content) {
1771
+ return this.event("message_sent", content);
1772
+ }
1773
+ /**
1774
+ * Add a message received event
1775
+ */
1776
+ messageReceived(content) {
1777
+ return this.event("message_received", content);
1778
+ }
1779
+ /**
1780
+ * Add a structured decision
1781
+ */
1782
+ decision(decision) {
1783
+ this.trajectory = addDecision(this.trajectory, decision);
1784
+ return this;
1785
+ }
1786
+ /**
1787
+ * Quick decision helper
1788
+ */
1789
+ decide(question, chosen, reasoning, alternatives) {
1790
+ return this.decision({
1791
+ question,
1792
+ chosen,
1793
+ reasoning,
1794
+ alternatives: alternatives ?? []
1795
+ });
1796
+ }
1797
+ /**
1798
+ * Record git commits
1799
+ */
1800
+ commits(...hashes) {
1801
+ this.trajectory = {
1802
+ ...this.trajectory,
1803
+ commits: [...this.trajectory.commits, ...hashes]
1804
+ };
1805
+ return this;
1806
+ }
1807
+ /**
1808
+ * Record files changed
1809
+ */
1810
+ filesChanged(...paths) {
1811
+ this.trajectory = {
1812
+ ...this.trajectory,
1813
+ filesChanged: [...this.trajectory.filesChanged, ...paths]
1814
+ };
1815
+ return this;
1816
+ }
1817
+ /**
1818
+ * Complete the trajectory with a retrospective
1819
+ * @param input - Retrospective details
1820
+ * @returns The completed trajectory
1821
+ */
1822
+ complete(input) {
1823
+ this.trajectory = completeTrajectory(this.trajectory, input);
1824
+ return this.trajectory;
1825
+ }
1826
+ /**
1827
+ * Quick complete with minimal fields
1828
+ */
1829
+ done(summary, confidence, approach) {
1830
+ return this.complete({
1831
+ summary,
1832
+ confidence,
1833
+ approach: approach ?? "Standard approach"
1834
+ });
1835
+ }
1836
+ /**
1837
+ * Abandon the trajectory
1838
+ */
1839
+ abandon(reason) {
1840
+ this.trajectory = abandonTrajectory(this.trajectory, reason);
1841
+ return this.trajectory;
1842
+ }
1843
+ /**
1844
+ * Get the current trajectory (without completing)
1845
+ */
1846
+ build() {
1847
+ return this.trajectory;
1848
+ }
1849
+ /**
1850
+ * Export to markdown
1851
+ */
1852
+ toMarkdown() {
1853
+ return exportToMarkdown(this.trajectory);
1854
+ }
1855
+ /**
1856
+ * Export to JSON
1857
+ */
1858
+ toJSON(compact) {
1859
+ return exportToJSON(this.trajectory, { compact });
1860
+ }
1861
+ /**
1862
+ * Export to timeline format
1863
+ */
1864
+ toTimeline() {
1865
+ return exportToTimeline(this.trajectory);
1866
+ }
1867
+ /**
1868
+ * Export to PR summary format
1869
+ */
1870
+ toPRSummary() {
1871
+ return exportToPRSummary(this.trajectory);
1872
+ }
1873
+ };
1874
+ function trajectory(title) {
1875
+ return TrajectoryBuilder.create(title);
1876
+ }
1877
+
1878
+ // src/core/trailers.ts
1879
+ import { execSync as execSync2 } from "child_process";
1880
+ import { readFileSync as readFileSync2 } from "fs";
1881
+
1882
+ // src/core/trace.ts
1883
+ import { execSync } from "child_process";
1884
+ import { createHash } from "crypto";
1885
+ function isValidGitRef(ref) {
1886
+ const validRefPattern = /^[a-zA-Z0-9_\-./]+$/;
1887
+ const commitHashPattern = /^[a-fA-F0-9]{7,40}$/;
1888
+ if (ref === "HEAD" || ref === "working") {
1889
+ return true;
1890
+ }
1891
+ if (commitHashPattern.test(ref)) {
1892
+ return true;
1893
+ }
1894
+ if (validRefPattern.test(ref) && ref.length <= 255) {
1895
+ if (!ref.includes("..") || ref.split("..").every((p) => p.length > 0)) {
1896
+ return true;
1897
+ }
1898
+ }
1899
+ return false;
1900
+ }
1901
+ function isGitRepo() {
1902
+ try {
1903
+ execSync("git rev-parse --is-inside-work-tree", {
1904
+ encoding: "utf-8",
1905
+ stdio: ["pipe", "pipe", "pipe"]
1906
+ });
1907
+ return true;
1908
+ } catch {
1909
+ return false;
1910
+ }
1911
+ }
1912
+
1913
+ // src/core/trailers.ts
1914
+ var TRAJECTORY_TRAILER_KEY = "Trajectory";
1915
+ function formatTrailer(trajectoryId) {
1916
+ return `${TRAJECTORY_TRAILER_KEY}: ${trajectoryId}`;
1917
+ }
1918
+ function parseTrajectoryFromMessage(commitMessage) {
1919
+ const lines = commitMessage.split("\n");
1920
+ for (const line of lines) {
1921
+ const match = line.match(
1922
+ new RegExp(`^${TRAJECTORY_TRAILER_KEY}:\\s*(traj_[a-z0-9_]+)$`)
1923
+ );
1924
+ if (match) {
1925
+ return match[1];
1926
+ }
1927
+ }
1928
+ return null;
1929
+ }
1930
+ function getTrajectoryFromCommit(commitHash) {
1931
+ if (!isGitRepo() || !isValidGitRef(commitHash)) {
1932
+ return null;
1933
+ }
1934
+ try {
1935
+ const message = execSync2(`git log -1 --format=%B ${commitHash}`, {
1936
+ encoding: "utf-8",
1937
+ stdio: ["pipe", "pipe", "pipe"]
1938
+ });
1939
+ return parseTrajectoryFromMessage(message);
1940
+ } catch {
1941
+ return null;
1942
+ }
1943
+ }
1944
+ function getCommitsBetween(startRef, endRef = "HEAD") {
1945
+ if (!isGitRepo()) {
1946
+ return [];
1947
+ }
1948
+ if (!isValidGitRef(startRef) || !isValidGitRef(endRef)) {
1949
+ return [];
1950
+ }
1951
+ try {
1952
+ const output = execSync2(
1953
+ `git log --format=%H%n%h%n%s%n%an%n%aI%n--- ${startRef}..${endRef}`,
1954
+ {
1955
+ encoding: "utf-8",
1956
+ stdio: ["pipe", "pipe", "pipe"]
1957
+ }
1958
+ );
1959
+ if (!output.trim()) {
1960
+ return [];
1961
+ }
1962
+ const commits = [];
1963
+ const entries = output.trim().split("\n---\n");
1964
+ for (const entry of entries) {
1965
+ const lines = entry.trim().split("\n");
1966
+ if (lines.length >= 5) {
1967
+ commits.push({
1968
+ fullHash: lines[0],
1969
+ hash: lines[1],
1970
+ subject: lines[2],
1971
+ author: lines[3],
1972
+ date: lines[4]
1973
+ });
1974
+ }
1975
+ }
1976
+ return commits;
1977
+ } catch {
1978
+ return [];
1979
+ }
1980
+ }
1981
+ function getFilesChangedBetween(startRef, endRef = "HEAD") {
1982
+ if (!isGitRepo()) {
1983
+ return [];
1984
+ }
1985
+ if (!isValidGitRef(startRef) || !isValidGitRef(endRef)) {
1986
+ return [];
1987
+ }
1988
+ try {
1989
+ const output = execSync2(`git diff --name-only ${startRef}..${endRef}`, {
1990
+ encoding: "utf-8",
1991
+ stdio: ["pipe", "pipe", "pipe"]
1992
+ });
1993
+ return output.trim().split("\n").filter(Boolean);
1994
+ } catch {
1995
+ return [];
1996
+ }
1997
+ }
1998
+
1999
+ export {
2000
+ generateTrajectoryId,
2001
+ generateChapterId,
2002
+ isValidTrajectoryId,
2003
+ isValidChapterId,
2004
+ TrajectoryEventSchema,
2005
+ DecisionSchema,
2006
+ ChapterSchema,
2007
+ RetrospectiveSchema,
2008
+ TrajectorySchema,
2009
+ validateTrajectory,
2010
+ validateCreateInput,
2011
+ validateCompleteInput,
2012
+ TrajectoryError,
2013
+ createTrajectory,
2014
+ addChapter,
2015
+ addEvent,
2016
+ addDecision,
2017
+ completeTrajectory,
2018
+ abandonTrajectory,
2019
+ exportToJSON,
2020
+ exportToMarkdown,
2021
+ exportToPRSummary,
2022
+ exportToTimeline,
2023
+ FileStorage,
2024
+ compactWorkflow,
2025
+ TrajectorySession,
2026
+ TrajectoryClient,
2027
+ TrajectoryBuilder,
2028
+ trajectory,
2029
+ TRAJECTORY_TRAILER_KEY,
2030
+ formatTrailer,
2031
+ parseTrajectoryFromMessage,
2032
+ getTrajectoryFromCommit,
2033
+ getCommitsBetween,
2034
+ getFilesChangedBetween
2035
+ };
2036
+ //# sourceMappingURL=chunk-W222QB6V.js.map