@archmax-ai/harness 0.9.0 → 1.0.1

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.
Files changed (207) hide show
  1. package/README.md +58 -14
  2. package/dist/agent.d.ts +4 -6
  3. package/dist/agent.js +4 -6
  4. package/dist/agent.js.map +1 -1
  5. package/dist/assembly/compose.d.ts +5 -24
  6. package/dist/assembly/compose.js +28 -42
  7. package/dist/assembly/compose.js.map +1 -1
  8. package/dist/assembly/delegation-registry.js +3 -14
  9. package/dist/assembly/delegation-registry.js.map +1 -1
  10. package/dist/assembly/index.d.ts +8 -15
  11. package/dist/assembly/index.js +21 -28
  12. package/dist/assembly/index.js.map +1 -1
  13. package/dist/assembly/plain.js +1 -2
  14. package/dist/assembly/plain.js.map +1 -1
  15. package/dist/authoring-skill/archmax-harness/README.md +5 -5
  16. package/dist/authoring-skill/archmax-harness/SKILL.md +72 -30
  17. package/dist/authoring-skill/archmax-harness/references/backend-integration.md +33 -24
  18. package/dist/authoring-skill/archmax-harness/references/cases.md +41 -8
  19. package/dist/authoring-skill/archmax-harness/references/{hook-and-test-scripts.md → code-state-and-test-scripts.md} +599 -339
  20. package/dist/authoring-skill/archmax-harness/references/code-states-and-scripts.md +115 -0
  21. package/dist/authoring-skill/archmax-harness/references/diagnostics.md +40 -7
  22. package/dist/authoring-skill/archmax-harness/references/wiring.md +19 -10
  23. package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +404 -293
  24. package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +151 -62
  25. package/dist/cli/state-flow.d.ts +1 -1
  26. package/dist/cli/state-flow.js +18 -28
  27. package/dist/cli/state-flow.js.map +1 -1
  28. package/dist/cli/turn.d.ts +1 -1
  29. package/dist/cli/turn.js +1 -1
  30. package/dist/core/code-description.d.ts +3 -3
  31. package/dist/core/code-description.js +3 -3
  32. package/dist/core/deepagents.d.ts +5 -12
  33. package/dist/core/deepagents.js +4 -17
  34. package/dist/core/deepagents.js.map +1 -1
  35. package/dist/core/events.d.ts +23 -42
  36. package/dist/core/events.js +21 -24
  37. package/dist/core/events.js.map +1 -1
  38. package/dist/core/message-readers.d.ts +4 -5
  39. package/dist/core/message-readers.js +3 -8
  40. package/dist/core/message-readers.js.map +1 -1
  41. package/dist/core/messages.d.ts +0 -22
  42. package/dist/core/messages.js +1 -32
  43. package/dist/core/messages.js.map +1 -1
  44. package/dist/core/mounts.js +3 -3
  45. package/dist/core/platform-prompt.generated.js +1 -1
  46. package/dist/core/platform-prompt.generated.js.map +1 -1
  47. package/dist/core/session-zone.d.ts +1 -1
  48. package/dist/core/session-zone.js +1 -1
  49. package/dist/core/workspace-context.d.ts +1 -1
  50. package/dist/core/workspace.d.ts +1 -1
  51. package/dist/core/workspace.js +1 -1
  52. package/dist/core/zones.d.ts +4 -4
  53. package/dist/core/zones.js +5 -5
  54. package/dist/core/zones.js.map +1 -1
  55. package/dist/env.d.ts +9 -9
  56. package/dist/index.d.ts +3 -7
  57. package/dist/index.js +2 -4
  58. package/dist/index.js.map +1 -1
  59. package/dist/kernel/kernel.d.ts +31 -61
  60. package/dist/kernel/kernel.js +54 -112
  61. package/dist/kernel/kernel.js.map +1 -1
  62. package/dist/machine/lint-spec.js +53 -12
  63. package/dist/machine/lint-spec.js.map +1 -1
  64. package/dist/machine/machine.d.ts +36 -18
  65. package/dist/machine/machine.js +62 -51
  66. package/dist/machine/machine.js.map +1 -1
  67. package/dist/machine/spec-schema.d.ts +32 -180
  68. package/dist/machine/spec-schema.js +52 -113
  69. package/dist/machine/spec-schema.js.map +1 -1
  70. package/dist/machine/tool-names.d.ts +34 -16
  71. package/dist/machine/tool-names.js +41 -16
  72. package/dist/machine/tool-names.js.map +1 -1
  73. package/dist/machine/types.d.ts +14 -46
  74. package/dist/machine/types.js +0 -2
  75. package/dist/machine/types.js.map +1 -1
  76. package/dist/machine/validate-spec.d.ts +1 -1
  77. package/dist/public/sandbox.d.ts +59 -95
  78. package/dist/public/sandbox.js +22 -23
  79. package/dist/public/sandbox.js.map +1 -1
  80. package/dist/public/spec.d.ts +13 -20
  81. package/dist/public/spec.js +12 -20
  82. package/dist/public/spec.js.map +1 -1
  83. package/dist/runtime/contract.d.ts +11 -12
  84. package/dist/runtime/contract.js +16 -16
  85. package/dist/runtime/contract.js.map +1 -1
  86. package/dist/sandbox/assets/parts/code-state.js +17 -0
  87. package/dist/sandbox/executor.d.ts +25 -7
  88. package/dist/sandbox/executor.js +56 -57
  89. package/dist/sandbox/executor.js.map +1 -1
  90. package/dist/sandbox/imports.d.ts +10 -10
  91. package/dist/sandbox/imports.js +11 -11
  92. package/dist/sandbox/imports.js.map +1 -1
  93. package/dist/sandbox/prelude.d.ts +5 -6
  94. package/dist/sandbox/prelude.js +38 -35
  95. package/dist/sandbox/prelude.js.map +1 -1
  96. package/dist/sandbox/ptc-gateway.d.ts +19 -18
  97. package/dist/sandbox/ptc-gateway.js +26 -22
  98. package/dist/sandbox/ptc-gateway.js.map +1 -1
  99. package/dist/sessions/resume.d.ts +2 -2
  100. package/dist/sessions/resume.js +1 -1
  101. package/dist/sessions/resume.js.map +1 -1
  102. package/dist/sessions/scope.d.ts +0 -10
  103. package/dist/sessions/scope.js +0 -17
  104. package/dist/sessions/scope.js.map +1 -1
  105. package/dist/sessions/summary.d.ts +1 -1
  106. package/dist/testing/assertions.js +9 -4
  107. package/dist/testing/assertions.js.map +1 -1
  108. package/dist/testing/driver.js +3 -0
  109. package/dist/testing/driver.js.map +1 -1
  110. package/dist/testing/interpreter.d.ts +2 -2
  111. package/dist/testing/runner.d.ts +2 -2
  112. package/dist/validate/validate.d.ts +1 -1
  113. package/dist/validate/validate.js +39 -70
  114. package/dist/validate/validate.js.map +1 -1
  115. package/dist/workflow/automated-controls.d.ts +63 -0
  116. package/dist/workflow/automated-controls.js +158 -0
  117. package/dist/workflow/automated-controls.js.map +1 -0
  118. package/dist/workflow/automated.d.ts +112 -0
  119. package/dist/workflow/automated.js +163 -0
  120. package/dist/workflow/automated.js.map +1 -0
  121. package/dist/workflow/code-state.d.ts +46 -0
  122. package/dist/workflow/code-state.js +26 -0
  123. package/dist/workflow/code-state.js.map +1 -0
  124. package/dist/workflow/control-tools.d.ts +54 -23
  125. package/dist/workflow/control-tools.js +99 -79
  126. package/dist/workflow/control-tools.js.map +1 -1
  127. package/dist/workflow/file-values.d.ts +4 -4
  128. package/dist/workflow/file-values.js +1 -1
  129. package/dist/workflow/file-values.js.map +1 -1
  130. package/dist/workflow/governance.d.ts +11 -22
  131. package/dist/workflow/governance.js +47 -104
  132. package/dist/workflow/governance.js.map +1 -1
  133. package/dist/workflow/grader-state.d.ts +55 -0
  134. package/dist/workflow/grader-state.js +182 -0
  135. package/dist/workflow/grader-state.js.map +1 -0
  136. package/dist/workflow/history.d.ts +11 -0
  137. package/dist/{lifecycle/transcript.js → workflow/history.js} +24 -8
  138. package/dist/workflow/history.js.map +1 -0
  139. package/dist/workflow/middleware.d.ts +14 -17
  140. package/dist/workflow/middleware.js +109 -95
  141. package/dist/workflow/middleware.js.map +1 -1
  142. package/dist/workflow/move-events.d.ts +21 -0
  143. package/dist/workflow/move-events.js +20 -0
  144. package/dist/workflow/move-events.js.map +1 -0
  145. package/dist/workflow/on-error.js +0 -2
  146. package/dist/workflow/on-error.js.map +1 -1
  147. package/dist/workflow/parks.d.ts +9 -2
  148. package/dist/workflow/parks.js +16 -10
  149. package/dist/workflow/parks.js.map +1 -1
  150. package/dist/workflow/paths.d.ts +9 -9
  151. package/dist/workflow/paths.js +19 -19
  152. package/dist/workflow/paths.js.map +1 -1
  153. package/dist/workflow/render-prompt.d.ts +7 -5
  154. package/dist/workflow/render-prompt.js +22 -37
  155. package/dist/workflow/render-prompt.js.map +1 -1
  156. package/dist/workflow/signature-checks.d.ts +2 -2
  157. package/dist/workflow/snapshot.js +5 -29
  158. package/dist/workflow/snapshot.js.map +1 -1
  159. package/dist/workflow/state.d.ts +14 -42
  160. package/dist/workflow/state.js +20 -33
  161. package/dist/workflow/state.js.map +1 -1
  162. package/dist/workflow/sub-workflow.d.ts +3 -3
  163. package/dist/workflow/sub-workflow.js +3 -7
  164. package/dist/workflow/sub-workflow.js.map +1 -1
  165. package/dist/workflow/tool-service.d.ts +1 -9
  166. package/dist/workflow/tool-service.js +21 -24
  167. package/dist/workflow/tool-service.js.map +1 -1
  168. package/dist/workflow/turn-boundary.d.ts +2 -2
  169. package/dist/workflow/turn-boundary.js +10 -15
  170. package/dist/workflow/turn-boundary.js.map +1 -1
  171. package/dist/workflow/turn-runner.js +5 -6
  172. package/dist/workflow/turn-runner.js.map +1 -1
  173. package/package.json +2 -2
  174. package/skills/archmax-harness/README.md +5 -5
  175. package/skills/archmax-harness/SKILL.md +72 -30
  176. package/skills/archmax-harness/references/backend-integration.md +33 -24
  177. package/skills/archmax-harness/references/cases.md +41 -8
  178. package/skills/archmax-harness/references/{hook-and-test-scripts.md → code-state-and-test-scripts.md} +599 -339
  179. package/skills/archmax-harness/references/code-states-and-scripts.md +115 -0
  180. package/skills/archmax-harness/references/diagnostics.md +40 -7
  181. package/skills/archmax-harness/references/wiring.md +19 -10
  182. package/skills/archmax-harness/references/workflow-schema.md +404 -293
  183. package/skills/archmax-harness/references/workflow-yaml.md +151 -62
  184. package/dist/authoring-skill/archmax-harness/references/hooks-and-scripts.md +0 -89
  185. package/dist/lifecycle/hook-shape.d.ts +0 -44
  186. package/dist/lifecycle/hook-shape.js +0 -66
  187. package/dist/lifecycle/hook-shape.js.map +0 -1
  188. package/dist/lifecycle/runner.d.ts +0 -197
  189. package/dist/lifecycle/runner.js +0 -419
  190. package/dist/lifecycle/runner.js.map +0 -1
  191. package/dist/lifecycle/tool-calls.d.ts +0 -9
  192. package/dist/lifecycle/tool-calls.js +0 -57
  193. package/dist/lifecycle/tool-calls.js.map +0 -1
  194. package/dist/lifecycle/transcript.d.ts +0 -31
  195. package/dist/lifecycle/transcript.js.map +0 -1
  196. package/dist/rubrics/rubrics.d.ts +0 -135
  197. package/dist/rubrics/rubrics.js +0 -145
  198. package/dist/rubrics/rubrics.js.map +0 -1
  199. package/dist/sandbox/assets/parts/core.js +0 -22
  200. package/dist/sandbox/assets/parts/lifecycle-hook.js +0 -47
  201. package/dist/sandbox/bridge.d.ts +0 -16
  202. package/dist/sandbox/bridge.js +0 -95
  203. package/dist/sandbox/bridge.js.map +0 -1
  204. package/dist/workflow/visits.d.ts +0 -21
  205. package/dist/workflow/visits.js +0 -45
  206. package/dist/workflow/visits.js.map +0 -1
  207. package/skills/archmax-harness/references/hooks-and-scripts.md +0 -89
@@ -14,7 +14,7 @@ instructions: |- # standing direction for the whole run (warning
14
14
  Records live in skills/order-data/assets/orders.json; write artifacts under scratchpad/.
15
15
 
16
16
  Answer only from records you have read; never fabricate.
17
- runtime: { engine: archmax-harness, version: "2" }
17
+ runtime: { engine: archmax-harness, version: "3" } # 3 is the only contract; omitted = 3
18
18
  disabled: false # true = out of service: no new turns; parked runs still drain
19
19
  settings: # sandbox limits: timeoutMs, memoryLimitBytes, maxPtcCalls,
20
20
  timeoutMs: 15000 # maxResultChars, prompt_cache: { enabled, ttl }
@@ -23,9 +23,7 @@ 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
27
- metadata: { canvas: {} } # host data, never read; accepted at root, state, rubric — leave as found
28
- extensions: { hooks: [] } # host-registered hook kinds; rarely authored
26
+ metadata: { canvas: {} } # host data, never read; accepted at root and on a state — leave as found
29
27
 
30
28
  tools: # tool governance, workflow level
31
29
  allow_always: # granted in EVERY state; a state's allow adds to or narrows it.
@@ -48,7 +46,7 @@ skills: # skill governance, workflow level — SLUGS, n
48
46
 
49
47
  states:
50
48
  triage: # key = slug = identity (^[a-z0-9]+(-[a-z0-9]+)*$)
51
- type: agent # agent (default) | human
49
+ type: agent # agent (default) | human | grader | code (grader/code = automated)
52
50
  title: Triage request # label for people/hosts; reaches no model call
53
51
  summary: Record the refund decision for review. # ditto: an authoring/host label, NOT in the prompt
54
52
  triggers: # a trigger is declared on the state it ENTERS; key = id
@@ -66,17 +64,8 @@ states:
66
64
  # entry types: string integer number boolean date date-time object array file
67
65
  instructions: >-
68
66
  Record the decision in scratchpad/refund.json, then archmax_advance to
69
- review; never close the case yourself.
70
- before: # gates entry: ok/veto only; a veto here rejects the run
71
- - script: hooks/check-requester.js
72
- after: # gates the archmax_advance out (or completion, when terminal)
73
- - script: hooks/check-policy.js
74
- max_iterations: 2 # how many `correct`s before a hard veto (per hook; default 0)
75
- - rubric: # LLM grader, inline; the agent never sees it
76
- instructions: |- # the only required key: criteria + verdict (ok/correct/veto + reason)
77
- Judge the tone of the last assistant message …
78
- max_iterations: 1
79
- model: gpt-4.1-mini # optional override
67
+ check-policy; never close the case yourself. If a note sends the case
68
+ back, fix what it names and advance again.
80
69
  tools:
81
70
  allow: # adds to allow_always; entry = name | { tool, args: {<arg>: [globs]} } | { tool, paths }
82
71
  - lookup_customer
@@ -94,13 +83,27 @@ states:
94
83
  on_error: closed # where a terminal failure of this segment routes
95
84
  metadata: { x: 40, y: 180 }
96
85
  transitions: # none => terminal state
97
- - to: review # slug; must exist
98
- description: The decision is recorded and needs a person's approval. # what the agent routes
86
+ - to: check-policy # slug; must exist
87
+ description: The decision is recorded in scratchpad/refund.json. # what the agent routes
99
88
  # on. Say WHEN, never where. Omit it to route on the target's
100
89
  # instructions instead (target must have some); REQUIRED on a
101
- # human state's transitions, where it is the button label.
90
+ # human state's transitions (the button label) and on every edge
91
+ # INTO a grader or code state, like this one.
102
92
  type: none # approve | reject | refine | none — semantic on human states
103
93
 
94
+ check-policy: # CODE state: the runtime runs its script on entry, NO model call
95
+ type: code
96
+ script: scripts/check-policy.js # REQUIRED on a code state, refused on any other type; resolves
97
+ # to workflows/<slug>/scripts/check-policy.js (.js|.mjs, no `..`)
98
+ summary: Re-derive the decision from the refund policy. # instructions/model/budget/evidence/
99
+ # approvers here are validate ERRORS: nothing reads them
100
+ skills: { allow: [order-data] } # the script's tools are THIS state's surface: grant here
101
+ # what it reads or calls (another state's grant never reaches it)
102
+ on_error: closed # a throw, a timeout, or finishing undecided routes here
103
+ transitions: # the script takes one: tools.archmax_advance({ to, reason })
104
+ - { to: review, description: The recorded decision matches the policy. }
105
+ - { to: triage, description: The decision contradicts the policy; send it back with the fix. }
106
+
104
107
  review: # human state: parks with a decision record; a PERSON picks the edge
105
108
  type: human
106
109
  title: Review refund
@@ -110,27 +113,57 @@ states:
110
113
  approvers: [finance@acme.com]
111
114
  evidence: [scratchpad/refund.json] # plus whatever archmax_advance attached via `evidence:`
112
115
  transitions: # required; no self-edge; one target per labeled type
113
- - { to: closed, type: approve, description: Approve and close. }
116
+ - { to: reply, type: approve, description: Approve; tell the customer. }
114
117
  - { to: triage, type: refine, description: Send back with a comment. }
115
118
 
116
- closed: # bare key = terminal state with no declarations
119
+ reply: # agent state (type omitted)
120
+ instructions: >-
121
+ Draft the customer's email in scratchpad/reply.md, then archmax_advance to
122
+ grade-reply; if a note sends it back, revise the draft as it says.
123
+ transitions:
124
+ - { to: grade-reply, description: The email is drafted in scratchpad/reply.md. }
125
+
126
+ grade-reply: # GRADER state: an isolated grader judges WORK HANDED ON (a draft,
127
+ type: grader # a report), never a live answer: a turn's reply is every text
128
+ # the agent said, a draft sent back included
129
+ instructions: |- # REQUIRED: its criteria, appended to a compiled-in grader preamble
130
+ Judge only the tone of scratchpad/reply.md.
131
+ - Friendly and professional: advance to `send-reply`.
132
+ - Curt or cold: advance back to `reply`, saying exactly what to change.
133
+ - If archmax_get_history's trail already holds two steps with kind
134
+ "grader" and to "reply", raise `reply-tone` instead of sending it back.
135
+ model: gpt-4.1-mini # optional; resolved under the `grader` model role
136
+ budget: { maxTurns: 4, timeoutMs: 60000 } # ITS OWN model calls / run time; no maxParks
137
+ on_error: closed # fails closed: an undecided grader routes here
138
+ transitions: # REQUIRED, at least one; no evidence/approvers on a grader
139
+ - { to: send-reply, description: The draft is friendly and professional. }
140
+ - { to: reply, description: The tone is off; send it back naming what to change. }
141
+
142
+ send-reply: # TERMINAL code state: the session completes when the script
143
+ type: code # returns — "approve, then end" with no further model call
144
+ script: scripts/send-reply.js
145
+ tools: { allow: [send_email] } # the host tool its script calls, granted HERE
146
+
147
+ closed: # bare key = terminal (agent) state with no declarations
117
148
  ```
118
149
 
119
150
  ## Machine rules
120
151
 
121
152
  - **Movement.** The agent moves only by `archmax_advance({ to, reason, evidence? })`
122
- — one per message, edge validated, then the leaving state's `after` hooks,
123
- the target's `before` hooks, then the target's tools unlock. A state with no
124
- transitions is terminal; the run ends when the agent finishes there. Human
125
- decisions and `on_error` are the other two movers. `archmax_reset({ reason })`
153
+ — one per message, edge and `requires` validated, then the target's tools
154
+ unlock (or, for a grader or code target, the target runs at once). A state
155
+ with no transitions is terminal; the run ends when the agent finishes there.
156
+ Human decisions, `on_error`, and grader and code states' own `archmax_advance`
157
+ are the other movers. `archmax_reset({ reason })`
126
158
  returns to the entry state (forbid it via `forbid_always` when a run must
127
159
  never restart). No fan-out: sequence states; concurrency lives in
128
160
  sub-workflow calls.
129
161
  - **Entry.** At least one state declares `triggers`; one state per trigger id;
130
162
  `manual` is the entry the CLI, a host firing and a caller all use. A
131
163
  re-invoked finished session is a **new turn in the state it ended in**, not
132
- at the entry — give a state re-entry transitions if a conversation must move
133
- on, or park instead of finishing.
164
+ at the entry (one that completed in a terminal code state opens at its
165
+ trigger's entry) — give a state re-entry transitions if a conversation must
166
+ move on, or park instead of finishing.
134
167
  - **Variables.** One flat checkpointed `name → value` store, never declared.
135
168
  Host seeds (`--variables`, `createAgent({ variables })`, a delivery) are
136
169
  **locked**; the agent uses `archmax_get_variables`/`archmax_set_variables`
@@ -156,7 +189,8 @@ states:
156
189
  judgement and any state with a big tool surface or several transitions on
157
190
  the default, since a small model that routes badly costs more in retries
158
191
  than it saves. Repeating the workflow's id on a state is a validate warning.
159
- A rubric keeps its own `model`, a sub-workflow uses its own spec's, and a
192
+ A grader state's `model` is resolved under the `grader` model role
193
+ (`modelFactory("grader", env, id)`), a sub-workflow uses its own spec's, and a
160
194
  host that passes an explicit `model` instance to `createAgent` outranks
161
195
  every declared id (it must pass `modelFactory` to honour them).
162
196
  - **Parking.** A state that needs an event or a time calls
@@ -170,9 +204,9 @@ states:
170
204
  - **Failing.** When the work cannot be completed, the agent calls
171
205
  `archmax_raise({ code, reason })` (alone in its message; any state): the
172
206
  session ends at once `failed`, with the agent's free-form `code` and
173
- `reason` as the outcome's `exit` — no `on_error` route, no hook, no
174
- `returns` check. A session that never raises is a success, so it is for
175
- failure only. Forbid it (`forbid_always`, or a state's `forbid`) where the
207
+ `reason` as the outcome's `exit` — no `on_error` route, no `returns`
208
+ check. A session that never raises is a success, so it is for failure
209
+ only. Forbid it (`forbid_always`, or a state's `forbid`) where the
176
210
  agent must not end the run itself; a child that raises fails its caller's
177
211
  call with kind `raised`.
178
212
  - **Human states** park with a record built from `instructions`, `evidence`
@@ -182,23 +216,54 @@ states:
182
216
  reply-only turn (every tool call refused) and re-parks unchanged; only
183
217
  `decide` moves the run. A
184
218
  `refine` edge delivers the reviewer's comment as correction.
185
- - **Hooks.** `before`: ok/veto (veto rejects entry; on a start state, the
186
- run; routed through `on_error` if declared). `after`: ok/correct/veto on
187
- the outgoing advance or on completion of a terminal state; `correct` sends
188
- the agent back with the reason, bounded per hook by `max_iterations`
189
- (default 0 → a correct is a hard veto). A list runs in order and stops at
190
- the first non-ok. Errors and missing files veto (fail-closed). Hooks run
191
- on runtime authority: the state's `allow`/`forbid`/`skills`/`mounts` do not
192
- bind them; `forbid_always`, safety rules and the workflow's mount grants
193
- (any state's) do. Human states may not declare hooks.
219
+ - **Grader and code states** (automated states). A **code** state runs
220
+ `script: scripts/<x>.js` in the sandbox; a **grader** state runs an isolated
221
+ grader whose criteria are the state's `instructions`. Both read the session
222
+ with `archmax_get_history` (`{ messages, trail }`: the whole transcript, a
223
+ tool result inline in its tool message's `text`, and the trail; a script
224
+ calls `JSON.parse(await tools.archmax_get_history())`). Both run on
225
+ entry — however the session got there — with **no call of the agent's model**
226
+ (a chain of code states makes none at all), and leave like every state:
227
+ `archmax_advance` along one of their transitions, whose `reason` reaches the
228
+ next state as a `[code]`/`[grader]` runtime note and a `kind: code|grader`
229
+ trail step, or `archmax_raise`. A **terminal** code state completes the
230
+ session when its script returns. Throwing, timing out, a spent grader budget,
231
+ or a non-terminal state that finishes without either call **fails closed** →
232
+ `on_error`. Each is offered exactly its own state's surface (its grants and
233
+ one control set for both types: `archmax_advance`, `archmax_raise`, the
234
+ variable tools, `archmax_get_history`), governed like the agent's calls — never
235
+ `write_todos`, `archmax_eval`, `archmax_run`, `archmax_wait`,
236
+ `archmax_reset`, even when `allow_always` lists one (a state's own
237
+ `tools.allow` naming one is an error): an automated state cannot park.
238
+ There is no iteration budget: a state that gives up after N rounds counts
239
+ them in the trail. Per type, `validate` errors on: a code state's
240
+ `instructions`/`model`/`budget`/`evidence`/`approvers`; a grader without
241
+ `instructions` or without a transition, or with `evidence`/`approvers`/
242
+ `budget.maxParks`. There are **no `before:`/`after:` hooks and no root
243
+ `extensions:`** — a leftover key is a load error naming it. Script contract:
244
+ [`code-states-and-scripts.md`](code-states-and-scripts.md).
245
+ - **Automated-state patterns.** A deterministic check **in front of** a state:
246
+ make the trigger's entry (or the edge's target) a code state that advances
247
+ to the work or to a state that declines it. A check **behind** a state: point
248
+ the state's transition at a code state that advances onward, or back with
249
+ the fix as its `reason` — the agent reads it and advances again. **Approve,
250
+ then end**: approve into a terminal code state, which costs no model call.
251
+ Judgment (tone, completeness): a grader behind the graded state with an edge
252
+ back; its `instructions` say when to raise ("after two send-backs" = two
253
+ trail steps `{ kind: grader, to: <graded state> }`). Grade work the agent
254
+ hands on (a report, a draft), not a live answer: a turn's `reply` is every
255
+ text the agent said in it, so a draft sent back has reached the person.
194
256
  - **Budgets and `on_error`.** `budget.maxTurns`/`timeoutMs`/`maxParks` bound
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
197
- corrections, exhausted budget, exhausted parks) routes to `on_error`
198
- instead of failing the run; recoverable rejections (invalid edge, veto,
199
- in-budget correct) keep the agent in place. Declare `on_error` on states
200
- with strict `after` hooks or bounded loops. A **tool that throws** is
201
- neither: the call is answered with an error-status tool message carrying
257
+ a state's segment (on a grader: its own model calls and run time; a code
258
+ state takes no `budget`, `settings.timeoutMs` bounds its script). There is
259
+ no root `budget` (declaring one is a load error): a whole turn is bounded by
260
+ LangGraph's `recursionLimit` (400 unless the host's invoke config sets one),
261
+ and a turn that hits it — a cycle between states, say — ends the session
262
+ `failed` (`recursion-limit`). A terminal failure (exhausted budget, exhausted parks, a
263
+ grader or code state that fails) routes to `on_error` instead of failing the
264
+ run; recoverable rejections (invalid edge, unmet `requires`) keep the agent
265
+ in place. Declare `on_error` on every automated state and on bounded loops.
266
+ A **tool that throws** is neither: the call is answered with an error-status tool message carrying
202
267
  the error, the agent stays in the state and can retry or switch tools, and
203
268
  `on_error` never sees it (a park or a cancelled run still propagates).
204
269
  - **Sub-workflows are tools.** Allow `archmax_workflow_<slug>` (slug verbatim)
@@ -226,10 +291,12 @@ states:
226
291
  sub-run; fan out with parallel tool calls or `Promise.all` in a script
227
292
  (bounds: depth 3, 4 concurrent). A child's human state parks the child;
228
293
  a script's delegation cannot park and fails closed.
229
- - **Choosing a mechanism.** Deterministic work with no model → an `archmax_run`
230
- script. Delegated work that is itself a process or reused across workflows
231
- → a sub-workflow. Judging work already done → a rubric hook (never
232
- callable by the agent; `task` is disclosed nowhere and not grantable).
294
+ - **Choosing a mechanism.** Deterministic work the agent triggers → an
295
+ `archmax_run` script. Deterministic work the graph runs at a fixed point (a
296
+ screen, a policy check, a record) → a code state. Delegated work that is itself
297
+ a process or reused across workflows → a sub-workflow. Judging work already
298
+ done → a grader state. Neither automated kind is callable by the agent (`task`
299
+ is disclosed nowhere and not grantable).
233
300
  - **Prompt economy.** The static prompt + tool schemas are re-sent every
234
301
  model call. The static part carries the workflow's `title` + `instructions`
235
302
  and **no state of the graph**; a state's `instructions` and its **own**
@@ -245,14 +312,19 @@ states:
245
312
 
246
313
  Only the state it is in. Every model call is handed that state's outgoing edges
247
314
  and nothing else of the machine — no other state's slug, `title`, `summary`,
248
- hooks or transitions, and no count of the states:
315
+ script, criteria or transitions, and no count of the states:
249
316
 
250
317
  ```
251
318
  Transitions — choose one with `archmax_advance` when this state's work is done:
319
+ - to `check-policy` (code) — The decision is recorded in scratchpad/refund.json.
252
320
  - to `review` (human decision node) — The decision is recorded and needs a person's approval.
321
+ - to `grade-reply` (grader) — The email is drafted in scratchpad/reply.md.
253
322
  - to `closed` (terminal) — Nothing to refund; the case is answered.
254
323
  ```
255
324
 
325
+ A `(grader)` or `(code)` target runs on its own and may send the run straight
326
+ back with a note saying what to change.
327
+
256
328
  Two rules follow, and they are the ones authors get wrong:
257
329
 
258
330
  1. **Write the `description` for the source state, not the target.** The agent
@@ -260,12 +332,19 @@ Two rules follow, and they are the ones authors get wrong:
260
332
  `The refund is over $50, or the customer has asked twice.` Not `Go to
261
333
  refund-review.` — that tells a reader who already knows the graph something
262
334
  they cannot use, and tells this reader nothing.
263
- 2. **Routing conditions cannot live downstream.** A target's `summary` or
264
- `instructions` is never disclosed to the state routing into it. If the
265
- condition is "when the order is over 30 days old", it belongs on the edge.
335
+ 2. **Routing conditions cannot live downstream.** A target's `summary` is never
336
+ disclosed to the state routing into it, nor its `instructions` across a
337
+ described edge. If the condition is "when the order is over 30 days old", it
338
+ belongs on the edge.
266
339
 
267
- A missing or blank `description` is a **load error**. There is no fallback, and
268
- no way to see more of the graph.
340
+ An **undescribed** edge (no `description`, or a blank one) routes on its target's
341
+ `instructions` instead, quoted under the edge's line as what the agent will do
342
+ once there: leave one undescribed only when those instructions already say when
343
+ it applies. It is a **load error** where the target has no instructions, on a
344
+ human state's transitions (the description is the button label), and into a
345
+ grader or code state (a grader's instructions are criteria the work is never
346
+ shown; a code state has none). A code state's own edges may go undescribed: its
347
+ script routes. There is no other way to see more of the graph.
269
348
 
270
349
  ## Governance
271
350
 
@@ -282,6 +361,14 @@ Closed by default; **deny beats allow, and no narrower level widens a denial.**
282
361
  `essentialTools`. Any other tool needs an `allow`/`allow_always` entry. A
283
362
  state entry naming a granted tool **narrows** it there, and one naming a
284
363
  default tool the list omits grants it there. `task` is ungrantable (error).
364
+ - **In a grader or code state** the surface is what the state is granted (the
365
+ workflow-wide grant included), minus `write_todos`, `archmax_eval`,
366
+ `archmax_run`, `archmax_wait` and `archmax_reset` even when `allow_always` lists
367
+ them (naming one in the state's
368
+ own `tools.allow` is an error), plus one control set for both types:
369
+ `archmax_advance`, `archmax_raise`, `archmax_get_variables`,
370
+ `archmax_set_variables` and `archmax_get_history` — never `archmax_wait` or
371
+ `archmax_reset`.
285
372
  - **Entries**: bare name; `{ tool, args: { <arg>: ["glob", …] } }` (matches
286
373
  if any glob matches; `${{var}}` allowed). An argument the tool declares as a
287
374
  path (else `file_path`) is canonicalized first; any other argument (a URL, an
@@ -303,8 +390,10 @@ Closed by default; **deny beats allow, and no narrower level widens a denial.**
303
390
  in the prompt). `allow` adds, `forbid` subtracts, `allow: []` denies
304
391
  nothing. A `tools.allow` entry may still narrow *within* an enabled bundle.
305
392
  A state enabling no skill can run no script at all.
306
- - **Scripts' `tools.*` calls** are governed per call: an `archmax_run` script
307
- is bound like the model (grant every tool it calls); a hook is exempt from
308
- the state's lists but held to its workflow's mount grants. Neither may read
309
- `workflows/**` or write a read-only mount. A refused call throws inside the
310
- script.
393
+ - **Scripts' `tools.*` calls** are governed per call against the state they
394
+ run in: an `archmax_run` script is bound like the model (grant every tool it
395
+ calls in the agent's state); a code state's script is bound by **its own**
396
+ state's grants (`tools`, `skills`, `mounts`) — there is no runtime authority.
397
+ `tools` holds exactly that state's surface: anything else is `undefined`.
398
+ Neither may read `workflows/**` or write a read-only mount. A refused call
399
+ rejects inside the script.
@@ -1,89 +0,0 @@
1
- # Hook scripts and `archmax_run` scripts — contracts
2
-
3
- Load this when writing a `workflows/<slug>/hooks/*.js` gate or a
4
- `skills/<cap>/scripts/*.js` source. Worked examples and the sandbox details:
5
- [`hook-and-test-scripts.md`](hook-and-test-scripts.md) Part 1.
6
-
7
- ## Hook script contract
8
-
9
- Default-export function; its return value is the verdict. Verdict helpers are
10
- globals (typed import optional: `import { ok, veto, correct } from "@archmax-ai/harness/sandbox";`,
11
- placed below the JSDoc; no other import is allowed).
12
-
13
- ```js
14
- /**
15
- * Requester gate — the request must name a requester the order records know.
16
- *
17
- * Reads the first user message, extracts the first email address, loads
18
- * skills/order-data/assets/orders.json through the bridge and matches the
19
- * address against every record's `email`, case-insensitively.
20
- *
21
- * Vetoes when the file cannot be parsed, no address is present, or none
22
- * matches — each with the reason. Otherwise ok.
23
- */
24
- export default async function hook({ state, phase, trigger, variables, messages, tools, from, to, reason }) {
25
- let orders;
26
- try { orders = JSON.parse(await tools.readFile({ file_path: "skills/order-data/assets/orders.json" })); }
27
- catch { return veto("Could not load skills/order-data/assets/orders.json."); }
28
- const text = messages.find((m) => m.role === "user")?.text ?? "";
29
- const email = text.match(/[\w.+-]+@[\w.-]+\.\w{2,}/)?.[0];
30
- if (!email) return veto("Include your email address so we can look up your account.");
31
- if (!orders.some((o) => o.email?.toLowerCase() === email.toLowerCase())) return veto(`"${email}" is not in the order database.`);
32
- return ok();
33
- }
34
- ```
35
-
36
- | Input | Meaning |
37
- | --- | --- |
38
- | `state`, `phase` | state slug; `"before"` or `"after"` |
39
- | `trigger` | trigger id string (payload is in `variables`) |
40
- | `variables` | read-only `name → value` map incl. `trigger`; structured values whole |
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 |
43
- | `from`, `to`, `reason` | `after` only: the attempted transition and the agent's reason |
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 |
45
-
46
- | Return | Effect |
47
- | --- | --- |
48
- | `ok()`, nothing, `true`/number/string | proceed |
49
- | `veto(reason)` | block; the reason is shown to the agent — make it actionable |
50
- | `correct(reason)` | `after` only: retry with the reason, within `max_iterations` |
51
- | `false` | veto ("precondition not met") |
52
- | any other object, a throw | veto with keys/error named — never silently ok |
53
-
54
- `console.log` goes to the trail. A file with no default export uses its
55
- completion value as the verdict. Parse inputs defensively; turn a failure that
56
- should not block into an explicit `ok()`.
57
-
58
- ## `archmax_run` script contract
59
-
60
- `skills/<cap>/scripts/<x>.js`: plain script, top-level `await`, `tools.*`,
61
- `console.log`; the **last expression** is the tool result. Input is the global
62
- `args`: the arguments the model passed plus `args.variables` (read-only snapshot).
63
- No `archmax_advance`, `archmax_set_variables` or `task`. Fan-out shape:
64
-
65
- ```js
66
- /** One sub-run per requested order, all in flight at once; returns [{ order_id, ...returns }]. */
67
- const orders = args.variables?.orders_to_enrich ?? [];
68
- await Promise.all(orders.map(async (o) => {
69
- const answer = await tools.archmaxWorkflowEnrichOrder({ order_id: o.order_id });
70
- return { order_id: o.order_id, ...JSON.parse(answer).returns };
71
- }));
72
- ```
73
-
74
- The calling state must allow every tool the script calls (here
75
- `archmax_workflow_enrich-order`). A script's `tools` lists exactly the calling
76
- state's tools, re-scoped at every evaluation (so after an advance the same REPL
77
- lists the next state's); a tool the state does not offer is unlisted, and
78
- calling it by name is refused with governance's reason. The listing is
79
- camelCased, but a tool's own name reaches the same function
80
- (`tools["archmax_workflow_enrich-order"]`, `tools.read_file`); a hook's `tools`
81
- takes the camelCased names alone.
82
-
83
- **Return into a variable.** A script's result reaches the model as one tool
84
- message and no further — it is not written to the variable store, and a script
85
- cannot write there itself. Return structured data and name the variable in the
86
- calling state's `requires:`; the model then records it with
87
- `archmax_set_variables`, which is what makes it survive the state, guard a
88
- `tools.allow` glob, and be quotable downstream as `${{name}}` instead of retyped.
89
- A result worth computing is worth naming.
@@ -1,44 +0,0 @@
1
- /**
2
- * Readers for the shape of one authored hook: a tagged object whose single key
3
- * names its kind (`script`, `rubric`, or a custom kind an executor is
4
- * registered for), optionally beside the reserved `max_iterations` sidecar.
5
- */
6
- import type { Hook, HookSpec } from "../machine/types.js";
7
- /** Reserved sidecar keys ignored by hook kind detection. */
8
- export declare const HOOK_SIDECAR_KEYS: ReadonlySet<string>;
9
- /** Normalize a `before`/`after` field into an ordered list of tagged hooks. */
10
- export declare function normalizeHooks(spec: HookSpec | undefined): Hook[];
11
- /**
12
- * The kind of a tagged hook — its single key other than the sidecars — or
13
- * `undefined` for a malformed hook, which callers treat as an error
14
- * (fail-closed veto / validation error).
15
- */
16
- export declare function hookKind(hook: Hook): string | undefined;
17
- /** The target value of a tagged hook (the script path, rubric name, …). */
18
- export declare function hookValue(hook: Hook): string | undefined;
19
- /**
20
- * The hook-level `max_iterations` sidecar — the bounded grade-and-retry budget
21
- * for an `after` hook of any kind — or `undefined` when absent or not a
22
- * non-negative number. It wins over the named rubric's own `max_iterations`,
23
- * because the sidecar describes *this* state's retry loop.
24
- */
25
- export declare function hookMaxIterations(hook: Hook): number | undefined;
26
- /**
27
- * Short, human-readable label for a hook, for operator surfaces (events, the CLI
28
- * state flow). `{ script }` renders as the bare path; a `{ rubric }` renders as
29
- * `rubric#<index>`, because an inline grader has no name and its position is
30
- * what distinguishes it from the next one in the same phase; every other kind
31
- * renders as `<kind>:<value>`.
32
- */
33
- export declare function afterHookLabel(hook: Hook, index?: number): string;
34
- /**
35
- * The key one hook's grade-and-retry budget is counted under. Per hook, not per
36
- * state: `max_iterations` is declared on a hook, so two `after` hooks on one
37
- * state have independent budgets and neither can reset the other's count.
38
- *
39
- * Keyed by **position** rather than by label, so the identity does not move when
40
- * a script is renamed, and so two inline rubrics — which have no names at all —
41
- * are always distinguishable. Reordering a phase's hooks re-keys them, which is
42
- * the same class of edit as any other spec change (it moves the spec hash).
43
- */
44
- export declare function iterationKey(state: string, phase: "before" | "after", index: number): string;
@@ -1,66 +0,0 @@
1
- /** Reserved sidecar keys ignored by hook kind detection. */
2
- export const HOOK_SIDECAR_KEYS = new Set(["max_iterations"]);
3
- /** Normalize a `before`/`after` field into an ordered list of tagged hooks. */
4
- export function normalizeHooks(spec) {
5
- if (spec == null)
6
- return [];
7
- return Array.isArray(spec) ? spec : [spec];
8
- }
9
- /**
10
- * The kind of a tagged hook — its single key other than the sidecars — or
11
- * `undefined` for a malformed hook, which callers treat as an error
12
- * (fail-closed veto / validation error).
13
- */
14
- export function hookKind(hook) {
15
- if (!hook || typeof hook !== "object" || Array.isArray(hook))
16
- return undefined;
17
- const keys = Object.keys(hook).filter((key) => !HOOK_SIDECAR_KEYS.has(key));
18
- return keys.length === 1 ? keys[0] : undefined;
19
- }
20
- /** The target value of a tagged hook (the script path, rubric name, …). */
21
- export function hookValue(hook) {
22
- const kind = hookKind(hook);
23
- return kind ? hook[kind] : undefined;
24
- }
25
- /**
26
- * The hook-level `max_iterations` sidecar — the bounded grade-and-retry budget
27
- * for an `after` hook of any kind — or `undefined` when absent or not a
28
- * non-negative number. It wins over the named rubric's own `max_iterations`,
29
- * because the sidecar describes *this* state's retry loop.
30
- */
31
- export function hookMaxIterations(hook) {
32
- if (!hook || typeof hook !== "object" || Array.isArray(hook))
33
- return undefined;
34
- const value = hook.max_iterations;
35
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
36
- }
37
- /**
38
- * Short, human-readable label for a hook, for operator surfaces (events, the CLI
39
- * state flow). `{ script }` renders as the bare path; a `{ rubric }` renders as
40
- * `rubric#<index>`, because an inline grader has no name and its position is
41
- * what distinguishes it from the next one in the same phase; every other kind
42
- * renders as `<kind>:<value>`.
43
- */
44
- export function afterHookLabel(hook, index = 0) {
45
- const kind = hookKind(hook);
46
- if (kind === undefined)
47
- return "hook";
48
- if (kind === "rubric")
49
- return `rubric#${index}`;
50
- const value = hook[kind];
51
- return kind === "script" ? value : `${kind}:${value}`;
52
- }
53
- /**
54
- * The key one hook's grade-and-retry budget is counted under. Per hook, not per
55
- * state: `max_iterations` is declared on a hook, so two `after` hooks on one
56
- * state have independent budgets and neither can reset the other's count.
57
- *
58
- * Keyed by **position** rather than by label, so the identity does not move when
59
- * a script is renamed, and so two inline rubrics — which have no names at all —
60
- * are always distinguishable. Reordering a phase's hooks re-keys them, which is
61
- * the same class of edit as any other spec change (it moves the spec hash).
62
- */
63
- export function iterationKey(state, phase, index) {
64
- return `${state}::${phase}::${index}`;
65
- }
66
- //# sourceMappingURL=hook-shape.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hook-shape.js","sourceRoot":"","sources":["../../src/lifecycle/hook-shape.ts"],"names":[],"mappings":"AAOA,4DAA4D;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAElF,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,IAA0B;IACvD,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAU;IACjC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,SAAS,CAAC,IAAU;IAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC,CAAC,CAAE,IAA+B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAU;IAC1C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/E,MAAM,KAAK,GAAI,IAAgC,CAAC,cAAc,CAAC;IAC/D,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAU,EAAE,KAAK,GAAG,CAAC;IAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACtC,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,UAAU,KAAK,EAAE,CAAC;IAChD,MAAM,KAAK,GAAI,IAA+B,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,KAAyB,EAAE,KAAa;IAClF,OAAO,GAAG,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE,CAAC;AACxC,CAAC"}