talon-agent 4.6.1 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +2 -1
- package/prompts/README.md +2 -2
- package/prompts/system/memory-recall.md +16 -0
- package/src/app.ts +22 -7
- package/src/backend/claude-sdk/factory.ts +21 -41
- package/src/backend/claude-sdk/handler.ts +28 -7
- package/src/backend/claude-sdk/host/in-process.ts +147 -0
- package/src/backend/claude-sdk/one-shot.ts +1 -1
- package/src/backend/claude-sdk/options.ts +7 -6
- package/src/backend/claude-sdk/stream.ts +30 -3
- package/src/backend/claude-sdk/warm.ts +1 -1
- package/src/backend/codex/constants.ts +1 -1
- package/src/backend/codex/factory.ts +2 -2
- package/src/backend/codex/handler/events.ts +1 -1
- package/src/backend/codex/handler/message.ts +26 -12
- package/src/backend/codex/handler/rollout-accounting.ts +1 -1
- package/src/backend/codex/init.ts +1 -1
- package/src/backend/codex/mcp-config.ts +2 -2
- package/src/backend/codex/one-shot.ts +1 -1
- package/src/backend/kilo/handler/message.ts +4 -1
- package/src/backend/openai-agents/constants.ts +1 -1
- package/src/backend/openai-agents/factory.ts +2 -2
- package/src/backend/openai-agents/handler/events.ts +1 -1
- package/src/backend/openai-agents/handler/message.ts +8 -4
- package/src/backend/openai-agents/init.ts +1 -1
- package/src/backend/openai-agents/mcp-pool.ts +2 -2
- package/src/backend/opencode/handler/message.ts +4 -1
- package/src/backend/remote-server/chat-turn.ts +31 -23
- package/src/backend/remote-server/events.ts +3 -3
- package/src/backend/remote-server/factory.ts +6 -3
- package/src/backend/remote-server/index.ts +1 -1
- package/src/backend/remote-server/mcp.ts +1 -1
- package/src/backend/remote-server/one-shot.ts +1 -1
- package/src/backend/remote-server/server-bindings.ts +1 -1
- package/src/backend/remote-server/turn.ts +1 -1
- package/src/backend/runtime/cache/cache-metrics.ts +126 -0
- package/src/backend/{shared → runtime/cache}/cache-telemetry.ts +22 -2
- package/src/backend/{shared → runtime}/index.ts +34 -25
- package/src/backend/{shared → runtime/prompt}/delivery-contract.ts +1 -1
- package/src/backend/{shared → runtime/prompt}/prompt-format.ts +46 -2
- package/src/backend/{shared → runtime/prompt}/system-prompt.ts +4 -4
- package/src/backend/{shared → runtime/turn}/delivered-text.ts +1 -1
- package/src/backend/{shared → runtime/turn}/delivery.ts +3 -3
- package/src/backend/{shared → runtime/turn}/handle-retry.ts +5 -5
- package/src/backend/{shared → runtime/turn}/handler-to-events.ts +24 -10
- package/src/backend/{shared → runtime/turn}/handler-types.ts +7 -1
- package/src/backend/{shared → runtime/turn}/model-retry.ts +2 -2
- package/src/backend/{shared → runtime/turn}/result-events.ts +2 -2
- package/src/backend/{shared → runtime/turn}/stream-state.ts +2 -2
- package/src/backend/{shared → runtime/turn}/turn-interrupt.ts +2 -2
- package/src/backend/{shared → runtime/turn}/turn-phases.ts +6 -6
- package/src/bootstrap.ts +6 -21
- package/src/cli.ts +6 -3
- package/src/core/agent-runtime/README.md +15 -0
- package/src/core/agent-runtime/agent-host.ts +387 -0
- package/src/core/agent-runtime/capabilities.ts +11 -0
- package/src/core/agent-runtime/contract-tests.ts +92 -1
- package/src/core/agent-runtime/events.ts +1 -1
- package/src/core/background/{isolated-agent.ts → cron/isolated-agent.ts} +3 -3
- package/src/core/background/{job-health.ts → cron/job-health.ts} +1 -1
- package/src/core/background/{job-oneshot.ts → cron/job-oneshot.ts} +5 -5
- package/src/core/background/{job-prompt.ts → cron/job-prompt.ts} +1 -1
- package/src/core/background/{cron.ts → cron/scheduler.ts} +6 -6
- package/src/core/background/{cron-spec.ts → cron/spec.ts} +1 -1
- package/src/core/background/{dream.ts → dream/index.ts} +10 -15
- package/src/core/background/{plan-alerts.ts → pulse/plan-alerts.ts} +3 -3
- package/src/core/background/{pulse.ts → pulse/pulse.ts} +8 -5
- package/src/core/background/triggers/command.ts +2 -2
- package/src/{util → core/config}/harden.ts +1 -1
- package/src/core/config/index.ts +16 -12
- package/src/{util → core/daemon}/boot-timer.ts +15 -1
- package/src/core/daemon/pidfile.ts +1 -1
- package/src/core/daemon/resource-sampler.ts +121 -0
- package/src/{util → core/daemon}/respawn.ts +1 -1
- package/src/core/doctor/index.ts +1 -1
- package/src/core/engine/dispatcher.ts +16 -0
- package/src/core/engine/gateway-actions/cron.ts +2 -2
- package/src/core/engine/gateway-actions/fetch-url.ts +1 -1
- package/src/core/engine/gateway-actions/index.ts +4 -1
- package/src/core/engine/gateway-actions/memory.ts +335 -0
- package/src/core/engine/gateway-actions/native/exec-background.ts +129 -0
- package/src/core/engine/gateway-actions/native/exec-remote.ts +85 -0
- package/src/core/engine/gateway-actions/native/exec.ts +182 -0
- package/src/core/engine/gateway-actions/native/index.ts +46 -0
- package/src/core/engine/gateway-actions/native/params.ts +46 -0
- package/src/core/engine/gateway-actions/native/read.ts +182 -0
- package/src/core/engine/gateway-actions/native/results.ts +34 -0
- package/src/core/engine/gateway-actions/native/search.ts +212 -0
- package/src/core/engine/gateway-actions/native/shell.ts +39 -0
- package/src/core/engine/gateway-actions/native/teleport.ts +55 -0
- package/src/core/engine/gateway-actions/native/write.ts +170 -0
- package/src/core/engine/gateway.ts +0 -6
- package/src/core/frontend-runtime/builtins.ts +2 -2
- package/src/{util → core/frontend-runtime}/chat-id.ts +30 -0
- package/src/core/mcp-hub/index.ts +1 -1
- package/src/core/mcp-hub/talon-server.ts +1 -1
- package/src/core/memory/import.ts +3 -2
- package/src/core/memory/taps.ts +199 -0
- package/src/core/memory/turn-retrieval.ts +222 -0
- package/src/core/plugin/mcp.ts +1 -1
- package/src/core/prompt/assemble.ts +2 -12
- package/src/core/prompt/index.ts +2 -2
- package/src/core/prompt/invalidation.ts +1 -1
- package/src/core/{scripting/lua-runner.ts → scripts/lua.ts} +1 -1
- package/src/core/tasks/index.ts +1 -1
- package/src/core/tools/{chat.ts → chat/chat.ts} +1 -1
- package/src/core/tools/{cross-send.ts → chat/cross-send.ts} +1 -1
- package/src/core/tools/{history.ts → chat/history.ts} +2 -2
- package/src/core/tools/{media.ts → chat/media.ts} +1 -1
- package/src/core/tools/{members.ts → chat/members.ts} +2 -2
- package/src/core/tools/{messaging.ts → chat/messaging.ts} +2 -2
- package/src/core/tools/{moderation.ts → chat/moderation.ts} +2 -2
- package/src/core/tools/{stickers.ts → chat/stickers.ts} +2 -2
- package/src/core/tools/{whatsapp.ts → chat/whatsapp.ts} +1 -1
- package/src/core/tools/content/memory.ts +128 -0
- package/src/core/tools/{web.ts → content/web.ts} +1 -1
- package/src/core/tools/index.ts +21 -19
- package/src/core/tools/{admin.ts → ops/admin.ts} +1 -1
- package/src/core/tools/{bridge.ts → ops/bridge.ts} +2 -2
- package/src/core/tools/{goals.ts → ops/goals.ts} +2 -2
- package/src/core/tools/{mesh.ts → ops/mesh.ts} +1 -1
- package/src/core/tools/{models.ts → ops/models.ts} +1 -1
- package/src/core/tools/{native.ts → ops/native.ts} +1 -1
- package/src/core/tools/{scheduling.ts → ops/scheduling.ts} +1 -1
- package/src/core/tools/{scripts.ts → ops/scripts.ts} +1 -1
- package/src/core/tools/{skills.ts → ops/skills.ts} +1 -1
- package/src/core/tools/{triggers.ts → ops/triggers.ts} +1 -1
- package/src/core/tools/types.ts +1 -0
- package/src/{util → core/vfs}/workspace.ts +2 -2
- package/src/core/weaver/turn-cpu.ts +32 -0
- package/src/core/weaver/weaver.ts +36 -0
- package/src/frontend/discord/admin.ts +1 -1
- package/src/frontend/discord/callbacks/components/backend-select.ts +1 -1
- package/src/frontend/discord/callbacks/components/effort.ts +1 -1
- package/src/frontend/discord/callbacks/components/index.ts +1 -1
- package/src/frontend/discord/callbacks/components/pulse.ts +1 -1
- package/src/frontend/discord/callbacks/components/settings.ts +2 -2
- package/src/frontend/discord/callbacks/modals.ts +4 -1
- package/src/frontend/discord/commands/admin.ts +2 -36
- package/src/frontend/discord/commands/definitions.ts +0 -11
- package/src/frontend/discord/commands/info.ts +1 -1
- package/src/frontend/discord/commands/interaction.ts +1 -1
- package/src/frontend/discord/commands/router.ts +0 -3
- package/src/frontend/discord/commands/session.ts +8 -2
- package/src/frontend/discord/commands/settings.ts +2 -2
- package/src/frontend/discord/handlers/messages.ts +1 -1
- package/src/frontend/discord/handlers/state.ts +1 -1
- package/src/frontend/discord/middleware.ts +2 -26
- package/src/frontend/discord/ready.ts +1 -1
- package/src/frontend/discord/render.ts +41 -259
- package/src/frontend/discord/runtime.ts +1 -2
- package/src/frontend/native/{auth.ts → bridge/auth.ts} +2 -2
- package/src/frontend/native/{discovery.ts → bridge/discovery.ts} +3 -3
- package/src/frontend/native/{routes → bridge/routes}/chats.ts +1 -1
- package/src/frontend/native/{routes → bridge/routes}/daemon.ts +1 -1
- package/src/frontend/native/{routes → bridge/routes}/host.ts +6 -3
- package/src/frontend/native/{routes → bridge/routes}/pre-auth.ts +1 -1
- package/src/frontend/native/{server.ts → bridge/server.ts} +3 -3
- package/src/frontend/native/{tls.ts → bridge/tls.ts} +2 -2
- package/src/frontend/native/{chat-lifecycle.ts → chats/chat-lifecycle.ts} +3 -3
- package/src/frontend/native/{chat-wire.ts → chats/chat-wire.ts} +4 -4
- package/src/frontend/native/{chats.ts → chats/chats.ts} +4 -4
- package/src/frontend/native/{empty-chat-sweep.ts → chats/empty-chat-sweep.ts} +4 -4
- package/src/frontend/native/{history.ts → chats/history.ts} +7 -7
- package/src/frontend/native/{reset.ts → chats/reset.ts} +7 -7
- package/src/frontend/native/index.ts +13 -10
- package/src/frontend/native/{media.ts → media/media.ts} +3 -3
- package/src/frontend/native/runtime.ts +1 -1
- package/src/frontend/native/{control.ts → surface/control.ts} +4 -4
- package/src/frontend/native/{extensions.ts → surface/extensions.ts} +12 -9
- package/src/frontend/native/{handlers.ts → surface/handlers.ts} +19 -14
- package/src/frontend/native/{logs.ts → surface/logs.ts} +2 -2
- package/src/frontend/native/{memory.ts → surface/memory.ts} +2 -2
- package/src/frontend/native/{models.ts → surface/models.ts} +17 -10
- package/src/frontend/native/{settings.ts → surface/settings.ts} +9 -9
- package/src/frontend/native/{status.ts → surface/status.ts} +3 -3
- package/src/frontend/native/{actions.ts → turn/actions.ts} +7 -4
- package/src/frontend/native/{context.ts → turn/context.ts} +8 -8
- package/src/frontend/native/{emit.ts → turn/emit.ts} +7 -7
- package/src/frontend/native/{queue.ts → turn/queue.ts} +3 -3
- package/src/frontend/native/{turn-meta.ts → turn/turn-meta.ts} +2 -2
- package/src/frontend/native/{turn.ts → turn/turn.ts} +9 -9
- package/src/frontend/{shared → presentation}/format.ts +4 -3
- package/src/frontend/{shared → presentation}/model-commands.ts +1 -1
- package/src/frontend/{shared → presentation}/reasoning-levels.ts +12 -0
- package/src/frontend/presentation/reports.ts +493 -0
- package/src/frontend/{shared → presentation}/session-status.ts +36 -9
- package/src/frontend/{shared → presentation}/status-context.ts +101 -2
- package/src/frontend/teams/commands.ts +2 -2
- package/src/frontend/teams/turn.ts +1 -1
- package/src/frontend/telegram/admin/background.ts +1 -1
- package/src/frontend/telegram/admin/health.ts +1 -1
- package/src/frontend/telegram/admin/sessions.ts +1 -1
- package/src/frontend/telegram/callbacks/effort.ts +2 -2
- package/src/frontend/telegram/callbacks/metrics.ts +1 -1
- package/src/frontend/telegram/callbacks/model/backend.ts +1 -1
- package/src/frontend/telegram/callbacks/model/views.ts +1 -1
- package/src/frontend/telegram/callbacks/pulse.ts +1 -1
- package/src/frontend/telegram/callbacks/query.ts +1 -1
- package/src/frontend/telegram/callbacks/settings.ts +3 -3
- package/src/frontend/telegram/commands/admin.ts +6 -41
- package/src/frontend/telegram/commands/index.ts +1 -1
- package/src/frontend/telegram/commands/info.ts +2 -2
- package/src/frontend/telegram/commands/session.ts +9 -3
- package/src/frontend/telegram/commands/settings.ts +5 -3
- package/src/frontend/telegram/handlers/messages.ts +0 -10
- package/src/frontend/telegram/handlers/state.ts +1 -1
- package/src/frontend/telegram/index.ts +1 -5
- package/src/frontend/telegram/middleware.ts +1 -15
- package/src/frontend/telegram/model-menu.ts +1 -1
- package/src/frontend/telegram/render/html.ts +30 -0
- package/src/frontend/telegram/{helpers → render}/menu.ts +46 -19
- package/src/frontend/telegram/render/reports.ts +182 -0
- package/src/frontend/terminal/builtins/context.ts +2 -2
- package/src/frontend/terminal/builtins/session.ts +1 -1
- package/src/frontend/terminal/builtins/status.ts +2 -2
- package/src/frontend/terminal/index.ts +2 -2
- package/src/frontend/terminal/renderer.ts +1 -1
- package/src/frontend/whatsapp/access.ts +2 -2
- package/src/frontend/whatsapp/actions/history.ts +2 -2
- package/src/frontend/whatsapp/actions/messaging.ts +2 -2
- package/src/frontend/whatsapp/actions/moderation.ts +1 -1
- package/src/frontend/whatsapp/actions/send.ts +1 -1
- package/src/frontend/whatsapp/commands.ts +12 -6
- package/src/frontend/whatsapp/{connection.ts → connection/connection.ts} +5 -5
- package/src/frontend/whatsapp/{pairing-service.ts → connection/pairing-service.ts} +3 -3
- package/src/frontend/whatsapp/{wa-logger.ts → connection/wa-logger.ts} +2 -2
- package/src/frontend/whatsapp/index.ts +4 -4
- package/src/frontend/whatsapp/{inbound.ts → messages/inbound.ts} +15 -15
- package/src/frontend/whatsapp/{media-store.ts → messages/media-store.ts} +4 -4
- package/src/frontend/whatsapp/{message-store.ts → messages/message-store.ts} +1 -1
- package/src/frontend/whatsapp/{turn-recovery.ts → messages/turn-recovery.ts} +4 -4
- package/src/frontend/whatsapp/registry.ts +2 -2
- package/src/frontend/whatsapp/runtime.ts +1 -1
- package/src/index.ts +6 -3
- package/src/storage/db.ts +6 -1
- package/src/storage/memory.ts +59 -8
- package/src/storage/metrics.ts +38 -0
- package/src/storage/repositories/sessions-repo.ts +6 -0
- package/src/storage/session-record.ts +13 -2
- package/src/storage/sessions.ts +12 -0
- package/src/storage/sql/db.sql +5 -0
- package/src/storage/sql/schema.sql +4 -0
- package/src/storage/sql/sessions.sql +3 -3
- package/src/storage/sql/statements.generated.ts +10 -3
- package/src/storage/sql/turn-meta.sql +1 -1
- package/src/util/log.ts +1 -1
- package/src/util/paths.ts +0 -2
- package/src/util/runtime.ts +1 -1
- package/src/core/engine/gateway-actions/native.ts +0 -1035
- package/src/core/soul/README.md +0 -110
- package/src/core/soul/RESEARCH.md +0 -98
- package/src/core/soul/associative.ts +0 -98
- package/src/core/soul/centrality.ts +0 -98
- package/src/core/soul/cluster.ts +0 -83
- package/src/core/soul/compiler.ts +0 -207
- package/src/core/soul/consolidate.ts +0 -179
- package/src/core/soul/critic.ts +0 -162
- package/src/core/soul/dag.ts +0 -265
- package/src/core/soul/delta.ts +0 -123
- package/src/core/soul/drift.ts +0 -99
- package/src/core/soul/embedder.ts +0 -129
- package/src/core/soul/emergent-critic.ts +0 -96
- package/src/core/soul/forgetting.ts +0 -131
- package/src/core/soul/governance.ts +0 -93
- package/src/core/soul/hash.ts +0 -97
- package/src/core/soul/hdc.ts +0 -154
- package/src/core/soul/kernel.ts +0 -540
- package/src/core/soul/lattice.ts +0 -103
- package/src/core/soul/lens.ts +0 -110
- package/src/core/soul/projector.ts +0 -240
- package/src/core/soul/reflect.ts +0 -170
- package/src/core/soul/reflex.ts +0 -164
- package/src/core/soul/retrieve.ts +0 -146
- package/src/core/soul/salience.ts +0 -146
- package/src/core/soul/service.ts +0 -204
- package/src/core/soul/settings.ts +0 -47
- package/src/core/soul/signals.ts +0 -117
- package/src/core/soul/talon-embedder.ts +0 -80
- package/src/core/soul/taps.ts +0 -199
- package/src/core/soul/types.ts +0 -298
- package/src/core/soul/valence.ts +0 -83
- package/src/frontend/telegram/helpers/diagnostics.ts +0 -428
- package/src/frontend/telegram/helpers/format.ts +0 -42
- package/src/frontend/telegram/helpers/index.ts +0 -13
- package/src/util/cleanup-registry.ts +0 -36
- /package/src/backend/{shared → runtime}/frontends.ts +0 -0
- /package/src/backend/{shared → runtime}/metrics.ts +0 -0
- /package/src/backend/{shared → runtime}/sleep.ts +0 -0
- /package/src/backend/{shared → runtime/turn}/flow-violation.ts +0 -0
- /package/src/backend/{shared → runtime}/usage.ts +0 -0
- /package/src/{util/mcp-launcher.ts → core/mcp-hub/launcher.ts} +0 -0
- /package/src/{util → core/tools/content}/web-content.ts +0 -0
- /package/src/core/tools/{mcp-env.ts → ops/mcp-env.ts} +0 -0
- /package/src/{util → core/weaver}/session-name.ts +0 -0
- /package/src/frontend/native/{routes → bridge/routes}/index.ts +0 -0
- /package/src/frontend/native/{routes → bridge/routes}/memory.ts +0 -0
- /package/src/frontend/native/{routes → bridge/routes}/mesh.ts +0 -0
- /package/src/frontend/native/{routes → bridge/routes}/models.ts +0 -0
- /package/src/frontend/native/{routes → bridge/routes}/params.ts +0 -0
- /package/src/frontend/native/{routes → bridge/routes}/table.ts +0 -0
- /package/src/frontend/native/{tool-result.ts → turn/tool-result.ts} +0 -0
- /package/src/frontend/{shared → presentation}/access.ts +0 -0
- /package/src/frontend/{shared → presentation}/plan-usage-report.ts +0 -0
- /package/src/frontend/whatsapp/{auth-state.ts → connection/auth-state.ts} +0 -0
- /package/src/frontend/whatsapp/{identity.ts → connection/identity.ts} +0 -0
- /package/src/frontend/whatsapp/{pairing-lock.ts → connection/pairing-lock.ts} +0 -0
- /package/src/frontend/whatsapp/{pairing.ts → connection/pairing.ts} +0 -0
- /package/src/frontend/whatsapp/{pins.ts → messages/pins.ts} +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-host seam — the contract between the daemon and the process
|
|
3
|
+
* that hosts the Claude Agent SDK.
|
|
4
|
+
*
|
|
5
|
+
* `docs/agent-host-sidecar.md` Phase 1. Today the SDK runs inside the
|
|
6
|
+
* daemon: an OOM or an SDK bug takes down every frontend with it. The
|
|
7
|
+
* sidecar moves it into its own process, and this file is the boundary
|
|
8
|
+
* that move happens across — written first, on purpose, so Phase 2 is a
|
|
9
|
+
* transport swap rather than a redesign.
|
|
10
|
+
*
|
|
11
|
+
* Two halves:
|
|
12
|
+
*
|
|
13
|
+
* - **The wire vocabulary.** `HostRequest` / `HostReply` / `HostEvent` /
|
|
14
|
+
* `HostNotice` are the NDJSON messages of the design's protocol
|
|
15
|
+
* table, one TypeScript type per row, plus `parseHostMessage` /
|
|
16
|
+
* `serializeHostMessage` — the codec both sides run, replayed against
|
|
17
|
+
* `protocol/fixtures/agent-host_v1.json` in
|
|
18
|
+
* `src/__tests__/agent-host-protocol.test.ts`.
|
|
19
|
+
* - **The client.** `AgentHostClient` is what the daemon holds. Phase 1
|
|
20
|
+
* ships one implementation (`backend/claude-sdk/host/in-process.ts`,
|
|
21
|
+
* a direct call-through); Phase 2 adds a second that speaks the
|
|
22
|
+
* messages above over a child process's stdio. The `Backend` object
|
|
23
|
+
* the rest of the daemon sees is identical either way.
|
|
24
|
+
*
|
|
25
|
+
* Layering: the interface and the codec live in `core/` because `core/`
|
|
26
|
+
* may not import `backend/` (depcruise `core-not-to-backend`). Every
|
|
27
|
+
* implementation lives under `backend/`.
|
|
28
|
+
*
|
|
29
|
+
* Wire types vs client types
|
|
30
|
+
* ──────────────────────────
|
|
31
|
+
* They are deliberately not the same types. Two client arguments do not
|
|
32
|
+
* survive a process boundary and the wire shapes say so:
|
|
33
|
+
*
|
|
34
|
+
* - `OneShotAgentParams` carries an `AbortController` and an
|
|
35
|
+
* `appendLog` callback. `HostOneShotParams` is the serialisable
|
|
36
|
+
* subset; Phase 2 maps `appendLog` onto `log` notices and the
|
|
37
|
+
* abort onto an `interrupt`-shaped request.
|
|
38
|
+
* - `hello.config` is the `claude-sdk` slice of `TalonConfig` as
|
|
39
|
+
* JSON. The in-process client takes the real `TalonConfig` object.
|
|
40
|
+
*
|
|
41
|
+
* Forward compatibility: unknown fields on a known message type are
|
|
42
|
+
* additive evolution and must be preserved, never rejected. An unknown
|
|
43
|
+
* `type` parses to `HostUnknown`, which callers log and drop — the codec
|
|
44
|
+
* never throws, so one bad line can't kill a turn or the loop reading it.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import type { AgentEvent, AgentError } from "./events.js";
|
|
48
|
+
import type { ChatRunParams, PlanUsage } from "./capabilities.js";
|
|
49
|
+
import type {
|
|
50
|
+
OneShotAgentParams,
|
|
51
|
+
OneShotUsage,
|
|
52
|
+
ReasoningEffortLevel,
|
|
53
|
+
UnifiedModelInfo,
|
|
54
|
+
} from "../types.js";
|
|
55
|
+
|
|
56
|
+
/** Wire-format version. Bump only on a breaking change — prefer additive. */
|
|
57
|
+
export const AGENT_HOST_PROTOCOL_VERSION = 1;
|
|
58
|
+
|
|
59
|
+
// ── Shared payload shapes ───────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The serialisable half of `OneShotAgentParams`. `abortController` and
|
|
63
|
+
* `appendLog` are host-local concerns (see the file header); everything
|
|
64
|
+
* else is exactly what a background run needs.
|
|
65
|
+
*
|
|
66
|
+
* Unexported on purpose — it is reachable as
|
|
67
|
+
* `Extract<HostRequest, { type: "one_shot" }>["params"]`, and a second
|
|
68
|
+
* name for the same shape is a thing to keep in sync for nothing.
|
|
69
|
+
*/
|
|
70
|
+
interface HostOneShotParams {
|
|
71
|
+
prompt: string;
|
|
72
|
+
systemPrompt: string;
|
|
73
|
+
workspace: string;
|
|
74
|
+
model: string;
|
|
75
|
+
reasoningEffort?: ReasoningEffortLevel;
|
|
76
|
+
contextLabel: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The MCP diff `set_mcp_servers` / `refresh_tools` answer with — the same
|
|
81
|
+
* shape `ToolRuntime.refreshTools` returns to the dispatcher today.
|
|
82
|
+
*/
|
|
83
|
+
export interface HostToolRefresh {
|
|
84
|
+
added: string[];
|
|
85
|
+
removed: string[];
|
|
86
|
+
errors: Record<string, string>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* What the host knows about one chat's session. `sessionId` is the SDK's
|
|
91
|
+
* resume handle; the context figures are the ones `warm_session` populates
|
|
92
|
+
* — which is why this query exists at all. In-process those numbers land
|
|
93
|
+
* in the daemon's own session store; across a process boundary they have
|
|
94
|
+
* to be asked for.
|
|
95
|
+
*/
|
|
96
|
+
export interface HostSessionInfo {
|
|
97
|
+
chatId: string;
|
|
98
|
+
sessionId?: string;
|
|
99
|
+
turns: number;
|
|
100
|
+
contextTokens: number;
|
|
101
|
+
contextWindow: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** `ready`'s payload — the handshake answer, minus the envelope. */
|
|
105
|
+
export interface HostReadyInfo {
|
|
106
|
+
protocol: number;
|
|
107
|
+
/** The host build's version. */
|
|
108
|
+
host: string;
|
|
109
|
+
/**
|
|
110
|
+
* The Claude Agent SDK version the host is running. Absent in-process,
|
|
111
|
+
* where there is no separately-pinned SDK to report (Phase 4 gives the
|
|
112
|
+
* host its own `package.json`).
|
|
113
|
+
*/
|
|
114
|
+
sdk?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Daemon → host ───────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Every request the daemon can send. Each carries an `id`; the reply
|
|
121
|
+
* carries the same `id`. Turn-shaped requests (`run_turn`, `one_shot`)
|
|
122
|
+
* additionally carry a `runId`, which every streamed `event` repeats.
|
|
123
|
+
*/
|
|
124
|
+
export type HostRequest =
|
|
125
|
+
| {
|
|
126
|
+
type: "hello";
|
|
127
|
+
id: string;
|
|
128
|
+
protocol: number;
|
|
129
|
+
/** The daemon's version, for the host's compatibility log line. */
|
|
130
|
+
daemon: string;
|
|
131
|
+
/** The `claude-sdk` slice of `TalonConfig`, as JSON. */
|
|
132
|
+
config: Record<string, unknown>;
|
|
133
|
+
}
|
|
134
|
+
| { type: "run_turn"; id: string; runId: string; params: ChatRunParams }
|
|
135
|
+
| { type: "interrupt"; id: string; chatId: string }
|
|
136
|
+
| { type: "one_shot"; id: string; runId: string; params: HostOneShotParams }
|
|
137
|
+
| { type: "warm_session"; id: string; chatId: string }
|
|
138
|
+
| {
|
|
139
|
+
type: "set_mcp_servers";
|
|
140
|
+
id: string;
|
|
141
|
+
chatId: string;
|
|
142
|
+
/** SDK `McpServerConfig` map, opaque here — the host hands it to the SDK. */
|
|
143
|
+
servers: Record<string, unknown>;
|
|
144
|
+
}
|
|
145
|
+
| { type: "refresh_tools"; id: string; chatId: string }
|
|
146
|
+
| { type: "list_models"; id: string; filter?: "free" | "all" }
|
|
147
|
+
| { type: "plan_usage"; id: string }
|
|
148
|
+
| { type: "session_info"; id: string; chatId: string }
|
|
149
|
+
| { type: "reset_session"; id: string; chatId: string }
|
|
150
|
+
| { type: "shutdown"; id: string };
|
|
151
|
+
|
|
152
|
+
// ── Host → daemon ───────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Every reply. `ok` is the generic ack, with one optional field per
|
|
156
|
+
* request that has something to say back; the queries get their own
|
|
157
|
+
* types so a reply is never ambiguous with the request that asked for it
|
|
158
|
+
* (`list_models` → `models`, `plan_usage` → `usage`, `session_info` →
|
|
159
|
+
* `session`).
|
|
160
|
+
*/
|
|
161
|
+
export type HostReply =
|
|
162
|
+
| ({ type: "ready"; id: string } & HostReadyInfo)
|
|
163
|
+
| {
|
|
164
|
+
type: "ok";
|
|
165
|
+
id: string;
|
|
166
|
+
/** `interrupt` — a running turn was found and signalled. */
|
|
167
|
+
interrupted?: boolean;
|
|
168
|
+
/** `set_mcp_servers` / `refresh_tools` — `null` when the chat has no live query. */
|
|
169
|
+
tools?: HostToolRefresh | null;
|
|
170
|
+
/** `reset_session` — host-side per-chat state was dropped. */
|
|
171
|
+
cleared?: boolean;
|
|
172
|
+
}
|
|
173
|
+
| { type: "run_done"; id: string; runId: string; usage?: OneShotUsage }
|
|
174
|
+
| { type: "error"; id: string; error: AgentError }
|
|
175
|
+
| { type: "models"; id: string; models: UnifiedModelInfo[]; total: number }
|
|
176
|
+
| { type: "usage"; id: string; usage?: PlanUsage }
|
|
177
|
+
| { type: "session"; id: string; session?: HostSessionInfo }
|
|
178
|
+
| { type: "bye"; id: string };
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* One turn event. The `AgentEvent` union is unchanged and unwrapped —
|
|
182
|
+
* that is the whole point of the seam: the daemon's consumers switch on
|
|
183
|
+
* `event.type` exactly as they do against an in-process backend.
|
|
184
|
+
*/
|
|
185
|
+
export interface HostEvent {
|
|
186
|
+
type: "event";
|
|
187
|
+
runId: string;
|
|
188
|
+
event: AgentEvent;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Unsolicited host → daemon traffic, carrying no `id` because nothing
|
|
193
|
+
* asked for it. `log` is the host's stdout logging (stderr is tailed by
|
|
194
|
+
* the supervisor like an MCP child's); `metric` forwards the `cache.*`
|
|
195
|
+
* and turn counters the host records so the daemon's rollups are
|
|
196
|
+
* unchanged by the move.
|
|
197
|
+
*/
|
|
198
|
+
export type HostNotice =
|
|
199
|
+
| {
|
|
200
|
+
type: "log";
|
|
201
|
+
level: "debug" | "info" | "warn" | "error";
|
|
202
|
+
component: string;
|
|
203
|
+
msg: string;
|
|
204
|
+
}
|
|
205
|
+
| { type: "metric"; name: string; value: number };
|
|
206
|
+
|
|
207
|
+
/** Anything that can appear on the wire, either direction. */
|
|
208
|
+
export type HostMessage = HostRequest | HostReply | HostEvent | HostNotice;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* A line the codec could not place. Never thrown — returned, so the
|
|
212
|
+
* reader logs it and drops it. `raw` is whatever came off the wire so
|
|
213
|
+
* the log can say what was skipped.
|
|
214
|
+
*/
|
|
215
|
+
export interface HostUnknown {
|
|
216
|
+
type: "unknown";
|
|
217
|
+
reason: "malformed_json" | "not_an_object" | "unknown_type";
|
|
218
|
+
raw: unknown;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── The type registry the codec discriminates on ────────────────────────────
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* `satisfies` rejects typos here; the `AssertNever` checks in
|
|
225
|
+
* `src/__tests__/agent-host-protocol.test.ts` fail to compile when a new
|
|
226
|
+
* member joins a union without being listed — which forces a fixture
|
|
227
|
+
* sample too, because the fixture test asserts these lists exactly.
|
|
228
|
+
*/
|
|
229
|
+
export const HOST_REQUEST_TYPES = [
|
|
230
|
+
"hello",
|
|
231
|
+
"run_turn",
|
|
232
|
+
"interrupt",
|
|
233
|
+
"one_shot",
|
|
234
|
+
"warm_session",
|
|
235
|
+
"set_mcp_servers",
|
|
236
|
+
"refresh_tools",
|
|
237
|
+
"list_models",
|
|
238
|
+
"plan_usage",
|
|
239
|
+
"session_info",
|
|
240
|
+
"reset_session",
|
|
241
|
+
"shutdown",
|
|
242
|
+
] as const satisfies readonly HostRequest["type"][];
|
|
243
|
+
|
|
244
|
+
export const HOST_REPLY_TYPES = [
|
|
245
|
+
"ready",
|
|
246
|
+
"ok",
|
|
247
|
+
"run_done",
|
|
248
|
+
"error",
|
|
249
|
+
"models",
|
|
250
|
+
"usage",
|
|
251
|
+
"session",
|
|
252
|
+
"bye",
|
|
253
|
+
] as const satisfies readonly HostReply["type"][];
|
|
254
|
+
|
|
255
|
+
export const HOST_NOTICE_TYPES = [
|
|
256
|
+
"log",
|
|
257
|
+
"metric",
|
|
258
|
+
] as const satisfies readonly HostNotice["type"][];
|
|
259
|
+
|
|
260
|
+
const KNOWN_TYPES: ReadonlySet<string> = new Set<string>([
|
|
261
|
+
...HOST_REQUEST_TYPES,
|
|
262
|
+
...HOST_REPLY_TYPES,
|
|
263
|
+
...HOST_NOTICE_TYPES,
|
|
264
|
+
"event",
|
|
265
|
+
]);
|
|
266
|
+
|
|
267
|
+
// ── Codec ───────────────────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Render one message as its NDJSON line — no trailing newline, so the
|
|
271
|
+
* transport owns the framing. Symmetric with `parseHostMessage`.
|
|
272
|
+
*/
|
|
273
|
+
export function serializeHostMessage(message: HostMessage): string {
|
|
274
|
+
return JSON.stringify(message);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Parse one NDJSON line. Total: malformed JSON, non-objects and unknown
|
|
279
|
+
* `type`s all come back as `HostUnknown` rather than throwing, because a
|
|
280
|
+
* single bad line from a newer peer must not take down the read loop.
|
|
281
|
+
*
|
|
282
|
+
* Unknown FIELDS on a known type are preserved as-is — additive evolution
|
|
283
|
+
* is the protocol's normal path, and dropping them here would silently
|
|
284
|
+
* downgrade a message the other end meant to send.
|
|
285
|
+
*/
|
|
286
|
+
export function parseHostMessage(line: string): HostMessage | HostUnknown {
|
|
287
|
+
let parsed: unknown;
|
|
288
|
+
try {
|
|
289
|
+
parsed = JSON.parse(line);
|
|
290
|
+
} catch {
|
|
291
|
+
return { type: "unknown", reason: "malformed_json", raw: line };
|
|
292
|
+
}
|
|
293
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
294
|
+
return { type: "unknown", reason: "not_an_object", raw: parsed };
|
|
295
|
+
}
|
|
296
|
+
const type = (parsed as { type?: unknown }).type;
|
|
297
|
+
if (typeof type !== "string" || !KNOWN_TYPES.has(type)) {
|
|
298
|
+
return { type: "unknown", reason: "unknown_type", raw: parsed };
|
|
299
|
+
}
|
|
300
|
+
return parsed as HostMessage;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ── The client the daemon holds ─────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* What the daemon calls instead of reaching into `backend/claude-sdk/`
|
|
307
|
+
* directly. One method per protocol-table row, with the signatures the
|
|
308
|
+
* claude-sdk `BackendFactory` actually uses today — no capability the
|
|
309
|
+
* backend does not have is invented here.
|
|
310
|
+
*
|
|
311
|
+
* Bound into the `Backend` object in Phase 1: `hello`, `runTurn`,
|
|
312
|
+
* `interrupt`, `runOneShot`, `warmSession`, `refreshTools`, `listModels`,
|
|
313
|
+
* `planUsage`. Defined-but-unbound: `setMcpServers` (the primitive
|
|
314
|
+
* `refreshTools` is built from), `sessionInfo` and `resetSession` (the
|
|
315
|
+
* Claude SDK backend exposes neither a `getSessionSnapshot` nor a
|
|
316
|
+
* `resetChat` slot, and Phase 1 may not change the `Backend` object), and
|
|
317
|
+
* `shutdown` (nothing to stop until there is a process).
|
|
318
|
+
*/
|
|
319
|
+
export interface AgentHostClient {
|
|
320
|
+
/**
|
|
321
|
+
* Handshake + initialisation. In-process this is `initAgent(config)`;
|
|
322
|
+
* across a process it is `hello` → `ready`. Must resolve before any
|
|
323
|
+
* other call — model discovery happens here.
|
|
324
|
+
*/
|
|
325
|
+
hello(): Promise<HostReadyInfo>;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* One chat turn. The returned stream is the canonical `AgentEvent`
|
|
329
|
+
* sequence, `run_started` first and `completed`/`error` last, exactly
|
|
330
|
+
* as `ChatBackend.runChatTurn` promises.
|
|
331
|
+
*/
|
|
332
|
+
runTurn(params: ChatRunParams): AsyncIterable<AgentEvent>;
|
|
333
|
+
|
|
334
|
+
/** Best-effort stop of a chat's in-flight turn. `true` when one was signalled. */
|
|
335
|
+
interrupt(chatId: string): Promise<boolean>;
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* One background run (heartbeat / dream / cron). Resolves with the
|
|
339
|
+
* run's usage when the SDK reports it.
|
|
340
|
+
*
|
|
341
|
+
* Callback-shaped, not a stream: `OneShotAgentParams.appendLog` is how
|
|
342
|
+
* the background producers write their markdown logs today, and turning
|
|
343
|
+
* that into an event stream would be a behaviour change, not a seam.
|
|
344
|
+
* The design's "stream as above" row is Phase 2's problem, and the wire
|
|
345
|
+
* type (`HostOneShotParams`) already records what has to give.
|
|
346
|
+
*/
|
|
347
|
+
runOneShot(params: OneShotAgentParams): Promise<OneShotUsage | void>;
|
|
348
|
+
|
|
349
|
+
/** Cold-start hint: spawn a throwaway query to prime the context figures. */
|
|
350
|
+
warmSession(chatId: string): Promise<void>;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Install an MCP server set on the chat's live query. `null` when the
|
|
354
|
+
* chat has no query in flight. The primitive `refreshTools` is built
|
|
355
|
+
* from; the two-phase teardown lives in the host, not the caller,
|
|
356
|
+
* because `Query` handles do not cross a process boundary.
|
|
357
|
+
*/
|
|
358
|
+
setMcpServers(
|
|
359
|
+
chatId: string,
|
|
360
|
+
servers: Record<string, unknown>,
|
|
361
|
+
): Promise<HostToolRefresh | null>;
|
|
362
|
+
|
|
363
|
+
/** Re-derive the chat's MCP config from the live plugin registry. */
|
|
364
|
+
refreshTools(chatId: string): Promise<HostToolRefresh | null>;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* The model catalog, as the host discovered it from the SDK. The other
|
|
368
|
+
* seven `ModelCatalog` members are pure daemon-side formatting over
|
|
369
|
+
* `core/models/catalog.ts`, which `hello` populates — see
|
|
370
|
+
* `docs/agent-host-sidecar.md` Phase 1.
|
|
371
|
+
*/
|
|
372
|
+
listModels(
|
|
373
|
+
filter?: "free" | "all",
|
|
374
|
+
): Promise<{ models: UnifiedModelInfo[]; total: number }>;
|
|
375
|
+
|
|
376
|
+
/** Subscription rate-limit windows for `/status`. */
|
|
377
|
+
planUsage(): Promise<PlanUsage | undefined>;
|
|
378
|
+
|
|
379
|
+
/** What the host knows about a chat's session, including the context figures. */
|
|
380
|
+
sessionInfo(chatId: string): Promise<HostSessionInfo | undefined>;
|
|
381
|
+
|
|
382
|
+
/** Drop the host's per-chat state. `true` when there was something to drop. */
|
|
383
|
+
resetSession(chatId: string): Promise<boolean>;
|
|
384
|
+
|
|
385
|
+
/** Drain in-flight turns, then stop. A no-op while the host is in-process. */
|
|
386
|
+
shutdown(): Promise<void>;
|
|
387
|
+
}
|
|
@@ -56,6 +56,17 @@ export interface ChatRunParams {
|
|
|
56
56
|
isGroup?: boolean;
|
|
57
57
|
/** Provider message ID. Telegram is numeric; Discord snowflakes are strings. */
|
|
58
58
|
messageId?: number | string;
|
|
59
|
+
/**
|
|
60
|
+
* Memory retrieved for this turn by `core/memory/turn-retrieval.ts`,
|
|
61
|
+
* already ranked, trust-filtered and budgeted. The Weaver resolves it
|
|
62
|
+
* before the call; a backend's only job is to hand it to
|
|
63
|
+
* `formatUserPrompt`, which owns the rendering.
|
|
64
|
+
*
|
|
65
|
+
* Turn-scoped and user-turn-only: it must never reach
|
|
66
|
+
* `prepareSystemPrompt()` or a backend `system` field, or the
|
|
67
|
+
* per-session frozen prompt stops being frozen (plan §3.6).
|
|
68
|
+
*/
|
|
69
|
+
retrievedMemory?: string;
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
// ── Catalog types ───────────────────────────────────────────────────────────
|
|
@@ -199,6 +199,80 @@ export async function assertCompletedUsageMatchesUsageEvent(
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* `ChatRunParams.retrievedMemory` must reach the prompt the backend
|
|
204
|
+
* sends, in the USER turn, after the message text.
|
|
205
|
+
*
|
|
206
|
+
* This is the clause #639 was written in the absence of: the old
|
|
207
|
+
* pre-retrieval field was honoured by two backends and dropped in
|
|
208
|
+
* silence by the other four, a divergence that would have become a
|
|
209
|
+
* real bug the day a retriever existed. Now there is one renderer
|
|
210
|
+
* (`backend/runtime/prompt/prompt-format.ts`) that every backend already calls
|
|
211
|
+
* for its time tag and `msg_id` framing — and this assertion is what
|
|
212
|
+
* stops a backend from quietly building its prompt some other way.
|
|
213
|
+
*
|
|
214
|
+
* The caller supplies `capturePrompt`, returning the prompt the
|
|
215
|
+
* backend built for the last run: `core/` cannot import `backend/`, so
|
|
216
|
+
* the rendering itself is asserted by the caller, which can. `marker`
|
|
217
|
+
* is the retrieved text; it must appear after the user's own text, and
|
|
218
|
+
* must not appear at all when the field is absent.
|
|
219
|
+
*/
|
|
220
|
+
export async function assertChatBackendCarriesRetrievedMemory(
|
|
221
|
+
backend: Backend,
|
|
222
|
+
capturePrompt: () => string | undefined,
|
|
223
|
+
options: { text?: string; chatId?: string; marker?: string } = {},
|
|
224
|
+
): Promise<void> {
|
|
225
|
+
const chat = backend.chat;
|
|
226
|
+
if (!chat) return;
|
|
227
|
+
const text = options.text ?? "ping";
|
|
228
|
+
const marker = options.marker ?? "#1 [fact] contract: remembered thing";
|
|
229
|
+
const run = async (retrievedMemory?: string): Promise<string> => {
|
|
230
|
+
await drain(
|
|
231
|
+
chat.runChatTurn({
|
|
232
|
+
chatId: options.chatId ?? "contract-test-chat",
|
|
233
|
+
model: makeBareModelRef(backend.id, "contract-test-model"),
|
|
234
|
+
text,
|
|
235
|
+
senderName: "ContractTest",
|
|
236
|
+
...(retrievedMemory !== undefined ? { retrievedMemory } : {}),
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
239
|
+
const prompt = capturePrompt();
|
|
240
|
+
if (prompt === undefined) {
|
|
241
|
+
throw new ContractViolation(
|
|
242
|
+
backend.id,
|
|
243
|
+
"ChatBackend.carriesRetrievedMemory",
|
|
244
|
+
"the backend built no prompt for the turn",
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return prompt;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const withMemory = await run(marker);
|
|
251
|
+
if (!withMemory.includes(marker)) {
|
|
252
|
+
throw new ContractViolation(
|
|
253
|
+
backend.id,
|
|
254
|
+
"ChatBackend.carriesRetrievedMemory",
|
|
255
|
+
`retrievedMemory never reached the prompt: ${JSON.stringify(withMemory)}`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (withMemory.indexOf(marker) < withMemory.indexOf(text)) {
|
|
259
|
+
throw new ContractViolation(
|
|
260
|
+
backend.id,
|
|
261
|
+
"ChatBackend.carriesRetrievedMemory",
|
|
262
|
+
"the memory block precedes the user text; it must be appended after it",
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const without = await run();
|
|
267
|
+
if (without.includes(marker)) {
|
|
268
|
+
throw new ContractViolation(
|
|
269
|
+
backend.id,
|
|
270
|
+
"ChatBackend.carriesRetrievedMemory",
|
|
271
|
+
"the memory block appeared in a turn that carried no retrievedMemory",
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
202
276
|
/**
|
|
203
277
|
* Backends that expose a `BackgroundRunner` must terminate with
|
|
204
278
|
* `completed` or `error` and must emit `run_started` first — same
|
|
@@ -323,7 +397,16 @@ export async function assertUsageTelemetryShape(
|
|
|
323
397
|
*/
|
|
324
398
|
export async function assertBackendContract(
|
|
325
399
|
backend: Backend,
|
|
326
|
-
options: {
|
|
400
|
+
options: {
|
|
401
|
+
text?: string;
|
|
402
|
+
chatId?: string;
|
|
403
|
+
/**
|
|
404
|
+
* The prompt the backend built for the last run, when the fixture
|
|
405
|
+
* can capture it. Supplying it opts the turn-retrieval clause into
|
|
406
|
+
* the suite; omitting it skips that clause only.
|
|
407
|
+
*/
|
|
408
|
+
capturePrompt?: () => string | undefined;
|
|
409
|
+
} = {},
|
|
327
410
|
): Promise<string[]> {
|
|
328
411
|
const checked: string[] = [];
|
|
329
412
|
|
|
@@ -336,6 +419,14 @@ export async function assertBackendContract(
|
|
|
336
419
|
checked.push("ChatBackend.singleUsage");
|
|
337
420
|
await assertCompletedUsageMatchesUsageEvent(backend, options);
|
|
338
421
|
checked.push("ChatBackend.completedUsageMatches");
|
|
422
|
+
if (options.capturePrompt) {
|
|
423
|
+
await assertChatBackendCarriesRetrievedMemory(
|
|
424
|
+
backend,
|
|
425
|
+
options.capturePrompt,
|
|
426
|
+
options,
|
|
427
|
+
);
|
|
428
|
+
checked.push("ChatBackend.carriesRetrievedMemory");
|
|
429
|
+
}
|
|
339
430
|
}
|
|
340
431
|
if (backend.background) {
|
|
341
432
|
await assertBackgroundRunnerLifecycle(backend);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* longer render markdown logs themselves and core no longer parses
|
|
10
10
|
* backend-specific output.
|
|
11
11
|
*
|
|
12
|
-
* The shared wrapper `backend/
|
|
12
|
+
* The shared wrapper `backend/runtime/turn/handler-to-events.ts` converts
|
|
13
13
|
* each backend's existing callback-driven `handleMessage` into the
|
|
14
14
|
* canonical sequence: `run_started → text_delta* →
|
|
15
15
|
* assistant_message* → tool_call* → usage → completed`. Backends
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* unset to avoid sweeping the other context's subprocesses.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { BackgroundRunner } from "
|
|
18
|
-
import type { OneShotAgentParams, OneShotUsage } from "
|
|
19
|
-
import { logWarn, logError } from "
|
|
17
|
+
import type { BackgroundRunner } from "../../agent-runtime/capabilities.js";
|
|
18
|
+
import type { OneShotAgentParams, OneShotUsage } from "../../types.js";
|
|
19
|
+
import { logWarn, logError } from "../../../util/log.js";
|
|
20
20
|
|
|
21
21
|
/** Default bounded grace after an abort before giving up on the backend. */
|
|
22
22
|
const DEFAULT_ABORT_GRACE_MS = 30 * 1000;
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { mkdir, appendFile } from "node:fs/promises";
|
|
17
|
-
import { dirs } from "
|
|
18
|
-
import { log, logWarn } from "
|
|
17
|
+
import { dirs } from "../../../util/paths.js";
|
|
18
|
+
import { log, logWarn } from "../../../util/log.js";
|
|
19
19
|
import {
|
|
20
20
|
acquireBackendInstance,
|
|
21
21
|
isModelValidForBackend,
|
|
22
|
-
} from "
|
|
23
|
-
import { taskTable } from "
|
|
24
|
-
import type { OneShotAgentParams } from "
|
|
22
|
+
} from "../../engine/backend-controller/index.js";
|
|
23
|
+
import { taskTable } from "../../tasks/index.js";
|
|
24
|
+
import type { OneShotAgentParams } from "../../types.js";
|
|
25
25
|
import { runIsolatedAgent } from "./isolated-agent.js";
|
|
26
26
|
import {
|
|
27
27
|
buildJobSystemPrompt,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { Cron } from "croner";
|
|
23
|
-
import { getActiveCount } from "
|
|
23
|
+
import { getActiveCount } from "../../engine/dispatcher.js";
|
|
24
24
|
import {
|
|
25
25
|
getAllCronJobs,
|
|
26
26
|
getCronJob,
|
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
describeSchedule,
|
|
32
32
|
type CronJob,
|
|
33
33
|
type CronRunOutcome,
|
|
34
|
-
} from "
|
|
35
|
-
import { appendDailyLog } from "
|
|
36
|
-
import { log, logError, logWarn } from "
|
|
37
|
-
import { numericChatIdFor } from "../../
|
|
34
|
+
} from "../../../storage/cron.js";
|
|
35
|
+
import { appendDailyLog } from "../../../storage/daily-log.js";
|
|
36
|
+
import { log, logError, logWarn } from "../../../util/log.js";
|
|
37
|
+
import { numericChatIdFor } from "../../frontend-runtime/chat-id.js";
|
|
38
38
|
import { runJobOneShot } from "./job-oneshot.js";
|
|
39
39
|
import {
|
|
40
40
|
jobAllowsRun,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
import {
|
|
47
47
|
catchupRunCount,
|
|
48
48
|
missedRunCount,
|
|
49
|
-
} from "
|
|
49
|
+
} from "../../../native/scheduler-core.js";
|
|
50
50
|
|
|
51
51
|
// ── Dependencies (injected at startup) ──────────────────────────────────────
|
|
52
52
|
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
type CatchupPolicy,
|
|
19
19
|
type CronJob,
|
|
20
20
|
type CronJobType,
|
|
21
|
-
} from "
|
|
21
|
+
} from "../../../storage/cron.js";
|
|
22
22
|
|
|
23
23
|
/** The scheduler ticks once a minute, so sub-minute intervals are meaningless. */
|
|
24
24
|
const MIN_INTERVAL_SECONDS = 60;
|
|
@@ -13,18 +13,17 @@
|
|
|
13
13
|
|
|
14
14
|
import { existsSync, mkdirSync, appendFileSync } from "node:fs";
|
|
15
15
|
import { resolve } from "node:path";
|
|
16
|
-
import { files as pathFiles, dirs } from "
|
|
17
|
-
import { kvGet, kvSet } from "
|
|
18
|
-
import { importLegacyJson } from "
|
|
16
|
+
import { files as pathFiles, dirs } from "../../../util/paths.js";
|
|
17
|
+
import { kvGet, kvSet } from "../../../storage/kv.js";
|
|
18
|
+
import { importLegacyJson } from "../../../storage/legacy-import.js";
|
|
19
19
|
import { readPromptAsset } from "#prompt-assets";
|
|
20
|
-
import { log, logError, logWarn } from "
|
|
21
|
-
import { getDefaultModel } from "
|
|
22
|
-
import type { OneShotAgentParams, ReasoningEffortLevel } from "
|
|
23
|
-
import type { Backend } from "
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { FailureBackoff } from "./failure-backoff.js";
|
|
20
|
+
import { log, logError, logWarn } from "../../../util/log.js";
|
|
21
|
+
import { getDefaultModel } from "../../models/catalog.js";
|
|
22
|
+
import type { OneShotAgentParams, ReasoningEffortLevel } from "../../types.js";
|
|
23
|
+
import type { Backend } from "../../agent-runtime/capabilities.js";
|
|
24
|
+
import { taskTable } from "../../tasks/index.js";
|
|
25
|
+
import { resolveBackgroundEffort } from "../effort.js";
|
|
26
|
+
import { FailureBackoff } from "../failure-backoff.js";
|
|
28
27
|
|
|
29
28
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
30
29
|
|
|
@@ -160,10 +159,6 @@ async function executeDream(trigger: "auto" | "forced"): Promise<void> {
|
|
|
160
159
|
"dream",
|
|
161
160
|
`Memory consolidation complete (${trigger}), log: ${dreamLogPath}`,
|
|
162
161
|
);
|
|
163
|
-
// The soul's organic maintenance shares the dream cadence. Inert (no-op)
|
|
164
|
-
// unless the soul is enabled, and isolated so a soul failure never breaks
|
|
165
|
-
// memory consolidation.
|
|
166
|
-
await getSoul().dream();
|
|
167
162
|
} catch (err) {
|
|
168
163
|
logError("dream", `Memory consolidation failed (${trigger})`, err);
|
|
169
164
|
writeDreamState({ last_run: state?.last_run ?? 0, status: "idle" });
|