@rubytech/create-maxy-code 0.1.499 → 0.1.500
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/package.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/admin-authoring-observer.test.sh +135 -0
- package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-directive.test.sh +11 -2
- package/payload/platform/plugins/admin/hooks/prompt-optimiser-directive.sh +6 -3
- package/payload/platform/plugins/admin/mcp/dist/index.js +52 -107
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +8 -4
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +67 -50
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/store-skill-core.d.ts +33 -0
- package/payload/platform/plugins/admin/mcp/dist/store-skill-core.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/store-skill-core.js +122 -0
- package/payload/platform/plugins/admin/mcp/dist/store-skill-core.js.map +1 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +9 -3
- package/payload/platform/plugins/contacts/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/contacts/PLUGIN.md +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/index.js +3 -12
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js +105 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +11 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +56 -4
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
- package/payload/platform/plugins/docs/references/contacts-guide.md +2 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +4 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -2
- package/payload/platform/plugins/email/skills/email-composition/SKILL.md +9 -1
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +6 -0
- package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -1
- package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/__tests__/restatement-ladder.test.sh +2 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +26 -1
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.d.ts +15 -0
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.js +61 -0
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-Ce5aBBWF.js → AdminLoginScreens-CKg_Dlpv.js} +1 -1
- package/payload/server/public/assets/{admin-BWb8me-E.js → admin-wtSUU5N0.js} +1 -1
- package/payload/server/public/assets/chat-DRuIxGtE.js +1 -0
- package/payload/server/public/assets/data-BdZCvyvN.js +1 -0
- package/payload/server/public/assets/{operator-DCNWUKP_.js → operator-DglDYmdV.js} +1 -1
- package/payload/server/public/assets/{page-C4TrTU6r.js → page-C-5sTAE-.js} +2 -2
- package/payload/server/public/assets/{page-DUgrGrJp.js → page-C8G5p4jr.js} +1 -1
- package/payload/server/public/assets/{public-DXJXQPwe.js → public-DqJjXR4p.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-Cu6zvuFx.js → useVoiceRecorder-Cj-VZDKO.js} +1 -1
- package/payload/server/public/chat.html +3 -3
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/public/operator.html +4 -4
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +42 -6
- package/payload/server/public/assets/chat-MvnxPJZ5.js +0 -1
- package/payload/server/public/assets/data-BjqC4hyM.js +0 -1
|
@@ -19,11 +19,17 @@ vi.mock("../../../../../../lib/graph-trash/dist/index.js", () => ({
|
|
|
19
19
|
vi.mock("neo4j-driver", () => ({
|
|
20
20
|
default: { int: (v) => v },
|
|
21
21
|
}));
|
|
22
|
-
import { contactLookup, normalizeNameQuery } from "../contact-lookup.js";
|
|
22
|
+
import { contactLookup, normalizeNameQuery, formatContactResults } from "../contact-lookup.js";
|
|
23
23
|
const ACCOUNT_ID = "20028fb5-db26-4de7-bff6-c5ec2d4fc766";
|
|
24
24
|
function personRecord(props, nodeId = "4:person:9") {
|
|
25
25
|
return {
|
|
26
|
-
get: (k) =>
|
|
26
|
+
get: (k) => {
|
|
27
|
+
if (k === "nodeId")
|
|
28
|
+
return nodeId;
|
|
29
|
+
if (k === "prefs")
|
|
30
|
+
return [];
|
|
31
|
+
return { properties: props };
|
|
32
|
+
},
|
|
27
33
|
};
|
|
28
34
|
}
|
|
29
35
|
beforeEach(() => {
|
|
@@ -68,4 +74,101 @@ describe("contact-lookup — composed full-name match (Task 985)", () => {
|
|
|
68
74
|
expect(params.name).toBe("Jane Smith");
|
|
69
75
|
});
|
|
70
76
|
});
|
|
77
|
+
function personWithPrefsRecord(props, prefs, nodeId = "4:person:9") {
|
|
78
|
+
return {
|
|
79
|
+
get: (k) => {
|
|
80
|
+
if (k === "nodeId")
|
|
81
|
+
return nodeId;
|
|
82
|
+
if (k === "prefs")
|
|
83
|
+
return prefs.map((p) => ({ properties: p }));
|
|
84
|
+
return { properties: props };
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
describe("contact-lookup — preference interrogation (Task 1953)", () => {
|
|
89
|
+
it("query carries the HAS_PREFERENCE traversal and the applicability filter", async () => {
|
|
90
|
+
mockSession.run.mockResolvedValueOnce({ records: [] });
|
|
91
|
+
await contactLookup({ email: "dale@acme.com", accountId: ACCOUNT_ID });
|
|
92
|
+
const [cypher] = mockSession.run.mock.calls[0];
|
|
93
|
+
expect(cypher).toContain("OPTIONAL MATCH (p)-[:HAS_PREFERENCE]->(pref:Preference)");
|
|
94
|
+
expect(cypher).toContain("coalesce(pref.notApplicable, false) = false");
|
|
95
|
+
expect(cypher).toContain("collect(pref) AS prefs");
|
|
96
|
+
});
|
|
97
|
+
it("returns the full property bag and the mapped preferences for a resolved contact", async () => {
|
|
98
|
+
mockSession.run.mockResolvedValueOnce({
|
|
99
|
+
records: [
|
|
100
|
+
personWithPrefsRecord({ givenName: "Ann", familyName: "Woolgrove", email: "ann@acme.com", jobTitle: "Buyer", alwaysCc: "dale@acme.com", source: "manual", status: "active", createdOn: "2026-01-01" }, [{ category: "communication", key: "cc_dale", value: "always CC Dale", confidence: 0.9, observedAt: "2026-02-01" }]),
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
const hits = await contactLookup({ email: "ann@acme.com", accountId: ACCOUNT_ID });
|
|
104
|
+
expect(hits).toHaveLength(1);
|
|
105
|
+
expect(hits[0].properties.alwaysCc).toBe("dale@acme.com");
|
|
106
|
+
expect(hits[0].properties.jobTitle).toBe("Buyer");
|
|
107
|
+
expect(hits[0].preferences).toHaveLength(1);
|
|
108
|
+
expect(hits[0].preferences[0]).toEqual({
|
|
109
|
+
category: "communication",
|
|
110
|
+
key: "cc_dale",
|
|
111
|
+
value: "always CC Dale",
|
|
112
|
+
confidence: 0.9,
|
|
113
|
+
observedAt: "2026-02-01",
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
it("returns an empty preferences array when the contact has none", async () => {
|
|
117
|
+
mockSession.run.mockResolvedValueOnce({
|
|
118
|
+
records: [
|
|
119
|
+
personWithPrefsRecord({ givenName: "Ben", familyName: "Terry", email: "ben@acme.com", source: "manual", status: "active", createdOn: "2026-01-02" }, []),
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
const hits = await contactLookup({ email: "ben@acme.com", accountId: ACCOUNT_ID });
|
|
123
|
+
expect(hits[0].preferences).toEqual([]);
|
|
124
|
+
expect(hits[0].properties.givenName).toBe("Ben");
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe("formatContactResults — surfaces open-by-default properties (Task 1953 review finding)", () => {
|
|
128
|
+
function baseContact(overrides = {}) {
|
|
129
|
+
return {
|
|
130
|
+
nodeId: "4:person:9",
|
|
131
|
+
givenName: "Ann",
|
|
132
|
+
familyName: "Woolgrove",
|
|
133
|
+
email: "ann@acme.com",
|
|
134
|
+
source: "manual",
|
|
135
|
+
status: "active",
|
|
136
|
+
createdOn: "2026-01-01",
|
|
137
|
+
properties: {},
|
|
138
|
+
preferences: [],
|
|
139
|
+
...overrides,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
it("renders a Properties block containing a routing property like alwaysCc", () => {
|
|
143
|
+
const text = formatContactResults([
|
|
144
|
+
baseContact({ properties: { alwaysCc: "dale@acme.com" } }),
|
|
145
|
+
]);
|
|
146
|
+
expect(text).toContain("Properties:");
|
|
147
|
+
expect(text).toContain("- alwaysCc: dale@acme.com");
|
|
148
|
+
});
|
|
149
|
+
it("does not repeat accountId even when present in properties", () => {
|
|
150
|
+
const text = formatContactResults([
|
|
151
|
+
baseContact({
|
|
152
|
+
properties: { alwaysCc: "dale@acme.com", accountId: "20028fb5-db26-4de7-bff6-c5ec2d4fc766" },
|
|
153
|
+
}),
|
|
154
|
+
]);
|
|
155
|
+
expect(text).not.toContain("accountId");
|
|
156
|
+
});
|
|
157
|
+
it("still renders Preferences and jobTitle alongside Properties (regression)", () => {
|
|
158
|
+
const text = formatContactResults([
|
|
159
|
+
baseContact({
|
|
160
|
+
jobTitle: "Buyer",
|
|
161
|
+
properties: { alwaysCc: "dale@acme.com" },
|
|
162
|
+
preferences: [
|
|
163
|
+
{ category: "communication", key: "cc_dale", value: "always CC Dale", confidence: 0.9, observedAt: "2026-02-01" },
|
|
164
|
+
],
|
|
165
|
+
}),
|
|
166
|
+
]);
|
|
167
|
+
expect(text).toContain("jobTitle: Buyer");
|
|
168
|
+
expect(text).toContain("Preferences:");
|
|
169
|
+
expect(text).toContain("- always CC Dale");
|
|
170
|
+
expect(text).toContain("Properties:");
|
|
171
|
+
expect(text).toContain("- alwaysCc: dale@acme.com");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
71
174
|
//# sourceMappingURL=contact-lookup.test.js.map
|
package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-lookup.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/contact-lookup.test.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,2EAA2E;AAC3E,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,+EAA+E;AAC/E,iEAAiE;AAEjE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9D,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;IACZ,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;CAC5C,CAAC;AAEF,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,UAAU,EAAE,GAAG,EAAE,CAAC,WAAW;CAC9B,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,UAAU,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,QAAQ,KAAK,WAAW;CACxD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7B,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,EAAE;CACnC,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-lookup.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/contact-lookup.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,14 @@ interface ContactLookupParams {
|
|
|
6
6
|
limit?: number;
|
|
7
7
|
}
|
|
8
8
|
export declare function normalizeNameQuery(name: string): string;
|
|
9
|
-
interface
|
|
9
|
+
interface PreferenceRecord {
|
|
10
|
+
category: string;
|
|
11
|
+
key: string;
|
|
12
|
+
value: string;
|
|
13
|
+
confidence: number | null;
|
|
14
|
+
observedAt: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface ContactRecord {
|
|
10
17
|
nodeId: string;
|
|
11
18
|
givenName: string;
|
|
12
19
|
familyName?: string;
|
|
@@ -16,7 +23,10 @@ interface ContactRecord {
|
|
|
16
23
|
source: string;
|
|
17
24
|
status: string;
|
|
18
25
|
createdOn: string;
|
|
26
|
+
properties: Record<string, unknown>;
|
|
27
|
+
preferences: PreferenceRecord[];
|
|
19
28
|
}
|
|
20
29
|
export declare function contactLookup(params: ContactLookupParams): Promise<ContactRecord[]>;
|
|
30
|
+
export declare function formatContactResults(results: ContactRecord[]): string;
|
|
21
31
|
export {};
|
|
22
32
|
//# sourceMappingURL=contact-lookup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-lookup.d.ts","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAIA,UAAU,mBAAmB;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,UAAU,aAAa;
|
|
1
|
+
{"version":3,"file":"contact-lookup.d.ts","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAIA,UAAU,mBAAmB;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,aAAa,EAAE,CAAC,CAwF1B;AAUD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CA0BrE"}
|
|
@@ -16,12 +16,16 @@ export async function contactLookup(params) {
|
|
|
16
16
|
let query;
|
|
17
17
|
let queryParams;
|
|
18
18
|
const trashFilter = notTrashed("p");
|
|
19
|
+
const prefFilter = `${notTrashed("pref")} AND coalesce(pref.notApplicable, false) = false`;
|
|
19
20
|
if (email) {
|
|
20
21
|
query = `
|
|
21
22
|
MATCH (p:Person {accountId: $accountId})
|
|
22
23
|
WHERE p.email = $email AND ${trashFilter}
|
|
23
|
-
|
|
24
|
+
WITH p, elementId(p) AS nodeId
|
|
24
25
|
LIMIT $limit
|
|
26
|
+
OPTIONAL MATCH (p)-[:HAS_PREFERENCE]->(pref:Preference)
|
|
27
|
+
WHERE ${prefFilter}
|
|
28
|
+
RETURN p, nodeId, collect(pref) AS prefs
|
|
25
29
|
`;
|
|
26
30
|
queryParams = { email: email.toLowerCase(), accountId, limit: neo4j.int(limit) };
|
|
27
31
|
}
|
|
@@ -29,8 +33,11 @@ export async function contactLookup(params) {
|
|
|
29
33
|
query = `
|
|
30
34
|
MATCH (p:Person {accountId: $accountId})
|
|
31
35
|
WHERE p.telephone = $telephone AND ${trashFilter}
|
|
32
|
-
|
|
36
|
+
WITH p, elementId(p) AS nodeId
|
|
33
37
|
LIMIT $limit
|
|
38
|
+
OPTIONAL MATCH (p)-[:HAS_PREFERENCE]->(pref:Preference)
|
|
39
|
+
WHERE ${prefFilter}
|
|
40
|
+
RETURN p, nodeId, collect(pref) AS prefs
|
|
34
41
|
`;
|
|
35
42
|
queryParams = { telephone: telephone.trim(), accountId, limit: neo4j.int(limit) };
|
|
36
43
|
}
|
|
@@ -39,8 +46,11 @@ export async function contactLookup(params) {
|
|
|
39
46
|
MATCH (p:Person {accountId: $accountId})
|
|
40
47
|
WHERE toLower(trim(coalesce(p.givenName, '') + ' ' + coalesce(p.familyName, ''))) CONTAINS toLower($name)
|
|
41
48
|
AND ${trashFilter}
|
|
42
|
-
|
|
49
|
+
WITH p, elementId(p) AS nodeId
|
|
43
50
|
LIMIT $limit
|
|
51
|
+
OPTIONAL MATCH (p)-[:HAS_PREFERENCE]->(pref:Preference)
|
|
52
|
+
WHERE ${prefFilter}
|
|
53
|
+
RETURN p, nodeId, collect(pref) AS prefs
|
|
44
54
|
`;
|
|
45
55
|
queryParams = { name: normalizeNameQuery(name), accountId, limit: neo4j.int(limit) };
|
|
46
56
|
}
|
|
@@ -48,15 +58,26 @@ export async function contactLookup(params) {
|
|
|
48
58
|
query = `
|
|
49
59
|
MATCH (p:Person {accountId: $accountId})
|
|
50
60
|
WHERE ${trashFilter}
|
|
51
|
-
|
|
61
|
+
WITH p, elementId(p) AS nodeId
|
|
52
62
|
ORDER BY p.createdOn DESC
|
|
53
63
|
LIMIT $limit
|
|
64
|
+
OPTIONAL MATCH (p)-[:HAS_PREFERENCE]->(pref:Preference)
|
|
65
|
+
WHERE ${prefFilter}
|
|
66
|
+
RETURN p, nodeId, collect(pref) AS prefs
|
|
54
67
|
`;
|
|
55
68
|
queryParams = { accountId, limit: neo4j.int(limit) };
|
|
56
69
|
}
|
|
57
70
|
const result = await session.run(query, queryParams);
|
|
58
71
|
return result.records.map((record) => {
|
|
59
72
|
const p = record.get("p").properties;
|
|
73
|
+
const prefNodes = record.get("prefs") ?? [];
|
|
74
|
+
const preferences = prefNodes.map((n) => ({
|
|
75
|
+
category: n.properties.category,
|
|
76
|
+
key: n.properties.key,
|
|
77
|
+
value: n.properties.value,
|
|
78
|
+
confidence: n.properties.confidence ?? null,
|
|
79
|
+
observedAt: n.properties.observedAt ?? null,
|
|
80
|
+
}));
|
|
60
81
|
return {
|
|
61
82
|
nodeId: record.get("nodeId"),
|
|
62
83
|
givenName: p.givenName,
|
|
@@ -67,6 +88,8 @@ export async function contactLookup(params) {
|
|
|
67
88
|
source: p.source,
|
|
68
89
|
status: p.status,
|
|
69
90
|
createdOn: p.createdOn,
|
|
91
|
+
properties: p,
|
|
92
|
+
preferences,
|
|
70
93
|
};
|
|
71
94
|
});
|
|
72
95
|
}
|
|
@@ -74,4 +97,33 @@ export async function contactLookup(params) {
|
|
|
74
97
|
await session.close();
|
|
75
98
|
}
|
|
76
99
|
}
|
|
100
|
+
// Keys already shown in the head/jobTitle lines, or internal, so they are not
|
|
101
|
+
// repeated in the Properties block. accountId is account-scoping; embedding-like
|
|
102
|
+
// and structured values are skipped by the scalar check below.
|
|
103
|
+
const IDENTITY_AND_INTERNAL_KEYS = new Set([
|
|
104
|
+
"givenName", "familyName", "email", "telephone", "jobTitle",
|
|
105
|
+
"source", "status", "createdOn", "accountId",
|
|
106
|
+
]);
|
|
107
|
+
export function formatContactResults(results) {
|
|
108
|
+
return results
|
|
109
|
+
.map((c) => {
|
|
110
|
+
const identifierPart = c.email
|
|
111
|
+
? `<${c.email}>`
|
|
112
|
+
: c.telephone
|
|
113
|
+
? `<${c.telephone}>`
|
|
114
|
+
: "";
|
|
115
|
+
const head = `${c.givenName}${c.familyName ? ` ${c.familyName}` : ""} ${identifierPart} — ${c.status} (from ${c.source}, ${c.createdOn})`;
|
|
116
|
+
const jobLine = c.jobTitle ? `\n jobTitle: ${c.jobTitle}` : "";
|
|
117
|
+
const propEntries = Object.entries(c.properties).filter(([k, v]) => !IDENTITY_AND_INTERNAL_KEYS.has(k) &&
|
|
118
|
+
(typeof v === "string" || typeof v === "number" || typeof v === "boolean"));
|
|
119
|
+
const propLines = propEntries.length > 0
|
|
120
|
+
? `\n Properties:\n${propEntries.map(([k, v]) => ` - ${k}: ${v}`).join("\n")}`
|
|
121
|
+
: "";
|
|
122
|
+
const prefLines = c.preferences.length > 0
|
|
123
|
+
? `\n Preferences:\n${c.preferences.map((p) => ` - ${p.value}`).join("\n")}`
|
|
124
|
+
: "";
|
|
125
|
+
return `${head}${jobLine}${propLines}${prefLines}`;
|
|
126
|
+
})
|
|
127
|
+
.join("\n");
|
|
128
|
+
}
|
|
77
129
|
//# sourceMappingURL=contact-lookup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-lookup.js","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAU1E,yEAAyE;AACzE,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-lookup.js","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,8 @@ Ask naturally:
|
|
|
27
27
|
|
|
28
28
|
{{productName}} searches by name, email, phone number, or any detail you provide.
|
|
29
29
|
|
|
30
|
+
When {{productName}} drafts an email to a contact, it reads that contact's record first. If the record carries routing you have set (for example an address to always copy, or a preferred send-to address), {{productName}} applies it to the draft and shows it to you for approval before anything is sent.
|
|
31
|
+
|
|
30
32
|
## Updating a Contact
|
|
31
33
|
|
|
32
34
|
Tell {{productName}} what changed:
|
|
@@ -39,6 +39,10 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
|
|
|
39
39
|
| `outlook-draft-edit` | Update an existing draft in place (`PATCH /me/messages/{draftId}`) and/or add and remove attachments. Graph drafts are mutable — pass draftId plus the fields to change (to/cc/bcc/subject/body/isHtml/attachments/removeAttachments). `attachments` adds; `removeAttachments` takes files off by name or attachment id; both together replace a file in one call. Sent messages cannot be edited. Needs `Mail.ReadWrite`. |
|
|
40
40
|
| `outlook-draft-send` | Send an existing draft by id (`POST /me/messages/{id}/send`). Graph consumes the draft, returns 202. Needs `Mail.Send`. |
|
|
41
41
|
|
|
42
|
+
## Drafting to a Contact
|
|
43
|
+
|
|
44
|
+
When the admin agent drafts or sends a message to a known contact, it reads that contact's record first and applies any routing the record carries, for example a preferred send-to address, showing the result for your approval before anything is sent.
|
|
45
|
+
|
|
42
46
|
## Observability
|
|
43
47
|
|
|
44
48
|
All log lines start with `[outlook-mcp]` and write to `server.log`. They are key=value, account-scoped:
|
|
@@ -136,9 +136,9 @@ Ask {{productName}}: "What plugins do I have?" or "List my plugins."
|
|
|
136
136
|
|
|
137
137
|
Skills you create at runtime through the admin `skill-builder` skill are saved on disk as their own plugin under `data/accounts/{accountId}/plugins/{pluginName}/`. The admin agent calls `mcp__plugin_admin_admin__store-skill`, which composes `PLUGIN.md` (on first call) and `skills/{skillName}/SKILL.md` plus any reference files. The agent supplies `pluginName`, `skillName`, `description`, `publicEmbed`, `body`, and optional references — the path is computed by the tool, never by the agent.
|
|
138
138
|
|
|
139
|
-
These operator-authored plugins survive reinstall because the installer's wipe zone excludes `data/`.
|
|
139
|
+
These operator-authored plugins survive reinstall because the installer's wipe zone excludes `data/`. They are loaded directly from `data/accounts/{accountId}/plugins/*`: the spawn manifest and the admin skill resolvers (`skill-load`, `skill-find`, `skill-search`, `plugin-read`) walk that per-account root alongside the shipped and premium plugin roots, so no copy into the platform plugins tree is made or needed. `store-skill` confirms the written skill resolves through that same walk before reporting success. Operator plugins are always active for their own account; the admin agent sees every operator skill by default, and per-skill `publicEmbed: true|false` controls which skills surface to the public agent.
|
|
140
140
|
|
|
141
|
-
To edit an operator-authored skill later, ask {{productName}} to update it — the admin agent re-runs `store-skill` for the same `pluginName`/`skillName` and the new content overwrites in place. To remove one, delete the directory under `data/accounts/{accountId}/plugins/{pluginName}/skills/{skillName}/` (or the whole plugin) — the next session
|
|
141
|
+
To edit an operator-authored skill later, ask {{productName}} to update it — the admin agent re-runs `store-skill` for the same `pluginName`/`skillName` and the new content overwrites in place. To remove one, delete the directory under `data/accounts/{accountId}/plugins/{pluginName}/skills/{skillName}/` (or the whole plugin) — the next session no longer surfaces it, since skills load directly from that path.
|
|
142
142
|
|
|
143
143
|
`pluginName` collisions with shipped plugin names are refused by `store-skill` with a structured error. See [.docs/agents.md](../../../../.docs/agents.md) § "Operator-authored skills as plugin files" for the full contract.
|
|
144
144
|
|
|
@@ -71,12 +71,18 @@ Compose the reply. Anchor to:
|
|
|
71
71
|
|
|
72
72
|
Keep drafts tight. Email is not a wall-of-text medium.
|
|
73
73
|
|
|
74
|
+
### 5b. Resolve the recipient and read their contact record
|
|
75
|
+
|
|
76
|
+
Before presenting any draft (reply or new outbound), resolve each named recipient to their `:Person` with `contact-lookup` (by email when you have it, else by name). When the contact resolves, read its properties and preferences: if the record carries routing (an always-CC, an alternate send-to address), apply it to the draft To/Cc and surface it in the presentation for the operator to approve. An explicit operator instruction on this send always overrides the stored routing. When the contact does not resolve, or carries no routing, leave Cc empty and draft to the operator-named address only. Never invent a recipient, and never create a `:Person` here.
|
|
77
|
+
|
|
78
|
+
Emit narration `[email-composition] contact-lookup recipient=<addr> resolved=<bool> prefsFound=<n> applied=<n>` for each resolved recipient, before the draft is presented.
|
|
79
|
+
|
|
74
80
|
### 6. Present
|
|
75
81
|
|
|
76
82
|
Show the draft inline with:
|
|
77
83
|
|
|
78
84
|
- **To** — recipient address (the original sender, unless the operator asked for `replyAll`).
|
|
79
|
-
- **Cc / Bcc** — empty unless the operator explicitly named one. Never copy people forward from the original thread without being asked.
|
|
85
|
+
- **Cc / Bcc** — empty unless the operator explicitly named one, or the recipient's contact record carries routing read in step 5b (an always-CC or alternate address), which is applied here and shown for approval. Never copy people forward from the original thread without being asked.
|
|
80
86
|
- **Subject** — the threaded subject (the original with `Re:` if not already prefixed; `email-reply` handles this).
|
|
81
87
|
- **Body** — the draft.
|
|
82
88
|
- **Notes** — any flags from step 3 (tone, attachments, OOO skip-already-applied).
|
|
@@ -168,6 +174,7 @@ Delete is move-to-Trash only. There is no permanent/hard delete, no delete-by-cr
|
|
|
168
174
|
|-----------|-----------|
|
|
169
175
|
| Multiple threads match the operator's description | Surface 3-5 candidates and ask. Never guess. |
|
|
170
176
|
| CC / BCC in the original thread | Reply to sender only by default. Ask explicitly before copying others. |
|
|
177
|
+
| Recipient's contact record carries an always-CC or alternate address | Apply it to To/Cc in the draft and show it for approval. Explicit operator instruction on this send overrides it. |
|
|
171
178
|
| `replyAll` requested | Pass `replyAll: true` to `email-reply`. Otherwise omit (defaults false). |
|
|
172
179
|
| Original thread has attachments referenced in the draft | List attachment names; ask whether to forward. |
|
|
173
180
|
| Inbound is an out-of-office autoreply | Skip. Flag to operator. Do not draft. |
|
|
@@ -201,6 +208,7 @@ The skill emits narration at each major step so a session log makes the full dra
|
|
|
201
208
|
| Thread locked | `[email-composition] thread-identified messageId=<id> subject="<subject>"` |
|
|
202
209
|
| Multiple matches | `[email-composition] ambiguous-thread candidates=<n>` |
|
|
203
210
|
| Draft shown | `[email-composition] draft-presented recipient=<addr> subjectPrefix="<first-60-chars>"` |
|
|
211
|
+
| Contact resolved | `[email-composition] contact-lookup recipient=<addr> resolved=<bool> prefsFound=<n> applied=<n>` |
|
|
204
212
|
| Send approved | `[email-composition] send-confirmed` |
|
|
205
213
|
| Draft saved (unsent) | `[email-composition] draft-saved folder=<drafts-folder> uid=<uid>` |
|
|
206
214
|
| Draft revised | `[email-composition] draft-edited targetUid=<old> newUid=<new>` |
|
|
@@ -68,6 +68,12 @@ Writing operator-reviewed messages into the business graph is the `outlook-mail-
|
|
|
68
68
|
|
|
69
69
|
- `outlook-mail-otp-extract sender=<domain>` — poll the inbox for a verification code from a sender (e.g. `cloudflare.com`). Optional `subjectPattern` (regex) and `timeout` (seconds, default 60). Reads full bodies to extract the code. Used during service authentication.
|
|
70
70
|
|
|
71
|
+
## Resolve the recipient before drafting
|
|
72
|
+
|
|
73
|
+
Before drafting or sending any message, resolve each named recipient to their `:Person` with `contact-lookup` (by email when you have it, else by name). When the contact resolves and its record carries routing (an always-CC or an alternate send-to address), apply it to the draft `to`/`cc` and show it for the operator to approve. An explicit operator instruction on this send always overrides the stored routing. When the contact does not resolve, or carries no routing, draft to the operator-named address only, leave `cc` empty, and never invent a recipient or create a `:Person` here.
|
|
74
|
+
|
|
75
|
+
Emit narration `[outlook] contact-lookup recipient=<addr> resolved=<bool> prefsFound=<n> applied=<n>` for each resolved recipient, before the draft is presented.
|
|
76
|
+
|
|
71
77
|
## Sending and drafting
|
|
72
78
|
|
|
73
79
|
- `outlook-mail-send` — send a message. `to` is an array of addresses (at least one); `cc`/`bcc` optional arrays; `subject`; `body`; `isHtml` true for an HTML body, otherwise plain text; `attachments` an array of file paths inside the account directory, up to 25 MB each. A copy is saved to Sent Items. Success is confirmed on Graph's 202 response.
|
|
@@ -23,7 +23,7 @@ This skill is invoked two ways, and they want opposite outputs. Decide which one
|
|
|
23
23
|
- Do not invent requirements or add scope the user did not ask for. If the prompt was already clear, say so in one line and restate it tightly.
|
|
24
24
|
- The restated brief is bound by the three doctrines (see `/doctrine`): precise/concise/plain, no speculation, observability-first. If the user's wording carries a speculative claim or skips an observability step the doctrines require, the restatement corrects it rather than echoing the slip.
|
|
25
25
|
- Reminder — source of knowledge: every factual claim in the restated brief, and in the work you then do from it, traces to one of IDENTITY's four permissible sources (a graph read this session, a tool output returned this session, content the operator pasted into this conversation, or the body of a loaded skill such as platform-architecture). Pretraining recall is not a source. If a fact the brief needs does not trace to one of the four, do not bake it in from memory — read the graph, run the tool, load the skill, or ask the operator first.
|
|
26
|
-
- End the restated brief with the
|
|
26
|
+
- End the restated brief with the four-rung routing ladder, applied to the restated task in order. **(1)** Delegate to the specialist that owns the deliverable's type via the Agent tool: ownership covers origination, not just the final render, and the brief states the outcome plus the binding constraints, never the lines, anchors, or literal text to insert. **(2)** Only if no installed specialist owns it, load a skill from the admin-usable list with skill-load and do the work from this seat. **(3)** Only if neither a specialist nor an admin skill already owns it, decide whether the deliverable recurs: a deliverable the business issues repeatedly, or one the operator gives a fixed template, numbering, naming, or filing rule for, is authored once as its own skill by loading skill-builder with skill-load (or agent-builder for a worker agent), then produced through that skill, not hand-built here. **(4)** Only if the deliverable is a genuine one-off, author it directly: freestyle inline authoring is the last resort, not a peer of the routes above. The live specialist roster and admin-usable list are injected each turn by the standing prompt-optimiser directive; the ranked ladder is the classifier. The closing line is not the trigger; the classifier is.
|
|
27
27
|
|
|
28
28
|
Then do the work from the restated task. Everything below this section is for the other mode and does not apply — stop reading here.
|
|
29
29
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Task 698 — the prompt-optimiser restatement mode must end with the
|
|
2
|
+
# Task 698 — the prompt-optimiser restatement mode must end with the four-rung
|
|
3
3
|
# routing ladder, not the lone "Delegate this task to the appropriate specialist."
|
|
4
4
|
# line it carried before.
|
|
5
5
|
set -u
|
|
@@ -14,6 +14,7 @@ chk "lone delegate line removed" '! grep -q "Delegate this task to the appropr
|
|
|
14
14
|
chk "tier 1 delegate to specialist" 'grep -qiE "delegate to (a |the )?specialist" "$SK"'
|
|
15
15
|
chk "tier 2 admin-usable skill" 'grep -qi "admin-usable" "$SK"'
|
|
16
16
|
chk "tier 3 freestyle last resort" 'grep -qiE "freestyle|last resort" "$SK"'
|
|
17
|
+
chk "rung: recurring deliverable -> skill-builder" 'grep -qi "skill-builder" "$SK"'
|
|
17
18
|
chk "restatement section present" 'grep -qi "In-session restatement mode" "$SK"'
|
|
18
19
|
|
|
19
20
|
echo "----"
|
|
@@ -50,7 +50,7 @@ import { projectsDirForCwd, sessionsDir, jsonlPathForSessionId, slugForExistingJ
|
|
|
50
50
|
import { readSidecar } from './session-sidecar.js';
|
|
51
51
|
import { runSystemPromptSelfTest, runIdentityDriftAssertion, runIdentityForbiddenTokenCheck } from './system-prompt.js';
|
|
52
52
|
import { assertSpecialistDrift } from './specialist-drift.js';
|
|
53
|
-
import { assertSkillRegistrationDrift, buildSubToMarketplace, readCcRegistrationSurfaces, readAccountEnabledPlugins, } from './skill-registration-drift.js';
|
|
53
|
+
import { assertSkillRegistrationDrift, buildSubToMarketplace, readCcRegistrationSurfaces, readAccountEnabledPlugins, auditAccountPluginSkills, } from './skill-registration-drift.js';
|
|
54
54
|
import { assertToolRegistrationDrift, readToolPluginManifestStates, } from './tool-registration-drift.js';
|
|
55
55
|
import { AccountTitleStores } from './account-title-stores.js';
|
|
56
56
|
import { WaChannelStore } from './wa-channel-store.js';
|
|
@@ -463,6 +463,31 @@ async function main() {
|
|
|
463
463
|
log(`startup-self-test skill-registration=ok inspected=${drift.inspected}`);
|
|
464
464
|
}
|
|
465
465
|
}
|
|
466
|
+
// Task 1962 — boot-time census of operator-authored account plugin skills.
|
|
467
|
+
// These live under <accountDir>/plugins/<plugin>/skills/<slug>/ and are loaded
|
|
468
|
+
// directly from that root (no platform mirror). Emitting one line per account
|
|
469
|
+
// makes the operator-skill population visible at boot — the observability that
|
|
470
|
+
// was missing when a canonical account copy loaded nowhere. A slug owned by
|
|
471
|
+
// more than one of an account's plugins is FATAL-logged (the resolvers surface
|
|
472
|
+
// it as ambiguous). Non-exiting by design, mirroring the gates above.
|
|
473
|
+
{
|
|
474
|
+
const accountsRoot = dirname(config.accountDir);
|
|
475
|
+
for (const row of auditAccountPluginSkills(accountsRoot)) {
|
|
476
|
+
log(`[account-plugin-skills] op=census accountId=${row.accountId} plugins=${row.plugins} skills=${row.skills} collisions=${row.collisions.length}`);
|
|
477
|
+
for (const slug of row.collisions) {
|
|
478
|
+
const line = `[account-plugin-skills] op=census FATAL accountId=${row.accountId} slug=${slug} reason=ambiguous-across-account-plugins`;
|
|
479
|
+
log(line);
|
|
480
|
+
try {
|
|
481
|
+
spawnSync('systemd-cat', ['-t', 'maxy-csm', '-p', 'err'], {
|
|
482
|
+
input: line + '\n',
|
|
483
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
484
|
+
timeout: 1_000,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
catch { /* journald mirror is best-effort */ }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
466
491
|
// Task 1574 — boot-time tool-availability reconciliation. A tool-bearing
|
|
467
492
|
// plugin on `mcp-manifest: skip` keeps its PLUGIN.md `mcp:` block (so the
|
|
468
493
|
// source-level mcp-allowlist-without-server gate passes) while the generator
|