astrocode-workflow 0.2.0 → 0.2.2
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.js +6 -0
- package/dist/state/db.d.ts +1 -1
- package/dist/state/db.js +62 -4
- package/dist/state/repo-lock.d.ts +3 -0
- package/dist/state/repo-lock.js +29 -0
- package/dist/tools/workflow.d.ts +1 -1
- package/dist/tools/workflow.js +224 -209
- package/dist/ui/inject.d.ts +9 -17
- package/dist/ui/inject.js +79 -102
- package/dist/workflow/state-machine.d.ts +32 -32
- package/dist/workflow/state-machine.js +85 -170
- package/package.json +1 -1
- package/src/index.ts +8 -0
- package/src/state/db.ts +63 -4
- package/src/state/repo-lock.ts +26 -0
- package/src/tools/workflow.ts +159 -142
- package/src/ui/inject.ts +98 -105
- package/src/workflow/state-machine.ts +123 -227
package/dist/tools/workflow.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
// src/tools/workflow.ts
|
|
1
2
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
3
|
import { withTx } from "../state/db";
|
|
3
4
|
import { buildContextSnapshot } from "../workflow/context";
|
|
4
|
-
import { decideNextAction, createRunForStory, startStage, completeRun, getActiveRun, EVENT_TYPES } from "../workflow/state-machine";
|
|
5
|
+
import { decideNextAction, createRunForStory, startStage, completeRun, failRun, getActiveRun, EVENT_TYPES, } from "../workflow/state-machine";
|
|
5
6
|
import { buildStageDirective, directiveHash } from "../workflow/directives";
|
|
6
7
|
import { injectChatPrompt } from "../ui/inject";
|
|
7
8
|
import { nowISO } from "../shared/time";
|
|
8
9
|
import { newEventId } from "../state/ids";
|
|
9
10
|
import { debug } from "../shared/log";
|
|
10
11
|
import { createToastManager } from "../ui/toasts";
|
|
12
|
+
import { acquireRepoLock } from "../state/repo-lock";
|
|
11
13
|
// Agent name mapping for case-sensitive resolution
|
|
12
14
|
export const STAGE_TO_AGENT_MAP = {
|
|
13
15
|
frame: "Frame",
|
|
@@ -31,24 +33,20 @@ export function resolveAgentName(stageKey, config, agents, warnings) {
|
|
|
31
33
|
// Validate that the agent actually exists in the registry
|
|
32
34
|
if (agents && !agents[candidate]) {
|
|
33
35
|
const warning = `Agent "${candidate}" not found in registry for stage "${stageKey}". Falling back to General.`;
|
|
34
|
-
if (warnings)
|
|
36
|
+
if (warnings)
|
|
35
37
|
warnings.push(warning);
|
|
36
|
-
|
|
37
|
-
else {
|
|
38
|
+
else
|
|
38
39
|
console.warn(`[Astrocode] ${warning}`);
|
|
39
|
-
}
|
|
40
40
|
candidate = "General";
|
|
41
41
|
}
|
|
42
42
|
// Final guard: ensure General exists, fallback to built-in "general" if not
|
|
43
43
|
if (agents && !agents[candidate]) {
|
|
44
44
|
const finalWarning = `Critical: General agent not found in registry. Falling back to built-in "general".`;
|
|
45
|
-
if (warnings)
|
|
45
|
+
if (warnings)
|
|
46
46
|
warnings.push(finalWarning);
|
|
47
|
-
|
|
48
|
-
else {
|
|
47
|
+
else
|
|
49
48
|
console.warn(`[Astrocode] ${finalWarning}`);
|
|
50
|
-
|
|
51
|
-
return "general"; // built-in, guaranteed by OpenCode
|
|
49
|
+
return "general";
|
|
52
50
|
}
|
|
53
51
|
return candidate;
|
|
54
52
|
}
|
|
@@ -84,9 +82,6 @@ function stageConstraints(stage, cfg) {
|
|
|
84
82
|
}
|
|
85
83
|
return common;
|
|
86
84
|
}
|
|
87
|
-
function agentNameForStage(stage, cfg) {
|
|
88
|
-
return cfg.agents.stage_agent_names[stage];
|
|
89
|
-
}
|
|
90
85
|
function buildDelegationPrompt(opts) {
|
|
91
86
|
const { stageDirective, run_id, stage_key, stage_agent_name } = opts;
|
|
92
87
|
const stageUpper = stage_key.toUpperCase();
|
|
@@ -108,10 +103,49 @@ function buildDelegationPrompt(opts) {
|
|
|
108
103
|
``,
|
|
109
104
|
`Important: do NOT do any stage work yourself in orchestrator mode.`,
|
|
110
105
|
].join("\n").trim();
|
|
111
|
-
// Debug log the delegation prompt to troubleshoot agent output issues
|
|
112
106
|
debug(`Delegating stage ${stage_key} to agent ${stage_agent_name}`, { prompt_length: prompt.length });
|
|
113
107
|
return prompt;
|
|
114
108
|
}
|
|
109
|
+
function buildUiMessage(e) {
|
|
110
|
+
switch (e.kind) {
|
|
111
|
+
case "stage_started": {
|
|
112
|
+
const agent = e.agent_name ? ` (${e.agent_name})` : "";
|
|
113
|
+
const title = "Astrocode";
|
|
114
|
+
const message = `Stage started: ${e.stage_key}${agent}`;
|
|
115
|
+
const chatText = [
|
|
116
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — STAGE_STARTED]`,
|
|
117
|
+
``,
|
|
118
|
+
`Run: ${e.run_id}`,
|
|
119
|
+
`Stage: ${e.stage_key}${agent}`,
|
|
120
|
+
].join("\n");
|
|
121
|
+
return { title, message, variant: "info", chatText };
|
|
122
|
+
}
|
|
123
|
+
case "run_completed": {
|
|
124
|
+
const title = "Astrocode";
|
|
125
|
+
const message = `Run completed: ${e.run_id}`;
|
|
126
|
+
const chatText = [
|
|
127
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — RUN_COMPLETED]`,
|
|
128
|
+
``,
|
|
129
|
+
`Run: ${e.run_id}`,
|
|
130
|
+
`Story: ${e.story_key}`,
|
|
131
|
+
].join("\n");
|
|
132
|
+
return { title, message, variant: "success", chatText };
|
|
133
|
+
}
|
|
134
|
+
case "run_failed": {
|
|
135
|
+
const title = "Astrocode";
|
|
136
|
+
const message = `Run failed: ${e.run_id} (${e.stage_key})`;
|
|
137
|
+
const chatText = [
|
|
138
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — RUN_FAILED]`,
|
|
139
|
+
``,
|
|
140
|
+
`Run: ${e.run_id}`,
|
|
141
|
+
`Story: ${e.story_key}`,
|
|
142
|
+
`Stage: ${e.stage_key}`,
|
|
143
|
+
`Error: ${e.error_text}`,
|
|
144
|
+
].join("\n");
|
|
145
|
+
return { title, message, variant: "error", chatText };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
115
149
|
export function createAstroWorkflowProceedTool(opts) {
|
|
116
150
|
const { ctx, config, db, agents } = opts;
|
|
117
151
|
const toasts = createToastManager({ ctx, throttleMs: config.ui.toasts.throttle_ms });
|
|
@@ -122,219 +156,200 @@ export function createAstroWorkflowProceedTool(opts) {
|
|
|
122
156
|
max_steps: tool.schema.number().int().positive().default(config.workflow.default_max_steps),
|
|
123
157
|
},
|
|
124
158
|
execute: async ({ mode, max_steps }) => {
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
if (mode === "step")
|
|
159
|
+
// Acquire repo lock to ensure no concurrent workflow operations
|
|
160
|
+
const lockPath = `${ctx.directory}/.astro/astro.lock`;
|
|
161
|
+
const repoLock = acquireRepoLock(lockPath);
|
|
162
|
+
try {
|
|
163
|
+
const sessionId = ctx.sessionID;
|
|
164
|
+
const steps = Math.min(max_steps, config.workflow.loop_max_steps_hard_cap);
|
|
165
|
+
const actions = [];
|
|
166
|
+
const warnings = [];
|
|
167
|
+
const startedAt = nowISO();
|
|
168
|
+
// Collect UI events emitted inside state-machine functions, then flush AFTER tx.
|
|
169
|
+
const uiEvents = [];
|
|
170
|
+
const emit = (e) => uiEvents.push(e);
|
|
171
|
+
for (let i = 0; i < steps; i++) {
|
|
172
|
+
const next = decideNextAction(db, config);
|
|
173
|
+
if (next.kind === "idle") {
|
|
174
|
+
actions.push("idle: no approved stories");
|
|
143
175
|
break;
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (next.kind === "complete_run") {
|
|
147
|
-
withTx(db, () => completeRun(db, next.run_id));
|
|
148
|
-
actions.push(`completed run ${next.run_id}`);
|
|
149
|
-
if (config.ui.toasts.enabled && config.ui.toasts.show_run_completed) {
|
|
150
|
-
await toasts.show({ title: "Astrocode", message: `Run completed (${next.run_id})`, variant: "success" });
|
|
151
176
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
`The Clara Forms implementation run has finished all stages. The spec has been analyzed and decomposed into prioritized implementation stories.`,
|
|
160
|
-
``,
|
|
161
|
-
`Next actions: Review the generated stories and approve the next one to continue development.`,
|
|
162
|
-
].join("\n");
|
|
163
|
-
await injectChatPrompt({
|
|
164
|
-
ctx,
|
|
165
|
-
sessionId,
|
|
166
|
-
text: continueDirective,
|
|
167
|
-
agent: "Astro"
|
|
168
|
-
});
|
|
169
|
-
actions.push(`injected continuation directive for completed run ${next.run_id}`);
|
|
177
|
+
if (next.kind === "start_run") {
|
|
178
|
+
// SINGLE tx boundary: caller owns tx, state-machine is pure.
|
|
179
|
+
const { run_id } = withTx(db, () => createRunForStory(db, config, next.story_key));
|
|
180
|
+
actions.push(`started run ${run_id} for story ${next.story_key}`);
|
|
181
|
+
if (mode === "step")
|
|
182
|
+
break;
|
|
183
|
+
continue;
|
|
170
184
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
`The previous run completed successfully. Start the next approved story.`,
|
|
178
|
-
``,
|
|
179
|
-
`Next Story: ${nextStory.story_key} — ${nextStory.title}`,
|
|
180
|
-
``,
|
|
181
|
-
`Action: Call astro_story_approve with story_key="${nextStory.story_key}" to start it, or select a different story.`,
|
|
182
|
-
].join("\n");
|
|
183
|
-
await injectChatPrompt({
|
|
184
|
-
ctx,
|
|
185
|
-
sessionId,
|
|
186
|
-
text: nextDirective,
|
|
187
|
-
agent: "Astro"
|
|
188
|
-
});
|
|
189
|
-
actions.push(`injected directive to start next story ${nextStory.story_key}`);
|
|
185
|
+
if (next.kind === "complete_run") {
|
|
186
|
+
withTx(db, () => completeRun(db, next.run_id, emit));
|
|
187
|
+
actions.push(`completed run ${next.run_id}`);
|
|
188
|
+
if (mode === "step")
|
|
189
|
+
break;
|
|
190
|
+
continue;
|
|
190
191
|
}
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
// For known stage agents, assume they exist (they are system-provided subagents)
|
|
216
|
-
const knownStageAgents = ["Frame", "Plan", "Spec", "Implement", "Review", "Verify", "Close"];
|
|
217
|
-
if (knownStageAgents.includes(name)) {
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
221
|
-
};
|
|
222
|
-
if (!agentExists(agentName)) {
|
|
223
|
-
const originalAgent = agentName;
|
|
224
|
-
console.warn(`[Astrocode] Agent ${agentName} not found in config. Falling back to orchestrator.`);
|
|
225
|
-
// First fallback: orchestrator
|
|
226
|
-
agentName = config.agents?.orchestrator_name || "Astro";
|
|
192
|
+
if (next.kind === "failed") {
|
|
193
|
+
// Ensure DB state reflects failure in one tx; emit UI event.
|
|
194
|
+
withTx(db, () => failRun(db, next.run_id, next.stage_key, next.error_text, emit));
|
|
195
|
+
actions.push(`failed: ${next.stage_key} — ${next.error_text}`);
|
|
196
|
+
if (mode === "step")
|
|
197
|
+
break;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (next.kind === "delegate_stage") {
|
|
201
|
+
const active = getActiveRun(db);
|
|
202
|
+
if (!active)
|
|
203
|
+
throw new Error("Invariant: delegate_stage but no active run.");
|
|
204
|
+
const run = db.prepare("SELECT * FROM runs WHERE run_id=?").get(active.run_id);
|
|
205
|
+
const story = db.prepare("SELECT * FROM stories WHERE story_key=?").get(run.story_key);
|
|
206
|
+
let agentName = resolveAgentName(next.stage_key, config, agents, warnings);
|
|
207
|
+
const agentExists = (name) => {
|
|
208
|
+
if (agents && agents[name])
|
|
209
|
+
return true;
|
|
210
|
+
const knownStageAgents = ["Frame", "Plan", "Spec", "Implement", "Review", "Verify", "Close", "General", "Astro", "general"];
|
|
211
|
+
if (knownStageAgents.includes(name))
|
|
212
|
+
return true;
|
|
213
|
+
return false;
|
|
214
|
+
};
|
|
227
215
|
if (!agentExists(agentName)) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
agentName = "
|
|
216
|
+
const originalAgent = agentName;
|
|
217
|
+
console.warn(`[Astrocode] Agent ${agentName} not found. Falling back to orchestrator.`);
|
|
218
|
+
agentName = config.agents?.orchestrator_name || "Astro";
|
|
231
219
|
if (!agentExists(agentName)) {
|
|
232
|
-
|
|
220
|
+
console.warn(`[Astrocode] Orchestrator ${agentName} not available. Falling back to General.`);
|
|
221
|
+
agentName = "General";
|
|
222
|
+
if (!agentExists(agentName)) {
|
|
223
|
+
throw new Error(`Critical: No agents available for delegation. Primary: ${originalAgent}, Orchestrator: ${config.agents?.orchestrator_name || "Astro"}, General: unavailable`);
|
|
224
|
+
}
|
|
233
225
|
}
|
|
234
226
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
227
|
+
// NOTE: startStage owns its own tx (state-machine.ts).
|
|
228
|
+
withTx(db, () => {
|
|
229
|
+
startStage(db, active.run_id, next.stage_key, { subagent_type: agentName }, emit);
|
|
230
|
+
});
|
|
231
|
+
const context = buildContextSnapshot({
|
|
232
|
+
db,
|
|
233
|
+
config,
|
|
234
|
+
run_id: active.run_id,
|
|
235
|
+
next_action: `delegate stage ${next.stage_key}`,
|
|
236
|
+
});
|
|
237
|
+
const stageDirective = buildStageDirective({
|
|
238
|
+
config,
|
|
239
|
+
stage_key: next.stage_key,
|
|
240
|
+
run_id: active.run_id,
|
|
241
|
+
story_key: run.story_key,
|
|
242
|
+
story_title: story?.title ?? "(missing)",
|
|
243
|
+
stage_agent_name: agentName,
|
|
244
|
+
stage_goal: stageGoal(next.stage_key, config),
|
|
245
|
+
stage_constraints: stageConstraints(next.stage_key, config),
|
|
246
|
+
context_snapshot_md: context,
|
|
247
|
+
}).body;
|
|
248
|
+
const delegatePrompt = buildDelegationPrompt({
|
|
249
|
+
stageDirective,
|
|
250
|
+
run_id: active.run_id,
|
|
251
|
+
stage_key: next.stage_key,
|
|
252
|
+
stage_agent_name: agentName,
|
|
253
|
+
});
|
|
254
|
+
// Record continuation (best-effort; no tx wrapper needed but safe either way)
|
|
255
|
+
const h = directiveHash(delegatePrompt);
|
|
256
|
+
const now = nowISO();
|
|
257
|
+
if (sessionId) {
|
|
258
|
+
// This assumes continuations table exists in vNext schema.
|
|
259
|
+
db.prepare("INSERT INTO continuations (session_id, run_id, directive_hash, kind, reason, created_at) VALUES (?, ?, ?, 'stage', ?, ?)").run(sessionId, active.run_id, h, `delegate ${next.stage_key}`, now);
|
|
242
260
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
run_id: active.run_id,
|
|
262
|
-
stage_key: next.stage_key,
|
|
263
|
-
stage_agent_name: agentName,
|
|
264
|
-
});
|
|
265
|
-
// Record in continuations as a stage directive (dedupe by hash)
|
|
266
|
-
const h = directiveHash(delegatePrompt);
|
|
267
|
-
const now = nowISO();
|
|
268
|
-
if (sessionId) {
|
|
269
|
-
db.prepare("INSERT INTO continuations (session_id, run_id, directive_hash, kind, reason, created_at) VALUES (?, ?, ?, 'stage', ?, ?)").run(sessionId, active.run_id, h, `delegate ${next.stage_key}`, now);
|
|
261
|
+
// Visible injection so user can see state (awaited)
|
|
262
|
+
if (sessionId) {
|
|
263
|
+
await injectChatPrompt({ ctx, sessionId, text: delegatePrompt, agent: "Astro" });
|
|
264
|
+
const continueMessage = [
|
|
265
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — AWAIT_STAGE_COMPLETION]`,
|
|
266
|
+
``,
|
|
267
|
+
`Stage \`${next.stage_key}\` delegated to \`${agentName}\`.`,
|
|
268
|
+
``,
|
|
269
|
+
`When \`${agentName}\` completes, call:`,
|
|
270
|
+
`astro_stage_complete(run_id="${active.run_id}", stage_key="${next.stage_key}", output_text="[paste subagent output here]")`,
|
|
271
|
+
``,
|
|
272
|
+
`This advances the workflow.`,
|
|
273
|
+
].join("\n");
|
|
274
|
+
await injectChatPrompt({ ctx, sessionId, text: continueMessage, agent: "Astro" });
|
|
275
|
+
}
|
|
276
|
+
actions.push(`delegated stage ${next.stage_key} via ${agentName}`);
|
|
277
|
+
// Stop here; subagent needs to run.
|
|
278
|
+
break;
|
|
270
279
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
280
|
+
if (next.kind === "await_stage_completion") {
|
|
281
|
+
actions.push(`await stage completion: ${next.stage_key}`);
|
|
282
|
+
if (sessionId) {
|
|
283
|
+
const context = buildContextSnapshot({
|
|
284
|
+
db,
|
|
285
|
+
config,
|
|
286
|
+
run_id: next.run_id,
|
|
287
|
+
next_action: `complete stage ${next.stage_key}`,
|
|
288
|
+
});
|
|
289
|
+
const prompt = [
|
|
290
|
+
`[SYSTEM DIRECTIVE: ASTROCODE — AWAIT_STAGE_OUTPUT]`,
|
|
291
|
+
``,
|
|
292
|
+
`Run \`${next.run_id}\` is waiting for stage \`${next.stage_key}\` output.`,
|
|
293
|
+
`If you have the subagent output, call astro_stage_complete with output_text=the FULL output.`,
|
|
294
|
+
``,
|
|
295
|
+
`Context snapshot:`,
|
|
296
|
+
context,
|
|
297
|
+
].join("\n").trim();
|
|
298
|
+
const h = directiveHash(prompt);
|
|
299
|
+
const now = nowISO();
|
|
300
|
+
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);
|
|
301
|
+
await injectChatPrompt({ ctx, sessionId, text: prompt, agent: "Astro" });
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
286
304
|
}
|
|
287
|
-
actions.push(`
|
|
288
|
-
// Stop here; subagent needs to run.
|
|
305
|
+
actions.push(`unhandled next action: ${next.kind}`);
|
|
289
306
|
break;
|
|
290
307
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
// Flush UI events (toast + prompt) AFTER state transitions
|
|
309
|
+
if (uiEvents.length > 0) {
|
|
310
|
+
for (const e of uiEvents) {
|
|
311
|
+
const msg = buildUiMessage(e);
|
|
312
|
+
if (config.ui.toasts.enabled) {
|
|
313
|
+
await toasts.show({
|
|
314
|
+
title: msg.title,
|
|
315
|
+
message: msg.message,
|
|
316
|
+
variant: msg.variant,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
if (ctx?.sessionID) {
|
|
320
|
+
await injectChatPrompt({
|
|
321
|
+
ctx,
|
|
322
|
+
sessionId: ctx.sessionID,
|
|
323
|
+
text: msg.chatText,
|
|
324
|
+
agent: "Astro",
|
|
325
|
+
});
|
|
326
|
+
}
|
|
309
327
|
}
|
|
310
|
-
|
|
328
|
+
actions.push(`ui: flushed ${uiEvents.length} event(s)`);
|
|
311
329
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
330
|
+
// Housekeeping event
|
|
331
|
+
db.prepare("INSERT INTO events (event_id, run_id, stage_key, type, body_json, created_at) VALUES (?, NULL, NULL, ?, ?, ?)").run(newEventId(), EVENT_TYPES.WORKFLOW_PROCEED, JSON.stringify({ started_at: startedAt, mode, max_steps: steps, actions }), nowISO());
|
|
332
|
+
const active = getActiveRun(db);
|
|
333
|
+
const lines = [];
|
|
334
|
+
lines.push(`# astro_workflow_proceed`);
|
|
335
|
+
lines.push(`- mode: ${mode}`);
|
|
336
|
+
lines.push(`- steps requested: ${max_steps} (cap=${steps})`);
|
|
337
|
+
if (active)
|
|
338
|
+
lines.push(`- active run: \`${active.run_id}\` (stage=${active.current_stage_key ?? "?"})`);
|
|
339
|
+
lines.push(``, `## Actions`);
|
|
340
|
+
for (const a of actions)
|
|
341
|
+
lines.push(`- ${a}`);
|
|
342
|
+
if (warnings.length > 0) {
|
|
343
|
+
lines.push(``, `## Warnings`);
|
|
344
|
+
for (const w of warnings)
|
|
345
|
+
lines.push(`⚠️ ${w}`);
|
|
315
346
|
}
|
|
316
|
-
|
|
317
|
-
actions.push(`unhandled next action: ${next.kind}`);
|
|
318
|
-
break;
|
|
347
|
+
return lines.join("\n").trim();
|
|
319
348
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const lines = [];
|
|
324
|
-
lines.push(`# astro_workflow_proceed`);
|
|
325
|
-
lines.push(`- mode: ${mode}`);
|
|
326
|
-
lines.push(`- steps requested: ${max_steps} (cap=${steps})`);
|
|
327
|
-
if (active)
|
|
328
|
-
lines.push(`- active run: \`${active.run_id}\` (stage=${active.current_stage_key ?? "?"})`);
|
|
329
|
-
lines.push(``, `## Actions`);
|
|
330
|
-
for (const a of actions)
|
|
331
|
-
lines.push(`- ${a}`);
|
|
332
|
-
if (warnings.length > 0) {
|
|
333
|
-
lines.push(``, `## Warnings`);
|
|
334
|
-
for (const w of warnings)
|
|
335
|
-
lines.push(`⚠️ ${w}`);
|
|
349
|
+
finally {
|
|
350
|
+
// Always release the lock
|
|
351
|
+
repoLock.release();
|
|
336
352
|
}
|
|
337
|
-
return lines.join("\n").trim();
|
|
338
353
|
},
|
|
339
354
|
});
|
|
340
355
|
}
|
package/dist/ui/inject.d.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Inject a visible prompt into the conversation.
|
|
3
|
+
* - Deterministic ordering per session
|
|
4
|
+
* - Correct SDK binding (prevents `this._client` undefined)
|
|
5
|
+
* - Awaitable: resolves when delivered, rejects after max retries
|
|
6
|
+
*/
|
|
7
|
+
export declare function injectChatPrompt(opts: {
|
|
2
8
|
ctx: any;
|
|
3
|
-
sessionId
|
|
9
|
+
sessionId?: string;
|
|
4
10
|
text: string;
|
|
5
11
|
agent?: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Enqueue an injection and ensure the worker is running.
|
|
9
|
-
* Does NOT wait for delivery — use `flushChatPrompts()` to wait.
|
|
10
|
-
*/
|
|
11
|
-
export declare function enqueueChatPrompt(opts: QueueItem): void;
|
|
12
|
-
/**
|
|
13
|
-
* Wait until all queued injections have been processed (sent or exhausted retries).
|
|
14
|
-
*/
|
|
15
|
-
export declare function flushChatPrompts(): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Deterministic helper: enqueue + flush (recommended for stage boundaries).
|
|
18
|
-
*/
|
|
19
|
-
export declare function injectChatPrompt(opts: QueueItem): Promise<void>;
|
|
20
|
-
export {};
|
|
12
|
+
}): Promise<void>;
|