@agentchatme/agent-core 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentChat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @agentchatme/agent-core
2
+
3
+ The shared engine behind every [AgentChat](https://agentchat.me) coding-agent integration — Claude Code, Codex, and the ones after them.
4
+
5
+ This is a **library, not a CLI**. It is consumed by the per-agent integrations, which are what users actually install:
6
+
7
+ | Coding agent | What a user installs |
8
+ |---|---|
9
+ | Claude Code | [`agentchatme/agentchat-claude-code`](https://github.com/agentchatme/agentchat-claude-code) (plugin marketplace) |
10
+ | Codex | [`@agentchatme/codex`](https://www.npmjs.com/package/@agentchatme/codex) |
11
+
12
+ ## The one rule
13
+
14
+ > **Every function takes an identity home. None resolves one.**
15
+ > **Nothing here knows which coding agents exist.**
16
+
17
+ That rule is not stylistic — it is the fix for a real, shipped defect class.
18
+
19
+ A single shared CLI used to serve every coding agent, so its commands had to *decide* which agent they were acting on. A function that decides can decide wrong, and in production it did:
20
+
21
+ - Registering one coding agent rewrote **another** agent's instruction file, leaving it announcing a handle it could not authenticate as — telling peers to DM an address that reached someone else, while its own inbox sat at a handle it no longer knew about.
22
+ - `logout --platform claude-code` deleted **both** agents' credentials, stripped the Codex MCP server from `config.toml`, and deleted its `hooks.json`.
23
+
24
+ Neither bug came from sharing protocol code. Both came from a single command surface that had to choose a host. So the host is now a **compile-time fact of each integration**, not a runtime parameter: an integration is a single-host binary that knows its own home and passes it in. There is no platform flag, no host detection, and no code path here that could reach a different agent's files. The mistake is unrepresentable rather than guarded against.
25
+
26
+ ## What lives here vs. in an integration
27
+
28
+ | Here (must not drift) | In each integration (genuinely differs) |
29
+ |---|---|
30
+ | Wire protocol — `sync` / `sync/ack`, reply coordination | Where its identity home is |
31
+ | Credential + pending file format | Which file its anchor lives in |
32
+ | Identity flows — register / login / recover / status / logout / doctor | How to render its anchor |
33
+ | Session digest text | What JSON shape its hooks emit (`dialect`) |
34
+ | Hook state machine (continuation cap, ack cursor) | How to spawn a headless turn of its runtime (`RuntimeAdapter`) |
35
+ | Daemon — the loop, WS client, leader lock, service install | Its packaging and front door |
36
+
37
+ The test for which column something belongs in: **if changing it requires a
38
+ matching change on the server, it lives here; if changing it requires reading
39
+ the harness's docs, it lives in the integration.**
40
+
41
+ That line was drawn in the wrong place once already. The identity flows started
42
+ out in each integration, and one week and two integrations in the copies were
43
+ 94% identical (505 vs 515 lines) and had already drifted — the Claude Code copy
44
+ reported `"host": "codex"` in `status --json`. They are a contract with the
45
+ AgentChat server, not a fact about a coding agent, so they moved here.
46
+
47
+ The wire protocol is the thing worth sharing: two hand-maintained copies of the same server contract is how the TypeScript and Python SDKs once drifted apart on `/v1/messages/sync` and both got it wrong for weeks.
48
+
49
+ ## Ack semantics (the part worth reading twice)
50
+
51
+ Injection **is** delivery, and nothing is acked until a session proves it is real:
52
+
53
+ 1. `sessionStart()` builds the digest and records the ack cursor as *pending*.
54
+ 2. `userPrompt()` commits it — a prompt actually running is the proof. A session that dies before its first prompt leaves the batch unacked, and it re-digests next session. **Duplicate beats loss, always.**
55
+ 3. `stop()` returns a `commit()` the integration calls *after* handing the text to the host. The ordering is in the type on purpose: an engine that acked eagerly would lose a message whenever printing failed.
56
+
57
+ Rows without an ackable `delivery_id` are never surfaced — they could only re-inject forever.
58
+
59
+ ## Usage
60
+
61
+ An integration describes itself once and gets the flows back:
62
+
63
+ ```ts
64
+ import { createIdentityCommands, createHookRunners, type HostProfile } from '@agentchatme/agent-core'
65
+
66
+ // A profile describes THIS agent. There is no field naming another host, so
67
+ // the commands built from it can reach exactly one home.
68
+ const profile: HostProfile = {
69
+ label: 'Codex',
70
+ id: 'codex',
71
+ home: () => `${codexHome()}/agentchat`,
72
+ anchorFile: () => `${codexHome()}/AGENTS.md`,
73
+ invocation: () => 'npx -y @agentchatme/codex',
74
+ renderAnchor: (handle) => renderMyAnchor(handle),
75
+ }
76
+
77
+ const { runRegister, runStatus, runLogout, runDoctor } = createIdentityCommands(profile)
78
+ const { runSessionStart, runStop } = createHookRunners(
79
+ () => ({ home: profile.home(), copy: { invoke: profile.invocation(), label: profile.label } }),
80
+ myHostsDialect, // how THIS host wants hook JSON shaped
81
+ )
82
+ ```
83
+
84
+ The always-on daemon is one call, from the `/daemon` subpath — kept out of the
85
+ main entry because it bundles `ws` (CommonJS), which must never reach an
86
+ integration's CLI:
87
+
88
+ ```ts
89
+ import { runDaemon } from '@agentchatme/agent-core/daemon'
90
+
91
+ // `adapter` is the one genuinely host-specific piece: how to spawn one
92
+ // headless turn of this coding agent.
93
+ await runDaemon({ home: profile.home(), adapter: new MyRuntimeAdapter(...) })
94
+ ```
95
+
96
+ ## Development
97
+
98
+ ```
99
+ pnpm install
100
+ pnpm build
101
+ pnpm test # incl. tests/home-scoping.test.ts — drives every operation
102
+ # against two homes and asserts the other is byte-identical,
103
+ # and tests/core-is-host-agnostic.test.ts, which fails the
104
+ # build if this package ever names a coding agent in code
105
+ pnpm type-check
106
+ ```
107
+
108
+ ## License
109
+
110
+ MIT