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 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
- --provider codex:exec \
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. Dispatch still performs the host-specific runtime probe;
146
- catalog discovery alone does not prove authentication or supervisor readiness.
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.5",
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/SKILL.md",
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 sourcePath = join(packageRoot, "skills", "agent-integration", "SKILL.md");
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
- const destination = join(target, "SKILL.md");
479
- const destinationStat = await existingPath(destination);
480
- if (destinationStat?.isSymbolicLink()) {
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 (destinationStat && !destinationStat.isFile()) {
484
- throw new Error(`existing SKILL.md is not a regular file: ${destination}`);
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
- if (destinationStat && !force) {
488
- const [source, existing] = await Promise.all([
489
- readFile(sourcePath),
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
- await copyFile(sourcePath, destination);
500
- console.log(`Installed agent integration skill: ${destination}`);
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 from a provider-agnostic agent workflow, including capability discovery, bounded dispatch, terminal collection, and live authentication handling.
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
- # Agent integration
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
- This skill is self-contained as an operating guide. It gives an agent enough
9
- information to prepare a safe run, choose a provider from observed
10
- capabilities, and collect a terminal result when the host exposes the
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
- ## Preconditions
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
- The host is responsible for installing the CLI. This skill must not install,
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
- Keep the supervisor configuration, Delegation Layer state root, and task
26
- workspace as separate canonical paths. Do not put credentials in a brief, task
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
- ## Start safely
57
+ ```sh
58
+ delegate providers --json
59
+ ```
30
60
 
31
- First check that the already-installed CLI is available, then discover compiled
32
- profiles:
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
- command -v delegate
36
- delegate help
37
- delegate providers --json
66
+ delegate preflight --provider PROVIDER_ID --cwd ABSOLUTE_WORKSPACE --json
38
67
  ```
39
68
 
40
- If `command -v delegate` fails, stop and report that the host prerequisite is
41
- missing. Do not install or build the CLI from this skill.
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
- `providers --json` is static discovery. It lists provider IDs, supported
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
- Choose a listed `PROFILE`, then inspect its provider-neutral contract:
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
- delegate capabilities --provider PROFILE --json
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
- This command is side-effect-free. It does not create state, contact Pueue, or
55
- launch a provider. A successful catalog response has
56
- `status: "unknown"`, `verification: "catalog"`, and
57
- `live_acceptance.status: "not_run"`. Unknown here means that dispatch has not
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
- Choose a listed `PROFILE`, ensure its provider executable is available to the
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
- mkdir -p "${HOME}/delegation-workspace" "${HOME}/delegation-state"
66
- printf '%s\n' 'Inspect the workspace and return a short summary.' > "${HOME}/delegation-brief.txt"
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
- The default release owns the private supervisor lifecycle under the state root
79
- and recovers its bundled daemon when control commands find it stopped. Collection
80
- stays observational and does not start a supervisor. If an existing compatible
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 returned `task_id`. Admission records one immutable request and at
89
- most one provider launch attempt; the command does not wait for completion.
90
- Observe and collect with that exact ID:
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 "${HOME}/delegation-state" status TASK_ID --json
94
- delegate --root "${HOME}/delegation-state" collect TASK_ID --watch 5s --json
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
- Repeat `collect` while the task is pending. Collection is observational: it
98
- may wait for the bounded watch interval and clean up a validated reservation,
99
- but it never launches, retries, or resumes provider work. A continuation is a
100
- new task with an exact predecessor ID when the selected profile advertises
101
- continuation support.
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
- ## Read the JSON contract
134
+ ### 6. Continue a timed out turn
104
135
 
105
- The capability response has this stable shape. The two arrays are
106
- profile-specific and can be non-empty; empty arrays below only keep the example
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
- "schema_version": 1,
112
- "command": "capabilities",
113
- "capability": {
114
- "contract": "runtime-capability-v1",
115
- "provider": "PROFILE",
116
- "status": "unknown",
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
- Interpret the fields as follows:
131
-
132
- - `status=unknown` with `verification=catalog` is a declaration from the
133
- compiled catalog.
134
- - `status=ready` with `verification=runtime` appears in a dispatch response
135
- only after the supervised probe observed the required behavior and exact
136
- executable identity for that admission boundary.
137
- - `status=unsupported` is a machine-readable refusal. Use `reason_code` and
138
- the bounded error to explain it; do not try another profile without a new
139
- explicit selection.
140
- - `version` is diagnostic observation. Accept any valid nonempty trimmed UTF-8
141
- version text. Never use a release allowlist, semantic-version comparison, or
142
- web lookup as a compatibility decision. Required flags, executable identity,
143
- and provider behavior decide compatibility. An unusually large version may
144
- be omitted from the bounded projection.
145
- - `live_acceptance` is separate from compatibility. `not_run` does not claim a
146
- live test; `passed` requires authenticated provider evidence; `blocked` means
147
- authentication or another prerequisite was unavailable and is neutral. A
148
- blocked result must never be reported as a pass or as incompatible behavior.
149
-
150
- Dispatch JSON contains the same `capability` projection plus separate task
151
- `admission`, `liveness`, and `publication` fields. Read those fields
152
- independently. Trust a terminal `outcome` only after it has been returned by
153
- the validated collection path and its payload and sealed evidence descriptors
154
- match the task records.
155
-
156
- ## Integration acceptance contract
157
-
158
- Apply these criteria to every provider and every agent integration:
159
-
160
- - required behavior is verified at the runtime boundary;
161
- - arbitrary valid provider version text remains acceptable;
162
- - missing flags, executable identity drift, malformed facts, and timeouts fail
163
- closed;
164
- - authentication is represented separately from compatibility;
165
- - missing authentication is `blocked`, never a passing skip;
166
- - admission is at most once and collection is observational;
167
- - terminal results come from durable validated evidence; and
168
- - JSON output is bounded, deterministic for observational commands, versioned,
169
- and free of credentials.
170
-
171
- Do not expose credentials, tokens, private raw provider output, or unrestricted
172
- diagnostics in prompts, tool results, receipts, or task metadata. After an
173
- uncertain admission or lost response, inspect the original task with
174
- `status`/`collect`; do not relaunch it. Stop and report the durable state when
175
- the task cannot be resolved from validated evidence.
176
-
177
- ## Completion record
178
-
179
- An integration is complete when it can provide:
180
-
181
- - the selected profile and original task ID;
182
- - the bounded capability and dispatch JSON responses;
183
- - the terminal committed or rejected outcome and validated evidence
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.