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
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { agentContactsDir, cleanAgentName } from "./akemon-home.js";
|
|
4
|
+
import { actorRef, createAkemonMessage, } from "./akemon-message.js";
|
|
5
|
+
export function normalizePublicAkemonProfile(raw, options = {}) {
|
|
6
|
+
if (!raw || typeof raw !== "object")
|
|
7
|
+
return null;
|
|
8
|
+
const item = raw;
|
|
9
|
+
const name = stringField(item, ["name", "id", "agent_id", "agentId"]);
|
|
10
|
+
if (!name)
|
|
11
|
+
return null;
|
|
12
|
+
const isPublic = booleanField(item, ["public", "is_public", "isPublic"]);
|
|
13
|
+
if (isPublic !== true && options.assumePublic !== true)
|
|
14
|
+
return null;
|
|
15
|
+
const description = stringField(item, ["description", "desc", "bio", "summary"]) || "";
|
|
16
|
+
const engine = stringField(item, ["engine", "model"]) || "";
|
|
17
|
+
const tags = stringListField(item, ["tags", "tag"]);
|
|
18
|
+
const interests = stringListField(item, ["interests", "interest_tags", "interestTags"]);
|
|
19
|
+
const profileUrl = stringField(item, ["profile_url", "profileUrl", "url"]);
|
|
20
|
+
return {
|
|
21
|
+
name,
|
|
22
|
+
description,
|
|
23
|
+
engine,
|
|
24
|
+
tags,
|
|
25
|
+
interests,
|
|
26
|
+
public: true,
|
|
27
|
+
status: stringField(item, ["status"]) || "unknown",
|
|
28
|
+
...optionalNumber("level", numberField(item, ["level"])),
|
|
29
|
+
...optionalNumber("totalTasks", numberField(item, ["total_tasks", "totalTasks"])),
|
|
30
|
+
...optionalNumber("successRate", numberField(item, ["success_rate", "successRate"])),
|
|
31
|
+
...optionalNumber("avgResponseMs", numberField(item, ["avg_response_ms", "avgResponseMs"])),
|
|
32
|
+
...(profileUrl ? { profileUrl } : {}),
|
|
33
|
+
source: options.source || "relay",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function discoverPublicAkemonProfiles(rawProfiles, query, options = {}) {
|
|
37
|
+
const terms = extractSearchTerms(query);
|
|
38
|
+
if (!terms.length)
|
|
39
|
+
return [];
|
|
40
|
+
const excluded = options.exclude ? cleanAgentName(options.exclude).toLowerCase() : undefined;
|
|
41
|
+
const results = [];
|
|
42
|
+
for (const raw of rawProfiles) {
|
|
43
|
+
const profile = normalizePublicAkemonProfile(raw, options);
|
|
44
|
+
if (!profile)
|
|
45
|
+
continue;
|
|
46
|
+
if (excluded && profile.name.toLowerCase() === excluded)
|
|
47
|
+
continue;
|
|
48
|
+
const scored = scorePublicAkemonProfile(profile, terms);
|
|
49
|
+
if (scored.score > 0)
|
|
50
|
+
results.push(scored);
|
|
51
|
+
}
|
|
52
|
+
results.sort((a, b) => {
|
|
53
|
+
if (b.score !== a.score)
|
|
54
|
+
return b.score - a.score;
|
|
55
|
+
const aOnline = a.profile.status === "online" ? 1 : 0;
|
|
56
|
+
const bOnline = b.profile.status === "online" ? 1 : 0;
|
|
57
|
+
if (bOnline !== aOnline)
|
|
58
|
+
return bOnline - aOnline;
|
|
59
|
+
return a.profile.name.localeCompare(b.profile.name);
|
|
60
|
+
});
|
|
61
|
+
return results.slice(0, Math.max(1, options.limit || 10));
|
|
62
|
+
}
|
|
63
|
+
export function createProfileQueryMessage(input) {
|
|
64
|
+
const transport = input.transport || "relay";
|
|
65
|
+
return createAkemonMessage({
|
|
66
|
+
type: "akemon.profile.query",
|
|
67
|
+
source: actorRef("agent", cleanAgentName(input.sourceAgent), transport),
|
|
68
|
+
target: actorRef("agent", cleanAgentName(input.targetAgent), transport),
|
|
69
|
+
conversationId: input.conversationId,
|
|
70
|
+
memoryScope: "public",
|
|
71
|
+
transport,
|
|
72
|
+
permissions: {
|
|
73
|
+
riskLevel: "low",
|
|
74
|
+
forbiddenActions: ["read-self-memory", "read-work-memory"],
|
|
75
|
+
},
|
|
76
|
+
payload: {
|
|
77
|
+
query: input.query.trim(),
|
|
78
|
+
interests: extractSearchTerms(input.query),
|
|
79
|
+
publicOnly: true,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export function createIntroductionRequestMessage(input) {
|
|
84
|
+
if (input.ownerApproved !== true) {
|
|
85
|
+
throw new Error("Owner approval is required before creating an introduction request");
|
|
86
|
+
}
|
|
87
|
+
const reason = input.reason.trim();
|
|
88
|
+
if (!reason)
|
|
89
|
+
throw new Error("Introduction reason is required");
|
|
90
|
+
const transport = input.transport || "relay";
|
|
91
|
+
return createAkemonMessage({
|
|
92
|
+
type: "akemon.intro.request",
|
|
93
|
+
source: actorRef("agent", cleanAgentName(input.sourceAgent), transport),
|
|
94
|
+
target: actorRef("agent", cleanAgentName(input.targetAgent), transport),
|
|
95
|
+
conversationId: input.conversationId,
|
|
96
|
+
memoryScope: "public",
|
|
97
|
+
transport,
|
|
98
|
+
permissions: {
|
|
99
|
+
requiresOwnerApproval: true,
|
|
100
|
+
riskLevel: "low",
|
|
101
|
+
forbiddenActions: ["read-self-memory", "read-work-memory", "send-without-owner-approval"],
|
|
102
|
+
},
|
|
103
|
+
metadata: { localOwnerApproved: true },
|
|
104
|
+
payload: {
|
|
105
|
+
reason,
|
|
106
|
+
publicOnly: true,
|
|
107
|
+
localOwnerApproved: true,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
export async function appendAcceptedSocialContact(record) {
|
|
112
|
+
const ownerAgent = cleanAgentName(record.ownerAgent);
|
|
113
|
+
const peerAgent = cleanAgentName(record.peerAgent);
|
|
114
|
+
const dir = agentContactsDir(ownerAgent);
|
|
115
|
+
await mkdir(dir, { recursive: true });
|
|
116
|
+
const normalized = {
|
|
117
|
+
schemaVersion: 1,
|
|
118
|
+
ts: record.ts,
|
|
119
|
+
ownerAgent,
|
|
120
|
+
peerAgent,
|
|
121
|
+
source: record.source,
|
|
122
|
+
...(record.reason ? { reason: record.reason } : {}),
|
|
123
|
+
...(record.messageId ? { messageId: record.messageId } : {}),
|
|
124
|
+
...(record.conversationId ? { conversationId: record.conversationId } : {}),
|
|
125
|
+
...(record.publicProfile ? { publicProfile: record.publicProfile } : {}),
|
|
126
|
+
};
|
|
127
|
+
await appendFile(join(dir, "accepted-social-contacts.jsonl"), `${JSON.stringify(normalized)}\n`, "utf-8");
|
|
128
|
+
}
|
|
129
|
+
function scorePublicAkemonProfile(profile, terms) {
|
|
130
|
+
let score = 0;
|
|
131
|
+
const reasons = new Set();
|
|
132
|
+
const name = normalizeSearchText(profile.name);
|
|
133
|
+
const description = normalizeSearchText(profile.description);
|
|
134
|
+
const engine = normalizeSearchText(profile.engine);
|
|
135
|
+
const tags = profile.tags.map((tag) => ({ raw: tag, normalized: normalizeSearchText(tag) }));
|
|
136
|
+
const interests = profile.interests.map((interest) => ({ raw: interest, normalized: normalizeSearchText(interest) }));
|
|
137
|
+
for (const term of terms) {
|
|
138
|
+
const tag = tags.find((item) => item.normalized === term || item.normalized.includes(term));
|
|
139
|
+
if (tag) {
|
|
140
|
+
score += 5;
|
|
141
|
+
reasons.add(`tag "${tag.raw}"`);
|
|
142
|
+
}
|
|
143
|
+
const interest = interests.find((item) => item.normalized === term || item.normalized.includes(term));
|
|
144
|
+
if (interest) {
|
|
145
|
+
score += 5;
|
|
146
|
+
reasons.add(`interest "${interest.raw}"`);
|
|
147
|
+
}
|
|
148
|
+
if (name.includes(term)) {
|
|
149
|
+
score += 3;
|
|
150
|
+
reasons.add(`name matches "${term}"`);
|
|
151
|
+
}
|
|
152
|
+
if (description.includes(term)) {
|
|
153
|
+
score += 2;
|
|
154
|
+
reasons.add(`description matches "${term}"`);
|
|
155
|
+
}
|
|
156
|
+
if (engine.includes(term)) {
|
|
157
|
+
score += 1;
|
|
158
|
+
reasons.add(`engine matches "${term}"`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
profile,
|
|
163
|
+
score,
|
|
164
|
+
reasons: Array.from(reasons).slice(0, 6),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function extractSearchTerms(value) {
|
|
168
|
+
const normalized = normalizeSearchText(value);
|
|
169
|
+
const terms = normalized
|
|
170
|
+
.split(" ")
|
|
171
|
+
.map((term) => term.trim())
|
|
172
|
+
.filter((term) => term.length > 1);
|
|
173
|
+
return Array.from(new Set(terms));
|
|
174
|
+
}
|
|
175
|
+
function normalizeSearchText(value) {
|
|
176
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
177
|
+
}
|
|
178
|
+
function stringField(item, keys) {
|
|
179
|
+
for (const key of keys) {
|
|
180
|
+
const value = item[key];
|
|
181
|
+
if (typeof value === "string" && value.trim())
|
|
182
|
+
return value.trim();
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
function stringListField(item, keys) {
|
|
187
|
+
for (const key of keys) {
|
|
188
|
+
const value = item[key];
|
|
189
|
+
if (Array.isArray(value)) {
|
|
190
|
+
return value
|
|
191
|
+
.filter((entry) => typeof entry === "string")
|
|
192
|
+
.map((entry) => entry.trim())
|
|
193
|
+
.filter(Boolean);
|
|
194
|
+
}
|
|
195
|
+
if (typeof value === "string" && value.trim()) {
|
|
196
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
function booleanField(item, keys) {
|
|
202
|
+
for (const key of keys) {
|
|
203
|
+
const value = item[key];
|
|
204
|
+
if (typeof value === "boolean")
|
|
205
|
+
return value;
|
|
206
|
+
if (typeof value === "string") {
|
|
207
|
+
const normalized = value.trim().toLowerCase();
|
|
208
|
+
if (normalized === "true")
|
|
209
|
+
return true;
|
|
210
|
+
if (normalized === "false")
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
function numberField(item, keys) {
|
|
217
|
+
for (const key of keys) {
|
|
218
|
+
const value = item[key];
|
|
219
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
220
|
+
return value;
|
|
221
|
+
if (typeof value === "string" && value.trim()) {
|
|
222
|
+
const parsed = Number(value);
|
|
223
|
+
if (Number.isFinite(parsed))
|
|
224
|
+
return parsed;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
function optionalNumber(key, value) {
|
|
230
|
+
return value === undefined ? {} : { [key]: value };
|
|
231
|
+
}
|