@ours.network/fleet 1.2.0-nightly.4 → 1.2.0-nightly.41
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/role-repository.js +2 -2
- 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 +18 -0
- package/dist/config.js +28 -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 +86 -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/runner.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { prepareManagedAgent, releaseManagedAgent } from './agent-ours/service.js';
|
|
2
2
|
import { type ResolvedRole } from './config.js';
|
|
3
|
-
import { type MonitorHandle, type MonitorOpts, type FetchLike } from './monitor.js';
|
|
3
|
+
import { probeIdentityPresence, type MonitorHandle, type MonitorOpts, type FetchLike } from './monitor.js';
|
|
4
4
|
import { type DaemonGenerationProbe } from './daemon-recovery.js';
|
|
5
5
|
import { type Exec } from './exec.js';
|
|
6
6
|
import { RoleControlServer } from './session/control.js';
|
|
@@ -20,6 +20,7 @@ export interface RunnerDeps {
|
|
|
20
20
|
log(line: string): void;
|
|
21
21
|
/** HTTP transport for the monitor's daemon long-poll (injectable for tests). */
|
|
22
22
|
fetch: FetchLike;
|
|
23
|
+
identityProbeDeps?: Parameters<typeof probeIdentityPresence>[3];
|
|
23
24
|
probeGeneration(env: NodeJS.ProcessEnv): Promise<DaemonGenerationProbe>;
|
|
24
25
|
/** Construct the supervisor mail monitor (injectable so tests stub it out). */
|
|
25
26
|
createMonitor(opts: MonitorOpts): MonitorHandle;
|
|
@@ -29,7 +30,7 @@ export interface RunnerDeps {
|
|
|
29
30
|
/** Neutral construction seam for deterministic runner lifecycle tests. */
|
|
30
31
|
startAgentSession(adapter: AgentSessionAdapter, options: AgentSessionStartOptions): Promise<AgentSession>;
|
|
31
32
|
/** Construct the authenticated role control route (injectable where sockets are unavailable). */
|
|
32
|
-
createControlServer(stateDir: string, session: AgentSession, log: (line: string) => void): Pick<RoleControlServer, 'start' | 'close' | 'setFleetSpawner' | 'setFleetAuditor' | 'setOwnerChannel' | 'setConfigReloader' | 'setLoopManager'
|
|
33
|
+
createControlServer(stateDir: string, session: AgentSession, log: (line: string) => void): Pick<RoleControlServer, 'start' | 'close' | 'setFleetSpawner' | 'setFleetAuditor' | 'setOwnerChannel' | 'setConfigReloader' | 'setLoopManager'> & Partial<Pick<RoleControlServer, 'setLayoutControl' | 'setCorrespondence'>>;
|
|
33
34
|
/** Construct scheduled-loop execution (injectable for fail-closed startup tests). */
|
|
34
35
|
createLoopManager(...args: ConstructorParameters<typeof ScheduledLoopManager>): ScheduledLoopManagerHandle;
|
|
35
36
|
/** Acquire the cross-process owner-channel binder lease before replacing the control socket. */
|
|
@@ -44,6 +45,11 @@ export declare class SupervisorRecycleRequiredError extends Error {
|
|
|
44
45
|
readonly code = "OWNER_CHANNEL_SUPERVISOR_RECYCLE_REQUIRED";
|
|
45
46
|
constructor();
|
|
46
47
|
}
|
|
48
|
+
export declare function localFleetAuditor(stateDir: string, caller: string, log: (line: string) => void, session: AgentSession): {
|
|
49
|
+
begin(requestId: string, argv: string[]): Promise<import("./fleet-command-audit.js").FleetAuditAttempt>;
|
|
50
|
+
finish(input: Parameters<NonNullable<OwnerChannelHandle["finishFleetCommandAudit"]>>[0]): Promise<import("./fleet-command-audit.js").FleetAuditAttempt>;
|
|
51
|
+
present(): Promise<void>;
|
|
52
|
+
};
|
|
47
53
|
/** Environment injected only into the managed harness process. */
|
|
48
54
|
export declare function managedFleetProxyEnv(role: ResolvedRole, stateDir: string): Record<string, string>;
|
|
49
55
|
/**
|
|
@@ -149,6 +155,16 @@ export declare const START_STAGGER_FILE = ".start-stagger-ms";
|
|
|
149
155
|
export declare function reserveLaunchSlot(root: string, staggerMs: number, deps: Pick<RunnerDeps, 'now' | 'sleep' | 'log'>): Promise<number>;
|
|
150
156
|
/** Read a temp role's config snapshot written by spawnTemp. */
|
|
151
157
|
export declare function loadTempRole(name: string): ResolvedRole;
|
|
158
|
+
/**
|
|
159
|
+
* Fall back to the config path applyRole() recorded at the last up/restart/spawn
|
|
160
|
+
* for this role. systemd's shared unit template (`ours-fleet-agent@.service`)
|
|
161
|
+
* execs `_run <name>` with no -c on every restart, so a role brought up from a
|
|
162
|
+
* non-default config (`ours-fleet up -c custom.yaml`) would otherwise silently
|
|
163
|
+
* resolve against the default ~/fleet.yaml on its very first restart and fail
|
|
164
|
+
* with "no such role". An empty/missing marker means "use the default", same as
|
|
165
|
+
* no -c was ever given.
|
|
166
|
+
*/
|
|
167
|
+
export declare function resolveConfigPath(dir: string, explicit?: string): string | undefined;
|
|
152
168
|
/** What one child session did, so the supervising loop can decide what follows. */
|
|
153
169
|
export interface AttemptResult {
|
|
154
170
|
elapsedSecs: number;
|
|
@@ -158,7 +174,7 @@ export interface AttemptResult {
|
|
|
158
174
|
mode: 'fresh' | 'resume';
|
|
159
175
|
modelRecovery?: 'advance' | 'hold';
|
|
160
176
|
/** Present only when a temporary-role lifecycle signal ended the session. */
|
|
161
|
-
retirementReason?: 'identity-closed' | 'operator-stop' | 'supervisor-signal';
|
|
177
|
+
retirementReason?: 'identity-closed' | 'operator-stop' | 'supervisor-signal' | 'idle-timeout';
|
|
162
178
|
}
|
|
163
179
|
/** Continuous authoritative absence required after an identity was observed. */
|
|
164
180
|
export declare const TEMP_IDENTITY_CLOSE_DEBOUNCE_MS = 5000;
|
package/dist/runner.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createLayoutControl } from './rooms-tasks/layout-control.js';
|
|
2
|
+
import { TemporaryChatIdle } from './temp-idle.js';
|
|
1
3
|
import { prepareManagedAgent, releaseManagedAgent } from './agent-ours/service.js';
|
|
2
4
|
import { prepareManagedHarness } from './agent-ours/harness.js';
|
|
3
5
|
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, realpathSync } from 'node:fs';
|
|
@@ -6,12 +8,13 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
8
|
import { randomUUID } from 'node:crypto';
|
|
7
9
|
import { parse } from 'yaml';
|
|
8
10
|
import { agentDir, stateRoot } from './paths.js';
|
|
9
|
-
import { loadConfig, findRole, isolationContextFor,
|
|
11
|
+
import { loadConfig, findRole, isolationContextFor, resolveSnapshotMonitor, resolvePermissions, } from './config.js';
|
|
10
12
|
import { getAdapter } from './harness/registry.js';
|
|
11
13
|
import { createMonitor, probeIdentityPresence, } from './monitor.js';
|
|
12
14
|
import { DaemonGenerationObserver, RoleRecoveryController, probeDaemonGeneration, } from './daemon-recovery.js';
|
|
13
15
|
import { realExec } from './exec.js';
|
|
14
16
|
import { resolveIsolation } from './isolation/policy.js';
|
|
17
|
+
import { pinSubscriptionForLaunch } from './subscriptions/launch.js';
|
|
15
18
|
import { selectIsolationBackend } from './isolation/registry.js';
|
|
16
19
|
import { resourceArgs, cpuControllerDelegated } from './isolation/resources.js';
|
|
17
20
|
import { resolveLaunchRuntime } from './isolation/runtime.js';
|
|
@@ -75,12 +78,16 @@ const defaultDeps = () => ({
|
|
|
75
78
|
});
|
|
76
79
|
const MONITOR_OWNER_FILE = '.monitor-owner';
|
|
77
80
|
const OBSOLETE_OURS_AUTOSTART_ENV = 'OURS_AUTOSTART';
|
|
78
|
-
function localFleetAuditor(stateDir, caller, log) {
|
|
81
|
+
export function localFleetAuditor(stateDir, caller, log, session) {
|
|
82
|
+
const bindings = new Map();
|
|
79
83
|
const store = new FleetCommandAuditStore(join(stateDir, '.fleet-command-audit.json'));
|
|
80
84
|
return {
|
|
81
85
|
async begin(requestId, argv) {
|
|
82
86
|
let attempt = store.begin(requestId, caller, argv);
|
|
83
87
|
if (attempt.invocation === 'sending') {
|
|
88
|
+
const binding = session.taskNoticeBinding?.();
|
|
89
|
+
if (binding)
|
|
90
|
+
bindings.set(attempt.correlationId, binding);
|
|
84
91
|
log(`[${caller}] fleet proxy command ${attempt.correlationId} `
|
|
85
92
|
+ `route=${attempt.classification.route} decision=${attempt.classification.decision}`);
|
|
86
93
|
attempt = store.invocation(attempt.correlationId, caller, 'delivered');
|
|
@@ -94,6 +101,19 @@ function localFleetAuditor(stateDir, caller, log) {
|
|
|
94
101
|
...(input.resourceIds ? { resourceIds: input.resourceIds } : {}),
|
|
95
102
|
...(input.presentations ? { presentations: input.presentations } : {}),
|
|
96
103
|
});
|
|
104
|
+
const binding = bindings.get(attempt.correlationId);
|
|
105
|
+
if (binding && attempt.outcome?.class === 'success' && attempt.outcome.effect === 'completed') {
|
|
106
|
+
for (const p of attempt.outcome.presentations ?? [])
|
|
107
|
+
if (p.kind === 'task' && p.operation === 'create') {
|
|
108
|
+
try {
|
|
109
|
+
session.recordTaskCreated?.(binding, { operationId: p.eventId, taskId: p.id, title: p.title ?? p.id, state: p.newState });
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
log(`[${caller}] task-created conversation notice could not be persisted`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
bindings.delete(attempt.correlationId);
|
|
116
|
+
}
|
|
97
117
|
if (attempt.outcome?.delivery === 'sending')
|
|
98
118
|
attempt = store.outcome(attempt.correlationId, caller, 'delivered');
|
|
99
119
|
return attempt;
|
|
@@ -414,7 +434,7 @@ export function loadTempRole(name) {
|
|
|
414
434
|
// Upgrade snapshots written before monitor.mode/interrupt existed. A snapshot
|
|
415
435
|
// with no monitor block keeps the historical native/no-supervisor behavior.
|
|
416
436
|
if (role.monitor)
|
|
417
|
-
role.monitor =
|
|
437
|
+
role.monitor = resolveSnapshotMonitor(role.monitor);
|
|
418
438
|
role.__temp = true;
|
|
419
439
|
return role;
|
|
420
440
|
}
|
|
@@ -427,7 +447,7 @@ export function loadTempRole(name) {
|
|
|
427
447
|
* with "no such role". An empty/missing marker means "use the default", same as
|
|
428
448
|
* no -c was ever given.
|
|
429
449
|
*/
|
|
430
|
-
function resolveConfigPath(dir, explicit) {
|
|
450
|
+
export function resolveConfigPath(dir, explicit) {
|
|
431
451
|
if (explicit)
|
|
432
452
|
return explicit;
|
|
433
453
|
const marker = join(dir, '.config-path');
|
|
@@ -506,6 +526,11 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
506
526
|
const sessionBackend = role.session ?? 'acp';
|
|
507
527
|
const sessionLabel = sessionBackend === 'acp' ? 'ACP' : 'Codex app-server';
|
|
508
528
|
let launch = adapter.agentSession.prepareLaunch(role, prep);
|
|
529
|
+
// Resolve the fleet-wide active subscription profile once, now: a later switch
|
|
530
|
+
// never changes this session (the operator restarts agents to move them).
|
|
531
|
+
const subscription = pinSubscriptionForLaunch(role, dir);
|
|
532
|
+
if (subscription?.pin.home)
|
|
533
|
+
deps.log(`[${name}] subscription: ${subscription.pin.provider} profile ${subscription.pin.profileId}`);
|
|
509
534
|
// Preserve the role's existing isolation policy.
|
|
510
535
|
let wrappedArgv = launch.argv;
|
|
511
536
|
if (role.isolation) {
|
|
@@ -516,6 +541,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
516
541
|
launch = { ...launch, argv: runtime.argv };
|
|
517
542
|
const ctx = {
|
|
518
543
|
...isolationContextFor(role), stateDir: dir, runCwd,
|
|
544
|
+
subscriptionHome: subscription?.writablePath,
|
|
519
545
|
runtimeReadPaths: [...runtime.readPaths, ...resolveLaunchRuntime([process.execPath, fileURLToPath(new URL('./agent-ours/bridge.js', import.meta.url))]).readPaths],
|
|
520
546
|
};
|
|
521
547
|
const policy = resolveIsolation(role.isolation, ctx);
|
|
@@ -539,8 +565,9 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
539
565
|
wrappedArgv = [...rprefix, ...wrappedArgv];
|
|
540
566
|
}
|
|
541
567
|
const managedService = await deps.prepareAgentOurs(role, dir, opts.identityLifetime ? opts.identityLifetime === 'temporary' : temp);
|
|
568
|
+
let layoutRetiring = false;
|
|
542
569
|
try {
|
|
543
|
-
const managedHarness = prepareManagedHarness(role, dir, runCwd, managedService.descriptor, harnessChildEnv(role, launch.env, dir));
|
|
570
|
+
const managedHarness = prepareManagedHarness(role, dir, runCwd, managedService.descriptor, { ...harnessChildEnv(role, launch.env, dir), ...subscription?.env });
|
|
544
571
|
// Start-stagger: space this launch at least start_stagger_ms after the previous
|
|
545
572
|
// agent launch across the whole host, so a burst of boots (systemd starts every
|
|
546
573
|
// user unit concurrently on boot; `ours-fleet up`/restart-all bulk-start) does not
|
|
@@ -662,6 +689,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
662
689
|
harness: role.harness,
|
|
663
690
|
config: role.owner_channel,
|
|
664
691
|
session: arbiter,
|
|
692
|
+
startupPending: () => !sessionStartupComplete,
|
|
665
693
|
stateDir: dir,
|
|
666
694
|
env: role.env,
|
|
667
695
|
log: deps.log,
|
|
@@ -669,11 +697,18 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
669
697
|
...(configPath ? { configPath } : {}),
|
|
670
698
|
});
|
|
671
699
|
control = deps.createControlServer(dir, arbiter, deps.log);
|
|
700
|
+
control.setCorrespondence?.({ contacts: () => managedService.runtime.readContacts(), history: query => managedService.runtime.readHistory(query) });
|
|
672
701
|
control.setFleetAuditor(ownerChannel ? {
|
|
673
702
|
begin: (requestId, argv) => ownerChannel.beginFleetCommandAudit(requestId, argv),
|
|
674
703
|
finish: input => ownerChannel.finishFleetCommandAudit(input),
|
|
675
704
|
present: presentations => ownerChannel.notifyFleetLifecycle(presentations),
|
|
676
|
-
} : localFleetAuditor(dir, name, deps.log));
|
|
705
|
+
} : localFleetAuditor(dir, name, deps.log, agentSession));
|
|
706
|
+
control.setLayoutControl?.(createLayoutControl({
|
|
707
|
+
agent: name, temporary: temp, standalone: !role.roomMemberStartup,
|
|
708
|
+
runtime: managedService.runtime, session: arbiter,
|
|
709
|
+
stopping: () => Boolean(deps.shouldStop?.()),
|
|
710
|
+
retire: async () => { layoutRetiring = true; await arbiter.close(); },
|
|
711
|
+
}));
|
|
677
712
|
await control.start();
|
|
678
713
|
}
|
|
679
714
|
catch (error) {
|
|
@@ -756,6 +791,26 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
756
791
|
// interruption to steering until this startup turn reaches a terminal
|
|
757
792
|
// success, so there is neither a deaf gap nor a boot-cancellation loop.
|
|
758
793
|
monitorLoop = monitor?.run(pid);
|
|
794
|
+
// Owner traffic and agent replies must be observed during a long first
|
|
795
|
+
// turn. Ordinary owner input steers until startup has proved successful.
|
|
796
|
+
if (ownerChannel) {
|
|
797
|
+
try {
|
|
798
|
+
await ownerChannel.start();
|
|
799
|
+
control.setOwnerChannel(ownerChannel);
|
|
800
|
+
}
|
|
801
|
+
catch (error) {
|
|
802
|
+
monitor?.stop();
|
|
803
|
+
if (monitorLoop)
|
|
804
|
+
await monitorLoop;
|
|
805
|
+
await control.close();
|
|
806
|
+
await ownerChannel.close().catch(() => undefined);
|
|
807
|
+
ownerBinder?.release();
|
|
808
|
+
await agentSession.close();
|
|
809
|
+
unsubscribeRecovery?.();
|
|
810
|
+
throw new Error(`[${name}] owner channel failed to start: `
|
|
811
|
+
+ `${error?.message ?? String(error)}`);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
759
814
|
const started = await starting;
|
|
760
815
|
// A temporary role's first turn can be the active turn when an ours wake
|
|
761
816
|
// needs immediate attention. A typed console/monitor cancellation ends
|
|
@@ -768,6 +823,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
768
823
|
if (!started.succeeded && !interruptedForWake) {
|
|
769
824
|
monitor?.stop();
|
|
770
825
|
await control.close();
|
|
826
|
+
await ownerChannel?.close().catch(() => undefined);
|
|
771
827
|
ownerBinder?.release();
|
|
772
828
|
await agentSession.close();
|
|
773
829
|
unsubscribeRecovery?.();
|
|
@@ -795,26 +851,6 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
795
851
|
deps.log(`[${name}] ${sessionLabel} startup prompt cancelled by ${started.cancellationSource}; `
|
|
796
852
|
+ 'keeping temporary supervisor alive');
|
|
797
853
|
sessionStartupComplete = true;
|
|
798
|
-
if (ownerChannel) {
|
|
799
|
-
try {
|
|
800
|
-
await ownerChannel.start();
|
|
801
|
-
}
|
|
802
|
-
catch (error) {
|
|
803
|
-
monitor?.stop();
|
|
804
|
-
if (monitorLoop)
|
|
805
|
-
await monitorLoop;
|
|
806
|
-
await ownerChannel.close().catch(() => undefined);
|
|
807
|
-
await control.close();
|
|
808
|
-
ownerBinder?.release();
|
|
809
|
-
await agentSession.close();
|
|
810
|
-
unsubscribeRecovery?.();
|
|
811
|
-
throw new Error(`[${name}] owner channel failed to start: `
|
|
812
|
-
+ `${error?.message ?? String(error)}`);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
if (ownerChannel) {
|
|
816
|
-
control.setOwnerChannel(ownerChannel);
|
|
817
|
-
}
|
|
818
854
|
reloadLoopConfig = async () => {
|
|
819
855
|
const nextRole = findRole(loadConfig(configPath), name);
|
|
820
856
|
const definitions = nextRole.loops ?? [];
|
|
@@ -900,6 +936,14 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
900
936
|
}
|
|
901
937
|
},
|
|
902
938
|
});
|
|
939
|
+
let chatIdle;
|
|
940
|
+
try {
|
|
941
|
+
if (temp && !role.roomMemberStartup)
|
|
942
|
+
chatIdle = new TemporaryChatIdle(dir, deps.now());
|
|
943
|
+
}
|
|
944
|
+
catch {
|
|
945
|
+
deps.log(`[${name}] automatic idle closure disabled: activity state is unreadable`);
|
|
946
|
+
}
|
|
903
947
|
const start = deps.now();
|
|
904
948
|
let nextLoopReloadAt = deps.now() + 30_000;
|
|
905
949
|
let nextIdentityPollAt = deps.now();
|
|
@@ -923,6 +967,19 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
923
967
|
void recoveryController.recover(observation.generation).catch(error => deps.log(`[${name}] daemon recovery controller failed: ${error?.name ?? 'Error'}`));
|
|
924
968
|
}
|
|
925
969
|
}
|
|
970
|
+
let idleExpired = false;
|
|
971
|
+
try {
|
|
972
|
+
idleExpired = chatIdle?.observe(sessionHandle.snapshot(), sessionHandle.conversationSnapshot?.().queueDepth ?? -1, now) ?? false;
|
|
973
|
+
}
|
|
974
|
+
catch {
|
|
975
|
+
chatIdle = undefined;
|
|
976
|
+
deps.log(`[${name}] automatic idle closure disabled: activity state could not be saved`);
|
|
977
|
+
}
|
|
978
|
+
if (idleExpired && await arbiter.retireIfIdle((snapshot, queue) => chatIdle.observe(snapshot, queue, deps.now()))) {
|
|
979
|
+
retirementReason = 'idle-timeout';
|
|
980
|
+
sessionClosed = true;
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
926
983
|
if (deps.shouldStop?.()) {
|
|
927
984
|
if (temp) {
|
|
928
985
|
retirementReason = requestedTempStopReason(dir) ?? 'supervisor-signal';
|
|
@@ -934,7 +991,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
934
991
|
}
|
|
935
992
|
if (temp && now >= nextIdentityPollAt) {
|
|
936
993
|
nextIdentityPollAt = now + TEMP_IDENTITY_POLL_MS;
|
|
937
|
-
const presence = await probeIdentityPresence(role.identity, deps.fetch, resolvedMonitorDeps.env);
|
|
994
|
+
const presence = await probeIdentityPresence(role.identity, deps.fetch, resolvedMonitorDeps.env, deps.identityProbeDeps);
|
|
938
995
|
if (presence.state === 'present') {
|
|
939
996
|
identityObserved = true;
|
|
940
997
|
identityAbsentSince = undefined;
|
|
@@ -1051,11 +1108,11 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
1051
1108
|
throw new SupervisorRecycleRequiredError();
|
|
1052
1109
|
return {
|
|
1053
1110
|
elapsedSecs: elapsed, exit: exitRecord, rotated, mode, modelRecovery,
|
|
1054
|
-
...(retirementReason ? { retirementReason } : {}),
|
|
1111
|
+
...(layoutRetiring ? { retirementReason: 'operator-stop' } : retirementReason ? { retirementReason } : {}),
|
|
1055
1112
|
};
|
|
1056
1113
|
}
|
|
1057
1114
|
finally {
|
|
1058
|
-
await managedService.close(
|
|
1115
|
+
await managedService.close(layoutRetiring);
|
|
1059
1116
|
}
|
|
1060
1117
|
}
|
|
1061
1118
|
/**
|
package/dist/session/acp.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type TaskNoticeBinding, type TaskCreatedNotice } from './task-notice.js';
|
|
1
2
|
import * as acp from '@agentclientprotocol/sdk';
|
|
2
3
|
import type { CommonPermissions } from '../config.js';
|
|
3
4
|
import type { AcpMcpServer } from '../harness/types.js';
|
|
@@ -338,8 +339,13 @@ export declare class AcpSession implements AgentSession {
|
|
|
338
339
|
* signal. Never infer commentary from text, message order, or unknown meta.
|
|
339
340
|
*/
|
|
340
341
|
private codexMessagePhase;
|
|
341
|
-
/**
|
|
342
|
+
/**
|
|
343
|
+
* Normalize every ACP update losslessly into the durable ledger. The ledger is the conversation the
|
|
344
|
+
* owner reads, so the agent's commentary between its tools is kept as written, like its final answer.
|
|
345
|
+
*/
|
|
342
346
|
private recordConversationUpdate;
|
|
347
|
+
taskNoticeBinding(): TaskNoticeBinding | undefined;
|
|
348
|
+
recordTaskCreated(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
|
|
343
349
|
conversationPage(request?: {
|
|
344
350
|
after?: string;
|
|
345
351
|
limit?: number;
|
package/dist/session/acp.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appendTaskCreated } from './task-notice.js';
|
|
1
2
|
import { spawn } from 'node:child_process';
|
|
2
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
4
|
import { existsSync, lstatSync, readFileSync, readlinkSync, realpathSync, writeFileSync, } from 'node:fs';
|
|
@@ -7,6 +8,7 @@ import * as acp from '@agentclientprotocol/sdk';
|
|
|
7
8
|
import { normalizeSessionUpdate } from './conversation-normalizer.js';
|
|
8
9
|
import { ConversationEventStore } from './conversation-store.js';
|
|
9
10
|
import { SessionEvents } from './events.js';
|
|
11
|
+
import { recordClaudeRateLimit } from '../subscriptions/observed.js';
|
|
10
12
|
import { DEFAULT_STALL_TIMEOUT_MS, STALL_RECOVERY_PROMPT, StallWatchdog, StallToolHistory, hasStallRecoveryClaim } from './stall-watchdog.js';
|
|
11
13
|
import { ACP_CANCEL_DEADLINE_EXCEEDED, SessionControlError, classifyChildExit, sessionBackendCapabilities, turnResult, } from './types.js';
|
|
12
14
|
const CANCEL_SETTLE_GRACE_MS = 15_000;
|
|
@@ -36,7 +38,6 @@ const TERMINAL_TOOL_STATUSES = new Set(['completed', 'failed']);
|
|
|
36
38
|
/** Consumed only by Fleet's authenticated bundled-Codex app-server proxy. */
|
|
37
39
|
export const CODEX_DISABLE_INHERITED_MCP_ENV = 'OURS_FLEET_CODEX_DISABLE_INHERITED_MCP';
|
|
38
40
|
const SCHEDULED_LOOP_REDACTION = '[scheduled-loop content redacted]';
|
|
39
|
-
const OWNER_COMMENTARY_REDACTION = '[assistant commentary redacted]';
|
|
40
41
|
const MAX_CANONICAL_SYMLINK_DEPTH = 40;
|
|
41
42
|
const scheduledTurn = (turn) => turn?.origin?.kind === 'scheduled-loop';
|
|
42
43
|
/**
|
|
@@ -760,6 +761,8 @@ export class AcpSession {
|
|
|
760
761
|
const existing = this.conversation.receiptFor(command.commandId, bodyDigest);
|
|
761
762
|
if (existing)
|
|
762
763
|
return existing;
|
|
764
|
+
if (command.expectedSessionGeneration !== undefined && command.expectedSessionGeneration !== this.conversationSnapshot().sessionGeneration)
|
|
765
|
+
throw new Error('session_changed: Agent session changed. Record a new voice message.');
|
|
763
766
|
const queued = await this.queuePrompt(command.text, {
|
|
764
767
|
origin: { kind: 'owner-admin-console', commandId: command.commandId },
|
|
765
768
|
actor: { browserSession: command.actorBrowserSession },
|
|
@@ -1707,9 +1710,15 @@ export class AcpSession {
|
|
|
1707
1710
|
const scheduled = this.activeTurn?.origin?.kind === 'scheduled-loop';
|
|
1708
1711
|
const messagePhase = update.sessionUpdate === 'agent_message_chunk'
|
|
1709
1712
|
? this.codexMessagePhase(update) : undefined;
|
|
1710
|
-
this.recordConversationUpdate(update, scheduled
|
|
1713
|
+
this.recordConversationUpdate(update, scheduled);
|
|
1711
1714
|
if (update.sessionUpdate === 'config_option_update')
|
|
1712
1715
|
this.captureRuntimeMetadata(update.configOptions);
|
|
1716
|
+
if (update.sessionUpdate === 'usage_update') {
|
|
1717
|
+
// Claude subscription rate-limit snapshot (claude-agent-acp forwards the SDK rate_limit_event).
|
|
1718
|
+
const rateLimit = update._meta?.['_claude/rateLimit'];
|
|
1719
|
+
if (rateLimit)
|
|
1720
|
+
recordClaudeRateLimit(this.options.stateDir, rateLimit);
|
|
1721
|
+
}
|
|
1713
1722
|
switch (update.sessionUpdate) {
|
|
1714
1723
|
case 'agent_message_chunk': {
|
|
1715
1724
|
const phase = messagePhase;
|
|
@@ -1785,13 +1794,15 @@ export class AcpSession {
|
|
|
1785
1794
|
return undefined;
|
|
1786
1795
|
return phase === 'commentary' || phase === 'final_answer' ? phase : 'ambiguous';
|
|
1787
1796
|
}
|
|
1788
|
-
/**
|
|
1789
|
-
|
|
1797
|
+
/**
|
|
1798
|
+
* Normalize every ACP update losslessly into the durable ledger. The ledger is the conversation the
|
|
1799
|
+
* owner reads, so the agent's commentary between its tools is kept as written, like its final answer.
|
|
1800
|
+
*/
|
|
1801
|
+
recordConversationUpdate(update, scheduled) {
|
|
1790
1802
|
const normalized = normalizeSessionUpdate(update, scheduled ? {
|
|
1791
1803
|
redactText: SCHEDULED_LOOP_REDACTION,
|
|
1792
1804
|
redactToolCallId: 'scheduled-loop-tool',
|
|
1793
|
-
}
|
|
1794
|
-
: commentary ? { redactText: OWNER_COMMENTARY_REDACTION } : {});
|
|
1805
|
+
} : {});
|
|
1795
1806
|
this.conversation.appendSafe({
|
|
1796
1807
|
kind: normalized.kind,
|
|
1797
1808
|
sessionGeneration: this.sessionGeneration,
|
|
@@ -1805,6 +1816,10 @@ export class AcpSession {
|
|
|
1805
1816
|
});
|
|
1806
1817
|
}
|
|
1807
1818
|
// ── conversation ledger access (AgentSession) ─────────────────────────────
|
|
1819
|
+
taskNoticeBinding() {
|
|
1820
|
+
return this.activeTurn && this.sessionId ? { sessionGeneration: this.sessionGeneration, acpSessionId: this.sessionId, promptId: this.activeTurn.id } : undefined;
|
|
1821
|
+
}
|
|
1822
|
+
recordTaskCreated(binding, notice) { appendTaskCreated(this.conversation, binding, notice); }
|
|
1808
1823
|
conversationPage(request = {}) {
|
|
1809
1824
|
const floor = Number(this.conversationStartCursor ?? 0);
|
|
1810
1825
|
const requested = Number(request.after ?? 0);
|
|
@@ -23,6 +23,7 @@ export declare class RoleTurnArbiter implements AgentSession {
|
|
|
23
23
|
private tail;
|
|
24
24
|
private unsettled;
|
|
25
25
|
private stopping;
|
|
26
|
+
private retiring;
|
|
26
27
|
/** Bumped by `retireStalledAdmission`; every claim carries the one it was made under. */
|
|
27
28
|
private generation;
|
|
28
29
|
/** Resolves the instant the CURRENT generation is retired. */
|
|
@@ -62,6 +63,8 @@ export declare class RoleTurnArbiter implements AgentSession {
|
|
|
62
63
|
tryScheduled(text: string, origin: Extract<PromptOrigin, {
|
|
63
64
|
kind: 'scheduled-loop';
|
|
64
65
|
}>, beforeQueue?: () => void | Promise<void>): Promise<ScheduledAttempt>;
|
|
66
|
+
/** Retire only at the same admission boundary used by all prompt producers. */
|
|
67
|
+
retireIfIdle(expired: (snapshot: SessionSnapshot, queueDepth: number) => boolean): Promise<boolean>;
|
|
65
68
|
stopScheduledAdmission(): void;
|
|
66
69
|
isAlive(): boolean;
|
|
67
70
|
snapshot(): SessionSnapshot;
|
package/dist/session/arbiter.js
CHANGED
|
@@ -29,6 +29,7 @@ export class RoleTurnArbiter {
|
|
|
29
29
|
tail = Promise.resolve();
|
|
30
30
|
unsettled = 0;
|
|
31
31
|
stopping = false;
|
|
32
|
+
retiring = false;
|
|
32
33
|
/** Bumped by `retireStalledAdmission`; every claim carries the one it was made under. */
|
|
33
34
|
generation = 0;
|
|
34
35
|
/** Resolves the instant the CURRENT generation is retired. */
|
|
@@ -89,7 +90,8 @@ export class RoleTurnArbiter {
|
|
|
89
90
|
retired.retire();
|
|
90
91
|
}
|
|
91
92
|
queuePrompt(text, options = {}) {
|
|
92
|
-
return this.exclusive(async () =>
|
|
93
|
+
return this.exclusive(async () => { if (this.retiring)
|
|
94
|
+
throw new Error('Chat is closing'); return this.track(await this.session.queuePrompt(text, options)); });
|
|
93
95
|
}
|
|
94
96
|
async submitPrompt(text, options = {}) {
|
|
95
97
|
return (await this.queuePrompt(text, options)).completion;
|
|
@@ -99,6 +101,8 @@ export class RoleTurnArbiter {
|
|
|
99
101
|
* answers and explicit interrupts must remain able to pass immediately.
|
|
100
102
|
*/
|
|
101
103
|
submitPromptAfterTool(text, options = {}) {
|
|
104
|
+
if (this.retiring)
|
|
105
|
+
return Promise.reject(new Error('Chat is closing'));
|
|
102
106
|
return this.session.submitPromptAfterTool?.(text, options)
|
|
103
107
|
?? this.session.submitPrompt(text, { ...options, interrupt: false, steer: false });
|
|
104
108
|
}
|
|
@@ -136,6 +140,19 @@ export class RoleTurnArbiter {
|
|
|
136
140
|
throw error;
|
|
137
141
|
});
|
|
138
142
|
}
|
|
143
|
+
/** Retire only at the same admission boundary used by all prompt producers. */
|
|
144
|
+
retireIfIdle(expired) {
|
|
145
|
+
return this.exclusive(async () => {
|
|
146
|
+
const snapshot = this.session.snapshot();
|
|
147
|
+
const queue = this.session.conversationSnapshot?.().queueDepth ?? -1;
|
|
148
|
+
if (this.retiring || this.unsettled > 0 || snapshot.readiness !== 'idle' || !expired(snapshot, queue))
|
|
149
|
+
return false;
|
|
150
|
+
this.retiring = true;
|
|
151
|
+
this.stopping = true;
|
|
152
|
+
await this.session.close();
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
139
156
|
stopScheduledAdmission() { this.stopping = true; }
|
|
140
157
|
isAlive() { return this.session.isAlive(); }
|
|
141
158
|
snapshot() { return this.session.snapshot(); }
|
|
@@ -157,7 +174,8 @@ export class RoleTurnArbiter {
|
|
|
157
174
|
submitPromptBrowser(command) {
|
|
158
175
|
if (!this.session.submitPromptBrowser)
|
|
159
176
|
return Promise.reject(new Error('browser prompt admission is unavailable'));
|
|
160
|
-
return this.exclusive(() => this.
|
|
177
|
+
return this.exclusive(() => { if (this.retiring)
|
|
178
|
+
throw new Error('Chat is closing'); return this.session.submitPromptBrowser(command); });
|
|
161
179
|
}
|
|
162
180
|
interrupt(source = 'local-console') {
|
|
163
181
|
return this.exclusive(async () => interruptOutcome(await this.session.interrupt(source)));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type TaskNoticeBinding, type TaskCreatedNotice } from './task-notice.js';
|
|
1
2
|
import type { CommonPermissions } from '../config.js';
|
|
2
3
|
import type { ConversationEventV1, ConversationSnapshot, PromptOrigin, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
|
|
3
4
|
import type { CodexAppServerTransportFactory } from './codex-app-server-transport.js';
|
|
@@ -76,6 +77,8 @@ export declare class CodexAppServerSession implements AgentSession {
|
|
|
76
77
|
setControllerAttached(attached: boolean): void;
|
|
77
78
|
exitResult(): ExitRecord | null;
|
|
78
79
|
close(): Promise<void>;
|
|
80
|
+
taskNoticeBinding(): TaskNoticeBinding | undefined;
|
|
81
|
+
recordTaskCreated(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
|
|
79
82
|
conversationPage(request?: {
|
|
80
83
|
after?: string;
|
|
81
84
|
limit?: number;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { appendTaskCreated } from './task-notice.js';
|
|
1
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
3
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
5
|
+
import { boundedJson } from './conversation-normalizer.js';
|
|
4
6
|
import { ConversationEventStore } from './conversation-store.js';
|
|
5
7
|
import { SessionEvents } from './events.js';
|
|
6
8
|
import { CodexAppServerTransport } from './codex-app-server-transport.js';
|
|
@@ -12,7 +14,6 @@ const INTERRUPT_TERMINATE_GRACE_MS = 5_000;
|
|
|
12
14
|
const TERMINAL_RECONCILE_QUIET_MS = 2_000;
|
|
13
15
|
const TERMINAL_RECONCILE_REQUEST_TIMEOUT_MS = 5_000;
|
|
14
16
|
const SCHEDULED_REDACTION = '[scheduled-loop content redacted]';
|
|
15
|
-
const COMMENTARY_REDACTION = '[assistant commentary redacted]';
|
|
16
17
|
const OWNER_ADMIN_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_admin_console';
|
|
17
18
|
const OWNER_CHANNEL_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_channel';
|
|
18
19
|
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -228,8 +229,10 @@ export class CodexAppServerSession {
|
|
|
228
229
|
}
|
|
229
230
|
async interrupt(source = 'local-console') {
|
|
230
231
|
const active = this.activeTurn;
|
|
231
|
-
if (!active || !this.threadId
|
|
232
|
+
if (!active || !this.threadId)
|
|
232
233
|
return { state: 'settled' };
|
|
234
|
+
if (!active.nativeTurnId)
|
|
235
|
+
throw new SessionControlError('backend', 'The operation is still pending without a native turn ID. Cancellation is not confirmed; inspect or restart the agent if it does not progress.');
|
|
233
236
|
active.cancellationSource ??= source;
|
|
234
237
|
this.events.emit('state', { turnId: active.promptId, status: 'running', origin: active.origin });
|
|
235
238
|
this.conversation.appendSafe({
|
|
@@ -335,6 +338,10 @@ export class CodexAppServerSession {
|
|
|
335
338
|
this.conversation.close();
|
|
336
339
|
await this.transport.close();
|
|
337
340
|
}
|
|
341
|
+
taskNoticeBinding() {
|
|
342
|
+
return this.activeTurn && this.threadId ? { sessionGeneration: this.sessionGeneration, acpSessionId: this.threadId, promptId: this.activeTurn.promptId } : undefined;
|
|
343
|
+
}
|
|
344
|
+
recordTaskCreated(binding, notice) { appendTaskCreated(this.conversation, binding, notice); }
|
|
338
345
|
conversationPage(request = {}) {
|
|
339
346
|
const floor = Number(this.conversationStartCursor ?? 0);
|
|
340
347
|
const requested = Number(request.after ?? 0);
|
|
@@ -367,6 +374,8 @@ export class CodexAppServerSession {
|
|
|
367
374
|
const existing = this.conversation.receiptFor(command.commandId, bodyDigest);
|
|
368
375
|
if (existing)
|
|
369
376
|
return existing;
|
|
377
|
+
if (command.expectedSessionGeneration !== undefined && command.expectedSessionGeneration !== this.conversationSnapshot().sessionGeneration)
|
|
378
|
+
throw new Error('session_changed: Agent session changed. Record a new voice message.');
|
|
370
379
|
const queued = await this.queuePrompt(command.text, {
|
|
371
380
|
origin: { kind: 'owner-admin-console', commandId: command.commandId },
|
|
372
381
|
actor: { browserSession: command.actorBrowserSession },
|
|
@@ -415,6 +424,10 @@ export class CodexAppServerSession {
|
|
|
415
424
|
this.reasoningEffort = { value: effort };
|
|
416
425
|
this.readiness = 'idle';
|
|
417
426
|
this.events.emit('state', { status: 'idle', text: `Codex thread ${this.threadId}` });
|
|
427
|
+
this.conversation.appendSafe({
|
|
428
|
+
kind: 'capabilities.updated', sessionGeneration: this.sessionGeneration,
|
|
429
|
+
payload: { commands: [{ name: 'compact', description: textBlock('Compact session context') }] },
|
|
430
|
+
});
|
|
418
431
|
this.conversation.appendSafe({
|
|
419
432
|
kind: 'session.state', sessionGeneration: this.sessionGeneration,
|
|
420
433
|
payload: { status: 'idle' },
|
|
@@ -486,6 +499,12 @@ export class CodexAppServerSession {
|
|
|
486
499
|
promptId, turnId: promptId, source: sourceFor(origin).source, payload: {},
|
|
487
500
|
});
|
|
488
501
|
try {
|
|
502
|
+
if (text.trim() === '/compact') {
|
|
503
|
+
// The acknowledgement is not completion. Standard turn notifications
|
|
504
|
+
// settle the same queued prompt and durable browser receipt.
|
|
505
|
+
await this.transport.request('thread/compact/start', { threadId: this.threadId });
|
|
506
|
+
return await settled;
|
|
507
|
+
}
|
|
489
508
|
const response = await this.transport.request('turn/start', {
|
|
490
509
|
threadId: this.threadId,
|
|
491
510
|
input: [{ type: 'text', text, text_elements: [] }],
|
|
@@ -699,7 +718,7 @@ export class CodexAppServerSession {
|
|
|
699
718
|
promptId: this.activeTurn?.promptId, turnId: this.activeTurn?.promptId,
|
|
700
719
|
messageId: id, source: 'agent',
|
|
701
720
|
payload: {
|
|
702
|
-
role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION :
|
|
721
|
+
role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION : undefined),
|
|
703
722
|
},
|
|
704
723
|
});
|
|
705
724
|
}
|
|
@@ -746,6 +765,10 @@ export class CodexAppServerSession {
|
|
|
746
765
|
toolCallId: scheduled ? 'scheduled-loop-tool' : id, snapshot,
|
|
747
766
|
title: scheduled ? 'scheduled-loop tool' : tool.title, status: tool.status,
|
|
748
767
|
kind: string(item.type),
|
|
768
|
+
...(!scheduled && item.arguments !== undefined ? { rawInput: boundedJson(item.arguments) } : {}),
|
|
769
|
+
...(!scheduled && item.type === 'commandExecution' ? { rawInput: boundedJson({ command: item.command, cwd: item.cwd }), ...(item.aggregatedOutput !== undefined ? { rawOutput: boundedJson({ output: item.aggregatedOutput, exitCode: item.exitCode }) } : {}) } : {}),
|
|
770
|
+
...(!scheduled && item.type === 'fileChange' && item.changes !== undefined ? { rawOutput: boundedJson(item.changes) } : {}),
|
|
771
|
+
...(!scheduled && (item.result !== undefined || item.error !== undefined) ? { rawOutput: boundedJson({ result: item.result, error: item.error }) } : {}),
|
|
749
772
|
},
|
|
750
773
|
});
|
|
751
774
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LayoutControlRequest } from '../rooms-tasks/layout-control.js';
|
|
2
|
+
import type { SupervisorCorrespondence, AgentHistoryQuery } from '../agent-ours/correspondence.js';
|
|
1
3
|
import { type Socket } from 'node:net';
|
|
2
4
|
import type { AgentSession, ControlFailureKind, SessionEvent, SessionSnapshot } from './types.js';
|
|
3
5
|
import type { OwnerChannelHandle, OwnerChannelManagementRequest } from '../owner-channel/channel.js';
|
|
@@ -9,8 +11,11 @@ export interface ControlRequest {
|
|
|
9
11
|
version: 1 | 2 | 3;
|
|
10
12
|
id: string;
|
|
11
13
|
token: string;
|
|
12
|
-
command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'interrupt_v2' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish';
|
|
14
|
+
command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'submit_voice_prompt' | 'interrupt_v2' | 'layout_control' | 'agent_contacts' | 'agent_history' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish';
|
|
15
|
+
layout?: LayoutControlRequest;
|
|
16
|
+
agentHistory?: AgentHistoryQuery;
|
|
13
17
|
text?: string;
|
|
18
|
+
expectedSessionGeneration?: string;
|
|
14
19
|
permissionId?: string;
|
|
15
20
|
optionId?: string;
|
|
16
21
|
since?: number;
|
|
@@ -112,9 +117,13 @@ export declare class RoleControlServer {
|
|
|
112
117
|
private readonly sockets;
|
|
113
118
|
/** Interrupt idempotency: same command id returns the first receipt. */
|
|
114
119
|
private readonly interruptCommands;
|
|
120
|
+
private correspondence?;
|
|
121
|
+
setCorrespondence(value: SupervisorCorrespondence): void;
|
|
115
122
|
private ownerChannel?;
|
|
116
123
|
private loopManager?;
|
|
117
124
|
private reloadConfig?;
|
|
125
|
+
private layoutControl?;
|
|
126
|
+
setLayoutControl(handler: (request: LayoutControlRequest) => Promise<unknown>): void;
|
|
118
127
|
private fleetSpawner?;
|
|
119
128
|
private fleetAuditor?;
|
|
120
129
|
constructor(stateDir: string, session: AgentSession, log: (line: string) => void);
|