@aiwg/cli 2026.10.0 → 2026.10.3
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/agentic/code/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3091 -24
- package/bin/aiwg.mjs +86 -21
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/artifacts/corpus-tools/vision-extract.js +105 -15
- package/dist/src/artifacts/repair.js +9 -4
- package/dist/src/channel/manager.mjs +59 -10
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- package/dist/src/cli/handlers/decision.js +297 -2
- package/dist/src/cli/handlers/diagnose.js +9 -2
- package/dist/src/cli/handlers/feedback.js +14 -5
- package/dist/src/cli/handlers/init.js +245 -30
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +69 -6
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +16 -0
- package/dist/src/cli/handlers/regenerate.js +14 -0
- package/dist/src/cli/handlers/runtime-info.js +24 -9
- package/dist/src/cli/handlers/script-runner.js +16 -6
- package/dist/src/cli/handlers/session.js +396 -62
- package/dist/src/cli/handlers/sessions.js +146 -40
- package/dist/src/cli/handlers/setup-manifest.js +439 -39
- package/dist/src/cli/handlers/setup.js +139 -39
- package/dist/src/cli/handlers/subcommands.js +3 -1
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/use.js +13 -0
- package/dist/src/cli/handlers/utilities.js +106 -23
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -0
- package/dist/src/config/aiwg-config.js +268 -4
- package/dist/src/config/cli.js +146 -19
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
- package/dist/src/config/project-artifacts-health.mjs +14 -3
- package/dist/src/config/user-config.js +36 -10
- package/dist/src/decision/adapters/jev.js +80 -28
- package/dist/src/decision/ask.js +352 -0
- package/dist/src/decision/driver.js +4 -0
- package/dist/src/decision/entry.js +38 -6
- package/dist/src/decision/heldout/collector.js +10 -1
- package/dist/src/decision/index.js +3 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +121 -0
- package/dist/src/decision/ledger.js +839 -0
- package/dist/src/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -0
- package/dist/src/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +3 -1
- package/dist/src/installation/manager-command.mjs +1 -1
- package/dist/src/installation/manager.mjs +17 -2
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/adapters/codex-runtime.js +360 -56
- package/dist/src/mcp/cli.mjs +587 -77
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.mjs +16 -26
- package/dist/src/mcp/helpers.mjs +106 -2
- package/dist/src/mcp/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +264 -57
- package/dist/src/mcp/registry.mjs +198 -45
- package/dist/src/mcp/server.mjs +403 -20
- package/dist/src/mcp/toml-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +58 -0
- package/dist/src/mcp/tools/orchestration.mjs +38 -0
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +1539 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +258 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +639 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +70 -13
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3091 -24
- package/dist/src/models/model-discovery.js +862 -63
- package/dist/src/models/provider-policy.js +625 -11
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +259 -0
- package/dist/src/models/worker-direct-readiness.js +529 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +131 -0
- package/dist/src/models/worker-installation.mjs +879 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.mjs +387 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -0
- package/dist/src/providers/grok-build-headless.js +43 -21
- package/dist/src/providers/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- package/dist/src/serve/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +9 -0
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +538 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +45 -0
- package/dist/src/sessions/adapters/generic.js +37 -3
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +423 -24
- package/dist/src/sessions/adapters/muse-workspace-scan.js +108 -0
- package/dist/src/sessions/adapters/muse.js +68 -29
- package/dist/src/sessions/adapters/pi.js +1 -0
- package/dist/src/sessions/batch-contracts.js +13 -1
- package/dist/src/sessions/batch-import.js +23 -42
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +35 -0
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/grok-build-cli.js +67 -5
- package/dist/src/sessions/importer.js +24 -8
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/repository.js +54 -13
- package/dist/src/sessions/workspace-discovery.js +99 -37
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- package/dist/src/smiths/context-pipeline/parallelism-section.js +192 -3
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +10 -14
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- package/dist/src/writing/fidelity.js +18 -1
- package/package.json +18 -1
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1066 -0
- package/tools/_resolve-impl.mjs +39 -0
- package/tools/agents/deploy-agents.mjs +15 -2
- package/tools/agents/providers/base.mjs +63 -43
- package/tools/agents/providers/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +6 -3
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- package/tools/providers/deepseek-harness-transport.mjs +358 -0
- package/tools/providers/omp-transport.mjs +192 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +333 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +690 -0
- package/tools/ralph-external/lib/worker-health.mjs +439 -0
- package/vscode-extension/schemas/aiwg.config.v1.json +1953 -0
|
@@ -14,19 +14,41 @@
|
|
|
14
14
|
* aiwg session --provider codex # explicit provider
|
|
15
15
|
* aiwg session mcp --provider cursor # MCP inject for cursor + start instructions
|
|
16
16
|
* aiwg session --no-repair # skip auto-repair (still check and report)
|
|
17
|
+
* aiwg session --prompt "<text>" # headless: one-shot, bounded, exits (#3062)
|
|
18
|
+
* aiwg session --prompt-file <path> # headless from a file instead of argv
|
|
19
|
+
* aiwg session --non-interactive # force the non-interactive decision (#3062)
|
|
20
|
+
*
|
|
21
|
+
* Without a terminal attached, launching a spawnable provider with no
|
|
22
|
+
* `--prompt`/`--prompt-file` would hand over a terminal that does not exist
|
|
23
|
+
* and hang. `--prompt`/`--prompt-file` map to each provider's documented
|
|
24
|
+
* non-interactive CLI form, delivered off argv (see
|
|
25
|
+
* `HEADLESS_SESSION_TRANSPORTS` below); providers without an evidenced one
|
|
26
|
+
* refuse instead of guessing. A provider with no spawnable binary at all
|
|
27
|
+
* (IDE-integrated) refuses `--prompt`/`--prompt-file` outright rather than
|
|
28
|
+
* discarding it. `--non-interactive` forces the same refusal even with a
|
|
29
|
+
* terminal attached, taking priority over the environment-derived check.
|
|
17
30
|
*
|
|
18
31
|
* @issue #884
|
|
19
32
|
*/
|
|
20
33
|
import { spawnSync } from 'child_process';
|
|
34
|
+
import { readFile } from 'node:fs/promises';
|
|
35
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
36
|
+
import { tmpdir } from 'node:os';
|
|
37
|
+
import { join } from 'node:path';
|
|
38
|
+
import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
|
|
39
|
+
import { existsSync } from 'node:fs';
|
|
21
40
|
import { ensureRuntimeHome, writeProfileConfig, launchWithProfile } from '../../mcp/adapters/codex-runtime.js';
|
|
22
41
|
import { getFrameworkRoot } from '../../channel/manager.mjs';
|
|
23
42
|
import { forceUpdateCheck } from '../../update/checker.mjs';
|
|
24
43
|
import { updateInstallation } from '../../update/service.mjs';
|
|
44
|
+
import { isInteractive } from '../env.js';
|
|
25
45
|
import { readAiwgConfig, getDeploymentSummary, VALID_PROVIDERS, } from '../../config/aiwg-config.js';
|
|
26
46
|
import { getProviderConfig, isSpawnableProvider, PROVIDER_CONFIGS, } from '../agent-spawn.js';
|
|
27
47
|
import { useHandler as useFrameworkHandler } from './use.js';
|
|
28
48
|
import { debug } from '../log.js';
|
|
29
49
|
import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
|
|
50
|
+
import { requireFlagValue, MissingFlagValueError, hasPositionalOrSwitch } from '../flag-parsing.js';
|
|
51
|
+
import { exitCodeForSpawnResult } from '../child-process-result.js';
|
|
30
52
|
// ── Provider resolution ──────────────────────────────────────────────────────
|
|
31
53
|
/**
|
|
32
54
|
* Resolve which provider to target.
|
|
@@ -46,31 +68,44 @@ async function resolveProvider(explicitProvider, cwd) {
|
|
|
46
68
|
}
|
|
47
69
|
return 'claude';
|
|
48
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse `aiwg session` flags.
|
|
73
|
+
*
|
|
74
|
+
* Every value-taking flag below is parsed through {@link requireFlagValue}
|
|
75
|
+
* (#3062 review item 11): a flag present with no value, or immediately
|
|
76
|
+
* followed by another flag, throws {@link MissingFlagValueError} rather
|
|
77
|
+
* than silently resolving to "not given" — callers MUST catch it before
|
|
78
|
+
* any preflight check, spawn, or write. `precedence: 'last'` matches this
|
|
79
|
+
* command's own pre-#3062 behavior (a hand-rolled loop that kept scanning
|
|
80
|
+
* and overwrote on every match, so a later `--provider`/`--profile` always
|
|
81
|
+
* won) — the first-round shared parser silently flipped that to
|
|
82
|
+
* first-wins; this restores it (#3062 second review, finding 4).
|
|
83
|
+
* `--prompt`/`--prompt-file` reject an empty string: an empty prompt can
|
|
84
|
+
* never be meaningful, and silently accepting one would let a mistake
|
|
85
|
+
* reach either the interactive launch path or a headless launch with
|
|
86
|
+
* nothing to say.
|
|
87
|
+
*
|
|
88
|
+
* The `mcp` positional and every boolean switch below go through
|
|
89
|
+
* {@link hasPositionalOrSwitch} rather than `args.includes(...)` (#3062
|
|
90
|
+
* go/no-go review, blocker 4): a plain `args.includes('mcp')` matches the
|
|
91
|
+
* literal word `mcp` *anywhere* in `args`, including inside a free-text
|
|
92
|
+
* `--prompt`/`--prompt-file` value (`aiwg session --prompt mcp` used to
|
|
93
|
+
* set `mcp: true` and reach MCP config injection) or after a bare `--`
|
|
94
|
+
* (`aiwg session -- --persist` used to still set `persist: true`).
|
|
95
|
+
* {@link hasPositionalOrSwitch} excludes both: anything consumed as the
|
|
96
|
+
* value of `VALUE_FLAGS` below, and anything at or after `--`.
|
|
97
|
+
*/
|
|
98
|
+
const VALUE_FLAGS = ['--provider', '--profile', '--prompt', '--prompt-file'];
|
|
49
99
|
function parseSessionArgs(args) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
else if (a === '--provider' && args[i + 1]) {
|
|
61
|
-
provider = args[++i];
|
|
62
|
-
}
|
|
63
|
-
else if (a === '--no-repair') {
|
|
64
|
-
noRepair = true;
|
|
65
|
-
}
|
|
66
|
-
else if (a === '--profile' && args[i + 1]) {
|
|
67
|
-
profile = args[++i];
|
|
68
|
-
}
|
|
69
|
-
else if (a === '--persist') {
|
|
70
|
-
persist = true;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return { mcp, provider, noRepair, profile, persist };
|
|
100
|
+
const mcp = hasPositionalOrSwitch(args, 'mcp', VALUE_FLAGS);
|
|
101
|
+
const noRepair = hasPositionalOrSwitch(args, '--no-repair', VALUE_FLAGS);
|
|
102
|
+
const persist = hasPositionalOrSwitch(args, '--persist', VALUE_FLAGS);
|
|
103
|
+
const nonInteractive = hasPositionalOrSwitch(args, '--non-interactive', VALUE_FLAGS);
|
|
104
|
+
const provider = requireFlagValue(args, { precedence: 'last' }, '--provider');
|
|
105
|
+
const profile = requireFlagValue(args, { precedence: 'last' }, '--profile');
|
|
106
|
+
const prompt = requireFlagValue(args, { precedence: 'last', rejectEmpty: true }, '--prompt');
|
|
107
|
+
const promptFile = requireFlagValue(args, { precedence: 'last', rejectEmpty: true }, '--prompt-file');
|
|
108
|
+
return { mcp, provider, noRepair, profile, persist, prompt, promptFile, nonInteractive };
|
|
74
109
|
}
|
|
75
110
|
// ── Pre-flight steps ─────────────────────────────────────────────────────────
|
|
76
111
|
/** Run `npm view aiwg version` and compare to current. Returns true if up-to-date. */
|
|
@@ -103,8 +138,17 @@ async function checkAndUpdateVersion(noRepair) {
|
|
|
103
138
|
* Uses the same script runner path as doctorHandler to avoid duplication.
|
|
104
139
|
*/
|
|
105
140
|
function runDoctor(_frameworkRoot, cwd) {
|
|
106
|
-
|
|
107
|
-
|
|
141
|
+
// Self-invocation of the aiwg binary, not the harness launch below; bounded
|
|
142
|
+
// as a long-tier call (#3062) since `aiwg doctor` can touch the network.
|
|
143
|
+
const result = boundedSpawnSync(process.execPath, [process.argv[1], 'doctor'], 'long', { stdio: 'inherit', cwd });
|
|
144
|
+
// #3062 review item 7: a `null` status (killed/timed out/failed to spawn)
|
|
145
|
+
// already falls through to `false` below via `?? 1`; surface the bounded
|
|
146
|
+
// helper's diagnostic (e.g. the enriched timeout message) instead of
|
|
147
|
+
// silently dropping it.
|
|
148
|
+
const { exitCode, message } = exitCodeForSpawnResult(result, 'aiwg doctor');
|
|
149
|
+
if (message)
|
|
150
|
+
debug('cli:session:doctor', message);
|
|
151
|
+
return exitCode === 0;
|
|
108
152
|
}
|
|
109
153
|
/**
|
|
110
154
|
* Attempt to repair a failed doctor result.
|
|
@@ -115,7 +159,15 @@ function runDoctor(_frameworkRoot, cwd) {
|
|
|
115
159
|
function repairInstallation(frameworkRoot, cwd, provider) {
|
|
116
160
|
// Strategy 1: sync (update + redeploy)
|
|
117
161
|
console.log('\n Attempting auto-repair via `aiwg sync`...');
|
|
118
|
-
|
|
162
|
+
// Self-invocation of the aiwg binary; bounded as a long-tier call (#3062)
|
|
163
|
+
// since `aiwg sync` updates and redeploys (network + install-shaped work).
|
|
164
|
+
const syncResult = boundedSpawnSync(process.execPath, [process.argv[1], 'sync'], 'long', { stdio: 'inherit', cwd });
|
|
165
|
+
// #3062 review item 7: `status === 0` already treats a `null` status
|
|
166
|
+
// (timeout/signal/spawn failure) as "not repaired", falling through to
|
|
167
|
+
// the manual-options message below; surface the diagnostic too.
|
|
168
|
+
const syncDiagnostic = exitCodeForSpawnResult(syncResult, 'aiwg sync').message;
|
|
169
|
+
if (syncDiagnostic)
|
|
170
|
+
debug('cli:session:repair', syncDiagnostic);
|
|
119
171
|
if (syncResult.status === 0) {
|
|
120
172
|
// Re-check doctor
|
|
121
173
|
const doctorOk = runDoctor(frameworkRoot, cwd);
|
|
@@ -170,28 +222,32 @@ async function redeployFrameworks(ctx, frameworks, provider) {
|
|
|
170
222
|
// ── MCP inject ────────────────────────────────────────────────────────────────
|
|
171
223
|
function injectMcp(provider, cwd) {
|
|
172
224
|
console.log(`\n Injecting configured MCP servers into ${provider}...`);
|
|
173
|
-
|
|
225
|
+
// Self-invocation of the aiwg binary; bounded as a long-tier call (#3062).
|
|
226
|
+
const result = boundedSpawnSync(process.execPath, [process.argv[1], 'mcp', 'inject', '--provider', provider], 'long', { stdio: 'inherit', cwd });
|
|
174
227
|
if (result.status !== 0) {
|
|
228
|
+
// #3062 review item 7: surface the bounded helper's diagnostic (if any)
|
|
229
|
+
// alongside the existing warning.
|
|
230
|
+
const { message } = exitCodeForSpawnResult(result, 'aiwg mcp inject');
|
|
231
|
+
if (message)
|
|
232
|
+
debug('cli:session:mcp-inject', message);
|
|
175
233
|
console.warn(' WARN MCP inject reported issues — continuing. Run `aiwg mcp list` to check registered servers.');
|
|
176
234
|
return false;
|
|
177
235
|
}
|
|
178
236
|
return true;
|
|
179
237
|
}
|
|
180
|
-
|
|
181
|
-
* Profile-aware inject (#891).
|
|
182
|
-
* Default is ephemeral (no persistent config mutation).
|
|
183
|
-
*
|
|
184
|
-
* For Claude: returns the ephemeral config path (for claude --mcp-config).
|
|
185
|
-
* For Codex: sets up the runtime home via the codex-runtime adapter (#892).
|
|
186
|
-
* Returns null for persistent mode or on failure.
|
|
187
|
-
*/
|
|
188
|
-
function injectMcpProfile(provider, profileName, cwd, persist) {
|
|
238
|
+
function injectMcpProfile(provider, profileName, cwd, persist, hasClaudePermissions = false) {
|
|
189
239
|
console.log(`\n Resolving profile "${profileName}" for ${provider}...`);
|
|
190
240
|
if (persist) {
|
|
191
241
|
// Persistent: write to provider's default config
|
|
192
|
-
|
|
242
|
+
// Self-invocation of the aiwg binary; bounded as a long-tier call (#3062).
|
|
243
|
+
const result = boundedSpawnSync(process.execPath, [process.argv[1], 'mcp', 'inject', '--provider', provider, '--profile', profileName], 'long', { stdio: 'inherit', cwd });
|
|
193
244
|
if (result.status !== 0) {
|
|
194
|
-
|
|
245
|
+
// #3062 review item 7: surface the bounded helper's diagnostic.
|
|
246
|
+
const { message } = exitCodeForSpawnResult(result, 'aiwg mcp inject');
|
|
247
|
+
if (provider === 'codex' || provider === 'openai' || provider === 'claude' || provider === 'claude-code') {
|
|
248
|
+
throw new Error(`${provider} profile injection failed; refusing to launch.${message ? ` ${message}` : ''}`);
|
|
249
|
+
}
|
|
250
|
+
console.warn(` WARN Profile inject failed — continuing without profile.${message ? ` ${message}` : ''}`);
|
|
195
251
|
}
|
|
196
252
|
return null; // persistent mode, no ephemeral path
|
|
197
253
|
}
|
|
@@ -202,28 +258,119 @@ function injectMcpProfile(provider, profileName, cwd, persist) {
|
|
|
202
258
|
}
|
|
203
259
|
// Claude/others: generate a temp config file
|
|
204
260
|
const tmpPath = `${process.env.TMPDIR ?? '/tmp'}/aiwg-mcp-${profileName}-${provider}-${Date.now()}.json`;
|
|
205
|
-
|
|
261
|
+
// Self-invocation of the aiwg binary; bounded as a long-tier call (#3062).
|
|
262
|
+
const result = boundedSpawnSync(process.execPath, [
|
|
206
263
|
process.argv[1], 'mcp', 'inject',
|
|
207
264
|
'--provider', provider,
|
|
208
265
|
'--profile', profileName,
|
|
209
266
|
'--ephemeral',
|
|
210
267
|
'--out', tmpPath,
|
|
211
|
-
], { stdio: 'inherit', cwd });
|
|
268
|
+
], 'long', { stdio: ['inherit', 'pipe', 'inherit'], encoding: 'utf-8', cwd });
|
|
269
|
+
if (result.stdout)
|
|
270
|
+
process.stdout.write(result.stdout);
|
|
212
271
|
if (result.status !== 0) {
|
|
213
|
-
|
|
272
|
+
// #3062 review item 7: surface the bounded helper's diagnostic.
|
|
273
|
+
const { message } = exitCodeForSpawnResult(result, 'aiwg mcp inject');
|
|
274
|
+
if (provider === 'claude' || provider === 'claude-code') {
|
|
275
|
+
throw new Error(`Claude profile injection failed; refusing to launch.${message ? ` ${message}` : ''}`);
|
|
276
|
+
}
|
|
277
|
+
console.warn(` WARN Ephemeral profile inject failed — launching without profile MCP config.${message ? ` ${message}` : ''}`);
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const reportedSettingsPath = result.stdout?.match(/^\s*Tool permissions: (.+)$/m)?.[1]?.trim();
|
|
281
|
+
const settingsPath = reportedSettingsPath && existsSync(reportedSettingsPath) ? reportedSettingsPath : null;
|
|
282
|
+
if (hasClaudePermissions && !settingsPath) {
|
|
283
|
+
throw new Error('Claude profile permission settings were not generated; refusing to launch.');
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
mcpConfigPath: tmpPath,
|
|
287
|
+
settingsPath,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const HEADLESS_SESSION_TRANSPORTS = {
|
|
291
|
+
claude: { kind: 'stdin', flags: ['--print', '--input-format', 'text'] },
|
|
292
|
+
'claude-code': { kind: 'stdin', flags: ['--print', '--input-format', 'text'] },
|
|
293
|
+
codex: { kind: 'stdin', flags: ['exec'], trailingArg: '-' },
|
|
294
|
+
openai: { kind: 'stdin', flags: ['exec'], trailingArg: '-' },
|
|
295
|
+
'deepseek-harness': { kind: 'stdin', flags: ['--profile', 'headless'], trailingArg: '-' },
|
|
296
|
+
opencode: {
|
|
297
|
+
kind: 'file',
|
|
298
|
+
flags: ['run', '--format', 'json'],
|
|
299
|
+
fileFlag: '-f',
|
|
300
|
+
fixedPositional: 'Follow the instructions in the attached file exactly.',
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Build the final argv (and stdin/temp-file payload) for a headless launch,
|
|
305
|
+
* or `null` if the provider has no evidenced non-interactive form.
|
|
306
|
+
*/
|
|
307
|
+
function buildHeadlessLaunch(provider, binaryArgs, prompt) {
|
|
308
|
+
const transport = HEADLESS_SESSION_TRANSPORTS[provider];
|
|
309
|
+
if (!transport)
|
|
214
310
|
return null;
|
|
311
|
+
if (transport.kind === 'stdin') {
|
|
312
|
+
const args = [...binaryArgs, ...transport.flags, ...(transport.trailingArg ? [transport.trailingArg] : [])];
|
|
313
|
+
return { args, input: prompt };
|
|
314
|
+
}
|
|
315
|
+
// File transport (opencode): the prompt never touches argv or stdin — it
|
|
316
|
+
// is written to a private temp file only this process can read, passed
|
|
317
|
+
// by path. `mkdtemp()` under the system temp directory (never a custom
|
|
318
|
+
// location — #3062 second review, finding 10) already gives the
|
|
319
|
+
// directory itself a private, unpredictable name owned by this process;
|
|
320
|
+
// the file within it additionally gets an explicit `0600` mode via an
|
|
321
|
+
// exclusive create (`flag: 'wx'` — fails rather than silently
|
|
322
|
+
// overwriting if a file with that exact name already existed, which it
|
|
323
|
+
// never should in a fresh `mkdtemp()` directory).
|
|
324
|
+
//
|
|
325
|
+
// Cleanup ownership starts the moment `mkdtemp()` returns, *before* the
|
|
326
|
+
// write: a failed write must not leave the directory behind any more
|
|
327
|
+
// than a failed launch would. The caller's own `finally` (in
|
|
328
|
+
// `launchProvider()`, keyed off `plan.tempDir`) handles the ordinary
|
|
329
|
+
// "launch ran, now clean up" path; this one exists only for the write
|
|
330
|
+
// itself failing before a `plan` is ever returned to that caller.
|
|
331
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'aiwg-session-prompt-'));
|
|
332
|
+
try {
|
|
333
|
+
const promptPath = join(tempDir, 'prompt.txt');
|
|
334
|
+
writeFileSync(promptPath, prompt, { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
|
|
335
|
+
const args = [...binaryArgs, ...transport.flags, transport.fileFlag, promptPath, transport.fixedPositional];
|
|
336
|
+
return { args, tempDir };
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
try {
|
|
340
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// Best-effort cleanup only; the original write failure is what matters.
|
|
344
|
+
}
|
|
345
|
+
throw err;
|
|
215
346
|
}
|
|
216
|
-
return tmpPath;
|
|
217
347
|
}
|
|
218
|
-
// ── Launch ────────────────────────────────────────────────────────────────────
|
|
219
348
|
/**
|
|
220
349
|
* Launch a spawnable provider binary, replacing the current process (exec-style).
|
|
221
350
|
* For IDE-integrated providers, print guidance and exit.
|
|
351
|
+
*
|
|
352
|
+
* `prompt`, when given, selects the headless path (#3062): the provider is
|
|
353
|
+
* launched with its documented non-interactive form, bounded and with stdin
|
|
354
|
+
* ignored, regardless of whether a terminal is attached. Without `prompt`
|
|
355
|
+
* and without a terminal, this refuses before spawning anything that would
|
|
356
|
+
* hang.
|
|
222
357
|
*/
|
|
223
|
-
function launchProvider(provider, mcpInjected, mcpConfigPath, profile) {
|
|
358
|
+
function launchProvider(provider, mcpInjected, mcpConfigPath, profile, settingsPath, prompt, interactive) {
|
|
224
359
|
const cfg = getProviderConfig(provider);
|
|
225
360
|
if (!isSpawnableProvider(provider)) {
|
|
226
|
-
// Not spawnable —
|
|
361
|
+
// Not spawnable — a prompt can never be delivered to a binary that does
|
|
362
|
+
// not exist. Refuse explicitly instead of discarding it and printing
|
|
363
|
+
// success (#3062 review item 9). `execute()` already checks this before
|
|
364
|
+
// any pre-flight step runs; this is defense-in-depth for this function
|
|
365
|
+
// on its own.
|
|
366
|
+
if (prompt) {
|
|
367
|
+
return {
|
|
368
|
+
exitCode: 1,
|
|
369
|
+
message: `Provider '${cfg.name}' has no CLI binary to spawn; --prompt/--prompt-file requires a ` +
|
|
370
|
+
'spawnable provider.',
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
// Print context-aware guidance
|
|
227
374
|
const mcpNote = mcpInjected
|
|
228
375
|
? '\n MCP servers have been injected into your provider config.\n'
|
|
229
376
|
: '';
|
|
@@ -237,25 +384,84 @@ ${cfg.guidanceMessage ?? `Open ${cfg.name} in your project directory to begin.`}
|
|
|
237
384
|
`);
|
|
238
385
|
return { exitCode: 0 };
|
|
239
386
|
}
|
|
240
|
-
// Spawnable — hand off the terminal
|
|
387
|
+
// Spawnable — hand off the terminal (or run headless, see below)
|
|
241
388
|
console.log(`\n── Launching ${cfg.name} ──\n`);
|
|
242
389
|
// Codex + profile: use runtime-home adapter (#892)
|
|
243
390
|
if ((provider === 'codex' || provider === 'openai') && profile) {
|
|
244
391
|
console.log(` Using runtime home for profile "${profile}"`);
|
|
245
|
-
|
|
246
|
-
|
|
392
|
+
// Headless (#3062): the prompt travels on stdin via a trailing `-`
|
|
393
|
+
// positional (docs/providers/codex-direct.md: "`-` reads stdin"),
|
|
394
|
+
// never on argv (review item 8). `launchWithProfile` itself
|
|
395
|
+
// bounds/refuses based on whether `extraArgs` is a non-interactive form
|
|
396
|
+
// and whether a terminal is attached — see its own doc comment.
|
|
397
|
+
const extraArgs = prompt ? ['exec', '-'] : [];
|
|
398
|
+
const result = launchWithProfile(profile, extraArgs, prompt ? { input: prompt, interactive } : { interactive });
|
|
399
|
+
const { exitCode, message } = exitCodeForSpawnResult(result, 'codex');
|
|
400
|
+
return message ? { exitCode, message } : { exitCode };
|
|
247
401
|
}
|
|
248
|
-
// Claude:
|
|
402
|
+
// Claude: load both the ephemeral MCP config and any permission settings.
|
|
249
403
|
const binaryArgs = [];
|
|
250
404
|
if (mcpConfigPath && (provider === 'claude' || provider === 'claude-code')) {
|
|
251
405
|
binaryArgs.push('--mcp-config', mcpConfigPath);
|
|
252
406
|
console.log(` Using ephemeral MCP config: ${mcpConfigPath}`);
|
|
407
|
+
if (settingsPath) {
|
|
408
|
+
binaryArgs.push('--settings', settingsPath);
|
|
409
|
+
console.log(` Using ephemeral permission settings: ${settingsPath}`);
|
|
410
|
+
}
|
|
253
411
|
}
|
|
412
|
+
if (prompt) {
|
|
413
|
+
const plan = buildHeadlessLaunch(provider, binaryArgs, prompt);
|
|
414
|
+
if (!plan) {
|
|
415
|
+
return {
|
|
416
|
+
exitCode: 1,
|
|
417
|
+
message: `Provider '${cfg.name}' has no evidenced non-interactive CLI form; --prompt/--prompt-file ` +
|
|
418
|
+
`is not supported for it. Run \`aiwg session --provider ${provider}\` from a terminal instead.`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
// Bounded (#3062): a headless run never hands over the terminal, so
|
|
423
|
+
// it is safe to bound. The prompt never rides on argv (review item
|
|
424
|
+
// 8); stdin-transport providers get it via `input` (never inherited
|
|
425
|
+
// from the parent), file-transport providers get it via a private
|
|
426
|
+
// temp file path already baked into `plan.args`.
|
|
427
|
+
const result = boundedSpawnSync(cfg.binary, plan.args, 'long', {
|
|
428
|
+
stdio: plan.input !== undefined ? ['pipe', 'inherit', 'inherit'] : ['ignore', 'inherit', 'inherit'],
|
|
429
|
+
env: process.env,
|
|
430
|
+
...(plan.input !== undefined ? { input: plan.input } : {}),
|
|
431
|
+
});
|
|
432
|
+
const { exitCode, message } = exitCodeForSpawnResult(result, cfg.binary);
|
|
433
|
+
return message ? { exitCode, message } : { exitCode };
|
|
434
|
+
}
|
|
435
|
+
finally {
|
|
436
|
+
if (plan.tempDir) {
|
|
437
|
+
try {
|
|
438
|
+
rmSync(plan.tempDir, { recursive: true, force: true });
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
// Best-effort cleanup only; a leftover temp file is not fatal.
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!interactive) {
|
|
447
|
+
return {
|
|
448
|
+
exitCode: 1,
|
|
449
|
+
message: 'aiwg session requires an interactive terminal to launch a provider without a prompt. ' +
|
|
450
|
+
'Pass --prompt <text> or --prompt-file <path> to run headless.',
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
// sync-spawn: interactive harness-launch — hands the terminal to the
|
|
454
|
+
// provider binary the user is meant to talk to, when no --prompt/
|
|
455
|
+
// --prompt-file was supplied (see tools/testing/sync-spawn-allowlist.json).
|
|
456
|
+
// Reached only when `interactive` is true (#3062) — the headless and
|
|
457
|
+
// no-terminal paths above are handled before this point. Not a candidate
|
|
458
|
+
// for a bounded timeout.
|
|
254
459
|
const result = spawnSync(cfg.binary, binaryArgs, {
|
|
255
460
|
stdio: 'inherit',
|
|
256
461
|
env: process.env,
|
|
257
462
|
});
|
|
258
|
-
|
|
463
|
+
const { exitCode, message } = exitCodeForSpawnResult(result, cfg.binary);
|
|
464
|
+
return message ? { exitCode, message } : { exitCode };
|
|
259
465
|
}
|
|
260
466
|
// ── Handler ───────────────────────────────────────────────────────────────────
|
|
261
467
|
export const sessionHandler = {
|
|
@@ -265,11 +471,93 @@ export const sessionHandler = {
|
|
|
265
471
|
category: 'project',
|
|
266
472
|
aliases: [],
|
|
267
473
|
async execute(ctx) {
|
|
268
|
-
|
|
474
|
+
let parsed;
|
|
475
|
+
try {
|
|
476
|
+
parsed = parseSessionArgs(ctx.args);
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
// #3062 review item 11: a value-taking flag present without a usable
|
|
480
|
+
// value is a usage error, reported before any preflight/spawn/write.
|
|
481
|
+
if (err instanceof MissingFlagValueError) {
|
|
482
|
+
return { exitCode: 2, message: `aiwg session: ${err.message}.` };
|
|
483
|
+
}
|
|
484
|
+
throw err;
|
|
485
|
+
}
|
|
486
|
+
const { mcp, provider: explicitProvider, noRepair, profile, persist, prompt: promptArg, promptFile, nonInteractive, } = parsed;
|
|
269
487
|
const cwd = ctx.cwd || process.cwd();
|
|
488
|
+
// #3062 review item 12: the one shared interactive decision, taking
|
|
489
|
+
// this command's own explicit `--non-interactive` flag as input instead
|
|
490
|
+
// of only ever checking the environment.
|
|
491
|
+
const interactive = isInteractive(nonInteractive);
|
|
492
|
+
// ── Resolve headless prompt (#3062) — before any pre-flight spawn ──────
|
|
493
|
+
if (promptArg && promptFile) {
|
|
494
|
+
return { exitCode: 2, message: 'aiwg session: pass only one of --prompt or --prompt-file.' };
|
|
495
|
+
}
|
|
496
|
+
let prompt = promptArg;
|
|
497
|
+
if (!prompt && promptFile) {
|
|
498
|
+
let fileText;
|
|
499
|
+
try {
|
|
500
|
+
fileText = await readFile(promptFile, 'utf-8');
|
|
501
|
+
}
|
|
502
|
+
catch (err) {
|
|
503
|
+
return {
|
|
504
|
+
exitCode: 1,
|
|
505
|
+
message: `aiwg session: could not read --prompt-file '${promptFile}': ${err instanceof Error ? err.message : String(err)}`,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
prompt = fileText.trim();
|
|
509
|
+
if (!prompt) {
|
|
510
|
+
return { exitCode: 1, message: `aiwg session: --prompt-file '${promptFile}' is empty.` };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
270
513
|
// ── Resolve provider ─────────────────────────────────────────
|
|
271
514
|
const provider = await resolveProvider(explicitProvider, cwd);
|
|
272
515
|
const providerCfg = PROVIDER_CONFIGS[provider];
|
|
516
|
+
// An unsupported (non-spawnable) provider can never run a supplied
|
|
517
|
+
// prompt — it has no binary to hand it to. Validate this before any
|
|
518
|
+
// pre-flight step runs a child process (#3062 review item 9): the old
|
|
519
|
+
// behavior silently discarded the prompt and printed IDE guidance at
|
|
520
|
+
// exit 0.
|
|
521
|
+
if (prompt && !isSpawnableProvider(provider)) {
|
|
522
|
+
const unsupportedCfg = getProviderConfig(provider);
|
|
523
|
+
return {
|
|
524
|
+
exitCode: 1,
|
|
525
|
+
message: `Provider '${unsupportedCfg.name}' has no CLI binary to spawn; --prompt/--prompt-file ` +
|
|
526
|
+
'requires a spawnable provider. Run `aiwg session --provider ' + provider +
|
|
527
|
+
'` from a terminal for IDE-integrated usage instead.',
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
// A spawnable provider with no evidenced non-interactive CLI form
|
|
531
|
+
// (e.g. antigravity) can never run a supplied prompt either — check
|
|
532
|
+
// this in the same early spot as the non-spawnable case above (#3062
|
|
533
|
+
// second review, finding 6): the old behavior deferred this to
|
|
534
|
+
// `buildHeadlessLaunch()` inside `launchProvider()`, reached only
|
|
535
|
+
// after the version-check/doctor/deployment/mcp-inject pre-flight
|
|
536
|
+
// already ran. The codex-profile launch path is exempt — it always
|
|
537
|
+
// uses `codex exec -`, never `HEADLESS_SESSION_TRANSPORTS`.
|
|
538
|
+
if (prompt && isSpawnableProvider(provider) && !HEADLESS_SESSION_TRANSPORTS[provider]
|
|
539
|
+
&& !((provider === 'codex' || provider === 'openai') && profile)) {
|
|
540
|
+
const unsupportedTransportCfg = getProviderConfig(provider);
|
|
541
|
+
return {
|
|
542
|
+
exitCode: 1,
|
|
543
|
+
message: `Provider '${unsupportedTransportCfg.name}' has no evidenced non-interactive CLI form; ` +
|
|
544
|
+
`--prompt/--prompt-file is not supported for it. Run \`aiwg session --provider ${provider}\` ` +
|
|
545
|
+
'from a terminal instead.',
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
// Without a terminal, a spawnable provider launched with no prompt would
|
|
549
|
+
// hand over a terminal that does not exist and hang. Fail fast, before
|
|
550
|
+
// any pre-flight step runs a child process (#3062). IDE-integrated
|
|
551
|
+
// providers only ever print guidance — never spawn — so they don't need
|
|
552
|
+
// a prompt to stay safe headless.
|
|
553
|
+
if (!prompt && isSpawnableProvider(provider) && !interactive) {
|
|
554
|
+
return {
|
|
555
|
+
exitCode: 1,
|
|
556
|
+
message: `aiwg session requires --prompt <text> or --prompt-file <path> when no interactive terminal is ` +
|
|
557
|
+
`attached (provider '${provider}' launches a binary that would otherwise hand over the terminal ` +
|
|
558
|
+
'and hang).',
|
|
559
|
+
};
|
|
560
|
+
}
|
|
273
561
|
const profileSuffix = profile ? ` · profile: ${profile}` : '';
|
|
274
562
|
console.log(`\n── aiwg session ── provider: ${providerCfg?.name ?? provider}${profileSuffix} ──\n`);
|
|
275
563
|
const frameworkRoot = await getFrameworkRoot();
|
|
@@ -298,38 +586,84 @@ export const sessionHandler = {
|
|
|
298
586
|
// ── Step 4: MCP inject ────────────────────────────────────────
|
|
299
587
|
let mcpInjected = false;
|
|
300
588
|
let mcpConfigPath = null;
|
|
589
|
+
let settingsPath = null;
|
|
301
590
|
if (profile) {
|
|
302
591
|
// Profile-aware injection (#891) — ephemeral by default
|
|
303
|
-
//
|
|
304
|
-
if (
|
|
592
|
+
// Codex profiles must pass policy and setup before any launch (#892)
|
|
593
|
+
if (provider === 'codex' || provider === 'openai') {
|
|
305
594
|
try {
|
|
306
|
-
console.log(`\n Setting up Codex runtime home for profile "${profile}"...`);
|
|
595
|
+
console.log(`\n Setting up Codex ${persist ? 'persistent config' : 'runtime home'} for profile "${profile}"...`);
|
|
307
596
|
// Import server list for this profile
|
|
308
597
|
const { McpProfileRegistry } = await import('../../mcp/profiles.js');
|
|
309
598
|
const { McpServerRegistry } = await import('../../mcp/registry.js');
|
|
310
599
|
const profiles = new McpProfileRegistry();
|
|
311
600
|
const registry = new McpServerRegistry();
|
|
312
601
|
const resolvedServers = await profiles.resolveServers(profile, registry);
|
|
313
|
-
await
|
|
314
|
-
await
|
|
315
|
-
|
|
602
|
+
const { resolveToolFilters } = await import('../../mcp/tool-filters.mjs');
|
|
603
|
+
const { assertCredentialPolicy, resolveCredentialPolicy } = await import('../../mcp/credentials.mjs');
|
|
604
|
+
const registryPolicy = await registry.getCredentialPolicy();
|
|
605
|
+
const floorPolicy = await registry.getCredentialPolicyFloor();
|
|
606
|
+
const floorSource = await registry.getCredentialPolicyFloorSource();
|
|
607
|
+
let credentialPolicy;
|
|
608
|
+
try {
|
|
609
|
+
credentialPolicy = resolveCredentialPolicy({ registryPolicy, floorPolicy, floorSource });
|
|
610
|
+
assertCredentialPolicy(resolvedServers, credentialPolicy);
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
console.error(` ${err instanceof Error ? err.message : String(err)}`);
|
|
614
|
+
return { exitCode: 1 };
|
|
615
|
+
}
|
|
616
|
+
if (persist) {
|
|
617
|
+
injectMcpProfile(provider, profile, cwd, true);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
await ensureRuntimeHome(profile);
|
|
621
|
+
// writeProfileConfig re-resolves and re-enforces the floor itself (so a
|
|
622
|
+
// direct API call cannot bypass it); passing it here too keeps this
|
|
623
|
+
// call's diagnostics consistent with the one already performed above.
|
|
624
|
+
const warnings = await writeProfileConfig(profile, resolvedServers, {
|
|
625
|
+
credentialPolicy, floorPolicy,
|
|
626
|
+
toolFilters: resolveToolFilters(await profiles.resolve(profile), 'codex'),
|
|
627
|
+
});
|
|
628
|
+
for (const warning of warnings)
|
|
629
|
+
console.warn(` WARN codex: ${warning}`);
|
|
630
|
+
console.log(` Runtime home ready. Profile servers: ${resolvedServers.map((s) => s.name).join(', ') || '(none)'}`);
|
|
631
|
+
}
|
|
316
632
|
mcpInjected = true;
|
|
317
633
|
}
|
|
318
634
|
catch (err) {
|
|
319
635
|
console.warn(` WARN Codex runtime home setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
320
|
-
|
|
636
|
+
return { exitCode: 1 };
|
|
321
637
|
}
|
|
322
638
|
}
|
|
323
639
|
else {
|
|
324
|
-
|
|
325
|
-
|
|
640
|
+
try {
|
|
641
|
+
let hasClaudePermissions = false;
|
|
642
|
+
if (provider === 'claude' || provider === 'claude-code') {
|
|
643
|
+
const { McpProfileRegistry } = await import('../../mcp/profiles.js');
|
|
644
|
+
const { McpServerRegistry } = await import('../../mcp/registry.js');
|
|
645
|
+
const { resolveToolFilters, planToolFilters } = await import('../../mcp/tool-filters.mjs');
|
|
646
|
+
const profiles = new McpProfileRegistry();
|
|
647
|
+
const servers = await profiles.resolveServers(profile, new McpServerRegistry());
|
|
648
|
+
const plan = planToolFilters(provider, servers.map(server => server.name), resolveToolFilters(await profiles.resolve(profile), provider));
|
|
649
|
+
hasClaudePermissions = plan.claudePermissions !== null;
|
|
650
|
+
}
|
|
651
|
+
const ephemeral = injectMcpProfile(provider, profile, cwd, persist, hasClaudePermissions);
|
|
652
|
+
mcpConfigPath = ephemeral?.mcpConfigPath ?? null;
|
|
653
|
+
settingsPath = ephemeral?.settingsPath ?? null;
|
|
654
|
+
mcpInjected = ephemeral !== null || persist;
|
|
655
|
+
}
|
|
656
|
+
catch (err) {
|
|
657
|
+
console.error(` ${err instanceof Error ? err.message : String(err)}`);
|
|
658
|
+
return { exitCode: 1 };
|
|
659
|
+
}
|
|
326
660
|
}
|
|
327
661
|
}
|
|
328
662
|
else if (mcp) {
|
|
329
663
|
mcpInjected = injectMcp(provider, cwd);
|
|
330
664
|
}
|
|
331
665
|
// ── Step 5: Launch ────────────────────────────────────────────
|
|
332
|
-
return launchProvider(provider, mcpInjected, mcpConfigPath, profile);
|
|
666
|
+
return launchProvider(provider, mcpInjected, mcpConfigPath, persist && (provider === 'codex' || provider === 'openai') ? null : profile, settingsPath, prompt, interactive);
|
|
333
667
|
},
|
|
334
668
|
};
|
|
335
669
|
//# sourceMappingURL=session.js.map
|