;; lib/flow/host.sx — the host integration ABI (Phase 8). ;; ;; `suspend` is flow's seam to the outside world, but a bare (suspend tag) is just a ;; signal — every author would invent their own tag shape. This layer defines a ;; stable request/response contract so a host (e.g. an art-dag driver, or a human ;; review UI) can hook in WITHOUT reverse-engineering ad-hoc tags. ;; ;; A flow asks the host to do something and waits for the answer: ;; (request kind payload) — suspend with a typed envelope (flow-request kind ;; payload); evaluates to the host's resume value. ;; (await-human prompt) — request kind=human (a decision point) ;; (await-render recipe) — request kind=render (e.g. an art-dag job) ;; (await-effect kind p) — request of an arbitrary kind ;; ;; The host drives flows by polling its work queue and resuming: ;; (flow-host-requests) — ((id kind payload) ...) for every SUSPENDED flow whose ;; waiting tag is a host request. The host dispatches by kind (render -> submit a ;; Celery job; human -> show UI), then calls (flow/resume id answer). ;; (request? tag) / (request-kind tag) / (request-payload tag) — parse one tag. ;; ;; Reference driver — the host only supplies `dispatch`, a (kind payload) -> answer: ;; (flow-drive-host dispatch) — one tick: service every CURRENTLY pending ;; request (snapshot), resuming each with (dispatch kind payload); returns the ;; count serviced. Resumes may create new requests — serviced on the next tick. ;; (flow-run-host dispatch maxticks) — tick until quiescent (no pending requests) ;; or maxticks reached; returns total requests serviced. Bounded for determinism. ;; ;; Contract: the host owns IO and persistence. flow stays deterministic — a flow ;; never performs IO itself, it only `request`s; the host performs the effect and ;; feeds the result back via resume (which the replay log records, so the effect is ;; not re-run on recovery). Persist with flow-store-export after each transition and ;; flow-store-import! on boot. (define flow-host-src "(define (request kind payload) (suspend (list (quote flow-request) kind payload)))\n (define (request? tag) (and (pair? tag) (eq? (car tag) (quote flow-request))))\n (define (request-kind tag) (car (cdr tag)))\n (define (request-payload tag) (car (cdr (cdr tag))))\n (define (await-human prompt) (request (quote human) prompt))\n (define (await-render recipe) (request (quote render) recipe))\n (define (await-effect kind payload) (request kind payload))\n (define (flow-host-req-step pend)\n (if (null? pend)\n (list)\n (let ((id (car (car pend))) (tag (car (cdr (car pend)))))\n (if (request? tag)\n (cons (list id (request-kind tag) (request-payload tag))\n (flow-host-req-step (cdr pend)))\n (flow-host-req-step (cdr pend))))))\n (define (flow-host-requests) (flow-host-req-step (flow/pending)))\n (define (flow-drive-host-step reqs dispatch)\n (if (null? reqs)\n 0\n (begin\n (flow/resume (car (car reqs)) (dispatch (car (cdr (car reqs))) (car (cdr (cdr (car reqs))))))\n (+ 1 (flow-drive-host-step (cdr reqs) dispatch)))))\n (define (flow-drive-host dispatch) (flow-drive-host-step (flow-host-requests) dispatch))\n (define (flow-run-host dispatch maxticks)\n (if (<= maxticks 0)\n 0\n (let ((n (flow-drive-host dispatch)))\n (if (= n 0) 0 (+ n (flow-run-host dispatch (- maxticks 1)))))))") (define flow-load-host! (fn (env) (begin (scheme-eval-program (scheme-parse-all flow-host-src) env) env)))