@awebai/oats 0.30.3 → 0.31.0
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/bin/oats.mjs +76 -44
- package/docs/configuration.md +3 -0
- package/docs/desktop-cli-api.md +112 -11
- package/docs/desktop-instance-start.md +3 -1
- package/docs/desktop.md +20 -0
- package/docs/execution-targets.md +53 -36
- package/docs/implementation.md +1 -1
- package/docs/integrations.md +1 -1
- package/docs/oats-local.schema.json +8 -0
- package/docs/official-catalog.md +1 -1
- package/docs/packages.md +2 -2
- package/docs/release-notes/v0.31.0.md +130 -0
- package/docs/schedules.md +4 -1
- package/docs/servers.md +94 -25
- package/docs/souls-and-instances.md +2 -2
- package/lib/attachments.mjs +2 -1
- package/lib/core.mjs +166 -209
- package/lib/errors.mjs +17 -0
- package/lib/instance-inspect.mjs +11 -3
- package/lib/schedule.mjs +21 -8
- package/lib/servers.mjs +265 -45
- package/lib/session-input.mjs +9 -23
- package/lib/session-viewer.mjs +0 -5
- package/lib/triggers.mjs +10 -8
- package/package-catalog.json +1 -1
- package/package.json +1 -1
- package/lib/herdr.mjs +0 -143
package/docs/desktop.md
CHANGED
|
@@ -110,6 +110,26 @@ registered remote workspace the timer and definitions live on that server, so
|
|
|
110
110
|
they do not depend on the Mac staying awake. See [Schedules](schedules.md) for
|
|
111
111
|
the CLI, cron semantics, observed outcomes and recovery commands.
|
|
112
112
|
|
|
113
|
+
## Remote terminals
|
|
114
|
+
|
|
115
|
+
A terminal for an instance on a registered server is a viewer over ssh
|
|
116
|
+
(`oats session attach --server`). When the link dies, ssh ends the viewer with
|
|
117
|
+
exit 255 within about a minute, and the tab reconnects by itself. It keeps its
|
|
118
|
+
place and scrollback, stops taking input, and shows "Disconnected from
|
|
119
|
+
<server>" with a countdown and a **Reconnect now** button. Attempts wait 1, 2,
|
|
120
|
+
4, 8 and 15 s, then 30 s each for as long as the tab is open. A successful
|
|
121
|
+
attach resets that. Tmux redraws the screen when the viewer attaches again.
|
|
122
|
+
|
|
123
|
+
Reconnecting stops, with a message and "Close this tab", when the server
|
|
124
|
+
answers that the session is gone, the instance is unknown, the terminal limit
|
|
125
|
+
is reached or the app's backend changed, and at once when ssh cannot be run on
|
|
126
|
+
this computer. It stops after four attempts in a
|
|
127
|
+
row in which OATS on this computer gave no answer, since that is more likely
|
|
128
|
+
the local CLI failing than the link. It also stops when the viewer ends with
|
|
129
|
+
any other exit code. Closing the tab ends the reconnecting. Reconnecting
|
|
130
|
+
never stops or restarts the agent on the server: only the local ssh viewer
|
|
131
|
+
ends.
|
|
132
|
+
|
|
113
133
|
## Attach files and screenshots
|
|
114
134
|
|
|
115
135
|
Drop a file onto an agent terminal to insert its path into that agent's draft.
|
|
@@ -35,39 +35,56 @@ variables point at are described in
|
|
|
35
35
|
|
|
36
36
|
## Backends
|
|
37
37
|
|
|
38
|
-
`oats spawn --backend tmux
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
tmux is the only session backend. `oats spawn --backend tmux` is accepted (it
|
|
39
|
+
is the default); `tmux` must be installed on the execution host. A launched
|
|
40
|
+
home keeps the session it recorded: `session start` and `restart` never
|
|
41
|
+
re-read the defaults. The session target is recorded twice: in
|
|
42
|
+
`instance.json` and in an independent lifecycle receipt. Every session command
|
|
43
|
+
checks that the two agree (`E_RUNTIME_AUTHORITY_MISMATCH` otherwise).
|
|
43
44
|
|
|
44
45
|
### tmux
|
|
45
46
|
|
|
46
|
-
Each instance is a window named after the instance in
|
|
47
|
-
|
|
47
|
+
Each instance is a window named after the instance in a tmux session: the
|
|
48
|
+
deployment's `session.tmuxSession`, else `OATS_TMUX_SESSION`, else
|
|
49
|
+
`PI_AGENTS_TMUX_SESSION` (the pre-0.31 variable), else `oats-agents`. Before
|
|
50
|
+
0.31 the default was `pi-agents`; a home launched then keeps the `pi-agents`
|
|
51
|
+
session it recorded, and `oats status` reads each home on its recorded tmux
|
|
52
|
+
socket and session, never the caller's `$TMUX` server: a caller outside the
|
|
53
|
+
agents' tmux (ssh, cron, a plain terminal) sees the same liveness as `session
|
|
54
|
+
inspect`. A recorded server that cannot be read gives `running: null` with
|
|
55
|
+
`runtimeState: "unreachable"`. The environment variables are read when the spawn runs, and `oats
|
|
56
|
+
inspect --json` reports the session a new spawn would open in as `session`
|
|
57
|
+
(`{tmuxSession}`). A spawn refuses an instance name that is a live window in the
|
|
58
|
+
session it would open in (`E_INSTANCE_NAME_TAKEN`); a live window of that name
|
|
59
|
+
in another session, such as a `pi-agents` window after the default moved, does
|
|
60
|
+
not block it. Session commands target each home's exact recorded window, so
|
|
61
|
+
the two never mix. The receipt records the
|
|
48
62
|
session, window and socket. The spawn result prints the attach command.
|
|
49
63
|
|
|
50
|
-
### Herdr
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
### Herdr (removed in 0.31.0)
|
|
65
|
+
|
|
66
|
+
OATS no longer supports Herdr. Everything that meets it refuses with
|
|
67
|
+
`E_HERDR_REMOVED`, whose message starts `Herdr is no longer supported by OATS
|
|
68
|
+
(removed in 0.31.0); tmux is the only session backend.` and says what to do:
|
|
69
|
+
|
|
70
|
+
- `oats spawn --backend herdr` or `--herdr-socket`, local or routed with
|
|
71
|
+
`--server` (refused before the server is contacted), and `oats server add
|
|
72
|
+
--herdr`: remove the flag, or use tmux.
|
|
73
|
+
- A schedule's `backend: herdr` or a trigger's `spawn.backend: herdr`: the
|
|
74
|
+
message names the file and key; remove it, or use tmux. A stored local job
|
|
75
|
+
that names it is reported invalid and never runs.
|
|
76
|
+
- `session inspect`, `attach`, `input`, `start`, `restart` and `instance stop`
|
|
77
|
+
on a home a Herdr-era kernel recorded (its `instance.json` records a
|
|
78
|
+
`sessionTarget` or `backend: "herdr"`, or its receipt records a session
|
|
79
|
+
target): retire it and spawn a new instance, which opens in tmux.
|
|
80
|
+
- `oats retire` of such a home needs no Herdr: when no process works in the
|
|
81
|
+
home it proceeds as for an absent session; when one does, it refuses and
|
|
82
|
+
names the pids, so stop its Herdr pane first (for example `herdr --session
|
|
83
|
+
oats server stop`).
|
|
84
|
+
|
|
85
|
+
`oats status` lists such a home with `running: null`, `runtimeState:
|
|
86
|
+
"unsupported"` and the refusal as `runtimeError`. A server registration or
|
|
87
|
+
saved route that records `herdrPath` still loads; the field is ignored.
|
|
71
88
|
|
|
72
89
|
## Lifecycle
|
|
73
90
|
|
|
@@ -83,8 +100,8 @@ id distinguishes a replacement occupant of the same pane.
|
|
|
83
100
|
|
|
84
101
|
`session start` keeps the instance's identity, work tree and notes. It runs no
|
|
85
102
|
spawn hooks and creates no new home. It runs the recorded launch recipe on the
|
|
86
|
-
recorded tmux session
|
|
87
|
-
|
|
103
|
+
recorded tmux session; a `--no-launch` home starts on the default tmux
|
|
104
|
+
server.
|
|
88
105
|
|
|
89
106
|
- `--model`, `--launch-config <name>|none`, `--harness` and `--yolo` /
|
|
90
107
|
`--no-yolo` re-resolve the recipe against the home's recorded context and
|
|
@@ -118,18 +135,18 @@ oats session input --home /abs/home --text-file message.txt --json
|
|
|
118
135
|
oats session attach --home /abs/home
|
|
119
136
|
```
|
|
120
137
|
|
|
121
|
-
- **inspect** reports `backend`, `present` and `state`:
|
|
122
|
-
|
|
138
|
+
- **inspect** reports `backend`, `present` and `state`: `unknown` for a live
|
|
139
|
+
harness, `shell` for a fallback shell,
|
|
123
140
|
`stopped` for an absent or dead terminal, or `not-launched`. An unavailable
|
|
124
141
|
backend is an error (`E_SESSION_UNAVAILABLE`), never a stopped result.
|
|
125
142
|
- **input** submits UTF-8 text (stdin or `--text-file`, at most 256 KiB, no
|
|
126
|
-
NUL) followed by Enter
|
|
127
|
-
|
|
143
|
+
NUL) followed by Enter, as a bracketed paste. The text is never run by a
|
|
144
|
+
shell. A fallback shell, a stopped session or a split
|
|
128
145
|
tmux window is refused. `submitted: true` means the terminal accepted the
|
|
129
146
|
text, not that the agent processed it. Wake schedules and messaging
|
|
130
147
|
capabilities use this command ([schedules.md](schedules.md)).
|
|
131
|
-
- **attach** is interactive and takes no `--json`. It opens a
|
|
132
|
-
|
|
148
|
+
- **attach** is interactive and takes no `--json`. It opens a temporary tmux
|
|
149
|
+
session linked to the agent's window alone.
|
|
133
150
|
Closing the viewer leaves the agent running.
|
|
134
151
|
|
|
135
152
|
### Attachments
|
package/docs/implementation.md
CHANGED
|
@@ -54,7 +54,7 @@ published to npm. Its developer docs are in
|
|
|
54
54
|
| `schedule.mjs`, `schedule-host.mjs`, `triggers.mjs`, `automations.mjs` | schedules, triggers and the host timer |
|
|
55
55
|
| `operator-dispatch.mjs` | capability commands run from a deployment, and its module store |
|
|
56
56
|
| `instance-*.mjs` | inspection, lifecycle, events and Git views of an instance |
|
|
57
|
-
| `
|
|
57
|
+
| `tmux-config.mjs`, `session-*.mjs` | the tmux session backend and terminal input |
|
|
58
58
|
| `capability-contract.mjs`, `provider-binding.mjs` | manifest validation, the hook environment rules, the readiness wire |
|
|
59
59
|
| `servers.mjs` | routing commands to a registered server |
|
|
60
60
|
|
package/docs/integrations.md
CHANGED
|
@@ -64,6 +64,14 @@
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
|
+
"session": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"description": "This host's terminal session defaults for NEW launches (0.31). A launched home keeps the session it recorded.",
|
|
71
|
+
"properties": {
|
|
72
|
+
"tmuxSession": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,64}$", "description": "The tmux session new tmux instances open their windows in. Absent: OATS_TMUX_SESSION, else PI_AGENTS_TMUX_SESSION (pre-0.31), else oats-agents." }
|
|
73
|
+
}
|
|
74
|
+
},
|
|
67
75
|
"host": {
|
|
68
76
|
"type": "object",
|
|
69
77
|
"additionalProperties": false,
|
package/docs/official-catalog.md
CHANGED
|
@@ -11,7 +11,7 @@ or workspace membership alone does not make a package official.
|
|
|
11
11
|
|---|---|---|---|
|
|
12
12
|
| `oats.framework` | `oats-framework/v1.4.1` (this repository) | `oats.core`, `oats.setup`, `oats.knowledge-theory` | `knowledge-theory-expert` |
|
|
13
13
|
| `oats.okf` | `v4.0.5` | `oats.okf` (knowledge), `oats.okf-harvest`, `oats.okf-maintenance` | `knowledge-harvester`, `knowledge-maintainer` |
|
|
14
|
-
| `oats.aweb` | `v1.17.
|
|
14
|
+
| `oats.aweb` | `v1.17.3` | `oats.aweb` (messaging) | |
|
|
15
15
|
| `oats.engineering` | `v1.3.0` | `oats.engineering-expert`, `oats.developer`, `oats.code-review` | `code-reviewer` |
|
|
16
16
|
| `oats.authoring` | `v1.0.3` | `oats.authoring` | |
|
|
17
17
|
| `oats.jira` | `v1.0.1` | `oats.jira` (tasks) | |
|
package/docs/packages.md
CHANGED
|
@@ -76,7 +76,7 @@ members:
|
|
|
76
76
|
packages:
|
|
77
77
|
oats.framework: v1.4.1
|
|
78
78
|
oats.okf: v4.0.5
|
|
79
|
-
oats.aweb: v1.17.
|
|
79
|
+
oats.aweb: v1.17.3
|
|
80
80
|
teams:
|
|
81
81
|
platform: { team: "platform:acme.aweb.ai", description: Platform engineering }
|
|
82
82
|
defaults:
|
|
@@ -134,7 +134,7 @@ Declaring a package in the workspace's `packages:` is the trust decision
|
|
|
134
134
|
## `oats package add | remove`
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
oats package add oats.aweb v1.17.
|
|
137
|
+
oats package add oats.aweb v1.17.3 # a catalog version
|
|
138
138
|
oats package add acme.tools git:github.com/acme/tools@v0.4.0
|
|
139
139
|
oats package remove acme.tools
|
|
140
140
|
```
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# OATS 0.31.0
|
|
2
|
+
|
|
3
|
+
## Changed
|
|
4
|
+
|
|
5
|
+
- **New tmux instances open in the tmux session `oats-agents`, not
|
|
6
|
+
`pi-agents`.** Running instances are unaffected: a home launched before 0.31
|
|
7
|
+
keeps the `pi-agents` session it recorded, `session start` and `restart` put
|
|
8
|
+
it back there, a self-retire kills its recorded window, and `oats status` and
|
|
9
|
+
the Desktop read each home in its recorded session. To keep new instances in
|
|
10
|
+
`pi-agents`, add one line to the deployment's `oats-local.yaml`:
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
session: { tmuxSession: pi-agents }
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`OATS_TMUX_SESSION` also sets it, and `PI_AGENTS_TMUX_SESSION` is still
|
|
17
|
+
honoured, read when the spawn runs. `oats inspect --json` reports the session
|
|
18
|
+
a new spawn would open in as `session`.
|
|
19
|
+
- **Routed ssh calls share one connection per host and notice a dead link.**
|
|
20
|
+
Every `--server` call, `server roster` and `session attach --server` runs
|
|
21
|
+
ssh with `ControlMaster=auto`, `ControlPath=~/.oats/ssh/%C` and
|
|
22
|
+
`ControlPersist=60`, plus `ServerAliveInterval=15` and
|
|
23
|
+
`ServerAliveCountMax=3`. The probe, the command and an attached viewer
|
|
24
|
+
ride one authenticated connection. A viewer whose link dies ends within
|
|
25
|
+
about 60 s with a message naming the command to reattach, instead of
|
|
26
|
+
hanging. These options override any `Control*` settings for the host in
|
|
27
|
+
`~/.ssh/config`. When the control socket path would not fit the 104-byte
|
|
28
|
+
limit or has characters ssh reads as syntax, or `~/.oats/ssh` is not
|
|
29
|
+
private to you, calls run with connection sharing off
|
|
30
|
+
(`ControlPath=none`) and the command warns once per server
|
|
31
|
+
([servers.md](../servers.md#connections)).
|
|
32
|
+
- **The version probe is asked once per process** per server and target.
|
|
33
|
+
- A server too old for `oats session` is refused with the tmux command for
|
|
34
|
+
the instance's recorded session and window, not a fixed `oats` session.
|
|
35
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.32.0`** (was `<0.31.0`), so
|
|
36
|
+
it runs against this release's kernel.
|
|
37
|
+
- **oats.aweb 1.17.3** (catalog and workspace pin, and the bundled mirror).
|
|
38
|
+
Session-delivery guidance matches aw 1.36.15's host wake broker: it
|
|
39
|
+
presents either a line naming what is waiting or the full mail/chat event,
|
|
40
|
+
and recovery after an uncertain crash, compaction or restart goes by exact
|
|
41
|
+
message id (`aw mail show --message-id <id> --json`) or paged
|
|
42
|
+
`aw mail inbox --show-all --json` with `--cursor`. A resident grant's
|
|
43
|
+
`renew: launch` re-registers the wake broker with the new grant, and grant
|
|
44
|
+
seats start with `aw whoami`.
|
|
45
|
+
|
|
46
|
+
## Added
|
|
47
|
+
|
|
48
|
+
- **Any instance on a server is controllable from here, whoever spawned it.**
|
|
49
|
+
`retire --server` and the routed `session` commands (inspect, attach,
|
|
50
|
+
start, restart, upload), `inspect`, `operation` and `launch-config` reach
|
|
51
|
+
an instance with no saved route by `--home`, or by a name the server's
|
|
52
|
+
roster lists once. A name two homes share there is refused with
|
|
53
|
+
`E_AMBIGUOUS`, listing the homes. Roster rows gain `addressable`, and
|
|
54
|
+
`savedRoute` is information only ([servers.md](../servers.md#run-there)).
|
|
55
|
+
- **The Desktop's reads and lifecycle plans route to the instance's
|
|
56
|
+
machine.** `readiness`, `instance events`, `instance git`, `instance diff`,
|
|
57
|
+
`instance stop --plan|--apply` and `retire --plan` (and its guarded apply)
|
|
58
|
+
take `--server <id>`; the host's kernel does the work, Git included, and
|
|
59
|
+
its envelope is relayed unchanged. A host that does not advertise the
|
|
60
|
+
feature is refused with `E_REMOTE_INCOMPATIBLE` before anything is sent.
|
|
61
|
+
`version --json` lists them in `remote`: `readiness`, `instance-events`,
|
|
62
|
+
`instance-git`, `lifecycle-plans`
|
|
63
|
+
([desktop-cli-api.md](../desktop-cli-api.md#routed-reads-and-plans)).
|
|
64
|
+
- **Remote roster rows carry the host's facts.** `oats server roster --json`
|
|
65
|
+
instance rows relay `identity`, `identityAddress`, `teams`, `startedAt`,
|
|
66
|
+
`createdAt`, `model`, `runtimeState`, `parentInstance`, `siblingInstance`,
|
|
67
|
+
`relation`, `relativeTo` and `spawnOrigin` from the host's `status --json`,
|
|
68
|
+
`null` where the host does not supply them
|
|
69
|
+
([desktop-cli-api.md](../desktop-cli-api.md#the-remote-roster-oats-server-roster---json)).
|
|
70
|
+
- **A remote terminal in the Desktop reconnects after a lost link.** A
|
|
71
|
+
viewer that ssh ends with exit 255 keeps its tab and scrollback, goes
|
|
72
|
+
inert, and re-attaches with backoff (1, 2, 4, 8, 15 s, then every 30 s),
|
|
73
|
+
with **Reconnect now** to try at once. It stops with the reason when the
|
|
74
|
+
server says the session is gone or refuses the attach, at once when ssh
|
|
75
|
+
cannot be run on this computer, or after four
|
|
76
|
+
attempts in a row in which this computer's `oats` gave no answer
|
|
77
|
+
([desktop.md](../desktop.md#remote-terminals)). Reconnecting never touches
|
|
78
|
+
the agent on the server.
|
|
79
|
+
|
|
80
|
+
## Fixed
|
|
81
|
+
|
|
82
|
+
- **`oats status` reads liveness from each instance's recorded tmux socket**,
|
|
83
|
+
not the caller's `$TMUX` server. Outside the agents' tmux (every `status
|
|
84
|
+
--server` over ssh, cron, a plain terminal) running instances read
|
|
85
|
+
`running: false`, and a row on the default server could read `true`. Now
|
|
86
|
+
`running` agrees with `session inspect`; a recorded server that cannot be
|
|
87
|
+
read gives `running: null` with `runtimeState: "unreachable"` (#347).
|
|
88
|
+
- **`session attach --server` exits 255 when ssh fails before the viewer
|
|
89
|
+
opens** (the version probe, or resolving a name through the host's roster),
|
|
90
|
+
as it does when ssh fails under the viewer; it exited 1, so a caller that
|
|
91
|
+
reconnects on 255 gave up on a flapping link. Every other refusal still
|
|
92
|
+
exits 1. An ssh failure now names the host: `ssh to <host> failed: …`
|
|
93
|
+
(#349).
|
|
94
|
+
- **An `E_SSH` envelope says when ssh never started.** A routed `--json`
|
|
95
|
+
command whose ssh could not be run on this machine (not installed, not
|
|
96
|
+
executable) answers `E_SSH` with `error.details: {"sshStarted": false}`, so
|
|
97
|
+
a caller can stop retrying; a failed link carries no details, as before.
|
|
98
|
+
|
|
99
|
+
## Removed
|
|
100
|
+
|
|
101
|
+
- **Herdr.** tmux is the only session backend; cross-machine work goes over
|
|
102
|
+
ssh routing (`--server`), which never used Herdr. Nothing is migrated
|
|
103
|
+
silently: everything that meets Herdr refuses with `E_HERDR_REMOVED`, whose
|
|
104
|
+
message says what to do.
|
|
105
|
+
- `oats spawn --backend herdr` or `--herdr-socket` (local, or routed with
|
|
106
|
+
`--server`, refused before the server is contacted) and `oats server add
|
|
107
|
+
--herdr`: remove the flag, or use tmux. `--backend tmux` is still accepted,
|
|
108
|
+
and `oats version --json` advertises `sessionBackends: ["tmux"]`.
|
|
109
|
+
- A schedule's `backend: herdr` or a trigger's `spawn.backend: herdr`: the
|
|
110
|
+
message names the file and key; remove it, or use tmux. A stored local job
|
|
111
|
+
that names it is reported invalid and never runs.
|
|
112
|
+
- `session inspect`, `attach`, `input`, `start`, `restart` and `instance
|
|
113
|
+
stop` on an instance opened in Herdr: retire it (`oats retire <name>`) and
|
|
114
|
+
spawn a new instance; it opens in tmux. `oats status` lists such an
|
|
115
|
+
instance with `running: null`, `runtimeState: "unsupported"` and the
|
|
116
|
+
refusal as `runtimeError`, and never fails because of it.
|
|
117
|
+
- `oats retire` of an instance opened in Herdr works without Herdr. When a
|
|
118
|
+
process still works in its home it refuses, naming the pids: stop its
|
|
119
|
+
Herdr pane (for example `herdr --session oats server stop`), then retire.
|
|
120
|
+
- A server registration or saved route that records `herdrPath` still
|
|
121
|
+
loads; the field is ignored.
|
|
122
|
+
- **The Desktop** no longer offers Herdr as a session backend and never
|
|
123
|
+
opens a Herdr terminal; it refuses one with `E_HERDR_REMOVED`, including
|
|
124
|
+
for a remote host still on an older OATS. An instance opened in Herdr
|
|
125
|
+
stays in the roster with the reason; its Open, Start and Restart are
|
|
126
|
+
disabled, and Remove (retire) still works, so it can be retired and
|
|
127
|
+
spawned again in tmux.
|
|
128
|
+
|
|
129
|
+
An idle Herdr server that OATS started (session `oats`) is not OATS state;
|
|
130
|
+
stop it with `herdr --session oats server stop`.
|
package/docs/schedules.md
CHANGED
|
@@ -54,7 +54,10 @@ both are evaluated by the croner library.
|
|
|
54
54
|
launchConfig?, harness?, model?, yolo?, wake?}` — every due minute launches
|
|
55
55
|
one disposable instance of `agent` with the options `oats spawn` takes.
|
|
56
56
|
`agentsRoot`, when given, must be the deployment's `agents/` root; `repo`
|
|
57
|
-
is the work repository, as `--repo`. `
|
|
57
|
+
is the work repository, as `--repo`. `backend` is `tmux`; `backend: herdr`
|
|
58
|
+
is refused (`E_HERDR_REMOVED`, naming the file and key: Herdr was removed in
|
|
59
|
+
0.31.0), and a stored job that names it is reported invalid and never runs.
|
|
60
|
+
`model` is a model id (a letter or
|
|
58
61
|
digit, then letters, digits and `. _ : / @ + - [ ]`, at most 128
|
|
59
62
|
characters) or `@native-default`; `agent` and `repo` never start with `-`,
|
|
60
63
|
so no value can be read as an option of the child `oats spawn`. Each run is
|
package/docs/servers.md
CHANGED
|
@@ -26,13 +26,59 @@ oats server remove build
|
|
|
26
26
|
- `--path` prepends directories to the minimal remote PATH of every routed
|
|
27
27
|
command (`~/.local/bin:/opt/pi/bin`), where the remote spawn looks for the
|
|
28
28
|
harness binary.
|
|
29
|
-
- `--herdr`
|
|
30
|
-
saved route
|
|
31
|
-
|
|
29
|
+
- `--herdr` is refused (`E_HERDR_REMOVED`): Herdr was removed in 0.31.0. A
|
|
30
|
+
registration or saved route that still records `herdrPath` loads; the field
|
|
31
|
+
is ignored, never written or printed.
|
|
32
32
|
- `--label` sets a display name. `--replace` overwrites an existing id.
|
|
33
33
|
- Registrations live in `~/.oats/servers.json` on this machine, never in a
|
|
34
34
|
repository.
|
|
35
35
|
|
|
36
|
+
## Connections
|
|
37
|
+
|
|
38
|
+
Every routed ssh call carries these options, before `--` and the host:
|
|
39
|
+
|
|
40
|
+
| Option | Why |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `BatchMode=yes`, `ConnectTimeout=15` | a prompt or an unreachable host fails fast |
|
|
43
|
+
| `ServerAliveInterval=15`, `ServerAliveCountMax=3` | a link that died without a reset ends the call, an attached viewer included, within about 60 s |
|
|
44
|
+
| `ControlMaster=auto`, `ControlPath=~/.oats/ssh/%C`, `ControlPersist=60` | every call to one host (the probe, the command, a viewer, concurrent Desktop reads) shares one authenticated connection, kept 60 s after its last client |
|
|
45
|
+
|
|
46
|
+
- Options given with `-o` override `~/.ssh/config`, so these replace any
|
|
47
|
+
`ControlMaster`, `ControlPath` or `ControlPersist` set there for the host.
|
|
48
|
+
Everything else in your ssh config (users, keys, `ProxyJump`, host
|
|
49
|
+
verification) applies as usual; a `ProxyJump` host is reached once per
|
|
50
|
+
master.
|
|
51
|
+
- `~/.oats/ssh` (under `OATS_HOME_DIR` when set) is created mode 0700. OATS
|
|
52
|
+
does not use it if it is owned by another user or writable by group or
|
|
53
|
+
others.
|
|
54
|
+
- ssh binds the control socket at the path plus 17 bytes, within the 104-byte
|
|
55
|
+
socket path limit of macOS, so the directory path may be at most 45 bytes.
|
|
56
|
+
`/Users/<name>/.oats/ssh` fits for any name up to 23 characters. The path
|
|
57
|
+
must also be one ssh reads literally: letters, digits and `. _ / + , : @ =
|
|
58
|
+
-` only (a space, `%`, `$`, a quote or `#` is ssh syntax).
|
|
59
|
+
- When the path does not fit, has other characters, or the directory is not
|
|
60
|
+
private, calls run with `ControlPath=none` instead: no connection sharing,
|
|
61
|
+
including any your ssh config sets up; keepalives still apply. The command
|
|
62
|
+
warns once per server on stderr, naming the path and the fix (an
|
|
63
|
+
`OATS_HOME_DIR` or `HOME` that is shorter or plain).
|
|
64
|
+
- A master left stale by a network drop is replaced by ssh on the next call.
|
|
65
|
+
- The version probe (`oats version --json`) is asked once per process per
|
|
66
|
+
server and target. `server add --replace` to another target forgets it.
|
|
67
|
+
- When ssh fails under an attached viewer (a lost link, or one never made),
|
|
68
|
+
`session attach --server` exits 255 and says that, if the link was lost,
|
|
69
|
+
the instance keeps running on the server, with the command to reattach.
|
|
70
|
+
When ssh fails before the viewer opens (the version probe, or a name
|
|
71
|
+
resolved through the host's roster), it also exits 255, with `oats: ssh to
|
|
72
|
+
<host> failed: …` (`--json`: the `E_SSH` envelope). Every other refusal
|
|
73
|
+
(an incompatible host, an unknown or ambiguous name, bad arguments) exits 1,
|
|
74
|
+
and so does an ssh that cannot be run on this machine at all: no link can
|
|
75
|
+
come back.
|
|
76
|
+
- Every routed command reports ssh's own failure as `E_SSH` (`ssh to <host>
|
|
77
|
+
failed: …`). When ssh never started on this machine, the `--json` envelope
|
|
78
|
+
says so with `error.details: {"sshStarted": false}`; without details, ssh
|
|
79
|
+
ran and the link failed
|
|
80
|
+
([desktop-cli-api.md](desktop-cli-api.md#ssh-failures-e_ssh)).
|
|
81
|
+
|
|
36
82
|
## Run there
|
|
37
83
|
|
|
38
84
|
```bash
|
|
@@ -50,14 +96,18 @@ sent as text or on stdin, never as paths.
|
|
|
50
96
|
|---|---|---|
|
|
51
97
|
| `spawn` | registered workspace | the requested harness, backend and yolo option; `launch-config` for `--launch-config`; `schedule` for a wake schedule |
|
|
52
98
|
| `status` | registered workspace | |
|
|
53
|
-
| `retire` |
|
|
54
|
-
| `session inspect`, `session attach` |
|
|
55
|
-
| `session start`, `session restart` |
|
|
56
|
-
| `session upload` |
|
|
99
|
+
| `retire` | the instance's home | `retire-home` to retire by exact home |
|
|
100
|
+
| `session inspect`, `session attach` | the instance's home | `session` |
|
|
101
|
+
| `session start`, `session restart` | the instance's home | `session-start`; `session-restart`; `launch-config` when `--launch-config`, `--harness` or `--yolo` is given |
|
|
102
|
+
| `session upload` | the instance's home | `session-upload` |
|
|
57
103
|
| `okf harvest --instance <name>` | the saved home | `harvest` |
|
|
58
104
|
| `schedule ...` | registered workspace | `schedule` |
|
|
59
|
-
| `launch-config list\|set\|remove\|preview` | `--dir`,
|
|
60
|
-
| `inspect`, `operation run` | `--dir`,
|
|
105
|
+
| `launch-config list\|set\|remove\|preview` | `--dir`, the instance's home, or the registered workspace | `launch-config` |
|
|
106
|
+
| `inspect`, `operation run` | `--dir`, the instance's home, or the registered workspace | `operations` |
|
|
107
|
+
| `readiness` | the instance's home, `--dir`, or the registered workspace | `readiness` (`readinessApi: 2`) |
|
|
108
|
+
| `instance events` | the instance's home | `instance-events-2` (`eventsApi: 2`) |
|
|
109
|
+
| `instance git`, `instance diff` | the instance's home; the Git runs there | `instance-git` (`instanceGitApi: 1`) |
|
|
110
|
+
| `instance stop --plan\|--apply`, `retire --plan`, `retire --plan-revision … --idempotency-key …` | the instance's home | `lifecycle-plans` (`lifecycleApi: 1`) |
|
|
61
111
|
|
|
62
112
|
Before every routed command except `status`, the kernel reads the server's
|
|
63
113
|
`oats version --json`. A remote OATS older than 0.22.1 is refused, and so is a
|
|
@@ -66,19 +116,36 @@ checked against the harness it would use (the flag, or the soul's default as
|
|
|
66
116
|
the remote roster reports it). A host that advertises no harness list is
|
|
67
117
|
assumed to run only pi and claude, on tmux, with no launch options.
|
|
68
118
|
|
|
69
|
-
**Addressing an instance.** Instance commands
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
119
|
+
**Addressing an instance.** Instance commands (`retire`, the `session`
|
|
120
|
+
commands, `inspect`, `operation`, `launch-config`) reach any instance on the
|
|
121
|
+
server, whoever spawned it, by `--home </remote/home>` or by name
|
|
122
|
+
(`--instance <name>`, or the positional name of `retire`):
|
|
123
|
+
|
|
124
|
+
- A name spawned from this machine resolves through its saved route.
|
|
125
|
+
- Any other name resolves through the server's roster (one `status --json`
|
|
126
|
+
in the registered workspace) to its one home. A name that two homes carry
|
|
127
|
+
there is `E_AMBIGUOUS`, listing the homes (`error.details.candidates`);
|
|
128
|
+
pass `--home`. A name the roster does not list is `E_SNAPSHOT_UNKNOWN`.
|
|
129
|
+
- A `--home` no saved route owns is sent through the registration's target;
|
|
130
|
+
the server's kernel decides whether it is an instance home and its refusal
|
|
131
|
+
is relayed as is.
|
|
132
|
+
- The home is the identity; use it when two souls on the host own
|
|
133
|
+
same-named instances. A name and a home that disagree are refused
|
|
134
|
+
(`E_HOME_MISMATCH`).
|
|
135
|
+
|
|
136
|
+
**`--dir` with `--server`.** For `inspect`, `operation`, `launch-config`,
|
|
137
|
+
`readiness`, `instance`, and a `retire --plan` or guarded retire apply, an
|
|
138
|
+
explicit `--dir` names a directory on the server and travels as is. Every
|
|
139
|
+
other routed command refuses `--dir`; its scope comes from the
|
|
140
|
+
registration.
|
|
77
141
|
|
|
78
142
|
**Not routed.** `session input` runs on the execution host, where schedules
|
|
79
143
|
and messaging capabilities call it. `session restart --stop-grace` is refused
|
|
80
144
|
with `--server`; the remote default applies. `session attach --print` shows
|
|
81
|
-
the ssh command without running it.
|
|
145
|
+
the ssh command without running it. A server without the `session` commands
|
|
146
|
+
(before 0.22.2) is refused with the tmux command to attach there directly,
|
|
147
|
+
naming the session and window its roster records for the instance (else
|
|
148
|
+
`pi-agents`, that kernel's default).
|
|
82
149
|
|
|
83
150
|
## The roster and harvest
|
|
84
151
|
|
|
@@ -91,8 +158,13 @@ The **roster** is what the Desktop shows: one group per server id and route
|
|
|
91
158
|
target (host and workspace), with the registration (present or not), the
|
|
92
159
|
probe result, the remote souls, the instances joined with saved routes
|
|
93
160
|
(`savedRoute`, `running` or `null` when unknown, `retirePending`,
|
|
94
|
-
`rollbackIncomplete`, `missingRemotely`), and `retireFailures`
|
|
95
|
-
self-retirements that failed there).
|
|
161
|
+
`rollbackIncomplete`, `missingRemotely`, `addressable`), and `retireFailures`
|
|
162
|
+
(deferred self-retirements that failed there). Each instance row also relays
|
|
163
|
+
the host's own facts from its `status --json`: `identity`,
|
|
164
|
+
`identityAddress`, `teams`, `startedAt`, `createdAt`, `model`,
|
|
165
|
+
`runtimeState`, `parentInstance`, `siblingInstance`, `relation`,
|
|
166
|
+
`relativeTo` and `spawnOrigin`. A fact the host does not supply is `null`
|
|
167
|
+
(an older host, or a saved route the host no longer lists). A removed or edited registration keeps
|
|
96
168
|
its group from the saved routes. State is pulled on every call within
|
|
97
169
|
`--per-target` (default 20 s) of a total `--budget` (default 45 s); a group
|
|
98
170
|
not reached is reported with `E_ROSTER_BUDGET`. `--server <id>` narrows it.
|
|
@@ -103,8 +175,8 @@ instance's saved home on the server and relays its envelope.
|
|
|
103
175
|
## What this machine keeps
|
|
104
176
|
|
|
105
177
|
A **saved route** per remote instance, under `~/.oats/remote/<server>/`,
|
|
106
|
-
written at spawn: the ssh host, workspace, oats path
|
|
107
|
-
|
|
178
|
+
written at spawn: the ssh host, workspace, oats path and PATH prefix, and the
|
|
179
|
+
remote home. Retire, harvest and session commands use
|
|
108
180
|
the saved route, not the current registration, so editing or removing a
|
|
109
181
|
registration never orphans a remote home. The route is removed when the
|
|
110
182
|
remote kernel reports the home gone.
|
|
@@ -123,8 +195,5 @@ remote kernel reports the home gone.
|
|
|
123
195
|
|
|
124
196
|
## Limits
|
|
125
197
|
|
|
126
|
-
- Lifecycle actions need a saved route from this machine. An instance the
|
|
127
|
-
remote reports but that was spawned elsewhere appears in the roster with
|
|
128
|
-
`savedRoute: false` and is read-only here.
|
|
129
198
|
- No Git runs over SSH: repository operations always run on the server, by
|
|
130
199
|
its kernel, in its deployment.
|
|
@@ -85,8 +85,8 @@ launch: { harness: claude, model: claude-opus-5-5 } # optional (0.30): what th
|
|
|
85
85
|
|
|
86
86
|
Schema: [`soul.schema.json`](soul.schema.json). Which teams a soul joins is the
|
|
87
87
|
deployment's choice (`oats-local.yaml`, [workspaces.md](workspaces.md#teams)),
|
|
88
|
-
and
|
|
89
|
-
(`--
|
|
88
|
+
and yolo and the launch configuration are spawn-time host choices
|
|
89
|
+
(`--yolo`, `--launch-config`, or a launch configuration in
|
|
90
90
|
`oats-local.yaml`), not soul identity. The harness and model are a soul's
|
|
91
91
|
*preference* at most (`launch:`), which each machine overrides and spawn flags
|
|
92
92
|
(`--harness`, `--model`) win over.
|
package/lib/attachments.mjs
CHANGED
|
@@ -147,7 +147,8 @@ export function uploadAttachment({ file, home, server, instance }, io = {}) {
|
|
|
147
147
|
if (!home) throw err("E_BAD_ARGS", "session upload needs --home </absolute/instance> or --server <id> with --instance <name> or --home");
|
|
148
148
|
return { ...receiveAttachment(home, name, bytes, { maxBytes }), name, home: realpathSync(home), source: abs };
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
io = { ...io, serverId: server };
|
|
151
|
+
const route = resolveRoute(server, { instance, home }, "session upload", io);
|
|
151
152
|
const remote = checkRemote(route.target, io);
|
|
152
153
|
// Both lists must carry the token; a probe without the arrays is an old kernel.
|
|
153
154
|
const advertises = (list) => Array.isArray(list) && list.includes("session-upload");
|