@ours.network/fleet 1.0.5 → 1.1.0-nightly.1

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 (75) hide show
  1. package/README.md +28 -41
  2. package/dist/application/capabilities.d.ts +0 -1
  3. package/dist/application/capabilities.js +3 -13
  4. package/dist/application/fleet-query-service.d.ts +0 -3
  5. package/dist/application/fleet-query-service.js +1 -24
  6. package/dist/application/role-creation-service.d.ts +5 -5
  7. package/dist/application/role-creation-service.js +5 -7
  8. package/dist/application/role-repository.d.ts +1 -2
  9. package/dist/application/role-repository.js +7 -7
  10. package/dist/application/session-control.d.ts +0 -12
  11. package/dist/application/session-control.js +0 -42
  12. package/dist/application/session-mutations.d.ts +5 -5
  13. package/dist/application/types.d.ts +0 -7
  14. package/dist/briefing.js +1 -4
  15. package/dist/build-info.json +5 -5
  16. package/dist/cli.js +9 -23
  17. package/dist/config.d.ts +1 -4
  18. package/dist/config.js +13 -17
  19. package/dist/docs.d.ts +1 -1
  20. package/dist/docs.js +20 -26
  21. package/dist/doctor.js +0 -7
  22. package/dist/fleet-proxy.d.ts +1 -1
  23. package/dist/harness/acp-session-transport.d.ts +8 -0
  24. package/dist/harness/acp-session-transport.js +3 -0
  25. package/dist/harness/agent-session.d.ts +33 -0
  26. package/dist/harness/agent-session.js +1 -0
  27. package/dist/harness/claude-code-session.d.ts +18 -0
  28. package/dist/harness/claude-code-session.js +28 -0
  29. package/dist/harness/claude-code.d.ts +2 -1
  30. package/dist/harness/claude-code.js +29 -81
  31. package/dist/harness/codex-session.d.ts +18 -0
  32. package/dist/harness/codex-session.js +28 -0
  33. package/dist/harness/codex.d.ts +2 -1
  34. package/dist/harness/codex.js +35 -45
  35. package/dist/harness/types.d.ts +6 -38
  36. package/dist/index.d.ts +3 -5
  37. package/dist/index.js +1 -3
  38. package/dist/isolation/resources.d.ts +3 -7
  39. package/dist/isolation/resources.js +3 -7
  40. package/dist/model-env.d.ts +2 -3
  41. package/dist/model-env.js +2 -3
  42. package/dist/monitor.d.ts +1 -24
  43. package/dist/monitor.js +5 -140
  44. package/dist/owner-channel/channel.d.ts +2 -2
  45. package/dist/runner.d.ts +5 -15
  46. package/dist/runner.js +26 -102
  47. package/dist/session/acp.d.ts +2 -2
  48. package/dist/session/acp.js +2 -3
  49. package/dist/session/activity.d.ts +2 -3
  50. package/dist/session/activity.js +2 -3
  51. package/dist/session/arbiter.d.ts +4 -4
  52. package/dist/session/control.d.ts +3 -3
  53. package/dist/session/types.d.ts +11 -5
  54. package/dist/spawn.js +8 -9
  55. package/dist/supervisor/none.d.ts +1 -1
  56. package/dist/supervisor/none.js +2 -2
  57. package/dist/tmux.d.ts +1 -14
  58. package/dist/tmux.js +1 -51
  59. package/dist/watchdog/config.js +6 -4
  60. package/dist/watchdog/run.js +8 -23
  61. package/dist/web/auth.d.ts +1 -1
  62. package/dist/web/runtime.js +4 -15
  63. package/dist/web/server.d.ts +1 -3
  64. package/dist/web/server.js +3 -14
  65. package/dist/web/topology-promote.js +2 -5
  66. package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
  67. package/dist/web-app/assets/{index-BCBK78hw.js → index-DhMDVRU1.js} +6 -6
  68. package/dist/web-app/index.html +2 -2
  69. package/package.json +3 -8
  70. package/dist/session/tmux.d.ts +0 -28
  71. package/dist/session/tmux.js +0 -80
  72. package/dist/web/terminal/bridge.d.ts +0 -27
  73. package/dist/web/terminal/bridge.js +0 -317
  74. package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
  75. package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
package/README.md CHANGED
@@ -8,8 +8,8 @@ harnesses — from one declarative file.**
8
8
  An AI coding agent in a terminal dies when you close the laptop. `ours-fleet`
9
9
  turns such sessions into **roles**: long-lived agents that
10
10
 
11
- - **run through a selectable session backend** existing detached tmux consoles
12
- or structured ACP sessions — which you can attach to, peek at, or prompt,
11
+ - **run through structured ACP sessions** behind a shared Codex/Claude Code
12
+ agent-session interface, which you can inspect or prompt,
13
13
  - are **supervised** — systemd (Linux) or launchd (macOS) restarts them on crash
14
14
  and brings them back after a reboot,
15
15
  - **resume their context** across restarts (when the harness supports it),
@@ -44,8 +44,7 @@ roles:
44
44
  ~/fleet.yaml + ~/fleet.d/*.yaml your declaration
45
45
  │ ours-fleet up
46
46
 
47
- briefing.md per role ──► tmux session ──► harness CLI (claude …)
48
- └─► ACP client ──► ACP agent (codex-acp …)
47
+ briefing.md per role ──► agent session adapter ──► ACP client/session ──► ACP agent
49
48
  ▲ │
50
49
  systemd --user / launchd ───────┘ restart on crash, start at boot/login
51
50
  ```
@@ -78,7 +77,6 @@ The state dir contract:
78
77
  | What | Why | Install |
79
78
  |---|---|---|
80
79
  | Node ≥ 20 | runs `ours-fleet` itself | nodejs.org, `apt`, or `brew` |
81
- | tmux | roles using `session: tmux` (the default) | `apt install tmux` / `brew install tmux` |
82
80
  | Node ≥ 22 | Claude roles using `session: acp` | required by the maintained Claude ACP adapter |
83
81
  | a harness CLI, logged in | the agent itself | e.g. Claude Code (`claude`) or Codex CLI (`codex`) |
84
82
  | `ours` CLI + shared daemon | identity + agent-to-agent messaging | `npm i -g @ours.network/cli && ours daemon start` |
@@ -98,8 +96,8 @@ ours-fleet doctor # verifies everything above, with actionable messages
98
96
  The maintained Codex and Claude ACP adapters install as optional dependencies of
99
97
  `ours-fleet` and are resolved internally; users do not install adapter commands
100
98
  or add them to `PATH`. An explicit `session_options.acp.command` remains
101
- available for custom adapters. On Node 20–21, tmux and Codex ACP remain
102
- available, while maintained Claude ACP requires upgrading to Node 22.
99
+ available for custom adapters. On Node 20–21, Codex ACP remains available,
100
+ while maintained Claude ACP requires upgrading to Node 22.
103
101
 
104
102
  Each OS user manages their own fleet — to host roles under a sandboxed account,
105
103
  become that account and repeat.
@@ -139,8 +137,8 @@ fleet monitor policy, and same-harness model inherit from the caller. Explicit
139
137
  flags win; changing harness without a model lets the selected harness/fleet
140
138
  defaults choose one. After creation succeeds, fleet can deterministically notify
141
139
  the caller's owner channel with the caller and spawned-role details. This is an
142
- honest-actor convenience and attribution path, not a security boundary; tmux,
143
- host shells, and deliberately bypassed absolute binaries retain direct behavior.
140
+ honest-actor convenience and attribution path, not a security boundary; host
141
+ shells and deliberately bypassed absolute binaries retain direct behavior.
144
142
 
145
143
  ## Local web console
146
144
 
@@ -209,8 +207,8 @@ offline shell and no stale fleet state.
209
207
 
210
208
  The console provides evidence-separated inventory and status, ACP activity and
211
209
  permission controls, redacted logs, typed text send, confirmed lifecycle
212
- actions, transactional permanent/temporary creation, and a shared tmux browser
213
- terminal. Identity is fixed to the role name. Creation uses the authenticated
210
+ actions, and transactional permanent/temporary creation. Identity is fixed to
211
+ the role name. Creation uses the authenticated
214
212
  daemon inventory and reports verified, missing, or unknown evidence. A missing
215
213
  permanent role identity is created deterministically before launch with local
216
214
  discovery and auto-accept enabled, then the provisioning lease is released so
@@ -243,19 +241,16 @@ never force-adopts or deletes identity state. Permanent roles are
243
241
  provisioned by fleet before launch and never delegate normal identity creation
244
242
  to the harness.
245
243
 
246
- `node-pty` is optional: if its native module cannot load, ACP and all
247
- non-terminal features remain available and tmux Terminal is disabled with a
248
- diagnostic.
244
+ The web console uses structured ACP activity rather than exposing a raw agent terminal.
249
245
 
250
246
  Security boundaries:
251
247
 
252
248
  - configured `Host` and `Origin`, CSRF, one-time WebSocket tickets, and explicit
253
249
  bind/origin policy are enforced server-side;
254
250
  - cwd values must resolve beneath configured local roots;
255
- - terminal bytes are intentionally unredacted and are never copied into audit
256
- records; normal logs are bounded and redacted;
257
- - tests use temporary fleet homes, fake supervisors/identity providers, and
258
- isolated tmux sockets—never active role state.
251
+ - normal logs are bounded and redacted;
252
+ - tests use temporary fleet homes and fake supervisors/identity providers—never
253
+ active role state.
259
254
 
260
255
  Permanent spawns are written to `~/fleet.d/<Name>.yaml`; the CLI never edits your
261
256
  hand-written `~/fleet.yaml`. `ours-fleet rm <Name>` unspawns.
@@ -292,7 +287,6 @@ them and unsticks them:
292
287
 
293
288
  ```sh
294
289
  ours-fleet peek Worker # what is it doing?
295
- ours-fleet send Worker --key 1 # answer the menu it's stuck on
296
290
  ours-fleet send Worker "continue with the tests, then report"
297
291
  ```
298
292
 
@@ -315,7 +309,7 @@ ours-fleet docs | man AI-friendly complete reference
315
309
  ours-fleet up|down|restart|force-restart [-c FILE] [Name...]
316
310
  ours-fleet config [-c FILE] validate + print merged plan
317
311
  ours-fleet ls | attach | peek | logs [-f] | status <Name>
318
- ours-fleet send <Name> "text" | --key <K>
312
+ ours-fleet send <Name> "text"
319
313
  ours-fleet spawn [--temp] [<Name> | --role <Name>] [--harness --session --mission --model --approval ...]
320
314
  ours-fleet loops validate|list|status
321
315
  ours-fleet loops reload <Role>
@@ -355,7 +349,7 @@ vars: { work_root: /home/me/work } # ${var} substitution anywhere below
355
349
  start_stagger_ms: 0 # delay between agent LAUNCHES (host-wide, ms); 0 = no stagger
356
350
  defaults:
357
351
  harness: claude-code # for roles that don't set one
358
- session: tmux # tmux (default) | acp
352
+ session: acp # supported Fleet↔agent session path
359
353
  permissions: # common intent, translated by each harness/backend
360
354
  approval: ask # ask | auto | allow (`deny` is a deprecated alias)
361
355
  filesystem: workspace # read-only | workspace | unrestricted
@@ -371,7 +365,7 @@ defaults:
371
365
  roles:
372
366
  Name: # [A-Za-z0-9_-]+
373
367
  harness: claude-code
374
- session: acp # one flag selects ACP; omit for tmux
368
+ session: acp # optional; ACP is the only supported session
375
369
  session_options:
376
370
  acp:
377
371
  command: claude-agent-acp # optional advanced override
@@ -446,15 +440,14 @@ loops: # trusted local scheduled ACP turns
446
440
 
447
441
  Merge order: `fleet.yaml` ← `fleet.d/*.yaml`; a duplicate role name is a hard
448
442
  error naming both files. Identities and roles are decoupled — removing a role
449
- never deletes an identity. `session` is independent of `harness`, so changing a
450
- role from tmux to ACP does not change its identity, mission, monitor, or permission
451
- contract. `defaults.harness_options` is shallow-merged with each
443
+ never deletes an identity. `session` is ACP-backed for both supported harnesses.
444
+ `defaults.harness_options` is shallow-merged with each
452
445
  role's `harness_options`, so a fleet can set common Codex permission/profile defaults
453
446
  and override individual keys per role. `monitor` merges the same way — a role block
454
447
  overrides `defaults.monitor` key-by-key.
455
448
 
456
449
  Every supervised role is a client of the operator-configured ours daemon. Fleet strips the
457
- obsolete, presence-sensitive `OURS_AUTOSTART` variable from both tmux and ACP harness
450
+ obsolete, presence-sensitive `OURS_AUTOSTART` variable from ACP harness
458
451
  processes. `ours-mcp proxy` is client-only and never starts a daemon; operators and explicit
459
452
  installer/setup flows remain responsible for starting it.
460
453
 
@@ -597,9 +590,7 @@ since a human can still attach a console and answer.
597
590
  `auto` selects Codex ACP `agent` (`on-request` + `workspace-write`) / Claude
598
591
  `acceptEdits`. `allow` selects Codex ACP's fully non-interactive yolo mode,
599
592
  reported by the adapter as `agent-full-access` (`never` +
600
- `danger-full-access`); Claude uses `bypassPermissions`. For Codex tmux, where the
601
- approval and sandbox flags remain independent, `auto` is `on-request` and
602
- `allow` is `never` while `filesystem` still chooses the sandbox.
593
+ `danger-full-access`); Claude uses `bypassPermissions`.
603
594
  `dontAsk` suppresses only the *prompt*, not the denial, which is why an
604
595
  `allow` role previously ran unable to do its job. Legacy `deny` is accepted
605
596
  only for compatibility and retains its conservative Codex `on-request` /
@@ -689,8 +680,8 @@ their boots ~4 s apart instead of firing all seven at once.
689
680
  With `monitor.mode: fleet` (the default), the **ours-fleet supervisor** delivers
690
681
  a role's mail wakes: the per-role runner long-polls the ours daemon's notification API and
691
682
  submits a single `[fleet-monitor] N new messages from … — run get_messages` prompt
692
- through the selected backend. ACP uses live steering when its adapter supports it
693
- and falls back to structured `session/prompt`; tmux uses verified console input.
683
+ through the shared agent session. ACP uses live steering when its adapter supports it
684
+ and falls back to structured `session/prompt`.
694
685
  Set `monitor.interrupt: true` on roles where every configured wake should cancel
695
686
  the active turn before the notification is delivered. This is intentionally
696
687
  content-blind: the supervisor cannot inspect encrypted message bodies, so all
@@ -700,8 +691,7 @@ tool result or pending permission. Fleet waits for terminal ACP tool/update
700
691
  evidence, then steers the wake without calling `session.cancel`. If the tool is
701
692
  still active after 120 seconds, or the adapter cannot expose authenticated tool
702
693
  boundaries/steering, fleet visibly degrades to non-cancelling steering or queued
703
- delivery. Tmux never receives `C-c` for `after_tool`. Explicit human and control
704
- interrupts remain immediate.
694
+ delivery. Explicit human and control interrupts remain immediate.
705
695
  The default is `false`: a role that must begin a post-readiness mission
706
696
  immediately, including second-and-later mail received while it is working, must
707
697
  set `monitor.mode: fleet` and `monitor.interrupt: true` explicitly. Readiness and
@@ -794,8 +784,8 @@ invites, message bodies, credentials, or keys. A corrupt overlay fails closed
794
784
  (no effective owners and no mutation), and the last effective owner cannot be
795
785
  revoked.
796
786
 
797
- These commands require a running ACP role with `owner_channel` enabled. Missing,
798
- stopped, tmux, disabled, draining, and unavailable-MCP targets fail without
787
+ These commands require a running role with `owner_channel` enabled. Missing,
788
+ stopped, disabled, draining, and unavailable-MCP targets fail without
799
789
  starting a second client, binding an identity, or opening a network listener.
800
790
 
801
791
  The managed agent can use its ordinary ours `send_message` or `send_file` tool to
@@ -1015,11 +1005,8 @@ gets a durable pre-send marker: a failed/ambiguous send becomes `uncertain` and
1015
1005
  not blindly retried, while files not yet attempted remain recoverable after restart.
1016
1006
  Logs contain counts and byte totals, never filenames or raw bytes.
1017
1007
 
1018
- Owner channels currently require `session: acp`. Fleet needs structured,
1019
- turn-correlated assistant output for automatic replies; scraping a tmux pane
1020
- cannot reliably distinguish the final answer from thoughts, tool output, or
1021
- unrelated concurrent work. The config rejects tmux instead of silently offering
1022
- weaker semantics.
1008
+ Owner channels use the structured agent-session interface for turn-correlated
1009
+ assistant output and reliable automatic replies.
1023
1010
 
1024
1011
  ## Codex roles
1025
1012
 
@@ -1083,7 +1070,7 @@ role-specific ours daemon, or configure the host default in `~/.ours/config.json
1083
1070
 
1084
1071
  Each role can be sandboxed at the environment level via an `isolation:` block —
1085
1072
  **fully additive: a role with no block behaves exactly as before.** The agent's
1086
- tmux-pane process is wrapped in [bubblewrap](https://github.com/containers/bubblewrap)
1073
+ agent process is wrapped in [bubblewrap](https://github.com/containers/bubblewrap)
1087
1074
  (rootless, no setuid), resource-limited by `systemd-run --user --scope`.
1088
1075
 
1089
1076
  An empty `isolation: {}` gives a sensible default posture: filesystem-confined to
@@ -1,6 +1,5 @@
1
1
  import type { RoleRecord, RoleCapabilities, RoleStatus } from './types.js';
2
2
  export interface CapabilityContext {
3
- terminalPtyAvailable?: boolean;
4
3
  controlProtocolVersion?: number;
5
4
  }
6
5
  export declare function roleCapabilities(role: RoleRecord, status: RoleStatus, context?: CapabilityContext): RoleCapabilities;
@@ -1,30 +1,20 @@
1
1
  export function roleCapabilities(role, status, context = {}) {
2
- const backend = role.configuredBackend ?? (role.detectedBackend === 'acp' || role.detectedBackend === 'tmux' ? role.detectedBackend : undefined);
2
+ const backend = role.configuredBackend ?? (role.detectedBackend === 'acp' ? 'acp' : undefined);
3
3
  const online = status.session.reachability === 'online';
4
4
  const acp = backend === 'acp';
5
- const tmux = backend === 'tmux';
6
5
  const protocolVersion = context.controlProtocolVersion ?? 1;
7
6
  const inactive = status.overall === 'offline'
8
7
  || (!online && status.supervisor.liveness === 'stopped');
9
- const terminalAvailable = tmux && online && context.terminalPtyAvailable === true;
10
- const terminalReason = terminalAvailable ? undefined
11
- : !tmux ? 'wrong_backend'
12
- : !online ? 'offline'
13
- : 'pty_unavailable';
14
8
  return {
15
9
  protocolVersion,
16
10
  inventory: true,
17
11
  status: true,
18
12
  output: {
19
- recent: acp || tmux, stream: (acp && protocolVersion >= 2) || terminalAvailable,
13
+ recent: acp, stream: acp && protocolVersion >= 2,
20
14
  structured: acp, replayCursor: acp && protocolVersion >= 2,
21
15
  },
22
- input: { text: online && (acp || tmux), rawKeys: terminalAvailable, interrupt: online && acp, steering: false },
16
+ input: { text: online && acp, interrupt: online && acp, steering: false },
23
17
  permissions: { observe: acp, respond: acp && online },
24
- terminal: {
25
- available: terminalAvailable, reason: terminalReason,
26
- multiViewer: terminalAvailable, writerLease: terminalAvailable,
27
- },
28
18
  lifecycle: {
29
19
  start: role.lifetime === 'permanent' && status.supervisor.liveness !== 'running',
30
20
  stop: role.lifetime === 'permanent' && !inactive && status.supervisor.liveness !== 'stopped',
@@ -1,13 +1,11 @@
1
1
  import type { SupervisorBackend } from '../supervisor/types.js';
2
2
  import { controlRequest } from '../session/control.js';
3
- import { Tmux } from '../tmux.js';
4
3
  import { type CapabilityContext } from './capabilities.js';
5
4
  import { RoleRepository } from './role-repository.js';
6
5
  import type { RoleCapabilities, RoleRecord, RoleStatus } from './types.js';
7
6
  export interface FleetQueryOptions {
8
7
  repository: RoleRepository;
9
8
  supervisor: SupervisorBackend;
10
- tmux?: Tmux;
11
9
  control?: typeof controlRequest;
12
10
  capabilityContext?: CapabilityContext;
13
11
  /**
@@ -24,7 +22,6 @@ export interface FleetQueryOptions {
24
22
  }
25
23
  export declare class FleetQueryService {
26
24
  private readonly options;
27
- private readonly tmux;
28
25
  private readonly control;
29
26
  constructor(options: FleetQueryOptions);
30
27
  list(): Promise<Array<{
@@ -4,7 +4,6 @@ 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 { Tmux } from '../tmux.js';
8
7
  import { roleCapabilities } from './capabilities.js';
9
8
  import { FleetError } from './errors.js';
10
9
  const clean = (value, max = 512) => value.replace(/[\0-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '').trim().slice(0, max);
@@ -50,7 +49,7 @@ function sessionOverall(supervisor, session, restart, monitor, isolation, proble
50
49
  || problems.some(problem => problem.severity === 'error' || problem.source === 'watchdog')
51
50
  || session.readiness === 'failed')
52
51
  return 'attention';
53
- // A reachable ACP/tmux session is the user's live interaction surface. Its
52
+ // A reachable agent session is the user's live interaction surface. Its
54
53
  // evidence remains authoritative even when a service manager no longer owns
55
54
  // the process (for example, a detached ACP session with an inactive unit).
56
55
  if (session.reachability === 'online'
@@ -69,11 +68,9 @@ function sessionOverall(supervisor, session, restart, monitor, isolation, proble
69
68
  }
70
69
  export class FleetQueryService {
71
70
  options;
72
- tmux;
73
71
  control;
74
72
  constructor(options) {
75
73
  this.options = options;
76
- this.tmux = options.tmux ?? new Tmux();
77
74
  this.control = options.control ?? controlRequest;
78
75
  }
79
76
  async list() {
@@ -174,26 +171,6 @@ export class FleetQueryService {
174
171
  };
175
172
  }
176
173
  }
177
- if (intended === 'tmux' || role.detectedBackend === 'tmux') {
178
- try {
179
- const has = await this.tmux.has(role.id);
180
- return {
181
- backend: 'tmux', reachability: has ? 'online' : supervisor === 'stopped' ? 'offline' : 'unknown',
182
- readiness: has ? 'idle' : supervisor === 'running' ? 'starting' : 'failed',
183
- evidence: 'inferred',
184
- // tmux exposes no agent-side evidence at all, and `readiness: idle`
185
- // here is a pane-liveness inference, not an activity claim.
186
- activity: { state: 'unobservable' },
187
- };
188
- }
189
- catch (error) {
190
- return {
191
- backend: 'tmux', reachability: 'unknown', readiness: 'unknown',
192
- evidence: 'inferred', lastError: clean(error.message),
193
- activity: { state: 'unobservable' },
194
- };
195
- }
196
- }
197
174
  return {
198
175
  backend: 'unknown', reachability: supervisor === 'stopped' ? 'offline' : 'unknown',
199
176
  readiness: supervisor === 'stopped' ? 'failed' : 'unknown', evidence: 'inferred',
@@ -12,7 +12,7 @@ export interface CreateRoleSessionRequest {
12
12
  /** null means the selected harness's own default; web blank fields send null. */
13
13
  model?: string | null;
14
14
  reasoningEffort?: string | null;
15
- session: 'acp' | 'tmux';
15
+ session: 'acp';
16
16
  cwd?: string;
17
17
  lifetime: 'permanent' | 'temporary';
18
18
  mission?: string;
@@ -41,7 +41,7 @@ export interface CreationCapabilities {
41
41
  harnesses: Array<{
42
42
  id: 'codex' | 'claude-code';
43
43
  available: boolean;
44
- sessions: Array<'acp' | 'tmux'>;
44
+ sessions: Array<'acp'>;
45
45
  defaultModel?: string;
46
46
  models: string[];
47
47
  modelOptions: HarnessModelOption[];
@@ -71,7 +71,7 @@ export interface CreationPreview {
71
71
  name: string;
72
72
  identity: string;
73
73
  harness: string;
74
- session: 'acp' | 'tmux';
74
+ session: 'acp';
75
75
  model?: string;
76
76
  reasoningEffort?: string;
77
77
  cwd?: string;
@@ -95,7 +95,7 @@ export interface CreationAction {
95
95
  actionId: string;
96
96
  requestHash: string;
97
97
  roleId: string;
98
- session: 'acp' | 'tmux';
98
+ session: 'acp';
99
99
  lifetime: 'permanent' | 'temporary';
100
100
  state: CreationStage;
101
101
  stages: Array<{
@@ -121,7 +121,7 @@ export interface RoleCreationServiceOptions {
121
121
  tempLauncher?: SupervisorLauncher;
122
122
  allowedCwdRoots?: string[];
123
123
  journalDir?: string;
124
- probeReady?: (name: string, session: 'acp' | 'tmux') => Promise<'ready' | 'attention' | 'unknown'>;
124
+ probeReady?: (name: string, session: 'acp') => Promise<'ready' | 'attention' | 'unknown'>;
125
125
  onProgress?: (action: CreationAction) => void;
126
126
  modelCatalogs?: Partial<Record<'codex' | 'claude-code', () => HarnessModelCatalog>>;
127
127
  /** Direct/managed callers must not create or restore the web action journal. */
@@ -109,13 +109,13 @@ export class RoleCreationService {
109
109
  reasons,
110
110
  harnesses: [
111
111
  {
112
- id: 'codex', available: true, sessions: ['acp', 'tmux'],
112
+ id: 'codex', available: true, sessions: ['acp'],
113
113
  defaultModel: resolveRoleModel(undefined, 'codex', defaults),
114
114
  ...codexModels, catalogSource: 'codex-runtime-catalog', customModelAllowed: true,
115
115
  warnings: codexCatalog.warnings,
116
116
  },
117
117
  {
118
- id: 'claude-code', available: true, sessions: ['acp', 'tmux'],
118
+ id: 'claude-code', available: true, sessions: ['acp'],
119
119
  defaultModel: resolveRoleModel(undefined, 'claude-code', defaults),
120
120
  ...claudeModels, catalogSource: 'claude-adapter-2.1', customModelAllowed: true,
121
121
  warnings: claudeCatalog.warnings,
@@ -149,7 +149,7 @@ export class RoleCreationService {
149
149
  const effective = {
150
150
  name: request.name, identity: request.name,
151
151
  harness: request.harness ?? defaults.harness ?? 'claude-code',
152
- session: request.session ?? defaults.session ?? 'tmux',
152
+ session: request.session ?? defaults.session ?? 'acp',
153
153
  model: resolveRoleModel(request.model, request.harness, defaults),
154
154
  reasoningEffort: request.reasoningEffort ?? undefined,
155
155
  cwd, lifetime: request.lifetime,
@@ -264,9 +264,7 @@ export class RoleCreationService {
264
264
  this.stage(action, 'waiting_for_session');
265
265
  const ready = await this.waitForReady(action.roleId, action.session);
266
266
  if (ready === 'ready') {
267
- action.openPath = action.session === 'acp'
268
- ? `/roles/${encodeURIComponent(action.roleId)}/activity`
269
- : `/roles/${encodeURIComponent(action.roleId)}/terminal`;
267
+ action.openPath = `/roles/${encodeURIComponent(action.roleId)}/activity`;
270
268
  this.stage(action, 'session_reachable', 'session is reachable; identity binding has no structured evidence in this version');
271
269
  }
272
270
  else if (ready === 'attention') {
@@ -311,7 +309,7 @@ export class RoleCreationService {
311
309
  throw new FleetError('invalid_request', 'invalid role name');
312
310
  if (!['codex', 'claude-code'].includes(input.harness))
313
311
  throw new FleetError('invalid_request', 'unsupported harness');
314
- if (!['acp', 'tmux'].includes(input.session))
312
+ if (input.session !== 'acp')
315
313
  throw new FleetError('invalid_request', 'unsupported session backend');
316
314
  if (!['permanent', 'temporary'].includes(input.lifetime))
317
315
  throw new FleetError('invalid_request', 'unsupported lifetime');
@@ -3,9 +3,8 @@ export interface RoleRepositoryOptions {
3
3
  configPath?: string;
4
4
  permanentRoot?: string;
5
5
  temporaryRoot?: string;
6
- probeBackend?: (name: string, intended?: 'acp' | 'tmux') => Promise<{
6
+ probeBackend?: (name: string, intended?: 'acp') => Promise<{
7
7
  acp: boolean;
8
- tmux: boolean;
9
8
  }>;
10
9
  concurrency?: number;
11
10
  timeoutMs?: number;
@@ -42,7 +42,11 @@ function snapshot(path, name) {
42
42
  const raw = parse(readFileSync(path, 'utf8'));
43
43
  if (!raw || typeof raw !== 'object')
44
44
  throw new Error('snapshot must be a mapping');
45
- const session = raw.session === 'acp' || raw.session === 'tmux' ? raw.session : 'tmux';
45
+ if (raw.session === 'tmux')
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';
46
50
  return {
47
51
  role: {
48
52
  ...raw, name, harness: typeof raw.harness === 'string' ? raw.harness : 'claude-code',
@@ -167,14 +171,10 @@ export class RoleRepository {
167
171
  const timeoutMs = this.options.timeoutMs ?? 2_000;
168
172
  const result = await Promise.race([
169
173
  this.options.probeBackend(name, intended),
170
- new Promise(resolve => setTimeout(() => resolve({ acp: false, tmux: false }), timeoutMs)),
171
- ]).catch(() => ({ acp: false, tmux: false }));
172
- if (result.acp && result.tmux)
173
- return 'ambiguous';
174
+ new Promise(resolve => setTimeout(() => resolve({ acp: false }), timeoutMs)),
175
+ ]).catch(() => ({ acp: false }));
174
176
  if (result.acp)
175
177
  return 'acp';
176
- if (result.tmux)
177
- return 'tmux';
178
178
  return 'none';
179
179
  }
180
180
  }
@@ -1,7 +1,6 @@
1
1
  import { controlRequest } from '../session/control.js';
2
2
  import type { ConversationEventV1, ConversationSnapshot, PromptReceipt } from '../session/conversation-types.js';
3
3
  import type { SessionSnapshot } from '../session/types.js';
4
- import { Tmux } from '../tmux.js';
5
4
  import type { OutputPage, SendReceipt, SessionDescriptor } from './types.js';
6
5
  export interface ConversationPageView {
7
6
  events: ConversationEventV1[];
@@ -120,14 +119,3 @@ export declare class AcpRoleSessionAdapter implements RoleSessionControl {
120
119
  }>;
121
120
  private call;
122
121
  }
123
- export declare class TmuxRoleSessionAdapter implements RoleSessionControl {
124
- private readonly roleId;
125
- private readonly tmux;
126
- constructor(roleId: string, tmux?: Tmux);
127
- describe(): Promise<SessionDescriptor>;
128
- snapshot(): Promise<SessionSnapshot>;
129
- recentOutput(request?: {
130
- limit?: number;
131
- }): Promise<OutputPage>;
132
- sendText(text: string): Promise<SendReceipt>;
133
- }
@@ -1,6 +1,4 @@
1
- import { randomUUID } from 'node:crypto';
2
1
  import { controlRequest, followConversation } from '../session/control.js';
3
- import { Tmux } from '../tmux.js';
4
2
  import { FleetError, normalizeError } from './errors.js';
5
3
  const visibleEvents = (events) => events.filter(event => event.kind !== 'thought');
6
4
  export class AcpRoleSessionAdapter {
@@ -149,43 +147,3 @@ export class AcpRoleSessionAdapter {
149
147
  }
150
148
  }
151
149
  }
152
- export class TmuxRoleSessionAdapter {
153
- roleId;
154
- tmux;
155
- constructor(roleId, tmux = new Tmux()) {
156
- this.roleId = roleId;
157
- this.tmux = tmux;
158
- }
159
- async describe() {
160
- return { backend: 'tmux', protocolVersion: 1, features: ['text', 'capture'] };
161
- }
162
- async snapshot() {
163
- const alive = await this.tmux.has(this.roleId);
164
- return { backend: 'tmux', alive, readiness: alive ? 'idle' : 'failed' };
165
- }
166
- async recentOutput(request = {}) {
167
- try {
168
- const text = await this.tmux.capture(this.roleId, Math.min(request.limit ?? 100, 500));
169
- return { events: [], text, truncated: false };
170
- }
171
- catch (error) {
172
- throw normalizeError(error);
173
- }
174
- }
175
- async sendText(text) {
176
- if (!text.trim())
177
- throw new FleetError('invalid_request', 'text is required');
178
- if (Buffer.byteLength(text) > 32 * 1024)
179
- throw new FleetError('invalid_request', 'text exceeds 32 KiB');
180
- try {
181
- await this.tmux.sendText(this.roleId, text);
182
- }
183
- catch (error) {
184
- throw normalizeError(error);
185
- }
186
- return {
187
- accepted: true, promptId: randomUUID(), queuedBehind: 0, terminalOutcomeKnown: false,
188
- detail: 'literal text and Enter sent; terminal outcome is unknown',
189
- };
190
- }
191
- }
@@ -1,7 +1,7 @@
1
- import type { InterruptOutcome, QueuedPrompt, SessionHandle, SubmitPromptOptions, TurnCancellationSource } from '../session/types.js';
1
+ import type { AgentSession, InterruptOutcome, QueuedPrompt, SubmitPromptOptions, TurnCancellationSource } from '../session/types.js';
2
2
  /** Thin in-process session mutation kernel. Callers retain every policy decision. */
3
- export declare const queueSessionPrompt: (session: SessionHandle, text: string, options?: SubmitPromptOptions) => Promise<QueuedPrompt>;
4
- export declare const interruptSession: (session: SessionHandle, source: TurnCancellationSource) => Promise<InterruptOutcome>;
5
- export declare const respondSessionPermission: (session: SessionHandle, permissionId: string, optionId: string) => boolean;
3
+ export declare const queueSessionPrompt: (session: AgentSession, text: string, options?: SubmitPromptOptions) => Promise<QueuedPrompt>;
4
+ export declare const interruptSession: (session: AgentSession, source: TurnCancellationSource) => Promise<InterruptOutcome>;
5
+ export declare const respondSessionPermission: (session: AgentSession, permissionId: string, optionId: string) => boolean;
6
6
  export type GenerationPermissionResult = 'accepted' | 'stale' | 'unavailable';
7
- export declare const respondSessionPermissionV2: (session: SessionHandle, permissionId: string, optionId: string, sessionGeneration: string) => GenerationPermissionResult;
7
+ export declare const respondSessionPermissionV2: (session: AgentSession, permissionId: string, optionId: string, sessionGeneration: string) => GenerationPermissionResult;
@@ -117,7 +117,6 @@ export interface RoleCapabilities {
117
117
  };
118
118
  input: {
119
119
  text: boolean;
120
- rawKeys: boolean;
121
120
  interrupt: boolean;
122
121
  steering: boolean;
123
122
  };
@@ -125,12 +124,6 @@ export interface RoleCapabilities {
125
124
  observe: boolean;
126
125
  respond: boolean;
127
126
  };
128
- terminal: {
129
- available: boolean;
130
- reason?: 'wrong_backend' | 'pty_unavailable' | 'offline' | 'incompatible_runner';
131
- multiViewer: boolean;
132
- writerLease: boolean;
133
- };
134
127
  lifecycle: {
135
128
  start: boolean;
136
129
  stop: boolean;
package/dist/briefing.js CHANGED
@@ -190,9 +190,6 @@ export function generateBriefing(role, v, opts) {
190
190
  L.push(`7. Await messages. When the monitor wakes you (or the owner requests a manual check),`);
191
191
  L.push(` call **${v.getMessagesTool}**, act on them,`);
192
192
  L.push(` and reply with ${v.sendTool}. No coordinator is configured — the owner drives you`);
193
- // NOT `tmux attach -t <name>`: each role's pane lives on its own tmux
194
- // socket (#32), so a bare attach finds no server. `ours-fleet attach`
195
- // addresses the right one.
196
193
  L.push(` via \`ours-fleet attach ${role.name}\` or by messaging "${id}".`);
197
194
  }
198
195
  if (role.oversee?.length) {
@@ -221,7 +218,7 @@ export function generateBriefing(role, v, opts) {
221
218
  L.push('idle or stalled from `readiness=idle` alone.');
222
219
  L.push('');
223
220
  L.push('Then judge the console content: stuck on a prompt/menu/trust dialog → answer it directly');
224
- L.push('with `ours-fleet send <Name> "<text>"` (or `--key <K>` for raw keys); idle with work');
221
+ L.push('with `ours-fleet send <Name> "<text>"`; idle with work');
225
222
  L.push('assigned → nudge; actively working → do nothing, and do not mistake a long turn for a');
226
223
  L.push('stall. Escalate over ours messaging only when you cannot resolve it yourself.');
227
224
  }
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "1.0.5",
3
- "buildId": "233dcfbef2e0",
4
- "commit": "5846aa20eb58d5c4ecf8a98bb6a7d3f578afdc2b",
5
- "dirty": false,
6
- "builtAt": "2026-08-26T08:39:49.505Z",
2
+ "version": "1.1.0-nightly.1",
3
+ "buildId": "bf3c406ba57c",
4
+ "commit": "10d6da8bfd9903d216bce80a9ef89a42c282f8d9",
5
+ "dirty": true,
6
+ "builtAt": "2026-08-30T07:44:27.551Z",
7
7
  "capabilities": [
8
8
  "monitor.interrupt.after_tool"
9
9
  ]