@archmax-ai/harness 0.1.0 → 0.2.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 (85) hide show
  1. package/README.md +45 -4
  2. package/dist/assembly/compose.js +3 -5
  3. package/dist/assembly/compose.js.map +1 -1
  4. package/dist/assembly/delegation-registry.js +4 -2
  5. package/dist/assembly/delegation-registry.js.map +1 -1
  6. package/dist/authoring-skill/archmax-harness/README.md +24 -16
  7. package/dist/authoring-skill/archmax-harness/SKILL.md +7 -1
  8. package/dist/authoring-skill/archmax-harness/references/backend-integration.md +58 -2
  9. package/dist/authoring-skill/archmax-harness/references/hook-and-test-scripts.md +1 -1
  10. package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +71 -22
  11. package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +16 -5
  12. package/dist/cli/turn.d.ts +9 -4
  13. package/dist/cli/turn.js +35 -8
  14. package/dist/cli/turn.js.map +1 -1
  15. package/dist/cli.js +6 -6
  16. package/dist/cli.js.map +1 -1
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.js +15 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/kernel/kernel.d.ts +2 -2
  21. package/dist/kernel/kernel.js +3 -2
  22. package/dist/kernel/kernel.js.map +1 -1
  23. package/dist/machine/machine.d.ts +9 -0
  24. package/dist/machine/machine.js +13 -0
  25. package/dist/machine/machine.js.map +1 -1
  26. package/dist/machine/signature.d.ts +104 -0
  27. package/dist/machine/signature.js +210 -0
  28. package/dist/machine/signature.js.map +1 -0
  29. package/dist/machine/spec-schema.d.ts +158 -8
  30. package/dist/machine/spec-schema.js +78 -6
  31. package/dist/machine/spec-schema.js.map +1 -1
  32. package/dist/machine/triggers.d.ts +6 -2
  33. package/dist/machine/triggers.js +7 -2
  34. package/dist/machine/triggers.js.map +1 -1
  35. package/dist/machine/types.d.ts +7 -1
  36. package/dist/machine/types.js.map +1 -1
  37. package/dist/machine/variables.d.ts +2 -0
  38. package/dist/machine/variables.js +4 -0
  39. package/dist/machine/variables.js.map +1 -1
  40. package/dist/public/spec.d.ts +17 -2
  41. package/dist/public/spec.js +15 -1
  42. package/dist/public/spec.js.map +1 -1
  43. package/dist/sessions/resume.d.ts +4 -0
  44. package/dist/sessions/resume.js +8 -1
  45. package/dist/sessions/resume.js.map +1 -1
  46. package/dist/workflow/control-tools.d.ts +7 -2
  47. package/dist/workflow/control-tools.js +27 -2
  48. package/dist/workflow/control-tools.js.map +1 -1
  49. package/dist/workflow/governance.js +35 -27
  50. package/dist/workflow/governance.js.map +1 -1
  51. package/dist/workflow/middleware.js +5 -10
  52. package/dist/workflow/middleware.js.map +1 -1
  53. package/dist/workflow/parks.js +8 -6
  54. package/dist/workflow/parks.js.map +1 -1
  55. package/dist/workflow/prompt-cache.d.ts +0 -2
  56. package/dist/workflow/prompt-cache.js +0 -7
  57. package/dist/workflow/prompt-cache.js.map +1 -1
  58. package/dist/workflow/render-prompt.js +28 -7
  59. package/dist/workflow/render-prompt.js.map +1 -1
  60. package/dist/workflow/signature-checks.d.ts +22 -0
  61. package/dist/workflow/signature-checks.js +52 -0
  62. package/dist/workflow/signature-checks.js.map +1 -0
  63. package/dist/workflow/state.d.ts +2 -2
  64. package/dist/workflow/state.js +2 -2
  65. package/dist/workflow/sub-workflow.d.ts +23 -16
  66. package/dist/workflow/sub-workflow.js +78 -14
  67. package/dist/workflow/sub-workflow.js.map +1 -1
  68. package/dist/workflow/tool-service.js +1 -1
  69. package/dist/workflow/tool-service.js.map +1 -1
  70. package/dist/workflow/turn-boundary.js +4 -5
  71. package/dist/workflow/turn-boundary.js.map +1 -1
  72. package/dist/workflow/workflow-tools.d.ts +17 -9
  73. package/dist/workflow/workflow-tools.js +30 -17
  74. package/dist/workflow/workflow-tools.js.map +1 -1
  75. package/package.json +2 -1
  76. package/skills/archmax-harness/README.md +82 -0
  77. package/skills/archmax-harness/SKILL.md +247 -0
  78. package/skills/archmax-harness/references/backend-integration.md +681 -0
  79. package/skills/archmax-harness/references/cases.md +41 -0
  80. package/skills/archmax-harness/references/diagnostics.md +51 -0
  81. package/skills/archmax-harness/references/hook-and-test-scripts.md +1070 -0
  82. package/skills/archmax-harness/references/hooks-and-scripts.md +82 -0
  83. package/skills/archmax-harness/references/wiring.md +101 -0
  84. package/skills/archmax-harness/references/workflow-schema.md +1680 -0
  85. package/skills/archmax-harness/references/workflow-yaml.md +252 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * A trigger's signature, typed: what each `requires`/`returns` entry declares,
3
+ * the one mapping from a signature to JSON Schema, and the one conformance rule.
4
+ *
5
+ * Pure and browser-safe — it depends on nothing but the spec's types — so a host
6
+ * builds an MCP tool schema, a start form or request validation from exactly the
7
+ * mapping and rule the runtime enforces. Every runtime check (the turn boundary,
8
+ * `archmax_set_variables`, the completion check, a delegation and its settle)
9
+ * decides by {@link signatureValueIssues}; nothing else judges a value's type.
10
+ */
11
+ import type { MachineSpec, SignatureEntryDeclaration } from "./types.js";
12
+ /**
13
+ * The types a signature entry may declare: JSON Schema's own type words, plus its
14
+ * two string formats `date` and `date-time`, promoted because a host renders and
15
+ * validates them differently from free text.
16
+ */
17
+ export declare const SIGNATURE_TYPES: readonly ["string", "integer", "number", "boolean", "date", "date-time", "object", "array"];
18
+ /** One of {@link SIGNATURE_TYPES}. */
19
+ export type SignatureType = (typeof SIGNATURE_TYPES)[number];
20
+ /**
21
+ * One signature entry, normalized: the variable's name, and optionally what kind
22
+ * of value it holds and what it is for. An entry without `type` is untyped — any
23
+ * value satisfies it, `null` included.
24
+ */
25
+ export interface SignatureEntry {
26
+ name: string;
27
+ type?: SignatureType;
28
+ description?: string;
29
+ }
30
+ /** A trigger's whole signature, normalized: its caller-facing `description` and both lists. */
31
+ export interface TriggerSignature {
32
+ /** What calling this entry does, written for a caller; never disclosed to the session's own model. */
33
+ description?: string;
34
+ /** What a firing must supply, in declaration order; empty when undeclared. */
35
+ requires: SignatureEntry[];
36
+ /** What a completed session guarantees is set, in declaration order; empty when undeclared. */
37
+ returns: SignatureEntry[];
38
+ }
39
+ /**
40
+ * One reason a value map does not satisfy a signature: a name that is not set,
41
+ * or a value that does not conform to its entry's type. `found` is the JSON kind
42
+ * of what arrived; `message` is a phrase naming the variable, worded for whoever
43
+ * has to correct it, which each site wraps in its own refusal.
44
+ */
45
+ export type SignatureValueIssue = {
46
+ kind: "missing";
47
+ name: string;
48
+ message: string;
49
+ } | {
50
+ kind: "invalid";
51
+ name: string;
52
+ type: SignatureType;
53
+ found: SignatureValueKind;
54
+ message: string;
55
+ };
56
+ /** The JSON kind of a value, as an issue reports what arrived. */
57
+ export type SignatureValueKind = "string" | "number" | "boolean" | "null" | "object" | "array";
58
+ /**
59
+ * A `requires`/`returns` list as `{ name, type?, description? }` entries, in
60
+ * declaration order: a bare name and `{ name }` are the same untyped entry.
61
+ * Total — a list the schema refused (a scalar, an entry naming nothing) yields
62
+ * what it can read, so `validate` can read a spec whose shape failed.
63
+ */
64
+ export declare function normalizeSignature(list: readonly (string | SignatureEntryDeclaration)[] | undefined | null): SignatureEntry[];
65
+ /** Whether a value is one of {@link SIGNATURE_TYPES}. */
66
+ export declare function isSignatureType(value: unknown): value is SignatureType;
67
+ /**
68
+ * A trigger's normalized signature read from a parsed spec, or `undefined` when no
69
+ * state declares the id. The same reading `WorkflowMachine.signatureForTrigger`
70
+ * gives, for a host that holds only a spec. Two states declaring one id is a
71
+ * document error; here the later one wins, as in `triggerBindings`.
72
+ */
73
+ export declare function signatureForTrigger(spec: MachineSpec, triggerId: string): TriggerSignature | undefined;
74
+ /** One property of {@link signatureJsonSchema}'s output. An untyped entry has no `type`. */
75
+ export interface SignaturePropertySchema {
76
+ type?: "string" | "integer" | "number" | "boolean" | "object" | "array";
77
+ format?: "date" | "date-time";
78
+ description?: string;
79
+ }
80
+ /** What {@link signatureJsonSchema} yields: a JSON Schema object over the signature's names. */
81
+ export interface SignatureJsonSchema {
82
+ type: "object";
83
+ properties: Record<string, SignaturePropertySchema>;
84
+ required: string[];
85
+ }
86
+ /**
87
+ * The one mapping from signature entries to a JSON Schema object: one property
88
+ * per entry in declaration order, the six JSON Schema type words as themselves,
89
+ * `date`/`date-time` as a string with that `format`, an untyped entry as `{}`,
90
+ * and every name `required` — each `requires` entry is mandatory, and the
91
+ * completion check guarantees each `returns` entry is set. The delegation tool's
92
+ * input schema is built by it, so a host building an MCP tool, a form or an API
93
+ * body from the same signature describes exactly what the runtime enforces.
94
+ */
95
+ export declare function signatureJsonSchema(entries: readonly SignatureEntry[]): SignatureJsonSchema;
96
+ /**
97
+ * The one conformance rule: one issue per entry whose name is not set in
98
+ * `values`, and one per typed entry whose value does not conform, in declaration
99
+ * order; an empty list when the map satisfies the signature. A name is set when
100
+ * `values` holds it with any value but `undefined`. An untyped entry takes any
101
+ * value, `null` included; a typed one never takes `null`. Nothing is coerced —
102
+ * the string `"4"` is not an `integer`.
103
+ */
104
+ export declare function signatureValueIssues(entries: readonly SignatureEntry[], values: Readonly<Record<string, unknown>>): SignatureValueIssue[];
@@ -0,0 +1,210 @@
1
+ /**
2
+ * The types a signature entry may declare: JSON Schema's own type words, plus its
3
+ * two string formats `date` and `date-time`, promoted because a host renders and
4
+ * validates them differently from free text.
5
+ */
6
+ export const SIGNATURE_TYPES = [
7
+ "string",
8
+ "integer",
9
+ "number",
10
+ "boolean",
11
+ "date",
12
+ "date-time",
13
+ "object",
14
+ "array",
15
+ ];
16
+ // --- Normalizing -----------------------------------------------------------------
17
+ /**
18
+ * A `requires`/`returns` list as `{ name, type?, description? }` entries, in
19
+ * declaration order: a bare name and `{ name }` are the same untyped entry.
20
+ * Total — a list the schema refused (a scalar, an entry naming nothing) yields
21
+ * what it can read, so `validate` can read a spec whose shape failed.
22
+ */
23
+ export function normalizeSignature(list) {
24
+ if (!Array.isArray(list))
25
+ return [];
26
+ const entries = [];
27
+ for (const item of list) {
28
+ if (typeof item === "string") {
29
+ entries.push({ name: item });
30
+ continue;
31
+ }
32
+ if (!item || typeof item !== "object" || Array.isArray(item))
33
+ continue;
34
+ const { name, type, description } = item;
35
+ if (typeof name !== "string")
36
+ continue;
37
+ const entry = { name };
38
+ if (isSignatureType(type))
39
+ entry.type = type;
40
+ if (typeof description === "string" && description.trim() !== "")
41
+ entry.description = description.trim();
42
+ entries.push(entry);
43
+ }
44
+ return entries;
45
+ }
46
+ /** Whether a value is one of {@link SIGNATURE_TYPES}. */
47
+ export function isSignatureType(value) {
48
+ return typeof value === "string" && SIGNATURE_TYPES.includes(value);
49
+ }
50
+ /**
51
+ * A trigger's normalized signature read from a parsed spec, or `undefined` when no
52
+ * state declares the id. The same reading `WorkflowMachine.signatureForTrigger`
53
+ * gives, for a host that holds only a spec. Two states declaring one id is a
54
+ * document error; here the later one wins, as in `triggerBindings`.
55
+ */
56
+ export function signatureForTrigger(spec, triggerId) {
57
+ let found;
58
+ for (const state of Object.values(spec.states ?? {})) {
59
+ const triggers = state?.triggers;
60
+ if (!triggers || !Object.hasOwn(triggers, triggerId))
61
+ continue;
62
+ const decl = triggers[triggerId];
63
+ const description = typeof decl?.description === "string" ? decl.description.trim() : "";
64
+ found = {
65
+ ...(description ? { description } : {}),
66
+ requires: normalizeSignature(decl?.requires),
67
+ returns: normalizeSignature(decl?.returns),
68
+ };
69
+ }
70
+ return found;
71
+ }
72
+ /**
73
+ * The one mapping from signature entries to a JSON Schema object: one property
74
+ * per entry in declaration order, the six JSON Schema type words as themselves,
75
+ * `date`/`date-time` as a string with that `format`, an untyped entry as `{}`,
76
+ * and every name `required` — each `requires` entry is mandatory, and the
77
+ * completion check guarantees each `returns` entry is set. The delegation tool's
78
+ * input schema is built by it, so a host building an MCP tool, a form or an API
79
+ * body from the same signature describes exactly what the runtime enforces.
80
+ */
81
+ export function signatureJsonSchema(entries) {
82
+ const properties = {};
83
+ for (const entry of entries) {
84
+ const property = entry.type === "date" || entry.type === "date-time"
85
+ ? { type: "string", format: entry.type }
86
+ : entry.type
87
+ ? { type: entry.type }
88
+ : {};
89
+ if (entry.description)
90
+ property.description = entry.description;
91
+ properties[entry.name] = property;
92
+ }
93
+ return { type: "object", properties, required: entries.map((entry) => entry.name) };
94
+ }
95
+ // --- Conformance --------------------------------------------------------------------
96
+ /**
97
+ * The one conformance rule: one issue per entry whose name is not set in
98
+ * `values`, and one per typed entry whose value does not conform, in declaration
99
+ * order; an empty list when the map satisfies the signature. A name is set when
100
+ * `values` holds it with any value but `undefined`. An untyped entry takes any
101
+ * value, `null` included; a typed one never takes `null`. Nothing is coerced —
102
+ * the string `"4"` is not an `integer`.
103
+ */
104
+ export function signatureValueIssues(entries, values) {
105
+ const issues = [];
106
+ for (const entry of entries) {
107
+ const value = Object.hasOwn(values, entry.name) ? values[entry.name] : undefined;
108
+ if (value === undefined) {
109
+ issues.push({ kind: "missing", name: entry.name, message: `'${entry.name}' is not set` });
110
+ continue;
111
+ }
112
+ if (!entry.type || conforms(entry.type, value))
113
+ continue;
114
+ const found = valueKind(value);
115
+ issues.push({
116
+ kind: "invalid",
117
+ name: entry.name,
118
+ type: entry.type,
119
+ found,
120
+ message: `'${entry.name}' must be ${TYPE_PHRASES[entry.type]}, but ${arrived(found, value)} arrived`,
121
+ });
122
+ }
123
+ return issues;
124
+ }
125
+ /** Whether one value conforms to one signature type. */
126
+ function conforms(type, value) {
127
+ switch (type) {
128
+ case "string":
129
+ return typeof value === "string";
130
+ case "integer":
131
+ return typeof value === "number" && Number.isInteger(value);
132
+ case "number":
133
+ return typeof value === "number" && Number.isFinite(value);
134
+ case "boolean":
135
+ return typeof value === "boolean";
136
+ case "date":
137
+ return typeof value === "string" && isFullDate(value);
138
+ case "date-time":
139
+ return typeof value === "string" && isDateTime(value);
140
+ case "object":
141
+ return value !== null && typeof value === "object" && !Array.isArray(value);
142
+ case "array":
143
+ return Array.isArray(value);
144
+ }
145
+ }
146
+ /** What each type means, as a refusal states it. */
147
+ const TYPE_PHRASES = {
148
+ string: "a string",
149
+ integer: "an integer (a number with no fractional part)",
150
+ number: "a number",
151
+ boolean: "a boolean (true or false)",
152
+ date: "a date (an RFC 3339 full-date, YYYY-MM-DD, naming a real calendar day)",
153
+ "date-time": "a date-time (an RFC 3339 date-time with an offset, e.g. 2026-03-01T09:30:00Z)",
154
+ object: "an object",
155
+ array: "an array",
156
+ };
157
+ function valueKind(value) {
158
+ if (value === null)
159
+ return "null";
160
+ if (Array.isArray(value))
161
+ return "array";
162
+ const kind = typeof value;
163
+ return kind === "string" || kind === "number" || kind === "boolean" ? kind : "object";
164
+ }
165
+ /** The arrival, worded: a short scalar is quoted so a malformed date or a fraction is visible. */
166
+ function arrived(found, value) {
167
+ if (found === "null")
168
+ return "null";
169
+ const article = found === "array" || found === "object" ? "an" : "a";
170
+ if (found === "string" && value.length <= 40)
171
+ return `${article} string (${JSON.stringify(value)})`;
172
+ if (found === "number")
173
+ return `${article} number (${String(value)})`;
174
+ return `${article} ${found}`;
175
+ }
176
+ // --- RFC 3339 -----------------------------------------------------------------------
177
+ //
178
+ // Hand-written rather than `Date.parse`, which accepts implementation-specific
179
+ // strings and rolls `2026-02-30` over into March.
180
+ const FULL_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
181
+ const DATE_TIME = /^(\d{4}-\d{2}-\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:[Zz]|[+-](\d{2}):(\d{2}))$/;
182
+ /** An RFC 3339 `full-date` naming a real calendar day. */
183
+ function isFullDate(value) {
184
+ const match = FULL_DATE.exec(value);
185
+ if (!match)
186
+ return false;
187
+ const year = Number(match[1]);
188
+ const month = Number(match[2]);
189
+ const day = Number(match[3]);
190
+ return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
191
+ }
192
+ /** An RFC 3339 `date-time`: a real day, a valid time, and a mandatory offset. */
193
+ function isDateTime(value) {
194
+ const match = DATE_TIME.exec(value);
195
+ if (!match || !isFullDate(match[1]))
196
+ return false;
197
+ const [hour, minute, second] = [match[2], match[3], match[4]].map(Number);
198
+ // 60 admits a leap second, as RFC 3339 does.
199
+ if (hour > 23 || minute > 59 || second > 60)
200
+ return false;
201
+ if (match[5] !== undefined && (Number(match[5]) > 23 || Number(match[6]) > 59))
202
+ return false;
203
+ return true;
204
+ }
205
+ function daysInMonth(year, month) {
206
+ if (month === 2)
207
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 ? 29 : 28;
208
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
209
+ }
210
+ //# sourceMappingURL=signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/machine/signature.ts"],"names":[],"mappings":"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"}
@@ -218,6 +218,48 @@ export declare const stateTypeSchema: z.ZodEnum<{
218
218
  human: "human";
219
219
  agent: "agent";
220
220
  }>;
221
+ /**
222
+ * @see SignatureEntryDeclaration
223
+ *
224
+ * The object spelling of a signature entry. Strict, unlike the loose declaration
225
+ * around it: an unknown key here is most likely a misspelled `type` or
226
+ * `description`, and read loosely it would silently mean "untyped".
227
+ */
228
+ export declare const signatureEntryObjectSchema: z.ZodObject<{
229
+ name: z.ZodString;
230
+ type: z.ZodOptional<z.ZodEnum<{
231
+ string: "string";
232
+ number: "number";
233
+ boolean: "boolean";
234
+ object: "object";
235
+ date: "date";
236
+ array: "array";
237
+ integer: "integer";
238
+ "date-time": "date-time";
239
+ }>>;
240
+ description: z.ZodOptional<z.ZodString>;
241
+ }, z.core.$strict>;
242
+ /**
243
+ * @see SignatureEntryDeclaration
244
+ *
245
+ * One signature entry: a bare run-variable name (untyped), or the object
246
+ * spelling. A failure is reported in the terms of the spelling the author used,
247
+ * so a mistyped bare name reads exactly as it did before objects were accepted.
248
+ */
249
+ export declare const signatureEntrySchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
250
+ name: z.ZodString;
251
+ type: z.ZodOptional<z.ZodEnum<{
252
+ string: "string";
253
+ number: "number";
254
+ boolean: "boolean";
255
+ object: "object";
256
+ date: "date";
257
+ array: "array";
258
+ integer: "integer";
259
+ "date-time": "date-time";
260
+ }>>;
261
+ description: z.ZodOptional<z.ZodString>;
262
+ }, z.core.$strict>]>;
221
263
  /**
222
264
  * @see TriggerDeclaration
223
265
  *
@@ -229,8 +271,35 @@ export declare const triggerDeclarationSchema: z.ZodObject<{
229
271
  session: z.ZodOptional<z.ZodString>;
230
272
  message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>>;
231
273
  connection: z.ZodOptional<z.ZodString>;
232
- requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
233
- returns: z.ZodOptional<z.ZodArray<z.ZodString>>;
274
+ description: z.ZodOptional<z.ZodString>;
275
+ requires: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
276
+ name: z.ZodString;
277
+ type: z.ZodOptional<z.ZodEnum<{
278
+ string: "string";
279
+ number: "number";
280
+ boolean: "boolean";
281
+ object: "object";
282
+ date: "date";
283
+ array: "array";
284
+ integer: "integer";
285
+ "date-time": "date-time";
286
+ }>>;
287
+ description: z.ZodOptional<z.ZodString>;
288
+ }, z.core.$strict>]>>>;
289
+ returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
290
+ name: z.ZodString;
291
+ type: z.ZodOptional<z.ZodEnum<{
292
+ string: "string";
293
+ number: "number";
294
+ boolean: "boolean";
295
+ object: "object";
296
+ date: "date";
297
+ array: "array";
298
+ integer: "integer";
299
+ "date-time": "date-time";
300
+ }>>;
301
+ description: z.ZodOptional<z.ZodString>;
302
+ }, z.core.$strict>]>>>;
234
303
  }, z.core.$loose>;
235
304
  /**
236
305
  * A state's `triggers:` — the one place a trigger is declared. The key is the
@@ -242,8 +311,35 @@ export declare const stateTriggersSchema: z.ZodRecord<z.ZodString, z.ZodNullable
242
311
  session: z.ZodOptional<z.ZodString>;
243
312
  message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>>;
244
313
  connection: z.ZodOptional<z.ZodString>;
245
- requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
246
- returns: z.ZodOptional<z.ZodArray<z.ZodString>>;
314
+ description: z.ZodOptional<z.ZodString>;
315
+ requires: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
316
+ name: z.ZodString;
317
+ type: z.ZodOptional<z.ZodEnum<{
318
+ string: "string";
319
+ number: "number";
320
+ boolean: "boolean";
321
+ object: "object";
322
+ date: "date";
323
+ array: "array";
324
+ integer: "integer";
325
+ "date-time": "date-time";
326
+ }>>;
327
+ description: z.ZodOptional<z.ZodString>;
328
+ }, z.core.$strict>]>>>;
329
+ returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
330
+ name: z.ZodString;
331
+ type: z.ZodOptional<z.ZodEnum<{
332
+ string: "string";
333
+ number: "number";
334
+ boolean: "boolean";
335
+ object: "object";
336
+ date: "date";
337
+ array: "array";
338
+ integer: "integer";
339
+ "date-time": "date-time";
340
+ }>>;
341
+ description: z.ZodOptional<z.ZodString>;
342
+ }, z.core.$strict>]>>>;
247
343
  }, z.core.$loose>>>;
248
344
  /** @see MachineState */
249
345
  export declare const machineStateSchema: z.ZodObject<{
@@ -260,8 +356,35 @@ export declare const machineStateSchema: z.ZodObject<{
260
356
  session: z.ZodOptional<z.ZodString>;
261
357
  message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>>;
262
358
  connection: z.ZodOptional<z.ZodString>;
263
- requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
264
- returns: z.ZodOptional<z.ZodArray<z.ZodString>>;
359
+ description: z.ZodOptional<z.ZodString>;
360
+ requires: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
361
+ name: z.ZodString;
362
+ type: z.ZodOptional<z.ZodEnum<{
363
+ string: "string";
364
+ number: "number";
365
+ boolean: "boolean";
366
+ object: "object";
367
+ date: "date";
368
+ array: "array";
369
+ integer: "integer";
370
+ "date-time": "date-time";
371
+ }>>;
372
+ description: z.ZodOptional<z.ZodString>;
373
+ }, z.core.$strict>]>>>;
374
+ returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
375
+ name: z.ZodString;
376
+ type: z.ZodOptional<z.ZodEnum<{
377
+ string: "string";
378
+ number: "number";
379
+ boolean: "boolean";
380
+ object: "object";
381
+ date: "date";
382
+ array: "array";
383
+ integer: "integer";
384
+ "date-time": "date-time";
385
+ }>>;
386
+ description: z.ZodOptional<z.ZodString>;
387
+ }, z.core.$strict>]>>>;
265
388
  }, z.core.$loose>>>>;
266
389
  before: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodObject<{
267
390
  script: z.ZodString;
@@ -531,8 +654,35 @@ export declare const machineSpecSchema: z.ZodObject<{
531
654
  session: z.ZodOptional<z.ZodString>;
532
655
  message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>>;
533
656
  connection: z.ZodOptional<z.ZodString>;
534
- requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
535
- returns: z.ZodOptional<z.ZodArray<z.ZodString>>;
657
+ description: z.ZodOptional<z.ZodString>;
658
+ requires: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
659
+ name: z.ZodString;
660
+ type: z.ZodOptional<z.ZodEnum<{
661
+ string: "string";
662
+ number: "number";
663
+ boolean: "boolean";
664
+ object: "object";
665
+ date: "date";
666
+ array: "array";
667
+ integer: "integer";
668
+ "date-time": "date-time";
669
+ }>>;
670
+ description: z.ZodOptional<z.ZodString>;
671
+ }, z.core.$strict>]>>>;
672
+ returns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
673
+ name: z.ZodString;
674
+ type: z.ZodOptional<z.ZodEnum<{
675
+ string: "string";
676
+ number: "number";
677
+ boolean: "boolean";
678
+ object: "object";
679
+ date: "date";
680
+ array: "array";
681
+ integer: "integer";
682
+ "date-time": "date-time";
683
+ }>>;
684
+ description: z.ZodOptional<z.ZodString>;
685
+ }, z.core.$strict>]>>>;
536
686
  }, z.core.$loose>>>>;
537
687
  before: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodObject<{
538
688
  script: z.ZodString;
@@ -10,6 +10,7 @@ import { z } from "zod";
10
10
  import { normalizeAllowEntry } from "./allow.js";
11
11
  import { normalizeHooks } from "../lifecycle/hook-shape.js";
12
12
  import { isSlug } from "./slug.js";
13
+ import { SIGNATURE_TYPES } from "./signature.js";
13
14
  import { MANUAL_TRIGGER, parseSessionPath, stateTriggerIds } from "./triggers.js";
14
15
  import { parseReferences, referenceError, TITLE_VARIABLE, TRIGGER_VARIABLE, VARIABLE_NAME_PATTERN, } from "./variables.js";
15
16
  // --- Shared scalars ------------------------------------------------------------
@@ -332,16 +333,81 @@ export const stateTypeSchema = z.enum(["agent", "human"], {
332
333
  error: (issue) => `unknown state type '${String(issue.input)}' (expected 'agent' or 'human')`,
333
334
  });
334
335
  // --- Triggers ------------------------------------------------------------------
335
- /** One half of a trigger's signature: a list of distinct run-variable names. */
336
+ /** The keys a signature entry object may carry, as a refusal lists them. */
337
+ const SIGNATURE_ENTRY_KEYS = "'name', 'type' and 'description'";
338
+ /** The accepted type words, as a refusal lists them. */
339
+ const SIGNATURE_TYPE_LIST = `${SIGNATURE_TYPES.slice(0, -1).join(", ")} or ${SIGNATURE_TYPES.at(-1)}`;
340
+ /**
341
+ * @see SignatureEntryDeclaration
342
+ *
343
+ * The object spelling of a signature entry. Strict, unlike the loose declaration
344
+ * around it: an unknown key here is most likely a misspelled `type` or
345
+ * `description`, and read loosely it would silently mean "untyped".
346
+ */
347
+ export const signatureEntryObjectSchema = z.strictObject({
348
+ name: z
349
+ .string({
350
+ error: "a signature entry needs a 'name' naming its variable (e.g. '{ name: order_id, type: string }')",
351
+ })
352
+ .regex(VARIABLE_NAME_PATTERN, {
353
+ error: (issue) => `'${String(issue.input)}' is not a valid run-variable name. Use lowercase letters, ` +
354
+ `digits and underscores, starting with a letter (e.g. 'order_id').`,
355
+ }),
356
+ type: z
357
+ .enum(SIGNATURE_TYPES, {
358
+ error: (issue) => `'type' must be one of ${SIGNATURE_TYPE_LIST}; got ${JSON.stringify(issue.input)}`,
359
+ })
360
+ .optional(),
361
+ description: z
362
+ .string({ error: `'description' ${NON_EMPTY}` })
363
+ .refine((value) => value.trim() !== "", `'description' ${NON_EMPTY}`)
364
+ .optional(),
365
+ }, {
366
+ error: (issue) => issue.code === "unrecognized_keys"
367
+ ? `${entryLabel(issue.input)} declares ${issue.keys.map((k) => `'${k}'`).join(", ")}, which a ` +
368
+ `signature entry does not take: it has ${SIGNATURE_ENTRY_KEYS} only.`
369
+ : undefined,
370
+ });
371
+ /** An entry as a message names it: by its `name` when it has a readable one. */
372
+ function entryLabel(input) {
373
+ const name = input?.name;
374
+ return typeof name === "string" && name !== "" ? `Entry '${name}'` : "The entry";
375
+ }
376
+ /**
377
+ * @see SignatureEntryDeclaration
378
+ *
379
+ * One signature entry: a bare run-variable name (untyped), or the object
380
+ * spelling. A failure is reported in the terms of the spelling the author used,
381
+ * so a mistyped bare name reads exactly as it did before objects were accepted.
382
+ */
383
+ export const signatureEntrySchema = z.union([variableName, signatureEntryObjectSchema], {
384
+ error: (issue) => {
385
+ if (issue.code !== "invalid_union")
386
+ return undefined;
387
+ const input = issue.input;
388
+ const isObject = input !== null && typeof input === "object" && !Array.isArray(input);
389
+ const branch = issue.errors[isObject ? 1 : 0] ?? [];
390
+ const messages = branch.map((inner) => inner.message);
391
+ if (!isObject)
392
+ return messages[0] ?? `'${String(input)}' is not a valid run-variable name`;
393
+ return `${entryLabel(input)}: ${messages.join("; ")}`;
394
+ },
395
+ });
396
+ /**
397
+ * One half of a trigger's signature: a list of entries with distinct names —
398
+ * each a bare name or `{ name, type?, description? }`, distinct by name across
399
+ * both spellings.
400
+ */
336
401
  function signatureSchema(key) {
337
402
  return z
338
- .array(variableName, {
339
- error: `must be a list of run-variable names (e.g. '${key}: [order_id]'). A signature names ` +
340
- `the contract only — what a variable holds belongs in the state instructions that set it.`,
403
+ .array(signatureEntrySchema, {
404
+ error: `must be a list of run-variable names, each a bare name or '{ name, type?, description? }' ` +
405
+ `(e.g. '${key}: [order_id, { name: due, type: date }]').`,
341
406
  })
342
- .superRefine((names, ctx) => {
407
+ .superRefine((entries, ctx) => {
343
408
  const seen = new Set();
344
- names.forEach((name, index) => {
409
+ entries.forEach((entry, index) => {
410
+ const name = typeof entry === "string" ? entry : entry.name;
345
411
  if (seen.has(name)) {
346
412
  ctx.addIssue({ code: "custom", path: [index], message: `'${name}' is listed more than once.` });
347
413
  }
@@ -403,6 +469,12 @@ export const triggerDeclarationSchema = z
403
469
  })
404
470
  .refine((value) => value.trim() !== "", "must be a non-empty string naming an access connection in the host's environment.")
405
471
  .optional(),
472
+ // For whoever calls the entry — a delegating model, a host's MCP client, a
473
+ // form's reader. The session's own brief is its `instructions`; this never reaches it.
474
+ description: z
475
+ .string({ error: "must be a non-empty string saying, for a caller, what calling this entry does." })
476
+ .refine((value) => value.trim() !== "", "must be a non-empty string saying, for a caller, what calling this entry does.")
477
+ .optional(),
406
478
  requires: signatureSchema("requires").optional(),
407
479
  returns: signatureSchema("returns").optional(),
408
480
  })