@markjaquith/agency 2.68.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 +37 -24
- 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 +9 -9
- package/src/commands/claim.ts +6 -6
- package/src/commands/doctor.test.ts +3 -3
- 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 +2 -2
- 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/schemas.test.ts +5 -5
- package/src/workbase/schemas.ts +8 -2
|
@@ -8,6 +8,7 @@ import { parseFrontmatter } from "../workbase/frontmatter"
|
|
|
8
8
|
import {
|
|
9
9
|
EntityId,
|
|
10
10
|
EpicFrontmatter,
|
|
11
|
+
GlobalConfig,
|
|
11
12
|
LegacyWorkbaseRegistry,
|
|
12
13
|
PhaseFrontmatter,
|
|
13
14
|
TaskFrontmatter,
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
import { validateWorktreeCreateCommand } from "../workbase/worktree-command"
|
|
23
24
|
import { validateWorkspaceCreateCommand } from "../workbase/workspace-command"
|
|
24
25
|
import { validatePostCheckoutCommand } from "../workbase/checkout-command"
|
|
25
|
-
import {
|
|
26
|
+
import { validateAgents } from "../workbase/agent-command"
|
|
26
27
|
import { findDependencyCycles } from "../workbase/dependency-graph"
|
|
27
28
|
import { validateDelivery } from "../workbase/delivery-command"
|
|
28
29
|
import { preferredVersionControl } from "../workbase/version-control"
|
|
@@ -90,6 +91,15 @@ const registryPath = (configDirectory?: string) =>
|
|
|
90
91
|
"workbases.json",
|
|
91
92
|
)
|
|
92
93
|
|
|
94
|
+
const globalConfigPath = (configDirectory?: string) =>
|
|
95
|
+
join(
|
|
96
|
+
configDirectory ||
|
|
97
|
+
process.env.XDG_CONFIG_HOME ||
|
|
98
|
+
join(homedir(), ".config"),
|
|
99
|
+
"agency",
|
|
100
|
+
"agency.json",
|
|
101
|
+
)
|
|
102
|
+
|
|
93
103
|
const registrationId = (path: string) =>
|
|
94
104
|
`wb-${new Bun.CryptoHasher("sha256").update(path).digest("hex").slice(0, 12)}`
|
|
95
105
|
|
|
@@ -178,6 +188,32 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
178
188
|
"WorkbaseService",
|
|
179
189
|
{
|
|
180
190
|
sync: () => ({
|
|
191
|
+
loadGlobalConfig: (configDirectory?: string) =>
|
|
192
|
+
Effect.gen(function* () {
|
|
193
|
+
const fs = yield* FileSystemService
|
|
194
|
+
const path = globalConfigPath(configDirectory)
|
|
195
|
+
if (!(yield* fs.exists(path))) return {}
|
|
196
|
+
const content = yield* fs.readFile(path)
|
|
197
|
+
let input: unknown
|
|
198
|
+
try {
|
|
199
|
+
input = JSON.parse(content)
|
|
200
|
+
} catch (cause) {
|
|
201
|
+
return yield* new WorkbaseConfigError({
|
|
202
|
+
path,
|
|
203
|
+
message: `Invalid JSON in global Agency configuration ${path}`,
|
|
204
|
+
cause,
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
const decoded = decode(GlobalConfig, input)
|
|
208
|
+
if (!decoded.success) {
|
|
209
|
+
return yield* new WorkbaseConfigError({
|
|
210
|
+
path,
|
|
211
|
+
message: `Invalid global Agency configuration in ${path}:\n${decoded.error}`,
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
return decoded.value
|
|
215
|
+
}),
|
|
216
|
+
|
|
181
217
|
initialize: (path: string = process.cwd()) =>
|
|
182
218
|
Effect.gen(function* () {
|
|
183
219
|
const fs = yield* FileSystemService
|
|
@@ -309,7 +345,7 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
309
345
|
}
|
|
310
346
|
}
|
|
311
347
|
try {
|
|
312
|
-
|
|
348
|
+
validateAgents(decoded.value.agents)
|
|
313
349
|
validateDelivery(decoded.value.delivery)
|
|
314
350
|
} catch (cause) {
|
|
315
351
|
return yield* new WorkbaseConfigError({
|
|
@@ -317,7 +353,7 @@ export class WorkbaseService extends Effect.Service<WorkbaseService>()(
|
|
|
317
353
|
message:
|
|
318
354
|
cause instanceof Error
|
|
319
355
|
? cause.message
|
|
320
|
-
: "Invalid
|
|
356
|
+
: "Invalid agent configuration",
|
|
321
357
|
})
|
|
322
358
|
}
|
|
323
359
|
return current
|
package/src/workbase/AGENTS.md
CHANGED
|
@@ -121,7 +121,7 @@ Do not rediscover commands that this recipe or a known-current
|
|
|
121
121
|
prepare the task with
|
|
122
122
|
`agency work prepare <slug> --evidence <creation-json> --dry-run --json`, then
|
|
123
123
|
execute the returned plan through `task-document-split` to prepare the
|
|
124
|
-
checkout and open or reuse the background tab. Stop before `
|
|
124
|
+
checkout and open or reuse the background tab. Stop before `agent-start`.
|
|
125
125
|
3. For work/launch/start/kickoff intent, run that same preflight and execute its
|
|
126
126
|
ordered kickoff steps. The plan owns worktree dry-run/preparation, a
|
|
127
127
|
retry-safe background Herdr tab, the side-by-side task document,
|
|
@@ -131,8 +131,8 @@ Do not rediscover commands that this recipe or a known-current
|
|
|
131
131
|
`agency-kickoff-v1` capability, execute the supplied actions directly. Do not
|
|
132
132
|
call Herdr help, skill, or CLI discovery. If capability/version evidence is
|
|
133
133
|
absent or stale, discovery is the compatibility path; then resume the same
|
|
134
|
-
idempotency key rather than creating another tab, checkout, or
|
|
135
|
-
5. After the one final context verification succeeds, leave the
|
|
134
|
+
idempotency key rather than creating another tab, checkout, or agent.
|
|
135
|
+
5. After the one final context verification succeeds, leave the agent in the
|
|
136
136
|
background and stop. Do not inspect, poll, or babysit it unless the user asks.
|
|
137
137
|
|
|
138
138
|
Validation evidence is a local, auditable optimization, not authority. Preflight
|
|
@@ -153,12 +153,12 @@ IDs and remediation commands before retrying.
|
|
|
153
153
|
|
|
154
154
|
`agency work` is the human launch flow: it reconciles managed integration,
|
|
155
155
|
selects work, checks readiness, prepares checkouts, marks execution work
|
|
156
|
-
`working` without creating a claim, and starts the
|
|
156
|
+
`working` without creating a claim, and starts the agent. Epic and multi-phase
|
|
157
157
|
task launches remain orchestration-only. External orchestrators instead claim
|
|
158
|
-
an execution unit, launch and monitor their
|
|
158
|
+
an execution unit, launch and monitor their agent separately, and finish or
|
|
159
159
|
release the claim with the current document revision.
|
|
160
160
|
|
|
161
|
-
An Agency-launched
|
|
161
|
+
An Agency-launched agent receives process-local worker identity through both
|
|
162
162
|
the `AGENCY_SESSION_ID` and `AGENCY_TARGET` environment variables and a generated
|
|
163
163
|
prompt beginning `Agency worker launch target: <target>.` Treat either form as
|
|
164
164
|
launch evidence only after `agency context . --json` confirms the same target,
|
|
@@ -166,7 +166,7 @@ document paths, valid context, and expected write authority. Once confirmed,
|
|
|
166
166
|
perform the assigned work directly and never invoke `agency work` to start the
|
|
167
167
|
same target again.
|
|
168
168
|
|
|
169
|
-
Some
|
|
169
|
+
Some agent clients attach to a long-lived process and may not preserve launch
|
|
170
170
|
environment variables. If the variables and prompt marker are absent, fail safe
|
|
171
171
|
when the initial instruction is a generated `Start`, `Continue`, or `Work on`
|
|
172
172
|
prompt whose absolute document paths match the current directory and the active,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test"
|
|
2
2
|
import {
|
|
3
3
|
printableEnvironment,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./
|
|
4
|
+
resolveAgentCommand,
|
|
5
|
+
agentEnvironment,
|
|
6
|
+
validateAgents,
|
|
7
|
+
} from "./agent-command"
|
|
8
8
|
|
|
9
9
|
const variables = {
|
|
10
10
|
prompt: "Read the task.",
|
|
@@ -17,45 +17,55 @@ const variables = {
|
|
|
17
17
|
claimRevision: "revision-1",
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
describe("
|
|
20
|
+
describe("agent commands", () => {
|
|
21
21
|
test("uses promptless interactive commands for built-in presets", () => {
|
|
22
22
|
expect(
|
|
23
|
-
|
|
23
|
+
resolveAgentCommand("opencode2", undefined, variables, false).argv,
|
|
24
24
|
).toEqual(["opencode2"])
|
|
25
25
|
expect(
|
|
26
|
-
|
|
26
|
+
resolveAgentCommand("opencode2", undefined, variables, true).argv,
|
|
27
27
|
).toEqual(["opencode2", "--continue"])
|
|
28
28
|
expect(
|
|
29
|
-
|
|
29
|
+
resolveAgentCommand("opencode", undefined, variables, false).argv,
|
|
30
30
|
).toEqual(["opencode"])
|
|
31
31
|
expect(
|
|
32
|
-
|
|
32
|
+
resolveAgentCommand("opencode", undefined, variables, true).argv,
|
|
33
33
|
).toEqual(["opencode", "--continue"])
|
|
34
|
+
expect(resolveAgentCommand("pi", undefined, variables, false).argv).toEqual(
|
|
35
|
+
["pi"],
|
|
36
|
+
)
|
|
37
|
+
expect(resolveAgentCommand("pi", undefined, variables, true).argv).toEqual([
|
|
38
|
+
"pi",
|
|
39
|
+
"--continue",
|
|
40
|
+
])
|
|
34
41
|
expect(
|
|
35
|
-
|
|
42
|
+
resolveAgentCommand("claude", undefined, variables, true).argv,
|
|
36
43
|
).toEqual(["claude", "--continue"])
|
|
37
44
|
})
|
|
38
45
|
|
|
39
46
|
test("uses autonomous commands when a prompt is requested", () => {
|
|
40
47
|
expect(
|
|
41
|
-
|
|
48
|
+
resolveAgentCommand("opencode2", undefined, variables, false, true).argv,
|
|
42
49
|
).toEqual(["opencode2", "--prompt", "Read the task."])
|
|
43
50
|
expect(
|
|
44
|
-
|
|
51
|
+
resolveAgentCommand("opencode2", undefined, variables, true, true).argv,
|
|
45
52
|
).toEqual(["opencode2", "--continue", "--prompt", "Read the task."])
|
|
46
53
|
expect(
|
|
47
|
-
|
|
54
|
+
resolveAgentCommand("opencode", undefined, variables, false, true).argv,
|
|
48
55
|
).toEqual(["opencode", "--prompt", "Read the task."])
|
|
49
56
|
expect(
|
|
50
|
-
|
|
57
|
+
resolveAgentCommand("opencode", undefined, variables, true, true).argv,
|
|
51
58
|
).toEqual(["opencode", "--continue", "--prompt", "Read the task."])
|
|
52
59
|
expect(
|
|
53
|
-
|
|
60
|
+
resolveAgentCommand("pi", undefined, variables, false, true).argv,
|
|
61
|
+
).toEqual(["pi", "Read the task."])
|
|
62
|
+
expect(
|
|
63
|
+
resolveAgentCommand("claude", undefined, variables, true, true).argv,
|
|
54
64
|
).toEqual(["claude", "--continue", "Read the task."])
|
|
55
65
|
})
|
|
56
66
|
|
|
57
67
|
test("expands configured argv and environment without a shell", () => {
|
|
58
|
-
const resolved =
|
|
68
|
+
const resolved = resolveAgentCommand(
|
|
59
69
|
"custom",
|
|
60
70
|
{
|
|
61
71
|
custom: {
|
|
@@ -79,33 +89,33 @@ describe("runner commands", () => {
|
|
|
79
89
|
})
|
|
80
90
|
})
|
|
81
91
|
|
|
82
|
-
test("rejects --auto for configured
|
|
92
|
+
test("rejects --auto for configured agents without an auto command", () => {
|
|
83
93
|
expect(() =>
|
|
84
|
-
|
|
94
|
+
resolveAgentCommand(
|
|
85
95
|
"custom",
|
|
86
96
|
{ custom: { command: ["agent"] } },
|
|
87
97
|
variables,
|
|
88
98
|
false,
|
|
89
99
|
true,
|
|
90
100
|
),
|
|
91
|
-
).toThrow("
|
|
101
|
+
).toThrow("Agent 'custom' does not support --auto")
|
|
92
102
|
})
|
|
93
103
|
|
|
94
104
|
test("rejects unknown placeholders", () => {
|
|
95
105
|
expect(() =>
|
|
96
|
-
|
|
97
|
-
).toThrow("Unknown
|
|
106
|
+
validateAgents({ custom: { command: ["agent", "{unknown}"] } }),
|
|
107
|
+
).toThrow("Unknown agent 'custom' placeholder: {unknown}")
|
|
98
108
|
})
|
|
99
109
|
|
|
100
110
|
test("provides normalized Agency environment and filters secret values", () => {
|
|
101
111
|
const environment = {
|
|
102
|
-
...
|
|
112
|
+
...agentEnvironment("custom", variables),
|
|
103
113
|
VISIBLE: "yes",
|
|
104
114
|
ACCESS_TOKEN: "secret",
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
expect(environment).toMatchObject({
|
|
108
|
-
|
|
118
|
+
AGENCY_AGENT: "custom",
|
|
109
119
|
AGENCY_CLAIMANT: "orchestrator",
|
|
110
120
|
AGENCY_SESSION_ID: "session-1",
|
|
111
121
|
AGENCY_WORKBASE: "/workbase",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WorkbaseConfig } from "./schemas"
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface AgentCommandVariables {
|
|
4
4
|
readonly prompt: string
|
|
5
5
|
readonly workbase: string
|
|
6
6
|
readonly target: string
|
|
@@ -11,7 +11,7 @@ export interface RunnerCommandVariables {
|
|
|
11
11
|
readonly claimRevision: string
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
interface
|
|
14
|
+
interface AgentDefinition {
|
|
15
15
|
readonly command: readonly string[]
|
|
16
16
|
readonly autoCommand?: readonly string[]
|
|
17
17
|
readonly resumeCommand?: readonly string[]
|
|
@@ -19,7 +19,7 @@ interface RunnerDefinition {
|
|
|
19
19
|
readonly environment?: Readonly<Record<string, string>>
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const PLACEHOLDERS = new Set<keyof
|
|
22
|
+
const PLACEHOLDERS = new Set<keyof AgentCommandVariables>([
|
|
23
23
|
"prompt",
|
|
24
24
|
"workbase",
|
|
25
25
|
"target",
|
|
@@ -30,7 +30,7 @@ const PLACEHOLDERS = new Set<keyof RunnerCommandVariables>([
|
|
|
30
30
|
"claimRevision",
|
|
31
31
|
])
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const BUILTIN_AGENTS: Readonly<Record<string, AgentDefinition>> = {
|
|
34
34
|
opencode2: {
|
|
35
35
|
command: ["opencode2"],
|
|
36
36
|
autoCommand: ["opencode2", "--prompt", "{prompt}"],
|
|
@@ -43,6 +43,12 @@ const BUILTIN_RUNNERS: Readonly<Record<string, RunnerDefinition>> = {
|
|
|
43
43
|
resumeCommand: ["opencode", "--continue"],
|
|
44
44
|
autoResumeCommand: ["opencode", "--continue", "--prompt", "{prompt}"],
|
|
45
45
|
},
|
|
46
|
+
pi: {
|
|
47
|
+
command: ["pi"],
|
|
48
|
+
autoCommand: ["pi", "{prompt}"],
|
|
49
|
+
resumeCommand: ["pi", "--continue"],
|
|
50
|
+
autoResumeCommand: ["pi", "--continue", "{prompt}"],
|
|
51
|
+
},
|
|
46
52
|
claude: {
|
|
47
53
|
command: ["claude"],
|
|
48
54
|
autoCommand: ["claude", "{prompt}"],
|
|
@@ -51,48 +57,46 @@ const BUILTIN_RUNNERS: Readonly<Record<string, RunnerDefinition>> = {
|
|
|
51
57
|
},
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
const validateTemplate = (
|
|
60
|
+
const validateTemplate = (agent: string, value: string) => {
|
|
55
61
|
for (const match of value.matchAll(/\{([^{}]+)\}/g)) {
|
|
56
62
|
const placeholder = match[1]!
|
|
57
|
-
if (!PLACEHOLDERS.has(placeholder as keyof
|
|
58
|
-
throw new Error(
|
|
59
|
-
`Unknown runner '${runner}' placeholder: {${placeholder}}`,
|
|
60
|
-
)
|
|
63
|
+
if (!PLACEHOLDERS.has(placeholder as keyof AgentCommandVariables)) {
|
|
64
|
+
throw new Error(`Unknown agent '${agent}' placeholder: {${placeholder}}`)
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
export const
|
|
66
|
-
for (const [name,
|
|
69
|
+
export const validateAgents = (agents: WorkbaseConfig["agents"]): void => {
|
|
70
|
+
for (const [name, agent] of Object.entries(agents ?? {})) {
|
|
67
71
|
for (const value of [
|
|
68
|
-
...
|
|
69
|
-
...(
|
|
70
|
-
...(
|
|
71
|
-
...(
|
|
72
|
-
...Object.values(
|
|
72
|
+
...agent.command,
|
|
73
|
+
...(agent.autoCommand ?? []),
|
|
74
|
+
...(agent.resumeCommand ?? []),
|
|
75
|
+
...(agent.autoResumeCommand ?? []),
|
|
76
|
+
...Object.values(agent.environment ?? {}),
|
|
73
77
|
]) {
|
|
74
78
|
validateTemplate(name, value)
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
const expand = (value: string, variables:
|
|
83
|
+
const expand = (value: string, variables: AgentCommandVariables) =>
|
|
80
84
|
value.replaceAll(
|
|
81
85
|
/\{([^{}]+)\}/g,
|
|
82
86
|
(match, placeholder: string) =>
|
|
83
|
-
variables[placeholder as keyof
|
|
87
|
+
variables[placeholder as keyof AgentCommandVariables] ?? match,
|
|
84
88
|
)
|
|
85
89
|
|
|
86
|
-
export const
|
|
90
|
+
export const resolveAgentCommand = (
|
|
87
91
|
name: string,
|
|
88
|
-
configured: WorkbaseConfig["
|
|
89
|
-
variables:
|
|
92
|
+
configured: WorkbaseConfig["agents"],
|
|
93
|
+
variables: AgentCommandVariables,
|
|
90
94
|
resume: boolean,
|
|
91
95
|
auto = false,
|
|
92
96
|
) => {
|
|
93
|
-
|
|
94
|
-
const definition = configured?.[name] ??
|
|
95
|
-
if (!definition) throw new Error(`Unknown
|
|
97
|
+
validateAgents(configured)
|
|
98
|
+
const definition = configured?.[name] ?? BUILTIN_AGENTS[name]
|
|
99
|
+
if (!definition) throw new Error(`Unknown agent: ${name}`)
|
|
96
100
|
const template = auto
|
|
97
101
|
? resume
|
|
98
102
|
? (definition.autoResumeCommand ?? definition.autoCommand)
|
|
@@ -101,7 +105,7 @@ export const resolveRunnerCommand = (
|
|
|
101
105
|
? definition.resumeCommand
|
|
102
106
|
: definition.command
|
|
103
107
|
if (!template) {
|
|
104
|
-
throw new Error(`
|
|
108
|
+
throw new Error(`Agent '${name}' does not support --auto`)
|
|
105
109
|
}
|
|
106
110
|
const argv = template.map((argument) => expand(argument, variables))
|
|
107
111
|
const environment = Object.fromEntries(
|
|
@@ -113,11 +117,11 @@ export const resolveRunnerCommand = (
|
|
|
113
117
|
return { argv, environment }
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
export const
|
|
117
|
-
|
|
118
|
-
variables:
|
|
120
|
+
export const agentEnvironment = (
|
|
121
|
+
agent: string,
|
|
122
|
+
variables: AgentCommandVariables,
|
|
119
123
|
): Record<string, string> => ({
|
|
120
|
-
|
|
124
|
+
AGENCY_AGENT: agent,
|
|
121
125
|
AGENCY_CLAIMANT: variables.claimant,
|
|
122
126
|
AGENCY_SESSION_ID: variables.sessionId,
|
|
123
127
|
AGENCY_CLAIM_REVISION: variables.claimRevision,
|
|
@@ -325,11 +325,11 @@ export const buildKickoffPlan = (input: {
|
|
|
325
325
|
recovery: "Reuse the existing split when present.",
|
|
326
326
|
},
|
|
327
327
|
{
|
|
328
|
-
id: "
|
|
328
|
+
id: "agent-start",
|
|
329
329
|
cwd: taskDirectory,
|
|
330
330
|
argv: ["agency", "work", ".", "--auto"],
|
|
331
331
|
recovery:
|
|
332
|
-
"Inspect the recorded tab before retrying; a working
|
|
332
|
+
"Inspect the recorded tab before retrying; a working agent must not be duplicated.",
|
|
333
333
|
},
|
|
334
334
|
{
|
|
335
335
|
id: "final-context-verification",
|
|
@@ -341,7 +341,7 @@ export const buildKickoffPlan = (input: {
|
|
|
341
341
|
],
|
|
342
342
|
exactlyOnce: true,
|
|
343
343
|
recovery:
|
|
344
|
-
"If verification fails, inspect the existing tab; do not launch another
|
|
344
|
+
"If verification fails, inspect the existing tab; do not launch another agent.",
|
|
345
345
|
},
|
|
346
346
|
],
|
|
347
347
|
successFields: [
|
|
@@ -351,7 +351,7 @@ export const buildKickoffPlan = (input: {
|
|
|
351
351
|
"preparedCheckout",
|
|
352
352
|
"herdrWorkspace",
|
|
353
353
|
"herdrTab",
|
|
354
|
-
"
|
|
354
|
+
"agentStart",
|
|
355
355
|
"contextVerification",
|
|
356
356
|
],
|
|
357
357
|
}
|
|
@@ -31,7 +31,7 @@ const body = () =>
|
|
|
31
31
|
"Handles Agency workbase orchestration and workflow operations with the Agency CLI",
|
|
32
32
|
mode: "subagent",
|
|
33
33
|
prompt:
|
|
34
|
-
"You are the Agency workflow specialist. Use the Agency CLI to handle delegated workbase orchestration and workflow operations. Always start with `agency context . --json`, follow the managed Agency instructions and reported authority, use Agency commands for durable mutations, and report the resulting state concisely. When delegated to start or kick off work in another agent, launch it, verify that the
|
|
34
|
+
"You are the Agency workflow specialist. Use the Agency CLI to handle delegated workbase orchestration and workflow operations. Always start with `agency context . --json`, follow the managed Agency instructions and reported authority, use Agency commands for durable mutations, and report the resulting state concisely. When delegated to start or kick off work in another agent, launch it, verify that the agent started successfully, and return without waiting for the task to finish.",
|
|
35
35
|
},
|
|
36
36
|
plan: {
|
|
37
37
|
disable: true,
|
|
@@ -276,11 +276,11 @@ describe("workspace creation configuration", () => {
|
|
|
276
276
|
})
|
|
277
277
|
})
|
|
278
278
|
|
|
279
|
-
describe("
|
|
279
|
+
describe("agent configuration", () => {
|
|
280
280
|
test("accepts named argv commands with resume commands and environment", () => {
|
|
281
281
|
const config = Schema.decodeUnknownSync(WorkbaseConfig)({
|
|
282
282
|
version: 2,
|
|
283
|
-
|
|
283
|
+
agents: {
|
|
284
284
|
custom: {
|
|
285
285
|
command: ["agent"],
|
|
286
286
|
autoCommand: ["agent", "{prompt}"],
|
|
@@ -291,14 +291,14 @@ describe("runner configuration", () => {
|
|
|
291
291
|
},
|
|
292
292
|
})
|
|
293
293
|
|
|
294
|
-
expect(config.
|
|
294
|
+
expect(config.agents?.custom?.autoCommand).toEqual(["agent", "{prompt}"])
|
|
295
295
|
})
|
|
296
296
|
|
|
297
297
|
test("rejects shell strings in place of argv arrays", () => {
|
|
298
298
|
expect(() =>
|
|
299
299
|
Schema.decodeUnknownSync(WorkbaseConfig)({
|
|
300
300
|
version: 2,
|
|
301
|
-
|
|
301
|
+
agents: { custom: { command: "agent {prompt}" } },
|
|
302
302
|
}),
|
|
303
303
|
).toThrow()
|
|
304
304
|
})
|
|
@@ -423,7 +423,7 @@ describe("work status", () => {
|
|
|
423
423
|
describe("claim records", () => {
|
|
424
424
|
const record = {
|
|
425
425
|
claimant: "orchestrator",
|
|
426
|
-
|
|
426
|
+
agent: "agent",
|
|
427
427
|
sessionId: "job-1",
|
|
428
428
|
startedAt: "2026-07-17T12:00:00.000Z",
|
|
429
429
|
targetRevision: "0".repeat(64),
|
package/src/workbase/schemas.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const DocumentRevision = Schema.String.pipe(
|
|
|
52
52
|
|
|
53
53
|
export const ClaimRecord = Schema.Struct({
|
|
54
54
|
claimant: NonEmptyString,
|
|
55
|
-
|
|
55
|
+
agent: NonEmptyString,
|
|
56
56
|
sessionId: NonEmptyString,
|
|
57
57
|
startedAt: IsoTimestamp,
|
|
58
58
|
targetRevision: DocumentRevision,
|
|
@@ -110,7 +110,7 @@ export const WorkbaseConfig = Schema.Struct({
|
|
|
110
110
|
chooserCommand: Schema.optional(Schema.NonEmptyArray(NonEmptyString)),
|
|
111
111
|
worktreeCreateCommand: Schema.optional(Schema.NonEmptyArray(NonEmptyString)),
|
|
112
112
|
workspaceCreateCommand: Schema.optional(Schema.NonEmptyArray(NonEmptyString)),
|
|
113
|
-
|
|
113
|
+
agents: Schema.optional(
|
|
114
114
|
Schema.Record({
|
|
115
115
|
key: EntityId,
|
|
116
116
|
value: Schema.Struct({
|
|
@@ -146,6 +146,12 @@ export const WorkbaseRegistry = Schema.Struct({
|
|
|
146
146
|
defaultId: Schema.optional(EntityId),
|
|
147
147
|
})
|
|
148
148
|
|
|
149
|
+
export const GlobalConfig = Schema.Struct({
|
|
150
|
+
agent: Schema.optional(
|
|
151
|
+
Schema.Literal("opencode2", "opencode", "pi", "claude"),
|
|
152
|
+
),
|
|
153
|
+
})
|
|
154
|
+
|
|
149
155
|
export const Dependency = Schema.Struct({
|
|
150
156
|
id: EntityId,
|
|
151
157
|
dependsOn: Schema.optional(Schema.Array(EntityId)),
|