@tryinget/pi-agent-registry 0.3.0
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/LICENSE +78 -0
- package/README.md +238 -0
- package/docs/engineering.local.md +90 -0
- package/docs/project/2026-08-27-agent-registry.md +287 -0
- package/docs/project/foundation.md +31 -0
- package/docs/project/vision.md +18 -0
- package/examples/.gitkeep +0 -0
- package/extensions/pi-agent-registry.ts +378 -0
- package/package.json +105 -0
- package/policy/engineering-lane.json +34 -0
- package/policy/security-policy.json +10 -0
- package/prompts/implementation-planning.md +20 -0
- package/prompts/security-review.md +20 -0
- package/scripts/fleet-lint.mjs +82 -0
- package/src/.gitkeep +0 -0
- package/src/agent-skill-resolver.ts +50 -0
- package/src/asc-execution-surface.ts +64 -0
- package/src/dispatch-authorization.ts +237 -0
- package/src/dispatch-contract.ts +89 -0
- package/src/dispatch-receipt.ts +326 -0
- package/src/dispatch-request.ts +135 -0
- package/src/dispatch.ts +498 -0
- package/src/ec-profiles.ts +392 -0
- package/src/fleet-git-snapshot.ts +323 -0
- package/src/fleet-lint-provenance.ts +356 -0
- package/src/fleet-lint-repository.ts +450 -0
- package/src/fleet-lint-skills.ts +131 -0
- package/src/fleet-lint-types.ts +113 -0
- package/src/fleet-lint-utils.ts +66 -0
- package/src/fleet-lint.ts +375 -0
- package/src/fleet-prompt-compiler.ts +155 -0
- package/src/manifest.ts +678 -0
- package/src/registry-discovery.ts +225 -0
- package/src/registry.ts +280 -0
- package/src/sessions-dir.ts +30 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ---
|
|
2
|
+
// summary: registry-owned ExtraSkillProfileResolver mapping standing-agent names onto materialized skill selections.
|
|
3
|
+
// read_when:
|
|
4
|
+
// - changing how ASC resolves pi-agent-registry agent names as skill profiles.
|
|
5
|
+
// ---
|
|
6
|
+
|
|
7
|
+
import type { ExtraSkillProfileResolver } from "@tryinget/pi-autonomous-session-control/execution";
|
|
8
|
+
import { materializeSkillDirs, planSkillSelection } from "./ec-profiles.ts";
|
|
9
|
+
import type { AgentRegistry } from "./registry.ts";
|
|
10
|
+
|
|
11
|
+
export const AGENT_SKILL_REGISTRY_LABEL = "pi-agent-registry" as const;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fleet Phase-2 skill seam: the registry resolves its own agent names for the
|
|
15
|
+
* ASC runtime's extraSkillProfileResolver hook. ASC consults this only after
|
|
16
|
+
* its built-in skill-librarian registry misses, and ASC owns cleanup of the
|
|
17
|
+
* materialized selection once the dispatched child settles.
|
|
18
|
+
*
|
|
19
|
+
* Returning undefined declines the profile so ASC fails closed with its own
|
|
20
|
+
* diagnostics; SubagentSkillSelectionError marks registry-owned fail-closed
|
|
21
|
+
* paths (unknown extra skill, unknown EC profile, filesystem failure).
|
|
22
|
+
*/
|
|
23
|
+
export function createAgentSkillProfileResolver(
|
|
24
|
+
registry: AgentRegistry,
|
|
25
|
+
): ExtraSkillProfileResolver {
|
|
26
|
+
return async (profile) => {
|
|
27
|
+
const manifest = registry.get(profile);
|
|
28
|
+
if (!manifest) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const selection = planSkillSelection({
|
|
32
|
+
...(manifest.skills?.profile !== undefined ? { profile: manifest.skills.profile } : {}),
|
|
33
|
+
...(manifest.skills?.extra ? { extra: manifest.skills.extra } : {}),
|
|
34
|
+
ec: registry.ec,
|
|
35
|
+
manifestRoot: manifest.root,
|
|
36
|
+
userSkillsRoot: registry.userSkillsRoot,
|
|
37
|
+
});
|
|
38
|
+
const materialized = await materializeSkillDirs(selection, manifest.name);
|
|
39
|
+
return {
|
|
40
|
+
noSkills: true,
|
|
41
|
+
skillSources: [materialized.dir],
|
|
42
|
+
skillProfile: manifest.name,
|
|
43
|
+
loadedSkills: materialized.skills,
|
|
44
|
+
librarySkills: [],
|
|
45
|
+
skillWarnings: [],
|
|
46
|
+
skillRegistry: AGENT_SKILL_REGISTRY_LABEL,
|
|
47
|
+
cleanup: materialized.cleanup,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// ---
|
|
2
|
+
// summary: capability-checked loader for the ASC-owned execution surface used by Fleet Phase-2 dispatch.
|
|
3
|
+
// read_when:
|
|
4
|
+
// - changing how pi-agent-registry consumes ASC runtime/session/model contracts.
|
|
5
|
+
// ---
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
AscExecutionRuntime,
|
|
9
|
+
ResolvedSubagentModelSelection,
|
|
10
|
+
ResolvedSubagentSessionsDir,
|
|
11
|
+
} from "@tryinget/pi-autonomous-session-control/execution";
|
|
12
|
+
|
|
13
|
+
export interface AscExecutionSurface {
|
|
14
|
+
createAscExecutionRuntime: (options: unknown) => AscExecutionRuntime;
|
|
15
|
+
resolveSubagentSessionsDir: (options?: {
|
|
16
|
+
explicitDir?: string;
|
|
17
|
+
cwd?: string;
|
|
18
|
+
agentDir?: string;
|
|
19
|
+
sessionDirEnv?: string;
|
|
20
|
+
}) => ResolvedSubagentSessionsDir;
|
|
21
|
+
resolveSubagentModelSelection: (ctx?: {
|
|
22
|
+
model?: { provider?: unknown; id?: unknown };
|
|
23
|
+
}) => ResolvedSubagentModelSelection;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let cached: AscExecutionSurface | undefined | "unloaded" = "unloaded";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Load ASC's exported execution surface. The registry declares the ASC
|
|
30
|
+
* dependency as a workspace link; an installed/published ASC that predates
|
|
31
|
+
* the execution exports resolves to `undefined` and every Phase-2 dispatch
|
|
32
|
+
* fails closed with `asc_execution_unavailable` (confirmed_no_effects).
|
|
33
|
+
* Execution machinery stays ASC-owned; this loader only checks capability.
|
|
34
|
+
*/
|
|
35
|
+
export async function loadAscExecutionSurface(): Promise<AscExecutionSurface | undefined> {
|
|
36
|
+
if (cached !== "unloaded") {
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const mod = (await import("@tryinget/pi-autonomous-session-control/execution")) as Record<
|
|
41
|
+
string,
|
|
42
|
+
unknown
|
|
43
|
+
>;
|
|
44
|
+
const surface = {
|
|
45
|
+
createAscExecutionRuntime: mod.createAscExecutionRuntime,
|
|
46
|
+
resolveSubagentSessionsDir: mod.resolveSubagentSessionsDir,
|
|
47
|
+
resolveSubagentModelSelection: mod.resolveSubagentModelSelection,
|
|
48
|
+
};
|
|
49
|
+
cached =
|
|
50
|
+
typeof surface.createAscExecutionRuntime === "function" &&
|
|
51
|
+
typeof surface.resolveSubagentSessionsDir === "function" &&
|
|
52
|
+
typeof surface.resolveSubagentModelSelection === "function"
|
|
53
|
+
? (surface as AscExecutionSurface)
|
|
54
|
+
: undefined;
|
|
55
|
+
} catch {
|
|
56
|
+
cached = undefined;
|
|
57
|
+
}
|
|
58
|
+
return cached;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Test-only surface cache reset. */
|
|
62
|
+
export function resetAscExecutionSurfaceCache(): void {
|
|
63
|
+
cached = "unloaded";
|
|
64
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// ---
|
|
2
|
+
// summary: exact-task AK authorization reads and dispatch evidence recording through the ak CLI boundary.
|
|
3
|
+
// read_when:
|
|
4
|
+
// - changing task authorization rules or AK evidence semantics for standing-agent dispatch.
|
|
5
|
+
// ---
|
|
6
|
+
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { promisify } from "node:util";
|
|
9
|
+
import {
|
|
10
|
+
type AkTaskSnapshot,
|
|
11
|
+
DISPATCH_EVIDENCE_CHECK_TYPE,
|
|
12
|
+
DISPATCH_PHASE,
|
|
13
|
+
DISPATCH_RECEIPT_SCHEMA,
|
|
14
|
+
} from "./dispatch-contract.ts";
|
|
15
|
+
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
|
|
18
|
+
const AK_TIMEOUT_MS = 15_000;
|
|
19
|
+
|
|
20
|
+
export type AkAuthorizationFailureCode =
|
|
21
|
+
| "ak_unavailable"
|
|
22
|
+
| "task_not_found"
|
|
23
|
+
| "task_repo_mismatch"
|
|
24
|
+
| "task_not_claimed"
|
|
25
|
+
| "task_lease_expired";
|
|
26
|
+
|
|
27
|
+
export class AkAuthorizationError extends Error {
|
|
28
|
+
readonly code: AkAuthorizationFailureCode;
|
|
29
|
+
|
|
30
|
+
constructor(code: AkAuthorizationFailureCode, message: string) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "AkAuthorizationError";
|
|
33
|
+
this.code = code;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Read one exact AK task through `ak task show <id> -F json` (read-only). */
|
|
38
|
+
export async function readAkTask(
|
|
39
|
+
taskId: number,
|
|
40
|
+
options?: { akBinary?: string },
|
|
41
|
+
): Promise<AkTaskSnapshot> {
|
|
42
|
+
const akBinary = options?.akBinary ?? "ak";
|
|
43
|
+
let stdout: string;
|
|
44
|
+
try {
|
|
45
|
+
const result = await execFileAsync(akBinary, ["task", "show", String(taskId), "-F", "json"], {
|
|
46
|
+
timeout: AK_TIMEOUT_MS,
|
|
47
|
+
maxBuffer: 1024 * 1024,
|
|
48
|
+
windowsHide: true,
|
|
49
|
+
});
|
|
50
|
+
stdout = result.stdout;
|
|
51
|
+
} catch {
|
|
52
|
+
throw new AkAuthorizationError(
|
|
53
|
+
"ak_unavailable",
|
|
54
|
+
`AK task ${taskId} could not be read (ak task show failed); dispatch authorization is unverifiable`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
let parsed: unknown;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(stdout);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new AkAuthorizationError(
|
|
62
|
+
"ak_unavailable",
|
|
63
|
+
`AK task ${taskId} produced unparseable output; dispatch authorization is unverifiable`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (!parsed || typeof parsed !== "object") {
|
|
67
|
+
throw new AkAuthorizationError("task_not_found", `AK task ${taskId} returned no task object`);
|
|
68
|
+
}
|
|
69
|
+
const row = parsed as Record<string, unknown>;
|
|
70
|
+
const id = row.id;
|
|
71
|
+
const repo = row.repo;
|
|
72
|
+
const title = row.title;
|
|
73
|
+
if (typeof id !== "number" || id !== taskId || typeof repo !== "string" || !repo) {
|
|
74
|
+
throw new AkAuthorizationError(
|
|
75
|
+
"task_not_found",
|
|
76
|
+
`AK task ${taskId} returned an unusable task identity`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
repo,
|
|
82
|
+
title: typeof title === "string" ? title : "",
|
|
83
|
+
status: typeof row.status === "string" ? row.status : "",
|
|
84
|
+
claimed_by: typeof row.claimed_by === "string" && row.claimed_by ? row.claimed_by : null,
|
|
85
|
+
lease_expires_at:
|
|
86
|
+
typeof row.lease_expires_at === "string" && row.lease_expires_at
|
|
87
|
+
? row.lease_expires_at
|
|
88
|
+
: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Phase-2 exact-task authorization: the task must exist, be registered to the
|
|
94
|
+
* dispatch-origin repository, and carry a live claim. Readiness never becomes
|
|
95
|
+
* authorization — only an in-flight claim authorizes one read-only dispatch.
|
|
96
|
+
*/
|
|
97
|
+
export function authorizeExactTask(
|
|
98
|
+
task: AkTaskSnapshot,
|
|
99
|
+
expectedRepoRoot: string,
|
|
100
|
+
): { ok: true } | { ok: false; code: AkAuthorizationFailureCode; message: string } {
|
|
101
|
+
if (normalizeRepoPath(task.repo) !== normalizeRepoPath(expectedRepoRoot)) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
code: "task_repo_mismatch",
|
|
105
|
+
message: `AK task ${task.id} belongs to repo ${task.repo}; dispatch origin is ${expectedRepoRoot}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (task.status !== "claimed") {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
code: "task_not_claimed",
|
|
112
|
+
message: `AK task ${task.id} status is "${task.status}"; only a claimed task authorizes one read-only standing-agent dispatch`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!task.claimed_by) {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
code: "task_not_claimed",
|
|
119
|
+
message: `AK task ${task.id} is claimed without a claimant; authorization is unverifiable`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (task.lease_expires_at) {
|
|
123
|
+
const expiry = Date.parse(task.lease_expires_at);
|
|
124
|
+
if (!Number.isFinite(expiry) || expiry <= Date.now()) {
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
code: "task_lease_expired",
|
|
128
|
+
message: `AK task ${task.id} claim lease is expired or unparseable; re-claim before dispatch`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
code: "task_lease_expired",
|
|
135
|
+
message: `AK task ${task.id} carries no lease expiry; authorization is unverifiable`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return { ok: true };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeRepoPath(value: string): string {
|
|
142
|
+
return value.replace(/\/+$/u, "");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Typed AK evidence details for one settled Phase-2 dispatch. */
|
|
146
|
+
export function buildDispatchEvidenceDetails(facts: {
|
|
147
|
+
agent: string;
|
|
148
|
+
agentRepoCommit: string;
|
|
149
|
+
manifestSha256: string;
|
|
150
|
+
task: number;
|
|
151
|
+
attemptIndex: number;
|
|
152
|
+
dispatchId: string;
|
|
153
|
+
attemptId: string;
|
|
154
|
+
sessionName: string;
|
|
155
|
+
effectDisposition: string;
|
|
156
|
+
effectCorrelationId: string;
|
|
157
|
+
effectCorrelationEchoVerified: boolean;
|
|
158
|
+
noMutationObserved: boolean;
|
|
159
|
+
outputSha256: string;
|
|
160
|
+
receiptSha256: string;
|
|
161
|
+
receiptName?: string;
|
|
162
|
+
}): Record<string, unknown> {
|
|
163
|
+
return {
|
|
164
|
+
schema: DISPATCH_RECEIPT_SCHEMA,
|
|
165
|
+
phase: DISPATCH_PHASE,
|
|
166
|
+
agent: facts.agent,
|
|
167
|
+
agentRepoCommit: facts.agentRepoCommit,
|
|
168
|
+
manifestSha256: facts.manifestSha256,
|
|
169
|
+
task: facts.task,
|
|
170
|
+
attemptIndex: facts.attemptIndex,
|
|
171
|
+
dispatchId: facts.dispatchId,
|
|
172
|
+
attemptId: facts.attemptId,
|
|
173
|
+
sessionName: facts.sessionName,
|
|
174
|
+
effectDisposition: facts.effectDisposition,
|
|
175
|
+
effectCorrelationId: facts.effectCorrelationId,
|
|
176
|
+
effectCorrelationEchoVerified: facts.effectCorrelationEchoVerified,
|
|
177
|
+
noMutationObserved: facts.noMutationObserved,
|
|
178
|
+
outputSha256: facts.outputSha256,
|
|
179
|
+
receiptSha256: facts.receiptSha256,
|
|
180
|
+
...(facts.receiptName ? { receiptName: facts.receiptName } : {}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface AkEvidenceRecordResult {
|
|
185
|
+
evidenceId: number;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface AkEvidenceRecordFailure {
|
|
189
|
+
error: Error;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Append one typed AK evidence row for a completed dispatch. Evidence is
|
|
194
|
+
* recorded only for a settled, no-mutation-observed dispatch; failures keep
|
|
195
|
+
* the immutable receipt as truth and leave AK recording to the parent task.
|
|
196
|
+
*/
|
|
197
|
+
export async function recordDispatchEvidence(
|
|
198
|
+
params: {
|
|
199
|
+
taskId: number;
|
|
200
|
+
details: Record<string, unknown>;
|
|
201
|
+
},
|
|
202
|
+
options?: { akBinary?: string },
|
|
203
|
+
): Promise<AkEvidenceRecordResult> {
|
|
204
|
+
const akBinary = options?.akBinary ?? "ak";
|
|
205
|
+
const detailsJson = JSON.stringify(params.details);
|
|
206
|
+
let stdout: string;
|
|
207
|
+
try {
|
|
208
|
+
const result = await execFileAsync(
|
|
209
|
+
akBinary,
|
|
210
|
+
[
|
|
211
|
+
"evidence",
|
|
212
|
+
"record",
|
|
213
|
+
"--task",
|
|
214
|
+
String(params.taskId),
|
|
215
|
+
"--check-type",
|
|
216
|
+
DISPATCH_EVIDENCE_CHECK_TYPE,
|
|
217
|
+
"--result",
|
|
218
|
+
"pass",
|
|
219
|
+
"--details",
|
|
220
|
+
detailsJson,
|
|
221
|
+
],
|
|
222
|
+
{ timeout: AK_TIMEOUT_MS, maxBuffer: 1024 * 1024, windowsHide: true },
|
|
223
|
+
);
|
|
224
|
+
stdout = result.stdout;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`AK evidence recording failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const id = Number(
|
|
231
|
+
/Recorded evidence\s+#?(\d+)/iu.exec(stdout)?.[1] ?? /#(\d+)/u.exec(stdout)?.[1],
|
|
232
|
+
);
|
|
233
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
234
|
+
throw new Error(`AK evidence recording returned no evidence id (output: ${stdout.trim()})`);
|
|
235
|
+
}
|
|
236
|
+
return { evidenceId: id };
|
|
237
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ---
|
|
2
|
+
// summary: Fleet Phase-2 exact-task read-only standing-agent dispatch contract constants and types.
|
|
3
|
+
// read_when:
|
|
4
|
+
// - changing the dispatch_agent Phase-2 gate, eligibility rules, or receipt/evidence semantics.
|
|
5
|
+
// ---
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fleet Phase 2 enables exactly one bounded execution shape:
|
|
9
|
+
* one registered standing agent, bound to one exact claimed AK task,
|
|
10
|
+
* read-only, executed through the ASC-owned runtime, recorded as one
|
|
11
|
+
* immutable receipt plus one AK evidence row. Everything else stays
|
|
12
|
+
* fail-closed exactly as in Phase 0/1.
|
|
13
|
+
*/
|
|
14
|
+
export const DISPATCH_PHASE = "fleet_phase_2" as const;
|
|
15
|
+
|
|
16
|
+
export const DISPATCH_RECEIPT_SCHEMA = "pi-agent-registry.dispatch-receipt/1" as const;
|
|
17
|
+
|
|
18
|
+
export const DISPATCH_EVIDENCE_CHECK_TYPE = "standing-agent-dispatch" as const;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Tool allowlist for Phase-2 read-only dispatch. `bash` is admitted only as
|
|
22
|
+
* the fleet's established read-only exploration instrument (profile parity
|
|
23
|
+
* with ASC explorer/reviewer/tester/researcher); mutation tools are excluded
|
|
24
|
+
* and the child task contract plus parent-side observation bound the posture.
|
|
25
|
+
*/
|
|
26
|
+
export const READ_ONLY_DISPATCH_TOOLS: readonly string[] = ["read", "bash"];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Provenance marker injected into every dispatched standing-agent child.
|
|
30
|
+
* `dispatch_agent` refuses to run inside a session that already carries it,
|
|
31
|
+
* keeping standing-agent dispatch one level deep.
|
|
32
|
+
*/
|
|
33
|
+
export const DISPATCH_CHILD_PROVENANCE_ENV = "PI_PROVENANCE_STANDING_AGENT_DISPATCH" as const;
|
|
34
|
+
|
|
35
|
+
/** Default bounded child execution timeout (seconds) for Phase-2 dispatch. */
|
|
36
|
+
export const DISPATCH_EXECUTION_TIMEOUT_SECONDS = 900;
|
|
37
|
+
|
|
38
|
+
/** Default bounded child startup timeout (seconds) for Phase-2 dispatch. */
|
|
39
|
+
export const DISPATCH_STARTUP_TIMEOUT_SECONDS = 120;
|
|
40
|
+
|
|
41
|
+
/** Bounded retry posture: a failed attempt never burns the pair, but at most
|
|
42
|
+
* this many receipts may exist per (agent, exact task) pair, and only ONE may
|
|
43
|
+
* be settled. */
|
|
44
|
+
export const MAX_DISPATCH_ATTEMPTS_PER_PAIR = 3;
|
|
45
|
+
|
|
46
|
+
export type DispatchFailureReason =
|
|
47
|
+
| "invalid_request"
|
|
48
|
+
| "recursive_dispatch"
|
|
49
|
+
| "unknown_agent"
|
|
50
|
+
| "agent_not_read_only"
|
|
51
|
+
| "agent_repo_dirty"
|
|
52
|
+
| "agent_repo_drift"
|
|
53
|
+
| "agent_resolution_failed"
|
|
54
|
+
| "dispatch_already_recorded"
|
|
55
|
+
| "dispatch_attempts_exhausted"
|
|
56
|
+
| "ak_unavailable"
|
|
57
|
+
| "asc_execution_unavailable"
|
|
58
|
+
| "task_not_found"
|
|
59
|
+
| "task_repo_mismatch"
|
|
60
|
+
| "task_not_claimed"
|
|
61
|
+
| "task_lease_expired"
|
|
62
|
+
| "parent_repo_unobservable"
|
|
63
|
+
| "dispatch_failed"
|
|
64
|
+
| "read_only_violation_observed"
|
|
65
|
+
| "receipt_write_failed"
|
|
66
|
+
| "evidence_record_failed";
|
|
67
|
+
|
|
68
|
+
export interface AkTaskSnapshot {
|
|
69
|
+
id: number;
|
|
70
|
+
repo: string;
|
|
71
|
+
title: string;
|
|
72
|
+
status: string;
|
|
73
|
+
claimed_by: string | null;
|
|
74
|
+
lease_expires_at: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DispatchAgentRequest {
|
|
78
|
+
/** Registered standing-agent name (agent.json `name`). */
|
|
79
|
+
agent: string;
|
|
80
|
+
/** Exact AK task id that authorizes this one read-only dispatch. */
|
|
81
|
+
task: number;
|
|
82
|
+
/** Bounded read-only objective for the dispatched agent. */
|
|
83
|
+
objective: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface DispatchEffectFact {
|
|
87
|
+
spawnAttempted: boolean;
|
|
88
|
+
effectDisposition: "confirmed_no_effects" | "settled" | "effect_indeterminate";
|
|
89
|
+
}
|