Replies: 1 comment
-
|
The opt-in + maxCostUsd design is a good instinct. Built-in agents can become expensive in surprising ways because tool loops, retries, and long context are usually what drive the bill, not a single model call. One addition I’d consider is model routing per agent phase:
I'm building a private-beta OpenAI-compatible API aggregation layer focused on Chinese models like DeepSeek / Qwen / GLM-style providers, using official-provider keys. If you ever want to benchmark lower-cost models for Harper agent runs, I’d be happy to share some test credits. Curious: do you expect the biggest cost driver to be long context, tool-call loops, or higher-end planning models? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Proposal/Motivation
Add an AI agent that ships inside the Harper server process (vs. the external
harper-agentCLI tool at/home/kzyp/dev/harper-agent). The motivation:operation API, instead of running a separate CLI on their dev machine.
operation()directly in-process (no auth round-trips, no HTTP), reads logs straight from the
log files, watches/edits component files, and can attach to the inspector that
threadServer.jsalready opens."build a recommendations component, then test it, then refine") without a human
in the loop on each turn.
Inherits model/provider choices, the
@openai/agentsloop, and Zod-typed toolpatterns from
harper-agentso the two share DNA.Architecture overview
Where it runs. Built-in component registered via
HARPER_BUILTIN_COMPONENTS,loaded by
core/components/componentLoader.ts:121. ExportsstartOnMainThread(legacy API, like
replication/subscriptionManager.ts:55). Lives on the mainthread alongside the operations API server (
core/server/operationsServer.ts:40,default port 9925). Worker threads handle application/REST traffic; the agent
stays out of their way.
Request flow (simple — no bridge needed since the ops API is main-thread):
startOnMainThreadregistersagent_prompt(and friends) viaserver.registerOperation. These handlers run directly on the main thread,in the same runtime as the agent loop.
agent_promptappends the user message to the named session and kicks theagent loop (or attaches to the in-flight loop for that session). Returns
{ session_id, message_id }immediately — the loop runs asynchronously onthe JS event loop.
get_agent_sessionto see transcript/status updates. SSEsubscription can come later.
turns are mostly
awaits on LLM/HTTP, so they cooperate cleanly. Per-sessionserialization prevents two prompts in the same session from racing.
Component structure
Add a new top-level directory at the harper-pro root, mirroring
replication/,licensing/,analytics/:Wire it up in
bin/harper.js:5-7:Operations to register
All gated on superuser role. Registered on the main thread in
startOnMainThread, same pattern asinstall_usage_licenseinlicensing/usageLicensing.ts:79-89:agent_prompt{ session_id, message_id }get_agent_sessionlist_agent_sessionscancel_agent_runapprove_agent_actionset_agent_configTool inventory (separate tools, per user's preference)
Models guide their behavior much better with named, scoped tools than with one
"do anything" tool. Each tool below is a separate
tool()definition.@openai/agentsaccepts a Zod schema today; if we want to avoid a new dependency we can pass a
plain JSON Schema object instead (the SDK supports
parametersas JSON Schema).Pick one and stay consistent.
Database / operations
run_operation— callsoperation(body, ctx, true)fromserverUtilities.ts:215.Authorizes as the agent's configured user. Body is any registered operation.
describe_schema,describe_table— pre-bound conveniences (already callablevia
run_operation, but separate tools instruct the model better).search,sql— same rationale.Filesystem
read_file,write_file,apply_patch,list_dir,grep_files—scoped to:
componentsRoot(CONFIG_PARAMS.COMPONENTSROOT), the log dir(
harper_logger.getLogPath()), and the config file dir (configUtils.getConfigFilePath()).No access elsewhere by default.
Logs / observability
read_log— wrapsutility/logging/readLog.get_analytics— already a registered operation; exposed for autonomous tuning.Configuration
get_config,set_config— wrapconfigUtils.getConfiguration/setConfiguration.Gated behind
allowDestructive: true(set_configonly).restart_service— wraps existing restart op for hot-reload after config edits.Gated behind
allowDestructive: true, AND requires approval even whenautoApprove=true(a misfire here bricks a node).Components / build
add_component,package_component,deploy_component,drop_component,install_node_modules— wrap the existing ops.HTTP
http_fetch— outboundfetchfor web research and self-testing apps theagent built (hits its own server via
localhost:<port>). One tool covers both.Debugging (worker threads only — main runs the agent itself; pausing its own
isolate would deadlock)
inspector_attach({ workerIndex })— connects to the CDP endpoint atTHREADS_DEBUG_STARTINGPORT + workerIndex(only available whenthreads_debug=true, seethreadServer.js:25-54). Application code runs inworkers anyway, so this covers the actual use case.
inspector_evaluate,inspector_set_breakpoint,inspector_set_logpoint,inspector_profile_cpu— CDP wrappers via a WS client to the worker's debug port.All reject
workerIndex < 0.Scheduling
schedule_followup({ delayMs, prompt })—setTimeout(...).unref(); onfire, appends a synthetic user message to the session, kicking the loop. This
is what enables periodic/autonomous behavior. Track scheduled timers in-memory
so they can be cancelled.
Configuration schema
New
agentblock inharperdb-config.yaml, validated againstcore/config-root.schema.json:API keys: env vars only, read at startup. Never logged. Model-provider imports
are lazy (only the chosen provider is loaded) so the rest stay
dead-code-eliminated.
Session storage
New system table
system.hdb_agent_sessionkeyed by session_id, holding anordered array of items conforming to
@openai/agents'sAgentInputItem, plusa
pendingApprovals: ApprovalRequest[]field used by the approval flow(
approve_agent_actionresolves entries here). Mirrors the structure ofharper-agent'sDiskSession(JSON file) but uses Harper's own LMDB/Rocksstorage. Implement
CombinedSessioninterface fromharper-agent/lifecycle/session.tsagainst the table so the existingrunAgentForOnePass.tsflow drops in unchanged.Permission / auth model
agent_prompt(and friends) require superuser — enforced bythe same pattern licensing uses (the operations layer's auth covers it; we
just don't set
bypass_auth).hdb_agentsuperuser created at startup if missing. Operators can swap to arestricted role to limit blast radius (e.g. read-only role for an analytics
agent).
Self-debugging support
threadServer.js:30-62opens the V8 inspector onTHREADS_DEBUG_PORT(main)or
THREADS_DEBUG_STARTINGPORT + workerIndex(workers) whenTHREADS_DEBUG=true. The agent'sinspector_*tools speak CDP over WS tothose ports — same as how Chrome DevTools attaches. The user should be told to
set
threads_debug: truein config (and a sensible starting port) beforeasking the agent to debug.
Key dependencies to add
To
harper-pro/package.json:@openai/agents(core loop, interruptions, session abstraction)ai+@ai-sdk/openai+@ai-sdk/anthropic+@ai-sdk/google(provider abstraction)ollama-ai-provider-v2(optional, can be optionalDependency)zod(tool schemas)These add ~several MB to the install. Consider making the whole agent component
an optional dependency or a separate npm package (
@harperfast/server-agent)loaded only when
agent.enabled=true. Recommended: separate package, loadeddynamically. That keeps the Harper baseline lean and aligns with the
licensing/replication style.
Resolved decisions
operationsServer.ts:40); registerdirectly there, no bridge.
get_agent_session(SSE/streaming is a later add).approve_agent_actionoperation (option b).
loop. Per-session serialization only.
allowDestructive: trueflag required to enableconfig-mutating / restart tools.
workerIndex < 0.harper-pro/agent/.Extract a shared package later if it earns its weight.
Remaining open questions
harper-agent/utils/sessions/cost.tsfor hard capsvia
maxCostUsd; emitnotify-level log when approaching the cap. Confirmthat's enough vs. needing a per-session cap too.
@openai/agents+@ai-sdk/*to harper-pro is ameaningful install-size hit. Recommend shipping as a separate package
(
@harperfast/server-agent) loaded only whenagent.enabled=true. Confirmor override.
@openai/agentsand givesus free runtime validation; plain JSON Schema avoids the dep. Mild preference
for Zod since it's already transitively present via
harper-agent. Confirm.Critical files to read / modify
Modify:
/home/kzyp/dev/harper-pro/bin/harper.js— addagent=@/dist/agent/agent.jsto
HARPER_BUILTIN_COMPONENTS./home/kzyp/dev/harper-pro/package.json— add dependencies./home/kzyp/dev/harper-pro/core/config-root.schema.json— addagentblock schema./home/kzyp/dev/harper-pro/core/utility/hdbTerms.ts— add config param constantsOPERATIONS_ENUMentries foragent_prompt,get_agent_session, etc.Create:
/home/kzyp/dev/harper-pro/agent/(full new directory).Read & reuse:
harper-agent/agent/runAgentForOnePass.ts— the loop. Largely copyable.harper-agent/lifecycle/getModel.ts— provider wrapping. Copyable.harper-agent/utils/sessions/cost.ts,rateLimits.ts— cost/rate tracking. Copyable.harper-agent/tools/files/*,harper-agent/tools/general/shellTool.ts— base FS/shell tools.harper-pro/licensing/usageLicensing.ts— exemplar for the componentshape (handleApplication, registerOperation, system table reads).
Verification
npm run buildin harper-pro, start a server with thenew env var; check logs for
Agent component initialized. No-op ifagent.enabled=false.curl -X POST /agent_prompt -d '{"message":"describe the system schema"}'→ returns
session_id→curl /get_agent_session/<id>shows the agentcalled
describe_schemaand produced a response.'hi'", confirm a component file appears, then call
localhost:9926/Helloto confirm the agent's app works.
debug any failures, repeat until working" — verifies
http_fetch+ toolchaining.
threads_debug=true, prompt "find why the Foo resourcethrows on POST" — should attach to inspector, set a logpoint, observe a
request, report root cause.
cluster status and alert me if any node is down" — verifies
schedule_followup+ persistence.read_file('/etc/passwd')is rejected;confirm non-superuser callers of
agent_promptget 403.harper-pro/integrationTests/agent/with astub provider (or recorded fixtures) so CI doesn't burn LLM credits.
Beta Was this translation helpful? Give feedback.
All reactions