astrocode-workflow 0.1.23 → 0.1.27

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.
@@ -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" });
@@ -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
@@ -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}`;
@@ -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;
@@ -154,16 +174,12 @@ export function createAstroWorkflowProceedTool(opts) {
154
174
  return false;
155
175
  };
156
176
  if (!agentExists(agentName)) {
157
- console.warn(`[Astrocode] Agent ${agentName} not found in config. Falling back to General.`);
177
+ console.warn(`[Astrocode] Agent ${agentName} not found in config. Falling back to orchestrator instead of General.`);
158
178
  console.warn(`[Astrocode] Agent check: config.agent exists: ${!!systemConfig.agent}, agentName in config: ${systemConfig.agent ? agentName in systemConfig.agent : 'N/A'}`);
159
- agentName = "General";
160
- // Second fallback to orchestrator if General also unavailable
179
+ // Skip General fallback for stage agents to avoid malformed output
180
+ agentName = config.agents?.orchestrator_name || "Astro";
161
181
  if (!agentExists(agentName)) {
162
- console.warn(`[Astrocode] General agent also unavailable. Falling back to orchestrator.`);
163
- agentName = config.agents?.orchestrator_name || "Astro";
164
- if (!agentExists(agentName)) {
165
- throw new Error(`Critical: No agents available for delegation. Primary: ${resolveAgentName(next.stage_key, config)}, General, Orchestrator: ${agentName}`);
166
- }
182
+ throw new Error(`Critical: No agents available for delegation. Primary: ${resolveAgentName(next.stage_key, config)}, Orchestrator: ${agentName}`);
167
183
  }
168
184
  }
169
185
  console.log(`[Astrocode] Delegating stage ${next.stage_key} to agent: ${agentName}`);
@@ -199,7 +215,7 @@ export function createAstroWorkflowProceedTool(opts) {
199
215
  }
200
216
  // Visible injection so user can see state
201
217
  if (sessionId) {
202
- await injectChatPrompt({ ctx, sessionId, text: delegatePrompt });
218
+ await injectChatPrompt({ ctx, sessionId, text: delegatePrompt, agent: "Astro" });
203
219
  }
204
220
  actions.push(`delegated stage ${next.stage_key} via ${agentName}`);
205
221
  // Stop here; subagent needs to run.
@@ -222,7 +238,7 @@ export function createAstroWorkflowProceedTool(opts) {
222
238
  const h = directiveHash(prompt);
223
239
  const now = nowISO();
224
240
  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);
225
- await injectChatPrompt({ ctx, sessionId, text: prompt });
241
+ await injectChatPrompt({ ctx, sessionId, text: prompt, agent: "Astro" });
226
242
  }
227
243
  break;
228
244
  }
@@ -2,4 +2,5 @@ export declare function injectChatPrompt(opts: {
2
2
  ctx: any;
3
3
  sessionId: string;
4
4
  text: string;
5
+ agent?: string;
5
6
  }): Promise<void>;
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 = `[Astro Agent]\n\n${text}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astrocode-workflow",
3
- "version": "0.1.23",
3
+ "version": "0.1.27",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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) {
@@ -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
 
@@ -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
 
@@ -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
  }
@@ -187,19 +213,15 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
187
213
  return false;
188
214
  };
189
215
 
190
- if (!agentExists(agentName)) {
191
- console.warn(`[Astrocode] Agent ${agentName} not found in config. Falling back to General.`);
192
- console.warn(`[Astrocode] Agent check: config.agent exists: ${!!systemConfig.agent}, agentName in config: ${systemConfig.agent ? agentName in systemConfig.agent : 'N/A'}`);
193
- agentName = "General";
194
- // Second fallback to orchestrator if General also unavailable
195
- if (!agentExists(agentName)) {
196
- console.warn(`[Astrocode] General agent also unavailable. Falling back to orchestrator.`);
197
- agentName = config.agents?.orchestrator_name || "Astro";
198
- if (!agentExists(agentName)) {
199
- throw new Error(`Critical: No agents available for delegation. Primary: ${resolveAgentName(next.stage_key, config)}, General, Orchestrator: ${agentName}`);
200
- }
201
- }
202
- }
216
+ if (!agentExists(agentName)) {
217
+ console.warn(`[Astrocode] Agent ${agentName} not found in config. Falling back to orchestrator instead of General.`);
218
+ console.warn(`[Astrocode] Agent check: config.agent exists: ${!!systemConfig.agent}, agentName in config: ${systemConfig.agent ? agentName in systemConfig.agent : 'N/A'}`);
219
+ // Skip General fallback for stage agents to avoid malformed output
220
+ agentName = config.agents?.orchestrator_name || "Astro";
221
+ if (!agentExists(agentName)) {
222
+ throw new Error(`Critical: No agents available for delegation. Primary: ${resolveAgentName(next.stage_key, config)}, Orchestrator: ${agentName}`);
223
+ }
224
+ }
203
225
 
204
226
  console.log(`[Astrocode] Delegating stage ${next.stage_key} to agent: ${agentName}`);
205
227
 
@@ -243,7 +265,7 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
243
265
 
244
266
  // Visible injection so user can see state
245
267
  if (sessionId) {
246
- await injectChatPrompt({ ctx, sessionId, text: delegatePrompt });
268
+ await injectChatPrompt({ ctx, sessionId, text: delegatePrompt, agent: "Astro" });
247
269
  }
248
270
 
249
271
  actions.push(`delegated stage ${next.stage_key} via ${agentName}`);
@@ -272,7 +294,7 @@ export function createAstroWorkflowProceedTool(opts: { ctx: any; config: Astroco
272
294
  "INSERT INTO continuations (session_id, run_id, directive_hash, kind, reason, created_at) VALUES (?, ?, ?, 'continue', ?, ?)"
273
295
  ).run(sessionId, next.run_id, h, `await ${next.stage_key}`, now);
274
296
 
275
- await injectChatPrompt({ ctx, sessionId, text: prompt });
297
+ await injectChatPrompt({ ctx, sessionId, text: prompt, agent: "Astro" });
276
298
  }
277
299
  break;
278
300
  }
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 = `[Astro Agent]\n\n${text}`;
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);