; lib/agentic/trace.sx — agentic-sx Phase 3: console traces as ATTACHED ; content-addressed objects. An agent's console/tool output accumulates in a ; per-agent append-only persist log stream; the commit verb drains everything ; since the last commit into a console-trace object and binds it to the new ; commit git-note style: ref "notes/trace/" -> trace cid. The ; trace is NOT in the commit's tree — attaching never changes the commit cid, ; and the note is a re-bindable ref layer over immutable objects. ; Granularity = the commit, agent-chosen: whatever was logged since the last ; drain travels with the next commit. ; Requires: lib/agentic/branch.sx (and its deps). ; ---- buffer stream + drain cursor (namespaced under the repo prefix) ---- (define agentic/trace-stream (fn (sp agent) (str (get (agentic/space-repo sp) :prefix) "/trace/" agent))) (define agentic/trace-cursor-key (fn (sp agent) (str (get (agentic/space-repo sp) :prefix) "/trace-cursor/" agent))) ; append one console/tool entry to the agent's buffer => true (define agentic/trace! (fn (sp agent kind text) (begin (persist/append (git/repo-db (agentic/space-repo sp)) (agentic/trace-stream sp agent) "trace-entry" 0 (agentic/trace-entry kind text)) true))) ; entries logged since the last drain, oldest first (define agentic/trace-pending (fn (sp agent) (let ((db (git/repo-db (agentic/space-repo sp)))) (let ((cur (persist/kv-get db (agentic/trace-cursor-key sp agent)))) (map (fn (e) (persist/event-data e)) (persist/read-from db (agentic/trace-stream sp agent) (+ (if (nil? cur) 0 cur) 1))))))) ; advance the drain cursor to the stream's high-water mark (define agentic/trace-mark! (fn (sp agent) (let ((db (git/repo-db (agentic/space-repo sp)))) (begin (persist/kv-put db (agentic/trace-cursor-key sp agent) (persist/last-seq db (agentic/trace-stream sp agent))) true)))) ; ---- git-note-style binding: commit cid -> trace cid ---- (define agentic/trace-note-ref (fn (commit-cid) (str "notes/trace/" commit-cid))) ; write the trace object and bind it to the commit => trace cid | {:error} (define agentic/attach-trace! (fn (sp commit-cid trace-obj) (let ((repo (agentic/space-repo sp))) (if (not (agentic/console-trace? trace-obj)) {:error "not-a-console-trace"} (let ((tcid (git/write repo trace-obj))) (begin (git/ref-set! repo (agentic/trace-note-ref commit-cid) tcid) tcid)))))) (define agentic/trace-cid-for (fn (sp commit-cid) (git/ref-get (agentic/space-repo sp) (agentic/trace-note-ref commit-cid)))) (define agentic/trace-for (fn (sp commit-cid) (let ((tcid (agentic/trace-cid-for sp commit-cid))) (if (nil? tcid) nil (git/read (agentic/space-repo sp) tcid))))) ; ---- the commit verb with trace binding ---- ; commit! then drain the buffer into an attached console-trace. ; => {:cid cid :trace tcid} | {:cid cid} when nothing was logged ; | commit!'s {:error ...}/{:conflict ...} passthrough (buffer kept) (define agentic/commit-with-trace! (fn (sp agent kind files meta) (let ((cid (agentic/commit! sp agent kind files meta))) (if (dict? cid) cid (let ((entries (agentic/trace-pending sp agent))) (if (= (len entries) 0) {:cid cid} (let ((tcid (agentic/attach-trace! sp cid (agentic/console-trace entries {:agent agent :commit cid})))) (begin (agentic/trace-mark! sp agent) {:trace tcid :cid cid})))))))) ; (commit-cid trace-cid) pairs for the agent's session, newest first, ; commits without a bound trace omitted (define agentic/session-traces (fn (sp agent) (filter (fn (p) (not (nil? (nth p 1)))) (map (fn (cid) (list cid (agentic/trace-cid-for sp cid))) (agentic/session-log sp agent)))))