delegation-layer 0.1.5 → 0.1.7
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/README.md +21 -4
- package/package.json +2 -2
- package/scripts/install-agent-integration.mjs +107 -21
- package/skills/agent-integration/SKILL.md +162 -143
- package/skills/agent-integration/evals/evals.json +31 -0
- package/skills/agent-integration/examples/json-results.md +14 -0
- package/skills/agent-integration/examples/read-only-task.md +12 -0
- package/skills/agent-integration/examples/workspace-write-task.md +12 -0
- package/skills/agent-integration/references/command-reference.md +38 -0
- package/skills/agent-integration/references/continuation.md +42 -0
- package/skills/agent-integration/references/result-handling.md +25 -0
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ already use instead of replacing them.
|
|
|
20
20
|
and time budget.
|
|
21
21
|
- **Recoverable results** — a later `collect` call can read a finished task
|
|
22
22
|
without launching the provider again.
|
|
23
|
+
- **Resumable turns** — a durable timeout can continue the exact provider
|
|
24
|
+
session through a linked successor task.
|
|
23
25
|
- **Runtime compatibility** — the selected provider's executable and advertised
|
|
24
26
|
capabilities are checked when the task is prepared.
|
|
25
27
|
- **Native authentication** — provider login stays with the provider CLI; task
|
|
@@ -113,7 +115,9 @@ stopped. An explicit `--pueue-config` or
|
|
|
113
115
|
`DELEGATE_PUEUE_CONFIG` can still select an existing compatible supervisor for
|
|
114
116
|
advanced integrations. Node.js 18 or newer is needed only for the npm-based
|
|
115
117
|
installers; it is not a runtime requirement for the released Go CLI. Provider
|
|
116
|
-
authentication remains native to each provider.
|
|
118
|
+
authentication remains native to each provider. Your provider settings, MCP
|
|
119
|
+
servers, plugins, and skills stay with that provider. Permission modes select
|
|
120
|
+
native behavior; Delegation Layer is not an independent sandbox.
|
|
117
121
|
|
|
118
122
|
## Try one bounded task
|
|
119
123
|
|
|
@@ -124,7 +128,7 @@ mkdir -p "$HOME/delegation-workspace"
|
|
|
124
128
|
printf '%s\n' 'Inspect the repository and summarize the current build status.' > brief.txt
|
|
125
129
|
|
|
126
130
|
delegate --root "$HOME/delegation-state" dispatch \
|
|
127
|
-
--
|
|
131
|
+
--auto \
|
|
128
132
|
--brief "$PWD/brief.txt" \
|
|
129
133
|
--cwd "$HOME/delegation-workspace" \
|
|
130
134
|
--permission read-only \
|
|
@@ -142,8 +146,21 @@ delegate collect TASK_ID --watch 5s --json
|
|
|
142
146
|
|
|
143
147
|
Use `delegate providers --json` for the compiled provider catalog and
|
|
144
148
|
`delegate capabilities --provider PROFILE --json` for a provider-neutral
|
|
145
|
-
capability response.
|
|
146
|
-
|
|
149
|
+
capability response. `delegate preflight --provider PROFILE --cwd ABS --json`
|
|
150
|
+
checks static admission without creating a task. Dispatch still performs the
|
|
151
|
+
host-specific runtime probe; catalog discovery alone does not prove
|
|
152
|
+
authentication or supervisor readiness.
|
|
153
|
+
|
|
154
|
+
If a task reports `status: "timed_out"` and
|
|
155
|
+
`continuation.resumable: true`, continue the exact session:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
delegate --root "$HOME/delegation-state" continue \
|
|
159
|
+
--task TASK_ID --brief "$HOME/follow-up.txt" --budget 30m --json
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The successor receives a new task ID and is observed with the same `status` and
|
|
163
|
+
`collect` commands.
|
|
147
164
|
|
|
148
165
|
## Documentation
|
|
149
166
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delegation-layer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Multi-provider AI agent delegation CLI and Agent Skill for running bounded coding tasks across provider CLIs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"LICENSE",
|
|
25
25
|
"docs/assets/delegation-layer-hero.jpg",
|
|
26
26
|
"install.sh",
|
|
27
|
-
"skills/agent-integration/
|
|
27
|
+
"skills/agent-integration/",
|
|
28
28
|
"scripts/install-agent-integration.mjs",
|
|
29
29
|
"scripts/install-cli.mjs"
|
|
30
30
|
],
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { accessSync, constants } from "node:fs";
|
|
4
|
-
import { copyFile, lstat, mkdir, readFile } from "node:fs/promises";
|
|
4
|
+
import { copyFile, lstat, mkdir, readdir, readFile } from "node:fs/promises";
|
|
5
5
|
import { emitKeypressEvents } from "node:readline";
|
|
6
6
|
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
|
|
10
10
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
-
const
|
|
11
|
+
const sourceDirectory = join(packageRoot, "skills", "agent-integration");
|
|
12
12
|
const harnesses = [
|
|
13
13
|
{
|
|
14
14
|
name: "universal",
|
|
@@ -215,6 +215,93 @@ async function existingPath(path) {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
async function assertNoSymlinkComponents(path) {
|
|
219
|
+
let current = resolve(path);
|
|
220
|
+
while (true) {
|
|
221
|
+
const stat = await existingPath(current);
|
|
222
|
+
if (stat?.isSymbolicLink()) {
|
|
223
|
+
throw new Error(`skill destination must not contain symlink components: ${current}`);
|
|
224
|
+
}
|
|
225
|
+
const parent = dirname(current);
|
|
226
|
+
if (parent === current) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
current = parent;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function skillFiles(directory, relative = "") {
|
|
234
|
+
const entries = await readdir(join(directory, relative), { withFileTypes: true });
|
|
235
|
+
const files = [];
|
|
236
|
+
for (const entry of entries) {
|
|
237
|
+
const child = join(relative, entry.name);
|
|
238
|
+
const path = join(directory, child);
|
|
239
|
+
const stat = await lstat(path);
|
|
240
|
+
if (stat.isSymbolicLink()) {
|
|
241
|
+
throw new Error(`skill source or destination must not contain symlinks: ${path}`);
|
|
242
|
+
}
|
|
243
|
+
if (stat.isDirectory()) {
|
|
244
|
+
files.push(...(await skillFiles(directory, child)));
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (!stat.isFile()) {
|
|
248
|
+
throw new Error(`skill entry must be a regular file: ${path}`);
|
|
249
|
+
}
|
|
250
|
+
files.push(child);
|
|
251
|
+
}
|
|
252
|
+
return files.sort();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function skillTreeMatches(target, files) {
|
|
256
|
+
for (const relative of files) {
|
|
257
|
+
const destination = join(target, relative);
|
|
258
|
+
await assertNoSymlinkComponents(destination);
|
|
259
|
+
const stat = await existingPath(destination);
|
|
260
|
+
if (!stat || !stat.isFile() || stat.isSymbolicLink()) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
const [source, existing] = await Promise.all([
|
|
264
|
+
readFile(join(sourceDirectory, relative)),
|
|
265
|
+
readFile(destination),
|
|
266
|
+
]);
|
|
267
|
+
if (!source.equals(existing)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function skillTreeHasExistingFiles(target, files) {
|
|
275
|
+
for (const relative of files) {
|
|
276
|
+
const destination = join(target, relative);
|
|
277
|
+
await assertNoSymlinkComponents(destination);
|
|
278
|
+
if (await existingPath(destination)) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function copySkillTree(target, files, force) {
|
|
286
|
+
for (const relative of files) {
|
|
287
|
+
const destination = join(target, relative);
|
|
288
|
+
const parent = dirname(destination);
|
|
289
|
+
await assertNoSymlinkComponents(destination);
|
|
290
|
+
await mkdir(parent, { recursive: true });
|
|
291
|
+
const stat = await existingPath(destination);
|
|
292
|
+
if (stat?.isSymbolicLink()) {
|
|
293
|
+
throw new Error(`existing skill file must not be a symlink: ${destination}`);
|
|
294
|
+
}
|
|
295
|
+
if (stat && !stat.isFile()) {
|
|
296
|
+
throw new Error(`existing skill entry is not a regular file: ${destination}`);
|
|
297
|
+
}
|
|
298
|
+
if (stat && !force) {
|
|
299
|
+
throw new Error(`skill files already exist; use --force to replace them: ${target}`);
|
|
300
|
+
}
|
|
301
|
+
await copyFile(join(sourceDirectory, relative), destination);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
218
305
|
function executableOnPath(command) {
|
|
219
306
|
const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
220
307
|
const candidates = process.platform === "win32" ? [command, `${command}.exe`, `${command}.cmd`] : [command];
|
|
@@ -465,6 +552,7 @@ function targetFor(harness, scope, cwd, home) {
|
|
|
465
552
|
}
|
|
466
553
|
|
|
467
554
|
async function install({ force, target }) {
|
|
555
|
+
await assertNoSymlinkComponents(target);
|
|
468
556
|
const targetStat = await existingPath(target);
|
|
469
557
|
if (targetStat?.isSymbolicLink()) {
|
|
470
558
|
throw new Error(`target directory must not be a symlink: ${target}`);
|
|
@@ -473,33 +561,31 @@ async function install({ force, target }) {
|
|
|
473
561
|
throw new Error(`target must be a directory: ${target}`);
|
|
474
562
|
}
|
|
475
563
|
|
|
564
|
+
const files = await skillFiles(sourceDirectory);
|
|
476
565
|
await mkdir(target, { recursive: true });
|
|
477
566
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
throw new Error(`existing SKILL.md must not be a symlink: ${destination}`);
|
|
567
|
+
if (!force && (await skillTreeMatches(target, files))) {
|
|
568
|
+
console.log(`Skill already installed: ${join(target, "SKILL.md")}`);
|
|
569
|
+
return;
|
|
482
570
|
}
|
|
483
|
-
if (
|
|
484
|
-
throw new Error(`
|
|
571
|
+
if (!force && (await skillTreeHasExistingFiles(target, files))) {
|
|
572
|
+
throw new Error(`skill files already exist; use --force to replace them: ${target}`);
|
|
485
573
|
}
|
|
486
574
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
readFile(destination),
|
|
491
|
-
]);
|
|
492
|
-
if (source.equals(existing)) {
|
|
493
|
-
console.log(`Skill already installed: ${destination}`);
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
throw new Error(`SKILL.md already exists; use --force to replace it: ${destination}`);
|
|
497
|
-
}
|
|
575
|
+
await copySkillTree(target, files, force);
|
|
576
|
+
console.log(`Installed agent integration skill: ${join(target, "SKILL.md")}`);
|
|
577
|
+
}
|
|
498
578
|
|
|
499
|
-
|
|
500
|
-
|
|
579
|
+
async function validateSourceTree() {
|
|
580
|
+
const files = await skillFiles(sourceDirectory);
|
|
581
|
+
if (!files.includes("SKILL.md")) {
|
|
582
|
+
throw new Error(`skill package is missing SKILL.md: ${sourceDirectory}`);
|
|
583
|
+
}
|
|
584
|
+
return files;
|
|
501
585
|
}
|
|
502
586
|
|
|
587
|
+
await validateSourceTree();
|
|
588
|
+
|
|
503
589
|
const argumentsList = process.argv.slice(2);
|
|
504
590
|
if (argumentsList[0] === "install-cli") {
|
|
505
591
|
try {
|
|
@@ -1,185 +1,204 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-integration
|
|
3
|
-
description: Use Delegation Layer
|
|
3
|
+
description: Use the installed Delegation Layer CLI to select a ready provider, run one bounded agent turn, collect durable JSON evidence, and continue a timed out session safely.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Delegation Layer
|
|
7
|
+
|
|
8
|
+
Use this skill when a task should be performed by another coding agent through
|
|
9
|
+
the local `delegate` CLI. The CLI is the only integration surface. Do not call,
|
|
10
|
+
inspect, authenticate, or install a provider CLI directly.
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
1. Check whether `delegate` is already installed. If it is missing, report the
|
|
15
|
+
missing prerequisite and stop. This skill never installs or repairs the CLI.
|
|
16
|
+
2. Use `dispatch --auto` unless the user explicitly selected a provider ID.
|
|
17
|
+
Provider selection, executable checks, compatibility checks, authentication
|
|
18
|
+
gates, and supervisor admission belong inside Delegation Layer.
|
|
19
|
+
3. Give every turn an absolute workspace, a separate state root, a finite
|
|
20
|
+
permission mode, and a finite budget.
|
|
21
|
+
4. Use `--json` and save the returned `task_id`. Read `admission`, `liveness`,
|
|
22
|
+
`publication`, `status`, `outcome`, `continuation`, and `error` as separate
|
|
23
|
+
fields.
|
|
24
|
+
5. Observe with `status` and collect with `collect`. Collection may recover the
|
|
25
|
+
saved supervisor only to observe an already-requested budget stop; it never
|
|
26
|
+
launches a provider or retries a task.
|
|
27
|
+
6. If a task is timed out and its JSON says `continuation.resumable: true`, use
|
|
28
|
+
`continue --task TASK_ID`, optionally with a new `--brief`. Do not create a
|
|
29
|
+
fresh unrelated dispatch to replace a resumable task.
|
|
30
|
+
7. Treat `blocked` authentication as unresolved. Do not call it compatible,
|
|
31
|
+
successful, or failed provider behavior, and do not invent login commands.
|
|
32
|
+
8. Never put credentials, tokens, or unrestricted provider output in a brief,
|
|
33
|
+
option, receipt, or agent message.
|
|
34
|
+
9. Permission modes select provider-native behavior, not an independent sandbox.
|
|
35
|
+
Providers load their own configuration and extensions. If a task is rejected,
|
|
36
|
+
report delegate's evidence; do not assume the user's configuration is faulty
|
|
37
|
+
or remove MCP servers, plugins, settings, or credentials to make it pass.
|
|
38
|
+
|
|
39
|
+
## Operating procedure
|
|
40
|
+
|
|
41
|
+
### 1. Verify the control CLI
|
|
42
|
+
|
|
43
|
+
Run a quick local check:
|
|
7
44
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Delegation Layer CLI. It applies to every provider profile and does not encode
|
|
12
|
-
provider names, release versions, or native CLI syntax.
|
|
45
|
+
```sh
|
|
46
|
+
command -v delegate && delegate --version
|
|
47
|
+
```
|
|
13
48
|
|
|
14
|
-
|
|
49
|
+
If this fails, tell the caller that Delegation Layer is not installed and
|
|
50
|
+
stop. Do not substitute a provider command.
|
|
15
51
|
|
|
16
|
-
|
|
17
|
-
build, upgrade, or repair it. It may check whether `delegate` is available and
|
|
18
|
-
create separate task and state directories that it is authorized to use. A
|
|
19
|
-
released CLI carries the compatible `pueue` and `pueued` supervisor binaries
|
|
20
|
-
and starts or recovers a private instance automatically; an explicit supervisor
|
|
21
|
-
config is only needed for an advanced integration.
|
|
22
|
-
Authentication is a separate live gate; do not invent a provider-specific
|
|
23
|
-
login check or classify a missing login as static incompatibility.
|
|
52
|
+
### 2. Discover only when useful
|
|
24
53
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
option, environment value recorded by the task, or agent-visible output.
|
|
54
|
+
For the normal path, use automatic selection directly. If you need to explain
|
|
55
|
+
what the installed build knows, run:
|
|
28
56
|
|
|
29
|
-
|
|
57
|
+
```sh
|
|
58
|
+
delegate providers --json
|
|
59
|
+
```
|
|
30
60
|
|
|
31
|
-
|
|
32
|
-
|
|
61
|
+
This is a catalog, not proof that a provider is installed, authenticated, or
|
|
62
|
+
ready. If the caller named one provider ID and wants a side-effect-free check,
|
|
63
|
+
run:
|
|
33
64
|
|
|
34
65
|
```sh
|
|
35
|
-
|
|
36
|
-
delegate help
|
|
37
|
-
delegate providers --json
|
|
66
|
+
delegate preflight --provider PROVIDER_ID --cwd ABSOLUTE_WORKSPACE --json
|
|
38
67
|
```
|
|
39
68
|
|
|
40
|
-
|
|
41
|
-
|
|
69
|
+
Preflight reports static admission only. It does not create a task, start the
|
|
70
|
+
supervisor, or launch a provider. Do not keep retrying preflight when it says
|
|
71
|
+
`blocked` or `unsupported`; report the bounded reason.
|
|
42
72
|
|
|
43
|
-
|
|
44
|
-
permission modes and options, and the help arguments and flags required by the
|
|
45
|
-
compiled adapters. It does not inspect the host, authenticate a provider, or
|
|
46
|
-
prove that a provider is launchable.
|
|
73
|
+
### 3. Prepare separate paths and a finite brief
|
|
47
74
|
|
|
48
|
-
|
|
75
|
+
Use an existing task brief when one is supplied. Otherwise create a bounded
|
|
76
|
+
brief file that contains the requested work and its acceptance criteria. Keep
|
|
77
|
+
the state root outside the workspace:
|
|
49
78
|
|
|
50
79
|
```sh
|
|
51
|
-
|
|
80
|
+
STATE_ROOT="${DELEGATE_ROOT:-$HOME/.local/state/delegation-layer}"
|
|
81
|
+
WORKSPACE="$PWD"
|
|
82
|
+
BRIEF="$(mktemp)"
|
|
83
|
+
cat >"$BRIEF" <<'EOF'
|
|
84
|
+
Describe the delegated task, the files or behavior to change, and the checks
|
|
85
|
+
that must pass. Keep the response concise and report validation evidence.
|
|
86
|
+
EOF
|
|
52
87
|
```
|
|
53
88
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
run the host probe yet; it is neither a compatibility failure nor live proof.
|
|
89
|
+
Replace the example brief with the actual user request. Do not place the state
|
|
90
|
+
root inside the workspace or place secrets in either path’s task data.
|
|
91
|
+
|
|
92
|
+
### 4. Dispatch one bounded turn
|
|
59
93
|
|
|
60
|
-
|
|
61
|
-
dispatch environment, and prepare a finite brief and two separate absolute
|
|
62
|
-
directories. Then dispatch one bounded task:
|
|
94
|
+
Use automatic selection unless the user explicitly supplied `PROVIDER_ID`:
|
|
63
95
|
|
|
64
96
|
```sh
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
delegate --root "${HOME}/delegation-state" \
|
|
69
|
-
dispatch \
|
|
70
|
-
--provider PROFILE \
|
|
71
|
-
--brief "${HOME}/delegation-brief.txt" \
|
|
72
|
-
--cwd "${HOME}/delegation-workspace" \
|
|
97
|
+
delegate --root "$STATE_ROOT" dispatch --auto \
|
|
98
|
+
--brief "$BRIEF" \
|
|
99
|
+
--cwd "$WORKSPACE" \
|
|
73
100
|
--permission read-only \
|
|
74
101
|
--budget 30m \
|
|
75
102
|
--json
|
|
76
103
|
```
|
|
77
104
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
supervisor must be used, pass its absolute
|
|
82
|
-
`--pueue-config` path or set `DELEGATE_PUEUE_CONFIG`. If the CLI or provider
|
|
83
|
-
executable is unavailable, stop on the CLI's structured error. For live
|
|
84
|
-
acceptance, an unavailable authentication prerequisite is `blocked`; preserve
|
|
85
|
-
that evidence and never relabel it as
|
|
86
|
-
incompatible or passed.
|
|
105
|
+
For an authorized code change, use `--permission workspace-write`. Keep the
|
|
106
|
+
budget finite. A successful dispatch means the request was durably admitted;
|
|
107
|
+
it does not mean the delegated work finished.
|
|
87
108
|
|
|
88
|
-
Save the
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
Save the exact `task_id` from the JSON response. If dispatch returns an error,
|
|
110
|
+
use its `status`, `capability`, and `error` fields to report whether the request
|
|
111
|
+
was invalid, unsupported, blocked by authentication, or operationally
|
|
112
|
+
uncertain. Do not silently try another provider after a task has been
|
|
113
|
+
admitted.
|
|
114
|
+
|
|
115
|
+
### 5. Observe and collect
|
|
116
|
+
|
|
117
|
+
Use the same state root and task ID:
|
|
91
118
|
|
|
92
119
|
```sh
|
|
93
|
-
delegate --root "$
|
|
94
|
-
delegate --root "$
|
|
120
|
+
delegate --root "$STATE_ROOT" status TASK_ID --json
|
|
121
|
+
delegate --root "$STATE_ROOT" collect TASK_ID --watch 5s --json
|
|
95
122
|
```
|
|
96
123
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
If `collect` exits with code `3` because publication is still pending, wait a
|
|
125
|
+
short interval and run `collect` again. Do not dispatch again. Continue until
|
|
126
|
+
the response has a terminal `outcome` or a bounded operational error that
|
|
127
|
+
requires the caller’s attention.
|
|
128
|
+
|
|
129
|
+
Accept a result only when the terminal outcome is present and its publication
|
|
130
|
+
is committed. A rejected outcome is terminal evidence and must be reported as
|
|
131
|
+
rejected. Payload and raw output are returned as validated descriptors; do not
|
|
132
|
+
copy unrestricted provider output into the control response.
|
|
102
133
|
|
|
103
|
-
|
|
134
|
+
### 6. Continue a timed out turn
|
|
104
135
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
provider-agnostic:
|
|
136
|
+
When `status` or `collect` returns `status: "timed_out"`, inspect the nested
|
|
137
|
+
continuation object:
|
|
108
138
|
|
|
109
139
|
```json
|
|
110
140
|
{
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"verification": "catalog",
|
|
118
|
-
"help_args": [],
|
|
119
|
-
"required_flags": [],
|
|
120
|
-
"reason_code": "runtime_probe_not_run",
|
|
121
|
-
"live_acceptance": {
|
|
122
|
-
"status": "not_run",
|
|
123
|
-
"authentication": "unknown",
|
|
124
|
-
"reason_code": "runtime_probe_not_run"
|
|
125
|
-
}
|
|
141
|
+
"status": "timed_out",
|
|
142
|
+
"continuation": {
|
|
143
|
+
"resumable": true,
|
|
144
|
+
"mode": "native",
|
|
145
|
+
"predecessor_task_id": "TASK_ID",
|
|
146
|
+
"continue_command": "delegate continue --task TASK_ID --json"
|
|
126
147
|
}
|
|
127
148
|
}
|
|
128
149
|
```
|
|
129
150
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
the
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
- admission
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
descriptors; and
|
|
185
|
-
- a sanitized live-acceptance receipt marked `passed`, `failed`, or `blocked`.
|
|
151
|
+
If `resumable` is true, create the linked successor through the public
|
|
152
|
+
continuation command. Supply a new follow-up brief when the next turn needs a
|
|
153
|
+
new instruction:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
delegate --root "$STATE_ROOT" continue \
|
|
157
|
+
--task TASK_ID \
|
|
158
|
+
--brief "$FOLLOW_UP_BRIEF" \
|
|
159
|
+
--budget 30m \
|
|
160
|
+
--json
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The command preserves the exact provider session and creates a new task ID.
|
|
164
|
+
Observe and collect the successor by repeating step 5. If `resumable` is
|
|
165
|
+
false, report the reason and stop; a fresh dispatch would lose the original
|
|
166
|
+
conversation and is not an equivalent recovery.
|
|
167
|
+
|
|
168
|
+
## JSON decisions
|
|
169
|
+
|
|
170
|
+
The capability projection uses the stable `runtime-capability-v1` contract.
|
|
171
|
+
Catalog responses have `verification: "catalog"` and `status: "unknown"`.
|
|
172
|
+
Preflight responses have `verification: "preflight"` and describe static
|
|
173
|
+
readiness. A dispatch response can report `verification: "runtime"` only after
|
|
174
|
+
the supervised runtime probe has observed the required behavior and executable
|
|
175
|
+
identity.
|
|
176
|
+
|
|
177
|
+
Versions and help text are observations. Accept any valid nonempty version
|
|
178
|
+
reported by the CLI; never apply a semantic-version allowlist or web lookup.
|
|
179
|
+
The adapter’s required flags and runtime behavior decide compatibility.
|
|
180
|
+
|
|
181
|
+
Authentication is a separate live gate. `live_acceptance.status` values are
|
|
182
|
+
`not_run`, `passed`, or `blocked`; a blocked authentication prerequisite is
|
|
183
|
+
neutral evidence and must not be relabeled.
|
|
184
|
+
|
|
185
|
+
The task response separates:
|
|
186
|
+
|
|
187
|
+
- `admission`: whether the request was accepted by the durable boundary;
|
|
188
|
+
- `liveness`: the observed supervisor state;
|
|
189
|
+
- `publication`: whether validated evidence was committed or rejected;
|
|
190
|
+
- `outcome`: the terminal authority, when available;
|
|
191
|
+
- `continuation`: whether the exact session can make a linked successor; and
|
|
192
|
+
- `error`: bounded operational diagnostics.
|
|
193
|
+
|
|
194
|
+
See [references/command-reference.md](references/command-reference.md) for
|
|
195
|
+
the command matrix, [references/result-handling.md](references/result-handling.md)
|
|
196
|
+
for decision rules, and [references/continuation.md](references/continuation.md)
|
|
197
|
+
for timeout recovery. The examples and machine-readable checks in `examples/`
|
|
198
|
+
and `evals/` are part of this skill package.
|
|
199
|
+
|
|
200
|
+
## Completion report
|
|
201
|
+
|
|
202
|
+
Report the original task ID, selected provider ID when the CLI returns one,
|
|
203
|
+
the final admission/liveness/publication fields, the terminal outcome and
|
|
204
|
+
evidence descriptors, and any continuation or blocked-authentication state.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "agent-integration",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"cases": [
|
|
5
|
+
{
|
|
6
|
+
"name": "missing-cli",
|
|
7
|
+
"prompt": "Delegate this read-only task.",
|
|
8
|
+
"expected": ["check delegate", "report missing prerequisite", "do not install a provider"]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "normal-dispatch",
|
|
12
|
+
"prompt": "Run one bounded task through the available agent provider.",
|
|
13
|
+
"expected": ["dispatch --auto", "--json", "save task_id", "status", "collect"]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "timeout",
|
|
17
|
+
"prompt": "The result says status timed_out and continuation.resumable is true.",
|
|
18
|
+
"expected": ["delegate continue --task", "new task id", "collect successor", "no fresh unrelated dispatch"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "blocked-auth",
|
|
22
|
+
"prompt": "Capability output says live_acceptance.status is blocked.",
|
|
23
|
+
"expected": ["preserve blocked", "do not call a provider login command", "do not claim success"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "provider-neutrality",
|
|
27
|
+
"prompt": "Use the skill without a provider name.",
|
|
28
|
+
"expected": ["delegate only", "dispatch --auto", "no direct provider CLI"]
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# JSON decision sketch
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
delegate --root "$STATE_ROOT" status "$TASK_ID" --json
|
|
5
|
+
delegate --root "$STATE_ROOT" collect "$TASK_ID" --watch 5s --json
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
Decision order:
|
|
9
|
+
|
|
10
|
+
1. Save `task_id`.
|
|
11
|
+
2. Check `admission` and `error`.
|
|
12
|
+
3. Wait while publication is pending.
|
|
13
|
+
4. Accept only a committed terminal `outcome`.
|
|
14
|
+
5. If `status` is `timed_out`, follow `continuation.resumable`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Read-only task
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
STATE_ROOT="${DELEGATE_ROOT:-$HOME/.local/state/delegation-layer}"
|
|
5
|
+
BRIEF="$(mktemp)"
|
|
6
|
+
printf '%s\n' 'Inspect the workspace and summarize the current test failures. Do not edit files.' >"$BRIEF"
|
|
7
|
+
delegate --root "$STATE_ROOT" dispatch --auto \
|
|
8
|
+
--brief "$BRIEF" --cwd "$PWD" --permission read-only --budget 15m --json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Save the returned `task_id`, then use `status` and `collect` with the same
|
|
12
|
+
state root.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Authorized workspace write task
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
STATE_ROOT="${DELEGATE_ROOT:-$HOME/.local/state/delegation-layer}"
|
|
5
|
+
delegate --root "$STATE_ROOT" dispatch --auto \
|
|
6
|
+
--brief "$PWD/delegated-task.md" --cwd "$PWD" \
|
|
7
|
+
--permission workspace-write --budget 30m --json
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Use `workspace-write` only when the caller authorized edits in the selected
|
|
11
|
+
workspace. The CLI still owns provider selection, policy, admission, and
|
|
12
|
+
publication.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Command reference
|
|
2
|
+
|
|
3
|
+
All paths passed to `--root`, `--cwd`, and `--brief` must be absolute. Add
|
|
4
|
+
`--json` to commands that return an agent-consumable response.
|
|
5
|
+
|
|
6
|
+
| Command | Purpose | Launches provider work? |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| `delegate --version` | Confirm the installed control CLI | No |
|
|
9
|
+
| `delegate providers --json` | List compiled provider metadata | No |
|
|
10
|
+
| `delegate capabilities --provider ID --json` | Read the catalog contract | No |
|
|
11
|
+
| `delegate preflight --provider ID --cwd ABS --json` | Check static provider admission | No |
|
|
12
|
+
| `delegate dispatch --auto ... --json` | Select and admit one bounded turn | Yes, at most once |
|
|
13
|
+
| `delegate status TASK --json` | Reconcile and observe task state | No provider launch |
|
|
14
|
+
| `delegate collect TASK --watch 5s --json` | Read or wait for publication | No provider launch |
|
|
15
|
+
| `delegate continue --task TASK ... --json` | Create an exact-session successor | Yes, at most once for the successor |
|
|
16
|
+
| `delegate logs TASK --json` | Return validated output descriptors | No |
|
|
17
|
+
| `delegate cancel TASK --json` | Record one explicit stop request | Stops, does not relaunch |
|
|
18
|
+
|
|
19
|
+
The bundled supervisor is managed by the released CLI under the state root.
|
|
20
|
+
Collection remains observational. Provider authentication remains the
|
|
21
|
+
provider’s own responsibility and is never performed by this skill.
|
|
22
|
+
|
|
23
|
+
Dispatch flags:
|
|
24
|
+
|
|
25
|
+
- `--brief FILE`: bounded task instructions;
|
|
26
|
+
- `--cwd ABS`: workspace for the delegated turn;
|
|
27
|
+
- `--permission read-only|workspace-write`: requested native permission mode (not independent containment);
|
|
28
|
+
- `--budget DURATION`: finite turn budget;
|
|
29
|
+
- `--auto`: let the CLI select a ready provider;
|
|
30
|
+
- `--provider ID`: use only when the caller explicitly selected an ID;
|
|
31
|
+
- `--model MODEL` and `--effort VALUE`: optional provider-advertised choices;
|
|
32
|
+
- `--native-timeout DURATION`: optional provider-native timeout where advertised.
|
|
33
|
+
|
|
34
|
+
Continuation flags:
|
|
35
|
+
|
|
36
|
+
- `--task TASK`: predecessor task ID;
|
|
37
|
+
- `--brief FILE`: optional new follow-up instruction;
|
|
38
|
+
- `--budget DURATION`, `--model MODEL`, and `--effort VALUE`: optional overrides.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Continuation and timeout recovery
|
|
2
|
+
|
|
3
|
+
Each task is one provider turn. A time limit ends that turn; it does not grant
|
|
4
|
+
permission to launch the same task again. The durable task record keeps the
|
|
5
|
+
exact provider session identity and the stop evidence.
|
|
6
|
+
|
|
7
|
+
When `status` or `collect` reports a timed-out turn with:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
"continuation": {
|
|
11
|
+
"resumable": true,
|
|
12
|
+
"mode": "native",
|
|
13
|
+
"predecessor_task_id": "..."
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
run:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
delegate --root "$STATE_ROOT" continue \
|
|
21
|
+
--task PREDECESSOR_TASK_ID \
|
|
22
|
+
--brief FOLLOW_UP_BRIEF \
|
|
23
|
+
--budget 30m \
|
|
24
|
+
--json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The follow-up brief is optional. If it is omitted, Delegation Layer reuses the
|
|
28
|
+
validated predecessor brief. A successful continuation returns a new task ID
|
|
29
|
+
and links it to the predecessor. Observe and collect the new ID separately.
|
|
30
|
+
|
|
31
|
+
`mode` describes the provider contract:
|
|
32
|
+
|
|
33
|
+
- `native` uses the provider’s exact persisted conversation/session ID;
|
|
34
|
+
- `checkpoint` requires a provider checkpoint and is resumable only when the
|
|
35
|
+
response explicitly says so;
|
|
36
|
+
- `unsupported` cannot be continued safely.
|
|
37
|
+
|
|
38
|
+
Do not use a guessed session ID, a provider-specific resume command, or a new
|
|
39
|
+
independent dispatch to recover a timeout. The CLI accepts either a validated
|
|
40
|
+
terminal outcome or durable supervisor-ended budget-stop evidence for the
|
|
41
|
+
session handoff; without one of those proofs it refuses continuation. Inspect
|
|
42
|
+
the predecessor again rather than bypassing that boundary.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Result handling
|
|
2
|
+
|
|
3
|
+
Use the response fields independently. A successful control command is not
|
|
4
|
+
the same as a successful provider result.
|
|
5
|
+
|
|
6
|
+
1. Save `task_id` immediately after dispatch or continue.
|
|
7
|
+
2. If `admission` is not admitted, report the bounded `error` and do not claim
|
|
8
|
+
that provider work ran.
|
|
9
|
+
3. If `liveness` is queued or running, call `status` or `collect` again with a
|
|
10
|
+
finite watch interval.
|
|
11
|
+
4. If `publication` is committed and `outcome.verdict` is committed, return
|
|
12
|
+
the result and its payload/evidence descriptors.
|
|
13
|
+
5. If `outcome.verdict` is rejected, report a terminal rejected result with its
|
|
14
|
+
refusal and evidence. Do not retry automatically.
|
|
15
|
+
6. If `status` is `timed_out`, follow the continuation procedure. A timeout is
|
|
16
|
+
recoverable only when `continuation.resumable` is true.
|
|
17
|
+
7. If authentication is `blocked`, preserve that neutral state and tell the
|
|
18
|
+
caller what prerequisite is unavailable without inventing a login command.
|
|
19
|
+
8. If a command reports an operational uncertainty, inspect the original task
|
|
20
|
+
with `status` and `collect` before considering any action. Never relaunch an
|
|
21
|
+
uncertain task by guessing.
|
|
22
|
+
|
|
23
|
+
Exit codes are bounded control signals: `0` is a completed command, `1` is an
|
|
24
|
+
operational error, `2` is an invalid or unsupported request, `3` is a pending
|
|
25
|
+
collection watch, and `4` is a validated rejected outcome.
|