@aiwg/cli 2026.8.0 → 2026.8.2
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/install.js +42 -4
- package/dist/src/cli/handlers/marketplace.js +375 -122
- 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/marketplace/exchange.js +602 -0
- package/dist/src/marketplace/provenance-types.js +19 -0
- package/dist/src/marketplace/provenance.js +834 -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/packages/adapters/git.js +79 -29
- package/dist/src/packages/package-discovery.js +81 -0
- package/dist/src/packages/package-registry.js +2 -0
- package/dist/src/packages/registry.js +119 -20
- 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,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
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fleet Mission Conductor — management-plane orchestration over N executor
|
|
3
|
+
* targets using the neutral fleet-workload/v1 vocabulary.
|
|
4
|
+
*
|
|
5
|
+
* AIWG owns parent fan-out, aggregation, audit, and completion. Execution
|
|
6
|
+
* substrates (Agentic Sandbox first, other adapters later) own target-local
|
|
7
|
+
* lifecycle and report revisioned observations through the RunFleetWorker seam.
|
|
8
|
+
*
|
|
9
|
+
* @implements #1991
|
|
10
|
+
*/
|
|
11
|
+
import { routeMission } from './agent-router.js';
|
|
12
|
+
import { StackAdapterRegistry } from './stack-adapters.js';
|
|
13
|
+
const terminalFailures = new Set(['failed', 'cancelled', 'timed-out']);
|
|
14
|
+
const reviewStates = new Set(['unknown', 'operator-review-required']);
|
|
15
|
+
function lifecycleSatisfied(kind, state, health) {
|
|
16
|
+
switch (kind) {
|
|
17
|
+
case 'one-shot-command': return state === 'succeeded';
|
|
18
|
+
case 'persistent-agent': return state === 'retained' || state === 'detached' || state === 'running';
|
|
19
|
+
case 'daemon': return (state === 'healthy' || state === 'running') && health === 'healthy';
|
|
20
|
+
case 'scheduled-collector': return state === 'scheduled' || state === 'succeeded';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function collapseEvents(events) {
|
|
24
|
+
let latest;
|
|
25
|
+
const stale = [];
|
|
26
|
+
const artifacts = new Map();
|
|
27
|
+
for (const event of events) {
|
|
28
|
+
if (latest && event.revision <= latest.revision) {
|
|
29
|
+
stale.push(event.revision);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
latest = event;
|
|
33
|
+
for (const artifact of event.artifacts ?? [])
|
|
34
|
+
artifacts.set(`${artifact.kind}:${artifact.uri}`, artifact);
|
|
35
|
+
}
|
|
36
|
+
return { latest, stale, artifacts: [...artifacts.values()] };
|
|
37
|
+
}
|
|
38
|
+
function emptyAggregation(policy) {
|
|
39
|
+
return {
|
|
40
|
+
policy,
|
|
41
|
+
successful: 0,
|
|
42
|
+
failed: 0,
|
|
43
|
+
pending: 0,
|
|
44
|
+
reviewRequired: 0,
|
|
45
|
+
evidenceMissing: [],
|
|
46
|
+
completionEligible: false,
|
|
47
|
+
reason: 'children have not been evaluated',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function aggregate(policy, cycles) {
|
|
51
|
+
const successful = cycles.filter((cycle) => cycle.satisfied).length;
|
|
52
|
+
const failed = cycles.filter((cycle) => terminalFailures.has(cycle.observedState)).length;
|
|
53
|
+
const reviewRequired = cycles.filter((cycle) => reviewStates.has(cycle.observedState)).length;
|
|
54
|
+
const pending = cycles.length - successful - failed - reviewRequired;
|
|
55
|
+
const evidenceMissing = cycles.filter((cycle) => !cycle.evidenceComplete).map((cycle) => cycle.cycleId);
|
|
56
|
+
const allEvidence = evidenceMissing.length === 0;
|
|
57
|
+
let policySatisfied = false;
|
|
58
|
+
let reason = '';
|
|
59
|
+
switch (policy.mode) {
|
|
60
|
+
case 'all-pass':
|
|
61
|
+
policySatisfied = cycles.length > 0 && successful === cycles.length;
|
|
62
|
+
reason = `${successful}/${cycles.length} children satisfied all-pass`;
|
|
63
|
+
break;
|
|
64
|
+
case 'quorum':
|
|
65
|
+
policySatisfied = successful >= policy.minimumSuccesses;
|
|
66
|
+
reason = `${successful}/${policy.minimumSuccesses} quorum successes`;
|
|
67
|
+
break;
|
|
68
|
+
case 'best-output':
|
|
69
|
+
policySatisfied = cycles.some((cycle) => cycle.satisfied && Boolean(cycle.output));
|
|
70
|
+
reason = policySatisfied ? 'at least one satisfied child produced output' : 'no satisfied child produced output';
|
|
71
|
+
break;
|
|
72
|
+
case 'fail-fast':
|
|
73
|
+
policySatisfied = failed === 0 && cycles.length > 0 && successful === cycles.length;
|
|
74
|
+
reason = failed > 0 ? 'a child reached a terminal failure' : `${successful}/${cycles.length} children satisfied`;
|
|
75
|
+
break;
|
|
76
|
+
case 'manual-review':
|
|
77
|
+
policySatisfied = false;
|
|
78
|
+
reason = 'manual-review policy requires an audited operator decision';
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (reviewRequired > 0)
|
|
82
|
+
reason = `${reviewRequired} child observation(s) require operator review`;
|
|
83
|
+
if (!allEvidence)
|
|
84
|
+
reason = `required evidence missing for: ${evidenceMissing.join(', ')}`;
|
|
85
|
+
return {
|
|
86
|
+
policy,
|
|
87
|
+
successful,
|
|
88
|
+
failed,
|
|
89
|
+
pending,
|
|
90
|
+
reviewRequired,
|
|
91
|
+
evidenceMissing,
|
|
92
|
+
completionEligible: policySatisfied && allEvidence && reviewRequired === 0,
|
|
93
|
+
reason,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function cloneLedger(ledger) {
|
|
97
|
+
return structuredClone(ledger);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Runs every newly admitted child concurrently. Target allocation happens
|
|
101
|
+
* before execution, so one slow child cannot serialize unrelated work.
|
|
102
|
+
*/
|
|
103
|
+
export class FleetMissionConductor {
|
|
104
|
+
adapters;
|
|
105
|
+
runWorker;
|
|
106
|
+
scoreOutput;
|
|
107
|
+
ledgerStore;
|
|
108
|
+
/** Prevent concurrent child completions from committing snapshots out of order. */
|
|
109
|
+
persistQueue = Promise.resolve();
|
|
110
|
+
constructor(options) {
|
|
111
|
+
this.adapters = options.adapters ?? new StackAdapterRegistry();
|
|
112
|
+
this.runWorker = options.runWorker;
|
|
113
|
+
this.scoreOutput = options.scoreOutput ?? ((result) => result.output?.length ?? 0);
|
|
114
|
+
this.ledgerStore = options.ledgerStore;
|
|
115
|
+
}
|
|
116
|
+
async conduct(plan, pool, resumeFrom) {
|
|
117
|
+
const priorById = new Map((resumeFrom?.cycles ?? []).map((cycle) => [cycle.cycleId, cycle]));
|
|
118
|
+
const ledger = {
|
|
119
|
+
missionId: plan.missionId,
|
|
120
|
+
goal: plan.goal,
|
|
121
|
+
completionCriterion: plan.completionCriterion,
|
|
122
|
+
activityLog: [`mission ${plan.missionId} fleet start — ${plan.cycles.length} child workload(s)`],
|
|
123
|
+
cycles: [],
|
|
124
|
+
totalCost: 0,
|
|
125
|
+
checkpoint: { completed: [], pending: plan.cycles.map((cycle) => cycle.id), failed: [] },
|
|
126
|
+
runtimesUsed: [],
|
|
127
|
+
parentState: 'running',
|
|
128
|
+
aggregation: emptyAggregation(plan.aggregation),
|
|
129
|
+
};
|
|
130
|
+
const admitted = [];
|
|
131
|
+
const usedExecutors = new Set();
|
|
132
|
+
for (const cycle of plan.cycles) {
|
|
133
|
+
const prior = priorById.get(cycle.id);
|
|
134
|
+
if (prior?.satisfied && prior.evidenceComplete && !reviewStates.has(prior.observedState)) {
|
|
135
|
+
ledger.cycles.push(prior);
|
|
136
|
+
ledger.activityLog.push(`child ${cycle.id} re-adopted from durable completed state`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const adapter = this.adapters.get(cycle.runtime);
|
|
140
|
+
if (!adapter) {
|
|
141
|
+
ledger.cycles.push(this.unroutable(cycle, `no stack adapter registered for runtime '${cycle.runtime}'`));
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
let candidates = pool;
|
|
145
|
+
if (cycle.targetId)
|
|
146
|
+
candidates = candidates.filter((executor) => executor.executorId === cycle.targetId);
|
|
147
|
+
if (plan.requireDistinctTargets !== false) {
|
|
148
|
+
candidates = candidates.filter((executor) => !usedExecutors.has(executor.executorId));
|
|
149
|
+
}
|
|
150
|
+
const routing = routeMission(candidates, {
|
|
151
|
+
capabilities: [adapter.runtimeCapability, ...(cycle.requiredCapabilities ?? [])],
|
|
152
|
+
}, cycle.longRunning ?? false);
|
|
153
|
+
if (!routing.selected) {
|
|
154
|
+
ledger.cycles.push(this.unroutable(cycle, 'no distinct connected executor satisfies the child requirements'));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const executor = routing.selected.executor;
|
|
158
|
+
usedExecutors.add(executor.executorId);
|
|
159
|
+
const dispatchId = cycle.dispatchId ?? `${plan.missionId}:${cycle.id}:dispatch`;
|
|
160
|
+
const lineage = {
|
|
161
|
+
orchestratorId: plan.orchestratorId,
|
|
162
|
+
missionId: plan.missionId,
|
|
163
|
+
dispatchId,
|
|
164
|
+
idempotencyKey: cycle.idempotencyKey ?? dispatchId,
|
|
165
|
+
parentId: plan.missionId,
|
|
166
|
+
childId: cycle.id,
|
|
167
|
+
targetId: cycle.targetId ?? executor.executorId,
|
|
168
|
+
executorId: executor.executorId,
|
|
169
|
+
runtimeId: cycle.runtime,
|
|
170
|
+
};
|
|
171
|
+
admitted.push({ cycle, executor, invocation: adapter.invoke(cycle.prompt), lineage });
|
|
172
|
+
ledger.activityLog.push(`child ${cycle.id} admitted on target ${lineage.targetId} as ${cycle.workloadKind}`);
|
|
173
|
+
}
|
|
174
|
+
ledger.aggregation = aggregate(plan.aggregation, ledger.cycles);
|
|
175
|
+
await this.persist(ledger);
|
|
176
|
+
await Promise.all(admitted.map(async ({ cycle, executor, invocation, lineage }) => {
|
|
177
|
+
let result;
|
|
178
|
+
try {
|
|
179
|
+
const ran = await this.runWorker(cycle, executor, invocation, lineage);
|
|
180
|
+
const collapsed = collapseEvents(ran.events);
|
|
181
|
+
const latest = collapsed.latest;
|
|
182
|
+
const observedState = latest?.observedState ?? 'unknown';
|
|
183
|
+
const artifacts = collapsed.artifacts;
|
|
184
|
+
const required = cycle.requiredEvidence ?? [];
|
|
185
|
+
const evidenceComplete = required.every((kind) => artifacts.some((artifact) => artifact.kind === kind));
|
|
186
|
+
result = {
|
|
187
|
+
cycleId: cycle.id,
|
|
188
|
+
runtime: cycle.runtime,
|
|
189
|
+
primitive: invocation.primitive,
|
|
190
|
+
executorId: executor.executorId,
|
|
191
|
+
routed: true,
|
|
192
|
+
reason: latest ? `observed revision ${latest.revision}: ${observedState}` : 'runtime returned no lifecycle event',
|
|
193
|
+
output: ran.output,
|
|
194
|
+
cost: ran.cost ?? 0,
|
|
195
|
+
workloadKind: cycle.workloadKind,
|
|
196
|
+
lineage,
|
|
197
|
+
commandId: ran.commandId ?? latest?.commandId,
|
|
198
|
+
taskId: latest?.taskId,
|
|
199
|
+
sessionId: ran.sessionId ?? latest?.sessionId,
|
|
200
|
+
observedState,
|
|
201
|
+
revision: latest?.revision ?? 0,
|
|
202
|
+
lastSeen: latest?.lastSeen,
|
|
203
|
+
health: latest?.health,
|
|
204
|
+
backpressure: latest?.backpressure,
|
|
205
|
+
artifacts,
|
|
206
|
+
staleEventRevisions: collapsed.stale,
|
|
207
|
+
evidenceComplete,
|
|
208
|
+
satisfied: evidenceComplete && lifecycleSatisfied(cycle.workloadKind, observedState, latest?.health),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
result = {
|
|
213
|
+
cycleId: cycle.id,
|
|
214
|
+
runtime: cycle.runtime,
|
|
215
|
+
primitive: invocation.primitive,
|
|
216
|
+
executorId: executor.executorId,
|
|
217
|
+
routed: true,
|
|
218
|
+
reason: `worker error: ${error instanceof Error ? error.message : String(error)}`,
|
|
219
|
+
cost: 0,
|
|
220
|
+
workloadKind: cycle.workloadKind,
|
|
221
|
+
lineage,
|
|
222
|
+
observedState: 'unknown',
|
|
223
|
+
revision: 0,
|
|
224
|
+
artifacts: [],
|
|
225
|
+
staleEventRevisions: [],
|
|
226
|
+
evidenceComplete: (cycle.requiredEvidence ?? []).length === 0,
|
|
227
|
+
satisfied: false,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
ledger.cycles.push(result);
|
|
231
|
+
ledger.activityLog.push(`child ${cycle.id} observed ${result.observedState} at revision ${result.revision}`);
|
|
232
|
+
ledger.aggregation = aggregate(plan.aggregation, ledger.cycles);
|
|
233
|
+
await this.persist(ledger);
|
|
234
|
+
}));
|
|
235
|
+
// Stable plan order makes durable snapshots and UI projection deterministic.
|
|
236
|
+
const order = new Map(plan.cycles.map((cycle, index) => [cycle.id, index]));
|
|
237
|
+
ledger.cycles.sort((a, b) => (order.get(a.cycleId) ?? 0) - (order.get(b.cycleId) ?? 0));
|
|
238
|
+
ledger.totalCost = ledger.cycles.reduce((sum, cycle) => sum + cycle.cost, 0);
|
|
239
|
+
ledger.runtimesUsed = [...new Set(ledger.cycles.filter((cycle) => cycle.routed).map((cycle) => cycle.runtime))];
|
|
240
|
+
ledger.checkpoint = {
|
|
241
|
+
completed: ledger.cycles.filter((cycle) => cycle.satisfied).map((cycle) => cycle.cycleId),
|
|
242
|
+
failed: ledger.cycles.filter((cycle) => terminalFailures.has(cycle.observedState)).map((cycle) => cycle.cycleId),
|
|
243
|
+
pending: ledger.cycles.filter((cycle) => !cycle.satisfied && !terminalFailures.has(cycle.observedState)).map((cycle) => cycle.cycleId),
|
|
244
|
+
};
|
|
245
|
+
let best;
|
|
246
|
+
for (const cycle of ledger.cycles) {
|
|
247
|
+
if (!cycle.output || !cycle.satisfied)
|
|
248
|
+
continue;
|
|
249
|
+
const score = this.scoreOutput(cycle);
|
|
250
|
+
if (!best || score > best.score)
|
|
251
|
+
best = { cycleId: cycle.cycleId, runtime: cycle.runtime, output: cycle.output, score };
|
|
252
|
+
}
|
|
253
|
+
ledger.bestOutput = best;
|
|
254
|
+
ledger.aggregation = aggregate(plan.aggregation, ledger.cycles);
|
|
255
|
+
ledger.parentState = ledger.aggregation.completionEligible
|
|
256
|
+
? 'completed'
|
|
257
|
+
: ledger.aggregation.reviewRequired > 0 || plan.aggregation.mode === 'manual-review'
|
|
258
|
+
? 'operator-review-required'
|
|
259
|
+
: ledger.aggregation.failed > 0
|
|
260
|
+
? 'failed'
|
|
261
|
+
: 'pending';
|
|
262
|
+
ledger.activityLog.push(`mission ${plan.missionId} fleet end — ${ledger.parentState}: ${ledger.aggregation.reason}`);
|
|
263
|
+
await this.persist(ledger);
|
|
264
|
+
return ledger;
|
|
265
|
+
}
|
|
266
|
+
unroutable(cycle, reason) {
|
|
267
|
+
return {
|
|
268
|
+
cycleId: cycle.id,
|
|
269
|
+
runtime: cycle.runtime,
|
|
270
|
+
primitive: '(none)',
|
|
271
|
+
routed: false,
|
|
272
|
+
reason,
|
|
273
|
+
cost: 0,
|
|
274
|
+
workloadKind: cycle.workloadKind,
|
|
275
|
+
observedState: 'operator-review-required',
|
|
276
|
+
revision: 0,
|
|
277
|
+
artifacts: [],
|
|
278
|
+
staleEventRevisions: [],
|
|
279
|
+
evidenceComplete: (cycle.requiredEvidence ?? []).length === 0,
|
|
280
|
+
satisfied: false,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async persist(ledger) {
|
|
284
|
+
if (!this.ledgerStore)
|
|
285
|
+
return;
|
|
286
|
+
const snapshot = cloneLedger(ledger);
|
|
287
|
+
this.persistQueue = this.persistQueue.then(async () => {
|
|
288
|
+
await this.ledgerStore.save(snapshot);
|
|
289
|
+
});
|
|
290
|
+
await this.persistQueue;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=fleet-mission-conductor.js.map
|
|
@@ -9,6 +9,7 @@ export * from './optional-backends.js';
|
|
|
9
9
|
export * from './knowledge-shard.js';
|
|
10
10
|
export * from './candidates.js';
|
|
11
11
|
export * from './promotion.js';
|
|
12
|
+
export * from './output-registration.js';
|
|
12
13
|
export * from './importer.js';
|
|
13
14
|
export * from './import-lease.js';
|
|
14
15
|
export * from './batch-contracts.js';
|