@ours.network/fleet 1.2.0-nightly.4 → 1.2.0-nightly.40
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 +165 -53
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -0
- package/dist/agent-ours/bridge.js +9 -0
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/harness.js +8 -0
- package/dist/agent-ours/runtime.d.ts +6 -0
- package/dist/agent-ours/runtime.js +36 -0
- package/dist/agent-ours/service.js +33 -13
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +25 -0
- package/dist/application/conversation-history.js +189 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +2 -2
- package/dist/application/legacy-supervisor-identity.d.ts +7 -0
- package/dist/application/legacy-supervisor-identity.js +43 -0
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/preset-provenance.d.ts +19 -0
- package/dist/application/preset-provenance.js +59 -0
- package/dist/application/role-creation-service.js +1 -1
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +18 -0
- package/dist/application/session-control.js +54 -3
- package/dist/application/supervisor-ours-tools.d.ts +159 -0
- package/dist/application/supervisor-ours-tools.js +113 -0
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +249 -0
- package/dist/application/task-room-service.d.ts +99 -3
- package/dist/application/task-room-service.js +296 -31
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/build-info.json +6 -4
- package/dist/capabilities.d.ts +3 -1
- package/dist/capabilities.js +4 -0
- package/dist/cli.js +186 -34
- package/dist/client-profile.d.ts +5 -16
- package/dist/client-profile.js +4 -112
- package/dist/config.d.ts +13 -0
- package/dist/config.js +19 -4
- package/dist/creation.js +2 -2
- package/dist/daemon-recovery.d.ts +1 -8
- package/dist/daemon-recovery.js +8 -113
- package/dist/docs.d.ts +3 -3
- package/dist/docs.js +44 -22
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +46 -59
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/fleet-command-audit.d.ts +1 -0
- package/dist/fleet-command-audit.js +11 -6
- package/dist/harness/claude-code.js +9 -1
- package/dist/harness/claude-runtime.d.ts +12 -0
- package/dist/harness/claude-runtime.js +43 -0
- package/dist/harness/codex-app-server-proxy.d.ts +4 -1
- package/dist/harness/codex-app-server-proxy.js +41 -5
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex.d.ts +3 -2
- package/dist/harness/codex.js +8 -5
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +2 -0
- package/dist/isolation/types.d.ts +5 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +1 -34
- package/dist/monitor.js +24 -151
- package/dist/notifications/fleet-producer.d.ts +18 -0
- package/dist/notifications/fleet-producer.js +129 -0
- package/dist/notifications/outbox.d.ts +42 -0
- package/dist/notifications/outbox.js +149 -0
- package/dist/owner-channel/channel.d.ts +3 -0
- package/dist/owner-channel/channel.js +34 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +2 -0
- package/dist/preset-migration.js +25 -0
- package/dist/rooms-tasks/add-member.d.ts +48 -0
- package/dist/rooms-tasks/add-member.js +83 -0
- package/dist/rooms-tasks/archived-absence.d.ts +8 -0
- package/dist/rooms-tasks/archived-absence.js +48 -0
- package/dist/rooms-tasks/cli.js +34 -5
- package/dist/rooms-tasks/close.d.ts +5 -2
- package/dist/rooms-tasks/close.js +208 -28
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
- package/dist/rooms-tasks/cowork-adapter.js +11 -117
- package/dist/rooms-tasks/cowork-http.d.ts +3 -0
- package/dist/rooms-tasks/cowork-http.js +70 -0
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +74 -5
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +169 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-service.d.ts +42 -0
- package/dist/rooms-tasks/layout-service.js +185 -0
- package/dist/rooms-tasks/layout.d.ts +114 -0
- package/dist/rooms-tasks/layout.js +250 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +14 -0
- package/dist/rooms-tasks/provision.js +173 -46
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +53 -8
- package/dist/rooms-tasks/task-state.d.ts +42 -7
- package/dist/rooms-tasks/task-state.js +153 -23
- package/dist/rooms-tasks/types.d.ts +28 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +26 -0
- package/dist/rooms-tasks/workspace.js +195 -0
- package/dist/runner.d.ts +19 -3
- package/dist/runner.js +89 -29
- package/dist/session/acp.d.ts +7 -1
- package/dist/session/acp.js +21 -6
- package/dist/session/arbiter.d.ts +3 -0
- package/dist/session/arbiter.js +20 -2
- package/dist/session/codex-app-server.d.ts +3 -0
- package/dist/session/codex-app-server.js +26 -3
- package/dist/session/control.d.ts +10 -1
- package/dist/session/control.js +37 -2
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-types.d.ts +5 -3
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +3 -0
- package/dist/setup-tunnel-preflight.d.ts +32 -0
- package/dist/setup-tunnel-preflight.js +58 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.js +7 -0
- package/dist/startup-readiness.d.ts +13 -0
- package/dist/startup-readiness.js +38 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +75 -0
- package/dist/subscriptions/login.js +182 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +140 -0
- package/dist/supervisor/launchd.js +2 -1
- package/dist/supervisor/systemd.js +9 -23
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +6 -1
- package/dist/temp-lifecycle.js +29 -13
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +29 -1
- package/dist/web/auth.js +81 -7
- package/dist/web/available-port.d.ts +2 -0
- package/dist/web/available-port.js +23 -0
- package/dist/web/control.d.ts +3 -3
- package/dist/web/control.js +10 -8
- package/dist/web/fleet-config-service.d.ts +4 -1
- package/dist/web/fleet-config-service.js +46 -15
- package/dist/web/prefix-gateway.d.ts +29 -0
- package/dist/web/prefix-gateway.js +206 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +116 -31
- package/dist/web/server.d.ts +16 -0
- package/dist/web/server.js +501 -53
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +29 -0
- package/dist/web/workspace-devices.js +84 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +32 -0
- package/dist/workspace-enrollment.d.ts +53 -0
- package/dist/workspace-enrollment.js +370 -0
- package/docs/room-layouts.md +227 -0
- package/docs/task-workspaces.md +130 -0
- package/examples/room-layouts/fresh.yaml +27 -0
- package/examples/room-layouts/scoped.yaml +21 -0
- package/examples/room-layouts/shared.yaml +19 -0
- package/package.json +13 -18
- package/presets/README.md +78 -0
- package/presets/brain-catalog.json +8 -2
- package/presets/fleet/agent_templates/Critic.yaml +1 -1
- package/presets/fleet/agent_templates/Developer.yaml +1 -1
- package/presets/fleet/agent_templates/Engineer.yaml +5 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
- package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +34 -11
- package/presets/fleet/roles/Engineer.yaml +40 -0
- package/presets/fleet/room_layouts/engineering.yaml +25 -0
- package/presets/fleet/room_layouts/pair.yaml +24 -0
- package/presets/fleet/room_layouts/single.yaml +17 -0
- package/presets/fleet/room_layouts/team.yaml +30 -0
- package/presets/fleet/room_templates/engineering.yaml +19 -0
- package/presets/manifest.json +2 -2
- package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
- package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
- package/dist/web-app/icons/ours-fleet.svg +0 -4
- package/dist/web-app/index.html +0 -17
- package/dist/web-app/manifest.webmanifest +0 -15
- package/dist/web-app/offline.html +0 -18
- package/dist/web-app/sw.js +0 -51
package/dist/session/control.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { socketPath as privateSocketPath } from '../socket-path.js';
|
|
1
2
|
import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
2
3
|
import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import { createConnection, createServer } from 'node:net';
|
|
@@ -8,7 +9,7 @@ import { interruptSession, queueSessionPrompt, respondSessionPermission, respond
|
|
|
8
9
|
const MAX_LINE_BYTES = 64 * 1024;
|
|
9
10
|
/** Commands that require protocol version 3. */
|
|
10
11
|
const V3_COMMANDS = new Set([
|
|
11
|
-
'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'interrupt_v2',
|
|
12
|
+
'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'submit_voice_prompt', 'interrupt_v2',
|
|
12
13
|
'respond_permission_v2',
|
|
13
14
|
]);
|
|
14
15
|
/** The one retained-range projection shared by polling and live-follow admission. */
|
|
@@ -99,7 +100,7 @@ export function oversightTaxonomy(name = '<Name>') {
|
|
|
99
100
|
export function oversightTaxonomyLines(name = '<Name>') {
|
|
100
101
|
return oversightTaxonomy(name).map(r => `- **${r.result}** — ${r.meaning} → ${r.action}`);
|
|
101
102
|
}
|
|
102
|
-
export const controlSocketPath = (stateDir) => join(stateDir, '.control.sock');
|
|
103
|
+
export const controlSocketPath = (stateDir) => privateSocketPath(join(stateDir, '.control.sock'));
|
|
103
104
|
export const controlTokenPath = (stateDir) => join(stateDir, '.control-token');
|
|
104
105
|
function sameToken(actual, supplied) {
|
|
105
106
|
const a = Buffer.from(actual);
|
|
@@ -116,9 +117,13 @@ export class RoleControlServer {
|
|
|
116
117
|
sockets = new Set();
|
|
117
118
|
/** Interrupt idempotency: same command id returns the first receipt. */
|
|
118
119
|
interruptCommands = new Map();
|
|
120
|
+
correspondence;
|
|
121
|
+
setCorrespondence(value) { this.correspondence = value; }
|
|
119
122
|
ownerChannel;
|
|
120
123
|
loopManager;
|
|
121
124
|
reloadConfig;
|
|
125
|
+
layoutControl;
|
|
126
|
+
setLayoutControl(handler) { this.layoutControl = handler; }
|
|
122
127
|
fleetSpawner;
|
|
123
128
|
fleetAuditor;
|
|
124
129
|
constructor(stateDir, session, log) {
|
|
@@ -235,7 +240,9 @@ export class RoleControlServer {
|
|
|
235
240
|
protocolVersion: this.session.conversationPage ? 3 : 2,
|
|
236
241
|
features: [
|
|
237
242
|
'events_since', 'observer_follow', 'retained_range',
|
|
243
|
+
...(this.correspondence ? ['agent_correspondence'] : []),
|
|
238
244
|
...(this.session.conversationPage ? ['conversation_v3'] : []),
|
|
245
|
+
...(this.session.submitPromptBrowser ? ['generation_bound_prompts'] : []),
|
|
239
246
|
...(this.session.capabilities?.steering ? ['steering'] : []),
|
|
240
247
|
...(this.session.capabilities?.permissions ? ['permissions'] : []),
|
|
241
248
|
...(this.session.capabilities?.messagePhases ? ['message_phases'] : []),
|
|
@@ -308,6 +315,13 @@ export class RoleControlServer {
|
|
|
308
315
|
});
|
|
309
316
|
return;
|
|
310
317
|
}
|
|
318
|
+
case 'layout_control': {
|
|
319
|
+
if (request.version !== 2 || !request.layout || !this.layoutControl)
|
|
320
|
+
throw new SessionControlError('rejected', 'layout control unavailable');
|
|
321
|
+
const result = await this.layoutControl(request.layout);
|
|
322
|
+
this.write(socket, { version: 1, id: request.id, ok: true, result });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
311
325
|
case 'fleet_spawn': {
|
|
312
326
|
if (request.version !== 2 || !request.spawn || typeof request.spawn.name !== 'string')
|
|
313
327
|
throw new SessionControlError('rejected', 'version 2 and typed spawn options are required');
|
|
@@ -362,6 +376,23 @@ export class RoleControlServer {
|
|
|
362
376
|
this.write(socket, { version: 1, id: request.id, ok: true, result: { delivered: true } });
|
|
363
377
|
return;
|
|
364
378
|
}
|
|
379
|
+
case 'agent_contacts':
|
|
380
|
+
case 'agent_history': {
|
|
381
|
+
if (!this.correspondence)
|
|
382
|
+
throw new SessionControlError('rejected', 'Supervisor correspondence API is unavailable; this session needs an updated supervisor.');
|
|
383
|
+
const query = request.agentHistory;
|
|
384
|
+
if (request.command === 'agent_history' && (!query || !/^[a-f0-9]{64}$/i.test(query.peer_cid) || (query.limit !== undefined && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) || (query.before_seq !== undefined && (!Number.isSafeInteger(query.before_seq) || query.before_seq < 1))))
|
|
385
|
+
throw new SessionControlError('rejected', 'Valid peer_cid, limit (1–200) and before_seq are required.');
|
|
386
|
+
let result;
|
|
387
|
+
try {
|
|
388
|
+
result = request.command === 'agent_contacts' ? await this.correspondence.contacts() : await this.correspondence.history(query);
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
throw new SessionControlError('rejected', 'Supervisor correspondence read failed; identity or transport is unavailable.');
|
|
392
|
+
}
|
|
393
|
+
this.write(socket, { version: 1, id: request.id, ok: true, result });
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
365
396
|
case 'owner_channel_manage': {
|
|
366
397
|
if (!request.ownerChannel || typeof request.ownerChannel.action !== 'string')
|
|
367
398
|
throw new SessionControlError('rejected', 'owner-channel management action is required');
|
|
@@ -421,7 +452,10 @@ export class RoleControlServer {
|
|
|
421
452
|
socket.write(JSON.stringify({ version: 1, conversationEvent: event }) + '\n');
|
|
422
453
|
}) };
|
|
423
454
|
}
|
|
455
|
+
case 'submit_voice_prompt':
|
|
424
456
|
case 'submit_prompt_v2': {
|
|
457
|
+
if (request.command === 'submit_voice_prompt' && !request.expectedSessionGeneration?.trim())
|
|
458
|
+
throw new SessionControlError('rejected', 'expectedSessionGeneration is required');
|
|
425
459
|
this.requireConversation(request);
|
|
426
460
|
if (!request.commandId?.trim() || !request.text?.trim() || !request.actor?.trim()
|
|
427
461
|
|| request.source !== 'owner_admin_console')
|
|
@@ -432,6 +466,7 @@ export class RoleControlServer {
|
|
|
432
466
|
const receipt = await this.session.submitPromptBrowser({
|
|
433
467
|
commandId: request.commandId, text: request.text,
|
|
434
468
|
source: 'owner_admin_console', actorBrowserSession: request.actor,
|
|
469
|
+
expectedSessionGeneration: request.expectedSessionGeneration,
|
|
435
470
|
});
|
|
436
471
|
this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
|
|
437
472
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as acp from '@agentclientprotocol/sdk';
|
|
2
|
-
import type { AdapterMeta, ConversationEventKind, ConversationPayload } from './conversation-types.js';
|
|
2
|
+
import type { AdapterMeta, BoundedJson, ConversationEventKind, ConversationPayload } from './conversation-types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Reduce one ACP v1 `session/update` into one conversation-domain event draft.
|
|
5
5
|
*
|
|
@@ -43,4 +43,5 @@ export interface NormalizedUpdate {
|
|
|
43
43
|
toolCallId?: string;
|
|
44
44
|
adapterMeta?: AdapterMeta[];
|
|
45
45
|
}
|
|
46
|
+
export declare function boundedJson(value: unknown): BoundedJson;
|
|
46
47
|
export declare function normalizeSessionUpdate(update: acp.SessionUpdate, options?: NormalizeOptions): NormalizedUpdate;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TaskCreatedNotice } from './task-notice.js';
|
|
1
2
|
import type { PromptOrigin, TurnCancellationSource, TurnOutcome } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Durable conversation domain schema for managed-session consoles.
|
|
@@ -7,11 +8,11 @@ import type { PromptOrigin, TurnCancellationSource, TurnOutcome } from './types.
|
|
|
7
8
|
* file touches the wire: transport updates are reduced into these shapes by a
|
|
8
9
|
* session adapter, and the store assigns `seq`/`eventId`/timestamps.
|
|
9
10
|
*/
|
|
10
|
-
export type ConversationEventKind = 'prompt.admitted' | 'prompt.started' | 'prompt.interrupt_requested' | 'message.chunk' | 'message.replace' | 'thought.chunk' | 'thought.replace' | 'plan.replace' | 'tool.upsert' | 'tool.content_chunk' | 'permission.requested' | 'permission.resolved' | 'monitor.delivery' | 'usage.updated' | 'turn.state' | 'turn.completed' | 'session.state' | 'session.info' | 'capabilities.updated' | 'error'
|
|
11
|
+
export type ConversationEventKind = 'prompt.admitted' | 'prompt.started' | 'prompt.interrupt_requested' | 'message.chunk' | 'message.replace' | 'thought.chunk' | 'thought.replace' | 'plan.replace' | 'tool.upsert' | 'tool.content_chunk' | 'permission.requested' | 'permission.resolved' | 'fleet.task_created' | 'monitor.delivery' | 'usage.updated' | 'turn.state' | 'turn.completed' | 'session.state' | 'session.info' | 'capabilities.updated' | 'error'
|
|
11
12
|
/** A well-formed transport update this version cannot represent. Bounded, never a crash. */
|
|
12
13
|
| 'unsupported';
|
|
13
14
|
/** Where a conversation record came from. Typed provenance, never prompt text. */
|
|
14
|
-
export type ConversationSource = 'owner_admin_console' | 'owner_channel' | 'fleet_monitor' | 'scheduled_loop' | 'startup' | 'local_console' | 'agent' | 'agent_replay';
|
|
15
|
+
export type ConversationSource = 'owner_admin_console' | 'owner_channel' | 'fleet_monitor' | 'scheduled_loop' | 'startup' | 'local_console' | 'agent' | 'agent_replay' | 'fleet_lifecycle';
|
|
15
16
|
export interface NormalizedText {
|
|
16
17
|
type: 'text';
|
|
17
18
|
text: string;
|
|
@@ -227,7 +228,7 @@ export interface BoundedJson {
|
|
|
227
228
|
digest?: string;
|
|
228
229
|
redacted?: true;
|
|
229
230
|
}
|
|
230
|
-
export type ConversationPayload = MessageChunkPayload | ThoughtChunkPayload | PlanReplacePayload | ToolUpsertPayload | UsageUpdatedPayload | SessionStatePayload | SessionInfoPayload | CapabilitiesUpdatedPayload | UnsupportedPayload | PromptAdmittedPayload | PromptStartedPayload | PromptInterruptRequestedPayload | PermissionRequestedPayload | PermissionResolvedPayload | TurnStatePayload | TurnCompletedPayload | MonitorDeliveryPayload | SessionLifecyclePayload | ErrorPayload;
|
|
231
|
+
export type ConversationPayload = MessageChunkPayload | ThoughtChunkPayload | PlanReplacePayload | ToolUpsertPayload | UsageUpdatedPayload | SessionStatePayload | SessionInfoPayload | CapabilitiesUpdatedPayload | UnsupportedPayload | PromptAdmittedPayload | PromptStartedPayload | PromptInterruptRequestedPayload | PermissionRequestedPayload | PermissionResolvedPayload | TurnStatePayload | TurnCompletedPayload | MonitorDeliveryPayload | SessionLifecyclePayload | ErrorPayload | TaskCreatedNotice;
|
|
231
232
|
export interface ConversationEventV1 {
|
|
232
233
|
schemaVersion: 1;
|
|
233
234
|
roleId: string;
|
|
@@ -262,6 +263,7 @@ export interface ConversationEventV1 {
|
|
|
262
263
|
adapterMeta?: AdapterMeta[];
|
|
263
264
|
}
|
|
264
265
|
export interface SubmitPromptCommand {
|
|
266
|
+
expectedSessionGeneration?: string;
|
|
265
267
|
/** Idempotency-Key / clientRequestId. Reuse with a different body is a conflict. */
|
|
266
268
|
commandId: string;
|
|
267
269
|
text: string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ConversationEventStore } from './conversation-store.js';
|
|
2
|
+
export interface TaskNoticeBinding {
|
|
3
|
+
sessionGeneration: string;
|
|
4
|
+
acpSessionId: string;
|
|
5
|
+
promptId: string;
|
|
6
|
+
}
|
|
7
|
+
export interface TaskCreatedNotice {
|
|
8
|
+
operationId: string;
|
|
9
|
+
taskId: string;
|
|
10
|
+
title: string;
|
|
11
|
+
state: string;
|
|
12
|
+
}
|
|
13
|
+
/** Only the trusted Fleet audit completion calls this, never model output. */
|
|
14
|
+
export declare function appendTaskCreated(store: ConversationEventStore, binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Only the trusted Fleet audit completion calls this, never model output. */
|
|
2
|
+
export function appendTaskCreated(store, binding, notice) {
|
|
3
|
+
let after;
|
|
4
|
+
do {
|
|
5
|
+
const page = store.page({ after, limit: 1000 });
|
|
6
|
+
if (page.events.some(e => e.kind === 'fleet.task_created' && e.payload.operationId === notice.operationId))
|
|
7
|
+
return;
|
|
8
|
+
if (!page.hasMore)
|
|
9
|
+
break;
|
|
10
|
+
after = page.nextCursor;
|
|
11
|
+
} while (after);
|
|
12
|
+
store.append({ kind: 'fleet.task_created', source: 'fleet_lifecycle', ...binding, turnId: binding.promptId, payload: notice });
|
|
13
|
+
}
|
package/dist/session/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TaskCreatedNotice, TaskNoticeBinding } from './task-notice.js';
|
|
1
2
|
import type { SessionBackendId } from '../config.js';
|
|
2
3
|
import type { ConversationEventV1, ConversationSnapshot, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
|
|
3
4
|
/**
|
|
@@ -289,6 +290,8 @@ export interface AgentSession {
|
|
|
289
290
|
readonly capabilities?: AgentSessionCapabilities;
|
|
290
291
|
isAlive(): boolean;
|
|
291
292
|
snapshot(): SessionSnapshot;
|
|
293
|
+
taskNoticeBinding?(): TaskNoticeBinding | undefined;
|
|
294
|
+
recordTaskCreated?(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
|
|
292
295
|
conversationPage?(request: {
|
|
293
296
|
after?: string;
|
|
294
297
|
limit?: number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Exec } from './exec.js';
|
|
2
|
+
import type { WorkspacePayload } from './workspace-enrollment.js';
|
|
3
|
+
/**
|
|
4
|
+
* Check what the steps after the one-time root proof depend on, before that
|
|
5
|
+
* proof is sent: a failure afterwards cannot be retried with a fresh command.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setupTunnelPreflight({ exec, platform, node }?: {
|
|
8
|
+
exec?: Exec;
|
|
9
|
+
platform?: NodeJS.Platform;
|
|
10
|
+
node?: string;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
/** What the steps after the proof need, kept so they can be finished without a new command. */
|
|
13
|
+
export interface PendingTunnelSetup {
|
|
14
|
+
appOrigin: string;
|
|
15
|
+
origin: string;
|
|
16
|
+
hostWorkspaceId: string;
|
|
17
|
+
rootCid: string;
|
|
18
|
+
challenge: WorkspacePayload['challenge'];
|
|
19
|
+
/** Fleet configuration the setup started with; a resume must use the same one. */
|
|
20
|
+
configuration: string;
|
|
21
|
+
/** Every port sent to the account as the tunnel target. No answer was confirmed while this record exists, so the remote target is any of these or the default. */
|
|
22
|
+
requestedPorts?: number[];
|
|
23
|
+
}
|
|
24
|
+
export declare function savePendingTunnelSetup(pending: PendingTunnelSetup): void;
|
|
25
|
+
export declare function clearPendingTunnelSetup(): void;
|
|
26
|
+
export declare function readPendingTunnelSetup(): PendingTunnelSetup;
|
|
27
|
+
export declare const DEFAULT_TUNNEL_PORT = 49271;
|
|
28
|
+
/**
|
|
29
|
+
* The port the web service must bind when the account can no longer retarget
|
|
30
|
+
* the tunnel. Unknown when a different port was requested and never confirmed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function requiredPortAfterExpiry(pending: PendingTunnelSetup): number;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { userInfo } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { replaceFileAtomically } from './atomic-file.js';
|
|
5
|
+
import { realExec } from './exec.js';
|
|
6
|
+
import { stateRoot } from './paths.js';
|
|
7
|
+
const UNCHANGED = 'Nothing was changed and this host was not bound.';
|
|
8
|
+
/**
|
|
9
|
+
* Check what the steps after the one-time root proof depend on, before that
|
|
10
|
+
* proof is sent: a failure afterwards cannot be retried with a fresh command.
|
|
11
|
+
*/
|
|
12
|
+
export async function setupTunnelPreflight({ exec = realExec, platform = process.platform, node = process.versions.node } = {}) {
|
|
13
|
+
const [major, minor] = node.split('.').map(Number);
|
|
14
|
+
if (major < 22 || (major === 22 && minor < 13))
|
|
15
|
+
throw Error(`Tunnel setup requires Node.js 22.13 or newer; this is ${node}. Update Node.js, then run a fresh tunnel setup command. ${UNCHANGED}`);
|
|
16
|
+
if (platform === 'linux') {
|
|
17
|
+
const manager = await exec('systemctl', ['--user', 'show-environment'], { timeout: 10_000 });
|
|
18
|
+
if (manager.code !== 0)
|
|
19
|
+
throw Error(`The user service manager is not reachable from this shell (${manager.stderr.trim().split('\n')[0] || `systemctl --user exited ${manager.code}`}). `
|
|
20
|
+
+ `Log in directly as ${userInfo().username} (not through su or sudo), or run: sudo loginctl enable-linger ${userInfo().username} && export XDG_RUNTIME_DIR=/run/user/${process.getuid?.()} `
|
|
21
|
+
+ `Then run a fresh tunnel setup command. ${UNCHANGED}`);
|
|
22
|
+
}
|
|
23
|
+
const help = await exec('cloudflared', ['tunnel', 'run', '--help'], { timeout: 10_000 });
|
|
24
|
+
if (help.code === 127)
|
|
25
|
+
throw Error(`cloudflared is not installed or not on PATH. Install it, then run a fresh tunnel setup command. ${UNCHANGED}`);
|
|
26
|
+
if (help.code !== 0 || !`${help.stdout}\n${help.stderr}`.includes('--token-file'))
|
|
27
|
+
throw Error(`This cloudflared is too old for tunnel setup (no --token-file support). Update cloudflared, then run a fresh tunnel setup command. ${UNCHANGED}`);
|
|
28
|
+
}
|
|
29
|
+
const pendingPath = () => join(stateRoot(), 'workspace', 'pending-setup.json');
|
|
30
|
+
export function savePendingTunnelSetup(pending) {
|
|
31
|
+
replaceFileAtomically(pendingPath(), JSON.stringify(pending) + '\n', 0o600);
|
|
32
|
+
}
|
|
33
|
+
export function clearPendingTunnelSetup() { rmSync(pendingPath(), { force: true }); }
|
|
34
|
+
export function readPendingTunnelSetup() {
|
|
35
|
+
const path = pendingPath();
|
|
36
|
+
if (!existsSync(path))
|
|
37
|
+
throw Error('No unfinished tunnel setup on this host. Run the tunnel setup command from the App.');
|
|
38
|
+
const stat = lstatSync(path);
|
|
39
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0)
|
|
40
|
+
throw Error('Unfinished tunnel setup record must be an owned private file');
|
|
41
|
+
const pending = JSON.parse(readFileSync(path, 'utf8'));
|
|
42
|
+
if (typeof pending?.appOrigin !== 'string' || typeof pending.origin !== 'string' || typeof pending.hostWorkspaceId !== 'string'
|
|
43
|
+
|| typeof pending.rootCid !== 'string' || !pending.challenge || !Number.isSafeInteger(pending.challenge.expiresAt) || typeof pending.configuration !== 'string'
|
|
44
|
+
|| (pending.requestedPorts !== undefined && (!Array.isArray(pending.requestedPorts) || !pending.requestedPorts.every(Number.isInteger))))
|
|
45
|
+
throw Error('Unfinished tunnel setup record is invalid');
|
|
46
|
+
return pending;
|
|
47
|
+
}
|
|
48
|
+
export const DEFAULT_TUNNEL_PORT = 49_271;
|
|
49
|
+
/**
|
|
50
|
+
* The port the web service must bind when the account can no longer retarget
|
|
51
|
+
* the tunnel. Unknown when a different port was requested and never confirmed.
|
|
52
|
+
*/
|
|
53
|
+
export function requiredPortAfterExpiry(pending) {
|
|
54
|
+
const other = [...new Set(pending.requestedPorts ?? [])].filter(port => port !== DEFAULT_TUNNEL_PORT);
|
|
55
|
+
if (!other.length)
|
|
56
|
+
return DEFAULT_TUNNEL_PORT;
|
|
57
|
+
throw Error(`The setup window has expired and it is unknown whether the tunnel points at port ${[...other, DEFAULT_TUNNEL_PORT].join(' or ')}: the account's answer to a port request was not received. This setup cannot be finished automatically`);
|
|
58
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstatSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
/** Optional private socket directory for deeply nested, isolated Fleet homes. */
|
|
5
|
+
export function socketPath(logicalPath) {
|
|
6
|
+
const root = process.env.OURS_FLEET_SOCKET_ROOT;
|
|
7
|
+
if (!root) {
|
|
8
|
+
if (Buffer.byteLength(logicalPath) > 103)
|
|
9
|
+
throw new Error('Unix socket path is too long; configure a short private OURS_FLEET_SOCKET_ROOT');
|
|
10
|
+
return logicalPath;
|
|
11
|
+
}
|
|
12
|
+
if (!isAbsolute(root) || resolve(root) !== root)
|
|
13
|
+
throw new Error('OURS_FLEET_SOCKET_ROOT must be an absolute normalized path');
|
|
14
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
15
|
+
const stat = lstatSync(root);
|
|
16
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || (stat.mode & 0o777) !== 0o700)
|
|
17
|
+
throw new Error('OURS_FLEET_SOCKET_ROOT must be an owner-only 0700 directory');
|
|
18
|
+
const path = join(root, createHash('sha256').update(resolve(logicalPath)).digest('hex').slice(0, 24) + '.sock');
|
|
19
|
+
if (Buffer.byteLength(path) > 103)
|
|
20
|
+
throw new Error('OURS_FLEET_SOCKET_ROOT is too long');
|
|
21
|
+
return path;
|
|
22
|
+
}
|
package/dist/spawn.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { storeRoomSecret, storeTemporaryLaunch, preparePermanentAssignment } from './agent-ours/service.js';
|
|
3
|
+
import { ensureWorkspace } from './rooms-tasks/workspace.js';
|
|
3
4
|
import { spawn as spawnChild } from 'node:child_process';
|
|
4
5
|
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
6
|
import { join } from 'node:path';
|
|
@@ -321,6 +322,12 @@ export async function spawnTemp(o, binPath, launch = independentSupervisor, crea
|
|
|
321
322
|
if (o.isolationFile)
|
|
322
323
|
readIsolationFile(o.isolationFile); // fail before reserving
|
|
323
324
|
const prepared = resolvedSpawn(o); // canonical validation before mutation
|
|
325
|
+
if (o.roomMemberStartup?.workspace) {
|
|
326
|
+
const workspace = o.roomMemberStartup.workspace;
|
|
327
|
+
if (prepared.role.cwd !== workspace.path)
|
|
328
|
+
throw new Error('room member cwd must equal its recorded owned workspace');
|
|
329
|
+
ensureWorkspace(workspace);
|
|
330
|
+
}
|
|
324
331
|
// Retire only supervisors whose recorded owner is definitively stopped. This
|
|
325
332
|
// bounded pass keeps the active roster clean without deleting old evidence.
|
|
326
333
|
await reclaimStaleTempState();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DaemonGenerationProbe } from './daemon-recovery.js';
|
|
2
|
+
export declare const STARTUP_WAIT_MS = 240000;
|
|
3
|
+
export declare const STARTUP_RETRY_MS = 5000;
|
|
4
|
+
interface ReadinessDeps {
|
|
5
|
+
probe(env: NodeJS.ProcessEnv): Promise<DaemonGenerationProbe>;
|
|
6
|
+
now(): number;
|
|
7
|
+
sleep(ms: number): Promise<void>;
|
|
8
|
+
log(message: string): void;
|
|
9
|
+
env: NodeJS.ProcessEnv;
|
|
10
|
+
}
|
|
11
|
+
/** Read-only preflight: never binds identities, starts a harness or edits its ledger. */
|
|
12
|
+
export declare function waitForRoleDaemon(name: string, configPath?: string, partial?: Partial<ReadinessDeps>): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { agentDir } from './paths.js';
|
|
2
|
+
import { findRole, loadConfig } from './config.js';
|
|
3
|
+
import { resolveConfigPath } from './runner.js';
|
|
4
|
+
import { probeDaemonGeneration } from './daemon-recovery.js';
|
|
5
|
+
export const STARTUP_WAIT_MS = 240_000;
|
|
6
|
+
export const STARTUP_RETRY_MS = 5_000;
|
|
7
|
+
/** Read-only preflight: never binds identities, starts a harness or edits its ledger. */
|
|
8
|
+
export async function waitForRoleDaemon(name, configPath, partial = {}) {
|
|
9
|
+
const deps = {
|
|
10
|
+
probe: env => probeDaemonGeneration(((url, init) => globalThis.fetch(url, {
|
|
11
|
+
...init, signal: AbortSignal.timeout(10_000),
|
|
12
|
+
})), env),
|
|
13
|
+
now: Date.now,
|
|
14
|
+
sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
|
|
15
|
+
log: message => process.stderr.write(message + '\n'),
|
|
16
|
+
env: process.env,
|
|
17
|
+
...partial,
|
|
18
|
+
};
|
|
19
|
+
const path = resolveConfigPath(agentDir(name), configPath);
|
|
20
|
+
const role = findRole(loadConfig(path), name);
|
|
21
|
+
const env = { ...deps.env, ...role.env };
|
|
22
|
+
const deadline = deps.now() + STARTUP_WAIT_MS;
|
|
23
|
+
let lastReason = '';
|
|
24
|
+
while (deps.now() < deadline) {
|
|
25
|
+
const result = await deps.probe(env);
|
|
26
|
+
if (result.state === 'ready') {
|
|
27
|
+
deps.log(`[${name}] daemon ready; starting supervised agent`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Only structured diagnostic codes, never endpoint responses or credentials.
|
|
31
|
+
const reason = /^[A-Z][A-Z0-9_]*$/.test(result.reason) ? result.reason : 'DAEMON_UNAVAILABLE';
|
|
32
|
+
if (reason !== lastReason)
|
|
33
|
+
deps.log(`[${name}] waiting for configured daemon: ${reason}`);
|
|
34
|
+
lastReason = reason;
|
|
35
|
+
await deps.sleep(Math.min(STARTUP_RETRY_MS, Math.max(0, deadline - deps.now())));
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Daemon not ready (${lastReason}); service manager will retry startup`);
|
|
38
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin adapters over the official CLIs. Nothing here reads or returns a token
|
|
3
|
+
* value: credentials stay in files the CLIs own, and secrets typed by the user
|
|
4
|
+
* (a Claude paste code) travel only over a child's stdin, never argv or logs.
|
|
5
|
+
*/
|
|
6
|
+
export interface SubscriptionBinaries {
|
|
7
|
+
claude: string;
|
|
8
|
+
codex: string;
|
|
9
|
+
/** `script(1)`, used to give `claude auth login` the terminal it expects. */
|
|
10
|
+
script: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const defaultBinaries: () => SubscriptionBinaries;
|
|
13
|
+
/**
|
|
14
|
+
* Child env for a profile helper: explicit home for added profiles, the CLI
|
|
15
|
+
* default otherwise. Every credential selector either CLI honours is removed,
|
|
16
|
+
* so a helper can only ever see the profile's own login.
|
|
17
|
+
*/
|
|
18
|
+
export declare function profileEnv(homeVar: string, profileId: string, home: string, base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
19
|
+
export interface ClaudeStatus {
|
|
20
|
+
loggedIn: boolean;
|
|
21
|
+
email?: string;
|
|
22
|
+
plan?: string;
|
|
23
|
+
org?: string;
|
|
24
|
+
authMethod?: string;
|
|
25
|
+
}
|
|
26
|
+
/** `claude auth status --json` (documented: exit 0 when logged in, 1 otherwise). */
|
|
27
|
+
export declare function claudeAuthStatus(bin: string, env: NodeJS.ProcessEnv, timeoutMs?: number): Promise<ClaudeStatus>;
|
|
28
|
+
/**
|
|
29
|
+
* Login expiry timestamps from the Claude credentials file. Field names are
|
|
30
|
+
* observed (Claude Code 2.1.x), not a documented contract, so absence is normal.
|
|
31
|
+
* Only the two numbers are kept; the parsed object is discarded immediately.
|
|
32
|
+
*/
|
|
33
|
+
export declare function claudeLoginExpiry(home: string): {
|
|
34
|
+
accessExpiresAt?: number;
|
|
35
|
+
refreshExpiresAt?: number;
|
|
36
|
+
};
|
|
37
|
+
/** Minimal stdio JSON-RPC client for `codex app-server` (the supported transport). */
|
|
38
|
+
export declare class CodexAppServer {
|
|
39
|
+
private readonly child;
|
|
40
|
+
private id;
|
|
41
|
+
private buffer;
|
|
42
|
+
private readonly pending;
|
|
43
|
+
private readonly listeners;
|
|
44
|
+
private closed;
|
|
45
|
+
private constructor();
|
|
46
|
+
static start(bin: string, env: NodeJS.ProcessEnv, initializeTimeoutMs?: number): Promise<CodexAppServer>;
|
|
47
|
+
call<T = unknown>(method: string, params: unknown, timeoutMs?: number): Promise<T>;
|
|
48
|
+
onNotification(fn: (method: string, params: unknown) => void): () => void;
|
|
49
|
+
close(): void;
|
|
50
|
+
private onData;
|
|
51
|
+
}
|
|
52
|
+
/** One-shot use of an app-server for a profile. */
|
|
53
|
+
export declare function withCodex<T>(bin: string, env: NodeJS.ProcessEnv, fn: (s: CodexAppServer) => Promise<T>, initializeTimeoutMs?: number): Promise<T>;
|
|
54
|
+
/** What a Codex runtime offers the account it is signed in to. An empty list is an answer; unknown is not. */
|
|
55
|
+
export type OfferedModels = {
|
|
56
|
+
state: 'known';
|
|
57
|
+
models: string[];
|
|
58
|
+
} | {
|
|
59
|
+
state: 'unknown';
|
|
60
|
+
reason: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Ask this Codex executable, with this profile's environment, which models it offers (`model/list`,
|
|
64
|
+
* the call the ACP adapter itself uses). Nothing is inferred when it does not answer.
|
|
65
|
+
*/
|
|
66
|
+
export declare function codexOfferedModels(bin: string, env: NodeJS.ProcessEnv, budgetMs?: number): Promise<OfferedModels>;
|
|
67
|
+
export interface UsageWindow {
|
|
68
|
+
id: string;
|
|
69
|
+
label: string;
|
|
70
|
+
usedPercent: number;
|
|
71
|
+
windowMins?: number;
|
|
72
|
+
resetsAt?: string;
|
|
73
|
+
}
|
|
74
|
+
interface CodexWindow {
|
|
75
|
+
usedPercent?: number;
|
|
76
|
+
windowDurationMins?: number | null;
|
|
77
|
+
resetsAt?: number | null;
|
|
78
|
+
}
|
|
79
|
+
interface CodexSnapshot {
|
|
80
|
+
limitId?: string | null;
|
|
81
|
+
limitName?: string | null;
|
|
82
|
+
primary?: CodexWindow | null;
|
|
83
|
+
secondary?: CodexWindow | null;
|
|
84
|
+
}
|
|
85
|
+
/** Flatten `account/rateLimits/read` into display windows. */
|
|
86
|
+
export declare function codexUsageWindows(result: {
|
|
87
|
+
rateLimits?: CodexSnapshot;
|
|
88
|
+
rateLimitsByLimitId?: Record<string, CodexSnapshot | undefined> | null;
|
|
89
|
+
}): UsageWindow[];
|
|
90
|
+
export declare function windowName(mins?: number): string;
|
|
91
|
+
export {};
|