You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ship an AI agent inside the Harper server process (as opposed to the external harper-agent CLI). It receives prompts from app developers and operators, then autonomously develops, tests, monitors, and debugs Harper components and applications running on that instance.
Single LLM-backed operation API instead of a separate CLI on the dev machine.
Zero-friction access to live server state: in-process operation dispatch (no auth round-trips, no HTTP), raw log files, component file edits, V8 inspector attach against worker threads.
Autonomous mode (timers + self-resumption) lets the agent iterate on a goal — e.g. "build a recommendations component, then test it, then refine" — without a human in the loop on each turn.
Coexists with harper-agent CLI and Studio chat: those become thin clients to agent_prompt rather than parallel engines.
ConversationResource (#511, app-defined, app data model)
Tools
Full registry + operator-only extensions
RBAC-filtered registry only
Identity
Configured agent user (default super_user)
Caller's identity
The two share the loop (one implementation in HarperFast/harper#612) but not session state, not tool surface, and not auth identity.
Architecture
Where it runs. Built-in component registered via HARPER_BUILTIN_COMPONENTS, loaded by core/components/componentLoader.ts. Exports startOnMainThread (legacy pattern, like replication/subscriptionManager.ts). Lives on the main thread alongside the operations API server (core/server/operationsServer.ts, default port 9925). Worker threads handle application/REST traffic; the agent stays out of their way and inspects them via CDP.
Request flow:
startOnMainThread registers agent_prompt (and friends) via server.registerOperation. These handlers run directly on the main thread.
agent_prompt appends the user message to the named session and kicks the agent loop (or attaches to an in-flight loop for that session). Returns { session_id, message_id } immediately.
Internally, the loop is a call to scope.models.generate(messages, { toolMode: 'auto', tools: composedToolSet, maxToolIterations, ... }) — the Add agent-loop orchestration / toolMode: 'auto' to scope.models harper#612 orchestrator handles tool dispatch, RBAC, audit-log integration, and re-invocation.
Clients pollget_agent_session to see transcript/status updates (SSE streaming can come later).
Concurrent sessions interleave on the event loop (turns are mostly awaits on LLM/HTTP). Per-session serialization prevents two prompts in the same session from racing.
Component structure
agent/
agent.ts // entry: exports startOnMainThread, handleApplication
operations.ts // registerOperation calls
session.ts // CombinedSession backed by hdb_agent_session table
toolset.ts // composes RBAC-filtered registry + operator-only tools per call
tools/
fsTools.ts // scoped read/write/list/grep against componentsRoot + logDir + configDir
inspectorTool.ts // CDP attach/evaluate/breakpoint/logpoint/profile against worker debug ports
scheduleTool.ts // schedule_followup via setTimeout(...).unref()
httpFetchTool.ts // outbound fetch (web research + self-test against own server)
The agent gets these for free; no per-tool wiring in agent/.
2. Operator-only tools, passed inline at the scope.models.generate call site. These are not registered in HarperFast/harper#615 because their runtime assumptions only hold on the main thread, and exposing them to application agents would conflict with Harper's app abstractions:
Safe only from a thread that isn't the one being inspected. Built-in agent on main thread → can debug workers. App agents on worker threads → would deadlock attaching to themselves.
Harper deliberately abstracts the filesystem from application code (Resources, not files). Apps mutating component source files is out of layer. Also provides low-level log access without needing a second read_log tool.
schedule_followup({ delayMs, prompt })
Worker threads restart on code reload — timers there get lost. Only the long-lived main thread has the right lifecycle for autonomous follow-up.
http_fetch
Outbound HTTP for web research and self-testing the agent's own deployed components against localhost:<port>. Apps that need outbound HTTP should wrap it in a Resource.
Operator tools are appended to the per-call tools: list passed to scope.models.generate. Without registration, there's no path by which they appear in tools/list for external MCP clients or for application toolMode: 'auto' callers.
Sessions
New system table system.hdb_agent_session keyed by session_id, holding an ordered array of AgentInputItems plus a pendingApprovals: ApprovalRequest[] field used by the approval flow (approve_agent_action resolves entries here). Implements a CombinedSession interface so the model-access call can hydrate / persist history transparently.
Intentionally separate from ConversationResource (#511), which is the app-developer-facing conversation primitive. Different audience, different lifecycle, different schema — the built-in agent's sessions are operator-owned and server-local; ConversationResource conversations are app-defined and may be tenant-scoped, multi-user, indexed, etc.
Configuration
New agent: block in harperdb-config.yaml, validated against core/config-root.schema.json:
```yaml
agent:
enabled: true # default false; opt-in to avoid surprise LLM bills
provider: anthropic # optional — falls back to scope.models default
model: claude-opus-4-7 # optional — falls back to scope.models default
maxTurns: 50
maxCostUsd: 5.00 # per-session hard cap; loop aborts with structured error when hit
autoApprove: false # if true, agent runs without approval gates (still gated by allowDestructive)
allowDestructive: false # required to enable destructive ops tools (drop_component, restart, set_configuration, ...)
user: hdb_agent # role/user the agent acts as (default: hdb_agent super_user, created at startup if missing)
componentsScope: ./components
```
Provider/model resolution: if agent.provider / agent.model are omitted, fall back to the scope.models default. Single-provider users configure once; power users can override per agent.
API keys: env vars only, read at startup, never in config. Inherited from the same env vars scope.models already uses (ANTHROPIC_API_KEY, OPENAI_API_KEY, …).
Permission / auth model
agent_prompt and friends require super_user — enforced by the standard operations auth layer (we don't set bypass_auth).
Tool calls execute under the agent's configured user. Default: a system hdb_agent super_user created at startup if missing. Operators can swap to a restricted role to limit blast radius (e.g. a read-only role for an analytics agent).
Destructive operator tools (any that mutate config, drop components, or restart) require allowDestructive: trueand still emit an approval request when autoApprove: false. restart and set_configuration require approval even when autoApprove: true — a misfire there bricks a node.
FS tools reject any path resolving outside the scoped roots.
Self-debugging support
threadServer.js opens the V8 inspector on THREADS_DEBUG_PORT (main) or THREADS_DEBUG_STARTINGPORT + workerIndex (workers) when threads_debug=true. The agent's inspector_* tools speak CDP over WS to the worker ports. Document in setup that operators must set threads_debug: true and a sensible starting port before asking the agent to debug.
inspector_attach rejects workerIndex < 0 (the main thread is where the agent itself runs; attaching would deadlock).
Dependencies
To harper-pro/package.json: nothing new for the loop itself — it's all scope.models (#510). The only direct deps:
ws (already transitively present, for CDP client to worker debug ports)
zod if we choose Zod-typed tool schemas for the operator-only tools (already transitively present)
No @openai/agents, no ai, no @ai-sdk/* direct in harper-pro — those live in scope.models resolution.
Provider/key config: agent: block falls back to scope.models defaults; keys env-only.
Status updates: clients poll get_agent_session; SSE/streaming later.
Approvals: stored on the session; resolved via approve_agent_action.
Concurrency: multiple sessions interleave on the event loop; per-session serialization.
Destructive ops: allowDestructive flag; restart and set_configuration always require approval.
Inspector: workers only.
Open questions
harper-agent CLI transition. With the in-process built-in agent doing the work, the CLI's role compresses to a transport/UI in front of agent_prompt. Worth confirming we want to migrate it to that shape rather than continue to maintain two engines.
Cost telemetry surfacing. Should get_agent_session include per-turn cost so operators can see budget burn-down without joining to analytics.model_call manually?
First-run UX. Server-side only — no REPL. Docs page with curl examples + Studio panel later.
Critical files to read / modify
Modify:
harper-pro/bin/harper.js — add agent=@/dist/agent/agent.js to HARPER_BUILTIN_COMPONENTS.
harper-pro/licensing/usageLicensing.ts — exemplar for component shape (handleApplication, registerOperation, system table reads).
harper-agent/tools/files/* — reusable scoped FS tool implementations to lift.
Verification
Build & boot: npm run build in harper-pro, start a server with the new env var; check logs for "Agent component initialized". No-op if agent.enabled=false.
Autonomous build: prompt "create a Resource called Hello that returns 'hi'" → confirm a component file appears (via scoped FS tools), then curl localhost:9926/Hello to confirm the deployed app works.
Debugging: with threads_debug=true, prompt "find why the Foo resource throws on POST" — should attach to inspector, set a logpoint, observe a request, report root cause.
Scheduled work: "every 5 minutes for the next hour, check cluster status and alert me if any node is down" — verifies schedule_followup + persistence across restarts.
Boundary checks: confirm read_file('/etc/passwd') is rejected; confirm inspector_* tools are NOT visible to tools/list over external MCP; confirm non-super_user callers of agent_prompt get 403.
Integration test at harper-pro/integrationTests/agent/ with a stub scope.models provider so CI doesn't burn LLM credits.
Cross-link: the durable execution epic HarperFast/harper#752 has been filed as a forward-looking peer to this work. No blocking coordination — #676 should ship independently — but a few points where the two will eventually align:
`schedule_followup` (currently `setTimeout(...).unref()` on the main thread) is a natural consumer of the durable timer service in Native durable timer service (sharded hierarchical timing wheel) harper#754 once it lands. Timers there would survive restarts, which is the gap the autonomous-mode verification scenarios ("every 5 minutes for the next hour…") would otherwise hit.
Proposal / Motivation
Ship an AI agent inside the Harper server process (as opposed to the external
harper-agentCLI). It receives prompts from app developers and operators, then autonomously develops, tests, monitors, and debugs Harper components and applications running on that instance.harper-agentCLI and Studio chat: those become thin clients toagent_promptrather than parallel engines.Relationship to HarperFast/harper#510 / HarperFast/harper#612 / HarperFast/harper#617
This issue depends on and builds on top of the model-access stack rather than duplicating it:
scope.models.generate(input, { toolMode: 'auto', tools, ... })from Add unified model-access API (scope.models) harper#510 + Add agent-loop orchestration /toolMode: 'auto'toscope.modelsharper#612. No bespoke@openai/agentsintegration; no second loop implementation.describe_*,search,restart,set_configuration,package_component,deploy_component, …) and the Application profile (test(cluster): make blockCacheEviction actually wait for the restart; pin the pre-crash replication gap #618, auto-generated per-Resource tools). The built-in agent consults the registry as the configured agent user, getting RBAC-filtered tools for free.This is distinct from application agents built with HarperFast/harper#612 by app developers in Resource code:
system.hdb_agent_session(server-local, persistent, operator-owned)ConversationResource(#511, app-defined, app data model)The two share the loop (one implementation in HarperFast/harper#612) but not session state, not tool surface, and not auth identity.
Architecture
Where it runs. Built-in component registered via
HARPER_BUILTIN_COMPONENTS, loaded bycore/components/componentLoader.ts. ExportsstartOnMainThread(legacy pattern, likereplication/subscriptionManager.ts). Lives on the main thread alongside the operations API server (core/server/operationsServer.ts, default port 9925). Worker threads handle application/REST traffic; the agent stays out of their way and inspects them via CDP.Request flow:
startOnMainThreadregistersagent_prompt(and friends) viaserver.registerOperation. These handlers run directly on the main thread.agent_promptappends the user message to the named session and kicks the agent loop (or attaches to an in-flight loop for that session). Returns{ session_id, message_id }immediately.scope.models.generate(messages, { toolMode: 'auto', tools: composedToolSet, maxToolIterations, ... })— the Add agent-loop orchestration /toolMode: 'auto'toscope.modelsharper#612 orchestrator handles tool dispatch, RBAC, audit-log integration, and re-invocation.get_agent_sessionto see transcript/status updates (SSE streaming can come later).awaits on LLM/HTTP). Per-session serialization prevents two prompts in the same session from racing.Component structure
Wire it up in
bin/harper.js:```js
process.env.HARPER_BUILTIN_COMPONENTS = ... + ',agent=@/dist/agent/agent.js';
```
No new top-level model-loop code, no provider SDK wiring — that all comes from
scope.modelsvia HarperFast/harper#510.Operations to register
All gated on super_user, registered on the main thread in
startOnMainThread(same pattern asinstall_usage_licenseinlicensing/usageLicensing.ts):agent_prompt{ session_id, message_id }get_agent_sessionlist_agent_sessionscancel_agent_runapprove_agent_actionset_agent_configTool composition
Per
agent_promptinvocation, the agent's tool set is composed from two sources:1. Unified MCP tool registry (#615), RBAC-filtered for the configured agent user. With HarperFast/harper#617 + HarperFast/harper#618 this includes:
describe_all,describe_table,search,read_audit_log,package_component,deploy_component,drop_component,restart,set_configuration,add_role, etc. (Subject to the per-operation allow/deny list and per-op annotations likedestructiveHint.)get_*,search_*,create_*,update_*,delete_*for each@export-ed Resource on the instance.mcpTools(replication: unresolved subscription placeholder Promise causes inbound-message TypeError storm and spurious full-database copy on every restart #622).read_log(the operations API), which lives in [MCP] Operations profile: tool generation over OPERATION_FUNCTION_MAP harper#617's profile. Note: this is not the low-level log file reader — that's covered by the scoped FS tools below.The agent gets these for free; no per-tool wiring in
agent/.2. Operator-only tools, passed inline at the
scope.models.generatecall site. These are not registered in HarperFast/harper#615 because their runtime assumptions only hold on the main thread, and exposing them to application agents would conflict with Harper's app abstractions:inspector_attach,inspector_evaluate,inspector_set_breakpoint,inspector_set_logpoint,inspector_profile_cpuread_file,write_file,apply_patch,list_dir,grep_files,tail_file(all scoped tocomponentsRoot+logDir+ config-file dir)read_logtool.schedule_followup({ delayMs, prompt })http_fetchlocalhost:<port>. Apps that need outbound HTTP should wrap it in a Resource.Operator tools are appended to the per-call
tools:list passed toscope.models.generate. Without registration, there's no path by which they appear intools/listfor external MCP clients or for applicationtoolMode: 'auto'callers.Sessions
New system table
system.hdb_agent_sessionkeyed bysession_id, holding an ordered array ofAgentInputItems plus apendingApprovals: ApprovalRequest[]field used by the approval flow (approve_agent_actionresolves entries here). Implements aCombinedSessioninterface so the model-access call can hydrate / persist history transparently.Intentionally separate from
ConversationResource(#511), which is the app-developer-facing conversation primitive. Different audience, different lifecycle, different schema — the built-in agent's sessions are operator-owned and server-local;ConversationResourceconversations are app-defined and may be tenant-scoped, multi-user, indexed, etc.Configuration
New
agent:block inharperdb-config.yaml, validated againstcore/config-root.schema.json:```yaml
agent:
enabled: true # default false; opt-in to avoid surprise LLM bills
provider: anthropic # optional — falls back to scope.models default
model: claude-opus-4-7 # optional — falls back to scope.models default
maxTurns: 50
maxCostUsd: 5.00 # per-session hard cap; loop aborts with structured error when hit
autoApprove: false # if true, agent runs without approval gates (still gated by allowDestructive)
allowDestructive: false # required to enable destructive ops tools (drop_component, restart, set_configuration, ...)
user: hdb_agent # role/user the agent acts as (default: hdb_agent super_user, created at startup if missing)
componentsScope: ./components
```
Provider/model resolution: if
agent.provider/agent.modelare omitted, fall back to thescope.modelsdefault. Single-provider users configure once; power users can override per agent.API keys: env vars only, read at startup, never in config. Inherited from the same env vars
scope.modelsalready uses (ANTHROPIC_API_KEY,OPENAI_API_KEY, …).Permission / auth model
agent_promptand friends require super_user — enforced by the standard operations auth layer (we don't setbypass_auth).hdb_agentsuper_user created at startup if missing. Operators can swap to a restricted role to limit blast radius (e.g. a read-only role for an analytics agent).allowDestructive: trueand still emit an approval request whenautoApprove: false.restartandset_configurationrequire approval even whenautoApprove: true— a misfire there bricks a node.Self-debugging support
threadServer.jsopens the V8 inspector onTHREADS_DEBUG_PORT(main) orTHREADS_DEBUG_STARTINGPORT + workerIndex(workers) whenthreads_debug=true. The agent'sinspector_*tools speak CDP over WS to the worker ports. Document in setup that operators must setthreads_debug: trueand a sensible starting port before asking the agent to debug.inspector_attachrejectsworkerIndex < 0(the main thread is where the agent itself runs; attaching would deadlock).Dependencies
To
harper-pro/package.json: nothing new for the loop itself — it's allscope.models(#510). The only direct deps:ws(already transitively present, for CDP client to worker debug ports)zodif we choose Zod-typed tool schemas for the operator-only tools (already transitively present)No
@openai/agents, noai, no@ai-sdk/*direct inharper-pro— those live inscope.modelsresolution.Resolved decisions
scope.models.generate(..., { toolMode: 'auto' })via Add agent-loop orchestration /toolMode: 'auto'toscope.modelsharper#612. No second implementation.hdb_agent_sessionis separate fromConversationResource(fix(replication): close on inbound message-handler errors instead of swallowing them (#440) #511) by design.agent:block falls back toscope.modelsdefaults; keys env-only.get_agent_session; SSE/streaming later.approve_agent_action.allowDestructiveflag;restartandset_configurationalways require approval.Open questions
harper-agentCLI transition. With the in-process built-in agent doing the work, the CLI's role compresses to a transport/UI in front ofagent_prompt. Worth confirming we want to migrate it to that shape rather than continue to maintain two engines.maxCostUsdenforcement. Add agent-loop orchestration /toolMode: 'auto'toscope.modelsharper#612 needs to expose a per-call token/cost budget for the orchestrator to enforce, or the built-in agent enforces it externally by inspecting cumulativeanalytics.model_callrows for the session. Prefer the former; see Add agent-loop orchestration /toolMode: 'auto'toscope.modelsharper#612 follow-up.get_agent_sessioninclude per-turn cost so operators can see budget burn-down without joining toanalytics.model_callmanually?Critical files to read / modify
Modify:
harper-pro/bin/harper.js— addagent=@/dist/agent/agent.jstoHARPER_BUILTIN_COMPONENTS.harper-pro/core/config-root.schema.json— addagentblock schema.harper-pro/core/utility/hdbTerms.ts— add config param constants + newOPERATIONS_ENUMentries foragent_prompt,get_agent_session, etc.Create:
harper-pro/agent/(full new directory, far thinner than the original proposal).Read & reuse:
scope.modelsmodel-access API (Replication W13: Base-copy & catch-up path (consolidation & correctness) #510) — backend.scope.modelstoolMode: 'auto'orchestrator (test(cluster): promote 4 QA replication/failover/blob-copy regression anchors #612) — loop.harper-pro/licensing/usageLicensing.ts— exemplar for component shape (handleApplication,registerOperation, system table reads).harper-agent/tools/files/*— reusable scoped FS tool implementations to lift.Verification
npm run buildin harper-pro, start a server with the new env var; check logs for "Agent component initialized". No-op ifagent.enabled=false.curl -X POST /agent_prompt -d '{"message":"describe the system schema"}'→ returnssession_id→curl /get_agent_session/<id>shows the agent calleddescribe_all(from [MCP] Operations profile: tool generation over OPERATION_FUNCTION_MAP harper#617) and produced a response.curl localhost:9926/Helloto confirm the deployed app works.http_fetch+ tool chaining through Add agent-loop orchestration /toolMode: 'auto'toscope.modelsharper#612.threads_debug=true, prompt "find why the Foo resource throws on POST" — should attach to inspector, set a logpoint, observe a request, report root cause.schedule_followup+ persistence across restarts.read_file('/etc/passwd')is rejected; confirminspector_*tools are NOT visible totools/listover external MCP; confirm non-super_user callers ofagent_promptget 403.harper-pro/integrationTests/agent/with a stubscope.modelsprovider so CI doesn't burn LLM credits.