@lazyingart/agintiflow 0.20.201 → 0.20.202

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/.env.example CHANGED
@@ -6,6 +6,7 @@ LOCALLLM_BASE_URL=http://127.0.0.1:8008/v1
6
6
  LOCALLLM_API_KEY=local-dev-key
7
7
  AGINTI_LOCALLLM_ROUTE_MODEL=localllm-fast
8
8
  AGINTI_LOCALLLM_MAIN_MODEL=localllm-deep
9
+ AGINTI_LOCALLLM_CODE_MODEL=localllm-code
9
10
  AGINTI_LOCALLLM_MAX_MODEL=localllm-max
10
11
  AGINTI_LOCALLLM_ALLOW_AUTO_MAX=false
11
12
  AGINTI_LOCALLLM_VISION_MODEL=localllm-vision-xl
@@ -13,8 +14,10 @@ AGINTI_LOCALLLM_CONTEXT_TOKENS=32768
13
14
  AGINTI_LOCALLLM_MAX_OUTPUT_TOKENS=8192
14
15
  AGINTI_LOCALLLM_TOOL_SCHEMA_TOKENS=4096
15
16
 
16
- # Auto-Max is off unless explicitly enabled. Enabled runs still require authenticated
17
- # alias discovery plus a fresh RAM/swap/GPU headroom check before selecting Max.
17
+ # Genuine implementation work uses the code alias only after authenticated model
18
+ # discovery confirms that exact configured alias; otherwise it remains on Deep.
19
+ # Auto-Max is off unless explicitly enabled. Enabled non-code runs still require
20
+ # authenticated alias discovery plus a fresh RAM/swap/GPU headroom check.
18
21
  # Hosted provider keys below never act as a LocalLLM failure fallback.
19
22
 
20
23
  # Optional explicit hosted upgrades. They are never automatic fallbacks.
package/README.md CHANGED
@@ -85,7 +85,7 @@ aginti init --template aaps
85
85
  aginti init --template supervision
86
86
  ```
87
87
 
88
- By default, AgInTiFlow connects to the sibling LocalLLM gateway at `http://127.0.0.1:8008/v1`, using `localllm-fast` for routing and the installed 30B-A3B Q4 `localllm-deep` for substantive coding and agent work. The Q8 `localllm-max` remains resource-gated on every new or resumed run (24 GiB available RAM, no more than 75% swap use, and 40 GiB aggregate free NVIDIA memory). The automatic Vision policy requires both a trusted image-input signal and confirmed model capability; the shipped CLI/web currently use the readiness-checked local `read_image` tool or an explicit `localllm-vision-xl` selection rather than inferring vision from prompt keywords. Routing does not load a model. Startup checks the LocalLLM service, its Ollama runtime, selected aliases, and any required Max headroom before inference. A local failure stops with an actionable error; it never silently sends the task to a hosted provider.
88
+ By default, AgInTiFlow connects to the sibling LocalLLM gateway at `http://127.0.0.1:8008/v1`, using `localllm-fast` for routing and the installed 30B-A3B Q4 `localllm-deep` for substantive general agent work. High-confidence coding and repository implementation requests begin on Deep, then switch to the provider-neutral `localllm-code` capability alias only after bearer-authenticated `/v1/models` discovery confirms that exact configured alias. If it is absent, the run stays on Deep; writing, research, documentation, design, explanation-only code questions, explicit providers, and manual model choices are not silently rerouted. Set `AGINTI_LOCALLLM_CODE_MODEL` to another LocalLLM capability alias without coupling AgInTiFlow to an engine tag. The Q8 `localllm-max` remains resource-gated on every new or resumed run (24 GiB available RAM, no more than 75% swap use, and 40 GiB aggregate free NVIDIA memory). The automatic Vision policy requires both a trusted image-input signal and confirmed model capability; the shipped CLI/web currently use the readiness-checked local `read_image` tool or an explicit `localllm-vision-xl` selection rather than inferring vision from prompt keywords. Routing does not load a model. Startup checks the LocalLLM service, its Ollama runtime, selected aliases, and any required Max headroom before inference. A local failure stops with an actionable error; it never silently sends the task to a hosted provider.
89
89
 
90
90
  Hosted providers are optional, explicit upgrades. When you select DeepSeek, OpenAI, OpenRouter, Qwen, or Venice, the auth wizard can save that provider's key account-wide in `~/.agintiflow/.env` with restricted permissions. Current project `.aginti/.env` files can still override account defaults when needed. You can rerun setup any time:
91
91
 
@@ -253,7 +253,7 @@ The website keeps the visual walkthrough in a carousel so this README can stay f
253
253
  | File tools | `inspect_project`, `list_files`, `read_file`, `search_files`, `write_file`, `apply_patch`, `open_workspace_file`, `preview_workspace`, and `read_image`. |
254
254
  | Shell tools | Guarded host or Docker workspace shell execution with package-install policy and command safety checks. |
255
255
  | Browser tools | Playwright browser actions with lazy startup and optional domain allowlists. |
256
- | Model routing | LocalLLM Fast/Deep defaults, explicit and resource-gated Max, image-capability-gated Vision XL, explicit DeepSeek/OpenAI/OpenRouter/Qwen/Venice/mock routes, and optional spare/wrapper/auxiliary models. |
256
+ | Model routing | LocalLLM Fast/Deep defaults, authenticated implementation-only Code capability, explicit and resource-gated Max, image-capability-gated Vision XL, explicit DeepSeek/OpenAI/OpenRouter/Qwen/Venice/mock routes, and optional spare/wrapper/auxiliary models. |
257
257
  | Writing specialist | A dedicated writing-only LLM call for prose, chapters, scripts, books, essays, research-paper sections, and revisions, with formatter handoff notes for Markdown/LaTeX/Final Draft. |
258
258
  | Patch workflow | Codex-style patch envelopes, unified diffs, exact replacements, hashes, compact diffs, and path guardrails. |
259
259
  | Parallel scouts | Optional scout calls for architecture, implementation, review, tests, git flow, research, symbol tracing, and dependency risk. |
@@ -275,6 +275,7 @@ AgInTiFlow does not treat "the model" as one global setting. It has roles:
275
275
  | --- | --- | --- |
276
276
  | Route | `localllm/localllm-fast` | Local planner, triage, short tasks, and routing decisions. |
277
277
  | Main | `localllm/localllm-deep` | Local complex executor for coding, debugging, writing, research, and long tasks. |
278
+ | Code capability | `localllm/localllm-code` | Smart implementation-only upgrade after authenticated alias discovery; unavailable aliases fall back to Deep. |
278
279
  | Spare | `localllm/localllm-deep` medium | Local cross-check lane; a hosted spare requires explicit selection. |
279
280
  | Wrapper | `codex/gpt-5.5` medium | Optional external coding-agent advisor; `research_wrapper` defaults to `gpt-5.4-mini` medium for image/web second opinions. |
280
281
  | Auxiliary | `grsai/nano-banana-2` (off) | Explicitly enabled image generation and other non-text helper tools; no credential-driven provider failover. |
@@ -372,6 +373,7 @@ LOCALLLM_BASE_URL=http://127.0.0.1:8008/v1
372
373
  LOCALLLM_API_KEY=local-dev-key
373
374
  AGINTI_LOCALLLM_ROUTE_MODEL=localllm-fast
374
375
  AGINTI_LOCALLLM_MAIN_MODEL=localllm-deep
376
+ AGINTI_LOCALLLM_CODE_MODEL=localllm-code
375
377
 
376
378
  # Optional explicit hosted upgrades; never automatic LocalLLM fallbacks.
377
379
  DEEPSEEK_API_KEY=...
@@ -30,6 +30,7 @@ health: http://127.0.0.1:8008/healthz
30
30
  models: authenticated GET /v1/models
31
31
  route lane: localllm-fast
32
32
  main lane: localllm-deep
33
+ coding lane: localllm-code (authenticated, implementation-only capability upgrade)
33
34
  maximum lane: localllm-max
34
35
  vision: localllm-vision-xl
35
36
  embedding: localllm-embed (sibling-service alias; not an AgInTi text-routing tier)
@@ -39,7 +40,7 @@ The default bearer value used by the local deployment is an interoperability pla
39
40
 
40
41
  Readiness requires both `health.ok === true` and `health.ollama.ok === true`, followed by an authenticated `/v1/models` check that confirms the requested alias exists. Health alone is not sufficient.
41
42
 
42
- The installed local ladder is capability- and resource-aware. `localllm-fast` handles short routing and bounded work; substantive coding and agent tasks use the 30B-A3B Q4 `localllm-deep` lane. The 30B-A3B Q8 `localllm-max` lane remains explicitly selectable, but every new or resumed Max run performs a fresh pre-inference gate after authenticated alias discovery. It requires at least 24 GiB available host RAM, swap use no higher than 75%, and 40 GiB aggregate free NVIDIA memory (Q8 weights plus working reserve). Missing GPU telemetry fails closed. Automatic Max is off by default and requires `AGINTI_LOCALLLM_ALLOW_AUTO_MAX=true`; an eligible run confirms the configured Max alias and fresh headroom before upgrading the actual model client from Deep. If the optional upgrade cannot be proven safe, it continues on Deep rather than failing. The automatic Vision policy requires both a trusted image-input signal and confirmed model capability. The shipped CLI/web currently reach vision through the local `read_image` tool or explicit `localllm-vision-xl` selection; both paths readiness-check the selected alias, and prompt keywords alone never activate Vision XL.
43
+ The installed local ladder is capability- and resource-aware. `localllm-fast` handles short routing and bounded work; substantive general agent tasks use the 30B-A3B Q4 `localllm-deep` lane. High-confidence implementation work begins on Deep and may upgrade to the independently configured `AGINTI_LOCALLLM_CODE_MODEL` alias only when the same bearer-authenticated `/v1/models` response reports it. Missing or unverified availability stays on Deep, never a hosted provider. The implementation classifier excludes writing, research, paper, book, novel, design, documentation, slides, education, and image profiles, and requires an implementation action rather than a bare code mention or explanation request. The effective selection and a secret-free candidate policy are durable: selected coding sessions resume exactly, while a Deep fallback may re-evaluate authenticated availability on a later resume. Manual models and explicit providers remain exact. The 30B-A3B Q8 `localllm-max` lane remains explicitly selectable, but every new or resumed Max run performs a fresh pre-inference gate after authenticated alias discovery. It requires at least 24 GiB available host RAM, swap use no higher than 75%, and 40 GiB aggregate free NVIDIA memory (Q8 weights plus working reserve). Missing GPU telemetry fails closed. Automatic Max is off by default and requires `AGINTI_LOCALLLM_ALLOW_AUTO_MAX=true`; an eligible non-code run confirms the configured Max alias and fresh headroom before upgrading the actual model client from Deep. If the optional upgrade cannot be proven safe, it continues on Deep rather than failing. The automatic Vision policy requires both a trusted image-input signal and confirmed model capability. The shipped CLI/web currently reach vision through the local `read_image` tool or explicit `localllm-vision-xl` selection; both paths readiness-check the selected alias, and prompt keywords alone never activate Vision XL.
43
44
 
44
45
  ## Provider-neutral capabilities
45
46
 
@@ -171,6 +172,7 @@ Offline deterministic tests run on every change:
171
172
  - timeout before output, cancellation, and no post-cancel dispatch;
172
173
  - context calculation includes tool schemas and output reserve;
173
174
  - no silent hosted fallback;
175
+ - implementation-only coding alias selection requires authenticated exact availability, with Deep fallback and durable resume behavior;
174
176
  - session resume preserves provider, model, capability profile, and executed-tool ledger.
175
177
 
176
178
  Live compatibility tests are opt-in because they load local models:
@@ -180,6 +182,7 @@ Live compatibility tests are opt-in because they load local models:
180
182
  | `localllm-pocket` / 4B (direct sibling-service compatibility alias, not an automatic AgInTi tier) | direct answer, one-tool selection, malformed-output recovery, truthful blocker |
181
183
  | `localllm-fast` / 8B | focused code inspection/edit/test loop, cancellation, resumed turn |
182
184
  | `localllm-deep` / 30B-A3B Q4 | multi-step plan, repair after failed tool evidence, long-context compaction |
185
+ | `localllm-code` / configured coding capability | repository implementation/tool loop, exact-alias readiness, Deep fallback, resumed selection |
183
186
  | `localllm-max` / 30B-A3B Q8 | explicit opt-in, resource preflight, highest-fidelity local code task |
184
187
  | `localllm-vision-xl` / 30B-A3B Q4 | attached-image understanding with no keyword-only activation |
185
188
  | DeepSeek/OpenAI | same fixtures, plus explicit escalation and stronger-model quality comparison |
@@ -20,10 +20,11 @@ The local tier policy uses the workstation's installed aliases without treating
20
20
  | --- | --- | --- |
21
21
  | `localllm-fast` | Qwen3 8B Q4 routing and bounded work | Default for simple work. |
22
22
  | `localllm-deep` | Qwen3 30B-A3B Q4 substantive coding/agent work | Default for complex work. |
23
+ | `localllm-code` | Provider-neutral coding capability (the sibling catalog currently maps it to Qwen3-Coder 30B-A3B Q4) | Smart selection only for high-confidence implementation work and only after authenticated availability; otherwise Deep. |
23
24
  | `localllm-max` | Qwen3 30B-A3B Q8 highest-fidelity local text/code | Explicit selection; automatic use additionally requires opt-in, authenticated availability, fresh resource readiness, and no shared-workstation pressure. |
24
25
  | `localllm-vision-xl` | Qwen3-VL 30B-A3B Q4 image understanding | The automatic policy requires a trusted image-input signal plus confirmed capability. The shipped CLI/web currently use readiness-checked `read_image` or explicit selection; prompt keywords alone do not activate it. |
25
26
 
26
- Installed aliases are not loaded during routing. Automatic Max is off by default; set `AGINTI_LOCALLLM_ALLOW_AUTO_MAX=true` to opt in. An opted-in high-complexity run starts on Deep, confirms the Max alias through authenticated `/v1/models`, and only then samples current resources. Unknown or pressured resource state stays on Deep. Explicit Max also cannot bypass the live gate: immediately before client creation, each new or resumed run rechecks at least 24 GiB available RAM, swap use at or below 75%, and 40 GiB aggregate free NVIDIA memory. A blocked explicit gate creates no model client and performs no inference.
27
+ Installed aliases are not loaded during routing. A genuine coding/implementation request starts on Deep, checks the authenticated model inventory, and selects the exact `AGINTI_LOCALLLM_CODE_MODEL` value only when present. Missing or unverified capability stays on Deep; the decision and effective model are recorded so fallback sessions can re-evaluate and selected sessions resume on the same model. Explanation-only code questions and non-code writing/research/documentation/design keep their existing route. Automatic Max is off by default; set `AGINTI_LOCALLLM_ALLOW_AUTO_MAX=true` to opt in. An opted-in high-complexity non-code run starts on Deep, confirms the Max alias through authenticated `/v1/models`, and only then samples current resources. Unknown or pressured resource state stays on Deep. Explicit Max also cannot bypass the live gate: immediately before client creation, each new or resumed run rechecks at least 24 GiB available RAM, swap use at or below 75%, and 40 GiB aggregate free NVIDIA memory. A blocked explicit gate creates no model client and performs no inference.
27
28
 
28
29
  Long writing tasks use an additional tool boundary: `writing_specialist`. The main model still plans the run, manages files, formats Markdown/LaTeX/Final Draft output, compiles/checks artifacts, and finishes. The specialist gets only the writing brief, canon, style guide, prior draft, target, audience, constraints, and format intent, then returns prose plus a formatter handoff. It follows the active LocalLLM provider by default even when hosted keys exist. Cross-provider writing requires both an explicit target (`AGINTI_WRITING_PROVIDER` or a per-run provider override) and `AGINTI_ALLOW_HOSTED_WRITING_SPECIALIST=true` (or the equivalent per-run permission flag); language detection and ambient credentials never grant that permission.
29
30
 
@@ -75,7 +76,7 @@ Provider families:
75
76
 
76
77
  | Family | Models |
77
78
  | --- | --- |
78
- | LocalLLM | `localllm-fast`, `localllm-deep`, `localllm-max`, `localllm-vision-xl`; Max and Vision retain the gates above |
79
+ | LocalLLM | `localllm-fast`, `localllm-deep`, `localllm-code`, `localllm-max`, `localllm-vision-xl`; Code, Max, and Vision retain the gates above |
79
80
  | DeepSeek | `deepseek-v4-flash`, `deepseek-v4-pro` |
80
81
  | Venice | `venice-uncensored-1-2`, `venice-uncensored`, `gemma-4-uncensored` |
81
82
  | OpenAI | `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex`, `gpt-5.3-codex-spark`, `gpt-5.2`; each has low/medium/high/xhigh reasoning |
@@ -148,7 +149,7 @@ It keeps smart routing enabled. If the Venice key is missing, run `/auth venice`
148
149
 
149
150
  ## Keys
150
151
 
151
- The LocalLLM baseline uses `http://127.0.0.1:8008/v1` and the installation's loopback key (`local-dev-key` in the documented local setup). `AGINTI_LOCALLLM_MODEL` or `LOCALLLM_MODEL` is the shared local text override; role-specific `AGINTI_LOCALLLM_ROUTE_MODEL` and `AGINTI_LOCALLLM_MAIN_MODEL` take precedence. Optional `AGINTI_LOCALLLM_MAX_MODEL` and `AGINTI_LOCALLLM_VISION_MODEL` name the gated heavyweight aliases. LocalLLM overrides must remain loopback URLs. Generic `LLM_*` values do not redefine the local provider.
152
+ The LocalLLM baseline uses `http://127.0.0.1:8008/v1` and the installation's loopback key (`local-dev-key` in the documented local setup). `AGINTI_LOCALLLM_MODEL` or `LOCALLLM_MODEL` is the shared local text override; role-specific `AGINTI_LOCALLLM_ROUTE_MODEL` and `AGINTI_LOCALLLM_MAIN_MODEL` take precedence. `AGINTI_LOCALLLM_CODE_MODEL` names the independently configurable coding capability alias; AgInTiFlow never needs the underlying engine tag. Optional `AGINTI_LOCALLLM_MAX_MODEL` and `AGINTI_LOCALLLM_VISION_MODEL` name the other gated heavyweight aliases. LocalLLM overrides must remain loopback URLs. Generic `LLM_*` values do not redefine the local provider.
152
153
 
153
154
  Keys are saved account-wide in `~/.agintiflow/.env` by default. Use `--project` only when the current project needs an override in ignored `.aginti/.env`:
154
155
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.201",
3
+ "version": "0.20.202",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -77,6 +77,7 @@
77
77
  "scripts/smoke-inbox.js",
78
78
  "scripts/smoke-long-jobs.js",
79
79
  "scripts/smoke-localllm-auto-max.js",
80
+ "scripts/smoke-localllm-code-routing.js",
80
81
  "scripts/smoke-local-resource-policy.js",
81
82
  "scripts/smoke-localllm-model-tiers.js",
82
83
  "scripts/smoke-localllm-provider.js",
@@ -144,6 +145,7 @@
144
145
  "smoke:run-stdin": "node scripts/smoke-run-stdin.js",
145
146
  "smoke:runtime-core": "node scripts/smoke-runtime-core.js",
146
147
  "smoke:localllm-auto-max": "node scripts/smoke-localllm-auto-max.js",
148
+ "smoke:localllm-code-routing": "node scripts/smoke-localllm-code-routing.js",
147
149
  "smoke:local-resource-policy": "node scripts/smoke-local-resource-policy.js",
148
150
  "smoke:context-budget-recovery": "node scripts/smoke-context-budget-recovery.js",
149
151
  "smoke:localllm-model-tiers": "node scripts/smoke-localllm-model-tiers.js",
@@ -177,7 +179,7 @@
177
179
  "storage:migrate": "node bin/aginti-cli.js storage migrate",
178
180
  "publish:env": "node scripts/npm-publish-from-env.js publish --access public",
179
181
  "publish:env:whoami": "node scripts/npm-publish-from-env.js whoami",
180
- "test": "npm run check && npm run smoke:localllm-provider && npm run smoke:localllm-model-tiers && npm run smoke:localllm-auto-max && npm run smoke:local-resource-policy && npm run smoke:context-budget-recovery && npm run smoke:session-runtime && npm run smoke:runtime-core && npm run smoke:progressive-tools && npm run smoke:truthful-completion && npm run smoke:writing-specialist-routing && npm run eval:local-first-agent && npm run eval:provider-attribution && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:math-rendering && npm run smoke:web-ui && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:docker-command && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:execution-policy && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:deep-research && npm run smoke:public-research && npm run smoke:safe-chat && npm run smoke:auth && npm run smoke:agentlink && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:mcp && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:permission-modes && npm run smoke:skills && npm run smoke:skillmesh && npm run smoke:tmux-tools && npm run smoke:long-jobs && npm run smoke:run-stdin && npm run smoke:cli-chat && npm run smoke:inbox",
182
+ "test": "npm run check && npm run smoke:localllm-provider && npm run smoke:localllm-model-tiers && npm run smoke:localllm-code-routing && npm run smoke:localllm-auto-max && npm run smoke:local-resource-policy && npm run smoke:context-budget-recovery && npm run smoke:session-runtime && npm run smoke:runtime-core && npm run smoke:progressive-tools && npm run smoke:truthful-completion && npm run smoke:writing-specialist-routing && npm run eval:local-first-agent && npm run eval:provider-attribution && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:math-rendering && npm run smoke:web-ui && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:docker-command && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:execution-policy && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:deep-research && npm run smoke:public-research && npm run smoke:safe-chat && npm run smoke:auth && npm run smoke:agentlink && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:mcp && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:permission-modes && npm run smoke:skills && npm run smoke:skillmesh && npm run smoke:tmux-tools && npm run smoke:long-jobs && npm run smoke:run-stdin && npm run smoke:cli-chat && npm run smoke:inbox",
181
183
  "pack:dry-run": "npm pack --dry-run",
182
184
  "smoke:capabilities": "node scripts/smoke-capabilities.js"
183
185
  },
@@ -4,26 +4,22 @@ import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { authProviderKeyHelp, authProviderKeyUrl, normalizeAuthProvider } from "../src/auth-onboarding.js";
8
- import { getProviderDefaults } from "../src/model-routing.js";
9
- import { maskProviderKey, providerKeyPreview, providerKeyStatus, setProviderKey } from "../src/project.js";
10
7
 
11
8
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
12
9
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auth-"));
13
- const envKeys = [
14
- "DEEPSEEK_API_KEY",
15
- "OPENAI_API_KEY",
16
- "OPENROUTER_API_KEY",
17
- "LLM_API_KEY",
18
- "QWEN_API_KEY",
19
- "VENICE_API_KEY",
20
- "GRSAI",
21
- "GRSAI_API_KEY",
22
- ];
23
- const originalEnv = Object.fromEntries(envKeys.map((key) => [key, process.env[key]]));
24
- const originalAgintiflowHome = process.env.AGINTIFLOW_HOME;
25
- for (const key of envKeys) delete process.env[key];
10
+ const CONFIG_ENV_PATTERN = /^(?:AGENT|AGINTI|AGINTIFLOW|LOCALLLM|LOCAL_LLM|LLM|DEEPSEEK|OPENAI|OPENROUTER|QWEN|VENICE|GRSAI|BRAVE|ALLOW_|WRAPPER_|MAX_STEPS$|SANDBOX_MODE$|USE_DOCKER_SANDBOX$|PACKAGE_INSTALL_POLICY$|COMMAND_CWD$|PREFERRED_WRAPPER$)/u;
11
+ for (const key of Object.keys(process.env)) {
12
+ if (CONFIG_ENV_PATTERN.test(key)) delete process.env[key];
13
+ }
26
14
  process.env.AGINTIFLOW_HOME = path.join(tempRoot, ".agintiflow-home");
15
+ const [authOnboarding, modelRouting, project] = await Promise.all([
16
+ import("../src/auth-onboarding.js"),
17
+ import("../src/model-routing.js"),
18
+ import("../src/project.js"),
19
+ ]);
20
+ const { authProviderKeyHelp, authProviderKeyUrl, normalizeAuthProvider } = authOnboarding;
21
+ const { getProviderDefaults } = modelRouting;
22
+ const { loadProjectEnv, maskProviderKey, providerKeyPreview, providerKeyStatus, setProviderKey } = project;
27
23
 
28
24
  function assert(condition, message) {
29
25
  if (!condition) throw new Error(message);
@@ -35,7 +31,12 @@ async function runCli(args, stdin = "") {
35
31
  cwd: tempRoot,
36
32
  stdio: ["pipe", "pipe", "pipe"],
37
33
  env: {
38
- ...process.env,
34
+ PATH: process.env.PATH || "/usr/bin:/bin",
35
+ HOME: tempRoot,
36
+ USERPROFILE: tempRoot,
37
+ LANG: "C.UTF-8",
38
+ LC_ALL: "C.UTF-8",
39
+ AGINTIFLOW_HOME: process.env.AGINTIFLOW_HOME,
39
40
  AGINTIFLOW_RUNTIME_DIR: "",
40
41
  },
41
42
  });
@@ -89,7 +90,17 @@ try {
89
90
 
90
91
  const projectOnlyRoot = path.join(tempRoot, "project-only");
91
92
  await fs.mkdir(path.join(projectOnlyRoot, ".aginti"), { recursive: true });
92
- await fs.writeFile(path.join(projectOnlyRoot, ".aginti", ".env"), 'OPENAI_API_KEY="project-only-openai-key"\n', "utf8");
93
+ await fs.writeFile(
94
+ path.join(projectOnlyRoot, ".aginti", ".env"),
95
+ ['OPENAI_API_KEY="project-only-openai-key"', 'AGINTI_LOCALLLM_CODE_MODEL="project-code-alias"', ""].join("\n"),
96
+ "utf8"
97
+ );
98
+ const projectOnlyEnv = loadProjectEnv(projectOnlyRoot);
99
+ assert(projectOnlyEnv.projectEnv, "project-only LocalLLM code model env was not parsed");
100
+ assert(
101
+ process.env.AGINTI_LOCALLLM_CODE_MODEL === "project-code-alias",
102
+ "AGINTI_LOCALLLM_CODE_MODEL was not loaded from the project env"
103
+ );
93
104
  let projectOnlyStatus = providerKeyStatus(projectOnlyRoot);
94
105
  assert(projectOnlyStatus.openai && projectOnlyStatus.projectEnv, "project-only key was not loaded");
95
106
  await fs.access(path.join(tempRoot, ".agintiflow-home", ".env"))
@@ -100,6 +111,7 @@ try {
100
111
  if (error?.code !== "ENOENT") throw error;
101
112
  });
102
113
  delete process.env.OPENAI_API_KEY;
114
+ delete process.env.AGINTI_LOCALLLM_CODE_MODEL;
103
115
 
104
116
  process.env.DEEPSEEK_API_KEY = "ambient-deepseek-key";
105
117
  process.env.OPENROUTER_API_KEY = "ambient-openrouter-key";
@@ -182,6 +194,7 @@ try {
182
194
  "openrouter-defaults",
183
195
  "venice-defaults",
184
196
  "project-key-not-auto-promoted",
197
+ "project-code-model-env",
185
198
  "ambient-key-discovery-is-read-only",
186
199
  "project-key-override",
187
200
  "qwen-key-status",
@@ -195,11 +208,8 @@ try {
195
208
  )
196
209
  );
197
210
  } finally {
198
- for (const key of envKeys) {
199
- if (originalEnv[key] === undefined) delete process.env[key];
200
- else process.env[key] = originalEnv[key];
211
+ for (const key of Object.keys(process.env)) {
212
+ if (CONFIG_ENV_PATTERN.test(key)) delete process.env[key];
201
213
  }
202
- if (originalAgintiflowHome === undefined) delete process.env.AGINTIFLOW_HOME;
203
- else process.env.AGINTIFLOW_HOME = originalAgintiflowHome;
204
214
  await fs.rm(tempRoot, { recursive: true, force: true });
205
215
  }
@@ -16,24 +16,16 @@ import {
16
16
  } from "../src/local-auto-max.js";
17
17
  import { LOCALLLM_AUTO_MAX_MIN_COMPLEXITY } from "../src/model-routing.js";
18
18
 
19
- const ENV_KEYS = [
20
- "AGINTIFLOW_HOME",
21
- "AGENT_PROVIDER",
22
- "AGINTI_LOCALLLM_ALLOW_AUTO_MAX",
23
- "AGINTI_LOCALLLM_MAX_MODEL",
24
- "AGINTI_ROUTE_PROVIDER",
25
- "AGINTI_ROUTE_MODEL",
26
- "AGINTI_MAIN_PROVIDER",
27
- "AGINTI_MAIN_MODEL",
28
- "LLM_MODEL",
29
- ];
30
- const envSnapshot = Object.fromEntries(ENV_KEYS.map((key) => [key, process.env[key]]));
19
+ const CONFIG_ENV_PATTERN = /^(?:AGENT|AGINTI|AGINTIFLOW|LOCALLLM|LOCAL_LLM|LLM|DEEPSEEK|OPENAI|OPENROUTER|QWEN|VENICE|GRSAI|BRAVE|ALLOW_|WRAPPER_|MAX_STEPS$|SANDBOX_MODE$|USE_DOCKER_SANDBOX$|PACKAGE_INSTALL_POLICY$|COMMAND_CWD$|PREFERRED_WRAPPER$)/u;
31
20
  const TEST_BEARER_TOKEN = "smoke-auto-max-local-bearer";
32
21
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
22
+ const smokeRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auto-max-"));
23
+ const smokeProject = path.join(smokeRoot, "project");
24
+ await fs.mkdir(smokeProject, { recursive: true });
33
25
  const complexGoal = [
34
- "Architect and implement a complex multi-file repository migration.",
35
- "Debug the root cause, repair the regression, update the database and CI configuration,",
36
- "review security and performance, then run the complete test suite.",
26
+ "Analyze a complex multi-file repository architecture and migration strategy.",
27
+ "Compare the root cause, database, CI, security, performance, Docker, Kubernetes, and systemd tradeoffs,",
28
+ "then produce a detailed design review without changing source files.",
37
29
  ].join(" ");
38
30
 
39
31
  function readiness(models = ["localllm-fast", "localllm-deep", "localllm-max"]) {
@@ -182,11 +174,14 @@ async function createIntegrationConfig({
182
174
  }
183
175
 
184
176
  try {
185
- for (const key of ENV_KEYS) delete process.env[key];
177
+ for (const key of Object.keys(process.env)) {
178
+ if (CONFIG_ENV_PATTERN.test(key)) delete process.env[key];
179
+ }
180
+ process.env.AGINTIFLOW_HOME = path.join(smokeRoot, "home");
186
181
 
187
182
  const disabledConfig = resolveRuntimeConfig(
188
183
  { goal: complexGoal, taskProfile: "large-codebase" },
189
- { baseDir: process.cwd(), provider: "localllm", routingMode: "smart" }
184
+ { baseDir: smokeProject, provider: "localllm", routingMode: "smart" }
190
185
  );
191
186
  assert.ok(disabledConfig.routeComplexityScore >= LOCALLLM_AUTO_MAX_MIN_COMPLEXITY);
192
187
  assert.equal(disabledConfig.model, "localllm-deep");
@@ -196,7 +191,7 @@ try {
196
191
  process.env.AGINTI_LOCALLLM_ALLOW_AUTO_MAX = "true";
197
192
  const optedInConfig = resolveRuntimeConfig(
198
193
  { goal: complexGoal, taskProfile: "large-codebase" },
199
- { baseDir: process.cwd(), provider: "localllm", routingMode: "smart" }
194
+ { baseDir: smokeProject, provider: "localllm", routingMode: "smart" }
200
195
  );
201
196
  assert.equal(optedInConfig.model, "localllm-deep", "phase one must remain on Deep before authenticated discovery");
202
197
  assert.equal(optedInConfig.allowLocalAutoMax, true);
@@ -255,7 +250,7 @@ try {
255
250
  const explicitMax = resolveRuntimeConfig(
256
251
  { goal: complexGoal, taskProfile: "large-codebase", model: "localllm-max" },
257
252
  {
258
- baseDir: process.cwd(),
253
+ baseDir: smokeProject,
259
254
  provider: "localllm",
260
255
  routingMode: "smart",
261
256
  model: "localllm-max",
@@ -266,7 +261,7 @@ try {
266
261
 
267
262
  const simpleConfig = resolveRuntimeConfig(
268
263
  { goal: "Say hello." },
269
- { baseDir: process.cwd(), provider: "localllm", routingMode: "smart", allowLocalAutoMax: true }
264
+ { baseDir: smokeProject, provider: "localllm", routingMode: "smart", allowLocalAutoMax: true }
270
265
  );
271
266
  let simpleResourceChecks = 0;
272
267
  const simpleDecision = await resolveLocalAutoMaxUpgrade(simpleConfig, readiness(), {
@@ -278,10 +273,9 @@ try {
278
273
  assert.equal(simpleDecision.outcome, LOCALLLM_AUTO_MAX_OUTCOMES.INELIGIBLE);
279
274
  assert.equal(simpleResourceChecks, 0, "low-complexity work must never probe Max resources");
280
275
 
281
- const integrationRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auto-max-"));
282
- process.env.AGINTIFLOW_HOME = path.join(integrationRoot, "home");
283
- try {
284
- await withLocalFixture(async ({ baseURL, observations: fixtureObservations }) => {
276
+ const integrationRoot = path.join(smokeRoot, "integration");
277
+ await fs.mkdir(integrationRoot, { recursive: true });
278
+ await withLocalFixture(async ({ baseURL, observations: fixtureObservations }) => {
285
279
  let selectedResourceChecks = 0;
286
280
  const selectedProbe = async () => {
287
281
  selectedResourceChecks += 1;
@@ -379,15 +373,12 @@ try {
379
373
  );
380
374
  assert.equal(fixtureObservations.modelRequests, 4);
381
375
  assert.equal(fixtureObservations.authenticatedModelRequests, 4);
382
- });
383
- } finally {
384
- await fs.rm(integrationRoot, { recursive: true, force: true });
385
- }
376
+ });
386
377
  } finally {
387
- for (const [key, value] of Object.entries(envSnapshot)) {
388
- if (value === undefined) delete process.env[key];
389
- else process.env[key] = value;
378
+ for (const key of Object.keys(process.env)) {
379
+ if (CONFIG_ENV_PATTERN.test(key)) delete process.env[key];
390
380
  }
381
+ await fs.rm(smokeRoot, { recursive: true, force: true });
391
382
  }
392
383
 
393
384
  console.log("LocalLLM automatic Max two-phase policy smoke passed (offline; no model loads).\n");