va-agent-protocol 0.1.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 +21 -0
- package/README.md +558 -0
- package/bin/va-orchestrate.mjs +153 -0
- package/dist/adapter/agent-adapter.d.ts +79 -0
- package/dist/adapter/agent-adapter.d.ts.map +1 -0
- package/dist/adapter/agent-adapter.js +36 -0
- package/dist/adapter/agent-adapter.js.map +1 -0
- package/dist/adapter/codex-adapter.d.ts +54 -0
- package/dist/adapter/codex-adapter.d.ts.map +1 -0
- package/dist/adapter/codex-adapter.js +409 -0
- package/dist/adapter/codex-adapter.js.map +1 -0
- package/dist/adapter/va-auto-pilot-adapter.d.ts +51 -0
- package/dist/adapter/va-auto-pilot-adapter.d.ts.map +1 -0
- package/dist/adapter/va-auto-pilot-adapter.js +275 -0
- package/dist/adapter/va-auto-pilot-adapter.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +101 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +452 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/registry.d.ts +39 -0
- package/dist/orchestrator/registry.d.ts.map +1 -0
- package/dist/orchestrator/registry.js +62 -0
- package/dist/orchestrator/registry.js.map +1 -0
- package/dist/orchestrator/scheduler.d.ts +66 -0
- package/dist/orchestrator/scheduler.d.ts.map +1 -0
- package/dist/orchestrator/scheduler.js +155 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/test/orchestrator-enqueue.test.d.ts +2 -0
- package/dist/test/orchestrator-enqueue.test.d.ts.map +1 -0
- package/dist/test/orchestrator-enqueue.test.js +64 -0
- package/dist/test/orchestrator-enqueue.test.js.map +1 -0
- package/dist/test/orchestrator-lifecycle.test.d.ts +2 -0
- package/dist/test/orchestrator-lifecycle.test.d.ts.map +1 -0
- package/dist/test/orchestrator-lifecycle.test.js +227 -0
- package/dist/test/orchestrator-lifecycle.test.js.map +1 -0
- package/dist/test/scheduler.test.d.ts +2 -0
- package/dist/test/scheduler.test.d.ts.map +1 -0
- package/dist/test/scheduler.test.js +140 -0
- package/dist/test/scheduler.test.js.map +1 -0
- package/dist/types/index.d.ts +209 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +45 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +61 -0
- package/schemas/agent-capability.schema.json +99 -0
- package/schemas/evidence.schema.json +201 -0
- package/schemas/lifecycle.schema.json +80 -0
- package/schemas/message.schema.json +181 -0
- package/schemas/task-unit.schema.json +137 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler — match tasks to capable agents, respecting constraints.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Match task requirements to agent capabilities
|
|
6
|
+
* - Respect concurrency limits
|
|
7
|
+
* - Handle dependency ordering
|
|
8
|
+
* - Select the best agent when multiple can handle a task
|
|
9
|
+
*/
|
|
10
|
+
export class Scheduler {
|
|
11
|
+
registry;
|
|
12
|
+
/** Track how many tasks each agent is currently running. */
|
|
13
|
+
activeCounts = new Map();
|
|
14
|
+
constructor(registry) {
|
|
15
|
+
this.registry = registry;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Schedule a single task: find the best available agent.
|
|
19
|
+
*
|
|
20
|
+
* Selection criteria (in order):
|
|
21
|
+
* 1. Must have required capabilities (inferred from task)
|
|
22
|
+
* 2. Must have available concurrency slots
|
|
23
|
+
* 3. Prefer agents with fewer extra capabilities (more specialized)
|
|
24
|
+
*/
|
|
25
|
+
schedule(task) {
|
|
26
|
+
const requiredCapabilities = this.inferCapabilities(task);
|
|
27
|
+
const candidates = this.registry.findByCapabilities(requiredCapabilities);
|
|
28
|
+
if (candidates.length === 0) {
|
|
29
|
+
return {
|
|
30
|
+
type: "rejected",
|
|
31
|
+
rejection: {
|
|
32
|
+
taskId: task.id,
|
|
33
|
+
reason: `No agent found with capabilities: ${requiredCapabilities.join(", ")}`,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Filter by concurrency availability
|
|
38
|
+
const available = candidates.filter((c) => this.hasCapacity(c));
|
|
39
|
+
if (available.length === 0) {
|
|
40
|
+
return {
|
|
41
|
+
type: "rejected",
|
|
42
|
+
rejection: {
|
|
43
|
+
taskId: task.id,
|
|
44
|
+
reason: `All capable agents are at max concurrency.`,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Pick the best available agent
|
|
49
|
+
const selected = available[0];
|
|
50
|
+
return {
|
|
51
|
+
type: "scheduled",
|
|
52
|
+
decision: {
|
|
53
|
+
taskId: task.id,
|
|
54
|
+
agentId: selected.capability.agentId,
|
|
55
|
+
adapter: selected.adapter,
|
|
56
|
+
reason: `Matched capabilities: ${requiredCapabilities.join(", ")}`,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Schedule multiple tasks, respecting dependencies.
|
|
62
|
+
* Returns tasks in executable order (dependencies first).
|
|
63
|
+
*/
|
|
64
|
+
scheduleBatch(tasks) {
|
|
65
|
+
const scheduled = [];
|
|
66
|
+
const rejected = [];
|
|
67
|
+
const deferred = [];
|
|
68
|
+
// Separate tasks with unsatisfied dependencies
|
|
69
|
+
const completedIds = new Set();
|
|
70
|
+
const pending = [...tasks];
|
|
71
|
+
// Simple topological pass: keep scheduling until no progress
|
|
72
|
+
let madeProgress = true;
|
|
73
|
+
while (madeProgress && pending.length > 0) {
|
|
74
|
+
madeProgress = false;
|
|
75
|
+
const remaining = [];
|
|
76
|
+
for (const task of pending) {
|
|
77
|
+
const deps = task.dependsOn ?? [];
|
|
78
|
+
const satisfied = deps.every((dep) => completedIds.has(dep));
|
|
79
|
+
if (!satisfied) {
|
|
80
|
+
remaining.push(task);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const result = this.schedule(task);
|
|
84
|
+
if (result.type === "scheduled") {
|
|
85
|
+
scheduled.push(result.decision);
|
|
86
|
+
completedIds.add(task.id);
|
|
87
|
+
this.acquire(result.decision.agentId);
|
|
88
|
+
madeProgress = true;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
rejected.push(result.rejection);
|
|
92
|
+
madeProgress = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
pending.length = 0;
|
|
96
|
+
pending.push(...remaining);
|
|
97
|
+
}
|
|
98
|
+
// Anything left has unsatisfied dependencies
|
|
99
|
+
deferred.push(...pending);
|
|
100
|
+
return { scheduled, rejected, deferred };
|
|
101
|
+
}
|
|
102
|
+
/** Mark an agent slot as acquired. */
|
|
103
|
+
acquire(agentId) {
|
|
104
|
+
this.activeCounts.set(agentId, (this.activeCounts.get(agentId) ?? 0) + 1);
|
|
105
|
+
}
|
|
106
|
+
/** Release an agent slot. */
|
|
107
|
+
release(agentId) {
|
|
108
|
+
const current = this.activeCounts.get(agentId) ?? 0;
|
|
109
|
+
if (current > 0) {
|
|
110
|
+
this.activeCounts.set(agentId, current - 1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Reset all concurrency tracking. */
|
|
114
|
+
reset() {
|
|
115
|
+
this.activeCounts.clear();
|
|
116
|
+
}
|
|
117
|
+
// ─── Private ─────────────────────────────────────────────────────────────
|
|
118
|
+
hasCapacity(agent) {
|
|
119
|
+
const max = agent.capability.constraints?.maxConcurrent ?? 1;
|
|
120
|
+
const current = this.activeCounts.get(agent.capability.agentId) ?? 0;
|
|
121
|
+
return current < max;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Infer required capabilities from task content.
|
|
125
|
+
* This is a simple heuristic — adapters can override with explicit capability tags.
|
|
126
|
+
*/
|
|
127
|
+
inferCapabilities(task) {
|
|
128
|
+
const caps = [];
|
|
129
|
+
const text = `${task.objective} ${(task.constraints ?? []).join(" ")}`.toLowerCase();
|
|
130
|
+
if (text.includes("implement") ||
|
|
131
|
+
text.includes("build") ||
|
|
132
|
+
text.includes("create") ||
|
|
133
|
+
text.includes("add")) {
|
|
134
|
+
caps.push("code-generation");
|
|
135
|
+
}
|
|
136
|
+
if (text.includes("review") || text.includes("audit")) {
|
|
137
|
+
caps.push("code-review");
|
|
138
|
+
}
|
|
139
|
+
if (text.includes("test")) {
|
|
140
|
+
caps.push("testing");
|
|
141
|
+
}
|
|
142
|
+
if (text.includes("refactor") || text.includes("optimize")) {
|
|
143
|
+
caps.push("refactoring");
|
|
144
|
+
}
|
|
145
|
+
if (text.includes("deploy") || text.includes("release")) {
|
|
146
|
+
caps.push("deployment");
|
|
147
|
+
}
|
|
148
|
+
// Default: if nothing inferred, require code-generation as baseline
|
|
149
|
+
if (caps.length === 0) {
|
|
150
|
+
caps.push("code-generation");
|
|
151
|
+
}
|
|
152
|
+
return caps;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/orchestrator/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator-enqueue.test.d.ts","sourceRoot":"","sources":["../../src/test/orchestrator-enqueue.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { AgentRegistry, Orchestrator, } from "../index.js";
|
|
4
|
+
const mockAdapter = {
|
|
5
|
+
id: "mock-agent",
|
|
6
|
+
capabilities() {
|
|
7
|
+
return {
|
|
8
|
+
agentId: "mock-agent",
|
|
9
|
+
name: "Mock Agent",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
capabilities: ["test"],
|
|
12
|
+
interface: {
|
|
13
|
+
type: "cli",
|
|
14
|
+
command: "mock-agent",
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
async dispatch(task) {
|
|
19
|
+
return {
|
|
20
|
+
correlationId: `corr-${task.id}`,
|
|
21
|
+
accepted: true,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
async poll(correlationId) {
|
|
25
|
+
return {
|
|
26
|
+
correlationId,
|
|
27
|
+
state: "running",
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
async cancel(_correlationId) {
|
|
31
|
+
return;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
function makeTask(id, dependsOn) {
|
|
35
|
+
return {
|
|
36
|
+
id,
|
|
37
|
+
objective: `Task ${id}`,
|
|
38
|
+
acceptanceCriteria: ["should complete"],
|
|
39
|
+
dependsOn,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
describe("Orchestrator.enqueue", () => {
|
|
43
|
+
it("adds tasks to the queue", () => {
|
|
44
|
+
const registry = new AgentRegistry();
|
|
45
|
+
registry.register(mockAdapter);
|
|
46
|
+
const orchestrator = new Orchestrator(registry);
|
|
47
|
+
orchestrator.enqueue(makeTask("task-1"), makeTask("task-2", ["task-1"]));
|
|
48
|
+
assert.equal(orchestrator.pendingCount, 2);
|
|
49
|
+
});
|
|
50
|
+
it("rejects tasks that participate in dependency cycles", () => {
|
|
51
|
+
const failedTaskIds = [];
|
|
52
|
+
const registry = new AgentRegistry();
|
|
53
|
+
registry.register(mockAdapter);
|
|
54
|
+
const orchestrator = new Orchestrator(registry, {
|
|
55
|
+
onFailed(taskId) {
|
|
56
|
+
failedTaskIds.push(taskId);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
orchestrator.enqueue(makeTask("task-a", ["task-b"]), makeTask("task-b", ["task-a"]));
|
|
60
|
+
assert.equal(orchestrator.pendingCount, 0);
|
|
61
|
+
assert.deepEqual(new Set(failedTaskIds), new Set(["task-a", "task-b"]));
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=orchestrator-enqueue.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator-enqueue.test.js","sourceRoot":"","sources":["../../src/test/orchestrator-enqueue.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EACL,aAAa,EACb,YAAY,GAKb,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,GAAiB;IAChC,EAAE,EAAE,YAAY;IAChB,YAAY;QACV,OAAO;YACL,OAAO,EAAE,YAAY;YACrB,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,OAAO;YAChB,YAAY,EAAE,CAAC,MAAM,CAAC;YACtB,SAAS,EAAE;gBACT,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,YAAY;aACtB;SACF,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,IAAc;QAC3B,OAAO;YACL,aAAa,EAAE,QAAQ,IAAI,CAAC,EAAE,EAAE;YAChC,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,aAAqB;QAC9B,OAAO;YACL,aAAa;YACb,KAAK,EAAE,SAAS;SACjB,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,cAAsB;QACjC,OAAO;IACT,CAAC;CACF,CAAC;AAEF,SAAS,QAAQ,CAAC,EAAU,EAAE,SAAoB;IAChD,OAAO;QACL,EAAE;QACF,SAAS,EAAE,QAAQ,EAAE,EAAE;QACvB,kBAAkB,EAAE,CAAC,iBAAiB,CAAC;QACvC,SAAS;KACV,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE/B,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;QAEhD,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAEzE,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,aAAa,GAAa,EAAE,CAAC;QAEnC,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QACrC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE/B,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE;YAC9C,QAAQ,CAAC,MAAM;gBACb,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;SACF,CAAC,CAAC;QAEH,YAAY,CAAC,OAAO,CAClB,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAC9B,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAC/B,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator-lifecycle.test.d.ts","sourceRoot":"","sources":["../../src/test/orchestrator-lifecycle.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it, beforeEach } from "node:test";
|
|
3
|
+
import { AgentRegistry, Orchestrator, noopLogger, } from "../index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Configurable mock adapter that lets tests control poll responses.
|
|
6
|
+
*/
|
|
7
|
+
class MockAdapter {
|
|
8
|
+
caps;
|
|
9
|
+
maxConcurrent;
|
|
10
|
+
id;
|
|
11
|
+
correlationCounter = 0;
|
|
12
|
+
/** Map of correlationId → sequence of PollResults to return. */
|
|
13
|
+
pollResponses = new Map();
|
|
14
|
+
pollIndex = new Map();
|
|
15
|
+
dispatched = [];
|
|
16
|
+
cancelled = [];
|
|
17
|
+
constructor(id, caps = ["code-generation"], maxConcurrent = 2) {
|
|
18
|
+
this.caps = caps;
|
|
19
|
+
this.maxConcurrent = maxConcurrent;
|
|
20
|
+
this.id = id;
|
|
21
|
+
}
|
|
22
|
+
capabilities() {
|
|
23
|
+
return {
|
|
24
|
+
agentId: this.id,
|
|
25
|
+
name: `Mock ${this.id}`,
|
|
26
|
+
version: "1.0.0",
|
|
27
|
+
capabilities: this.caps,
|
|
28
|
+
interface: { type: "cli", command: this.id },
|
|
29
|
+
constraints: { maxConcurrent: this.maxConcurrent },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Pre-configure the poll responses for the next dispatched task. */
|
|
33
|
+
setPollSequence(correlationId, responses) {
|
|
34
|
+
this.pollResponses.set(correlationId, responses);
|
|
35
|
+
this.pollIndex.set(correlationId, 0);
|
|
36
|
+
}
|
|
37
|
+
async dispatch(task) {
|
|
38
|
+
this.dispatched.push(task);
|
|
39
|
+
const correlationId = `corr-${this.id}-${++this.correlationCounter}`;
|
|
40
|
+
return { correlationId, accepted: true };
|
|
41
|
+
}
|
|
42
|
+
async poll(correlationId) {
|
|
43
|
+
const responses = this.pollResponses.get(correlationId);
|
|
44
|
+
if (!responses) {
|
|
45
|
+
return { correlationId, state: "running" };
|
|
46
|
+
}
|
|
47
|
+
const idx = this.pollIndex.get(correlationId) ?? 0;
|
|
48
|
+
const response = responses[Math.min(idx, responses.length - 1)];
|
|
49
|
+
this.pollIndex.set(correlationId, idx + 1);
|
|
50
|
+
return response;
|
|
51
|
+
}
|
|
52
|
+
async cancel(correlationId) {
|
|
53
|
+
this.cancelled.push(correlationId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function makeTask(id, opts = {}) {
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
objective: opts.objective ?? `Task ${id}`,
|
|
60
|
+
acceptanceCriteria: opts.acceptanceCriteria ?? ["should pass"],
|
|
61
|
+
...opts,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
describe("Orchestrator lifecycle", () => {
|
|
65
|
+
let registry;
|
|
66
|
+
let adapter;
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
registry = new AgentRegistry();
|
|
69
|
+
adapter = new MockAdapter("test-agent");
|
|
70
|
+
registry.register(adapter);
|
|
71
|
+
});
|
|
72
|
+
describe("state transitions", () => {
|
|
73
|
+
it("transitions to completed and fires onCompleted callback", async () => {
|
|
74
|
+
const completed = [];
|
|
75
|
+
const orchestrator = new Orchestrator(registry, {
|
|
76
|
+
logger: noopLogger,
|
|
77
|
+
onCompleted: (taskId) => completed.push(taskId),
|
|
78
|
+
});
|
|
79
|
+
const task = makeTask("T1", { objective: "Build something" });
|
|
80
|
+
const correlationId = await orchestrator.dispatchNow(task);
|
|
81
|
+
assert.ok(correlationId);
|
|
82
|
+
// Configure adapter to return completed on next poll
|
|
83
|
+
adapter.setPollSequence(correlationId, [
|
|
84
|
+
{
|
|
85
|
+
correlationId,
|
|
86
|
+
state: "completed",
|
|
87
|
+
evidence: {
|
|
88
|
+
taskId: "T1",
|
|
89
|
+
status: "completed",
|
|
90
|
+
verification: "All gates passed",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
// Manually trigger a poll cycle by starting/stopping quickly
|
|
95
|
+
// Use dispatchNow + direct status check
|
|
96
|
+
const status = orchestrator.status();
|
|
97
|
+
assert.equal(status.length, 1);
|
|
98
|
+
assert.equal(status[0].state, "running");
|
|
99
|
+
await orchestrator.shutdown();
|
|
100
|
+
});
|
|
101
|
+
it("transitions to failed and retries with pitfall context", async () => {
|
|
102
|
+
const failedPermanently = [];
|
|
103
|
+
const orchestrator = new Orchestrator(registry, {
|
|
104
|
+
logger: noopLogger,
|
|
105
|
+
maxRetries: 0, // No retries — fail immediately
|
|
106
|
+
onFailed: (taskId) => failedPermanently.push(taskId),
|
|
107
|
+
});
|
|
108
|
+
const task = makeTask("T1", { objective: "Create a new feature" });
|
|
109
|
+
const correlationId = await orchestrator.dispatchNow(task);
|
|
110
|
+
assert.ok(correlationId);
|
|
111
|
+
// Configure adapter to return failed
|
|
112
|
+
adapter.setPollSequence(correlationId, [
|
|
113
|
+
{
|
|
114
|
+
correlationId,
|
|
115
|
+
state: "failed",
|
|
116
|
+
evidence: {
|
|
117
|
+
taskId: "T1",
|
|
118
|
+
status: "failed",
|
|
119
|
+
failureDetail: {
|
|
120
|
+
failureType: "gate",
|
|
121
|
+
attempted: "typecheck",
|
|
122
|
+
hypothesis: "Missing import statement",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
await orchestrator.shutdown();
|
|
128
|
+
});
|
|
129
|
+
it("handles blocked state and onBlocked callback", async () => {
|
|
130
|
+
const blocked = [];
|
|
131
|
+
const orchestrator = new Orchestrator(registry, {
|
|
132
|
+
logger: noopLogger,
|
|
133
|
+
onBlocked: (taskId, reason) => blocked.push({ taskId, reason }),
|
|
134
|
+
});
|
|
135
|
+
const task = makeTask("T1", { objective: "Implement auth" });
|
|
136
|
+
const correlationId = await orchestrator.dispatchNow(task);
|
|
137
|
+
assert.ok(correlationId);
|
|
138
|
+
// Configure adapter to return blocked
|
|
139
|
+
adapter.setPollSequence(correlationId, [
|
|
140
|
+
{
|
|
141
|
+
correlationId,
|
|
142
|
+
state: "blocked",
|
|
143
|
+
evidence: {
|
|
144
|
+
taskId: "T1",
|
|
145
|
+
status: "blocked",
|
|
146
|
+
blockReason: {
|
|
147
|
+
type: "human-decision",
|
|
148
|
+
description: "Needs architecture review",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
]);
|
|
153
|
+
await orchestrator.shutdown();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe("input resolution", () => {
|
|
157
|
+
it("dispatches tasks with static inputs unchanged", async () => {
|
|
158
|
+
const orchestrator = new Orchestrator(registry, {
|
|
159
|
+
logger: noopLogger,
|
|
160
|
+
});
|
|
161
|
+
const task = makeTask("T1", {
|
|
162
|
+
objective: "Build a component",
|
|
163
|
+
inputs: { framework: "react", version: 18 },
|
|
164
|
+
});
|
|
165
|
+
const correlationId = await orchestrator.dispatchNow(task);
|
|
166
|
+
assert.ok(correlationId);
|
|
167
|
+
// The adapter should receive the task with inputs intact
|
|
168
|
+
assert.equal(adapter.dispatched.length, 1);
|
|
169
|
+
assert.deepEqual(adapter.dispatched[0].inputs, {
|
|
170
|
+
framework: "react",
|
|
171
|
+
version: 18,
|
|
172
|
+
});
|
|
173
|
+
await orchestrator.shutdown();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe("timeout", () => {
|
|
177
|
+
it("respects task timeout during dispatch", async () => {
|
|
178
|
+
const failedTasks = [];
|
|
179
|
+
const orchestrator = new Orchestrator(registry, {
|
|
180
|
+
logger: noopLogger,
|
|
181
|
+
onFailed: (taskId) => failedTasks.push(taskId),
|
|
182
|
+
});
|
|
183
|
+
// Create a task with an already-expired timeout (0ms)
|
|
184
|
+
const task = makeTask("T1", {
|
|
185
|
+
objective: "Build something",
|
|
186
|
+
timeout: 1, // 1ms — will timeout immediately on next poll
|
|
187
|
+
});
|
|
188
|
+
const correlationId = await orchestrator.dispatchNow(task);
|
|
189
|
+
assert.ok(correlationId);
|
|
190
|
+
// Verify the task is active before timeout check
|
|
191
|
+
const status = orchestrator.status();
|
|
192
|
+
assert.equal(status.length, 1);
|
|
193
|
+
await orchestrator.shutdown();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe("cancellation", () => {
|
|
197
|
+
it("cancels an active task", async () => {
|
|
198
|
+
const orchestrator = new Orchestrator(registry, {
|
|
199
|
+
logger: noopLogger,
|
|
200
|
+
});
|
|
201
|
+
const task = makeTask("T1", { objective: "Build a module" });
|
|
202
|
+
const correlationId = await orchestrator.dispatchNow(task);
|
|
203
|
+
assert.ok(correlationId);
|
|
204
|
+
await orchestrator.cancelTask(correlationId);
|
|
205
|
+
// Task should be removed from active
|
|
206
|
+
assert.equal(orchestrator.status().length, 0);
|
|
207
|
+
// Adapter should have received cancel
|
|
208
|
+
assert.ok(adapter.cancelled.includes(correlationId));
|
|
209
|
+
await orchestrator.shutdown();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe("dependency ordering", () => {
|
|
213
|
+
it("enqueues tasks and respects pendingCount", () => {
|
|
214
|
+
const orchestrator = new Orchestrator(registry, {
|
|
215
|
+
logger: noopLogger,
|
|
216
|
+
});
|
|
217
|
+
const t1 = makeTask("T1", { objective: "Build core" });
|
|
218
|
+
const t2 = makeTask("T2", {
|
|
219
|
+
objective: "Add tests",
|
|
220
|
+
dependsOn: ["T1"],
|
|
221
|
+
});
|
|
222
|
+
orchestrator.enqueue(t1, t2);
|
|
223
|
+
assert.equal(orchestrator.pendingCount, 2);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
//# sourceMappingURL=orchestrator-lifecycle.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator-lifecycle.test.js","sourceRoot":"","sources":["../../src/test/orchestrator-lifecycle.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.test.d.ts","sourceRoot":"","sources":["../../src/test/scheduler.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it, beforeEach } from "node:test";
|
|
3
|
+
import { AgentRegistry, Scheduler, } from "../index.js";
|
|
4
|
+
function makeAdapter(id, capabilities, maxConcurrent = 1) {
|
|
5
|
+
return {
|
|
6
|
+
id,
|
|
7
|
+
capabilities() {
|
|
8
|
+
return {
|
|
9
|
+
agentId: id,
|
|
10
|
+
name: `Agent ${id}`,
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
capabilities,
|
|
13
|
+
interface: { type: "cli", command: id },
|
|
14
|
+
constraints: { maxConcurrent },
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
async dispatch(_task) {
|
|
18
|
+
return { correlationId: `corr-${_task.id}`, accepted: true };
|
|
19
|
+
},
|
|
20
|
+
async poll(correlationId) {
|
|
21
|
+
return { correlationId, state: "running" };
|
|
22
|
+
},
|
|
23
|
+
async cancel() { },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function makeTask(id, objective, dependsOn) {
|
|
27
|
+
return {
|
|
28
|
+
id,
|
|
29
|
+
objective,
|
|
30
|
+
acceptanceCriteria: ["should pass"],
|
|
31
|
+
dependsOn,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
describe("Scheduler", () => {
|
|
35
|
+
let registry;
|
|
36
|
+
let scheduler;
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
registry = new AgentRegistry();
|
|
39
|
+
scheduler = new Scheduler(registry);
|
|
40
|
+
});
|
|
41
|
+
describe("capability matching", () => {
|
|
42
|
+
it("matches a task to an agent with the required capability", () => {
|
|
43
|
+
registry.register(makeAdapter("coder", ["code-generation"]));
|
|
44
|
+
const result = scheduler.schedule(makeTask("T1", "Implement a login form"));
|
|
45
|
+
assert.equal(result.type, "scheduled");
|
|
46
|
+
if (result.type === "scheduled") {
|
|
47
|
+
assert.equal(result.decision.agentId, "coder");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
it("rejects a task when no agent has the required capability", () => {
|
|
51
|
+
registry.register(makeAdapter("reviewer", ["code-review"]));
|
|
52
|
+
// "Implement" infers "code-generation" which the reviewer doesn't have
|
|
53
|
+
const result = scheduler.schedule(makeTask("T1", "Implement feature X"));
|
|
54
|
+
assert.equal(result.type, "rejected");
|
|
55
|
+
if (result.type === "rejected") {
|
|
56
|
+
assert.ok(result.rejection.reason.includes("No agent found"));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
it("prefers specialized agents over generalists", () => {
|
|
60
|
+
// Generalist has more capabilities
|
|
61
|
+
registry.register(makeAdapter("generalist", [
|
|
62
|
+
"code-generation",
|
|
63
|
+
"code-review",
|
|
64
|
+
"testing",
|
|
65
|
+
"refactoring",
|
|
66
|
+
]));
|
|
67
|
+
// Specialist has only what's needed
|
|
68
|
+
registry.register(makeAdapter("specialist", ["code-generation"]));
|
|
69
|
+
const result = scheduler.schedule(makeTask("T1", "Build a new API endpoint"));
|
|
70
|
+
assert.equal(result.type, "scheduled");
|
|
71
|
+
if (result.type === "scheduled") {
|
|
72
|
+
assert.equal(result.decision.agentId, "specialist");
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("concurrency limits", () => {
|
|
77
|
+
it("rejects when all capable agents are at max concurrency", () => {
|
|
78
|
+
registry.register(makeAdapter("coder", ["code-generation"], 1));
|
|
79
|
+
scheduler.acquire("coder"); // Fill the single slot
|
|
80
|
+
const result = scheduler.schedule(makeTask("T1", "Create a new module"));
|
|
81
|
+
assert.equal(result.type, "rejected");
|
|
82
|
+
if (result.type === "rejected") {
|
|
83
|
+
assert.ok(result.rejection.reason.includes("max concurrency"));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
it("allows scheduling when concurrency slots are available", () => {
|
|
87
|
+
registry.register(makeAdapter("coder", ["code-generation"], 2));
|
|
88
|
+
scheduler.acquire("coder"); // Fill one of two slots
|
|
89
|
+
const result = scheduler.schedule(makeTask("T1", "Create a new module"));
|
|
90
|
+
assert.equal(result.type, "scheduled");
|
|
91
|
+
});
|
|
92
|
+
it("releases slots correctly", () => {
|
|
93
|
+
registry.register(makeAdapter("coder", ["code-generation"], 1));
|
|
94
|
+
scheduler.acquire("coder");
|
|
95
|
+
// Should be rejected — slot full
|
|
96
|
+
const r1 = scheduler.schedule(makeTask("T1", "Build feature"));
|
|
97
|
+
assert.equal(r1.type, "rejected");
|
|
98
|
+
scheduler.release("coder");
|
|
99
|
+
// Now should succeed
|
|
100
|
+
const r2 = scheduler.schedule(makeTask("T2", "Build another feature"));
|
|
101
|
+
assert.equal(r2.type, "scheduled");
|
|
102
|
+
});
|
|
103
|
+
it("reset clears all concurrency tracking", () => {
|
|
104
|
+
registry.register(makeAdapter("coder", ["code-generation"], 1));
|
|
105
|
+
scheduler.acquire("coder");
|
|
106
|
+
scheduler.reset();
|
|
107
|
+
const result = scheduler.schedule(makeTask("T1", "Add a new component"));
|
|
108
|
+
assert.equal(result.type, "scheduled");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("scheduleBatch", () => {
|
|
112
|
+
it("schedules tasks in dependency order", () => {
|
|
113
|
+
registry.register(makeAdapter("coder", ["code-generation", "testing"], 10));
|
|
114
|
+
const tasks = [
|
|
115
|
+
makeTask("T2", "Add tests for module", ["T1"]),
|
|
116
|
+
makeTask("T1", "Build the module"),
|
|
117
|
+
];
|
|
118
|
+
const { scheduled, rejected, deferred } = scheduler.scheduleBatch(tasks);
|
|
119
|
+
// T1 goes first (no deps), T2 depends on T1
|
|
120
|
+
assert.equal(scheduled.length, 2);
|
|
121
|
+
assert.equal(scheduled[0].taskId, "T1");
|
|
122
|
+
assert.equal(scheduled[1].taskId, "T2");
|
|
123
|
+
assert.equal(rejected.length, 0);
|
|
124
|
+
assert.equal(deferred.length, 0);
|
|
125
|
+
});
|
|
126
|
+
it("defers tasks with unresolvable dependencies", () => {
|
|
127
|
+
registry.register(makeAdapter("coder", ["code-generation"], 10));
|
|
128
|
+
const tasks = [
|
|
129
|
+
makeTask("T1", "Build the module"),
|
|
130
|
+
makeTask("T2", "Add tests", ["T-MISSING"]), // depends on nonexistent
|
|
131
|
+
];
|
|
132
|
+
const { scheduled, deferred } = scheduler.scheduleBatch(tasks);
|
|
133
|
+
assert.equal(scheduled.length, 1);
|
|
134
|
+
assert.equal(scheduled[0].taskId, "T1");
|
|
135
|
+
assert.equal(deferred.length, 1);
|
|
136
|
+
assert.equal(deferred[0].id, "T2");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=scheduler.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.test.js","sourceRoot":"","sources":["../../src/test/scheduler.test.ts"],"names":[],"mappings":"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"}
|