akemon 0.3.5 → 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.
Files changed (54) hide show
  1. package/DATA_POLICY.md +128 -0
  2. package/README.md +156 -19
  3. package/TRADEMARK.md +74 -0
  4. package/dist/akemon-home.js +56 -0
  5. package/dist/akemon-message.js +107 -0
  6. package/dist/best-effort.js +8 -0
  7. package/dist/cli.js +1411 -132
  8. package/dist/cognitive-artifact-store.js +101 -0
  9. package/dist/cognitive-event-log.js +47 -0
  10. package/dist/config.js +45 -9
  11. package/dist/context.js +27 -6
  12. package/dist/core/contracts/layers.js +1 -0
  13. package/dist/core/contracts/permission.js +1 -0
  14. package/dist/core/contracts/workspace.js +1 -0
  15. package/dist/core-cognitive-module.js +768 -0
  16. package/dist/engine-peripheral.js +127 -26
  17. package/dist/engine-routing.js +58 -17
  18. package/dist/interactive-session.js +361 -0
  19. package/dist/local-interconnect.js +156 -0
  20. package/dist/local-registry.js +178 -0
  21. package/dist/mcp-server.js +4 -1
  22. package/dist/memory-proposal.js +379 -0
  23. package/dist/memory-recorder.js +368 -0
  24. package/dist/orphan-scan.js +36 -24
  25. package/dist/passive-reflection-cognitive-module.js +172 -0
  26. package/dist/peripheral-registry.js +235 -0
  27. package/dist/permission-audit.js +132 -0
  28. package/dist/relay-client.js +68 -9
  29. package/dist/relay-mode.js +34 -0
  30. package/dist/relay-peripheral.js +139 -49
  31. package/dist/runtime-platform.js +122 -0
  32. package/dist/secretariat/client.js +87 -0
  33. package/dist/self.js +15 -6
  34. package/dist/server.js +3695 -439
  35. package/dist/social-discovery.js +231 -0
  36. package/dist/software-agent-peripheral.js +314 -235
  37. package/dist/software-agent-result-cli.js +69 -0
  38. package/dist/software-agent-stream-cli.js +23 -0
  39. package/dist/software-agent-transport.js +177 -0
  40. package/dist/task-module.js +243 -0
  41. package/dist/task-registry.js +756 -0
  42. package/dist/vendor/xterm/addon-fit.js +2 -0
  43. package/dist/vendor/xterm/addon-search.js +2 -0
  44. package/dist/vendor/xterm/addon-web-links.js +2 -0
  45. package/dist/vendor/xterm/xterm.css +285 -0
  46. package/dist/vendor/xterm/xterm.js +2 -0
  47. package/dist/work-memory.js +339 -0
  48. package/dist/workbench-peripheral-guide.js +79 -0
  49. package/dist/workbench-session.js +1074 -0
  50. package/dist/workbench.html +4011 -0
  51. package/package.json +11 -4
  52. package/scripts/build.cjs +24 -0
  53. package/scripts/check-architecture-baseline.cjs +68 -0
  54. package/scripts/test.cjs +38 -0
@@ -0,0 +1,101 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { appendFile, mkdir, readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { redactSecrets } from "./redaction.js";
5
+ const ARTIFACT_FILE_BY_KIND = {
6
+ reflection: "reflections.jsonl",
7
+ landmark_candidate: "landmark-candidates.jsonl",
8
+ memory_weight: "memory-weights.jsonl",
9
+ memory_relation: "memory-relations.jsonl",
10
+ failed_attempt: "failed-attempts.jsonl",
11
+ rejected_approach: "rejected-approaches.jsonl",
12
+ };
13
+ export function createCognitiveArtifactId(kind) {
14
+ return `${kind}_${Date.now()}_${randomUUID().slice(0, 8)}`;
15
+ }
16
+ export function cognitiveArtifactsDir(workdir, agentName) {
17
+ return join(workdir, ".akemon", "agents", agentName, "cognitive");
18
+ }
19
+ export function cognitiveArtifactPath(workdir, agentName, kind) {
20
+ return join(cognitiveArtifactsDir(workdir, agentName), ARTIFACT_FILE_BY_KIND[kind]);
21
+ }
22
+ export async function appendCognitiveArtifact(input) {
23
+ const now = new Date().toISOString();
24
+ const kind = input.record.kind;
25
+ const record = redactSecrets({
26
+ ...input.record,
27
+ schemaVersion: 1,
28
+ id: input.record.id || createCognitiveArtifactId(kind),
29
+ createdAt: input.record.createdAt || now,
30
+ updatedAt: input.record.updatedAt || input.record.createdAt || now,
31
+ agentName: input.agentName,
32
+ workdir: input.workdir,
33
+ refs: input.record.refs || [],
34
+ tags: normalizeTags(input.record.tags),
35
+ });
36
+ await mkdir(cognitiveArtifactsDir(input.workdir, input.agentName), { recursive: true });
37
+ await appendFile(cognitiveArtifactPath(input.workdir, input.agentName, kind), `${JSON.stringify(record)}\n`, "utf-8");
38
+ return record;
39
+ }
40
+ export async function recordFailedAttempt(input) {
41
+ return appendCognitiveArtifact({
42
+ workdir: input.workdir,
43
+ agentName: input.agentName,
44
+ record: {
45
+ ...input.record,
46
+ kind: "failed_attempt",
47
+ status: input.record.status || "observed",
48
+ },
49
+ });
50
+ }
51
+ export async function recordRejectedApproach(input) {
52
+ return appendCognitiveArtifact({
53
+ workdir: input.workdir,
54
+ agentName: input.agentName,
55
+ record: {
56
+ ...input.record,
57
+ kind: "rejected_approach",
58
+ status: input.record.status || "rejected",
59
+ },
60
+ });
61
+ }
62
+ export async function readCognitiveArtifacts(input) {
63
+ try {
64
+ const content = await readFile(cognitiveArtifactPath(input.workdir, input.agentName, input.kind), "utf-8");
65
+ const lines = content.split(/\r?\n/).filter((line) => line.trim());
66
+ const selected = typeof input.limit === "number" && input.limit > 0
67
+ ? lines.slice(-input.limit)
68
+ : lines;
69
+ return selected.flatMap((line) => {
70
+ try {
71
+ return [JSON.parse(line)];
72
+ }
73
+ catch {
74
+ return [];
75
+ }
76
+ });
77
+ }
78
+ catch {
79
+ return [];
80
+ }
81
+ }
82
+ export function normalizeMemoryWeights(weights) {
83
+ return {
84
+ importance: clampScore(weights.importance),
85
+ confidence: clampScore(weights.confidence),
86
+ validation: clampScore(weights.validation),
87
+ userPriority: clampScore(weights.userPriority),
88
+ ...(weights.relevance === undefined ? {} : { relevance: clampScore(weights.relevance) }),
89
+ ...(weights.freshness === undefined ? {} : { freshness: clampScore(weights.freshness) }),
90
+ };
91
+ }
92
+ function normalizeTags(tags) {
93
+ if (!Array.isArray(tags))
94
+ return [];
95
+ return [...new Set(tags.map((tag) => String(tag).trim()).filter(Boolean))];
96
+ }
97
+ function clampScore(value) {
98
+ if (typeof value !== "number" || !Number.isFinite(value))
99
+ return 0;
100
+ return Math.max(0, Math.min(1, value));
101
+ }
@@ -0,0 +1,47 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { appendFile, mkdir, readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { redactSecrets } from "./redaction.js";
5
+ const EVENT_FILE_BY_STREAM = {
6
+ main_chat: "main_chat.jsonl",
7
+ peripheral: "peripheral.jsonl",
8
+ cm: "cm.jsonl",
9
+ compute: "compute.jsonl",
10
+ task: "task.jsonl",
11
+ };
12
+ export function createCognitiveEventId(prefix = "evt") {
13
+ return `${prefix}_${Date.now()}_${randomUUID().slice(0, 8)}`;
14
+ }
15
+ export function cognitiveEventsDir(workdir, agentName) {
16
+ return join(workdir, ".akemon", "agents", agentName, "events");
17
+ }
18
+ export function cognitiveEventLogPath(workdir, agentName, stream) {
19
+ return join(cognitiveEventsDir(workdir, agentName), EVENT_FILE_BY_STREAM[stream]);
20
+ }
21
+ export async function appendCognitiveEvent(input) {
22
+ const event = {
23
+ schemaVersion: 1,
24
+ id: input.event.id || createCognitiveEventId(),
25
+ createdAt: input.event.createdAt || new Date().toISOString(),
26
+ ...input.event,
27
+ };
28
+ const dir = cognitiveEventsDir(input.workdir, input.agentName);
29
+ await mkdir(dir, { recursive: true });
30
+ const path = cognitiveEventLogPath(input.workdir, input.agentName, event.stream);
31
+ await appendFile(path, `${JSON.stringify(redactSecrets(event))}\n`, "utf-8");
32
+ return event;
33
+ }
34
+ export async function readCognitiveEvents(input) {
35
+ try {
36
+ const path = cognitiveEventLogPath(input.workdir, input.agentName, input.stream);
37
+ const content = await readFile(path, "utf-8");
38
+ const lines = content.split(/\r?\n/).filter((line) => line.trim());
39
+ const selected = typeof input.limit === "number" && input.limit > 0
40
+ ? lines.slice(-input.limit)
41
+ : lines;
42
+ return selected.map((line) => JSON.parse(line));
43
+ }
44
+ catch {
45
+ return [];
46
+ }
47
+ }
package/dist/config.js CHANGED
@@ -1,24 +1,42 @@
1
1
  import { readFile, writeFile, mkdir } from "fs/promises";
2
2
  import { existsSync } from "fs";
3
3
  import { join } from "path";
4
- import { homedir } from "os";
5
4
  import { randomBytes, randomUUID } from "crypto";
6
- const CONFIG_DIR = join(homedir(), ".akemon");
7
- const CONFIG_PATH = join(CONFIG_DIR, "config.json");
5
+ import { akemonHome } from "./akemon-home.js";
6
+ function configPath() {
7
+ return join(akemonHome(), "config.json");
8
+ }
8
9
  export function generateKey(prefix = "ak") {
9
10
  return prefix + "_" + randomBytes(24).toString("base64url");
10
11
  }
11
12
  export async function loadConfig() {
12
- if (!existsSync(CONFIG_PATH))
13
+ const path = configPath();
14
+ if (!existsSync(path))
13
15
  return {};
14
- const raw = await readFile(CONFIG_PATH, "utf-8");
16
+ const raw = await readFile(path, "utf-8");
15
17
  return JSON.parse(raw);
16
18
  }
17
19
  export async function saveConfig(config) {
18
- if (!existsSync(CONFIG_DIR)) {
19
- await mkdir(CONFIG_DIR, { recursive: true });
20
+ const dir = akemonHome();
21
+ if (!existsSync(dir)) {
22
+ await mkdir(dir, { recursive: true });
20
23
  }
21
- await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
24
+ await writeFile(configPath(), JSON.stringify(config, null, 2));
25
+ }
26
+ export async function getLocalManagerName() {
27
+ const config = await loadConfig();
28
+ return typeof config.local_manager === "string" && config.local_manager
29
+ ? config.local_manager
30
+ : null;
31
+ }
32
+ export async function setLocalManagerName(name) {
33
+ const config = await loadConfig();
34
+ const cleaned = name.trim();
35
+ if (!cleaned)
36
+ throw new Error("Local manager name must not be empty");
37
+ config.local_manager = cleaned;
38
+ await saveConfig(config);
39
+ return cleaned;
22
40
  }
23
41
  // Legacy: single key for direct mode
24
42
  export async function getOrCreateKey(explicitKey) {
@@ -35,6 +53,22 @@ export async function getOrCreateKey(explicitKey) {
35
53
  await saveConfig(config);
36
54
  return key;
37
55
  }
56
+ export async function getOrCreateLocalOwnerSecret() {
57
+ const config = await loadConfig();
58
+ if (typeof config.local_owner_key === "string" && config.local_owner_key) {
59
+ return config.local_owner_key;
60
+ }
61
+ // Keep existing local installations usable: older local owner endpoints used
62
+ // secret_key even when relay was disabled.
63
+ if (typeof config.secret_key === "string" && config.secret_key) {
64
+ config.local_owner_key = config.secret_key;
65
+ await saveConfig(config);
66
+ return config.local_owner_key;
67
+ }
68
+ config.local_owner_key = generateKey("ak_local");
69
+ await saveConfig(config);
70
+ return config.local_owner_key;
71
+ }
38
72
  export async function getOrCreateRelayCredentials() {
39
73
  const config = await loadConfig();
40
74
  let changed = false;
@@ -43,7 +77,9 @@ export async function getOrCreateRelayCredentials() {
43
77
  changed = true;
44
78
  }
45
79
  if (!config.secret_key) {
46
- config.secret_key = generateKey("ak_secret");
80
+ config.secret_key = typeof config.local_owner_key === "string" && config.local_owner_key
81
+ ? config.local_owner_key
82
+ : generateKey("ak_secret");
47
83
  changed = true;
48
84
  }
49
85
  if (!config.access_key) {
package/dist/context.js CHANGED
@@ -15,6 +15,7 @@
15
15
  import { readFile, writeFile, mkdir, appendFile, readdir, stat, unlink } from "fs/promises";
16
16
  import { join } from "path";
17
17
  import { localNow } from "./self.js";
18
+ import { createConversationMessage, } from "./akemon-message.js";
18
19
  function conversationsDir(workdir, agentName) {
19
20
  return join(workdir, ".akemon", "agents", agentName, "conversations");
20
21
  }
@@ -30,7 +31,7 @@ export function resolveConvId(publisherId, sessionId) {
30
31
  return "ses_anonymous";
31
32
  }
32
33
  /** Parse a conversation markdown file into structured data. */
33
- function parseConversation(content) {
34
+ function parseConversation(content, agentName = "unknown", convId = "unknown") {
34
35
  let summary = "";
35
36
  const rounds = [];
36
37
  const summaryMatch = content.match(/## Summary\n([\s\S]*?)(?=\n## Recent|$)/);
@@ -44,16 +45,28 @@ function parseConversation(content) {
44
45
  // Format: [ts] [kind] Role: text OR (legacy) [ts] Role: text
45
46
  const m = line.match(/^\[(.+?)\] (?:\[(order)\] )?(User|Agent): (.*)$/);
46
47
  if (m) {
48
+ const kind = m[2] ?? "chat";
49
+ const role = m[3];
50
+ const text = m[4];
47
51
  rounds.push({
48
52
  ts: m[1],
49
- kind: m[2] ?? "chat",
50
- role: m[3].toLowerCase(),
51
- content: m[4],
53
+ kind,
54
+ role: role.toLowerCase(),
55
+ content: text,
56
+ message: createConversationMessage({
57
+ agentName,
58
+ conversationId: convId,
59
+ role,
60
+ text,
61
+ kind,
62
+ createdAt: m[1],
63
+ }),
52
64
  });
53
65
  }
54
66
  else if (rounds.length > 0 && line !== "") {
55
67
  // Continuation line — append to previous round
56
68
  rounds[rounds.length - 1].content += "\n" + line;
69
+ rounds[rounds.length - 1].message.payload.text = rounds[rounds.length - 1].content;
57
70
  }
58
71
  }
59
72
  }
@@ -63,7 +76,7 @@ function parseConversation(content) {
63
76
  export async function loadConversation(workdir, agentName, convId) {
64
77
  try {
65
78
  const content = await readFile(conversationPath(workdir, agentName, convId), "utf-8");
66
- return parseConversation(content);
79
+ return parseConversation(content, agentName, convId);
67
80
  }
68
81
  catch {
69
82
  return { summary: "", rounds: [] };
@@ -88,6 +101,14 @@ export async function appendMessage(workdir, agentName, convId, role, message, k
88
101
  const kindTag = kind === "order" ? "[order] " : "";
89
102
  content = content.trimEnd() + "\n" + `[${ts}] ${kindTag}${role}: ${message}` + "\n";
90
103
  await writeFile(p, content);
104
+ return createConversationMessage({
105
+ agentName,
106
+ conversationId: convId,
107
+ role,
108
+ text: message,
109
+ kind,
110
+ createdAt: ts,
111
+ });
91
112
  }
92
113
  /** Append a user+agent round to a conversation file. Creates file if needed. */
93
114
  export async function appendRound(workdir, agentName, convId, userMsg, agentMsg) {
@@ -144,7 +165,7 @@ export async function listConversations(workdir, agentName) {
144
165
  try {
145
166
  const st = await stat(join(dir, f));
146
167
  const content = await readFile(join(dir, f), "utf-8");
147
- const conv = parseConversation(content);
168
+ const conv = parseConversation(content, agentName, id);
148
169
  results.push({
149
170
  id,
150
171
  lastActive: st.mtime.toISOString(),
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};