ultra-builder-pro-cli 3.0.2 → 5.0.1
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/.ultra-template/contexts/TEMPLATE.md +6 -0
- package/CHANGELOG.md +83 -1
- package/README.md +126 -36
- package/README.zh-CN.md +440 -0
- package/adapters/_shared/host-profile.cjs +3 -151
- package/adapters/_shared/plugin-core.cjs +57 -0
- package/adapters/_shared/runtime-assets.cjs +64 -12
- package/adapters/codex.js +3 -6
- package/bin/delegate-worker.cjs +2 -345
- package/bin/delegate.cjs +3 -530
- package/docs/ARCHITECTURE.md +59 -18
- package/docs/ARTIFACT-AUTHORITY.md +21 -5
- package/docs/DECISIONS.md +31 -10
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +28 -12
- package/docs/RUNTIME-COMPAT-MATRIX.md +106 -26
- package/docs/SKILL-AUTHORING.md +15 -3
- package/docs/WORKFLOW-LIFECYCLE.md +83 -29
- package/docs/evals/ultra-5-0-journey-results.md +62 -0
- package/docs/evals/ultra-5-0-live-acp-conformance.md +93 -0
- package/docs/evals/ultra-5-0-route-journeys.md +145 -0
- package/package.json +2 -1
- package/skills/ultra-change/SKILL.md +30 -4
- package/skills/ultra-delegate/SKILL.md +80 -7
- package/skills/ultra-delegate/patches/zcode-acp-0.1.0-zcode-0.16.5.patch +184 -0
- package/skills/ultra-delegate/references/delegation-contract.md +60 -1
- package/skills/ultra-delegate/scripts/acp-conformance.cjs +287 -0
- package/skills/ultra-delegate/scripts/acp-driver.cjs +762 -0
- package/skills/ultra-delegate/scripts/agent-execution-driver.cjs +320 -0
- package/skills/ultra-delegate/scripts/cli-driver.cjs +61 -0
- package/skills/ultra-delegate/scripts/delegate-worker.cjs +328 -0
- package/skills/ultra-delegate/scripts/delegate.cjs +690 -0
- package/skills/ultra-delegate/scripts/host-profile.cjs +267 -0
- package/skills/ultra-deliver/SKILL.md +13 -0
- package/skills/ultra-dev/SKILL.md +18 -2
- package/skills/ultra-init/SKILL.md +12 -0
- package/skills/ultra-plan/SKILL.md +21 -3
- package/skills/ultra-research/SKILL.md +33 -5
- package/skills/ultra-status/SKILL.md +37 -7
- package/skills/ultra-status/references/route-contract.md +100 -0
- package/skills/ultra-test/SKILL.md +16 -1
- /package/{adapters/_shared → skills/ultra-delegate/scripts}/delegate-profiles/kimi-read-only.md +0 -0
- /package/{adapters/_shared → skills/ultra-delegate/scripts}/delegate-profiles/kimi-write.md +0 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const crypto = require('node:crypto');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const { execFileSync } = require('node:child_process');
|
|
9
|
+
|
|
10
|
+
const { run: runDelegate } = require('./delegate.cjs');
|
|
11
|
+
const { hostProfile, ZCODE_ACP_SOURCE } = require('./host-profile.cjs');
|
|
12
|
+
|
|
13
|
+
const HOSTS = new Set(['claude', 'codex', 'opencode', 'kimi', 'grok', 'zcode']);
|
|
14
|
+
const RECEIPT_SCHEMA = 'ultra-acp-conformance-receipt-v1';
|
|
15
|
+
|
|
16
|
+
function parseArgs(argv) {
|
|
17
|
+
const allowed = new Set(['--host', '--output', '--timeout', '--command', '--args-json']);
|
|
18
|
+
const values = {};
|
|
19
|
+
for (let index = 0; index < argv.length; index += 2) {
|
|
20
|
+
const key = argv[index];
|
|
21
|
+
const value = argv[index + 1];
|
|
22
|
+
if (!allowed.has(key) || value === undefined || value === '' || values[key] !== undefined) {
|
|
23
|
+
throw new Error(`invalid ACP conformance argument: ${key || '<missing>'}`);
|
|
24
|
+
}
|
|
25
|
+
values[key] = value;
|
|
26
|
+
}
|
|
27
|
+
if (!HOSTS.has(values['--host'])) throw new Error('ACP conformance requires a supported --host');
|
|
28
|
+
if (!values['--output']) throw new Error('ACP conformance requires --output');
|
|
29
|
+
const timeoutSeconds = values['--timeout'] === undefined ? 240 : Number(values['--timeout']);
|
|
30
|
+
if (!Number.isFinite(timeoutSeconds) || timeoutSeconds < 1 || timeoutSeconds > 600) {
|
|
31
|
+
throw new Error('ACP conformance --timeout must be between 1 and 600 seconds');
|
|
32
|
+
}
|
|
33
|
+
const hasCommand = values['--command'] !== undefined;
|
|
34
|
+
const hasArgs = values['--args-json'] !== undefined;
|
|
35
|
+
if (hasCommand !== hasArgs) throw new Error('--command and --args-json must be supplied together');
|
|
36
|
+
let argsOverride = null;
|
|
37
|
+
if (hasArgs) {
|
|
38
|
+
try { argsOverride = JSON.parse(values['--args-json']); } catch {
|
|
39
|
+
throw new Error('--args-json must be a JSON string array');
|
|
40
|
+
}
|
|
41
|
+
if (!Array.isArray(argsOverride) || !argsOverride.every((item) => typeof item === 'string')) {
|
|
42
|
+
throw new Error('--args-json must be a JSON string array');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
host: values['--host'],
|
|
47
|
+
output: path.resolve(values['--output']),
|
|
48
|
+
timeoutSeconds,
|
|
49
|
+
commandOverride: values['--command'] || null,
|
|
50
|
+
argsOverride,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function git(cwd, args) {
|
|
55
|
+
return execFileSync('git', args, {
|
|
56
|
+
cwd,
|
|
57
|
+
encoding: 'utf8',
|
|
58
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
59
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
|
+
}).trim();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function sha256(file) {
|
|
64
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function artifactDigest(file) {
|
|
68
|
+
const stat = fs.statSync(file);
|
|
69
|
+
return { sha256: sha256(file), bytes: stat.size };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function processAlive(pid) {
|
|
73
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
74
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function waitForFile(file, timeoutMs) {
|
|
78
|
+
const deadline = Date.now() + timeoutMs;
|
|
79
|
+
while (Date.now() < deadline) {
|
|
80
|
+
if (fs.existsSync(file) && fs.statSync(file).size > 0) return;
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
82
|
+
}
|
|
83
|
+
throw new Error('ACP conformance worker did not publish a terminal result before timeout');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function waitForExit(pid, timeoutMs = 5000) {
|
|
87
|
+
const deadline = Date.now() + timeoutMs;
|
|
88
|
+
while (Date.now() < deadline) {
|
|
89
|
+
if (!processAlive(pid)) return true;
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
91
|
+
}
|
|
92
|
+
return !processAlive(pid);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function createFixture(host) {
|
|
96
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), `ubp-${host}-acp-conformance-`));
|
|
97
|
+
git(root, ['init', '-b', 'main']);
|
|
98
|
+
git(root, ['config', 'user.email', 'acp-conformance@example.invalid']);
|
|
99
|
+
git(root, ['config', 'user.name', 'Ultra ACP Conformance']);
|
|
100
|
+
fs.writeFileSync(path.join(root, 'README.md'), '# Ultra ACP conformance fixture\n');
|
|
101
|
+
fs.writeFileSync(path.join(root, '.gitignore'), '.ultra/.runtime/\n');
|
|
102
|
+
git(root, ['add', 'README.md', '.gitignore']);
|
|
103
|
+
git(root, ['commit', '-m', 'test: create ACP conformance fixture']);
|
|
104
|
+
|
|
105
|
+
const id = `${host}-${crypto.randomUUID()}`;
|
|
106
|
+
const delegation = path.join(root, '.ultra', '.runtime', 'delegations', id);
|
|
107
|
+
const worktree = path.join(root, '.ultra', '.runtime', 'worktrees', id);
|
|
108
|
+
fs.mkdirSync(path.dirname(worktree), { recursive: true });
|
|
109
|
+
git(root, ['worktree', 'add', '--detach', worktree, 'HEAD']);
|
|
110
|
+
fs.mkdirSync(delegation, { recursive: true });
|
|
111
|
+
const expectedResult = {
|
|
112
|
+
$schema: 'ultra-delegation-result-v1',
|
|
113
|
+
status: 'finished',
|
|
114
|
+
summary: 'ACP conformance completed.',
|
|
115
|
+
changed_files: [],
|
|
116
|
+
checks: [],
|
|
117
|
+
evidence: [],
|
|
118
|
+
questions: [],
|
|
119
|
+
residual_risks: [],
|
|
120
|
+
};
|
|
121
|
+
fs.writeFileSync(path.join(delegation, 'instruction.md'), [
|
|
122
|
+
'# ACP conformance',
|
|
123
|
+
'',
|
|
124
|
+
'Do not use tools or modify files.',
|
|
125
|
+
'Return exactly this one-line JSON object with no prose, fences, or field changes:',
|
|
126
|
+
JSON.stringify(expectedResult),
|
|
127
|
+
'',
|
|
128
|
+
].join('\n'));
|
|
129
|
+
fs.writeFileSync(path.join(delegation, 'permission.json'), `${JSON.stringify({
|
|
130
|
+
$schema: 'ultra-delegation-permission-v1',
|
|
131
|
+
writable_roots: [],
|
|
132
|
+
external_effects: [],
|
|
133
|
+
}, null, 2)}\n`);
|
|
134
|
+
return { root, delegation, worktree };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function writeReceipt(output, receipt) {
|
|
138
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
139
|
+
fs.writeFileSync(output, `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function runConformance(options) {
|
|
143
|
+
const profile = hostProfile(options.host);
|
|
144
|
+
if (!profile.acp || profile.acp.transport !== 'stdio'
|
|
145
|
+
|| !profile.acp.protocolVersions.includes(1)) {
|
|
146
|
+
throw new Error(`host ${options.host} has no stable ACP v1 stdio candidate`);
|
|
147
|
+
}
|
|
148
|
+
const command = options.commandOverride || profile.acp.command({
|
|
149
|
+
host: options.host,
|
|
150
|
+
cliCommand: profile.binary,
|
|
151
|
+
env: process.env,
|
|
152
|
+
});
|
|
153
|
+
const args = options.argsOverride || profile.acp.argv({
|
|
154
|
+
host: options.host,
|
|
155
|
+
cliCommand: profile.binary,
|
|
156
|
+
env: process.env,
|
|
157
|
+
});
|
|
158
|
+
const fixture = createFixture(options.host);
|
|
159
|
+
let receipt;
|
|
160
|
+
try {
|
|
161
|
+
const runArgs = [
|
|
162
|
+
'--to', options.host,
|
|
163
|
+
'--instruction', path.join(fixture.delegation, 'instruction.md'),
|
|
164
|
+
'--permission', path.join(fixture.delegation, 'permission.json'),
|
|
165
|
+
'--worktree', fixture.worktree,
|
|
166
|
+
'--timeout', String(options.timeoutSeconds),
|
|
167
|
+
];
|
|
168
|
+
if (profile.transportMaturity === 'experimental') runArgs.push('--ack-experimental');
|
|
169
|
+
const started = runDelegate(runArgs, {
|
|
170
|
+
projectRoot: fixture.root,
|
|
171
|
+
chooseExecutionDriver: () => ({
|
|
172
|
+
driver: 'acp',
|
|
173
|
+
command,
|
|
174
|
+
args,
|
|
175
|
+
protocolVersion: 1,
|
|
176
|
+
fallbackReason: null,
|
|
177
|
+
acpCapability: {
|
|
178
|
+
candidate: true,
|
|
179
|
+
surface: profile.acp.surface,
|
|
180
|
+
transport: 'stdio',
|
|
181
|
+
protocol_versions: [1],
|
|
182
|
+
conformance_status: 'probe',
|
|
183
|
+
conformance_evidence: null,
|
|
184
|
+
conformance_reason: 'Live conformance probe in progress.',
|
|
185
|
+
executable_available: true,
|
|
186
|
+
candidate_valid: true,
|
|
187
|
+
},
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
const resultFile = path.join(fixture.delegation, 'result.json');
|
|
191
|
+
await waitForFile(resultFile, (options.timeoutSeconds * 1000) + 10000);
|
|
192
|
+
const terminal = JSON.parse(fs.readFileSync(resultFile, 'utf8'));
|
|
193
|
+
const workerExitObserved = await waitForExit(started.worker_pid);
|
|
194
|
+
const changedFiles = Array.isArray(terminal.changed_files) ? terminal.changed_files : [];
|
|
195
|
+
const gitClean = git(fixture.worktree, ['status', '--porcelain=v1', '--untracked-files=all']) === '';
|
|
196
|
+
const artifactFiles = {
|
|
197
|
+
result: resultFile,
|
|
198
|
+
stdout: path.join(fixture.delegation, 'stdout.log'),
|
|
199
|
+
stderr: path.join(fixture.delegation, 'stderr.log'),
|
|
200
|
+
events: path.join(fixture.delegation, 'events.jsonl'),
|
|
201
|
+
host_output: path.join(fixture.delegation, 'host-result.json'),
|
|
202
|
+
};
|
|
203
|
+
const artifactDigests = {};
|
|
204
|
+
for (const [name, file] of Object.entries(artifactFiles)) {
|
|
205
|
+
if (fs.existsSync(file) && fs.statSync(file).isFile()) artifactDigests[name] = artifactDigest(file);
|
|
206
|
+
}
|
|
207
|
+
const events = fs.existsSync(artifactFiles.events)
|
|
208
|
+
? fs.readFileSync(artifactFiles.events, 'utf8').trim().split('\n').filter(Boolean)
|
|
209
|
+
: [];
|
|
210
|
+
const passed = terminal.status === 'finished'
|
|
211
|
+
&& terminal.$schema === 'ultra-delegation-result-v1'
|
|
212
|
+
&& terminal.execution_driver === 'acp'
|
|
213
|
+
&& terminal.acp_protocol_version === 1
|
|
214
|
+
&& terminal.acp_stop_reason === 'end_turn'
|
|
215
|
+
&& changedFiles.length === 0
|
|
216
|
+
&& gitClean
|
|
217
|
+
&& workerExitObserved;
|
|
218
|
+
receipt = {
|
|
219
|
+
$schema: RECEIPT_SCHEMA,
|
|
220
|
+
host: options.host,
|
|
221
|
+
candidate: {
|
|
222
|
+
surface: profile.acp.surface,
|
|
223
|
+
command,
|
|
224
|
+
args,
|
|
225
|
+
protocol_versions: [1],
|
|
226
|
+
override_used: options.commandOverride !== null,
|
|
227
|
+
...(options.host === 'zcode' ? {
|
|
228
|
+
bridge_source: ZCODE_ACP_SOURCE,
|
|
229
|
+
installed_runtime: profile.binary,
|
|
230
|
+
} : {}),
|
|
231
|
+
},
|
|
232
|
+
result: {
|
|
233
|
+
status: passed ? 'passed' : 'failed',
|
|
234
|
+
execution_driver: terminal.execution_driver || null,
|
|
235
|
+
protocol_version: terminal.acp_protocol_version ?? null,
|
|
236
|
+
stop_reason: terminal.acp_stop_reason ?? null,
|
|
237
|
+
adapter_exit_code: terminal.acp_adapter_exit_code ?? null,
|
|
238
|
+
adapter_exit_signal: terminal.acp_adapter_exit_signal ?? null,
|
|
239
|
+
git_clean: gitClean,
|
|
240
|
+
changed_files_count: changedFiles.length,
|
|
241
|
+
event_count: events.length,
|
|
242
|
+
worker_exit_observed: workerExitObserved,
|
|
243
|
+
failure_type: terminal.failure_type || null,
|
|
244
|
+
validation_diagnostic: terminal.failure_type === 'invalid_result'
|
|
245
|
+
? terminal.summary
|
|
246
|
+
: null,
|
|
247
|
+
transport_diagnostic: typeof terminal.failure_type === 'string'
|
|
248
|
+
&& terminal.failure_type.startsWith('acp_')
|
|
249
|
+
? terminal.summary
|
|
250
|
+
: null,
|
|
251
|
+
},
|
|
252
|
+
artifact_digests: artifactDigests,
|
|
253
|
+
redaction: {
|
|
254
|
+
provider_content_retained: false,
|
|
255
|
+
prompt_retained: false,
|
|
256
|
+
hidden_reasoning_retained: false,
|
|
257
|
+
credentials_retained: false,
|
|
258
|
+
temporary_artifacts_removed: true,
|
|
259
|
+
},
|
|
260
|
+
observed_at: new Date().toISOString(),
|
|
261
|
+
};
|
|
262
|
+
writeReceipt(options.output, receipt);
|
|
263
|
+
if (!passed) throw new Error(`ACP conformance failed for ${options.host}: ${terminal.failure_type || 'terminal_mismatch'}`);
|
|
264
|
+
return receipt;
|
|
265
|
+
} finally {
|
|
266
|
+
fs.rmSync(fixture.root, { recursive: true, force: true });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function main(argv = process.argv.slice(2)) {
|
|
271
|
+
const options = parseArgs(argv);
|
|
272
|
+
const receipt = await runConformance(options);
|
|
273
|
+
process.stdout.write(`${JSON.stringify({
|
|
274
|
+
host: receipt.host,
|
|
275
|
+
status: receipt.result.status,
|
|
276
|
+
receipt: options.output,
|
|
277
|
+
})}\n`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (require.main === module) {
|
|
281
|
+
main().catch((error) => {
|
|
282
|
+
process.stderr.write(`${error.message}\n`);
|
|
283
|
+
process.exitCode = 1;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
module.exports = { parseArgs, runConformance };
|