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
package/bin/delegate.cjs
CHANGED
|
@@ -1,532 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const { execFileSync, spawn } = require('node:child_process');
|
|
7
|
-
|
|
8
|
-
const { hostProfile, transportSurface } = require('../adapters/_shared/host-profile.cjs');
|
|
9
|
-
const { writeAtomic } = require('../adapters/_shared/file-ops.cjs');
|
|
10
|
-
|
|
11
|
-
const PERMISSION_SCHEMA = 'ultra-delegation-permission-v1';
|
|
12
|
-
const RECEIPT_SCHEMA = 'ultra-delegation-receipt-v1';
|
|
13
|
-
const RESULT_SCHEMA = 'ultra-delegation-result-v1';
|
|
14
|
-
const PERMISSION_FIELDS = new Set(['$schema', 'writable_roots', 'external_effects']);
|
|
15
|
-
const PROTECTED_ROOT = '.ultra';
|
|
16
|
-
const RESULT_JSON_SCHEMA = Object.freeze({
|
|
17
|
-
type: 'object',
|
|
18
|
-
additionalProperties: false,
|
|
19
|
-
required: [
|
|
20
|
-
'$schema', 'status', 'summary', 'changed_files', 'checks', 'evidence',
|
|
21
|
-
'questions', 'residual_risks',
|
|
22
|
-
],
|
|
23
|
-
properties: {
|
|
24
|
-
$schema: { type: 'string', const: RESULT_SCHEMA },
|
|
25
|
-
status: { type: 'string', enum: ['finished', 'blocked', 'failed'] },
|
|
26
|
-
summary: { type: 'string' },
|
|
27
|
-
changed_files: {
|
|
28
|
-
type: 'array', items: { type: 'string' },
|
|
29
|
-
},
|
|
30
|
-
checks: {
|
|
31
|
-
type: 'array',
|
|
32
|
-
items: {
|
|
33
|
-
type: 'object',
|
|
34
|
-
additionalProperties: false,
|
|
35
|
-
required: ['command', 'status', 'output_ref'],
|
|
36
|
-
properties: {
|
|
37
|
-
command: { type: 'string' },
|
|
38
|
-
status: { type: 'string', enum: ['passed', 'failed', 'not_run'] },
|
|
39
|
-
output_ref: { type: 'string' },
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
evidence: { type: 'array', items: { type: 'string' } },
|
|
44
|
-
questions: { type: 'array', items: { type: 'string' } },
|
|
45
|
-
residual_risks: { type: 'array', items: { type: 'string' } },
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
function inside(root, candidate) {
|
|
50
|
-
const relative = path.relative(root, candidate);
|
|
51
|
-
return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`)
|
|
52
|
-
&& !path.isAbsolute(relative);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function atOrInside(root, candidate) {
|
|
56
|
-
return candidate === root || inside(root, candidate);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function projectPath(projectRoot, value, label, { directory = false } = {}) {
|
|
60
|
-
const lexical = path.resolve(projectRoot, value);
|
|
61
|
-
const resolved = fs.existsSync(lexical) ? fs.realpathSync(lexical) : lexical;
|
|
62
|
-
if (!inside(projectRoot, resolved)) throw new Error(`${label} must be inside the current project`);
|
|
63
|
-
if (directory && (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory())) {
|
|
64
|
-
throw new Error(`${label} must be an existing directory`);
|
|
65
|
-
}
|
|
66
|
-
return resolved;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function sha256(file) {
|
|
70
|
-
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function git(cwd, args) {
|
|
74
|
-
return execFileSync('git', args, {
|
|
75
|
-
cwd,
|
|
76
|
-
encoding: 'utf8',
|
|
77
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
78
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
79
|
-
}).trim();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function observedChangedFiles(cwd, baseHead) {
|
|
83
|
-
const run = (args) => execFileSync('git', args, {
|
|
84
|
-
cwd,
|
|
85
|
-
encoding: 'buffer',
|
|
86
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
87
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
88
|
-
}).toString('utf8').split('\0').filter(Boolean);
|
|
89
|
-
return [...new Set([
|
|
90
|
-
...run(['diff', '--name-only', '-z', baseHead, '--']),
|
|
91
|
-
...run(['ls-files', '--others', '--exclude-standard', '-z']),
|
|
92
|
-
].map((file) => file.split(path.sep).join('/')))].sort();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function validateWorktree(worktree) {
|
|
96
|
-
let top;
|
|
97
|
-
try {
|
|
98
|
-
top = fs.realpathSync(git(worktree, ['rev-parse', '--show-toplevel']));
|
|
99
|
-
} catch {
|
|
100
|
-
throw new Error('worktree must be a real Git worktree');
|
|
101
|
-
}
|
|
102
|
-
if (top !== worktree) throw new Error('worktree must be the root of a real Git worktree');
|
|
103
|
-
const registered = git(worktree, ['worktree', 'list', '--porcelain'])
|
|
104
|
-
.split('\n')
|
|
105
|
-
.filter((line) => line.startsWith('worktree '))
|
|
106
|
-
.map((line) => line.slice('worktree '.length))
|
|
107
|
-
.some((entry) => {
|
|
108
|
-
try { return fs.realpathSync(entry) === worktree; } catch { return false; }
|
|
109
|
-
});
|
|
110
|
-
if (!registered) throw new Error('worktree is not registered as a Git worktree');
|
|
111
|
-
const dirty = git(worktree, ['status', '--porcelain=v1', '--untracked-files=all']);
|
|
112
|
-
if (dirty) throw new Error('delegated Git worktree must start clean');
|
|
113
|
-
return git(worktree, ['rev-parse', 'HEAD']);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function readPermission(file, worktree) {
|
|
117
|
-
let payload;
|
|
118
|
-
let source;
|
|
119
|
-
try {
|
|
120
|
-
source = fs.readFileSync(file, 'utf8');
|
|
121
|
-
payload = JSON.parse(source);
|
|
122
|
-
} catch {
|
|
123
|
-
throw new Error('permission must be valid JSON');
|
|
124
|
-
}
|
|
125
|
-
if (!payload || Array.isArray(payload) || typeof payload !== 'object') {
|
|
126
|
-
throw new Error('permission must be a JSON object');
|
|
127
|
-
}
|
|
128
|
-
for (const key of Object.keys(payload)) {
|
|
129
|
-
if (!PERMISSION_FIELDS.has(key)) throw new Error(`unknown permission field: ${key}`);
|
|
130
|
-
}
|
|
131
|
-
if (payload.$schema !== PERMISSION_SCHEMA) {
|
|
132
|
-
throw new Error(`permission $schema must be ${PERMISSION_SCHEMA}`);
|
|
133
|
-
}
|
|
134
|
-
if (!Array.isArray(payload.writable_roots)) {
|
|
135
|
-
throw new Error('permission writable_roots must be an array');
|
|
136
|
-
}
|
|
137
|
-
const writableRoots = [];
|
|
138
|
-
const writableRootPaths = [];
|
|
139
|
-
for (const value of payload.writable_roots) {
|
|
140
|
-
if (typeof value !== 'string' || !value.trim() || path.isAbsolute(value)) {
|
|
141
|
-
throw new Error('permission writable_roots entries must be relative non-empty paths');
|
|
142
|
-
}
|
|
143
|
-
const resolved = path.resolve(worktree, value);
|
|
144
|
-
if (!atOrInside(worktree, resolved)) {
|
|
145
|
-
throw new Error('permission writable_roots must stay inside the delegated worktree');
|
|
146
|
-
}
|
|
147
|
-
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
|
|
148
|
-
throw new Error(`permission writable_roots entry must be an existing directory: ${value}`);
|
|
149
|
-
}
|
|
150
|
-
const real = fs.realpathSync(resolved);
|
|
151
|
-
if (!atOrInside(worktree, real)) {
|
|
152
|
-
throw new Error('permission writable_roots must not escape through symlinks');
|
|
153
|
-
}
|
|
154
|
-
const normalized = path.relative(worktree, real).split(path.sep).join('/') || '.';
|
|
155
|
-
// `.ultra` is the project's memory and has exactly one writer: the primary host.
|
|
156
|
-
// Rejected here so the delegation never starts, and again against the actual diff
|
|
157
|
-
// in the worker, which also covers the `.` grant.
|
|
158
|
-
if (normalized === PROTECTED_ROOT || normalized.startsWith(`${PROTECTED_ROOT}/`)) {
|
|
159
|
-
throw new Error(`permission writable_roots must not include ${PROTECTED_ROOT}; the primary host is its only writer`);
|
|
160
|
-
}
|
|
161
|
-
if (!writableRoots.includes(normalized)) {
|
|
162
|
-
writableRoots.push(normalized);
|
|
163
|
-
writableRootPaths.push(real);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (!Array.isArray(payload.external_effects)) {
|
|
167
|
-
throw new Error('permission external_effects must be an array');
|
|
168
|
-
}
|
|
169
|
-
if (payload.external_effects.length !== 0) {
|
|
170
|
-
throw new Error('permission external_effects must be empty; the primary host performs separately authorized effects');
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
writableRoots,
|
|
174
|
-
writableRootPaths,
|
|
175
|
-
source,
|
|
176
|
-
digest: crypto.createHash('sha256').update(source).digest('hex'),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function delegatePrompt({
|
|
181
|
-
instructionSource, instructionDigest, permissionSource, permissionDigest,
|
|
182
|
-
worktree, readOnly,
|
|
183
|
-
}) {
|
|
184
|
-
return [
|
|
185
|
-
'Execute one bounded Ultra Builder Pro delegation in the isolated Git worktree.',
|
|
186
|
-
'The launcher embedded the digest-bound packet below. Do not access packet paths outside the worktree.',
|
|
187
|
-
`Instruction SHA-256: ${instructionDigest}`,
|
|
188
|
-
'--- instruction.md ---',
|
|
189
|
-
instructionSource.trimEnd(),
|
|
190
|
-
'--- end instruction.md ---',
|
|
191
|
-
`Permission SHA-256: ${permissionDigest}`,
|
|
192
|
-
'--- permission.json ---',
|
|
193
|
-
permissionSource.trimEnd(),
|
|
194
|
-
'--- end permission.json ---',
|
|
195
|
-
`Use this worktree as the only filesystem boundary: ${worktree}`,
|
|
196
|
-
readOnly
|
|
197
|
-
? 'This is read-only delegation. Do not create, modify, or delete any project file.'
|
|
198
|
-
: 'Modify only paths covered by permission.writable_roots.',
|
|
199
|
-
'Do not perform external effects, edit the primary checkout, or change Ultra task authority.',
|
|
200
|
-
'Batch independent reads in the earliest tool turn. Do not emit a preliminary result, and reserve a final turn for the one terminal JSON response.',
|
|
201
|
-
`Return exactly one JSON object as the final response using $schema ${RESULT_SCHEMA}.`,
|
|
202
|
-
'The exact fields are $schema, status, summary, changed_files, checks, evidence, questions, and residual_risks.',
|
|
203
|
-
'status is finished, blocked, or failed. blocked requires at least one question.',
|
|
204
|
-
'changed_files must exactly list every repository-relative path changed in the worktree.',
|
|
205
|
-
'checks items use command, status (passed, failed, or not_run), and output_ref. Use a short evidence reference when no file exists.',
|
|
206
|
-
'Do not write the receipt yourself. The launcher validates structured output, process exit, permissions, and the actual Git diff before publication.',
|
|
207
|
-
].join('\n');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function selectedBinary(runtime, fallback) {
|
|
211
|
-
return process.env[`UBP_DELEGATE_${runtime.toUpperCase()}_BIN`] || fallback;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function selectedModel(runtime, explicit) {
|
|
215
|
-
return explicit || process.env[`UBP_DELEGATE_${runtime.toUpperCase()}_MODEL`] || null;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function parsePairs(argv, allowed) {
|
|
219
|
-
const values = {};
|
|
220
|
-
for (let index = 0; index < argv.length; index += 2) {
|
|
221
|
-
const key = argv[index];
|
|
222
|
-
const value = argv[index + 1];
|
|
223
|
-
if (!allowed.has(key) || value === undefined || value === '') {
|
|
224
|
-
throw new Error(`invalid delegate argument: ${key || '<missing>'}`);
|
|
225
|
-
}
|
|
226
|
-
if (values[key]) throw new Error(`duplicate delegate argument: ${key}`);
|
|
227
|
-
values[key] = value;
|
|
228
|
-
}
|
|
229
|
-
return values;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function parseRun(argv) {
|
|
233
|
-
// --ack-experimental is the one valueless flag: launching an experimental
|
|
234
|
-
// transport must be a visible choice, never a silent default.
|
|
235
|
-
const ackExperimental = argv.includes('--ack-experimental');
|
|
236
|
-
const pairs = argv.filter((arg) => arg !== '--ack-experimental');
|
|
237
|
-
const values = parsePairs(pairs, new Set([
|
|
238
|
-
'--to', '--instruction', '--permission', '--worktree', '--timeout', '--model',
|
|
239
|
-
]));
|
|
240
|
-
for (const key of ['--to', '--instruction', '--permission', '--worktree']) {
|
|
241
|
-
if (!values[key]) throw new Error(`delegate run requires ${key}`);
|
|
242
|
-
}
|
|
243
|
-
const timeout = values['--timeout'] === undefined ? 1800 : Number(values['--timeout']);
|
|
244
|
-
if (!Number.isFinite(timeout) || timeout <= 0 || timeout > 86400) {
|
|
245
|
-
throw new Error('delegate --timeout must be greater than 0 and at most 86400 seconds');
|
|
246
|
-
}
|
|
247
|
-
return {
|
|
248
|
-
to: values['--to'], instruction: values['--instruction'],
|
|
249
|
-
permission: values['--permission'], worktree: values['--worktree'],
|
|
250
|
-
model: values['--model'] || null,
|
|
251
|
-
timeoutMs: Math.ceil(timeout * 1000),
|
|
252
|
-
ackExperimental,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function readJson(file) {
|
|
257
|
-
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function processAlive(pid) {
|
|
261
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
262
|
-
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function run(argv, { projectRoot = process.cwd() } = {}) {
|
|
266
|
-
const args = parseRun(argv);
|
|
267
|
-
projectRoot = fs.realpathSync(projectRoot);
|
|
268
|
-
const profile = hostProfile(args.to);
|
|
269
|
-
if (args.model && !profile.supportsModelSelection) {
|
|
270
|
-
throw new Error(`delegate --model is not supported by host ${args.to}`);
|
|
271
|
-
}
|
|
272
|
-
const model = profile.supportsModelSelection ? selectedModel(args.to, args.model) : null;
|
|
273
|
-
const binary = selectedBinary(args.to, profile.binary);
|
|
274
|
-
const transportMaturity = profile.transportMaturity;
|
|
275
|
-
const transportSurfaceNote = transportSurface(args.to, binary);
|
|
276
|
-
if (transportMaturity === 'experimental' && !args.ackExperimental) {
|
|
277
|
-
throw new Error(
|
|
278
|
-
`delegate --to ${args.to} would launch ${transportSurfaceNote}. `
|
|
279
|
-
+ 'This transport is experimental (verified-local only; the provider publishes no '
|
|
280
|
-
+ 'stability contract), and it is not the documented ZCode Desktop interactive '
|
|
281
|
-
+ 'surface. Acknowledge explicitly with --ack-experimental to proceed, or pick a '
|
|
282
|
-
+ 'documented transport.',
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
if (transportMaturity === 'experimental') {
|
|
286
|
-
process.stderr.write(
|
|
287
|
-
`ultra-delegate: warning: host '${args.to}' runs ${transportSurfaceNote}; `
|
|
288
|
-
+ `recovery: ubp delegate cancel --delegation <dir>\n`,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
const instruction = projectPath(projectRoot, args.instruction, 'instruction');
|
|
292
|
-
const permission = projectPath(projectRoot, args.permission, 'permission');
|
|
293
|
-
const worktree = projectPath(projectRoot, args.worktree, 'worktree', { directory: true });
|
|
294
|
-
if (!fs.statSync(instruction).isFile()) throw new Error('instruction must be a file');
|
|
295
|
-
if (!fs.statSync(permission).isFile()) throw new Error('permission must be a file');
|
|
296
|
-
if (path.dirname(instruction) !== path.dirname(permission)) {
|
|
297
|
-
throw new Error('instruction and permission must share one delegation directory');
|
|
298
|
-
}
|
|
299
|
-
const baseHead = validateWorktree(worktree);
|
|
300
|
-
const permissionContract = readPermission(permission, worktree);
|
|
301
|
-
const instructionSource = fs.readFileSync(instruction, 'utf8');
|
|
302
|
-
const delegationRoot = path.dirname(instruction);
|
|
303
|
-
const delegationId = path.basename(delegationRoot);
|
|
304
|
-
const result = path.join(delegationRoot, 'result.json');
|
|
305
|
-
const receiptFile = path.join(delegationRoot, 'receipt.json');
|
|
306
|
-
const stdout = path.join(delegationRoot, 'stdout.log');
|
|
307
|
-
const stderr = path.join(delegationRoot, 'stderr.log');
|
|
308
|
-
const spec = path.join(delegationRoot, 'worker-spec.json');
|
|
309
|
-
const outputSchema = path.join(delegationRoot, 'result-schema.json');
|
|
310
|
-
const hostOutput = path.join(delegationRoot, 'host-result.json');
|
|
311
|
-
const lock = path.join(delegationRoot, 'run.lock');
|
|
312
|
-
const cancel = path.join(delegationRoot, 'cancel.request');
|
|
313
|
-
if (fs.existsSync(result)) throw new Error('delegation result.json already exists; use a new delegation id');
|
|
314
|
-
if (fs.existsSync(lock)) throw new Error('delegation is already running');
|
|
315
|
-
if (fs.existsSync(receiptFile) || fs.existsSync(spec) || fs.existsSync(outputSchema) || fs.existsSync(hostOutput)) {
|
|
316
|
-
throw new Error('delegation id already has run artifacts; cancel or use a new delegation id');
|
|
317
|
-
}
|
|
318
|
-
let lockFd;
|
|
319
|
-
try {
|
|
320
|
-
lockFd = fs.openSync(lock, 'wx', 0o600);
|
|
321
|
-
} catch (error) {
|
|
322
|
-
if (error.code === 'EEXIST') throw new Error('delegation is already running');
|
|
323
|
-
throw error;
|
|
324
|
-
}
|
|
325
|
-
const startedAt = new Date().toISOString();
|
|
326
|
-
const instructionDigest = crypto.createHash('sha256').update(instructionSource).digest('hex');
|
|
327
|
-
const permissionDigest = permissionContract.digest;
|
|
328
|
-
try {
|
|
329
|
-
fs.writeFileSync(lockFd, `${JSON.stringify({ pid: process.pid, started_at: startedAt })}\n`);
|
|
330
|
-
fs.closeSync(lockFd);
|
|
331
|
-
lockFd = undefined;
|
|
332
|
-
writeAtomic(outputSchema, `${JSON.stringify(RESULT_JSON_SCHEMA, null, 2)}\n`);
|
|
333
|
-
fs.chmodSync(outputSchema, 0o600);
|
|
334
|
-
const outputSchemaDigest = sha256(outputSchema);
|
|
335
|
-
const profileOptions = {
|
|
336
|
-
readOnly: permissionContract.writableRoots.length === 0,
|
|
337
|
-
writableRoots: permissionContract.writableRoots,
|
|
338
|
-
schemaFile: outputSchema,
|
|
339
|
-
schemaJson: JSON.stringify(RESULT_JSON_SCHEMA),
|
|
340
|
-
hostOutput,
|
|
341
|
-
model,
|
|
342
|
-
};
|
|
343
|
-
const prompt = delegatePrompt({
|
|
344
|
-
instructionSource,
|
|
345
|
-
instructionDigest,
|
|
346
|
-
permissionSource: permissionContract.source,
|
|
347
|
-
permissionDigest,
|
|
348
|
-
worktree,
|
|
349
|
-
readOnly: profileOptions.readOnly,
|
|
350
|
-
});
|
|
351
|
-
const workerSpec = {
|
|
352
|
-
$schema: 'ultra-delegation-worker-spec-v1',
|
|
353
|
-
delegation_id: delegationId,
|
|
354
|
-
host: args.to,
|
|
355
|
-
model,
|
|
356
|
-
command: binary,
|
|
357
|
-
transport_maturity: transportMaturity,
|
|
358
|
-
transport_surface: transportSurfaceNote,
|
|
359
|
-
experimental_ack: transportMaturity === 'experimental' ? args.ackExperimental : undefined,
|
|
360
|
-
args: profile.delegateArgv(prompt, worktree, profileOptions),
|
|
361
|
-
cwd: worktree,
|
|
362
|
-
instruction,
|
|
363
|
-
permission,
|
|
364
|
-
instruction_digest: instructionDigest,
|
|
365
|
-
permission_digest: permissionDigest,
|
|
366
|
-
output_schema: outputSchema,
|
|
367
|
-
output_schema_digest: outputSchemaDigest,
|
|
368
|
-
host_output: hostOutput,
|
|
369
|
-
read_only: profileOptions.readOnly,
|
|
370
|
-
writable_roots: permissionContract.writableRoots,
|
|
371
|
-
writable_root_paths: permissionContract.writableRootPaths,
|
|
372
|
-
base_head: baseHead,
|
|
373
|
-
started_at: startedAt,
|
|
374
|
-
timeout_ms: args.timeoutMs,
|
|
375
|
-
result,
|
|
376
|
-
stdout,
|
|
377
|
-
stderr,
|
|
378
|
-
lock,
|
|
379
|
-
cancel,
|
|
380
|
-
env: {
|
|
381
|
-
...(profile.delegateEnv ? profile.delegateEnv(profileOptions) : {}),
|
|
382
|
-
UBP_DELEGATE_INSTRUCTION_FILE: instruction,
|
|
383
|
-
UBP_DELEGATE_PERMISSION_FILE: permission,
|
|
384
|
-
UBP_DELEGATE_WORKTREE: worktree,
|
|
385
|
-
},
|
|
386
|
-
};
|
|
387
|
-
writeAtomic(spec, `${JSON.stringify(workerSpec, null, 2)}\n`);
|
|
388
|
-
fs.chmodSync(spec, 0o600);
|
|
389
|
-
const specDigest = sha256(spec);
|
|
390
|
-
const worker = spawn(process.execPath, [
|
|
391
|
-
path.join(__dirname, 'delegate-worker.cjs'), spec, specDigest,
|
|
392
|
-
], {
|
|
393
|
-
cwd: projectRoot,
|
|
394
|
-
detached: true,
|
|
395
|
-
stdio: 'ignore',
|
|
396
|
-
env: process.env,
|
|
397
|
-
});
|
|
398
|
-
if (!worker.pid) throw new Error('failed to start delegation worker');
|
|
399
|
-
worker.unref();
|
|
400
|
-
const receipt = {
|
|
401
|
-
$schema: RECEIPT_SCHEMA,
|
|
402
|
-
status: 'started',
|
|
403
|
-
delegation_id: delegationId,
|
|
404
|
-
host: args.to,
|
|
405
|
-
model,
|
|
406
|
-
transport_maturity: transportMaturity,
|
|
407
|
-
transport_surface: transportSurfaceNote,
|
|
408
|
-
...(transportMaturity === 'experimental' ? { experimental_ack: args.ackExperimental } : {}),
|
|
409
|
-
worker_pid: worker.pid,
|
|
410
|
-
instruction_digest: instructionDigest,
|
|
411
|
-
permission_digest: permissionDigest,
|
|
412
|
-
output_schema_digest: outputSchemaDigest,
|
|
413
|
-
spec_digest: specDigest,
|
|
414
|
-
base_head: baseHead,
|
|
415
|
-
started_at: startedAt,
|
|
416
|
-
timeout_ms: args.timeoutMs,
|
|
417
|
-
result_file: result,
|
|
418
|
-
read_only: profileOptions.readOnly,
|
|
419
|
-
stdout_file: stdout,
|
|
420
|
-
stderr_file: stderr,
|
|
421
|
-
cancel_command: `ubp delegate cancel --delegation ${delegationRoot}`,
|
|
422
|
-
};
|
|
423
|
-
writeAtomic(receiptFile, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
424
|
-
writeAtomic(lock, `${JSON.stringify({ pid: worker.pid, started_at: startedAt })}\n`);
|
|
425
|
-
fs.chmodSync(lock, 0o600);
|
|
426
|
-
if (fs.existsSync(result)) {
|
|
427
|
-
try { fs.unlinkSync(lock); } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
428
|
-
}
|
|
429
|
-
return receipt;
|
|
430
|
-
} catch (error) {
|
|
431
|
-
if (lockFd !== undefined) fs.closeSync(lockFd);
|
|
432
|
-
try { fs.unlinkSync(lock); } catch (unlinkError) { if (unlinkError.code !== 'ENOENT') throw unlinkError; }
|
|
433
|
-
for (const file of [spec, outputSchema, hostOutput]) {
|
|
434
|
-
try { fs.unlinkSync(file); } catch (unlinkError) { if (unlinkError.code !== 'ENOENT') throw unlinkError; }
|
|
435
|
-
}
|
|
436
|
-
throw error;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function delegationPath(projectRoot, value) {
|
|
441
|
-
return projectPath(projectRoot, value, 'delegation', { directory: true });
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function status(argv, { projectRoot = process.cwd() } = {}) {
|
|
445
|
-
const values = parsePairs(argv, new Set(['--delegation']));
|
|
446
|
-
if (!values['--delegation']) throw new Error('delegate status requires --delegation');
|
|
447
|
-
projectRoot = fs.realpathSync(projectRoot);
|
|
448
|
-
const root = delegationPath(projectRoot, values['--delegation']);
|
|
449
|
-
const result = path.join(root, 'result.json');
|
|
450
|
-
if (fs.existsSync(result)) return readJson(result);
|
|
451
|
-
const receiptFile = path.join(root, 'receipt.json');
|
|
452
|
-
const receipt = fs.existsSync(receiptFile) ? readJson(receiptFile) : null;
|
|
453
|
-
const lockFile = path.join(root, 'run.lock');
|
|
454
|
-
const lock = fs.existsSync(lockFile) ? readJson(lockFile) : null;
|
|
455
|
-
const cancelling = fs.existsSync(path.join(root, 'cancel.request'));
|
|
456
|
-
return {
|
|
457
|
-
$schema: 'ultra-delegation-status-v1',
|
|
458
|
-
status: cancelling ? 'cancelling' : lock && processAlive(lock.pid) ? 'running' : receipt ? 'stalled' : 'not_started',
|
|
459
|
-
delegation_id: path.basename(root),
|
|
460
|
-
worker_pid: lock?.pid || receipt?.worker_pid || null,
|
|
461
|
-
result_file: result,
|
|
462
|
-
repair: receipt ? `ubp delegate cancel --delegation ${root}` : null,
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function failureFromSpec(spec, failureType, summary) {
|
|
467
|
-
let finalHead = null;
|
|
468
|
-
let changedFiles = [];
|
|
469
|
-
try { finalHead = git(spec.cwd, ['rev-parse', 'HEAD']); } catch {}
|
|
470
|
-
try { changedFiles = observedChangedFiles(spec.cwd, spec.base_head); } catch {}
|
|
471
|
-
return {
|
|
472
|
-
$schema: RESULT_SCHEMA,
|
|
473
|
-
status: 'failed',
|
|
474
|
-
summary,
|
|
475
|
-
changed_files: changedFiles,
|
|
476
|
-
checks: [],
|
|
477
|
-
evidence: [],
|
|
478
|
-
questions: [],
|
|
479
|
-
residual_risks: [],
|
|
480
|
-
failure_type: failureType,
|
|
481
|
-
delegation_id: spec.delegation_id,
|
|
482
|
-
host: spec.host,
|
|
483
|
-
transport_maturity: spec.transport_maturity ?? null,
|
|
484
|
-
transport_surface: spec.transport_surface ?? null,
|
|
485
|
-
// Terminal recovery results carry the same transport truth as live ones.
|
|
486
|
-
...(spec.experimental_ack === true ? { experimental_ack: true } : {}),
|
|
487
|
-
instruction_digest: spec.instruction_digest,
|
|
488
|
-
permission_digest: spec.permission_digest,
|
|
489
|
-
output_schema_digest: spec.output_schema_digest,
|
|
490
|
-
base_head: spec.base_head,
|
|
491
|
-
final_head: finalHead,
|
|
492
|
-
started_at: spec.started_at,
|
|
493
|
-
finished_at: new Date().toISOString(),
|
|
494
|
-
exit_code: null,
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function cancel(argv, { projectRoot = process.cwd() } = {}) {
|
|
499
|
-
const values = parsePairs(argv, new Set(['--delegation']));
|
|
500
|
-
if (!values['--delegation']) throw new Error('delegate cancel requires --delegation');
|
|
501
|
-
projectRoot = fs.realpathSync(projectRoot);
|
|
502
|
-
const root = delegationPath(projectRoot, values['--delegation']);
|
|
503
|
-
const result = path.join(root, 'result.json');
|
|
504
|
-
if (fs.existsSync(result)) return { status: 'already_terminal', result: readJson(result) };
|
|
505
|
-
const specFile = path.join(root, 'worker-spec.json');
|
|
506
|
-
if (!fs.existsSync(specFile)) throw new Error('delegation has no worker spec to cancel');
|
|
507
|
-
const spec = readJson(specFile);
|
|
508
|
-
const request = path.join(root, 'cancel.request');
|
|
509
|
-
writeAtomic(request, `${JSON.stringify({ requested_at: new Date().toISOString() })}\n`);
|
|
510
|
-
const receiptFile = path.join(root, 'receipt.json');
|
|
511
|
-
const receipt = fs.existsSync(receiptFile) ? readJson(receiptFile) : null;
|
|
512
|
-
if (!processAlive(receipt?.worker_pid)) {
|
|
513
|
-
writeAtomic(result, `${JSON.stringify(failureFromSpec(spec, 'cancelled', 'Delegation cancelled after its worker stopped.'), null, 2)}\n`);
|
|
514
|
-
try { fs.unlinkSync(path.join(root, 'run.lock')); } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
515
|
-
}
|
|
516
|
-
return {
|
|
517
|
-
$schema: 'ultra-delegation-cancel-v1',
|
|
518
|
-
status: 'cancel_requested',
|
|
519
|
-
delegation_id: path.basename(root),
|
|
520
|
-
result_file: result,
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
function handle(argv, options) {
|
|
525
|
-
if (argv[0] !== 'delegate') throw new Error('delegate command is required');
|
|
526
|
-
if (argv[1] === 'run') return run(argv.slice(2), options);
|
|
527
|
-
if (argv[1] === 'status') return status(argv.slice(2), options);
|
|
528
|
-
if (argv[1] === 'cancel') return cancel(argv.slice(2), options);
|
|
529
|
-
throw new Error('usage: ubp delegate <run|status|cancel> ...');
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
module.exports = { handle, run, status, cancel };
|
|
3
|
+
// Compatibility surface for one release cycle. The installed Skill owns the
|
|
4
|
+
// implementation used by both `ubp delegate` and direct Skill invocation.
|
|
5
|
+
module.exports = require('../skills/ultra-delegate/scripts/delegate.cjs');
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -14,14 +14,14 @@ bounded effects, and recovery.
|
|
|
14
14
|
Owner request
|
|
15
15
|
│ explicit selection or verified execution grant (session-local / durable)
|
|
16
16
|
▼
|
|
17
|
-
|
|
17
|
+
Public Skill mode ───────── stops at the next owner boundary
|
|
18
18
|
│
|
|
19
19
|
├── model-invoked disciplines (reasoning methods)
|
|
20
20
|
├── source / tests / runtime / primary documentation
|
|
21
21
|
└── canonical project files + Git
|
|
22
22
|
│
|
|
23
23
|
├── optional five hooks (derived context and effect guard)
|
|
24
|
-
└── optional
|
|
24
|
+
└── optional delegation (ACP-first driver or CLI fallback)
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Ultra has no semantic runtime supervisor. Reading the files is the recovery protocol;
|
|
@@ -37,15 +37,20 @@ Fourteen `skills/*/SKILL.md` files carry the workflow:
|
|
|
37
37
|
- five model-invoked reusable disciplines;
|
|
38
38
|
- one read-only router.
|
|
39
39
|
|
|
40
|
-
The exact allowlists
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
The exact allowlists and per-mode invocation map live in
|
|
41
|
+
`adapters/_shared/runtime-assets.cjs`. Public modes are `explicit-entry`,
|
|
42
|
+
`grant-continuable`, or read-only `router`. Under an accepted Change-scoped Execution
|
|
43
|
+
Grant, change-scoped Research, same-Change reconciliation, Plan, Dev, Test, and Deliver
|
|
44
|
+
reconciliation may be selected by the model only while `session-local` activation is
|
|
45
|
+
live or a fresh Agent has stably verified a `durable work-package` grant. Host metadata
|
|
46
|
+
does not preserve session activation: a fresh session, Host switch, or compaction stops
|
|
47
|
+
`session-local` continuation until the Owner activates it again. Host metadata
|
|
48
|
+
is a coarse discovery bit; each loaded Skill rechecks its mode, Change, effects, and
|
|
49
|
+
material checkpoints. Stored text alone grants nothing, and neither mode grants Change
|
|
50
|
+
intake/acceptance, primary transfer, Deliver finalization or archive; every external
|
|
51
|
+
effect still stops. The derived Route Card makes each boundary owner-visible without
|
|
52
|
+
becoming workflow state or authority. The router itself remains owner-invoked; public
|
|
53
|
+
Skills derive their own cards from the shared Route Contract instead of invoking it.
|
|
49
54
|
|
|
50
55
|
Research's seventeen references are workflow-owned semantic lenses. Its optional
|
|
51
56
|
`wayfinding.md` maps a multi-lens question without becoming a new Skill or authority.
|
|
@@ -108,9 +113,12 @@ hard ceilings, while the model interprets whether acceptance and stop conditions
|
|
|
108
113
|
- hook command paths and wire normalization;
|
|
109
114
|
- atomic managed installation, provenance, doctor, and uninstall behavior.
|
|
110
115
|
|
|
111
|
-
`adapters/_shared/plugin-core.cjs` owns the common copy
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
`adapters/_shared/plugin-core.cjs` owns the common copy, managed publication, and
|
|
117
|
+
delegation-runner inventory and driver-reporting checks. The canonical six-host
|
|
118
|
+
non-interactive profile lives
|
|
119
|
+
beside the installed launcher at `skills/ultra-delegate/scripts/host-profile.cjs`;
|
|
120
|
+
`adapters/_shared/host-profile.cjs` is a one-release compatibility re-export. Adapters
|
|
121
|
+
never rewrite workflow meaning.
|
|
114
122
|
|
|
115
123
|
### Why the old Agents are Skill assets now
|
|
116
124
|
|
|
@@ -139,6 +147,12 @@ successfully, then it may be garbage-collected. Premature loss requires a fresh
|
|
|
139
147
|
and Test; never reconstruct the old receipt. The dangerous-command hook is the only
|
|
140
148
|
hard effect guard; it classifies a small named destructive set and provides an
|
|
141
149
|
exact-command authorization repair.
|
|
150
|
+
|
|
151
|
+
ACP `session/update` telemetry follows the same rule. The Skill-local driver stores only
|
|
152
|
+
bounded redacted event metadata under the delegation directory, drops hidden thought
|
|
153
|
+
chunks and raw tool payloads, and exposes the latest safe observation through status.
|
|
154
|
+
The journal, session id, capability report, permission decision, and stop reason are all
|
|
155
|
+
disposable execution observations; none can select a route or establish completion.
|
|
142
156
|
Additive protected-branch publication remains advisory when portable host wiring cannot
|
|
143
157
|
project a trusted owner-approval receipt; history rewrites and branch deletion remain
|
|
144
158
|
guarded.
|
|
@@ -180,16 +194,42 @@ These are independent sensors. None rewrites a finding into product truth.
|
|
|
180
194
|
|
|
181
195
|
## Delegation
|
|
182
196
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
197
|
+
The canonical installed `ultra-delegate/scripts/delegate.cjs` validates all paths inside
|
|
198
|
+
the selected project, reads a bounded instruction and permission JSON, probes the
|
|
199
|
+
Skill-local Host profile, and starts the Skill-local background worker. One
|
|
200
|
+
`AgentExecutionDriver` boundary selects stable ACP v1 stdio only after the exact Host's
|
|
201
|
+
versioned conformance row passes with a non-empty evidence reference and the declared
|
|
202
|
+
executable and bounded candidate-identity probes succeed; otherwise the same packet
|
|
203
|
+
runs through `CliDriver` and records the fallback reason. Capability inspection uses
|
|
204
|
+
local version commands only and does not start a provider task. The
|
|
205
|
+
one-release `ubp delegate` surface imports those same functions and owns no forked
|
|
206
|
+
behavior. Declared writable roots must already exist inside the delegated
|
|
186
207
|
worktree; no readable-root or external-effect grant can widen the host's native
|
|
187
208
|
sandbox. Empty writable roots select native read-only mode. The model returns a
|
|
188
|
-
schema-constrained final response;
|
|
209
|
+
schema-constrained final response; `AcpDriver` reconstructs only agent-message text as
|
|
210
|
+
that same validator input, while ACP stop reasons remain observations. It accepts a
|
|
211
|
+
bounded set of creation-time session metadata that some agents emit before
|
|
212
|
+
`session/new` resolves, then validates every buffered session identity before exposing
|
|
213
|
+
derived events; early message/tool content and unbounded startup traffic fail closed.
|
|
214
|
+
The Node worker validates `finished`, `blocked`, or
|
|
189
215
|
`failed` and atomically publishes `result.json` beside the instruction. The model never
|
|
190
216
|
writes its own receipt; stdout, stderr, native final output, and the launch receipt
|
|
191
217
|
remain diagnostic files.
|
|
192
218
|
|
|
219
|
+
`ULTRA_DELEGATE_FORCE_CLI=1` is a per-run recovery seam that selects the same bounded
|
|
220
|
+
CLI validator path and records `acp_forced_cli`; it does not erase conformance evidence.
|
|
221
|
+
An `acp_auth_required` error from `session/new` before `session/prompt` also selects that
|
|
222
|
+
already-bound CLI path once. The terminal result retains redacted ACP fallback metadata;
|
|
223
|
+
no post-prompt or other ACP failure is automatically retried.
|
|
224
|
+
ZCode uses the independently installed, source-pinned `zcode-acp` bridge plus the
|
|
225
|
+
digest-bound 0.16.5 runtime-preferences patch to translate stable ACP v1 to the installed
|
|
226
|
+
Desktop runtime. Ultra ships only the patch, neither the bridge binary nor App bytes.
|
|
227
|
+
The profile verifies an Ultra build marker on that bridge plus the exact installed
|
|
228
|
+
runtime version before ACP selection.
|
|
229
|
+
The Host/runtime is ZCode Desktop while provider identity remains owner-controlled; the
|
|
230
|
+
verified local receipt uses domestic BigModel rather than international Z.AI OAuth. The
|
|
231
|
+
headless ZCode transport remains explicitly experimental even after conformance.
|
|
232
|
+
|
|
193
233
|
The instruction binds the active `change_id` and one of three semantic scopes: task
|
|
194
234
|
execution/continuation, scoped Research evidence, or aggregate Change
|
|
195
235
|
review/verification. Only task execution requires a task row. The read-only Research
|
|
@@ -226,6 +266,7 @@ The architecture does not include:
|
|
|
226
266
|
- an MCP server or tool protocol;
|
|
227
267
|
- a workflow state machine or semantic validator;
|
|
228
268
|
- a background orchestration daemon;
|
|
269
|
+
- a resident ACP service, ACP session store, or semantic route ledger;
|
|
229
270
|
- command or custom-agent prompt projections;
|
|
230
271
|
- general memory, code graph, browser, deployment, or framework packages.
|
|
231
272
|
|