@archmax-ai/harness 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/dist/agent.d.ts +4 -2
- package/dist/agent.js +4 -2
- package/dist/agent.js.map +1 -1
- package/dist/assembly/compose.d.ts +5 -3
- package/dist/assembly/compose.js +24 -8
- package/dist/assembly/compose.js.map +1 -1
- package/dist/assembly/index.d.ts +13 -2
- package/dist/assembly/index.js +35 -10
- package/dist/assembly/index.js.map +1 -1
- package/dist/authoring-skill/archmax-harness/SKILL.md +9 -5
- package/dist/authoring-skill/archmax-harness/references/backend-integration.md +12 -8
- package/dist/authoring-skill/archmax-harness/references/cases.md +3 -2
- package/dist/authoring-skill/archmax-harness/references/diagnostics.md +5 -3
- package/dist/authoring-skill/archmax-harness/references/hook-and-test-scripts.md +52 -7
- package/dist/authoring-skill/archmax-harness/references/hooks-and-scripts.md +2 -1
- package/dist/authoring-skill/archmax-harness/references/wiring.md +1 -1
- package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +107 -46
- package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +43 -17
- package/dist/cli/turn.js +1 -2
- package/dist/cli/turn.js.map +1 -1
- package/dist/core/clock.d.ts +26 -0
- package/dist/core/clock.js +64 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/deepagents.d.ts +9 -2
- package/dist/core/deepagents.js +15 -2
- package/dist/core/deepagents.js.map +1 -1
- package/dist/core/message-readers.d.ts +21 -0
- package/dist/core/message-readers.js +36 -0
- package/dist/core/message-readers.js.map +1 -1
- package/dist/core/messages.d.ts +8 -8
- package/dist/core/messages.js +11 -13
- package/dist/core/messages.js.map +1 -1
- package/dist/core/platform-prompt.generated.js +1 -1
- package/dist/core/platform-prompt.generated.js.map +1 -1
- package/dist/core/workspace-prompt.js +2 -1
- package/dist/core/workspace-prompt.js.map +1 -1
- package/dist/kernel/kernel.js +15 -10
- package/dist/kernel/kernel.js.map +1 -1
- package/dist/lifecycle/runner.d.ts +14 -0
- package/dist/lifecycle/runner.js +50 -4
- package/dist/lifecycle/runner.js.map +1 -1
- package/dist/lifecycle/tool-calls.d.ts +9 -0
- package/dist/lifecycle/tool-calls.js +57 -0
- package/dist/lifecycle/tool-calls.js.map +1 -0
- package/dist/lifecycle/transcript.d.ts +10 -6
- package/dist/lifecycle/transcript.js +15 -10
- package/dist/lifecycle/transcript.js.map +1 -1
- package/dist/machine/allow.d.ts +17 -2
- package/dist/machine/allow.js +16 -6
- package/dist/machine/allow.js.map +1 -1
- package/dist/machine/lint-spec.js +77 -29
- package/dist/machine/lint-spec.js.map +1 -1
- package/dist/machine/machine.d.ts +38 -17
- package/dist/machine/machine.js +64 -32
- package/dist/machine/machine.js.map +1 -1
- package/dist/machine/spec-schema.d.ts +24 -13
- package/dist/machine/spec-schema.js +91 -15
- package/dist/machine/spec-schema.js.map +1 -1
- package/dist/machine/tool-names.d.ts +16 -7
- package/dist/machine/tool-names.js +17 -8
- package/dist/machine/tool-names.js.map +1 -1
- package/dist/machine/tool-paths.d.ts +10 -3
- package/dist/machine/tool-paths.js +14 -3
- package/dist/machine/tool-paths.js.map +1 -1
- package/dist/machine/types.d.ts +23 -9
- package/dist/machine/types.js +2 -0
- package/dist/machine/types.js.map +1 -1
- package/dist/machine/variables.d.ts +2 -0
- package/dist/machine/variables.js.map +1 -1
- package/dist/public/sandbox.d.ts +35 -3
- package/dist/public/sandbox.js +1 -1
- package/dist/public/sandbox.js.map +1 -1
- package/dist/public/spec.d.ts +5 -5
- package/dist/public/spec.js +4 -4
- package/dist/public/spec.js.map +1 -1
- package/dist/sandbox/executor.js +10 -0
- package/dist/sandbox/executor.js.map +1 -1
- package/dist/sessions/operations.js +4 -5
- package/dist/sessions/operations.js.map +1 -1
- package/dist/sessions/resume.d.ts +10 -5
- package/dist/sessions/resume.js +11 -8
- package/dist/sessions/resume.js.map +1 -1
- package/dist/sessions/scope.d.ts +10 -0
- package/dist/sessions/scope.js +17 -0
- package/dist/sessions/scope.js.map +1 -1
- package/dist/testing/case-document.d.ts +2 -0
- package/dist/testing/case-document.js.map +1 -1
- package/dist/testing/case-schema.d.ts +1 -1
- package/dist/testing/case-schema.js +17 -1
- package/dist/testing/case-schema.js.map +1 -1
- package/dist/testing/driver.d.ts +4 -0
- package/dist/testing/driver.js +19 -3
- package/dist/testing/driver.js.map +1 -1
- package/dist/testing/grade.js +3 -3
- package/dist/testing/grade.js.map +1 -1
- package/dist/testing/interpreter.js +1 -0
- package/dist/testing/interpreter.js.map +1 -1
- package/dist/workflow/checkpointer.d.ts +7 -0
- package/dist/workflow/checkpointer.js +28 -0
- package/dist/workflow/checkpointer.js.map +1 -1
- package/dist/workflow/control-tools.js +26 -2
- package/dist/workflow/control-tools.js.map +1 -1
- package/dist/workflow/governance.d.ts +3 -1
- package/dist/workflow/governance.js +27 -6
- package/dist/workflow/governance.js.map +1 -1
- package/dist/workflow/middleware.js +22 -4
- package/dist/workflow/middleware.js.map +1 -1
- package/dist/workflow/on-error.js +2 -0
- package/dist/workflow/on-error.js.map +1 -1
- package/dist/workflow/parks.js +2 -0
- package/dist/workflow/parks.js.map +1 -1
- package/dist/workflow/render-prompt.d.ts +2 -1
- package/dist/workflow/render-prompt.js +24 -5
- package/dist/workflow/render-prompt.js.map +1 -1
- package/dist/workflow/state.d.ts +41 -0
- package/dist/workflow/state.js +33 -4
- package/dist/workflow/state.js.map +1 -1
- package/dist/workflow/sub-workflow.js +7 -3
- package/dist/workflow/sub-workflow.js.map +1 -1
- package/dist/workflow/tool-service.d.ts +1 -1
- package/dist/workflow/tool-service.js +1 -1
- package/dist/workflow/tool-service.js.map +1 -1
- package/dist/workflow/turn-boundary.js +8 -0
- package/dist/workflow/turn-boundary.js.map +1 -1
- package/dist/workflow/turn-runner.js +36 -7
- package/dist/workflow/turn-runner.js.map +1 -1
- package/dist/workflow/visits.d.ts +21 -0
- package/dist/workflow/visits.js +45 -0
- package/dist/workflow/visits.js.map +1 -0
- package/package.json +1 -1
- package/skills/archmax-harness/SKILL.md +9 -5
- package/skills/archmax-harness/references/backend-integration.md +12 -8
- package/skills/archmax-harness/references/cases.md +3 -2
- package/skills/archmax-harness/references/diagnostics.md +5 -3
- package/skills/archmax-harness/references/hook-and-test-scripts.md +52 -7
- package/skills/archmax-harness/references/hooks-and-scripts.md +2 -1
- package/skills/archmax-harness/references/wiring.md +1 -1
- package/skills/archmax-harness/references/workflow-schema.md +107 -46
- package/skills/archmax-harness/references/workflow-yaml.md +43 -17
|
@@ -161,10 +161,41 @@ export default async function hook({ state, phase, trigger, variables, messages,
|
|
|
161
161
|
| `state`, `phase` | The state whose hook is running; `"before"` or `"after"`. |
|
|
162
162
|
| `trigger` | The trigger **id string** the run started from. It carries no payload — the run's input is in `variables`. |
|
|
163
163
|
| `variables` | The run's variables as a plain `name → value` map, including the built-in `trigger`. Structured values are present whole (`variables.order.items[0].sku`). **Read-only**: assigning changes nothing, and the variable tools are absent from `tools.*` — a hook that could write variables would be editing the governance inputs of the state it judges. |
|
|
164
|
-
| `messages` | The
|
|
164
|
+
| `messages` | The transcript of the **current visit**, newest last, as `{ role, text, toolCalls? }`: everything since the session entered the state it is in. On an advance that is the state being left, for its `after` hook and the target's `before` hook alike; a visit spans turns, and entering a state again starts a new one. `role` is `user`, `assistant`, `tool`, `system`, or **`runtime`** — a note the runtime wrote (an arrival, a decision, an error route, a completion check; `note` names the kind). A `tool` entry carries `tool` and the call's `id` with an empty `text`: its result comes from `toolCalls`. A hook that reacts to narration matches `role === "runtime"`, never a `[bracket]` prefix in a user message. |
|
|
165
|
+
| `toolCalls(filter?)` | **Async.** The session's tool calls with their results, across every visit, oldest first: `{ id, name, args, result, status, state, visit }` (`result` parsed when it is JSON; `status` `completed`, `failed`, `rejected` by governance, or `pending`). `filter` narrows by `name`, `state`, `visit` (a number or `"current"`), `status` and `last`; an unknown key throws. The control tools are not in it, nor are calls a script or hook made through `tools.*`. Only the records it returns are copied into the sandbox. |
|
|
165
166
|
| `from`, `to`, `reason` | Present on `after` hooks: the transition the agent is attempting (`reason` is the agent's `archmax_advance` reason). |
|
|
166
167
|
| `tools` | The privileged tool-call bridge (async), camelCased: `await tools.readFile({ file_path: "skills/order-data/assets/orders.json" })` → file text, or the binary notice for a binary file (see below). See [Governance](#governance-of-tools-calls). |
|
|
167
168
|
|
|
169
|
+
**What a hook checks.** The conversation is the model's memory, not evidence: a
|
|
170
|
+
hook checks its own visit and the session's records — `variables`, files, and
|
|
171
|
+
the tool results `toolCalls` keeps as the tools returned them. Check a result
|
|
172
|
+
itself, never a copy the model wrote into a variable, and never call a tool
|
|
173
|
+
again just to see its result (for a host whose tools reach an external system,
|
|
174
|
+
that is a second action):
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
/**
|
|
178
|
+
* Cancel gate — an order may be cancelled only while it is pending.
|
|
179
|
+
*
|
|
180
|
+
* Finds the latest get_order call for the order being cancelled and vetoes
|
|
181
|
+
* unless its result says pending.
|
|
182
|
+
*/
|
|
183
|
+
export default async function hook({ toolCalls }) {
|
|
184
|
+
const [cancel] = await toolCalls({ name: "cancel_order", visit: "current", last: 1 });
|
|
185
|
+
if (!cancel) return ok();
|
|
186
|
+
const lookups = await toolCalls({ name: "get_order", status: "completed" });
|
|
187
|
+
const order = lookups.findLast((c) => c.args.id === cancel.args.id);
|
|
188
|
+
if (!order) return veto(`Look order ${cancel.args.id} up with get_order before cancelling it.`);
|
|
189
|
+
if (order.result.status !== "pending") return veto(`Order ${cancel.args.id} is ${order.result.status}, not pending.`);
|
|
190
|
+
return ok();
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
What a later state or turn needs from the conversation (the person's words, a
|
|
195
|
+
decision) is stored in a variable or a file by the state that has it. A `before`
|
|
196
|
+
hook re-run by `archmax_reset` starts a new visit and no longer sees the
|
|
197
|
+
session's opening request.
|
|
198
|
+
|
|
168
199
|
**The verdict** — the three helpers are globals:
|
|
169
200
|
|
|
170
201
|
| Return | Effect |
|
|
@@ -275,8 +306,9 @@ resolves to an `Error: …` string instead.
|
|
|
275
306
|
**Authoring rule for `archmax_run` scripts:** if sandboxed code calls `tools.X`,
|
|
276
307
|
the state's `allow` list must cover `tool: X` (with any argument constraints the
|
|
277
308
|
script's call satisfies), or the call is blocked. Grant it per state with
|
|
278
|
-
`tools.allow`, or workflow-wide with `tools.allow_always
|
|
279
|
-
|
|
309
|
+
`tools.allow`, or workflow-wide with `tools.allow_always`; a default tool such as
|
|
310
|
+
`read_file` is covered wherever the workflow-wide grant includes it. Lifecycle
|
|
311
|
+
hooks need no such grant.
|
|
280
312
|
|
|
281
313
|
### `archmax_run` scripts: the `args` contract
|
|
282
314
|
|
|
@@ -501,6 +533,9 @@ get the exact same flow when they return `correct(reason)` under a
|
|
|
501
533
|
into a verdict **fails closed** (veto with a snippet of the raw output). A rubric
|
|
502
534
|
receives a compact JSON payload (`userRequest`, `history`, `state`, `phase`) —
|
|
503
535
|
its `instructions` are written against that, not against a hook's `messages`.
|
|
536
|
+
`history` is the visit the hook gates, tool results in full; `userRequest` is the
|
|
537
|
+
person's latest message (what the reply answers), which an earlier state usually
|
|
538
|
+
received.
|
|
504
539
|
|
|
505
540
|
---
|
|
506
541
|
|
|
@@ -607,6 +642,7 @@ Unknown keys are validation **errors**. All keys except `title` and
|
|
|
607
642
|
| `trigger` | `{ id }` | The trigger every driven turn of the case runs under. Carries **only an id**; `args` is rejected with a pointer to `variables:`. Validated against the workflow machine before any step — an unknown id fails the case (even with no steps), listing the workflow's declared triggers. Omitted ⇒ the manual trigger. See [Start conditions](#start-conditions-trigger-variables-and-workspace). |
|
|
608
643
|
| `workspace` | `{ <path>: <content> }` | Files seeded into the case's run workspace **before any step**. Strings are written verbatim; non-string YAML values are JSON-serialized; a single-key `{ from: <tests/-relative path> }` mapping copies a file byte-for-byte. Only agent-visible run-zone paths are seedable (root-level files like `trigger.json`, or `scratchpad/…`); authored (`skills/…`), runtime-internal, and escaping paths are rejected before anything is written. |
|
|
609
644
|
| `mocks` | `[{ tool, whenInput?, result }]` | Declarative tool mocks: when the driven agent calls `tool` with input partially matching `whenInput`, return `result` instead of executing. Intercepts **agent-initiated and PTC** (script `tools.*`) calls alike. See [the mocking section](#settle-the-mocking-depth-before-writing-a-case). |
|
|
645
|
+
| `clock` | `"2026-03-20"` / `"…T14:30:00Z"` | The date and time every model call of the case states as "now": an ISO 8601 date (midnight UTC) or a date-time with `Z` or an offset; a zoneless or impossible value is a schema error. Carried on every action, a handoff and a child session included, and outranks the host's `clock` option. See [Start conditions](#start-conditions-trigger-variables-and-workspace). |
|
|
610
646
|
| `steps` | flat list of step entries | The case script: actions and assertions as peers (below). |
|
|
611
647
|
|
|
612
648
|
There is **no multi-session surface** in test spec version 1 — no `sessions:`,
|
|
@@ -665,7 +701,7 @@ failure in one of them **halts the case** — see
|
|
|
665
701
|
| `raised: true` / `raised: <code>` / `raised: { code?, reason? }` | **Structural.** The agent ended the turn with `archmax_raise({ code, reason })`. A string pins the code exactly; the mapping pins the code and matches the reason — exactly, or as a regex when written `"/pattern/flags"`. A miss names the code that was raised, or says the run did not raise. |
|
|
666
702
|
| `variables: { expect, path?, locked? }` | Every named variable is set to the expected value — deep equality, so a structured expectation compares by value. `path` addresses into a value per name (`items.-1.sku` is the last element); `locked` asserts the lock state. |
|
|
667
703
|
| `reachedState: <slug>` | **Structural.** A **committed** (non-trigger) audit-trail step entered the state — agent `archmax_advance`, human decision, `archmax_reset`, or `on_error` route. The trigger-arrival step never counts (it is recorded before the entry state's `before` gate runs, so a vetoed-entry run never "reaches" its start state). |
|
|
668
|
-
| `reply: { includes?, excludes? }` | `includes`: substrings or `/pattern/flags` regex strings that must **all** match; `excludes`: none may match (leak checks — a refusal that quotes the data it refuses still leaks it). Each accepts a **single token or a list** (`includes: "ORD-1003"` works). Matched against the
|
|
704
|
+
| `reply: { includes?, excludes? }` | `includes`: substrings or `/pattern/flags` regex strings that must **all** match; `excludes`: none may match (leak checks — a refusal that quotes the data it refuses still leaks it). Each accepts a **single token or a list** (`includes: "ORD-1003"` works). Matched against **every assistant message of the session so far** (earlier turns included), not just the final message (a parked run often closes with a short "moving on" message while the substantive answer came earlier). |
|
|
669
705
|
| `calledTool: { name, input? }` | The turn recorded a call to tool `name`; `input` is a **partial** match — mappings match on the declared keys (nested mappings partially), arrays by deep equality, and `/pattern/flags` string values match as **regexes** (recommended for file paths, so the model's exact spelling doesn't matter). |
|
|
670
706
|
| `notCalledTool: { name, input? }` | No matching call occurred. |
|
|
671
707
|
| `blockedTool: { name, input? }` | A matching call was made **and refused by governance** — the assertion for testing a guard. Distinct from `notCalledTool` (nothing was tried, which proves nothing about the guard) and from a tool that ran and errored (only a policy rejection counts). Use it to test a state's `tools.allow` narrowing or its `skills.allow` set. |
|
|
@@ -673,7 +709,7 @@ failure in one of them **halts the case** — see
|
|
|
673
709
|
| `trail: { to?, kind?, reason?, count }` | **Structural.** The number of audit-trail steps matching **all** given fields equals `count` (declare at least one of `to`/`kind`/`reason`). `kind`: `trigger` \| `agent` \| `human` \| `on_error` \| `branch` \| `join`. The trail accumulates across turns — each turn starts with a `trigger` step. |
|
|
674
710
|
| `noTraversal: true` | **Structural.** The trail holds nothing but trigger arrivals — a veto at entry committed no transitions. |
|
|
675
711
|
| `triggerArrival: <trigger-id>` | **Structural.** Some trail step has `kind: trigger` and that trigger id as its reason — the run really started from the declared trigger. |
|
|
676
|
-
| `grade: { closedQA, atLeast }` | LLM-grade the turn's **run evidence** against the `closedQA` criterion —
|
|
712
|
+
| `grade: { closedQA, atLeast }` | LLM-grade the turn's **run evidence** against the `closedQA` criterion — what the action's turn said (every text of that turn, empty when it said nothing) *plus* a bounded chronological record of the session's assistant messages and tool calls so far (each call with input, output, and status; mocked calls appear like real ones). The two are weighed differently: a criterion about what the user was **told** is met only by the reply, one about what the agent **did** may be met by the record — so `closedQA: looked the order up before answering` is a legitimate criterion, not only phrasing checks. **`atLeast` decides the outcome** — score ≥ `atLeast` passes, below it **fails the case**; the judge model's own opinion never overrides your declared bar, so the record can't disagree with the verdict. The judge is instructed to keep its reason to 1–3 sentences, so it fits on the verdict line. Requires `tests.judge` in `workflow.yaml`. |
|
|
677
713
|
|
|
678
714
|
**Regex form**: a string shaped `/pattern/flags` is compiled as a regular
|
|
679
715
|
expression; anything else matches as a plain substring (in `reply` tokens) or
|
|
@@ -775,13 +811,22 @@ steps:
|
|
|
775
811
|
Two operational notes:
|
|
776
812
|
|
|
777
813
|
- Seeding writes the file; **reading it is still governed** — the triggered
|
|
778
|
-
start state must allow the read tool the agent needs (`read_file` is
|
|
779
|
-
|
|
814
|
+
start state must allow the read tool the agent needs (`read_file` is granted
|
|
815
|
+
wherever the workflow-wide grant includes it, unless a state narrows it).
|
|
780
816
|
- Hosts that pin case sessions via `sessionIdForCase` reuse sessions across test
|
|
781
817
|
runs: declared files are overwritten each run, but a file seeded by an
|
|
782
818
|
_earlier_ run whose declaration was since removed can linger. The default
|
|
783
819
|
(per-run unique sessions) starts clean.
|
|
784
820
|
|
|
821
|
+
**Pin the date** with a case-level `clock:` whenever a fixture or a prompt
|
|
822
|
+
carries a date (an order placed on `2026-03-15`, "refundable for 30 days"):
|
|
823
|
+
without it the model reads the real "now" and the verdict drifts with the
|
|
824
|
+
calendar. Pin on a ten-minute mark (the line is rounded down to ten minutes,
|
|
825
|
+
UTC only). Only the prompt is pinned — hook scripts, the sandbox's `Date`, a
|
|
826
|
+
park's `resumeAt` and every recorded timestamp stay real, so a script that needs
|
|
827
|
+
the case's "today" reads it from a seeded variable, and a wait resumed in the
|
|
828
|
+
case is resumed with `deliver`, not by its `until`.
|
|
829
|
+
|
|
785
830
|
### Recovering a case's inputs from previous runs
|
|
786
831
|
|
|
787
832
|
A trigger carries only an id, so everything a firing passes arrives as
|
|
@@ -38,7 +38,8 @@ export default async function hook({ state, phase, trigger, variables, messages,
|
|
|
38
38
|
| `state`, `phase` | state slug; `"before"` or `"after"` |
|
|
39
39
|
| `trigger` | trigger id string (payload is in `variables`) |
|
|
40
40
|
| `variables` | read-only `name → value` map incl. `trigger`; structured values whole |
|
|
41
|
-
| `messages` |
|
|
41
|
+
| `messages` | the **current visit**, newest last: everything since the session entered the state it is in (on an advance, the state being left; it can span turns). `{ role, text, toolCalls? }`; roles `user`/`assistant`/`tool`/`system`/`runtime` (a runtime note; match `role === "runtime"`, never a `[bracket]`). A `tool` entry has `tool`, `id` and an empty `text`: read results with `toolCalls` |
|
|
42
|
+
| `toolCalls(filter?)` | async: the session's tool calls with results, every visit, oldest first, `{ id, name, args, result, status, state, visit }`; filter by `name`, `state`, `visit` (number or `"current"`), `status`, `last`. No control tools. Only what it returns enters the sandbox |
|
|
42
43
|
| `from`, `to`, `reason` | `after` only: the attempted transition and the agent's reason |
|
|
43
44
|
| `tools` | async camelCased bridge: `tools.readFile`, `tools.writeFile`, `tools.archmaxWorkflowEnrichOrder(...)`; no control tools; a hook's lists every tool on the bridge, not only the state's |
|
|
44
45
|
|
|
@@ -13,7 +13,7 @@ const agent = await createAgent({
|
|
|
13
13
|
workspace: { rootDir, /* mounts?, sessionStore? */ },
|
|
14
14
|
onEvent: (e) => { usage.handler(e); publish(e); }, // omit for the console subscriber
|
|
15
15
|
// model | modelFactory, tools, systemPrompt, middleware, backend, checkpointer,
|
|
16
|
-
// trigger, variables, promptCache, pricing, hookExecutors …
|
|
16
|
+
// trigger, variables, promptCache, pricing, clock, hookExecutors …
|
|
17
17
|
});
|
|
18
18
|
const sessionId = `run-${runId}`;
|
|
19
19
|
const result = await agent.invoke(
|
|
@@ -80,8 +80,8 @@ instructions?: string # the workflow's STANDING instructions — free-fo
|
|
|
80
80
|
# simply ignored; use `instructions:`, rereading the
|
|
81
81
|
# text as direction, not narration.
|
|
82
82
|
# A test case's `description` is UNAFFECTED. A
|
|
83
|
-
# TRANSITION's `description` is
|
|
84
|
-
#
|
|
83
|
+
# TRANSITION's `description` is what the agent routes
|
|
84
|
+
# on (see `transitions` below).
|
|
85
85
|
|
|
86
86
|
disabled?: boolean # `true` = this machine STARTS NO NEW TURNS. Bounds
|
|
87
87
|
# STARTING, not finishing.
|
|
@@ -115,6 +115,17 @@ runtime?:
|
|
|
115
115
|
engine?: string # "archmax-harness"
|
|
116
116
|
version?: string | number # target contract — use "2"
|
|
117
117
|
|
|
118
|
+
budget?:
|
|
119
|
+
maxModelCalls?: number # default 50 — model calls ONE TURN may make across EVERY
|
|
120
|
+
# state (a turn = one message, decision, reply or delivery;
|
|
121
|
+
# each counts from zero). A state's maxTurns restarts on
|
|
122
|
+
# every entry, so only this ends an agent cycling between
|
|
123
|
+
# states. Exhaustion routes once through the current
|
|
124
|
+
# state's on_error, else the turn ends rejected; the agent
|
|
125
|
+
# is warned once a fifth is left. The step limit follows
|
|
126
|
+
# it. RAISE it for turns that legitimately run long (a long
|
|
127
|
+
# state chain, a self-driven review cycle).
|
|
128
|
+
|
|
118
129
|
settings?:
|
|
119
130
|
model?: string # MODEL ID ONLY — the id every state of this workflow runs
|
|
120
131
|
# on, over the endpoint/credentials the assembly already
|
|
@@ -146,7 +157,13 @@ tests?: # cases suite config (declarative data;
|
|
|
146
157
|
# factory's `judge` role; other keys are errors
|
|
147
158
|
|
|
148
159
|
tools?:
|
|
149
|
-
allow_always?: AllowEntry[] #
|
|
160
|
+
allow_always?: AllowEntry[] # the WORKFLOW-WIDE GRANT, in every state. ABSENT = the
|
|
161
|
+
# default tools (ls, read_file, write_file, edit_file,
|
|
162
|
+
# glob, grep, copy_file, move_file, remove_file,
|
|
163
|
+
# write_todos, archmax_eval, archmax_run). DECLARED
|
|
164
|
+
# ([] included) = exactly what it lists: it REPLACES
|
|
165
|
+
# the default tools, so list each one you want. A
|
|
166
|
+
# new workflow lists all twelve.
|
|
150
167
|
forbid_always?: ForbidEntry[]
|
|
151
168
|
# DENIED in EVERY state, and absolute: nothing reaches
|
|
152
169
|
# past it — no state grant, allow_always entry,
|
|
@@ -259,6 +276,18 @@ states:
|
|
|
259
276
|
# reference GUARANTEED rather than dependent on agent
|
|
260
277
|
# behavior. Name here anything a downstream guard
|
|
261
278
|
# reads.
|
|
279
|
+
locks?: [string] # RUN VARIABLES this state OWNS: only it may write them,
|
|
280
|
+
# on any visit, before or after they are set; a write
|
|
281
|
+
# from another state is refused naming the owner. To
|
|
282
|
+
# change one (the person meant another order), the
|
|
283
|
+
# session goes BACK to the owner, whose rules apply
|
|
284
|
+
# again, and later guards re-read it — so give the
|
|
285
|
+
# owner an edge back from the states that use it. Any
|
|
286
|
+
# variable, guarded or not. One owner per variable;
|
|
287
|
+
# never `trigger`/`title`; not on a human state.
|
|
288
|
+
# `lock: true` is refused for an owned variable, and a
|
|
289
|
+
# host-supplied value with the same name stays final.
|
|
290
|
+
# Pair with `requires` to make the owner set it.
|
|
262
291
|
title?: string # HUMAN-READABLE label. Presentational only: carried
|
|
263
292
|
# in a human state's pending-decision record and shown
|
|
264
293
|
# in host surfaces. REACHES NO MODEL CALL. Never a
|
|
@@ -312,8 +341,9 @@ states:
|
|
|
312
341
|
# NO PART OF THE PROMPT — the model never reads it,
|
|
313
342
|
# so it is free, and it can NOT carry routing logic.
|
|
314
343
|
# The routing text the agent reads is the SOURCE
|
|
315
|
-
# state's transition `description`s
|
|
316
|
-
#
|
|
344
|
+
# state's transition `description`s, or a target's
|
|
345
|
+
# `instructions` where an edge is undescribed; a
|
|
346
|
+
# target's summary is never disclosed.
|
|
317
347
|
|
|
318
348
|
instructions?: string # behavior guidance.
|
|
319
349
|
# agent node: surfaced to the LLM while active.
|
|
@@ -328,13 +358,14 @@ states:
|
|
|
328
358
|
# for a terminal state)
|
|
329
359
|
|
|
330
360
|
tools?:
|
|
331
|
-
allow?: AllowEntry[] # grants beyond the
|
|
332
|
-
#
|
|
333
|
-
#
|
|
334
|
-
#
|
|
361
|
+
allow?: AllowEntry[] # grants beyond the workflow-wide grant (plus the
|
|
362
|
+
# always-allowed archmax_advance); an entry naming
|
|
363
|
+
# a tool that grant includes NARROWS it, and one
|
|
364
|
+
# naming a default tool a declared allow_always
|
|
365
|
+
# leaves out GRANTS it here.
|
|
335
366
|
forbid?: ForbidEntry[] # what this state DENIES while active — beating the
|
|
336
|
-
# workflow
|
|
337
|
-
# and
|
|
367
|
+
# workflow-wide grant, this state's own allow,
|
|
368
|
+
# and a host's essentialTools alike. Deny beats allow.
|
|
338
369
|
# Not inherited by a sub-workflow; a lifecycle hook
|
|
339
370
|
# is exempt from it (runtime authority).
|
|
340
371
|
|
|
@@ -421,25 +452,35 @@ states:
|
|
|
421
452
|
#
|
|
422
453
|
# Transition = {
|
|
423
454
|
# to: string # a declared state's SLUG
|
|
424
|
-
# description
|
|
455
|
+
# description?: string # what the agent routes on
|
|
425
456
|
# type?: "approve" | "reject" | "refine" | "none"
|
|
426
457
|
# }
|
|
427
458
|
#
|
|
428
459
|
# THE AGENT SEES THIS STATE'S EDGES AND NOTHING
|
|
429
460
|
# ELSE OF THE GRAPH — no other state's slug, title,
|
|
430
461
|
# summary, hooks or transitions, and no count of
|
|
431
|
-
# the states. So `description` is
|
|
432
|
-
#
|
|
433
|
-
#
|
|
434
|
-
#
|
|
462
|
+
# the states. So `description` is what it knows
|
|
463
|
+
# about where an edge leads. An UNDESCRIBED edge
|
|
464
|
+
# (absent/empty/blank) routes on the TARGET's
|
|
465
|
+
# `instructions` instead: the edge's line carries
|
|
466
|
+
# them, quoted, and nothing else of the target.
|
|
467
|
+
# LOAD ERRORS (states.<slug>.transitions.<i>.description):
|
|
468
|
+
# an undescribed edge whose target has no
|
|
469
|
+
# instructions; an undescribed transition of a
|
|
470
|
+
# HUMAN state (it is the button label); a
|
|
471
|
+
# non-string. An undescribed edge INTO a human
|
|
472
|
+
# state discloses that state's reviewer-facing
|
|
473
|
+
# instructions to the agent — describe it to keep
|
|
474
|
+
# them private.
|
|
435
475
|
#
|
|
436
476
|
# Write it for a reader standing in THIS state,
|
|
437
477
|
# which knows nothing about the target:
|
|
438
478
|
# GOOD: "The refund is over $50." (when)
|
|
439
479
|
# BAD: "Go to refund-review." (where)
|
|
440
480
|
# A routing condition CANNOT live in the target's
|
|
441
|
-
# summary
|
|
442
|
-
#
|
|
481
|
+
# summary, which is never disclosed; it lives in the
|
|
482
|
+
# target's instructions only when the edge is left
|
|
483
|
+
# undescribed.
|
|
443
484
|
#
|
|
444
485
|
# The rendered line marks a target that is a human
|
|
445
486
|
# decision node or terminal (each changes the call
|
|
@@ -666,11 +707,12 @@ an entry naming an unprefixed variant governs a tool that does not exist,
|
|
|
666
707
|
silently granting nothing. A host tool may never claim the prefix. Deep Agents' built-ins
|
|
667
708
|
(`read_file`, `task`, …) keep their own names.
|
|
668
709
|
|
|
669
|
-
- A state with **no `tools` block** gets the **
|
|
670
|
-
|
|
671
|
-
`
|
|
672
|
-
|
|
673
|
-
whatever a `forbid` list
|
|
710
|
+
- A state with **no `tools` block** gets the **workflow-wide grant only**: what
|
|
711
|
+
`tools.allow_always` lists, or the default tools (`ls`, `read_file`, `write_file`,
|
|
712
|
+
`edit_file`, `copy_file`, `move_file`, `remove_file`, `glob`, `grep`, `write_todos`,
|
|
713
|
+
`archmax_eval`, `archmax_run`) when the key is absent, plus the always-allowed
|
|
714
|
+
`archmax_advance` and a host's `essentialTools`, minus whatever a `forbid` list
|
|
715
|
+
denies there, and read-only-zone protection.
|
|
674
716
|
Governance is closed by default; there is no way to declare a fully open state.
|
|
675
717
|
- `tools.forbid` on a state **denies** while that state is active, beating every
|
|
676
718
|
grant that would otherwise reach it (see [Tool governance](#tool-governance)).
|
|
@@ -712,7 +754,8 @@ success.
|
|
|
712
754
|
```yaml
|
|
713
755
|
transitions:
|
|
714
756
|
- to: string # REQUIRED target state SLUG (must exist); never a title
|
|
715
|
-
description?: string # shown to the agent (or human) to pick the edge
|
|
757
|
+
description?: string # shown to the agent (or human) to pick the edge; absent =
|
|
758
|
+
# the agent is shown the target's instructions instead
|
|
716
759
|
type?: "approve" | "reject" | "refine" | "none" # default "none"
|
|
717
760
|
```
|
|
718
761
|
|
|
@@ -746,22 +789,30 @@ platform-provided tool resolves through when its collection has several) and
|
|
|
746
789
|
`source` (any string: a host's own label for where the tool came from) — the
|
|
747
790
|
runtime matches governance by `tool` name alone and never reads `source`.
|
|
748
791
|
|
|
749
|
-
**The model is closed by default, and a denial beats every grant.** Every state
|
|
750
|
-
**
|
|
751
|
-
|
|
792
|
+
**The model is closed by default, and a denial beats every grant.** Every state has the
|
|
793
|
+
**workflow-wide grant**: what `tools.allow_always` lists, or, when the workflow does not
|
|
794
|
+
declare that key, the **default tools** (`ls`, `read_file`, `write_file`, `edit_file`,
|
|
795
|
+
`copy_file`, `move_file`, `remove_file`, `glob`, `grep`, `write_todos`, `archmax_eval`,
|
|
796
|
+
`archmax_run`). A declared list **replaces** the default tools, never adds to them:
|
|
797
|
+
`allow_always: []` grants no tool workflow-wide, and a list without `write_file` leaves
|
|
798
|
+
every state that does not name it unable to write. So a new workflow lists all twelve and
|
|
799
|
+
removes only what it must not have. Every state also has the always-allowed controls and
|
|
800
|
+
any tool the host passes as `essentialTools`, whatever the workflow declares. One built-in is **never
|
|
752
801
|
disclosed** and grantable by nothing: `task`, the runtime's own dispatch for
|
|
753
802
|
grading rubrics — a rubric grades the agent rather than serving it, so naming
|
|
754
803
|
`task` in an allow entry is a `validate` error and the kernel refuses the call.
|
|
755
804
|
The planning scratchpad `write_todos` and the sandbox
|
|
756
|
-
`archmax_eval` are
|
|
757
|
-
|
|
805
|
+
`archmax_eval` are default tools: granted wherever the workflow-wide grant includes
|
|
806
|
+
them (everywhere, in a workflow without `allow_always`). `write_todos`'s prompt
|
|
807
|
+
guidance appears only when some state is granted it, and hooks run in the sandbox
|
|
808
|
+
whether or not the agent is granted `archmax_eval`. The interpreter is safe as a
|
|
758
809
|
default because the `tools.*` calls its code makes are decided against the active
|
|
759
810
|
state by the same kernel: it adds computation over what the state permits, not
|
|
760
|
-
access. `archmax_run` — executing an authored script **file** — is
|
|
811
|
+
access. `archmax_run` — executing an authored script **file** — is a default tool on
|
|
761
812
|
the same terms (the script is a file the state could already read); declare it
|
|
762
813
|
per state to **narrow** which files may run there. The file operations —
|
|
763
814
|
`copy_file({ source, destination, overwrite? })`, `move_file` (same arguments)
|
|
764
|
-
and `remove_file({ file_path })` — are
|
|
815
|
+
and `remove_file({ file_path })` — are default tools on the same terms: every tool
|
|
765
816
|
**declares its path arguments** with how the call uses each (`read`, `list`,
|
|
766
817
|
`search`, `write`, `remove`, `execute`), and every path rule reads that
|
|
767
818
|
declaration. `copy_file` reads `source` and writes `destination`; `move_file`
|
|
@@ -774,16 +825,19 @@ entry guards every declared path argument: a grant needs all of them to match
|
|
|
774
825
|
declares its paths (`toolPaths`, or a descriptor's `paths`) is governed the same
|
|
775
826
|
way; an argument may hold a list of paths, each one governed (and each one
|
|
776
827
|
tested by a `paths:` guard), and any other shape is refused
|
|
777
|
-
(`tool.path-argument`). `scratchpad/` is open in every state to the
|
|
778
|
-
|
|
779
|
-
granted,
|
|
828
|
+
(`tool.path-argument`). `scratchpad/` is open in every state to each default file tool the state
|
|
829
|
+
is granted, whatever the granting entry narrows, and to a host's `essentialTools`.
|
|
830
|
+
A file tool the state is not granted gets nothing from it, and a host tool a state
|
|
831
|
+
must grant stays refused where it is not granted, whatever its paths. Any other tool
|
|
780
832
|
must be declared in the state's `tools.allow` or workflow-level
|
|
781
|
-
`tools.allow_always`. A `tools.allow` entry naming
|
|
782
|
-
it for that state.
|
|
833
|
+
`tools.allow_always`. A `tools.allow` entry naming a tool the workflow-wide grant
|
|
834
|
+
includes **narrows** it for that state. A guard on an `allow_always` entry is
|
|
835
|
+
enforced like any other (`{ tool: write_file, paths: [reports/**] }` binds in every
|
|
836
|
+
state that does not name `write_file`).
|
|
783
837
|
Denial is declared beside the grant, in the same two positions and with the same
|
|
784
838
|
entries: `tools.forbid_always` at the root denies in every state,
|
|
785
839
|
`tools.forbid` on a state denies while it is active — subtracting from the
|
|
786
|
-
workflow
|
|
840
|
+
workflow-wide grant, the state's own `allow` and a host's `essentialTools` alike.
|
|
787
841
|
**Deny beats allow, and no narrower level widens a denial.** A forbid entry may
|
|
788
842
|
name the tool `*` (every tool); `*` in an allow list is a load error. A lifecycle
|
|
789
843
|
hook is exempt from the state's `allow` **and** its `forbid` — runtime authority,
|
|
@@ -803,17 +857,18 @@ Precedence (highest first):
|
|
|
803
857
|
(and regardless of always-allowed): deny beats allow, and no narrower level
|
|
804
858
|
widens a denial.
|
|
805
859
|
2. **Always allowed**: `archmax_advance` (baked in, every state).
|
|
806
|
-
3. **Per-state `tools.allow`** — the listed tools/arg-shapes. An entry for
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
860
|
+
3. **Per-state `tools.allow`** — the listed tools/arg-shapes. An entry for a
|
|
861
|
+
tool the workflow-wide grant includes takes precedence (**narrower wins** —
|
|
862
|
+
the state's arg globs apply, not the broader grant); an entry for a default
|
|
863
|
+
tool a declared `allow_always` omits grants it in this state.
|
|
864
|
+
4. **The workflow-wide grant** — the declared `tools.allow_always`, guards
|
|
865
|
+
included, or the default tools, unconstrained, when the key is absent.
|
|
866
|
+
5. **The host's `essentialTools`** — always permitted when no state entry
|
|
867
|
+
narrows them, whatever the workflow declares.
|
|
813
868
|
6. **Default**: everything else is blocked.
|
|
814
869
|
|
|
815
870
|
**Progressive disclosure.** The model is only shown the active state's tool
|
|
816
|
-
surface:
|
|
871
|
+
surface: the workflow-wide grant + a host's `essentialTools` + the state's entries + `archmax_advance`
|
|
817
872
|
(hidden in terminal states), minus every tool denied there by name. Disclosure is
|
|
818
873
|
name-level (arg-constrained tools stay visible; the kernel enforces args at
|
|
819
874
|
call time) and never replaces enforcement. **Skills are disclosed the same way**:
|
|
@@ -1265,6 +1320,12 @@ states:
|
|
|
1265
1320
|
escalation: { instructions: "Explain the failure and hand off." } # terminal
|
|
1266
1321
|
```
|
|
1267
1322
|
|
|
1323
|
+
The workflow's root `budget: { maxModelCalls }` (default 50) bounds a whole
|
|
1324
|
+
turn across states, beside each state's own `maxTurns`; its exhaustion routes the
|
|
1325
|
+
same way. A turn that throws (model failure, cancellation, the step limit) ends
|
|
1326
|
+
the session `failed` with exit code `turn-error`, `turn-cancelled` or
|
|
1327
|
+
`recursion-limit`; the next message starts a fresh turn.
|
|
1328
|
+
|
|
1268
1329
|
Two failure classes, routed differently:
|
|
1269
1330
|
|
|
1270
1331
|
- **Recoverable rejections keep the agent in place**: an invalid edge in
|
|
@@ -1839,7 +1900,7 @@ them. Its shape:
|
|
|
1839
1900
|
- `orders-question` (terminal) answers from `skills/order-data/assets/orders.json`; an
|
|
1840
1901
|
`after: { rubric: order-reply-tone }` grades the reply at completion.
|
|
1841
1902
|
- `refund-request` records a decision to `scratchpad/refund.json` (no allow entry
|
|
1842
|
-
narrows it — the working area is open
|
|
1903
|
+
narrows it — the working area is open to `write_file` wherever it is granted; what holds the agent to a
|
|
1843
1904
|
correct file is the hook), runs `after: check-refund.js`, and pairs
|
|
1844
1905
|
`budget.maxTurns: 24` with `on_error: escalation` so a terminal failure
|
|
1845
1906
|
(hook error, exhausted corrections, exhausted budget) routes to escalation
|
|
@@ -23,11 +23,24 @@ tests: # case suite config (no tests.config.js exists)
|
|
|
23
23
|
maxConcurrency: 1 # only 1 accepted
|
|
24
24
|
caseTimeoutMs: 120000 # per-case wall clock, distinct from settings.timeoutMs
|
|
25
25
|
judge: {} # required for grade:; {} = env default model, or { model, modelOptions }
|
|
26
|
+
budget: { maxModelCalls: 50 } # model calls one turn may make across ALL states (default 50); raise for long turns
|
|
26
27
|
metadata: { canvas: {} } # host data, never read; accepted at root, state, rubric — leave as found
|
|
27
28
|
extensions: { hooks: [] } # host-registered hook kinds; rarely authored
|
|
28
29
|
|
|
29
30
|
tools: # tool governance, workflow level
|
|
30
|
-
allow_always:
|
|
31
|
+
allow_always: # granted in EVERY state; a state's allow adds to or narrows it.
|
|
32
|
+
- ls # The DEFAULT TOOLS: declared, this list REPLACES them (absent,
|
|
33
|
+
- read_file # all twelve are granted), so a new workflow lists all twelve and
|
|
34
|
+
- write_file # removes only what it must not have. Workflow-wide host tools
|
|
35
|
+
- edit_file # go below them; state-specific ones under a state's tools.allow.
|
|
36
|
+
- glob
|
|
37
|
+
- grep
|
|
38
|
+
- copy_file
|
|
39
|
+
- move_file
|
|
40
|
+
- remove_file
|
|
41
|
+
- write_todos
|
|
42
|
+
- archmax_eval
|
|
43
|
+
- archmax_run
|
|
31
44
|
forbid_always: [archmax_reset] # denied in EVERY state; beats every grant; "*" = every tool
|
|
32
45
|
skills: # skill governance, workflow level — SLUGS, never paths
|
|
33
46
|
allow_always: [order-data] # enabled in every state; [] = every capability is state-specific
|
|
@@ -67,23 +80,25 @@ states:
|
|
|
67
80
|
tools:
|
|
68
81
|
allow: # adds to allow_always; entry = name | { tool, args: {<arg>: [globs]} } | { tool, paths }
|
|
69
82
|
- lookup_customer
|
|
70
|
-
- { tool: archmax_run, paths: ["skills/refund-policy/scripts/**"] } # narrows
|
|
83
|
+
- { tool: archmax_run, paths: ["skills/refund-policy/scripts/**"] } # narrows the workflow-wide grant, within an enabled skill
|
|
71
84
|
- { tool: write_file, args: { file_path: ["reports/${{case_id}}/*.json"] } } # variable-bound guard
|
|
72
85
|
- archmax_workflow_enrich-order # runs a sibling workflow
|
|
73
|
-
forbid: [archmax_wait] # denies here; beats every grant incl.
|
|
86
|
+
forbid: [archmax_wait] # denies here; beats every grant incl. allow_always
|
|
74
87
|
skills:
|
|
75
88
|
allow: [refund-policy] # adds to allow_always (bundle readable + scripts runnable, no path entries)
|
|
76
89
|
forbid: [order-data] # subtracts, even from allow_always ([] never denies)
|
|
77
90
|
requires: [case_id] # variables that must be set before this state may be left
|
|
91
|
+
locks: [case_id] # variables only this state may write (change = come back here)
|
|
78
92
|
model: claude-opus-5 # MODEL ID ONLY — this state's turns; beats settings.model
|
|
79
93
|
budget: { maxTurns: 12, timeoutMs: 60000, maxParks: 5 } # bounds the state's segment
|
|
80
94
|
on_error: closed # where a terminal failure of this segment routes
|
|
81
95
|
metadata: { x: 40, y: 180 }
|
|
82
96
|
transitions: # none => terminal state
|
|
83
97
|
- to: review # slug; must exist
|
|
84
|
-
description: The decision is recorded and needs a person's approval. #
|
|
85
|
-
#
|
|
86
|
-
#
|
|
98
|
+
description: The decision is recorded and needs a person's approval. # what the agent routes
|
|
99
|
+
# on. Say WHEN, never where. Omit it to route on the target's
|
|
100
|
+
# instructions instead (target must have some); REQUIRED on a
|
|
101
|
+
# human state's transitions, where it is the button label.
|
|
87
102
|
type: none # approve | reject | refine | none — semantic on human states
|
|
88
103
|
|
|
89
104
|
review: # human state: parks with a decision record; a PERSON picks the edge
|
|
@@ -119,7 +134,10 @@ states:
|
|
|
119
134
|
- **Variables.** One flat checkpointed `name → value` store, never declared.
|
|
120
135
|
Host seeds (`--variables`, `createAgent({ variables })`, a delivery) are
|
|
121
136
|
**locked**; the agent uses `archmax_get_variables`/`archmax_set_variables`
|
|
122
|
-
(`lock: true` to freeze
|
|
137
|
+
(`lock: true` to freeze, until `archmax_reset` releases the agent's locks).
|
|
138
|
+
A value that must be decided in one place yet stay correctable belongs to a
|
|
139
|
+
state: `locks: [order_id]` lets only that state write it, so a change routes
|
|
140
|
+
back there. Reserved: `trigger` (current trigger id, always
|
|
123
141
|
locked) and `title` (the run's one-line task label, agent-owned; a
|
|
124
142
|
sub-workflow child is not asked for one). A
|
|
125
143
|
`${{name}}` / `${{a.b.-1.c}}` reference works in `tools.allow` globs
|
|
@@ -174,7 +192,8 @@ states:
|
|
|
174
192
|
bind them; `forbid_always`, safety rules and the workflow's mount grants
|
|
175
193
|
(any state's) do. Human states may not declare hooks.
|
|
176
194
|
- **Budgets and `on_error`.** `budget.maxTurns`/`timeoutMs`/`maxParks` bound
|
|
177
|
-
a state's segment
|
|
195
|
+
a state's segment; the root `budget.maxModelCalls` (default 50) bounds a whole
|
|
196
|
+
turn across states, so a cycle between states ends there. A terminal failure (hook execution error, exhausted
|
|
178
197
|
corrections, exhausted budget, exhausted parks) routes to `on_error`
|
|
179
198
|
instead of failing the run; recoverable rejections (invalid edge, veto,
|
|
180
199
|
in-budget correct) keep the agent in place. Declare `on_error` on states
|
|
@@ -252,16 +271,23 @@ no way to see more of the graph.
|
|
|
252
271
|
|
|
253
272
|
Closed by default; **deny beats allow, and no narrower level widens a denial.**
|
|
254
273
|
|
|
255
|
-
- **
|
|
256
|
-
|
|
257
|
-
`
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
- **The workflow-wide grant**: `tools.allow_always`, in every state. Absent,
|
|
275
|
+
it is the **default tools**: `ls`, `read_file`, `write_file`, `edit_file`,
|
|
276
|
+
`copy_file`, `move_file`, `remove_file`, `glob`, `grep`, `write_todos`,
|
|
277
|
+
`archmax_eval`, `archmax_run`. Declared (`[]` included), it is exactly what it
|
|
278
|
+
lists and **replaces** the default tools, so a new workflow lists all twelve
|
|
279
|
+
(as in the skeleton) and removes only what it must not have. Never declared:
|
|
280
|
+
the controls `archmax_advance`, `archmax_reset`, `archmax_wait`,
|
|
281
|
+
`archmax_raise`, `archmax_get_variables`, `archmax_set_variables`, and a host's
|
|
282
|
+
`essentialTools`. Any other tool needs an `allow`/`allow_always` entry. A
|
|
283
|
+
state entry naming a granted tool **narrows** it there, and one naming a
|
|
284
|
+
default tool the list omits grants it there. `task` is ungrantable (error).
|
|
263
285
|
- **Entries**: bare name; `{ tool, args: { <arg>: ["glob", …] } }` (matches
|
|
264
|
-
if any glob matches; `${{var}}` allowed)
|
|
286
|
+
if any glob matches; `${{var}}` allowed). An argument the tool declares as a
|
|
287
|
+
path (else `file_path`) is canonicalized first; any other argument (a URL, an
|
|
288
|
+
address, an email) is matched **as written**, and a denial also catches its
|
|
289
|
+
path form. `""` matches only an empty value. `*` never crosses `/` (`**` does).
|
|
290
|
+
`{ tool, paths: [...] }`
|
|
265
291
|
(guards every path argument the tool declares — all must match to grant,
|
|
266
292
|
any matching denies). `"*"` as the tool is legal only in a
|
|
267
293
|
forbid list. Globs match dot-segments (`secrets/**` covers `secrets/.env`).
|
package/dist/cli/turn.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { lastAgentText } from "../core/messages.js";
|
|
2
1
|
import { formatUsage, hasUsage } from "../core/usage.js";
|
|
3
2
|
import { icons } from "./style.js";
|
|
4
3
|
import { CliError, err, out } from "./command.js";
|
|
@@ -81,7 +80,7 @@ export function printResumed(style, sessionId, verb, outcome) {
|
|
|
81
80
|
return 1;
|
|
82
81
|
}
|
|
83
82
|
err(style.green(style.bold(`${icons.check} ${verb}`)));
|
|
84
|
-
out(
|
|
83
|
+
out(outcome.reply || `Session ${sessionId} resumed (state=${outcome.workflowState ?? "?"}).`);
|
|
85
84
|
return 0;
|
|
86
85
|
}
|
|
87
86
|
export function printReply(style, sessionId, outcome) {
|
package/dist/cli/turn.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn.js","sourceRoot":"","sources":["../../src/cli/turn.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"turn.js","sourceRoot":"","sources":["../../src/cli/turn.ts"],"names":[],"mappings":"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"}
|