@the-open-engine/zeroshot 6.4.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/providers.js +11 -5
- package/cli/index.js +79 -16
- package/cli/lib/first-run.js +14 -3
- package/docker/zeroshot-cluster/Dockerfile +11 -1
- package/lib/agent-cli-provider/acp-stdio-runner.d.ts +4 -0
- package/lib/agent-cli-provider/acp-stdio-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/acp-stdio-runner.js +259 -0
- package/lib/agent-cli-provider/acp-stdio-runner.js.map +1 -0
- package/lib/agent-cli-provider/adapters/acp.d.ts +23 -0
- package/lib/agent-cli-provider/adapters/acp.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/acp.js +373 -0
- package/lib/agent-cli-provider/adapters/acp.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude.js +2 -29
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
- package/lib/agent-cli-provider/adapters/copilot-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.js +154 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/copilot.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot.js +176 -0
- package/lib/agent-cli-provider/adapters/copilot.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gateway.d.ts +6 -0
- package/lib/agent-cli-provider/adapters/gateway.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gateway.js +161 -0
- package/lib/agent-cli-provider/adapters/gateway.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/gemini.js +9 -2
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -1
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/index.js +6 -34
- package/lib/agent-cli-provider/adapters/index.js.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +5 -1
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/adapters/pi.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/pi.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/pi.js +331 -0
- package/lib/agent-cli-provider/adapters/pi.js.map +1 -0
- package/lib/agent-cli-provider/claude-command.d.ts +6 -0
- package/lib/agent-cli-provider/claude-command.d.ts.map +1 -0
- package/lib/agent-cli-provider/claude-command.js +43 -0
- package/lib/agent-cli-provider/claude-command.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-actions.js +13 -17
- package/lib/agent-cli-provider/contract-actions.js.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.js +10 -3
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-options.js +57 -2
- package/lib/agent-cli-provider/contract-options.js.map +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-support.js +25 -3
- package/lib/agent-cli-provider/contract-support.js.map +1 -1
- package/lib/agent-cli-provider/errors.d.ts +1 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -1
- package/lib/agent-cli-provider/errors.js +14 -5
- package/lib/agent-cli-provider/errors.js.map +1 -1
- package/lib/agent-cli-provider/gateway-client.d.ts +32 -0
- package/lib/agent-cli-provider/gateway-client.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-client.js +135 -0
- package/lib/agent-cli-provider/gateway-client.js.map +1 -0
- package/lib/agent-cli-provider/gateway-runner.d.ts +9 -0
- package/lib/agent-cli-provider/gateway-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-runner.js +294 -0
- package/lib/agent-cli-provider/gateway-runner.js.map +1 -0
- package/lib/agent-cli-provider/gateway-tools.d.ts +14 -0
- package/lib/agent-cli-provider/gateway-tools.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-tools.js +495 -0
- package/lib/agent-cli-provider/gateway-tools.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +3 -2
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +14 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.d.ts +2 -2
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.js +17 -3
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +437 -0
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -0
- package/lib/agent-cli-provider/provider-registry.js +411 -0
- package/lib/agent-cli-provider/provider-registry.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +7 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +139 -19
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +106 -4
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/lib/docker-config.js +24 -13
- package/lib/provider-defaults.js +15 -4
- package/lib/provider-detection.js +2 -0
- package/lib/provider-names.js +32 -12
- package/lib/repo-settings.js +15 -1
- package/lib/settings/claude-auth.js +3 -6
- package/lib/settings.js +17 -2
- package/lib/setup-apply.js +300 -0
- package/lib/setup-journal.js +109 -0
- package/lib/setup-plan.js +406 -0
- package/lib/setup-undo.js +88 -0
- package/lib/start-cluster.js +12 -1
- package/lib/stream-json-parser.js +7 -5
- package/package.json +31 -1
- package/scripts/assert-release-published.js +53 -0
- package/scripts/live-provider-smoke.js +221 -0
- package/scripts/release-preflight.js +236 -0
- package/src/agent/agent-lifecycle.js +5 -4
- package/src/agent/agent-quality-gate-schema.js +2 -2
- package/src/agent/agent-task-executor.js +41 -2
- package/src/agent-cli-provider/acp-stdio-runner.ts +314 -0
- package/src/agent-cli-provider/adapters/acp.ts +493 -0
- package/src/agent-cli-provider/adapters/claude.ts +2 -32
- package/src/agent-cli-provider/adapters/copilot-parser.ts +166 -0
- package/src/agent-cli-provider/adapters/copilot.ts +231 -0
- package/src/agent-cli-provider/adapters/gateway.ts +186 -0
- package/src/agent-cli-provider/adapters/gemini.ts +9 -2
- package/src/agent-cli-provider/adapters/index.ts +5 -36
- package/src/agent-cli-provider/adapters/opencode.ts +4 -1
- package/src/agent-cli-provider/adapters/pi.ts +410 -0
- package/src/agent-cli-provider/claude-command.ts +47 -0
- package/src/agent-cli-provider/contract-actions.ts +14 -17
- package/src/agent-cli-provider/contract-invoke.ts +12 -3
- package/src/agent-cli-provider/contract-options.ts +72 -3
- package/src/agent-cli-provider/contract-support.ts +30 -3
- package/src/agent-cli-provider/errors.ts +14 -4
- package/src/agent-cli-provider/gateway-client.ts +170 -0
- package/src/agent-cli-provider/gateway-runner.ts +353 -0
- package/src/agent-cli-provider/gateway-tools.ts +616 -0
- package/src/agent-cli-provider/index.ts +28 -0
- package/src/agent-cli-provider/invoke-evidence.ts +24 -3
- package/src/agent-cli-provider/provider-registry.ts +515 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +192 -18
- package/src/agent-cli-provider/types.ts +145 -4
- package/src/config-validator.js +33 -15
- package/src/isolation-manager.js +124 -19
- package/src/orchestrator.js +23 -10
- package/src/preflight.js +83 -30
- package/src/providers/capabilities.js +15 -42
- package/src/providers/index.js +52 -72
- package/src/worktree-claude-config.js +17 -0
- package/task-lib/commands/run.js +1 -0
- package/task-lib/provider-helper-runtime.js +11 -0
- package/task-lib/runner.js +7 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup plan — read-only facts collector + the pinned, versioned setup contract.
|
|
3
|
+
*
|
|
4
|
+
* buildSetupPlan() is pure over its injected inputs: no direct process.env/fs
|
|
5
|
+
* reads for config data, no writes, no prompts. Everything downstream (apply,
|
|
6
|
+
* undo, the TTY wizard, agents) consumes the object this returns, so its shape
|
|
7
|
+
* (schemaVersion, facts, decisions, recommended, risk, proposedWrites) and the
|
|
8
|
+
* decisionId registry below are a stable contract — do not rename IDs or add a
|
|
9
|
+
* parallel run-mode field without updating every consumer.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const SCHEMA_VERSION = 1;
|
|
15
|
+
|
|
16
|
+
// Canonical settings key each decision maps to. `defaultIsolation` and
|
|
17
|
+
// `defaultDelivery` MUST stay pinned to the keys resolveEffectiveRunPlan
|
|
18
|
+
// already reads (today: settings.defaultDocker) — see the canonical-path
|
|
19
|
+
// rule in issue #605. Never introduce a second parallel key here.
|
|
20
|
+
const DECISION_PATHS = {
|
|
21
|
+
defaultProvider: { scope: 'global', path: 'defaultProvider' },
|
|
22
|
+
defaultIsolation: { scope: 'global', path: 'defaultDocker' },
|
|
23
|
+
allowLocalNoIsolation: { scope: 'global', path: 'allowLocalNoIsolation' },
|
|
24
|
+
defaultDelivery: { scope: 'global', path: 'defaultDelivery' },
|
|
25
|
+
defaultIssueSource: { scope: 'global', path: 'defaultIssueSource' },
|
|
26
|
+
prBase: { scope: 'repo', path: 'prBase' },
|
|
27
|
+
dockerMounts: { scope: 'global', path: 'dockerMounts' },
|
|
28
|
+
dockerEnvPassthrough: { scope: 'global', path: 'dockerEnvPassthrough' },
|
|
29
|
+
updatePolicy: { scope: 'global', path: 'updatePolicy' },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function providerLevelDecisionId(providerName) {
|
|
33
|
+
return `providerLevel.${providerName}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Settings keys a run-mode resolver actually reads today. Shared by
|
|
37
|
+
// buildProposedWrites below (never propose a write nobody will read) and by
|
|
38
|
+
// lib/setup-apply.js (never perform a write nobody will read) — one
|
|
39
|
+
// canonical answer to "is this key consumed?" so the two can't drift.
|
|
40
|
+
const CONSUMED_PATHS = new Set([
|
|
41
|
+
'global:defaultProvider',
|
|
42
|
+
'global:defaultDocker',
|
|
43
|
+
'global:defaultDelivery',
|
|
44
|
+
'global:defaultIssueSource',
|
|
45
|
+
'global:dockerMounts',
|
|
46
|
+
'global:dockerEnvPassthrough',
|
|
47
|
+
// No resolver consumes this yet; consumption is deferred to a future
|
|
48
|
+
// update-policy issue, but issue #606 explicitly sanctions writing it now.
|
|
49
|
+
'global:updatePolicy',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
function isConsumedPath(scope, targetPath) {
|
|
53
|
+
if (scope === 'global' && targetPath.startsWith('providerSettings.')) return true;
|
|
54
|
+
return CONSUMED_PATHS.has(`${scope}:${targetPath}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// providerLevel.<provider> decisionIds map to a per-provider settings key
|
|
58
|
+
// (providerSettings.<provider>) not present in DECISION_PATHS above, since
|
|
59
|
+
// the provider name is only known at runtime.
|
|
60
|
+
function resolveDecisionPath(decisionId) {
|
|
61
|
+
if (decisionId.startsWith('providerLevel.')) {
|
|
62
|
+
const providerName = decisionId.slice('providerLevel.'.length);
|
|
63
|
+
return { scope: 'global', path: `providerSettings.${providerName}` };
|
|
64
|
+
}
|
|
65
|
+
return DECISION_PATHS[decisionId] || null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getNestedValue(source, pathStr) {
|
|
69
|
+
return pathStr
|
|
70
|
+
.split('.')
|
|
71
|
+
.reduce((acc, key) => (acc === null || acc === undefined ? undefined : acc[key]), source);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function defaultDeps() {
|
|
75
|
+
const { commandExists, getCommandPath } = require('./provider-detection');
|
|
76
|
+
const { checkDocker, checkGhAuth } = require('../src/preflight');
|
|
77
|
+
const { execSync } = require('../src/lib/safe-exec');
|
|
78
|
+
const { listProviders, getProvider } = require('../src/providers');
|
|
79
|
+
const { getProviderDefaults } = require('./provider-defaults');
|
|
80
|
+
const packageJson = require('../package.json');
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
commandExists,
|
|
84
|
+
getCommandPath,
|
|
85
|
+
checkDocker,
|
|
86
|
+
checkGhAuth,
|
|
87
|
+
execSync,
|
|
88
|
+
listProviders,
|
|
89
|
+
getProvider,
|
|
90
|
+
getProviderDefaults,
|
|
91
|
+
getNodeVersion: () => process.version,
|
|
92
|
+
getPackageVersion: () => packageJson.version,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function detectInstallSource(cwd, env) {
|
|
97
|
+
if (env.npm_config_global === 'true') return 'npm-global';
|
|
98
|
+
if (env.npm_execpath && /_npx|npx/.test(env.npm_execpath)) return 'npx';
|
|
99
|
+
try {
|
|
100
|
+
const ownNodeModules = path.join(__dirname, '..', 'node_modules');
|
|
101
|
+
if (cwd && (cwd === path.join(__dirname, '..') || cwd.startsWith(ownNodeModules))) {
|
|
102
|
+
return 'local';
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// fall through to unknown
|
|
106
|
+
}
|
|
107
|
+
return 'unknown';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function buildNodeFacts({ cwd, env, deps }) {
|
|
111
|
+
return {
|
|
112
|
+
version: deps.getNodeVersion(),
|
|
113
|
+
packageVersion: deps.getPackageVersion(),
|
|
114
|
+
installSource: detectInstallSource(cwd, env),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildProviderFacts(deps) {
|
|
119
|
+
const providers = {};
|
|
120
|
+
for (const name of deps.listProviders()) {
|
|
121
|
+
let cliCommand = name;
|
|
122
|
+
try {
|
|
123
|
+
cliCommand = deps.getProvider(name).cliCommand || name;
|
|
124
|
+
} catch {
|
|
125
|
+
// Provider metadata unavailable — fall back to the provider name as the CLI command.
|
|
126
|
+
}
|
|
127
|
+
const cliAvailable = deps.commandExists(cliCommand);
|
|
128
|
+
providers[name] = {
|
|
129
|
+
cliAvailable,
|
|
130
|
+
path: cliAvailable ? deps.getCommandPath(cliCommand) : null,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return providers;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function safeExecTrim(deps, command, cwd) {
|
|
137
|
+
try {
|
|
138
|
+
return deps.execSync(command, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim();
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildGitFacts({ cwd, deps }) {
|
|
145
|
+
const isRepo = safeExecTrim(deps, 'git rev-parse --is-inside-work-tree', cwd) === 'true';
|
|
146
|
+
const ghAvailable = deps.commandExists('gh');
|
|
147
|
+
|
|
148
|
+
if (!isRepo) {
|
|
149
|
+
return { isRepo: false, branch: null, remote: null, ghAvailable, ghAuthed: null };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const branch = safeExecTrim(deps, 'git rev-parse --abbrev-ref HEAD', cwd);
|
|
153
|
+
const remote = safeExecTrim(deps, 'git remote get-url origin', cwd);
|
|
154
|
+
const ghAuthed = ghAvailable ? !!deps.checkGhAuth().authenticated : null;
|
|
155
|
+
|
|
156
|
+
return { isRepo: true, branch, remote, ghAvailable, ghAuthed };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function buildFacts({ cwd, settings, repoSettings, env, deps }) {
|
|
160
|
+
return {
|
|
161
|
+
node: buildNodeFacts({ cwd, env, deps }),
|
|
162
|
+
providers: buildProviderFacts(deps),
|
|
163
|
+
git: buildGitFacts({ cwd, deps }),
|
|
164
|
+
docker: { available: !!deps.checkDocker().available },
|
|
165
|
+
settings: {
|
|
166
|
+
hasGlobal: settings?.__meta ? !!settings.__meta.fileExists : true,
|
|
167
|
+
hasRepo: repoSettings !== null && repoSettings !== undefined,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function inferIssueSource(facts) {
|
|
173
|
+
const { parseGitRemoteUrl } = require('./git-remote-utils');
|
|
174
|
+
const parsed = parseGitRemoteUrl(facts.git.remote);
|
|
175
|
+
return parsed?.provider || null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function inferPrBase(cwd, deps) {
|
|
179
|
+
const branch = safeExecTrim(deps, 'git rev-parse --abbrev-ref origin/HEAD', cwd);
|
|
180
|
+
if (!branch) return null;
|
|
181
|
+
return branch.replace(/^origin\//, '');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function buildProviderLevelRecommendation(name, deps) {
|
|
185
|
+
const providerDefaults = deps.getProviderDefaults()[name] || {};
|
|
186
|
+
const minLevel = providerDefaults.minLevel;
|
|
187
|
+
const defaultLevel = providerDefaults.defaultLevel;
|
|
188
|
+
const maxLevel = providerDefaults.maxLevel;
|
|
189
|
+
const overrides = providerDefaults.levelOverrides || {};
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const provider = deps.getProvider(name);
|
|
193
|
+
return {
|
|
194
|
+
min: provider.resolveModelSpec(minLevel, overrides).model,
|
|
195
|
+
default: provider.resolveModelSpec(defaultLevel, overrides).model,
|
|
196
|
+
max: provider.resolveModelSpec(maxLevel, overrides).model,
|
|
197
|
+
};
|
|
198
|
+
} catch {
|
|
199
|
+
return { min: null, default: null, max: null };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function buildRecommendedAndRisk({ cwd, facts, env, deps }) {
|
|
204
|
+
const recommended = {};
|
|
205
|
+
const risk = {};
|
|
206
|
+
|
|
207
|
+
const firstAvailableProvider = Object.entries(facts.providers).find(
|
|
208
|
+
([, info]) => info.cliAvailable
|
|
209
|
+
);
|
|
210
|
+
recommended.defaultProvider = firstAvailableProvider ? firstAvailableProvider[0] : 'claude';
|
|
211
|
+
risk.defaultProvider = firstAvailableProvider ? 'low' : 'medium';
|
|
212
|
+
|
|
213
|
+
for (const name of Object.keys(facts.providers)) {
|
|
214
|
+
recommended[providerLevelDecisionId(name)] = buildProviderLevelRecommendation(name, deps);
|
|
215
|
+
risk[providerLevelDecisionId(name)] = 'low';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (facts.git.isRepo) {
|
|
219
|
+
recommended.defaultIsolation = 'worktree';
|
|
220
|
+
risk.defaultIsolation = 'low';
|
|
221
|
+
} else if (facts.docker.available) {
|
|
222
|
+
recommended.defaultIsolation = 'docker';
|
|
223
|
+
risk.defaultIsolation = 'low';
|
|
224
|
+
} else {
|
|
225
|
+
recommended.defaultIsolation = 'none';
|
|
226
|
+
risk.defaultIsolation = 'high';
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
recommended.allowLocalNoIsolation = false;
|
|
230
|
+
risk.allowLocalNoIsolation = 'low';
|
|
231
|
+
|
|
232
|
+
recommended.defaultDelivery = 'none';
|
|
233
|
+
risk.defaultDelivery = 'low';
|
|
234
|
+
|
|
235
|
+
const inferredIssueSource = inferIssueSource(facts);
|
|
236
|
+
recommended.defaultIssueSource = inferredIssueSource || 'github';
|
|
237
|
+
risk.defaultIssueSource = inferredIssueSource ? 'low' : 'medium';
|
|
238
|
+
|
|
239
|
+
const inferredPrBase = inferPrBase(cwd, deps);
|
|
240
|
+
recommended.prBase = inferredPrBase || 'main';
|
|
241
|
+
risk.prBase = inferredPrBase ? 'low' : 'medium';
|
|
242
|
+
|
|
243
|
+
recommended.dockerMounts = ['gh', 'git', 'ssh'];
|
|
244
|
+
risk.dockerMounts = 'low';
|
|
245
|
+
|
|
246
|
+
recommended.dockerEnvPassthrough = [];
|
|
247
|
+
risk.dockerEnvPassthrough = 'low';
|
|
248
|
+
|
|
249
|
+
const nonInteractive = env.CI === 'true' || env.__isTTY === false;
|
|
250
|
+
recommended.updatePolicy = nonInteractive ? 'off' : 'notify';
|
|
251
|
+
risk.updatePolicy = 'low';
|
|
252
|
+
|
|
253
|
+
return { recommended, risk, inferredIssueSource, inferredPrBase };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function currentValueFor(decisionId, settings, repoSettings) {
|
|
257
|
+
const target = resolveDecisionPath(decisionId);
|
|
258
|
+
if (!target) return null;
|
|
259
|
+
const source = target.scope === 'repo' ? repoSettings || {} : settings || {};
|
|
260
|
+
const value = getNestedValue(source, target.path);
|
|
261
|
+
return value === undefined ? null : value;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function buildDecisions({ facts, settings, repoSettings, inferredIssueSource, inferredPrBase }) {
|
|
265
|
+
const decisions = [];
|
|
266
|
+
const hasGlobal = facts.settings.hasGlobal;
|
|
267
|
+
const hasRepo = facts.settings.hasRepo;
|
|
268
|
+
|
|
269
|
+
const globalDecisionIds = [
|
|
270
|
+
'defaultProvider',
|
|
271
|
+
...Object.keys(facts.providers).map(providerLevelDecisionId),
|
|
272
|
+
'defaultIsolation',
|
|
273
|
+
'allowLocalNoIsolation',
|
|
274
|
+
'defaultDelivery',
|
|
275
|
+
'defaultIssueSource',
|
|
276
|
+
'dockerMounts',
|
|
277
|
+
'dockerEnvPassthrough',
|
|
278
|
+
'updatePolicy',
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
for (const decisionId of globalDecisionIds) {
|
|
282
|
+
const shouldInclude =
|
|
283
|
+
!hasGlobal || (decisionId === 'defaultIssueSource' && !inferredIssueSource);
|
|
284
|
+
if (!shouldInclude) continue;
|
|
285
|
+
decisions.push({
|
|
286
|
+
decisionId,
|
|
287
|
+
domain: domainFor(decisionId),
|
|
288
|
+
currentValue: currentValueFor(decisionId, settings, repoSettings),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!hasRepo || !inferredPrBase) {
|
|
293
|
+
decisions.push({
|
|
294
|
+
decisionId: 'prBase',
|
|
295
|
+
domain: domainFor('prBase'),
|
|
296
|
+
currentValue: currentValueFor('prBase', settings, repoSettings),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return decisions;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function domainFor(decisionId) {
|
|
304
|
+
if (decisionId.startsWith('providerLevel.')) return '{ min, default, max } of haiku|sonnet|opus';
|
|
305
|
+
const domains = {
|
|
306
|
+
defaultProvider: 'claude | codex | gemini | opencode',
|
|
307
|
+
defaultIsolation: 'worktree | docker | none',
|
|
308
|
+
allowLocalNoIsolation: 'boolean',
|
|
309
|
+
defaultDelivery: 'none | pr | ship',
|
|
310
|
+
defaultIssueSource: 'github | gitlab | jira | azure-devops | linear',
|
|
311
|
+
prBase: 'string (branch)',
|
|
312
|
+
dockerMounts: 'array of presets/objects',
|
|
313
|
+
dockerEnvPassthrough: 'string[]',
|
|
314
|
+
updatePolicy: 'off | notify | auto',
|
|
315
|
+
};
|
|
316
|
+
return domains[decisionId] || 'unknown';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function buildProposedWrites({ decisions, recommended }) {
|
|
320
|
+
const writes = [];
|
|
321
|
+
for (const decision of decisions) {
|
|
322
|
+
const target = resolveDecisionPath(decision.decisionId);
|
|
323
|
+
if (!target) continue;
|
|
324
|
+
// A decision can still be surfaced for the user to make (e.g. a future
|
|
325
|
+
// wizard asking about prBase), but proposing a *write* for a settings key
|
|
326
|
+
// no resolver reads would advertise a write that apply will always skip —
|
|
327
|
+
// dead config. Only propose writes apply will actually perform.
|
|
328
|
+
if (!isConsumedPath(target.scope, target.path)) continue;
|
|
329
|
+
let to = recommended[decision.decisionId];
|
|
330
|
+
if (decision.decisionId === 'defaultIsolation') {
|
|
331
|
+
to = to === 'docker';
|
|
332
|
+
}
|
|
333
|
+
if (to === decision.currentValue) continue;
|
|
334
|
+
writes.push({
|
|
335
|
+
scope: target.scope,
|
|
336
|
+
path: target.path,
|
|
337
|
+
from: decision.currentValue ?? null,
|
|
338
|
+
to,
|
|
339
|
+
decisionId: decision.decisionId,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return writes;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Build the pinned, versioned setup contract. Pure over injected inputs —
|
|
347
|
+
* performs only cheap, read-only detection (no writes, no prompts).
|
|
348
|
+
*
|
|
349
|
+
* @param {Object} params
|
|
350
|
+
* @param {string} params.cwd
|
|
351
|
+
* @param {Object} params.settings - loaded global settings (optionally with __meta.fileExists)
|
|
352
|
+
* @param {Object|null} params.repoSettings - repo-local settings object, or null
|
|
353
|
+
* @param {Object} params.env - caller-provided env (e.g. process.env), plus optional __isTTY
|
|
354
|
+
* @param {Object} [params.deps] - injected dependencies (for testing)
|
|
355
|
+
* @returns {Object} plan
|
|
356
|
+
*/
|
|
357
|
+
function buildSetupPlan({ cwd, settings, repoSettings, env, deps } = {}) {
|
|
358
|
+
const resolvedDeps = { ...defaultDeps(), ...(deps || {}) };
|
|
359
|
+
const resolvedEnv = env || {};
|
|
360
|
+
const resolvedCwd = cwd || '.';
|
|
361
|
+
const resolvedSettings = settings || {};
|
|
362
|
+
|
|
363
|
+
const facts = buildFacts({
|
|
364
|
+
cwd: resolvedCwd,
|
|
365
|
+
settings: resolvedSettings,
|
|
366
|
+
repoSettings,
|
|
367
|
+
env: resolvedEnv,
|
|
368
|
+
deps: resolvedDeps,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
const { recommended, risk, inferredIssueSource, inferredPrBase } = buildRecommendedAndRisk({
|
|
372
|
+
cwd: resolvedCwd,
|
|
373
|
+
facts,
|
|
374
|
+
env: resolvedEnv,
|
|
375
|
+
deps: resolvedDeps,
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const decisions = buildDecisions({
|
|
379
|
+
facts,
|
|
380
|
+
settings: resolvedSettings,
|
|
381
|
+
repoSettings,
|
|
382
|
+
inferredIssueSource,
|
|
383
|
+
inferredPrBase,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const proposedWrites = buildProposedWrites({ decisions, recommended });
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
schemaVersion: SCHEMA_VERSION,
|
|
390
|
+
facts,
|
|
391
|
+
decisions,
|
|
392
|
+
recommended,
|
|
393
|
+
risk,
|
|
394
|
+
proposedWrites,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
module.exports = {
|
|
399
|
+
buildSetupPlan,
|
|
400
|
+
resolveDecisionPath,
|
|
401
|
+
domainFor,
|
|
402
|
+
DECISION_PATHS,
|
|
403
|
+
getNestedValue,
|
|
404
|
+
isConsumedPath,
|
|
405
|
+
CONSUMED_PATHS,
|
|
406
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `zeroshot setup undo` — reverts writes made by `zeroshot setup apply` using
|
|
3
|
+
* the journal recorded at apply time (lib/setup-journal.js).
|
|
4
|
+
*
|
|
5
|
+
* Three-way conflict rule per journaled write, comparing `current` against
|
|
6
|
+
* `appliedValue`:
|
|
7
|
+
* - current === appliedValue -> restore priorValue (delete if null)
|
|
8
|
+
* - current === priorValue -> already-restored (no-op)
|
|
9
|
+
* - otherwise (changed elsewhere) -> skipped-modified, never clobbered
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
loadJournal,
|
|
14
|
+
getNestedValue,
|
|
15
|
+
setNestedValue,
|
|
16
|
+
deleteNestedKey,
|
|
17
|
+
deepEqual,
|
|
18
|
+
} = require('./setup-journal');
|
|
19
|
+
|
|
20
|
+
function defaultUndoDeps() {
|
|
21
|
+
const { loadSettings, saveSettings } = require('./settings');
|
|
22
|
+
const { readRepoSettings, writeRepoSettings } = require('./repo-settings');
|
|
23
|
+
|
|
24
|
+
return { loadSettings, saveSettings, readRepoSettings, writeRepoSettings };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Undo every journaled write, per the three-way conflict rule.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} [params]
|
|
31
|
+
* @param {Object} [params.deps] - Injected dependencies (for testing).
|
|
32
|
+
* @returns {Array<{scope: string, path: string, repoRoot: string|null, status: string, current?: *, wouldRestore?: *}>}
|
|
33
|
+
*/
|
|
34
|
+
function undo({ deps = {} } = {}) {
|
|
35
|
+
const resolvedDeps = { ...defaultUndoDeps(), ...deps };
|
|
36
|
+
const journal = loadJournal();
|
|
37
|
+
|
|
38
|
+
const globalSettings = resolvedDeps.loadSettings();
|
|
39
|
+
const repoSettingsCache = new Map();
|
|
40
|
+
let globalDirty = false;
|
|
41
|
+
const dirtyRepoRoots = new Set();
|
|
42
|
+
|
|
43
|
+
function repoSettingsFor(repoRoot) {
|
|
44
|
+
if (!repoSettingsCache.has(repoRoot)) {
|
|
45
|
+
const { settings } = resolvedDeps.readRepoSettings(repoRoot);
|
|
46
|
+
repoSettingsCache.set(repoRoot, settings || {});
|
|
47
|
+
}
|
|
48
|
+
return repoSettingsCache.get(repoRoot);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const results = journal.entries.map((entry) => {
|
|
52
|
+
const settingsObj = entry.scope === 'repo' ? repoSettingsFor(entry.repoRoot) : globalSettings;
|
|
53
|
+
const current = getNestedValue(settingsObj, entry.path) ?? null;
|
|
54
|
+
|
|
55
|
+
if (deepEqual(current, entry.priorValue)) {
|
|
56
|
+
return { ...entry, status: 'already-restored' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!deepEqual(current, entry.appliedValue)) {
|
|
60
|
+
return { ...entry, status: 'skipped-modified', current, wouldRestore: entry.priorValue };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (entry.priorValue === null) {
|
|
64
|
+
deleteNestedKey(settingsObj, entry.path);
|
|
65
|
+
} else {
|
|
66
|
+
setNestedValue(settingsObj, entry.path, entry.priorValue);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (entry.scope === 'repo') {
|
|
70
|
+
dirtyRepoRoots.add(entry.repoRoot);
|
|
71
|
+
} else {
|
|
72
|
+
globalDirty = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { ...entry, status: entry.priorValue === null ? 'deleted' : 'restored' };
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (globalDirty) resolvedDeps.saveSettings(globalSettings);
|
|
79
|
+
for (const repoRoot of dirtyRepoRoots) {
|
|
80
|
+
resolvedDeps.writeRepoSettings(repoRoot, repoSettingsFor(repoRoot));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Journal entries are left in place (not cleared) so a re-run reports
|
|
84
|
+
// 'already-restored' instead of finding nothing to undo.
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { undo };
|
package/lib/start-cluster.js
CHANGED
|
@@ -295,6 +295,16 @@ function resolveConfigOrThrow({
|
|
|
295
295
|
return loadClusterConfig(orchestrator, resolvedPath, settings, providerOverride);
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
function applyDefaultDeliveryOptions(options, settings) {
|
|
299
|
+
if (settings.defaultDelivery === 'pr') {
|
|
300
|
+
return { ...options, pr: true };
|
|
301
|
+
}
|
|
302
|
+
if (settings.defaultDelivery === 'ship') {
|
|
303
|
+
return { ...options, ship: true };
|
|
304
|
+
}
|
|
305
|
+
return options;
|
|
306
|
+
}
|
|
307
|
+
|
|
298
308
|
function startClusterWithInput(args, input) {
|
|
299
309
|
const { orchestrator, config, configPath, configName, settings, providerOverride } = args;
|
|
300
310
|
if (!orchestrator) {
|
|
@@ -310,7 +320,7 @@ function startClusterWithInput(args, input) {
|
|
|
310
320
|
});
|
|
311
321
|
const startOptions = buildStartOptions({
|
|
312
322
|
clusterId: args.clusterId,
|
|
313
|
-
options: args.options || {},
|
|
323
|
+
options: applyDefaultDeliveryOptions(args.options || {}, settings),
|
|
314
324
|
settings,
|
|
315
325
|
providerOverride,
|
|
316
326
|
modelOverride: args.modelOverride,
|
|
@@ -344,6 +354,7 @@ module.exports = {
|
|
|
344
354
|
resolveConfigPath,
|
|
345
355
|
loadClusterConfig,
|
|
346
356
|
buildStartOptions,
|
|
357
|
+
resolveEffectiveRunPlan,
|
|
347
358
|
startClusterFromText,
|
|
348
359
|
startClusterFromIssue,
|
|
349
360
|
startClusterFromFile,
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* Provider parsing is delegated to the helper-backed runtime provider facade.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { getProvider } = require('../src/providers');
|
|
7
|
+
const { getProvider, listProviders } = require('../src/providers');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
function createProviderParsers() {
|
|
10
|
+
return listProviders().map((name) => getProvider(name));
|
|
11
|
+
}
|
|
11
12
|
|
|
12
13
|
function stripTimestampPrefix(line) {
|
|
13
14
|
if (!line || typeof line !== 'string') return '';
|
|
@@ -28,7 +29,7 @@ function stripTimestampPrefix(line) {
|
|
|
28
29
|
return trimmed;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
function parseEvent(line) {
|
|
32
|
+
function parseEvent(line, providerParsers = createProviderParsers()) {
|
|
32
33
|
const content = stripTimestampPrefix(line);
|
|
33
34
|
if (!content) return null;
|
|
34
35
|
|
|
@@ -52,10 +53,11 @@ function collectEvent(events, event) {
|
|
|
52
53
|
function parseChunk(chunk) {
|
|
53
54
|
const events = [];
|
|
54
55
|
const lines = String(chunk || '').split('\n');
|
|
56
|
+
const providerParsers = createProviderParsers();
|
|
55
57
|
|
|
56
58
|
for (const line of lines) {
|
|
57
59
|
if (!line.trim()) continue;
|
|
58
|
-
collectEvent(events, parseEvent(line));
|
|
60
|
+
collectEvent(events, parseEvent(line, providerParsers));
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
return events;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-open-engine/zeroshot",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.0",
|
|
4
4
|
"description": "Multi-agent orchestration engine for Claude, Codex, and Gemini",
|
|
5
5
|
"main": "src/orchestrator.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,32 @@
|
|
|
11
11
|
"example": "examples",
|
|
12
12
|
"test": "tests"
|
|
13
13
|
},
|
|
14
|
+
"release": {
|
|
15
|
+
"branches": [
|
|
16
|
+
"main"
|
|
17
|
+
],
|
|
18
|
+
"plugins": [
|
|
19
|
+
[
|
|
20
|
+
"@semantic-release/commit-analyzer",
|
|
21
|
+
{
|
|
22
|
+
"releaseRules": [
|
|
23
|
+
{
|
|
24
|
+
"type": "release",
|
|
25
|
+
"release": "minor"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"@semantic-release/release-notes-generator",
|
|
31
|
+
[
|
|
32
|
+
"@semantic-release/npm",
|
|
33
|
+
{
|
|
34
|
+
"npmPublish": true
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"@semantic-release/github"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
14
40
|
"scripts": {
|
|
15
41
|
"pretest": "npm run build:agent-cli-provider",
|
|
16
42
|
"test": "node tests/run-tests.js",
|
|
@@ -28,6 +54,8 @@
|
|
|
28
54
|
"lint:agent-cli-provider": "eslint \"src/agent-cli-provider/**/*.ts\" \"tests/agent-cli-provider/**/*.ts\"",
|
|
29
55
|
"build:agent-cli-provider": "tsc --project tsconfig.agent-cli-provider.build.json",
|
|
30
56
|
"test:agent-cli-provider": "node --test tests/agent-cli-provider/*.test.js",
|
|
57
|
+
"test:providers:live": "npm run build:agent-cli-provider && node scripts/live-provider-smoke.js",
|
|
58
|
+
"check:agent-cli-provider": "npm run check:agent-cli-provider:ci",
|
|
31
59
|
"check:agent-cli-provider:ci": "npm run typecheck:agent-cli-provider && npm run lint:agent-cli-provider && npm run build:agent-cli-provider && npm run test:agent-cli-provider",
|
|
32
60
|
"dev:link": "npm link",
|
|
33
61
|
"lint": "eslint .",
|
|
@@ -43,6 +71,8 @@
|
|
|
43
71
|
"check": "npm run typecheck && npm run lint",
|
|
44
72
|
"check:all": "npm run check && npm run deadcode:all",
|
|
45
73
|
"release": "semantic-release",
|
|
74
|
+
"release:preflight": "node scripts/release-preflight.js",
|
|
75
|
+
"release:assert-published": "node scripts/assert-release-published.js",
|
|
46
76
|
"prepublishOnly": "npm run lint && npm run typecheck && npm run check:agent-cli-provider:ci",
|
|
47
77
|
"prepare": "npm run build:agent-cli-provider && husky"
|
|
48
78
|
},
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execFileSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
function run(command, args) {
|
|
6
|
+
return execFileSync(command, args, { encoding: 'utf8' }).trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function packageName() {
|
|
10
|
+
return require('../package.json').name;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function npmLatest(name) {
|
|
14
|
+
return JSON.parse(run('npm', ['view', name, 'dist-tags.latest', '--json']));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function tagsPointingAtHead() {
|
|
18
|
+
run('git', ['fetch', '--tags', '--force']);
|
|
19
|
+
return run('git', ['tag', '--points-at', 'HEAD', '--list', 'v[0-9]*'])
|
|
20
|
+
.split(/\r?\n/)
|
|
21
|
+
.map((tag) => tag.trim())
|
|
22
|
+
.filter(Boolean);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function main() {
|
|
26
|
+
const name = packageName();
|
|
27
|
+
const latest = npmLatest(name);
|
|
28
|
+
const expectedTag = `v${latest}`;
|
|
29
|
+
const headTags = tagsPointingAtHead();
|
|
30
|
+
|
|
31
|
+
console.log(`npm latest for ${name}: ${latest}`);
|
|
32
|
+
console.log(`tags on HEAD: ${headTags.join(', ') || '(none)'}`);
|
|
33
|
+
|
|
34
|
+
if (!headTags.includes(expectedTag)) {
|
|
35
|
+
throw new Error(`expected ${expectedTag} to point at HEAD after release`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(`Release publication verified: ${name}@${latest}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (require.main === module) {
|
|
42
|
+
try {
|
|
43
|
+
main();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`Release publication check failed: ${error.message}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
npmLatest,
|
|
52
|
+
tagsPointingAtHead,
|
|
53
|
+
};
|