AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
archmax harness LangChain Deep Agent를 워크플로당 하나 조립. 단일 YAML 파일로 세션 관리
The archmax harness assembles one LangChain Deep Agent per workflow and governs every session with a declarative state machine you author in a single YAML file.
개발 도구TypeScript · JavaScriptGitHub @archmax-ai온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Governed workflows for AI agents: a thin layer over LangChain Deep Agents that keeps an agent inside a declarative state machine, so it follows the business process and stays safe, compliant and auditable.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A thin, governed layer over LangChain Deep
Agents. Every session runs
under a declarative workflow state machine you author in one YAML file.
Reliable automation with AI agents should follow the process, and the people who own that
process should be able to author it. Today that is out of reach for most domain experts.
Current automation tools fall short in three ways.README 전문 보기 ↗A thin, governed layer over LangChain Deep Agents. Every session runs under a declarative workflow state machine you author in one YAML file.
Reliable automation with AI agents should follow the process, and the people who own that process should be able to author it. Today that is out of reach for most domain experts. Current automation tools fall short in three ways.
The archmax harness turns this around. The agent runs the whole process, and one workflow.yaml both
describes it and enforces it. Data work happens in a sandbox the same rules govern.
Full documentation: https://harness.archmax.ai/
npm install @archmax-ai/harness
Node 22 or later. The model is configured through the environment, against any OpenAI-compatible
endpoint. The archmax harness reads those keys from a .env in the workspace root:
ARCHMAX_API_BASE_URL=https://api.example.com/v1
ARCHMAX_API_KEY=...
ARCHMAX_MODEL=...
ARCHMAX_MODEL is the default. A workflow names the id its states run on with settings.model,
and a single state overrides that with its own model:. Either way it is an id, resolved over
the same endpoint and credentials. So a mechanical state can run on a small model while the state
whose output a person reads runs on a capable one.
See configuration for every variable.
To have a coding agent (Claude Code, Cursor, Codex, …) author workflows for you, install the authoring skill:
npx skills add archmax-ai/harness
my-workspace/
├── AGENTS.md # persona
├── workflows/order-lookup/
│ ├── workflow.yaml # the enforced state machine
│ ├── WORKFLOW.md # optional prose addendum
│ ├── scripts/screen-request.js # code-state scripts, run by the runtime
│ └── tests/*.test.yaml # cases for `archmax test`
├── skills/<slug>/ # skill bundles: SKILL.md, assets/, scripts/
└── sessions/ # default session store (gitignored)
# workflows/order-lookup/workflow.yaml (excerpt)
title: Order lookup
instructions: Only ever disclose the requester's own company's orders.
runtime: { engine: archmax-harness, version: "3" }
tools:
allow_always: [ls, read_file, write_file, edit_file, glob, grep, copy_file,
move_file, remove_file, write_todos, archmax_eval, archmax_run]
# granted in every state. Without this key a
# workflow gets these default tools; declared,
# it gets exactly what it lists
forbid_always: [archmax_reset] # denied in every state; deny beats allow
skills: { allow_always: [order-data] } # enabled in every state; a state's own
# `skills.allow` adds, `skills.forbid` subtracts
mounts: { allow_always: [reference] } # which STATES reach a host-governed mount;
# a grant may say `access: read` or `read_write`,
# and only `read_write` lets this workflow write it
states:
screen-request: # a code state: the runtime runs its script on
type: code # entry, with no model call, and the script moves
script: scripts/screen-request.js # the session with tools.archmax_advance
triggers:
manual:
transitions: # its script routes, so a code state's own edges
# may go undescribed
- to: identify-case
description: The requester is known and asks about their own company's orders.
- to: decline
description: The request names no known requester.
identify-case:
instructions: Classify the request and archmax_advance to the matching path.
transitions: # the agent is shown this state's edges and nothing
# else of the graph: it routes on each description,
# or, where an edge has none, on the target's
# instructions
- to: orders-question
description: The user is asking about orders, status, shipping, or tracking.
orders-question: # no transitions: terminal, so a live answer
# ends where it is given
instructions: Answer from skills/order-data/assets/orders.json.
# no skills block: the workflow's grant reaches
# here, and enabling a bundle is the whole grant
enrich-order:
triggers:
enrichment_requested: # a host event: no person is waiting on this path
skills: { allow: [order-enrichment] } # the fan-out script's bundle
tools: { allow: [archmax_workflow_enrich-order] } # delegation is granted by name
instructions: >-
Run skills/order-enrichment/scripts/enrich-orders.js with archmax_run, record
its result as `enrichments`, then archmax_advance to review-enrichment. If a
note sends it back, fix what it names.
transitions:
- to: review-enrichment
description: Every order has been enriched and `enrichments` is recorded.
review-enrichment: # a grader state: an isolated grader judges the work
type: grader # against these criteria and decides the edge
instructions: >-
Check `enrichments` against `orders_to_enrich` and each returned file.
Correct: advance to enrichment-reported. Wrong: send them back naming what
to fix. After two send-backs, raise enrichment-incorrect.
transitions:
- to: enrichment-reported
description: Every requested order has exactly one enrichment, and each matches its file.
- to: enrich-order
description: The enrichments are incomplete or do not match their files; send them back.
# decline, enrichment-reported: terminal states that tell the person why, and
# summarize the approved enrichments
Grader and code states are automated states: the runtime runs them on entry, with no model
call of the session's agent, so a chain of code states costs nothing. A code state's script
(workflows/<slug>/scripts/) runs in the sandbox; a grader is an isolated subagent whose system
prompt is the state's instructions. Both read the session with archmax_get_history: its whole
transcript, tool results included, and its trail. Each is offered exactly its own state's tools,
and each leaves the way every state does: archmax_advance along one of its transitions, whose
reason reaches the next state as a note (a send-back is the correction the agent reads), or
archmax_raise to end the session as failed. A check is a state on the graph, in front of the
work or behind it, and an edge into one always carries a description: a grader's instructions are
its criteria, and a code state has none. A grader suits work the agent hands on, such as the
enrichments above, rather than a live answer: everything the agent says in a turn reaches the person,
a draft sent back included. Both live on the authoring plane, so the agent they check can neither
read nor call them. See
grader and code states.
A trigger may also declare the contract a caller relies on: what a firing must supply and what the session guarantees when it completes. A delegating workflow, a host's MCP tool and a start form all read the same declaration, and the runtime holds it at the boundary:
states:
intake:
triggers:
manual:
description: Refund one order and report what was refunded. # for callers, not the agent
requires:
- order_id # a bare name is untyped
- { name: due, type: date, description: The day the refund is due. }
returns:
- { name: total, type: number, description: Refunded amount in EUR. }
A start whose due is not a real YYYY-MM-DD day is refused before any model call, and a session
that finishes with total not a number is rejected. One that finishes with total unset is
rejected too, unless it runs as a sub-workflow: then its caller gets total empty (null), plus a
note naming it. A return is owed only when the session finishes, and a seed of one lands unlocked. signatureJsonSchema on
@archmax-ai/harness/spec turns the same signature into a JSON Schema. See
triggers.
An entry typed file takes a path from the workspace root. A host hands one in with
send(sessionId, { message, files: { invoice: { name, content } } }), or the CLI with
--file invoice=./invoice.pdf, and the file lands in the session's scratchpad/attachments/. A
sub-workflow is its own session with its own files, so a file argument is copied into the child,
and a file return is copied back under a name that never overwrites the caller's files. See
passing files. The child's
session id is <parent>~<n>, n the dispatch's number in its caller, and a host tool called in
the child is handed that id as ToolContext.sessionId, so a host that stores session files itself
keys them under the child's <sessionId>/, not its caller's.
examples/customer-support/ in this repository is a complete reference workspace.
npx archmax run order-lookup "Which orders are delayed for Acme?" --root ./my-workspace
npx archmax run reconcile "Reconcile this" --file invoice=./invoice.pdf # hand the session a file
npx archmax test order-lookup --root ./my-workspace # run the workflow's cases
npx archmax validate order-lookup --root ./my-workspace # static checks, no model calls
npx archmax sessions --root ./my-workspace # list durable sessions
npx archmax decide <session> --to approved # resume a session parked at a human state
npx archmax reply <session> "Any update?" # answer a parked session; it stays parked
npx archmax deliver <session> --trigger email_reply # resume a session parked with archmax_wait
Results go to stdout and the state flow to stderr. Exit 0 means done or parked, 1 a failure
(including a session the agent ended with archmax_raise), 2 a usage error. See the
CLI reference.
import { createAgent } from "@archmax-ai/harness";
const agent = await createAgent({
workflow: "order-lookup",
workspace: { rootDir: "./my-workspace" },
});
// A turn, a decision, a reply or a delivery: one call, one Outcome.
const outcome = await agent.workflow!.send("session-42", {
message: "Which orders are delayed for Acme?",
});
console.log(outcome.kind, outcome.state, outcome.reply); // "parked" "refund-review" "..."
if (outcome.kind === "parked" && outcome.parkedChannel === "decision") {
await agent.workflow!.decide("session-42", { target: "approved", comment: "Within policy." });
}
A session is a success unless the agent says otherwise. When the work cannot be completed, the
agent calls archmax_raise({ code, reason }), which is offered in every state and only for
failure. The session then ends failed, with no further model call and no on_error route, and
outcome.kind === "failed" carries outcome.exit: { success: false, code, reason }, so a host
branches on the agent's own code. A completed turn carries exit: { success: true }; a rejection
by governance carries outcome.rejected instead. See
how a session ended.
Omit workflow for a plain Deep Agent with the same parameters, or pass workflow: false to
declare an agent ungoverned. onEvent receives the typed event stream and
workspace.sessionStore puts sessions elsewhere. workspace.mounts composes what the agent can
see, and marking a mount governed: true hands the spec's mounts block the decision of which
states reach it. Marking one searchable: false keeps root-wide searches away from a backend
that must refuse them.
Five subpaths keep production imports small:
@archmax-ai/harness/sandbox: code-state script types (defineCodeState, CodeStateInput)@archmax-ai/harness/testing: the case engine@archmax-ai/harness/cli: the state-flow renderer@archmax-ai/harness/spec: the workflow.yaml schema, the pure validator and the grammars@archmax-ai/harness/messages: transcript readersThe last two are browser-safe, with no runtime behind them. See the public API reference.
The authoring skill teaches a coding agent the schema, grader and code states, governance, human states, cases and the CLI. Install it with the skills CLI:
npx skills add archmax-ai/harness
That installs the latest version from GitHub. To pin the skill to the SDK version your project
depends on, install the copy shipped inside the package instead, after npm install:
npx skills add ./node_modules/@archmax-ai/harness
For skills middleware in your own agent, the BUNDLED_AUTHORING_SKILL_DIR export resolves to
the packaged copy. The source is skills/archmax-harness/.
npm run dev -- run order-lookup "..." --root examples/customer-support
npm run typecheck && npm test
npm run docs:dev
Substantive changes flow through OpenSpec under
openspec/. Documentation, README.md and the authoring skill ship in the same change as the
behaviour they describe. See CONTRIBUTING.md and the
development setup.
MIT. See LICENSE.