@echomem/mcp 1.4.22 → 1.4.24
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/dist/hud/web.js +3 -3
- package/dist/index.js +300 -7
- package/dist/migrate.js +6 -5
- package/dist/package-metadata.js +3 -0
- package/dist/setup-page/client-core.js +4 -1
- package/dist/setup-page/client-extraction.js +20 -13
- package/dist/setup-page/styles-mvp.js +103 -0
- package/dist/setup-preview.js +4 -4
- package/dist/setup.js +11 -0
- package/dist/v1-contract.js +248 -11
- package/package.json +1 -1
package/dist/hud/web.js
CHANGED
|
@@ -1723,7 +1723,7 @@ export const HUD_HTML = String.raw `<!doctype html>
|
|
|
1723
1723
|
if (previewParams.get("capture") === "1") hud.classList.add("capture-preview");
|
|
1724
1724
|
if (previewColor === "green" || previewColor === "amber" || previewColor === "red") {
|
|
1725
1725
|
const previewProcessingUsed = previewColor === "red" ? 200000 : previewColor === "amber" ? 163000 : 84000;
|
|
1726
|
-
const previewSearchUsed = previewColor === "red" ?
|
|
1726
|
+
const previewSearchUsed = previewColor === "red" ? 500 : previewColor === "amber" ? 410 : 160;
|
|
1727
1727
|
billingStatus = {
|
|
1728
1728
|
ok: true,
|
|
1729
1729
|
state: "ok",
|
|
@@ -1731,8 +1731,8 @@ export const HUD_HTML = String.raw `<!doctype html>
|
|
|
1731
1731
|
paid: true,
|
|
1732
1732
|
pricingUrl: "https://yeahecho.com/pricing?source=hud_preview",
|
|
1733
1733
|
memoryProcessingQuota: { used: previewProcessingUsed, limit: 200000, remaining: 200000 - previewProcessingUsed, resetAt: new Date(Date.now() + 31 * 60 * 60 * 1000).toISOString() },
|
|
1734
|
-
memorySearchQuota: { used: previewSearchUsed, limit:
|
|
1735
|
-
historicalConversationQuota: { used: 191, limit:
|
|
1734
|
+
memorySearchQuota: { used: previewSearchUsed, limit: 500, remaining: 500 - previewSearchUsed, resetAt: new Date(Date.now() + 31 * 60 * 60 * 1000).toISOString() },
|
|
1735
|
+
historicalConversationQuota: { used: 191, limit: 500, remaining: 309 },
|
|
1736
1736
|
};
|
|
1737
1737
|
const sample = {
|
|
1738
1738
|
client: "codex",
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
import { ZodError } from "zod";
|
|
7
|
-
import { canonicalToolNames, deleteMemorySchema, getByContextSchema, keywordsSchema, listFriendsSchema, listToolSpecs, othersSchema, publicMemorySchema, resolveCanonicalToolName, saveConversationSchema, searchMemoriesSchema, searchUsersSchema, sendFriendRequestSchema, timeRangeSchema, } from "./v1-contract.js";
|
|
7
|
+
import { canonicalToolNames, completeGroupPublicationSchema, createGroupInviteSchema, createGroupSchema, deleteMemorySchema, getByContextSchema, groupContextSchema, joinGroupSchema, keywordsSchema, listFriendsSchema, listToolSpecs, othersSchema, publishBatchToGroupSchema, publishToGroupSchema, prepareGroupPublicationSchema, publicMemorySchema, resolveCanonicalToolName, saveConversationSchema, searchMemoriesSchema, searchUsersSchema, sendFriendRequestSchema, timeRangeSchema, updateGroupProfileSchema, } from "./v1-contract.js";
|
|
8
8
|
import { KeyStore } from "./keystore.js";
|
|
9
9
|
import { EventLogger, hashText } from "./events.js";
|
|
10
10
|
import { buildReportText } from "./report.js";
|
|
@@ -488,12 +488,20 @@ function inputAnalyticsForTool(canonicalName, args) {
|
|
|
488
488
|
limit: numberArg(a, "limit"),
|
|
489
489
|
};
|
|
490
490
|
}
|
|
491
|
-
case canonicalToolNames.publicMemory:
|
|
491
|
+
case canonicalToolNames.publicMemory:
|
|
492
|
+
case canonicalToolNames.publishToGroup: {
|
|
492
493
|
const memoryId = readString(a, "memoryId");
|
|
493
494
|
return {
|
|
494
495
|
memory_id_hash: memoryId ? hashText(memoryId) : undefined,
|
|
495
496
|
};
|
|
496
497
|
}
|
|
498
|
+
case canonicalToolNames.publishBatchToGroup: {
|
|
499
|
+
const memoryIds = Array.isArray(a.memoryIds) ? a.memoryIds.filter((id) => typeof id === "string") : [];
|
|
500
|
+
return {
|
|
501
|
+
memory_count: memoryIds.length,
|
|
502
|
+
context_id_hash: typeof a.contextId === "string" ? hashText(a.contextId) : undefined,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
497
505
|
default:
|
|
498
506
|
return {};
|
|
499
507
|
}
|
|
@@ -944,6 +952,77 @@ class EchoMemApiClient {
|
|
|
944
952
|
throw new Error(`get_public_memory failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
|
|
945
953
|
}
|
|
946
954
|
}
|
|
955
|
+
async getGroupContext(args) {
|
|
956
|
+
groupContextSchema.parse(args ?? {});
|
|
957
|
+
try {
|
|
958
|
+
const response = await this.axios.get("/api/extension/social/groups/current");
|
|
959
|
+
return response.data;
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
throw new Error(`get_group_context failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
async createGroup(args) {
|
|
966
|
+
const parsed = createGroupSchema.parse(args ?? {});
|
|
967
|
+
const response = await this.axios.post("/api/extension/social/groups", parsed);
|
|
968
|
+
return response.data;
|
|
969
|
+
}
|
|
970
|
+
async createGroupInvite(args) {
|
|
971
|
+
const parsed = createGroupInviteSchema.parse(args ?? {});
|
|
972
|
+
const response = await this.axios.post("/api/extension/social/groups/current/invite", parsed);
|
|
973
|
+
return response.data;
|
|
974
|
+
}
|
|
975
|
+
async joinGroup(args) {
|
|
976
|
+
const parsed = joinGroupSchema.parse(args ?? {});
|
|
977
|
+
const response = await this.axios.post("/api/extension/social/groups/join", parsed);
|
|
978
|
+
return response.data;
|
|
979
|
+
}
|
|
980
|
+
async prepareGroupPublication(args) {
|
|
981
|
+
const parsed = prepareGroupPublicationSchema.parse(args ?? {});
|
|
982
|
+
const enc = await this.encState();
|
|
983
|
+
const response = await this.axios.post("/api/extension/social/groups/current/publications/prepare", parsed, { headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined });
|
|
984
|
+
return response.data;
|
|
985
|
+
}
|
|
986
|
+
async updateGroupProfile(args) {
|
|
987
|
+
const parsed = updateGroupProfileSchema.parse(args ?? {});
|
|
988
|
+
const response = await this.axios.patch("/api/extension/social/groups/current/profile", parsed);
|
|
989
|
+
return response.data;
|
|
990
|
+
}
|
|
991
|
+
async completeGroupPublication(args) {
|
|
992
|
+
const parsed = completeGroupPublicationSchema.parse(args ?? {});
|
|
993
|
+
const enc = await this.encState();
|
|
994
|
+
const response = await this.axios.post("/api/extension/social/groups/current/publications/complete", parsed, { headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined });
|
|
995
|
+
return response.data;
|
|
996
|
+
}
|
|
997
|
+
async publishMemoryToGroup(args) {
|
|
998
|
+
const parsed = publishToGroupSchema.parse(args ?? {});
|
|
999
|
+
const enc = await this.encState();
|
|
1000
|
+
try {
|
|
1001
|
+
const response = await this.axios.post(`/api/extension/social/groups/current/memories/${encodeURIComponent(parsed.memoryId)}/publish`, {}, {
|
|
1002
|
+
headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined,
|
|
1003
|
+
});
|
|
1004
|
+
return response.data;
|
|
1005
|
+
}
|
|
1006
|
+
catch (error) {
|
|
1007
|
+
throw new Error(`publish_memory_to_group failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
async publishMemoriesToGroup(args) {
|
|
1011
|
+
const parsed = publishBatchToGroupSchema.parse(args ?? {});
|
|
1012
|
+
const enc = await this.encState();
|
|
1013
|
+
try {
|
|
1014
|
+
const response = await this.axios.post("/api/extension/social/groups/current/memories/publish-batch", {
|
|
1015
|
+
memoryIds: parsed.memoryIds,
|
|
1016
|
+
contextId: parsed.contextId,
|
|
1017
|
+
}, {
|
|
1018
|
+
headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined,
|
|
1019
|
+
});
|
|
1020
|
+
return response.data;
|
|
1021
|
+
}
|
|
1022
|
+
catch (error) {
|
|
1023
|
+
throw new Error(`publish_memories_to_group failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
947
1026
|
}
|
|
948
1027
|
const SERVER_VERSION = MCP_PACKAGE_VERSION;
|
|
949
1028
|
class EchoMemMCPServer {
|
|
@@ -1121,6 +1200,24 @@ class EchoMemMCPServer {
|
|
|
1121
1200
|
return await this.handleOthers(request.params.arguments);
|
|
1122
1201
|
case canonicalToolNames.publicMemory:
|
|
1123
1202
|
return await this.handlePublicMemory(request.params.arguments);
|
|
1203
|
+
case canonicalToolNames.groupContext:
|
|
1204
|
+
return await this.handleGroupContext(request.params.arguments);
|
|
1205
|
+
case canonicalToolNames.createGroup:
|
|
1206
|
+
return await this.handleCreateGroup(request.params.arguments);
|
|
1207
|
+
case canonicalToolNames.createGroupInvite:
|
|
1208
|
+
return await this.handleCreateGroupInvite(request.params.arguments);
|
|
1209
|
+
case canonicalToolNames.joinGroup:
|
|
1210
|
+
return await this.handleJoinGroup(request.params.arguments);
|
|
1211
|
+
case canonicalToolNames.prepareGroupPublication:
|
|
1212
|
+
return await this.handlePrepareGroupPublication(request.params.arguments);
|
|
1213
|
+
case canonicalToolNames.updateGroupProfile:
|
|
1214
|
+
return await this.handleUpdateGroupProfile(request.params.arguments);
|
|
1215
|
+
case canonicalToolNames.completeGroupPublication:
|
|
1216
|
+
return await this.handleCompleteGroupPublication(request.params.arguments);
|
|
1217
|
+
case canonicalToolNames.publishToGroup:
|
|
1218
|
+
return await this.handlePublishToGroup(request.params.arguments);
|
|
1219
|
+
case canonicalToolNames.publishBatchToGroup:
|
|
1220
|
+
return await this.handlePublishBatchToGroup(request.params.arguments);
|
|
1124
1221
|
case canonicalToolNames.delete:
|
|
1125
1222
|
return await this.handleDelete(request.params.arguments, rec);
|
|
1126
1223
|
default:
|
|
@@ -1302,7 +1399,7 @@ class EchoMemMCPServer {
|
|
|
1302
1399
|
return { content: [{ type: "text", text: "No relevant memories found." }] };
|
|
1303
1400
|
}
|
|
1304
1401
|
const formattedResults = memories
|
|
1305
|
-
.map((m, idx) => `[Result ${idx + 1}] (Similarity: ${m.similarity_score?.toFixed(3) || "N/A"})
|
|
1402
|
+
.map((m, idx) => `[Result ${idx + 1}] Memory ID: ${m.id || "unknown"} (Similarity: ${m.similarity_score?.toFixed(3) || "N/A"})
|
|
1306
1403
|
Time: ${m.time} | Location: ${m.location}
|
|
1307
1404
|
Category: ${m.category} | Object: ${m.object} | Emotion: ${m.emotion}
|
|
1308
1405
|
Description: ${m.description}
|
|
@@ -1504,11 +1601,11 @@ Details: ${m.details || "N/A"}`)
|
|
|
1504
1601
|
const memories = payload?.memories ?? [];
|
|
1505
1602
|
if (!memories.length) {
|
|
1506
1603
|
const scope = parsed.ownerUserId
|
|
1507
|
-
? ` for
|
|
1604
|
+
? ` for peer ${parsed.ownerUserId}`
|
|
1508
1605
|
: parsed.ownerName
|
|
1509
|
-
? ` for
|
|
1606
|
+
? ` for peer ${parsed.ownerName}`
|
|
1510
1607
|
: parsed.target
|
|
1511
|
-
? ` for
|
|
1608
|
+
? ` for peer ${parsed.target}`
|
|
1512
1609
|
: parsed.targetFriendIds?.length
|
|
1513
1610
|
? ` for ${parsed.targetFriendIds.length} selected friends`
|
|
1514
1611
|
: parsed.targetFriendNames?.length
|
|
@@ -1521,7 +1618,12 @@ Details: ${m.details || "N/A"}`)
|
|
|
1521
1618
|
}
|
|
1522
1619
|
const metadata = [
|
|
1523
1620
|
typeof payload?.friendCount === "number" ? `friendCount=${payload.friendCount}` : "",
|
|
1524
|
-
typeof payload?.
|
|
1621
|
+
typeof payload?.groupPeerCount === "number" ? `groupPeerCount=${payload.groupPeerCount}` : "",
|
|
1622
|
+
typeof payload?.searchedPeerCount === "number"
|
|
1623
|
+
? `searchedPeerCount=${payload.searchedPeerCount}`
|
|
1624
|
+
: typeof payload?.searchedFriendCount === "number"
|
|
1625
|
+
? `searchedPeerCount=${payload.searchedFriendCount}`
|
|
1626
|
+
: "",
|
|
1525
1627
|
typeof payload?.memoryViewsInserted === "number" ? `memoryViewsInserted=${payload.memoryViewsInserted}` : "",
|
|
1526
1628
|
].filter(Boolean).join(", ");
|
|
1527
1629
|
const formattedResults = memories
|
|
@@ -1674,6 +1776,197 @@ Details: ${m.details || "N/A"}`;
|
|
|
1674
1776
|
content: [{ type: "text", text }],
|
|
1675
1777
|
};
|
|
1676
1778
|
}
|
|
1779
|
+
async handleGroupContext(args) {
|
|
1780
|
+
groupContextSchema.parse(args ?? {});
|
|
1781
|
+
const payload = await this.client.getGroupContext(args);
|
|
1782
|
+
const group = isRecord(payload?.group) ? payload.group : null;
|
|
1783
|
+
const participants = Array.isArray(payload?.participants)
|
|
1784
|
+
? payload.participants.filter(isRecord)
|
|
1785
|
+
: [];
|
|
1786
|
+
const currentParticipant = isRecord(payload?.currentParticipant)
|
|
1787
|
+
? payload.currentParticipant
|
|
1788
|
+
: null;
|
|
1789
|
+
const coverage = isRecord(payload?.coverage) ? payload.coverage : {};
|
|
1790
|
+
if (!group) {
|
|
1791
|
+
return {
|
|
1792
|
+
content: [{
|
|
1793
|
+
type: "text",
|
|
1794
|
+
text: "You are not assigned to a company group yet. Ask an EchoMem administrator to add you before using group orientation or group publishing.",
|
|
1795
|
+
}],
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
const participantText = participants.map((participant, index) => [
|
|
1799
|
+
`[${index + 1}] ${readString(participant, "displayName") ?? "Unnamed participant"}`,
|
|
1800
|
+
`User ID: ${readString(participant, "userId") ?? "unknown"}`,
|
|
1801
|
+
`Title: ${readString(participant, "title") ?? "Not declared"}`,
|
|
1802
|
+
`Declared responsibility: ${readString(participant, "responsibilitySummary") ?? "Not provided"}`,
|
|
1803
|
+
`Published memories: ${readNumber(participant, "publicMemoryCount") ?? 0}`,
|
|
1804
|
+
].join("\n")).join("\n\n");
|
|
1805
|
+
const participantCount = readNumber(coverage, "participantCount") ?? participants.length;
|
|
1806
|
+
const coveredCount = readNumber(coverage, "participantsWithPublishedMemories") ?? 0;
|
|
1807
|
+
const totalPublished = readNumber(coverage, "totalPublishedMemories") ?? 0;
|
|
1808
|
+
const text = [
|
|
1809
|
+
`Company group: ${readString(group, "name") ?? "Unnamed group"}`,
|
|
1810
|
+
readString(group, "description") ? `Description: ${readString(group, "description")}` : "",
|
|
1811
|
+
`Coverage: ${coveredCount} of ${participantCount} participants have published memories (${totalPublished} total).`,
|
|
1812
|
+
"",
|
|
1813
|
+
participantText,
|
|
1814
|
+
"",
|
|
1815
|
+
"Declared titles and responsibilities are directory facts. Use search_others_memories for current work evidence, and label suggested contribution areas as inference that should be confirmed with the team.",
|
|
1816
|
+
currentParticipant
|
|
1817
|
+
&& (!readString(currentParticipant, "title") || !readString(currentParticipant, "responsibilitySummary"))
|
|
1818
|
+
? "Your group profile is incomplete. Use prepare_group_publication to review your memory evidence, propose the missing fields, and save them only after confirmation with update_group_profile."
|
|
1819
|
+
: "",
|
|
1820
|
+
].filter(Boolean).join("\n");
|
|
1821
|
+
return { content: [{ type: "text", text }] };
|
|
1822
|
+
}
|
|
1823
|
+
async handlePublishToGroup(args) {
|
|
1824
|
+
const parsed = publishToGroupSchema.parse(args ?? {});
|
|
1825
|
+
const payload = await this.client.publishMemoryToGroup(args);
|
|
1826
|
+
if (payload?.success === false) {
|
|
1827
|
+
throw new Error(`EchoMem API Error: ${payload.error || "publish failed"}`);
|
|
1828
|
+
}
|
|
1829
|
+
const group = isRecord(payload?.group) ? payload.group : {};
|
|
1830
|
+
const groupName = readString(group, "name") ?? "your company group";
|
|
1831
|
+
const memberCount = typeof payload?.searchableByGroupMemberCount === "number"
|
|
1832
|
+
? payload.searchableByGroupMemberCount
|
|
1833
|
+
: 0;
|
|
1834
|
+
const alreadyPublished = payload?.alreadyPublished === true;
|
|
1835
|
+
return {
|
|
1836
|
+
content: [{
|
|
1837
|
+
type: "text",
|
|
1838
|
+
text: alreadyPublished
|
|
1839
|
+
? `Memory ${parsed.memoryId} was already published to ${groupName}. It is searchable by up to ${memberCount} other group members.`
|
|
1840
|
+
: `Published memory ${parsed.memoryId} to ${groupName}. It is now searchable by up to ${memberCount} other group members.`,
|
|
1841
|
+
}],
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
async handleCreateGroup(args) {
|
|
1845
|
+
const parsed = createGroupSchema.parse(args ?? {});
|
|
1846
|
+
const payload = await this.client.createGroup(parsed);
|
|
1847
|
+
return { content: [{ type: "text", text: `Created company group ${payload?.group?.name ?? parsed.name}. Generate an invite code when you are ready to add teammates.` }] };
|
|
1848
|
+
}
|
|
1849
|
+
async handleCreateGroupInvite(args) {
|
|
1850
|
+
createGroupInviteSchema.parse(args ?? {});
|
|
1851
|
+
const payload = await this.client.createGroupInvite(args);
|
|
1852
|
+
return {
|
|
1853
|
+
content: [{
|
|
1854
|
+
type: "text",
|
|
1855
|
+
text: [
|
|
1856
|
+
`Invite code for ${payload?.group?.name ?? "your group"}: ${payload?.code}`,
|
|
1857
|
+
`Expires: ${payload?.expiresAt}`,
|
|
1858
|
+
`Maximum uses: ${payload?.maxUses}`,
|
|
1859
|
+
"Share this secret code only with intended teammates.",
|
|
1860
|
+
].join("\n"),
|
|
1861
|
+
}],
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
async handleJoinGroup(args) {
|
|
1865
|
+
const parsed = joinGroupSchema.parse(args ?? {});
|
|
1866
|
+
const payload = await this.client.joinGroup(parsed);
|
|
1867
|
+
return {
|
|
1868
|
+
content: [{
|
|
1869
|
+
type: "text",
|
|
1870
|
+
text: payload?.alreadyMember
|
|
1871
|
+
? `You are already a member of ${payload?.group?.name ?? "this group"}. No memories were published. Use prepare_group_publication to review memories and propose any missing title or responsibility fields.`
|
|
1872
|
+
: `Joined ${payload?.group?.name ?? "the company group"}. No memories were published. Next use prepare_group_publication to review candidates, infer a proposed title and responsibility summary, and ask the user to confirm that profile together with the publication preview.`,
|
|
1873
|
+
}],
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
async handlePrepareGroupPublication(args) {
|
|
1877
|
+
const parsed = prepareGroupPublicationSchema.parse(args ?? {});
|
|
1878
|
+
const payload = await this.client.prepareGroupPublication(parsed);
|
|
1879
|
+
const candidates = Array.isArray(payload?.candidates) ? payload.candidates : [];
|
|
1880
|
+
const participantProfile = isRecord(payload?.participantProfile)
|
|
1881
|
+
? payload.participantProfile
|
|
1882
|
+
: {};
|
|
1883
|
+
const formatted = candidates.map((candidate, index) => [
|
|
1884
|
+
`[${index + 1}] Memory ID: ${readString(candidate, "memoryId") ?? "unknown"}`,
|
|
1885
|
+
`Created: ${readString(candidate, "createdAt") ?? "unknown"}`,
|
|
1886
|
+
`Category: ${readString(candidate, "category") ?? "unknown"}`,
|
|
1887
|
+
`Description: ${readString(candidate, "description") ?? ""}`,
|
|
1888
|
+
readString(candidate, "details") ? `Details: ${readString(candidate, "details")}` : "",
|
|
1889
|
+
`Already published: ${candidate.alreadyPublished === true ? "yes" : "no"}`,
|
|
1890
|
+
`Exact content duplicate: ${candidate.exactContentDuplicate === true ? "yes" : "no"}`,
|
|
1891
|
+
].filter(Boolean).join("\n")).join("\n\n");
|
|
1892
|
+
return {
|
|
1893
|
+
content: [{
|
|
1894
|
+
type: "text",
|
|
1895
|
+
text: [
|
|
1896
|
+
`Prepared publication scan ${payload?.scanId} for ${payload?.group?.name ?? "your group"}.`,
|
|
1897
|
+
`Window: ${payload?.windowStart ?? "context start"} → ${payload?.windowEnd}`,
|
|
1898
|
+
`Candidates: ${candidates.length}. Nothing has been published.`,
|
|
1899
|
+
`Current title: ${readString(participantProfile, "title") ?? "Not declared"}`,
|
|
1900
|
+
`Current declared responsibility: ${readString(participantProfile, "responsibilitySummary") ?? "Not provided"}`,
|
|
1901
|
+
"",
|
|
1902
|
+
formatted,
|
|
1903
|
+
"",
|
|
1904
|
+
"Select exact memory IDs matching the user's instruction. From memory evidence, draft a concise title and responsibility summary. Show the profile proposal and memory preview together, then ask for one explicit confirmation. After confirmation, call update_group_profile and complete_group_publication.",
|
|
1905
|
+
].join("\n"),
|
|
1906
|
+
}],
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
async handleUpdateGroupProfile(args) {
|
|
1910
|
+
const parsed = updateGroupProfileSchema.parse(args ?? {});
|
|
1911
|
+
const payload = await this.client.updateGroupProfile(parsed);
|
|
1912
|
+
const participant = isRecord(payload?.participant) ? payload.participant : {};
|
|
1913
|
+
return {
|
|
1914
|
+
content: [{
|
|
1915
|
+
type: "text",
|
|
1916
|
+
text: [
|
|
1917
|
+
"Confirmed company-group profile updated.",
|
|
1918
|
+
`Display name: ${readString(participant, "displayName") ?? parsed.displayName ?? "unchanged"}`,
|
|
1919
|
+
`Title: ${readString(participant, "title") ?? parsed.title}`,
|
|
1920
|
+
`Declared responsibility: ${readString(participant, "responsibilitySummary") ?? parsed.responsibilitySummary}`,
|
|
1921
|
+
"These fields are now declared directory facts visible to group members.",
|
|
1922
|
+
].join("\n"),
|
|
1923
|
+
}],
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
async handleCompleteGroupPublication(args) {
|
|
1927
|
+
const parsed = completeGroupPublicationSchema.parse(args ?? {});
|
|
1928
|
+
const payload = await this.client.completeGroupPublication(parsed);
|
|
1929
|
+
const memories = Array.isArray(payload?.memories) ? payload.memories : [];
|
|
1930
|
+
const published = memories.filter((memory) => memory.alreadyPublished !== true && memory.skippedDuplicate !== true).length;
|
|
1931
|
+
const duplicates = memories.filter((memory) => memory.skippedDuplicate === true).length;
|
|
1932
|
+
return {
|
|
1933
|
+
content: [{
|
|
1934
|
+
type: "text",
|
|
1935
|
+
text: [
|
|
1936
|
+
`Completed publication scan ${parsed.scanId}.`,
|
|
1937
|
+
`Newly published: ${published}.`,
|
|
1938
|
+
`Exact duplicates skipped: ${duplicates}.`,
|
|
1939
|
+
`Selected memory IDs: ${parsed.memoryIds.join(", ") || "(none)"}.`,
|
|
1940
|
+
"The scan cursor advanced; encrypted originals and global public settings were unchanged.",
|
|
1941
|
+
].join("\n"),
|
|
1942
|
+
}],
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
async handlePublishBatchToGroup(args) {
|
|
1946
|
+
const parsed = publishBatchToGroupSchema.parse(args ?? {});
|
|
1947
|
+
const payload = await this.client.publishMemoriesToGroup(parsed);
|
|
1948
|
+
if (payload?.success === false) {
|
|
1949
|
+
throw new Error(`EchoMem API Error: ${payload.error || "batch publish failed"}`);
|
|
1950
|
+
}
|
|
1951
|
+
const group = isRecord(payload?.group) ? payload.group : {};
|
|
1952
|
+
const groupName = readString(group, "name") ?? "your company group";
|
|
1953
|
+
const publishedCount = typeof payload?.publishedCount === "number" ? payload.publishedCount : 0;
|
|
1954
|
+
const alreadyPublishedCount = typeof payload?.alreadyPublishedCount === "number"
|
|
1955
|
+
? payload.alreadyPublishedCount
|
|
1956
|
+
: 0;
|
|
1957
|
+
return {
|
|
1958
|
+
content: [{
|
|
1959
|
+
type: "text",
|
|
1960
|
+
text: [
|
|
1961
|
+
`Published ${publishedCount} memory snapshots to ${groupName}.`,
|
|
1962
|
+
alreadyPublishedCount ? `${alreadyPublishedCount} were already published and were refreshed.` : "",
|
|
1963
|
+
`Selected memory IDs: ${parsed.memoryIds.join(", ")}`,
|
|
1964
|
+
parsed.selectionReason ? `Selection reason: ${parsed.selectionReason}` : "",
|
|
1965
|
+
"The encrypted originals and their global public settings were not changed.",
|
|
1966
|
+
].filter(Boolean).join("\n"),
|
|
1967
|
+
}],
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1677
1970
|
async handleDelete(args, rec) {
|
|
1678
1971
|
const parsed = deleteMemorySchema.parse(args ?? {});
|
|
1679
1972
|
if (rec) {
|
package/dist/migrate.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Why this lives in the bridge (client-side): the session logs exist ONLY on the user's machine
|
|
6
6
|
* (~/.codex/sessions, ~/.claude/projects). The bridge discovers each session, assembles it into a
|
|
7
|
-
* text-turn-only `## ` transcript, and feeds it to the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* text-turn-only `## ` transcript, and feeds it to the shared durable import queue
|
|
8
|
+
* through MCP-owned policy entrypoints. POST /api/extension/mcp/historical-import-sessions
|
|
9
|
+
* creates one import_jobs row per session, then the bridge POSTs each transcript to
|
|
10
|
+
* /api/extension/mcp/historical-import-jobs/{id}/run. The web dashboard polls
|
|
10
11
|
* GET /import-sessions/{id} for live progress — the bridge owns local-file access, the server owns the
|
|
11
12
|
* queue + status. (Headless CLI also works; it just shows progress in the terminal.)
|
|
12
13
|
*
|
|
@@ -1353,7 +1354,7 @@ async function createImportSession(client, sessions, bareId, userTz, signal) {
|
|
|
1353
1354
|
sourceDate: s.firstTs,
|
|
1354
1355
|
userTz,
|
|
1355
1356
|
}));
|
|
1356
|
-
const res = await client.post("/api/extension/import-sessions", { items }, signal ? { signal } : undefined);
|
|
1357
|
+
const res = await client.post("/api/extension/mcp/historical-import-sessions", { items }, signal ? { signal } : undefined);
|
|
1357
1358
|
const data = res.data || {};
|
|
1358
1359
|
const serverConcurrency = Number(data.session?.concurrency);
|
|
1359
1360
|
return {
|
|
@@ -1378,7 +1379,7 @@ async function runImportJob(client, jobId, s, userTz, encKey) {
|
|
|
1378
1379
|
const cfg = encKey ? { headers: { "X-Encryption-Key": encKey } } : undefined;
|
|
1379
1380
|
for (let attempt = 0;; attempt++) {
|
|
1380
1381
|
try {
|
|
1381
|
-
const res = await client.post(`/api/extension/import-jobs/${jobId}/run`, body, cfg);
|
|
1382
|
+
const res = await client.post(`/api/extension/mcp/historical-import-jobs/${jobId}/run`, body, cfg);
|
|
1382
1383
|
const data = res.data || {};
|
|
1383
1384
|
if (data.claimed === false) {
|
|
1384
1385
|
// Already completed on a prior run → idempotent, not an error; otherwise it didn't start.
|
package/dist/package-metadata.js
CHANGED
|
@@ -28,6 +28,9 @@ export const MCP_SERVER_INSTRUCTIONS = [
|
|
|
28
28
|
`If the user or local config expects a newer EchoMem MCP version than ${MCP_PACKAGE_VERSION}, update once with \`${MCP_UPDATE_ALL_COMMAND}\` and start a new MCP session.`,
|
|
29
29
|
"Use echomem_update_status to check whether npm has a newer bridge; it is cached and non-blocking during normal tool listing.",
|
|
30
30
|
"Do not auto-update on every MCP startup; this bridge is intentionally stable between explicit updates.",
|
|
31
|
+
"For company-group sharing, use get_group_context for orientation; create_memory_group/create_group_invite/join_memory_group for membership; and prepare_group_publication as a no-publication preview.",
|
|
32
|
+
"After joining or when profile fields are missing, use candidate memory evidence to propose a title and responsibility summary. Ask the user to confirm that proposal together with the publication preview, then call update_group_profile and complete_group_publication.",
|
|
33
|
+
"Never save an inferred group profile or complete a group publication without explicit confirmation, and never store or log an echo_grp_ invite code.",
|
|
31
34
|
].join(" ");
|
|
32
35
|
export function withMcpVersion(description) {
|
|
33
36
|
return `${description}\n\nEchoMem MCP bridge: ${MCP_PACKAGE_LABEL}. If this version is stale, update once with \`${MCP_UPDATE_ALL_COMMAND}\` (or add \`--client cursor|windsurf|claude-desktop|claude-code|codex\` for a single client), then start a new MCP session. Do not run updates repeatedly or on every startup.`;
|
|
@@ -131,7 +131,10 @@ export const SETUP_PAGE_CLIENT_CORE = String.raw ` var params = new URLSear
|
|
|
131
131
|
}
|
|
132
132
|
async function getJson(path) {
|
|
133
133
|
var sep = path.indexOf("?") >= 0 ? "&" : "?";
|
|
134
|
-
var res = await fetch(path + sep + "nonce=" + encodeURIComponent(nonce), {
|
|
134
|
+
var res = await fetch(path + sep + "nonce=" + encodeURIComponent(nonce), {
|
|
135
|
+
credentials: "omit",
|
|
136
|
+
cache: "no-store"
|
|
137
|
+
});
|
|
135
138
|
if (!res.ok) throw new Error(await res.text() || ("HTTP " + res.status));
|
|
136
139
|
return await res.json();
|
|
137
140
|
}
|
|
@@ -627,8 +627,8 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
627
627
|
return Math.max(0, Math.floor(quota.remaining));
|
|
628
628
|
}
|
|
629
629
|
var plan = String(billingStatus && billingStatus.plan || "").toLowerCase();
|
|
630
|
-
if (plan === "power" || plan === "enterprise") return
|
|
631
|
-
if (paidRecallPlan(plan)) return
|
|
630
|
+
if (plan === "power" || plan === "enterprise") return 2000;
|
|
631
|
+
if (paidRecallPlan(plan)) return 500;
|
|
632
632
|
return 100;
|
|
633
633
|
}
|
|
634
634
|
function selectedSessionKeyList() {
|
|
@@ -912,7 +912,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
912
912
|
homeName: "Power",
|
|
913
913
|
promise: "More room. More agents. Much more Echo.",
|
|
914
914
|
price: "$100",
|
|
915
|
-
cadence: "per month ·
|
|
915
|
+
cadence: "per month · 14-day trial",
|
|
916
916
|
sticker: "/hud-assets/echo-pricing-power-sticker.png",
|
|
917
917
|
stickerAlt: "Power Echo arrives with a gold key and a crew of notebook helpers.",
|
|
918
918
|
features: ["2,000 past chats", "agent-heavy memory", "2,000 context returns / week"]
|
|
@@ -923,7 +923,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
923
923
|
homeName: "Pro",
|
|
924
924
|
promise: "A room for the work you return to every day.",
|
|
925
925
|
price: "$20",
|
|
926
|
-
cadence: "per month ·
|
|
926
|
+
cadence: "per month · 14-day trial",
|
|
927
927
|
sticker: "/hud-assets/echo-pricing-pro-sticker.png",
|
|
928
928
|
stickerAlt: "Pro Echo organizes tabbed notebooks and a daily refresh control.",
|
|
929
929
|
popular: true,
|
|
@@ -960,6 +960,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
960
960
|
return '<li>' + setupIcon("check") + '<span>' + esc(feature) + '</span></li>';
|
|
961
961
|
}).join("");
|
|
962
962
|
var chooseId = plan.id === "free" ? "chooseFreePlan" : (plan.id === "power" ? "choosePowerPlan" : "chooseProPlan");
|
|
963
|
+
var chooseLabel = plan.id === "free" ? "Take " + plan.homeName + " home" : "Start 14-day trial";
|
|
963
964
|
var discovery = discovered > 0
|
|
964
965
|
? '<p class="mvpPricingDiscovery">Echo found <strong>' + esc(number(discovered)) + '</strong> new coding session' + (discovered === 1 ? "" : "s") + ' on this Mac.</p>'
|
|
965
966
|
: "";
|
|
@@ -980,7 +981,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
980
981
|
'<div class="mvpPlanPrice"><strong>' + esc(plan.price) + '</strong><span>' + esc(plan.cadence) + '</span></div>' +
|
|
981
982
|
'<p class="mvpPacksLabel">Echo packs</p>' +
|
|
982
983
|
'<ul class="mvpPlanFacts">' + facts + '</ul>' +
|
|
983
|
-
'<button type="button" class="primary mvpPlanCta" id="' + chooseId + '">
|
|
984
|
+
'<button type="button" class="primary mvpPlanCta" id="' + chooseId + '">' + esc(chooseLabel) + ' <span aria-hidden="true">→</span></button>' +
|
|
984
985
|
'</div>' +
|
|
985
986
|
'</article>' +
|
|
986
987
|
'<p class="mvpPlanStatus" id="setupPlanStatus" role="status"></p>' +
|
|
@@ -1022,7 +1023,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1022
1023
|
if (!canExtract) {
|
|
1023
1024
|
slot.innerHTML = "";
|
|
1024
1025
|
slot.classList.add("is-hidden");
|
|
1025
|
-
if (readySettings) readySettings.classList.remove("is-plan-open");
|
|
1026
|
+
if (readySettings) readySettings.classList.remove("is-plan-open", "is-checkout-pending");
|
|
1026
1027
|
return;
|
|
1027
1028
|
}
|
|
1028
1029
|
slot.classList.remove("is-hidden");
|
|
@@ -1035,7 +1036,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1035
1036
|
renderDashboard();
|
|
1036
1037
|
return;
|
|
1037
1038
|
}
|
|
1038
|
-
if (readySettings) readySettings.classList.remove("is-plan-open");
|
|
1039
|
+
if (readySettings) readySettings.classList.remove("is-plan-open", "is-checkout-pending");
|
|
1039
1040
|
rememberSetupPlanChoice("");
|
|
1040
1041
|
setupPlanChoice = "paid";
|
|
1041
1042
|
var planLabel = plan.charAt(0).toUpperCase() + plan.slice(1);
|
|
@@ -1046,7 +1047,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1046
1047
|
? paidHistory.used + " / " + paidHistory.limit + " bulk history imports used."
|
|
1047
1048
|
: "Your bulk history import and weekly new-chat allowance are ready.";
|
|
1048
1049
|
if (checkoutJustConfirmed) document.title = "Echo plan active — continue setup";
|
|
1049
|
-
slot.innerHTML = '<div class="setupPlanConfirmed' + (checkoutJustConfirmed ? ' is-checkout-confirmed' : '') + '"><span class="setupPlanConfirmedIcon" aria-hidden="true">' + setupIcon("check") + '</span><span class="setupPlanConfirmedCopy"><strong>' + (checkoutJustConfirmed ? '
|
|
1050
|
+
slot.innerHTML = '<div class="setupPlanConfirmed' + (checkoutJustConfirmed ? ' is-checkout-confirmed' : '') + '"><span class="setupPlanConfirmedIcon" aria-hidden="true">' + setupIcon("check") + '</span><span class="setupPlanConfirmedCopy"><strong>' + (checkoutJustConfirmed ? 'Your Echo is ready. ' : '') + esc(planLabel) + ' Echo</strong><span>' + esc(checkoutJustConfirmed ? "Continue below." : paidUsage) + '</span></span><button type="button" class="textButton" id="changeActivePlan">Change</button></div>';
|
|
1050
1051
|
var changeActivePlan = document.getElementById("changeActivePlan");
|
|
1051
1052
|
if (changeActivePlan) changeActivePlan.onclick = openHostedPlanOptions;
|
|
1052
1053
|
renderSessionSelection(true);
|
|
@@ -1054,8 +1055,8 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1054
1055
|
return;
|
|
1055
1056
|
}
|
|
1056
1057
|
if (setupPlanChoice === "free") {
|
|
1057
|
-
if (readySettings) readySettings.classList.remove("is-plan-open");
|
|
1058
|
-
slot.innerHTML = '<div class="setupPlanConfirmed"><span class="setupPlanConfirmedIcon" aria-hidden="true">' + setupIcon("check") + '</span><span class="setupPlanConfirmedCopy"><strong>Original Echo</strong><span>100 bulk history imports, 25K new-chat tokens each week, and
|
|
1058
|
+
if (readySettings) readySettings.classList.remove("is-plan-open", "is-checkout-pending");
|
|
1059
|
+
slot.innerHTML = '<div class="setupPlanConfirmed"><span class="setupPlanConfirmedIcon" aria-hidden="true">' + setupIcon("check") + '</span><span class="setupPlanConfirmedCopy"><strong>Original Echo</strong><span>100 bulk history imports, 25K new-chat tokens each week, and 100 memory searches each week.</span></span><button type="button" class="textButton" id="changeSetupPlan">Change</button></div>';
|
|
1059
1060
|
var changeBtn = document.getElementById("changeSetupPlan");
|
|
1060
1061
|
if (changeBtn) changeBtn.onclick = function () {
|
|
1061
1062
|
rememberSetupPlanChoice("");
|
|
@@ -1068,11 +1069,14 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1068
1069
|
? "power"
|
|
1069
1070
|
: (setupPlanChoice === "pro" || billingActivationPendingPlan === "pro" ? "pro" : "");
|
|
1070
1071
|
if (pendingPaidPlan) {
|
|
1071
|
-
if (readySettings)
|
|
1072
|
+
if (readySettings) {
|
|
1073
|
+
readySettings.classList.remove("is-plan-open");
|
|
1074
|
+
readySettings.classList.add("is-checkout-pending");
|
|
1075
|
+
}
|
|
1072
1076
|
var selectedPaidPlan = pendingPaidPlan === "power" ? "Power" : "Pro";
|
|
1073
1077
|
slot.innerHTML =
|
|
1074
1078
|
'<section class="setupPlanChoice setupPlanCheckout" aria-labelledby="setupPlanHeading">' +
|
|
1075
|
-
'<div class="setupPlanIntro"><h3 id="setupPlanHeading">
|
|
1079
|
+
'<div class="setupPlanIntro"><h3 id="setupPlanHeading">Start your 14-day trial.</h3><p>Finish in the secure Stripe tab, then return here. Echo will update automatically.</p></div>' +
|
|
1076
1080
|
'<button type="button" class="primary" id="continueSelectedPaidPlan">Reopen secure checkout</button>' +
|
|
1077
1081
|
'<div class="setupPlanFoot"><button type="button" class="textButton" id="changeSelectedPaidPlan">Choose another Echo</button><span id="setupPlanStatus" role="status"></span></div>' +
|
|
1078
1082
|
'</section>';
|
|
@@ -1090,7 +1094,10 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1090
1094
|
renderSessionSelection(true);
|
|
1091
1095
|
return;
|
|
1092
1096
|
}
|
|
1093
|
-
if (readySettings)
|
|
1097
|
+
if (readySettings) {
|
|
1098
|
+
readySettings.classList.remove("is-checkout-pending");
|
|
1099
|
+
readySettings.classList.add("is-plan-open");
|
|
1100
|
+
}
|
|
1094
1101
|
slot.innerHTML = renderPlanHabitat();
|
|
1095
1102
|
bindPlanHabitat();
|
|
1096
1103
|
if (migrateBtn) migrateBtn.disabled = true;
|
|
@@ -807,6 +807,95 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
807
807
|
padding: 0 8px;
|
|
808
808
|
font-size: 11px;
|
|
809
809
|
}
|
|
810
|
+
.extractionReady .readySettings.is-checkout-pending {
|
|
811
|
+
align-items: flex-start;
|
|
812
|
+
}
|
|
813
|
+
.extractionReady .readySettings.is-checkout-pending .readyPlanSetting {
|
|
814
|
+
display: block;
|
|
815
|
+
width: min(680px, calc(100vw - 260px));
|
|
816
|
+
overflow: visible;
|
|
817
|
+
border: 0;
|
|
818
|
+
border-radius: 0;
|
|
819
|
+
background: transparent;
|
|
820
|
+
padding: 0;
|
|
821
|
+
box-shadow: none;
|
|
822
|
+
backdrop-filter: none;
|
|
823
|
+
}
|
|
824
|
+
.extractionReady .readySettings.is-checkout-pending .readyPlanSetting > .readySettingLabel {
|
|
825
|
+
position: absolute;
|
|
826
|
+
width: 1px;
|
|
827
|
+
height: 1px;
|
|
828
|
+
overflow: hidden;
|
|
829
|
+
clip: rect(0 0 0 0);
|
|
830
|
+
clip-path: inset(50%);
|
|
831
|
+
white-space: nowrap;
|
|
832
|
+
}
|
|
833
|
+
.extractionReady .readySettings.is-checkout-pending .setupPlanSlot {
|
|
834
|
+
margin: 0;
|
|
835
|
+
}
|
|
836
|
+
.extractionReady .setupPlanCheckout {
|
|
837
|
+
display: grid;
|
|
838
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
839
|
+
grid-template-rows: auto auto;
|
|
840
|
+
column-gap: 18px;
|
|
841
|
+
row-gap: 2px;
|
|
842
|
+
align-items: center;
|
|
843
|
+
margin: 0;
|
|
844
|
+
overflow: hidden;
|
|
845
|
+
border: 1px solid rgba(26,58,143,0.16);
|
|
846
|
+
border-radius: 18px;
|
|
847
|
+
background: rgba(255,255,255,0.94);
|
|
848
|
+
padding: 13px 15px 12px 18px;
|
|
849
|
+
box-shadow: 0 12px 32px rgba(26,58,143,0.12);
|
|
850
|
+
backdrop-filter: blur(16px);
|
|
851
|
+
}
|
|
852
|
+
.extractionReady .setupPlanCheckout::before {
|
|
853
|
+
display: block;
|
|
854
|
+
position: absolute;
|
|
855
|
+
inset: 0 auto 0 0;
|
|
856
|
+
width: 4px;
|
|
857
|
+
height: auto;
|
|
858
|
+
background: var(--echo-ink-primary);
|
|
859
|
+
opacity: 1;
|
|
860
|
+
}
|
|
861
|
+
.extractionReady .setupPlanCheckout .setupPlanIntro {
|
|
862
|
+
min-width: 0;
|
|
863
|
+
padding: 0;
|
|
864
|
+
}
|
|
865
|
+
.extractionReady .setupPlanCheckout .setupPlanIntro h3 {
|
|
866
|
+
font-size: 16px;
|
|
867
|
+
line-height: 1.2;
|
|
868
|
+
letter-spacing: -0.01em;
|
|
869
|
+
}
|
|
870
|
+
.extractionReady .setupPlanCheckout .setupPlanIntro p {
|
|
871
|
+
margin-top: 3px;
|
|
872
|
+
font-size: 11px;
|
|
873
|
+
line-height: 1.35;
|
|
874
|
+
}
|
|
875
|
+
.extractionReady .setupPlanCheckout > .primary {
|
|
876
|
+
grid-column: 2;
|
|
877
|
+
grid-row: 1 / span 2;
|
|
878
|
+
min-height: 42px;
|
|
879
|
+
border-radius: 999px;
|
|
880
|
+
padding: 0 18px;
|
|
881
|
+
white-space: nowrap;
|
|
882
|
+
box-shadow: 0 7px 18px rgba(26,58,143,0.18);
|
|
883
|
+
}
|
|
884
|
+
.extractionReady .setupPlanCheckout .setupPlanFoot {
|
|
885
|
+
justify-content: flex-start;
|
|
886
|
+
gap: 8px;
|
|
887
|
+
margin-top: 1px;
|
|
888
|
+
}
|
|
889
|
+
.extractionReady .setupPlanCheckout .setupPlanFoot .textButton {
|
|
890
|
+
min-height: 0;
|
|
891
|
+
padding: 2px 0;
|
|
892
|
+
color: var(--echo-ink-mute);
|
|
893
|
+
font-size: 10px;
|
|
894
|
+
font-weight: 700;
|
|
895
|
+
}
|
|
896
|
+
.extractionReady .setupPlanCheckout .setupPlanFoot span {
|
|
897
|
+
font-size: 10px;
|
|
898
|
+
}
|
|
810
899
|
.extractionReady .readyFoot {
|
|
811
900
|
width: min(620px, 100%);
|
|
812
901
|
margin: 12px auto 0;
|
|
@@ -954,6 +1043,20 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
954
1043
|
.mvpHabitatDetails { padding: 28px 24px 34px; }
|
|
955
1044
|
.mvpHabitatEcho { width: min(66%, 360px); }
|
|
956
1045
|
.extractionReady .readySettings { flex-direction: column; gap: 9px; }
|
|
1046
|
+
body.readyMode:has(.readySettings.is-checkout-pending) main {
|
|
1047
|
+
padding-top: 190px;
|
|
1048
|
+
}
|
|
1049
|
+
.extractionReady .readySettings.is-checkout-pending .readyAccountSetting { display: none; }
|
|
1050
|
+
.extractionReady .readySettings.is-checkout-pending .readyPlanSetting { width: 100%; }
|
|
1051
|
+
.extractionReady .setupPlanCheckout {
|
|
1052
|
+
grid-template-columns: 1fr;
|
|
1053
|
+
gap: 9px;
|
|
1054
|
+
}
|
|
1055
|
+
.extractionReady .setupPlanCheckout > .primary {
|
|
1056
|
+
grid-column: 1;
|
|
1057
|
+
grid-row: auto;
|
|
1058
|
+
width: 100%;
|
|
1059
|
+
}
|
|
957
1060
|
.exHero.exHeroActive { grid-template-columns: 1fr; }
|
|
958
1061
|
.exHeroActive .exPlate { min-height: 440px; }
|
|
959
1062
|
}
|
package/dist/setup-preview.js
CHANGED
|
@@ -339,22 +339,22 @@ export function renderSetupPreviewBootstrap(state) {
|
|
|
339
339
|
if (state === "extract-pro-active")
|
|
340
340
|
return `${watermark}${extractionPreviewBootstrap({
|
|
341
341
|
plan: "pro", paid: true, trialAvailable: false, trialUsed: true,
|
|
342
|
-
subscriptionStatus: "active", quotaLimit:
|
|
342
|
+
subscriptionStatus: "active", quotaLimit: 500, quotaRemaining: 500,
|
|
343
343
|
})}`;
|
|
344
344
|
if (state === "extract-power-active")
|
|
345
345
|
return `${watermark}${extractionPreviewBootstrap({
|
|
346
346
|
plan: "power", paid: true, trialAvailable: false, trialUsed: true,
|
|
347
|
-
subscriptionStatus: "active", quotaLimit:
|
|
347
|
+
subscriptionStatus: "active", quotaLimit: 2_000, quotaRemaining: 2_000,
|
|
348
348
|
})}`;
|
|
349
349
|
if (state === "extract-team-active")
|
|
350
350
|
return `${watermark}${extractionPreviewBootstrap({
|
|
351
351
|
plan: "team", paid: true, trialAvailable: false, trialUsed: true,
|
|
352
|
-
subscriptionStatus: "active", quotaLimit:
|
|
352
|
+
subscriptionStatus: "active", quotaLimit: 500, quotaRemaining: 500,
|
|
353
353
|
})}`;
|
|
354
354
|
if (state === "extract-enterprise-active")
|
|
355
355
|
return `${watermark}${extractionPreviewBootstrap({
|
|
356
356
|
plan: "enterprise", paid: true, trialAvailable: false, trialUsed: true,
|
|
357
|
-
subscriptionStatus: "active", quotaLimit:
|
|
357
|
+
subscriptionStatus: "active", quotaLimit: 2_000, quotaRemaining: 2_000,
|
|
358
358
|
})}`;
|
|
359
359
|
if (state === "extract-unknown")
|
|
360
360
|
return `${watermark}${extractionPreviewBootstrap({
|
package/dist/setup.js
CHANGED
|
@@ -391,6 +391,16 @@ function echomemGuidanceBlock() {
|
|
|
391
391
|
"- Before re-deriving past decisions, project context, or anything the user likely settled before: call `search_memories` first.",
|
|
392
392
|
"- When meaningful work wraps up (a decision, a fix, a milestone) or the user asks to remember something: call `save_conversation`.",
|
|
393
393
|
"- If the user pastes a session carryover/checkpoint: it may reference `get_checkpoint_by_context` — use it to pull the checkpoint/decision trail when you need more than the snapshot.",
|
|
394
|
+
"",
|
|
395
|
+
"### Company group memory",
|
|
396
|
+
"- Use `get_group_context` when the user asks who is in their company group, what teammates are responsible for, or what work is already covered. Treat declared participant fields as facts and published-memory conclusions as evidence or inference.",
|
|
397
|
+
"- A group publication is separate from a globally public memory: publishing to a group creates a group-scoped snapshot and must not change the encrypted original or its global `is_public` setting.",
|
|
398
|
+
"- If a user asks to create a group, call `create_memory_group`; if they ask for a code to share, call `create_group_invite` and return the secret invite code only to that user. Never save the invite code to memory or include it in logs, analytics, summaries, or unrelated output.",
|
|
399
|
+
"- If a user supplies an `echo_grp_...` code and explicitly asks to join, call `join_memory_group`. Joining never authorizes publishing by itself and must not move a user out of another group. After joining, continue into the profile-and-publication preview instead of leaving title or responsibility blank.",
|
|
400
|
+
"- For requests such as “prepare my recent work memories,” “upload work from this ticket,” or “publish work since my last sync,” call `prepare_group_publication` first. This is a no-publication preview. For encrypted accounts, tell the user to run `echomem-mcp unlock` locally if the tool reports that the key is required.",
|
|
401
|
+
"- After preparing, select only exact candidate memory IDs that match the user's stated scope and exclude already-published or exact-content duplicates. Use the candidate evidence to draft a concise title and responsibility summary for the current member, but label both as proposals rather than facts.",
|
|
402
|
+
"- Present the proposed title/responsibility and the memory publication preview together and ask for one explicit confirmation. Never save an inferred profile or publish memories before confirmation.",
|
|
403
|
+
"- On confirmation, call `update_group_profile` with the confirmed title, responsibility summary, and `confirmed: true`, then call `complete_group_publication` with the exact `scanId`, selected memory IDs, and `confirmed: true`. If the user edits either proposal, use their wording. An explicit request to join and upload still requires this combined preview and confirmation.",
|
|
394
404
|
"- If the user asks to show, reopen, restart, or bring back the EchoMem HUD (the context-health overlay), run the shell command `echomem-hud app --client auto`.",
|
|
395
405
|
"- If the user wants the HUD to come back after a computer restart, run the shell command `echomem-hud autostart on --client auto`.",
|
|
396
406
|
AGENTS_MD_END,
|
|
@@ -1716,6 +1726,7 @@ export function startCallbackServer(opts = {}) {
|
|
|
1716
1726
|
return;
|
|
1717
1727
|
}
|
|
1718
1728
|
if (route === "/billing-status" && req.method === "GET") {
|
|
1729
|
+
res.setHeader("Cache-Control", "no-store, max-age=0");
|
|
1719
1730
|
if (!checkNonce(url.searchParams.get("nonce") || undefined))
|
|
1720
1731
|
return void text(res, 403, "bad nonce");
|
|
1721
1732
|
if (requiresReportConsent && !reportConsentGranted) {
|
package/dist/v1-contract.js
CHANGED
|
@@ -10,6 +10,15 @@ export const canonicalToolNames = {
|
|
|
10
10
|
sendFriendRequest: "send_friend_request",
|
|
11
11
|
others: "search_others_memories",
|
|
12
12
|
publicMemory: "get_public_memory",
|
|
13
|
+
groupContext: "get_group_context",
|
|
14
|
+
createGroup: "create_memory_group",
|
|
15
|
+
createGroupInvite: "create_group_invite",
|
|
16
|
+
joinGroup: "join_memory_group",
|
|
17
|
+
prepareGroupPublication: "prepare_group_publication",
|
|
18
|
+
updateGroupProfile: "update_group_profile",
|
|
19
|
+
completeGroupPublication: "complete_group_publication",
|
|
20
|
+
publishToGroup: "publish_memory_to_group",
|
|
21
|
+
publishBatchToGroup: "publish_memories_to_group",
|
|
13
22
|
report: "echomem_usage_report",
|
|
14
23
|
updateStatus: "echomem_update_status",
|
|
15
24
|
contextHealth: "echo_context_health",
|
|
@@ -59,9 +68,17 @@ export const timeRangeSchema = z.object({
|
|
|
59
68
|
endDate: z.string(),
|
|
60
69
|
limit: z.number().optional().default(50),
|
|
61
70
|
});
|
|
71
|
+
const keywordListSchema = z.preprocess((value) => {
|
|
72
|
+
if (typeof value !== "string")
|
|
73
|
+
return value;
|
|
74
|
+
return value
|
|
75
|
+
.split(",")
|
|
76
|
+
.map((keyword) => keyword.trim())
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
}, z.array(z.string().trim().min(1)).min(1).max(50));
|
|
62
79
|
export const keywordsSchema = z.object({
|
|
63
80
|
...triggerMetadataSchema,
|
|
64
|
-
keywords:
|
|
81
|
+
keywords: keywordListSchema,
|
|
65
82
|
limit: z.number().optional().default(10),
|
|
66
83
|
});
|
|
67
84
|
export const listFriendsSchema = z.object({
|
|
@@ -94,6 +111,63 @@ export const publicMemorySchema = z.object({
|
|
|
94
111
|
...triggerMetadataSchema,
|
|
95
112
|
memoryId: z.string().min(1),
|
|
96
113
|
});
|
|
114
|
+
export const groupContextSchema = z.object({
|
|
115
|
+
...triggerMetadataSchema,
|
|
116
|
+
});
|
|
117
|
+
export const createGroupSchema = z.object({
|
|
118
|
+
...triggerMetadataSchema,
|
|
119
|
+
name: z.string().min(1).max(120),
|
|
120
|
+
description: z.string().max(1000).optional(),
|
|
121
|
+
displayName: z.string().min(1).max(120),
|
|
122
|
+
title: z.string().max(160).optional(),
|
|
123
|
+
responsibilitySummary: z.string().max(1000).optional(),
|
|
124
|
+
});
|
|
125
|
+
export const createGroupInviteSchema = z.object({
|
|
126
|
+
...triggerMetadataSchema,
|
|
127
|
+
expiresInDays: z.number().int().min(1).max(30).optional(),
|
|
128
|
+
maxUses: z.number().int().min(1).max(100).optional(),
|
|
129
|
+
});
|
|
130
|
+
export const joinGroupSchema = z.object({
|
|
131
|
+
...triggerMetadataSchema,
|
|
132
|
+
code: z.string().min(30),
|
|
133
|
+
displayName: z.string().min(1).max(120),
|
|
134
|
+
title: z.string().max(160).optional(),
|
|
135
|
+
responsibilitySummary: z.string().max(1000).optional(),
|
|
136
|
+
});
|
|
137
|
+
export const prepareGroupPublicationSchema = z.object({
|
|
138
|
+
...triggerMetadataSchema,
|
|
139
|
+
scope: z.enum(["bootstrap", "since_last_scan", "context", "time_range"]).default("since_last_scan"),
|
|
140
|
+
contextId: z.string().min(1).optional(),
|
|
141
|
+
startAt: z.string().optional(),
|
|
142
|
+
endAt: z.string().optional(),
|
|
143
|
+
lookbackDays: z.number().int().min(1).max(365).optional(),
|
|
144
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
145
|
+
instruction: z.string().max(500).optional(),
|
|
146
|
+
});
|
|
147
|
+
export const updateGroupProfileSchema = z.object({
|
|
148
|
+
...triggerMetadataSchema,
|
|
149
|
+
displayName: z.string().min(1).max(120).optional(),
|
|
150
|
+
title: z.string().min(1).max(160),
|
|
151
|
+
responsibilitySummary: z.string().min(1).max(1000),
|
|
152
|
+
confirmed: z.literal(true),
|
|
153
|
+
});
|
|
154
|
+
export const completeGroupPublicationSchema = z.object({
|
|
155
|
+
...triggerMetadataSchema,
|
|
156
|
+
scanId: z.string().min(1),
|
|
157
|
+
memoryIds: z.array(z.string().min(1)).max(50),
|
|
158
|
+
confirmed: z.literal(true),
|
|
159
|
+
selectionReason: z.string().max(500).optional(),
|
|
160
|
+
});
|
|
161
|
+
export const publishToGroupSchema = z.object({
|
|
162
|
+
...triggerMetadataSchema,
|
|
163
|
+
memoryId: z.string().min(1),
|
|
164
|
+
});
|
|
165
|
+
export const publishBatchToGroupSchema = z.object({
|
|
166
|
+
...triggerMetadataSchema,
|
|
167
|
+
memoryIds: z.array(z.string().min(1)).min(1).max(50),
|
|
168
|
+
contextId: z.string().min(1).optional(),
|
|
169
|
+
selectionReason: z.string().max(500).optional(),
|
|
170
|
+
});
|
|
97
171
|
export const deleteMemorySchema = z.object({
|
|
98
172
|
memoryId: z.string().min(1),
|
|
99
173
|
confirmed: z.boolean().optional().default(false),
|
|
@@ -108,7 +182,7 @@ export function listToolSpecs(opts = {}) {
|
|
|
108
182
|
const currentTime = new Date().toISOString();
|
|
109
183
|
const map = opts.map?.trim();
|
|
110
184
|
const updateNotice = opts.updateNotice?.trim();
|
|
111
|
-
const recallPlanNote = "Available on every plan: Free includes
|
|
185
|
+
const recallPlanNote = "Available on every plan: Free includes 100 searches each week, Pro includes 500, and Power includes 2,000.";
|
|
112
186
|
const searchBillingReplyInstruction = "If search returns an ACTION REQUIRED subscription message, tell the user to start their trial or subscription and include the exact URL from that result verbatim. Do not respond only with \"connect\" or \"upgrade\".";
|
|
113
187
|
const updateSection = updateNotice ? `\n\nUPDATE NOTICE: ${updateNotice}` : "";
|
|
114
188
|
const mapSection = map
|
|
@@ -204,11 +278,27 @@ export function listToolSpecs(opts = {}) {
|
|
|
204
278
|
},
|
|
205
279
|
{
|
|
206
280
|
name: canonicalToolNames.keywords,
|
|
207
|
-
description: `Search memories based on keywords in keys field. ${recallPlanNote}`,
|
|
281
|
+
description: `Search memories based on keywords in keys field. Pass keywords as valid JSON: preferably an array of quoted strings, for example {"keywords":["flow-lab","flow.html","Rive"],"limit":8}. A comma-separated JSON string is also accepted as a compatibility fallback. Never emit bare comma-separated tokens. ${recallPlanNote}`,
|
|
208
282
|
inputSchema: {
|
|
209
283
|
type: "object",
|
|
210
284
|
properties: {
|
|
211
|
-
keywords: {
|
|
285
|
+
keywords: {
|
|
286
|
+
oneOf: [
|
|
287
|
+
{
|
|
288
|
+
type: "array",
|
|
289
|
+
minItems: 1,
|
|
290
|
+
maxItems: 50,
|
|
291
|
+
items: { type: "string", minLength: 1 },
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
type: "string",
|
|
295
|
+
minLength: 1,
|
|
296
|
+
description: "Compatibility form: one comma-separated JSON string, such as \"flow-lab, flow.html, Rive\".",
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
description: "Use a JSON array of quoted strings. Do not pass unquoted comma-separated tokens.",
|
|
300
|
+
examples: [["flow-lab", "flow.html", "Rive"]],
|
|
301
|
+
},
|
|
212
302
|
limit: { type: "number", default: 10 },
|
|
213
303
|
triggerMessage: {
|
|
214
304
|
type: "string",
|
|
@@ -274,7 +364,7 @@ export function listToolSpecs(opts = {}) {
|
|
|
274
364
|
},
|
|
275
365
|
{
|
|
276
366
|
name: canonicalToolNames.others,
|
|
277
|
-
description: "Search accepted friends
|
|
367
|
+
description: "Search public memories from accepted friends or people who share your company group. For onboarding and division-of-work questions, call get_group_context first, then use this tool for current evidence. Returned memories are recorded in memory_views for the owners.",
|
|
278
368
|
inputSchema: {
|
|
279
369
|
type: "object",
|
|
280
370
|
properties: {
|
|
@@ -282,25 +372,25 @@ export function listToolSpecs(opts = {}) {
|
|
|
282
372
|
limit: { type: "number", default: 10 },
|
|
283
373
|
target: {
|
|
284
374
|
type: "string",
|
|
285
|
-
description: "
|
|
375
|
+
description: "Accessible friend or group-member user id or exact display name. Prefer this for @Name asks.",
|
|
286
376
|
},
|
|
287
377
|
ownerUserId: {
|
|
288
378
|
type: "string",
|
|
289
|
-
description: "Optional
|
|
379
|
+
description: "Optional accessible user id to scope the search to one person.",
|
|
290
380
|
},
|
|
291
381
|
ownerName: {
|
|
292
382
|
type: "string",
|
|
293
|
-
description: "Optional
|
|
383
|
+
description: "Optional accessible friend or group-member display name to scope the search.",
|
|
294
384
|
},
|
|
295
385
|
targetFriendIds: {
|
|
296
386
|
type: "array",
|
|
297
387
|
items: { type: "string" },
|
|
298
|
-
description: "
|
|
388
|
+
description: "Legacy field for optional accessible friend or group-member user ids.",
|
|
299
389
|
},
|
|
300
390
|
targetFriendNames: {
|
|
301
391
|
type: "array",
|
|
302
392
|
items: { type: "string" },
|
|
303
|
-
description: "
|
|
393
|
+
description: "Legacy field for optional accessible friend or group-member display names.",
|
|
304
394
|
},
|
|
305
395
|
recordAccess: {
|
|
306
396
|
type: "boolean",
|
|
@@ -319,7 +409,7 @@ export function listToolSpecs(opts = {}) {
|
|
|
319
409
|
},
|
|
320
410
|
{
|
|
321
411
|
name: canonicalToolNames.publicMemory,
|
|
322
|
-
description: "Fetch one accepted friend
|
|
412
|
+
description: "Fetch one public memory by id when its owner is an accepted friend or shares your company group. If the caller is not the owner, EchoMem records the access in memory_views.",
|
|
323
413
|
inputSchema: {
|
|
324
414
|
type: "object",
|
|
325
415
|
properties: {
|
|
@@ -336,6 +426,153 @@ export function listToolSpecs(opts = {}) {
|
|
|
336
426
|
required: ["memoryId"],
|
|
337
427
|
},
|
|
338
428
|
},
|
|
429
|
+
{
|
|
430
|
+
name: canonicalToolNames.groupContext,
|
|
431
|
+
description: "Get your current company group, its participant directory, declared titles and responsibilities, and published-memory coverage. Use this before answering who works on what or suggesting where a new group member could contribute. Treat declared profile fields as facts and memory-derived work as evidence or inference.",
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: "object",
|
|
434
|
+
properties: {
|
|
435
|
+
triggerMessage: {
|
|
436
|
+
type: "string",
|
|
437
|
+
description: "Optional user message that caused this group-orientation lookup.",
|
|
438
|
+
},
|
|
439
|
+
triggerMessageRole: { type: "string", default: "user" },
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: canonicalToolNames.createGroup,
|
|
445
|
+
description: "Create one company memory group for the current user. The MVP allows at most one group per user. Call only after the user explicitly asks to create a group.",
|
|
446
|
+
inputSchema: {
|
|
447
|
+
type: "object",
|
|
448
|
+
properties: {
|
|
449
|
+
name: { type: "string" },
|
|
450
|
+
description: { type: "string" },
|
|
451
|
+
displayName: { type: "string" },
|
|
452
|
+
title: { type: "string" },
|
|
453
|
+
responsibilitySummary: { type: "string" },
|
|
454
|
+
},
|
|
455
|
+
required: ["name", "displayName"],
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
name: canonicalToolNames.createGroupInvite,
|
|
460
|
+
description: "Generate a secret, expiring invite code for the current group. Only the group creator may call this in the MVP. Return the code only to the user so they can share it intentionally; never log it.",
|
|
461
|
+
inputSchema: {
|
|
462
|
+
type: "object",
|
|
463
|
+
properties: {
|
|
464
|
+
expiresInDays: { type: "number", default: 7 },
|
|
465
|
+
maxUses: { type: "number", default: 20 },
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: canonicalToolNames.joinGroup,
|
|
471
|
+
description: "Join a company memory group using an invite code after the user explicitly asks to join. Joining never publishes memories. Next call prepare_group_publication, infer a proposed title and responsibility summary from the user's own candidate memories, and ask the user to confirm the profile together with the publication preview.",
|
|
472
|
+
inputSchema: {
|
|
473
|
+
type: "object",
|
|
474
|
+
properties: {
|
|
475
|
+
code: { type: "string" },
|
|
476
|
+
displayName: { type: "string" },
|
|
477
|
+
title: { type: "string" },
|
|
478
|
+
responsibilitySummary: { type: "string" },
|
|
479
|
+
},
|
|
480
|
+
required: ["code", "displayName"],
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: canonicalToolNames.prepareGroupPublication,
|
|
485
|
+
description: "Prepare a no-publication preview of up to 50 owned memories for group publication. For encrypted accounts the local bridge must be unlocked. The host agent selects exact work-related memory ids, drafts a concise title and responsibility summary from memory evidence, and asks the user to confirm both the profile and publication selection together. This tool never publishes.",
|
|
486
|
+
inputSchema: {
|
|
487
|
+
type: "object",
|
|
488
|
+
properties: {
|
|
489
|
+
scope: { type: "string", enum: ["bootstrap", "since_last_scan", "context", "time_range"], default: "since_last_scan" },
|
|
490
|
+
contextId: { type: "string" },
|
|
491
|
+
startAt: { type: "string" },
|
|
492
|
+
endAt: { type: "string" },
|
|
493
|
+
lookbackDays: { type: "number", default: 30 },
|
|
494
|
+
limit: { type: "number", default: 50 },
|
|
495
|
+
instruction: { type: "string", default: "work-related" },
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
name: canonicalToolNames.updateGroupProfile,
|
|
501
|
+
description: "Save the current member's declared group title and responsibility summary only after the user explicitly confirms the agent's proposal. These become directory facts visible to group members; never save an unconfirmed inference.",
|
|
502
|
+
inputSchema: {
|
|
503
|
+
type: "object",
|
|
504
|
+
properties: {
|
|
505
|
+
displayName: { type: "string" },
|
|
506
|
+
title: { type: "string" },
|
|
507
|
+
responsibilitySummary: { type: "string" },
|
|
508
|
+
confirmed: { type: "boolean", const: true },
|
|
509
|
+
},
|
|
510
|
+
required: ["title", "responsibilitySummary", "confirmed"],
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
name: canonicalToolNames.completeGroupPublication,
|
|
515
|
+
description: "Publish only the exact memory ids from a prepared scan after the user explicitly confirms the preview. confirmed must be true. Completing an empty selection safely advances the scan cursor without publishing.",
|
|
516
|
+
inputSchema: {
|
|
517
|
+
type: "object",
|
|
518
|
+
properties: {
|
|
519
|
+
scanId: { type: "string" },
|
|
520
|
+
memoryIds: { type: "array", maxItems: 50, items: { type: "string" } },
|
|
521
|
+
confirmed: { type: "boolean", const: true },
|
|
522
|
+
selectionReason: { type: "string" },
|
|
523
|
+
},
|
|
524
|
+
required: ["scanId", "memoryIds", "confirmed"],
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
name: canonicalToolNames.publishToGroup,
|
|
529
|
+
description: "Publish one exact memory snapshot to your current company group without changing the memory's global is_public or encryption state. Call only after the user explicitly asks to publish or push that memory. The operation is idempotent.",
|
|
530
|
+
inputSchema: {
|
|
531
|
+
type: "object",
|
|
532
|
+
properties: {
|
|
533
|
+
memoryId: {
|
|
534
|
+
type: "string",
|
|
535
|
+
description: "Exact id of a memory owned by the current user.",
|
|
536
|
+
},
|
|
537
|
+
triggerMessage: {
|
|
538
|
+
type: "string",
|
|
539
|
+
description: "Optional user message that explicitly requested publication.",
|
|
540
|
+
},
|
|
541
|
+
triggerMessageRole: { type: "string", default: "user" },
|
|
542
|
+
},
|
|
543
|
+
required: ["memoryId"],
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: canonicalToolNames.publishBatchToGroup,
|
|
548
|
+
description: "Publish up to 50 exact memory snapshots to your current company group without changing global is_public or the encrypted originals. For a session request, first call get_memories_by_context, select the exact ids that match the user's instruction, then pass those ids with the same contextId. Call only after explicit group-publication intent; report what was selected when the user delegates work-related filtering.",
|
|
549
|
+
inputSchema: {
|
|
550
|
+
type: "object",
|
|
551
|
+
properties: {
|
|
552
|
+
memoryIds: {
|
|
553
|
+
type: "array",
|
|
554
|
+
minItems: 1,
|
|
555
|
+
maxItems: 50,
|
|
556
|
+
items: { type: "string" },
|
|
557
|
+
description: "Exact ids owned by the current user.",
|
|
558
|
+
},
|
|
559
|
+
contextId: {
|
|
560
|
+
type: "string",
|
|
561
|
+
description: "Optional session context that every supplied memory must belong to.",
|
|
562
|
+
},
|
|
563
|
+
selectionReason: {
|
|
564
|
+
type: "string",
|
|
565
|
+
description: "Short explanation to return when the agent selected a subset, such as work-related memories.",
|
|
566
|
+
},
|
|
567
|
+
triggerMessage: {
|
|
568
|
+
type: "string",
|
|
569
|
+
description: "Optional user message that explicitly requested publication.",
|
|
570
|
+
},
|
|
571
|
+
triggerMessageRole: { type: "string", default: "user" },
|
|
572
|
+
},
|
|
573
|
+
required: ["memoryIds"],
|
|
574
|
+
},
|
|
575
|
+
},
|
|
339
576
|
{
|
|
340
577
|
name: canonicalToolNames.delete,
|
|
341
578
|
description: "Delete one of the user's EchoMem memories only after explicit user confirmation. First call with memoryId and confirmed omitted/false to preview the target and receive a confirmationToken; this first call never deletes. Only call again with confirmed=true and that exact confirmationToken after the user explicitly confirms deletion. Deletes the memory row only; raw source_of_truth conversation records are preserved.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@echomem/mcp",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.24",
|
|
4
4
|
"description": "EchoMem MCP bridge: cloud-first memory tools, local context HUD, and the Agent Doctor workspace forensics report (cost ledger + 3D repo city)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|