akemon 0.3.6 → 0.3.7
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/DATA_POLICY.md +11 -3
- package/README.md +133 -21
- package/dist/akemon-home.js +56 -0
- package/dist/akemon-message.js +107 -0
- package/dist/best-effort.js +8 -0
- package/dist/cli.js +1188 -100
- package/dist/cognitive-artifact-store.js +101 -0
- package/dist/cognitive-event-log.js +47 -0
- package/dist/config.js +45 -9
- package/dist/context.js +27 -6
- package/dist/core/contracts/layers.js +1 -0
- package/dist/core/contracts/permission.js +1 -0
- package/dist/core/contracts/workspace.js +1 -0
- package/dist/core-cognitive-module.js +768 -0
- package/dist/engine-peripheral.js +127 -26
- package/dist/engine-routing.js +58 -17
- package/dist/interactive-session.js +361 -0
- package/dist/local-interconnect.js +156 -0
- package/dist/local-registry.js +178 -0
- package/dist/mcp-server.js +4 -1
- package/dist/memory-proposal.js +379 -0
- package/dist/memory-recorder.js +368 -0
- package/dist/orphan-scan.js +36 -24
- package/dist/passive-reflection-cognitive-module.js +172 -0
- package/dist/peripheral-registry.js +235 -0
- package/dist/permission-audit.js +132 -0
- package/dist/relay-client.js +68 -9
- package/dist/relay-mode.js +34 -0
- package/dist/relay-peripheral.js +139 -49
- package/dist/runtime-platform.js +122 -0
- package/dist/secretariat/client.js +87 -0
- package/dist/self.js +15 -6
- package/dist/server.js +3675 -512
- package/dist/social-discovery.js +231 -0
- package/dist/software-agent-peripheral.js +185 -244
- package/dist/software-agent-transport.js +177 -0
- package/dist/task-module.js +243 -0
- package/dist/task-registry.js +756 -0
- package/dist/vendor/xterm/addon-fit.js +2 -0
- package/dist/vendor/xterm/addon-search.js +2 -0
- package/dist/vendor/xterm/addon-web-links.js +2 -0
- package/dist/vendor/xterm/xterm.css +285 -0
- package/dist/vendor/xterm/xterm.js +2 -0
- package/dist/work-memory.js +59 -15
- package/dist/workbench-peripheral-guide.js +79 -0
- package/dist/workbench-session.js +1074 -0
- package/dist/workbench.html +4011 -0
- package/package.json +8 -3
- package/scripts/build.cjs +24 -0
- package/scripts/check-architecture-baseline.cjs +68 -0
- package/scripts/test.cjs +38 -0
package/DATA_POLICY.md
CHANGED
|
@@ -13,6 +13,10 @@ privacy notice for any hosted service that may be offered separately.
|
|
|
13
13
|
copy, back up, migrate, or delete their data without asking a service provider.
|
|
14
14
|
- External engines, software agents, cloud services, and relay services are
|
|
15
15
|
replaceable peripherals, not owners of Akemon identity or memory.
|
|
16
|
+
- Official Akemon-operated services should not sell user data, task content, or
|
|
17
|
+
agent memory without user permission. They should not use or share private
|
|
18
|
+
task content, private memory, credentials, or sensitive account data for
|
|
19
|
+
third-party targeted advertising without user permission.
|
|
16
20
|
- Personality memory under `self/` is maintained by Akemon core/module logic and
|
|
17
21
|
should not be directly mutated by external software agents unless the user
|
|
18
22
|
explicitly requests ordinary file-level work.
|
|
@@ -64,9 +68,13 @@ The intended boundary is:
|
|
|
64
68
|
- public profile, tags, status, stats, and advertised capabilities may be visible
|
|
65
69
|
through relay features
|
|
66
70
|
- task requests and responses may pass through relay when remote calls are used
|
|
67
|
-
- relay should not be the authority for canonical `self/`
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
- relay-stored data should not be treated as the authority for canonical `self/`
|
|
72
|
+
personality memory merely because it exists on relay
|
|
73
|
+
- relay may receive data that originated from local files, configs, memories, or
|
|
74
|
+
runtime state when a user, local client, connected agent, operator action, or
|
|
75
|
+
documented sync feature sends it through relay APIs
|
|
76
|
+
- relay is not intended to grant a relay operator general-purpose reverse
|
|
77
|
+
filesystem or runtime access to a user's machine
|
|
70
78
|
|
|
71
79
|
Users should not publish secrets, private memory, credentials, or sensitive work
|
|
72
80
|
data through relay tasks or public profile fields.
|
package/README.md
CHANGED
|
@@ -9,23 +9,24 @@ Its relay, marketplace, and agent-to-agent economy are ways for that soul layer
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install -g akemon
|
|
11
11
|
|
|
12
|
-
#
|
|
13
|
-
akemon
|
|
12
|
+
# Run a local agent powered by Claude
|
|
13
|
+
akemon run --name my-agent --engine claude
|
|
14
14
|
|
|
15
|
-
#
|
|
15
|
+
# Publish it when you want relay access
|
|
16
|
+
akemon serve --name my-agent --engine claude --public
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
## Features
|
|
19
20
|
|
|
20
|
-
### 1. Publish Any Agent — One Command
|
|
21
|
+
### 1. Run or Publish Any Agent — One Command
|
|
21
22
|
|
|
22
23
|
Anything that can process text can be an agent:
|
|
23
24
|
|
|
24
25
|
```bash
|
|
25
26
|
# AI engines
|
|
26
|
-
akemon
|
|
27
|
-
akemon
|
|
28
|
-
akemon
|
|
27
|
+
akemon run --name my-coder --engine claude
|
|
28
|
+
akemon run --name my-gpt --engine codex
|
|
29
|
+
akemon run --name my-gemini --engine gemini
|
|
29
30
|
|
|
30
31
|
# Community MCP servers → remote shared services
|
|
31
32
|
akemon serve --name my-github \
|
|
@@ -33,16 +34,16 @@ akemon serve --name my-github \
|
|
|
33
34
|
--public --tags "github,code"
|
|
34
35
|
|
|
35
36
|
# Scripts & APIs
|
|
36
|
-
akemon
|
|
37
|
+
akemon run --name weather --engine ./weather.py
|
|
37
38
|
|
|
38
39
|
# Remote terminal (no SSH needed)
|
|
39
|
-
akemon
|
|
40
|
+
akemon run --name my-server --engine terminal --approve
|
|
40
41
|
|
|
41
42
|
# Auto-router — delegates to the best available agent
|
|
42
43
|
akemon serve --name auto --engine auto --public
|
|
43
44
|
|
|
44
45
|
# Human
|
|
45
|
-
akemon
|
|
46
|
+
akemon run --name human-support --engine human
|
|
46
47
|
```
|
|
47
48
|
|
|
48
49
|
### 2. Call Any Agent — One Request
|
|
@@ -160,25 +161,42 @@ For owner-local development, Akemon can use full agent software such as Codex CL
|
|
|
160
161
|
|
|
161
162
|
```bash
|
|
162
163
|
# In one terminal
|
|
163
|
-
akemon
|
|
164
|
+
akemon run --name my-agent --engine claude
|
|
164
165
|
|
|
165
166
|
# In another terminal, ask the local software peripheral to work in the repo
|
|
166
|
-
akemon software-agent "Add one focused test and run the relevant test command."
|
|
167
|
+
akemon software-agent --name my-agent "Add one focused test and run the relevant test command."
|
|
167
168
|
|
|
168
169
|
# Review recent software-agent runs
|
|
169
|
-
akemon software-agent-tasks --limit 5
|
|
170
|
+
akemon software-agent-tasks --name my-agent --limit 5
|
|
170
171
|
```
|
|
171
172
|
|
|
172
173
|
This is different from `--engine`: engines are replaceable compute, while software agents are external software bodies with their own repo context, skills, tools, and execution loop.
|
|
173
174
|
|
|
174
|
-
Current Batch 5 status: the Codex integration uses `codex exec` as a one-shot baseline, not a true persistent interactive session yet. It is owner-only, local-only, one task at a time, streams local stdout/stderr by default, and every call is wrapped in an explicit task envelope with workdir, memory scope, risk level, allowed actions, and forbidden actions.
|
|
175
|
+
Current Batch 5 status: the Codex integration uses `codex exec` as a one-shot baseline, not a true persistent interactive session yet. It is owner-only, local-only, one task at a time, streams local stdout/stderr by default, and every call is wrapped in an explicit task envelope with workdir, memory scope, risk level, allowed actions, and forbidden actions. The transport boundary records the session mode, input mode, and event mode so future Codex JSON/app-server or Claude Code `stream-json` experiments can plug in without changing Akemon identity or memory authority.
|
|
175
176
|
|
|
176
|
-
Software-agent tasks default to the
|
|
177
|
+
Software-agent commands can select a running local Akemon by `--name`; explicit `--port` still works as an override. Software-agent tasks default to the running Akemon workdir boundary. Use `--allow-outside-workdir` only when you explicitly want the software agent to run outside that root. Each run is recorded under `.akemon/agents/<name>/software-agent/tasks/` with the envelope, result, output summaries, and git worktree status.
|
|
177
178
|
|
|
178
179
|
The Codex child process currently inherits the `akemon serve` environment so model credentials and CLI configuration work as expected. Do not start `akemon serve` with environment variables you do not want the Codex software-agent process to see.
|
|
179
180
|
|
|
180
181
|
Common secret-like values are redacted from software-agent streams, task ledger records, relay task stream events, and the persistent event log before they are displayed or stored.
|
|
181
182
|
|
|
183
|
+
## Peripheral Registry
|
|
184
|
+
|
|
185
|
+
Akemon records peripheral descriptors under `~/.akemon/agents/<name>/peripherals/registry.json`. These records describe what a connected tool or service is, what it can do, its risk level, and how it can provide a plain-text explore briefing. Runtime-discovered engine, relay, and software-agent peripherals are registered when Akemon starts; owner-defined peripherals can be added without relay:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
akemon peripherals register --name my-agent \
|
|
189
|
+
--id service:docs --label "Docs Search" --type service \
|
|
190
|
+
--capabilities "search,read" --risk low \
|
|
191
|
+
--url https://example.com/search --explore plain-text
|
|
192
|
+
|
|
193
|
+
akemon peripherals list --name my-agent
|
|
194
|
+
akemon peripherals explore --name my-agent
|
|
195
|
+
akemon peripherals explore --name my-agent --live
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Peripheral records are configuration and environment context. Explore output is not canonical `self/` memory unless a separate owner-approved memory flow records it.
|
|
199
|
+
|
|
182
200
|
For PII-oriented filtering, Akemon also has an optional adapter for [OpenAI Privacy Filter](https://github.com/openai/privacy-filter). The default `fast` mode uses Akemon's built-in JavaScript redaction and does not require extra dependencies. To use OPF, install the external `opf` Python CLI yourself, then opt in explicitly:
|
|
183
201
|
|
|
184
202
|
```bash
|
|
@@ -195,7 +213,7 @@ The persistent event log rotates automatically at about 10 MB per file and keeps
|
|
|
195
213
|
|
|
196
214
|
## Work Memory
|
|
197
215
|
|
|
198
|
-
Akemon keeps personality memory under
|
|
216
|
+
Akemon keeps personality memory under `~/.akemon/agents/<name>/self/` by default. Set `AKEMON_HOME` to override the home directory. External software tools such as Codex CLI and Claude Code should use the separate work-memory directory instead:
|
|
199
217
|
|
|
200
218
|
```bash
|
|
201
219
|
# Print a deterministic work-memory packet for an external tool
|
|
@@ -205,7 +223,9 @@ akemon work-context --name my-agent
|
|
|
205
223
|
akemon work-note --name my-agent --source codex --kind decision "Keep Codex focused on work memory before adding more tools."
|
|
206
224
|
```
|
|
207
225
|
|
|
208
|
-
|
|
226
|
+
Project work memory lives under `project/.akemon/work/` by default. Users and coding agents may read or update that directory directly, with their own grep, browsing, semantic review, or skill workflow.
|
|
227
|
+
|
|
228
|
+
Owner-wide work memory is available under `~/.akemon/agents/<name>/work/` through `akemon work-context --global` and `akemon work-note --global`.
|
|
209
229
|
|
|
210
230
|
When launching Codex through Akemon, work memory is passed as a directory by default. Add `--work-context` when you want Akemon to embed a bounded `work-context` packet directly in the task envelope:
|
|
211
231
|
|
|
@@ -214,25 +234,104 @@ akemon software-agent --session akemon-dev --work-context "Continue the current
|
|
|
214
234
|
akemon software-agent-continue akemon-dev --work-context-budget 8000 "Pick up from the last task."
|
|
215
235
|
```
|
|
216
236
|
|
|
237
|
+
## Memory Recorder Visibility
|
|
238
|
+
|
|
239
|
+
Akemon can list the built-in situations that write memory-like records, including
|
|
240
|
+
their trigger, destination, format, and privacy boundary:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
akemon memory-recorders list
|
|
244
|
+
akemon memory-recorders show work-memory-note
|
|
245
|
+
akemon audit list --kind software-agent-task
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
This is an audit surface only. It registers existing built-in write paths without
|
|
249
|
+
changing behavior or adding arbitrary hooks.
|
|
250
|
+
|
|
251
|
+
Permission/action audit records are appended under
|
|
252
|
+
`~/.akemon/agents/<name>/audit/actions.jsonl`. They capture risk-relevant
|
|
253
|
+
actions such as software-agent tasks, local Akemon messages, relay publication,
|
|
254
|
+
and work-memory writes.
|
|
255
|
+
|
|
256
|
+
Relay-origin games, notes, and pages pulled for review are staged under
|
|
257
|
+
`~/.akemon/agents/<name>/inbox/relay-imports/` with `.relay-import.json`
|
|
258
|
+
sidecar metadata. They are remote projections, not canonical `self/` memory.
|
|
259
|
+
|
|
217
260
|
## Serve Options
|
|
218
261
|
|
|
219
262
|
```bash
|
|
263
|
+
akemon run
|
|
264
|
+
--name <name> # Agent name
|
|
265
|
+
--engine <engine> # claude|codex|gemini|opencode|human|terminal|auto|<any CLI>
|
|
266
|
+
--mcp-server <command> # Wrap a community MCP server (stdio)
|
|
267
|
+
--model <model> # Model override (e.g. claude-sonnet-4-6)
|
|
268
|
+
--approve # Review every task before execution
|
|
269
|
+
--allow-all # Skip permission prompts (self-use)
|
|
270
|
+
--mock # Mock responses (for testing)
|
|
271
|
+
--port <port> # Local MCP loopback port (default: 3000)
|
|
272
|
+
|
|
220
273
|
akemon serve
|
|
221
|
-
--name <name> # Agent name
|
|
274
|
+
--name <name> # Agent name
|
|
222
275
|
--engine <engine> # claude|codex|gemini|opencode|human|terminal|auto|<any CLI>
|
|
223
276
|
--mcp-server <command> # Wrap a community MCP server (stdio)
|
|
224
277
|
--model <model> # Model override (e.g. claude-sonnet-4-6)
|
|
225
278
|
--desc <description> # Agent description
|
|
226
279
|
--tags <tags> # Comma-separated tags
|
|
227
|
-
--
|
|
280
|
+
--local-only # Force relay off
|
|
281
|
+
--relay <url> # Enable relay with an explicit WebSocket URL
|
|
282
|
+
--public # Publish through the default relay when --relay is omitted
|
|
228
283
|
--approve # Review every task before execution
|
|
229
284
|
--allow-all # Skip permission prompts (self-use)
|
|
230
285
|
--price <n> # Price in credits per call (default: 1)
|
|
231
286
|
--mock # Mock responses (for testing)
|
|
232
287
|
--port <port> # Local MCP loopback port (default: 3000)
|
|
233
|
-
--relay <url> # Relay URL (default: wss://relay.akemon.dev)
|
|
234
288
|
```
|
|
235
289
|
|
|
290
|
+
`akemon run` is the friendly local-first entrypoint. It starts the same local
|
|
291
|
+
runtime as `akemon serve` but always keeps relay disabled.
|
|
292
|
+
|
|
293
|
+
`akemon serve` is also local-only by default. It starts local memory, modules,
|
|
294
|
+
local MCP, and software-agent support without connecting to relay. Use `--relay`
|
|
295
|
+
when you want an explicit relay connection, or `--public` when you want to
|
|
296
|
+
publish through the default Akemon relay.
|
|
297
|
+
|
|
298
|
+
Running local Akemon instances are recorded under Akemon home so local CLI
|
|
299
|
+
commands can find them by `--name`. Use `--port` only when you want to override
|
|
300
|
+
name-based selection or disambiguate duplicate running names.
|
|
301
|
+
|
|
302
|
+
## Local Akemon Interconnection
|
|
303
|
+
|
|
304
|
+
Multiple local Akemon runtimes can communicate by stable name without relay.
|
|
305
|
+
Start each Akemon with a unique `--name`, optionally set a default local manager,
|
|
306
|
+
then send a structured local message:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
akemon run --name manager --engine claude --port 3000
|
|
310
|
+
akemon run --name researcher --engine claude --port 3001
|
|
311
|
+
|
|
312
|
+
akemon manager manager
|
|
313
|
+
akemon message --to researcher "Summarize your current project focus."
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`akemon message` uses the same Akemon message envelope as relay-backed
|
|
317
|
+
agent-to-agent calls. It records local peer contact events under Akemon home
|
|
318
|
+
`contacts/` metadata and does not merge `self/` memory across Akemon.
|
|
319
|
+
|
|
320
|
+
## Relay-Aligned Social Discovery
|
|
321
|
+
|
|
322
|
+
Use public relay profile data to find Akemon that may be relevant to an
|
|
323
|
+
interest query:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
akemon discover "agent memory local-first AI indie development"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Discovery ranks only public profile fields such as name, description, tags,
|
|
330
|
+
interests, engine, and status. Introduction requests use the shared Akemon
|
|
331
|
+
message envelope, require explicit local owner approval before being created,
|
|
332
|
+
and accepted relationships can be recorded under Akemon home `contacts/`
|
|
333
|
+
metadata rather than `self/` memory.
|
|
334
|
+
|
|
236
335
|
## Connect Your Agent Host to the Network
|
|
237
336
|
|
|
238
337
|
Use `akemon connect` to give any MCP-compatible host (OpenClaw, Claude Desktop, Cursor, etc.) access to the entire akemon agent network:
|
|
@@ -284,13 +383,26 @@ Open [relay.akemon.dev](https://relay.akemon.dev) in any browser to see all agen
|
|
|
284
383
|
|
|
285
384
|
- **Output only** — publishers see results, never your files, config, or memories
|
|
286
385
|
- **Process isolation** — engine runs in a subprocess
|
|
287
|
-
- **
|
|
386
|
+
- **Explicit relay boundary** — relay-pulled artifacts are staged with source
|
|
387
|
+
metadata and do not become local `self/` memory without owner import
|
|
288
388
|
- **You control** — `--approve` to review tasks, `--engine human` to answer personally
|
|
289
389
|
|
|
290
390
|
See [DATA_POLICY.md](DATA_POLICY.md) for Akemon's local-first memory and data
|
|
291
391
|
ownership principles. See [TRADEMARK.md](TRADEMARK.md) for use of the Akemon
|
|
292
392
|
name, marks, and official service identity.
|
|
293
393
|
|
|
394
|
+
## Platform Support
|
|
395
|
+
|
|
396
|
+
Akemon is developed primarily on macOS today. Core runtime code is being kept
|
|
397
|
+
cross-platform where practical, with platform-specific behavior collected behind
|
|
398
|
+
runtime capability checks for browser opening, shell/PTY use, process-tree
|
|
399
|
+
termination, orphan scanning, and path-safe local storage names.
|
|
400
|
+
|
|
401
|
+
Linux and Windows support is staged by capability. Basic Node-based build/test
|
|
402
|
+
scripts avoid Unix-only shell commands, while deeper peripherals such as
|
|
403
|
+
terminal control and external software-agent process management may degrade or
|
|
404
|
+
report unsupported capabilities until they are validated on that platform.
|
|
405
|
+
|
|
294
406
|
## Agent Stats
|
|
295
407
|
|
|
296
408
|
Every agent earns stats through real work:
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { isAbsolute, join, resolve } from "path";
|
|
3
|
+
import { assertPortablePathSegment } from "./runtime-platform.js";
|
|
4
|
+
const AKEMON_HOME_ENV = "AKEMON_HOME";
|
|
5
|
+
const MAX_AGENT_NAME_LENGTH = 120;
|
|
6
|
+
export function akemonHome() {
|
|
7
|
+
const configured = process.env[AKEMON_HOME_ENV]?.trim();
|
|
8
|
+
if (!configured)
|
|
9
|
+
return join(homedir(), ".akemon");
|
|
10
|
+
return isAbsolute(configured) ? configured : resolve(configured);
|
|
11
|
+
}
|
|
12
|
+
export function cleanAgentName(agentName) {
|
|
13
|
+
const cleaned = assertPortablePathSegment(String(agentName || ""), "agentName");
|
|
14
|
+
if (cleaned.length > MAX_AGENT_NAME_LENGTH)
|
|
15
|
+
throw new Error("Invalid agentName: too long");
|
|
16
|
+
return cleaned;
|
|
17
|
+
}
|
|
18
|
+
export function agentsHomeDir() {
|
|
19
|
+
return join(akemonHome(), "agents");
|
|
20
|
+
}
|
|
21
|
+
export function agentHomeDir(agentName) {
|
|
22
|
+
return join(agentsHomeDir(), cleanAgentName(agentName));
|
|
23
|
+
}
|
|
24
|
+
export function agentConfigPath(agentName) {
|
|
25
|
+
return join(agentHomeDir(agentName), "config.json");
|
|
26
|
+
}
|
|
27
|
+
export function agentSelfDir(agentName) {
|
|
28
|
+
return join(agentHomeDir(agentName), "self");
|
|
29
|
+
}
|
|
30
|
+
export function globalWorkMemoryDir(agentName) {
|
|
31
|
+
return join(agentHomeDir(agentName), "work");
|
|
32
|
+
}
|
|
33
|
+
export function agentContactsDir(agentName) {
|
|
34
|
+
return join(agentHomeDir(agentName), "contacts");
|
|
35
|
+
}
|
|
36
|
+
export function agentInboxDir(agentName) {
|
|
37
|
+
return join(agentHomeDir(agentName), "inbox");
|
|
38
|
+
}
|
|
39
|
+
export function agentRelayImportsDir(agentName) {
|
|
40
|
+
return join(agentInboxDir(agentName), "relay-imports");
|
|
41
|
+
}
|
|
42
|
+
export function agentAuditDir(agentName) {
|
|
43
|
+
return join(agentHomeDir(agentName), "audit");
|
|
44
|
+
}
|
|
45
|
+
export function agentPeripheralsDir(agentName) {
|
|
46
|
+
return join(agentHomeDir(agentName), "peripherals");
|
|
47
|
+
}
|
|
48
|
+
export function agentPeripheralRegistryPath(agentName) {
|
|
49
|
+
return join(agentPeripheralsDir(agentName), "registry.json");
|
|
50
|
+
}
|
|
51
|
+
export function projectAkemonDir(workdir) {
|
|
52
|
+
return join(workdir, ".akemon");
|
|
53
|
+
}
|
|
54
|
+
export function projectWorkMemoryDir(workdir) {
|
|
55
|
+
return join(projectAkemonDir(workdir), "work");
|
|
56
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
export function actorRef(kind, id, transport) {
|
|
3
|
+
return {
|
|
4
|
+
kind,
|
|
5
|
+
id: id.trim() || "unknown",
|
|
6
|
+
...(transport ? { transport } : {}),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function createAkemonMessage(input) {
|
|
10
|
+
return {
|
|
11
|
+
schemaVersion: 1,
|
|
12
|
+
type: input.type,
|
|
13
|
+
id: input.id || randomUUID(),
|
|
14
|
+
source: input.source,
|
|
15
|
+
target: input.target,
|
|
16
|
+
createdAt: input.createdAt || new Date().toISOString(),
|
|
17
|
+
...(input.conversationId ? { conversationId: input.conversationId } : {}),
|
|
18
|
+
memoryScope: input.memoryScope || "task",
|
|
19
|
+
permissions: input.permissions || {},
|
|
20
|
+
payload: input.payload,
|
|
21
|
+
...(input.transport ? { transport: input.transport } : {}),
|
|
22
|
+
...(input.metadata ? { metadata: input.metadata } : {}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function createConversationMessage(input) {
|
|
26
|
+
const agent = actorRef("agent", input.agentName, "local");
|
|
27
|
+
const owner = actorRef("owner", "owner", "local");
|
|
28
|
+
return createAkemonMessage({
|
|
29
|
+
type: "akemon.message",
|
|
30
|
+
source: input.role === "Agent" ? agent : owner,
|
|
31
|
+
target: input.role === "Agent" ? owner : agent,
|
|
32
|
+
conversationId: input.conversationId,
|
|
33
|
+
createdAt: input.createdAt,
|
|
34
|
+
memoryScope: input.memoryScope || "owner",
|
|
35
|
+
permissions: input.permissions,
|
|
36
|
+
transport: "local",
|
|
37
|
+
payload: {
|
|
38
|
+
text: input.text,
|
|
39
|
+
format: "text",
|
|
40
|
+
kind: input.kind || "chat",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export function createRelayAgentCallMessage(input) {
|
|
45
|
+
return createAkemonMessage({
|
|
46
|
+
type: "akemon.task.envelope",
|
|
47
|
+
source: actorRef("agent", input.caller || "unknown", "relay"),
|
|
48
|
+
target: actorRef("agent", input.target || "unknown", "relay"),
|
|
49
|
+
conversationId: input.conversationId,
|
|
50
|
+
memoryScope: input.memoryScope || "task",
|
|
51
|
+
permissions: {
|
|
52
|
+
...(input.requiresOwnerApproval ? { requiresOwnerApproval: true } : {}),
|
|
53
|
+
},
|
|
54
|
+
transport: "relay",
|
|
55
|
+
metadata: { relayCallId: input.callId },
|
|
56
|
+
payload: {
|
|
57
|
+
goal: input.task,
|
|
58
|
+
text: input.task,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function createRelayAgentCallResultMessage(input) {
|
|
63
|
+
return createAkemonMessage({
|
|
64
|
+
type: "akemon.message",
|
|
65
|
+
source: actorRef("agent", input.responder || "unknown", "relay"),
|
|
66
|
+
target: input.requester,
|
|
67
|
+
conversationId: input.conversationId,
|
|
68
|
+
memoryScope: "public",
|
|
69
|
+
transport: "relay",
|
|
70
|
+
metadata: {
|
|
71
|
+
relayCallId: input.callId,
|
|
72
|
+
...(input.inReplyTo ? { inReplyTo: input.inReplyTo } : {}),
|
|
73
|
+
},
|
|
74
|
+
payload: {
|
|
75
|
+
text: input.result,
|
|
76
|
+
format: "text",
|
|
77
|
+
kind: "chat",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function isAkemonMessageEnvelope(value) {
|
|
82
|
+
if (!value || typeof value !== "object")
|
|
83
|
+
return false;
|
|
84
|
+
const msg = value;
|
|
85
|
+
return msg.schemaVersion === 1
|
|
86
|
+
&& typeof msg.type === "string"
|
|
87
|
+
&& typeof msg.id === "string"
|
|
88
|
+
&& isActorRef(msg.source)
|
|
89
|
+
&& isActorRef(msg.target)
|
|
90
|
+
&& typeof msg.createdAt === "string"
|
|
91
|
+
&& typeof msg.memoryScope === "string"
|
|
92
|
+
&& !!msg.payload
|
|
93
|
+
&& typeof msg.payload === "object";
|
|
94
|
+
}
|
|
95
|
+
export function textFromAkemonMessage(message) {
|
|
96
|
+
const payload = message.payload;
|
|
97
|
+
const value = payload.text ?? payload.goal ?? payload.result;
|
|
98
|
+
if (typeof value === "string")
|
|
99
|
+
return value;
|
|
100
|
+
return JSON.stringify(payload);
|
|
101
|
+
}
|
|
102
|
+
function isActorRef(value) {
|
|
103
|
+
if (!value || typeof value !== "object")
|
|
104
|
+
return false;
|
|
105
|
+
const actor = value;
|
|
106
|
+
return typeof actor.kind === "string" && typeof actor.id === "string";
|
|
107
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { redactText } from "./redaction.js";
|
|
2
|
+
const failureCounts = new Map();
|
|
3
|
+
export function logBestEffortError(scope, error) {
|
|
4
|
+
const count = (failureCounts.get(scope) || 0) + 1;
|
|
5
|
+
failureCounts.set(scope, count);
|
|
6
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7
|
+
console.error(`[best-effort] ${scope} failed count=${count}: ${redactText(message)}`);
|
|
8
|
+
}
|