@awebai/oats 0.40.1 → 0.41.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 +45 -18
- package/docs/capabilities.md +44 -3
- package/docs/desktop-cli-api.md +147 -23
- package/docs/desktop.md +5 -4
- package/docs/execution-targets.md +342 -38
- package/docs/implementation.md +85 -11
- package/docs/oats-local.schema.json +2 -2
- package/docs/official-catalog.md +1 -1
- package/docs/packages.md +5 -5
- package/docs/release-lane.md +8 -4
- package/docs/release-notes/v0.40.2.md +120 -0
- package/docs/release-notes/v0.41.0.md +238 -0
- package/docs/schedules.md +40 -11
- package/docs/servers.md +7 -2
- package/docs/souls-and-instances.md +32 -6
- package/docs/workspaces.md +1 -1
- package/lib/core.mjs +579 -177
- package/lib/dir-lock.mjs +7 -4
- package/lib/instance-events.mjs +130 -46
- package/lib/instance-git.mjs +113 -4
- package/lib/instance-lifecycle.mjs +3 -2
- package/lib/packages.mjs +1 -1
- package/lib/resolve.mjs +1 -1
- package/lib/schedule-command-child.mjs +39 -9
- package/lib/schedule.mjs +57 -28
- package/lib/servers.mjs +23 -9
- package/lib/session-input.mjs +42 -47
- package/package-catalog.json +1 -1
- package/package.json +1 -1
- package/skills/oats-getting-started/SKILL.md +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# OATS 0.40.2
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
- **Unconfirmed spawn and operation failures carry structural evidence**
|
|
6
|
+
([#548](https://github.com/awebai/oats/issues/548)). Incomplete keyed spawns
|
|
7
|
+
and failed spawn compensation set `error.details.unconfirmed: true`; the
|
|
8
|
+
operation wrapper promotes a provider's literal true marker while retaining
|
|
9
|
+
its complete envelope. Completed compensation stays unmarked. Existing
|
|
10
|
+
text fallbacks and scheduler slot/reconcile behavior remain during this
|
|
11
|
+
additive migration; older copied providers are not upgraded by this change.
|
|
12
|
+
|
|
13
|
+
## Fixed
|
|
14
|
+
|
|
15
|
+
- **Schedule registry reads are lock-free and do not mutate state**
|
|
16
|
+
([#579](https://github.com/awebai/oats/issues/579)). Status and other readers
|
|
17
|
+
use coherent atomic-file snapshots, including during registry writes. Legacy
|
|
18
|
+
cap migration is projected in memory and persisted only by locked mutations,
|
|
19
|
+
with a one-slot restoration notice on stderr. Invalid stored schedule caps
|
|
20
|
+
now refuse instead of silently increasing capacity; explicit CLI cap options
|
|
21
|
+
can repair them. Old hand-set and implicit ones cannot be distinguished, nor
|
|
22
|
+
can current kernels identify a one reintroduced by an older writer sharing
|
|
23
|
+
the registry; stop mixed-version writes and explicitly set the desired cap.
|
|
24
|
+
Shared directory locks wait through transient owner publication/removal within
|
|
25
|
+
their retry deadline without stealing locks.
|
|
26
|
+
- **OATS Desktop: "Needs input" shows on a remote instance, and a withheld
|
|
27
|
+
note reads as the reason.** Desktop hid every remote claim: a remote roster
|
|
28
|
+
row carries no `runtimeState`, and Desktop read "not reported" as "not
|
|
29
|
+
running" (#582). An unreported state no longer hides the claim; a row
|
|
30
|
+
reported stopped, unreachable or unsupported, or one whose server did not
|
|
31
|
+
answer, still shows none. When a claim's note holds a link or text such as
|
|
32
|
+
`token: …`, Desktop does not show it. The row's description, the terminal
|
|
33
|
+
tab's title and the hover card now show the reason in words, such as "Asked
|
|
34
|
+
you a question", where they showed "[Detail withheld]" (#584). The activity
|
|
35
|
+
view still marks such a note as withheld, and `oats status` prints it in
|
|
36
|
+
full.
|
|
37
|
+
- **A remote instance's "needs input" reaches the roster**
|
|
38
|
+
([#582](https://github.com/awebai/oats/issues/582)). `oats server roster`
|
|
39
|
+
built a remote row from a fixed list of facts and dropped the host's
|
|
40
|
+
`waitingOnYou`. A row now carries it when, and only when, the host's kernel
|
|
41
|
+
reports it: a row from a host before 0.40.0, and a saved route the host did
|
|
42
|
+
not list, have no such key, so "not reported" stays distinct from `null`
|
|
43
|
+
("no claim"). The value passes the kernel's read rule again on this side;
|
|
44
|
+
anything that is not a claim is `null`. See
|
|
45
|
+
[the remote roster](../desktop-cli-api.md#the-remote-roster-oats-server-roster---json).
|
|
46
|
+
- **"Needs input" in a deployment addressed through a symlink**
|
|
47
|
+
([#583](https://github.com/awebai/oats/issues/583)). A home there has two
|
|
48
|
+
spellings: `oats status --dir <symlink>` addressed it lexically, while a
|
|
49
|
+
started or restarted session carries the real path. Event rows are keyed by
|
|
50
|
+
the home, so the session's claims never showed on status, a restart did not
|
|
51
|
+
void a claim written under the other spelling, and a clear through one
|
|
52
|
+
spelling found nothing. Rows are now stored and matched under the home's
|
|
53
|
+
real path, whichever spelling a writer or reader uses, and both spellings
|
|
54
|
+
use one workspace log, the deployment's own `.agents/events`. Answers keep
|
|
55
|
+
the spelling they were asked in: the `home` in `oats instance events --json`
|
|
56
|
+
(the top-level field and each row's) and in the `oats instance waiting` and
|
|
57
|
+
`oats instance attention` answers is the home as the caller addressed it.
|
|
58
|
+
Deployments not reached through a symlink see no change.
|
|
59
|
+
|
|
60
|
+
Rows an earlier kernel wrote under the lexical spelling are not rewritten
|
|
61
|
+
and not matched: after the upgrade they are foreign, counted in
|
|
62
|
+
`integrity.foreignRows`. In a symlinked deployment that means:
|
|
63
|
+
- a claim that was live under the lexical spelling (a session that was
|
|
64
|
+
spawned and never restarted) stops showing. It shows again only when it
|
|
65
|
+
is made anew: at the agent's next permission prompt, question or
|
|
66
|
+
`oats instance attention`. A restart starts a new session with no claim;
|
|
67
|
+
- a claim that stayed set because its restart was recorded under the real
|
|
68
|
+
path is gone, not stuck;
|
|
69
|
+
- the rows a started or restarted session wrote under the real path are
|
|
70
|
+
read now. They cannot show a stale claim: such a session wrote its clears
|
|
71
|
+
and its session boundaries under the real path too.
|
|
72
|
+
- **oats.core 2.4.1: the Claude Code emitter no longer speaks for another
|
|
73
|
+
instance** ([#584](https://github.com/awebai/oats/issues/584)). Its hook
|
|
74
|
+
script took the home from `$OATS_INSTANCE_HOME`. A Claude process that
|
|
75
|
+
loaded one home's `.claude/settings.json` while carrying another instance's
|
|
76
|
+
environment (a nested `claude -p`, a `claude -p` started with its working
|
|
77
|
+
directory in another home, a pane that inherited the variables) set and
|
|
78
|
+
cleared that other instance's claim, and its `Stop` and `SessionEnd` clears
|
|
79
|
+
could erase a real one. The launch hook now writes the home's real path
|
|
80
|
+
into each hook command, and the script acts only when `$OATS_INSTANCE_HOME`
|
|
81
|
+
names that home, through any spelling; otherwise it does nothing. Ships in
|
|
82
|
+
oats.framework 1.6.1 (compatibility unchanged; catalog and workspace pin
|
|
83
|
+
`oats-framework/v1.6.1`): a workspace's `oats.framework: v1.6.1` resolves
|
|
84
|
+
to that tag through the official catalog, and a workspace pinned to v1.6.0
|
|
85
|
+
keeps oats.core 2.4.0 until it moves the pin and syncs. A home picks it up
|
|
86
|
+
at its next spawn. See [capabilities](../capabilities.md).
|
|
87
|
+
- **A waiting row whose time is not a date is no claim**
|
|
88
|
+
([#584](https://github.com/awebai/oats/issues/584)). The reader validated a
|
|
89
|
+
stored claim's producer, reason and message but passed its time through.
|
|
90
|
+
And `oats help` lists `[--dir <d>]` for `oats instance waiting`.
|
|
91
|
+
- **Catchable scheduler-supervisor shutdown cleans up its owned child group**
|
|
92
|
+
([#580](https://github.com/awebai/oats/issues/580)). SIGINT, SIGTERM and SIGHUP
|
|
93
|
+
enter the existing bounded TERM/KILL cleanup once, preserving observed child
|
|
94
|
+
exit evidence and treating interrupted envelopes as unconfirmed. The group
|
|
95
|
+
is checked when its leader exits and never signalled again after it is seen
|
|
96
|
+
empty, reducing but not eliminating process-group ID reuse races. Escaped
|
|
97
|
+
sessions and unrecoverable supervisor deaths such as SIGKILL/OOM remain
|
|
98
|
+
outside that cleanup guarantee; no receipt means no proven child exit, so
|
|
99
|
+
the unknown attempt and host slot remain pending reconciliation.
|
|
100
|
+
- **Workspace schedule opt-outs and named trust accept 100-character IDs**
|
|
101
|
+
([#581](https://github.com/awebai/oats/issues/581)). Host configuration now
|
|
102
|
+
accepts the full schedule name limit in `schedules.disabled` and
|
|
103
|
+
`automations.trust`, so enabling/disabling and trusting schedules with
|
|
104
|
+
41–100-character IDs works through the CLI and workspace placement checks.
|
|
105
|
+
Trigger definitions and `triggers.disabled` retain their 40-character limit;
|
|
106
|
+
member syntax, allowed characters and uniqueness rules are unchanged.
|
|
107
|
+
|
|
108
|
+
## Changed
|
|
109
|
+
|
|
110
|
+
- **Session input restores single-paste, single-Enter terminal semantics**
|
|
111
|
+
([#562](https://github.com/awebai/oats/issues/562)). Corrects 0.39.4's
|
|
112
|
+
screen-derived extra Enters and `enter-not-taken` refusal after successful
|
|
113
|
+
terminal commands. Pre-Enter settling remains, with a shared monotonic
|
|
114
|
+
2-second observation budget; at most two read-only post-Enter looks share
|
|
115
|
+
1 second. Each probe and sleep is limited by its remaining budget.
|
|
116
|
+
`submitted: true` reports terminal-operation success; `verified` reports
|
|
117
|
+
display change only, and false never authorizes retry. Neither proves model
|
|
118
|
+
acceptance, pending draft state or absence of effects. Command errors and
|
|
119
|
+
authority checks are unchanged; busy-pane submission, exactly-once delivery
|
|
120
|
+
and generic-error retry uncertainty are not solved by this correction.
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# OATS 0.41.0
|
|
2
|
+
|
|
3
|
+
## Changed
|
|
4
|
+
|
|
5
|
+
- **The framework workspace has a separate maintainer soul.** The new
|
|
6
|
+
`oats-maintainer` soul is the OATS maintainer: direction, the roadmap,
|
|
7
|
+
architecture coherence, routing work to experts, review, merge and releases.
|
|
8
|
+
It composes `oats.maintainer` and carries the OATS-only `/oats-pr-review` and
|
|
9
|
+
`/git-tag-release` skills. `oats-expert` stays an expert, the generalist that
|
|
10
|
+
leads cross-area work, coordinates the domain experts and reports to
|
|
11
|
+
`oats-maintainer`. The other souls send direction, contract decisions and
|
|
12
|
+
merges to `oats-maintainer`, and read its knowledge node
|
|
13
|
+
(`oats/oats-maintainer`, which holds what the `oats-expert` node held).
|
|
14
|
+
- The `oats` knowledge store's main must declare the `oats-maintainer`
|
|
15
|
+
node before this reaches a deployment: souls that read or own it fail
|
|
16
|
+
their knowledge check with `E_CONFIG unresolved node:
|
|
17
|
+
oats/oats-maintainer` against a base without it.
|
|
18
|
+
- Operators: `oats sync`, then spawn `oats-maintainer` for maintainer work.
|
|
19
|
+
Running instances keep what they were spawned with.
|
|
20
|
+
- **Desktop terminals keep text readable (#602).** Desktop terminals now keep
|
|
21
|
+
text at a contrast of at least 4.5:1 with its background, so coloured text written for
|
|
22
|
+
another palette (a grey meant for a dark terminal, shown in White) stays
|
|
23
|
+
readable. The built-in palettes' colours on the terminal background are
|
|
24
|
+
unchanged; text on a coloured background may be drawn lighter or darker than
|
|
25
|
+
the program asked.
|
|
26
|
+
- **Desktop can match this computer's theme (#602).** A fourth theme choice,
|
|
27
|
+
**This computer**, shows Desktop's chrome and every terminal in the theme of
|
|
28
|
+
the computer that runs Desktop. On an Omarchy computer that is the current
|
|
29
|
+
Omarchy theme, followed when it changes, with no restart. Elsewhere it is
|
|
30
|
+
Dark or White by the system's appearance. Text colours taken from the host
|
|
31
|
+
are adjusted where needed to stay at least 4.5:1 on Desktop's surfaces; a
|
|
32
|
+
terminal's 16 colours are the host's own. If the host's theme cannot be
|
|
33
|
+
read, Desktop shows Dark or White and says so. A theme you chose does not
|
|
34
|
+
change: choose **Theme: This computer** in the command palette, or cycle to
|
|
35
|
+
it with the theme button.
|
|
36
|
+
- On an Omarchy computer Desktop reads the theme's colours file at start and
|
|
37
|
+
each time a window gets focus, whichever theme is chosen. It opens the file
|
|
38
|
+
without blocking and reads it only if it is a regular file, so a colours
|
|
39
|
+
file replaced by a named pipe, for example, cannot hang it (#612).
|
|
40
|
+
- On Linux, This computer is the default: a Desktop where no theme was ever
|
|
41
|
+
chosen starts in This computer (the computer's Omarchy theme, or the
|
|
42
|
+
system's light or dark appearance where there is none) instead of White.
|
|
43
|
+
That includes an existing install where the theme was never changed. If
|
|
44
|
+
you chose White at any time, it stays White. Choose another theme with the
|
|
45
|
+
theme button or the palette, and it stays. On other platforms the default
|
|
46
|
+
is still White.
|
|
47
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.42.0`**, so it runs against
|
|
48
|
+
this release's kernel; the Desktop 0.40.x refuses a 0.41 CLI.
|
|
49
|
+
- **Desktop's Remove has one choice, the worktree.** Remove no longer offers
|
|
50
|
+
to delete a branch: retirement leaves branches to the operator, and the
|
|
51
|
+
kernel refuses the flag. The dialog's one choice is **Also delete the
|
|
52
|
+
worktree**, and it says "Remove never deletes a branch." A home left by a
|
|
53
|
+
failed spawn that still owes its branch stays incomplete while that branch
|
|
54
|
+
exists, and Remove can no longer complete it: check the branch and delete it
|
|
55
|
+
with Git, then remove the home again.
|
|
56
|
+
- **New sessions run on an OATS-owned tmux server (#602).** The server is
|
|
57
|
+
`tmux -L oats`, so tools that restyle the default tmux server no longer
|
|
58
|
+
reach agent terminals. Running instances stay where they are. Plain
|
|
59
|
+
`tmux ls` and `tmux attach` no longer show new sessions: use `oats session
|
|
60
|
+
attach` or the `attach:` line that spawn prints. An existing instance moves
|
|
61
|
+
only when its window is recreated: a start after its window is gone or its
|
|
62
|
+
tmux server cannot be reached now opens on the OATS server instead of the
|
|
63
|
+
recorded socket. To move one deliberately, stop it, close its window and
|
|
64
|
+
start it: see
|
|
65
|
+
[Existing instances](../execution-targets.md#existing-instances).
|
|
66
|
+
- Operators: after upgrading, and after a reboot, start the `oats` server
|
|
67
|
+
from your own shell before anything else does: `tmux -L oats new-session
|
|
68
|
+
-d -s <session> -n hq` (`<session>` is the deployment's session name,
|
|
69
|
+
`oats-agents` by default). A tmux server keeps the environment of whoever
|
|
70
|
+
starts it: without this step the first Desktop-run or scheduled spawn
|
|
71
|
+
decides the ambient environment of later panes, and a spawn or start run
|
|
72
|
+
from inside an instance whose own tmux server cannot be read is refused
|
|
73
|
+
([the environment of the server and its panes](../execution-targets.md#the-servers-start-environment)).
|
|
74
|
+
- Nothing of an instance's environment reaches a server or a pane through
|
|
75
|
+
the calls that create an agents' session or window: an instance creates
|
|
76
|
+
the session with a reduced copy of the environment of the server its
|
|
77
|
+
home records (a variable whose value holds a `$`, a line break or a
|
|
78
|
+
character tmux prints encoded is not carried), and a window with only
|
|
79
|
+
`PATH` and the locale names tmux's client needs
|
|
80
|
+
(`LANG`, `LC_ALL`, `LC_CTYPE`), which tmux does not hand on. Every
|
|
81
|
+
creator's environment goes without the names the kernel sets
|
|
82
|
+
(`OATS_INSTANCE`, `OATS_ROOT`, `PI_AGENTS_ROOT`, launch references and
|
|
83
|
+
the others the docs list). A server that already runs keeps the
|
|
84
|
+
environment it has: OATS does not certify where that came from.
|
|
85
|
+
- On each agent window it creates, OATS resets `window-style`,
|
|
86
|
+
`window-active-style` and `cursor-colour` to `default`, and removes
|
|
87
|
+
`COLORFGBG` from the pane, so the pane shows the colours of the terminal
|
|
88
|
+
that views it. It does not reset `pane-colours`: a palette set in your
|
|
89
|
+
tmux configuration still reaches agent panes
|
|
90
|
+
([what OATS sets](../execution-targets.md#the-oats-tmux-server)).
|
|
91
|
+
- OATS sets nothing server-global on any server when it creates a session
|
|
92
|
+
or a window. `window-size latest` and `aggressive-resize on`, which it
|
|
93
|
+
used to set globally on the server it created its session on, are now set
|
|
94
|
+
on the windows it creates.
|
|
95
|
+
- The spawn result's `attach` is `tmux -S <socket> attach -t <session>` for
|
|
96
|
+
a launched spawn and `oats session attach --home <home>` for
|
|
97
|
+
`--no-launch`; a routed spawn (`--server`) prints `oats session attach
|
|
98
|
+
--server <id> --instance <instance>`. The JSON shapes are unchanged.
|
|
99
|
+
- The kernel no longer exports `launchEnvTmuxFlags` (it had no consumer).
|
|
100
|
+
|
|
101
|
+
## Upgrade
|
|
102
|
+
|
|
103
|
+
**Retirement no longer deletes branches.** What to do:
|
|
104
|
+
|
|
105
|
+
- **`oats retire --delete-branch` is refused** with `E_BAD_ARGS` before
|
|
106
|
+
anything happens: no child or session is stopped, no hook runs, nothing is
|
|
107
|
+
written. Retire without it; the branch is left in the repository. Inspect it
|
|
108
|
+
there and delete it with Git if it is no longer wanted. Scripts that pass the
|
|
109
|
+
flag must drop it. A self-retire an older OATS recorded with the flag is
|
|
110
|
+
completed with the branch and the worktree left, and its `retired` event says
|
|
111
|
+
so.
|
|
112
|
+
- **A home a failed spawn left that still owes its branch** stays incomplete
|
|
113
|
+
while that branch exists, with the item `the branch the failed spawn created
|
|
114
|
+
is left: OATS does not delete it. Inspect it and delete it with Git if it is
|
|
115
|
+
not wanted, then retry`. The item names no branch; the CLI prints it on the
|
|
116
|
+
next line (the receipt's `retention.recordedBranch`, or the `branch` in the
|
|
117
|
+
retained home's `instance.json` when the worktree step did not run). While
|
|
118
|
+
Git cannot show the branch gone, the item is `git branch <b>: could not
|
|
119
|
+
verify whether it still exists (…)`. Delete the branch with Git, then retire
|
|
120
|
+
again.
|
|
121
|
+
- **A worktree whose branch has no commit yet:** `oats retire
|
|
122
|
+
--discard-worktree`, or a retire that has work to copy, on such a worktree
|
|
123
|
+
refuses with `E_WORK_INSPECTION_FAILED` and removes nothing. Make a commit,
|
|
124
|
+
switch the worktree to a branch that has one, or remove the worktree with
|
|
125
|
+
Git; then retire again.
|
|
126
|
+
- **A worktree Git refuses to read without your own `safe.directory`**
|
|
127
|
+
(typically one owned by another user) now refuses a retire that has work to
|
|
128
|
+
preserve or that removes the worktree, with `E_WORK_INSPECTION_FAILED`. A
|
|
129
|
+
plain retire of such a worktree with nothing to preserve retains it under a
|
|
130
|
+
`detached-unknown` name, without its branch in the receipt. Retire as the
|
|
131
|
+
owner, or with Git's ownership check satisfied for that path.
|
|
132
|
+
|
|
133
|
+
## Fixed
|
|
134
|
+
|
|
135
|
+
- **A retire keeps what only the worktree has.** The recovery a retire writes
|
|
136
|
+
of a worktree is proven against the commit the worktree has checked out, and
|
|
137
|
+
is checked out at that commit on the worktree's own branch (detached, when
|
|
138
|
+
the branch's name is not one OATS carries). A clean worktree whose HEAD is
|
|
139
|
+
detached at a commit no ref of the repository reaches is preserved in a
|
|
140
|
+
recovery before `--discard-worktree` removes it, also when a retire hook made
|
|
141
|
+
that commit. HEAD is read again immediately before the worktree is removed:
|
|
142
|
+
if it moved since the retire's last inspection, the worktree is not removed
|
|
143
|
+
and the retire stops with `E_WORK_PRESERVATION_FAILED`; the home and the
|
|
144
|
+
worktree are kept, and so is any recovery the retire wrote.
|
|
145
|
+
- **A stopped instance's harness mark is readable in Solarized (#602).** In
|
|
146
|
+
the Desktop's roster, the harness mark of a stopped instance was drawn at
|
|
147
|
+
4.0:1 in Solarized, under the 4.5:1 the rest of the app keeps. It is now
|
|
148
|
+
4.7:1. The mark's block is slightly lighter in White and Solarized and
|
|
149
|
+
slightly darker in Dark.
|
|
150
|
+
- **Review threads reach an agent whose session is not on the default tmux
|
|
151
|
+
server (#602).** Sending a pull request's review threads to an agent's
|
|
152
|
+
terminal from the Desktop now uses the tmux socket the instance records, as
|
|
153
|
+
the Desktop's terminals do. Before, the paste always went to the default tmux
|
|
154
|
+
server: for an instance that records another socket it answered that the
|
|
155
|
+
paste failed, or it could go into a window of the same name on the default
|
|
156
|
+
server. An instance that records no socket is pasted to as before.
|
|
157
|
+
- **The Desktop names a retire refused at inspection.** When `oats retire`
|
|
158
|
+
refused a local instance with `E_WORK_INSPECTION_FAILED`, the Remove dialog
|
|
159
|
+
said "The lifecycle CLI is unavailable.", although the CLI had answered. It
|
|
160
|
+
now says: "Retirement was refused: the instance's home or work could not be
|
|
161
|
+
inspected. The home is kept; its session may already have been stopped, and
|
|
162
|
+
earlier retirement steps may have run." The outcome is still reported as
|
|
163
|
+
unknown, because the kernel can refuse after it stopped the children and the
|
|
164
|
+
session and ran the retire hooks. For an instance on a server the same
|
|
165
|
+
sentence replaces that headline, above the host's own message in Details.
|
|
166
|
+
For a local refusal the CLI's own message, which names the path and the
|
|
167
|
+
remedy, is in Details too (next entry). Part of
|
|
168
|
+
[#601](https://github.com/awebai/oats/issues/601).
|
|
169
|
+
- **The Desktop shows the kernel's own message when a local Stop or Remove
|
|
170
|
+
fails.** The dialog said only its own sentence for the error code; the
|
|
171
|
+
message the CLI answered with was dropped. It is now in the dialog's
|
|
172
|
+
**Details**, as the code and the message, under that sentence, as it already
|
|
173
|
+
was for an instance on a server. It is shown only when the installed CLI
|
|
174
|
+
answered with an error and the Desktop has a sentence for its code: a
|
|
175
|
+
timeout, an unreadable answer or an unknown code shows none. Closes
|
|
176
|
+
[#601](https://github.com/awebai/oats/issues/601).
|
|
177
|
+
- **The Desktop shows a kernel or host message as one plain line.** In the
|
|
178
|
+
Details of a refusal (Stop and Remove, the Git panel, Activity, readiness),
|
|
179
|
+
line feeds, the Unicode line and paragraph separators and tabs are collapsed
|
|
180
|
+
into single spaces. The C1 control characters, the bidirectional embedding,
|
|
181
|
+
override and isolate characters, the zero-width space, the word joiner, the
|
|
182
|
+
byte order mark and tag characters are each replaced by U+FFFD, the
|
|
183
|
+
replacement character. A message that holds a carriage return, DEL or
|
|
184
|
+
another ASCII control character other than tab and line feed (a message
|
|
185
|
+
with Windows line endings, for example), or that looks like a credential,
|
|
186
|
+
is withheld whole as `[Detail withheld]`, as before. A long message is cut
|
|
187
|
+
at 2048 characters. For readiness, the message is no longer in the "Couldn't
|
|
188
|
+
refresh" line's tooltip, which keeps the Desktop's sentence and the error
|
|
189
|
+
code; it stays in Details. In the Git panel a failed read's message, which
|
|
190
|
+
could span several lines, is now one line.
|
|
191
|
+
- **A tmux server started from the Desktop gets your environment, not the
|
|
192
|
+
Desktop's (#602).** A tmux server started from the Desktop (by a spawn or a
|
|
193
|
+
start, or by opening a terminal) no longer carries the Desktop's own runtime
|
|
194
|
+
settings. They made Electron programs started from an agent's pane run as
|
|
195
|
+
Node, and on the AppImage they pointed library and data paths into the
|
|
196
|
+
Desktop's mount, which exists only while that Desktop runs. Every program
|
|
197
|
+
the Desktop starts (the `oats` CLI, `tmux`, `gh`, the login shell that
|
|
198
|
+
resolves `PATH`) now gets your environment without them. One change in a
|
|
199
|
+
diagnostic: `probePath` in the backend's `GET /api/cli` answer no longer
|
|
200
|
+
lists the AppImage's own entries. A tmux server that was already running
|
|
201
|
+
keeps the environment it was started with (#616). Known limits are in the
|
|
202
|
+
Desktop README.
|
|
203
|
+
- **The Desktop builds a view's failure placeholder as text.** A tab whose
|
|
204
|
+
view fails to load or to mount shows the same words, built without markup.
|
|
205
|
+
- **`oats retire` no longer refuses a launched instance only because its
|
|
206
|
+
tmux socket file is missing.** After a reboot that cleared tmux's socket
|
|
207
|
+
directory, tmux answers `error connecting to <socket> (No such file or
|
|
208
|
+
directory)` for the socket the instance's session was recorded on, and
|
|
209
|
+
retire refused with `E_RUNTIME_QUIESCE_FAILED`. Retire now goes on when the
|
|
210
|
+
recorded server cannot be reached because its socket file is missing and no
|
|
211
|
+
process on the host works in the instance's home (any process whose working
|
|
212
|
+
directory is in the home, not only the harness). A tmux server keeps running
|
|
213
|
+
when its socket file is removed, so when a process still works in the home,
|
|
214
|
+
or the process scan (`lsof`) cannot run or does not complete, retire refuses
|
|
215
|
+
as before and the message says which. Such a server recreates its socket
|
|
216
|
+
when its process is sent `SIGUSR1`. The check needs `lsof`: on a host
|
|
217
|
+
without it, a retire whose recorded socket file is missing is still refused,
|
|
218
|
+
and the message says that `lsof` is missing; install it, then retire. The
|
|
219
|
+
scan counts only when `lsof` completed: one that was cut off (a timeout, too
|
|
220
|
+
much output, a signal) is no longer read as "no process", here, for a home
|
|
221
|
+
without its session receipt and for a home opened in Herdr; those retires
|
|
222
|
+
are refused. So is one whose listing names no process at all, which a
|
|
223
|
+
completed scan never prints. A window that is still running, or a server
|
|
224
|
+
that cannot be read for another reason, is refused as before.
|
|
225
|
+
[#620](https://github.com/awebai/oats/issues/620).
|
|
226
|
+
|
|
227
|
+
## Removed
|
|
228
|
+
|
|
229
|
+
- **Three unused routes of the Desktop's local server.**
|
|
230
|
+
`GET /api/session/<instance>` (a capture of the instance's terminal),
|
|
231
|
+
`POST /api/keys/<instance>` (keys or a paste typed into it) and
|
|
232
|
+
`POST /api/interrupt/<instance>` (Ctrl-C) are gone. Nothing in the Desktop
|
|
233
|
+
used them: its terminals attach to the instance's session directly. They
|
|
234
|
+
ran `tmux` without the socket an instance records, so for an instance on
|
|
235
|
+
its own tmux server they would have reached another one. Each path now
|
|
236
|
+
answers as any unknown route does (`404`, `not found`).
|
|
237
|
+
`GET /api/chat/<instance>` is unchanged. Closes
|
|
238
|
+
[#609](https://github.com/awebai/oats/issues/609).
|
package/docs/schedules.md
CHANGED
|
@@ -86,8 +86,19 @@ jobs) sets it so that its command jobs can be told apart.
|
|
|
86
86
|
sends SIGTERM to the child's process group, allows two seconds for cleanup,
|
|
87
87
|
then sends SIGKILL if the group remains. It observes the direct child's exit
|
|
88
88
|
before returning; inherited output pipes cannot hold the tick indefinitely.
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
SIGINT, SIGTERM or SIGHUP received by the supervisor enters that same cleanup
|
|
90
|
+
once; repeated signals do not bypass it. A timeout or interrupted supervisor
|
|
91
|
+
leaves effects unconfirmed even if the child printed an envelope. Spawn
|
|
92
|
+
previews use the same bounded runner.
|
|
93
|
+
|
|
94
|
+
Cleanup covers the owned process group. A descendant that creates its own
|
|
95
|
+
session can escape it; inherited pipes are bounded but that escaped process
|
|
96
|
+
is not terminated by this group cleanup. The supervisor checks the group when
|
|
97
|
+
the leader exits and never signals it after observing it empty. This reduces
|
|
98
|
+
the group-ID reuse window; it does not eliminate PID reuse races. SIGKILL,
|
|
99
|
+
OOM and other unrecoverable supervisor deaths cannot run JavaScript handlers:
|
|
100
|
+
cleanup is not guaranteed then. Without a private supervisor receipt, the
|
|
101
|
+
scheduler keeps the unknown attempt and its slot until reconciliation.
|
|
91
102
|
- **wake** `{…, home, message}` — every due minute inspects the instance at
|
|
92
103
|
`home`. Running: `message` is delivered once as terminal input (bracketed
|
|
93
104
|
paste plus Enter), never an interrupt. Not running: the home is started with
|
|
@@ -273,9 +284,12 @@ add` / `oats schedule add` definitions need no trust.
|
|
|
273
284
|
|
|
274
285
|
**Opting out on one host.** `oats trigger disable <member>/<id>` writes
|
|
275
286
|
`triggers.disabled`, and `oats schedule disable <member>/<id>` writes
|
|
276
|
-
`schedules.disabled`, in `oats-local.yaml`; `enable` removes the entry.
|
|
277
|
-
|
|
278
|
-
`
|
|
287
|
+
`schedules.disabled`, in `oats-local.yaml`; `enable` removes the entry. The
|
|
288
|
+
schedule part of a qualified ID accepts up to 100 characters in both
|
|
289
|
+
`schedules.disabled` and named `automations.trust` entries. Trigger definitions
|
|
290
|
+
and `triggers.disabled` keep their 40-character limit; the shared trust list
|
|
291
|
+
does not widen trigger IDs. A workspace definition is never edited or removed
|
|
292
|
+
from the CLI (`update` and `remove` answer `E_AUTOMATION_WORKSPACE`): change the file in Git.
|
|
279
293
|
|
|
280
294
|
**Refresh.**
|
|
281
295
|
|
|
@@ -329,12 +343,27 @@ current choices. Invalid values fail with `E_BAD_ARGS` before registration or
|
|
|
329
343
|
timer changes. `host status` reports the effective `maxConcurrent` and
|
|
330
344
|
`triggersMaxConcurrent` (`null` when uncapped).
|
|
331
345
|
|
|
332
|
-
The registry stores explicit choices only.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
346
|
+
The registry stores explicit choices only. Reading status or the registry takes
|
|
347
|
+
no registry lock and creates or rewrites no files or directories. A reader
|
|
348
|
+
interprets a pre-migration stored `maxConcurrent: 1` as the default of five in
|
|
349
|
+
memory. Registration, unregistration and cap updates persist that migration
|
|
350
|
+
once, under the registry lock, even if the workspace membership is unchanged.
|
|
351
|
+
Other explicit values and the independent trigger cap survive.
|
|
352
|
+
|
|
353
|
+
A pre-migration hand-set one is indistinguishable from the old implicit one;
|
|
354
|
+
both follow that compatibility choice. When a write migrates one to the default,
|
|
355
|
+
it prints a notice on stderr with `oats schedule host install --max-concurrent 1`
|
|
356
|
+
to restore one if needed. Pure reads stay silent and JSON stdout is unchanged.
|
|
357
|
+
A later explicit one survives writes by current kernels. Older binaries sharing
|
|
358
|
+
the registry can write one back while preserving `capsVersion: 2`; there is no
|
|
359
|
+
provenance to distinguish that from a new explicit one. Stop mixed-version
|
|
360
|
+
writes and explicitly set the desired cap with the supported CLI.
|
|
361
|
+
|
|
362
|
+
A present invalid `maxConcurrent` is `E_SCHEDULE_INVALID`, not a fallback to
|
|
363
|
+
five. Correct it with `oats schedule host install --max-concurrent N` (or
|
|
364
|
+
`--max-concurrent default`) from the deployment, or with `--dir <deployment>`.
|
|
365
|
+
An absent value still means five. Set caps through the CLI; do not edit the
|
|
366
|
+
registry by hand.
|
|
338
367
|
|
|
339
368
|
`oats schedule list --json` answers:
|
|
340
369
|
|
package/docs/servers.md
CHANGED
|
@@ -277,7 +277,10 @@ with `--server`; the remote default applies. `session attach --print` shows
|
|
|
277
277
|
the ssh command without running it. A server without the `session` commands
|
|
278
278
|
(before 0.22.2) is refused with the tmux command to attach there directly,
|
|
279
279
|
naming the session and window its roster records for the instance (else
|
|
280
|
-
`pi-agents`, that kernel's default)
|
|
280
|
+
`pi-agents`, that kernel's default), and the tmux socket when the roster
|
|
281
|
+
records one. The remote command is one quoted word either way: `ssh -t
|
|
282
|
+
<host> 'tmux attach -t <target>'`, or `ssh -t <host> 'tmux -S <socket>
|
|
283
|
+
attach -t <target>'`.
|
|
281
284
|
|
|
282
285
|
## The roster and harvest
|
|
283
286
|
|
|
@@ -298,7 +301,9 @@ the host's own facts from its `status --json`: `identity`,
|
|
|
298
301
|
`identityAddress`, `teams`, `startedAt`, `createdAt`, `model`,
|
|
299
302
|
`runtimeState`, `parentInstance`, `siblingInstance`, `relation`,
|
|
300
303
|
`relativeTo` and `spawnOrigin`. A fact the host does not supply is `null`
|
|
301
|
-
(an older host, or a saved route the host no longer lists). A
|
|
304
|
+
(an older host, or a saved route the host no longer lists). A row also
|
|
305
|
+
carries `waitingOnYou` (needs input) when the host's kernel reports it, and
|
|
306
|
+
only then: an older host's row has no such key. A removed or edited registration keeps
|
|
302
307
|
its group from the saved routes. State is pulled on every call within
|
|
303
308
|
`--per-target` (default 20 s) of a total `--budget` (default 45 s); a group
|
|
304
309
|
not reached is reported with `E_ROSTER_BUDGET`. `--server <id>` narrows it.
|
|
@@ -443,6 +443,18 @@ cannot run, retire refuses with
|
|
|
443
443
|
`E_RUNTIME_ENDPOINT_UNKNOWN`, even with `--force`: stop that session yourself,
|
|
444
444
|
then retire again. `oats retire <instance> --plan` says which case applies.
|
|
445
445
|
|
|
446
|
+
With a receipt, if the recorded window is still running, or its tmux server
|
|
447
|
+
cannot be read, retire refuses with `E_RUNTIME_QUIESCE_FAILED` and keeps the
|
|
448
|
+
home. When the recorded server cannot be reached because its socket file is
|
|
449
|
+
missing (after a reboot), retire proceeds only when no process on this host
|
|
450
|
+
works in the home (any process whose working directory is in the home, not
|
|
451
|
+
only the harness); a tmux server that lost its socket file still runs, and
|
|
452
|
+
recreates the socket when its process is sent `SIGUSR1`. That check needs
|
|
453
|
+
`lsof`: on a host without it, a retire whose recorded socket file is missing
|
|
454
|
+
is refused, and the message says that `lsof` is missing; install it, then
|
|
455
|
+
retire. A scan that does not complete (a timeout, for example) refuses the
|
|
456
|
+
same way, also for a home without its receipt.
|
|
457
|
+
|
|
446
458
|
`oats retire <instance> --self` lets an instance retire itself when the human
|
|
447
459
|
or briefing says it is done. A live harness cannot give a stable final
|
|
448
460
|
inspection of its own work, so the calling process inspects, runs, and removes
|
|
@@ -461,17 +473,31 @@ When a retire hook reports incomplete cleanup, the home is quarantined before
|
|
|
461
473
|
any worktree step: the worktree, its git admin entry and the branch stay
|
|
462
474
|
exactly as they were, so the retry can reach the hook and the work it needs.
|
|
463
475
|
The retry does the worktree step only once nothing else is outstanding:
|
|
464
|
-
retain by default, remove with `--discard-worktree
|
|
476
|
+
retain by default, remove with `--discard-worktree`.
|
|
465
477
|
`--force` removes the home regardless, so it does the worktree step first. A
|
|
466
478
|
work directory whose git admin entry is gone is never removed: the hooks still
|
|
467
479
|
run, the home is kept, and `--force` refuses it until you move the directory
|
|
468
480
|
out or delete it by hand.
|
|
469
481
|
|
|
470
|
-
Retire never deletes a branch
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
branch
|
|
482
|
+
Retire never deletes a branch: not a plain retire, `--discard-worktree`, a
|
|
483
|
+
quarantine, its retry or `--force`. `--delete-branch` is refused with
|
|
484
|
+
`E_BAD_ARGS` before anything happens. The branch stays in the repository:
|
|
485
|
+
inspect it there, and delete it with Git if it is no longer wanted. A spawn
|
|
486
|
+
that fails deletes the branch it created only while the branch's tip is
|
|
487
|
+
still where the spawn created it. If something was committed there, the
|
|
488
|
+
branch is kept and the failure says so; the quarantine's retry stays
|
|
489
|
+
incomplete until the branch is deleted with Git.
|
|
490
|
+
|
|
491
|
+
Before it removes a worktree (`--discard-worktree`, or a failed spawn's
|
|
492
|
+
quarantine that owes it), a retire preserves a commit that only the worktree
|
|
493
|
+
reaches (HEAD detached at a commit no ref of the repository reaches) in a
|
|
494
|
+
recovery, and reads HEAD again immediately before the removal: if HEAD moved
|
|
495
|
+
since the retire's last inspection, the worktree is not removed and the
|
|
496
|
+
retire stops with `E_WORK_PRESERVATION_FAILED`; the home and the worktree are
|
|
497
|
+
kept, and so is any recovery the retire wrote. A worktree whose branch has no
|
|
498
|
+
commit yet cannot be read that way: a retire that would remove it, or that
|
|
499
|
+
has work of it to copy, refuses with `E_WORK_INSPECTION_FAILED` and removes
|
|
500
|
+
nothing.
|
|
475
501
|
|
|
476
502
|
## Work modes
|
|
477
503
|
|
package/docs/workspaces.md
CHANGED
|
@@ -54,7 +54,7 @@ members: # repo refs, NO @revision (E_WORKSPAC
|
|
|
54
54
|
- git:github.com/acme/tools # a member that ALSO publishes a package (see below)
|
|
55
55
|
|
|
56
56
|
packages: # the ONLY versioned things
|
|
57
|
-
oats.framework: v1.6.
|
|
57
|
+
oats.framework: v1.6.1 # bare version → resolves through the official catalog
|
|
58
58
|
oats.okf: v4.1.1
|
|
59
59
|
acme.tools: git:github.com/acme/tools@v0.4.0 # outside the catalog → git:<repo>@<tag|OID>; still a package
|
|
60
60
|
|