@awebai/oats 0.36.1 → 0.38.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 +71 -65
- package/docs/capabilities.md +64 -9
- package/docs/capability-manifest.schema.json +4 -0
- package/docs/configuration.md +16 -21
- package/docs/design/2026-09-27-team-model-v2.md +1 -1
- package/docs/design/2026-10-02-team-model-3.md +146 -0
- package/docs/design/README.md +5 -1
- package/docs/desktop-cli-api.md +159 -121
- package/docs/desktop.md +33 -0
- package/docs/first-team.md +14 -7
- package/docs/integrations.md +1 -1
- package/docs/oats-local.schema.json +2 -14
- package/docs/oats-workspace.schema.json +4 -4
- package/docs/official-catalog.md +2 -2
- package/docs/packages.md +10 -9
- package/docs/release-notes/v0.37.0.md +111 -0
- package/docs/release-notes/v0.38.0.md +128 -0
- package/docs/souls-and-instances.md +4 -3
- package/docs/workspaces.md +122 -77
- package/lib/core.mjs +123 -32
- package/lib/instance-inspect.mjs +18 -15
- package/lib/instance-resolution.mjs +9 -6
- package/lib/resolve.mjs +5 -4
- package/lib/schedule.mjs +1 -1
- package/lib/teams-verbs.mjs +44 -70
- package/lib/teams.mjs +151 -112
- package/lib/workspace.mjs +15 -5
- package/package-catalog.json +2 -2
- package/package.json +1 -1
- package/skills/oats-getting-started/SKILL.md +20 -9
package/docs/desktop.md
CHANGED
|
@@ -104,6 +104,39 @@ opened, or your home directory when there is none (never ~/Downloads);
|
|
|
104
104
|
|
|
105
105
|
Launch flags for scripted use: `--dir <workspace>` and `OATS_DESKTOP_PORT`.
|
|
106
106
|
|
|
107
|
+
### One window per workspace
|
|
108
|
+
|
|
109
|
+
Each workspace has at most one window, titled with the workspace's name (a
|
|
110
|
+
workspace on a server: `name — server`).
|
|
111
|
+
|
|
112
|
+
- **Switching.** Choosing a workspace in the switcher shows it in the current
|
|
113
|
+
window. If that workspace already has a window, that window comes to the
|
|
114
|
+
front instead and the current one doesn't change.
|
|
115
|
+
- **Open in new window.** Each workspace in the switcher has an **Open in new
|
|
116
|
+
window** button beside it. From the keyboard: Right Arrow on the workspace,
|
|
117
|
+
then Enter, or ⌘Enter on macOS / Ctrl+Enter on Linux and Windows. A
|
|
118
|
+
workspace that already has a window is brought to the front.
|
|
119
|
+
- **New Window.** On macOS, **File → New Window** (⌘⇧N); everywhere,
|
|
120
|
+
**Window: new window** in the command palette. The new window has no
|
|
121
|
+
workspace yet: it opens the switcher, and shows "Choose a workspace" until
|
|
122
|
+
you pick one. It reads nothing until then.
|
|
123
|
+
- **Moving between windows.** On macOS, ⌘\` cycles the app's windows, and the
|
|
124
|
+
**Window** menu lists them.
|
|
125
|
+
- **Restore.** Quitting and relaunching brings every window back with its
|
|
126
|
+
size and place (moved onto a visible display if its own is gone). A window
|
|
127
|
+
whose workspace isn't served at launch doesn't come back, but it is
|
|
128
|
+
remembered until you close it while its workspace is served. With nothing
|
|
129
|
+
to restore, one window opens on the last workspace you used.
|
|
130
|
+
- **Launching again.** Running the app again (`open -a "OATS Desktop" --args
|
|
131
|
+
--dir <deployment>`, or from inside a deployment) adds that deployment if
|
|
132
|
+
needed and brings its window to the front, opening one if it has none. Any
|
|
133
|
+
other launch brings the most recently used window to the front.
|
|
134
|
+
- **Closing.** Closing a window leaves the other windows, and their
|
|
135
|
+
terminals, running. Closing the last window quits the app.
|
|
136
|
+
- Tabs belong to their window: a workspace's terminals and tabs stay in the
|
|
137
|
+
window they were opened in, and switching that window back to the
|
|
138
|
+
workspace brings them back.
|
|
139
|
+
|
|
107
140
|
## One workspace, several machines
|
|
108
141
|
|
|
109
142
|
The switcher lists each workspace once, however many deployments it has: the
|
package/docs/first-team.md
CHANGED
|
@@ -102,17 +102,24 @@ that its session will stop at the folder-trust prompt, and so does
|
|
|
102
102
|
With a messaging capability in the soul's composition, every instance lives in
|
|
103
103
|
a team, and readiness fails with `E_TEAM_UNCONFIGURED` until there is a
|
|
104
104
|
default. Create the team with your messaging provider (its own docs say how;
|
|
105
|
-
for `oats.aweb`, see its skills), then
|
|
105
|
+
for `oats.aweb`, see its skills), then commit it in `oats-workspace.yaml` as a
|
|
106
|
+
**shared** team and the workspace's default:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
teams:
|
|
110
|
+
research: { team: "<provider team id>" }
|
|
111
|
+
defaultTeam: research
|
|
112
|
+
```
|
|
106
113
|
|
|
107
114
|
```bash
|
|
108
|
-
oats teams
|
|
109
|
-
oats teams # shared and local teams, and the default
|
|
115
|
+
oats teams # shared and local teams, the default, the workspace's souls:
|
|
110
116
|
```
|
|
111
117
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
Which other teams each soul may join is committed there too, in `souls:`
|
|
119
|
+
([workspaces.md](workspaces.md#teams)). A team only this deployment uses is a
|
|
120
|
+
**local** team (`oats teams add research --team <provider team id>`; the first
|
|
121
|
+
one becomes this deployment's default), which the workspace must allow with
|
|
122
|
+
`localTeams: true`. Without messaging, skip this step.
|
|
116
123
|
|
|
117
124
|
## 4. Look before you spawn
|
|
118
125
|
|
package/docs/integrations.md
CHANGED
|
@@ -126,28 +126,16 @@
|
|
|
126
126
|
"uniqueItems": true,
|
|
127
127
|
"items": { "type": "string", "pattern": "^(?:[a-z0-9][a-z0-9._-]*/)?[a-z0-9]+(?:-[a-z0-9]+)*$" }
|
|
128
128
|
},
|
|
129
|
-
"teams": {
|
|
130
|
-
"description": "Which teams each soul belongs to on this deployment (`oats soul teams` writes it): \"*\" applies to every soul; a soul's own entry (its bare name, or <package>/<soul> for a package soul) adds to it. Every soul is also in its default. Eligible, never auto-joined: an instance joins its default at spawn and the others on request.",
|
|
131
|
-
"type": "object",
|
|
132
|
-
"propertyNames": { "type": "string", "pattern": "^(?:\\*|(?:[a-z0-9][a-z0-9._-]*/)?[a-z0-9]+(?:-[a-z0-9]+)*)$" },
|
|
133
|
-
"additionalProperties": { "type": "array", "uniqueItems": true, "items": { "$ref": "#/$defs/label" } }
|
|
134
|
-
},
|
|
135
129
|
"launch": {
|
|
136
130
|
"description": "Launch preferences on this machine (0.30; feature launch-preference), overriding the soul's own launch: \"*\" applies to every soul; a soul's own entry (its bare name, or <package>/<soul>) wins over it. A value is a launch configuration's name (in launch-configs of this file) or an inline { harness, model? }. Explicit spawn flags win over both. A changed preference affects no existing home until --reselect-launch or a respawn.",
|
|
137
131
|
"type": "object",
|
|
138
132
|
"propertyNames": { "type": "string", "pattern": "^(?:\\*|(?:[a-z0-9][a-z0-9._-]*/)?[a-z0-9]+(?:-[a-z0-9]+)*)$" },
|
|
139
133
|
"additionalProperties": { "oneOf": [ { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" }, { "$ref": "#/$defs/launchPreference" } ] }
|
|
140
|
-
},
|
|
141
|
-
"default": {
|
|
142
|
-
"description": "A per-soul override of defaultTeam (`oats soul teams <soul> --default <label>`); it must be one of that soul's teams here (E_TEAM_NOT_ELIGIBLE).",
|
|
143
|
-
"type": "object",
|
|
144
|
-
"propertyNames": { "type": "string", "pattern": "^(?:[a-z0-9][a-z0-9._-]*/)?[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
|
145
|
-
"additionalProperties": { "$ref": "#/$defs/label" }
|
|
146
134
|
}
|
|
147
135
|
}
|
|
148
136
|
},
|
|
149
137
|
"teams": {
|
|
150
|
-
"description": "LOCAL teams (`oats teams add` writes it): <label>: { team, description? }, the messaging provider's team id for a team only this deployment uses. Shared teams are committed in oats-workspace.yaml teams:; a label in both is team-label-collision (the shared one wins).",
|
|
138
|
+
"description": "LOCAL teams (`oats teams add` writes it): <label>: { team, description? }, the messaging provider's team id for a team only this deployment uses. Allowed only when oats-workspace.yaml says localTeams: true (or in the standalone view); otherwise E_WORKSPACE_SCHEMA reason local-teams-closed. Every soul may join every local team. Shared teams are committed in oats-workspace.yaml teams:; a label in both is team-label-collision (the shared one wins). souls.teams and souls.default were removed in 0.38.0: souls: in oats-workspace.yaml.",
|
|
151
139
|
"type": "object",
|
|
152
140
|
"propertyNames": { "$ref": "#/$defs/label" },
|
|
153
141
|
"additionalProperties": {
|
|
@@ -162,7 +150,7 @@
|
|
|
162
150
|
},
|
|
163
151
|
"defaultTeam": {
|
|
164
152
|
"$ref": "#/$defs/label",
|
|
165
|
-
"description": "
|
|
153
|
+
"description": "This deployment's default team (`oats teams default` writes it; the first `oats teams add` sets it): a label of teams here or in oats-workspace.yaml. Allowed only when oats-workspace.yaml says localTeams: true (or in the standalone view). A soul's souls: default in the workspace wins over it; it wins over the workspace's defaultTeam."
|
|
166
154
|
}
|
|
167
155
|
},
|
|
168
156
|
"$defs": {
|
|
@@ -25,21 +25,21 @@
|
|
|
25
25
|
"type": "object",
|
|
26
26
|
"propertyNames": { "$ref": "#/$defs/label" },
|
|
27
27
|
"additionalProperties": { "$ref": "#/$defs/team" },
|
|
28
|
-
"description": "SHARED teams: <label>: { description?, team? }. `team` is the messaging provider's team id, the same for everyone; without it the team is declared but not yet created (readiness team-unmapped).
|
|
28
|
+
"description": "SHARED teams: <label>: { description?, team? }. `team` is the messaging provider's team id, the same for everyone; without it the team is declared but not yet created (readiness team-unmapped). The default team and which teams each soul may join are defaultTeam and souls: below; a deployment's local teams live in its oats-local.yaml where localTeams allows them. A label never gates, restricts or partitions anything."
|
|
29
29
|
},
|
|
30
30
|
"defaultTeam": {
|
|
31
31
|
"$ref": "#/$defs/label",
|
|
32
|
-
"description": "
|
|
32
|
+
"description": "The workspace's fallback default team, a label of teams: in this file: a soul's souls: default wins over it, and so does a deployment's local defaultTeam where localTeams: true."
|
|
33
33
|
},
|
|
34
34
|
"localTeams": {
|
|
35
35
|
"type": "boolean",
|
|
36
|
-
"description": "
|
|
36
|
+
"description": "Whether deployments may declare their own teams and defaultTeam in oats-local.yaml (every soul may join a local team). Absent: false; then local teams and a local defaultTeam are refused (E_WORKSPACE_SCHEMA reason local-teams-closed)."
|
|
37
37
|
},
|
|
38
38
|
"souls": {
|
|
39
39
|
"type": "object",
|
|
40
40
|
"propertyNames": { "type": "string", "pattern": "^(?:\\*|[a-z0-9][a-z0-9._-]*/(?:\\*|[a-z0-9]+(?:-[a-z0-9]+)*))$" },
|
|
41
41
|
"additionalProperties": { "$ref": "#/$defs/soulTeams" },
|
|
42
|
-
"description": "
|
|
42
|
+
"description": "Per soul pattern (\"*\", <member|package>/* or <member|package>/<soul>), the soul's default team and the other teams it may join. The most specific key gives the soul's teams outright (lists never merge); the most specific key that sets a default gives its default. A soul no key matches has its default only. Every label is a label of teams: in this file."
|
|
43
43
|
},
|
|
44
44
|
"defaults": { "$ref": "#/$defs/defaults" },
|
|
45
45
|
"stores": {
|
package/docs/official-catalog.md
CHANGED
|
@@ -9,9 +9,9 @@ or workspace membership alone does not make a package official.
|
|
|
9
9
|
|
|
10
10
|
| package | release | capabilities | package souls |
|
|
11
11
|
|---|---|---|---|
|
|
12
|
-
| `oats.framework` | `oats-framework/v1.
|
|
12
|
+
| `oats.framework` | `oats-framework/v1.5.0` (this repository) | `oats.core`, `oats.setup`, `oats.knowledge-theory` | `knowledge-theory-expert` |
|
|
13
13
|
| `oats.okf` | `v4.1.1` | `oats.okf` (knowledge), `oats.okf-harvest`, `oats.okf-maintenance` | `knowledge-harvester`, `knowledge-maintainer` |
|
|
14
|
-
| `oats.aweb` | `v1.
|
|
14
|
+
| `oats.aweb` | `v1.19.0` | `oats.aweb` (messaging) | |
|
|
15
15
|
| `oats.engineering` | `v1.5.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
|
@@ -51,7 +51,7 @@ packages:
|
|
|
51
51
|
- **Bare version** (`v4.1.1`, `4.1.1`, `1.0.0-rc.1`): the id is looked up in
|
|
52
52
|
the official catalog — `package-catalog.json` in the `oats` repo, or the file
|
|
53
53
|
named by `OATS_PACKAGE_CATALOG` — which supplies the repo url, the tag
|
|
54
|
-
convention (`v4.1.1` or `oats-framework/v1.
|
|
54
|
+
convention (`v4.1.1` or `oats-framework/v1.5.0`) and the payload path. An id
|
|
55
55
|
the catalog does not know is `E_PACKAGE_MISSING` ("use `git:<repo>@<ref>` for
|
|
56
56
|
a package outside the catalog"). The catalog is the reviewed official list
|
|
57
57
|
([official-catalog.md](official-catalog.md)) and the only way a
|
|
@@ -74,9 +74,9 @@ members:
|
|
|
74
74
|
- git:github.com/acme/agents
|
|
75
75
|
- git:github.com/acme/platform
|
|
76
76
|
packages:
|
|
77
|
-
oats.framework: v1.
|
|
77
|
+
oats.framework: v1.5.0
|
|
78
78
|
oats.okf: v4.1.1
|
|
79
|
-
oats.aweb: v1.
|
|
79
|
+
oats.aweb: v1.19.0
|
|
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.
|
|
137
|
+
oats package add oats.aweb v1.19.0 # 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
|
```
|
|
@@ -247,8 +247,9 @@ oats-package/
|
|
|
247
247
|
souls; otherwise `E_SOUL_AMBIGUOUS` names each qualified form
|
|
248
248
|
(`details.qualified`). A member soul's qualified form is `<member name>/<soul>`.
|
|
249
249
|
- **Resolved** like any soul: the workspace defaults apply, `off` and
|
|
250
|
-
`<slot>: none` work, its teams
|
|
251
|
-
|
|
250
|
+
`<slot>: none` work, its teams are keyed `<package>/<soul>` (or `<package>/*`)
|
|
251
|
+
in the workspace's `souls:` (unlisted, it joins its default only), and
|
|
252
|
+
`from: here` means **this package** at the locked commit
|
|
252
253
|
(a capability it does not provide is `E_CAPABILITY_MISSING`).
|
|
253
254
|
- **Spawned** at the locked commit: the soul is fetched into the per-commit
|
|
254
255
|
soul cache and its digest must equal the lock's (`E_PACKAGE_INTEGRITY
|
|
@@ -332,11 +333,11 @@ A soul that names one of the package's capabilities with
|
|
|
332
333
|
"policy": "docs/official-catalog.md",
|
|
333
334
|
"packages": {
|
|
334
335
|
"oats.okf": { "url": "https://github.com/awebai/oats-okf.git", "ref": "v4.1.1", "path": "oats-package" },
|
|
335
|
-
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.
|
|
336
|
+
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.5.0", "path": "oats-package" }
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
```
|
|
339
340
|
|
|
340
|
-
`ref` carries the tag convention: a workspace's `oats.framework: v1.
|
|
341
|
-
resolves to tag `oats-framework/v1.
|
|
341
|
+
`ref` carries the tag convention: a workspace's `oats.framework: v1.5.0`
|
|
342
|
+
resolves to tag `oats-framework/v1.5.0`. Resolving through the catalog never
|
|
342
343
|
advances a lock by itself: `oats sync` does, and says so.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# OATS 0.37.0
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
|
|
5
|
+
- **The Desktop opens one window per workspace** (awebai/oats#481). Each
|
|
6
|
+
workspace has at most one window, titled with its name (a workspace on a
|
|
7
|
+
server: `name — server`). Choosing a workspace in the switcher shows it in the
|
|
8
|
+
current window, or brings its own window to the front when it has one.
|
|
9
|
+
Each workspace in the switcher has an **Open in new window** button
|
|
10
|
+
(keyboard: Right Arrow then Enter, or ⌘Enter / Ctrl+Enter). On macOS,
|
|
11
|
+
**File → New Window** (⌘⇧N) opens a window with no workspace that asks you
|
|
12
|
+
to choose one, and ⌘\` cycles the windows; on Linux and Windows the command
|
|
13
|
+
palette has **Window: new window**, with no default chord. Quitting and
|
|
14
|
+
relaunching brings every window back with its size and place; a window
|
|
15
|
+
whose workspace isn't served at launch stays remembered until you close it
|
|
16
|
+
while it is served. Launching the app again with `--dir <deployment>`
|
|
17
|
+
brings that deployment's window to the front, adding the deployment if
|
|
18
|
+
needed. An unfocused window re-reads its instance list at the server's
|
|
19
|
+
blurred cadence (30 s). A window's requests always carry its own
|
|
20
|
+
workspace: one the server doesn't serve is refused with
|
|
21
|
+
`E_WORKSPACE_NOT_SERVED`, never answered with another workspace's data.
|
|
22
|
+
Window positions are kept in `windows.json` in the app's data folder.
|
|
23
|
+
|
|
24
|
+
## Changed
|
|
25
|
+
|
|
26
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.38.0`**, so it runs against
|
|
27
|
+
this release's kernel. Install the CLI and the Desktop 0.37.0 together: the
|
|
28
|
+
Desktop 0.36.x refuses a 0.37 CLI.
|
|
29
|
+
|
|
30
|
+
- **Team model 3 is OATS 0.38.0, not 0.37.0.** 0.37.0 keeps 0.36.1's
|
|
31
|
+
behaviour: the new `oats-workspace.yaml` keys are validated but not applied,
|
|
32
|
+
and the `team-model-3-migration` warning, which now names 0.38.0, says what
|
|
33
|
+
0.38.0 will refuse. The migration steps in the 0.36.1 notes apply unchanged.
|
|
34
|
+
|
|
35
|
+
- **oats.framework 1.4.3** (oats.setup 2.2.2, catalog and workspace pin
|
|
36
|
+
`oats-framework/v1.4.3`): the `oats-teams` and
|
|
37
|
+
`oats-workspace-config` skills say team model 3 is 0.38.0, and that 0.36.x
|
|
38
|
+
and 0.37.x keep applying the local team keys. Remove them only when the
|
|
39
|
+
deployment moves to 0.38.0.
|
|
40
|
+
|
|
41
|
+
- **oats.aweb 1.18.1** (catalog and workspace pin, and the bundled mirror):
|
|
42
|
+
how mail reaches a session now depends on the runtime. Under the default
|
|
43
|
+
`delivery: channel`, Claude Code takes mail and chat through its
|
|
44
|
+
`aweb-channel` plugin and pi through the `@awebai/pi` extension, and
|
|
45
|
+
neither goes through the host wake broker. Codex keeps the wake broker. A
|
|
46
|
+
Codex home under `channel` is now registered with it at every start, where
|
|
47
|
+
before it got no wake at all. Every start of a home re-decides the path for
|
|
48
|
+
that start's runtime and leaves the home on exactly one path. A start that
|
|
49
|
+
cannot register or deregister the home is refused. Existing instances keep
|
|
50
|
+
the delivery they were spawned with until they are respawned.
|
|
51
|
+
`delivery: session` still sends every runtime through the broker.
|
|
52
|
+
|
|
53
|
+
oats.aweb 1.18.1 declares `launchPreview` (see Fixed below). A launch
|
|
54
|
+
preview, including Desktop's start dialog, and a start refused by
|
|
55
|
+
preflight change no broker registration: the hook calls `aw` only in the
|
|
56
|
+
real run, after preflight has passed.
|
|
57
|
+
|
|
58
|
+
## Fixed
|
|
59
|
+
|
|
60
|
+
- **Launch hooks no longer have side effects under a preview, or before a
|
|
61
|
+
start's preflight has passed** (awebai/oats#500). A launch hook may register
|
|
62
|
+
a home with its provider; oats.aweb's registers it with the host wake
|
|
63
|
+
broker. But `oats launch-config preview`, which Desktop's start dialog
|
|
64
|
+
calls for running homes, ran every hook for real. A start refused by
|
|
65
|
+
preflight, including a start of a home that was already running, had also
|
|
66
|
+
run them. Previewing a running Claude home with another harness could
|
|
67
|
+
therefore change how that live session is woken.
|
|
68
|
+
|
|
69
|
+
A capability now declares that its launch hook is preview-aware, with
|
|
70
|
+
`"launchPreview": true` at the top level of its manifest. The kernel reads
|
|
71
|
+
the declaration from the home's own module copy. A preview-aware hook
|
|
72
|
+
runs twice per start:
|
|
73
|
+
- **As a preview,** with `OATS_LAUNCH_PREVIEW=1` in the hook environment
|
|
74
|
+
(a new, additive hook environment variable). This run's contribution
|
|
75
|
+
drives the preflight and the rendered command.
|
|
76
|
+
- **For real, without the flag,** only once every check has passed, and
|
|
77
|
+
before a restart stops the running harness.
|
|
78
|
+
|
|
79
|
+
A preview runs only the first pass. A preview-aware hook must change
|
|
80
|
+
nothing under the flag. It must also return the same contribution either
|
|
81
|
+
way, apart from the values of the env names its preview answer lists in
|
|
82
|
+
`volatileEnv` (below). If the real run's contribution differs otherwise,
|
|
83
|
+
the start is refused with `E_LAUNCH_PREPARATION` and nothing is stopped or
|
|
84
|
+
started.
|
|
85
|
+
|
|
86
|
+
A hook that doesn't declare `launchPreview` runs as it did in 0.36: once
|
|
87
|
+
per start, for real, during preflight. `oats launch-config preview` never
|
|
88
|
+
runs it; the preview shows its recorded contribution and says so. The
|
|
89
|
+
kernel never passes on an `OATS_LAUNCH_PREVIEW` inherited from the
|
|
90
|
+
environment.
|
|
91
|
+
|
|
92
|
+
**Provider authors:** declare `launchPreview` once your launch hook
|
|
93
|
+
honours `OATS_LAUNCH_PREVIEW`, and expect it to run twice per start
|
|
94
|
+
(docs/capabilities.md). oats.aweb 1.18.1 does. A kernel before 0.37
|
|
95
|
+
ignores the key and runs the hook once, as before.
|
|
96
|
+
- **Existing homes whose launch hook renews a credential at every start
|
|
97
|
+
start again** (awebai/oats#504). The first version of the two-pass rule
|
|
98
|
+
above ran every launch hook twice. A hook from before the preview flag
|
|
99
|
+
then really ran under the preview too, and a hook that answers new values
|
|
100
|
+
at every call failed the comparison. That covers oats.aweb homes with
|
|
101
|
+
`identity.mode: global` and `renew: launch`, which mint a grant at each
|
|
102
|
+
start: their starts were refused with `E_LAUNCH_PREPARATION`. Two passes
|
|
103
|
+
are now opt-in, through `launchPreview`.
|
|
104
|
+
|
|
105
|
+
A preview-aware hook can also name, in its preview answer, env whose
|
|
106
|
+
values only the real run can know, such as `"volatileEnv":
|
|
107
|
+
["AWEB_IDENTITY_HOME"]`. The start takes those values from the real run
|
|
108
|
+
and leaves them out of the comparison. Each must be a name the same hook
|
|
109
|
+
returned. A harness configuration selector (`CLAUDE_CONFIG_DIR`,
|
|
110
|
+
`CODEX_HOME`, `PI_CODING_AGENT_DIR`) is refused as volatile, because the
|
|
111
|
+
package probe reads it before the real run.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# OATS 0.38.0
|
|
2
|
+
|
|
3
|
+
## Changed (breaking)
|
|
4
|
+
|
|
5
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.39.0`**, so it runs against
|
|
6
|
+
this release's kernel. Install the CLI and the Desktop 0.38.0 together: the
|
|
7
|
+
Desktop 0.37.x refuses a 0.38 CLI.
|
|
8
|
+
|
|
9
|
+
- **Team model 3: the teams a soul may join, and its default, are committed
|
|
10
|
+
in the workspace** (awebai/oats#484; feature `team-model-3` replaces
|
|
11
|
+
`team-model-2`). An instance in two teams is a bridge between them, so which
|
|
12
|
+
teams an organisation's instances may be in is now its decision, committed
|
|
13
|
+
in `oats-workspace.yaml` and closed by default. This prevents accidental
|
|
14
|
+
joins and makes the intended team set reviewable in git; it does not stop a
|
|
15
|
+
deliberate relay by someone holding credentials for two teams (see
|
|
16
|
+
docs/design/2026-10-02-team-model-3.md).
|
|
17
|
+
- `oats-workspace.yaml` `defaultTeam`, `localTeams` and `souls:` (accepted
|
|
18
|
+
since 0.36.1) now apply. `souls:` keys are `"*"`, `<member|package>/*` and
|
|
19
|
+
`<member|package>/<soul>`; the most specific key gives a soul's teams
|
|
20
|
+
outright (lists never merge), and the most specific key that sets one
|
|
21
|
+
gives its default: `a/*: {default: security}` and `a/x: {teams: [docs]}`
|
|
22
|
+
give `a/x` the default `security` and the teams `[docs]` only. `teams:
|
|
23
|
+
any` is every shared team. A soul no key matches has its default only,
|
|
24
|
+
package souls included.
|
|
25
|
+
- A soul's default team is its `souls:` default; else the deployment's
|
|
26
|
+
`oats-local.yaml` `defaultTeam`, only when the workspace says
|
|
27
|
+
`localTeams: true`; else the workspace's `defaultTeam`; else none.
|
|
28
|
+
`defaultTeam.from` and `OATS_DEFAULT_TEAM_FROM` are `soul`, `deployment`
|
|
29
|
+
or `workspace`; `soul` now means the workspace's `souls:` default (it was
|
|
30
|
+
the local `souls.default`).
|
|
31
|
+
- A soul may join its default, its `souls:` teams and, with
|
|
32
|
+
`localTeams: true`, every local team. Every team row carries `via`
|
|
33
|
+
(`default`, `workspace`, `local`), in reports, `OATS_TEAMS` and
|
|
34
|
+
`instance.json`.
|
|
35
|
+
- `oats-local.yaml` `souls.teams` and `souls.default` are refused
|
|
36
|
+
(`E_WORKSPACE_SCHEMA`, reason `removed-key`). The refusal names each key
|
|
37
|
+
and prints the `souls:` to commit instead (`details.replacement`); `oats
|
|
38
|
+
doctor` answers with it.
|
|
39
|
+
- `oats-local.yaml` `teams` and `defaultTeam` are refused unless the
|
|
40
|
+
workspace says `localTeams: true` (`E_WORKSPACE_SCHEMA`, reason
|
|
41
|
+
`local-teams-closed`, naming both fixes). Spawn, preview and inspect
|
|
42
|
+
refuse; `oats teams`, readiness and `oats doctor` (offline, from the cached
|
|
43
|
+
workspace file) report it as a failure. The standalone view allows them.
|
|
44
|
+
- `oats teams add` and `oats teams default` are refused where local teams
|
|
45
|
+
are closed; `oats teams remove` still works there. `oats soul teams` is
|
|
46
|
+
read only: `--add`, `--remove`, `--default` and `--clear-default` are
|
|
47
|
+
`E_BAD_ARGS`, naming `souls:` in `oats-workspace.yaml`.
|
|
48
|
+
- `oats teams --json` is `teamsApi: 2` (`localTeams`, a `DefaultTeam`, the
|
|
49
|
+
workspace's `souls:`) and now discovers the workspace's souls; `oats soul
|
|
50
|
+
teams --json` is `soulTeamsApi: 2` (`key` is the qualified soul key,
|
|
51
|
+
`match` and `defaultMatch` name the `souls:` keys that applied).
|
|
52
|
+
- New readiness items: local-teams-closed (failure) and `team-soul-unknown`
|
|
53
|
+
(a warning for a `souls:` key that names no soul of the workspace).
|
|
54
|
+
`team-model-3-migration` is retired, and the kernel no longer reports
|
|
55
|
+
`E_TEAM_NOT_ELIGIBLE` (a soul's default is always eligible).
|
|
56
|
+
- Shapes: docs/desktop-cli-api.md, under Teams.
|
|
57
|
+
- **The Desktop follows team model 3.** With a 0.38 CLI (feature `team-model-3`) the Workspace ›
|
|
58
|
+
Teams page shows the workspace's `souls:` read only, and offers Add and Make default for local teams
|
|
59
|
+
only where the workspace says `localTeams: true`; where it doesn't, a local team still in
|
|
60
|
+
`oats-local.yaml` keeps Remove under the kernel's local-teams-closed message. A soul's *Teams here* is
|
|
61
|
+
read only: it says where the soul's default and teams come from and that they change as `souls:` in
|
|
62
|
+
`oats-workspace.yaml` (a PR to the workspace file). The Desktop never sends the removed `oats soul
|
|
63
|
+
teams` edit flags, and a 0.37 or older CLI keeps the team model v2 views.
|
|
64
|
+
- **oats.aweb 1.19.0** (catalog and workspace pin, and the bundled mirror),
|
|
65
|
+
for team model 3. A default team that comes from the workspace's
|
|
66
|
+
`defaultTeam` is reported as `from: "workspace"` in `oats aweb teams --json`
|
|
67
|
+
and the recorded meta (1.18 reported it as `deployment`). `oats aweb setup
|
|
68
|
+
--create` asks the kernel whether the workspace allows local teams before it
|
|
69
|
+
creates anything; where it does not, it creates the team and its per-team
|
|
70
|
+
root, records no local team, and prints the `teams:` entry (and
|
|
71
|
+
`defaultTeam:`) to commit in `oats-workspace.yaml`, or the `localTeams: true`
|
|
72
|
+
alternative. `--username` and setup's other advice say the same where local
|
|
73
|
+
teams are closed, and an unmapped default's remedy names `defaultTeam:` in
|
|
74
|
+
the workspace file. Joining stays limited to the eligible `OATS_TEAMS` rows.
|
|
75
|
+
|
|
76
|
+
- **oats.framework 1.5.0** (oats.core 2.3.0, oats.setup 2.3.0; catalog and
|
|
77
|
+
workspace pin `oats-framework/v1.5.0`): the skills
|
|
78
|
+
describe team model 3. `oats-teams`, `oats-workspace-config`,
|
|
79
|
+
`oats-setup-model`, `oats-onboarding` and the setup inject say a soul's teams
|
|
80
|
+
and default are committed in `souls:` of `oats-workspace.yaml`, local teams
|
|
81
|
+
only with `localTeams: true`; `oats-operate` and `oats-souls` say where an
|
|
82
|
+
instance's teams come from.
|
|
83
|
+
|
|
84
|
+
## Upgrade
|
|
85
|
+
|
|
86
|
+
Before upgrading a deployment, its workspace must say what its
|
|
87
|
+
`oats-local.yaml` said. 0.36.1 and 0.37.x already accept the workspace keys,
|
|
88
|
+
and their `team-model-3-migration` warnings list what each deployment has to
|
|
89
|
+
move.
|
|
90
|
+
|
|
91
|
+
1. **In the workspace file** (a PR, once everyone who reads it runs 0.36.1 or
|
|
92
|
+
later):
|
|
93
|
+
- commit `souls:` entries for what the deployments' `souls.teams` and
|
|
94
|
+
`souls.default` said. A v2 soul's teams were its default, `"*"`'s teams
|
|
95
|
+
and its own, and patterns never merge, so a soul's own entry lists
|
|
96
|
+
`"*"`'s teams too. Qualify bare soul names with their member's name
|
|
97
|
+
(`<member>/<soul>`, as `souls.disabled` names it). Running 0.38.0 against
|
|
98
|
+
an unmigrated `oats-local.yaml` prints this snippet for you;
|
|
99
|
+
- commit `defaultTeam: <label>` when the deployments' default is a shared
|
|
100
|
+
team;
|
|
101
|
+
- for local (personal) teams and a local default, choose: (a) keep them
|
|
102
|
+
personal, with `localTeams: true`; or (b) commit them in `teams:` (and the
|
|
103
|
+
default as `defaultTeam:`).
|
|
104
|
+
2. **In each `oats-local.yaml`**, when the deployment moves to 0.38.0: remove
|
|
105
|
+
`souls.teams` and `souls.default`, and, under (b), the local `teams` and
|
|
106
|
+
`defaultTeam` (`oats teams remove` works while local teams are closed).
|
|
107
|
+
3. **Check:** `oats teams` lists no problems, and `oats spawn <soul>
|
|
108
|
+
--preview` shows each soul's `defaultTeam` and `teams` as intended. Running
|
|
109
|
+
instances keep their spawn-time default; readiness reports
|
|
110
|
+
`default-team-changed` where it moved, and a respawn follows it.
|
|
111
|
+
|
|
112
|
+
What each kind of deployment changes:
|
|
113
|
+
|
|
114
|
+
- **A workspace whose people keep personal teams or a personal default** (for
|
|
115
|
+
example awebai/oats): `localTeams: true`, plus `souls:` for the souls'
|
|
116
|
+
shared teams (e.g. `"*": { teams: [oats] }`); drop `souls.teams` locally.
|
|
117
|
+
- **A workspace whose default team is a committed shared team**: commit
|
|
118
|
+
`defaultTeam: <label>` and the `souls:` entries; drop the local
|
|
119
|
+
`defaultTeam` (and any `souls.*` team keys).
|
|
120
|
+
- **A standalone deployment**: nothing changes for local teams and the local
|
|
121
|
+
default; drop `souls.teams` and `souls.default` (a standalone view has no
|
|
122
|
+
`souls:` patterns: every soul may join every local team).
|
|
123
|
+
|
|
124
|
+
Consumers: the Desktop's team views read `teamsApi: 1` / `soulTeamsApi: 1` and
|
|
125
|
+
edit through `oats soul teams --add`; they follow in their own change. Another
|
|
126
|
+
messaging provider receives `OATS_DEFAULT_TEAM_FROM=workspace` and `via` on
|
|
127
|
+
the `OATS_TEAMS` rows; one that records local teams with `oats teams add` needs
|
|
128
|
+
the workspace to allow them (oats.aweb 1.19.0 does both, above).
|
|
@@ -164,8 +164,8 @@ composed skills and instructions, a spawn records:
|
|
|
164
164
|
"key": "github.com/acme/agents", "commit": "3f2a9c1e…", "resolution": "20ec8ec527311d71d0973086",
|
|
165
165
|
"soul": { "repoKey": "github.com/acme/agents", "commit": "3f2a9c1e…" }
|
|
166
166
|
},
|
|
167
|
-
"teams": [{ "label": "ana-acme", "team": "ana-acme:ana.aweb.ai", "default": true, "from": "local" },
|
|
168
|
-
{ "label": "engineering", "team": "engineering:acme.aweb.ai", "default": false, "from": "shared" }],
|
|
167
|
+
"teams": [{ "label": "ana-acme", "team": "ana-acme:ana.aweb.ai", "default": true, "from": "local", "via": ["default", "local"] },
|
|
168
|
+
{ "label": "engineering", "team": "engineering:acme.aweb.ai", "default": false, "from": "shared", "via": ["workspace"] }],
|
|
169
169
|
"defaultTeam": { "label": "ana-acme", "team": "ana-acme:ana.aweb.ai", "from": "deployment" }
|
|
170
170
|
}
|
|
171
171
|
```
|
|
@@ -183,7 +183,8 @@ composed skills and instructions, a spawn records:
|
|
|
183
183
|
compares `workspace.soul` with the member's current commit too: `soul: <name>
|
|
184
184
|
from <member> @ <c7> [member moved since …]` (`--json`: `instances[].soul`).
|
|
185
185
|
- `teams` / `defaultTeam`: the soul's teams at spawn, exactly as the providers
|
|
186
|
-
received them (mapped teams only
|
|
186
|
+
received them (mapped teams only, each with `via`: why the soul may join it)
|
|
187
|
+
and its default: evidence, never rewritten.
|
|
187
188
|
A running home's hooks and messaging commands read the teams live
|
|
188
189
|
([capabilities.md](capabilities.md#teams-in-the-provider-environment)).
|
|
189
190
|
|