@awebai/oats 0.31.0 → 0.33.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/README.md +1 -1
- package/bin/oats.mjs +162 -42
- package/docs/capabilities.md +5 -0
- package/docs/configuration.md +65 -8
- package/docs/design/2026-09-23-workspace-module-contracts.md +3 -2
- package/docs/desktop-cli-api.md +167 -18
- package/docs/desktop.md +16 -0
- package/docs/first-team.md +20 -2
- package/docs/implementation.md +121 -1
- package/docs/integrations.md +1 -1
- package/docs/oats-local.schema.json +2 -1
- package/docs/official-catalog.md +2 -2
- package/docs/packages.md +2 -2
- package/docs/plans/0.30-close-out.md +7 -4
- package/docs/release-notes/v0.32.0.md +137 -0
- package/docs/release-notes/v0.33.0.md +174 -0
- package/docs/souls-and-instances.md +64 -0
- package/docs/workspaces.md +1 -1
- package/lib/automations.mjs +5 -1
- package/lib/core.mjs +164 -46
- package/lib/harness-trust.mjs +139 -0
- package/lib/instance-inspect.mjs +18 -3
- 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/process-group.mjs +54 -0
- package/lib/remote.mjs +1363 -115
- package/lib/resolve.mjs +23 -7
- package/lib/servers.mjs +7 -0
- package/lib/workspace.mjs +163 -56
- package/package-catalog.json +2 -2
- package/package.json +1 -1
|
@@ -60,7 +60,8 @@
|
|
|
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
|
},
|
package/docs/official-catalog.md
CHANGED
|
@@ -11,8 +11,8 @@ 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.
|
|
15
|
-
| `oats.engineering` | `v1.
|
|
14
|
+
| `oats.aweb` | `v1.17.5` | `oats.aweb` (messaging) | |
|
|
15
|
+
| `oats.engineering` | `v1.4.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) | |
|
|
18
18
|
| `oats.linear` | `v1.0.1` | `oats.linear` (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.5
|
|
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.5 # 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,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.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# OATS 0.33.0
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
- **`oats spawn <soul> --preview --max-age <seconds>`** (feature
|
|
6
|
+
`spawn-preview-max-age`). A preview reuses member heads this machine
|
|
7
|
+
observed at most that many seconds ago, as the read verbs do since 0.32.0,
|
|
8
|
+
so a warm preview asks no remote. With the flag the preview's JSON carries
|
|
9
|
+
the read verbs' `observation {observedAt, reused, localRevision}` block;
|
|
10
|
+
without it the preview is unchanged. The decision still covers the heads
|
|
11
|
+
the preview used: an apply with `--expect-decision` observes live and
|
|
12
|
+
refuses `E_DECISION_STALE` when a reused head has moved, and a re-preview
|
|
13
|
+
then shows the new head. The apply refuses `--max-age` (`E_BAD_ARGS`), and
|
|
14
|
+
the refusal message of every form that refuses the flag now lists
|
|
15
|
+
`spawn --preview` among the read forms.
|
|
16
|
+
|
|
17
|
+
## Changed
|
|
18
|
+
|
|
19
|
+
- **OATS fetches only what it reads from a remote** (awebai/oats#384). A
|
|
20
|
+
commit comes with its trees and its files up to 64 KiB; a larger file is
|
|
21
|
+
fetched when a read or a module needs it. The first spawn from a large
|
|
22
|
+
workspace host downloads megabytes instead of its whole tree (tsm: 13 MB in
|
|
23
|
+
3 s, instead of 2.4 GB in 6.5 minutes). A server that cannot serve partial
|
|
24
|
+
fetches (a default `git daemon`, an old self-hosted git), or a local git
|
|
25
|
+
older than 2.45, gets whole trees as before, with one `oats: warning` per
|
|
26
|
+
repository.
|
|
27
|
+
|
|
28
|
+
- **Installing a soul's modules reads their files without a process per
|
|
29
|
+
file.** `oats spawn` copies each module's files from the remote cache
|
|
30
|
+
through the command's one `git cat-file --batch` reader per repository,
|
|
31
|
+
as the reads and discovery already do, instead of one `git cat-file blob`
|
|
32
|
+
per file. A Desktop developer spawn on this deployment ran 50 to 59
|
|
33
|
+
processes instead of 81 (34 per-file reads became 3 to 7 readers).
|
|
34
|
+
Budgets, digests, partial caches and errors are unchanged.
|
|
35
|
+
|
|
36
|
+
- **oats.engineering 1.4.0** (catalog and workspace pin, and the bundled
|
|
37
|
+
mirrors): the developer's loop REQUIRES loading `/understand-the-spec` and
|
|
38
|
+
`/execution-strategy` before implementation.
|
|
39
|
+
|
|
40
|
+
- **oats.aweb 1.17.5** (catalog and workspace pin, and the bundled mirror;
|
|
41
|
+
1.17.4 and 1.17.5): a faster spawn and retire. The spawn hook mints the identity with one
|
|
42
|
+
`aw init --join-from` instead of `aw team invite` + `aw team join` +
|
|
43
|
+
`aw init`, so the invite token no longer appears in any argv. The aw floor
|
|
44
|
+
check stops at `aw version`'s version line instead of waiting on its GitHub
|
|
45
|
+
update check. Retire deletes by workspace id, with `aw wake deregister`
|
|
46
|
+
running at the same time. Measured spawn hook: 7.72 s to 4.62 s mean
|
|
47
|
+
(awebai/oats-aweb#31). Hook output, compensation and the aw floor (1.36.13)
|
|
48
|
+
are unchanged. The mint now has one 120 s budget where three calls had
|
|
49
|
+
about 210 s; raise `OATS_AWEB_JOIN_TIMEOUT_MS` on a slow network. Since the
|
|
50
|
+
hook no longer holds the invite token, 1.17.5 always records the alias it
|
|
51
|
+
requested and warns, without quoting the reply, when aw reports another
|
|
52
|
+
(awebai/oats-aweb#32).
|
|
53
|
+
|
|
54
|
+
- **Pressing Spawn (Cmd-Enter) closes the spawn dialog at once.** The spawn
|
|
55
|
+
completes in the background: a pending row ("Spawning…") appears in the
|
|
56
|
+
sidebar roster where the instance will stand, and is replaced in place by
|
|
57
|
+
the real row once the roster reports it. Outcomes arrive as notifications:
|
|
58
|
+
"<name> spawned" with Open, what didn't finish for a partial or incomplete
|
|
59
|
+
spawn (with View schedules for a wake that wasn't saved), and, for a
|
|
60
|
+
refused or failed spawn, the reason with **Reopen spawn**, which restores
|
|
61
|
+
the whole draft. An unknown outcome keeps the row, reading "Outcome
|
|
62
|
+
unknown", with Check result. Prepare reuses the dialog's fresh preview
|
|
63
|
+
instead of reading it again, and the kernel's `--expect-decision` still
|
|
64
|
+
refuses a decision that changed. Dialog previews use `--max-age 60` when the
|
|
65
|
+
CLI supports it (feature `spawn-preview-max-age`)
|
|
66
|
+
([desktop-spawn-preview.md](../../packages/desktop/docs/desktop-spawn-preview.md#background-spawn-the-dialog-closes-on-spawn)).
|
|
67
|
+
|
|
68
|
+
- **The spawn dialog never waits on the preview while you edit.** The last
|
|
69
|
+
preview stays visible while it updates, Spawn stays pressable, and answers
|
|
70
|
+
are reused for 60 s (#378).
|
|
71
|
+
|
|
72
|
+
- **Core capabilities and Capabilities read as one system** on the soul page,
|
|
73
|
+
the capability page, the instance inspector and the spawn preview. Every
|
|
74
|
+
core row says why it's there, including a slot the soul empties or that has
|
|
75
|
+
no default. The capability page opened from a soul gains a "Why" row.
|
|
76
|
+
Workspace › Capabilities lists Repo owned before Packages (#377).
|
|
77
|
+
|
|
78
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.34.0`**, so it runs against
|
|
79
|
+
this release's kernel. Install the CLI and the Desktop 0.33.0 together: the
|
|
80
|
+
Desktop 0.32.x refuses a 0.33 CLI.
|
|
81
|
+
|
|
82
|
+
- **Unattended claude and codex launches** (awebai/oats#341,
|
|
83
|
+
[souls-and-instances.md](../souls-and-instances.md#unattended-launches-folder-trust)).
|
|
84
|
+
- Every codex launch passes `-c check_for_update_on_startup=false`, so
|
|
85
|
+
Codex's update prompt no longer blocks it.
|
|
86
|
+
- Codex does not apply a trusted parent to the folders below it. When
|
|
87
|
+
`~/.codex/config.toml` trusts the deployment or an ancestor, a codex
|
|
88
|
+
launch now trusts its new home for that session; the file is never
|
|
89
|
+
written.
|
|
90
|
+
- A claude or codex spawn whose home is not covered by the operator's
|
|
91
|
+
one-time trust of the deployment warns `the <harness> session will stop
|
|
92
|
+
at its folder-trust prompt: trust <deployment> once (…)`. `oats readiness`
|
|
93
|
+
reports the same as a `harness-trust` item in `checks.configured` (not
|
|
94
|
+
required).
|
|
95
|
+
|
|
96
|
+
- **The Desktop names an OATS cache problem, and keeps the roster through an
|
|
97
|
+
unreadable remote.** When the kernel can't read a remote
|
|
98
|
+
(`E_REMOTE_UNREADABLE`), the sidebar roster keeps the instances it last
|
|
99
|
+
observed instead of going empty. A cache problem (`details.reason: "cache"`)
|
|
100
|
+
reads "Couldn't refresh instances · OATS cache problem · observed <age>",
|
|
101
|
+
with the kernel's message in full (it names the lock file or the process
|
|
102
|
+
holding it, and the remedy) and Retry. With nothing observed yet, the failed
|
|
103
|
+
state shows that message. A network failure or a timeout reads "Couldn't
|
|
104
|
+
reach <host> · showing what was read <age>", with the raw code behind
|
|
105
|
+
Details. Of the kernel's structured `details`, only the reason and the
|
|
106
|
+
host of the remote cross to the window; the kernel's message is shown as
|
|
107
|
+
given, with the file or process it names
|
|
108
|
+
([desktop-cli-api.md](../desktop-cli-api.md#workspace)).
|
|
109
|
+
|
|
110
|
+
## Fixed
|
|
111
|
+
|
|
112
|
+
- **A background spawn never fails silently, and a soul can't be spawned
|
|
113
|
+
twice at once** (awebai/oats#383). While a spawn of a soul is in flight,
|
|
114
|
+
opening Spawn for it shows the press disabled with "A spawn of <soul> is in
|
|
115
|
+
progress" and a link to its pending row; it re-enables when the spawn
|
|
116
|
+
settles. After a window reload, a spawn whose outcome wasn't known yet comes
|
|
117
|
+
back as a pending row and its outcome is reported, a failure included, even
|
|
118
|
+
when it settled while another workspace was on screen; Reopen spawn then
|
|
119
|
+
restores the soul and the name. Quitting the Desktop mid-spawn
|
|
120
|
+
still loses an unreported failure
|
|
121
|
+
([desktop-spawn-preview.md](../../packages/desktop/docs/desktop-spawn-preview.md#background-spawn-the-dialog-closes-on-spawn)).
|
|
122
|
+
|
|
123
|
+
- **A git that the system kills is no longer reported as a timeout**
|
|
124
|
+
(awebai/oats#387). A remote read reports `timeout` only when OATS's own
|
|
125
|
+
timer stopped git. When something else kills git (an out-of-memory kill
|
|
126
|
+
during a large fetch, for example), the error now reads `cannot read remote
|
|
127
|
+
<url> (killed): git was killed (signal SIGKILL)`, with `reason: killed` and
|
|
128
|
+
`details.signal`. Before, it said `(timeout)`.
|
|
129
|
+
|
|
130
|
+
- **Codex sessions see their instance** (awebai/oats#342). Codex can run tool
|
|
131
|
+
commands under its shared app-server daemon, without the session's
|
|
132
|
+
environment, so `oats aweb roster` inside Codex asked for `--soul`.
|
|
133
|
+
- A codex launch now sets the instance environment for tool commands with
|
|
134
|
+
`shell_environment_policy.set`: `OATS_INSTANCE`/`OATS_INSTANCE_HOME`, the
|
|
135
|
+
capabilities' launch environment (`AWEB_IDENTITY_HOME`, `AWEB_DELIVERY`)
|
|
136
|
+
and the launch configuration's literals.
|
|
137
|
+
- It also sets `PATH` with the home's `.oats/bin` first. The user's login
|
|
138
|
+
shell may put its profile's entries ahead of it.
|
|
139
|
+
- A capability command with no home in its environment finds the home from
|
|
140
|
+
its working directory.
|
|
141
|
+
|
|
142
|
+
- The partial-fetch tests now pass on a host whose git is older than 2.45
|
|
143
|
+
(awebai/oats#389). The kernel already fell back to whole trees there; the
|
|
144
|
+
tests now read the kernel's own git probe:
|
|
145
|
+
- on an older git they assert that fallback (whole trees, one warning per
|
|
146
|
+
repository);
|
|
147
|
+
- they skip the partial-cache mechanics that cannot happen there, and name
|
|
148
|
+
why;
|
|
149
|
+
- under CI, an older git fails those tests instead.
|
|
150
|
+
|
|
151
|
+
- **A killed fetch no longer leaves a remote cache unusable** (awebai/oats#386).
|
|
152
|
+
OATS ends git with SIGTERM first, so git removes its own lock files, and
|
|
153
|
+
SIGKILL only after a grace. A lock left by a git killed earlier is removed
|
|
154
|
+
once it is older than the longest fetch, with an `oats: warning` naming it.
|
|
155
|
+
A younger one is never removed: the error names the file and says it is
|
|
156
|
+
safe to remove once no oats or git process is running.
|
|
157
|
+
|
|
158
|
+
- **Ending git leaves no survivor in its process group** (awebai/oats#386). After
|
|
159
|
+
git's own output closes, OATS keeps checking git's process group until the grace
|
|
160
|
+
ends: a member that ignores SIGTERM and holds no pipe is still killed, and a
|
|
161
|
+
group seen empty is never signalled again (its id may already belong to another
|
|
162
|
+
process).
|
|
163
|
+
|
|
164
|
+
- **Processes making the first fetch from one remote at once all succeed**
|
|
165
|
+
(two spawns, two Desktop previews). Every write to a cache takes a
|
|
166
|
+
per-cache lock: a live holder is waited for, a dead one's lock is reclaimed,
|
|
167
|
+
and the cache repo appears whole. A failure to write the local cache is now
|
|
168
|
+
reported as `E_REMOTE_UNREADABLE` with the new reason `cache`, never as
|
|
169
|
+
`network`.
|
|
170
|
+
|
|
171
|
+
- Fetching a commit into the remote cache may take 10 minutes instead of 30 s, so
|
|
172
|
+
the first spawn from a large workspace host no longer fails with `cannot read
|
|
173
|
+
remote … (timeout)`; the timeout error names the fetch and the elapsed time
|
|
174
|
+
(awebai/oats#362).
|
|
@@ -204,6 +204,70 @@ the harness's own precedence. The `CLAUDE.md → AGENTS.md` and
|
|
|
204
204
|
`.claude/skills → ../.agents/skills` aliases are kept. OATS composes
|
|
205
205
|
instructions and pins model/provider settings; it excludes nothing.
|
|
206
206
|
|
|
207
|
+
### Unattended launches: folder trust
|
|
208
|
+
|
|
209
|
+
Claude Code and Codex ask before they work in a folder they have not seen, and
|
|
210
|
+
every instance home is new. A launch stopped at that prompt waits for a human,
|
|
211
|
+
so the operator trusts the **deployment directory** (where `oats-local.yaml`
|
|
212
|
+
is) once per harness. OATS only reads the harnesses' configuration; it never
|
|
213
|
+
writes it.
|
|
214
|
+
|
|
215
|
+
- **Claude Code** looks for an accepted entry for its folder or an ancestor, up
|
|
216
|
+
to a git root. Instance homes are not inside a git repository, so one entry
|
|
217
|
+
for the deployment covers every home under it. To add it, run `claude` in the
|
|
218
|
+
deployment once and accept the prompt. That records
|
|
219
|
+
`projects["<deployment>"].hasTrustDialogAccepted` in `~/.claude.json`
|
|
220
|
+
(`$CLAUDE_CONFIG_DIR/.claude.json` when that is set).
|
|
221
|
+
- **Codex** applies only an exact entry: a trusted parent does not cover the
|
|
222
|
+
folders below it. To give the operator's consent, run `codex` in the
|
|
223
|
+
deployment once and choose "Trust and continue". That records
|
|
224
|
+
`[projects."<deployment>"] trust_level = "trusted"` in
|
|
225
|
+
`~/.codex/config.toml` (`$CODEX_HOME/config.toml`). With that entry, or one
|
|
226
|
+
for an ancestor of the deployment, each codex launch trusts its own new home
|
|
227
|
+
for that session (`-c 'projects={"<home>"={trust_level="trusted"}}'`) and
|
|
228
|
+
leaves the config file unchanged. A yolo launch always does this. The plan
|
|
229
|
+
re-reads the entry at every start.
|
|
230
|
+
- Every codex launch also passes `-c check_for_update_on_startup=false`, so
|
|
231
|
+
Codex's "Update available" choice cannot block it.
|
|
232
|
+
|
|
233
|
+
When a claude or codex home is not covered, the spawn says so (text and
|
|
234
|
+
`--json` `warnings`): `the <harness> session will stop at its folder-trust
|
|
235
|
+
prompt: trust <deployment> once (<the step>)`. `oats readiness` reports the
|
|
236
|
+
same in `checks.configured` (code `harness-trust`, not required).
|
|
237
|
+
|
|
238
|
+
### Codex tool commands and the instance environment
|
|
239
|
+
|
|
240
|
+
Codex can run tool commands under its shared app-server daemon rather than as
|
|
241
|
+
children of the session OATS launched, and then they do not inherit the
|
|
242
|
+
session's environment. Codex (0.157.1) runs a session that has `-c` overrides
|
|
243
|
+
embedded, without the daemon, and every kernel codex launch has them, so an
|
|
244
|
+
OATS codex session does not appear in `codex agents`. So that the environment
|
|
245
|
+
does not depend on this, a codex launch also sets it for tool
|
|
246
|
+
commands explicitly with `-c shell_environment_policy.set.<NAME>="<value>"`:
|
|
247
|
+
|
|
248
|
+
- the instance: `OATS_INSTANCE`, `OATS_INSTANCE_HOME`, `PI_AGENT_INSTANCE`,
|
|
249
|
+
`PI_AGENT_HOME`;
|
|
250
|
+
- every capability's launch environment (for example the messaging
|
|
251
|
+
provider's identity home and delivery mode);
|
|
252
|
+
- the launch configuration's literal values. A reference's value never goes
|
|
253
|
+
on a command line.
|
|
254
|
+
|
|
255
|
+
`PATH` comes from the launching shell, with the home's `.oats/bin` first, so
|
|
256
|
+
only the execution passes it; the persisted command does not carry it. Codex
|
|
257
|
+
runs tool commands through the user's login shell, and a profile that prepends
|
|
258
|
+
directories puts those entries ahead of `.oats/bin`. A second `oats` in such a
|
|
259
|
+
directory is found first.
|
|
260
|
+
|
|
261
|
+
A capability command (`oats <namespace> …`) run with none of
|
|
262
|
+
`OATS_INSTANCE_HOME`, `PI_AGENT_HOME` or `OATS_HOME` set finds its instance
|
|
263
|
+
from the working directory. It uses the nearest enclosing directory laid out as
|
|
264
|
+
`<agents-root>/<soul>/instances/<name>` whose `instance.json` records that
|
|
265
|
+
name, and validates it like a home named by the environment. The walk uses the
|
|
266
|
+
directory as the shell names it (`$PWD`). That matters for an attached
|
|
267
|
+
instance, whose `work/` links into its owner's tree: below it, the physical
|
|
268
|
+
path is the owner's. A process that has no `$PWD` there would act as the
|
|
269
|
+
owner, so an attached instance runs capability commands from its home.
|
|
270
|
+
|
|
207
271
|
## Lifecycle
|
|
208
272
|
|
|
209
273
|
### Spawn
|
package/docs/workspaces.md
CHANGED
|
@@ -183,7 +183,7 @@ as `confirmed` or the reason it is not:
|
|
|
183
183
|
| `not-listed` | the workspace does not list the repo |
|
|
184
184
|
| `no-backlink` | no (or invalid) `oats-membership.yaml` at the member's default branch |
|
|
185
185
|
| `backlink-elsewhere` | the member names a different workspace (a case-only difference is flagged: repo paths are case-sensitive identities) |
|
|
186
|
-
| `cannot-read` | the operator cannot read the member (auth / not-found / network / timeout) |
|
|
186
|
+
| `cannot-read` | the operator cannot read the member (auth / not-found / network / timeout / killed: the system killed git, e.g. out of memory), or this machine's remote cache could not be written (cache) |
|
|
187
187
|
|
|
188
188
|
An unconfirmed member contributes nothing but its row: its souls are invisible,
|
|
189
189
|
its capabilities unresolvable (`E_NOT_A_MEMBER` / `E_MEMBERSHIP_UNCONFIRMED`).
|
package/lib/automations.mjs
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
import { spawnSync } from "node:child_process";
|
|
35
35
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
36
36
|
import { dirname, join } from "node:path";
|
|
37
|
+
import { recordLocalInput } from "./local-inputs.mjs";
|
|
37
38
|
import YAML from "yaml";
|
|
38
39
|
import { parseConfigData } from "./config-data.mjs";
|
|
39
40
|
|
|
@@ -192,8 +193,11 @@ export function soulOriginOf(index, soul) {
|
|
|
192
193
|
|
|
193
194
|
export const snapshotPath = (dep) => join(dep, ".agents", "automations", "snapshot.json");
|
|
194
195
|
export function readSnapshot(dep) {
|
|
196
|
+
let text;
|
|
197
|
+
try { text = readFileSync(snapshotPath(dep), "utf8"); } catch { recordLocalInput(snapshotPath(dep), null); return null; }
|
|
198
|
+
recordLocalInput(snapshotPath(dep), text);
|
|
195
199
|
try {
|
|
196
|
-
const doc = JSON.parse(
|
|
200
|
+
const doc = JSON.parse(text);
|
|
197
201
|
return isObject(doc) && KIND_NAMES.every((k) => Array.isArray(doc[`${k}s`])) ? doc : null;
|
|
198
202
|
} catch { return null; }
|
|
199
203
|
}
|