astrocode-workflow 0.1.24 → 0.1.28
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/hooks/continuation-enforcer.js +2 -2
- package/dist/index.js +0 -1
- package/dist/tools/stage.js +0 -5
- package/dist/tools/story.js +0 -2
- package/dist/tools/workflow.js +22 -8
- package/dist/ui/inject.d.ts +1 -0
- package/dist/ui/inject.js +5 -3
- package/dist/workflow/state-machine.js +0 -5
- package/package.json +1 -1
- package/src/hooks/continuation-enforcer.ts +2 -2
- package/src/index.ts +0 -2
- package/src/tools/stage.ts +0 -5
- package/src/tools/story.ts +0 -3
- package/src/tools/workflow.ts +28 -10
- package/src/ui/inject.ts +7 -4
- package/src/workflow/state-machine.ts +0 -5
|
@@ -117,12 +117,12 @@ export function createContinuationEnforcer(opts) {
|
|
|
117
117
|
await recordContinuation(sessionId, active.run_id, directive, reason);
|
|
118
118
|
// Injection mode
|
|
119
119
|
if (config.continuation.injection_mode === "visible") {
|
|
120
|
-
await injectChatPrompt({ ctx, sessionId, text: directive.body });
|
|
120
|
+
await injectChatPrompt({ ctx, sessionId, text: directive.body, agent: "Astro" });
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
123
123
|
// Silent mode is TODO: requires experimental.chat.messages.transform.
|
|
124
124
|
// For v2-alpha, we fall back to visible injection but mark it.
|
|
125
|
-
await injectChatPrompt({ ctx, sessionId, text: directive.body + "\n\n(Injected in silent mode fallback)" });
|
|
125
|
+
await injectChatPrompt({ ctx, sessionId, text: directive.body + "\n\n(Injected in silent mode fallback)", agent: "Astro" });
|
|
126
126
|
}
|
|
127
127
|
if (config.ui.toasts.enabled && config.ui.toasts.show_auto_continue) {
|
|
128
128
|
await toasts.show({ title: "Astrocode", message: "Continue directive injected", variant: "info" });
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,6 @@ import { createContinuationEnforcer } from "./hooks/continuation-enforcer";
|
|
|
7
7
|
import { createToolOutputTruncatorHook } from "./hooks/tool-output-truncator";
|
|
8
8
|
import { createToastManager } from "./ui/toasts";
|
|
9
9
|
import { createAstroAgents } from "./agents/registry";
|
|
10
|
-
console.log("Astrocode plugin loading...");
|
|
11
10
|
const Astrocode = async (ctx) => {
|
|
12
11
|
const repoRoot = ctx.directory;
|
|
13
12
|
// Always load config first - this provides defaults even in limited mode
|
package/dist/tools/stage.js
CHANGED
|
@@ -27,7 +27,6 @@ function splitTasksIntoStories(db, tasks, run, now, newStoryKeys, relationReason
|
|
|
27
27
|
const subtasks = task.subtasks ?? [];
|
|
28
28
|
if (subtasks.length > 0) {
|
|
29
29
|
// Split into subtasks
|
|
30
|
-
console.log(`[Astrocode] Splitting task "${task.title}" into ${subtasks.length} subtasks`);
|
|
31
30
|
for (const subtask of subtasks) {
|
|
32
31
|
const key = insertStory(db, {
|
|
33
32
|
title: `${task.title}: ${subtask}`,
|
|
@@ -37,13 +36,11 @@ function splitTasksIntoStories(db, tasks, run, now, newStoryKeys, relationReason
|
|
|
37
36
|
epic_key: run.story_key
|
|
38
37
|
});
|
|
39
38
|
newStoryKeys.push(key);
|
|
40
|
-
console.log(`[Astrocode] Created story ${key} for subtask`);
|
|
41
39
|
db.prepare("INSERT INTO story_relations (parent_story_key, child_story_key, relation_type, reason, created_at) VALUES (?, ?, ?, ?, ?)").run(run.story_key, key, "split", relationReason, now);
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
else if (complexity > 6) {
|
|
45
43
|
// Split complex tasks
|
|
46
|
-
console.log(`[Astrocode] Splitting complex task "${task.title}" (complexity ${complexity})`);
|
|
47
44
|
const key = insertStory(db, {
|
|
48
45
|
title: task.title,
|
|
49
46
|
body_md: task.description ?? "",
|
|
@@ -52,7 +49,6 @@ function splitTasksIntoStories(db, tasks, run, now, newStoryKeys, relationReason
|
|
|
52
49
|
epic_key: run.story_key
|
|
53
50
|
});
|
|
54
51
|
newStoryKeys.push(key);
|
|
55
|
-
console.log(`[Astrocode] Created story ${key} for complex task`);
|
|
56
52
|
db.prepare("INSERT INTO story_relations (parent_story_key, child_story_key, relation_type, reason, created_at) VALUES (?, ?, ?, ?, ?)").run(run.story_key, key, "split", relationReason, now);
|
|
57
53
|
}
|
|
58
54
|
}
|
|
@@ -233,7 +229,6 @@ Ensure JSON has required fields (stage_key, status) and valid syntax.`;
|
|
|
233
229
|
}
|
|
234
230
|
// Automatic story splitting for complex tasks
|
|
235
231
|
if (allow_new_stories && parsed.astro_json.tasks?.length) {
|
|
236
|
-
console.log(`[Astrocode] Splitting ${parsed.astro_json.tasks.length} tasks into stories`);
|
|
237
232
|
splitTasksIntoStories(db, parsed.astro_json.tasks, run, now, newStoryKeys, "split from stage output");
|
|
238
233
|
}
|
|
239
234
|
// Skip spec stage if spec already exists
|
package/dist/tools/story.js
CHANGED
|
@@ -13,10 +13,8 @@ export function createAstroStoryQueueTool(opts) {
|
|
|
13
13
|
priority: tool.schema.number().int().default(0),
|
|
14
14
|
},
|
|
15
15
|
execute: async ({ title, body_md, epic_key, priority }) => {
|
|
16
|
-
console.log(`[Astrocode] Creating story: ${title}`);
|
|
17
16
|
const story_key = withTx(db, () => {
|
|
18
17
|
const key = insertStory(db, { title, body_md, epic_key: epic_key ?? null, priority: priority ?? 0, state: 'queued' });
|
|
19
|
-
console.log(`[Astrocode] Inserted story ${key} into DB`);
|
|
20
18
|
return key;
|
|
21
19
|
});
|
|
22
20
|
return `✅ Queued story ${story_key}: ${title}`;
|
package/dist/tools/workflow.js
CHANGED
|
@@ -122,6 +122,26 @@ export function createAstroWorkflowProceedTool(opts) {
|
|
|
122
122
|
if (config.ui.toasts.enabled && config.ui.toasts.show_run_completed) {
|
|
123
123
|
await toasts.show({ title: "Astrocode", message: `Run completed (${next.run_id})`, variant: "success" });
|
|
124
124
|
}
|
|
125
|
+
// Check for next approved story to start
|
|
126
|
+
const nextStory = db.prepare("SELECT story_key, title FROM stories WHERE state = 'approved' ORDER BY priority DESC, created_at ASC LIMIT 1").get();
|
|
127
|
+
if (nextStory && sessionId) {
|
|
128
|
+
const nextDirective = [
|
|
129
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — START_NEXT_STORY]`,
|
|
130
|
+
``,
|
|
131
|
+
`The previous run completed successfully. Start the next approved story.`,
|
|
132
|
+
``,
|
|
133
|
+
`Next Story: ${nextStory.story_key} — ${nextStory.title}`,
|
|
134
|
+
``,
|
|
135
|
+
`Action: Call astro_story_approve with story_key="${nextStory.story_key}" to start it, or select a different story.`,
|
|
136
|
+
].join("\n");
|
|
137
|
+
await injectChatPrompt({
|
|
138
|
+
ctx,
|
|
139
|
+
sessionId,
|
|
140
|
+
text: nextDirective,
|
|
141
|
+
agent: "Astro"
|
|
142
|
+
});
|
|
143
|
+
actions.push(`injected directive to start next story ${nextStory.story_key}`);
|
|
144
|
+
}
|
|
125
145
|
if (mode === "step")
|
|
126
146
|
break;
|
|
127
147
|
continue;
|
|
@@ -134,23 +154,18 @@ export function createAstroWorkflowProceedTool(opts) {
|
|
|
134
154
|
const story = db.prepare("SELECT * FROM stories WHERE story_key=?").get(run.story_key);
|
|
135
155
|
// Mark stage started + set subagent_type to the stage agent.
|
|
136
156
|
let agentName = resolveAgentName(next.stage_key, config);
|
|
137
|
-
console.log(`[Astrocode] Resolving agent for ${next.stage_key}: ${agentName}`);
|
|
138
|
-
console.log(`[Astrocode] Available agents:`, Object.keys(config.agent || {}));
|
|
139
157
|
// Validate agent availability with fallback chain
|
|
140
158
|
const systemConfig = config;
|
|
141
159
|
// Check both the system config agent map (if present) OR the local agents map passed to the tool
|
|
142
160
|
const agentExists = (name) => {
|
|
143
161
|
// Check local agents map first (populated from src/index.ts)
|
|
144
162
|
if (agents && agents[name]) {
|
|
145
|
-
console.log(`[Astrocode] Found agent ${name} in local agents map`);
|
|
146
163
|
return true;
|
|
147
164
|
}
|
|
148
165
|
// Check system config agent map
|
|
149
166
|
if (systemConfig.agent && systemConfig.agent[name]) {
|
|
150
|
-
console.log(`[Astrocode] Found agent ${name} in systemConfig.agent`);
|
|
151
167
|
return true;
|
|
152
168
|
}
|
|
153
|
-
console.log(`[Astrocode] Agent ${name} NOT found in local map (keys: ${Object.keys(agents || {}).join(",")}) or systemConfig`);
|
|
154
169
|
return false;
|
|
155
170
|
};
|
|
156
171
|
if (!agentExists(agentName)) {
|
|
@@ -162,7 +177,6 @@ export function createAstroWorkflowProceedTool(opts) {
|
|
|
162
177
|
throw new Error(`Critical: No agents available for delegation. Primary: ${resolveAgentName(next.stage_key, config)}, Orchestrator: ${agentName}`);
|
|
163
178
|
}
|
|
164
179
|
}
|
|
165
|
-
console.log(`[Astrocode] Delegating stage ${next.stage_key} to agent: ${agentName}`);
|
|
166
180
|
withTx(db, () => {
|
|
167
181
|
startStage(db, active.run_id, next.stage_key, { subagent_type: agentName });
|
|
168
182
|
});
|
|
@@ -195,7 +209,7 @@ export function createAstroWorkflowProceedTool(opts) {
|
|
|
195
209
|
}
|
|
196
210
|
// Visible injection so user can see state
|
|
197
211
|
if (sessionId) {
|
|
198
|
-
await injectChatPrompt({ ctx, sessionId, text: delegatePrompt });
|
|
212
|
+
await injectChatPrompt({ ctx, sessionId, text: delegatePrompt, agent: "Astro" });
|
|
199
213
|
}
|
|
200
214
|
actions.push(`delegated stage ${next.stage_key} via ${agentName}`);
|
|
201
215
|
// Stop here; subagent needs to run.
|
|
@@ -218,7 +232,7 @@ export function createAstroWorkflowProceedTool(opts) {
|
|
|
218
232
|
const h = directiveHash(prompt);
|
|
219
233
|
const now = nowISO();
|
|
220
234
|
db.prepare("INSERT INTO continuations (session_id, run_id, directive_hash, kind, reason, created_at) VALUES (?, ?, ?, 'continue', ?, ?)").run(sessionId, next.run_id, h, `await ${next.stage_key}`, now);
|
|
221
|
-
await injectChatPrompt({ ctx, sessionId, text: prompt });
|
|
235
|
+
await injectChatPrompt({ ctx, sessionId, text: prompt, agent: "Astro" });
|
|
222
236
|
}
|
|
223
237
|
break;
|
|
224
238
|
}
|
package/dist/ui/inject.d.ts
CHANGED
package/dist/ui/inject.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
let isInjecting = false;
|
|
2
2
|
let queuedInjection = null;
|
|
3
3
|
export async function injectChatPrompt(opts) {
|
|
4
|
-
const { ctx, sessionId, text } = opts;
|
|
4
|
+
const { ctx, sessionId, text, agent = "Astro" } = opts;
|
|
5
5
|
// Prefix to clearly indicate source as Astro agent
|
|
6
|
-
const prefixedText = `[
|
|
6
|
+
const prefixedText = `[${agent}]\n\n${text}`;
|
|
7
7
|
if (isInjecting) {
|
|
8
8
|
// Replace any existing queued injection (keep only latest)
|
|
9
|
-
queuedInjection = { ...opts, text: prefixedText };
|
|
9
|
+
queuedInjection = { ...opts, text: prefixedText, agent };
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
isInjecting = true;
|
|
@@ -15,6 +15,8 @@ export async function injectChatPrompt(opts) {
|
|
|
15
15
|
path: { id: sessionId },
|
|
16
16
|
body: {
|
|
17
17
|
parts: [{ type: "text", text: prefixedText }],
|
|
18
|
+
// Pass agent context for systems that support it
|
|
19
|
+
agent: agent,
|
|
18
20
|
},
|
|
19
21
|
});
|
|
20
22
|
}
|
|
@@ -52,7 +52,6 @@ function isInitialStory(db, storyKey) {
|
|
|
52
52
|
return relations.count === 0;
|
|
53
53
|
}
|
|
54
54
|
export function createRunForStory(db, config, storyKey) {
|
|
55
|
-
console.log(`[Astrocode] Creating run for story: ${storyKey}`);
|
|
56
55
|
const story = getStory(db, storyKey);
|
|
57
56
|
if (!story)
|
|
58
57
|
throw new Error(`Story not found: ${storyKey}`);
|
|
@@ -61,21 +60,17 @@ export function createRunForStory(db, config, storyKey) {
|
|
|
61
60
|
const run_id = newRunId();
|
|
62
61
|
const now = nowISO();
|
|
63
62
|
const pipeline = config.workflow.pipeline;
|
|
64
|
-
console.log(`[Astrocode] Generated run ID: ${run_id}`);
|
|
65
63
|
// Convert to genesis planning story if needed
|
|
66
64
|
const isGenesisCandidate = storyKey === 'S-0001' || isInitialStory(db, storyKey) ||
|
|
67
65
|
(story.body_md && story.body_md.length > 100 &&
|
|
68
66
|
(story.title.toLowerCase().includes('implement') || story.body_md.toLowerCase().includes('implement')));
|
|
69
67
|
if (isGenesisCandidate) {
|
|
70
|
-
console.log(`[Astrocode] Converting story ${storyKey} to genesis planning story`);
|
|
71
68
|
const planningTitle = `Plan and decompose: ${story.title}`;
|
|
72
69
|
const planningBody = `Analyze the requirements and break down "${story.title}" into 50-200 detailed, granular implementation stories. Each story should be focused on a specific, implementable task with clear acceptance criteria.\n\nOriginal request: ${story.body_md || ''}`;
|
|
73
70
|
db.prepare("UPDATE stories SET title=?, body_md=? WHERE story_key=?").run(planningTitle, planningBody, storyKey);
|
|
74
71
|
}
|
|
75
72
|
// Lock story
|
|
76
|
-
console.log(`[Astrocode] Locking story ${storyKey} for run ${run_id}`);
|
|
77
73
|
db.prepare("UPDATE stories SET state='in_progress', in_progress=1, locked_by_run_id=?, locked_at=?, updated_at=? WHERE story_key=?").run(run_id, now, now, storyKey);
|
|
78
|
-
console.log(`[Astrocode] Inserting run ${run_id} into DB`);
|
|
79
74
|
db.prepare("INSERT INTO runs (run_id, story_key, status, pipeline_stages_json, current_stage_key, created_at, started_at, updated_at) VALUES (?, ?, 'running', ?, ?, ?, ?, ?)").run(run_id, storyKey, JSON.stringify(pipeline), pipeline[0] ?? null, now, now, now);
|
|
80
75
|
// Stage runs
|
|
81
76
|
const insertStage = db.prepare("INSERT INTO stage_runs (stage_run_id, run_id, stage_key, stage_index, status, updated_at) VALUES (?, ?, ?, ?, 'pending', ?)");
|
package/package.json
CHANGED
|
@@ -166,11 +166,11 @@ export function createContinuationEnforcer(opts: {
|
|
|
166
166
|
|
|
167
167
|
// Injection mode
|
|
168
168
|
if (config.continuation.injection_mode === "visible") {
|
|
169
|
-
await injectChatPrompt({ ctx, sessionId, text: directive.body });
|
|
169
|
+
await injectChatPrompt({ ctx, sessionId, text: directive.body, agent: "Astro" });
|
|
170
170
|
} else {
|
|
171
171
|
// Silent mode is TODO: requires experimental.chat.messages.transform.
|
|
172
172
|
// For v2-alpha, we fall back to visible injection but mark it.
|
|
173
|
-
await injectChatPrompt({ ctx, sessionId, text: directive.body + "\n\n(Injected in silent mode fallback)" });
|
|
173
|
+
await injectChatPrompt({ ctx, sessionId, text: directive.body + "\n\n(Injected in silent mode fallback)", agent: "Astro" });
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
if (config.ui.toasts.enabled && config.ui.toasts.show_auto_continue) {
|
package/src/index.ts
CHANGED
|
@@ -10,8 +10,6 @@ import { createToastManager } from "./ui/toasts";
|
|
|
10
10
|
import { createAstroAgents } from "./agents/registry";
|
|
11
11
|
import { info, warn } from "./shared/log";
|
|
12
12
|
|
|
13
|
-
console.log("Astrocode plugin loading...");
|
|
14
|
-
|
|
15
13
|
const Astrocode: Plugin = async (ctx) => {
|
|
16
14
|
const repoRoot = ctx.directory as string;
|
|
17
15
|
|
package/src/tools/stage.ts
CHANGED
|
@@ -39,7 +39,6 @@ function splitTasksIntoStories(
|
|
|
39
39
|
const subtasks = task.subtasks ?? [];
|
|
40
40
|
if (subtasks.length > 0) {
|
|
41
41
|
// Split into subtasks
|
|
42
|
-
console.log(`[Astrocode] Splitting task "${task.title}" into ${subtasks.length} subtasks`);
|
|
43
42
|
for (const subtask of subtasks) {
|
|
44
43
|
const key = insertStory(db, {
|
|
45
44
|
title: `${task.title}: ${subtask}`,
|
|
@@ -49,7 +48,6 @@ function splitTasksIntoStories(
|
|
|
49
48
|
epic_key: run.story_key
|
|
50
49
|
});
|
|
51
50
|
newStoryKeys.push(key);
|
|
52
|
-
console.log(`[Astrocode] Created story ${key} for subtask`);
|
|
53
51
|
db.prepare(
|
|
54
52
|
"INSERT INTO story_relations (parent_story_key, child_story_key, relation_type, reason, created_at) VALUES (?, ?, ?, ?, ?)"
|
|
55
53
|
).run(
|
|
@@ -62,7 +60,6 @@ function splitTasksIntoStories(
|
|
|
62
60
|
}
|
|
63
61
|
} else if (complexity > 6) {
|
|
64
62
|
// Split complex tasks
|
|
65
|
-
console.log(`[Astrocode] Splitting complex task "${task.title}" (complexity ${complexity})`);
|
|
66
63
|
const key = insertStory(db, {
|
|
67
64
|
title: task.title,
|
|
68
65
|
body_md: task.description ?? "",
|
|
@@ -71,7 +68,6 @@ function splitTasksIntoStories(
|
|
|
71
68
|
epic_key: run.story_key
|
|
72
69
|
});
|
|
73
70
|
newStoryKeys.push(key);
|
|
74
|
-
console.log(`[Astrocode] Created story ${key} for complex task`);
|
|
75
71
|
db.prepare(
|
|
76
72
|
"INSERT INTO story_relations (parent_story_key, child_story_key, relation_type, reason, created_at) VALUES (?, ?, ?, ?, ?)"
|
|
77
73
|
).run(
|
|
@@ -313,7 +309,6 @@ Ensure JSON has required fields (stage_key, status) and valid syntax.`;
|
|
|
313
309
|
|
|
314
310
|
// Automatic story splitting for complex tasks
|
|
315
311
|
if (allow_new_stories && parsed.astro_json.tasks?.length) {
|
|
316
|
-
console.log(`[Astrocode] Splitting ${parsed.astro_json.tasks.length} tasks into stories`);
|
|
317
312
|
splitTasksIntoStories(db, parsed.astro_json.tasks, run, now, newStoryKeys, "split from stage output");
|
|
318
313
|
}
|
|
319
314
|
|
package/src/tools/story.ts
CHANGED
|
@@ -20,11 +20,8 @@ export function createAstroStoryQueueTool(opts: { ctx: any; config: AstrocodeCon
|
|
|
20
20
|
priority: tool.schema.number().int().default(0),
|
|
21
21
|
},
|
|
22
22
|
execute: async ({ title, body_md, epic_key, priority }) => {
|
|
23
|
-
console.log(`[Astrocode] Creating story: ${title}`);
|
|
24
|
-
|
|
25
23
|
const story_key = withTx(db, () => {
|
|
26
24
|
const key = insertStory(db, { title, body_md, epic_key: epic_key ?? null, priority: priority ?? 0, state: 'queued' });
|
|
27
|
-
console.log(`[Astrocode] Inserted story ${key} into DB`);
|
|
28
25
|
return key;
|
|
29
26
|
});
|
|
30
27
|
|
package/src/tools/workflow.ts
CHANGED
|
@@ -153,6 +153,32 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
|
|
|
153
153
|
await toasts.show({ title: "Astrocode", message: `Run completed (${next.run_id})`, variant: "success" });
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// Check for next approved story to start
|
|
157
|
+
const nextStory = db.prepare(
|
|
158
|
+
"SELECT story_key, title FROM stories WHERE state = 'approved' ORDER BY priority DESC, created_at ASC LIMIT 1"
|
|
159
|
+
).get() as { story_key: string; title: string } | undefined;
|
|
160
|
+
|
|
161
|
+
if (nextStory && sessionId) {
|
|
162
|
+
const nextDirective = [
|
|
163
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — START_NEXT_STORY]`,
|
|
164
|
+
``,
|
|
165
|
+
`The previous run completed successfully. Start the next approved story.`,
|
|
166
|
+
``,
|
|
167
|
+
`Next Story: ${nextStory.story_key} — ${nextStory.title}`,
|
|
168
|
+
``,
|
|
169
|
+
`Action: Call astro_story_approve with story_key="${nextStory.story_key}" to start it, or select a different story.`,
|
|
170
|
+
].join("\n");
|
|
171
|
+
|
|
172
|
+
await injectChatPrompt({
|
|
173
|
+
ctx,
|
|
174
|
+
sessionId,
|
|
175
|
+
text: nextDirective,
|
|
176
|
+
agent: "Astro"
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
actions.push(`injected directive to start next story ${nextStory.story_key}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
156
182
|
if (mode === "step") break;
|
|
157
183
|
continue;
|
|
158
184
|
}
|
|
@@ -166,24 +192,18 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
|
|
|
166
192
|
// Mark stage started + set subagent_type to the stage agent.
|
|
167
193
|
let agentName = resolveAgentName(next.stage_key, config);
|
|
168
194
|
|
|
169
|
-
console.log(`[Astrocode] Resolving agent for ${next.stage_key}: ${agentName}`);
|
|
170
|
-
console.log(`[Astrocode] Available agents:`, Object.keys((config as any).agent || {}));
|
|
171
|
-
|
|
172
195
|
// Validate agent availability with fallback chain
|
|
173
196
|
const systemConfig = config as any;
|
|
174
197
|
// Check both the system config agent map (if present) OR the local agents map passed to the tool
|
|
175
198
|
const agentExists = (name: string) => {
|
|
176
199
|
// Check local agents map first (populated from src/index.ts)
|
|
177
200
|
if (agents && agents[name]) {
|
|
178
|
-
console.log(`[Astrocode] Found agent ${name} in local agents map`);
|
|
179
201
|
return true;
|
|
180
202
|
}
|
|
181
203
|
// Check system config agent map
|
|
182
204
|
if (systemConfig.agent && systemConfig.agent[name]) {
|
|
183
|
-
console.log(`[Astrocode] Found agent ${name} in systemConfig.agent`);
|
|
184
205
|
return true;
|
|
185
206
|
}
|
|
186
|
-
console.log(`[Astrocode] Agent ${name} NOT found in local map (keys: ${Object.keys(agents || {}).join(",")}) or systemConfig`);
|
|
187
207
|
return false;
|
|
188
208
|
};
|
|
189
209
|
|
|
@@ -197,8 +217,6 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
|
|
|
197
217
|
}
|
|
198
218
|
}
|
|
199
219
|
|
|
200
|
-
console.log(`[Astrocode] Delegating stage ${next.stage_key} to agent: ${agentName}`);
|
|
201
|
-
|
|
202
220
|
withTx(db, () => {
|
|
203
221
|
startStage(db, active.run_id, next.stage_key, { subagent_type: agentName });
|
|
204
222
|
});
|
|
@@ -239,7 +257,7 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
|
|
|
239
257
|
|
|
240
258
|
// Visible injection so user can see state
|
|
241
259
|
if (sessionId) {
|
|
242
|
-
await injectChatPrompt({ ctx, sessionId, text: delegatePrompt });
|
|
260
|
+
await injectChatPrompt({ ctx, sessionId, text: delegatePrompt, agent: "Astro" });
|
|
243
261
|
}
|
|
244
262
|
|
|
245
263
|
actions.push(`delegated stage ${next.stage_key} via ${agentName}`);
|
|
@@ -268,7 +286,7 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
|
|
|
268
286
|
"INSERT INTO continuations (session_id, run_id, directive_hash, kind, reason, created_at) VALUES (?, ?, ?, 'continue', ?, ?)"
|
|
269
287
|
).run(sessionId, next.run_id, h, `await ${next.stage_key}`, now);
|
|
270
288
|
|
|
271
|
-
await injectChatPrompt({ ctx, sessionId, text: prompt });
|
|
289
|
+
await injectChatPrompt({ ctx, sessionId, text: prompt, agent: "Astro" });
|
|
272
290
|
}
|
|
273
291
|
break;
|
|
274
292
|
}
|
package/src/ui/inject.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
let isInjecting = false;
|
|
2
|
-
let queuedInjection: { ctx: any; sessionId: string; text: string } | null = null;
|
|
2
|
+
let queuedInjection: { ctx: any; sessionId: string; text: string; agent?: string } | null = null;
|
|
3
3
|
|
|
4
4
|
export async function injectChatPrompt(opts: {
|
|
5
5
|
ctx: any;
|
|
6
6
|
sessionId: string;
|
|
7
7
|
text: string;
|
|
8
|
+
agent?: string;
|
|
8
9
|
}) {
|
|
9
|
-
const { ctx, sessionId, text } = opts;
|
|
10
|
+
const { ctx, sessionId, text, agent = "Astro" } = opts;
|
|
10
11
|
|
|
11
12
|
// Prefix to clearly indicate source as Astro agent
|
|
12
|
-
const prefixedText = `[
|
|
13
|
+
const prefixedText = `[${agent}]\n\n${text}`;
|
|
13
14
|
|
|
14
15
|
if (isInjecting) {
|
|
15
16
|
// Replace any existing queued injection (keep only latest)
|
|
16
|
-
queuedInjection = { ...opts, text: prefixedText };
|
|
17
|
+
queuedInjection = { ...opts, text: prefixedText, agent };
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -24,6 +25,8 @@ export async function injectChatPrompt(opts: {
|
|
|
24
25
|
path: { id: sessionId },
|
|
25
26
|
body: {
|
|
26
27
|
parts: [{ type: "text", text: prefixedText }],
|
|
28
|
+
// Pass agent context for systems that support it
|
|
29
|
+
agent: agent,
|
|
27
30
|
},
|
|
28
31
|
});
|
|
29
32
|
} finally {
|
|
@@ -81,7 +81,6 @@ function isInitialStory(db: SqliteDb, storyKey: string): boolean {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export function createRunForStory(db: SqliteDb, config: AstrocodeConfig, storyKey: string): { run_id: string } {
|
|
84
|
-
console.log(`[Astrocode] Creating run for story: ${storyKey}`);
|
|
85
84
|
const story = getStory(db, storyKey);
|
|
86
85
|
if (!story) throw new Error(`Story not found: ${storyKey}`);
|
|
87
86
|
if (story.state !== "approved") throw new Error(`Story must be approved to run: ${storyKey} (state=${story.state})`);
|
|
@@ -89,7 +88,6 @@ export function createRunForStory(db: SqliteDb, config: AstrocodeConfig, storyKe
|
|
|
89
88
|
const run_id = newRunId();
|
|
90
89
|
const now = nowISO();
|
|
91
90
|
const pipeline = config.workflow.pipeline;
|
|
92
|
-
console.log(`[Astrocode] Generated run ID: ${run_id}`);
|
|
93
91
|
|
|
94
92
|
// Convert to genesis planning story if needed
|
|
95
93
|
const isGenesisCandidate = storyKey === 'S-0001' || isInitialStory(db, storyKey) ||
|
|
@@ -97,19 +95,16 @@ export function createRunForStory(db: SqliteDb, config: AstrocodeConfig, storyKe
|
|
|
97
95
|
(story.title.toLowerCase().includes('implement') || story.body_md.toLowerCase().includes('implement')));
|
|
98
96
|
|
|
99
97
|
if (isGenesisCandidate) {
|
|
100
|
-
console.log(`[Astrocode] Converting story ${storyKey} to genesis planning story`);
|
|
101
98
|
const planningTitle = `Plan and decompose: ${story.title}`;
|
|
102
99
|
const planningBody = `Analyze the requirements and break down "${story.title}" into 50-200 detailed, granular implementation stories. Each story should be focused on a specific, implementable task with clear acceptance criteria.\n\nOriginal request: ${story.body_md || ''}`;
|
|
103
100
|
db.prepare("UPDATE stories SET title=?, body_md=? WHERE story_key=?").run(planningTitle, planningBody, storyKey);
|
|
104
101
|
}
|
|
105
102
|
|
|
106
103
|
// Lock story
|
|
107
|
-
console.log(`[Astrocode] Locking story ${storyKey} for run ${run_id}`);
|
|
108
104
|
db.prepare(
|
|
109
105
|
"UPDATE stories SET state='in_progress', in_progress=1, locked_by_run_id=?, locked_at=?, updated_at=? WHERE story_key=?"
|
|
110
106
|
).run(run_id, now, now, storyKey);
|
|
111
107
|
|
|
112
|
-
console.log(`[Astrocode] Inserting run ${run_id} into DB`);
|
|
113
108
|
db.prepare(
|
|
114
109
|
"INSERT INTO runs (run_id, story_key, status, pipeline_stages_json, current_stage_key, created_at, started_at, updated_at) VALUES (?, ?, 'running', ?, ?, ?, ?, ?)"
|
|
115
110
|
).run(run_id, storyKey, JSON.stringify(pipeline), pipeline[0] ?? null, now, now, now);
|