@lazyingart/agintiflow 0.8.12 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/docs/auxiliary-image-generation.md +62 -0
- package/package.json +4 -2
- package/public/app.js +8 -2
- package/public/index.html +6 -0
- package/scripts/smoke-auxiliary-tools.js +95 -0
- package/src/agent-runner.js +81 -0
- package/src/auxiliary-tools.js +344 -0
- package/src/capabilities.js +14 -1
- package/src/cli.js +31 -7
- package/src/config.js +4 -0
- package/src/guardrails.js +15 -0
- package/src/interactive-cli.js +58 -8
- package/src/model-client.js +54 -0
- package/src/project.js +35 -13
- package/src/redaction.js +1 -1
- package/src/task-profiles.js +7 -0
- package/src/workspace-tools.js +2 -0
- package/web.js +4 -0
package/src/model-client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { normalizeWrapperName, wrapperStatusText } from "./tool-wrappers.js";
|
|
3
3
|
import { getTaskProfile } from "./task-profiles.js";
|
|
4
|
+
import { listAuxiliarySkills } from "./auxiliary-tools.js";
|
|
4
5
|
|
|
5
6
|
export function createClient(config) {
|
|
6
7
|
if (config.provider === "mock") {
|
|
@@ -137,6 +138,18 @@ function mockCanvasToolForGoal(goal = "") {
|
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
function mockAuxiliaryToolForGoal(goal = "") {
|
|
142
|
+
const text = String(goal || "").toLowerCase();
|
|
143
|
+
if (!/\b(image|picture|photo|illustration|cover|poster|logo|generate art|draw a)\b/.test(text)) return null;
|
|
144
|
+
return mockToolCall("generate_image", {
|
|
145
|
+
prompt: `Mock image generation request: ${goal}`,
|
|
146
|
+
outputDir: "artifacts/images/mock-image",
|
|
147
|
+
outputStem: "mock-image",
|
|
148
|
+
aspectRatio: "1:1",
|
|
149
|
+
dryRun: true,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
140
153
|
function mockChatResponse(content, toolCalls = []) {
|
|
141
154
|
return {
|
|
142
155
|
choices: [
|
|
@@ -186,6 +199,11 @@ export async function createPlan(client, config, state) {
|
|
|
186
199
|
config.allowWrapperTools
|
|
187
200
|
? `Agent wrappers are enabled. Use the selected wrapper only: ${normalizeWrapperName(config.preferredWrapper)}. Status: ${wrapperStatusText()}.`
|
|
188
201
|
: "",
|
|
202
|
+
config.allowAuxiliaryTools
|
|
203
|
+
? `Auxiliary skills are enabled: ${listAuxiliarySkills()
|
|
204
|
+
.map((skill) => `${skill.id} via ${skill.toolName} (${skill.available ? "key available" : `needs ${skill.keyName}`})`)
|
|
205
|
+
.join(", ")}. For raster image generation requests, plan to use generate_image when a GRSAI key is available; otherwise ask the user to run /auxilliary grsai or aginti login grsai.`
|
|
206
|
+
: "Auxiliary skills are disabled for this run.",
|
|
189
207
|
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
190
208
|
"A canvas/artifacts tunnel is available through send_to_canvas. Use it when an output should be highlighted visually, such as screenshots, image files, important markdown, diffs, or generated artifact paths. It is optional for ordinary text answers.",
|
|
191
209
|
"Work like a practical coding agent: inspect when useful, edit with file tools, run safe checks when they add confidence, and keep outputs inside the workspace.",
|
|
@@ -514,6 +532,35 @@ export async function requestNextStep(client, config, messages) {
|
|
|
514
532
|
});
|
|
515
533
|
}
|
|
516
534
|
|
|
535
|
+
if (config.allowAuxiliaryTools) {
|
|
536
|
+
tools.splice(-1, 0, {
|
|
537
|
+
type: "function",
|
|
538
|
+
function: {
|
|
539
|
+
name: "generate_image",
|
|
540
|
+
description:
|
|
541
|
+
"Generate a raster image artifact through the optional GRS AI Nano Banana image-generation skill. Use for user requests that explicitly need a real image/photo/illustration/cover/poster/logo concept rather than SVG/code-native graphics. Saves prompt, redacted payload, manifest, and downloaded images in the workspace. If the GRSAI key is missing, ask the user to run /auxilliary grsai or aginti login grsai.",
|
|
542
|
+
parameters: {
|
|
543
|
+
type: "object",
|
|
544
|
+
properties: {
|
|
545
|
+
prompt: { type: "string", description: "Detailed image-generation prompt." },
|
|
546
|
+
outputDir: { type: "string", description: "Workspace-relative output directory. Defaults to artifacts/images/<timestamp>." },
|
|
547
|
+
outputStem: { type: "string", description: "Filename stem for downloaded images." },
|
|
548
|
+
aspectRatio: { type: "string", description: "Aspect ratio such as 1:1, 16:9, 2:3, or 3:2." },
|
|
549
|
+
imageSize: { type: "string", description: "Image size such as 1K, 2K, or 4K." },
|
|
550
|
+
model: { type: "string", description: "Image model. Defaults to nano-banana-2." },
|
|
551
|
+
referenceImages: {
|
|
552
|
+
type: "array",
|
|
553
|
+
items: { type: "string" },
|
|
554
|
+
description: "Optional workspace-relative image paths, HTTPS URLs, or data URLs.",
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
required: ["prompt"],
|
|
558
|
+
additionalProperties: false,
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
517
564
|
tools.splice(-1, 0, {
|
|
518
565
|
type: "function",
|
|
519
566
|
function: {
|
|
@@ -590,6 +637,13 @@ export async function requestNextStep(client, config, messages) {
|
|
|
590
637
|
}
|
|
591
638
|
}
|
|
592
639
|
|
|
640
|
+
if (config.allowAuxiliaryTools) {
|
|
641
|
+
const auxiliaryTool = mockAuxiliaryToolForGoal(config.goal);
|
|
642
|
+
if (auxiliaryTool) {
|
|
643
|
+
return mockChatResponse("Mock mode will exercise an auxiliary skill tool in dry-run mode.", [auxiliaryTool]);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
593
647
|
const canvasTool = mockCanvasToolForGoal(config.goal);
|
|
594
648
|
if (canvasTool) {
|
|
595
649
|
return mockChatResponse("Mock mode will publish a canvas artifact for the UI tunnel.", [canvasTool]);
|
package/src/project.js
CHANGED
|
@@ -15,6 +15,8 @@ const LOCAL_ENV_KEYS = new Set([
|
|
|
15
15
|
"DEEPSEEK_FAST_MODEL",
|
|
16
16
|
"DEEPSEEK_PRO_MODEL",
|
|
17
17
|
"OPENAI_DEFAULT_MODEL",
|
|
18
|
+
"GRSAI",
|
|
19
|
+
"GRSAI_API_KEY",
|
|
18
20
|
]);
|
|
19
21
|
|
|
20
22
|
export function resolveProjectRoot(input = process.cwd()) {
|
|
@@ -27,6 +29,7 @@ export function projectPaths(projectRoot = process.cwd()) {
|
|
|
27
29
|
root,
|
|
28
30
|
controlDir: path.join(root, ".aginti"),
|
|
29
31
|
envPath: path.join(root, ".aginti", ".env"),
|
|
32
|
+
rootEnvPath: path.join(root, ".env"),
|
|
30
33
|
envExamplePath: path.join(root, ".aginti", ".env.example"),
|
|
31
34
|
controlReadmePath: path.join(root, ".aginti", "README.md"),
|
|
32
35
|
notesDir: path.join(root, "notes"),
|
|
@@ -59,19 +62,24 @@ export function parseEnvText(text = "") {
|
|
|
59
62
|
|
|
60
63
|
export function loadProjectEnv(projectRoot = process.cwd(), { override = false } = {}) {
|
|
61
64
|
const paths = projectPaths(projectRoot);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const envPaths = [paths.rootEnvPath, paths.envPath];
|
|
66
|
+
const loadedPaths = [];
|
|
67
|
+
for (const envPath of envPaths) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = parseEnvText(fs.readFileSync(envPath, "utf8"));
|
|
70
|
+
if (Object.keys(parsed).length === 0) continue;
|
|
71
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
72
|
+
if (override || !process.env[key]) process.env[key] = value;
|
|
73
|
+
}
|
|
74
|
+
loadedPaths.push(envPath);
|
|
75
|
+
} catch {
|
|
76
|
+
// Ignore missing or unreadable optional local env files.
|
|
67
77
|
}
|
|
68
|
-
loaded = true;
|
|
69
|
-
} catch {
|
|
70
|
-
loaded = false;
|
|
71
78
|
}
|
|
72
79
|
return {
|
|
73
|
-
loaded,
|
|
80
|
+
loaded: loadedPaths.length > 0,
|
|
74
81
|
path: paths.envPath,
|
|
82
|
+
paths: loadedPaths,
|
|
75
83
|
};
|
|
76
84
|
}
|
|
77
85
|
|
|
@@ -137,6 +145,7 @@ export async function initProject(projectRoot = process.cwd()) {
|
|
|
137
145
|
"# Copy values into .aginti/.env. Never commit real secrets.",
|
|
138
146
|
"DEEPSEEK_API_KEY=",
|
|
139
147
|
"OPENAI_API_KEY=",
|
|
148
|
+
"GRSAI=",
|
|
140
149
|
"DEEPSEEK_FAST_MODEL=deepseek-v4-flash",
|
|
141
150
|
"DEEPSEEK_PRO_MODEL=deepseek-v4-pro",
|
|
142
151
|
"",
|
|
@@ -153,6 +162,8 @@ export async function initProject(projectRoot = process.cwd()) {
|
|
|
153
162
|
);
|
|
154
163
|
|
|
155
164
|
const gitignore = await ensureLine(paths.gitignorePath, [
|
|
165
|
+
".env",
|
|
166
|
+
".env.*",
|
|
156
167
|
".aginti/.env",
|
|
157
168
|
".aginti/.env.*",
|
|
158
169
|
"!.aginti/.env.example",
|
|
@@ -177,21 +188,31 @@ export function providerKeyStatus(projectRoot = process.cwd()) {
|
|
|
177
188
|
return {
|
|
178
189
|
openai: Boolean(process.env.OPENAI_API_KEY || process.env.LLM_API_KEY),
|
|
179
190
|
deepseek: Boolean(process.env.DEEPSEEK_API_KEY || process.env.LLM_API_KEY),
|
|
191
|
+
grsai: Boolean(process.env.GRSAI || process.env.GRSAI_API_KEY),
|
|
180
192
|
mock: true,
|
|
181
193
|
localEnv: env.loaded,
|
|
182
194
|
localEnvPath: env.path,
|
|
183
195
|
envVars: {
|
|
184
196
|
openai: ["OPENAI_API_KEY", "LLM_API_KEY"],
|
|
185
197
|
deepseek: ["DEEPSEEK_API_KEY", "LLM_API_KEY"],
|
|
198
|
+
grsai: ["GRSAI", "GRSAI_API_KEY"],
|
|
186
199
|
},
|
|
187
200
|
};
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
export async function setProviderKey(projectRoot, provider, value) {
|
|
191
204
|
const normalizedProvider = String(provider || "").toLowerCase();
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
const aliases = {
|
|
206
|
+
auxiliary: "grsai",
|
|
207
|
+
auxilliary: "grsai",
|
|
208
|
+
image: "grsai",
|
|
209
|
+
imagegen: "grsai",
|
|
210
|
+
};
|
|
211
|
+
const canonicalProvider = aliases[normalizedProvider] || normalizedProvider;
|
|
212
|
+
const keyName =
|
|
213
|
+
canonicalProvider === "openai" ? "OPENAI_API_KEY" : canonicalProvider === "grsai" ? "GRSAI" : "DEEPSEEK_API_KEY";
|
|
214
|
+
if (!["deepseek", "openai", "grsai"].includes(canonicalProvider)) {
|
|
215
|
+
throw new Error("Provider must be deepseek, openai, or grsai.");
|
|
195
216
|
}
|
|
196
217
|
|
|
197
218
|
const keyValue = String(value || "").trim();
|
|
@@ -220,7 +241,7 @@ export async function setProviderKey(projectRoot, provider, value) {
|
|
|
220
241
|
loadProjectEnv(projectRoot, { override: true });
|
|
221
242
|
return {
|
|
222
243
|
ok: true,
|
|
223
|
-
provider:
|
|
244
|
+
provider: canonicalProvider,
|
|
224
245
|
keyName,
|
|
225
246
|
path: paths.envPath,
|
|
226
247
|
};
|
|
@@ -324,6 +345,7 @@ export async function doctorReport(projectRoot, packageVersion, config) {
|
|
|
324
345
|
keys: {
|
|
325
346
|
openai: keyStatus.openai,
|
|
326
347
|
deepseek: keyStatus.deepseek,
|
|
348
|
+
grsai: keyStatus.grsai,
|
|
327
349
|
mock: true,
|
|
328
350
|
},
|
|
329
351
|
sandbox: dockerStatus,
|
package/src/redaction.js
CHANGED
|
@@ -2,7 +2,7 @@ const SECRET_PATTERNS = [
|
|
|
2
2
|
/\bsk-[A-Za-z0-9_-]{16,}\b/g,
|
|
3
3
|
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
4
4
|
/\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g,
|
|
5
|
-
/((?:api[_-]?key|token|secret|password|passwd|npm_token|_authToken)\s*[:=]\s*)[^\s"'`]+/gi,
|
|
5
|
+
/((?:api[_-]?key|token|secret|password|passwd|npm_token|_authToken|grsai)\s*[:=]\s*)[^\s"'`]+/gi,
|
|
6
6
|
/(\/\/registry\.npmjs\.org\/:_authToken=)[^\s"'`]+/gi,
|
|
7
7
|
/(Authorization:\s*Bearer\s+)[^\s"'`]+/gi,
|
|
8
8
|
];
|
package/src/task-profiles.js
CHANGED
|
@@ -55,6 +55,13 @@ export const TASK_PROFILES = {
|
|
|
55
55
|
"For website/app tasks, create or inspect real site files, preview with workspace preview tools, add local checks when useful, and use the configured sandbox/package policy for dependencies.",
|
|
56
56
|
tools: ["files", "shell", "canvas", "sandbox"],
|
|
57
57
|
},
|
|
58
|
+
image: {
|
|
59
|
+
id: "image",
|
|
60
|
+
label: "Image generation",
|
|
61
|
+
prompt:
|
|
62
|
+
"For raster image, cover, poster, illustration, photo, and logo-concept tasks, write a clear visual prompt, use generate_image when the optional GRS AI key is available, save outputs under artifacts/images, and send the selected image to the canvas. If the key is missing, ask the user to run /auxilliary grsai or aginti login grsai.",
|
|
63
|
+
tools: ["auxiliary:image_generation", "files", "canvas"],
|
|
64
|
+
},
|
|
58
65
|
aaps: {
|
|
59
66
|
id: "aaps",
|
|
60
67
|
label: "AAPS",
|
package/src/workspace-tools.js
CHANGED
|
@@ -110,8 +110,10 @@ function isSensitivePath(relativePath) {
|
|
|
110
110
|
const segments = pathSegments(relativePath);
|
|
111
111
|
const basename = segments.at(-1) || "";
|
|
112
112
|
const lowerBase = basename.toLowerCase();
|
|
113
|
+
const lowerSegments = segments.map((segment) => segment.toLowerCase());
|
|
113
114
|
const lowerPath = normalizeRelative(relativePath).toLowerCase();
|
|
114
115
|
if (segments.includes(".git")) return true;
|
|
116
|
+
if (lowerSegments.some((segment) => segment === ".env" || segment.startsWith(".env."))) return true;
|
|
115
117
|
if (lowerBase === ".env" || lowerBase.startsWith(".env.")) return true;
|
|
116
118
|
if (SENSITIVE_BASENAMES.has(lowerBase)) return true;
|
|
117
119
|
if (SENSITIVE_EXTENSIONS.has(path.extname(lowerBase))) return true;
|
package/web.js
CHANGED
|
@@ -141,6 +141,8 @@ function normalizePreferencePayload(body = {}, current = db.getPreferences()) {
|
|
|
141
141
|
taskProfile: normalizeTaskProfile(body.taskProfile || current.taskProfile || "auto"),
|
|
142
142
|
allowShellTool: typeof body.allowShellTool === "boolean" ? body.allowShellTool : Boolean(current.allowShellTool),
|
|
143
143
|
allowFileTools: typeof body.allowFileTools === "boolean" ? body.allowFileTools : current.allowFileTools !== false,
|
|
144
|
+
allowAuxiliaryTools:
|
|
145
|
+
typeof body.allowAuxiliaryTools === "boolean" ? body.allowAuxiliaryTools : current.allowAuxiliaryTools !== false,
|
|
144
146
|
allowWrapperTools:
|
|
145
147
|
typeof body.allowWrapperTools === "boolean" ? body.allowWrapperTools : Boolean(current.allowWrapperTools),
|
|
146
148
|
preferredWrapper: normalizeWrapperName(body.preferredWrapper || current.preferredWrapper || "codex"),
|
|
@@ -183,6 +185,7 @@ function publicKeyStatus(projectRoot = baseDir) {
|
|
|
183
185
|
return {
|
|
184
186
|
openai: status.openai,
|
|
185
187
|
deepseek: status.deepseek,
|
|
188
|
+
grsai: status.grsai,
|
|
186
189
|
mock: true,
|
|
187
190
|
localEnv: status.localEnv,
|
|
188
191
|
envVars: status.envVars,
|
|
@@ -217,6 +220,7 @@ function buildRunConfig(body, overrides = {}) {
|
|
|
217
220
|
allowDestructive: merged.allowDestructive,
|
|
218
221
|
allowShellTool: merged.allowShellTool,
|
|
219
222
|
allowFileTools: merged.allowFileTools,
|
|
223
|
+
allowAuxiliaryTools: merged.allowAuxiliaryTools,
|
|
220
224
|
allowWrapperTools: merged.allowWrapperTools,
|
|
221
225
|
preferredWrapper: merged.preferredWrapper,
|
|
222
226
|
wrapperTimeoutMs: merged.wrapperTimeoutMs,
|