@lazyingart/agintiflow 0.20.25 → 0.20.39
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 +9 -4
- package/docs/agent-runtime-pipe.md +5 -4
- package/docs/cli-i18n.md +1 -1
- package/docs/project-agent-lessons.md +1 -1
- package/docs/real-deepseek-capabilities.md +1 -1
- package/docs/self-development-supervision.md +1 -2
- package/docs/skills-and-tools.md +1 -1
- package/docs/supervised-capability-curriculum.md +111 -0
- package/package.json +9 -3
- package/public/app.js +4 -0
- package/references/backend-frontend-artifact-tunnel.md +3 -3
- package/references/general-task-capability-taxonomy.md +109 -0
- package/references/supervision-student-recursive-training.md +116 -0
- package/scripts/smoke-auto-update.js +96 -0
- package/scripts/smoke-auxiliary-tools.js +1 -0
- package/scripts/smoke-canvas-artifacts.js +67 -0
- package/scripts/smoke-capabilities.js +51 -2
- package/scripts/smoke-cli-chat.js +2 -0
- package/scripts/smoke-coding-tools.js +41 -0
- package/scripts/smoke-inbox.js +40 -1
- package/scripts/smoke-skills.js +53 -1
- package/scripts/smoke-toolchain-docker.js +1 -0
- package/scripts/smoke-web-api.js +6 -1
- package/skills/android/SKILL.md +2 -1
- package/skills/code-review/SKILL.md +24 -0
- package/skills/data-analysis/SKILL.md +29 -0
- package/skills/database/SKILL.md +27 -0
- package/skills/devops-deployment/SKILL.md +29 -0
- package/skills/docs-knowledge/SKILL.md +28 -0
- package/skills/dotnet-csharp/SKILL.md +24 -0
- package/skills/education-tutorial/SKILL.md +26 -0
- package/skills/go/SKILL.md +23 -0
- package/skills/ios-swift/SKILL.md +26 -0
- package/skills/java-jvm/SKILL.md +25 -0
- package/skills/php/SKILL.md +24 -0
- package/skills/presentation-slides/SKILL.md +26 -0
- package/skills/qa-testing/SKILL.md +27 -0
- package/skills/ruby/SKILL.md +24 -0
- package/skills/rust/SKILL.md +23 -0
- package/skills/security-review/SKILL.md +28 -0
- package/skills/supervision-student/SKILL.md +67 -0
- package/skills/writing-editing/SKILL.md +28 -0
- package/src/agent-runner.js +49 -7
- package/src/artifact-tunnel.js +75 -4
- package/src/auto-update.js +389 -0
- package/src/capabilities.js +6 -1
- package/src/cli.js +129 -14
- package/src/config.js +6 -2
- package/src/engineering-guidance.js +12 -2
- package/src/interactive-cli.js +42 -5
- package/src/model-client.js +2 -2
- package/src/model-routing.js +3 -1
- package/src/parallel-scouts.js +17 -1
- package/src/project.js +200 -20
- package/src/session-index.js +135 -0
- package/src/session-store.js +67 -3
- package/src/skill-library.js +16 -2
- package/src/sqlite.js +19 -0
- package/src/static-preview-server.js +8 -1
- package/src/task-profiles.js +318 -5
- package/src/web-db.js +58 -6
- package/src/workspace-tools.js +1 -0
- package/web.js +37 -11
package/src/cli.js
CHANGED
|
@@ -15,20 +15,25 @@ import { startInteractiveCli } from "./interactive-cli.js";
|
|
|
15
15
|
import { SessionStore } from "./session-store.js";
|
|
16
16
|
import {
|
|
17
17
|
doctorReport,
|
|
18
|
+
ensureProjectSessionStorage,
|
|
18
19
|
initProject,
|
|
19
20
|
listProjectSessions,
|
|
21
|
+
renameProjectSession,
|
|
20
22
|
providerKeyStatus,
|
|
21
23
|
setProviderKey,
|
|
22
24
|
showProjectSession,
|
|
25
|
+
sessionStoreOptions,
|
|
23
26
|
} from "./project.js";
|
|
24
27
|
import { listTaskProfiles } from "./task-profiles.js";
|
|
25
28
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
26
29
|
import { normalizeAuthProvider, promptHidden, runAuthWizard, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
27
30
|
import { listSkills, selectSkillsForGoal } from "./skill-library.js";
|
|
28
31
|
import { languageLabel, resolveLanguage } from "./i18n.js";
|
|
32
|
+
import { maybeAutoUpdate } from "./auto-update.js";
|
|
29
33
|
import fs from "node:fs/promises";
|
|
30
34
|
import path from "node:path";
|
|
31
35
|
import { fileURLToPath } from "node:url";
|
|
36
|
+
import readline from "node:readline/promises";
|
|
32
37
|
|
|
33
38
|
const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
34
39
|
const packageJson = JSON.parse(await fs.readFile(path.join(packageDir, "package.json"), "utf8"));
|
|
@@ -94,6 +99,7 @@ export function parseArgs(argv) {
|
|
|
94
99
|
latex: false,
|
|
95
100
|
image: false,
|
|
96
101
|
language: "",
|
|
102
|
+
autoUpdate: undefined,
|
|
97
103
|
};
|
|
98
104
|
|
|
99
105
|
const parts = [];
|
|
@@ -129,6 +135,14 @@ export function parseArgs(argv) {
|
|
|
129
135
|
}
|
|
130
136
|
continue;
|
|
131
137
|
}
|
|
138
|
+
if (arg === "--no-auto-update") {
|
|
139
|
+
result.autoUpdate = false;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (arg === "--auto-update") {
|
|
143
|
+
result.autoUpdate = true;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
132
146
|
if (arg === "--start-url") {
|
|
133
147
|
result.startUrl = readOption(argv, i);
|
|
134
148
|
i += 1;
|
|
@@ -364,7 +378,7 @@ export function parseArgs(argv) {
|
|
|
364
378
|
|
|
365
379
|
function printUsage() {
|
|
366
380
|
console.log(
|
|
367
|
-
'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti models OR aginti skills [query] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
|
|
381
|
+
'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti update OR aginti models OR aginti skills [query] OR aginti auth [deepseek|openai|qwen|venice|grsai] OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--no-auto-update] [--language en|ja|zh-Hans|zh-Hant|ko|fr|es|ar|vi|de|ru] [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|qwen|venice|mock] [--model MODEL] [--route-model MODEL] [--main-model MODEL] [--spare-model MODEL --spare-reasoning medium] [--aux-provider grsai|venice --aux-model MODEL] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex --wrapper-model gpt-5.5] [--list-models|--list-routes] "your task"'
|
|
368
382
|
);
|
|
369
383
|
console.log(`Languages: ${["en", "ja", "zh-Hans", "zh-Hant", "ko", "fr", "es", "ar", "vi", "de", "ru"].map((code) => `${code}=${languageLabel(code)}`).join(", ")}`);
|
|
370
384
|
}
|
|
@@ -379,29 +393,46 @@ function providerLabel(provider) {
|
|
|
379
393
|
}
|
|
380
394
|
|
|
381
395
|
function agentDefaults(args) {
|
|
396
|
+
const envSandboxMode = process.env.SANDBOX_MODE || "";
|
|
397
|
+
const envPackageInstallPolicy = process.env.PACKAGE_INSTALL_POLICY || "";
|
|
398
|
+
const envUseDockerSandbox = process.env.USE_DOCKER_SANDBOX;
|
|
399
|
+
const envScoutCount = Number(process.env.AGINTI_SCOUT_COUNT);
|
|
400
|
+
const taskProfile = args.taskProfile || process.env.AGINTI_TASK_PROFILE || (args.latex ? "latex" : "auto");
|
|
382
401
|
const defaults = {
|
|
383
402
|
...args,
|
|
403
|
+
provider: args.provider || process.env.AGENT_PROVIDER || "",
|
|
404
|
+
routingMode: args.routingMode || process.env.AGENT_ROUTING_MODE || "smart",
|
|
405
|
+
routeProvider: args.routeProvider || process.env.AGINTI_ROUTE_PROVIDER || "",
|
|
406
|
+
routeModel: args.routeModel || process.env.AGINTI_ROUTE_MODEL || "",
|
|
407
|
+
mainProvider: args.mainProvider || process.env.AGINTI_MAIN_PROVIDER || "",
|
|
408
|
+
mainModel: args.mainModel || process.env.AGINTI_MAIN_MODEL || "",
|
|
409
|
+
spareProvider: args.spareProvider || process.env.AGINTI_SPARE_PROVIDER || "",
|
|
410
|
+
spareModel: args.spareModel || process.env.AGINTI_SPARE_MODEL || "",
|
|
411
|
+
spareReasoning: args.spareReasoning || process.env.AGINTI_SPARE_REASONING || "",
|
|
412
|
+
taskProfile,
|
|
384
413
|
language: resolveLanguage(args.language || process.env.AGINTI_LANGUAGE || ""),
|
|
385
414
|
allowShellTool: args.allowShellTool ?? true,
|
|
386
415
|
allowFileTools: args.allowFileTools ?? true,
|
|
387
416
|
allowAuxiliaryTools: args.allowAuxiliaryTools ?? true,
|
|
388
417
|
allowWebSearch: args.allowWebSearch ?? true,
|
|
389
418
|
allowParallelScouts: args.allowParallelScouts ?? true,
|
|
390
|
-
parallelScoutCount: args.parallelScoutCount || 3,
|
|
391
|
-
sandboxMode: args.sandboxMode || "docker-workspace",
|
|
392
|
-
packageInstallPolicy: args.packageInstallPolicy || "allow",
|
|
393
|
-
useDockerSandbox:
|
|
419
|
+
parallelScoutCount: args.parallelScoutCount || (Number.isFinite(envScoutCount) && envScoutCount > 0 ? envScoutCount : 3),
|
|
420
|
+
sandboxMode: args.sandboxMode || envSandboxMode || "docker-workspace",
|
|
421
|
+
packageInstallPolicy: args.packageInstallPolicy || envPackageInstallPolicy || "allow",
|
|
422
|
+
useDockerSandbox:
|
|
423
|
+
args.useDockerSandbox ??
|
|
424
|
+
(envUseDockerSandbox === undefined ? true : String(envUseDockerSandbox).toLowerCase() !== "false"),
|
|
394
425
|
maxSteps:
|
|
395
426
|
args.maxSteps ||
|
|
396
427
|
recommendedMaxStepsForTask({
|
|
397
428
|
goal: args.goal || "",
|
|
398
|
-
taskProfile
|
|
429
|
+
taskProfile,
|
|
399
430
|
}),
|
|
400
431
|
};
|
|
401
432
|
|
|
402
433
|
if (defaults.sandboxMode === "host") {
|
|
403
434
|
defaults.useDockerSandbox = false;
|
|
404
|
-
defaults.packageInstallPolicy = args.packageInstallPolicy || "prompt";
|
|
435
|
+
defaults.packageInstallPolicy = args.packageInstallPolicy || envPackageInstallPolicy || "prompt";
|
|
405
436
|
}
|
|
406
437
|
|
|
407
438
|
return defaults;
|
|
@@ -469,7 +500,7 @@ function printInitResult(result) {
|
|
|
469
500
|
console.log(`AgInTiFlow project initialized: ${result.projectRoot}`);
|
|
470
501
|
console.log(`instructions=${result.instructionsPath}`);
|
|
471
502
|
console.log(`control=${result.controlDir}`);
|
|
472
|
-
console.log(`
|
|
503
|
+
console.log(`projectSessions=${result.sessionsDir}`);
|
|
473
504
|
console.log(`created=${result.created.length} updated=${result.updated.length} skipped=${result.skipped.length}`);
|
|
474
505
|
}
|
|
475
506
|
|
|
@@ -479,7 +510,8 @@ function printDoctorReport(report) {
|
|
|
479
510
|
console.log(`platform=${report.platform.label} family=${report.platform.linuxFamily || report.platform.platform}`);
|
|
480
511
|
console.log(`project=${report.project.root}`);
|
|
481
512
|
console.log(`instructions=${report.project.instructionsPath} present=${report.project.instructionsPresent}`);
|
|
482
|
-
console.log(`
|
|
513
|
+
console.log(`projectSessions=${report.project.sessionsDir}`);
|
|
514
|
+
console.log(`globalSessions=${report.project.globalSessionsDir}`);
|
|
483
515
|
console.log(`sessionDb=${report.project.sessionDbPath}`);
|
|
484
516
|
console.log(
|
|
485
517
|
`keys: deepseek=${report.keys.deepseek ? "available" : "missing"} openai=${
|
|
@@ -569,7 +601,7 @@ function printAuthWizardResult(result) {
|
|
|
569
601
|
}
|
|
570
602
|
|
|
571
603
|
async function handleSessionsCommand(argv) {
|
|
572
|
-
const [verb = "list", sessionId = ""] = argv;
|
|
604
|
+
const [verb = "list", sessionId = "", ...rest] = argv;
|
|
573
605
|
if (verb === "list") {
|
|
574
606
|
const sessions = await listProjectSessions(process.cwd(), 80);
|
|
575
607
|
if (sessions.length === 0) {
|
|
@@ -593,14 +625,49 @@ async function handleSessionsCommand(argv) {
|
|
|
593
625
|
return;
|
|
594
626
|
}
|
|
595
627
|
|
|
596
|
-
|
|
628
|
+
if (verb === "rename") {
|
|
629
|
+
const title = rest.join(" ").trim();
|
|
630
|
+
if (!sessionId || !title) {
|
|
631
|
+
console.error('Usage: aginti sessions rename <session-id> "new title"');
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
const result = await renameProjectSession(process.cwd(), sessionId, title);
|
|
635
|
+
console.log(`renamed ${result.sessionId}: ${result.title}`);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
console.error('Usage: aginti sessions list OR aginti sessions show <session-id> OR aginti sessions rename <session-id> "title"');
|
|
597
640
|
process.exit(1);
|
|
598
641
|
}
|
|
599
642
|
|
|
643
|
+
async function promptSelectSession(sessions) {
|
|
644
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return sessions[0]?.sessionId || "";
|
|
645
|
+
console.log("Select a session to resume:");
|
|
646
|
+
sessions.slice(0, 20).forEach((session, index) => {
|
|
647
|
+
const title = session.title || session.goal || "(untitled)";
|
|
648
|
+
console.log(
|
|
649
|
+
`${index + 1}. ${session.sessionId} ${session.provider || "unknown"}/${session.model || "unknown"} ${session.updatedAt || ""} ${title.slice(0, 90)}`
|
|
650
|
+
);
|
|
651
|
+
});
|
|
652
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
653
|
+
try {
|
|
654
|
+
const answer = await rl.question("Session number: ");
|
|
655
|
+
const index = Number(answer.trim()) - 1;
|
|
656
|
+
return sessions[index]?.sessionId || "";
|
|
657
|
+
} finally {
|
|
658
|
+
rl.close();
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
600
662
|
async function resolveResumeSessionId(sessionId) {
|
|
601
663
|
if (sessionId && sessionId !== "latest") return sessionId;
|
|
602
|
-
const sessions = await listProjectSessions(process.cwd(),
|
|
603
|
-
if (
|
|
664
|
+
const sessions = await listProjectSessions(process.cwd(), 50);
|
|
665
|
+
if (sessionId === "latest" || sessions.length <= 1 || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
666
|
+
if (sessions[0]?.sessionId) return sessions[0].sessionId;
|
|
667
|
+
} else {
|
|
668
|
+
const selected = await promptSelectSession(sessions);
|
|
669
|
+
if (selected) return selected;
|
|
670
|
+
}
|
|
604
671
|
throw new Error("No project-local sessions found. Run `aginti sessions list` to check this folder.");
|
|
605
672
|
}
|
|
606
673
|
|
|
@@ -612,12 +679,31 @@ async function handleQueueCommand(argv) {
|
|
|
612
679
|
process.exit(1);
|
|
613
680
|
}
|
|
614
681
|
|
|
615
|
-
const
|
|
682
|
+
const paths = await ensureProjectSessionStorage(process.cwd());
|
|
683
|
+
const store = new SessionStore(paths.globalSessionsDir, sessionId, sessionStoreOptions(process.cwd(), sessionId));
|
|
616
684
|
await store.appendInbox(content, { source: "cli" });
|
|
617
685
|
await store.appendEvent("conversation.queued_input", { prompt: content, source: "cli" }).catch(() => {});
|
|
618
686
|
console.log(`queued message for ${sessionId}`);
|
|
619
687
|
}
|
|
620
688
|
|
|
689
|
+
async function handleStorageCommand(argv) {
|
|
690
|
+
const [verb = "status", ...rest] = argv;
|
|
691
|
+
if (verb !== "migrate" && verb !== "status") {
|
|
692
|
+
console.error("Usage: aginti storage migrate [project-root ...] OR aginti storage status");
|
|
693
|
+
process.exit(1);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const roots = verb === "migrate" && rest.length > 0 ? rest : [process.cwd()];
|
|
697
|
+
for (const root of roots) {
|
|
698
|
+
const projectRoot = path.resolve(root);
|
|
699
|
+
const paths = await ensureProjectSessionStorage(projectRoot);
|
|
700
|
+
const sessions = await listProjectSessions(projectRoot, 1000);
|
|
701
|
+
console.log(
|
|
702
|
+
`${verb === "migrate" ? "migrated" : "storage"} project=${paths.root} projectSessions=${paths.sessionsDir} globalSessions=${paths.globalSessionsDir} sessions=${sessions.length}`
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
621
707
|
export async function main(argv = process.argv.slice(2)) {
|
|
622
708
|
if (argv[0] === "help" || argv.includes("--help") || argv.includes("-h")) {
|
|
623
709
|
printUsage();
|
|
@@ -629,6 +715,30 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
629
715
|
return;
|
|
630
716
|
}
|
|
631
717
|
|
|
718
|
+
if (argv[0] === "update" || argv[0] === "upgrade") {
|
|
719
|
+
const updateResult = await maybeAutoUpdate({
|
|
720
|
+
argv,
|
|
721
|
+
force: true,
|
|
722
|
+
manual: true,
|
|
723
|
+
packageDir,
|
|
724
|
+
packageName: packageJson.name,
|
|
725
|
+
packageVersion: packageJson.version,
|
|
726
|
+
restart: false,
|
|
727
|
+
});
|
|
728
|
+
if (updateResult.error) process.exit(1);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const autoUpdateResult = await maybeAutoUpdate({
|
|
733
|
+
argv,
|
|
734
|
+
force: argv.includes("--auto-update"),
|
|
735
|
+
packageDir,
|
|
736
|
+
packageName: packageJson.name,
|
|
737
|
+
packageVersion: packageJson.version,
|
|
738
|
+
restart: true,
|
|
739
|
+
});
|
|
740
|
+
if (autoUpdateResult.restarted) process.exit(autoUpdateResult.exitCode ?? 0);
|
|
741
|
+
|
|
632
742
|
if (argv[0] === "init") {
|
|
633
743
|
printInitResult(await initProject(process.cwd()));
|
|
634
744
|
return;
|
|
@@ -706,6 +816,11 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
706
816
|
return;
|
|
707
817
|
}
|
|
708
818
|
|
|
819
|
+
if (argv[0] === "storage") {
|
|
820
|
+
await handleStorageCommand(argv.slice(1));
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
|
|
709
824
|
if (argv[0] === "models" || argv[0] === "model") {
|
|
710
825
|
printModels();
|
|
711
826
|
return;
|
package/src/config.js
CHANGED
|
@@ -3,7 +3,7 @@ import crypto from "node:crypto";
|
|
|
3
3
|
import { getModelRoleDefaults, getProviderDefaults, normalizeRoutingMode, selectModelRoute } from "./model-routing.js";
|
|
4
4
|
import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./command-policy.js";
|
|
5
5
|
import { normalizeWrapperName } from "./tool-wrappers.js";
|
|
6
|
-
import { loadProjectEnv, resolveProjectRoot } from "./project.js";
|
|
6
|
+
import { loadProjectEnv, projectPaths, resolveProjectRoot } from "./project.js";
|
|
7
7
|
import { normalizeTaskProfile } from "./task-profiles.js";
|
|
8
8
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
9
9
|
import { resolveLanguage } from "./i18n.js";
|
|
@@ -32,6 +32,7 @@ function parseList(value) {
|
|
|
32
32
|
|
|
33
33
|
export function resolveRuntimeConfig(args, overrides = {}) {
|
|
34
34
|
const baseDir = resolveProjectRoot(overrides.baseDir || process.cwd());
|
|
35
|
+
const paths = projectPaths(baseDir);
|
|
35
36
|
loadProjectEnv(baseDir);
|
|
36
37
|
const requestedProvider =
|
|
37
38
|
overrides.provider ||
|
|
@@ -155,7 +156,10 @@ export function resolveRuntimeConfig(args, overrides = {}) {
|
|
|
155
156
|
useDockerSandbox: sandboxMode !== "host",
|
|
156
157
|
dockerSandboxImage: overrides.dockerSandboxImage || process.env.DOCKER_SANDBOX_IMAGE || "agintiflow-sandbox:latest",
|
|
157
158
|
commandCwd: path.resolve(overrides.commandCwd || args.commandCwd || process.env.COMMAND_CWD || baseDir),
|
|
158
|
-
sessionsDir:
|
|
159
|
+
sessionsDir: paths.globalSessionsDir,
|
|
160
|
+
projectSessionsDir: paths.sessionsDir,
|
|
161
|
+
sessionDbPath: paths.sessionDbPath,
|
|
162
|
+
globalSessionIndexPath: paths.globalSessionIndexPath,
|
|
159
163
|
onLog: overrides.onLog,
|
|
160
164
|
onEvent: overrides.onEvent,
|
|
161
165
|
};
|
|
@@ -11,7 +11,7 @@ const LANGUAGE_HINTS = [
|
|
|
11
11
|
id: "python",
|
|
12
12
|
pattern: /\b(python|pytest|pip|uv|poetry|conda|venv|jupyter|fastapi|django|flask|pandas|numpy)\b/i,
|
|
13
13
|
text:
|
|
14
|
-
"Python: inspect pyproject/requirements, prefer project-local venv/uv/conda or Docker, run python -m pytest or focused module checks, avoid global package installs on host.",
|
|
14
|
+
"Python: inspect pyproject/requirements, prefer project-local venv/uv/conda or Docker, run python -m pytest or focused module checks, avoid global package installs on host, and check Python caches recursively with find . -type d -name __pycache__ -o -name '*.pyc' before claiming none exist.",
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
id: "rust",
|
|
@@ -29,7 +29,7 @@ const LANGUAGE_HINTS = [
|
|
|
29
29
|
id: "android",
|
|
30
30
|
pattern: /\b(android|apk|adb|avd|emulator|gradle wrapper|gradlew|android sdk|kotlin android|jetpack|compose)\b/i,
|
|
31
31
|
text:
|
|
32
|
-
"Android: inspect git status, Gradle/settings/manifests, ANDROID_HOME/SDK paths, Java/Kotlin versions, adb devices, and emulator/AVD list first. Never start host sudo or host OS package installs; use the existing SDK, a project-local Gradle wrapper, user-writable caches, or return a precise setup report. Build, install with adb, launch with am, verify with logcat/screencap when possible, then commit.",
|
|
32
|
+
"Android: inspect git status, Gradle/settings/manifests, ANDROID_HOME/SDK paths, Java/Kotlin versions, adb devices, and emulator/AVD list first. Never start host sudo or host OS package installs; use the existing SDK, a project-local Gradle wrapper, user-writable caches, or return a precise setup report. Build, install with adb, launch with am, verify with logcat/screencap when possible, save screenshots under a durable workspace path, then commit.",
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
id: "java-jvm",
|
|
@@ -55,6 +55,12 @@ const LANGUAGE_HINTS = [
|
|
|
55
55
|
text:
|
|
56
56
|
"Git: always run git status --short and git diff --stat before commit/push. Commit only requested changes, use a clear message, run git fetch before push when remote state matters, prefer git pull --ff-only, and stop to ask if there are conflicts, unrelated dirty files, divergent branches, or ambiguous merge choices.",
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
id: "supervision",
|
|
60
|
+
pattern: /\b(supervise|supervisor|student agent|homework|curriculum|self[- ]?supervision|train the agent|agent training|monitor tmux)\b/i,
|
|
61
|
+
text:
|
|
62
|
+
"Supervision: do not simply believe the student agent. Define acceptance criteria, give normal user-level prompts, monitor tmux/session logs, verify artifacts and git state externally, record evidence, and turn repeated failures into durable profile, skill, tool, policy, or test improvements.",
|
|
63
|
+
},
|
|
58
64
|
{
|
|
59
65
|
id: "r-stats",
|
|
60
66
|
pattern: /\b(rstats|r language|cmdstanr|stan|renv|tidyverse|shiny)\b/i,
|
|
@@ -92,7 +98,11 @@ export function engineeringGuidanceForTask(goal = "", taskProfile = "auto") {
|
|
|
92
98
|
"Build a compact context pack before major edits: project instructions, manifests/scripts, git status/diff, relevant symbols/search hits, target files, and the narrowest checks. Do not paste whole trees or huge files into model context.",
|
|
93
99
|
"For system repair, act like a doctor: gather evidence first, avoid silent destructive host changes, prefer Docker or project-local scripts for installs, and make every stronger action explicit in logs.",
|
|
94
100
|
"Never send sudo passwords or wait at interactive password prompts. If host-level permission is truly required, stop that path, explain the blocker, and provide a manual command instead of hanging.",
|
|
101
|
+
"Shell commands already start in the configured workspace. Prefer workspace-relative commands such as python3 scripts/check.py or cd subdir && make; do not prefix commands with absolute host cd paths unless the tool explicitly requires it.",
|
|
102
|
+
"When you create or fix scripts, reports, tables, generated files, or analysis outputs, inspect the actual output. If it contains obvious duplicates, noisy rows, stale names, broken markdown, or contradictions, patch the source/output and rerun the check rather than merely explaining the defect in the report.",
|
|
95
103
|
"Before claiming a coding task is finished, run git status --short when git is available. Leave the worktree clean, or explicitly report and justify each remaining untracked/unstaged artifact.",
|
|
104
|
+
"Do not claim there are no transient artifacts unless you checked recursively for the relevant stack, such as find . -type d -name __pycache__ -o -name '*.pyc' for Python. A clean git status means tracked work is clean; ignored caches such as __pycache__ may still exist and should be removed or described accurately if relevant.",
|
|
105
|
+
"For generated screenshots, images, PDFs, reports, archives, and app packages, choose a descriptive non-conflicting workspace path when the user did not specify one. Verify the file still exists after cleanup before claiming it was saved.",
|
|
96
106
|
];
|
|
97
107
|
|
|
98
108
|
if (matched.length > 0) {
|
package/src/interactive-cli.js
CHANGED
|
@@ -3,7 +3,15 @@ import { emitKeypressEvents } from "node:readline";
|
|
|
3
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
4
4
|
import { runAgent } from "./agent-runner.js";
|
|
5
5
|
import { loadConfig } from "./config.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
initProject,
|
|
8
|
+
listProjectSessions,
|
|
9
|
+
projectPaths,
|
|
10
|
+
providerKeyStatus,
|
|
11
|
+
readProjectInstructions,
|
|
12
|
+
renameProjectSession,
|
|
13
|
+
sessionStoreOptions,
|
|
14
|
+
} from "./project.js";
|
|
7
15
|
import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./command-policy.js";
|
|
8
16
|
import { defaultMaxStepsForProfile, normalizeTaskProfile } from "./task-profiles.js";
|
|
9
17
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
@@ -61,6 +69,7 @@ const SLASH_COMMANDS = [
|
|
|
61
69
|
"/new",
|
|
62
70
|
"/resume",
|
|
63
71
|
"/sessions",
|
|
72
|
+
"/rename",
|
|
64
73
|
"/skills",
|
|
65
74
|
"/skill",
|
|
66
75
|
"/profile",
|
|
@@ -624,6 +633,7 @@ function printHelp() {
|
|
|
624
633
|
` ${command("/auxiliary [status|grsai|venice|model [provider/model]|on|off|image]", "Manage optional auxiliary skills, including image generation.", "helpAuxiliary")}`,
|
|
625
634
|
` ${command("/new", "Start a fresh session on the next message.", "helpNew")}`,
|
|
626
635
|
` ${command("/resume <session-id>", "Continue a saved session.", "helpResume")}`,
|
|
636
|
+
` ${command("/rename [title|auto]", "Rename the current session.", "helpRename")}`,
|
|
627
637
|
` ${command("/sessions", "List recent sessions in this project.", "helpSessions")}`,
|
|
628
638
|
` ${command("/skills [query]", "List Markdown skills selected for a topic.", "helpSkills")}`,
|
|
629
639
|
` ${command("/profile <name>", "Set task profile, e.g. code, website, latex, maintenance.", "helpProfile")}`,
|
|
@@ -1564,8 +1574,17 @@ function isAbortError(error) {
|
|
|
1564
1574
|
}
|
|
1565
1575
|
|
|
1566
1576
|
function formatSessionLine(session) {
|
|
1567
|
-
const
|
|
1568
|
-
|
|
1577
|
+
const title = session.title || session.goal || "";
|
|
1578
|
+
const preview = title ? ` ${title.slice(0, 72)}` : "";
|
|
1579
|
+
return `${session.sessionId} ${session.provider || "unknown"}/${session.model || "unknown"} ${session.updatedAt || ""}${preview}`.trim();
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
function autoSessionTitle(state) {
|
|
1583
|
+
const route = `${state.routeProvider || state.provider || "auto"}/${state.routeModel || state.model || "auto"}`;
|
|
1584
|
+
const main = `${state.mainProvider || state.provider || "auto"}/${state.mainModel || state.model || "auto"}`;
|
|
1585
|
+
const goal = String(state.activeGoal || "").replace(/\s+/g, " ").trim();
|
|
1586
|
+
const prefix = state.taskProfile && state.taskProfile !== "auto" ? state.taskProfile : "auto";
|
|
1587
|
+
return [prefix, `route ${route}`, `main ${main}`, goal.slice(0, 36)].filter(Boolean).join(" · ").slice(0, 90);
|
|
1569
1588
|
}
|
|
1570
1589
|
|
|
1571
1590
|
async function latestSession() {
|
|
@@ -1582,7 +1601,8 @@ function printHistoryEntry(entry) {
|
|
|
1582
1601
|
|
|
1583
1602
|
async function printResumeHistory(state, { limit = 0 } = {}) {
|
|
1584
1603
|
if (!state.sessionId) return;
|
|
1585
|
-
const
|
|
1604
|
+
const paths = projectPaths(process.cwd());
|
|
1605
|
+
const store = new SessionStore(paths.globalSessionsDir, state.sessionId, sessionStoreOptions(process.cwd(), state.sessionId));
|
|
1586
1606
|
const saved = await store.loadState().catch(() => null);
|
|
1587
1607
|
const events = await store.loadEvents().catch(() => []);
|
|
1588
1608
|
const chat = Array.isArray(saved?.chat) ? saved.chat.filter((entry) => entry?.content) : [];
|
|
@@ -2583,6 +2603,20 @@ async function handleCommand(line, state, packageDir) {
|
|
|
2583
2603
|
}
|
|
2584
2604
|
return true;
|
|
2585
2605
|
}
|
|
2606
|
+
if (command === "rename") {
|
|
2607
|
+
if (!state.sessionId) {
|
|
2608
|
+
printAgentMessage("No active session to rename. Start or resume a session first.");
|
|
2609
|
+
return true;
|
|
2610
|
+
}
|
|
2611
|
+
const title = value === "auto" || !value ? autoSessionTitle(state) : value;
|
|
2612
|
+
try {
|
|
2613
|
+
const result = await renameProjectSession(process.cwd(), state.sessionId, title);
|
|
2614
|
+
printSystemLine(`session=${result.sessionId} title=${result.title}`);
|
|
2615
|
+
} catch (error) {
|
|
2616
|
+
printAgentMessage(error instanceof Error ? error.message : String(error));
|
|
2617
|
+
}
|
|
2618
|
+
return true;
|
|
2619
|
+
}
|
|
2586
2620
|
if (command === "skills" || command === "skill") {
|
|
2587
2621
|
const skills = value
|
|
2588
2622
|
? selectSkillsForGoal(value, { taskProfile: state.taskProfile, limit: 12, includeBody: false })
|
|
@@ -2914,7 +2948,10 @@ async function runPrompt(prompt, state, packageDir) {
|
|
|
2914
2948
|
state.activeGoal = compactLine(prompt, 84);
|
|
2915
2949
|
state.lastEvent = "";
|
|
2916
2950
|
|
|
2917
|
-
const store = new SessionStore(config.sessionsDir, state.sessionId
|
|
2951
|
+
const store = new SessionStore(config.sessionsDir, state.sessionId, {
|
|
2952
|
+
projectRoot: config.baseDir,
|
|
2953
|
+
projectSessionsDir: config.projectSessionsDir,
|
|
2954
|
+
});
|
|
2918
2955
|
const liveInput = new LiveRunInput({ state, store, controller });
|
|
2919
2956
|
const liveStarted = liveInput.start();
|
|
2920
2957
|
const detachInterrupts = liveStarted ? () => {} : attachRunInterrupts(controller);
|
package/src/model-client.js
CHANGED
|
@@ -1020,7 +1020,7 @@ export async function requestNextStep(client, config, messages) {
|
|
|
1020
1020
|
function: {
|
|
1021
1021
|
name: "send_to_canvas",
|
|
1022
1022
|
description:
|
|
1023
|
-
"Send an optional artifact notification to the frontend canvas/artifacts tunnel. Use for important markdown/text, generated images, screenshots, figures, diffs, or workspace file paths the user should preview. Proactively use this for draw/plot/graph/chart/diagram/figure requests even when the user did not mention canvas. This does not replace finish.",
|
|
1023
|
+
"Send an optional artifact notification to the frontend canvas/artifacts tunnel. Use for important markdown/text, generated images, screenshots, figures, diffs, or workspace file paths the user should preview. Workspace files sent here are copied into session artifacts for durable preview, but if the user asked to save/capture/generate a file, keep a descriptive non-conflicting copy in the working directory too. Proactively use this for draw/plot/graph/chart/diagram/figure requests even when the user did not mention canvas. This does not replace finish.",
|
|
1024
1024
|
parameters: {
|
|
1025
1025
|
type: "object",
|
|
1026
1026
|
properties: {
|
|
@@ -1031,7 +1031,7 @@ export async function requestNextStep(client, config, messages) {
|
|
|
1031
1031
|
description: "Renderer hint. Use image/file with path, markdown/text/json/diff with content.",
|
|
1032
1032
|
},
|
|
1033
1033
|
content: { type: "string", description: "Inline text or markdown content to render." },
|
|
1034
|
-
path: { type: "string", description: "Optional workspace-relative file path to preview." },
|
|
1034
|
+
path: { type: "string", description: "Optional existing workspace-relative file path to preview and persist into the session artifact store." },
|
|
1035
1035
|
note: { type: "string", description: "Short notification message for the artifact explorer." },
|
|
1036
1036
|
selected: { type: "boolean", description: "Whether the frontend should select this item immediately." },
|
|
1037
1037
|
},
|
package/src/model-routing.js
CHANGED
|
@@ -776,7 +776,9 @@ export function modelsForProviderGroup(groupId) {
|
|
|
776
776
|
export function scoreTaskComplexity(goal = "", taskProfile = "auto") {
|
|
777
777
|
const text = String(goal).toLowerCase();
|
|
778
778
|
let score = text.length > 600 ? 2 : text.length > 240 ? 1 : 0;
|
|
779
|
-
|
|
779
|
+
const profile = String(taskProfile || "").toLowerCase();
|
|
780
|
+
if (["large-codebase", "engineering", "codebase", "code", "qa", "database", "devops", "security"].includes(profile)) score += 3;
|
|
781
|
+
if (["app", "data", "paper", "research", "latex", "github", "maintenance", "supervision"].includes(profile)) score += 2;
|
|
780
782
|
for (const keyword of COMPLEXITY_KEYWORDS) {
|
|
781
783
|
if (text.includes(keyword)) score += 1;
|
|
782
784
|
}
|
package/src/parallel-scouts.js
CHANGED
|
@@ -69,7 +69,23 @@ function shouldUseComplexScouts(config, state) {
|
|
|
69
69
|
const goal = String(config.goal || state.goal || "");
|
|
70
70
|
return (
|
|
71
71
|
Number(config.routeComplexityScore || 0) >= 3 ||
|
|
72
|
-
[
|
|
72
|
+
[
|
|
73
|
+
"large-codebase",
|
|
74
|
+
"maintenance",
|
|
75
|
+
"code",
|
|
76
|
+
"qa",
|
|
77
|
+
"database",
|
|
78
|
+
"devops",
|
|
79
|
+
"security",
|
|
80
|
+
"github",
|
|
81
|
+
"java",
|
|
82
|
+
"ios",
|
|
83
|
+
"go",
|
|
84
|
+
"rust",
|
|
85
|
+
"dotnet",
|
|
86
|
+
"php",
|
|
87
|
+
"ruby",
|
|
88
|
+
].includes(profile) ||
|
|
73
89
|
/\b(large|complex|complicated|debug|bug|failing|system|install|setup|codebase|repo|multi[- ]file|architecture|refactor|migration)\b/i.test(
|
|
74
90
|
goal
|
|
75
91
|
)
|