@archmax-ai/harness 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/dist/agent.d.ts +4 -2
- package/dist/agent.js +4 -2
- package/dist/agent.js.map +1 -1
- package/dist/assembly/compose.d.ts +5 -3
- package/dist/assembly/compose.js +24 -8
- package/dist/assembly/compose.js.map +1 -1
- package/dist/assembly/index.d.ts +13 -2
- package/dist/assembly/index.js +35 -10
- package/dist/assembly/index.js.map +1 -1
- package/dist/authoring-skill/archmax-harness/SKILL.md +9 -5
- package/dist/authoring-skill/archmax-harness/references/backend-integration.md +12 -8
- package/dist/authoring-skill/archmax-harness/references/cases.md +3 -2
- package/dist/authoring-skill/archmax-harness/references/diagnostics.md +5 -3
- package/dist/authoring-skill/archmax-harness/references/hook-and-test-scripts.md +52 -7
- package/dist/authoring-skill/archmax-harness/references/hooks-and-scripts.md +2 -1
- package/dist/authoring-skill/archmax-harness/references/wiring.md +1 -1
- package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +107 -46
- package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +43 -17
- package/dist/cli/turn.js +1 -2
- package/dist/cli/turn.js.map +1 -1
- package/dist/core/clock.d.ts +26 -0
- package/dist/core/clock.js +64 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/deepagents.d.ts +9 -2
- package/dist/core/deepagents.js +15 -2
- package/dist/core/deepagents.js.map +1 -1
- package/dist/core/message-readers.d.ts +21 -0
- package/dist/core/message-readers.js +36 -0
- package/dist/core/message-readers.js.map +1 -1
- package/dist/core/messages.d.ts +8 -8
- package/dist/core/messages.js +11 -13
- package/dist/core/messages.js.map +1 -1
- package/dist/core/platform-prompt.generated.js +1 -1
- package/dist/core/platform-prompt.generated.js.map +1 -1
- package/dist/core/workspace-prompt.js +2 -1
- package/dist/core/workspace-prompt.js.map +1 -1
- package/dist/kernel/kernel.js +15 -10
- package/dist/kernel/kernel.js.map +1 -1
- package/dist/lifecycle/runner.d.ts +14 -0
- package/dist/lifecycle/runner.js +50 -4
- package/dist/lifecycle/runner.js.map +1 -1
- package/dist/lifecycle/tool-calls.d.ts +9 -0
- package/dist/lifecycle/tool-calls.js +57 -0
- package/dist/lifecycle/tool-calls.js.map +1 -0
- package/dist/lifecycle/transcript.d.ts +10 -6
- package/dist/lifecycle/transcript.js +15 -10
- package/dist/lifecycle/transcript.js.map +1 -1
- package/dist/machine/allow.d.ts +17 -2
- package/dist/machine/allow.js +16 -6
- package/dist/machine/allow.js.map +1 -1
- package/dist/machine/lint-spec.js +77 -29
- package/dist/machine/lint-spec.js.map +1 -1
- package/dist/machine/machine.d.ts +38 -17
- package/dist/machine/machine.js +64 -32
- package/dist/machine/machine.js.map +1 -1
- package/dist/machine/spec-schema.d.ts +24 -13
- package/dist/machine/spec-schema.js +91 -15
- package/dist/machine/spec-schema.js.map +1 -1
- package/dist/machine/tool-names.d.ts +16 -7
- package/dist/machine/tool-names.js +17 -8
- package/dist/machine/tool-names.js.map +1 -1
- package/dist/machine/tool-paths.d.ts +10 -3
- package/dist/machine/tool-paths.js +14 -3
- package/dist/machine/tool-paths.js.map +1 -1
- package/dist/machine/types.d.ts +23 -9
- package/dist/machine/types.js +2 -0
- package/dist/machine/types.js.map +1 -1
- package/dist/machine/variables.d.ts +2 -0
- package/dist/machine/variables.js.map +1 -1
- package/dist/public/sandbox.d.ts +35 -3
- package/dist/public/sandbox.js +1 -1
- package/dist/public/sandbox.js.map +1 -1
- package/dist/public/spec.d.ts +5 -5
- package/dist/public/spec.js +4 -4
- package/dist/public/spec.js.map +1 -1
- package/dist/sandbox/executor.js +10 -0
- package/dist/sandbox/executor.js.map +1 -1
- package/dist/sessions/operations.js +4 -5
- package/dist/sessions/operations.js.map +1 -1
- package/dist/sessions/resume.d.ts +10 -5
- package/dist/sessions/resume.js +11 -8
- package/dist/sessions/resume.js.map +1 -1
- package/dist/sessions/scope.d.ts +10 -0
- package/dist/sessions/scope.js +17 -0
- package/dist/sessions/scope.js.map +1 -1
- package/dist/testing/case-document.d.ts +2 -0
- package/dist/testing/case-document.js.map +1 -1
- package/dist/testing/case-schema.d.ts +1 -1
- package/dist/testing/case-schema.js +17 -1
- package/dist/testing/case-schema.js.map +1 -1
- package/dist/testing/driver.d.ts +4 -0
- package/dist/testing/driver.js +19 -3
- package/dist/testing/driver.js.map +1 -1
- package/dist/testing/grade.js +3 -3
- package/dist/testing/grade.js.map +1 -1
- package/dist/testing/interpreter.js +1 -0
- package/dist/testing/interpreter.js.map +1 -1
- package/dist/workflow/checkpointer.d.ts +7 -0
- package/dist/workflow/checkpointer.js +28 -0
- package/dist/workflow/checkpointer.js.map +1 -1
- package/dist/workflow/control-tools.js +26 -2
- package/dist/workflow/control-tools.js.map +1 -1
- package/dist/workflow/governance.d.ts +3 -1
- package/dist/workflow/governance.js +27 -6
- package/dist/workflow/governance.js.map +1 -1
- package/dist/workflow/middleware.js +22 -4
- package/dist/workflow/middleware.js.map +1 -1
- package/dist/workflow/on-error.js +2 -0
- package/dist/workflow/on-error.js.map +1 -1
- package/dist/workflow/parks.js +2 -0
- package/dist/workflow/parks.js.map +1 -1
- package/dist/workflow/render-prompt.d.ts +2 -1
- package/dist/workflow/render-prompt.js +24 -5
- package/dist/workflow/render-prompt.js.map +1 -1
- package/dist/workflow/state.d.ts +41 -0
- package/dist/workflow/state.js +33 -4
- package/dist/workflow/state.js.map +1 -1
- package/dist/workflow/sub-workflow.js +7 -3
- package/dist/workflow/sub-workflow.js.map +1 -1
- package/dist/workflow/tool-service.d.ts +1 -1
- package/dist/workflow/tool-service.js +1 -1
- package/dist/workflow/tool-service.js.map +1 -1
- package/dist/workflow/turn-boundary.js +8 -0
- package/dist/workflow/turn-boundary.js.map +1 -1
- package/dist/workflow/turn-runner.js +36 -7
- package/dist/workflow/turn-runner.js.map +1 -1
- package/dist/workflow/visits.d.ts +21 -0
- package/dist/workflow/visits.js +45 -0
- package/dist/workflow/visits.js.map +1 -0
- package/package.json +1 -1
- package/skills/archmax-harness/SKILL.md +9 -5
- package/skills/archmax-harness/references/backend-integration.md +12 -8
- package/skills/archmax-harness/references/cases.md +3 -2
- package/skills/archmax-harness/references/diagnostics.md +5 -3
- package/skills/archmax-harness/references/hook-and-test-scripts.md +52 -7
- package/skills/archmax-harness/references/hooks-and-scripts.md +2 -1
- package/skills/archmax-harness/references/wiring.md +1 -1
- package/skills/archmax-harness/references/workflow-schema.md +107 -46
- package/skills/archmax-harness/references/workflow-yaml.md +43 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-boundary.js","sourceRoot":"","sources":["../../src/workflow/turn-boundary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAwD,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"turn-boundary.js","sourceRoot":"","sources":["../../src/workflow/turn-boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { INTERRUPT } from "@langchain/langgraph";
|
|
1
|
+
import { GraphRecursionError, INTERRUPT } from "@langchain/langgraph";
|
|
2
2
|
import { contentToString, isAiMessage } from "../core/messages.js";
|
|
3
|
-
import { currentWorkflowState } from "./state.js";
|
|
4
|
-
import { DEFAULT_SESSION_ID } from "../sessions/scope.js";
|
|
3
|
+
import { currentWorkflowState, WORKFLOW_STATUSES } from "./state.js";
|
|
4
|
+
import { DEFAULT_SESSION_ID, withInvocation } from "../sessions/scope.js";
|
|
5
5
|
export function sessionIdOfConfig(config) {
|
|
6
6
|
const id = config?.configurable?.thread_id;
|
|
7
7
|
return typeof id === "string" && id ? id : DEFAULT_SESSION_ID;
|
|
@@ -26,9 +26,10 @@ export function createTurnRunner(opts) {
|
|
|
26
26
|
* streamed so far as `agent-text` events flagged `partial`, since the
|
|
27
27
|
* governance hooks that emit the final `agent-text` never ran for it.
|
|
28
28
|
*/
|
|
29
|
-
async function drive(input,
|
|
29
|
+
async function drive(input, invoked) {
|
|
30
|
+
const config = withInvocation(invoked);
|
|
30
31
|
const stream = await graph.stream(input, {
|
|
31
|
-
...
|
|
32
|
+
...config,
|
|
32
33
|
streamMode: ["values", "messages"],
|
|
33
34
|
});
|
|
34
35
|
let state = opts.entryState;
|
|
@@ -79,9 +80,37 @@ export function createTurnRunner(opts) {
|
|
|
79
80
|
for (const { text, messageId } of streamedText.values()) {
|
|
80
81
|
emit({ type: "agent-text", state, text, partial: true, ...(messageId ? { messageId } : {}) });
|
|
81
82
|
}
|
|
83
|
+
await endFailed(config, err);
|
|
82
84
|
throw err;
|
|
83
85
|
}
|
|
84
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* A turn that throws while the session is still `running` would leave it
|
|
89
|
+
* refused for good, so it ends `failed` with an exit record, as `archmax_raise`
|
|
90
|
+
* would, and the next message starts a fresh turn. A park is left as it is.
|
|
91
|
+
*/
|
|
92
|
+
async function endFailed(config, err) {
|
|
93
|
+
const at = { configurable: { thread_id: sessionIdOfConfig(config) } };
|
|
94
|
+
try {
|
|
95
|
+
const values = (await graph.getState(at))?.values ?? {};
|
|
96
|
+
if (values.status !== WORKFLOW_STATUSES.running)
|
|
97
|
+
return;
|
|
98
|
+
const code = err instanceof GraphRecursionError
|
|
99
|
+
? "recursion-limit"
|
|
100
|
+
: config?.signal?.aborted || err?.name === "AbortError"
|
|
101
|
+
? "turn-cancelled"
|
|
102
|
+
: "turn-error";
|
|
103
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
104
|
+
await graph.updateState(at, {
|
|
105
|
+
status: WORKFLOW_STATUSES.failed,
|
|
106
|
+
raised: { code, reason, state: currentWorkflowState(values, opts.entryState) },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (failure) {
|
|
110
|
+
// The original error is what the caller needs; this one only explains why the session stayed `running`.
|
|
111
|
+
emit({ type: "warning", scope: "session", message: `could not end the failed turn: ${failure.message}` });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
85
114
|
return {
|
|
86
115
|
// `async`, so a refused session id is a rejection like every other failure of
|
|
87
116
|
// the call, never a synchronous throw out of `invoke`.
|
|
@@ -89,10 +118,10 @@ export function createTurnRunner(opts) {
|
|
|
89
118
|
return bindSession(sessionIdOfConfig(config), () => drive(input, config));
|
|
90
119
|
},
|
|
91
120
|
stream(input, config) {
|
|
92
|
-
return boundIterable(sessionIdOfConfig(config), bindSession, () => graph.stream(input, config));
|
|
121
|
+
return boundIterable(sessionIdOfConfig(config), bindSession, () => graph.stream(input, withInvocation(config)));
|
|
93
122
|
},
|
|
94
123
|
streamEvents(input, config, options) {
|
|
95
|
-
return boundIterable(sessionIdOfConfig(config), bindSession, async () => graph.streamEvents(input, config, options));
|
|
124
|
+
return boundIterable(sessionIdOfConfig(config), bindSession, async () => graph.streamEvents(input, withInvocation(config), options));
|
|
96
125
|
},
|
|
97
126
|
};
|
|
98
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-runner.js","sourceRoot":"","sources":["../../src/workflow/turn-runner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"turn-runner.js","sourceRoot":"","sources":["../../src/workflow/turn-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type VisitStart } from "./state.js";
|
|
2
|
+
/** One visit as found in a transcript: its number (from 1), its state and its first message's index. */
|
|
3
|
+
export interface VisitSpan {
|
|
4
|
+
number: number;
|
|
5
|
+
state: string;
|
|
6
|
+
start: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The session's visits over `messages`, in order. A session from before the
|
|
10
|
+
* visit log has no entry for what precedes its first logged visit: that span
|
|
11
|
+
* is one visit, at the session's entry state.
|
|
12
|
+
*/
|
|
13
|
+
export declare function visitSpans(messages: readonly unknown[], state: unknown): VisitSpan[];
|
|
14
|
+
/** The visit holding the message at `index`. */
|
|
15
|
+
export declare function visitAt(spans: readonly VisitSpan[], index: number): VisitSpan;
|
|
16
|
+
/** The messages of the visit the session is in. */
|
|
17
|
+
export declare function currentVisitMessages(messages: readonly unknown[], state: unknown): unknown[];
|
|
18
|
+
/** The log entry for entering `target` with the transcript standing at `messages`. */
|
|
19
|
+
export declare function visitStart(target: string, messages: unknown): VisitStart[];
|
|
20
|
+
/** A checkpointed state's transcript. */
|
|
21
|
+
export declare function messagesIn(state: unknown): unknown[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visits: the span of a session from its entry into a state until it next
|
|
3
|
+
* enters one. What a hook sees of the conversation is the current visit, and
|
|
4
|
+
* every tool call is attributed to the visit of the message that made it.
|
|
5
|
+
*/
|
|
6
|
+
import { anchorIndex, turnAnchorOf } from "../core/messages.js";
|
|
7
|
+
import { readWorkflowState } from "./state.js";
|
|
8
|
+
/**
|
|
9
|
+
* The session's visits over `messages`, in order. A session from before the
|
|
10
|
+
* visit log has no entry for what precedes its first logged visit: that span
|
|
11
|
+
* is one visit, at the session's entry state.
|
|
12
|
+
*/
|
|
13
|
+
export function visitSpans(messages, state) {
|
|
14
|
+
const fields = readWorkflowState(state);
|
|
15
|
+
const spans = (fields.visits ?? []).map((visit) => ({ state: visit.state, start: anchorIndex(messages, visit.from) }));
|
|
16
|
+
if (spans.length === 0 || spans[0].start > 0) {
|
|
17
|
+
spans.unshift({ state: fields.entryState ?? fields.workflowState ?? "", start: 0 });
|
|
18
|
+
}
|
|
19
|
+
return spans.map((span, i) => ({ number: i + 1, ...span }));
|
|
20
|
+
}
|
|
21
|
+
/** The visit holding the message at `index`. */
|
|
22
|
+
export function visitAt(spans, index) {
|
|
23
|
+
let found = spans[0];
|
|
24
|
+
for (const span of spans) {
|
|
25
|
+
if (span.start > index)
|
|
26
|
+
break;
|
|
27
|
+
found = span;
|
|
28
|
+
}
|
|
29
|
+
return found;
|
|
30
|
+
}
|
|
31
|
+
/** The messages of the visit the session is in. */
|
|
32
|
+
export function currentVisitMessages(messages, state) {
|
|
33
|
+
const spans = visitSpans(messages, state);
|
|
34
|
+
return messages.slice(spans[spans.length - 1].start);
|
|
35
|
+
}
|
|
36
|
+
/** The log entry for entering `target` with the transcript standing at `messages`. */
|
|
37
|
+
export function visitStart(target, messages) {
|
|
38
|
+
return [{ state: target, from: turnAnchorOf(messages) }];
|
|
39
|
+
}
|
|
40
|
+
/** A checkpointed state's transcript. */
|
|
41
|
+
export function messagesIn(state) {
|
|
42
|
+
const messages = state?.messages;
|
|
43
|
+
return Array.isArray(messages) ? messages : [];
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=visits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visits.js","sourceRoot":"","sources":["../../src/workflow/visits.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAC;AAShE;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,QAA4B,EAAE,KAAc;IACrE,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACvH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,aAAa,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,OAAO,CAAC,KAA2B,EAAE,KAAa;IAChE,IAAI,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,GAAG,KAAK;YAAE,MAAM;QAC9B,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,oBAAoB,CAAC,QAA4B,EAAE,KAAc;IAC/E,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,QAAiB;IAC1D,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,MAAM,QAAQ,GAAI,KAA4C,EAAE,QAAQ,CAAC;IACzE,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;AACjD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archmax-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agents can't reliably follow a process. archmax harness makes them: a governed layer over LangChain Deep Agents that enforces a declarative workflow state machine on every step.",
|
|
6
6
|
"keywords": [
|
|
@@ -55,8 +55,10 @@ then verify. Do not write a file, validate, and discover the next file.
|
|
|
55
55
|
`triggers: { manual: }` (plus any host trigger ids that enter there),
|
|
56
56
|
terminal states (no `transitions`), the edges between them — and for each
|
|
57
57
|
edge, the *condition* under which it is taken. That condition is the edge's
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
`description`, which is what the agent knows about where the edge leads. Leave
|
|
59
|
+
an edge undescribed only when the target's `instructions` already say when it
|
|
60
|
+
applies: the agent is then shown them instead (never on a human state's
|
|
61
|
+
transitions, whose description is the button label). If something outside the session calls the workflow
|
|
60
62
|
(another workflow, a host's MCP tool or form), give its trigger a
|
|
61
63
|
`description` for the caller and a `requires`/`returns` signature, typing an
|
|
62
64
|
entry (`{ name, type, description }`; `string` `integer` `number` `boolean`
|
|
@@ -77,7 +79,9 @@ then verify. Do not write a file, validate, and discover the next file.
|
|
|
77
79
|
`skills/<capability>/` bundle only for data, an `archmax_run` script, or
|
|
78
80
|
guidance two or more states share identically; `AGENTS.md` only for a
|
|
79
81
|
workspace-wide persona.
|
|
80
|
-
3. **Write `workflow.yaml`** from the skeleton in [`references/workflow-yaml.md`](references/workflow-yaml.md)
|
|
82
|
+
3. **Write `workflow.yaml`** from the skeleton in [`references/workflow-yaml.md`](references/workflow-yaml.md),
|
|
83
|
+
keeping its `tools.allow_always` list of the twelve default tools: a declared
|
|
84
|
+
list replaces them, so drop only a tool this workflow must not have. Each state's behaviour
|
|
81
85
|
goes inline in its `instructions`; run-wide rules once in the top-level
|
|
82
86
|
`instructions`; nothing about the machine itself (the platform prompt and
|
|
83
87
|
the disclosed edges already say it, and prose is billed on every call), and
|
|
@@ -212,8 +216,8 @@ Rules that follow:
|
|
|
212
216
|
(`script.skill-only`): a script in `scratchpad/` or `hooks/` is unrunnable.
|
|
213
217
|
- **Paths have no leading slash** (`skills/order-data/assets/orders.json`,
|
|
214
218
|
`scratchpad/refund.json`); never author a `run/` prefix. `scratchpad/` is
|
|
215
|
-
writable in every state
|
|
216
|
-
inside it narrows nothing (warning); to govern *where* a result lands, name
|
|
219
|
+
writable in every state that is granted `write_file`, whatever its entry
|
|
220
|
+
narrows, so an allow entry inside it narrows nothing (warning); to govern *where* a result lands, name
|
|
217
221
|
a non-reserved run path (`reports/*.json`) and allow it.
|
|
218
222
|
- **Guidance has three homes, one per rule**: every workflow → `AGENTS.md`;
|
|
219
223
|
the whole run → top-level `instructions` (`|-` literal block, since `>-`
|
|
@@ -100,7 +100,8 @@ All optional (`CreateAgentParams`, `src/assembly/index.ts`):
|
|
|
100
100
|
| `sessionStore` | `SessionStore` | Physical storage for run state (checkpoints, artifacts, the per-session `scratchpad/`). Zero-config default: `createFilesystemSessionStore({ dir: "<rootDir>/sessions" })`. Required with a custom `backend` (else `SessionStoreRequiredError`). Build with `createBackendSessionStore({ backend, prefix? })` for S3-style storage or `createMemorySessionStore()` for ephemeral runs. |
|
|
101
101
|
| `systemPrompt` | `string` | Appended after workspace `AGENTS.md`. |
|
|
102
102
|
| `tools` | `StructuredTool[]` | Host tools, granted per state by `tools.allow`. Build them with `toolsFromMap` — see [host tools](#host-tools). A name starting `archmax_`, or naming a runtime file operation (`copy_file`, `move_file`, `remove_file`), throws `ReservedToolNameError`. |
|
|
103
|
-
| `essentialTools` | `string[]` | Host tools treated as always on: disclosed and permitted in every state; a state's own entry still narrows one. |
|
|
103
|
+
| `essentialTools` | `string[]` | Host tools treated as always on: disclosed and permitted in every state, whatever the workflow's `tools.allow_always` declares; a state's own entry still narrows one. |
|
|
104
|
+
| `clock` | `string` | Pins the "now" every model call states, for cases and evaluations: an ISO 8601 date (`2026-03-20`, midnight UTC) or a date-time with `Z`/an offset; zoneless or impossible values fail assembly, `""` means the real clock. Rendered in UTC, rounded to ten minutes, no local time. Recorded timestamps, `resumeAt` and the sandbox's `Date` stay real. Not for production: a pinned session resumed days later still thinks it is the pinned day. A case's `clock:` outranks it. |
|
|
104
105
|
| `images` | `boolean \| ImageReadOptions` | Show a model with vision the images it reads: the model's `read_file` of a PNG/JPEG/GIF/WebP answers with one line and the image is added to each later request as a `user` message after that batch of tool results, read per request — no bytes in history, checkpoints, events or scripts. `{ maxBytes?, keep? }` (default 10 MB, every image). Off by default; every other binary stays refused. |
|
|
105
106
|
| `toolPaths` | `Record<string, ToolPaths>` | Each host tool's path arguments and how a call uses each (`read`, `list`, `search`, `write`, `remove`, `execute`): `{ get_markdown: { path: "read" } }`. Every path rule then governs them like the built-ins'. Wins over a descriptor's `paths`; declaring a built-in's paths throws `ToolPathsError`. |
|
|
106
107
|
| `checkpointer` | `BaseCheckpointSaver` | Custom-adapter escape hatch; takes precedence over the session store for checkpoint persistence. Defaults to a durable `BackendCheckpointSaver` writing through the session store. Use `MemorySaver` for ephemeral runs. |
|
|
@@ -153,7 +154,7 @@ try {
|
|
|
153
154
|
return lastAssistantText(result.messages);
|
|
154
155
|
}
|
|
155
156
|
} finally {
|
|
156
|
-
agent.dispose(sessionId); //
|
|
157
|
+
agent.dispose(sessionId); // frees the session's sandbox/PTC state and its checkpoints in memory; the next turn reloads them from the store
|
|
157
158
|
}
|
|
158
159
|
```
|
|
159
160
|
|
|
@@ -220,11 +221,13 @@ await agent.invoke(
|
|
|
220
221
|
|
|
221
222
|
Gotchas:
|
|
222
223
|
|
|
223
|
-
- **A hook reads
|
|
224
|
-
|
|
225
|
-
the
|
|
226
|
-
|
|
227
|
-
|
|
224
|
+
- **A hook reads its visit from `messages`, newest last** — the conversation since
|
|
225
|
+
the session entered the state it is in, which can span turns. A hook that keys
|
|
226
|
+
off "the current request" should take the *last* `user` entry; an earlier
|
|
227
|
+
state's messages are not in it, so what a hook needs from there is stored (a
|
|
228
|
+
variable, a file) or read with `toolCalls`. (A grading rubric receives the
|
|
229
|
+
compact `userRequest`/`history` payload its rubric is written against: `history`
|
|
230
|
+
is the same visit, `userRequest` the person's *latest* message.)
|
|
228
231
|
- **A definition edit is adopted, not refused.** A session takes its next turn
|
|
229
232
|
under the `workflow.yaml` in force now; the recorded `specHash` says which
|
|
230
233
|
definition each turn ran under but gates nothing. A turn positioned in a state
|
|
@@ -681,7 +684,8 @@ const agent = await createAgent({ tools, essentialTools: ["get_markdown"] });
|
|
|
681
684
|
never runs the handler. No declaration, no path rules. An argument may hold a
|
|
682
685
|
list of paths (`files: string[]`), each governed; any other shape is refused.
|
|
683
686
|
Declaring paths only narrows: `scratchpad/` is open in every state to the
|
|
684
|
-
|
|
687
|
+
default file tools the state is granted and to `essentialTools`, so a tool a
|
|
688
|
+
state must grant stays refused where it is not.
|
|
685
689
|
- `context.workspace` is the turn's workspace, bound to the session — the
|
|
686
690
|
instance `read_file` uses — with `downloadFiles`, `uploadFiles` (when the
|
|
687
691
|
session store has it) and `delete` besides the text methods. The plain agent
|
|
@@ -19,6 +19,7 @@ it). Host-interpreted; no sandbox runs a case. **Unknown keys are errors.**
|
|
|
19
19
|
| `variables` | `{ name: value }` seeded and **locked** every turn — the firing's payload; never seed what the agent must establish (assert it with `variables:` instead) |
|
|
20
20
|
| `workspace` | `{ <run path>: content }` — strings verbatim, other YAML → JSON, `{ from: <tests-relative path> }` copies a file; only run-zone paths (`trigger.json`, `scratchpad/…`) |
|
|
21
21
|
| `mocks` | `[{ tool, whenInput?, result }]` — intercepts agent and script calls alike (partial-input match) |
|
|
22
|
+
| `clock` | ISO 8601 date (`2026-03-20`) or date-time with `Z`/offset — the "now" every model call of the case states; pin it whenever fixtures or prompts carry dates. Prompt only: scripts, the sandbox and recorded timestamps stay real |
|
|
22
23
|
| `steps` | one flat list; actions and assertions are peers; an assertion evaluates against the nearest action above it |
|
|
23
24
|
|
|
24
25
|
Actions: `send: "<message>"` (a new turn; against a parked run, a reply-only
|
|
@@ -31,10 +32,10 @@ reported `not-executed`): `succeeded: true`ˢ (fails on a raise too) · `parked:
|
|
|
31
32
|
`reachedState: <slug>`ˢ (a committed non-trigger trail step) ·
|
|
32
33
|
`trail: { to?, kind?, reason?, count }`ˢ (`kind`: trigger/agent/human/on_error) ·
|
|
33
34
|
`noTraversal: true`ˢ · `triggerArrival: <id>`ˢ ·
|
|
34
|
-
`reply: { includes?, excludes? }` (token or list; `"/regex/flags"` strings are regexes; matched against the
|
|
35
|
+
`reply: { includes?, excludes? }` (token or list; `"/regex/flags"` strings are regexes; matched against every assistant message of the session so far, earlier turns included) ·
|
|
35
36
|
`calledTool` / `notCalledTool` / `blockedTool: { name, input? }` (partial input; `blockedTool` = governance refused it — the assertion that tests a guard) ·
|
|
36
37
|
`ranWorkflow: { workflow, status?, count? }` · `usedNoTools: true` (whole conversation) ·
|
|
37
|
-
`variables: { expect, path?, locked? }` · `grade: { closedQA, atLeast }` (LLM grades the
|
|
38
|
+
`variables: { expect, path?, locked? }` · `grade: { closedQA, atLeast }` (LLM grades what the action's turn said plus the session's tool record so far; `atLeast` is a hard bar; needs `tests.judge`).
|
|
38
39
|
|
|
39
40
|
Put the structural assertion right after the action that should satisfy it,
|
|
40
41
|
before content assertions that only make sense in that state:
|
|
@@ -27,12 +27,14 @@ message alone. Migrations for removed keys in full:
|
|
|
27
27
|
| not a skill slug (path/glob given) | Use the bundle directory name; narrow paths with a `tools.allow` entry |
|
|
28
28
|
| `${{…}}` not a valid variable reference | `${{name}}` or `${{name.path}}`; snake_case names |
|
|
29
29
|
| guards on `${{x}}` but no state requires it (warning) | Add `x` to an earlier state's `requires`, or seed it at assembly |
|
|
30
|
-
| `allow` on a path in `scratchpad/` (warning) | Drop it, or govern a non-reserved run path instead |
|
|
31
|
-
| `tools.allow_always`
|
|
30
|
+
| `allow` or `allow_always` on a path in `scratchpad/` (warning) | Drop it, or govern a non-reserved run path instead |
|
|
31
|
+
| `tools.allow_always` is declared but grants none of the default tools (warning) | A declared list replaces the default tools: list the ones the workflow needs (`ls`, `read_file`, `write_file`, … as in the skeleton), or remove the key to grant them all |
|
|
32
32
|
| state both allows and forbids / forbids what nothing enables / enables what allow_always already does / `skills: { allow: [] }` reads like a deny (warnings) | Drop the redundant entry; use `forbid` to subtract |
|
|
33
33
|
| no top-level `instructions` (warning) | Add the standing block |
|
|
34
34
|
| state has several transitions but no `instructions` (warning) | Add routing guidance so the agent classifies the request before it advances |
|
|
35
|
-
| `transitions.<i>.description` must be a non-empty string (error) |
|
|
35
|
+
| `transitions.<i>.description` must be a non-empty string here: '<target>' declares none (error) | The edge is undescribed and its target has no `instructions` to route on. Add the condition under which the edge is taken (say *when*, not *where*), or give the target instructions |
|
|
36
|
+
| `transitions.<i>.description` must be a non-empty string on a human state's transition (error) | It is the decision's button label: say what choosing it means |
|
|
37
|
+
| `transitions.<i>.description` must be a string (error) | Quote it, or omit it to route on the target's instructions |
|
|
36
38
|
| trigger declares unknown key (warning) | Keys read: `session`, `message`, `connection`, `requires`, `returns`; others are preserved |
|
|
37
39
|
| `maxConcurrency` above 1 | Set to 1 or remove |
|
|
38
40
|
| `WORKFLOW.md` has frontmatter | Strip it; the machine is `workflow.yaml` |
|
|
@@ -161,10 +161,41 @@ export default async function hook({ state, phase, trigger, variables, messages,
|
|
|
161
161
|
| `state`, `phase` | The state whose hook is running; `"before"` or `"after"`. |
|
|
162
162
|
| `trigger` | The trigger **id string** the run started from. It carries no payload — the run's input is in `variables`. |
|
|
163
163
|
| `variables` | The run's variables as a plain `name → value` map, including the built-in `trigger`. Structured values are present whole (`variables.order.items[0].sku`). **Read-only**: assigning changes nothing, and the variable tools are absent from `tools.*` — a hook that could write variables would be editing the governance inputs of the state it judges. |
|
|
164
|
-
| `messages` | The
|
|
164
|
+
| `messages` | The transcript of the **current visit**, newest last, as `{ role, text, toolCalls? }`: everything since the session entered the state it is in. On an advance that is the state being left, for its `after` hook and the target's `before` hook alike; a visit spans turns, and entering a state again starts a new one. `role` is `user`, `assistant`, `tool`, `system`, or **`runtime`** — a note the runtime wrote (an arrival, a decision, an error route, a completion check; `note` names the kind). A `tool` entry carries `tool` and the call's `id` with an empty `text`: its result comes from `toolCalls`. A hook that reacts to narration matches `role === "runtime"`, never a `[bracket]` prefix in a user message. |
|
|
165
|
+
| `toolCalls(filter?)` | **Async.** The session's tool calls with their results, across every visit, oldest first: `{ id, name, args, result, status, state, visit }` (`result` parsed when it is JSON; `status` `completed`, `failed`, `rejected` by governance, or `pending`). `filter` narrows by `name`, `state`, `visit` (a number or `"current"`), `status` and `last`; an unknown key throws. The control tools are not in it, nor are calls a script or hook made through `tools.*`. Only the records it returns are copied into the sandbox. |
|
|
165
166
|
| `from`, `to`, `reason` | Present on `after` hooks: the transition the agent is attempting (`reason` is the agent's `archmax_advance` reason). |
|
|
166
167
|
| `tools` | The privileged tool-call bridge (async), camelCased: `await tools.readFile({ file_path: "skills/order-data/assets/orders.json" })` → file text, or the binary notice for a binary file (see below). See [Governance](#governance-of-tools-calls). |
|
|
167
168
|
|
|
169
|
+
**What a hook checks.** The conversation is the model's memory, not evidence: a
|
|
170
|
+
hook checks its own visit and the session's records — `variables`, files, and
|
|
171
|
+
the tool results `toolCalls` keeps as the tools returned them. Check a result
|
|
172
|
+
itself, never a copy the model wrote into a variable, and never call a tool
|
|
173
|
+
again just to see its result (for a host whose tools reach an external system,
|
|
174
|
+
that is a second action):
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
/**
|
|
178
|
+
* Cancel gate — an order may be cancelled only while it is pending.
|
|
179
|
+
*
|
|
180
|
+
* Finds the latest get_order call for the order being cancelled and vetoes
|
|
181
|
+
* unless its result says pending.
|
|
182
|
+
*/
|
|
183
|
+
export default async function hook({ toolCalls }) {
|
|
184
|
+
const [cancel] = await toolCalls({ name: "cancel_order", visit: "current", last: 1 });
|
|
185
|
+
if (!cancel) return ok();
|
|
186
|
+
const lookups = await toolCalls({ name: "get_order", status: "completed" });
|
|
187
|
+
const order = lookups.findLast((c) => c.args.id === cancel.args.id);
|
|
188
|
+
if (!order) return veto(`Look order ${cancel.args.id} up with get_order before cancelling it.`);
|
|
189
|
+
if (order.result.status !== "pending") return veto(`Order ${cancel.args.id} is ${order.result.status}, not pending.`);
|
|
190
|
+
return ok();
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
What a later state or turn needs from the conversation (the person's words, a
|
|
195
|
+
decision) is stored in a variable or a file by the state that has it. A `before`
|
|
196
|
+
hook re-run by `archmax_reset` starts a new visit and no longer sees the
|
|
197
|
+
session's opening request.
|
|
198
|
+
|
|
168
199
|
**The verdict** — the three helpers are globals:
|
|
169
200
|
|
|
170
201
|
| Return | Effect |
|
|
@@ -275,8 +306,9 @@ resolves to an `Error: …` string instead.
|
|
|
275
306
|
**Authoring rule for `archmax_run` scripts:** if sandboxed code calls `tools.X`,
|
|
276
307
|
the state's `allow` list must cover `tool: X` (with any argument constraints the
|
|
277
308
|
script's call satisfies), or the call is blocked. Grant it per state with
|
|
278
|
-
`tools.allow`, or workflow-wide with `tools.allow_always
|
|
279
|
-
|
|
309
|
+
`tools.allow`, or workflow-wide with `tools.allow_always`; a default tool such as
|
|
310
|
+
`read_file` is covered wherever the workflow-wide grant includes it. Lifecycle
|
|
311
|
+
hooks need no such grant.
|
|
280
312
|
|
|
281
313
|
### `archmax_run` scripts: the `args` contract
|
|
282
314
|
|
|
@@ -501,6 +533,9 @@ get the exact same flow when they return `correct(reason)` under a
|
|
|
501
533
|
into a verdict **fails closed** (veto with a snippet of the raw output). A rubric
|
|
502
534
|
receives a compact JSON payload (`userRequest`, `history`, `state`, `phase`) —
|
|
503
535
|
its `instructions` are written against that, not against a hook's `messages`.
|
|
536
|
+
`history` is the visit the hook gates, tool results in full; `userRequest` is the
|
|
537
|
+
person's latest message (what the reply answers), which an earlier state usually
|
|
538
|
+
received.
|
|
504
539
|
|
|
505
540
|
---
|
|
506
541
|
|
|
@@ -607,6 +642,7 @@ Unknown keys are validation **errors**. All keys except `title` and
|
|
|
607
642
|
| `trigger` | `{ id }` | The trigger every driven turn of the case runs under. Carries **only an id**; `args` is rejected with a pointer to `variables:`. Validated against the workflow machine before any step — an unknown id fails the case (even with no steps), listing the workflow's declared triggers. Omitted ⇒ the manual trigger. See [Start conditions](#start-conditions-trigger-variables-and-workspace). |
|
|
608
643
|
| `workspace` | `{ <path>: <content> }` | Files seeded into the case's run workspace **before any step**. Strings are written verbatim; non-string YAML values are JSON-serialized; a single-key `{ from: <tests/-relative path> }` mapping copies a file byte-for-byte. Only agent-visible run-zone paths are seedable (root-level files like `trigger.json`, or `scratchpad/…`); authored (`skills/…`), runtime-internal, and escaping paths are rejected before anything is written. |
|
|
609
644
|
| `mocks` | `[{ tool, whenInput?, result }]` | Declarative tool mocks: when the driven agent calls `tool` with input partially matching `whenInput`, return `result` instead of executing. Intercepts **agent-initiated and PTC** (script `tools.*`) calls alike. See [the mocking section](#settle-the-mocking-depth-before-writing-a-case). |
|
|
645
|
+
| `clock` | `"2026-03-20"` / `"…T14:30:00Z"` | The date and time every model call of the case states as "now": an ISO 8601 date (midnight UTC) or a date-time with `Z` or an offset; a zoneless or impossible value is a schema error. Carried on every action, a handoff and a child session included, and outranks the host's `clock` option. See [Start conditions](#start-conditions-trigger-variables-and-workspace). |
|
|
610
646
|
| `steps` | flat list of step entries | The case script: actions and assertions as peers (below). |
|
|
611
647
|
|
|
612
648
|
There is **no multi-session surface** in test spec version 1 — no `sessions:`,
|
|
@@ -665,7 +701,7 @@ failure in one of them **halts the case** — see
|
|
|
665
701
|
| `raised: true` / `raised: <code>` / `raised: { code?, reason? }` | **Structural.** The agent ended the turn with `archmax_raise({ code, reason })`. A string pins the code exactly; the mapping pins the code and matches the reason — exactly, or as a regex when written `"/pattern/flags"`. A miss names the code that was raised, or says the run did not raise. |
|
|
666
702
|
| `variables: { expect, path?, locked? }` | Every named variable is set to the expected value — deep equality, so a structured expectation compares by value. `path` addresses into a value per name (`items.-1.sku` is the last element); `locked` asserts the lock state. |
|
|
667
703
|
| `reachedState: <slug>` | **Structural.** A **committed** (non-trigger) audit-trail step entered the state — agent `archmax_advance`, human decision, `archmax_reset`, or `on_error` route. The trigger-arrival step never counts (it is recorded before the entry state's `before` gate runs, so a vetoed-entry run never "reaches" its start state). |
|
|
668
|
-
| `reply: { includes?, excludes? }` | `includes`: substrings or `/pattern/flags` regex strings that must **all** match; `excludes`: none may match (leak checks — a refusal that quotes the data it refuses still leaks it). Each accepts a **single token or a list** (`includes: "ORD-1003"` works). Matched against the
|
|
704
|
+
| `reply: { includes?, excludes? }` | `includes`: substrings or `/pattern/flags` regex strings that must **all** match; `excludes`: none may match (leak checks — a refusal that quotes the data it refuses still leaks it). Each accepts a **single token or a list** (`includes: "ORD-1003"` works). Matched against **every assistant message of the session so far** (earlier turns included), not just the final message (a parked run often closes with a short "moving on" message while the substantive answer came earlier). |
|
|
669
705
|
| `calledTool: { name, input? }` | The turn recorded a call to tool `name`; `input` is a **partial** match — mappings match on the declared keys (nested mappings partially), arrays by deep equality, and `/pattern/flags` string values match as **regexes** (recommended for file paths, so the model's exact spelling doesn't matter). |
|
|
670
706
|
| `notCalledTool: { name, input? }` | No matching call occurred. |
|
|
671
707
|
| `blockedTool: { name, input? }` | A matching call was made **and refused by governance** — the assertion for testing a guard. Distinct from `notCalledTool` (nothing was tried, which proves nothing about the guard) and from a tool that ran and errored (only a policy rejection counts). Use it to test a state's `tools.allow` narrowing or its `skills.allow` set. |
|
|
@@ -673,7 +709,7 @@ failure in one of them **halts the case** — see
|
|
|
673
709
|
| `trail: { to?, kind?, reason?, count }` | **Structural.** The number of audit-trail steps matching **all** given fields equals `count` (declare at least one of `to`/`kind`/`reason`). `kind`: `trigger` \| `agent` \| `human` \| `on_error` \| `branch` \| `join`. The trail accumulates across turns — each turn starts with a `trigger` step. |
|
|
674
710
|
| `noTraversal: true` | **Structural.** The trail holds nothing but trigger arrivals — a veto at entry committed no transitions. |
|
|
675
711
|
| `triggerArrival: <trigger-id>` | **Structural.** Some trail step has `kind: trigger` and that trigger id as its reason — the run really started from the declared trigger. |
|
|
676
|
-
| `grade: { closedQA, atLeast }` | LLM-grade the turn's **run evidence** against the `closedQA` criterion —
|
|
712
|
+
| `grade: { closedQA, atLeast }` | LLM-grade the turn's **run evidence** against the `closedQA` criterion — what the action's turn said (every text of that turn, empty when it said nothing) *plus* a bounded chronological record of the session's assistant messages and tool calls so far (each call with input, output, and status; mocked calls appear like real ones). The two are weighed differently: a criterion about what the user was **told** is met only by the reply, one about what the agent **did** may be met by the record — so `closedQA: looked the order up before answering` is a legitimate criterion, not only phrasing checks. **`atLeast` decides the outcome** — score ≥ `atLeast` passes, below it **fails the case**; the judge model's own opinion never overrides your declared bar, so the record can't disagree with the verdict. The judge is instructed to keep its reason to 1–3 sentences, so it fits on the verdict line. Requires `tests.judge` in `workflow.yaml`. |
|
|
677
713
|
|
|
678
714
|
**Regex form**: a string shaped `/pattern/flags` is compiled as a regular
|
|
679
715
|
expression; anything else matches as a plain substring (in `reply` tokens) or
|
|
@@ -775,13 +811,22 @@ steps:
|
|
|
775
811
|
Two operational notes:
|
|
776
812
|
|
|
777
813
|
- Seeding writes the file; **reading it is still governed** — the triggered
|
|
778
|
-
start state must allow the read tool the agent needs (`read_file` is
|
|
779
|
-
|
|
814
|
+
start state must allow the read tool the agent needs (`read_file` is granted
|
|
815
|
+
wherever the workflow-wide grant includes it, unless a state narrows it).
|
|
780
816
|
- Hosts that pin case sessions via `sessionIdForCase` reuse sessions across test
|
|
781
817
|
runs: declared files are overwritten each run, but a file seeded by an
|
|
782
818
|
_earlier_ run whose declaration was since removed can linger. The default
|
|
783
819
|
(per-run unique sessions) starts clean.
|
|
784
820
|
|
|
821
|
+
**Pin the date** with a case-level `clock:` whenever a fixture or a prompt
|
|
822
|
+
carries a date (an order placed on `2026-03-15`, "refundable for 30 days"):
|
|
823
|
+
without it the model reads the real "now" and the verdict drifts with the
|
|
824
|
+
calendar. Pin on a ten-minute mark (the line is rounded down to ten minutes,
|
|
825
|
+
UTC only). Only the prompt is pinned — hook scripts, the sandbox's `Date`, a
|
|
826
|
+
park's `resumeAt` and every recorded timestamp stay real, so a script that needs
|
|
827
|
+
the case's "today" reads it from a seeded variable, and a wait resumed in the
|
|
828
|
+
case is resumed with `deliver`, not by its `until`.
|
|
829
|
+
|
|
785
830
|
### Recovering a case's inputs from previous runs
|
|
786
831
|
|
|
787
832
|
A trigger carries only an id, so everything a firing passes arrives as
|
|
@@ -38,7 +38,8 @@ export default async function hook({ state, phase, trigger, variables, messages,
|
|
|
38
38
|
| `state`, `phase` | state slug; `"before"` or `"after"` |
|
|
39
39
|
| `trigger` | trigger id string (payload is in `variables`) |
|
|
40
40
|
| `variables` | read-only `name → value` map incl. `trigger`; structured values whole |
|
|
41
|
-
| `messages` |
|
|
41
|
+
| `messages` | the **current visit**, newest last: everything since the session entered the state it is in (on an advance, the state being left; it can span turns). `{ role, text, toolCalls? }`; roles `user`/`assistant`/`tool`/`system`/`runtime` (a runtime note; match `role === "runtime"`, never a `[bracket]`). A `tool` entry has `tool`, `id` and an empty `text`: read results with `toolCalls` |
|
|
42
|
+
| `toolCalls(filter?)` | async: the session's tool calls with results, every visit, oldest first, `{ id, name, args, result, status, state, visit }`; filter by `name`, `state`, `visit` (number or `"current"`), `status`, `last`. No control tools. Only what it returns enters the sandbox |
|
|
42
43
|
| `from`, `to`, `reason` | `after` only: the attempted transition and the agent's reason |
|
|
43
44
|
| `tools` | async camelCased bridge: `tools.readFile`, `tools.writeFile`, `tools.archmaxWorkflowEnrichOrder(...)`; no control tools; a hook's lists every tool on the bridge, not only the state's |
|
|
44
45
|
|
|
@@ -13,7 +13,7 @@ const agent = await createAgent({
|
|
|
13
13
|
workspace: { rootDir, /* mounts?, sessionStore? */ },
|
|
14
14
|
onEvent: (e) => { usage.handler(e); publish(e); }, // omit for the console subscriber
|
|
15
15
|
// model | modelFactory, tools, systemPrompt, middleware, backend, checkpointer,
|
|
16
|
-
// trigger, variables, promptCache, pricing, hookExecutors …
|
|
16
|
+
// trigger, variables, promptCache, pricing, clock, hookExecutors …
|
|
17
17
|
});
|
|
18
18
|
const sessionId = `run-${runId}`;
|
|
19
19
|
const result = await agent.invoke(
|