@aiwg/cli 2026.8.0 → 2026.8.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/README.md +33 -0
- package/agentic/code/providers/capability-matrix.yaml +511 -0
- package/agentic/code/providers/model-capabilities.v1.json +120 -0
- package/agentic/code/providers/model-catalog.v1.json +96 -0
- package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
- package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
- package/bin/aiwg.mjs +14 -10
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/cli.js +2 -0
- package/dist/src/artifacts/types.js +4 -0
- package/dist/src/auth/client.js +209 -0
- package/dist/src/auth/config.js +38 -0
- package/dist/src/auth/credential-store.js +141 -0
- package/dist/src/auth/resource-credentials.js +25 -0
- package/dist/src/auth/types.js +2 -0
- package/dist/src/channel/manager.mjs +5 -5
- package/dist/src/cli/handlers/auth.js +125 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/resource-versions.js +2 -0
- package/dist/src/cli/handlers/sessions.js +23 -5
- package/dist/src/cli/handlers/subcommands.js +10 -1
- package/dist/src/cli/handlers/use.js +342 -43
- package/dist/src/config/gitignore.js +1 -0
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/memory/canonical-context.js +342 -0
- package/dist/src/memory/context-pack.js +282 -0
- package/dist/src/memory/index.js +4 -0
- package/dist/src/memory/intake.js +118 -0
- package/dist/src/resources/resolver.js +1 -0
- package/dist/src/resources/web-release.d.ts +3 -1
- package/dist/src/resources/web-release.js +14 -6
- package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
- package/dist/src/serve/fleet-mission-conductor.js +293 -0
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/output-registration.js +338 -0
- package/dist/src/sessions/promotion.js +73 -2
- package/dist/src/sessions/repository.js +2 -1
- package/dist/src/update/notifier.mjs +13 -2
- package/package.json +8 -1
- package/tools/_resolve-impl.mjs +74 -0
- package/tools/agents/deploy-agents.mjs +962 -0
- package/tools/agents/providers/base.mjs +2954 -0
- package/tools/agents/providers/claude.mjs +711 -0
- package/tools/agents/providers/codex.mjs +699 -0
- package/tools/agents/providers/copilot.mjs +659 -0
- package/tools/agents/providers/cursor.mjs +714 -0
- package/tools/agents/providers/factory.mjs +1130 -0
- package/tools/agents/providers/hermes.mjs +663 -0
- package/tools/agents/providers/hook-capabilities.mjs +85 -0
- package/tools/agents/providers/model-role.mjs +56 -0
- package/tools/agents/providers/openclaw-translator.mjs +348 -0
- package/tools/agents/providers/openclaw.mjs +680 -0
- package/tools/agents/providers/opencode.mjs +675 -0
- package/tools/agents/providers/openhuman.mjs +292 -0
- package/tools/agents/providers/warp.mjs +413 -0
- package/tools/agents/providers/windsurf.mjs +748 -0
- package/tools/commands/deploy-prompts-codex.mjs +336 -0
- package/tools/plugin/package-plugins.mjs +1013 -0
- package/tools/skills/deploy-skills-codex.mjs +571 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { basename, dirname, extname, relative, resolve, sep } from 'node:path';
|
|
4
|
+
function sha256(value) {
|
|
5
|
+
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
6
|
+
}
|
|
7
|
+
function safeProjectFile(projectRoot, requested) {
|
|
8
|
+
const root = realpathSync(projectRoot);
|
|
9
|
+
const candidate = realpathSync(resolve(root, requested));
|
|
10
|
+
if (candidate !== root && !candidate.startsWith(`${root}${sep}`)) {
|
|
11
|
+
throw new Error('intake source must resolve inside the project');
|
|
12
|
+
}
|
|
13
|
+
const segments = relative(root, candidate).split(sep);
|
|
14
|
+
if (segments.some(segment => segment === '.env' || segment === '.ssh'
|
|
15
|
+
|| /^(?:credentials?|secrets?|tokens?)(?:\.|$)/i.test(segment))) {
|
|
16
|
+
throw new Error('protected paths cannot be ingested into ordinary project memory');
|
|
17
|
+
}
|
|
18
|
+
return { absolute: candidate, locator: segments.join('/') };
|
|
19
|
+
}
|
|
20
|
+
function kindFor(filePath) {
|
|
21
|
+
const extension = extname(filePath).toLocaleLowerCase();
|
|
22
|
+
if (['.jsonl', '.transcript'].includes(extension))
|
|
23
|
+
return 'session-transcript';
|
|
24
|
+
if (['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg'].includes(extension))
|
|
25
|
+
return 'image';
|
|
26
|
+
if (extension === '.pdf')
|
|
27
|
+
return 'pdf';
|
|
28
|
+
if (['.yaml', '.yml', '.json'].includes(extension))
|
|
29
|
+
return 'structured';
|
|
30
|
+
if (['.md', '.txt', '.html', '.htm'].includes(extension))
|
|
31
|
+
return 'document';
|
|
32
|
+
return 'artifact';
|
|
33
|
+
}
|
|
34
|
+
function assertFuturePathInsideProject(projectRoot, target) {
|
|
35
|
+
let ancestor = target;
|
|
36
|
+
while (!existsSync(ancestor))
|
|
37
|
+
ancestor = dirname(ancestor);
|
|
38
|
+
const actual = realpathSync(ancestor);
|
|
39
|
+
if (actual !== projectRoot && !actual.startsWith(`${projectRoot}${sep}`)) {
|
|
40
|
+
throw new Error('intake storage cannot traverse a link outside the project');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function atomicJson(filePath, value) {
|
|
44
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
45
|
+
const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
46
|
+
writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
47
|
+
renameSync(temporary, filePath);
|
|
48
|
+
}
|
|
49
|
+
export class MemoryIntakeCoordinator {
|
|
50
|
+
projectRoot;
|
|
51
|
+
rawRoot;
|
|
52
|
+
receiptRoot;
|
|
53
|
+
constructor(projectRoot) {
|
|
54
|
+
this.projectRoot = realpathSync(projectRoot);
|
|
55
|
+
this.rawRoot = resolve(this.projectRoot, '.aiwg/wiki/raw');
|
|
56
|
+
this.receiptRoot = resolve(this.projectRoot, '.aiwg/memory/compound-memory/intake-receipts');
|
|
57
|
+
assertFuturePathInsideProject(this.projectRoot, this.rawRoot);
|
|
58
|
+
assertFuturePathInsideProject(this.projectRoot, this.receiptRoot);
|
|
59
|
+
}
|
|
60
|
+
preview(requested) {
|
|
61
|
+
const source = safeProjectFile(this.projectRoot, requested);
|
|
62
|
+
const stat = statSync(source.absolute);
|
|
63
|
+
if (!stat.isFile())
|
|
64
|
+
throw new Error('intake source must be a regular file');
|
|
65
|
+
const bytes = readFileSync(source.absolute);
|
|
66
|
+
const digest = sha256(bytes);
|
|
67
|
+
const kind = kindFor(source.absolute);
|
|
68
|
+
const safeName = basename(source.absolute).replace(/[^a-zA-Z0-9._-]+/g, '-');
|
|
69
|
+
const rawLocator = `.aiwg/wiki/raw/${digest.slice(7, 23)}-${safeName}`;
|
|
70
|
+
const rawPath = resolve(this.projectRoot, rawLocator);
|
|
71
|
+
const duplicate = existsSync(rawPath) && sha256(readFileSync(rawPath)) === digest;
|
|
72
|
+
const identity = {
|
|
73
|
+
source: { locator: source.locator, digest, byteLength: bytes.length, kind },
|
|
74
|
+
rawLocator,
|
|
75
|
+
route: kind === 'session-transcript' ? 'sessions' : 'llm-wiki',
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
schemaVersion: 'aiwg.compound-memory.intake-preview.v1',
|
|
79
|
+
operationId: sha256(JSON.stringify(identity)),
|
|
80
|
+
...identity,
|
|
81
|
+
duplicate,
|
|
82
|
+
confirmationRequired: true,
|
|
83
|
+
mutation: { wouldCopyRaw: !duplicate, wouldPromoteKnowledge: false },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
confirm(requested, operationId) {
|
|
87
|
+
const preview = this.preview(requested);
|
|
88
|
+
const receiptPath = resolve(this.receiptRoot, `${operationId.replace(':', '_')}.json`);
|
|
89
|
+
if (existsSync(receiptPath)) {
|
|
90
|
+
return { ...JSON.parse(readFileSync(receiptPath, 'utf8')), duplicate: true };
|
|
91
|
+
}
|
|
92
|
+
if (preview.operationId !== operationId) {
|
|
93
|
+
throw new Error('intake confirmation requires the exact current preview');
|
|
94
|
+
}
|
|
95
|
+
const source = safeProjectFile(this.projectRoot, requested);
|
|
96
|
+
const rawPath = resolve(this.projectRoot, preview.rawLocator);
|
|
97
|
+
mkdirSync(this.rawRoot, { recursive: true, mode: 0o700 });
|
|
98
|
+
if (!preview.duplicate)
|
|
99
|
+
copyFileSync(source.absolute, rawPath, 1);
|
|
100
|
+
if (sha256(readFileSync(rawPath)) !== preview.source.digest) {
|
|
101
|
+
throw new Error('immutable raw copy digest does not match the source preview');
|
|
102
|
+
}
|
|
103
|
+
const receipt = {
|
|
104
|
+
schemaVersion: 'aiwg.compound-memory.intake-receipt.v1',
|
|
105
|
+
receiptId: sha256(`${operationId}\0${preview.rawLocator}`),
|
|
106
|
+
operationId,
|
|
107
|
+
sourceLocator: preview.source.locator,
|
|
108
|
+
sourceDigest: preview.source.digest,
|
|
109
|
+
rawLocator: preview.rawLocator,
|
|
110
|
+
route: preview.route,
|
|
111
|
+
duplicate: preview.duplicate,
|
|
112
|
+
registeredAt: new Date().toISOString(),
|
|
113
|
+
};
|
|
114
|
+
atomicJson(receiptPath, receipt);
|
|
115
|
+
return receipt;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=intake.js.map
|
|
@@ -117,6 +117,7 @@ export async function resolveAiwgResourceBytes(logicalId, options) {
|
|
|
117
117
|
const bytes = await fetchVerifiedRawResource(webRelease, parsed.rawPath, {
|
|
118
118
|
baseUrl: options.webReleaseOptions?.baseUrl,
|
|
119
119
|
fetcher: options.webReleaseOptions?.fetcher,
|
|
120
|
+
credentialProvider: options.webReleaseOptions?.credentialProvider,
|
|
120
121
|
allowInsecureLoopbackHttp: options.webReleaseOptions?.allowInsecureLoopbackHttp,
|
|
121
122
|
offline: options.offline,
|
|
122
123
|
});
|
|
@@ -45,6 +45,8 @@ export interface WebReleaseOptions {
|
|
|
45
45
|
cacheRoot?: string;
|
|
46
46
|
publicKeyPem?: string | Buffer;
|
|
47
47
|
fetcher?: ResourceFetcher;
|
|
48
|
+
/** Returns a bearer token at request time. Tokens never participate in URLs or cache keys. */
|
|
49
|
+
credentialProvider?: () => Promise<string | null>;
|
|
48
50
|
/** Test/development escape hatch. HTTP remains restricted to loopback. */
|
|
49
51
|
allowInsecureLoopbackHttp?: boolean;
|
|
50
52
|
}
|
|
@@ -72,7 +74,7 @@ export interface VerifiedWebRelease {
|
|
|
72
74
|
channelSequence?: number;
|
|
73
75
|
descriptors: ReadonlyMap<string, VerifiedReleaseDescriptor>;
|
|
74
76
|
}
|
|
75
|
-
export interface VerifiedRawResourceOptions extends Pick<WebReleaseOptions, "baseUrl" | "fetcher" | "allowInsecureLoopbackHttp"> {
|
|
77
|
+
export interface VerifiedRawResourceOptions extends Pick<WebReleaseOptions, "baseUrl" | "fetcher" | "credentialProvider" | "allowInsecureLoopbackHttp"> {
|
|
76
78
|
offline?: boolean;
|
|
77
79
|
}
|
|
78
80
|
export declare function verifySignedResourceBytes(bytes: Uint8Array, signatureBytes: Uint8Array, publicKeyPem?: string | Buffer, label?: string): string;
|
|
@@ -297,7 +297,7 @@ function resourceUrl(base, relativePath) {
|
|
|
297
297
|
url.pathname = `${prefix}/${relativePath}`;
|
|
298
298
|
return url.toString();
|
|
299
299
|
}
|
|
300
|
-
async function fetchBytes(fetcher, url, label, maxBytes) {
|
|
300
|
+
async function fetchBytes(fetcher, url, label, maxBytes, bearerToken) {
|
|
301
301
|
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
|
|
302
302
|
throw new Error(`${label} has an invalid network size limit`);
|
|
303
303
|
const controller = new AbortController();
|
|
@@ -315,7 +315,10 @@ async function fetchBytes(fetcher, url, label, maxBytes) {
|
|
|
315
315
|
const response = await Promise.race([
|
|
316
316
|
fetcher(url, {
|
|
317
317
|
redirect: "error",
|
|
318
|
-
headers: {
|
|
318
|
+
headers: {
|
|
319
|
+
"accept-encoding": "identity",
|
|
320
|
+
...(bearerToken ? { authorization: `Bearer ${bearerToken}` } : {}),
|
|
321
|
+
},
|
|
319
322
|
signal: controller.signal,
|
|
320
323
|
}),
|
|
321
324
|
timeoutFailure,
|
|
@@ -904,16 +907,21 @@ export async function resolveWebRelease(options = {}) {
|
|
|
904
907
|
const base = normalizeBaseUrl(options.baseUrl ?? DEFAULT_RESOURCE_BASE_URL, options.allowInsecureLoopbackHttp === true);
|
|
905
908
|
// Validate injected trust material before reading cache or making requests.
|
|
906
909
|
loadTrustRoot(publicKeyPem);
|
|
910
|
+
const bearerToken = options.offline ? null : await options.credentialProvider?.() ?? null;
|
|
911
|
+
const authorize = async (input, init = {}) => {
|
|
912
|
+
const headers = { ...(init.headers || {}), ...(bearerToken ? { authorization: `Bearer ${bearerToken}` } : {}) };
|
|
913
|
+
return (options.fetcher ?? globalThis.fetch)(input, { ...init, headers });
|
|
914
|
+
};
|
|
907
915
|
if (selector.kind === "exact") {
|
|
908
916
|
if (options.offline)
|
|
909
917
|
return resolveOfflineExact(cacheRoot, selector, selector.value, publicKeyPem, base);
|
|
910
|
-
const fetcher =
|
|
918
|
+
const fetcher = authorize;
|
|
911
919
|
if (!fetcher)
|
|
912
920
|
throw new Error("No fetch implementation is available for AIWG web resources");
|
|
913
921
|
return fetchAndCacheRelease(base, fetcher, cacheRoot, selector, selector.value, publicKeyPem);
|
|
914
922
|
}
|
|
915
923
|
if (selector.kind === "range" || selector.kind === "digest") {
|
|
916
|
-
const fetcher =
|
|
924
|
+
const fetcher = authorize;
|
|
917
925
|
const index = await resolveVersionIndex(base, fetcher, cacheRoot, publicKeyPem, options.offline);
|
|
918
926
|
const selected = selectVersionFromIndex(index, selector);
|
|
919
927
|
if (options.offline) {
|
|
@@ -929,7 +937,7 @@ export async function resolveWebRelease(options = {}) {
|
|
|
929
937
|
throw new Error(`AIWG resource channel ${selector.value} is not cached; offline mode cannot fetch it`);
|
|
930
938
|
return resolveOfflineExact(cacheRoot, selector, cached.manifest.version, publicKeyPem, base, cached.manifest.releaseManifestSha256, cached.manifest.sequence);
|
|
931
939
|
}
|
|
932
|
-
const fetcher =
|
|
940
|
+
const fetcher = authorize;
|
|
933
941
|
if (!fetcher)
|
|
934
942
|
throw new Error("No fetch implementation is available for AIWG web resources");
|
|
935
943
|
const channelPrefix = `resources/channels/${selector.value}`;
|
|
@@ -989,7 +997,7 @@ export async function fetchVerifiedRawResource(release, resourcePath, options =
|
|
|
989
997
|
const fetcher = options.fetcher ?? globalThis.fetch;
|
|
990
998
|
if (!fetcher)
|
|
991
999
|
throw new Error("No fetch implementation is available for AIWG web resources");
|
|
992
|
-
const bytes = await fetchBytes(fetcher, resourceUrl(base, `resources/${release.version}/${resourcePath}`), `raw resource ${resourcePath}`, Math.min(descriptor.size, MAX_RAW_RESOURCE_BYTES));
|
|
1000
|
+
const bytes = await fetchBytes(fetcher, resourceUrl(base, `resources/${release.version}/${resourcePath}`), `raw resource ${resourcePath}`, Math.min(descriptor.size, MAX_RAW_RESOURCE_BYTES), await options.credentialProvider?.() ?? null);
|
|
993
1001
|
verifyDescriptor(bytes, descriptor);
|
|
994
1002
|
const stagingRoot = path.join(release.cacheDir, ".raw-staging");
|
|
995
1003
|
fs.mkdirSync(stagingRoot, { recursive: true });
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIWG management-plane adapter for Agentic Sandbox's neutral fleet API.
|
|
3
|
+
* The adapter owns no runtime state: it dispatches contract records and
|
|
4
|
+
* converts durable Sandbox observations into FleetMissionConductor events.
|
|
5
|
+
*
|
|
6
|
+
* @implements #1991
|
|
7
|
+
*/
|
|
8
|
+
import { routeDispatch } from './dispatch-router.js';
|
|
9
|
+
const terminal = new Set([
|
|
10
|
+
'retained', 'healthy', 'scheduled', 'succeeded', 'failed', 'cancelled', 'timed-out',
|
|
11
|
+
'unknown', 'operator-review-required',
|
|
12
|
+
]);
|
|
13
|
+
export class AgenticSandboxFleetClient {
|
|
14
|
+
baseUrl;
|
|
15
|
+
token;
|
|
16
|
+
fetchImpl;
|
|
17
|
+
executorFetch;
|
|
18
|
+
pollIntervalMs;
|
|
19
|
+
maxPolls;
|
|
20
|
+
defaultPolicy;
|
|
21
|
+
defaultBudgets;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
24
|
+
this.token = options.token;
|
|
25
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
26
|
+
this.executorFetch = options.executorFetch ?? globalThis.fetch.bind(globalThis);
|
|
27
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
28
|
+
this.maxPolls = options.maxPolls ?? 120;
|
|
29
|
+
this.defaultPolicy = options.defaultPolicy ?? { trustTier: 'T2', isolationKind: 'vm' };
|
|
30
|
+
this.defaultBudgets = options.defaultBudgets ?? { maxAttempts: 1, timeoutSeconds: 3600 };
|
|
31
|
+
}
|
|
32
|
+
/** Directly assignable to FleetMissionConductor's `runWorker` option. */
|
|
33
|
+
runWorker = async (cycle, executor, invocation, lineage) => {
|
|
34
|
+
const record = this.workloadRecord(cycle, executor, lineage);
|
|
35
|
+
const admitted = await this.request('/api/v2/fleet/workloads', { method: 'POST', body: JSON.stringify(record) });
|
|
36
|
+
const events = [this.toEvent(admitted.workload)];
|
|
37
|
+
if (!admitted.workload.lineage.task_id) {
|
|
38
|
+
const dispatched = await routeDispatch(executor, {
|
|
39
|
+
mission_id: lineage.dispatchId,
|
|
40
|
+
objective: cycle.prompt,
|
|
41
|
+
long_running: cycle.longRunning ?? cycle.workloadKind !== 'one-shot-command',
|
|
42
|
+
fleet_workload_kind: cycle.workloadKind,
|
|
43
|
+
fleet_child_id: lineage.childId,
|
|
44
|
+
native_primitive: invocation.primitive,
|
|
45
|
+
...(executor.a2aInstanceId ? { a2a_instance_id: executor.a2aInstanceId } : {}),
|
|
46
|
+
}, { fetch: this.executorFetch });
|
|
47
|
+
if (!dispatched.task?.id) {
|
|
48
|
+
throw new Error(`Agentic Sandbox dispatch for '${lineage.childId}' returned no durable task identity`);
|
|
49
|
+
}
|
|
50
|
+
const bound = await this.bindDispatchedTask(cycle, lineage.childId, admitted.workload.status, dispatched.task.id, this.taskObservedState(cycle, dispatched.task.status.state));
|
|
51
|
+
events.push(this.toEvent(bound));
|
|
52
|
+
}
|
|
53
|
+
for (let attempt = 0; attempt < this.maxPolls && !this.settled(cycle, events.at(-1)); attempt += 1) {
|
|
54
|
+
if (this.pollIntervalMs > 0) {
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
|
|
56
|
+
}
|
|
57
|
+
const next = await this.request(`/api/v2/fleet/workloads/${encodeURIComponent(lineage.childId)}`);
|
|
58
|
+
const event = this.toEvent(next);
|
|
59
|
+
if (event.revision > events.at(-1).revision)
|
|
60
|
+
events.push(event);
|
|
61
|
+
}
|
|
62
|
+
const latest = events.at(-1);
|
|
63
|
+
return {
|
|
64
|
+
output: latest.artifacts?.find((artifact) => artifact.kind === 'result')?.uri,
|
|
65
|
+
commandId: latest.commandId,
|
|
66
|
+
sessionId: latest.sessionId,
|
|
67
|
+
events,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
async bindDispatchedTask(cycle, childId, current, taskId, observedState) {
|
|
71
|
+
const status = {
|
|
72
|
+
observed_state: observedState,
|
|
73
|
+
revision: current.revision + 1,
|
|
74
|
+
last_seen: new Date().toISOString(),
|
|
75
|
+
artifacts: current.artifacts ?? [],
|
|
76
|
+
...(cycle.workloadKind === 'daemon' ? { health: observedState === 'healthy' ? 'healthy' : 'unknown' } : {}),
|
|
77
|
+
...(observedState === 'blocked' ? { backpressure: { reason: 'approval', retryable: false } } : {}),
|
|
78
|
+
};
|
|
79
|
+
return this.request(`/api/v2/fleet/workloads/${encodeURIComponent(childId)}/observations`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
expected_revision: current.revision,
|
|
83
|
+
runtime_identity: { task_id: taskId },
|
|
84
|
+
status,
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
taskObservedState(cycle, taskState) {
|
|
89
|
+
switch (taskState) {
|
|
90
|
+
case 'submitted': return 'starting';
|
|
91
|
+
case 'working': return cycle.workloadKind === 'daemon' ? 'healthy' : 'running';
|
|
92
|
+
case 'input-required': return 'blocked';
|
|
93
|
+
case 'completed':
|
|
94
|
+
if (cycle.workloadKind === 'persistent-agent')
|
|
95
|
+
return 'retained';
|
|
96
|
+
if (cycle.workloadKind === 'daemon')
|
|
97
|
+
return 'healthy';
|
|
98
|
+
if (cycle.workloadKind === 'scheduled-collector')
|
|
99
|
+
return 'scheduled';
|
|
100
|
+
return 'succeeded';
|
|
101
|
+
case 'failed': return 'failed';
|
|
102
|
+
case 'canceled':
|
|
103
|
+
case 'cancelled': return 'cancelled';
|
|
104
|
+
case 'rejected': return 'failed';
|
|
105
|
+
default: return 'unknown';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async inventory() {
|
|
109
|
+
return this.request('/api/v2/fleet/workloads');
|
|
110
|
+
}
|
|
111
|
+
async reconcile(beforeRevision, childIds) {
|
|
112
|
+
return this.request('/api/v2/fleet/reconcile', {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
body: JSON.stringify({ before_revision: beforeRevision, child_ids: childIds }),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
workloadRecord(cycle, executor, lineage) {
|
|
118
|
+
if (cycle.workloadKind === 'scheduled-collector' && !cycle.schedule) {
|
|
119
|
+
throw new Error(`scheduled collector '${cycle.id}' requires a schedule`);
|
|
120
|
+
}
|
|
121
|
+
const policy = cycle.policy ?? this.defaultPolicy;
|
|
122
|
+
const budgets = cycle.budgets ?? this.defaultBudgets;
|
|
123
|
+
const spec = {
|
|
124
|
+
desired_state: 'running',
|
|
125
|
+
capabilities: (cycle.requiredCapabilities ?? []).map((name) => ({ name, status: 'supported' })),
|
|
126
|
+
policy: {
|
|
127
|
+
trust_tier: policy.trustTier,
|
|
128
|
+
isolation_kind: policy.isolationKind,
|
|
129
|
+
...(policy.credentialPolicyRef ? { credential_policy_ref: policy.credentialPolicyRef } : {}),
|
|
130
|
+
...(policy.networkPolicyRef ? { network_policy_ref: policy.networkPolicyRef } : {}),
|
|
131
|
+
},
|
|
132
|
+
budgets: {
|
|
133
|
+
max_attempts: budgets.maxAttempts,
|
|
134
|
+
timeout_seconds: budgets.timeoutSeconds,
|
|
135
|
+
...(budgets.maxCostUsd === undefined ? {} : { max_cost_usd: budgets.maxCostUsd }),
|
|
136
|
+
},
|
|
137
|
+
...(cycle.schedule ? { schedule: cycle.schedule } : {}),
|
|
138
|
+
orchestrator_metadata: { executor_spec_version: executor.specVersion },
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
document_type: 'workload',
|
|
142
|
+
api_version: 'agentic-orchestration/v1',
|
|
143
|
+
kind: cycle.workloadKind,
|
|
144
|
+
lineage: {
|
|
145
|
+
orchestrator_id: lineage.orchestratorId,
|
|
146
|
+
mission_id: lineage.missionId,
|
|
147
|
+
dispatch_id: lineage.dispatchId,
|
|
148
|
+
idempotency_key: lineage.idempotencyKey,
|
|
149
|
+
parent_id: lineage.parentId,
|
|
150
|
+
child_id: lineage.childId,
|
|
151
|
+
target_id: lineage.targetId,
|
|
152
|
+
executor_id: lineage.executorId,
|
|
153
|
+
runtime_id: lineage.runtimeId,
|
|
154
|
+
session_id: null,
|
|
155
|
+
task_id: null,
|
|
156
|
+
command_id: null,
|
|
157
|
+
},
|
|
158
|
+
spec,
|
|
159
|
+
status: {
|
|
160
|
+
observed_state: 'pending',
|
|
161
|
+
revision: 0,
|
|
162
|
+
last_seen: new Date().toISOString(),
|
|
163
|
+
...(cycle.workloadKind === 'daemon' ? { health: 'unknown' } : {}),
|
|
164
|
+
artifacts: [],
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
settled(cycle, event) {
|
|
169
|
+
if (event.observedState === 'running' && cycle.workloadKind === 'persistent-agent')
|
|
170
|
+
return true;
|
|
171
|
+
if (event.observedState === 'running' && cycle.workloadKind === 'daemon' && event.health === 'healthy')
|
|
172
|
+
return true;
|
|
173
|
+
if (event.observedState === 'blocked')
|
|
174
|
+
return event.backpressure?.retryable !== true;
|
|
175
|
+
return terminal.has(event.observedState);
|
|
176
|
+
}
|
|
177
|
+
toEvent(record) {
|
|
178
|
+
const { status, lineage } = record;
|
|
179
|
+
return {
|
|
180
|
+
revision: status.revision,
|
|
181
|
+
observedState: status.observed_state,
|
|
182
|
+
lastSeen: status.last_seen,
|
|
183
|
+
sessionId: lineage.session_id ?? undefined,
|
|
184
|
+
taskId: lineage.task_id ?? undefined,
|
|
185
|
+
commandId: lineage.command_id ?? undefined,
|
|
186
|
+
health: status.health,
|
|
187
|
+
backpressure: status.backpressure ? {
|
|
188
|
+
reason: status.backpressure.reason,
|
|
189
|
+
retryable: status.backpressure.retryable,
|
|
190
|
+
retryAfter: status.backpressure.retry_after,
|
|
191
|
+
} : undefined,
|
|
192
|
+
artifacts: status.artifacts,
|
|
193
|
+
errorCode: status.error_code,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async request(path, init = {}) {
|
|
197
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
198
|
+
...init,
|
|
199
|
+
headers: {
|
|
200
|
+
accept: 'application/json',
|
|
201
|
+
authorization: `Bearer ${this.token}`,
|
|
202
|
+
...(init.body ? { 'content-type': 'application/json' } : {}),
|
|
203
|
+
...init.headers,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
const body = await response.text();
|
|
208
|
+
throw new Error(`Agentic Sandbox fleet API ${response.status}: ${body.slice(0, 512)}`);
|
|
209
|
+
}
|
|
210
|
+
return response.json();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=agentic-sandbox-fleet-client.js.map
|