@markjaquith/agency 2.67.0 → 2.69.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/README.md +61 -33
- package/cli-main.ts +2 -2
- package/fixtures/protocol/orchestration-recipes.json +2 -2
- package/package.json +1 -1
- package/schemas/agency-graph-v1.schema.json +2 -2
- package/src/cli-parser.test.ts +7 -7
- package/src/cli-parser.ts +11 -11
- package/src/cli.test.ts +11 -9
- package/src/commands/claim.ts +6 -6
- package/src/commands/doctor.test.ts +3 -3
- package/src/commands/init.test.ts +6 -0
- package/src/commands/integration.test.ts +20 -2
- package/src/commands/integration.ts +6 -4
- package/src/commands/work.test.ts +74 -22
- package/src/commands/work.ts +32 -38
- package/src/protocol.test.ts +2 -2
- package/src/services/ArchiveBulkService.test.ts +2 -2
- package/src/services/ClaimService.test.ts +3 -3
- package/src/services/ClaimService.ts +4 -4
- package/src/services/DoctorService.ts +24 -24
- package/src/services/IntegrationService.test.ts +158 -2
- package/src/services/IntegrationService.ts +51 -1
- package/src/services/ReadinessService.test.ts +1 -1
- package/src/services/ReviewService.test.ts +2 -2
- package/src/services/SyncService.test.ts +2 -2
- package/src/services/VcsMigrationService.test.ts +1 -1
- package/src/services/WorkbaseService.test.ts +33 -2
- package/src/services/WorkbaseService.ts +39 -3
- package/src/workbase/AGENTS.md +7 -7
- package/src/workbase/{runner-command.test.ts → agent-command.test.ts} +33 -23
- package/src/workbase/{runner-command.ts → agent-command.ts} +33 -29
- package/src/workbase/kickoff-contract.ts +4 -4
- package/src/workbase/opencode-file.ts +1 -1
- package/src/workbase/pi-extension-file.ts +158 -0
- package/src/workbase/schemas.test.ts +5 -5
- package/src/workbase/schemas.ts +8 -2
|
@@ -18,6 +18,7 @@ interface IntegrationResult {
|
|
|
18
18
|
| "opencode-plugin"
|
|
19
19
|
| "opencode-tui"
|
|
20
20
|
| "opencode-tui-plugin"
|
|
21
|
+
| "pi-extension"
|
|
21
22
|
readonly path: string
|
|
22
23
|
readonly state: string
|
|
23
24
|
readonly diagnostic: string
|
|
@@ -32,6 +33,7 @@ const integrationNames = {
|
|
|
32
33
|
"opencode-plugin": "OpenCode workbase plugin",
|
|
33
34
|
"opencode-tui": "OpenCode TUI config",
|
|
34
35
|
"opencode-tui-plugin": "OpenCode /agency-debug",
|
|
36
|
+
"pi-extension": "Pi workbase extension",
|
|
35
37
|
} as const
|
|
36
38
|
|
|
37
39
|
const logHumanResult = (
|
|
@@ -89,10 +91,10 @@ export const help = `
|
|
|
89
91
|
Usage: agency integration <subcommand>
|
|
90
92
|
|
|
91
93
|
Inspect or explicitly synchronize managed agent integration files. OpenCode
|
|
92
|
-
launches load the managed instructions and project config at runtime.
|
|
93
|
-
|
|
94
|
-
the /agency-debug TUI diagnostic reports integration state
|
|
95
|
-
Agency write authority.
|
|
94
|
+
launches load the managed instructions and project config at runtime. Managed
|
|
95
|
+
OpenCode and Pi extensions provide whole-workbase context and writable-checkout
|
|
96
|
+
skills, and the /agency-debug TUI diagnostic reports OpenCode integration state
|
|
97
|
+
without changing Agency write authority.
|
|
96
98
|
|
|
97
99
|
Subcommands:
|
|
98
100
|
status Report file state, access diagnostics, and safe remediation
|
|
@@ -54,7 +54,8 @@ interface HarnessOptions {
|
|
|
54
54
|
readonly materializeError?: Error
|
|
55
55
|
readonly available?: Readonly<Record<string, boolean>>
|
|
56
56
|
readonly chooserCommand?: readonly string[]
|
|
57
|
-
readonly
|
|
57
|
+
readonly agent?: "opencode2" | "opencode" | "pi" | "claude"
|
|
58
|
+
readonly agents?: Record<
|
|
58
59
|
string,
|
|
59
60
|
{
|
|
60
61
|
command: readonly [string, ...string[]]
|
|
@@ -121,6 +122,7 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
121
122
|
},
|
|
122
123
|
}
|
|
123
124
|
const workbase = {
|
|
125
|
+
loadGlobalConfig: () => Effect.succeed({ agent: options.agent }),
|
|
124
126
|
discover: (path: string) =>
|
|
125
127
|
options.outsideWorkbase && path === "/outside"
|
|
126
128
|
? Effect.fail({
|
|
@@ -136,7 +138,7 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
136
138
|
config: {
|
|
137
139
|
version: 2 as const,
|
|
138
140
|
chooserCommand: options.chooserCommand,
|
|
139
|
-
|
|
141
|
+
agents: options.agents,
|
|
140
142
|
},
|
|
141
143
|
}),
|
|
142
144
|
repositoryAliases: () => Effect.succeed(["agency"]),
|
|
@@ -307,8 +309,11 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
307
309
|
commandOptions: Parameters<typeof work>[0],
|
|
308
310
|
pick: PickWorkTarget = defaultPick,
|
|
309
311
|
pickBase: PickWorkbase = defaultPickWorkbase,
|
|
310
|
-
|
|
311
|
-
|
|
312
|
+
preserveSession = false,
|
|
313
|
+
) => {
|
|
314
|
+
const agencySessionId = process.env.AGENCY_SESSION_ID
|
|
315
|
+
if (!preserveSession) delete process.env.AGENCY_SESSION_ID
|
|
316
|
+
return Effect.runPromise(
|
|
312
317
|
work(commandOptions, launch, pick, progress, pickBase).pipe(
|
|
313
318
|
Effect.provideService(WorktreeService, worktrees as never),
|
|
314
319
|
Effect.provideService(FileSystemService, fs as never),
|
|
@@ -319,7 +324,12 @@ const createHarness = (options: HarnessOptions = {}) => {
|
|
|
319
324
|
Effect.provideService(ReadinessService, readiness as never),
|
|
320
325
|
Effect.provideService(IntegrationService, integrations as never),
|
|
321
326
|
) as Effect.Effect<void, unknown, never>,
|
|
322
|
-
)
|
|
327
|
+
).finally(() => {
|
|
328
|
+
if (agencySessionId !== undefined) {
|
|
329
|
+
process.env.AGENCY_SESSION_ID = agencySessionId
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
}
|
|
323
333
|
const runPrepare = (commandOptions: Parameters<typeof workPrepare>[0]) =>
|
|
324
334
|
Effect.runPromise(
|
|
325
335
|
workPrepare(commandOptions).pipe(
|
|
@@ -805,7 +815,7 @@ describe("work command", () => {
|
|
|
805
815
|
|
|
806
816
|
await expect(
|
|
807
817
|
harness.run({ taskId: "example", opencode: true, claude: true }),
|
|
808
|
-
).rejects.toThrow("Cannot combine --
|
|
818
|
+
).rejects.toThrow("Cannot combine --agent, --opencode, and --claude")
|
|
809
819
|
expect(harness.events).toEqual([])
|
|
810
820
|
})
|
|
811
821
|
|
|
@@ -897,12 +907,17 @@ describe("work command", () => {
|
|
|
897
907
|
const harness = createHarness({ workspace: multiPhaseWorkspace })
|
|
898
908
|
process.env.AGENCY_SESSION_ID = "existing-session"
|
|
899
909
|
try {
|
|
900
|
-
await harness.run(
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
910
|
+
await harness.run(
|
|
911
|
+
{
|
|
912
|
+
taskId: "example",
|
|
913
|
+
phaseId: "implementation",
|
|
914
|
+
opencode: true,
|
|
915
|
+
auto: true,
|
|
916
|
+
},
|
|
917
|
+
undefined,
|
|
918
|
+
undefined,
|
|
919
|
+
true,
|
|
920
|
+
)
|
|
906
921
|
} finally {
|
|
907
922
|
delete process.env.AGENCY_SESSION_ID
|
|
908
923
|
}
|
|
@@ -919,10 +934,10 @@ describe("work command", () => {
|
|
|
919
934
|
})
|
|
920
935
|
})
|
|
921
936
|
|
|
922
|
-
test("expands a named
|
|
937
|
+
test("expands a named agent with shared context", async () => {
|
|
923
938
|
const harness = createHarness({
|
|
924
939
|
available: { codex: true },
|
|
925
|
-
|
|
940
|
+
agents: {
|
|
926
941
|
custom: {
|
|
927
942
|
command: ["codex"],
|
|
928
943
|
autoCommand: ["codex", "--task", "{task}", "{prompt}"],
|
|
@@ -934,7 +949,7 @@ describe("work command", () => {
|
|
|
934
949
|
},
|
|
935
950
|
})
|
|
936
951
|
|
|
937
|
-
await harness.run({ taskId: "example",
|
|
952
|
+
await harness.run({ taskId: "example", agent: "custom", auto: true })
|
|
938
953
|
|
|
939
954
|
expect(harness.probes).toEqual(["codex"])
|
|
940
955
|
expect(harness.launches[0]).toEqual({
|
|
@@ -948,7 +963,7 @@ describe("work command", () => {
|
|
|
948
963
|
cwd: taskDirectory,
|
|
949
964
|
})
|
|
950
965
|
expect(harness.launchEnvironments[0]).toMatchObject({
|
|
951
|
-
|
|
966
|
+
AGENCY_AGENT: "custom",
|
|
952
967
|
AGENCY_CLAIMANT: process.env.USER ?? "agency",
|
|
953
968
|
AGENCY_WORKBASE: "/workbase",
|
|
954
969
|
AGENCY_TARGET: "execution-unit:task/example",
|
|
@@ -962,7 +977,7 @@ describe("work command", () => {
|
|
|
962
977
|
test("prints the exact command contract without launching and omits secrets", async () => {
|
|
963
978
|
const harness = createHarness({
|
|
964
979
|
available: { agent: true },
|
|
965
|
-
|
|
980
|
+
agents: {
|
|
966
981
|
custom: {
|
|
967
982
|
command: ["agent"],
|
|
968
983
|
autoCommand: ["agent", "{prompt}"],
|
|
@@ -977,7 +992,7 @@ describe("work command", () => {
|
|
|
977
992
|
const output = await captureLogs(() =>
|
|
978
993
|
harness.run({
|
|
979
994
|
taskId: "example",
|
|
980
|
-
|
|
995
|
+
agent: "custom",
|
|
981
996
|
printCommand: true,
|
|
982
997
|
}),
|
|
983
998
|
)
|
|
@@ -1078,12 +1093,12 @@ describe("work command", () => {
|
|
|
1078
1093
|
|
|
1079
1094
|
test("automatically falls back from OpenCode to Claude", async () => {
|
|
1080
1095
|
const harness = createHarness({
|
|
1081
|
-
available: { opencode2: false, opencode: false },
|
|
1096
|
+
available: { opencode2: false, opencode: false, pi: false },
|
|
1082
1097
|
})
|
|
1083
1098
|
|
|
1084
1099
|
await harness.run({ taskId: "example" })
|
|
1085
1100
|
|
|
1086
|
-
expect(harness.probes).toEqual(["opencode2", "opencode", "claude"])
|
|
1101
|
+
expect(harness.probes).toEqual(["opencode2", "opencode", "pi", "claude"])
|
|
1087
1102
|
expect(harness.launches[0]).toEqual({
|
|
1088
1103
|
cli: "claude",
|
|
1089
1104
|
args: ["claude"],
|
|
@@ -1091,6 +1106,38 @@ describe("work command", () => {
|
|
|
1091
1106
|
})
|
|
1092
1107
|
})
|
|
1093
1108
|
|
|
1109
|
+
test("automatically falls back from OpenCode to Pi", async () => {
|
|
1110
|
+
const harness = createHarness({
|
|
1111
|
+
available: { opencode2: false, opencode: false },
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
await harness.run({ taskId: "example" })
|
|
1115
|
+
|
|
1116
|
+
expect(harness.probes).toEqual(["opencode2", "opencode", "pi"])
|
|
1117
|
+
expect(harness.launches[0]).toMatchObject({ cli: "pi", args: ["pi"] })
|
|
1118
|
+
})
|
|
1119
|
+
|
|
1120
|
+
test("uses the global agent before automatic detection", async () => {
|
|
1121
|
+
const harness = createHarness({ agent: "pi" })
|
|
1122
|
+
|
|
1123
|
+
await harness.run({ taskId: "example" })
|
|
1124
|
+
|
|
1125
|
+
expect(harness.probes).toEqual(["pi"])
|
|
1126
|
+
expect(harness.launches[0]).toMatchObject({ cli: "pi", args: ["pi"] })
|
|
1127
|
+
})
|
|
1128
|
+
|
|
1129
|
+
test("lets an invocation agent override the global agent", async () => {
|
|
1130
|
+
const harness = createHarness({ agent: "pi" })
|
|
1131
|
+
|
|
1132
|
+
await harness.run({ taskId: "example", agent: "claude" })
|
|
1133
|
+
|
|
1134
|
+
expect(harness.probes).toEqual(["claude"])
|
|
1135
|
+
expect(harness.launches[0]).toMatchObject({
|
|
1136
|
+
cli: "claude",
|
|
1137
|
+
args: ["claude"],
|
|
1138
|
+
})
|
|
1139
|
+
})
|
|
1140
|
+
|
|
1094
1141
|
test("does not fall back when OpenCode is explicitly required", async () => {
|
|
1095
1142
|
const harness = createHarness({ available: { opencode: false } })
|
|
1096
1143
|
|
|
@@ -1117,13 +1164,18 @@ describe("work command", () => {
|
|
|
1117
1164
|
|
|
1118
1165
|
test("fails when neither agent tool is available", async () => {
|
|
1119
1166
|
const harness = createHarness({
|
|
1120
|
-
available: {
|
|
1167
|
+
available: {
|
|
1168
|
+
opencode2: false,
|
|
1169
|
+
opencode: false,
|
|
1170
|
+
pi: false,
|
|
1171
|
+
claude: false,
|
|
1172
|
+
},
|
|
1121
1173
|
})
|
|
1122
1174
|
|
|
1123
1175
|
await expect(harness.run({ taskId: "example" })).rejects.toThrow(
|
|
1124
1176
|
"claude CLI tool not found",
|
|
1125
1177
|
)
|
|
1126
|
-
expect(harness.probes).toEqual(["opencode2", "opencode", "claude"])
|
|
1178
|
+
expect(harness.probes).toEqual(["opencode2", "opencode", "pi", "claude"])
|
|
1127
1179
|
expect(harness.launches).toEqual([])
|
|
1128
1180
|
})
|
|
1129
1181
|
|
package/src/commands/work.ts
CHANGED
|
@@ -26,9 +26,9 @@ import {
|
|
|
26
26
|
} from "../workbase/workbase-choice"
|
|
27
27
|
import {
|
|
28
28
|
printableEnvironment,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} from "../workbase/
|
|
29
|
+
resolveAgentCommand,
|
|
30
|
+
agentEnvironment,
|
|
31
|
+
} from "../workbase/agent-command"
|
|
32
32
|
import {
|
|
33
33
|
assessValidationEvidence,
|
|
34
34
|
buildKickoffPlan,
|
|
@@ -44,7 +44,7 @@ export interface WorkOptions extends BaseCommandOptions {
|
|
|
44
44
|
readonly epicId?: string
|
|
45
45
|
readonly opencode?: boolean
|
|
46
46
|
readonly claude?: boolean
|
|
47
|
-
readonly
|
|
47
|
+
readonly agent?: string
|
|
48
48
|
readonly printCommand?: boolean
|
|
49
49
|
readonly auto?: boolean
|
|
50
50
|
readonly force?: boolean
|
|
@@ -94,10 +94,10 @@ export const work = (
|
|
|
94
94
|
Effect.gen(function* () {
|
|
95
95
|
if (
|
|
96
96
|
(options.opencode && options.claude) ||
|
|
97
|
-
(options.
|
|
97
|
+
(options.agent && (options.opencode || options.claude))
|
|
98
98
|
) {
|
|
99
99
|
return yield* Effect.fail(
|
|
100
|
-
new Error("Cannot combine --
|
|
100
|
+
new Error("Cannot combine --agent, --opencode, and --claude"),
|
|
101
101
|
)
|
|
102
102
|
}
|
|
103
103
|
const previousEnvironment = { ...process.env }
|
|
@@ -132,6 +132,7 @@ export const work = (
|
|
|
132
132
|
if (!root) return
|
|
133
133
|
yield* integrations.sync(root)
|
|
134
134
|
const { config } = yield* workbase.loadConfig(root)
|
|
135
|
+
const globalConfig = yield* workbase.loadGlobalConfig()
|
|
135
136
|
|
|
136
137
|
let target: WorkTarget | null = null
|
|
137
138
|
if (options.epicId) {
|
|
@@ -274,22 +275,15 @@ export const work = (
|
|
|
274
275
|
}
|
|
275
276
|
prompt = `Agency worker launch target: ${targetNodeId(target)}. ${prompt}`
|
|
276
277
|
|
|
277
|
-
const
|
|
278
|
-
options.
|
|
279
|
-
|
|
280
|
-
options.claude
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
let
|
|
285
|
-
let
|
|
286
|
-
options.runner ??
|
|
287
|
-
process.env.AGENCY_RUNNER ??
|
|
288
|
-
(options.claude
|
|
289
|
-
? "claude"
|
|
290
|
-
: options.opencode
|
|
291
|
-
? "opencode"
|
|
292
|
-
: defaultRunners[defaultRunnerIndex]!)
|
|
278
|
+
const invocationAgent =
|
|
279
|
+
options.agent ??
|
|
280
|
+
process.env.AGENCY_AGENT ??
|
|
281
|
+
(options.claude ? "claude" : options.opencode ? "opencode" : undefined)
|
|
282
|
+
const selectedAgent = invocationAgent ?? globalConfig.agent
|
|
283
|
+
const explicitlyRequested = selectedAgent !== undefined
|
|
284
|
+
const defaultAgents = ["opencode2", "opencode", "pi", "claude"] as const
|
|
285
|
+
let defaultAgentIndex = 0
|
|
286
|
+
let agent: string = selectedAgent ?? defaultAgents[defaultAgentIndex]!
|
|
293
287
|
const claimant = process.env.AGENCY_CLAIMANT ?? process.env.USER ?? "agency"
|
|
294
288
|
const sessionId =
|
|
295
289
|
process.env.AGENCY_SESSION_ID ?? `${process.pid}-${Date.now()}`
|
|
@@ -306,9 +300,9 @@ export const work = (
|
|
|
306
300
|
sessionId,
|
|
307
301
|
claimRevision: "",
|
|
308
302
|
}
|
|
309
|
-
let resolved =
|
|
310
|
-
|
|
311
|
-
config.
|
|
303
|
+
let resolved = resolveAgentCommand(
|
|
304
|
+
agent,
|
|
305
|
+
config.agents,
|
|
312
306
|
variables,
|
|
313
307
|
resume,
|
|
314
308
|
options.auto,
|
|
@@ -320,12 +314,12 @@ export const work = (
|
|
|
320
314
|
while (
|
|
321
315
|
available.exitCode !== 0 &&
|
|
322
316
|
!explicitlyRequested &&
|
|
323
|
-
|
|
317
|
+
defaultAgentIndex < defaultAgents.length - 1
|
|
324
318
|
) {
|
|
325
|
-
|
|
326
|
-
resolved =
|
|
327
|
-
|
|
328
|
-
config.
|
|
319
|
+
agent = defaultAgents[++defaultAgentIndex]!
|
|
320
|
+
resolved = resolveAgentCommand(
|
|
321
|
+
agent,
|
|
322
|
+
config.agents,
|
|
329
323
|
variables,
|
|
330
324
|
resume,
|
|
331
325
|
options.auto,
|
|
@@ -336,9 +330,9 @@ export const work = (
|
|
|
336
330
|
if (available.exitCode !== 0) {
|
|
337
331
|
return yield* Effect.fail(new Error(`${cli} CLI tool not found`))
|
|
338
332
|
}
|
|
339
|
-
resolved =
|
|
340
|
-
|
|
341
|
-
config.
|
|
333
|
+
resolved = resolveAgentCommand(
|
|
334
|
+
agent,
|
|
335
|
+
config.agents,
|
|
342
336
|
variables,
|
|
343
337
|
resume,
|
|
344
338
|
options.auto,
|
|
@@ -346,7 +340,7 @@ export const work = (
|
|
|
346
340
|
cli = resolved.argv[0]!
|
|
347
341
|
const environment = {
|
|
348
342
|
...resolved.environment,
|
|
349
|
-
...
|
|
343
|
+
...agentEnvironment(agent, variables),
|
|
350
344
|
}
|
|
351
345
|
if (writablePath) environment.AGENCY_WRITABLE_CHECKOUT = writablePath
|
|
352
346
|
if (options.printCommand) {
|
|
@@ -579,7 +573,7 @@ export const workPrepare = (options: WorkOptions = {}) =>
|
|
|
579
573
|
})
|
|
580
574
|
|
|
581
575
|
export const help = `
|
|
582
|
-
Usage: agency work [<directory-or-task-id> | --epic <epic-id>] [--
|
|
576
|
+
Usage: agency work [<directory-or-task-id> | --epic <epic-id>] [--agent <name>] [--auto]
|
|
583
577
|
agency work prepare [target] [--evidence <json-or-path>] [--dry-run] [--json]
|
|
584
578
|
|
|
585
579
|
Launch an agent for an epic, task, or phase. With no directory, select one
|
|
@@ -587,7 +581,7 @@ interactively. A positional argument resolves as a directory first, then as a ta
|
|
|
587
581
|
ID. Use '.' for the current directory. Outside a workbase, select a registered
|
|
588
582
|
workbase first. Managed OpenCode launches receive whole-workbase access through
|
|
589
583
|
Agency's project plugin; Agency context remains authoritative for writes. Automatic
|
|
590
|
-
|
|
584
|
+
agent discovery checks opencode2, opencode, pi, then claude.
|
|
591
585
|
|
|
592
586
|
The prepare subcommand resolves and materializes an execution workspace without
|
|
593
587
|
launching an agent or changing lifecycle status. --dry-run reports planned Git
|
|
@@ -603,8 +597,8 @@ Options:
|
|
|
603
597
|
--phase <id> Work on a phase selected with --task
|
|
604
598
|
--workbase <target> Select a workbase by ID, name, or path
|
|
605
599
|
--cwd <path> Resolve context from a specific directory
|
|
606
|
-
--
|
|
607
|
-
--auto Send the generated context prompt to the
|
|
600
|
+
--agent <name> Select a configured agent or built-in preset
|
|
601
|
+
--auto Send the generated context prompt to the agent
|
|
608
602
|
--print-command Print cwd, argv, and non-secret environment without launch
|
|
609
603
|
--opencode Require the OpenCode preset
|
|
610
604
|
--claude Require the Claude Code preset
|
package/src/protocol.test.ts
CHANGED
|
@@ -124,7 +124,7 @@ describe("machine protocol", () => {
|
|
|
124
124
|
currentRevision: "a".repeat(64),
|
|
125
125
|
claim: {
|
|
126
126
|
claimant: "orchestrator",
|
|
127
|
-
|
|
127
|
+
agent: "agent",
|
|
128
128
|
sessionId: "job-1",
|
|
129
129
|
startedAt: "2026-07-17T12:00:00.000Z",
|
|
130
130
|
targetRevision: "0".repeat(64),
|
|
@@ -137,7 +137,7 @@ describe("machine protocol", () => {
|
|
|
137
137
|
retryable: true,
|
|
138
138
|
fields: {
|
|
139
139
|
target: "task 'example'",
|
|
140
|
-
claim: {
|
|
140
|
+
claim: { agent: "agent", sessionId: "job-1" },
|
|
141
141
|
},
|
|
142
142
|
},
|
|
143
143
|
})
|
|
@@ -324,7 +324,7 @@ describe("ArchiveService bulk task archive", () => {
|
|
|
324
324
|
`status: dropped
|
|
325
325
|
claim:
|
|
326
326
|
claimant: orchestrator
|
|
327
|
-
|
|
327
|
+
agent: opencode
|
|
328
328
|
sessionId: task-session
|
|
329
329
|
startedAt: 2026-08-07T00:00:00.000Z
|
|
330
330
|
targetRevision: ${"a".repeat(64)}
|
|
@@ -373,7 +373,7 @@ claim:
|
|
|
373
373
|
`status: dropped
|
|
374
374
|
claim:
|
|
375
375
|
claimant: orchestrator
|
|
376
|
-
|
|
376
|
+
agent: opencode
|
|
377
377
|
sessionId: phase-session
|
|
378
378
|
startedAt: 2026-08-07T00:00:00.000Z
|
|
379
379
|
targetRevision: ${"b".repeat(64)}
|
|
@@ -56,7 +56,7 @@ describe("claim service", () => {
|
|
|
56
56
|
{
|
|
57
57
|
taskId: "single",
|
|
58
58
|
claimant: "orchestrator-1",
|
|
59
|
-
|
|
59
|
+
agent: "agent-1",
|
|
60
60
|
sessionId,
|
|
61
61
|
revision,
|
|
62
62
|
now,
|
|
@@ -79,7 +79,7 @@ describe("claim service", () => {
|
|
|
79
79
|
|
|
80
80
|
expect(acquired.claim).toEqual({
|
|
81
81
|
claimant: "orchestrator-1",
|
|
82
|
-
|
|
82
|
+
agent: "agent-1",
|
|
83
83
|
sessionId: "session-1",
|
|
84
84
|
startedAt: "2026-07-17T12:00:00.000Z",
|
|
85
85
|
targetRevision: initial.revision,
|
|
@@ -315,7 +315,7 @@ describe("claim service", () => {
|
|
|
315
315
|
taskId: "multi",
|
|
316
316
|
phaseId: "implementation",
|
|
317
317
|
claimant: "orchestrator",
|
|
318
|
-
|
|
318
|
+
agent: "agent",
|
|
319
319
|
sessionId: "phase-session",
|
|
320
320
|
revision: phase.revision,
|
|
321
321
|
},
|
|
@@ -70,7 +70,7 @@ interface ClaimInput {
|
|
|
70
70
|
readonly taskId: string
|
|
71
71
|
readonly phaseId?: string
|
|
72
72
|
readonly claimant: string
|
|
73
|
-
readonly
|
|
73
|
+
readonly agent: string
|
|
74
74
|
readonly sessionId: string
|
|
75
75
|
readonly revision: string
|
|
76
76
|
readonly expiresAt?: string
|
|
@@ -401,7 +401,7 @@ export class ClaimService extends Effect.Service<ClaimService>()(
|
|
|
401
401
|
Effect.gen(function* () {
|
|
402
402
|
for (const [label, value] of [
|
|
403
403
|
["Claimant", input.claimant],
|
|
404
|
-
["
|
|
404
|
+
["Agent", input.agent],
|
|
405
405
|
["Session ID", input.sessionId],
|
|
406
406
|
] as const) {
|
|
407
407
|
assertIdentity(label, value)
|
|
@@ -435,7 +435,7 @@ export class ClaimService extends Effect.Service<ClaimService>()(
|
|
|
435
435
|
target: inspected.target.label,
|
|
436
436
|
currentRevision: input.revision,
|
|
437
437
|
claim: data.claim,
|
|
438
|
-
message: `${inspected.target.label} is claimed by '${data.claim.
|
|
438
|
+
message: `${inspected.target.label} is claimed by '${data.claim.agent}'`,
|
|
439
439
|
})
|
|
440
440
|
}
|
|
441
441
|
if (
|
|
@@ -457,7 +457,7 @@ export class ClaimService extends Effect.Service<ClaimService>()(
|
|
|
457
457
|
}
|
|
458
458
|
const claim: ClaimRecord = {
|
|
459
459
|
claimant: input.claimant.trim(),
|
|
460
|
-
|
|
460
|
+
agent: input.agent.trim(),
|
|
461
461
|
sessionId: input.sessionId.trim(),
|
|
462
462
|
startedAt: operationTime.toISOString(),
|
|
463
463
|
targetRevision: input.revision,
|
|
@@ -138,16 +138,16 @@ export class DoctorService extends Effect.Service<DoctorService>()(
|
|
|
138
138
|
const versionControlAvailable =
|
|
139
139
|
gitAvailable && (config.vcs !== "jj" || jjAvailable)
|
|
140
140
|
yield* tool(
|
|
141
|
-
"capability.
|
|
141
|
+
"capability.agent.opencode",
|
|
142
142
|
"opencode",
|
|
143
143
|
"optional",
|
|
144
|
-
"OpenCode
|
|
144
|
+
"OpenCode agent",
|
|
145
145
|
)
|
|
146
146
|
yield* tool(
|
|
147
|
-
"capability.
|
|
147
|
+
"capability.agent.claude",
|
|
148
148
|
"claude",
|
|
149
149
|
"optional",
|
|
150
|
-
"Claude
|
|
150
|
+
"Claude agent",
|
|
151
151
|
)
|
|
152
152
|
|
|
153
153
|
const configuredCommands: readonly (readonly [
|
|
@@ -194,43 +194,43 @@ export class DoctorService extends Effect.Service<DoctorService>()(
|
|
|
194
194
|
]
|
|
195
195
|
: [],
|
|
196
196
|
),
|
|
197
|
-
...Object.entries(config.
|
|
198
|
-
([name,
|
|
197
|
+
...Object.entries(config.agents ?? {}).map(
|
|
198
|
+
([name, agent]) =>
|
|
199
199
|
[
|
|
200
|
-
`integration.
|
|
201
|
-
|
|
202
|
-
`Configured
|
|
200
|
+
`integration.agent.${name}`,
|
|
201
|
+
agent.command,
|
|
202
|
+
`Configured agent '${name}'`,
|
|
203
203
|
] as const,
|
|
204
204
|
),
|
|
205
|
-
...Object.entries(config.
|
|
206
|
-
|
|
205
|
+
...Object.entries(config.agents ?? {}).flatMap(([name, agent]) =>
|
|
206
|
+
agent.autoCommand
|
|
207
207
|
? [
|
|
208
208
|
[
|
|
209
|
-
`integration.
|
|
210
|
-
|
|
211
|
-
`Configured
|
|
209
|
+
`integration.agent.${name}.auto`,
|
|
210
|
+
agent.autoCommand,
|
|
211
|
+
`Configured agent '${name}' auto`,
|
|
212
212
|
] as const,
|
|
213
213
|
]
|
|
214
214
|
: [],
|
|
215
215
|
),
|
|
216
|
-
...Object.entries(config.
|
|
217
|
-
|
|
216
|
+
...Object.entries(config.agents ?? {}).flatMap(([name, agent]) =>
|
|
217
|
+
agent.resumeCommand
|
|
218
218
|
? [
|
|
219
219
|
[
|
|
220
|
-
`integration.
|
|
221
|
-
|
|
222
|
-
`Configured
|
|
220
|
+
`integration.agent.${name}.resume`,
|
|
221
|
+
agent.resumeCommand,
|
|
222
|
+
`Configured agent '${name}' resume`,
|
|
223
223
|
] as const,
|
|
224
224
|
]
|
|
225
225
|
: [],
|
|
226
226
|
),
|
|
227
|
-
...Object.entries(config.
|
|
228
|
-
|
|
227
|
+
...Object.entries(config.agents ?? {}).flatMap(([name, agent]) =>
|
|
228
|
+
agent.autoResumeCommand
|
|
229
229
|
? [
|
|
230
230
|
[
|
|
231
|
-
`integration.
|
|
232
|
-
|
|
233
|
-
`Configured
|
|
231
|
+
`integration.agent.${name}.auto-resume`,
|
|
232
|
+
agent.autoResumeCommand,
|
|
233
|
+
`Configured agent '${name}' auto resume`,
|
|
234
234
|
] as const,
|
|
235
235
|
]
|
|
236
236
|
: [],
|