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.
- package/dist/index.cjs +1407 -537
- package/dist/src/cli/commands/messaging.d.ts +44 -0
- package/dist/src/cli/commands/messaging.d.ts.map +1 -1
- package/dist/src/cli/commands/messaging.js +195 -8
- package/dist/src/cli/commands/messaging.js.map +1 -1
- package/dist/src/cli/commands/setup.d.ts +30 -1
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +102 -85
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/node_modules/@agent-relay/cloud/dist/auth.d.ts +2 -2
- package/node_modules/@agent-relay/cloud/dist/auth.d.ts.map +1 -1
- package/node_modules/@agent-relay/cloud/dist/auth.js +108 -62
- package/node_modules/@agent-relay/cloud/dist/auth.js.map +1 -1
- package/node_modules/@agent-relay/cloud/package.json +2 -2
- package/node_modules/@agent-relay/config/package.json +1 -1
- package/node_modules/@agent-relay/hooks/package.json +4 -4
- package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.d.ts +5 -35
- package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.js +158 -292
- package/node_modules/@agent-relay/sdk/dist/workflows/trajectory.js.map +1 -1
- package/node_modules/@agent-relay/sdk/package.json +3 -2
- package/node_modules/@agent-relay/telemetry/package.json +1 -1
- package/node_modules/@agent-relay/trajectory/package.json +2 -2
- package/node_modules/@agent-relay/user-directory/package.json +2 -2
- package/node_modules/@agent-relay/utils/package.json +2 -2
- package/node_modules/@clack/core/CHANGELOG.md +200 -0
- package/node_modules/@clack/core/LICENSE +9 -0
- package/node_modules/@clack/core/README.md +22 -0
- package/node_modules/@clack/core/dist/index.cjs +15 -0
- package/node_modules/@clack/core/dist/index.cjs.map +1 -0
- package/node_modules/@clack/core/dist/index.d.cts +151 -0
- package/node_modules/@clack/core/dist/index.d.mts +151 -0
- package/node_modules/@clack/core/dist/index.d.ts +151 -0
- package/node_modules/@clack/core/dist/index.mjs +15 -0
- package/node_modules/@clack/core/dist/index.mjs.map +1 -0
- package/node_modules/@clack/core/package.json +62 -0
- package/node_modules/@clack/prompts/CHANGELOG.md +256 -0
- package/node_modules/@clack/prompts/LICENSE +23 -0
- package/node_modules/@clack/prompts/README.md +158 -0
- package/node_modules/@clack/prompts/dist/index.cjs +77 -0
- package/node_modules/@clack/prompts/dist/index.d.ts +106 -0
- package/node_modules/@clack/prompts/dist/index.mjs +77 -0
- package/node_modules/@clack/prompts/node_modules/is-unicode-supported/index.d.ts +12 -0
- package/node_modules/@clack/prompts/node_modules/is-unicode-supported/index.js +17 -0
- package/node_modules/@clack/prompts/node_modules/is-unicode-supported/license +9 -0
- package/node_modules/@clack/prompts/node_modules/is-unicode-supported/package.json +43 -0
- package/node_modules/@clack/prompts/node_modules/is-unicode-supported/readme.md +35 -0
- package/node_modules/@clack/prompts/package.json +65 -0
- package/node_modules/@smithy/config-resolver/package.json +2 -2
- package/node_modules/@smithy/util-defaults-mode-node/package.json +2 -2
- package/node_modules/@smithy/util-endpoints/dist-cjs/index.js +154 -61
- package/node_modules/@smithy/util-endpoints/dist-es/bdd/BinaryDecisionDiagram.js +15 -0
- package/node_modules/@smithy/util-endpoints/dist-es/decideEndpoint.js +41 -0
- package/node_modules/@smithy/util-endpoints/dist-es/index.js +2 -0
- package/node_modules/@smithy/util-endpoints/dist-es/lib/coalesce.js +8 -0
- package/node_modules/@smithy/util-endpoints/dist-es/lib/index.js +3 -0
- package/node_modules/@smithy/util-endpoints/dist-es/lib/ite.js +3 -0
- package/node_modules/@smithy/util-endpoints/dist-es/lib/split.js +13 -0
- package/node_modules/@smithy/util-endpoints/dist-es/lib/substring.js +1 -1
- package/node_modules/@smithy/util-endpoints/dist-es/utils/endpointFunctions.js +4 -1
- package/node_modules/@smithy/util-endpoints/dist-es/utils/evaluateExpression.js +20 -5
- package/node_modules/@smithy/util-endpoints/dist-es/utils/evaluateTemplate.js +3 -6
- package/node_modules/@smithy/util-endpoints/dist-es/utils/getReferenceValue.js +1 -5
- package/node_modules/@smithy/util-endpoints/dist-types/bdd/BinaryDecisionDiagram.d.ts +22 -0
- package/node_modules/@smithy/util-endpoints/dist-types/decideEndpoint.d.ts +7 -0
- package/node_modules/@smithy/util-endpoints/dist-types/index.d.ts +2 -0
- package/node_modules/@smithy/util-endpoints/dist-types/lib/coalesce.d.ts +7 -0
- package/node_modules/@smithy/util-endpoints/dist-types/lib/index.d.ts +3 -0
- package/node_modules/@smithy/util-endpoints/dist-types/lib/ite.d.ts +6 -0
- package/node_modules/@smithy/util-endpoints/dist-types/lib/split.d.ts +11 -0
- package/node_modules/@smithy/util-endpoints/dist-types/utils/endpointFunctions.d.ts +4 -0
- package/node_modules/@smithy/util-endpoints/dist-types/utils/getReferenceValue.d.ts +3 -1
- package/node_modules/@smithy/util-endpoints/package.json +1 -1
- package/node_modules/agent-trajectories/README.md +562 -0
- package/node_modules/agent-trajectories/dist/chunk-W222QB6V.js +2036 -0
- package/node_modules/agent-trajectories/dist/chunk-W222QB6V.js.map +1 -0
- package/node_modules/agent-trajectories/dist/cli/index.d.ts +2 -0
- package/node_modules/agent-trajectories/dist/cli/index.js +4592 -0
- package/node_modules/agent-trajectories/dist/cli/index.js.map +1 -0
- package/node_modules/agent-trajectories/dist/index-7tzw_CMS.d.ts +1777 -0
- package/node_modules/agent-trajectories/dist/index.d.ts +90 -0
- package/node_modules/agent-trajectories/dist/index.js +73 -0
- package/node_modules/agent-trajectories/dist/index.js.map +1 -0
- package/node_modules/agent-trajectories/dist/sdk/index.d.ts +2 -0
- package/node_modules/agent-trajectories/dist/sdk/index.js +39 -0
- package/node_modules/agent-trajectories/dist/sdk/index.js.map +1 -0
- package/node_modules/agent-trajectories/package.json +72 -0
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1157 -0
- package/node_modules/commander/esm.mjs +16 -0
- package/node_modules/commander/index.js +24 -0
- package/node_modules/commander/lib/argument.js +149 -0
- package/node_modules/commander/lib/command.js +2509 -0
- package/node_modules/commander/lib/error.js +39 -0
- package/node_modules/commander/lib/help.js +520 -0
- package/node_modules/commander/lib/option.js +330 -0
- package/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/node_modules/commander/package-support.json +16 -0
- package/node_modules/commander/package.json +84 -0
- package/node_modules/commander/typings/esm.d.mts +3 -0
- package/node_modules/commander/typings/index.d.ts +969 -0
- package/node_modules/picocolors/LICENSE +15 -0
- package/node_modules/picocolors/README.md +21 -0
- package/node_modules/picocolors/package.json +25 -0
- package/node_modules/picocolors/picocolors.browser.js +4 -0
- package/node_modules/picocolors/picocolors.d.ts +5 -0
- package/node_modules/picocolors/picocolors.js +75 -0
- package/node_modules/picocolors/types.d.ts +51 -0
- package/node_modules/sisteransi/license +21 -0
- package/node_modules/sisteransi/package.json +34 -0
- package/node_modules/sisteransi/readme.md +113 -0
- package/node_modules/sisteransi/src/index.js +58 -0
- package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
- package/package.json +10 -10
- package/packages/cloud/dist/auth.d.ts +2 -2
- package/packages/cloud/dist/auth.d.ts.map +1 -1
- package/packages/cloud/dist/auth.js +108 -62
- package/packages/cloud/dist/auth.js.map +1 -1
- package/packages/cloud/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/sdk/dist/workflows/trajectory.d.ts +5 -35
- package/packages/sdk/dist/workflows/trajectory.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/trajectory.js +158 -292
- package/packages/sdk/dist/workflows/trajectory.js.map +1 -1
- package/packages/sdk/package.json +3 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- 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
|