Accept scheduled work (remote.scheduled, off by default) said as limits.scheduled; a scheduled turn is an agent session marked `scheduled` in the schedule's mode (Edit when none chosen, Plan refused, lowered to remote.max_mode) with its Min/Max; the answer reports status ok, silent, failed or cancelled and the final reply. Approvals wait the schedule's wait and go to the person through the server, then count as denied. _lembas/monitor runs a watch command under the mode's rules (one that would ask is refused) and answers a hash and an excerpt. Schedule routes of /api/v2 accept the device's token. team_add under a sub-manager names it (added_under). Conformance areas scheduled, silent, monitor; team cases changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
8 files changed
+639
-21
No files matched your search
+35
-1
@@ -22,7 +22,7 @@ page *Harness-parity* in LLeMbas CLI's wiki.
|
|||||||
| `permission/defaults.json` | The rules every session starts from |
|
| `permission/defaults.json` | The rules every session starts from |
|
||||||
| `permission/modes.json` | The four modes — manual, edit, auto, plan — as what each does with each risk class |
|
| `permission/modes.json` | The four modes — manual, edit, auto, plan — as what each does with each risk class |
|
||||||
| `models/schema.json` | Model metadata: context, output limit, efforts and the thinking switch, vision, tools, notes, capacity (`parallel`); `levels` — the effort levels a chat offers and what each sends |
|
| `models/schema.json` | Model metadata: context, output limit, efforts and the thinking switch, vision, tools, notes, capacity (`parallel`); `levels` — the effort levels a chat offers and what each sends |
|
||||||
| `loop.json` | How one prompt runs: the step backstop, budgets, wrap-up, nudges, parallel calls, capacity; `jobs` — background work, its marks and limits; `subagents` — the model refusals, what names a model, the setting's label; `team` — a team's size, names, marks, refusals and texts; `progress` — the phases' texts; `context` — result ids, stubs, the output cap, the compaction table, the environment lines, the session notes |
|
| `loop.json` | How one prompt runs: the step backstop, budgets, wrap-up, nudges, parallel calls, capacity; `jobs` — background work, its marks and limits; `subagents` — the model refusals, what names a model, the setting's label; `team` — a team's size, names, marks, refusals and texts; `progress` — the phases' texts; `context` — result ids, stubs, the output cap, the compaction table, the environment lines, the session notes; `scheduled` (3.5.0) — scheduled work on a linked computer: the setting, the modes, the approval wait, the sentinel, the watch command's limits and the refusals |
|
||||||
| `filesystem.md` | The file system a session works in: paths, node kinds, the four access levels, errors, listings, search and indexing by tokens, stacked instruction files, skills, memory |
|
| `filesystem.md` | The file system a session works in: paths, node kinds, the four access levels, errors, listings, search and indexing by tokens, stacked instruction files, skills, memory |
|
||||||
| `context.md` | Context hygiene: what happens to a tool result when it is inserted, and never afterwards; the prefix that stays still; reasoning sent back during its turn; no absolute paths in the prompt |
|
| `context.md` | Context hygiene: what happens to a tool result when it is inserted, and never afterwards; the prefix that stays still; reasoning sent back during its turn; no absolute paths in the prompt |
|
||||||
| `team.md` | Teams: a lead and the members it adds, the size setting, the room, who receives what, reports, a manager's refusals, capacity, stopping |
|
| `team.md` | Teams: a lead and the members it adds, the size setting, the room, who receives what, reports, a manager's refusals, capacity, stopping |
|
||||||
@@ -71,6 +71,40 @@ page *Harness-parity* in LLeMbas CLI's wiki.
|
|||||||
approval in plan mode and answers its `unavailable` text outside it. Their permission key is
|
approval in plan mode and answers its `unavailable` text outside it. Their permission key is
|
||||||
`plan` (`permission/defaults.json`), which a rule written for the former `todo` key still reaches.
|
`plan` (`permission/defaults.json`), which a rule written for the former `todo` key still reaches.
|
||||||
|
|
||||||
|
## 3.5.0
|
||||||
|
|
||||||
|
Written ahead of both implementations; the server vendors it as written, both carry it in the same
|
||||||
|
round (`acp.md` *Scheduled work on a linked computer*, `loop.json` `scheduled`).
|
||||||
|
|
||||||
|
- **Scheduled turns on a linked computer.** A schedule may run each time as an agent turn on a
|
||||||
|
linked device, in a folder of it. The device must allow it: **Accept scheduled work**
|
||||||
|
(`remote.scheduled`, off by default; `lembas service scheduled on|off`), said to the server as
|
||||||
|
`limits.scheduled` and judged again at every request — off, every scheduled request is refused
|
||||||
|
with one fixed text. The server opens an agent session marked `scheduled` (schedule, run, mode,
|
||||||
|
approval wait) with the schedule's Min/Max as an agent chat's, and prompts it with the run's own
|
||||||
|
text. **The mode** is the schedule's — Manual, Edit or Auto, **Edit when none was chosen**, so never
|
||||||
|
wider than Edit unless the person picked Auto; Plan refused — lowered to `remote.max_mode`. The
|
||||||
|
answer reports the run: `status` `ok`, `silent` (the `[SILENT]` sentinel on the first or last
|
||||||
|
line, the server's rule), `failed` or `cancelled`, and the final reply's `text`. A device that is
|
||||||
|
away is waited for like a busy model, then the run is missed; a refusal fails the run.
|
||||||
|
- **Approvals in a scheduled turn** go to the person through the server (a phone notification with
|
||||||
|
the card), carry `scheduled` on `session/request_permission`, and wait the schedule's wait
|
||||||
|
(default 30 minutes) instead of `remote.approval_timeout`; then they count as denied and the turn
|
||||||
|
goes on. `ask_user` and `plan_submit` have nobody to answer; `show` is not offered.
|
||||||
|
- **Monitor mode, the device's half**: `_lembas/monitor` runs a schedule's watch command in its
|
||||||
|
folder, with a timeout, under the mode's permission rules — **a command the mode would ask about
|
||||||
|
is refused** — and answers the output's hash (`sha256:` over the exit status and the bytes) and a
|
||||||
|
short excerpt; the server runs the model only when the hash changed, and records `unchanged`
|
||||||
|
otherwise.
|
||||||
|
- **Schedules over `/api/v2` with the device's token** (scope `link`): the CLI's `/schedule` lists,
|
||||||
|
creates from words (compile), pauses, resumes, runs now and reads history. A run may be
|
||||||
|
`unchanged`, may wait for the `device`; a schedule may say `runs_on` and `watch`.
|
||||||
|
- **`team_add` under a sub-manager** says where the report goes: `team.texts.added_under` and
|
||||||
|
`added_waiting_under` ("Its report goes to <parent> when it finishes").
|
||||||
|
- Capability `scheduled`. Conformance areas **`scheduled`** (the mode, the wait, the refusals),
|
||||||
|
**`silent`** (the sentinel and the stripped text) and **`monitor`** (the hash, and what a watch
|
||||||
|
command may run); `team` cases changed for `added_under`.
|
||||||
|
|
||||||
## 3.4.0
|
## 3.4.0
|
||||||
|
|
||||||
Written ahead of both implementations; the server vendors it as written, both carry it in the same
|
Written ahead of both implementations; the server vendors it as written, both carry it in the same
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
3.4.0
|
3.5.0
|
||||||
+173
-3
@@ -68,6 +68,7 @@ change to the protocol is a change here first, then in both projects.
|
|||||||
| `progress` | spec 3.4.0 (`progress.md`): the `phase` event; `progress` in `_lembas/session/status` |
|
| `progress` | spec 3.4.0 (`progress.md`): the `phase` event; `progress` in `_lembas/session/status` |
|
||||||
| `agents` | spec 3.4.0: the session's Subagents setting `{ min, max }` is read from `_meta.lembas.agents` on `session/new`, `session/prompt` and `_lembas/configure` (*Subagents in an agent chat*, below) |
|
| `agents` | spec 3.4.0: the session's Subagents setting `{ min, max }` is read from `_meta.lembas.agents` on `session/new`, `session/prompt` and `_lembas/configure` (*Subagents in an agent chat*, below) |
|
||||||
| `tree` | spec 3.4.0 (`subagents.md` §5–§13): the `team` event; `_lembas/team`, `_lembas/team/transcript`, `_lembas/team/findings`, `_lembas/team/stop`; `subagent` on `session/request_permission` |
|
| `tree` | spec 3.4.0 (`subagents.md` §5–§13): the `team` event; `_lembas/team`, `_lembas/team/transcript`, `_lembas/team/findings`, `_lembas/team/stop`; `subagent` on `session/request_permission` |
|
||||||
|
| `scheduled` | spec 3.5.0 (*Scheduled work on a linked computer*, below): `scheduled` in `_meta.lembas` of `session/new` and `session/prompt` read, and the run's outcome in the prompt's answer; `scheduled` on `session/request_permission`; `_lembas/monitor`; `limits.scheduled` in `_lembas/hello` and `initialize`. Listed by every build that has it, whatever the setting says: whether this device *accepts* scheduled work is `limits.scheduled` |
|
||||||
|
|
||||||
## Standard ACP, as used here
|
## Standard ACP, as used here
|
||||||
|
|
||||||
@@ -90,14 +91,15 @@ Result:
|
|||||||
"_meta": { "lembas": {
|
"_meta": { "lembas": {
|
||||||
"protocol": 3,
|
"protocol": 3,
|
||||||
"harness_spec": "3.0.0",
|
"harness_spec": "3.0.0",
|
||||||
"limits": { "roots": ["~/work"], "max_mode": "edit" },
|
"limits": { "roots": ["~/work"], "max_mode": "edit", "scheduled": false },
|
||||||
"connection": "example",
|
"connection": "example",
|
||||||
"capabilities": ["directories", "configure", "…"]
|
"capabilities": ["directories", "configure", "…"]
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`limits` is `null` on `serve --stdio`. `connection` is the device's name for the instance — the
|
`limits` is `null` on `serve --stdio`. `limits.scheduled` (spec 3.5.0, capability `scheduled`): whether
|
||||||
|
this device accepts scheduled work now (`remote.scheduled`, *Scheduled work on a linked computer*). `connection` is the device's name for the instance — the
|
||||||
login's connection — or `null`; the `model` event and `announce` say directly whether a model is
|
login's connection — or `null`; the `model` event and `announce` say directly whether a model is
|
||||||
the instance's (`instance`).
|
the instance's (`instance`).
|
||||||
|
|
||||||
@@ -251,6 +253,10 @@ person may also answer at the terminal (a shared session), so the client does no
|
|||||||
can say which — a background subagent may ask while the session itself is idle. `subagent` (capability `tree`, spec 3.4.0):
|
can say which — a background subagent may ask while the session itself is idle. `subagent` (capability `tree`, spec 3.4.0):
|
||||||
`{ id, name, position }` when the call is a tree subagent's that nobody in the tree could allow (`subagents.md` §7) —
|
`{ id, name, position }` when the call is a tree subagent's that nobody in the tree could allow (`subagents.md` §7) —
|
||||||
the card names it; the answers are the same as for the main agent's own call.
|
the card names it; the answers are the same as for the main agent's own call.
|
||||||
|
`scheduled` (capability `scheduled`, spec 3.5.0): `{ schedule_id, run_id, wait_s }` when the call is
|
||||||
|
a scheduled turn's — nobody is at the chat, so the client puts the card to the person where they are
|
||||||
|
(LLeMbas: a notification on their phone carrying the card, answerable there and in every client),
|
||||||
|
and the device waits `wait_s` for it, not `remote.approval_timeout` (*Scheduled work*, below).
|
||||||
Answer: ACP's `{ outcome: { outcome: "selected", optionId } | { outcome: "cancelled" } }`, with
|
Answer: ACP's `{ outcome: { outcome: "selected", optionId } | { outcome: "cancelled" } }`, with
|
||||||
optional `_meta.lembas.command` (the corrected command line, `once` only) or
|
optional `_meta.lembas.command` (the corrected command line, `once` only) or
|
||||||
`_meta.lembas.feedback` (why it was denied). No answer within `remote.approval_timeout` is a denial.
|
`_meta.lembas.feedback` (why it was denied). No answer within `remote.approval_timeout` is a denial.
|
||||||
@@ -306,6 +312,134 @@ is on the device, and gets it once the device links again. Over `/api/v2` the ch
|
|||||||
the tree as it changes and the endpoints to read a subagent's transcript and findings, the same for
|
the tree as it changes and the endpoints to read a subagent's transcript and findings, the same for
|
||||||
a chat's own subagents and for an agent chat's — so Web, Desktop and Android draw both alike.
|
a chat's own subagents and for an agent chat's — so Web, Desktop and Android draw both alike.
|
||||||
|
|
||||||
|
## Scheduled work on a linked computer (spec 3.5.0)
|
||||||
|
|
||||||
|
A schedule (the server's: its rule, its runs, its history and notifications) may run **on a linked
|
||||||
|
computer**: each run is an agent turn on that device, in a folder of it, instead of a chat on the
|
||||||
|
server. The server decides *when*; the device decides *whether*, and *how far* — every rule an agent
|
||||||
|
chat has still holds, and the ones below are added. Numbers and texts: `loop.json` `scheduled`.
|
||||||
|
Conformance areas `scheduled`, `silent`, `monitor`.
|
||||||
|
|
||||||
|
### The device's setting: Accept scheduled work
|
||||||
|
|
||||||
|
- **`remote.scheduled`** in the device's global config, **off by default**; shown as **Accept
|
||||||
|
scheduled work** (`loop.json` `scheduled.setting`). Set from the command line with
|
||||||
|
`lembas service scheduled on|off` (also `lembas remote scheduled on|off`; `lembas remote list
|
||||||
|
--json` says `scheduled`), and by LLeMbas Desktop through the same command. It is global only: a
|
||||||
|
project's config cannot turn it on.
|
||||||
|
- It narrows remote work and widens nothing: a scheduled turn also needs `remote.enabled`, a folder
|
||||||
|
inside `remote.roots`, a trusted project where `remote.require_trust`, and a mode within
|
||||||
|
`remote.max_mode`, exactly as an agent chat does.
|
||||||
|
- The device says it in `limits.scheduled` (hello and `initialize`). Turning it on or off changes
|
||||||
|
the limits, so the device dials again once nothing works (as for any limit) and the client hears
|
||||||
|
it. The client may offer the device in a schedule's form only while `limits.scheduled` is true,
|
||||||
|
and says why not otherwise. The device judges it again at every `session/new`, `session/prompt`
|
||||||
|
and `_lembas/monitor` that is scheduled, whatever it said earlier.
|
||||||
|
- **Off** (or remote work off): every scheduled request is refused with `-32602`, the message
|
||||||
|
`loop.json` `scheduled.texts.off` word for word, and `data: { reason: "scheduled_off" }`. Not
|
||||||
|
retried: it is the person's setting. A folder the device does not accept is `-32602` with the
|
||||||
|
usual reason and `data: { reason: "folder" }`.
|
||||||
|
|
||||||
|
### Starting a scheduled turn
|
||||||
|
|
||||||
|
The client opens an agent session as for an agent chat (`session/new`: `cwd`, `lembas_model`,
|
||||||
|
`effort`, `_meta.lembas` with `agents` — the schedule's **Min/Max**, as an agent chat's — and the
|
||||||
|
`Context`), marked scheduled:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"_meta": { "mode": "edit", "lembas_model": "qwen", "lembas": {
|
||||||
|
"agents": { "min": 1, "max": 4 },
|
||||||
|
"scheduled": { "schedule_id": "sched1", "run_id": "run7", "title": "Waybread check",
|
||||||
|
"mode": "edit", "approval_wait_s": 1800 } } }
|
||||||
|
```
|
||||||
|
|
||||||
|
and runs the turn with `session/prompt`, its `_meta.lembas` carrying the same `scheduled` object
|
||||||
|
(and a `turnId`, the run's — so a prompt sent again after a dropped link gets the run's answer
|
||||||
|
instead of a second run). A schedule that continues one conversation (`continue`) prompts the same
|
||||||
|
session again on its next run; a fresh run opens a new one. The prompt's text is the server's own
|
||||||
|
for a scheduled run — the instruction, the run's time, and for a schedule that notifies only when
|
||||||
|
it matters the `[SILENT]` hint — composed exactly as for a run on the server; the device adds nothing.
|
||||||
|
|
||||||
|
**The mode** (`scheduled.mode`; `_meta.mode` is ignored for a scheduled session): `manual`, `edit`
|
||||||
|
or `auto`, and **`edit` when absent** — so a run is never wider than Edit unless the person picked
|
||||||
|
Auto on that schedule. `plan` is refused (`scheduled.texts.plan`, `data: { reason: "plan" }`):
|
||||||
|
nobody is there to approve a plan. Then lowered to `remote.max_mode`, as every remote session is —
|
||||||
|
Auto runs only where both the schedule and the device allow it (`conformance/scheduled.json`). The
|
||||||
|
mode holds for the turn; neither `_lembas/configure` nor a command in the prompt widens it.
|
||||||
|
|
||||||
|
In a scheduled turn:
|
||||||
|
|
||||||
|
- **Approvals** go to the person through the client (`session/request_permission` with
|
||||||
|
`_meta.lembas.scheduled: { schedule_id, run_id, wait_s }`), and the device waits
|
||||||
|
`approval_wait_s` — the schedule's wait, 30 minutes by default, clamped to
|
||||||
|
`scheduled.approval_wait_seconds` — instead of `remote.approval_timeout`. Unanswered by then, the
|
||||||
|
call is denied with `scheduled.texts.denied` as the feedback the model reads (`{{minutes}}` the
|
||||||
|
wait in whole minutes), the card is settled (`_lembas/permission/settled`), and the turn goes on —
|
||||||
|
the model works around the refusal or fails. Not `patient`.
|
||||||
|
- **`ask_user` and `plan_submit`** have nobody to answer: the device answers them as it does with
|
||||||
|
no client (`ask_user` tells the model nobody can be asked; `plan_submit` saves the plan and says
|
||||||
|
to stop). `show` is not offered: nobody is looking.
|
||||||
|
- Subagents as in an agent chat (`agents`, the tree, findings); a subagent's call only the person
|
||||||
|
can allow is an approval like the main agent's, with `scheduled` beside `subagent`.
|
||||||
|
|
||||||
|
### How the turn ends and reports
|
||||||
|
|
||||||
|
`session/prompt`'s answer carries the run's outcome in `_meta.lembas.scheduled`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "stopReason": "end_turn", "_meta": { "lembas": { "turnId": "run7",
|
||||||
|
"scheduled": { "run_id": "run7", "status": "ok", "text": "All the waybread is wrapped.",
|
||||||
|
"error": null } } } }
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`text`**: the turn's final reply — the text of the last assistant message the turn wrote, as
|
||||||
|
written (`[SILENT]` included). Model output: untrusted wherever it is shown.
|
||||||
|
- **`status`**: `cancelled` — the turn was cancelled (`session/cancel`: the person stopped it, or the
|
||||||
|
client's inactivity limit); `failed` — the turn ended on an error (`error` its text), or with no
|
||||||
|
reply text at all (`scheduled.texts.no_reply`); `silent` — the reply says the sentinel
|
||||||
|
`[SILENT]` on its first or last non-empty line (any case, a trailing full stop forgiven;
|
||||||
|
`conformance/silent.json`, the server's own rule for its runs); otherwise `ok`. A turn that hit the
|
||||||
|
step backstop or a budget and still wrote a reply is `ok`; `stopReason` says why it stopped.
|
||||||
|
- The client records the run from this (its history, its notification by the schedule's policy —
|
||||||
|
`silent` is never notified, a failure is told as a failure), and keeps the session as the run's
|
||||||
|
agent chat, whose events it received as for any agent chat. What the sentinel stripped looks like
|
||||||
|
is `silent.json`'s `stripped`.
|
||||||
|
|
||||||
|
### When the device is away, or refuses
|
||||||
|
|
||||||
|
- **Away** (not linked when the run is due, or the link drops before `session/new` is answered): the
|
||||||
|
client treats the device like a busy model — the run is `waiting` (`waiting_for: "device"`) and
|
||||||
|
tried again on the same steps a run waiting for a free model is, until the run's window closes;
|
||||||
|
then it is **missed**. A turn already running when the link drops is followed again by
|
||||||
|
`_lembas/session/status` and the turn id, as for any agent chat.
|
||||||
|
- **Refused** (`-32602` from a scheduled `session/new`, `session/prompt` or `_lembas/monitor`): the
|
||||||
|
run **fails** with the device's message, said in the run's history and in the failure
|
||||||
|
notification; `data.reason` (`scheduled_off`, `folder`, `plan`, `permission`) lets the client say
|
||||||
|
what to change. Not retried.
|
||||||
|
|
||||||
|
### Monitor mode: a command watched on the device
|
||||||
|
|
||||||
|
A schedule may **watch** something and run the model only when it changed. A URL is fetched by the
|
||||||
|
server and needs nothing from the device; a **command's output** is watched on the device with
|
||||||
|
`_lembas/monitor` (below): the client asks the device to run the watch command, compares the
|
||||||
|
answer's `hash` with the one the previous check stored, and when it is the same records the run as
|
||||||
|
`unchanged` — no model, nobody notified. Changed (or the first check), the model runs as the
|
||||||
|
schedule says — on the server, or as a scheduled turn on the device — told what changed through the
|
||||||
|
`excerpt`. A check that fails (timed out, refused) is a failed run.
|
||||||
|
|
||||||
|
The command runs under the **same permission rules as the schedule's mode**, with one difference:
|
||||||
|
**a command the mode would ask about is refused**, because a watch has nobody to ask. The device
|
||||||
|
judges the command line as `bash` would be judged in a session in that folder (the hardline, the
|
||||||
|
default rules, the global rules and — in a trusted project — the project's), and runs it only on
|
||||||
|
`allow`; `ask` is refused with `scheduled.texts.monitor_asks`, `deny` with
|
||||||
|
`scheduled.texts.monitor_denied`, both `-32602` with `data: { reason: "permission" }`
|
||||||
|
(`conformance/monitor.json`).
|
||||||
|
|
||||||
|
**The hash** (`monitor.json` `hash` cases): `sha256:` and the lowercase hex SHA-256 of the exit
|
||||||
|
status as decimal digits, a newline, and then the output's bytes exactly as read — standard output
|
||||||
|
and standard error as one stream, as the `bash` tool reads them. Nothing is normalised: a watch
|
||||||
|
command that prints a time changes every time, and should not.
|
||||||
|
|
||||||
## `_lembas/*` methods
|
## `_lembas/*` methods
|
||||||
|
|
||||||
Direction is who sends it. "→ result" marks a request; the others are notifications.
|
Direction is who sends it. "→ result" marks a request; the others are notifications.
|
||||||
@@ -313,7 +447,9 @@ Direction is who sends it. "→ result" marks a request; the others are notifica
|
|||||||
### `_lembas/hello` — agent → client
|
### `_lembas/hello` — agent → client
|
||||||
|
|
||||||
Sent once when the link opens, before `initialize`:
|
Sent once when the link opens, before `initialize`:
|
||||||
`{ protocol, version, harness_spec, device: { host, platform }, limits: { roots, max_mode, require_trust } }`.
|
`{ protocol, version, harness_spec, device: { host, platform }, limits: { roots, max_mode, require_trust, scheduled } }`
|
||||||
|
(`scheduled` since spec 3.5.0: whether the device accepts scheduled work; a change of it is a
|
||||||
|
change of the limits, below).
|
||||||
|
|
||||||
A device links whether its remote work is on or not, and with no roots at all. Off, or with no
|
A device links whether its remote work is on or not, and with no roots at all. Off, or with no
|
||||||
roots, it says `limits.roots: []` here and in `initialize`, and refuses every `session/new`, every
|
roots, it says `limits.roots: []` here and in `initialize`, and refuses every `session/new`, every
|
||||||
@@ -350,6 +486,22 @@ client hears the new limits.
|
|||||||
| `phase` | `progress` — the progress object (`progress.md` §1), or `null` when nothing runs; `agent?` — the subagent's id when it is a subagent's, absent for the main agent's (capability `progress`). Sent when the phase changes, and for a change of its data at most four times a second. The existing `progress` event (the prompt's tokens read) is unchanged |
|
| `phase` | `progress` — the progress object (`progress.md` §1), or `null` when nothing runs; `agent?` — the subagent's id when it is a subagent's, absent for the main agent's (capability `progress`). Sent when the phase changes, and for a change of its data at most four times a second. The existing `progress` event (the prompt's tokens read) is unchanged |
|
||||||
| `team` | `tree` — the session's subagent tree (`subagents.md` §12, the data) — whenever it changes, at most four times a second (capability `tree`). A client draws it and fetches nothing else to do so |
|
| `team` | `tree` — the session's subagent tree (`subagents.md` §12, the data) — whenever it changes, at most four times a second (capability `tree`). A client draws it and fetches nothing else to do so |
|
||||||
|
|
||||||
|
### `_lembas/monitor` — client → agent → result (capability `scheduled`)
|
||||||
|
|
||||||
|
The watch command of a schedule in monitor mode (*Monitor mode*, above). Params
|
||||||
|
`{ cwd, command, mode?, timeout_s?, schedule_id?, run_id? }` — `cwd` absolute, judged as
|
||||||
|
`session/new`'s; `mode` the schedule's (`manual`, `edit` or `auto`; `edit` when absent; `plan`
|
||||||
|
refused), lowered to `remote.max_mode`; `timeout_s` defaulting to and clamped by
|
||||||
|
`loop.json` `scheduled.monitor.timeout_seconds`. Refused like any scheduled request when the device
|
||||||
|
does not accept scheduled work. The command runs as `bash -c` in `cwd`, in its own process group,
|
||||||
|
with no input; at the timeout the group is stopped.
|
||||||
|
|
||||||
|
Result `{ hash, exit, timed_out, excerpt, truncated, bytes, duration_ms }`: `hash` as above (`null`
|
||||||
|
when it timed out), `exit` the exit status (`null` on a signal or a timeout), `excerpt` the start of
|
||||||
|
the output decoded as UTF-8 (invalid bytes replaced), at most `scheduled.monitor.excerpt_chars`
|
||||||
|
characters, `truncated` whether there was more, `bytes` the output's length. The device keeps at
|
||||||
|
most `scheduled.monitor.output_max_bytes` of output and hashes all of it as it streams.
|
||||||
|
|
||||||
### `_lembas/directories` — client → agent → result
|
### `_lembas/directories` — client → agent → result
|
||||||
|
|
||||||
Params `{ path?: string }`. Without a path: `{ path: "", places: Dir[] }`, the remote roots and every
|
Params `{ path?: string }`. Without a path: `{ path: "", places: Dir[] }`, the remote roots and every
|
||||||
@@ -682,6 +834,24 @@ Besides `/v1/models`, the chat completions, `/api/v1/instance` and `/mcp`:
|
|||||||
older `library` scope counts as `memory`. Local memory files are not synced up; logged out, there
|
older `library` scope counts as `memory`. Local memory files are not synced up; logged out, there
|
||||||
is no memory.
|
is no memory.
|
||||||
|
|
||||||
|
- **Schedules** (spec 3.5.0): the schedule routes of `/api/v2` — `GET`/`POST /api/v2/schedules`,
|
||||||
|
`GET`/`PATCH`/`DELETE /api/v2/schedules/{id}`, `POST …/{id}/pause`, `/resume`, `/run`,
|
||||||
|
`GET …/{id}/runs`, `GET …/{id}/runs/{run}`, `POST /api/v2/schedules/compile` and `/preview` —
|
||||||
|
**accept the device's token** (scope `link`) as well as an app's (`app`), for the same person and
|
||||||
|
under the same `schedule.use` permission; nothing else of `/api/v2` does. The shapes are the
|
||||||
|
server's (its contract fixtures); what the CLI's `/schedule` relies on: a schedule's `id`,
|
||||||
|
`title`, `instruction`, `description`, `enabled`, `next_run`, `last_run` (`status`, `timing`,
|
||||||
|
`due_at`, `finished_at`, `preview`, `error.text`), `running`; a run's `id`, `status`, `due_at`,
|
||||||
|
`started_at`, `finished_at`, `model.name`, `preview`, `error`; compile's `ok`, `reason`, `title`,
|
||||||
|
`instruction`, `rule`, `description`, `next_runs`, `timezone`. A run's `status` may also be
|
||||||
|
**`unchanged`** (monitor mode: the watch saw nothing new; no model ran), and `waiting_for` may be
|
||||||
|
**`device`**. A schedule that runs on a linked computer has
|
||||||
|
**`runs_on: { device, device_name, cwd, mode, approval_wait_minutes }`** (else `null`); one that
|
||||||
|
watches has **`watch: { kind: "url", url } | { kind: "command", command, timeout_s }`** (else
|
||||||
|
`null`). Creating one with the device's token, `runs_on.device: "this"` names the calling device.
|
||||||
|
A server without these answers 401 or 403 to the device's token, and the CLI says the server is
|
||||||
|
older.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- `_lembas/files/search`'s `root` is the session's working directory, which `@path` mentions
|
- `_lembas/files/search`'s `root` is the session's working directory, which `@path` mentions
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
{
|
||||||
|
"about": "Spec 3.5.0 (acp.md *Monitor mode*, `_lembas/monitor`): the watch command on a device. `hash`: `exit` and `output` (the bytes as UTF-8) → the answer's `hash`. `judge`: a command line in the schedule's mode, with the default rules (and any extra global rules given), the project root /project and nothing named on disk, as conformance/permission.json judges it → `run` (the mode allows it without asking), or `{refused}` with `asks` (the mode would ask: scheduled.texts.monitor_asks) or `denied` (scheduled.texts.monitor_denied). The device's own; the server keeps only the hash.",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"about": "Exit 0 and a line of output.",
|
||||||
|
"kind": "hash",
|
||||||
|
"exit": 0,
|
||||||
|
"output": "ok\n",
|
||||||
|
"expect": "sha256:9dbd5131695af2d8636ea64178344ffcb5d682333c2ac8342153b5f8a999037a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "The same output with another exit status is a change.",
|
||||||
|
"kind": "hash",
|
||||||
|
"exit": 1,
|
||||||
|
"output": "ok\n",
|
||||||
|
"expect": "sha256:262115bdbc989849fb45967adac594dde8d45deb291f3a26b38fb2bf4e10512b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "No output.",
|
||||||
|
"kind": "hash",
|
||||||
|
"exit": 0,
|
||||||
|
"output": "",
|
||||||
|
"expect": "sha256:9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A trailing newline counts: nothing is normalised.",
|
||||||
|
"kind": "hash",
|
||||||
|
"exit": 0,
|
||||||
|
"output": "ok",
|
||||||
|
"expect": "sha256:05df918976ca296956abf9b5fb424bb9bef7c32bb471b1005b417abc696e25a9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "UTF-8 output, hashed as its bytes.",
|
||||||
|
"kind": "hash",
|
||||||
|
"exit": 0,
|
||||||
|
"output": "lembas · ✓\n",
|
||||||
|
"expect": "sha256:cb00179ad9e36546b1b973ded0903592a6b975552b5596feac573426c67e24e4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A read-only command in Edit runs.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "git status",
|
||||||
|
"mode": "edit",
|
||||||
|
"expect": "run"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A command Edit would ask about is refused: a watch has nobody to ask.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "rm -rf build",
|
||||||
|
"mode": "edit",
|
||||||
|
"expect": {
|
||||||
|
"refused": "asks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "In Auto it runs.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "rm -rf build",
|
||||||
|
"mode": "auto",
|
||||||
|
"expect": "run"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "The hardline is a floor in every mode.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "rm -rf /",
|
||||||
|
"mode": "auto",
|
||||||
|
"expect": {
|
||||||
|
"refused": "denied"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A line that pipes a download into a shell asks in Manual: refused.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "curl -s https://example.org/feed | sh",
|
||||||
|
"mode": "manual",
|
||||||
|
"expect": {
|
||||||
|
"refused": "asks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A rule the person wrote allows it.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "make check",
|
||||||
|
"mode": "edit",
|
||||||
|
"rules": {
|
||||||
|
"bash": {
|
||||||
|
"make check": "allow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expect": "run"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A rule the person wrote denies it, even in Auto.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "make deploy",
|
||||||
|
"mode": "auto",
|
||||||
|
"rules": {
|
||||||
|
"bash": {
|
||||||
|
"make deploy": "deny"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expect": {
|
||||||
|
"refused": "denied"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Reading a secret asks: refused.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "cat .env",
|
||||||
|
"mode": "edit",
|
||||||
|
"expect": {
|
||||||
|
"refused": "asks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "An ordinary listing runs in Manual.",
|
||||||
|
"kind": "judge",
|
||||||
|
"line": "ls -la",
|
||||||
|
"mode": "manual",
|
||||||
|
"expect": "run"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"about": "Spec 3.5.0 (acp.md *Scheduled work on a linked computer*): what a device makes of a scheduled turn. `accept`: the device's Accept scheduled work (remote.scheduled; false means remote work is off as well, the same refusal); `mode`: the schedule's (absent: none chosen); `max_mode`: remote.max_mode; `approval_wait_s`: the schedule's wait (absent: none). → `{mode, wait_s}` — the session's mode and how long an approval waits — or `{refused}` with the `data.reason` of the -32602 (`scheduled_off`, `plan`), whose message is loop.json scheduled.texts.off / .plan. Written from the rules; the device's other refusals (folder, trust) are the agent chat's.",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"about": "Off by default: refused, whatever else is asked.",
|
||||||
|
"accept": false,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "edit",
|
||||||
|
"expect": {
|
||||||
|
"refused": "scheduled_off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "No mode chosen: Edit.",
|
||||||
|
"accept": true,
|
||||||
|
"max_mode": "edit",
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "No mode chosen, the device allows Auto: still Edit — Auto only when the schedule says so.",
|
||||||
|
"accept": true,
|
||||||
|
"max_mode": "auto",
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Auto chosen and allowed.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "auto",
|
||||||
|
"max_mode": "auto",
|
||||||
|
"expect": {
|
||||||
|
"mode": "auto",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Auto chosen, the device allows Edit at most: lowered.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "auto",
|
||||||
|
"max_mode": "edit",
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Edit chosen, the device allows Manual at most: lowered.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "manual",
|
||||||
|
"expect": {
|
||||||
|
"mode": "manual",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Manual chosen.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "manual",
|
||||||
|
"max_mode": "auto",
|
||||||
|
"expect": {
|
||||||
|
"mode": "manual",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Plan is refused: nobody is there to approve a plan.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "plan",
|
||||||
|
"max_mode": "auto",
|
||||||
|
"expect": {
|
||||||
|
"refused": "plan"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "An unknown mode is read as the default, never as anything wider.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "unrestricted",
|
||||||
|
"max_mode": "auto",
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "The device allows Plan at most: every scheduled turn is lowered to it.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "plan",
|
||||||
|
"expect": {
|
||||||
|
"mode": "plan",
|
||||||
|
"wait_s": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "The wait, in seconds, as the schedule says.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "edit",
|
||||||
|
"approval_wait_s": 600,
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 600
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Below the floor: one minute.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "edit",
|
||||||
|
"approval_wait_s": 5,
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Above the ceiling: a day.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "edit",
|
||||||
|
"approval_wait_s": 999999,
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 86400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A fraction is cut to whole seconds.",
|
||||||
|
"accept": true,
|
||||||
|
"mode": "edit",
|
||||||
|
"max_mode": "edit",
|
||||||
|
"approval_wait_s": 90.7,
|
||||||
|
"expect": {
|
||||||
|
"mode": "edit",
|
||||||
|
"wait_s": 90
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"about": "Spec 3.5.0: whether a scheduled run's reply is `silent` — loop.json scheduled.sentinel on its first or last non-empty line, compared after trimming, any case, every trailing full stop forgiven — and the reply with every sentinel line taken out (`stripped`, the text a preview shows), trimmed. The server's rule for its own runs, and the device's for a scheduled turn.",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"about": "The sentinel alone.",
|
||||||
|
"text": "[SILENT]",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Lower case, with a full stop.",
|
||||||
|
"text": "[silent].",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "On the last line, after a sentence.",
|
||||||
|
"text": "Nothing new since yesterday.\n[SILENT]",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": "Nothing new since yesterday."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "On the first line, before a sentence.",
|
||||||
|
"text": "[SILENT]\nChecked the feed: nothing.",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": "Checked the feed: nothing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "In the middle: not silent (only the first or last line counts), and the line still goes from the preview.",
|
||||||
|
"text": "Two new posts.\n[SILENT]\nBoth about waybread.",
|
||||||
|
"expect": {
|
||||||
|
"silent": false,
|
||||||
|
"stripped": "Two new posts.\nBoth about waybread."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Inside a sentence: not silent.",
|
||||||
|
"text": "I would say [SILENT] but there is news: the build broke.",
|
||||||
|
"expect": {
|
||||||
|
"silent": false,
|
||||||
|
"stripped": "I would say [SILENT] but there is news: the build broke."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Blank lines around it are not lines.",
|
||||||
|
"text": "\n\n [SILENT] \n\n",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "A space before the full stop.",
|
||||||
|
"text": "All quiet.\n[SILENT] .",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": "All quiet."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Without brackets: not the sentinel.",
|
||||||
|
"text": "SILENT",
|
||||||
|
"expect": {
|
||||||
|
"silent": false,
|
||||||
|
"stripped": "SILENT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Empty: not silent.",
|
||||||
|
"text": "",
|
||||||
|
"expect": {
|
||||||
|
"silent": false,
|
||||||
|
"stripped": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "An ordinary reply.",
|
||||||
|
"text": "The backup finished at 03:12; 4.2 GB.",
|
||||||
|
"expect": {
|
||||||
|
"silent": false,
|
||||||
|
"stripped": "The backup finished at 03:12; 4.2 GB."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"about": "Two full stops are forgiven too (every trailing one is).",
|
||||||
|
"text": "[SILENT]..",
|
||||||
|
"expect": {
|
||||||
|
"silent": true,
|
||||||
|
"stripped": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"about": "Teams (team.md, spec 3.2.0; the main agent and its tree, subagents.md §5–§13, spec 3.4.0 — a member's team_add is now the lead's with `parent`, and `offered` lists 3.4.0's tools): one lead session's team, step by step, written from a reference model of the rules. `start`: the size setting `min` and `max`, `subagents` (enabled for the session; default true) and the chat model's `capacity` (models/schema.json: `parallel`, `single_session`; {} — no limit). The lead is `lead`; its first prompt is running as the case begins, and every agent is on the chat's model, and it is the only model there is (spec 3.3.0: other models are subagents.json's). Steps and what each answers: `offered` (`by` an agent, default lead) — `{tools, setting}`: the team tools listed for that agent, sorted, and whether the setting is shown; `call` (`by`, `tool`, `args`) — `{result, delivered}`: the call's answer word for word (null where an ordinary tool would run: the case looks only at whether it is refused), and what was delivered to whom by this step, `{name: [message, …]}`; `team_read` answers `{room, delivered}` instead, `room` being the messages it shows as `{n, from, to}` (the tree and the text format are loop.json team.read's, checked by each project's own tests); `task` runs a subagent that counts toward the size (and holds a slot) until `task_done` (`by`) — answers null; `finish` (`by`, `reply`, `reason`: stop by default, or steps, budget, `error — <text>`) — the agent's turn ends: a member's reply is its report — `{delivered}`; `set` (`min`, `max`, `capacity`: the person's setting or the model's metadata changed) — null; `person_stop` (`name`) — the person stopped that member in the UI — null; `user` — the person's message to the lead — `{note}`: the note on it (context.md §10), null when there is none; `state` — the team in tree order as `{name, manager, state}`: working, waiting (for a slot), finished, idle (the lead between turns). Delivering to a finished member or the idle lead wakes it (it is then working, or waiting when its model is full). Spec 3.4.0 adds: `start.mode` — the main agent's mode (default edit); `call` to team_whoami, team_finish, team_findings, team_request, team_decide, team_assign, team_replace, answered word for word; `context` (`by`, `used`, `window`) — its engine's context use — null; `context_full` (`by`) — its context reached team.context.pause_at: `{delivered}`; `started` (`by`) — `{text}`: what its latest turn was started with (a brief, or the messages that woke it); `tree` — `{size, open_slots, agents: [{id, name, parent, state, mode, request}], ended: [{id, name, state}]}`; `main_mode` (`mode`) — the main agent's mode changed — null. LLeMbas: services/team.py; CLI: team/.",
|
"about": "Teams (team.md, spec 3.2.0; the main agent and its tree, subagents.md §5–§13, spec 3.4.0 — a member's team_add is now the lead's with `parent`, and `offered` lists 3.4.0's tools): one lead session's team, step by step, written from a reference model of the rules. `start`: the size setting `min` and `max`, `subagents` (enabled for the session; default true) and the chat model's `capacity` (models/schema.json: `parallel`, `single_session`; {} — no limit). The lead is `lead`; its first prompt is running as the case begins, and every agent is on the chat's model, and it is the only model there is (spec 3.3.0: other models are subagents.json's). Spec 3.5.0: a subagent added under a sub-manager is said to report to it (`added_under`). Steps and what each answers: `offered` (`by` an agent, default lead) — `{tools, setting}`: the team tools listed for that agent, sorted, and whether the setting is shown; `call` (`by`, `tool`, `args`) — `{result, delivered}`: the call's answer word for word (null where an ordinary tool would run: the case looks only at whether it is refused), and what was delivered to whom by this step, `{name: [message, …]}`; `team_read` answers `{room, delivered}` instead, `room` being the messages it shows as `{n, from, to}` (the tree and the text format are loop.json team.read's, checked by each project's own tests); `task` runs a subagent that counts toward the size (and holds a slot) until `task_done` (`by`) — answers null; `finish` (`by`, `reply`, `reason`: stop by default, or steps, budget, `error — <text>`) — the agent's turn ends: a member's reply is its report — `{delivered}`; `set` (`min`, `max`, `capacity`: the person's setting or the model's metadata changed) — null; `person_stop` (`name`) — the person stopped that member in the UI — null; `user` — the person's message to the lead — `{note}`: the note on it (context.md §10), null when there is none; `state` — the team in tree order as `{name, manager, state}`: working, waiting (for a slot), finished, idle (the lead between turns). Delivering to a finished member or the idle lead wakes it (it is then working, or waiting when its model is full). Spec 3.4.0 adds: `start.mode` — the main agent's mode (default edit); `call` to team_whoami, team_finish, team_findings, team_request, team_decide, team_assign, team_replace, answered word for word; `context` (`by`, `used`, `window`) — its engine's context use — null; `context_full` (`by`) — its context reached team.context.pause_at: `{delivered}`; `started` (`by`) — `{text}`: what its latest turn was started with (a brief, or the messages that woke it); `tree` — `{size, open_slots, agents: [{id, name, parent, state, mode, request}], ended: [{id, name, state}]}`; `main_mode` (`mode`) — the main agent's mode changed — null. LLeMbas: services/team.py; CLI: team/.",
|
||||||
"cases": [
|
"cases": [
|
||||||
{
|
{
|
||||||
"about": "Offered on a model that serves two requests at once: the lead has all four tools and the setting is shown.",
|
"about": "Offered on a model that serves two requests at once: the lead has all four tools and the setting is shown.",
|
||||||
@@ -717,7 +717,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added db (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 4 agents.",
|
"result": "Added db (owns a part) under backend: it is working. Its report goes to backend when it finishes. The team is 3 of 4 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1637,7 +1637,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1878,7 +1878,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2155,11 +2155,11 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 8 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 8 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added db (owns a part): it is working. Its report comes to you when it finishes. The team is 4 of 8 agents.",
|
"result": "Added db (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 4 of 8 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2464,7 +2464,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 5 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 5 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2836,15 +2836,15 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 8 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 8 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added auth (owns a part): it is working. Its report comes to you when it finishes. The team is 4 of 8 agents.",
|
"result": "Added auth (owns a part) under routes: it is working. Its report goes to routes when it finishes. The team is 4 of 8 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added db (owns a part): it is working. Its report comes to you when it finishes. The team is 5 of 8 agents.",
|
"result": "Added db (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 5 of 8 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2890,7 +2890,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
"result": "Added auth (owns a part): it is working. Its report comes to you when it finishes. The team is 5 of 8 agents.",
|
"result": "Added auth (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 5 of 8 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2968,7 +2968,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3647,7 +3647,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3978,7 +3978,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -4382,7 +4382,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -4843,7 +4843,7 @@
|
|||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"result": "Added routes (owns a part): it is working. Its report comes to you when it finishes. The team is 3 of 6 agents.",
|
"result": "Added routes (owns a part) under api: it is working. Its report goes to api when it finishes. The team is 3 of 6 agents.",
|
||||||
"delivered": {}
|
"delivered": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -146,6 +146,8 @@
|
|||||||
"texts": {
|
"texts": {
|
||||||
"added": "Added {{name}} ({{role}}): it is working. Its report comes to you when it finishes. The team is {{size}} of {{max}} agents.",
|
"added": "Added {{name}} ({{role}}): it is working. Its report comes to you when it finishes. The team is {{size}} of {{max}} agents.",
|
||||||
"added_waiting": "Added {{name}} ({{role}}): it waits for a free slot — the model serves {{parallel}} requests at once and all are busy — and starts when one frees. Its report comes to you when it finishes. The team is {{size}} of {{max}} agents.",
|
"added_waiting": "Added {{name}} ({{role}}): it waits for a free slot — the model serves {{parallel}} requests at once and all are busy — and starts when one frees. Its report comes to you when it finishes. The team is {{size}} of {{max}} agents.",
|
||||||
|
"added_under": "Added {{name}} ({{role}}) under {{parent}}: it is working. Its report goes to {{parent}} when it finishes. The team is {{size}} of {{max}} agents.",
|
||||||
|
"added_waiting_under": "Added {{name}} ({{role}}) under {{parent}}: it waits for a free slot — the model serves {{parallel}} requests at once and all are busy — and starts when one frees. Its report goes to {{parent}} when it finishes. The team is {{size}} of {{max}} agents.",
|
||||||
"sent": "Sent to {{to}}.",
|
"sent": "Sent to {{to}}.",
|
||||||
"stopped": "Stopped {{names}}. The team is {{size}} of {{max}} agents.",
|
"stopped": "Stopped {{names}}. The team is {{size}} of {{max}} agents.",
|
||||||
"manager": "You lead a team: give this to a member (team_add or team_send).",
|
"manager": "You lead a team: give this to a member (team_add or team_send).",
|
||||||
@@ -438,5 +440,48 @@
|
|||||||
],
|
],
|
||||||
"what_max": 80,
|
"what_max": 80,
|
||||||
"pace_per_second": 4
|
"pace_per_second": 4
|
||||||
|
},
|
||||||
|
"scheduled": {
|
||||||
|
"about": "Spec 3.5.0 (acp.md *Scheduled work on a linked computer*): a schedule's run as an agent turn on a linked computer, and the watch command of monitor mode. `setting` is how the device's switch is shown; `modes` the modes a schedule may choose (`default_mode` when it chose none), lowered to remote.max_mode; `approval_wait_seconds` how long an approval in a scheduled turn waits (the schedule's wait, clamped); `sentinel` the word a reply says when there is nothing to tell; `monitor` the watch command's limits. conformance/scheduled.json, silent.json, monitor.json.",
|
||||||
|
"setting": {
|
||||||
|
"key": "remote.scheduled",
|
||||||
|
"label": "Accept scheduled work",
|
||||||
|
"hint": "Let your schedules run here as agent turns, in the folders remote work may use. Off by default."
|
||||||
|
},
|
||||||
|
"modes": [
|
||||||
|
"manual",
|
||||||
|
"edit",
|
||||||
|
"auto"
|
||||||
|
],
|
||||||
|
"default_mode": "edit",
|
||||||
|
"approval_wait_seconds": {
|
||||||
|
"default": 1800,
|
||||||
|
"min": 60,
|
||||||
|
"max": 86400
|
||||||
|
},
|
||||||
|
"sentinel": "[SILENT]",
|
||||||
|
"statuses": [
|
||||||
|
"ok",
|
||||||
|
"silent",
|
||||||
|
"failed",
|
||||||
|
"cancelled"
|
||||||
|
],
|
||||||
|
"monitor": {
|
||||||
|
"timeout_seconds": {
|
||||||
|
"default": 60,
|
||||||
|
"min": 1,
|
||||||
|
"max": 600
|
||||||
|
},
|
||||||
|
"excerpt_chars": 2000,
|
||||||
|
"output_max_bytes": 1048576
|
||||||
|
},
|
||||||
|
"texts": {
|
||||||
|
"off": "This computer does not accept scheduled work. Turn on \"Accept scheduled work\" on it (lembas service scheduled on).",
|
||||||
|
"plan": "A scheduled run cannot use plan mode: nobody is there to approve a plan. Choose Manual, Edit or Auto.",
|
||||||
|
"denied": "Nobody answered this approval within {{minutes}} minutes, so it counts as denied.",
|
||||||
|
"monitor_asks": "The watch command would need approval in {{mode}} mode ({{reason}}), and a watch has nobody to ask. Allow the command in the project's permission rules, or choose a wider mode for the schedule.",
|
||||||
|
"monitor_denied": "The watch command is refused here: {{reason}}.",
|
||||||
|
"no_reply": "The run ended without a reply."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user