astrocode-workflow 0.1.53 → 0.1.55
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/config/schema.d.ts +6 -0
- package/dist/config/schema.js +10 -0
- package/dist/hooks/inject-provider.d.ts +14 -0
- package/dist/hooks/inject-provider.js +57 -0
- package/dist/index.js +7 -0
- package/dist/tools/injects.d.ts +25 -0
- package/dist/tools/injects.js +40 -0
- package/dist/tools/workflow.js +7 -7
- package/package.json +1 -1
- package/src/config/schema.ts +11 -0
- package/src/hooks/inject-provider.ts +79 -0
- package/src/index.ts +11 -4
- package/src/tools/injects.ts +68 -0
- package/src/tools/workflow.ts +7 -7
package/dist/config/schema.d.ts
CHANGED
|
@@ -172,6 +172,12 @@ export declare const AstrocodeConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
172
172
|
idle_prompt_ms: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
173
173
|
}, z.core.$strip>>>;
|
|
174
174
|
}, z.core.$strip>>>;
|
|
175
|
+
inject: z.ZodOptional<z.ZodDefault<z.ZodObject<{
|
|
176
|
+
enabled: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
177
|
+
scope_allowlist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
|
|
178
|
+
type_allowlist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
|
|
179
|
+
max_per_turn: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
180
|
+
}, z.core.$strip>>>;
|
|
175
181
|
}, z.core.$strip>>;
|
|
176
182
|
export type AstrocodeConfig = z.infer<typeof AstrocodeConfigSchema>;
|
|
177
183
|
export {};
|
package/dist/config/schema.js
CHANGED
|
@@ -161,6 +161,15 @@ const GitSchema = z.object({
|
|
|
161
161
|
commit_message_template: z.string().default("astro: {{story_key}} {{title}}"),
|
|
162
162
|
persist_diff_artifacts: z.boolean().default(true),
|
|
163
163
|
}).partial().default({});
|
|
164
|
+
const InjectSchema = z
|
|
165
|
+
.object({
|
|
166
|
+
enabled: z.boolean().default(true),
|
|
167
|
+
scope_allowlist: z.array(z.string()).default(["repo", "global"]),
|
|
168
|
+
type_allowlist: z.array(z.string()).default(["note", "policy"]),
|
|
169
|
+
max_per_turn: z.number().int().positive().default(5),
|
|
170
|
+
})
|
|
171
|
+
.partial()
|
|
172
|
+
.default({});
|
|
164
173
|
const UiSchema = z
|
|
165
174
|
.object({
|
|
166
175
|
toasts: ToastsSchema,
|
|
@@ -196,4 +205,5 @@ export const AstrocodeConfigSchema = z.object({
|
|
|
196
205
|
permissions: PermissionsSchema,
|
|
197
206
|
git: GitSchema,
|
|
198
207
|
ui: UiSchema,
|
|
208
|
+
inject: InjectSchema,
|
|
199
209
|
}).partial().default({});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AstrocodeConfig } from "../config/schema";
|
|
2
|
+
import type { SqliteDb } from "../state/db";
|
|
3
|
+
type ChatMessageInput = {
|
|
4
|
+
sessionID: string;
|
|
5
|
+
agent: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function createInjectProvider(opts: {
|
|
8
|
+
ctx: any;
|
|
9
|
+
config: AstrocodeConfig;
|
|
10
|
+
db: SqliteDb;
|
|
11
|
+
}): {
|
|
12
|
+
onChatMessage(input: ChatMessageInput): Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { selectEligibleInjects } from "../tools/injects";
|
|
2
|
+
import { injectChatPrompt } from "../ui/inject";
|
|
3
|
+
import { nowISO } from "../shared/time";
|
|
4
|
+
export function createInjectProvider(opts) {
|
|
5
|
+
const { ctx, config, db } = opts;
|
|
6
|
+
// Cache to avoid re-injecting the same injects repeatedly
|
|
7
|
+
const injectedCache = new Map();
|
|
8
|
+
function shouldSkipInject(injectId, nowMs) {
|
|
9
|
+
const lastInjected = injectedCache.get(injectId);
|
|
10
|
+
if (!lastInjected)
|
|
11
|
+
return false;
|
|
12
|
+
// Skip if injected within the last 5 minutes (configurable?)
|
|
13
|
+
const cooldownMs = 5 * 60 * 1000;
|
|
14
|
+
return nowMs - lastInjected < cooldownMs;
|
|
15
|
+
}
|
|
16
|
+
function markInjected(injectId, nowMs) {
|
|
17
|
+
injectedCache.set(injectId, nowMs);
|
|
18
|
+
}
|
|
19
|
+
async function injectEligibleInjects(sessionId) {
|
|
20
|
+
const now = nowISO();
|
|
21
|
+
const nowMs = Date.now();
|
|
22
|
+
// Get eligible injects - use allowlists from config or defaults
|
|
23
|
+
const scopeAllowlist = config.inject?.scope_allowlist ?? ["repo", "global"];
|
|
24
|
+
const typeAllowlist = config.inject?.type_allowlist ?? ["note", "policy"];
|
|
25
|
+
const eligibleInjects = selectEligibleInjects(db, {
|
|
26
|
+
nowIso: now,
|
|
27
|
+
scopeAllowlist,
|
|
28
|
+
typeAllowlist,
|
|
29
|
+
limit: config.inject?.max_per_turn ?? 5,
|
|
30
|
+
});
|
|
31
|
+
if (eligibleInjects.length === 0)
|
|
32
|
+
return;
|
|
33
|
+
// Inject each eligible inject, skipping recently injected ones
|
|
34
|
+
for (const inject of eligibleInjects) {
|
|
35
|
+
if (shouldSkipInject(inject.inject_id, nowMs))
|
|
36
|
+
continue;
|
|
37
|
+
// Format as injection message
|
|
38
|
+
const formattedText = `[Inject: ${inject.title}]\n\n${inject.body_md}`;
|
|
39
|
+
await injectChatPrompt({
|
|
40
|
+
ctx,
|
|
41
|
+
sessionId,
|
|
42
|
+
text: formattedText,
|
|
43
|
+
agent: "Astrocode"
|
|
44
|
+
});
|
|
45
|
+
markInjected(inject.inject_id, nowMs);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Public hook handlers
|
|
49
|
+
return {
|
|
50
|
+
async onChatMessage(input) {
|
|
51
|
+
if (!config.inject?.enabled)
|
|
52
|
+
return;
|
|
53
|
+
// Inject eligible injects before processing the user's message
|
|
54
|
+
await injectEligibleInjects(input.sessionID);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getAstroPaths, ensureAstroDirs } from "./shared/paths";
|
|
|
5
5
|
import { createAstroTools } from "./tools";
|
|
6
6
|
import { createContinuationEnforcer } from "./hooks/continuation-enforcer";
|
|
7
7
|
import { createToolOutputTruncatorHook } from "./hooks/tool-output-truncator";
|
|
8
|
+
import { createInjectProvider } from "./hooks/inject-provider";
|
|
8
9
|
import { createToastManager } from "./ui/toasts";
|
|
9
10
|
import { createAstroAgents } from "./agents/registry";
|
|
10
11
|
const Astrocode = async (ctx) => {
|
|
@@ -21,6 +22,7 @@ const Astrocode = async (ctx) => {
|
|
|
21
22
|
let configHandler = null;
|
|
22
23
|
let continuation = null;
|
|
23
24
|
let truncatorHook = null;
|
|
25
|
+
let injectProvider = null;
|
|
24
26
|
let toasts = null;
|
|
25
27
|
try {
|
|
26
28
|
db = openSqlite(paths.dbPath, { busyTimeoutMs: pluginConfig.db.busy_timeout_ms });
|
|
@@ -31,6 +33,7 @@ const Astrocode = async (ctx) => {
|
|
|
31
33
|
tools = createAstroTools({ ctx, config: pluginConfig, db, agents });
|
|
32
34
|
continuation = createContinuationEnforcer({ ctx, config: pluginConfig, db });
|
|
33
35
|
truncatorHook = createToolOutputTruncatorHook({ ctx, config: pluginConfig, db });
|
|
36
|
+
injectProvider = createInjectProvider({ ctx, config: pluginConfig, db });
|
|
34
37
|
toasts = createToastManager({ ctx, throttleMs: pluginConfig.ui.toasts.throttle_ms });
|
|
35
38
|
}
|
|
36
39
|
catch (e) {
|
|
@@ -46,6 +49,7 @@ const Astrocode = async (ctx) => {
|
|
|
46
49
|
tools = createAstroTools({ ctx, config: pluginConfig, db, agents });
|
|
47
50
|
continuation = null;
|
|
48
51
|
truncatorHook = null;
|
|
52
|
+
injectProvider = null;
|
|
49
53
|
toasts = null;
|
|
50
54
|
}
|
|
51
55
|
return {
|
|
@@ -80,6 +84,9 @@ const Astrocode = async (ctx) => {
|
|
|
80
84
|
}
|
|
81
85
|
},
|
|
82
86
|
"chat.message": async (input, output) => {
|
|
87
|
+
if (injectProvider && !pluginConfig.disabled_hooks.includes("inject-provider")) {
|
|
88
|
+
await injectProvider.onChatMessage(input);
|
|
89
|
+
}
|
|
83
90
|
if (continuation && !pluginConfig.disabled_hooks.includes("continuation-enforcer")) {
|
|
84
91
|
await continuation.onChatMessage(input);
|
|
85
92
|
}
|
package/dist/tools/injects.d.ts
CHANGED
|
@@ -21,3 +21,28 @@ export declare function createAstroInjectSearchTool(opts: {
|
|
|
21
21
|
config: AstrocodeConfig;
|
|
22
22
|
db: SqliteDb;
|
|
23
23
|
}): ToolDefinition;
|
|
24
|
+
export type InjectRow = {
|
|
25
|
+
inject_id: string;
|
|
26
|
+
type: string;
|
|
27
|
+
title: string;
|
|
28
|
+
body_md: string;
|
|
29
|
+
tags_json: string;
|
|
30
|
+
scope: string;
|
|
31
|
+
source: string;
|
|
32
|
+
priority: number;
|
|
33
|
+
expires_at: string | null;
|
|
34
|
+
sha256: string;
|
|
35
|
+
created_at: string;
|
|
36
|
+
updated_at: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function selectEligibleInjects(db: SqliteDb, opts: {
|
|
39
|
+
nowIso: string;
|
|
40
|
+
scopeAllowlist: string[];
|
|
41
|
+
typeAllowlist: string[];
|
|
42
|
+
limit?: number;
|
|
43
|
+
}): InjectRow[];
|
|
44
|
+
export declare function createAstroInjectEligibleTool(opts: {
|
|
45
|
+
ctx: any;
|
|
46
|
+
config: AstrocodeConfig;
|
|
47
|
+
db: SqliteDb;
|
|
48
|
+
}): ToolDefinition;
|
package/dist/tools/injects.js
CHANGED
|
@@ -97,3 +97,43 @@ export function createAstroInjectSearchTool(opts) {
|
|
|
97
97
|
},
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
+
export function selectEligibleInjects(db, opts) {
|
|
101
|
+
const { nowIso, scopeAllowlist, typeAllowlist, limit = 50 } = opts;
|
|
102
|
+
// Build placeholders safely
|
|
103
|
+
const scopeQs = scopeAllowlist.map(() => "?").join(", ");
|
|
104
|
+
const typeQs = typeAllowlist.map(() => "?").join(", ");
|
|
105
|
+
const sql = `
|
|
106
|
+
SELECT *
|
|
107
|
+
FROM injects
|
|
108
|
+
WHERE (expires_at IS NULL OR expires_at > ?)
|
|
109
|
+
AND scope IN (${scopeQs})
|
|
110
|
+
AND type IN (${typeQs})
|
|
111
|
+
ORDER BY priority DESC, updated_at DESC
|
|
112
|
+
LIMIT ?
|
|
113
|
+
`;
|
|
114
|
+
const params = [nowIso, ...scopeAllowlist, ...typeAllowlist, limit];
|
|
115
|
+
return db.prepare(sql).all(...params);
|
|
116
|
+
}
|
|
117
|
+
export function createAstroInjectEligibleTool(opts) {
|
|
118
|
+
const { db } = opts;
|
|
119
|
+
return tool({
|
|
120
|
+
description: "Debug: show which injects are eligible right now for injection.",
|
|
121
|
+
args: {
|
|
122
|
+
scopes_json: tool.schema.string().default('["repo","global"]'),
|
|
123
|
+
types_json: tool.schema.string().default('["note","policy"]'),
|
|
124
|
+
limit: tool.schema.number().int().positive().default(50),
|
|
125
|
+
},
|
|
126
|
+
execute: async ({ scopes_json, types_json, limit }) => {
|
|
127
|
+
const now = nowISO();
|
|
128
|
+
const scopes = JSON.parse(scopes_json);
|
|
129
|
+
const types = JSON.parse(types_json);
|
|
130
|
+
const rows = selectEligibleInjects(db, {
|
|
131
|
+
nowIso: now,
|
|
132
|
+
scopeAllowlist: scopes,
|
|
133
|
+
typeAllowlist: types,
|
|
134
|
+
limit,
|
|
135
|
+
});
|
|
136
|
+
return JSON.stringify({ now, count: rows.length, rows }, null, 2);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
package/dist/tools/workflow.js
CHANGED
|
@@ -10,13 +10,13 @@ import { debug } from "../shared/log";
|
|
|
10
10
|
import { createToastManager } from "../ui/toasts";
|
|
11
11
|
// Agent name mapping for case-sensitive resolution
|
|
12
12
|
const STAGE_TO_AGENT_MAP = {
|
|
13
|
-
frame: "
|
|
14
|
-
plan: "
|
|
15
|
-
spec: "
|
|
16
|
-
implement: "
|
|
17
|
-
review: "
|
|
18
|
-
verify: "
|
|
19
|
-
close: "
|
|
13
|
+
frame: "Frame",
|
|
14
|
+
plan: "Plan",
|
|
15
|
+
spec: "Spec",
|
|
16
|
+
implement: "Implement",
|
|
17
|
+
review: "Review",
|
|
18
|
+
verify: "Verify",
|
|
19
|
+
close: "Close"
|
|
20
20
|
};
|
|
21
21
|
function resolveAgentName(stageKey, config) {
|
|
22
22
|
// Use configurable agent names from config, fallback to hardcoded map, then General
|
package/package.json
CHANGED
package/src/config/schema.ts
CHANGED
|
@@ -194,6 +194,16 @@ const GitSchema = z.object({
|
|
|
194
194
|
persist_diff_artifacts: z.boolean().default(true),
|
|
195
195
|
}).partial().default({});
|
|
196
196
|
|
|
197
|
+
const InjectSchema = z
|
|
198
|
+
.object({
|
|
199
|
+
enabled: z.boolean().default(true),
|
|
200
|
+
scope_allowlist: z.array(z.string()).default(["repo", "global"]),
|
|
201
|
+
type_allowlist: z.array(z.string()).default(["note", "policy"]),
|
|
202
|
+
max_per_turn: z.number().int().positive().default(5),
|
|
203
|
+
})
|
|
204
|
+
.partial()
|
|
205
|
+
.default({});
|
|
206
|
+
|
|
197
207
|
const UiSchema = z
|
|
198
208
|
.object({
|
|
199
209
|
toasts: ToastsSchema,
|
|
@@ -232,6 +242,7 @@ export const AstrocodeConfigSchema = z.object({
|
|
|
232
242
|
permissions: PermissionsSchema,
|
|
233
243
|
git: GitSchema,
|
|
234
244
|
ui: UiSchema,
|
|
245
|
+
inject: InjectSchema,
|
|
235
246
|
}).partial().default({});
|
|
236
247
|
|
|
237
248
|
export type AstrocodeConfig = z.infer<typeof AstrocodeConfigSchema>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { AstrocodeConfig } from "../config/schema";
|
|
2
|
+
import type { SqliteDb } from "../state/db";
|
|
3
|
+
import { selectEligibleInjects } from "../tools/injects";
|
|
4
|
+
import { injectChatPrompt } from "../ui/inject";
|
|
5
|
+
import { nowISO } from "../shared/time";
|
|
6
|
+
|
|
7
|
+
type ChatMessageInput = {
|
|
8
|
+
sessionID: string;
|
|
9
|
+
agent: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function createInjectProvider(opts: {
|
|
13
|
+
ctx: any;
|
|
14
|
+
config: AstrocodeConfig;
|
|
15
|
+
db: SqliteDb;
|
|
16
|
+
}) {
|
|
17
|
+
const { ctx, config, db } = opts;
|
|
18
|
+
|
|
19
|
+
// Cache to avoid re-injecting the same injects repeatedly
|
|
20
|
+
const injectedCache = new Map<string, number>();
|
|
21
|
+
|
|
22
|
+
function shouldSkipInject(injectId: string, nowMs: number): boolean {
|
|
23
|
+
const lastInjected = injectedCache.get(injectId);
|
|
24
|
+
if (!lastInjected) return false;
|
|
25
|
+
|
|
26
|
+
// Skip if injected within the last 5 minutes (configurable?)
|
|
27
|
+
const cooldownMs = 5 * 60 * 1000;
|
|
28
|
+
return nowMs - lastInjected < cooldownMs;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function markInjected(injectId: string, nowMs: number) {
|
|
32
|
+
injectedCache.set(injectId, nowMs);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function injectEligibleInjects(sessionId: string) {
|
|
36
|
+
const now = nowISO();
|
|
37
|
+
const nowMs = Date.now();
|
|
38
|
+
|
|
39
|
+
// Get eligible injects - use allowlists from config or defaults
|
|
40
|
+
const scopeAllowlist = config.inject?.scope_allowlist ?? ["repo", "global"];
|
|
41
|
+
const typeAllowlist = config.inject?.type_allowlist ?? ["note", "policy"];
|
|
42
|
+
|
|
43
|
+
const eligibleInjects = selectEligibleInjects(db, {
|
|
44
|
+
nowIso: now,
|
|
45
|
+
scopeAllowlist,
|
|
46
|
+
typeAllowlist,
|
|
47
|
+
limit: config.inject?.max_per_turn ?? 5,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (eligibleInjects.length === 0) return;
|
|
51
|
+
|
|
52
|
+
// Inject each eligible inject, skipping recently injected ones
|
|
53
|
+
for (const inject of eligibleInjects) {
|
|
54
|
+
if (shouldSkipInject(inject.inject_id, nowMs)) continue;
|
|
55
|
+
|
|
56
|
+
// Format as injection message
|
|
57
|
+
const formattedText = `[Inject: ${inject.title}]\n\n${inject.body_md}`;
|
|
58
|
+
|
|
59
|
+
await injectChatPrompt({
|
|
60
|
+
ctx,
|
|
61
|
+
sessionId,
|
|
62
|
+
text: formattedText,
|
|
63
|
+
agent: "Astrocode"
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
markInjected(inject.inject_id, nowMs);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Public hook handlers
|
|
71
|
+
return {
|
|
72
|
+
async onChatMessage(input: ChatMessageInput) {
|
|
73
|
+
if (!config.inject?.enabled) return;
|
|
74
|
+
|
|
75
|
+
// Inject eligible injects before processing the user's message
|
|
76
|
+
await injectEligibleInjects(input.sessionID);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { getAstroPaths, ensureAstroDirs } from "./shared/paths";
|
|
|
6
6
|
import { createAstroTools } from "./tools";
|
|
7
7
|
import { createContinuationEnforcer } from "./hooks/continuation-enforcer";
|
|
8
8
|
import { createToolOutputTruncatorHook } from "./hooks/tool-output-truncator";
|
|
9
|
+
import { createInjectProvider } from "./hooks/inject-provider";
|
|
9
10
|
import { createToastManager } from "./ui/toasts";
|
|
10
11
|
import { createAstroAgents } from "./agents/registry";
|
|
11
12
|
import { info, warn } from "./shared/log";
|
|
@@ -25,10 +26,11 @@ const Astrocode: Plugin = async (ctx) => {
|
|
|
25
26
|
|
|
26
27
|
let db: any = null;
|
|
27
28
|
let tools: any = null;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
let configHandler: any = null;
|
|
30
|
+
let continuation: any = null;
|
|
31
|
+
let truncatorHook: any = null;
|
|
32
|
+
let injectProvider: any = null;
|
|
33
|
+
let toasts: any = null;
|
|
32
34
|
|
|
33
35
|
try {
|
|
34
36
|
|
|
@@ -41,6 +43,7 @@ const Astrocode: Plugin = async (ctx) => {
|
|
|
41
43
|
tools = createAstroTools({ ctx, config: pluginConfig, db, agents });
|
|
42
44
|
continuation = createContinuationEnforcer({ ctx, config: pluginConfig, db });
|
|
43
45
|
truncatorHook = createToolOutputTruncatorHook({ ctx, config: pluginConfig, db });
|
|
46
|
+
injectProvider = createInjectProvider({ ctx, config: pluginConfig, db });
|
|
44
47
|
toasts = createToastManager({ ctx, throttleMs: pluginConfig.ui.toasts.throttle_ms });
|
|
45
48
|
} catch (e) {
|
|
46
49
|
// Database initialization failed - setup limited mode
|
|
@@ -58,6 +61,7 @@ const Astrocode: Plugin = async (ctx) => {
|
|
|
58
61
|
tools = createAstroTools({ ctx, config: pluginConfig, db, agents });
|
|
59
62
|
continuation = null;
|
|
60
63
|
truncatorHook = null;
|
|
64
|
+
injectProvider = null;
|
|
61
65
|
toasts = null;
|
|
62
66
|
}
|
|
63
67
|
|
|
@@ -102,6 +106,9 @@ const Astrocode: Plugin = async (ctx) => {
|
|
|
102
106
|
},
|
|
103
107
|
|
|
104
108
|
"chat.message": async (input: any, output: any) => {
|
|
109
|
+
if (injectProvider && !pluginConfig.disabled_hooks.includes("inject-provider")) {
|
|
110
|
+
await injectProvider.onChatMessage(input);
|
|
111
|
+
}
|
|
105
112
|
if (continuation && !pluginConfig.disabled_hooks.includes("continuation-enforcer")) {
|
|
106
113
|
await continuation.onChatMessage(input);
|
|
107
114
|
}
|
package/src/tools/injects.ts
CHANGED
|
@@ -106,3 +106,71 @@ export function createAstroInjectSearchTool(opts: { ctx: any; config: AstrocodeC
|
|
|
106
106
|
},
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
export type InjectRow = {
|
|
111
|
+
inject_id: string;
|
|
112
|
+
type: string;
|
|
113
|
+
title: string;
|
|
114
|
+
body_md: string;
|
|
115
|
+
tags_json: string;
|
|
116
|
+
scope: string;
|
|
117
|
+
source: string;
|
|
118
|
+
priority: number;
|
|
119
|
+
expires_at: string | null;
|
|
120
|
+
sha256: string;
|
|
121
|
+
created_at: string;
|
|
122
|
+
updated_at: string;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export function selectEligibleInjects(db: SqliteDb, opts: {
|
|
126
|
+
nowIso: string;
|
|
127
|
+
scopeAllowlist: string[];
|
|
128
|
+
typeAllowlist: string[];
|
|
129
|
+
limit?: number;
|
|
130
|
+
}): InjectRow[] {
|
|
131
|
+
const { nowIso, scopeAllowlist, typeAllowlist, limit = 50 } = opts;
|
|
132
|
+
|
|
133
|
+
// Build placeholders safely
|
|
134
|
+
const scopeQs = scopeAllowlist.map(() => "?").join(", ");
|
|
135
|
+
const typeQs = typeAllowlist.map(() => "?").join(", ");
|
|
136
|
+
|
|
137
|
+
const sql = `
|
|
138
|
+
SELECT *
|
|
139
|
+
FROM injects
|
|
140
|
+
WHERE (expires_at IS NULL OR expires_at > ?)
|
|
141
|
+
AND scope IN (${scopeQs})
|
|
142
|
+
AND type IN (${typeQs})
|
|
143
|
+
ORDER BY priority DESC, updated_at DESC
|
|
144
|
+
LIMIT ?
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
const params = [nowIso, ...scopeAllowlist, ...typeAllowlist, limit];
|
|
148
|
+
return db.prepare(sql).all(...params) as InjectRow[];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function createAstroInjectEligibleTool(opts: { ctx: any; config: AstrocodeConfig; db: SqliteDb }): ToolDefinition {
|
|
152
|
+
const { db } = opts;
|
|
153
|
+
|
|
154
|
+
return tool({
|
|
155
|
+
description: "Debug: show which injects are eligible right now for injection.",
|
|
156
|
+
args: {
|
|
157
|
+
scopes_json: tool.schema.string().default('["repo","global"]'),
|
|
158
|
+
types_json: tool.schema.string().default('["note","policy"]'),
|
|
159
|
+
limit: tool.schema.number().int().positive().default(50),
|
|
160
|
+
},
|
|
161
|
+
execute: async ({ scopes_json, types_json, limit }) => {
|
|
162
|
+
const now = nowISO();
|
|
163
|
+
const scopes = JSON.parse(scopes_json) as string[];
|
|
164
|
+
const types = JSON.parse(types_json) as string[];
|
|
165
|
+
|
|
166
|
+
const rows = selectEligibleInjects(db, {
|
|
167
|
+
nowIso: now,
|
|
168
|
+
scopeAllowlist: scopes,
|
|
169
|
+
typeAllowlist: types,
|
|
170
|
+
limit,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return JSON.stringify({ now, count: rows.length, rows }, null, 2);
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
package/src/tools/workflow.ts
CHANGED
|
@@ -14,13 +14,13 @@ import { createToastManager } from "../ui/toasts";
|
|
|
14
14
|
|
|
15
15
|
// Agent name mapping for case-sensitive resolution
|
|
16
16
|
const STAGE_TO_AGENT_MAP: Record<string, string> = {
|
|
17
|
-
frame: "
|
|
18
|
-
plan: "
|
|
19
|
-
spec: "
|
|
20
|
-
implement: "
|
|
21
|
-
review: "
|
|
22
|
-
verify: "
|
|
23
|
-
close: "
|
|
17
|
+
frame: "Frame",
|
|
18
|
+
plan: "Plan",
|
|
19
|
+
spec: "Spec",
|
|
20
|
+
implement: "Implement",
|
|
21
|
+
review: "Review",
|
|
22
|
+
verify: "Verify",
|
|
23
|
+
close: "Close"
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
function resolveAgentName(stageKey: StageKey, config: AstrocodeConfig): string {
|