macher-agent-delegate.mov
The macher-agent package provides an Emacs-native artificial intelligence agent harness. It integrates deeply with gptel and macher to enable autonomous, multi-agent workflows directly within Emacs buffers.
The architecture operates entirely inside native Emacs buffers rather than external terminal interfaces. Tools and subagents operate as sentinels within dedicated buffers. Subagents coordinate through direct Emacs Lisp callbacks for point-to-point communication. Programmatic tool calling runs within a secure Emacs Lisp sandbox, allowing agents to chain operations and transform data in a single turn. The macher-agent-make-tool constructor returns structured data first and presentation second, using Emacs as an extensible multiplexing environment.
The harness is optimised to execute large numbers of concurrent agents inside a single Emacs session. Each subagent operates on an isolated Virtual File System (VFS) context cloned from its parent. The Virtual File System and the sandboxed evaluation runtime allow agents to resolve complex tasks without premature disk modification. Proposed file and buffer modifications are staged in memory and presented as unified diffs for review before applying changes to disk.
The package integrates with gptel across several core boundaries:
- Functions registered in
gptel-prompt-transform-functionssynchronise the Virtual File System, dynamically resolve inline skill mentions (such as@skill-name), compile hidden prompt directives, and prune duplicate tool call history prior to transmission. - Finite-state machine handlers (
gptel-fsm-handlers) capture user prompts, inject base64-encoded visual media, protect callback closures, and trigger task completion flushes when execution reaches terminal states. - The pre-tool execution hook
gptel-pre-tool-call-functionsenforces strict tool scoping, blocking invocations of tools not permitted in the active skill configuration. - Settings such as
gptel-model,gptel-backend,gptel-system-prompt, andgptel-toolsare scoped buffer-locally to allow each agent buffer to maintain an independent identity.
The package extends macher to provide workspace-level isolation and Virtual File System capabilities:
machercontext perstets until invalidated (clear, merge or fail-fast out of band modeification)machertools are wrapped to inject a persistent context. This prevents tool calls from executing against unhydrated contexts and ensures disk-based operations operate on in-memory buffers.- File modifications and buffer edits stage directly in the Virtual File System context. Diffs are generated separately for buffer changes and file modifications, presenting unified diffs for user review before committing changes.
- Active workspaces and persistent contexts register in
macher-agent-active-workspacesby project root. Subagents receive isolated child contexts that merge back into the orchestrator context upon task submission. -The toolsearch_in_workspaceuses direct file system traversal to ensure consistent performance and avoid garbage collection bottlenecks on large workspaces.
The architecture uses an extensible plug-in model built on ordered pipeline reducers and lifecycle hooks. Custom packages and user configurations extend core behaviour by registering steps into named pipelines with explicit priority depths.
Pipelines process state property lists sequentially through registered step functions. Steps execute in ascending priority order, where lower integer values execute earlier in the pipeline sequence.
Core pipeline registries include:
context-resolution: Resolves the activemacher-contextfrom inputs, buffers, payloads, state machines, or workspace directories.preset-composition: Merges skill definitions, allowed tools, model parameters, and programmatic tool calling primitives into unified payloads.transmission: Hydrates model settings, compiles hidden directives, injects memory tools, and prepares the network payload forgptel-send.artifact-compose: Packages completed subagent outputs, diffs, and context modifications when a task finishes.payload-merge: Merges subagent Virtual File System diffs into the parent orchestrator context.
Use macher-agent-register-pipeline-step to attach custom logic to a pipeline. The function accepts the pipeline symbol, the step function, and an integer priority.
;; Define a custom transmission step that injects a project header directive
(defun my-custom-transmission-header-step (state orig-buf presets skills redirect)
"Inject a workspace banner into the transmission directives."
(let ((banner (format "PROJECT ENVIRONMENT: %s" (macher-agent-root default-directory))))
(push banner (macher-agent-transmission-state-directives state)))
state)
;; Register the step in the transmission pipeline at priority 65
(macher-agent-register-pipeline-step
'transmission
#'my-custom-transmission-header-step
65)
;; Define a custom context resolution step for custom buffer types
(defun my-custom-buffer-context-step (state)
"Resolve context from specialised buffer-local properties."
(if (and (plist-get state :resolved) (null (plist-get state :input)))
state
(let ((input (plist-get state :input)))
(if (and (bufferp input) (buffer-local-value 'my-custom-context-var input))
(plist-put state :resolved (buffer-local-value 'my-custom-context-var input))
state))))
;; Register the custom context resolver at priority 12
(macher-agent-register-pipeline-step
'context-resolution
#'my-custom-buffer-context-step
12)The framework provides event hooks to monitor, gate, and audit tool execution and workspace changes:
macher-agent-pre-tool-use-hook: Runs immediately before any tool executes, receiving the tool symbol and argument property list.macher-agent-permission-request-hook: Evaluates tool execution permissions. Hook functions receive the tool symbol and argument property list, returningtto permit execution ornilto deny.macher-agent-post-tool-use-hook: Runs after successful tool execution, receiving the tool symbol, arguments, and the result string.macher-agent-post-tool-use-failure-hook: Runs when tool execution fails, receiving the tool symbol, arguments, and error data.macher-agent-task-flush-hook: Runs when a task completes and flushes context data.macher-agent-vfs-flush-hook: Runs after the Virtual File System processes file and buffer modifications.macher-agent-context-mutated-hook: Runs whenever the Virtual File System context is modified.
;; Example: Interactive permission gate for destructive tools
(defun my-permission-gate (tool-name payload)
"Prompt the user before executing destructive workspace operations."
(if (memq tool-name '(write_file_in_workspace delete_file_in_workspace))
(let ((path (plist-get payload :path)))
(y-or-n-p (format "Permit agent to execute '%s' on '%s'? " tool-name path)))
t))
(add-hook 'macher-agent-permission-request-hook #'my-permission-gate)
;; Example: Audit logging for completed tool executions
(defun my-tool-audit-logger (tool-name payload output)
"Log tool execution details to an audit buffer."
(let ((log-buffer (get-buffer-create "*macher-agent-audit*")))
(with-current-buffer log-buffer
(goto-char (point-max))
(insert (format "[%s] TOOL: %s | ARGS: %S | RESULT SIZE: %d chars\n"
(format-time-string "%Y-%m-%d %H:%M:%S")
tool-name
payload
(length output))))))
(add-hook 'macher-agent-post-tool-use-hook #'my-tool-audit-logger)Agents interact through point-to-point Agent-to-Agent (A2A) payloads and callback closures.
| Tool | Description | Communication type | Virtual File System synchronisation |
|---|---|---|---|
delegate_tasks_to_subagents |
Dispatches tasks synchronously to worker agents and aggregates responses | Direct message dispatch | Merges child diffs upon task submission |
execute_subagents |
Dispatches fire-and-forget background tasks in parallel | Asynchronous dispatch | Staged in child context |
submit_task_result |
Submits completed task output back to the originating caller | Artifact update | Merges Virtual File System diffs |
spawn_subagent |
Creates a named subagent buffer configured with specific skill presets | Lifecycle initialisation | Clones parent context |
send_message |
Sends an asynchronous message to a resident specialist bot buffer | Direct point-to-point message | Transmits instructions directly |
wait_for_message |
Suspends a resident specialist bot until an incoming message arrives | Event suspension | Merges VFS from sender |
wait_for_vfs_semaphore |
Blocks execution until a Virtual File System resource lock is acquired | Point-to-point lock | Synchronises targeted resource path |
Memory recall beyond the macher-agent-max-context-chars boundary operates through the search_conversation_history tool.
1,000 Traces
| Engine | Time | GC Cycles | GC Time |
|---|---|---|---|
| Float PPR | 0.050142 s | 2 | 0.034173 s |
| Fixed-Point PPR | 0.028505 s | 1 | 0.016375 s |
| Glob | 0.000026 s | 0 | 0 s |
5,000 Traces
| Engine | Time | GC Cycles | GC Time |
|---|---|---|---|
| Float PPR | 0.277900 s | 11 | 0.200374 s |
| Fixed-Point PPR | 0.142280 s | 5 | 0.092353 s |
| Glob | 0.000062 s | 0 | 0 s |
10,000 Traces
| Engine | Time | GC Cycles | GC Time |
|---|---|---|---|
| Float PPR | 0.589837 s | 20 | 0.429688 s |
| Fixed-Point PPR | 0.293908 s | 9 | 0.197646 s |
| Glob | 0.000115 s | 0 | 0 s |
Programmatic tool calling in Emacs Lisp allows complex operations that would otherwise require multiple conversational turns to execute within a single request. The execution runs inside a yielding Emacs Lisp sandbox.
The tools list-directory-in-workspace, spawn-subagent, and delegate-tasks-to-subagents are exposed as callable primitives:
(let*
((listing (list-directory-in-workspace "" "" ""))
(lines (split-string listing "\n"))
(file-lines
(cl-loop
for line in lines when (string-prefix-p "file: " line) collect (substring line 6)))
(agent-count (length file-lines))
(agent-names (cl-loop for i from 0 below agent-count collect (format "agent-%d" i)))
(tasks
(cl-loop
for path in file-lines for name in agent-names
collect (list :buffer_name name
:instructions (format "Read the first paragraph of the file at '%s' and provide a concise summary." path)
:presets (list "macher-agent-worker")))))
(if (zerop agent-count)
"No files found"
(progn
(mapcar (lambda (name) (spawn-subagent name (list "macher-agent-worker"))) agent-names)
(delegate-tasks-to-subagents tasks))))Each subagent operates on a discrete Virtual File System context within the workspace, featuring automatic conflict detection and merge resolution.
Ensure the following utilities and packages are available:
- Emacs 29.1 or higher
gptelandmacher- Git
- Rsync
Install and configure macher-agent using use-package:
(use-package macher-agent
:vc (:url "https://github.com/elij/macher-agent/")
:ensure t
:after (gptel macher)
:config
(require 'macher-agent-vfs) ;; Enable Virtual File System
(require 'macher-agent-sandbox) ;; Enable Programmatic Tool Calling
(require 'macher-agent-zero-mem) ;; Enable dual-ledger conversation memory
(macher-agent-install)
(add-hook 'gptel-mode-hook #'macher-agent-mode))- Initialise a Git repository in your project directory:
git init- Open or create a file within the project, then start an interactive chat buffer using standard
gptelormacher-discuss. - Interact with the language model directly. The model can inspect files, stage changes in the Virtual File System, evaluate scripts using programmatic tool calling, and dispatch subagents.
For detailed guides, architectural diagrams, and cookbook patterns, refer to the project wiki.