@archmax-ai/harness 0.9.0 → 1.0.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 +58 -14
- package/dist/agent.d.ts +4 -6
- package/dist/agent.js +4 -6
- package/dist/agent.js.map +1 -1
- package/dist/assembly/compose.d.ts +5 -24
- package/dist/assembly/compose.js +28 -42
- package/dist/assembly/compose.js.map +1 -1
- package/dist/assembly/delegation-registry.js +3 -14
- package/dist/assembly/delegation-registry.js.map +1 -1
- package/dist/assembly/index.d.ts +8 -15
- package/dist/assembly/index.js +21 -28
- package/dist/assembly/index.js.map +1 -1
- package/dist/assembly/plain.js +1 -2
- package/dist/assembly/plain.js.map +1 -1
- package/dist/authoring-skill/archmax-harness/README.md +5 -5
- package/dist/authoring-skill/archmax-harness/SKILL.md +72 -30
- package/dist/authoring-skill/archmax-harness/references/backend-integration.md +33 -24
- package/dist/authoring-skill/archmax-harness/references/cases.md +41 -8
- package/dist/authoring-skill/archmax-harness/references/{hook-and-test-scripts.md → code-state-and-test-scripts.md} +599 -339
- package/dist/authoring-skill/archmax-harness/references/code-states-and-scripts.md +115 -0
- package/dist/authoring-skill/archmax-harness/references/diagnostics.md +40 -7
- package/dist/authoring-skill/archmax-harness/references/wiring.md +19 -10
- package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +404 -293
- package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +151 -62
- package/dist/cli/state-flow.d.ts +1 -1
- package/dist/cli/state-flow.js +18 -28
- package/dist/cli/state-flow.js.map +1 -1
- package/dist/cli/turn.d.ts +1 -1
- package/dist/cli/turn.js +1 -1
- package/dist/core/code-description.d.ts +3 -3
- package/dist/core/code-description.js +3 -3
- package/dist/core/deepagents.d.ts +5 -12
- package/dist/core/deepagents.js +4 -17
- package/dist/core/deepagents.js.map +1 -1
- package/dist/core/events.d.ts +23 -42
- package/dist/core/events.js +21 -24
- package/dist/core/events.js.map +1 -1
- package/dist/core/message-readers.d.ts +4 -5
- package/dist/core/message-readers.js +3 -8
- package/dist/core/message-readers.js.map +1 -1
- package/dist/core/messages.d.ts +0 -22
- package/dist/core/messages.js +1 -32
- package/dist/core/messages.js.map +1 -1
- package/dist/core/mounts.js +3 -3
- package/dist/core/platform-prompt.generated.js +1 -1
- package/dist/core/platform-prompt.generated.js.map +1 -1
- package/dist/core/session-zone.d.ts +1 -1
- package/dist/core/session-zone.js +1 -1
- package/dist/core/workspace-context.d.ts +1 -1
- package/dist/core/workspace.d.ts +1 -1
- package/dist/core/workspace.js +1 -1
- package/dist/core/zones.d.ts +4 -4
- package/dist/core/zones.js +5 -5
- package/dist/core/zones.js.map +1 -1
- package/dist/env.d.ts +9 -9
- package/dist/index.d.ts +3 -7
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/kernel/kernel.d.ts +31 -61
- package/dist/kernel/kernel.js +54 -112
- package/dist/kernel/kernel.js.map +1 -1
- package/dist/machine/lint-spec.js +53 -12
- package/dist/machine/lint-spec.js.map +1 -1
- package/dist/machine/machine.d.ts +36 -18
- package/dist/machine/machine.js +62 -51
- package/dist/machine/machine.js.map +1 -1
- package/dist/machine/spec-schema.d.ts +32 -180
- package/dist/machine/spec-schema.js +52 -113
- package/dist/machine/spec-schema.js.map +1 -1
- package/dist/machine/tool-names.d.ts +34 -16
- package/dist/machine/tool-names.js +41 -16
- package/dist/machine/tool-names.js.map +1 -1
- package/dist/machine/types.d.ts +14 -46
- package/dist/machine/types.js +0 -2
- package/dist/machine/types.js.map +1 -1
- package/dist/machine/validate-spec.d.ts +1 -1
- package/dist/public/sandbox.d.ts +59 -95
- package/dist/public/sandbox.js +22 -23
- package/dist/public/sandbox.js.map +1 -1
- package/dist/public/spec.d.ts +13 -20
- package/dist/public/spec.js +12 -20
- package/dist/public/spec.js.map +1 -1
- package/dist/runtime/contract.d.ts +11 -12
- package/dist/runtime/contract.js +16 -16
- package/dist/runtime/contract.js.map +1 -1
- package/dist/sandbox/assets/parts/code-state.js +17 -0
- package/dist/sandbox/executor.d.ts +25 -7
- package/dist/sandbox/executor.js +56 -57
- package/dist/sandbox/executor.js.map +1 -1
- package/dist/sandbox/imports.d.ts +10 -10
- package/dist/sandbox/imports.js +11 -11
- package/dist/sandbox/imports.js.map +1 -1
- package/dist/sandbox/prelude.d.ts +5 -6
- package/dist/sandbox/prelude.js +38 -35
- package/dist/sandbox/prelude.js.map +1 -1
- package/dist/sandbox/ptc-gateway.d.ts +19 -18
- package/dist/sandbox/ptc-gateway.js +26 -22
- package/dist/sandbox/ptc-gateway.js.map +1 -1
- package/dist/sessions/resume.d.ts +2 -2
- package/dist/sessions/resume.js +1 -1
- package/dist/sessions/resume.js.map +1 -1
- package/dist/sessions/scope.d.ts +0 -10
- package/dist/sessions/scope.js +0 -17
- package/dist/sessions/scope.js.map +1 -1
- package/dist/sessions/summary.d.ts +1 -1
- package/dist/testing/assertions.js +9 -4
- package/dist/testing/assertions.js.map +1 -1
- package/dist/testing/driver.js +3 -0
- package/dist/testing/driver.js.map +1 -1
- package/dist/testing/interpreter.d.ts +2 -2
- package/dist/testing/runner.d.ts +2 -2
- package/dist/validate/validate.d.ts +1 -1
- package/dist/validate/validate.js +39 -70
- package/dist/validate/validate.js.map +1 -1
- package/dist/workflow/automated-controls.d.ts +63 -0
- package/dist/workflow/automated-controls.js +158 -0
- package/dist/workflow/automated-controls.js.map +1 -0
- package/dist/workflow/automated.d.ts +112 -0
- package/dist/workflow/automated.js +163 -0
- package/dist/workflow/automated.js.map +1 -0
- package/dist/workflow/code-state.d.ts +46 -0
- package/dist/workflow/code-state.js +26 -0
- package/dist/workflow/code-state.js.map +1 -0
- package/dist/workflow/control-tools.d.ts +54 -23
- package/dist/workflow/control-tools.js +99 -79
- package/dist/workflow/control-tools.js.map +1 -1
- package/dist/workflow/file-values.d.ts +4 -4
- package/dist/workflow/file-values.js +1 -1
- package/dist/workflow/file-values.js.map +1 -1
- package/dist/workflow/governance.d.ts +11 -22
- package/dist/workflow/governance.js +47 -104
- package/dist/workflow/governance.js.map +1 -1
- package/dist/workflow/grader-state.d.ts +55 -0
- package/dist/workflow/grader-state.js +182 -0
- package/dist/workflow/grader-state.js.map +1 -0
- package/dist/workflow/history.d.ts +11 -0
- package/dist/{lifecycle/transcript.js → workflow/history.js} +24 -8
- package/dist/workflow/history.js.map +1 -0
- package/dist/workflow/middleware.d.ts +14 -17
- package/dist/workflow/middleware.js +109 -95
- package/dist/workflow/middleware.js.map +1 -1
- package/dist/workflow/move-events.d.ts +21 -0
- package/dist/workflow/move-events.js +20 -0
- package/dist/workflow/move-events.js.map +1 -0
- package/dist/workflow/on-error.js +0 -2
- package/dist/workflow/on-error.js.map +1 -1
- package/dist/workflow/parks.d.ts +9 -2
- package/dist/workflow/parks.js +16 -10
- package/dist/workflow/parks.js.map +1 -1
- package/dist/workflow/paths.d.ts +9 -9
- package/dist/workflow/paths.js +19 -19
- package/dist/workflow/paths.js.map +1 -1
- package/dist/workflow/render-prompt.d.ts +7 -5
- package/dist/workflow/render-prompt.js +22 -37
- package/dist/workflow/render-prompt.js.map +1 -1
- package/dist/workflow/signature-checks.d.ts +2 -2
- package/dist/workflow/snapshot.js +5 -29
- package/dist/workflow/snapshot.js.map +1 -1
- package/dist/workflow/state.d.ts +14 -42
- package/dist/workflow/state.js +20 -33
- package/dist/workflow/state.js.map +1 -1
- package/dist/workflow/sub-workflow.d.ts +3 -3
- package/dist/workflow/sub-workflow.js +3 -7
- package/dist/workflow/sub-workflow.js.map +1 -1
- package/dist/workflow/tool-service.d.ts +1 -9
- package/dist/workflow/tool-service.js +21 -24
- package/dist/workflow/tool-service.js.map +1 -1
- package/dist/workflow/turn-boundary.d.ts +2 -2
- package/dist/workflow/turn-boundary.js +10 -15
- package/dist/workflow/turn-boundary.js.map +1 -1
- package/dist/workflow/turn-runner.js +5 -6
- package/dist/workflow/turn-runner.js.map +1 -1
- package/package.json +1 -1
- package/skills/archmax-harness/README.md +5 -5
- package/skills/archmax-harness/SKILL.md +72 -30
- package/skills/archmax-harness/references/backend-integration.md +33 -24
- package/skills/archmax-harness/references/cases.md +41 -8
- package/skills/archmax-harness/references/{hook-and-test-scripts.md → code-state-and-test-scripts.md} +599 -339
- package/skills/archmax-harness/references/code-states-and-scripts.md +115 -0
- package/skills/archmax-harness/references/diagnostics.md +40 -7
- package/skills/archmax-harness/references/wiring.md +19 -10
- package/skills/archmax-harness/references/workflow-schema.md +404 -293
- package/skills/archmax-harness/references/workflow-yaml.md +151 -62
- package/dist/authoring-skill/archmax-harness/references/hooks-and-scripts.md +0 -89
- package/dist/lifecycle/hook-shape.d.ts +0 -44
- package/dist/lifecycle/hook-shape.js +0 -66
- package/dist/lifecycle/hook-shape.js.map +0 -1
- package/dist/lifecycle/runner.d.ts +0 -197
- package/dist/lifecycle/runner.js +0 -419
- package/dist/lifecycle/runner.js.map +0 -1
- package/dist/lifecycle/tool-calls.d.ts +0 -9
- package/dist/lifecycle/tool-calls.js +0 -57
- package/dist/lifecycle/tool-calls.js.map +0 -1
- package/dist/lifecycle/transcript.d.ts +0 -31
- package/dist/lifecycle/transcript.js.map +0 -1
- package/dist/rubrics/rubrics.d.ts +0 -135
- package/dist/rubrics/rubrics.js +0 -145
- package/dist/rubrics/rubrics.js.map +0 -1
- package/dist/sandbox/assets/parts/core.js +0 -22
- package/dist/sandbox/assets/parts/lifecycle-hook.js +0 -47
- package/dist/sandbox/bridge.d.ts +0 -16
- package/dist/sandbox/bridge.js +0 -95
- package/dist/sandbox/bridge.js.map +0 -1
- package/dist/workflow/visits.d.ts +0 -21
- package/dist/workflow/visits.js +0 -45
- package/dist/workflow/visits.js.map +0 -1
- package/skills/archmax-harness/references/hooks-and-scripts.md +0 -89
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The complete, field-by-field reference for authoring a `workflow.yaml` (and its
|
|
4
4
|
optional `WORKFLOW.md` prose addendum) plus the surrounding workspace. Targets
|
|
5
|
-
**runtime contract version `"
|
|
5
|
+
**runtime contract version `"3"`**.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -14,9 +14,9 @@ optional `WORKFLOW.md` prose addendum) plus the surrounding workspace. Targets
|
|
|
14
14
|
- [Tool governance](#tool-governance)
|
|
15
15
|
- [Skill governance](#skill-governance)
|
|
16
16
|
- [Mount governance](#mount-governance)
|
|
17
|
-
- [
|
|
17
|
+
- [Grader and code states](#grader-and-code-states)
|
|
18
18
|
— full script contract + worked examples in
|
|
19
|
-
[`
|
|
19
|
+
[`code-state-and-test-scripts.md`](code-state-and-test-scripts.md)
|
|
20
20
|
- [Budgets & error routing](#budgets--error-routing)
|
|
21
21
|
- [Human states](#human-states)
|
|
22
22
|
- [Sub-workflows are tools](#sub-workflows-are-tools)
|
|
@@ -36,8 +36,8 @@ optional `WORKFLOW.md` prose addendum) plus the surrounding workspace. Targets
|
|
|
36
36
|
- **`workflow.yaml`** — the canonical, enforced machine spec: a **pure YAML
|
|
37
37
|
mapping** (no `---` frontmatter fences, no markdown). Parsed with YAML 1.2
|
|
38
38
|
semantics and validated against the schema. Top-level keys: `title`,
|
|
39
|
-
`instructions`, `runtime`, `settings`, `tests`, `tools`, `skills`,
|
|
40
|
-
`
|
|
39
|
+
`instructions`, `disabled`, `runtime`, `settings`, `tests`, `tools`, `skills`,
|
|
40
|
+
`mounts`, `metadata`, `states`.
|
|
41
41
|
- **`WORKFLOW.md`** (optional, beside it) — a plain-markdown prose addendum with
|
|
42
42
|
**no frontmatter**. A frontmatter block in `WORKFLOW.md` when `workflow.yaml`
|
|
43
43
|
exists is a **validation error**. The runtime renders the system prompt's
|
|
@@ -91,7 +91,7 @@ disabled?: boolean # `true` = this machine STARTS NO NEW TURNS. Bound
|
|
|
91
91
|
# (a new turn is new work) — all rejected AT THE TURN
|
|
92
92
|
# BOUNDARY with `status: rejected` and
|
|
93
93
|
# "Refusing to start: workflow '<slug>' is disabled.",
|
|
94
|
-
# before any state
|
|
94
|
+
# before any state or model call; and a caller's
|
|
95
95
|
# `archmax_workflow_<slug>` call (blocked call, refusal
|
|
96
96
|
# kind `disabled`, before a child is composed — it also
|
|
97
97
|
# beats a declared MOCK, so a case can never assert a
|
|
@@ -113,18 +113,17 @@ disabled?: boolean # `true` = this machine STARTS NO NEW TURNS. Bound
|
|
|
113
113
|
|
|
114
114
|
runtime?:
|
|
115
115
|
engine?: string # "archmax-harness"
|
|
116
|
-
version?: string | number # target contract —
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# state chain, a self-driven review cycle).
|
|
116
|
+
version?: string | number # target contract — "3", the only one supported (and
|
|
117
|
+
# the default when omitted). "1"/"2" are refused as
|
|
118
|
+
# an unsupported runtime contract, naming the
|
|
119
|
+
# supported set.
|
|
120
|
+
|
|
121
|
+
# There is NO root `budget`: declaring one is an unknown-key load error
|
|
122
|
+
# (`Unrecognized key: "budget"`). A whole turn — one message, decision, reply or
|
|
123
|
+
# delivery, across every state — is bounded by LangGraph's `recursionLimit`:
|
|
124
|
+
# 400 unless the host's invoke config sets its own, which wins. A turn that hits
|
|
125
|
+
# it ends the session `failed` (exit code `recursion-limit`). A state's own
|
|
126
|
+
# `budget` bounds that state (see Budgets & error routing).
|
|
128
127
|
|
|
129
128
|
settings?:
|
|
130
129
|
model?: string # MODEL ID ONLY — the id every state of this workflow runs
|
|
@@ -133,8 +132,12 @@ settings?:
|
|
|
133
132
|
# it. NOT a block: `{ id, temperature }` is a load error —
|
|
134
133
|
# sampling and endpoint are env/host configuration.
|
|
135
134
|
# Opaque: nothing validates the id against a list.
|
|
136
|
-
#
|
|
137
|
-
|
|
135
|
+
# A grader state runs on it too unless it declares its
|
|
136
|
+
# own `model`. Does NOT change the case judge
|
|
137
|
+
# (tests.judge.model).
|
|
138
|
+
timeoutMs?: number # default 15000; per sandbox evaluation — one
|
|
139
|
+
# archmax_eval/archmax_run call, or a code state's
|
|
140
|
+
# whole script
|
|
138
141
|
memoryLimitBytes?: number
|
|
139
142
|
maxPtcCalls?: number | null
|
|
140
143
|
maxResultChars?: number
|
|
@@ -150,8 +153,9 @@ tests?: # cases suite config (declarative data;
|
|
|
150
153
|
caseTimeoutMs?: number # per-case WALL-CLOCK budget, default 120000 —
|
|
151
154
|
# deliberately named differently from
|
|
152
155
|
# settings.timeoutMs (per-script sandbox budget)
|
|
153
|
-
judge?: # {} = enable the
|
|
154
|
-
model?: string # env-configured default model
|
|
156
|
+
judge?: # {} = enable the case judge (`grade:`) with the
|
|
157
|
+
model?: string # env-configured default model — unrelated to
|
|
158
|
+
# grader states
|
|
155
159
|
modelOptions?: { temperature?: number, maxTokens?: number }
|
|
156
160
|
# applied over the env model config, via the
|
|
157
161
|
# factory's `judge` role; other keys are errors
|
|
@@ -223,20 +227,15 @@ mounts?:
|
|
|
223
227
|
# validate ERRORS on a name the table does not mount.
|
|
224
228
|
forbid_always?: string[] # MOUNT NAMES out of reach of EVERY state, governed or
|
|
225
229
|
# not, whatever any list grants. Deny beats allow.
|
|
226
|
-
# Binds this workflow's
|
|
227
|
-
# inherited by every sub-workflow, naming the workflow
|
|
230
|
+
# Binds this workflow's grader and code states too,
|
|
231
|
+
# and is inherited by every sub-workflow, naming the workflow
|
|
228
232
|
# that declared it. A NAME only — a denial is total, so
|
|
229
233
|
# there is no `access` to qualify. (A root `mounts.allow`
|
|
230
234
|
# is a load error: one mounts model.)
|
|
231
235
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
#
|
|
235
|
-
# unknown hook kinds are validation errors
|
|
236
|
-
|
|
237
|
-
editor?: Record<string, unknown> # HOST PRESENTATION METADATA — an authoring
|
|
238
|
-
# UI's own state (canvas state positions keyed by
|
|
239
|
-
# slug, edge routing). The one key the runtime
|
|
236
|
+
metadata?: Record<string, unknown> # HOST PRESENTATION METADATA — an authoring
|
|
237
|
+
# UI's own state (canvas zoom, pan). Also accepted on
|
|
238
|
+
# a state (its position). The one key the runtime
|
|
240
239
|
# never reads: not shape-checked, not prompted,
|
|
241
240
|
# not governed, and excluded from the spec hash.
|
|
242
241
|
# Do NOT author it by hand and do NOT invent a
|
|
@@ -244,7 +243,8 @@ editor?: Record<string, unknown> # HOST PRESENTATION METADATA — an authoring
|
|
|
244
243
|
# rewrite a spec that already carries one, or the
|
|
245
244
|
# builder that wrote it loses every state position.
|
|
246
245
|
# The root is otherwise STRICT: any other
|
|
247
|
-
# unrecognized key is a load error
|
|
246
|
+
# unrecognized key is a load error — `extensions:`
|
|
247
|
+
# included.
|
|
248
248
|
|
|
249
249
|
states: Record<slug, MachineState> # REQUIRED; ≥1 state, ≥1 with a trigger
|
|
250
250
|
# the KEY is the state's SLUG (see below)
|
|
@@ -277,7 +277,7 @@ states:
|
|
|
277
277
|
# behavior. Name here anything a downstream guard
|
|
278
278
|
# reads.
|
|
279
279
|
locks?: [string] # RUN VARIABLES this state OWNS: only it may write them,
|
|
280
|
-
#
|
|
280
|
+
# whenever it is active, before or after they are set; a write
|
|
281
281
|
# from another state is refused naming the owner. To
|
|
282
282
|
# change one (the person meant another order), the
|
|
283
283
|
# session goes BACK to the owner, whose rules apply
|
|
@@ -296,11 +296,26 @@ states:
|
|
|
296
296
|
# NOT a substitute for `instructions` (behavior
|
|
297
297
|
# guidance) or a transition `description` (routing).
|
|
298
298
|
|
|
299
|
-
type?: "agent" | "human"
|
|
300
|
-
# default "agent"
|
|
299
|
+
type?: "agent" | "human" | "grader" | "code"
|
|
300
|
+
# default "agent" = the session's agent works here.
|
|
301
|
+
# "human" = a person routes.
|
|
302
|
+
# "grader" = an isolated grader judges the session
|
|
303
|
+
# and decides the edge (see Grader and code states).
|
|
304
|
+
# "code" = the runtime runs `script` and the
|
|
305
|
+
# script decides the edge.
|
|
306
|
+
# grader + code = AUTOMATED states: run on entry
|
|
307
|
+
# with no call of the agent's model.
|
|
301
308
|
# (`workflow` was removed: a state CALLS another
|
|
302
309
|
# workflow by allowing `archmax_workflow_<slug>`.)
|
|
303
310
|
|
|
311
|
+
script?: string # CODE STATES ONLY, and REQUIRED there: the file the
|
|
312
|
+
# state runs, workflow-relative inside `scripts/`,
|
|
313
|
+
# ending .js or .mjs, no `..` segment —
|
|
314
|
+
# script: scripts/check-refund.js
|
|
315
|
+
# resolves to workflows/<slug>/scripts/check-refund.js
|
|
316
|
+
# on the authoring plane. A code state without it,
|
|
317
|
+
# or `script` on any other type, is a LOAD ERROR.
|
|
318
|
+
|
|
304
319
|
|
|
305
320
|
triggers?: Record<triggerId, TriggerDeclaration | null>
|
|
306
321
|
# marks a start state. The KEY is the trigger id
|
|
@@ -346,16 +361,19 @@ states:
|
|
|
346
361
|
# target's summary is never disclosed.
|
|
347
362
|
|
|
348
363
|
instructions?: string # behavior guidance.
|
|
349
|
-
# agent
|
|
364
|
+
# agent state: surfaced to the LLM while active.
|
|
350
365
|
# Write the state's behavior INLINE here; a
|
|
351
366
|
# skill bundle (skills/<slug>/) is a capability
|
|
352
367
|
# with data or scripts, enabled by slug — not a
|
|
353
368
|
# per-state instruction sheet.
|
|
354
|
-
# human state:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
#
|
|
369
|
+
# human state: REQUIRED — the decision for the packet
|
|
370
|
+
# grader state: REQUIRED — its criteria, the
|
|
371
|
+
# grader's system prompt after a compiled-in
|
|
372
|
+
# preamble; never shown to the agent
|
|
373
|
+
# code state: a validate ERROR (nothing reads it)
|
|
374
|
+
# There is NO `before:`/`after:` on a state: hooks
|
|
375
|
+
# are removed, and a leftover key is a load error
|
|
376
|
+
# naming it (rewrite it as a grader or code state).
|
|
359
377
|
|
|
360
378
|
tools?:
|
|
361
379
|
allow?: AllowEntry[] # grants beyond the workflow-wide grant (plus the
|
|
@@ -366,8 +384,8 @@ states:
|
|
|
366
384
|
forbid?: ForbidEntry[] # what this state DENIES while active — beating the
|
|
367
385
|
# workflow-wide grant, this state's own allow,
|
|
368
386
|
# and a host's essentialTools alike. Deny beats allow.
|
|
369
|
-
# Not inherited by a sub-workflow
|
|
370
|
-
#
|
|
387
|
+
# Not inherited by a sub-workflow. Binds a grader's
|
|
388
|
+
# and a code state's calls exactly as the agent's.
|
|
371
389
|
|
|
372
390
|
skills?:
|
|
373
391
|
allow?: string[] # which CAPABILITIES this state ADDS, by SLUG — on top
|
|
@@ -389,11 +407,11 @@ states:
|
|
|
389
407
|
# listing of skills/ does not show it.
|
|
390
408
|
# A tools.allow entry still NARROWS within the enabled
|
|
391
409
|
# set: the two compose as enabled AND allowed.
|
|
392
|
-
#
|
|
393
|
-
# skills/order-data/assets/orders.json
|
|
394
|
-
#
|
|
395
|
-
# agent's use of a capability while
|
|
396
|
-
#
|
|
410
|
+
# A grader or code state is bound like the agent: a
|
|
411
|
+
# code state reading skills/order-data/assets/orders.json
|
|
412
|
+
# enables `order-data` in ITS OWN list. To gate the
|
|
413
|
+
# agent's use of a capability while a check still
|
|
414
|
+
# reads it, enable it on the check's state only.
|
|
397
415
|
|
|
398
416
|
mounts?:
|
|
399
417
|
allow?: MountGrant[] # which MOUNTS this state ADDS, by NAME — on top of the
|
|
@@ -422,27 +440,32 @@ states:
|
|
|
422
440
|
# every declaration) AND undisclosed — absent from the
|
|
423
441
|
# volatile prompt block, and filtered out of every
|
|
424
442
|
# ls/glob/grep result, the WORKSPACE ROOT LISTING
|
|
425
|
-
# included.
|
|
426
|
-
#
|
|
427
|
-
#
|
|
428
|
-
# governed mount only if allow_always or SOME state's
|
|
429
|
-
# allow names it, in any state, and writes it only
|
|
430
|
-
# where a grant of the workflow asks `read_write`.
|
|
443
|
+
# included. Binds a grader's and a code state's
|
|
444
|
+
# calls exactly as the agent's: grant the mount on
|
|
445
|
+
# the automated state that reads it.
|
|
431
446
|
|
|
432
447
|
model?: string # MODEL ID ONLY — this state's turns run on it (and the
|
|
433
448
|
# handoff message of a park it holds). Most specific of
|
|
434
449
|
# state.model > settings.model > the assembly's default
|
|
435
450
|
# (createAgent `model`/`modelFactory`, else ARCHMAX_MODEL).
|
|
436
451
|
# Same id as settings.model ⇒ validate WARNS (inert).
|
|
437
|
-
#
|
|
438
|
-
#
|
|
452
|
+
# On a GRADER state: the id the grader runs on, resolved
|
|
453
|
+
# under the `grader` model role
|
|
454
|
+
# (modelFactory("grader", env, id)). On a CODE state: a
|
|
455
|
+
# validate ERROR. A sub-workflow resolves its OWN
|
|
456
|
+
# spec's ids — nothing is inherited across it.
|
|
439
457
|
# A host passing an explicit `model` INSTANCE to
|
|
440
458
|
# createAgent outranks every declared id (one assembly
|
|
441
459
|
# warning names them); `modelFactory` is what honours one.
|
|
442
460
|
|
|
443
461
|
budget?: # per-state execution budget
|
|
444
|
-
maxTurns?: number # bounds this turn's agent loop
|
|
445
|
-
|
|
462
|
+
maxTurns?: number # bounds this turn's agent loop (on a grader: the
|
|
463
|
+
# grader's own model calls)
|
|
464
|
+
timeoutMs?: number # aborts the turn when exceeded (on a grader: its run)
|
|
465
|
+
maxParks?: number # bounds archmax_wait parks here (see Parking); an
|
|
466
|
+
# ERROR on a grader, which cannot park
|
|
467
|
+
# Any `budget` on a CODE state is a validate ERROR:
|
|
468
|
+
# settings.timeoutMs bounds its script.
|
|
446
469
|
on_error?: string # state to route to on a TERMINAL failure
|
|
447
470
|
# (see Budgets & error routing)
|
|
448
471
|
|
|
@@ -458,20 +481,24 @@ states:
|
|
|
458
481
|
#
|
|
459
482
|
# THE AGENT SEES THIS STATE'S EDGES AND NOTHING
|
|
460
483
|
# ELSE OF THE GRAPH — no other state's slug, title,
|
|
461
|
-
# summary,
|
|
462
|
-
# the states. So `description` is what it
|
|
463
|
-
# about where an edge leads. An UNDESCRIBED
|
|
464
|
-
# (absent/empty/blank) routes on the TARGET's
|
|
484
|
+
# summary, script, criteria or transitions, and no
|
|
485
|
+
# count of the states. So `description` is what it
|
|
486
|
+
# knows about where an edge leads. An UNDESCRIBED
|
|
487
|
+
# edge (absent/empty/blank) routes on the TARGET's
|
|
465
488
|
# `instructions` instead: the edge's line carries
|
|
466
489
|
# them, quoted, and nothing else of the target.
|
|
467
490
|
# LOAD ERRORS (states.<slug>.transitions.<i>.description):
|
|
468
491
|
# an undescribed edge whose target has no
|
|
469
492
|
# instructions; an undescribed transition of a
|
|
470
|
-
# HUMAN state (it is the button label);
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
#
|
|
493
|
+
# HUMAN state (it is the button label); an
|
|
494
|
+
# undescribed edge INTO a GRADER (its instructions
|
|
495
|
+
# are criteria the work it judges is never shown)
|
|
496
|
+
# or a CODE state (it has none); a non-string. A
|
|
497
|
+
# code state's OWN edges may go undescribed: its
|
|
498
|
+
# script routes, and nothing reads them. An
|
|
499
|
+
# undescribed edge INTO a human state discloses
|
|
500
|
+
# that state's reviewer-facing instructions to the
|
|
501
|
+
# agent — describe it to keep them private.
|
|
475
502
|
#
|
|
476
503
|
# Write it for a reader standing in THIS state,
|
|
477
504
|
# which knows nothing about the target:
|
|
@@ -483,10 +510,15 @@ states:
|
|
|
483
510
|
# undescribed.
|
|
484
511
|
#
|
|
485
512
|
# The rendered line marks a target that is a human
|
|
486
|
-
# decision node
|
|
487
|
-
# or what follows it);
|
|
488
|
-
|
|
489
|
-
|
|
513
|
+
# decision node, a grader, a code state or terminal
|
|
514
|
+
# (each changes the call or what follows it);
|
|
515
|
+
# nothing else of the target.
|
|
516
|
+
# A GRADER needs at least one transition (validate
|
|
517
|
+
# ERROR otherwise): with nowhere to send the session
|
|
518
|
+
# it could only approve by saying nothing.
|
|
519
|
+
|
|
520
|
+
# human states only (ignored on agent states; a validate ERROR on grader and
|
|
521
|
+
# code states):
|
|
490
522
|
evidence?: string[] # workspace-relative paths always shown in the packet —
|
|
491
523
|
# a baseline the advancing agent adds to (see Human states)
|
|
492
524
|
approvers?: string[] # informational; not enforced
|
|
@@ -599,8 +631,9 @@ states:
|
|
|
599
631
|
overwrite a value the session already holds. So do not list a return in a
|
|
600
632
|
state's `requires:` unless that state must settle it, and do not tell the
|
|
601
633
|
agent to lock one before its value is final. `note` is reserved: a `returns`
|
|
602
|
-
list may not declare it. Want the model to try again?
|
|
603
|
-
|
|
634
|
+
list may not declare it. Want the model to try again? Route the finishing
|
|
635
|
+
state through a code state that checks the returns and sends the work back
|
|
636
|
+
with what is missing. Each entry is a bare
|
|
604
637
|
run-variable name (**untyped**: any value, `null` included) or a strict object
|
|
605
638
|
`{ name, type?, description? }` naming one; the spellings mix in one list and
|
|
606
639
|
names are distinct across both. What the agent must *do* to produce a
|
|
@@ -621,14 +654,15 @@ states:
|
|
|
621
654
|
refused before any model call), **at the write** (`archmax_set_variables` of a
|
|
622
655
|
mistyped typed return of the current trigger is a correctable refusal,
|
|
623
656
|
nothing written) and **at completion** (a mistyped typed return rejects the
|
|
624
|
-
run — in practice a
|
|
657
|
+
run — in practice a code state's write). There are no enums, item or
|
|
625
658
|
property schemas, ranges or optional inputs. An unknown `type`, an unknown
|
|
626
659
|
entry key (`default:`) or an empty `description` fails load.
|
|
627
660
|
- **A `file` value must name a readable file.** The pure rule checks only the
|
|
628
661
|
path's shape (a host runs it in a browser). At the start, at the write, at
|
|
629
662
|
completion and at a delegation call the runtime also requires a **file** (not a
|
|
630
663
|
folder) at the path that the session may read **on runtime authority** — the
|
|
631
|
-
|
|
664
|
+
runtime's own `file`-value read check, the one call that runs outside a state's
|
|
665
|
+
surface: zone rules (`checkpoints/`, `artifacts/`, `_specs/`
|
|
632
666
|
never), the workflow's mount grants taken across all its states (a governed
|
|
633
667
|
mount no state is granted is out of reach), and every workflow-wide
|
|
634
668
|
`forbid_always`, inherited ones included; the state's allow list and its own
|
|
@@ -684,8 +718,8 @@ states:
|
|
|
684
718
|
connection: acme-slack
|
|
685
719
|
```
|
|
686
720
|
|
|
687
|
-
One state, one behavior, one governance block; `${{trigger}}` and a
|
|
688
|
-
`
|
|
721
|
+
One state, one behavior, one governance block; `${{trigger}}` and a code
|
|
722
|
+
state's `trigger` input still read which one actually fired.
|
|
689
723
|
- Every declared trigger **starts** a session. An event that must only ever reach
|
|
690
724
|
a live one — a ticket closing, a payment settling — is declared nowhere: the
|
|
691
725
|
host names the session it delivers into (`archmax deliver <session> --trigger
|
|
@@ -701,7 +735,8 @@ along the chosen edge, a delivery resumes the parked state.
|
|
|
701
735
|
|
|
702
736
|
**Runtime control tools** carry the `archmax_` prefix — `archmax_advance`,
|
|
703
737
|
`archmax_reset`, `archmax_wait`, `archmax_raise`, `archmax_run`,
|
|
704
|
-
`archmax_get_variables`, `archmax_set_variables
|
|
738
|
+
`archmax_get_variables`, `archmax_set_variables`, and `archmax_get_history`
|
|
739
|
+
(grader states only) — and are the names to write in `tools.allow`,
|
|
705
740
|
`tools.allow_always`, and the `forbid`/`forbid_always` lists. These are the only spellings:
|
|
706
741
|
an entry naming an unprefixed variant governs a tool that does not exist,
|
|
707
742
|
silently granting nothing. A host tool may never claim the prefix. Deep Agents' built-ins
|
|
@@ -712,7 +747,9 @@ silently granting nothing. A host tool may never claim the prefix. Deep Agents'
|
|
|
712
747
|
`edit_file`, `copy_file`, `move_file`, `remove_file`, `glob`, `grep`, `write_todos`,
|
|
713
748
|
`archmax_eval`, `archmax_run`) when the key is absent, plus the always-allowed
|
|
714
749
|
`archmax_advance` and a host's `essentialTools`, minus whatever a `forbid` list
|
|
715
|
-
denies there, and read-only-zone protection.
|
|
750
|
+
denies there, and read-only-zone protection. A grader or code state never has
|
|
751
|
+
`write_todos`, `archmax_eval`, `archmax_run`, `archmax_wait` or `archmax_reset`,
|
|
752
|
+
even when `allow_always` lists one; a grader also has `archmax_get_history`.
|
|
716
753
|
Governance is closed by default; there is no way to declare a fully open state.
|
|
717
754
|
- `tools.forbid` on a state **denies** while that state is active, beating every
|
|
718
755
|
grant that would otherwise reach it (see [Tool governance](#tool-governance)).
|
|
@@ -730,8 +767,8 @@ success.
|
|
|
730
767
|
- The call must be the **only** tool call in its message; one sent beside other
|
|
731
768
|
calls is refused, and the model may raise again on its own.
|
|
732
769
|
- The session ends **at once** with status `failed` (classified **finished**): no
|
|
733
|
-
further model call, no `on_error` route, no
|
|
734
|
-
|
|
770
|
+
further model call, no `on_error` route, no further state (an automated one
|
|
771
|
+
included), no check of the trigger's `returns`. A rejection still
|
|
735
772
|
pending from earlier in the turn is replaced. The position stays where the
|
|
736
773
|
agent raised; the trail records no step.
|
|
737
774
|
- The host reads `Outcome.kind === "failed"` and `Outcome.exit`
|
|
@@ -745,9 +782,11 @@ success.
|
|
|
745
782
|
its own `on_error`, or raise in turn.
|
|
746
783
|
- Ban it with `tools: { forbid_always: [archmax_raise] }` (or one state's
|
|
747
784
|
`tools.forbid`) where the agent must not end the run itself — say, a state that
|
|
748
|
-
must always reach a person instead.
|
|
785
|
+
must always reach a person instead. An agent's script cannot call it; a code
|
|
786
|
+
state's script (`tools.archmax_raise`) and a grader can, and their raise ends the
|
|
787
|
+
session the same way (the note and `exit` carry their code).
|
|
749
788
|
- Test it with the structural `raised` assertion; `succeeded: true` fails on a
|
|
750
|
-
raise.
|
|
789
|
+
raise (it passes only on a `completed` outcome).
|
|
751
790
|
|
|
752
791
|
## Transitions
|
|
753
792
|
|
|
@@ -755,12 +794,17 @@ success.
|
|
|
755
794
|
transitions:
|
|
756
795
|
- to: string # REQUIRED target state SLUG (must exist); never a title
|
|
757
796
|
description?: string # shown to the agent (or human) to pick the edge; absent =
|
|
758
|
-
# the agent is shown the target's instructions instead
|
|
797
|
+
# the agent is shown the target's instructions instead.
|
|
798
|
+
# REQUIRED on a human state's edges and on an edge INTO
|
|
799
|
+
# a grader or code state; a code state's own may omit it
|
|
759
800
|
type?: "approve" | "reject" | "refine" | "none" # default "none"
|
|
760
801
|
```
|
|
761
802
|
|
|
762
803
|
- On **agent** states the agent chooses one edge via
|
|
763
804
|
`archmax_advance({ to, reason })`; `type` is metadata only.
|
|
805
|
+
- On **grader** and **code** states the grader or the script chooses one the same
|
|
806
|
+
way (`archmax_advance` / `tools.archmax_advance`), and its `reason` is what the
|
|
807
|
+
next state reads.
|
|
764
808
|
- On **human** states each transition is a selectable outcome (a UI renders one
|
|
765
809
|
button per edge, labeled by `type`). `refine` conventionally loops back to a
|
|
766
810
|
prior state with the reviewer's comment as correction. Max one transition per
|
|
@@ -796,16 +840,19 @@ declare that key, the **default tools** (`ls`, `read_file`, `write_file`, `edit_
|
|
|
796
840
|
`archmax_run`). A declared list **replaces** the default tools, never adds to them:
|
|
797
841
|
`allow_always: []` grants no tool workflow-wide, and a list without `write_file` leaves
|
|
798
842
|
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
|
|
800
|
-
any tool the host passes as `essentialTools`, whatever the workflow declares.
|
|
801
|
-
|
|
802
|
-
|
|
843
|
+
removes only what it must not have. Every state also has its type's control tools and
|
|
844
|
+
any tool the host passes as `essentialTools`, whatever the workflow declares. A grader or
|
|
845
|
+
code state is never offered `write_todos`, `archmax_eval`, `archmax_run`, `archmax_wait` or
|
|
846
|
+
`archmax_reset`, even when `allow_always` lists them, and naming one in its own `tools.allow`
|
|
847
|
+
is a `validate` error.
|
|
848
|
+
One built-in is **never disclosed** and grantable by nothing: `task`, Deep Agents'
|
|
849
|
+
subagent dispatch — grading is a grader state and delegating is a sub-workflow, so naming
|
|
803
850
|
`task` in an allow entry is a `validate` error and the kernel refuses the call.
|
|
804
851
|
The planning scratchpad `write_todos` and the sandbox
|
|
805
852
|
`archmax_eval` are default tools: granted wherever the workflow-wide grant includes
|
|
806
853
|
them (everywhere, in a workflow without `allow_always`). `write_todos`'s prompt
|
|
807
|
-
guidance appears only when some state is granted it, and
|
|
808
|
-
whether or not
|
|
854
|
+
guidance appears only when some state is granted it, and code states run in the
|
|
855
|
+
sandbox whether or not any state is granted `archmax_eval`. The interpreter is safe as a
|
|
809
856
|
default because the `tools.*` calls its code makes are decided against the active
|
|
810
857
|
state by the same kernel: it adds computation over what the state permits, not
|
|
811
858
|
access. `archmax_run` — executing an authored script **file** — is a default tool on
|
|
@@ -839,10 +886,11 @@ entries: `tools.forbid_always` at the root denies in every state,
|
|
|
839
886
|
`tools.forbid` on a state denies while it is active — subtracting from the
|
|
840
887
|
workflow-wide grant, the state's own `allow` and a host's `essentialTools` alike.
|
|
841
888
|
**Deny beats allow, and no narrower level widens a denial.** A forbid entry may
|
|
842
|
-
name the tool `*` (every tool); `*` in an allow list is a load error. A
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
889
|
+
name the tool `*` (every tool); `*` in an allow list is a load error. A grader's
|
|
890
|
+
calls and a code state's script are governed against **their own state** exactly
|
|
891
|
+
as the agent is — its `allow`, its `forbid`, `forbid_always` and every ancestor's.
|
|
892
|
+
The only call on runtime authority is the runtime's own `file`-value read check,
|
|
893
|
+
held to the workflow's grants across all its states and to every `forbid_always`.
|
|
846
894
|
|
|
847
895
|
Precedence (highest first):
|
|
848
896
|
|
|
@@ -856,20 +904,27 @@ Precedence (highest first):
|
|
|
856
904
|
`tools.forbid` denies in the active state, both regardless of any allow list
|
|
857
905
|
(and regardless of always-allowed): deny beats allow, and no narrower level
|
|
858
906
|
widens a denial.
|
|
859
|
-
2. **
|
|
907
|
+
2. **The state type's control tools** (baked in): an agent or human state has
|
|
908
|
+
`archmax_advance`, `archmax_reset`, `archmax_wait`, `archmax_raise` and the
|
|
909
|
+
variable tools; a code state `archmax_advance`, `archmax_raise` and the
|
|
910
|
+
variable tools; a grader state those plus `archmax_get_history`.
|
|
860
911
|
3. **Per-state `tools.allow`** — the listed tools/arg-shapes. An entry for a
|
|
861
912
|
tool the workflow-wide grant includes takes precedence (**narrower wins** —
|
|
862
913
|
the state's arg globs apply, not the broader grant); an entry for a default
|
|
863
914
|
tool a declared `allow_always` omits grants it in this state.
|
|
864
915
|
4. **The workflow-wide grant** — the declared `tools.allow_always`, guards
|
|
865
|
-
included, or the default tools, unconstrained, when the key is absent
|
|
916
|
+
included, or the default tools, unconstrained, when the key is absent; in a
|
|
917
|
+
grader or code state, minus `write_todos`, `archmax_eval` and `archmax_run`.
|
|
866
918
|
5. **The host's `essentialTools`** — always permitted when no state entry
|
|
867
919
|
narrows them, whatever the workflow declares.
|
|
868
920
|
6. **Default**: everything else is blocked.
|
|
869
921
|
|
|
870
922
|
**Progressive disclosure.** The model is only shown the active state's tool
|
|
871
|
-
surface: the workflow-wide grant + a host's `essentialTools` + the state's entries +
|
|
872
|
-
(hidden in terminal states), minus every tool
|
|
923
|
+
surface: the workflow-wide grant + a host's `essentialTools` + the state's entries +
|
|
924
|
+
its control tools (`archmax_advance` hidden in terminal states), minus every tool
|
|
925
|
+
denied there by name. A grader is offered, and a code state's script reaches,
|
|
926
|
+
exactly the same derivation for its own state — anything else is absent
|
|
927
|
+
(`undefined` on `tools`). Disclosure is
|
|
873
928
|
name-level (arg-constrained tools stay visible; the kernel enforces args at
|
|
874
929
|
call time) and never replaces enforcement. **Skills are disclosed the same way**:
|
|
875
930
|
each model call names only the skills the active state enables, with their
|
|
@@ -923,7 +978,7 @@ Why by slug, and not with `tools.allow` paths:
|
|
|
923
978
|
Capability governance and execution governance are different questions.
|
|
924
979
|
- **`archmax_run` can only ever be narrowed.** A non-overridable kernel rule
|
|
925
980
|
(`script.skill-only`) confines it to skill bundles, so a script the agent writes
|
|
926
|
-
into `scratchpad/` is unrunnable, a
|
|
981
|
+
into `scratchpad/` is unrunnable, a code-state script is unrunnable, and
|
|
927
982
|
`{ tool: archmax_run, paths: ["**"] }` grants nothing — `validate` reports such
|
|
928
983
|
an entry as inert.
|
|
929
984
|
|
|
@@ -939,11 +994,12 @@ that serves bundles, naming what the workflow cannot reach. Any root block silen
|
|
|
939
994
|
that last one — write `skills: { allow_always: [] }` when a workflow grants no
|
|
940
995
|
capability workflow-wide.
|
|
941
996
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
997
|
+
Grader and code states are **not exempt**: their calls are governed against their
|
|
998
|
+
own state, so a code state that reads a bundle enables it in its own
|
|
999
|
+
`skills.allow`. That is the shape to reach for when a capability should *enforce*
|
|
1000
|
+
something without being *usable* by the agent: the agent's state enables nothing,
|
|
1001
|
+
and a code state in front of or behind it enables the bundle and runs the check. A
|
|
1002
|
+
bundle the workflow's `skills.forbid_always` denies is refused to every state.
|
|
947
1003
|
|
|
948
1004
|
**`tools.forbid` on a state denies there**, and `tools.forbid_always` at the
|
|
949
1005
|
root denies everywhere. Both beat every grant. Migrating a workflow off the
|
|
@@ -1083,20 +1139,13 @@ so `AGENTS.md.bak` is not captured.
|
|
|
1083
1139
|
governs mounts, naming what the workflow cannot reach. Any root block silences
|
|
1084
1140
|
it — write `mounts: { allow_always: [] }` when a workflow reaches none.
|
|
1085
1141
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
mount only a child grants, and the caller's hooks cannot reach that;
|
|
1094
|
-
- it writes one only where a grant of the workflow asks `access: read_write`, and an
|
|
1095
|
-
`access: read` in `allow_always` narrows any mount for it (`mount.read-only`).
|
|
1096
|
-
|
|
1097
|
-
So when a hook needs a mount no state's model should see, grant it in a state the
|
|
1098
|
-
hook's workflow has anyway, or in `allow_always` with the model's states
|
|
1099
|
-
forbidding it.
|
|
1142
|
+
Grader and code states are bound by **their own state's** mount grants, exactly
|
|
1143
|
+
like the agent: a code state reads a governed mount only where `allow_always` or
|
|
1144
|
+
its own `mounts.allow` grants it, and writes one only where that grant asks
|
|
1145
|
+
`access: read_write`. So when a check needs a mount no agent state should see,
|
|
1146
|
+
grant it on the check's code state alone. The one read on runtime authority is
|
|
1147
|
+
the runtime's own `file`-value check, held to the **workflow's** grants taken
|
|
1148
|
+
across every state and to every `forbid_always`.
|
|
1100
1149
|
|
|
1101
1150
|
## Session variables in guards
|
|
1102
1151
|
|
|
@@ -1224,89 +1273,149 @@ the tool's effect instead (the file it wrote, the variable it set).
|
|
|
1224
1273
|
To write the characters `${{…}}` literally — authoring a guard through
|
|
1225
1274
|
`write_file`, or writing about the syntax — double the dollar: `$${{name}}`.
|
|
1226
1275
|
|
|
1227
|
-
This is **model-facing
|
|
1228
|
-
|
|
1229
|
-
|
|
1276
|
+
This is **model-facing**: the agent's calls and a grader's get it. An
|
|
1277
|
+
`archmax_run` script (`args.variables`) or a code state's script (`variables`)
|
|
1278
|
+
already holds the run's variables and composes strings in JavaScript, so `${{…}}`
|
|
1279
|
+
in a script's `tools.*` call is ordinary text.
|
|
1230
1280
|
|
|
1231
|
-
##
|
|
1281
|
+
## Grader and code states
|
|
1232
1282
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
# resolves to workflows/<slug>/hooks/check.js
|
|
1238
|
-
after: { rubric: reply-tone, max_iterations: 2 } # per hook, not per state
|
|
1239
|
-
# a script after hook may carry the same budget:
|
|
1240
|
-
# after: { script: hooks/review.js, max_iterations: 2 }
|
|
1241
|
-
|
|
1242
|
-
# or a list, run in sequence (short-circuits on veto):
|
|
1243
|
-
before:
|
|
1244
|
-
- { script: hooks/pre.js }
|
|
1245
|
-
- rubric: { instructions: judge the tone }
|
|
1246
|
-
```
|
|
1283
|
+
Two state types are run by the runtime itself, together called **automated
|
|
1284
|
+
states**. Both are ordinary states on the graph: entered by a transition, leaving
|
|
1285
|
+
by `archmax_advance` along one of their own transitions, ending the session with
|
|
1286
|
+
`archmax_raise`, governed by their own tool surface.
|
|
1247
1287
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
- **`max_iterations`** bounds how many times a `correct` may send the agent back
|
|
1282
|
-
before the hook becomes a hard veto: inside the declaration for a rubric, a
|
|
1283
|
-
sidecar for a script or custom hook, else 0. It is counted **per hook by
|
|
1284
|
-
position**, so two graders on one state never share a budget. An
|
|
1285
|
-
exhausted budget is a *terminal failure* — see
|
|
1286
|
-
[Budgets & error routing](#budgets--error-routing).
|
|
1287
|
-
- **`before` hooks are ok/veto only**: a `correct` verdict from a `before` hook
|
|
1288
|
-
is treated as veto, and `max_iterations` on a `before` hook is a validation
|
|
1289
|
-
error.
|
|
1290
|
-
- **Fail-closed**: a hook that *errors* vetoes the transition. A rubric
|
|
1291
|
-
whose response cannot be parsed into a verdict also fails closed (**veto**),
|
|
1292
|
-
never coerced to ok — as does a `model` id the host cannot serve.
|
|
1293
|
-
|
|
1294
|
-
The hook contract — a default-export function called with one object:
|
|
1295
|
-
|
|
1296
|
-
```js
|
|
1297
|
-
export default async function hook({
|
|
1298
|
-
state, phase /* "before"|"after" */, trigger, variables, messages,
|
|
1299
|
-
from, to, reason, // from/to/reason present on "after"
|
|
1300
|
-
tools, // privileged tool bridge, e.g. tools.readFile
|
|
1301
|
-
}) {
|
|
1302
|
-
return ok(); // or veto(reason) / correct(reason)
|
|
1303
|
-
}
|
|
1288
|
+
```yaml
|
|
1289
|
+
states:
|
|
1290
|
+
check-refund: # CODE state: runs its script, makes no model call
|
|
1291
|
+
type: code
|
|
1292
|
+
script: scripts/check-refund.js
|
|
1293
|
+
skills: { allow: [order-data] } # what the SCRIPT reads is granted HERE
|
|
1294
|
+
on_error: escalation
|
|
1295
|
+
transitions:
|
|
1296
|
+
- to: refund-review
|
|
1297
|
+
description: The recorded decision matches the refund policy.
|
|
1298
|
+
- to: refund-request
|
|
1299
|
+
description: The recorded decision contradicts the policy; send it back with the fix.
|
|
1300
|
+
|
|
1301
|
+
review-enrichment: # GRADER state: an isolated grader decides the edge
|
|
1302
|
+
type: grader
|
|
1303
|
+
budget: { maxTurns: 8 } # the grader's own model calls
|
|
1304
|
+
on_error: escalation
|
|
1305
|
+
skills: { allow: [] } # the GRADER is governed HERE too: no bundle, and
|
|
1306
|
+
# read_file (workflow-wide) reads the returned files
|
|
1307
|
+
instructions: |- # REQUIRED — its criteria
|
|
1308
|
+
Read `orders_to_enrich` and `enrichments` with archmax_get_variables, then
|
|
1309
|
+
each entry's `enrichment_file` with read_file.
|
|
1310
|
+
- One entry per requested `order_id`, none missing or extra, each `delayed`
|
|
1311
|
+
matching its file: advance to `enrichment-reported`.
|
|
1312
|
+
- Otherwise advance back to `enrich-order`, naming what to fix. Before
|
|
1313
|
+
that, count the steps in archmax_get_history's trail with kind "grader"
|
|
1314
|
+
and to "enrich-order"; if there are already two, raise
|
|
1315
|
+
`enrichment-incorrect` instead.
|
|
1316
|
+
transitions: # REQUIRED — at least one
|
|
1317
|
+
- to: enrichment-reported
|
|
1318
|
+
description: Every requested order has exactly one enrichment, and each matches its file.
|
|
1319
|
+
- to: enrich-order
|
|
1320
|
+
description: The enrichments are incomplete or do not match their files; send them back.
|
|
1304
1321
|
```
|
|
1305
1322
|
|
|
1306
|
-
|
|
1307
|
-
`
|
|
1308
|
-
|
|
1309
|
-
|
|
1323
|
+
- **When it runs.** Whenever the session's position reaches it — the agent's
|
|
1324
|
+
`archmax_advance`, another automated state's advance, a human decision, an
|
|
1325
|
+
`on_error` route, a turn that opens there, an `archmax_reset` back to it — the
|
|
1326
|
+
runtime runs it **in place of the next model call** of the session's agent. The
|
|
1327
|
+
agent's model is not called while the session stands there (and an automated
|
|
1328
|
+
state spends none of the agent's `maxTurns`). Consecutive automated states run
|
|
1329
|
+
one after another, each committed as its own checkpointed step, so **a chain of
|
|
1330
|
+
code states makes no model call at all**. A chain that never reaches an agent
|
|
1331
|
+
state, a human state or a completion is stopped by the turn's recursion limit
|
|
1332
|
+
(`GraphRecursionError`: the session ends `failed` with `recursion-limit`, not an
|
|
1333
|
+
`on_error` route).
|
|
1334
|
+
- **What it is offered** — exactly its own state's surface: its `tools.allow`, the
|
|
1335
|
+
workflow-wide grant (`allow_always`, or the default tools when it is absent), the
|
|
1336
|
+
host's essential tools, and its controls — one set for both types:
|
|
1337
|
+
`archmax_advance` (absent when terminal), `archmax_raise`,
|
|
1338
|
+
`archmax_get_variables`, `archmax_set_variables`, `archmax_get_history` (which
|
|
1339
|
+
no agent or human state is offered). Never `write_todos`, `archmax_eval`,
|
|
1340
|
+
`archmax_run`, `archmax_wait` or `archmax_reset` — not even when `allow_always`
|
|
1341
|
+
lists one (it is left out), and naming one in its own `tools.allow` is a
|
|
1342
|
+
`validate` error. An automated state **cannot park**. Every call is decided by
|
|
1343
|
+
the kernel against that state, like the agent's: its `forbid`, `forbid_always`,
|
|
1344
|
+
its skill and mount grants and every safety rule bind it, and a tool another
|
|
1345
|
+
state grants is not reachable.
|
|
1346
|
+
- **How it moves.** `archmax_advance({ to, reason })` is checked at the call (the
|
|
1347
|
+
edge exists, the state's `requires` hold against the variables at that moment;
|
|
1348
|
+
`evidence` only into a human state); a refusal can be retried. The **first
|
|
1349
|
+
accepted** advance or raise is the decision and every later one is refused. The
|
|
1350
|
+
decision takes effect when the run finishes **cleanly**.
|
|
1351
|
+
- **How it settles** — exactly one of:
|
|
1352
|
+
- **advanced** → the session moves; the trail records `{ to, kind: grader | code,
|
|
1353
|
+
reason }`; a runtime note carries the reason to the next state
|
|
1354
|
+
(`[grader] The '<state>' grader moved the run to '<to>': <reason>. …` /
|
|
1355
|
+
`[code] '<state>' moved the run to '<to>': <reason>. …`); a human target parks
|
|
1356
|
+
after its one reply-only handoff message.
|
|
1357
|
+
- **raised** → the session ends `failed`, its `exit` carrying the code and reason.
|
|
1358
|
+
- **completed** → a terminal state finished without either call; the session
|
|
1359
|
+
completes, held to the trigger's `returns`.
|
|
1360
|
+
- **failed** → it threw, timed out, exhausted its budget, or a **non-terminal**
|
|
1361
|
+
state finished without either call (`<type> state '<slug>' finished without
|
|
1362
|
+
calling archmax_advance or archmax_raise`) — routed through `on_error`, else the
|
|
1363
|
+
session ends rejected. A raise wins over an error; an error discards a recorded
|
|
1364
|
+
advance. Finishing undecided is never read as approval.
|
|
1365
|
+
- **The history.** Both types read the session with `archmax_get_history`, which
|
|
1366
|
+
answers `{ messages, trail }` (`SessionHistory`) as JSON: the session as it
|
|
1367
|
+
stood when the state was entered, built from the checkpointed session, whole
|
|
1368
|
+
and oldest first, nothing windowed. A message is `{ role, text, toolCalls?,
|
|
1369
|
+
tool?, id?, note? }`: an `assistant` entry lists its calls (`{ id?, name, args
|
|
1370
|
+
}`), the `tool` entry with the matching `id` carries the result inline in
|
|
1371
|
+
`text`, and a runtime note is `role: "runtime"` with its `note` kind.
|
|
1372
|
+
- **Code state.** `script` (required, `scripts/<file>.js|.mjs`) runs in the QuickJS
|
|
1373
|
+
sandbox in a fresh REPL per run. Its default export receives `{ state, trigger?,
|
|
1374
|
+
variables, tools }` and reads the history with
|
|
1375
|
+
`JSON.parse(await tools.archmax_get_history())`. Its return value is never
|
|
1376
|
+
read. `instructions`, `model`, `budget`, `evidence` and `approvers` on a code
|
|
1377
|
+
state are `validate` errors; `settings.timeoutMs` bounds the script. A
|
|
1378
|
+
**terminal** code state completes the session when its script returns: approve
|
|
1379
|
+
into one to end without another model call. Script contract and worked
|
|
1380
|
+
examples: [`code-state-and-test-scripts.md`](code-state-and-test-scripts.md).
|
|
1381
|
+
- **Grader state.** A subagent with its own isolated context, built per run: its
|
|
1382
|
+
system prompt is a compiled-in grader preamble followed by the state's
|
|
1383
|
+
`instructions`; its task names the state and lists its transitions; it reads the
|
|
1384
|
+
session only through `archmax_get_history`, and the work with its own state's
|
|
1385
|
+
tools. Its messages never enter the session's transcript, its tokens never
|
|
1386
|
+
stream, it writes no checkpoint; the session reads its decision as the trail
|
|
1387
|
+
step and the note. `instructions` (non-empty) and at least one transition are
|
|
1388
|
+
required; `evidence`, `approvers` and `budget.maxParks` are errors. Every edge
|
|
1389
|
+
into a grader needs a `description` (its instructions are never shown to the
|
|
1390
|
+
work it judges). `budget.maxTurns` bounds its own model calls and
|
|
1391
|
+
`budget.timeoutMs` its run. `model` (else `settings.model`) is resolved under the
|
|
1392
|
+
**`grader`** model role.
|
|
1393
|
+
- **No iteration budget.** Nothing caps how often a state is entered. A
|
|
1394
|
+
state that gives up after N rounds counts them in the trail — each earlier
|
|
1395
|
+
send-back is `{ kind: grader | code, to: <graded state> }` — and raises (or routes
|
|
1396
|
+
to an escalation) on the round after.
|
|
1397
|
+
- **Events.** `code-start { state, script }`, `code-output { state, line }`,
|
|
1398
|
+
`code-result { state, status, durationMs, reason? }`, `grader-start { state }`,
|
|
1399
|
+
`grader-result { state, status, durationMs, reason? }`; a grader's model calls are
|
|
1400
|
+
`model-usage` events under its state, added to the session's usage.
|
|
1401
|
+
|
|
1402
|
+
**Patterns.**
|
|
1403
|
+
|
|
1404
|
+
| Want | Shape |
|
|
1405
|
+
| --- | --- |
|
|
1406
|
+
| A deterministic check **in front of** a state | Make the trigger's entry (or the edge's target) a code state that advances to the work, or to a state that declines it (its agent reads the reason from the note) — or raises |
|
|
1407
|
+
| A check **behind** a state | Point the state's transition at a code state with an edge onward and an edge **back**; a send-back's `reason` is the correction the agent reads |
|
|
1408
|
+
| **Approve, then end** | Approve into a terminal code state (records the result, completes the session, no model call) |
|
|
1409
|
+
| Judgment (tone, completeness) | A grader behind the graded state, with an edge back; its `instructions` say when to raise, counting send-backs in the trail. Grade work the agent hands on (a report, a draft), not a live answer: a turn's `reply` is every text the agent said in it, a draft sent back included |
|
|
1410
|
+
|
|
1411
|
+
**Migrating hooks.** `before:`/`after:` are removed, with every hook kind
|
|
1412
|
+
(`{ script }`, `{ rubric }`, custom kinds), `max_iterations` and the root
|
|
1413
|
+
`extensions:` block; each is a load error naming the key. A `before` check becomes
|
|
1414
|
+
a code state in front of the state; an `after` check a code state behind it; a
|
|
1415
|
+
rubric a grader state behind it. Move `workflows/<slug>/hooks/*.js` to
|
|
1416
|
+
`scripts/` and rewrite each to the code-state contract (it advances or raises
|
|
1417
|
+
instead of returning `ok`/`veto`/`correct`), and grant on the code state whatever
|
|
1418
|
+
the script reads — there is no runtime authority any more.
|
|
1310
1419
|
|
|
1311
1420
|
## Budgets & error routing
|
|
1312
1421
|
|
|
@@ -1320,29 +1429,35 @@ states:
|
|
|
1320
1429
|
escalation: { instructions: "Explain the failure and hand off." } # terminal
|
|
1321
1430
|
```
|
|
1322
1431
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1432
|
+
A state's `budget` bounds that state (a grader's own calls are bounded by its
|
|
1433
|
+
`budget.maxTurns`). There is no workflow-wide budget: a whole turn is bounded by
|
|
1434
|
+
LangGraph's `recursionLimit`, 400 unless the host's invoke config sets its own (a
|
|
1435
|
+
sub-workflow's child session runs under the same bound). A turn that throws (model
|
|
1436
|
+
failure, cancellation, the recursion limit) ends the session `failed` with exit
|
|
1437
|
+
code `turn-error`, `turn-cancelled` or `recursion-limit`; the next message starts
|
|
1438
|
+
a fresh turn.
|
|
1328
1439
|
|
|
1329
1440
|
Two failure classes, routed differently:
|
|
1330
1441
|
|
|
1331
|
-
- **Recoverable rejections keep the
|
|
1332
|
-
`archmax_advance
|
|
1333
|
-
|
|
1334
|
-
throws** is answered the same way: its error message is the
|
|
1335
|
-
error-status answer, and it never routes to `on_error`.
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
`on_error
|
|
1442
|
+
- **Recoverable rejections keep the mover in place**: an invalid edge in
|
|
1443
|
+
`archmax_advance` or an unmet `requires` — the agent (or a grader, or a code
|
|
1444
|
+
state's script, which may catch it) sees the reason and retries within the same
|
|
1445
|
+
state. A **tool that throws** is answered the same way: its error message is the
|
|
1446
|
+
call's error-status answer, and it never routes to `on_error`. A grader or code
|
|
1447
|
+
state *sending the work back* is a transition, not a failure: the agent reads
|
|
1448
|
+
the reason as a note.
|
|
1449
|
+
- **Terminal failures route to `on_error`**: an exhausted `budget`
|
|
1450
|
+
(`maxTurns`/`timeoutMs`/`maxParks`), a failed sub-workflow call nothing
|
|
1451
|
+
recovered, and a grader or code state that **fails** — threw, timed out, spent
|
|
1452
|
+
its budget, or finished without advancing or raising. With `on_error: <state>`
|
|
1453
|
+
declared, the run routes to that state (the failure reason arrives as an
|
|
1454
|
+
`[error]` runtime note) instead of ending; without it, the failure ends the run.
|
|
1455
|
+
|
|
1456
|
+
**Recommendation**: declare `on_error` on every grader and code state, so a script
|
|
1457
|
+
error or an undecided grader routes somewhere instead of ending the session
|
|
1458
|
+
rejected. The bundled example demonstrates this: `refund-request` pairs
|
|
1459
|
+
`budget.maxTurns: 12` with `on_error: escalation`, and its `screen-request`,
|
|
1460
|
+
`check-refund` and `review-enrichment` route to the same terminal escalation state.
|
|
1346
1461
|
|
|
1347
1462
|
## Human states
|
|
1348
1463
|
|
|
@@ -1523,7 +1638,7 @@ enrich:
|
|
|
1523
1638
|
(`.platform/system/GRAPH_STATE.md`) keeps that by wrapping what children skip
|
|
1524
1639
|
in `<!-- top-level-only -->` … `<!-- /top-level-only -->` lines.
|
|
1525
1640
|
- **Same agent, isolated context, own session.** Same model, backend, host
|
|
1526
|
-
tools; its **own**
|
|
1641
|
+
tools; its **own** grader and code states, from its own spec; a fresh transcript; the child's *own* per-state
|
|
1527
1642
|
`tools.allow`. It is a separate session (`<parent>~<n>`, `n` the dispatch's
|
|
1528
1643
|
number in the caller, counted across its turns) with its own checkpoints and its own `scratchpad/`: a path it reads or writes
|
|
1529
1644
|
resolves inside its own session, so it can neither see nor overwrite a file of
|
|
@@ -1658,6 +1773,14 @@ This is a change: these keys used to be ignored without a diagnostic. A spec tha
|
|
|
1658
1773
|
| state `for_each:` | One call per entry — parallel tool calls from the agent, or `Promise.all` in a script |
|
|
1659
1774
|
| state `result:` | The tool result; add `requires: [<name>]` to the state so it must record what it needs |
|
|
1660
1775
|
| `archmax_advance({ params })` | The call's arguments |
|
|
1776
|
+
| state `before:` (any hook kind) | A **code state in front** of the state — the trigger's entry, or the target of the edge into it — advancing to the work or to a declining state (see [Grader and code states](#grader-and-code-states)) |
|
|
1777
|
+
| state `after: { script: hooks/<x>.js }` | A **code state behind** the state: its transition points at the code state, which advances onward or back with the fix as its `reason` |
|
|
1778
|
+
| `{ rubric: … }` hook | A **grader state** behind the graded state with an edge back; the criteria become its `instructions` |
|
|
1779
|
+
| `max_iterations` / `max_corrections` | Nothing: the grader's `instructions` (or the script) count send-backs in the trail and raise |
|
|
1780
|
+
| root `extensions:` (custom hook kinds) and `createAgent({ hookExecutors })` | Nothing: a custom check is a code state |
|
|
1781
|
+
| `workflows/<slug>/hooks/*.js` returning `ok`/`veto`/`correct` | `workflows/<slug>/scripts/*.js` that call `tools.archmax_advance` / `tools.archmax_raise` |
|
|
1782
|
+
| `runtime.version: "1"` / `"2"` | `"3"` (or omit `runtime`) — 1 and 2 are refused as unsupported |
|
|
1783
|
+
| root `budget: { maxModelCalls }` | Nothing — delete it. A turn is bounded by LangGraph's `recursionLimit` (400 unless the host's invoke config sets one); bound a state with its own `budget.maxTurns` |
|
|
1661
1784
|
|
|
1662
1785
|
**No fan-out.** There is no concurrent branching: a run traverses **one state at a
|
|
1663
1786
|
time**, moved only by `archmax_advance`, a human decision, or `on_error` routing.
|
|
@@ -1689,7 +1812,8 @@ it costs latency, not correctness.
|
|
|
1689
1812
|
```
|
|
1690
1813
|
workflows/<name>/workflow.yaml # the machine (canonical spec)
|
|
1691
1814
|
workflows/<name>/WORKFLOW.md # OPTIONAL prose addendum (no frontmatter)
|
|
1692
|
-
workflows/<name>/
|
|
1815
|
+
workflows/<name>/scripts/*.js # code-state scripts — run BY THE RUNTIME when a
|
|
1816
|
+
# `type: code` state is entered.
|
|
1693
1817
|
# Authoring plane: the agent cannot read or run
|
|
1694
1818
|
# anything under workflows/
|
|
1695
1819
|
workflows/<name>/tests/ # cases (*.test.yaml, one case per
|
|
@@ -1700,9 +1824,10 @@ skills/<capability>/SKILL.md # a capability, bundled (Agent Skills format).
|
|
|
1700
1824
|
# behavior goes inline in that state's
|
|
1701
1825
|
# `instructions` (never one SKILL.md per state)
|
|
1702
1826
|
skills/<capability>/scripts/*.js # its archmax_run sources — run BY THE AGENT
|
|
1703
|
-
# (
|
|
1704
|
-
#
|
|
1705
|
-
#
|
|
1827
|
+
# (code-state scripts are NOT here: they live in
|
|
1828
|
+
# workflows/<name>/scripts/ — same directory
|
|
1829
|
+
# name, other plane — are run by the runtime,
|
|
1830
|
+
# and the agent cannot read them)
|
|
1706
1831
|
skills/<capability>/assets/* # its inputs (read-only)
|
|
1707
1832
|
scratchpad/ # THE working area: intermediate files and the
|
|
1708
1833
|
# artifacts a state produces. The run IS the
|
|
@@ -1718,7 +1843,7 @@ This is the **complete** directory contract — there are no other magic
|
|
|
1718
1843
|
locations, so design the full file set up front instead of exploring.
|
|
1719
1844
|
Reference these paths **workspace-relative, without a leading slash**
|
|
1720
1845
|
(`skills/order-data/assets/orders.json`, `scratchpad/refund.json`) — in `instructions`, `evidence`,
|
|
1721
|
-
allow-entry globs, and paths referenced inside
|
|
1846
|
+
allow-entry globs, and paths referenced inside code-state scripts alike.
|
|
1722
1847
|
|
|
1723
1848
|
The authoring-plane prefix (`workflows/`) is **reserved**: a
|
|
1724
1849
|
host cannot mount it (`MountCollisionError` at assembly), an allow entry naming
|
|
@@ -1726,48 +1851,13 @@ it is a `validate` **error**, and a script that reads it is blocked with a
|
|
|
1726
1851
|
diagnostic naming the prefix. Never write an allow entry, an `evidence` path, or a
|
|
1727
1852
|
case `workspace:` key under it.
|
|
1728
1853
|
|
|
1729
|
-
### Grading rubrics — inline on the hook
|
|
1730
|
-
|
|
1731
|
-
A grader is the **value** of the hook that applies it, not a file beside the spec
|
|
1732
|
-
and not a named entry a hook points at:
|
|
1733
|
-
|
|
1734
|
-
```yaml
|
|
1735
|
-
states:
|
|
1736
|
-
respond:
|
|
1737
|
-
after:
|
|
1738
|
-
- rubric:
|
|
1739
|
-
instructions: |- # REQUIRED — the criteria and the verdict
|
|
1740
|
-
Judge the tone of the last assistant message. Return `ok`,
|
|
1741
|
-
`correct` or `veto`, always with a concise `reason`.
|
|
1742
|
-
max_iterations?: number # the grade-and-retry budget, counted per hook
|
|
1743
|
-
model?: string # a model id to grade on, resolved through the
|
|
1744
|
-
# assembly's modelFactory (third argument)
|
|
1745
|
-
metadata?: object # host data the runtime never reads
|
|
1746
|
-
```
|
|
1747
|
-
|
|
1748
|
-
A rubric has **no name**, and there is deliberately **no `title`, `description`
|
|
1749
|
-
or `response_format`**: nothing routes to a rubric, nothing labels one in a prompt
|
|
1750
|
-
(a display label goes in `metadata`), and a rubric only ever grades — so every
|
|
1751
|
-
dispatch requests the verdict schema unconditionally.
|
|
1752
|
-
|
|
1753
|
-
Verdict shape: `{ verdict: "ok"|"correct"|"veto", reason: string }`.
|
|
1754
|
-
|
|
1755
|
-
Two states needing the same grader declare it **twice**: a state legible on its
|
|
1756
|
-
own beats a de-duplicated document. Its identity is its position (state, phase,
|
|
1757
|
-
index), which is what the runtime dispatches it under and what its retry budget
|
|
1758
|
-
is keyed against — so two graders on one state never share a budget.
|
|
1759
|
-
|
|
1760
|
-
The agent learns nothing of it: the rendered graph says a phase carries a hook of
|
|
1761
|
-
kind `rubric`, there is no `task` tool to call one with, and no sandbox context
|
|
1762
|
-
has `task()`.
|
|
1763
|
-
|
|
1764
1854
|
## Cases
|
|
1765
1855
|
|
|
1766
1856
|
Declarative YAML case documents under `workflows/<name>/tests/*.test.yaml`
|
|
1767
1857
|
(or `.test.yml`) — **one case per file**; the case id is the tests/-relative
|
|
1768
1858
|
path minus the extension. `archmax test <workflow>` interprets them
|
|
1769
1859
|
**host-side**: no QuickJS sandbox runs a case (the sandbox serves only
|
|
1770
|
-
|
|
1860
|
+
code-state scripts and `archmax_run` inside the driven agent). These are distinct
|
|
1771
1861
|
from the runtime's own colocated unit tests. Only `*.test.yaml`/`*.test.yml`
|
|
1772
1862
|
files are discovered — a leftover `*.test.js` is ignored without a diagnostic.
|
|
1773
1863
|
|
|
@@ -1780,14 +1870,14 @@ the static feedback while a workflow is out of service.
|
|
|
1780
1870
|
(`maxConcurrency`, `caseTimeoutMs`, `judge` — see [Root fields](#root-fields)).
|
|
1781
1871
|
`tests.config.js` is **REMOVED**: its presence is a hard error in
|
|
1782
1872
|
`archmax test` and `archmax validate`. There is no `reporters` option.
|
|
1783
|
-
`judge: {}` enables the
|
|
1873
|
+
`judge: {}` enables the case judge (`grade:`) with the env-configured default model.
|
|
1784
1874
|
|
|
1785
1875
|
**The complete case schema (top-level keys, the flat step-entry list, the
|
|
1786
1876
|
assertion grammar, start conditions, mocks, `from:` fixture references) plus
|
|
1787
1877
|
worked examples — routing,
|
|
1788
1878
|
tool mocking, human-in-the-loop decide flows, triggered starts — and the
|
|
1789
1879
|
JS→YAML migration table are in
|
|
1790
|
-
[`
|
|
1880
|
+
[`code-state-and-test-scripts.md`](code-state-and-test-scripts.md).**
|
|
1791
1881
|
|
|
1792
1882
|
Every case carries two mandatory prose fields, both length-capped: a `title`
|
|
1793
1883
|
(a short label, ≤ 60 chars, printed beside the verdict) and a `description`
|
|
@@ -1802,7 +1892,7 @@ errors — fail closed.
|
|
|
1802
1892
|
|
|
1803
1893
|
```yaml
|
|
1804
1894
|
# Nothing is mocked — the case reads the real orders.json fixture, so the
|
|
1805
|
-
# denial is re-derived by the refund
|
|
1895
|
+
# denial is re-derived by the check-refund code state rather than asserted directly.
|
|
1806
1896
|
title: Refund denied for a shipped order
|
|
1807
1897
|
description: >-
|
|
1808
1898
|
A refund for shipped A-1002 parks at refund-review; the reviewer confirms the
|
|
@@ -1821,7 +1911,7 @@ steps:
|
|
|
1821
1911
|
|
|
1822
1912
|
## Code description docblocks
|
|
1823
1913
|
|
|
1824
|
-
**Every** code file in the workspace — both kinds:
|
|
1914
|
+
**Every** code file in the workspace — both kinds: code-state scripts and
|
|
1825
1915
|
`archmax_run` sources — MUST begin with a leading JSDoc block (`/** ... */`): a
|
|
1826
1916
|
title line, a blank line, then prose stating what the code does **and how**.
|
|
1827
1917
|
Cases are YAML documents, not code — they carry no JSDoc block;
|
|
@@ -1829,15 +1919,16 @@ their mandatory `title` and `description` fields are short and capped instead,
|
|
|
1829
1919
|
with any longer rationale in a YAML comment above the document. The canonical
|
|
1830
1920
|
statement of the
|
|
1831
1921
|
rule, with the required depth per file kind, is
|
|
1832
|
-
[SKILL.md →
|
|
1922
|
+
[SKILL.md → Workspace layout](../SKILL.md#workspace-layout-complete--nothing-else-exists).
|
|
1833
1923
|
|
|
1834
1924
|
- The block is the **human-facing contract**: admin UIs display only this
|
|
1835
1925
|
description, and the code below it must honor it. Author
|
|
1836
1926
|
description-first; when behavior changes, update the description, then edit
|
|
1837
1927
|
the code to match — never leave the two contradicting each other.
|
|
1838
1928
|
- Write it deep enough to review on its own: inputs read, the sequence of
|
|
1839
|
-
checks or assertions, and each outcome the code can produce
|
|
1840
|
-
|
|
1929
|
+
checks or assertions, and each outcome the code can produce (for a code-state
|
|
1930
|
+
script: each `archmax_advance` target and why, each `archmax_raise` code). A
|
|
1931
|
+
one-line label is not enough.
|
|
1841
1932
|
- It is inert at runtime — the QuickJS sandbox executes the source verbatim
|
|
1842
1933
|
(typed `@archmax-ai/harness/*` imports excepted; they are stripped).
|
|
1843
1934
|
- The prose ends at the first `@tag` line (tags are allowed but not part of
|
|
@@ -1845,7 +1936,7 @@ rule, with the required depth per file kind, is
|
|
|
1845
1936
|
`@specification` tag and no generation marker: the block is a human
|
|
1846
1937
|
description, not a machine contract.
|
|
1847
1938
|
- `archmax validate` **errors** when an cases is missing its
|
|
1848
|
-
`description` (the case schema requires it).
|
|
1939
|
+
`description` (the case schema requires it). Code-state scripts and `archmax_run`
|
|
1849
1940
|
sources are **not** checked — there the JSDoc rule is an authoring standard
|
|
1850
1941
|
you uphold yourself.
|
|
1851
1942
|
- Backends/UIs extract it with the package's exported
|
|
@@ -1886,30 +1977,49 @@ owns no second artifact area.
|
|
|
1886
1977
|
|
|
1887
1978
|
The bundled reference workspace lives at
|
|
1888
1979
|
`examples/customer-support/workflows/order-lookup/` and demonstrates the full
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
(ASCII graph + domain constraints).
|
|
1892
|
-
example. It declares a top-level `title`
|
|
1893
|
-
slugs below as the reference targets, plus
|
|
1894
|
-
holding the rules that hold across every state —
|
|
1895
|
-
live, the tenancy rule, and naming an order's exact
|
|
1896
|
-
them. Its shape:
|
|
1897
|
-
|
|
1898
|
-
- `
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
`
|
|
1980
|
+
v3 layout: `workflow.yaml` (the machine, including the `tests:` block and its
|
|
1981
|
+
grader and code states), the code-state scripts under `scripts/`, plus a
|
|
1982
|
+
frontmatter-free `WORKFLOW.md` prose addendum (ASCII graph + domain constraints).
|
|
1983
|
+
Read them in full for a real, working example. It declares a top-level `title`
|
|
1984
|
+
and a `title` on every state, with the slugs below as the reference targets, plus
|
|
1985
|
+
a top-level `instructions` block holding the rules that hold across every state —
|
|
1986
|
+
where the data and artifacts live, the tenancy rule, and naming an order's exact
|
|
1987
|
+
status — so no state repeats them. Its shape:
|
|
1988
|
+
|
|
1989
|
+
- `screen-request` (code, `triggers: { manual: , email_reply: }`,
|
|
1990
|
+
`scripts/screen-request.js`, enables `order-data`) — a check **in front**: it
|
|
1991
|
+
merges the requester and tenancy checks before any model call, and advances to
|
|
1992
|
+
`identify-case`, or to `decline` (a terminal agent state that tells the person
|
|
1993
|
+
why) with the reason.
|
|
1994
|
+
- `identify-case` routes to one of four paths via transition descriptions.
|
|
1995
|
+
- `orders-question` (terminal) answers from `skills/order-data/assets/orders.json`:
|
|
1996
|
+
a live answer ends where it is given.
|
|
1902
1997
|
- `refund-request` records a decision to `scratchpad/refund.json` (no allow entry
|
|
1903
|
-
narrows it — the working area is open to `write_file` wherever it is granted;
|
|
1904
|
-
correct file is the
|
|
1905
|
-
`
|
|
1906
|
-
|
|
1907
|
-
|
|
1998
|
+
narrows it — the working area is open to `write_file` wherever it is granted;
|
|
1999
|
+
what holds the agent to a correct file is the check) and advances to
|
|
2000
|
+
`check-refund`, a code state **behind** it (`scripts/check-refund.js`): it
|
|
2001
|
+
re-derives the policy, advances to `refund-review` when the decision matches,
|
|
2002
|
+
sends it back with the fix otherwise, and escalates after two send-backs.
|
|
2003
|
+
`refund-request` pairs `budget.maxTurns: 12` with `on_error: escalation`.
|
|
1908
2004
|
- `refund-review` (`type: human`, `evidence: [scratchpad/refund.json]`) — a person
|
|
1909
2005
|
approves (→ terminal `refund-closed`) or refines (→ back to `refund-request`).
|
|
1910
|
-
- `escalation` (terminal) is the error handler entered
|
|
1911
|
-
`refund
|
|
2006
|
+
- `escalation` (terminal) is the error handler entered via `on_error` (and from
|
|
2007
|
+
`check-refund`'s give-up edge).
|
|
1912
2008
|
- `general-question` (terminal) answers and writes `scratchpad/answer.json`.
|
|
2009
|
+
- `report-requested` (terminal, `triggers: { report_requested: }`) writes
|
|
2010
|
+
`scratchpad/report.json` for the requested `company` and summarizes it; when the
|
|
2011
|
+
order records cannot be read, the agent ends the session with `archmax_raise`
|
|
2012
|
+
instead (case `orders-unavailable`).
|
|
2013
|
+
- `enrich-order` (`triggers: { enrichment_requested: }`) — a host-fired path no
|
|
2014
|
+
person waits on — runs the `order-enrichment` fan-out script with `archmax_run`,
|
|
2015
|
+
records its result as `enrichments` (`requires: [enrichments]`), and advances to
|
|
2016
|
+
`review-enrichment`, a **grader** behind it: it reads `orders_to_enrich` and
|
|
2017
|
+
`enrichments` with `archmax_get_variables` and each returned `enrichment_file`
|
|
2018
|
+
with `read_file`. It approves into `enrichment-reported` (a terminal agent state
|
|
2019
|
+
that summarizes the enrichments), sends them back to `enrich-order` naming what
|
|
2020
|
+
to fix, or raises `enrichment-incorrect` once it has sent them back twice,
|
|
2021
|
+
counted on `archmax_get_history`'s trail. Case `enrichment-fan-out` pins the
|
|
2022
|
+
approval as a `grader` trail step.
|
|
1913
2023
|
|
|
1914
2024
|
### Source of truth in code
|
|
1915
2025
|
|
|
@@ -1917,13 +2027,14 @@ If you need to confirm a detail, these files define the schema in the runtime:
|
|
|
1917
2027
|
|
|
1918
2028
|
| Concern | File |
|
|
1919
2029
|
| --- | --- |
|
|
1920
|
-
|
|
|
2030
|
+
| The schema (every key, `script` grammar, `refineSpec`) | `src/machine/spec-schema.ts` |
|
|
2031
|
+
| Per-type lint errors and warnings | `src/machine/lint-spec.ts` |
|
|
2032
|
+
| Spec types (`MachineState`, `Transition`, `AllowEntry`, `WorkflowTestsConfig`) | `src/machine/types.ts` |
|
|
1921
2033
|
| Two-file loading (`workflow.yaml` + prose addendum) | `src/machine/load-spec.ts` |
|
|
1922
|
-
|
|
|
1923
|
-
| allow-entry matching
|
|
2034
|
+
| Type-aware surfaces (`controlTools`, `disclosedTools`, `checkAllowed`) | `src/machine/machine.ts`, `tool-names.ts` |
|
|
2035
|
+
| allow-entry matching | `src/machine/allow.ts` |
|
|
1924
2036
|
| Rendered workflow header + per-state graph block | `src/workflow/render-prompt.ts` |
|
|
1925
|
-
|
|
|
2037
|
+
| Automated states: running, outcomes, controls, code and grader runs, history | `src/workflow/automated.ts`, `automated-controls.ts`, `code-state.ts`, `grader-state.ts`, `history.ts` |
|
|
1926
2038
|
| Governance kernel (precedence) | `src/kernel/kernel.ts` |
|
|
1927
|
-
| `archmax_advance`
|
|
2039
|
+
| `archmax_advance` and the other control tools | `src/workflow/control-tools.ts` |
|
|
1928
2040
|
| Checkpointed state, `WORKFLOW_STATUSES` | `src/workflow/state.ts` |
|
|
1929
|
-
| Grading rubrics, verdict schema | `src/rubrics/rubrics.ts` |
|