@awebai/oats 0.30.3 → 0.32.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 +216 -79
- package/docs/configuration.md +68 -8
- package/docs/desktop-cli-api.md +233 -27
- package/docs/desktop-instance-start.md +3 -1
- package/docs/desktop.md +36 -0
- package/docs/execution-targets.md +53 -36
- package/docs/implementation.md +59 -1
- package/docs/integrations.md +1 -1
- package/docs/oats-local.schema.json +10 -1
- package/docs/official-catalog.md +1 -1
- package/docs/packages.md +2 -2
- package/docs/plans/0.30-close-out.md +7 -4
- package/docs/release-notes/v0.31.0.md +130 -0
- package/docs/release-notes/v0.32.0.md +137 -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/automations.mjs +5 -1
- package/lib/core.mjs +241 -238
- package/lib/errors.mjs +17 -0
- package/lib/instance-inspect.mjs +13 -4
- package/lib/instance-resolution.mjs +3 -3
- package/lib/local-inputs.mjs +46 -0
- package/lib/materialize.mjs +3 -2
- package/lib/packages.mjs +26 -6
- package/lib/remote.mjs +792 -36
- package/lib/resolve.mjs +23 -7
- package/lib/schedule.mjs +21 -8
- package/lib/servers.mjs +272 -45
- package/lib/session-input.mjs +9 -23
- package/lib/session-viewer.mjs +0 -5
- package/lib/triggers.mjs +10 -8
- package/lib/workspace.mjs +163 -56
- package/package-catalog.json +1 -1
- package/package.json +1 -1
- package/lib/herdr.mjs +0 -143
|
@@ -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
|
@@ -44,6 +44,7 @@ published to npm. Its developer docs are in
|
|
|
44
44
|
| module | owns |
|
|
45
45
|
|---|---|
|
|
46
46
|
| `remote.mjs` | repo refs, reading Git remotes, content digests |
|
|
47
|
+
| `local-inputs.mjs` | the local configuration a command read, for `observation.localRevision` |
|
|
47
48
|
| `workspace.mjs` | workspace, membership and soul files; discovery |
|
|
48
49
|
| `resolve.mjs` | a soul's resolution: capabilities, slots, provenance |
|
|
49
50
|
| `packages.mjs` | `packages:`, the catalog, `oats sync`, `oats-lock.json` |
|
|
@@ -54,7 +55,7 @@ published to npm. Its developer docs are in
|
|
|
54
55
|
| `schedule.mjs`, `schedule-host.mjs`, `triggers.mjs`, `automations.mjs` | schedules, triggers and the host timer |
|
|
55
56
|
| `operator-dispatch.mjs` | capability commands run from a deployment, and its module store |
|
|
56
57
|
| `instance-*.mjs` | inspection, lifecycle, events and Git views of an instance |
|
|
57
|
-
| `
|
|
58
|
+
| `tmux-config.mjs`, `session-*.mjs` | the tmux session backend and terminal input |
|
|
58
59
|
| `capability-contract.mjs`, `provider-binding.mjs` | manifest validation, the hook environment rules, the readiness wire |
|
|
59
60
|
| `servers.mjs` | routing commands to a registered server |
|
|
60
61
|
|
|
@@ -62,6 +63,63 @@ The kernel is runtime-neutral: nothing in `lib/` depends on a harness or on
|
|
|
62
63
|
a provider. Provider behaviour lives in capabilities; the kernel supplies
|
|
63
64
|
their contracts ([layers](layers.md)).
|
|
64
65
|
|
|
66
|
+
### The remote read path
|
|
67
|
+
|
|
68
|
+
Every CLI command owns one read session (`createReadSession` in
|
|
69
|
+
`remote.mjs`, carried to every remote call as `remoteOptions.session`; the
|
|
70
|
+
CLI closes it when the command ends). A library caller without a session
|
|
71
|
+
gets the plain per-call behaviour. Within a session:
|
|
72
|
+
|
|
73
|
+
- a head is observed once per (cache repo, ref), and a commit peeled once; at
|
|
74
|
+
most eight observations run at once (`OBSERVE_LIMIT`), each holding its slot
|
|
75
|
+
for all its git work (the `ls-remote` and the fetch of the commit it names,
|
|
76
|
+
or the fetch of a reused record's commit);
|
|
77
|
+
- a whole-workspace discovery prefetches its members' heads together with the
|
|
78
|
+
host's (`prefetchMembers` in `workspace.mjs`): the member list comes from the
|
|
79
|
+
host's last observation record and the parsed `workspace` entry at that
|
|
80
|
+
commit, never from a git process, and the answer still uses the list at the
|
|
81
|
+
host commit observed now. A prefetched failure is adopted by the member's own
|
|
82
|
+
observation, not retried in the command. A prefetch no caller adopts (the
|
|
83
|
+
host could not be observed, or the member was dropped since) is abandoned:
|
|
84
|
+
one still queued runs no git. `observeWorkspace` alone (the `teams` reads,
|
|
85
|
+
`inspect --home`) never prefetches;
|
|
86
|
+
- closing the session (the end of the command, or `process.exit`) rejects
|
|
87
|
+
every queued observation and aborts every git child still running for it
|
|
88
|
+
(the session's `AbortSignal` rides every `runGit`). `runGit` starts git as
|
|
89
|
+
its own process group, so a timeout, an output overflow or an abort kills
|
|
90
|
+
git's ssh or remote helper with it; before a capability
|
|
91
|
+
command runs its provider, the CLI ends the idle batch readers
|
|
92
|
+
(`closeBatches`);
|
|
93
|
+
- a commit's tree is listed once (`git ls-tree -r -t -l`, bounded by
|
|
94
|
+
`TREE_INDEX_BUDGET`; anything odd falls back to the per-path reads), and
|
|
95
|
+
blobs come from one `git cat-file --batch` reader per cache repo (at most
|
|
96
|
+
12 open, killed through `process-group.mjs` on timeout and at close);
|
|
97
|
+
- discovery reads members eight at a time (`DISCOVERY_CONCURRENCY`) with
|
|
98
|
+
serial results: declaration order, the first failure in that order. The
|
|
99
|
+
observations and the member reads are two pools, so a discovery runs at
|
|
100
|
+
most sixteen short-lived git processes at once (eight of them fetches at
|
|
101
|
+
most), plus up to twelve cat-file readers: twenty-eight git processes. One
|
|
102
|
+
shared pool would deadlock: a member read holding a slot waits on its
|
|
103
|
+
member's observation, which needs a slot of its own.
|
|
104
|
+
|
|
105
|
+
Across commands, `memoAtCommit` keeps parsed reads under
|
|
106
|
+
`<cache>/.parsed/<kernel fingerprint>/`, keyed by (repo key, full commit,
|
|
107
|
+
item). The items: `workspace` (the workspace file), `membership` (a member's
|
|
108
|
+
backlink outcome), `enumerate` (a member's souls and capabilities),
|
|
109
|
+
`package-soul` and `external-soul` (one soul file each; their error handling
|
|
110
|
+
differs), `package-manifests` (a package's manifests), `list` (a skill
|
|
111
|
+
listing) and `tree-oids` (the tree ids of a set of directories). An entry is only ever a pure function of those bytes and this kernel's
|
|
112
|
+
code, never local state and never a transient error; it is written
|
|
113
|
+
atomically, a corrupt one is a miss, and `pruneStores` bounds the store
|
|
114
|
+
(`PARSED_LIMITS`, least recently used first). `--max-age` adds the observation
|
|
115
|
+
store `<cache>/.observed/` ([Observation reuse](desktop-cli-api.md#observation-reuse-feature-observe-max-age-oats-0311)):
|
|
116
|
+
one record per (repo key, ref args, url digest), so two spellings of one repo
|
|
117
|
+
keep a record each; the url itself is never written. Adding a cached item
|
|
118
|
+
means choosing an item name unique to its producer (the item string its
|
|
119
|
+
call site passes to `memoAtCommit`, or to `atCommit` in `workspace.mjs`) and
|
|
120
|
+
adding it to `test/parsed-cache.test.mjs`; `test/read-path-scale.test.mjs` pins the member
|
|
121
|
+
scaling by call count.
|
|
122
|
+
|
|
65
123
|
## Tests and gates
|
|
66
124
|
|
|
67
125
|
| command | what it checks |
|
package/docs/integrations.md
CHANGED
|
@@ -60,10 +60,19 @@
|
|
|
60
60
|
"description": "Environment for the harness. A string is a literal (non-secret by contract; still redacted in every answer). {fromEnv: NAME} is resolved from the execution host's environment at start time; the reference, never the value, is recorded. A missing reference refuses the start before anything stops."
|
|
61
61
|
},
|
|
62
62
|
"model": { "type": "string", "minLength": 1, "description": "Model for this configuration's harness; overrides the soul default when this configuration is selected." },
|
|
63
|
-
"yolo": { "type": "boolean", "description": "Permission bypass for this configuration; overrides scope and soul defaults when selected." }
|
|
63
|
+
"yolo": { "type": "boolean", "description": "Permission bypass for this configuration; overrides scope and soul defaults when selected." },
|
|
64
|
+
"default": { "type": "boolean", "description": "This host's baseline for its harness (0.32, feature launch-config-default): a new launch that picks this harness without naming a configuration (a soul's or souls.launch preference, --harness, the host default) runs this configuration's executable, args, env and yolo; its model is the last fallback. At most one per harness. `--launch-config none` bypasses it. OATS 0.31 and older refuse the key." }
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
},
|
|
68
|
+
"session": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"description": "This host's terminal session defaults for NEW launches (0.31). A launched home keeps the session it recorded.",
|
|
72
|
+
"properties": {
|
|
73
|
+
"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." }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
67
76
|
"host": {
|
|
68
77
|
"type": "object",
|
|
69
78
|
"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
|
```
|
|
@@ -69,8 +69,11 @@ Found by the rehearsals, and fixed before the tag:
|
|
|
69
69
|
## After the tag
|
|
70
70
|
- **Flag day:** the oats.engineering release with the souls' `launch:` (experts on Claude Code +
|
|
71
71
|
Opus 5.5; `code-reviewer` on Codex + Astra; compat `>=0.30.0`), and committing the shared team id.
|
|
72
|
-
- **0.30.1:**
|
|
73
|
-
-
|
|
72
|
+
- **0.30.1:** one release, cut when the Desktop load work has merged (the kernel read-path PR with
|
|
73
|
+
`observe-max-age` and `observation.localRevision`, Desktop #321 and #322), or on 2026-09-30 18:00 UTC,
|
|
74
|
+
whichever comes first; what has not merged by then goes to 0.30.2. Retire of homes whose session is
|
|
75
|
+
gone (#299) and automations.trust (#300) shipped in 0.30.0. The kernel items below have no owner yet:
|
|
76
|
+
each rides 0.30.1 only if it merges before the cut.
|
|
74
77
|
- deployment-scope capability commands without `--soul`;
|
|
75
78
|
- the case-8 pre-check (`E_TEAM_UNCONFIGURED`).
|
|
76
79
|
- the kernel refuses an unrecognised spawn argument (a bare `join=oats` without
|
|
@@ -79,5 +82,5 @@ Found by the rehearsals, and fixed before the tag:
|
|
|
79
82
|
FIRST on the instance's PATH and records it in the launch recipe, so plain `oats` inside an
|
|
80
83
|
instance is the kernel that made it (two kernels side by side: a 0.30 home ran the global 0.24.6
|
|
81
84
|
and `oats aweb` was `E_UNKNOWN_COMMAND`; the injected instructions tell agents to run plain `oats`).
|
|
82
|
-
- **
|
|
83
|
-
|
|
85
|
+
- **Done:** item L merged in #314 (d4f68da9): the remaining legacy `agents/` soul trees and the
|
|
86
|
+
`validate:okf` gate are removed.
|
|
@@ -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`.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# OATS 0.32.0
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
- **A default launch configuration per harness.** `default: true` on a
|
|
6
|
+
launch configuration in `oats-local.yaml` makes it this machine's baseline
|
|
7
|
+
for its harness. Every new launch of that harness that names no
|
|
8
|
+
configuration runs its executable, args, env and `yolo`: a soul's own
|
|
9
|
+
`launch:`, an inline `souls.launch` preference, `--harness`, the host
|
|
10
|
+
default. For example, every claude instance on a machine runs with its
|
|
11
|
+
`CLAUDE_CONFIG_DIR`:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
launch-configs:
|
|
15
|
+
mine:
|
|
16
|
+
harness: claude
|
|
17
|
+
default: true
|
|
18
|
+
env: { CLAUDE_CONFIG_DIR: /home/me/.claude-personal }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The model still comes from whatever chose the harness; the default's
|
|
22
|
+
`model` is only the last fallback. A named configuration runs as declared,
|
|
23
|
+
and `--launch-config none` asks for the bare harness. One default per
|
|
24
|
+
harness (a second is refused, naming both). `oats launch-config list`,
|
|
25
|
+
`spawn --preview`, `launch-config preview`, `instance.json` and
|
|
26
|
+
`oats inspect --home` say when a launch came from the default
|
|
27
|
+
(`launchConfigDefault`), yolo included. Existing instances keep their
|
|
28
|
+
launch until `--reselect-launch` or a respawn, and meanwhile show the
|
|
29
|
+
`launch-changed` readiness warning. Feature
|
|
30
|
+
`launch-config-default`
|
|
31
|
+
([configuration.md](../configuration.md#the-harness-default)).
|
|
32
|
+
**Declaring a default needs OATS 0.32+ for every kernel that reads this
|
|
33
|
+
deployment:** OATS 0.31 and older refuse the whole `oats-local.yaml`
|
|
34
|
+
(`E_WORKSPACE_SCHEMA`). A routed `launch-config set --server` with
|
|
35
|
+
`default: true` to an older host is refused (`E_REMOTE_INCOMPATIBLE`)
|
|
36
|
+
before anything is sent.
|
|
37
|
+
|
|
38
|
+
- **Every instance a server reports is first-class in the Desktop, whoever
|
|
39
|
+
spawned it.** A remote row opens its terminal, starts, restarts, stops and
|
|
40
|
+
retires, and shows its readiness, activity, Git and diffs, just like a local
|
|
41
|
+
one; only its pull request is not read here, since the forge reads this
|
|
42
|
+
computer's clones. Stop and Remove keep their confirmations: the plan and
|
|
43
|
+
its guarded apply run on the instance's machine. Every command goes to the
|
|
44
|
+
server by the instance's home (`--server <id> --home <path>`), never by a
|
|
45
|
+
bare name, so two instances with the same name stay apart. While a read is
|
|
46
|
+
in flight, the view says "Reading from <server>…". When the server refuses,
|
|
47
|
+
it shows the server's code and message under a headline that usually names
|
|
48
|
+
the server; it never falls back to a local read. A remote stop or remove
|
|
49
|
+
that loses its link reads as an unknown outcome, never a failure, and the
|
|
50
|
+
server's roster is read again at once
|
|
51
|
+
([desktop.md](../desktop.md#instances-on-servers)).
|
|
52
|
+
- **A row that can't be opened says why, on the row.** The roster shows a short
|
|
53
|
+
reason where it said "state unknown": Herdr no longer supported, gone from
|
|
54
|
+
<server>, not reachable on <server>, or <server> not reached. The full
|
|
55
|
+
sentence stays in the row's tooltip and its actions menu. For an instance a
|
|
56
|
+
server no longer lists, it names the command that removes it from this
|
|
57
|
+
computer (`oats server forget <server> --instance <name>`).
|
|
58
|
+
- **This computer's OATS 0.31 decides which server rows can be used.** The
|
|
59
|
+
Desktop accepts OATS CLIs up to 0.32.x. With a local OATS before 0.31, which
|
|
60
|
+
does not report that fact, an instance spawned from this computer stays
|
|
61
|
+
usable through its saved route, as before; its routed reads and plans ask you
|
|
62
|
+
to update OATS here.
|
|
63
|
+
- **A team card lists its members wherever they run.** On a local workspace's
|
|
64
|
+
Teams page, each team with a provider id lists every instance in it: this
|
|
65
|
+
workspace's, and every registered server's whose messaging identity is in
|
|
66
|
+
that team. They are grouped by machine ("This computer" first), with each
|
|
67
|
+
one's state in words. A member's name shows its roster row, where every
|
|
68
|
+
action lives; **Terminal** opens a running member there. A server whose
|
|
69
|
+
last roster read failed keeps its last-known members under "<server> · not
|
|
70
|
+
reached"; servers with nothing to show are named under the page head. The
|
|
71
|
+
card's count reads "N members · M on other machines". The Desktop reads
|
|
72
|
+
this from what it already holds and runs nothing new
|
|
73
|
+
([desktop-teams.md](../../packages/desktop/docs/desktop-teams.md#team-members)).
|
|
74
|
+
- **The spawn dialog asks "Where to run" up front** (it was "Run on", under
|
|
75
|
+
Developer settings). "This computer" comes first, then each registered
|
|
76
|
+
server. A server is disabled, with the reason, when it wasn't reached or
|
|
77
|
+
the roster knows only an older registration of it. With a server chosen,
|
|
78
|
+
the dialog says where the instance will run, and the relationship picker
|
|
79
|
+
lists only that server's instances: a relation never crosses machines. When
|
|
80
|
+
the server refuses the spawn (a soul it doesn't offer, for example), the
|
|
81
|
+
dialog says so in the server's own words.
|
|
82
|
+
- **The Desktop keeps and sets a harness's default launch configuration.**
|
|
83
|
+
Editing a configuration marked `default: true` saves it with its default
|
|
84
|
+
intact, and with an OATS that has `launch-config-default` the editor offers
|
|
85
|
+
"Default for <harness> on this machine" for this computer's own
|
|
86
|
+
configurations. A remote host's default survives editing too, but is set on
|
|
87
|
+
that host. A second default for the same harness is refused in OATS's own
|
|
88
|
+
words; unset the old one first.
|
|
89
|
+
|
|
90
|
+
- **Faster read verbs, and `--max-age <s>` to reuse recent remote heads**
|
|
91
|
+
(feature `observe-max-age`). `status`, `workspace status`, `souls`,
|
|
92
|
+
`capabilities` and `inspect` start far fewer git processes, cache what
|
|
93
|
+
they parse by commit, and start the members' remote reads together with the
|
|
94
|
+
host's. With `--max-age` they reuse a head observed in the last `<s>`
|
|
95
|
+
seconds and report `observation {observedAt, reused, localRevision}`
|
|
96
|
+
(`localRevision` changes whenever the local configuration read for the
|
|
97
|
+
answer does). See
|
|
98
|
+
[Observation reuse](../desktop-cli-api.md#observation-reuse-feature-observe-max-age-oats-0311).
|
|
99
|
+
|
|
100
|
+
## Fixed
|
|
101
|
+
|
|
102
|
+
- **A remote spawn with an empty opening instruction starts it waiting**, as
|
|
103
|
+
the dialog says. It was refused ("--task needs a value").
|
|
104
|
+
- **Focus stays put on the Workspace's Teams, Capabilities and Setup pages.**
|
|
105
|
+
Every roster poll rebuilt the page, since the workspace status it compared
|
|
106
|
+
carries a fresh observation time each time, so keyboard focus (and the
|
|
107
|
+
caret in the add-team form) fell back to the page every few seconds.
|
|
108
|
+
- **A click on a Desktop instance's actions menu does what it says.** With the
|
|
109
|
+
pointer over the open menu, the row no longer counted as hovered, so its
|
|
110
|
+
tools, the menu included, went hidden and a click on an item did nothing;
|
|
111
|
+
the keyboard still worked.
|
|
112
|
+
- **The Stop and Remove confirmations show only their own choices.** Stop
|
|
113
|
+
showed Remove's "delete the worktree" and "delete the local branch" options
|
|
114
|
+
(enabled, and ignored), and Remove showed Stop's "include recorded children".
|
|
115
|
+
|
|
116
|
+
- **A remote read that times out no longer leaves its ssh or https helper
|
|
117
|
+
running.** The kernel killed git but not the ssh or `git-remote-https` it
|
|
118
|
+
had started, which stayed connected to an unreachable host until its own
|
|
119
|
+
connection gave up. Git now runs as its own process group, and a timeout
|
|
120
|
+
(or the end of the command) kills the whole group.
|
|
121
|
+
|
|
122
|
+
- **A capability command whose provider dies of a signal exits as the shell
|
|
123
|
+
reports it** (128 + the signal number, 130 for a Ctrl-C), not 1.
|
|
124
|
+
|
|
125
|
+
- **`oats --help` piped into another program is no longer cut off at 8 KB.**
|
|
126
|
+
The top-level usage (about 20 KB) now arrives whole, including the
|
|
127
|
+
"Observation reuse" section; exit codes are unchanged.
|
|
128
|
+
|
|
129
|
+
## Removed
|
|
130
|
+
|
|
131
|
+
- **`oats-claude-config` is no longer read.** The one-line file that named
|
|
132
|
+
the claude binary for every deployment below it is replaced by the claude
|
|
133
|
+
default launch configuration. A new claude launch with one in reach is
|
|
134
|
+
refused with `E_CLAUDE_CONFIG_REMOVED`, naming the file and the
|
|
135
|
+
configuration to declare instead (`harness: claude`, `executable: <the
|
|
136
|
+
name it held>`, `default: true`); then delete the file. Instances already
|
|
137
|
+
launched with it keep their recorded executable.
|
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
|