@renseiai/agentfactory 0.8.6 → 0.8.8
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/README.md +2 -2
- package/dist/src/config/repository-config.d.ts +14 -0
- package/dist/src/config/repository-config.d.ts.map +1 -1
- package/dist/src/config/repository-config.js +20 -0
- package/dist/src/governor/decision-engine.d.ts +7 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -1
- package/dist/src/governor/decision-engine.js +59 -1
- package/dist/src/governor/event-types.d.ts +18 -1
- package/dist/src/governor/event-types.d.ts.map +1 -1
- package/dist/src/governor/event-types.js +4 -0
- package/dist/src/governor/governor.d.ts +5 -1
- package/dist/src/governor/governor.d.ts.map +1 -1
- package/dist/src/governor/governor.js +6 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/merge-queue/adapters/github-native.d.ts +22 -0
- package/dist/src/merge-queue/adapters/github-native.d.ts.map +1 -0
- package/dist/src/merge-queue/adapters/github-native.js +243 -0
- package/dist/src/merge-queue/adapters/github-native.test.d.ts +2 -0
- package/dist/src/merge-queue/adapters/github-native.test.d.ts.map +1 -0
- package/dist/src/merge-queue/adapters/github-native.test.js +384 -0
- package/dist/src/merge-queue/index.d.ts +18 -0
- package/dist/src/merge-queue/index.d.ts.map +1 -0
- package/dist/src/merge-queue/index.js +28 -0
- package/dist/src/merge-queue/merge-queue.integration.test.d.ts +2 -0
- package/dist/src/merge-queue/merge-queue.integration.test.d.ts.map +1 -0
- package/dist/src/merge-queue/merge-queue.integration.test.js +128 -0
- package/dist/src/merge-queue/types.d.ts +48 -0
- package/dist/src/merge-queue/types.d.ts.map +1 -0
- package/dist/src/merge-queue/types.js +8 -0
- package/dist/src/orchestrator/activity-emitter.d.ts +3 -3
- package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -1
- package/dist/src/orchestrator/activity-emitter.js +1 -1
- package/dist/src/orchestrator/artifact-tracker.d.ts +93 -0
- package/dist/src/orchestrator/artifact-tracker.d.ts.map +1 -0
- package/dist/src/orchestrator/artifact-tracker.js +235 -0
- package/dist/src/orchestrator/artifact-tracker.test.d.ts +2 -0
- package/dist/src/orchestrator/artifact-tracker.test.d.ts.map +1 -0
- package/dist/src/orchestrator/artifact-tracker.test.js +189 -0
- package/dist/src/orchestrator/context-manager.d.ts +72 -0
- package/dist/src/orchestrator/context-manager.d.ts.map +1 -0
- package/dist/src/orchestrator/context-manager.js +120 -0
- package/dist/src/orchestrator/context-manager.test.d.ts +2 -0
- package/dist/src/orchestrator/context-manager.test.d.ts.map +1 -0
- package/dist/src/orchestrator/context-manager.test.js +137 -0
- package/dist/src/orchestrator/detect-work-type.test.js +25 -16
- package/dist/src/orchestrator/index.d.ts +12 -2
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +9 -1
- package/dist/src/orchestrator/issue-tracker-client.d.ts +103 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -0
- package/dist/src/orchestrator/issue-tracker-client.js +8 -0
- package/dist/src/orchestrator/log-analyzer.d.ts +19 -4
- package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -1
- package/dist/src/orchestrator/log-analyzer.js +26 -50
- package/dist/src/orchestrator/orchestrator-utils.test.js +3 -0
- package/dist/src/orchestrator/orchestrator.d.ts +16 -2
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +449 -115
- package/dist/src/orchestrator/parse-work-result.d.ts +1 -1
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
- package/dist/src/orchestrator/parse-work-result.js +1 -1
- package/dist/src/orchestrator/session-logger.d.ts +1 -1
- package/dist/src/orchestrator/session-logger.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.d.ts +22 -3
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.js +55 -2
- package/dist/src/orchestrator/state-recovery.test.js +106 -2
- package/dist/src/orchestrator/state-types.d.ts +63 -1
- package/dist/src/orchestrator/state-types.d.ts.map +1 -1
- package/dist/src/orchestrator/state-types.js +5 -1
- package/dist/src/orchestrator/summary-builder.d.ts +47 -0
- package/dist/src/orchestrator/summary-builder.d.ts.map +1 -0
- package/dist/src/orchestrator/summary-builder.js +240 -0
- package/dist/src/orchestrator/summary-builder.test.d.ts +2 -0
- package/dist/src/orchestrator/summary-builder.test.d.ts.map +1 -0
- package/dist/src/orchestrator/summary-builder.test.js +236 -0
- package/dist/src/orchestrator/types.d.ts +24 -2
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/work-types.d.ts +50 -0
- package/dist/src/orchestrator/work-types.d.ts.map +1 -0
- package/dist/src/orchestrator/work-types.js +20 -0
- package/dist/src/templates/registry.d.ts +1 -1
- package/dist/src/templates/registry.test.js +2 -2
- package/dist/src/templates/renderer.d.ts +1 -1
- package/dist/src/templates/types.d.ts +6 -2
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +2 -0
- package/dist/src/templates/types.test.js +4 -3
- package/dist/src/tools/index.d.ts +0 -3
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +0 -2
- package/dist/src/workflow/branching-router.d.ts +38 -0
- package/dist/src/workflow/branching-router.d.ts.map +1 -0
- package/dist/src/workflow/branching-router.js +52 -0
- package/dist/src/workflow/branching-router.test.d.ts +2 -0
- package/dist/src/workflow/branching-router.test.d.ts.map +1 -0
- package/dist/src/workflow/branching-router.test.js +209 -0
- package/dist/src/workflow/duration.d.ts +28 -0
- package/dist/src/workflow/duration.d.ts.map +1 -0
- package/dist/src/workflow/duration.js +57 -0
- package/dist/src/workflow/duration.test.d.ts +2 -0
- package/dist/src/workflow/duration.test.d.ts.map +1 -0
- package/dist/src/workflow/duration.test.js +74 -0
- package/dist/src/workflow/expression/ast.d.ts +53 -0
- package/dist/src/workflow/expression/ast.d.ts.map +1 -0
- package/dist/src/workflow/expression/ast.js +8 -0
- package/dist/src/workflow/expression/context.d.ts +40 -0
- package/dist/src/workflow/expression/context.d.ts.map +1 -0
- package/dist/src/workflow/expression/context.js +37 -0
- package/dist/src/workflow/expression/evaluator.d.ts +28 -0
- package/dist/src/workflow/expression/evaluator.d.ts.map +1 -0
- package/dist/src/workflow/expression/evaluator.js +165 -0
- package/dist/src/workflow/expression/evaluator.test.d.ts +2 -0
- package/dist/src/workflow/expression/evaluator.test.d.ts.map +1 -0
- package/dist/src/workflow/expression/evaluator.test.js +792 -0
- package/dist/src/workflow/expression/expression.test.d.ts +2 -0
- package/dist/src/workflow/expression/expression.test.d.ts.map +1 -0
- package/dist/src/workflow/expression/expression.test.js +516 -0
- package/dist/src/workflow/expression/helpers.d.ts +21 -0
- package/dist/src/workflow/expression/helpers.d.ts.map +1 -0
- package/dist/src/workflow/expression/helpers.js +56 -0
- package/dist/src/workflow/expression/index.d.ts +55 -0
- package/dist/src/workflow/expression/index.d.ts.map +1 -0
- package/dist/src/workflow/expression/index.js +71 -0
- package/dist/src/workflow/expression/lexer.d.ts +37 -0
- package/dist/src/workflow/expression/lexer.d.ts.map +1 -0
- package/dist/src/workflow/expression/lexer.js +166 -0
- package/dist/src/workflow/expression/parser.d.ts +23 -0
- package/dist/src/workflow/expression/parser.d.ts.map +1 -0
- package/dist/src/workflow/expression/parser.js +181 -0
- package/dist/src/workflow/index.d.ts +21 -0
- package/dist/src/workflow/index.d.ts.map +1 -0
- package/dist/src/workflow/index.js +15 -0
- package/dist/src/workflow/retry-resolver.d.ts +51 -0
- package/dist/src/workflow/retry-resolver.d.ts.map +1 -0
- package/dist/src/workflow/retry-resolver.js +70 -0
- package/dist/src/workflow/retry-resolver.test.d.ts +2 -0
- package/dist/src/workflow/retry-resolver.test.d.ts.map +1 -0
- package/dist/src/workflow/retry-resolver.test.js +149 -0
- package/dist/src/workflow/transition-engine.d.ts +46 -0
- package/dist/src/workflow/transition-engine.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.js +113 -0
- package/dist/src/workflow/transition-engine.test.d.ts +2 -0
- package/dist/src/workflow/transition-engine.test.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.test.js +425 -0
- package/dist/src/workflow/workflow-loader.d.ts +21 -0
- package/dist/src/workflow/workflow-loader.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.js +40 -0
- package/dist/src/workflow/workflow-loader.test.d.ts +2 -0
- package/dist/src/workflow/workflow-loader.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.test.js +134 -0
- package/dist/src/workflow/workflow-registry.d.ts +97 -0
- package/dist/src/workflow/workflow-registry.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.js +173 -0
- package/dist/src/workflow/workflow-registry.test.d.ts +2 -0
- package/dist/src/workflow/workflow-registry.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.test.js +201 -0
- package/dist/src/workflow/workflow-types.d.ts +442 -0
- package/dist/src/workflow/workflow-types.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.js +113 -0
- package/dist/src/workflow/workflow-types.test.d.ts +2 -0
- package/dist/src/workflow/workflow-types.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.test.js +440 -0
- package/package.json +3 -4
- package/dist/src/linear-cli.d.ts +0 -38
- package/dist/src/linear-cli.d.ts.map +0 -1
- package/dist/src/linear-cli.js +0 -674
- package/dist/src/tools/linear-runner.d.ts +0 -34
- package/dist/src/tools/linear-runner.d.ts.map +0 -1
- package/dist/src/tools/linear-runner.js +0 -700
- package/dist/src/tools/plugins/linear.d.ts +0 -9
- package/dist/src/tools/plugins/linear.d.ts.map +0 -1
- package/dist/src/tools/plugins/linear.js +0 -138
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { SUMMARY_SCHEMA_VERSION } from './state-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds and merges structured summaries from agent event spans.
|
|
4
|
+
* Implements Factory.ai-style incremental compression.
|
|
5
|
+
*
|
|
6
|
+
* Currently uses structural extraction from events.
|
|
7
|
+
* LLM-based summarization can be added in the future.
|
|
8
|
+
*/
|
|
9
|
+
export class SummaryBuilder {
|
|
10
|
+
/**
|
|
11
|
+
* Generate a structured summary from a span of agent events.
|
|
12
|
+
* Extracts file modifications, decisions, and next steps structurally.
|
|
13
|
+
*
|
|
14
|
+
* @param events - The events being dropped from context (not the full history)
|
|
15
|
+
* @param existingSummary - The current persisted summary (if any)
|
|
16
|
+
* @param artifacts - The current artifact index
|
|
17
|
+
*/
|
|
18
|
+
summarizeSpan(events, existingSummary, artifacts) {
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
// Extract file modifications from tool_use events in the span
|
|
21
|
+
const fileModifications = this.extractFileModifications(events);
|
|
22
|
+
// Extract intent from assistant text (first substantial message)
|
|
23
|
+
const sessionIntent = this.extractSessionIntent(events) || existingSummary?.sessionIntent || '';
|
|
24
|
+
// Extract next steps from assistant text
|
|
25
|
+
const nextSteps = this.extractNextSteps(events);
|
|
26
|
+
const summary = {
|
|
27
|
+
schemaVersion: SUMMARY_SCHEMA_VERSION,
|
|
28
|
+
sessionIntent,
|
|
29
|
+
fileModifications,
|
|
30
|
+
decisionsMade: [], // Decisions require LLM extraction — structural extraction not reliable
|
|
31
|
+
nextSteps,
|
|
32
|
+
compactionCount: (existingSummary?.compactionCount ?? 0) + 1,
|
|
33
|
+
lastCompactedAt: now,
|
|
34
|
+
tokenEstimate: 0,
|
|
35
|
+
};
|
|
36
|
+
// Estimate token count (rough: 4 chars per token)
|
|
37
|
+
summary.tokenEstimate = Math.ceil(JSON.stringify(summary).length / 4);
|
|
38
|
+
return summary;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Merge a newly generated span summary into the existing persisted summary.
|
|
42
|
+
* Deduplicates file modifications, preserves decision chronology,
|
|
43
|
+
* updates next steps (removing completed ones, adding new ones).
|
|
44
|
+
*/
|
|
45
|
+
mergeSummaries(existing, incoming) {
|
|
46
|
+
// Session intent: keep existing unless incoming explicitly redefines it
|
|
47
|
+
const sessionIntent = incoming.sessionIntent || existing.sessionIntent;
|
|
48
|
+
// File modifications: union by path, keep latest action per file
|
|
49
|
+
const fileMap = new Map();
|
|
50
|
+
for (const mod of existing.fileModifications) {
|
|
51
|
+
fileMap.set(mod.path, mod);
|
|
52
|
+
}
|
|
53
|
+
for (const mod of incoming.fileModifications) {
|
|
54
|
+
const existingMod = fileMap.get(mod.path);
|
|
55
|
+
if (existingMod) {
|
|
56
|
+
// Merge: keep latest action, combine reasons
|
|
57
|
+
fileMap.set(mod.path, {
|
|
58
|
+
path: mod.path,
|
|
59
|
+
action: mod.action,
|
|
60
|
+
reason: existingMod.reason !== mod.reason
|
|
61
|
+
? `${existingMod.reason}; ${mod.reason}`
|
|
62
|
+
: mod.reason,
|
|
63
|
+
lastModifiedAt: Math.max(existingMod.lastModifiedAt, mod.lastModifiedAt),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
fileMap.set(mod.path, mod);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Decisions: append chronologically, never drop
|
|
71
|
+
const decisionsMade = [
|
|
72
|
+
...existing.decisionsMade,
|
|
73
|
+
...incoming.decisionsMade,
|
|
74
|
+
];
|
|
75
|
+
// Next steps: remove completed items (ones that appear in incoming file mods),
|
|
76
|
+
// add new items from incoming
|
|
77
|
+
const completedPaths = new Set(incoming.fileModifications.map(m => m.path));
|
|
78
|
+
const existingSteps = existing.nextSteps.filter(step => !completedPaths.has(step) && !incoming.nextSteps.includes(step));
|
|
79
|
+
const nextSteps = [...existingSteps, ...incoming.nextSteps];
|
|
80
|
+
const merged = {
|
|
81
|
+
schemaVersion: SUMMARY_SCHEMA_VERSION,
|
|
82
|
+
sessionIntent,
|
|
83
|
+
fileModifications: Array.from(fileMap.values()),
|
|
84
|
+
decisionsMade,
|
|
85
|
+
nextSteps,
|
|
86
|
+
compactionCount: incoming.compactionCount,
|
|
87
|
+
lastCompactedAt: incoming.lastCompactedAt,
|
|
88
|
+
tokenEstimate: 0,
|
|
89
|
+
};
|
|
90
|
+
merged.tokenEstimate = Math.ceil(JSON.stringify(merged).length / 4);
|
|
91
|
+
return merged;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generate the context injection string from a summary + artifacts.
|
|
95
|
+
* Uses neutral framing to avoid "context anxiety" (from SUP-1186 research).
|
|
96
|
+
*/
|
|
97
|
+
toPromptSection(summary, artifacts) {
|
|
98
|
+
const lines = [];
|
|
99
|
+
lines.push('<context-summary>');
|
|
100
|
+
// Session Intent
|
|
101
|
+
if (summary.sessionIntent) {
|
|
102
|
+
lines.push('## Session Intent');
|
|
103
|
+
lines.push(summary.sessionIntent);
|
|
104
|
+
lines.push('');
|
|
105
|
+
}
|
|
106
|
+
// Files Modified
|
|
107
|
+
if (summary.fileModifications.length > 0) {
|
|
108
|
+
lines.push(`## Files Modified (${summary.fileModifications.length} files)`);
|
|
109
|
+
for (const mod of summary.fileModifications) {
|
|
110
|
+
lines.push(`- \`${mod.path}\` — ${capitalize(mod.action)}: ${mod.reason}`);
|
|
111
|
+
}
|
|
112
|
+
lines.push('');
|
|
113
|
+
}
|
|
114
|
+
// Key Decisions
|
|
115
|
+
if (summary.decisionsMade.length > 0) {
|
|
116
|
+
lines.push('## Key Decisions');
|
|
117
|
+
for (let i = 0; i < summary.decisionsMade.length; i++) {
|
|
118
|
+
const d = summary.decisionsMade[i];
|
|
119
|
+
let line = `${i + 1}. ${d.description} — ${d.rationale}`;
|
|
120
|
+
if (d.alternatives && d.alternatives.length > 0) {
|
|
121
|
+
line += ` (rejected: ${d.alternatives.join(', ')})`;
|
|
122
|
+
}
|
|
123
|
+
lines.push(line);
|
|
124
|
+
}
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
127
|
+
// Next Steps
|
|
128
|
+
if (summary.nextSteps.length > 0) {
|
|
129
|
+
lines.push('## Next Steps');
|
|
130
|
+
for (const step of summary.nextSteps) {
|
|
131
|
+
lines.push(`- [ ] ${step}`);
|
|
132
|
+
}
|
|
133
|
+
lines.push('');
|
|
134
|
+
}
|
|
135
|
+
// Tracked Files (from ArtifactTracker)
|
|
136
|
+
const trackedFiles = Object.values(artifacts.files);
|
|
137
|
+
if (trackedFiles.length > 0) {
|
|
138
|
+
const modified = trackedFiles.filter(f => f.actions.some(a => a === 'created' || a === 'modified' || a === 'deleted'));
|
|
139
|
+
const readOnly = trackedFiles.filter(f => !f.actions.some(a => a === 'created' || a === 'modified' || a === 'deleted') && f.actions.includes('read'));
|
|
140
|
+
lines.push('## Tracked Files');
|
|
141
|
+
if (modified.length > 0) {
|
|
142
|
+
lines.push(`Modified: ${modified.map(f => f.relativePath).join(', ')}`);
|
|
143
|
+
}
|
|
144
|
+
if (readOnly.length > 0) {
|
|
145
|
+
lines.push(`Read: ${readOnly.map(f => f.relativePath).join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
lines.push('');
|
|
148
|
+
}
|
|
149
|
+
lines.push('</context-summary>');
|
|
150
|
+
return lines.join('\n');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Extract file modifications from tool_use events in the span
|
|
154
|
+
*/
|
|
155
|
+
extractFileModifications(events) {
|
|
156
|
+
const fileMap = new Map();
|
|
157
|
+
for (const event of events) {
|
|
158
|
+
if (event.type !== 'tool_use')
|
|
159
|
+
continue;
|
|
160
|
+
let filePath;
|
|
161
|
+
let action;
|
|
162
|
+
switch (event.toolName) {
|
|
163
|
+
case 'Read':
|
|
164
|
+
filePath = event.input.file_path;
|
|
165
|
+
action = 'read';
|
|
166
|
+
break;
|
|
167
|
+
case 'Write':
|
|
168
|
+
filePath = event.input.file_path;
|
|
169
|
+
action = 'created';
|
|
170
|
+
break;
|
|
171
|
+
case 'Edit':
|
|
172
|
+
filePath = event.input.file_path;
|
|
173
|
+
action = 'modified';
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
if (filePath && action) {
|
|
177
|
+
const existing = fileMap.get(filePath);
|
|
178
|
+
if (existing) {
|
|
179
|
+
// Update to latest action
|
|
180
|
+
existing.action = action;
|
|
181
|
+
existing.lastModifiedAt = Date.now();
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
fileMap.set(filePath, {
|
|
185
|
+
path: filePath,
|
|
186
|
+
action,
|
|
187
|
+
reason: `${capitalize(action)} during session`,
|
|
188
|
+
lastModifiedAt: Date.now(),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return Array.from(fileMap.values());
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Extract session intent from assistant text events.
|
|
197
|
+
* Looks for the first substantial text that describes what the agent is doing.
|
|
198
|
+
*/
|
|
199
|
+
extractSessionIntent(events) {
|
|
200
|
+
for (const event of events) {
|
|
201
|
+
if (event.type !== 'assistant_text')
|
|
202
|
+
continue;
|
|
203
|
+
const text = event.text.trim();
|
|
204
|
+
// Skip very short messages (acknowledgments, etc.)
|
|
205
|
+
if (text.length > 50) {
|
|
206
|
+
// Take first sentence or first 200 chars
|
|
207
|
+
const firstSentence = text.match(/^[^.!?]+[.!?]/);
|
|
208
|
+
return firstSentence ? firstSentence[0].trim() : text.slice(0, 200);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return '';
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Extract next steps from assistant text events.
|
|
215
|
+
* Looks for bullet points, numbered lists, or "TODO" markers.
|
|
216
|
+
*/
|
|
217
|
+
extractNextSteps(events) {
|
|
218
|
+
const steps = [];
|
|
219
|
+
for (const event of events) {
|
|
220
|
+
if (event.type !== 'assistant_text')
|
|
221
|
+
continue;
|
|
222
|
+
const lines = event.text.split('\n');
|
|
223
|
+
for (const line of lines) {
|
|
224
|
+
const trimmed = line.trim();
|
|
225
|
+
// Match common todo/next step patterns
|
|
226
|
+
const match = trimmed.match(/^(?:[-*]|\d+[.)]\s*)\s*\[?\s*[x ]?\s*\]?\s*(.+)/);
|
|
227
|
+
if (match && match[1] && (trimmed.toLowerCase().includes('todo') || trimmed.toLowerCase().includes('next'))) {
|
|
228
|
+
steps.push(match[1].trim());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return steps;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Capitalize the first letter of a string
|
|
237
|
+
*/
|
|
238
|
+
function capitalize(s) {
|
|
239
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
240
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-builder.test.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/summary-builder.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { SummaryBuilder } from './summary-builder.js';
|
|
3
|
+
import { SUMMARY_SCHEMA_VERSION } from './state-types.js';
|
|
4
|
+
function makeToolUse(toolName, input) {
|
|
5
|
+
return { type: 'tool_use', toolName, input, raw: {} };
|
|
6
|
+
}
|
|
7
|
+
function makeAssistantText(text) {
|
|
8
|
+
return { type: 'assistant_text', text, raw: {} };
|
|
9
|
+
}
|
|
10
|
+
function makeEmptyArtifacts() {
|
|
11
|
+
return { files: {}, totalReads: 0, totalWrites: 0, lastUpdatedAt: 0 };
|
|
12
|
+
}
|
|
13
|
+
function makeSummary(overrides = {}) {
|
|
14
|
+
return {
|
|
15
|
+
schemaVersion: SUMMARY_SCHEMA_VERSION,
|
|
16
|
+
sessionIntent: 'Test session intent',
|
|
17
|
+
fileModifications: [],
|
|
18
|
+
decisionsMade: [],
|
|
19
|
+
nextSteps: [],
|
|
20
|
+
compactionCount: 1,
|
|
21
|
+
lastCompactedAt: 1000,
|
|
22
|
+
tokenEstimate: 100,
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
describe('SummaryBuilder — summarizeSpan', () => {
|
|
27
|
+
it('extracts file modifications from tool_use events', () => {
|
|
28
|
+
const builder = new SummaryBuilder();
|
|
29
|
+
const events = [
|
|
30
|
+
makeToolUse('Read', { file_path: '/src/foo.ts' }),
|
|
31
|
+
makeToolUse('Edit', { file_path: '/src/bar.ts' }),
|
|
32
|
+
makeToolUse('Write', { file_path: '/src/new.ts' }),
|
|
33
|
+
];
|
|
34
|
+
const summary = builder.summarizeSpan(events, null, makeEmptyArtifacts());
|
|
35
|
+
expect(summary.fileModifications).toHaveLength(3);
|
|
36
|
+
expect(summary.fileModifications.map(m => m.action)).toEqual(['read', 'modified', 'created']);
|
|
37
|
+
});
|
|
38
|
+
it('extracts session intent from assistant text', () => {
|
|
39
|
+
const builder = new SummaryBuilder();
|
|
40
|
+
const events = [
|
|
41
|
+
makeAssistantText('I will implement the authentication system for the admin dashboard. This involves creating JWT tokens and middleware.'),
|
|
42
|
+
];
|
|
43
|
+
const summary = builder.summarizeSpan(events, null, makeEmptyArtifacts());
|
|
44
|
+
expect(summary.sessionIntent).toContain('authentication system');
|
|
45
|
+
});
|
|
46
|
+
it('preserves existing session intent when no new intent found', () => {
|
|
47
|
+
const builder = new SummaryBuilder();
|
|
48
|
+
const existing = makeSummary({ sessionIntent: 'Original intent' });
|
|
49
|
+
const events = [
|
|
50
|
+
makeToolUse('Read', { file_path: '/src/foo.ts' }),
|
|
51
|
+
];
|
|
52
|
+
const summary = builder.summarizeSpan(events, existing, makeEmptyArtifacts());
|
|
53
|
+
expect(summary.sessionIntent).toBe('Original intent');
|
|
54
|
+
});
|
|
55
|
+
it('increments compaction count from existing summary', () => {
|
|
56
|
+
const builder = new SummaryBuilder();
|
|
57
|
+
const existing = makeSummary({ compactionCount: 2 });
|
|
58
|
+
const summary = builder.summarizeSpan([], existing, makeEmptyArtifacts());
|
|
59
|
+
expect(summary.compactionCount).toBe(3);
|
|
60
|
+
});
|
|
61
|
+
it('starts compaction count at 1 when no existing summary', () => {
|
|
62
|
+
const builder = new SummaryBuilder();
|
|
63
|
+
const summary = builder.summarizeSpan([], null, makeEmptyArtifacts());
|
|
64
|
+
expect(summary.compactionCount).toBe(1);
|
|
65
|
+
});
|
|
66
|
+
it('estimates token count', () => {
|
|
67
|
+
const builder = new SummaryBuilder();
|
|
68
|
+
const summary = builder.summarizeSpan([], null, makeEmptyArtifacts());
|
|
69
|
+
expect(summary.tokenEstimate).toBeGreaterThan(0);
|
|
70
|
+
});
|
|
71
|
+
it('deduplicates file modifications for same path', () => {
|
|
72
|
+
const builder = new SummaryBuilder();
|
|
73
|
+
const events = [
|
|
74
|
+
makeToolUse('Read', { file_path: '/src/foo.ts' }),
|
|
75
|
+
makeToolUse('Edit', { file_path: '/src/foo.ts' }),
|
|
76
|
+
];
|
|
77
|
+
const summary = builder.summarizeSpan(events, null, makeEmptyArtifacts());
|
|
78
|
+
expect(summary.fileModifications).toHaveLength(1);
|
|
79
|
+
expect(summary.fileModifications[0].action).toBe('modified'); // Latest action wins
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('SummaryBuilder — mergeSummaries', () => {
|
|
83
|
+
it('keeps existing session intent when incoming is empty', () => {
|
|
84
|
+
const builder = new SummaryBuilder();
|
|
85
|
+
const existing = makeSummary({ sessionIntent: 'Original intent' });
|
|
86
|
+
const incoming = makeSummary({ sessionIntent: '' });
|
|
87
|
+
const merged = builder.mergeSummaries(existing, incoming);
|
|
88
|
+
expect(merged.sessionIntent).toBe('Original intent');
|
|
89
|
+
});
|
|
90
|
+
it('uses incoming session intent when provided', () => {
|
|
91
|
+
const builder = new SummaryBuilder();
|
|
92
|
+
const existing = makeSummary({ sessionIntent: 'Old intent' });
|
|
93
|
+
const incoming = makeSummary({ sessionIntent: 'New intent' });
|
|
94
|
+
const merged = builder.mergeSummaries(existing, incoming);
|
|
95
|
+
expect(merged.sessionIntent).toBe('New intent');
|
|
96
|
+
});
|
|
97
|
+
it('unions file modifications by path', () => {
|
|
98
|
+
const builder = new SummaryBuilder();
|
|
99
|
+
const existing = makeSummary({
|
|
100
|
+
fileModifications: [
|
|
101
|
+
{ path: '/src/a.ts', action: 'read', reason: 'Initial read', lastModifiedAt: 1000 },
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
const incoming = makeSummary({
|
|
105
|
+
fileModifications: [
|
|
106
|
+
{ path: '/src/a.ts', action: 'modified', reason: 'Updated logic', lastModifiedAt: 2000 },
|
|
107
|
+
{ path: '/src/b.ts', action: 'created', reason: 'New file', lastModifiedAt: 2000 },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
const merged = builder.mergeSummaries(existing, incoming);
|
|
111
|
+
expect(merged.fileModifications).toHaveLength(2);
|
|
112
|
+
const fileA = merged.fileModifications.find(m => m.path === '/src/a.ts');
|
|
113
|
+
expect(fileA?.action).toBe('modified');
|
|
114
|
+
expect(fileA?.reason).toContain('Initial read');
|
|
115
|
+
expect(fileA?.reason).toContain('Updated logic');
|
|
116
|
+
expect(fileA?.lastModifiedAt).toBe(2000);
|
|
117
|
+
});
|
|
118
|
+
it('appends decisions chronologically', () => {
|
|
119
|
+
const builder = new SummaryBuilder();
|
|
120
|
+
const existing = makeSummary({
|
|
121
|
+
decisionsMade: [
|
|
122
|
+
{ description: 'First decision', rationale: 'Because', madeAt: 1000 },
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
const incoming = makeSummary({
|
|
126
|
+
decisionsMade: [
|
|
127
|
+
{ description: 'Second decision', rationale: 'Also because', madeAt: 2000 },
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
const merged = builder.mergeSummaries(existing, incoming);
|
|
131
|
+
expect(merged.decisionsMade).toHaveLength(2);
|
|
132
|
+
expect(merged.decisionsMade[0].description).toBe('First decision');
|
|
133
|
+
expect(merged.decisionsMade[1].description).toBe('Second decision');
|
|
134
|
+
});
|
|
135
|
+
it('merges next steps, removing duplicates', () => {
|
|
136
|
+
const builder = new SummaryBuilder();
|
|
137
|
+
const existing = makeSummary({ nextSteps: ['Step A', 'Step B'] });
|
|
138
|
+
const incoming = makeSummary({ nextSteps: ['Step C'] });
|
|
139
|
+
const merged = builder.mergeSummaries(existing, incoming);
|
|
140
|
+
expect(merged.nextSteps).toContain('Step A');
|
|
141
|
+
expect(merged.nextSteps).toContain('Step B');
|
|
142
|
+
expect(merged.nextSteps).toContain('Step C');
|
|
143
|
+
});
|
|
144
|
+
it('uses incoming compaction count', () => {
|
|
145
|
+
const builder = new SummaryBuilder();
|
|
146
|
+
const existing = makeSummary({ compactionCount: 2 });
|
|
147
|
+
const incoming = makeSummary({ compactionCount: 3 });
|
|
148
|
+
const merged = builder.mergeSummaries(existing, incoming);
|
|
149
|
+
expect(merged.compactionCount).toBe(3);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('SummaryBuilder — toPromptSection', () => {
|
|
153
|
+
it('generates context-summary tags', () => {
|
|
154
|
+
const builder = new SummaryBuilder();
|
|
155
|
+
const summary = makeSummary();
|
|
156
|
+
const output = builder.toPromptSection(summary, makeEmptyArtifacts());
|
|
157
|
+
expect(output).toContain('<context-summary>');
|
|
158
|
+
expect(output).toContain('</context-summary>');
|
|
159
|
+
});
|
|
160
|
+
it('includes session intent', () => {
|
|
161
|
+
const builder = new SummaryBuilder();
|
|
162
|
+
const summary = makeSummary({ sessionIntent: 'Implement auth flow' });
|
|
163
|
+
const output = builder.toPromptSection(summary, makeEmptyArtifacts());
|
|
164
|
+
expect(output).toContain('## Session Intent');
|
|
165
|
+
expect(output).toContain('Implement auth flow');
|
|
166
|
+
});
|
|
167
|
+
it('includes file modifications', () => {
|
|
168
|
+
const builder = new SummaryBuilder();
|
|
169
|
+
const summary = makeSummary({
|
|
170
|
+
fileModifications: [
|
|
171
|
+
{ path: 'src/auth.ts', action: 'created', reason: 'New auth module', lastModifiedAt: 1000 },
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
const output = builder.toPromptSection(summary, makeEmptyArtifacts());
|
|
175
|
+
expect(output).toContain('## Files Modified');
|
|
176
|
+
expect(output).toContain('src/auth.ts');
|
|
177
|
+
expect(output).toContain('Created');
|
|
178
|
+
});
|
|
179
|
+
it('includes decisions with alternatives', () => {
|
|
180
|
+
const builder = new SummaryBuilder();
|
|
181
|
+
const summary = makeSummary({
|
|
182
|
+
decisionsMade: [
|
|
183
|
+
{
|
|
184
|
+
description: 'Use JWT',
|
|
185
|
+
rationale: 'Stateless API',
|
|
186
|
+
alternatives: ['session cookies'],
|
|
187
|
+
madeAt: 1000,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
});
|
|
191
|
+
const output = builder.toPromptSection(summary, makeEmptyArtifacts());
|
|
192
|
+
expect(output).toContain('## Key Decisions');
|
|
193
|
+
expect(output).toContain('Use JWT');
|
|
194
|
+
expect(output).toContain('Stateless API');
|
|
195
|
+
expect(output).toContain('rejected: session cookies');
|
|
196
|
+
});
|
|
197
|
+
it('includes next steps as checklist', () => {
|
|
198
|
+
const builder = new SummaryBuilder();
|
|
199
|
+
const summary = makeSummary({ nextSteps: ['Write tests', 'Deploy'] });
|
|
200
|
+
const output = builder.toPromptSection(summary, makeEmptyArtifacts());
|
|
201
|
+
expect(output).toContain('## Next Steps');
|
|
202
|
+
expect(output).toContain('- [ ] Write tests');
|
|
203
|
+
expect(output).toContain('- [ ] Deploy');
|
|
204
|
+
});
|
|
205
|
+
it('includes tracked files from artifact index', () => {
|
|
206
|
+
const builder = new SummaryBuilder();
|
|
207
|
+
const summary = makeSummary();
|
|
208
|
+
const artifacts = {
|
|
209
|
+
files: {
|
|
210
|
+
'/abs/src/foo.ts': {
|
|
211
|
+
path: '/abs/src/foo.ts',
|
|
212
|
+
relativePath: 'src/foo.ts',
|
|
213
|
+
actions: ['modified'],
|
|
214
|
+
firstSeenAt: 1000,
|
|
215
|
+
lastTouchedAt: 2000,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
totalReads: 0,
|
|
219
|
+
totalWrites: 1,
|
|
220
|
+
lastUpdatedAt: 2000,
|
|
221
|
+
};
|
|
222
|
+
const output = builder.toPromptSection(summary, artifacts);
|
|
223
|
+
expect(output).toContain('## Tracked Files');
|
|
224
|
+
expect(output).toContain('src/foo.ts');
|
|
225
|
+
});
|
|
226
|
+
it('does not expose token counts or compaction count (context anxiety prevention)', () => {
|
|
227
|
+
const builder = new SummaryBuilder();
|
|
228
|
+
const summary = makeSummary({ compactionCount: 5, tokenEstimate: 5000 });
|
|
229
|
+
const output = builder.toPromptSection(summary, makeEmptyArtifacts());
|
|
230
|
+
expect(output).not.toContain('5000');
|
|
231
|
+
expect(output).not.toContain('compaction');
|
|
232
|
+
expect(output).not.toContain('token');
|
|
233
|
+
expect(output).not.toContain('compressed');
|
|
234
|
+
expect(output).not.toContain('limit');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Orchestrator Types
|
|
3
3
|
*/
|
|
4
|
-
import type { AgentWorkType } from '
|
|
4
|
+
import type { AgentWorkType, WorkTypeStatusMappings } from './work-types.js';
|
|
5
5
|
import type { AgentProvider, AgentProviderName } from '../providers/types.js';
|
|
6
|
+
import type { IssueTrackerClient } from './issue-tracker-client.js';
|
|
7
|
+
import type { ToolPlugin } from '../tools/types.js';
|
|
6
8
|
/**
|
|
7
9
|
* Result of parsing an agent's output to determine pass/fail
|
|
8
10
|
* Used for QA and acceptance work types to decide status transitions
|
|
@@ -26,8 +28,26 @@ export interface OrchestratorConfig {
|
|
|
26
28
|
project?: string;
|
|
27
29
|
/** Base path for git worktrees (default: .worktrees) */
|
|
28
30
|
worktreePath?: string;
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Linear API key (defaults to LINEAR_API_KEY env var).
|
|
33
|
+
* @deprecated Use issueTrackerClient instead. Kept for backwards compatibility.
|
|
34
|
+
*/
|
|
30
35
|
linearApiKey?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Platform-agnostic issue tracker client.
|
|
38
|
+
* When provided, the orchestrator uses this instead of creating a Linear client from linearApiKey.
|
|
39
|
+
*/
|
|
40
|
+
issueTrackerClient?: IssueTrackerClient;
|
|
41
|
+
/**
|
|
42
|
+
* Status mapping configuration for the issue tracker.
|
|
43
|
+
* Required when using issueTrackerClient. Defines how statuses map to work types.
|
|
44
|
+
*/
|
|
45
|
+
statusMappings?: WorkTypeStatusMappings;
|
|
46
|
+
/**
|
|
47
|
+
* Tool plugins to register with the orchestrator.
|
|
48
|
+
* These are registered in addition to any built-in plugins.
|
|
49
|
+
*/
|
|
50
|
+
toolPlugins?: ToolPlugin[];
|
|
31
51
|
/** Whether to auto-transition issue status (default: true) */
|
|
32
52
|
autoTransition?: boolean;
|
|
33
53
|
/**
|
|
@@ -93,6 +113,8 @@ export interface OrchestratorConfig {
|
|
|
93
113
|
* at startup and before spawning agents. Supports both HTTPS and SSH URL formats.
|
|
94
114
|
*/
|
|
95
115
|
repository?: string;
|
|
116
|
+
/** Merge queue adapter for automated merge operations */
|
|
117
|
+
mergeQueueAdapter?: import('../merge-queue/types.js').MergeQueueAdapter;
|
|
96
118
|
}
|
|
97
119
|
export interface OrchestratorIssue {
|
|
98
120
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform-Agnostic Work Types
|
|
3
|
+
*
|
|
4
|
+
* Core-owned types for agent work type routing, status mappings,
|
|
5
|
+
* and environment issue tracking. These replace direct imports from
|
|
6
|
+
* the Linear package, enabling core to remain platform-independent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Agent work type — determines prompt template, status transitions,
|
|
10
|
+
* and whether a git worktree is needed.
|
|
11
|
+
*/
|
|
12
|
+
export type AgentWorkType = 'research' | 'backlog-creation' | 'development' | 'inflight' | 'inflight-coordination' | 'qa' | 'acceptance' | 'refinement' | 'refinement-coordination' | 'coordination' | 'qa-coordination' | 'acceptance-coordination' | 'merge';
|
|
13
|
+
/**
|
|
14
|
+
* Workflow status — platform-agnostic string (e.g., 'Started', 'Finished').
|
|
15
|
+
* Concrete values are defined by the issue tracker plugin.
|
|
16
|
+
*/
|
|
17
|
+
export type WorkflowStatus = string;
|
|
18
|
+
/**
|
|
19
|
+
* Status mapping configuration injected by the issue tracker plugin.
|
|
20
|
+
* Maps between issue statuses and agent work types.
|
|
21
|
+
*/
|
|
22
|
+
export interface WorkTypeStatusMappings {
|
|
23
|
+
/** Map issue status name → work type (e.g., 'Backlog' → 'development') */
|
|
24
|
+
statusToWorkType: Record<string, AgentWorkType>;
|
|
25
|
+
/** Map work type → status to set when agent starts (null = no transition) */
|
|
26
|
+
workTypeStartStatus: Record<AgentWorkType, string | null>;
|
|
27
|
+
/** Map work type → status to set when agent completes successfully */
|
|
28
|
+
workTypeCompleteStatus: Record<AgentWorkType, string | null>;
|
|
29
|
+
/** Map work type → status to set when agent fails */
|
|
30
|
+
workTypeFailStatus: Record<AgentWorkType, string | null>;
|
|
31
|
+
/** Statuses that indicate an issue is terminal (no further work) */
|
|
32
|
+
terminalStatuses: readonly string[];
|
|
33
|
+
/** Work types that require a git worktree */
|
|
34
|
+
workTypesRequiringWorktree: ReadonlySet<AgentWorkType>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Environment issue type constants for categorizing agent environment problems.
|
|
38
|
+
*/
|
|
39
|
+
export declare const ENVIRONMENT_ISSUE_TYPES: {
|
|
40
|
+
readonly PERMISSION: "permission";
|
|
41
|
+
readonly NETWORK: "network";
|
|
42
|
+
readonly SANDBOX: "sandbox";
|
|
43
|
+
readonly LINEAR_CLI: "linear-cli";
|
|
44
|
+
readonly DEPENDENCY: "dependency";
|
|
45
|
+
readonly TIMEOUT: "timeout";
|
|
46
|
+
readonly TOOL: "tool";
|
|
47
|
+
readonly HUMAN_BLOCKER: "human-blocker";
|
|
48
|
+
};
|
|
49
|
+
export type EnvironmentIssueType = (typeof ENVIRONMENT_ISSUE_TYPES)[keyof typeof ENVIRONMENT_ISSUE_TYPES];
|
|
50
|
+
//# sourceMappingURL=work-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-types.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/work-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,kBAAkB,GAClB,aAAa,GACb,UAAU,GACV,uBAAuB,GACvB,IAAI,GACJ,YAAY,GACZ,YAAY,GACZ,yBAAyB,GACzB,cAAc,GACd,iBAAiB,GACjB,yBAAyB,GACzB,OAAO,CAAA;AAEX;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAA;AAEnC;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAC/C,6EAA6E;IAC7E,mBAAmB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;IACzD,sEAAsE;IACtE,sBAAsB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;IAC5D,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;IACxD,oEAAoE;IACpE,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,6CAA6C;IAC7C,0BAA0B,EAAE,WAAW,CAAC,aAAa,CAAC,CAAA;CACvD;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;CAS1B,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,OAAO,uBAAuB,CAAC,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform-Agnostic Work Types
|
|
3
|
+
*
|
|
4
|
+
* Core-owned types for agent work type routing, status mappings,
|
|
5
|
+
* and environment issue tracking. These replace direct imports from
|
|
6
|
+
* the Linear package, enabling core to remain platform-independent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Environment issue type constants for categorizing agent environment problems.
|
|
10
|
+
*/
|
|
11
|
+
export const ENVIRONMENT_ISSUE_TYPES = {
|
|
12
|
+
PERMISSION: 'permission',
|
|
13
|
+
NETWORK: 'network',
|
|
14
|
+
SANDBOX: 'sandbox',
|
|
15
|
+
LINEAR_CLI: 'linear-cli',
|
|
16
|
+
DEPENDENCY: 'dependency',
|
|
17
|
+
TIMEOUT: 'timeout',
|
|
18
|
+
TOOL: 'tool',
|
|
19
|
+
HUMAN_BLOCKER: 'human-blocker',
|
|
20
|
+
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Supports layered resolution with built-in defaults, project overrides,
|
|
6
6
|
* and inline config overrides.
|
|
7
7
|
*/
|
|
8
|
-
import type { AgentWorkType } from '
|
|
8
|
+
import type { AgentWorkType } from '../orchestrator/work-types.js';
|
|
9
9
|
import type { WorkflowTemplate, TemplateContext, TemplateRegistryConfig, ToolPermission, ToolPermissionAdapter } from './types.js';
|
|
10
10
|
/**
|
|
11
11
|
* Template Registry manages workflow templates and renders prompts.
|
|
@@ -127,9 +127,9 @@ describe('TemplateRegistry', () => {
|
|
|
127
127
|
describe('built-in defaults', () => {
|
|
128
128
|
it('loads built-in default templates when useBuiltinDefaults is true', () => {
|
|
129
129
|
const fullRegistry = TemplateRegistry.create({ useBuiltinDefaults: true });
|
|
130
|
-
// Should have loaded templates for all
|
|
130
|
+
// Should have loaded templates for all 13 base work types + 5 strategy templates
|
|
131
131
|
const workTypes = fullRegistry.getRegisteredWorkTypes();
|
|
132
|
-
expect(workTypes.length).toBe(
|
|
132
|
+
expect(workTypes.length).toBe(18);
|
|
133
133
|
expect(workTypes).toContain('development');
|
|
134
134
|
expect(workTypes).toContain('qa');
|
|
135
135
|
expect(workTypes).toContain('coordination');
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* functions. Provides a drop-in replacement for generatePromptForWorkType
|
|
6
6
|
* that uses templates when available and falls back to hardcoded prompts.
|
|
7
7
|
*/
|
|
8
|
-
import type { AgentWorkType } from '
|
|
8
|
+
import type { AgentWorkType } from '../orchestrator/work-types.js';
|
|
9
9
|
import type { TemplateContext } from './types.js';
|
|
10
10
|
import type { TemplateRegistry } from './registry.js';
|
|
11
11
|
/**
|