@borgee/agents-host 0.2.28 → 0.2.31
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 +42 -28
- package/dist/agents-host.d.ts +1 -1
- package/dist/agents-host.js +45 -18
- package/dist/cli-args.d.ts +1 -1
- package/dist/cli-args.js +2 -2
- package/dist/compatibility-gates.d.ts +4 -1
- package/dist/compatibility-gates.js +42 -3
- package/dist/config.d.ts +8 -0
- package/dist/config.js +54 -16
- package/dist/gateway/localhost-gateway.js +5 -1
- package/dist/local-config.js +20 -1
- package/dist/managed-daemon.js +199 -49
- package/dist/providers/claude/cli-client.d.ts +55 -16
- package/dist/providers/claude/cli-client.js +811 -345
- package/dist/providers/create-provider.js +8 -13
- package/dist/types.d.ts +1 -0
- package/package.json +3 -2
- package/skills/borgee-agent/borgee-agent.mjs +2 -1
- package/skills/borgee-agent/borgee-agent.py +3 -1
package/README.md
CHANGED
|
@@ -54,6 +54,7 @@ agents-host start-managed <serverUrl> <apiKey> [agent-options]
|
|
|
54
54
|
agents-host describe-managed <serverUrl>
|
|
55
55
|
agents-host apply-managed <serverUrl> --stdin
|
|
56
56
|
agents-host apply-managed <serverUrl> --spec-json <json>
|
|
57
|
+
agents-host cleanup-managed <serverUrl> [--purge]
|
|
57
58
|
agents-host log <serverUrl> [--lines <n>] [--follow]
|
|
58
59
|
agents-host log <serverUrl> --path
|
|
59
60
|
agents-host start --config <path-to-host-config> [--debug]
|
|
@@ -77,7 +78,28 @@ RUNTIME_PROVIDER=claude \
|
|
|
77
78
|
pnpm --filter @borgee/agents-host dev
|
|
78
79
|
```
|
|
79
80
|
|
|
80
|
-
Codex
|
|
81
|
+
Codex now ships on the same default hosted-provider surface as Claude and Copilot. Select `RUNTIME_PROVIDER=codex` directly unless you are explicitly rolling that path back with `AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES=codex-provider`.
|
|
82
|
+
|
|
83
|
+
### Shipped internal compatibility defaults and rollback controls
|
|
84
|
+
|
|
85
|
+
The current shipped internal compatibility surface resolves gates in this order:
|
|
86
|
+
|
|
87
|
+
1. start from the shipped defaults: `copilot-provider-v2`, `codex-provider`, `managed-runtime-convergence`, and `policy-audit-enforcement`
|
|
88
|
+
2. add any explicit entries from `AGENTS_HOST_INTERNAL_COMPATIBILITY_GATES`
|
|
89
|
+
3. subtract any explicit entries from `AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES`
|
|
90
|
+
|
|
91
|
+
The shipped default set now also includes `connections-state-layer`, `context-injection`, `skill-runtime`, `localhost-gateway`, `token-binding`, `collaboration-outcome-model`, `task-thread-collaboration-contract`, and `collaboration-capabilities-diagnostics`. `attention-follow-semantics`, `collaboration-skill-first`, and hosted `praestoclaw-provider` still remain opt-in.
|
|
92
|
+
|
|
93
|
+
Managed-runtime settings still preserve the managed rollback rule for `connections-state-layer` plus `token-binding`: when `managed-runtime-convergence` remains enabled, the managed surface forces that pair on; explicitly disabling `managed-runtime-convergence` removes that pair from the managed surface again unless you also explicitly re-enable them.
|
|
94
|
+
|
|
95
|
+
Managed runtime precedence stays intentional: detached daemon respawns always reuse the persisted `managed-runtime-settings.json` snapshot, bare `start-managed <serverUrl>` and `apply-managed` treat the caller's current env as an explicit request to update that managed runtime surface, and full `start-managed <serverUrl> <apiKey> ...` validates the agent upsert against the existing managed-root snapshot before ambient rollback env can reject an otherwise valid existing surface.
|
|
96
|
+
Legacy managed roots created before `managed-runtime-settings.json` existed need one bare `agents-host start-managed <serverUrl>` migration pass before full per-agent updates can rely on that snapshot-backed precedence.
|
|
97
|
+
|
|
98
|
+
Rollback and hardening controls stay explicit:
|
|
99
|
+
|
|
100
|
+
- `AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES=<comma-list>` disables shipped gates for rollback
|
|
101
|
+
- `AGENTS_HOST_INTERNAL_PROVIDER_IMPLEMENTATIONS=copilot:v1` forces Copilot back to the v1 adapter while its v2 gate stays enabled; stale `claude:v1|v2` entries are tolerated as no-ops for compatibility
|
|
102
|
+
- `AGENTS_HOST_INTERNAL_POLICY_MODE=enforce` hardens the shipped `policy-audit-enforcement` gate beyond its default `audit-only` mode
|
|
81
103
|
|
|
82
104
|
Or with the CLI:
|
|
83
105
|
|
|
@@ -117,6 +139,7 @@ and resumable in that case.
|
|
|
117
139
|
Each managed runtime root keeps:
|
|
118
140
|
|
|
119
141
|
- `agents-host.yaml` and `agents/` as the stable generated local-config entrypoints
|
|
142
|
+
- `managed-runtime-settings.json` as the persisted managed runtime surface snapshot
|
|
120
143
|
- `daemon.log` as the detached managed daemon stdout/stderr log file
|
|
121
144
|
- `.state/` as the per-agent runtime state base
|
|
122
145
|
- `ctl.sock` as the local control socket
|
|
@@ -210,8 +233,8 @@ Notes:
|
|
|
210
233
|
| `BORGEE_AGENT_API_KEY` | positional `<apiKey>` | yes | — | Agent API key from the web UI |
|
|
211
234
|
| `AGENTS_HOST_DEBUG` | `--debug` | no | off | Extra host/provider lifecycle logs. Set `AGENTS_HOST_DEBUG=1` to enable without the CLI flag. |
|
|
212
235
|
| `BORGEE_AGENT_NAME` | `--name` | no | `Assistant` | Display name used in prompts |
|
|
213
|
-
| `RUNTIME_PROVIDER` | `--provider` | no | `claude` | `claude`, `copilot`, or `codex`
|
|
214
|
-
| `CLAUDE_COMMAND` / `CLAUDE_ARGS` | `--claude-command` / `--claude-args` | no | `claude` /
|
|
236
|
+
| `RUNTIME_PROVIDER` | `--provider` | no | `claude` | `claude`, `copilot`, or `codex` |
|
|
237
|
+
| `CLAUDE_COMMAND` / `CLAUDE_ARGS` | `--claude-command` / `--claude-args` | no | `claude-agent-acp` / empty | Local Claude ACP adapter command + args. The shipped bundled `claude-agent-acp` path requires empty `CLAUDE_ARGS`; non-empty args are only valid when `CLAUDE_COMMAND` points at a custom ACP adapter command. Legacy `claude --print --permission-mode bypassPermissions` is tolerated as a compatibility alias. |
|
|
215
238
|
| `CODEX_COMMAND` / `CODEX_ARGS` | `--codex-command` / `--codex-args` | no | `codex-acp` / empty | Local Codex ACP adapter command + args. The default launch resolves the bundled `@agentclientprotocol/codex-acp` entrypoint. |
|
|
216
239
|
| `COPILOT_COMMAND` / `COPILOT_ARGS` | `--copilot-command` / `--copilot-args` | no | `copilot` / parsed but ignored by ACP runtime | Local Copilot CLI command. The persistent Copilot ACP prototype always launches `copilot --acp`. |
|
|
217
240
|
| `COPILOT_SESSION_TTL_MINUTES` | `--copilot-session-ttl-minutes` | no | `2880` | Idle TTL for per-channel Copilot ACP sessions |
|
|
@@ -294,8 +317,8 @@ The spec shape is:
|
|
|
294
317
|
"host": {
|
|
295
318
|
"borgeeBaseUrl": "https://borgee.example.com",
|
|
296
319
|
"defaults": {
|
|
297
|
-
"claudeCommand": "claude",
|
|
298
|
-
"claudeArgs": [
|
|
320
|
+
"claudeCommand": "claude-agent-acp",
|
|
321
|
+
"claudeArgs": [],
|
|
299
322
|
"codexCommand": "codex-acp",
|
|
300
323
|
"codexArgs": [],
|
|
301
324
|
"copilotCommand": "copilot",
|
|
@@ -361,11 +384,8 @@ paths are also allowed.
|
|
|
361
384
|
borgeeBaseUrl: https://borgee.example.com
|
|
362
385
|
agentsDir: ./agents
|
|
363
386
|
defaults:
|
|
364
|
-
claudeCommand: claude
|
|
365
|
-
claudeArgs:
|
|
366
|
-
- --print
|
|
367
|
-
- --permission-mode
|
|
368
|
-
- bypassPermissions
|
|
387
|
+
claudeCommand: claude-agent-acp
|
|
388
|
+
claudeArgs: []
|
|
369
389
|
codexCommand: codex-acp
|
|
370
390
|
codexArgs: []
|
|
371
391
|
copilotCommand: copilot
|
|
@@ -401,12 +421,6 @@ name: Support Bot
|
|
|
401
421
|
apiKey: bgr_support_xxxxxxxx
|
|
402
422
|
provider: claude
|
|
403
423
|
enabled: true
|
|
404
|
-
claudeArgs:
|
|
405
|
-
- --print
|
|
406
|
-
- --permission-mode
|
|
407
|
-
- bypassPermissions
|
|
408
|
-
- --model
|
|
409
|
-
- sonnet
|
|
410
424
|
```
|
|
411
425
|
|
|
412
426
|
Agent config fields:
|
|
@@ -414,8 +428,9 @@ Agent config fields:
|
|
|
414
428
|
- `key` (**required**): stable identity used for diffing and reload decisions
|
|
415
429
|
- `name` (**required**)
|
|
416
430
|
- `apiKey` (**required**)
|
|
417
|
-
- `provider` (**required**): `claude`, `copilot`, or
|
|
431
|
+
- `provider` (**required**): `claude`, `copilot`, or `codex` (roll back Codex only with `AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES=codex-provider`)
|
|
418
432
|
- `enabled` (optional, default `true`)
|
|
433
|
+
- `claudeCommand` / `claudeArgs` (optional): leave unset to use the shipped bundled `claude-agent-acp` path. On that bundled path `claudeArgs` must stay empty. Non-empty `claudeArgs` are only supported when `claudeCommand` points at a custom ACP adapter command.
|
|
419
434
|
- optional overrides for `claudeCommand`, `claudeArgs`, `codexCommand`,
|
|
420
435
|
`codexArgs`, `copilotCommand`, `copilotArgs`, and
|
|
421
436
|
`copilotSessionTtlMinutes`
|
|
@@ -455,10 +470,16 @@ successfully applied snapshot running.
|
|
|
455
470
|
Each Borgee channel is mapped 1:1 to a provider-native session while it stays
|
|
456
471
|
active:
|
|
457
472
|
|
|
458
|
-
- **Claude**:
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
473
|
+
- **Claude**: one persistent `@agentclientprotocol/claude-agent-acp` subprocess
|
|
474
|
+
is shared by a single `AgentsHost`, with one ACP session per Borgee channel.
|
|
475
|
+
Same-channel turns are serialized; different channels keep isolated ACP
|
|
476
|
+
sessions. Ordinary threads keep the shared runtime workspace cwd, while
|
|
477
|
+
eligible `task_assignment` threads switch the real ACP session cwd to that
|
|
478
|
+
same hidden local task-scoped workspace. The host persists the
|
|
479
|
+
channel→ACP-session map in its state root, restores same-host continuity with
|
|
480
|
+
ACP `session/resume` when available, retries stale restores with a fresh
|
|
481
|
+
session immediately, and injects the per-channel local context paths through
|
|
482
|
+
Claude ACP `_meta.systemPrompt.append`.
|
|
462
483
|
- **Codex**: one persistent `@agentclientprotocol/codex-acp` subprocess is
|
|
463
484
|
shared by a single `AgentsHost`, with one ACP session per Borgee channel. When
|
|
464
485
|
the current turn has a materialized channel context payload, Codex refreshes a
|
|
@@ -481,13 +502,6 @@ active:
|
|
|
481
502
|
state root and restores same-host continuity with ACP `session/resume` when
|
|
482
503
|
available, otherwise `session/load` plus local replay cleanup on runtimes
|
|
483
504
|
that advertise only load support.
|
|
484
|
-
- **Claude**: Claude still keeps one provider-native session mapping per Borgee
|
|
485
|
-
channel, but unlike the ACP-backed adapters it spawns a fresh CLI process for
|
|
486
|
-
each turn. Ordinary threads inherit the host runtime cwd; eligible
|
|
487
|
-
`task_assignment` threads run the CLI process with the hidden local
|
|
488
|
-
task-scoped workspace as the process cwd, again without implying that the
|
|
489
|
-
target repository is already checked out there.
|
|
490
|
-
|
|
491
505
|
There is no separate transcript/history store on our side. Claude, Codex, and
|
|
492
506
|
Copilot only persist the native session ids they should try to resume later;
|
|
493
507
|
cross-host continuity still does not exist in this runtime.
|
package/dist/agents-host.d.ts
CHANGED
|
@@ -128,7 +128,7 @@ export declare class AgentsHost {
|
|
|
128
128
|
private resolveVisibleTurnBody;
|
|
129
129
|
private shouldRelayProtocolReply;
|
|
130
130
|
private normalizeMessage;
|
|
131
|
-
private
|
|
131
|
+
private resolveTaskAssignmentDelivery;
|
|
132
132
|
private ensureSelfAgentId;
|
|
133
133
|
private classifyAuthor;
|
|
134
134
|
private refreshParticipantDirectory;
|
package/dist/agents-host.js
CHANGED
|
@@ -527,6 +527,11 @@ export class AgentsHost {
|
|
|
527
527
|
if (!normalized) {
|
|
528
528
|
return;
|
|
529
529
|
}
|
|
530
|
+
const effective = await this.resolveTaskAssignmentDelivery(msg, normalized);
|
|
531
|
+
if (!effective) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
msg = effective;
|
|
530
535
|
let taskThreadContext = { state: 'inactive' };
|
|
531
536
|
let attentionSnapshot;
|
|
532
537
|
if (this.attentionFollowSemanticsEnabled
|
|
@@ -542,9 +547,6 @@ export class AgentsHost {
|
|
|
542
547
|
}
|
|
543
548
|
}
|
|
544
549
|
}
|
|
545
|
-
if (msg.message_type === 'task_assignment') {
|
|
546
|
-
await this.markAssignedTaskInProgress(msg.channel_id, normalized.content);
|
|
547
|
-
}
|
|
548
550
|
await this.runUngatedTurn(msg, normalized.authorId, normalized.content, taskThreadContext, attentionSnapshot);
|
|
549
551
|
}
|
|
550
552
|
async handleCollaborationMessage(msg) {
|
|
@@ -552,9 +554,11 @@ export class AgentsHost {
|
|
|
552
554
|
if (!normalized) {
|
|
553
555
|
return;
|
|
554
556
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
+
const effective = await this.resolveTaskAssignmentDelivery(msg, normalized);
|
|
558
|
+
if (!effective) {
|
|
559
|
+
return;
|
|
557
560
|
}
|
|
561
|
+
msg = effective;
|
|
558
562
|
const channelState = this.ensureCollaborationChannelState(msg.channel_id);
|
|
559
563
|
await this.ensureProtocolStateLoaded(msg.channel_id, channelState);
|
|
560
564
|
await this.ensureAttentionSnapshotLoaded(msg.channel_id, channelState);
|
|
@@ -2640,28 +2644,51 @@ export class AgentsHost {
|
|
|
2640
2644
|
content,
|
|
2641
2645
|
};
|
|
2642
2646
|
}
|
|
2643
|
-
|
|
2647
|
+
// Only a task assigned to this agent drives a task-assignment turn: it keeps the message a
|
|
2648
|
+
// task_assignment (and best-effort flips an open task to in_progress). An unassigned or
|
|
2649
|
+
// other-assigned task_assignment degrades to an ordinary message, and this agent's own echo of
|
|
2650
|
+
// it drops, so the creator never auto-starts work it was not assigned.
|
|
2651
|
+
async resolveTaskAssignmentDelivery(msg, normalized) {
|
|
2652
|
+
if (msg.message_type !== 'task_assignment') {
|
|
2653
|
+
return msg;
|
|
2654
|
+
}
|
|
2655
|
+
const selfAgentId = await this.ensureSelfAgentId();
|
|
2656
|
+
let task;
|
|
2644
2657
|
try {
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
preferredTaskId: extractTaskIdFromTaskAssignmentContent(content),
|
|
2658
|
+
task = await waitForTaskForThread(this.borgee, msg.channel_id, {
|
|
2659
|
+
preferredTaskId: extractTaskIdFromTaskAssignmentContent(normalized.content),
|
|
2648
2660
|
attempts: 10,
|
|
2649
2661
|
delayMs: 250,
|
|
2650
2662
|
});
|
|
2651
|
-
if (!task || task.status !== 'open' || task.assigneeId !== selfAgentId) {
|
|
2652
|
-
return;
|
|
2653
|
-
}
|
|
2654
|
-
await this.borgee.updateTask({
|
|
2655
|
-
taskId: task.id,
|
|
2656
|
-
status: 'in_progress',
|
|
2657
|
-
});
|
|
2658
2663
|
}
|
|
2659
2664
|
catch (error) {
|
|
2660
|
-
this.logger.debugError('
|
|
2661
|
-
channelId:
|
|
2665
|
+
this.logger.debugError('task-assignment resolution failed', {
|
|
2666
|
+
channelId: msg.channel_id,
|
|
2662
2667
|
error,
|
|
2663
2668
|
});
|
|
2669
|
+
task = null;
|
|
2670
|
+
}
|
|
2671
|
+
if (task && task.assigneeId === selfAgentId) {
|
|
2672
|
+
if (task.status === 'open') {
|
|
2673
|
+
try {
|
|
2674
|
+
await this.borgee.updateTask({
|
|
2675
|
+
taskId: task.id,
|
|
2676
|
+
status: 'in_progress',
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2679
|
+
catch (error) {
|
|
2680
|
+
this.logger.debugError('best-effort task auto-start failed', {
|
|
2681
|
+
channelId: msg.channel_id,
|
|
2682
|
+
error,
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
return msg;
|
|
2687
|
+
}
|
|
2688
|
+
if (normalized.authorId === selfAgentId) {
|
|
2689
|
+
return null;
|
|
2664
2690
|
}
|
|
2691
|
+
return { ...msg, message_type: 'default' };
|
|
2665
2692
|
}
|
|
2666
2693
|
async ensureSelfAgentId() {
|
|
2667
2694
|
if (this.selfAgentId) {
|
package/dist/cli-args.d.ts
CHANGED
|
@@ -83,4 +83,4 @@ export declare function parseLogArgs(argv: string[]): ParsedLogArgs;
|
|
|
83
83
|
export declare function parseDescribeManagedArgs(argv: string[]): ParsedDescribeManagedArgs;
|
|
84
84
|
export declare function parseCleanupManagedArgs(argv: string[]): ParsedCleanupManagedArgs;
|
|
85
85
|
export declare function parseApplyManagedArgs(argv: string[]): ParsedApplyManagedArgs;
|
|
86
|
-
export declare const USAGE = "Usage:\n agents-host start <serverUrl> <apiKey> [options]\n agents-host start-managed <serverUrl>\n agents-host start-managed <serverUrl> <apiKey> [agent-options]\n agents-host describe-managed <serverUrl>\n agents-host cleanup-managed <serverUrl> [--purge]\n agents-host apply-managed <serverUrl> --stdin\n agents-host apply-managed <serverUrl> --spec-json <json>\n agents-host log <serverUrl> [--lines <n>] [--follow]\n agents-host log <serverUrl> --path\n agents-host start --config <path-to-host-config> [--debug]\n agents-host validate --config <path-to-host-config>\n agents-host describe --config <path-to-host-config>\n agents-host print-layout --root <dir>\n agents-host generate-config --root <dir> --stdin\n agents-host generate-config --root <dir> --spec-json <json>\n\nForeground start options:\n --debug Enable host/provider debug logs (or set AGENTS_HOST_DEBUG=1)\n\nSingle-agent agent options:\n --name <name> Display name (default: Assistant)\n --provider <claude|codex|copilot>\n Runtime provider (default: claude)\n --claude-command <cmd> Local Claude
|
|
86
|
+
export declare const USAGE = "Usage:\n agents-host start <serverUrl> <apiKey> [options]\n agents-host start-managed <serverUrl>\n agents-host start-managed <serverUrl> <apiKey> [agent-options]\n agents-host describe-managed <serverUrl>\n agents-host cleanup-managed <serverUrl> [--purge]\n agents-host apply-managed <serverUrl> --stdin\n agents-host apply-managed <serverUrl> --spec-json <json>\n agents-host log <serverUrl> [--lines <n>] [--follow]\n agents-host log <serverUrl> --path\n agents-host start --config <path-to-host-config> [--debug]\n agents-host validate --config <path-to-host-config>\n agents-host describe --config <path-to-host-config>\n agents-host print-layout --root <dir>\n agents-host generate-config --root <dir> --stdin\n agents-host generate-config --root <dir> --spec-json <json>\n\nForeground start options:\n --debug Enable host/provider debug logs (or set AGENTS_HOST_DEBUG=1)\n\nSingle-agent agent options:\n --name <name> Display name (default: Assistant)\n --provider <claude|codex|copilot>\n Runtime provider (default: claude)\n --claude-command <cmd> Local Claude ACP adapter command (default: claude-agent-acp)\n --claude-args <args> Local Claude ACP adapter args (empty on the shipped bundled path)\n --codex-command <cmd> Local Codex ACP adapter command (default: codex-acp)\n --codex-args <args> Local Codex ACP adapter args\n --copilot-command <cmd> Local Copilot CLI command (default: copilot)\n --copilot-args <args> Ignored by the Copilot ACP prototype\n --copilot-session-ttl-minutes <minutes>\n Idle session TTL for Copilot ACP sessions (default: 2880)\n\nCommand summary:\n start <serverUrl> <apiKey> Start one foreground hosted agent (legacy-compatible behavior)\n start --config <path> Start agents + supervisor + watchers from a host config file\n start-managed <serverUrl> Restart or resume an existing per-serverUrl managed daemon\n start-managed <serverUrl> <apiKey>\n Upsert one agent into the per-serverUrl local daemon and exit after reconcile\n describe-managed <serverUrl> Print managed-runtime spec JSON for machine callers\n cleanup-managed <serverUrl> Stop one managed-runtime daemon locally; --purge also removes its runtime root\n apply-managed <serverUrl> Apply one managed-runtime full-set spec for machine callers\n log <serverUrl> Print the managed daemon log tail for one server runtime\n validate --config <path> Validate local-config files without starting agents or watchers\n describe --config <path> Print the current managed full-set spec as JSON\n print-layout --root <dir> Print the canonical default local-config layout as JSON\n generate-config --root <dir> --stdin Materialize canonical local-config files from stdin\n generate-config --root <dir> --spec-json <json>\n Compatibility input; JSON is exposed in process arguments\n\nExamples:\n agents-host start https://borgee.example.com bgr_xxxxxxxx --provider copilot --debug\n agents-host start-managed https://borgee.example.com\n agents-host start-managed https://borgee.example.com bgr_xxxxxxxx --provider copilot\n agents-host describe-managed https://borgee.example.com\n agents-host cleanup-managed https://borgee.example.com --purge\n printf '%s' '{\"host\":{\"borgeeBaseUrl\":\"https://borgee.example.com\"},\"agents\":[{\"key\":\"cp1\",\"name\":\"Copilot\",\"apiKey\":\"bgr_xxx\",\"provider\":\"copilot\"}]}' | agents-host apply-managed https://borgee.example.com --stdin\n agents-host log https://borgee.example.com --lines 200 --follow\n agents-host log https://borgee.example.com --path\n agents-host start --config ./agents-host.yaml --debug\n agents-host validate --config ./agents-host.yaml\n agents-host describe --config ./agents-host.yaml\n agents-host print-layout --root ./runtime-root\n printf '%s' '{\"host\":{\"borgeeBaseUrl\":\"https://borgee.example.com\"},\"agents\":[{\"key\":\"cp1\",\"name\":\"Copilot\",\"apiKey\":\"bgr_xxx\",\"provider\":\"copilot\"}]}' | agents-host generate-config --root ./runtime-root --stdin\n";
|
package/dist/cli-args.js
CHANGED
|
@@ -494,8 +494,8 @@ Single-agent agent options:
|
|
|
494
494
|
--name <name> Display name (default: Assistant)
|
|
495
495
|
--provider <claude|codex|copilot>
|
|
496
496
|
Runtime provider (default: claude)
|
|
497
|
-
--claude-command <cmd> Local Claude
|
|
498
|
-
--claude-args <args> Local Claude
|
|
497
|
+
--claude-command <cmd> Local Claude ACP adapter command (default: claude-agent-acp)
|
|
498
|
+
--claude-args <args> Local Claude ACP adapter args (empty on the shipped bundled path)
|
|
499
499
|
--codex-command <cmd> Local Codex ACP adapter command (default: codex-acp)
|
|
500
500
|
--codex-args <args> Local Codex ACP adapter args
|
|
501
501
|
--copilot-command <cmd> Local Copilot CLI command (default: copilot)
|
|
@@ -14,6 +14,7 @@ export declare const TOKEN_BINDING_COMPATIBILITY_GATE = "token-binding";
|
|
|
14
14
|
export declare const POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE = "policy-audit-enforcement";
|
|
15
15
|
export declare const MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE = "managed-runtime-convergence";
|
|
16
16
|
export declare const COMPATIBILITY_GATES_ENV = "AGENTS_HOST_INTERNAL_COMPATIBILITY_GATES";
|
|
17
|
+
export declare const INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV = "AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES";
|
|
17
18
|
export declare const INTERNAL_POLICY_MODE_ENV = "AGENTS_HOST_INTERNAL_POLICY_MODE";
|
|
18
19
|
export declare const INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV = "AGENTS_HOST_INTERNAL_PROVIDER_IMPLEMENTATIONS";
|
|
19
20
|
export declare const MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION = 1;
|
|
@@ -27,8 +28,10 @@ export interface ManagedRuntimeSettingsSnapshot {
|
|
|
27
28
|
providerImplementationOverrides: string[];
|
|
28
29
|
internalPolicyMode: InternalPolicyMode;
|
|
29
30
|
}
|
|
31
|
+
export declare const DEFAULT_INTERNAL_COMPATIBILITY_GATES: readonly string[];
|
|
30
32
|
export declare function parseCompatibilityGates(rawValue: string | undefined): ReadonlySet<string>;
|
|
31
|
-
export declare function resolveInternalCompatibilityGates(): ReadonlySet<string>;
|
|
33
|
+
export declare function resolveInternalCompatibilityGates(env?: NodeJS.ProcessEnv): ReadonlySet<string>;
|
|
34
|
+
export declare function resolveDisabledDefaultCompatibilityGates(compatibilityGates: Iterable<string>): string[];
|
|
32
35
|
export declare function normalizeInternalProviderImplementationOverrides(rawValue: string | undefined): string[];
|
|
33
36
|
export declare function parseInternalProviderImplementationOverrides(rawValue: string | undefined): InternalProviderImplementationOverrides;
|
|
34
37
|
export declare function resolveManagedRuntimeProviderImplementationOverrides(env?: NodeJS.ProcessEnv, compatibilityGates?: string[]): string[];
|
|
@@ -15,10 +15,25 @@ export const TOKEN_BINDING_COMPATIBILITY_GATE = 'token-binding';
|
|
|
15
15
|
export const POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE = 'policy-audit-enforcement';
|
|
16
16
|
export const MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE = 'managed-runtime-convergence';
|
|
17
17
|
export const COMPATIBILITY_GATES_ENV = 'AGENTS_HOST_INTERNAL_COMPATIBILITY_GATES';
|
|
18
|
+
export const INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV = 'AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES';
|
|
18
19
|
export const INTERNAL_POLICY_MODE_ENV = 'AGENTS_HOST_INTERNAL_POLICY_MODE';
|
|
19
20
|
export const INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV = 'AGENTS_HOST_INTERNAL_PROVIDER_IMPLEMENTATIONS';
|
|
20
21
|
export const MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION = 1;
|
|
21
22
|
const VALID_PROVIDER_IMPLEMENTATION_ENTRIES = '"claude:v1", "claude:v2", "copilot:v1", or "copilot:v2"';
|
|
23
|
+
export const DEFAULT_INTERNAL_COMPATIBILITY_GATES = Object.freeze([
|
|
24
|
+
COLLABORATION_CAPABILITIES_DIAGNOSTICS_COMPATIBILITY_GATE,
|
|
25
|
+
COLLABORATION_OUTCOME_MODEL_COMPATIBILITY_GATE,
|
|
26
|
+
CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE,
|
|
27
|
+
CONTEXT_INJECTION_COMPATIBILITY_GATE,
|
|
28
|
+
CODEX_PROVIDER_COMPATIBILITY_GATE,
|
|
29
|
+
COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
|
|
30
|
+
LOCALHOST_GATEWAY_COMPATIBILITY_GATE,
|
|
31
|
+
MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE,
|
|
32
|
+
POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE,
|
|
33
|
+
SKILL_RUNTIME_COMPATIBILITY_GATE,
|
|
34
|
+
TASK_THREAD_COLLABORATION_CONTRACT_COMPATIBILITY_GATE,
|
|
35
|
+
TOKEN_BINDING_COMPATIBILITY_GATE,
|
|
36
|
+
]);
|
|
22
37
|
const PROVIDER_IMPLEMENTATION_COMPATIBILITY_GATES = {
|
|
23
38
|
claude: CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE,
|
|
24
39
|
copilot: COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
|
|
@@ -50,8 +65,19 @@ export function parseCompatibilityGates(rawValue) {
|
|
|
50
65
|
}
|
|
51
66
|
return gates;
|
|
52
67
|
}
|
|
53
|
-
export function resolveInternalCompatibilityGates() {
|
|
54
|
-
|
|
68
|
+
export function resolveInternalCompatibilityGates(env = process.env) {
|
|
69
|
+
const compatibilityGates = new Set(DEFAULT_INTERNAL_COMPATIBILITY_GATES);
|
|
70
|
+
for (const gate of parseCompatibilityGates(env[COMPATIBILITY_GATES_ENV])) {
|
|
71
|
+
compatibilityGates.add(gate);
|
|
72
|
+
}
|
|
73
|
+
for (const gate of parseCompatibilityGates(env[INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV])) {
|
|
74
|
+
compatibilityGates.delete(gate);
|
|
75
|
+
}
|
|
76
|
+
return compatibilityGates;
|
|
77
|
+
}
|
|
78
|
+
export function resolveDisabledDefaultCompatibilityGates(compatibilityGates) {
|
|
79
|
+
const enabledGates = new Set(compatibilityGates);
|
|
80
|
+
return DEFAULT_INTERNAL_COMPATIBILITY_GATES.filter((gate) => !enabledGates.has(gate));
|
|
55
81
|
}
|
|
56
82
|
export function normalizeInternalProviderImplementationOverrides(rawValue) {
|
|
57
83
|
if (!rawValue || rawValue.trim().length === 0) {
|
|
@@ -91,15 +117,28 @@ export function resolveManagedRuntimeProviderImplementationOverrides(env = proce
|
|
|
91
117
|
const enabledGates = new Set(compatibilityGates);
|
|
92
118
|
return normalizeInternalProviderImplementationOverrides(env[INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV]).filter((assignment) => {
|
|
93
119
|
const [provider] = assignment.split(':');
|
|
120
|
+
if (provider === 'claude') {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
94
123
|
return enabledGates.has(PROVIDER_IMPLEMENTATION_COMPATIBILITY_GATES[provider]);
|
|
95
124
|
});
|
|
96
125
|
}
|
|
97
126
|
export function resolveManagedRuntimeCompatibilityGates(env = process.env) {
|
|
98
|
-
const compatibilityGates = new Set(
|
|
127
|
+
const compatibilityGates = new Set(resolveInternalCompatibilityGates(env));
|
|
128
|
+
compatibilityGates.delete(CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE);
|
|
129
|
+
const explicitlyEnabledGates = parseCompatibilityGates(env[COMPATIBILITY_GATES_ENV]);
|
|
99
130
|
if (compatibilityGates.has(MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE)) {
|
|
100
131
|
compatibilityGates.add(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE);
|
|
101
132
|
compatibilityGates.add(TOKEN_BINDING_COMPATIBILITY_GATE);
|
|
102
133
|
}
|
|
134
|
+
else {
|
|
135
|
+
if (!explicitlyEnabledGates.has(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE)) {
|
|
136
|
+
compatibilityGates.delete(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE);
|
|
137
|
+
}
|
|
138
|
+
if (!explicitlyEnabledGates.has(TOKEN_BINDING_COMPATIBILITY_GATE)) {
|
|
139
|
+
compatibilityGates.delete(TOKEN_BINDING_COMPATIBILITY_GATE);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
103
142
|
return sortUniqueStrings(compatibilityGates);
|
|
104
143
|
}
|
|
105
144
|
export function resolveInternalPolicyMode(gateEnabled, env = process.env) {
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { AgentsHostConfig, ProviderCommandConfig, ProviderKind } from './types.js';
|
|
2
2
|
export declare const MAX_COPILOT_SESSION_TTL_MINUTES: number;
|
|
3
3
|
export declare const DEFAULT_COPILOT_SESSION_TTL_MINUTES: number;
|
|
4
|
+
export declare const DEFAULT_CLAUDE_COMMAND = "claude-agent-acp";
|
|
5
|
+
export declare const LEGACY_CLAUDE_COMMAND = "claude";
|
|
6
|
+
export declare const LEGACY_CLAUDE_ONE_SHOT_ARGS: readonly ["--print", "--permission-mode", "bypassPermissions"];
|
|
4
7
|
export declare const DEFAULT_PROVIDER_COMMAND_CONFIG: ProviderCommandConfig;
|
|
5
8
|
export declare function requireNonEmptyString(value: unknown, message: string): string;
|
|
6
9
|
export declare function optionalNonEmptyString(value: unknown, fieldName: string, sourceLabel: string): string | undefined;
|
|
@@ -9,5 +12,10 @@ export declare function optionalStringArray(value: unknown, fieldName: string, s
|
|
|
9
12
|
export declare function parseCopilotSessionTtlMinutesValue(value: unknown, sourceLabel: string): number;
|
|
10
13
|
export declare function resolveProvider(rawValue: string, sourceLabel: string): ProviderKind;
|
|
11
14
|
export declare function assertProviderCompatibility(provider: ProviderKind, sourceLabel: string, env?: NodeJS.ProcessEnv): void;
|
|
15
|
+
export declare function isLegacyClaudeCompatibilityAlias(command: string, args: string[]): boolean;
|
|
16
|
+
export declare function isLegacyClaudeBinaryCommand(command: string): boolean;
|
|
17
|
+
export declare function hasLegacyClaudeOneShotArgs(args: string[]): boolean;
|
|
18
|
+
export declare function assertClaudeCommandCompatibility(command: string, args: string[], sourceLabel: string): void;
|
|
19
|
+
export declare function assertProviderCommandCompatibility(provider: ProviderKind, config: Pick<ProviderCommandConfig, 'claudeCommand' | 'claudeArgs'>, sourceLabel: string): void;
|
|
12
20
|
export declare function resolveProviderCommandConfig(overrides?: Partial<ProviderCommandConfig>): ProviderCommandConfig;
|
|
13
21
|
export declare function loadConfigFromEnv(env?: NodeJS.ProcessEnv): AgentsHostConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
1
2
|
import { resolveSingleAgentStateRoot } from './state-paths.js';
|
|
2
|
-
import { CODEX_PROVIDER_COMPATIBILITY_GATE,
|
|
3
|
+
import { CODEX_PROVIDER_COMPATIBILITY_GATE, resolveInternalCompatibilityGates, } from './compatibility-gates.js';
|
|
3
4
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
4
5
|
export const MAX_COPILOT_SESSION_TTL_MINUTES = MAX_TIMER_DELAY_MS / 60_000;
|
|
5
6
|
export const DEFAULT_COPILOT_SESSION_TTL_MINUTES = 2 * 24 * 60;
|
|
7
|
+
export const DEFAULT_CLAUDE_COMMAND = 'claude-agent-acp';
|
|
8
|
+
export const LEGACY_CLAUDE_COMMAND = 'claude';
|
|
9
|
+
export const LEGACY_CLAUDE_ONE_SHOT_ARGS = ['--print', '--permission-mode', 'bypassPermissions'];
|
|
6
10
|
export const DEFAULT_PROVIDER_COMMAND_CONFIG = {
|
|
7
|
-
claudeCommand:
|
|
8
|
-
claudeArgs: [
|
|
11
|
+
claudeCommand: DEFAULT_CLAUDE_COMMAND,
|
|
12
|
+
claudeArgs: [],
|
|
9
13
|
codexCommand: 'codex-acp',
|
|
10
14
|
codexArgs: [],
|
|
11
15
|
copilotCommand: 'copilot',
|
|
@@ -69,9 +73,41 @@ export function assertProviderCompatibility(provider, sourceLabel, env = process
|
|
|
69
73
|
if (provider !== 'codex') {
|
|
70
74
|
return;
|
|
71
75
|
}
|
|
72
|
-
const compatibilityGates =
|
|
76
|
+
const compatibilityGates = resolveInternalCompatibilityGates(env);
|
|
73
77
|
if (!compatibilityGates.has(CODEX_PROVIDER_COMPATIBILITY_GATE)) {
|
|
74
|
-
throw new Error(`${sourceLabel}: provider "codex" requires ${
|
|
78
|
+
throw new Error(`${sourceLabel}: provider "codex" requires the shipped ${CODEX_PROVIDER_COMPATIBILITY_GATE} path to remain enabled`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function isLegacyClaudeCompatibilityAlias(command, args) {
|
|
82
|
+
if (command !== LEGACY_CLAUDE_COMMAND || args.length !== LEGACY_CLAUDE_ONE_SHOT_ARGS.length) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return args.every((arg, index) => arg === LEGACY_CLAUDE_ONE_SHOT_ARGS[index]);
|
|
86
|
+
}
|
|
87
|
+
export function isLegacyClaudeBinaryCommand(command) {
|
|
88
|
+
return basename(command) === LEGACY_CLAUDE_COMMAND;
|
|
89
|
+
}
|
|
90
|
+
export function hasLegacyClaudeOneShotArgs(args) {
|
|
91
|
+
return (args.length === LEGACY_CLAUDE_ONE_SHOT_ARGS.length
|
|
92
|
+
&& args.every((arg, index) => arg === LEGACY_CLAUDE_ONE_SHOT_ARGS[index]));
|
|
93
|
+
}
|
|
94
|
+
export function assertClaudeCommandCompatibility(command, args, sourceLabel) {
|
|
95
|
+
if (command === DEFAULT_CLAUDE_COMMAND) {
|
|
96
|
+
if (args.length > 0) {
|
|
97
|
+
throw new Error(`Invalid Claude config in ${sourceLabel}: the shipped ${DEFAULT_CLAUDE_COMMAND} path does not accept CLAUDE_ARGS/claudeArgs. Leave them empty or point claudeCommand/CLAUDE_COMMAND at a custom ACP adapter command instead.`);
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (isLegacyClaudeBinaryCommand(command)) {
|
|
102
|
+
if (isLegacyClaudeCompatibilityAlias(command, args)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
throw new Error(`Invalid Claude config in ${sourceLabel}: raw "${LEGACY_CLAUDE_COMMAND}" commands no longer speak ACP directly. Use the shipped ${DEFAULT_CLAUDE_COMMAND} default, keep only the exact legacy compatibility alias "${LEGACY_CLAUDE_COMMAND} ${LEGACY_CLAUDE_ONE_SHOT_ARGS.join(' ')}", or point claudeCommand/CLAUDE_COMMAND at a real ACP adapter command.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function assertProviderCommandCompatibility(provider, config, sourceLabel) {
|
|
109
|
+
if (provider === 'claude') {
|
|
110
|
+
assertClaudeCommandCompatibility(config.claudeCommand, config.claudeArgs, sourceLabel);
|
|
75
111
|
}
|
|
76
112
|
}
|
|
77
113
|
export function resolveProviderCommandConfig(overrides = {}) {
|
|
@@ -89,20 +125,22 @@ export function loadConfigFromEnv(env = process.env) {
|
|
|
89
125
|
const provider = resolveProvider(envOr('RUNTIME_PROVIDER', 'claude', env), 'Unsupported RUNTIME_PROVIDER');
|
|
90
126
|
assertProviderCompatibility(provider, 'Unsupported RUNTIME_PROVIDER', env);
|
|
91
127
|
const agentApiKey = requireEnv('BORGEE_AGENT_API_KEY', env);
|
|
128
|
+
const providerConfig = resolveProviderCommandConfig({
|
|
129
|
+
claudeCommand: envOr('CLAUDE_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand, env),
|
|
130
|
+
claudeArgs: parseArgs(envOr('CLAUDE_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs.join(' '), env)),
|
|
131
|
+
codexCommand: envOr('CODEX_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.codexCommand, env),
|
|
132
|
+
codexArgs: parseArgs(envOr('CODEX_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.codexArgs.join(' '), env)),
|
|
133
|
+
copilotCommand: envOr('COPILOT_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotCommand, env),
|
|
134
|
+
copilotArgs: parseArgs(envOr('COPILOT_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotArgs.join(' '), env)),
|
|
135
|
+
copilotSessionTtlMinutes: env.COPILOT_SESSION_TTL_MINUTES && env.COPILOT_SESSION_TTL_MINUTES.trim().length > 0
|
|
136
|
+
? parseCopilotSessionTtlMinutesValue(env.COPILOT_SESSION_TTL_MINUTES, 'environment variable COPILOT_SESSION_TTL_MINUTES')
|
|
137
|
+
: DEFAULT_PROVIDER_COMMAND_CONFIG.copilotSessionTtlMinutes,
|
|
138
|
+
});
|
|
139
|
+
assertProviderCommandCompatibility(provider, providerConfig, 'environment variables');
|
|
92
140
|
return {
|
|
93
141
|
borgeeBaseUrl: requireEnv('BORGEE_BASE_URL', env),
|
|
94
142
|
stateRootDir: resolveSingleAgentStateRoot(env, undefined, agentApiKey),
|
|
95
|
-
...
|
|
96
|
-
claudeCommand: envOr('CLAUDE_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand, env),
|
|
97
|
-
claudeArgs: parseArgs(envOr('CLAUDE_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs.join(' '), env)),
|
|
98
|
-
codexCommand: envOr('CODEX_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.codexCommand, env),
|
|
99
|
-
codexArgs: parseArgs(envOr('CODEX_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.codexArgs.join(' '), env)),
|
|
100
|
-
copilotCommand: envOr('COPILOT_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotCommand, env),
|
|
101
|
-
copilotArgs: parseArgs(envOr('COPILOT_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotArgs.join(' '), env)),
|
|
102
|
-
copilotSessionTtlMinutes: env.COPILOT_SESSION_TTL_MINUTES && env.COPILOT_SESSION_TTL_MINUTES.trim().length > 0
|
|
103
|
-
? parseCopilotSessionTtlMinutesValue(env.COPILOT_SESSION_TTL_MINUTES, 'environment variable COPILOT_SESSION_TTL_MINUTES')
|
|
104
|
-
: DEFAULT_PROVIDER_COMMAND_CONFIG.copilotSessionTtlMinutes,
|
|
105
|
-
}),
|
|
143
|
+
...providerConfig,
|
|
106
144
|
agent: {
|
|
107
145
|
agentApiKey,
|
|
108
146
|
agentName: envOr('BORGEE_AGENT_NAME', 'Assistant', env),
|
|
@@ -778,8 +778,12 @@ function parseUpdateTaskInput(taskId, body) {
|
|
|
778
778
|
status: readOptionalString(body.status),
|
|
779
779
|
assigneeId: readOptionalString(body.assigneeId) ?? readOptionalString(body.assignee_id),
|
|
780
780
|
title: readOptionalString(body.title),
|
|
781
|
+
description: readOptionalString(body.description),
|
|
781
782
|
};
|
|
782
|
-
if (input.status === undefined &&
|
|
783
|
+
if (input.status === undefined &&
|
|
784
|
+
input.assigneeId === undefined &&
|
|
785
|
+
input.title === undefined &&
|
|
786
|
+
input.description === undefined) {
|
|
783
787
|
throw new GatewayHttpError(400, { error: 'no_updates' }, 'bad-request');
|
|
784
788
|
}
|
|
785
789
|
return input;
|
package/dist/local-config.js
CHANGED
|
@@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { dirname, extname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
4
|
import { parseDocument, stringify } from 'yaml';
|
|
5
|
-
import { assertProviderCompatibility, optionalNonEmptyString, optionalStringArray, parseCopilotSessionTtlMinutesValue, requireNonEmptyString, resolveProvider, resolveProviderCommandConfig, } from './config.js';
|
|
5
|
+
import { assertProviderCompatibility, assertProviderCommandCompatibility, optionalNonEmptyString, optionalStringArray, parseCopilotSessionTtlMinutesValue, requireNonEmptyString, resolveProvider, resolveProviderCommandConfig, } from './config.js';
|
|
6
6
|
import { resolveLocalConfigAgentStateRoot } from './state-paths.js';
|
|
7
7
|
const SUPPORTED_CONFIG_EXTENSIONS = new Set(['.json', '.yaml', '.yml']);
|
|
8
8
|
export const DEFAULT_LOCAL_HOST_CONFIG_FILENAME = 'agents-host.yaml';
|
|
@@ -186,6 +186,8 @@ export function parseGenerateConfigSpec(value, sourceLabel, env = process.env) {
|
|
|
186
186
|
throw new Error(`Invalid generate-config agents[${index}] in ${sourceLabel}: expected an object`);
|
|
187
187
|
}
|
|
188
188
|
const agent = parseAgentConfigFile(`generate-config agents[${index}] in ${sourceLabel}`, entry, env);
|
|
189
|
+
const providerConfig = resolveAgentProviderCommandConfig(host.defaults, agent);
|
|
190
|
+
assertProviderCommandCompatibility(agent.provider, providerConfig, `generate-config agents[${index}] in ${sourceLabel}`);
|
|
189
191
|
const existingIndex = seenKeys.get(agent.key);
|
|
190
192
|
if (existingIndex !== undefined) {
|
|
191
193
|
throw new Error(`Duplicate generate-config agent key "${agent.key}" in ${sourceLabel} at indexes ${existingIndex} and ${index}`);
|
|
@@ -598,6 +600,7 @@ function buildManagedAgentSnapshot(host, stateRootBaseDir, sourcePath, agent) {
|
|
|
598
600
|
...host.defaults,
|
|
599
601
|
...parseProviderCommandOverrides(agent, `agent config ${sourcePath}`),
|
|
600
602
|
});
|
|
603
|
+
assertProviderCommandCompatibility(agent.provider, providerConfig, `agent config ${sourcePath}`);
|
|
601
604
|
const config = {
|
|
602
605
|
borgeeBaseUrl: host.borgeeBaseUrl,
|
|
603
606
|
stateRootDir: resolveLocalConfigAgentStateRoot(stateRootBaseDir, agent.key),
|
|
@@ -614,6 +617,20 @@ function buildManagedAgentSnapshot(host, stateRootBaseDir, sourcePath, agent) {
|
|
|
614
617
|
config,
|
|
615
618
|
};
|
|
616
619
|
}
|
|
620
|
+
function resolveAgentProviderCommandConfig(hostDefaults, agent) {
|
|
621
|
+
return resolveProviderCommandConfig({
|
|
622
|
+
...hostDefaults,
|
|
623
|
+
...(agent.claudeCommand !== undefined ? { claudeCommand: agent.claudeCommand } : {}),
|
|
624
|
+
...(agent.claudeArgs !== undefined ? { claudeArgs: agent.claudeArgs } : {}),
|
|
625
|
+
...(agent.codexCommand !== undefined ? { codexCommand: agent.codexCommand } : {}),
|
|
626
|
+
...(agent.codexArgs !== undefined ? { codexArgs: agent.codexArgs } : {}),
|
|
627
|
+
...(agent.copilotCommand !== undefined ? { copilotCommand: agent.copilotCommand } : {}),
|
|
628
|
+
...(agent.copilotArgs !== undefined ? { copilotArgs: agent.copilotArgs } : {}),
|
|
629
|
+
...(agent.copilotSessionTtlMinutes !== undefined
|
|
630
|
+
? { copilotSessionTtlMinutes: agent.copilotSessionTtlMinutes }
|
|
631
|
+
: {}),
|
|
632
|
+
});
|
|
633
|
+
}
|
|
617
634
|
export async function loadLocalConfigSnapshot(hostConfigPath, deps = {}) {
|
|
618
635
|
const fileSystem = deps.fileSystem ?? nodeFileSystem;
|
|
619
636
|
const absoluteHostConfigPath = resolve(hostConfigPath);
|
|
@@ -698,6 +715,8 @@ export async function loadLocalConfigGenerateSpec(hostConfigPath, deps = {}) {
|
|
|
698
715
|
}
|
|
699
716
|
const agentRecord = await loadParsedDocument(fileSystem, agentFilePath, 'agent config');
|
|
700
717
|
const agentConfig = parseAgentConfigFile(agentFilePath, agentRecord, deps.env);
|
|
718
|
+
const providerConfig = resolveAgentProviderCommandConfig(hostConfig.defaults, agentConfig);
|
|
719
|
+
assertProviderCommandCompatibility(agentConfig.provider, providerConfig, `agent config ${agentFilePath}`);
|
|
701
720
|
const existingPath = seenKeys.get(agentConfig.key);
|
|
702
721
|
if (existingPath) {
|
|
703
722
|
throw new Error(`Duplicate agent key "${agentConfig.key}" in ${existingPath} and ${agentFilePath}`);
|