delegation-layer 0.3.1 → 0.4.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/package.json +1 -1
- package/plugins/delegation-layer/.claude-plugin/plugin.json +1 -1
- package/plugins/delegation-layer/.codex-plugin/plugin.json +1 -1
- package/plugins/delegation-layer/skills/agent-integration/references/command-reference.md +31 -3
- package/plugins/delegation-layer/skills/agent-integration/scripts/delegate-routing-context.sh +2 -2
- package/scripts/installer/routing-store.mjs +79 -46
- package/skills/agent-integration/references/command-reference.md +31 -3
- package/skills/agent-integration/scripts/delegate-routing-context.sh +2 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delegation-layer",
|
|
3
3
|
"displayName": "Delegation Layer",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"description": "Consult external AI harnesses and CCR-configured models for reviews, research, implementation, and debate through the resumable Delegate CLI.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Hesham Karam",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delegation-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Consult external AI harnesses and CCR-configured models for reviews, research, implementation, and debate through the resumable Delegate CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Hesham Karam",
|
|
@@ -295,6 +295,32 @@ Missing task files and corrupt evidence are distinct; see
|
|
|
295
295
|
[scheduled result handling](result-handling.md#scheduled-execution-results)
|
|
296
296
|
for freshness, no-result states, response bounds, exit codes and recovery.
|
|
297
297
|
|
|
298
|
+
## Supervisor owner control
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
delegate supervisor stop-owner --json
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Use only when the installed build advertises this command. Preserve the explicit
|
|
305
|
+
root for work bound to a custom or historical root. This command contacts one
|
|
306
|
+
existing infrastructure owner; it does not initialize, repair or start one.
|
|
307
|
+
It leaves Pueue and admitted tasks running. Startup, accepted operations and
|
|
308
|
+
unresolved cleanup can make the owner busy.
|
|
309
|
+
|
|
310
|
+
The `supervisor_owner` response separates `state` (`serving_released`,
|
|
311
|
+
`not_initialized`, `busy` or `unknown`) from `process_closure` (`closed`,
|
|
312
|
+
`pending` or `unknown`). A released owner can remain alive as the sole waiter for
|
|
313
|
+
its daemon; serving release is not proof of process exit or root quiescence.
|
|
314
|
+
The optional `process` value (`pid`, `start_token`) identifies the validated
|
|
315
|
+
selected `startup`; it does not establish liveness. It is omitted when no owner
|
|
316
|
+
was selected or validated. Observe that exact incarnation after daemon shutdown;
|
|
317
|
+
a new root-selected stop can address a newer owner.
|
|
318
|
+
An absent root reports `not_initialized` with unknown process closure.
|
|
319
|
+
Exit `0` reports the observed release or uninitialized state, `3` reports busy,
|
|
320
|
+
and `1` reports a control or observation fault. Preserve an uncertain result;
|
|
321
|
+
the command does not resend an uncertain stop request. Human output exposes
|
|
322
|
+
the same separate observations.
|
|
323
|
+
|
|
298
324
|
## Scheduler runtime
|
|
299
325
|
|
|
300
326
|
Check `delegate help` for the installed build's command surface. Use `serve`,
|
|
@@ -487,9 +513,11 @@ Inspection-backed admission can return `status: "ready"` with
|
|
|
487
513
|
`verification: "runtime"` after its probe. Managed runtime-only admission
|
|
488
514
|
keeps the catalog projection; its supervised worker checks compatibility before
|
|
489
515
|
provider execution. Collect the created task to establish the result.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
or
|
|
516
|
+
Provider versions are optional diagnostics. Historical executable-digest members
|
|
517
|
+
remain readable as recorded data. Do not depend on these fields or calculate new
|
|
518
|
+
executable fingerprints for diagnostics or admission. Required behavior and the
|
|
519
|
+
selected installed executable govern compatibility. Live acceptance remains separate (`not_run`,
|
|
520
|
+
`passed`, or `blocked`); a blocked prerequisite is unresolved.
|
|
493
521
|
|
|
494
522
|
## Choosing models and effort
|
|
495
523
|
|
package/plugins/delegation-layer/skills/agent-integration/scripts/delegate-routing-context.sh
CHANGED
|
@@ -5,11 +5,11 @@ case "${1:-}" in
|
|
|
5
5
|
SessionStart|SubagentStart) event=$1 ;;
|
|
6
6
|
*) printf '%s\n' 'Expected SessionStart or SubagentStart.' >&2; exit 2 ;;
|
|
7
7
|
esac
|
|
8
|
-
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
8
|
+
script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
|
|
9
9
|
rule_file="$script_dir/../references/routing-rule.json"
|
|
10
10
|
# Read before emitting anything so a missing asset cannot produce partial JSON.
|
|
11
11
|
rule=$(cat "$rule_file")
|
|
12
|
-
skill_file="$(CDPATH= cd -- "$script_dir/.." && pwd)/SKILL.md"
|
|
12
|
+
skill_file="$(CDPATH='' cd -- "$script_dir/.." && pwd)/SKILL.md"
|
|
13
13
|
DELEGATE_ROUTING_EVENT="$event" DELEGATE_ROUTING_RULE="$rule" DELEGATE_ROUTING_SKILL="$skill_file" awk '
|
|
14
14
|
BEGIN {
|
|
15
15
|
# Paths are task data, not shell code. Escape every JSON control character.
|
|
@@ -5,7 +5,7 @@ import { assertNoSymlinkComponents, existingPath } from "./filesystem.mjs";
|
|
|
5
5
|
import { countRoutingEvents, createRoutingPlan, routingPath, validateHookConfig } from "./routing-rules.mjs";
|
|
6
6
|
import { targetFor } from "./selection.mjs";
|
|
7
7
|
|
|
8
|
-
export async function prepareRouting(selected, action, { environment, platform
|
|
8
|
+
export async function prepareRouting(selected, action, { environment, platform }) {
|
|
9
9
|
const plans = [];
|
|
10
10
|
for (const name of new Set(selected.harnessNames)) {
|
|
11
11
|
const path = routingPath(name, {
|
|
@@ -55,85 +55,118 @@ export async function prepareRouting(selected, action, { environment, platform,
|
|
|
55
55
|
return plans;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
const routingFileOperations = Object.freeze({ open, mkdir, readFile, rename, unlink, existingPath, assertNoSymlinkComponents });
|
|
59
|
+
|
|
60
|
+
function errorMessage(error) {
|
|
61
|
+
return error instanceof Error ? error.message : String(error);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function combinedFailure(failures) {
|
|
65
|
+
if (failures.length === 1) return failures[0];
|
|
66
|
+
return new AggregateError(failures, failures.map(errorMessage).join("; "), { cause: failures[0] });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function writeAndClose(file, contents) {
|
|
70
|
+
const failures = [];
|
|
71
|
+
try {
|
|
72
|
+
await file.writeFile(contents);
|
|
73
|
+
await file.sync();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
failures.push(error);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await file.close();
|
|
79
|
+
} catch (error) {
|
|
80
|
+
failures.push(error);
|
|
81
|
+
}
|
|
82
|
+
if (failures.length) throw combinedFailure(failures);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function cleanupReplacement(lock, temporary, lockPath, operations) {
|
|
86
|
+
const failures = [];
|
|
87
|
+
try {
|
|
88
|
+
await operations.unlink(temporary);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) failures.push(error);
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
await lock.close();
|
|
94
|
+
} catch (error) {
|
|
95
|
+
failures.push(error);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
await operations.unlink(lockPath);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
failures.push(error);
|
|
101
|
+
}
|
|
102
|
+
return failures;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function replaceRoutingConfig(plan, before, after, operations, onCommitted) {
|
|
106
|
+
await operations.assertNoSymlinkComponents(plan.path);
|
|
107
|
+
await operations.mkdir(dirname(plan.path), { recursive: true });
|
|
61
108
|
const lockPath = plan.path + ".delegation-layer.lock";
|
|
62
109
|
let lock;
|
|
63
110
|
try {
|
|
64
|
-
lock = await open(lockPath, "wx", 0o600);
|
|
111
|
+
lock = await operations.open(lockPath, "wx", 0o600);
|
|
65
112
|
} catch (error) {
|
|
66
113
|
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
67
|
-
throw new Error(`routing configuration is locked: ${lockPath}; verify no other installer is running before removing a stale lock
|
|
114
|
+
throw new Error(`routing configuration is locked: ${lockPath}; verify no other installer is running before removing a stale lock`, { cause: error });
|
|
68
115
|
}
|
|
69
116
|
throw error;
|
|
70
117
|
}
|
|
71
118
|
const suffix = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
72
119
|
const temporary = plan.path + `.delegation-layer-stage-${suffix}`;
|
|
120
|
+
const failures = [];
|
|
73
121
|
try {
|
|
74
|
-
const stat = await existingPath(plan.path);
|
|
122
|
+
const stat = await operations.existingPath(plan.path);
|
|
75
123
|
if (stat && (!stat.isFile() || stat.isSymbolicLink())) {
|
|
76
124
|
throw new Error(`routing configuration changed during installation: ${plan.path}`);
|
|
77
125
|
}
|
|
78
|
-
const current = stat ? await readFile(plan.path, "utf8") : null;
|
|
126
|
+
const current = stat ? await operations.readFile(plan.path, "utf8") : null;
|
|
79
127
|
if (current !== before) throw new Error(`routing configuration changed during installation; retry: ${plan.path}`);
|
|
80
128
|
if (before !== null) {
|
|
81
|
-
const backup = await open(plan.path + `.delegation-layer-backup-${suffix}`, "wx", 0o600);
|
|
82
|
-
|
|
83
|
-
await backup.writeFile(before);
|
|
84
|
-
await backup.sync();
|
|
85
|
-
} finally {
|
|
86
|
-
await backup.close();
|
|
87
|
-
}
|
|
129
|
+
const backup = await operations.open(plan.path + `.delegation-layer-backup-${suffix}`, "wx", 0o600);
|
|
130
|
+
await writeAndClose(backup, before);
|
|
88
131
|
}
|
|
89
132
|
if (after === null) {
|
|
90
|
-
await unlink(plan.path);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
await staged.writeFile(after);
|
|
96
|
-
await staged.sync();
|
|
97
|
-
} finally {
|
|
98
|
-
await staged.close();
|
|
99
|
-
}
|
|
100
|
-
await rename(temporary, plan.path);
|
|
101
|
-
} finally {
|
|
102
|
-
try {
|
|
103
|
-
try {
|
|
104
|
-
await unlink(temporary);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
|
|
107
|
-
}
|
|
108
|
-
} finally {
|
|
109
|
-
try {
|
|
110
|
-
await lock.close();
|
|
111
|
-
} finally {
|
|
112
|
-
await unlink(lockPath);
|
|
113
|
-
}
|
|
133
|
+
await operations.unlink(plan.path);
|
|
134
|
+
} else {
|
|
135
|
+
const staged = await operations.open(temporary, "wx", plan.mode);
|
|
136
|
+
await writeAndClose(staged, after);
|
|
137
|
+
await operations.rename(temporary, plan.path);
|
|
114
138
|
}
|
|
139
|
+
onCommitted?.();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
failures.push(error);
|
|
115
142
|
}
|
|
143
|
+
failures.push(...await cleanupReplacement(lock, temporary, lockPath, operations));
|
|
144
|
+
if (failures.length) throw combinedFailure(failures);
|
|
116
145
|
}
|
|
117
146
|
|
|
118
|
-
export async function applyRouting(plans) {
|
|
147
|
+
export async function applyRouting(plans, fileOperations = {}) {
|
|
148
|
+
const operations = { ...routingFileOperations, ...fileOperations };
|
|
149
|
+
// This transaction owns committed mutations, including commits followed by a cleanup failure.
|
|
119
150
|
const applied = [];
|
|
120
151
|
try {
|
|
121
152
|
for (const plan of plans) {
|
|
122
153
|
if (!plan.path || plan.before === plan.after) continue;
|
|
123
|
-
await replaceRoutingConfig(plan, plan.before, plan.after);
|
|
124
|
-
applied.push(plan);
|
|
154
|
+
await replaceRoutingConfig(plan, plan.before, plan.after, operations, () => applied.push(plan));
|
|
125
155
|
}
|
|
126
156
|
} catch (error) {
|
|
127
157
|
const failures = [];
|
|
128
158
|
for (const plan of applied.reverse()) {
|
|
129
159
|
try {
|
|
130
|
-
await replaceRoutingConfig(plan, plan.after, plan.before);
|
|
160
|
+
await replaceRoutingConfig(plan, plan.after, plan.before, operations);
|
|
131
161
|
} catch (rollbackError) {
|
|
132
|
-
failures.push(rollbackError
|
|
162
|
+
failures.push(rollbackError);
|
|
133
163
|
}
|
|
134
164
|
}
|
|
135
|
-
const reason =
|
|
136
|
-
|
|
165
|
+
const reason = errorMessage(error);
|
|
166
|
+
if (failures.length) {
|
|
167
|
+
throw new AggregateError([error, ...failures], `${reason}; rollback needs attention: ${failures.map(errorMessage).join("; ")}`, { cause: error });
|
|
168
|
+
}
|
|
169
|
+
throw new Error(`${reason}; earlier routing changes rolled back`, { cause: error });
|
|
137
170
|
}
|
|
138
171
|
}
|
|
139
172
|
|
|
@@ -295,6 +295,32 @@ Missing task files and corrupt evidence are distinct; see
|
|
|
295
295
|
[scheduled result handling](result-handling.md#scheduled-execution-results)
|
|
296
296
|
for freshness, no-result states, response bounds, exit codes and recovery.
|
|
297
297
|
|
|
298
|
+
## Supervisor owner control
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
delegate supervisor stop-owner --json
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Use only when the installed build advertises this command. Preserve the explicit
|
|
305
|
+
root for work bound to a custom or historical root. This command contacts one
|
|
306
|
+
existing infrastructure owner; it does not initialize, repair or start one.
|
|
307
|
+
It leaves Pueue and admitted tasks running. Startup, accepted operations and
|
|
308
|
+
unresolved cleanup can make the owner busy.
|
|
309
|
+
|
|
310
|
+
The `supervisor_owner` response separates `state` (`serving_released`,
|
|
311
|
+
`not_initialized`, `busy` or `unknown`) from `process_closure` (`closed`,
|
|
312
|
+
`pending` or `unknown`). A released owner can remain alive as the sole waiter for
|
|
313
|
+
its daemon; serving release is not proof of process exit or root quiescence.
|
|
314
|
+
The optional `process` value (`pid`, `start_token`) identifies the validated
|
|
315
|
+
selected `startup`; it does not establish liveness. It is omitted when no owner
|
|
316
|
+
was selected or validated. Observe that exact incarnation after daemon shutdown;
|
|
317
|
+
a new root-selected stop can address a newer owner.
|
|
318
|
+
An absent root reports `not_initialized` with unknown process closure.
|
|
319
|
+
Exit `0` reports the observed release or uninitialized state, `3` reports busy,
|
|
320
|
+
and `1` reports a control or observation fault. Preserve an uncertain result;
|
|
321
|
+
the command does not resend an uncertain stop request. Human output exposes
|
|
322
|
+
the same separate observations.
|
|
323
|
+
|
|
298
324
|
## Scheduler runtime
|
|
299
325
|
|
|
300
326
|
Check `delegate help` for the installed build's command surface. Use `serve`,
|
|
@@ -487,9 +513,11 @@ Inspection-backed admission can return `status: "ready"` with
|
|
|
487
513
|
`verification: "runtime"` after its probe. Managed runtime-only admission
|
|
488
514
|
keeps the catalog projection; its supervised worker checks compatibility before
|
|
489
515
|
provider execution. Collect the created task to establish the result.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
or
|
|
516
|
+
Provider versions are optional diagnostics. Historical executable-digest members
|
|
517
|
+
remain readable as recorded data. Do not depend on these fields or calculate new
|
|
518
|
+
executable fingerprints for diagnostics or admission. Required behavior and the
|
|
519
|
+
selected installed executable govern compatibility. Live acceptance remains separate (`not_run`,
|
|
520
|
+
`passed`, or `blocked`); a blocked prerequisite is unresolved.
|
|
493
521
|
|
|
494
522
|
## Choosing models and effort
|
|
495
523
|
|
|
@@ -5,11 +5,11 @@ case "${1:-}" in
|
|
|
5
5
|
SessionStart|SubagentStart) event=$1 ;;
|
|
6
6
|
*) printf '%s\n' 'Expected SessionStart or SubagentStart.' >&2; exit 2 ;;
|
|
7
7
|
esac
|
|
8
|
-
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
8
|
+
script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
|
|
9
9
|
rule_file="$script_dir/../references/routing-rule.json"
|
|
10
10
|
# Read before emitting anything so a missing asset cannot produce partial JSON.
|
|
11
11
|
rule=$(cat "$rule_file")
|
|
12
|
-
skill_file="$(CDPATH= cd -- "$script_dir/.." && pwd)/SKILL.md"
|
|
12
|
+
skill_file="$(CDPATH='' cd -- "$script_dir/.." && pwd)/SKILL.md"
|
|
13
13
|
DELEGATE_ROUTING_EVENT="$event" DELEGATE_ROUTING_RULE="$rule" DELEGATE_ROUTING_SKILL="$skill_file" awk '
|
|
14
14
|
BEGIN {
|
|
15
15
|
# Paths are task data, not shell code. Escape every JSON control character.
|