@ours.network/fleet 1.1.0-nightly.3 → 1.1.0-nightly.30

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.
Files changed (208) hide show
  1. package/README.md +244 -40
  2. package/dist/agent-recovery-gate.d.ts +17 -0
  3. package/dist/agent-recovery-gate.js +135 -0
  4. package/dist/application/capabilities.js +18 -6
  5. package/dist/application/fleet-query-service.js +20 -5
  6. package/dist/application/role-command-service.js +9 -0
  7. package/dist/application/role-creation-service.d.ts +4 -4
  8. package/dist/application/role-creation-service.js +19 -1
  9. package/dist/application/role-repository.d.ts +4 -2
  10. package/dist/application/role-repository.js +6 -3
  11. package/dist/application/session-control.d.ts +3 -1
  12. package/dist/application/session-control.js +4 -2
  13. package/dist/application/task-room-service.d.ts +93 -40
  14. package/dist/application/task-room-service.js +579 -173
  15. package/dist/briefing.js +85 -24
  16. package/dist/build-info.json +4 -4
  17. package/dist/canonical-json.d.ts +2 -0
  18. package/dist/canonical-json.js +10 -0
  19. package/dist/cli.js +217 -51
  20. package/dist/config-yaml.d.ts +1 -1
  21. package/dist/config.d.ts +38 -3
  22. package/dist/config.js +240 -28
  23. package/dist/creation.d.ts +1 -1
  24. package/dist/creation.js +7 -4
  25. package/dist/daemon-recovery.d.ts +93 -0
  26. package/dist/daemon-recovery.js +328 -0
  27. package/dist/docs.d.ts +1 -1
  28. package/dist/docs.js +213 -42
  29. package/dist/doctor.d.ts +2 -0
  30. package/dist/doctor.js +54 -4
  31. package/dist/fleet-command-audit.d.ts +181 -0
  32. package/dist/fleet-command-audit.js +738 -0
  33. package/dist/fleet-proxy.d.ts +7 -2
  34. package/dist/harness/agent-session.d.ts +5 -0
  35. package/dist/harness/claude-code-session.d.ts +2 -0
  36. package/dist/harness/claude-code-session.js +6 -1
  37. package/dist/harness/claude-code.js +2 -0
  38. package/dist/harness/codex-app-server-proxy.d.ts +40 -0
  39. package/dist/harness/codex-app-server-proxy.js +348 -14
  40. package/dist/harness/codex-session.d.ts +11 -2
  41. package/dist/harness/codex-session.js +22 -2
  42. package/dist/harness/codex.d.ts +5 -1
  43. package/dist/harness/codex.js +110 -10
  44. package/dist/harness/types.d.ts +1 -1
  45. package/dist/index.d.ts +4 -3
  46. package/dist/index.js +3 -2
  47. package/dist/init-guidance.d.ts +1 -1
  48. package/dist/init-guidance.js +4 -3
  49. package/dist/init-wizard.d.ts +98 -0
  50. package/dist/init-wizard.js +631 -0
  51. package/dist/lifecycle-summary.d.ts +134 -0
  52. package/dist/lifecycle-summary.js +179 -0
  53. package/dist/loops/config.d.ts +10 -1
  54. package/dist/loops/config.js +38 -5
  55. package/dist/model-recovery.d.ts +1 -1
  56. package/dist/ops.js +2 -17
  57. package/dist/owner-channel/channel.d.ts +75 -3
  58. package/dist/owner-channel/channel.js +531 -123
  59. package/dist/owner-channel/commands.d.ts +28 -6
  60. package/dist/owner-channel/commands.js +112 -36
  61. package/dist/owner-channel/lifecycle-outbox.d.ts +24 -0
  62. package/dist/owner-channel/lifecycle-outbox.js +87 -0
  63. package/dist/owner-channel/ours-client.d.ts +19 -0
  64. package/dist/owner-channel/ours-client.js +3 -0
  65. package/dist/owner-channel/state.d.ts +2 -0
  66. package/dist/owner-channel/state.js +8 -2
  67. package/dist/permissions.js +1 -1
  68. package/dist/preset-bootstrap.d.ts +10 -0
  69. package/dist/preset-bootstrap.js +85 -0
  70. package/dist/preset-migration.d.ts +42 -0
  71. package/dist/preset-migration.js +450 -0
  72. package/dist/reports/artifact.d.ts +10 -0
  73. package/dist/reports/artifact.js +32 -0
  74. package/dist/reports/index.d.ts +5 -0
  75. package/dist/reports/index.js +5 -0
  76. package/dist/reports/render.d.ts +6 -0
  77. package/dist/reports/render.js +240 -0
  78. package/dist/reports/service.d.ts +42 -0
  79. package/dist/reports/service.js +57 -0
  80. package/dist/reports/tasks.d.ts +52 -0
  81. package/dist/reports/tasks.js +177 -0
  82. package/dist/reports/types.d.ts +315 -0
  83. package/dist/reports/types.js +6 -0
  84. package/dist/resolved-plan.js +13 -1
  85. package/dist/rooms-tasks/cli.d.ts +2 -0
  86. package/dist/rooms-tasks/cli.js +519 -261
  87. package/dist/rooms-tasks/close.d.ts +9 -1
  88. package/dist/rooms-tasks/close.js +23 -7
  89. package/dist/rooms-tasks/config.js +16 -29
  90. package/dist/rooms-tasks/cowork-adapter.d.ts +5 -0
  91. package/dist/rooms-tasks/cowork-adapter.js +13 -0
  92. package/dist/rooms-tasks/deletion.d.ts +51 -0
  93. package/dist/rooms-tasks/deletion.js +217 -0
  94. package/dist/rooms-tasks/external-worker.d.ts +9 -0
  95. package/dist/rooms-tasks/external-worker.js +52 -9
  96. package/dist/rooms-tasks/launch-snapshot.d.ts +19 -0
  97. package/dist/rooms-tasks/launch-snapshot.js +181 -0
  98. package/dist/rooms-tasks/member-overrides.d.ts +30 -0
  99. package/dist/rooms-tasks/member-overrides.js +204 -0
  100. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  101. package/dist/rooms-tasks/member-startup.js +9 -2
  102. package/dist/rooms-tasks/provision.js +146 -56
  103. package/dist/rooms-tasks/room-state.d.ts +1 -0
  104. package/dist/rooms-tasks/room-state.js +12 -1
  105. package/dist/rooms-tasks/task-room-name.d.ts +12 -0
  106. package/dist/rooms-tasks/task-room-name.js +61 -0
  107. package/dist/rooms-tasks/task-state.d.ts +115 -3
  108. package/dist/rooms-tasks/task-state.js +361 -11
  109. package/dist/rooms-tasks/templates.d.ts +4 -2
  110. package/dist/rooms-tasks/templates.js +42 -78
  111. package/dist/rooms-tasks/terminal.d.ts +3 -0
  112. package/dist/rooms-tasks/terminal.js +10 -4
  113. package/dist/rooms-tasks/types.d.ts +94 -8
  114. package/dist/rooms-tasks/types.js +14 -1
  115. package/dist/runner.d.ts +12 -2
  116. package/dist/runner.js +167 -56
  117. package/dist/session/acp.d.ts +9 -1
  118. package/dist/session/acp.js +35 -9
  119. package/dist/session/arbiter.d.ts +2 -1
  120. package/dist/session/arbiter.js +2 -0
  121. package/dist/session/codex-app-server-transport.d.ts +53 -0
  122. package/dist/session/codex-app-server-transport.js +184 -0
  123. package/dist/session/codex-app-server.d.ts +122 -0
  124. package/dist/session/codex-app-server.js +1031 -0
  125. package/dist/session/control.d.ts +14 -1
  126. package/dist/session/control.js +51 -0
  127. package/dist/session/conversation-types.d.ts +4 -4
  128. package/dist/session/types.d.ts +24 -6
  129. package/dist/session/types.js +28 -0
  130. package/dist/spawn.d.ts +9 -0
  131. package/dist/spawn.js +77 -9
  132. package/dist/temp-lifecycle.d.ts +1 -1
  133. package/dist/web/fleet-config-service.d.ts +1 -0
  134. package/dist/web/fleet-config-service.js +27 -8
  135. package/dist/web/runtime.js +11 -6
  136. package/dist/web/server.js +28 -1
  137. package/dist/web-app/assets/{index-BbTc5KtZ.js → index-C0h3ALvs.js} +8 -8
  138. package/dist/web-app/index.html +1 -1
  139. package/package.json +5 -4
  140. package/presets/brain-catalog.json +17 -0
  141. package/presets/fleet/agent_templates/Critic.yaml +5 -0
  142. package/presets/fleet/agent_templates/Developer.yaml +5 -0
  143. package/presets/fleet/agent_templates/LocalCoordinator.yaml +50 -0
  144. package/presets/fleet/agents/FleetCoordinator.yaml +3 -0
  145. package/presets/fleet/brains/claude-default.yaml +5 -0
  146. package/presets/fleet/brains/claude-fable-5-high.yaml +5 -0
  147. package/presets/fleet/brains/claude-fable-5-low.yaml +5 -0
  148. package/presets/fleet/brains/claude-fable-5-max.yaml +5 -0
  149. package/presets/fleet/brains/claude-fable-5-medium.yaml +5 -0
  150. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +5 -0
  151. package/presets/fleet/brains/claude-opus-5-high.yaml +5 -0
  152. package/presets/fleet/brains/claude-opus-5-low.yaml +5 -0
  153. package/presets/fleet/brains/claude-opus-5-max.yaml +5 -0
  154. package/presets/fleet/brains/claude-opus-5-medium.yaml +5 -0
  155. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +5 -0
  156. package/presets/fleet/brains/claude-sonnet-5-high.yaml +5 -0
  157. package/presets/fleet/brains/claude-sonnet-5-low.yaml +5 -0
  158. package/presets/fleet/brains/claude-sonnet-5-max.yaml +5 -0
  159. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +5 -0
  160. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +5 -0
  161. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +5 -0
  162. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +5 -0
  163. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +5 -0
  164. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +5 -0
  165. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +5 -0
  166. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +5 -0
  167. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +5 -0
  168. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +5 -0
  169. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +5 -0
  170. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +5 -0
  171. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +5 -0
  172. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +5 -0
  173. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +5 -0
  174. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +5 -0
  175. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +5 -0
  176. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +5 -0
  177. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +5 -0
  178. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +5 -0
  179. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +5 -0
  180. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +5 -0
  181. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +5 -0
  182. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +5 -0
  183. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +5 -0
  184. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +5 -0
  185. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +5 -0
  186. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +5 -0
  187. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +5 -0
  188. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +5 -0
  189. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +5 -0
  190. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +5 -0
  191. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +5 -0
  192. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +5 -0
  193. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +5 -0
  194. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +5 -0
  195. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +5 -0
  196. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +5 -0
  197. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +5 -0
  198. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +5 -0
  199. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +5 -0
  200. package/presets/fleet/roles/Coordinator.yaml +30 -0
  201. package/presets/fleet/roles/Critic.yaml +28 -0
  202. package/presets/fleet/roles/Developer.yaml +28 -0
  203. package/presets/fleet/roles/LocalCoordinator.yaml +35 -0
  204. package/presets/fleet/room_templates/pair.yaml +13 -0
  205. package/presets/fleet/room_templates/single.yaml +9 -0
  206. package/presets/fleet/room_templates/team.yaml +16 -0
  207. package/presets/fleet.yaml +7 -0
  208. package/presets/manifest.json +6 -0
@@ -4,6 +4,7 @@ import { classifyActivity } from '../session/activity.js';
4
4
  import { controlRequest } from '../session/control.js';
5
5
  import { SessionControlError } from '../session/types.js';
6
6
  import { readExitRecord, readRestartLedger } from '../runner.js';
7
+ import { readDaemonRecoveryStatus } from '../daemon-recovery.js';
7
8
  import { roleCapabilities } from './capabilities.js';
8
9
  import { FleetError } from './errors.js';
9
10
  const clean = (value, max = 512) => value.replace(/[\0-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '').trim().slice(0, max);
@@ -46,7 +47,8 @@ function readIsolation(dir) {
46
47
  function sessionOverall(supervisor, session, restart, monitor, isolation, problems) {
47
48
  if (restart.circuit === 'open' || monitor.health === 'failed' || monitor.health === 'degraded'
48
49
  || isolation.degraded
49
- || problems.some(problem => problem.severity === 'error' || problem.source === 'watchdog')
50
+ || problems.some(problem => problem.severity === 'error'
51
+ || problem.source === 'watchdog' || problem.source === 'daemon-recovery')
50
52
  || session.readiness === 'failed')
51
53
  return 'attention';
52
54
  // A reachable agent session is the user's live interaction surface. Its
@@ -74,7 +76,8 @@ export class FleetQueryService {
74
76
  this.control = options.control ?? controlRequest;
75
77
  }
76
78
  async list() {
77
- const roles = await this.options.repository.list();
79
+ const roles = (await this.options.repository.list())
80
+ .filter(role => role.configured && role.lifetime === 'permanent');
78
81
  return Promise.all(roles.map(async (role) => {
79
82
  const status = await this.status(role);
80
83
  return { role, status, capabilities: roleCapabilities(role, status, this.options.capabilityContext) };
@@ -103,6 +106,16 @@ export class FleetQueryService {
103
106
  const lastExit = dir ? readExitRecord(join(dir, '.exit-status')) ?? undefined : undefined;
104
107
  const session = await this.session(role, dir, live.state);
105
108
  const problems = [...role.problems];
109
+ const recovery = dir ? readDaemonRecoveryStatus(dir) : undefined;
110
+ if (recovery && recovery.state !== 'recovered') {
111
+ const paths = ['agent', 'owner']
112
+ .filter(name => recovery.paths[name].state !== 'recovered')
113
+ .map(name => `${name}:${recovery.paths[name].state}`);
114
+ problems.push({
115
+ code: 'daemon_recovery', severity: 'warning', source: 'daemon-recovery',
116
+ detail: `${recovery.state}; ${paths.join(', ') || 'no degraded path'}; epoch ${recovery.epoch || 'unavailable'}`,
117
+ });
118
+ }
106
119
  if (live.state === 'running' && session.reachability !== 'online')
107
120
  problems.push({
108
121
  code: 'supervisor_session_disagreement', severity: 'warning',
@@ -141,14 +154,16 @@ export class FleetQueryService {
141
154
  }
142
155
  async session(role, dir, supervisor) {
143
156
  const intended = role.configuredBackend;
144
- if (intended === 'acp' && dir) {
157
+ if (intended && dir) {
145
158
  try {
146
159
  const response = await this.control(dir, { command: 'snapshot' }, 2_000);
147
160
  if (!response.ok)
148
161
  throw new SessionControlError(response.kind ?? 'backend', response.error ?? 'snapshot failed');
149
162
  const snapshot = response.result;
163
+ if (snapshot.backend !== intended)
164
+ throw new SessionControlError('backend', `live session reports backend '${String(snapshot.backend)}', expected '${intended}'`);
150
165
  return {
151
- backend: 'acp', reachability: snapshot.alive ? 'online' : 'offline',
166
+ backend: intended, reachability: snapshot.alive ? 'online' : 'offline',
152
167
  readiness: snapshot.readiness, evidence: 'authoritative',
153
168
  sessionId: snapshot.sessionId, lastError: snapshot.lastError,
154
169
  pendingPermissionId: snapshot.pendingPermissionId,
@@ -162,7 +177,7 @@ export class FleetQueryService {
162
177
  const failure = error instanceof SessionControlError ? error.kind : 'backend';
163
178
  const offline = failure === 'offline';
164
179
  return {
165
- backend: 'acp',
180
+ backend: intended,
166
181
  reachability: offline ? 'offline'
167
182
  : failure === 'control-unavailable' ? 'unavailable' : 'unknown',
168
183
  readiness: offline ? 'failed' : 'unknown',
@@ -12,6 +12,10 @@ export class RoleLifecycleService {
12
12
  const config = input.config ?? loadConfig(this.options.configPath);
13
13
  const selected = input.roleIds.length ? input.roleIds : config.roles.map(role => role.name);
14
14
  for (const roleId of selected) {
15
+ if (config.agentTemplates?.[roleId] && !config.roles.some(role => role.name === roleId))
16
+ throw new FleetError('capability_unavailable', `'${roleId}' is an inert Agent Template; lifecycle commands target only persistent fleet/agents instances`);
17
+ if (!config.roles.some(role => role.name === roleId))
18
+ throw new FleetError('role_not_found', `no such role '${roleId}'`);
15
19
  const role = await this.options.repository.get(roleId);
16
20
  if (!role)
17
21
  throw new FleetError('role_not_found', `no such role '${roleId}'`);
@@ -51,6 +55,11 @@ export class RoleCommandService {
51
55
  throw new FleetError('idempotency_conflict', 'action ID already belongs to another operation');
52
56
  return prior;
53
57
  }
58
+ const config = loadConfig(this.options.configPath);
59
+ if (config.agentTemplates?.[input.roleId] && !config.roles.some(role => role.name === input.roleId))
60
+ throw new FleetError('capability_unavailable', `'${input.roleId}' is an inert Agent Template; lifecycle commands target only persistent fleet/agents instances`);
61
+ if (!config.roles.some(role => role.name === input.roleId))
62
+ throw new FleetError('role_not_found', `no such role '${input.roleId}'`);
54
63
  const role = await this.options.repository.get(input.roleId);
55
64
  if (!role)
56
65
  throw new FleetError('role_not_found', `no such role '${input.roleId}'`);
@@ -1,4 +1,4 @@
1
- import { type CommonPermissions, type MonitorConfig, type MonitorInterrupt, type NotifyEventType } from '../config.js';
1
+ import { type CommonPermissions, type MonitorConfig, type MonitorInterrupt, type NotifyEventType, type SessionBackendId } from '../config.js';
2
2
  import { type IdentityProvisioner } from '../creation.js';
3
3
  import { spawnDryRun, type SpawnOpts, type SupervisorLauncher } from '../spawn.js';
4
4
  import type { OpsDeps } from '../ops.js';
@@ -53,7 +53,7 @@ export interface CreationPreview {
53
53
  name: string;
54
54
  identity: string;
55
55
  harness: string;
56
- session: 'acp';
56
+ session: SessionBackendId;
57
57
  model?: string;
58
58
  reasoningEffort?: string;
59
59
  cwd?: string;
@@ -77,7 +77,7 @@ export interface CreationAction {
77
77
  actionId: string;
78
78
  requestHash: string;
79
79
  roleId: string;
80
- session: 'acp';
80
+ session: SessionBackendId;
81
81
  lifetime: 'permanent' | 'temporary';
82
82
  state: CreationStage;
83
83
  stages: Array<{
@@ -103,7 +103,7 @@ export interface RoleCreationServiceOptions {
103
103
  tempLauncher?: SupervisorLauncher;
104
104
  allowedCwdRoots?: string[];
105
105
  journalDir?: string;
106
- probeReady?: (name: string, session: 'acp') => Promise<'ready' | 'attention' | 'unknown'>;
106
+ probeReady?: (name: string, session: SessionBackendId) => Promise<'ready' | 'attention' | 'unknown'>;
107
107
  onProgress?: (action: CreationAction) => void;
108
108
  /** Direct/managed callers must not create or restore the web action journal. */
109
109
  journal?: boolean;
@@ -10,6 +10,7 @@ import { FleetError, normalizeError } from './errors.js';
10
10
  import { inheritCallerSpawnDefaults } from '../fleet-proxy.js';
11
11
  import { effectivePermissionMode } from '../permissions.js';
12
12
  import { effectiveRoleModel } from '../model-env.js';
13
+ import { selectionOrigin, summarizeResolvedLaunch } from '../lifecycle-summary.js';
13
14
  const canonical = (value) => {
14
15
  if (Array.isArray(value))
15
16
  return `[${value.map(canonical).join(',')}]`;
@@ -61,14 +62,31 @@ export class RoleCreationService {
61
62
  const creationActionId = randomUUID();
62
63
  plan.options.creationActionId = creationActionId;
63
64
  const statePath = await this.launchSync(plan.options);
65
+ const selectionSummary = (kind) => {
66
+ const value = plan.options[kind];
67
+ const provenance = plan.inherited.includes(kind) ? 'inherited' : 'explicit';
68
+ if (!value)
69
+ return `unresolved (${provenance})`;
70
+ if ('ref' in value)
71
+ return `ref:${value.ref} (${provenance})`;
72
+ const fingerprint = hash(value.inline).slice(0, 16);
73
+ return `inline:sha256:${fingerprint} (${provenance})`;
74
+ };
75
+ const permissionMode = effectivePermissionMode(plan.preview.resolvedRole);
64
76
  return {
65
77
  caller: plan.caller, role: plan.options.name,
66
78
  lifetime: plan.options.temp ? 'temporary' : 'permanent', statePath,
67
79
  harness: plan.preview.resolvedRole.harness, session: plan.preview.resolvedRole.session,
68
80
  ...(effectiveRoleModel(plan.preview.resolvedRole) ? { model: effectiveRoleModel(plan.preview.resolvedRole) } : {}),
69
81
  monitor: { mode: plan.preview.resolvedRole.monitor.mode, interrupt: plan.preview.resolvedRole.monitor.interrupt },
70
- permissionMode: effectivePermissionMode(plan.preview.resolvedRole), inherited: plan.inherited,
82
+ permissionMode, inherited: plan.inherited,
71
83
  creationActionId,
84
+ brainSummary: selectionSummary('brain'), roleSummary: selectionSummary('role'),
85
+ configuration: summarizeResolvedLaunch(plan.preview.resolvedRole, {
86
+ role: selectionOrigin(plan.options.role ?? plan.options.agentDefinition?.role),
87
+ brain: selectionOrigin(plan.options.brain ?? plan.options.agentDefinition?.brain),
88
+ permissionMode,
89
+ }),
72
90
  };
73
91
  }
74
92
  launchSync(options, creation) {
@@ -1,10 +1,12 @@
1
+ import { type SessionBackendId } from '../config.js';
1
2
  import type { RoleRecord } from './types.js';
2
3
  export interface RoleRepositoryOptions {
3
4
  configPath?: string;
4
5
  permanentRoot?: string;
5
6
  temporaryRoot?: string;
6
- probeBackend?: (name: string, intended?: 'acp') => Promise<{
7
- acp: boolean;
7
+ probeBackend?: (name: string, intended?: SessionBackendId) => Promise<{
8
+ acp?: boolean;
9
+ backend?: SessionBackendId;
8
10
  }>;
9
11
  concurrency?: number;
10
12
  timeoutMs?: number;
@@ -44,9 +44,10 @@ function snapshot(path, name) {
44
44
  throw new Error('snapshot must be a mapping');
45
45
  if (raw.session === 'tmux')
46
46
  throw new Error('session: tmux is no longer supported; use session: acp with the Codex or Claude Code adapter');
47
- if (raw.session !== undefined && raw.session !== 'acp')
48
- throw new Error(`unsupported session '${String(raw.session)}'; expected session: acp`);
49
- const session = 'acp';
47
+ if (raw.session !== undefined && raw.session !== 'acp'
48
+ && raw.session !== 'codex-app-server')
49
+ throw new Error(`unsupported session '${String(raw.session)}'; expected session: acp or codex-app-server`);
50
+ const session = raw.session ?? 'acp';
50
51
  return {
51
52
  role: {
52
53
  ...raw, name, harness: typeof raw.harness === 'string' ? raw.harness : 'claude-code',
@@ -173,6 +174,8 @@ export class RoleRepository {
173
174
  this.options.probeBackend(name, intended),
174
175
  new Promise(resolve => setTimeout(() => resolve({ acp: false }), timeoutMs)),
175
176
  ]).catch(() => ({ acp: false }));
177
+ if (result.backend)
178
+ return result.backend;
176
179
  if (result.acp)
177
180
  return 'acp';
178
181
  return 'none';
@@ -71,7 +71,7 @@ export interface RoleSessionControl {
71
71
  onClose(reason?: string): void;
72
72
  }): Promise<ConversationFollowHandle>;
73
73
  }
74
- export declare class AcpRoleSessionAdapter implements RoleSessionControl {
74
+ export declare class RoleSessionControlAdapter implements RoleSessionControl {
75
75
  private readonly stateDir;
76
76
  private readonly request;
77
77
  constructor(stateDir: string, request?: typeof controlRequest);
@@ -119,3 +119,5 @@ export declare class AcpRoleSessionAdapter implements RoleSessionControl {
119
119
  }>;
120
120
  private call;
121
121
  }
122
+ /** @deprecated Compatibility name; control is provider-neutral. */
123
+ export { RoleSessionControlAdapter as AcpRoleSessionAdapter };
@@ -1,7 +1,7 @@
1
1
  import { controlRequest, followConversation } from '../session/control.js';
2
2
  import { FleetError, normalizeError } from './errors.js';
3
3
  const visibleEvents = (events) => events.filter(event => event.kind !== 'thought');
4
- export class AcpRoleSessionAdapter {
4
+ export class RoleSessionControlAdapter {
5
5
  stateDir;
6
6
  request;
7
7
  constructor(stateDir, request = controlRequest) {
@@ -12,7 +12,7 @@ export class AcpRoleSessionAdapter {
12
12
  const response = await this.call('snapshot');
13
13
  const result = response;
14
14
  return {
15
- backend: 'acp', protocolVersion: result.protocolVersion ?? 1,
15
+ backend: result.backend, protocolVersion: result.protocolVersion ?? 1,
16
16
  features: result.features ?? [], snapshot: result,
17
17
  };
18
18
  }
@@ -147,3 +147,5 @@ export class AcpRoleSessionAdapter {
147
147
  }
148
148
  }
149
149
  }
150
+ /** @deprecated Compatibility name; control is provider-neutral. */
151
+ export { RoleSessionControlAdapter as AcpRoleSessionAdapter };
@@ -2,7 +2,10 @@ import { type FleetConfig } from '../config.js';
2
2
  import { type CoworkAdapter } from '../rooms-tasks/cowork-adapter.js';
3
3
  import { provisionMembers } from '../rooms-tasks/provision.js';
4
4
  import { moveTaskToList } from '../rooms-tasks/task-state.js';
5
- import type { RoomOrchestrationRecord, TaskListRecord, TaskOrigin, TaskOutcome, TaskRecord, TaskState } from '../rooms-tasks/types.js';
5
+ import { type MemberOverrides } from '../rooms-tasks/member-overrides.js';
6
+ import { type TaskDeletionSettleResult } from '../rooms-tasks/deletion.js';
7
+ import type { RoomLaunchPolicy, RoomOrchestrationRecord, TaskListRecord, TaskOrigin, TaskOutcome, TaskRecord, TaskState, TemplateSnapshot } from '../rooms-tasks/types.js';
8
+ import type { TaskDeletionAcceptance } from '../rooms-tasks/task-state.js';
6
9
  export type TaskRoomActor = {
7
10
  kind: 'local_control';
8
11
  surface: 'cli' | 'web';
@@ -18,8 +21,8 @@ export interface TaskSettlementPlan {
18
21
  task: TaskRecord;
19
22
  settlementRequired: boolean;
20
23
  }
21
- export type TaskRecoveryIssue = {
22
- code: 'terminal_pending' | 'waiting_cowork' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'waiting_seats' | 'provisioning_resumed';
24
+ export type TaskProvisioningContinuationIssue = {
25
+ code: 'waiting_cowork' | 'waiting_owner_authorization' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'waiting_seats' | 'provisioning_resumed';
23
26
  } | {
24
27
  code: 'member_failed';
25
28
  stepIndex: number;
@@ -27,24 +30,36 @@ export type TaskRecoveryIssue = {
27
30
  code: 'resume_failed';
28
31
  error: string;
29
32
  };
30
- export interface TaskRecoveryResult {
31
- kind: 'provisioning_resumed' | 'provisioning_resume_failed' | 'provisioning_non_resumable' | 'terminal' | 'no_op';
33
+ export interface TaskProvisioningContinuationResult {
34
+ kind: 'provisioning_resumed' | 'provisioning_resume_failed' | 'provisioning_non_resumable' | 'no_op';
32
35
  task: TaskRecord;
33
36
  room: RoomOrchestrationRecord | undefined;
34
- issues: TaskRecoveryIssue[];
37
+ issues: TaskProvisioningContinuationIssue[];
35
38
  reason?: 'missing_room' | 'missing_durable_template' | 'non_resumable_phase';
36
39
  }
37
- export type TaskRecoveryBegin = {
38
- kind: 'terminal_worker_required';
39
- taskId: string;
40
- } | {
41
- kind: 'final';
42
- result: TaskRecoveryResult;
43
- };
40
+ export interface TaskProvisioningOutcome {
41
+ kind: 'ready' | 'in_progress' | 'failed';
42
+ task: TaskRecord;
43
+ room?: RoomOrchestrationRecord;
44
+ launch: {
45
+ template?: string;
46
+ anonymous: boolean;
47
+ owner_attached: boolean;
48
+ };
49
+ members: {
50
+ expected: number;
51
+ active: number;
52
+ launched: number;
53
+ };
54
+ blocker?: string;
55
+ next_action?: string;
56
+ }
57
+ /** Repairable terminal observation derived entirely from durable Task/Room state. */
58
+ export declare function recordTaskProvisioningOutcome(outcome: TaskProvisioningOutcome): void;
44
59
  export declare class TaskRoomApplicationError extends Error {
45
- readonly code: 'template_not_found' | 'task_template_drift' | 'template_mismatch' | 'task_terminal' | 'task_terminal_already' | 'task_non_resumable' | 'room_not_found' | 'room_record_not_found';
60
+ readonly code: 'template_not_found' | 'task_template_drift' | 'template_mismatch' | 'task_terminal' | 'task_terminal_already' | 'task_non_resumable' | 'task_deleting' | 'room_not_found' | 'room_record_not_found';
46
61
  readonly fields: Readonly<Record<string, string>>;
47
- constructor(code: 'template_not_found' | 'task_template_drift' | 'template_mismatch' | 'task_terminal' | 'task_terminal_already' | 'task_non_resumable' | 'room_not_found' | 'room_record_not_found', message: string, fields?: Readonly<Record<string, string>>);
62
+ constructor(code: 'template_not_found' | 'task_template_drift' | 'template_mismatch' | 'task_terminal' | 'task_terminal_already' | 'task_non_resumable' | 'task_deleting' | 'room_not_found' | 'room_record_not_found', message: string, fields?: Readonly<Record<string, string>>);
48
63
  }
49
64
  export interface CreateTaskRequest {
50
65
  actor: TaskRoomActor;
@@ -57,6 +72,8 @@ export interface CreateTaskRequest {
57
72
  idempotencyKey?: string;
58
73
  origin: TaskOrigin;
59
74
  list?: string;
75
+ members?: MemberOverrides;
76
+ anonymous?: boolean;
60
77
  }
61
78
  export interface CreateRoomRequest {
62
79
  actor: TaskRoomActor;
@@ -65,34 +82,44 @@ export interface CreateRoomRequest {
65
82
  goal?: string;
66
83
  brief?: string;
67
84
  briefFile?: string;
85
+ members?: MemberOverrides;
86
+ anonymous?: boolean;
68
87
  }
88
+ export declare function resolveRoomLaunchPolicy(template: TemplateSnapshot | undefined, override: boolean | undefined): RoomLaunchPolicy;
69
89
  export interface TaskRoomServiceDeps {
70
90
  loadConfiguration?(path?: string): FleetConfig;
71
91
  cowork?(config: FleetConfig): CoworkAdapter;
72
92
  binPath?(): string;
73
93
  provisionMembers?: typeof provisionMembers;
74
94
  moveTaskToList?: typeof moveTaskToList;
95
+ launchDeletionWorker?(taskId: string): Promise<void>;
96
+ sleep?(ms: number): Promise<void>;
97
+ now?(): number;
75
98
  }
76
99
  /** Exact extraction of the previously CLI-owned task create/start behavior. */
77
100
  export declare class TaskRoomApplicationService {
78
101
  private readonly configurationPath?;
79
102
  private readonly deps;
80
- private recovery?;
81
103
  constructor(configurationPath?: string | undefined, deps?: TaskRoomServiceDeps);
82
104
  createTask(request: CreateTaskRequest): Promise<TaskRecord>;
83
105
  createRoom(request: CreateRoomRequest): Promise<RoomOrchestrationRecord>;
84
106
  startTask(input: {
85
107
  actor: TaskRoomActor;
86
108
  taskId: string;
109
+ template?: string;
110
+ members?: MemberOverrides;
111
+ anonymous?: boolean;
87
112
  }): Promise<TaskRecord>;
88
113
  listTasks(filter?: {
89
114
  state?: TaskState | TaskState[];
90
115
  list?: string;
116
+ includeDeleting?: boolean;
91
117
  }): TaskRecord[];
92
118
  listTaskLists(): TaskListRecord[];
93
119
  groupedTasks(filter?: {
94
120
  state?: TaskState | TaskState[];
95
121
  list?: string;
122
+ includeDeleting?: boolean;
96
123
  }): Array<{
97
124
  list: TaskListRecord;
98
125
  tasks: TaskRecord[];
@@ -124,6 +151,12 @@ export declare class TaskRoomApplicationService {
124
151
  task: TaskRecord;
125
152
  orchestration: RoomOrchestrationRecord | undefined;
126
153
  };
154
+ taskProvisioningOutcome(taskId: string): TaskProvisioningOutcome;
155
+ awaitTaskProvisioning(input: {
156
+ actor: TaskRoomActor;
157
+ taskId: string;
158
+ waitMs?: number;
159
+ }): Promise<TaskProvisioningOutcome>;
127
160
  blockTask(input: {
128
161
  actor: TaskRoomActor;
129
162
  taskId: string;
@@ -137,10 +170,40 @@ export declare class TaskRoomApplicationService {
137
170
  actor: TaskRoomActor;
138
171
  taskId: string;
139
172
  }): TaskRecord;
140
- deleteTask(input: {
173
+ private deletionActor;
174
+ /** Accept a permanent deletion in any lifecycle state; audit at acceptance. */
175
+ requestTaskDeletion(input: {
141
176
  actor: TaskRoomActor;
142
177
  taskId: string;
143
- }): boolean;
178
+ }): Promise<TaskDeletionAcceptance>;
179
+ /** Worker entry: converge an accepted deletion; Cowork is resolved lazily. */
180
+ settleTaskDeletion(input: {
181
+ actor: {
182
+ kind: 'internal_worker';
183
+ surface: 'cli';
184
+ };
185
+ taskId: string;
186
+ }): Promise<TaskDeletionSettleResult>;
187
+ /**
188
+ * Launch the external deletion worker outside the caller's lifecycle and
189
+ * wait boundedly for physical absence. Timeouts and launch failures report
190
+ * a pending, recoverable state — never success. A concurrent worker that
191
+ * already removed the record reads as settled.
192
+ */
193
+ launchTaskDeletionWorker(input: {
194
+ taskId: string;
195
+ waitMs?: number;
196
+ }): Promise<{
197
+ deleted: boolean;
198
+ pending: boolean;
199
+ error?: string;
200
+ }>;
201
+ recordDeletionError(input: {
202
+ actor: TaskRoomActor;
203
+ taskId: string;
204
+ error: string;
205
+ recoveryHint: string;
206
+ }): Promise<TaskRecord>;
144
207
  completeTask(input: {
145
208
  actor: TaskRoomActor;
146
209
  taskId: string;
@@ -163,15 +226,14 @@ export declare class TaskRoomApplicationService {
163
226
  error: string;
164
227
  recoveryHint: string;
165
228
  }): Promise<TaskRecord>;
166
- beginTaskRecovery(input: {
167
- actor: TaskRoomActor;
168
- taskId: string;
169
- }): Promise<TaskRecoveryBegin>;
170
- continueTaskRecovery(input: {
171
- actor: TaskRoomActor;
229
+ continueTaskProvisioning(input: {
230
+ actor: {
231
+ kind: 'internal_worker';
232
+ surface: 'cli';
233
+ };
172
234
  taskId: string;
173
- terminalTimedOut: boolean;
174
- }): Promise<TaskRecoveryResult>;
235
+ }): Promise<TaskProvisioningContinuationResult>;
236
+ private reconcileProvisioningOwner;
175
237
  private acceptTerminal;
176
238
  listTemplates(): import("../rooms-tasks/types.js").TemplateDefinition[];
177
239
  getTemplate(name: string): {
@@ -179,7 +241,7 @@ export declare class TaskRoomApplicationService {
179
241
  name: string;
180
242
  version: number;
181
243
  description: string;
182
- builtin?: boolean;
244
+ sourceFile?: string;
183
245
  room?: import("../rooms-tasks/types.js").TemplateRoomConfig;
184
246
  contract?: string;
185
247
  members: import("../rooms-tasks/types.js").TemplateMemberSlot[];
@@ -197,6 +259,7 @@ export declare class TaskRoomApplicationService {
197
259
  identity_cid: string;
198
260
  room_name: string;
199
261
  state: "provisioning" | "active" | "closing" | "closed";
262
+ anonymous: boolean;
200
263
  seats: import("../rooms-tasks/cowork-adapter.js").CoworkSeatInfo[];
201
264
  goal?: string;
202
265
  briefing?: string;
@@ -231,18 +294,6 @@ export declare class TaskRoomApplicationService {
231
294
  error: string;
232
295
  recoveryHint: string;
233
296
  }): Promise<RoomOrchestrationRecord>;
234
- recoverRoom(input: {
235
- actor: TaskRoomActor;
236
- roomId: string;
237
- }): Promise<{
238
- kind: 'deletion_worker_required';
239
- roomId: string;
240
- } | {
241
- kind: 'recovered' | 'provisioning_resumed' | 'provisioning_resume_failed';
242
- room: Awaited<ReturnType<CoworkAdapter['recoverRoom']>>;
243
- orchestration: RoomOrchestrationRecord | undefined;
244
- issues: string[];
245
- }>;
246
297
  finishTask(input: {
247
298
  actor: TaskRoomActor;
248
299
  taskId: string;
@@ -252,9 +303,11 @@ export declare class TaskRoomApplicationService {
252
303
  actor: TaskRoomActor;
253
304
  taskId: string;
254
305
  template?: string;
306
+ members?: MemberOverrides;
307
+ anonymous?: boolean;
255
308
  }): Promise<{
256
309
  task: TaskRecord;
257
- status: 'ready' | 'already_active';
310
+ status: 'ready' | 'already_active' | 'in_progress';
258
311
  }>;
259
312
  private createTemplate;
260
313
  private existingTemplate;