@rubytech/create-maxy-code 0.1.498 → 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/lib/active-rules/dist/index.d.ts +3 -7
- package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/active-rules/dist/index.js +20 -52
- package/payload/platform/lib/active-rules/dist/index.js.map +1 -1
- package/payload/platform/lib/active-rules/src/index.test.ts +35 -69
- package/payload/platform/lib/active-rules/src/index.ts +17 -58
- 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 +11 -5
- 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/internals.md +2 -2
- 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/mcp/dist/__tests__/compose-signature.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-signature.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-signature.test.js +53 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/compose-signature.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.js +50 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js +15 -2
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts +29 -0
- package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/signature.js +55 -0
- package/payload/platform/plugins/email/mcp/dist/lib/signature.js.map +1 -0
- package/payload/platform/plugins/email/references/email-reference.md +10 -0
- package/payload/platform/plugins/email/skills/email-composition/SKILL.md +9 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +10 -9
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -1
- package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +2 -0
- package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +16 -0
- 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/preference-reconciliation-audit.d.ts +19 -19
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +28 -34
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.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-icJwMkZF.js → operator-DglDYmdV.js} +1 -1
- package/payload/server/public/assets/{page-iG57nKOg.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-CkOumzpf.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 +89 -63
- package/payload/server/public/assets/chat-qtOiJX-Y.js +0 -1
- package/payload/server/public/assets/data-BjqC4hyM.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAkC,MAAM,WAAW,CAAC;AAK7E,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CASxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CA8DxB"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import MailComposer from "nodemailer/lib/mail-composer/index.js";
|
|
3
4
|
import { buildMailOptions, replySubject } from "./smtp.js";
|
|
4
5
|
import { fetchEnvelopeByMessageId } from "./imap.js";
|
|
5
6
|
import { resolveOutboundAttachments, accountDirFor } from "./attachment-resolve.js";
|
|
7
|
+
import { loadSignature, applySignature } from "./signature.js";
|
|
6
8
|
/**
|
|
7
9
|
* Compile the shared mail options into raw RFC822 bytes via nodemailer's own composer.
|
|
8
10
|
*
|
|
@@ -64,11 +66,22 @@ export async function composeDraft(config, password, accountId, params) {
|
|
|
64
66
|
// value is the source of truth an edit re-resolves the draft by.
|
|
65
67
|
const host = (config.agentAddress || config.email).split("@")[1] || "localhost";
|
|
66
68
|
const draftMessageId = `<${randomUUID()}@${host}>`;
|
|
69
|
+
// Apply the account's saved signature deterministically, before the MIME is
|
|
70
|
+
// built, so every draft carries it without the model adding signature text.
|
|
71
|
+
// Resolve the account dir the same way the attachment path does: the param
|
|
72
|
+
// accountId (the sending account) is authoritative, ACCOUNT_DIR is the
|
|
73
|
+
// fallback, so the signature reads from the same account root the attachments
|
|
74
|
+
// are validated against.
|
|
75
|
+
const sigDir = accountDirFor(accountId) || process.env.ACCOUNT_DIR;
|
|
76
|
+
const sig = loadSignature(sigDir);
|
|
77
|
+
const signed = applySignature({ body: params.body, html: params.html }, sig);
|
|
78
|
+
const assetNote = sigDir ? join(sigDir, "email-signature") : "none";
|
|
79
|
+
console.error(`[email-draft] op=signature accountId=${accountId.slice(0, 8)} applied=${signed.applied} asset=${assetNote}`);
|
|
67
80
|
const sendOptions = {
|
|
68
81
|
to: params.to,
|
|
69
82
|
subject: resolvedSubject,
|
|
70
|
-
body:
|
|
71
|
-
html:
|
|
83
|
+
body: signed.body,
|
|
84
|
+
html: signed.html,
|
|
72
85
|
cc: params.cc,
|
|
73
86
|
bcc: params.bcc,
|
|
74
87
|
attachments,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,YAAY,MAAM,uCAAuC,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAoB,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface Signature {
|
|
2
|
+
text: string | null;
|
|
3
|
+
html: string | null;
|
|
4
|
+
font: string | null;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Load an account's saved email signature from the fixed account-dir convention.
|
|
8
|
+
* `.txt` and `.html` are returned verbatim (appended byte-for-byte). `.font` is a
|
|
9
|
+
* single CSS font-family value, trimmed of surrounding whitespace. A missing file
|
|
10
|
+
* is null, never an error: a draft still composes without a signature.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadSignature(accountDir: string | undefined): Signature;
|
|
13
|
+
/**
|
|
14
|
+
* Append the signature to a draft's body/html deterministically, with no model
|
|
15
|
+
* judgement. The plain-text signature is appended to body whenever present. The
|
|
16
|
+
* html signature and the font wrap apply only when the caller supplied html:
|
|
17
|
+
* font is HTML-only and has no plain-text analogue. `applied` is true when at
|
|
18
|
+
* least one signature part (text or html) was appended; the font wrap alone does
|
|
19
|
+
* not set it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function applySignature(input: {
|
|
22
|
+
body: string;
|
|
23
|
+
html?: string;
|
|
24
|
+
}, sig: Signature): {
|
|
25
|
+
body: string;
|
|
26
|
+
html?: string;
|
|
27
|
+
applied: boolean;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/lib/signature.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAUD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CASvE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACtC,GAAG,EAAE,SAAS,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAkBnD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
function readIf(path) {
|
|
4
|
+
try {
|
|
5
|
+
return readFileSync(path, "utf8");
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Load an account's saved email signature from the fixed account-dir convention.
|
|
13
|
+
* `.txt` and `.html` are returned verbatim (appended byte-for-byte). `.font` is a
|
|
14
|
+
* single CSS font-family value, trimmed of surrounding whitespace. A missing file
|
|
15
|
+
* is null, never an error: a draft still composes without a signature.
|
|
16
|
+
*/
|
|
17
|
+
export function loadSignature(accountDir) {
|
|
18
|
+
if (!accountDir)
|
|
19
|
+
return { text: null, html: null, font: null };
|
|
20
|
+
const base = join(accountDir, "email-signature");
|
|
21
|
+
const font = readIf(`${base}.font`);
|
|
22
|
+
return {
|
|
23
|
+
text: readIf(`${base}.txt`),
|
|
24
|
+
html: readIf(`${base}.html`),
|
|
25
|
+
font: font ? font.trim() : null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Append the signature to a draft's body/html deterministically, with no model
|
|
30
|
+
* judgement. The plain-text signature is appended to body whenever present. The
|
|
31
|
+
* html signature and the font wrap apply only when the caller supplied html:
|
|
32
|
+
* font is HTML-only and has no plain-text analogue. `applied` is true when at
|
|
33
|
+
* least one signature part (text or html) was appended; the font wrap alone does
|
|
34
|
+
* not set it.
|
|
35
|
+
*/
|
|
36
|
+
export function applySignature(input, sig) {
|
|
37
|
+
let body = input.body;
|
|
38
|
+
let html = input.html;
|
|
39
|
+
let applied = false;
|
|
40
|
+
if (sig.text !== null) {
|
|
41
|
+
body = `${body}\n\n${sig.text}`;
|
|
42
|
+
applied = true;
|
|
43
|
+
}
|
|
44
|
+
if (html !== undefined) {
|
|
45
|
+
if (sig.html !== null) {
|
|
46
|
+
html = `${html}${sig.html}`;
|
|
47
|
+
applied = true;
|
|
48
|
+
}
|
|
49
|
+
if (sig.font !== null) {
|
|
50
|
+
html = `<div style="font-family: ${sig.font}">${html}</div>`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { body, html, applied };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/lib/signature.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,UAA8B;IAC1D,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC;IACpC,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC;QAC5B,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAsC,EACtC,GAAc;IAEd,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACtB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACtB,IAAI,GAAG,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5B,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,GAAG,4BAA4B,GAAG,CAAC,IAAI,KAAK,IAAI,QAAQ,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -169,6 +169,16 @@ To get this resilience, pass the `draftMessageId` from the prior confirmation ba
|
|
|
169
169
|
|
|
170
170
|
If neither the UID nor the Message-ID resolves a live draft, the prior draft is genuinely gone. The edit does not fail — it saves the replacement as a fresh draft and says so (`previous draft no longer present; saved a fresh draft`). The atomicity rule still holds: the replacement is always written before any old copy is removed, so a draft is never left with zero copies.
|
|
171
171
|
|
|
172
|
+
### Signature
|
|
173
|
+
|
|
174
|
+
`email-draft` and `email-draft-edit` apply the account's saved signature to every draft deterministically, so the model never adds signature text itself. The signature lives at a fixed path under the account directory:
|
|
175
|
+
|
|
176
|
+
- `email-signature.txt` is the plain-text signature, appended to the message body.
|
|
177
|
+
- `email-signature.html` is the HTML signature block, appended to the message html.
|
|
178
|
+
- `email-signature.font` is an optional single-line CSS font-family; when present the html part is wrapped in one `font-family` div.
|
|
179
|
+
|
|
180
|
+
The plain-text signature is appended whenever it exists. The html signature and the font wrap apply only when the caller supplied html, because font has no plain-text analogue. The caller passes body and html without the signature, and the tool appends it. Each draft compose logs `[email-draft] op=signature accountId=<id8> applied=<bool> asset=<path>`; `applied=false` on a turn that produced a draft is the failure signal, meaning the asset was missing or the account directory did not resolve.
|
|
181
|
+
|
|
172
182
|
### Transient connection drops
|
|
173
183
|
|
|
174
184
|
A single transient transport drop (socket timeout, unexpected close, connection-not-available) during a draft write is retried once on a fresh connection. Authentication failures and other permanent errors are not retried — they surface immediately.
|
|
@@ -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>` |
|
package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// Task 1486 — profile-read exposes the account's active
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Task 1486 / 1951 — profile-read exposes the account's active rule-set (the
|
|
2
|
+
// same set injection uses). Injection is uncapped and never dedups: every stored
|
|
3
|
+
// active owner preference is returned verbatim, in confidence/observedAt order.
|
|
4
4
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
5
|
-
import { INJECTION_RULE_CAP } from "../../../../../../lib/active-rules/dist/index.js";
|
|
6
5
|
const mockSession = {
|
|
7
6
|
run: vi.fn(),
|
|
8
7
|
close: vi.fn().mockResolvedValue(undefined),
|
|
@@ -29,7 +28,9 @@ function wire(activeCandidates) {
|
|
|
29
28
|
}
|
|
30
29
|
describe("profile-read — activeRules", () => {
|
|
31
30
|
beforeEach(() => wire([]));
|
|
32
|
-
it("
|
|
31
|
+
it("returns same-subject candidates verbatim, without dedup", async () => {
|
|
32
|
+
// 'a' and 'b' are near-duplicates that the old read-side dedup would have
|
|
33
|
+
// folded; injection no longer dedups, so BOTH are returned.
|
|
33
34
|
wire([
|
|
34
35
|
{ preferenceId: "a", category: "content", key: "header_a", value: "use the standard header", confidence: 0.8, observedAt: "2026-07-01T00:00:00Z", embedding: [1, 0, 0, 0] },
|
|
35
36
|
{ preferenceId: "b", category: "content", key: "header_b", value: "standard header restated", confidence: 0.6, observedAt: "2026-07-02T00:00:00Z", embedding: [0.99, 0.01, 0, 0] },
|
|
@@ -37,9 +38,9 @@ describe("profile-read — activeRules", () => {
|
|
|
37
38
|
]);
|
|
38
39
|
const res = await profileRead({ accountId: "acct-1", userId: "user-1" });
|
|
39
40
|
const ids = res.activeRules.map((r) => r.preferenceId);
|
|
40
|
-
expect(ids).toEqual(["a", "c"]);
|
|
41
|
+
expect(ids).toEqual(["a", "b", "c"]);
|
|
41
42
|
});
|
|
42
|
-
it("
|
|
43
|
+
it("returns the uncapped set — all 20 candidates, no truncation", async () => {
|
|
43
44
|
const many = Array.from({ length: 20 }, (_, i) => ({
|
|
44
45
|
preferenceId: `p${i}`, category: "content", key: `k${i}`, value: `rule ${i}`,
|
|
45
46
|
confidence: 1 - i * 0.01, observedAt: "2026-07-01T00:00:00Z",
|
|
@@ -47,8 +48,8 @@ describe("profile-read — activeRules", () => {
|
|
|
47
48
|
}));
|
|
48
49
|
wire(many);
|
|
49
50
|
const res = await profileRead({ accountId: "acct-1", userId: "user-1" });
|
|
50
|
-
const
|
|
51
|
-
expect(
|
|
51
|
+
const ids = res.activeRules.map((r) => r.preferenceId);
|
|
52
|
+
expect(ids).toEqual(many.map((m) => m.preferenceId));
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
//# sourceMappingURL=profile-read-active-rules.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-read-active-rules.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/profile-read-active-rules.test.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"profile-read-active-rules.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/profile-read-active-rules.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,8 @@ Memory accumulates from conversation — never from questionnaires, quizzes, or
|
|
|
13
13
|
|
|
14
14
|
Every active preference is prepended to the agent's context on **every single turn**, for the life of the account. A verbose preference is not a one-time cost. It is paid again on every future turn, and it crowds out the live conversation. Concision here is not a matter of style. It is a hard, non-negotiable requirement: a preference that is not precise and concise is a defect.
|
|
15
15
|
|
|
16
|
+
Injection surfaces every stored active preference verbatim. Nothing de-duplicates or trims at injection time, so a redundant or bloated preference you store is injected on every future turn exactly as written. Write-time discipline is the only guard: keep each value minimal, and fold an overlapping preference into the existing one rather than storing a second.
|
|
17
|
+
|
|
16
18
|
Each `value` is one precise imperative: the rule itself and nothing else. Strip everything that is not the rule.
|
|
17
19
|
|
|
18
20
|
- No provenance or history ("Set 09/07/2026 after X went wrong", "SUPERSEDES the old rule", "Confirmed by Dale").
|
|
@@ -30,6 +30,16 @@ A stored preference is drifted when its substance maps to an installed capabilit
|
|
|
30
30
|
|
|
31
31
|
Do not treat this as a fixed keyword list. Check each preference's substance against the capabilities this install actually has. When one maps, restate the preference to route there, or convert it to a real job and retire it.
|
|
32
32
|
|
|
33
|
+
## Invariant versus soft classification
|
|
34
|
+
|
|
35
|
+
Beyond the four moves, classify each preference as invariant or soft.
|
|
36
|
+
|
|
37
|
+
A preference is invariant-class when its value describes a document layout, a naming convention, a template, or a fixed numeric or style default (a font, a header, a signature block, a numbering scheme, a filing structure). An invariant does not belong in the injected store as prose the model must read and obey. It belongs in the skill that owns the document or process, applied by that skill's template or tool step. For each invariant, name the candidate owning skill: quotes, works-orders, valuations, invoices, filing, or email.
|
|
38
|
+
|
|
39
|
+
A preference is soft when it is a genuine standing choice with no owning skill to enforce it: a contact, a timezone, a one-invoice-per-message rule, a skip-size numeral, a no-proactive-reminders rule. Soft preferences stay in the injected store.
|
|
40
|
+
|
|
41
|
+
For every invariant, the plan names the owning skill and whether that skill already applies it deterministically or a migration is still pending. A preference whose owning skill you cannot name is reported as unassigned rather than silently left in place. The email signature is the worked first instance: `email-draft` and `email-draft-edit` apply the account's saved signature asset deterministically, so its preference is retired once the asset is in place.
|
|
42
|
+
|
|
33
43
|
## Dry run first, always
|
|
34
44
|
|
|
35
45
|
The audit never mutates before the operator sees the plan. In order:
|
|
@@ -48,3 +58,9 @@ Close every audit with one line, whether it was a dry run or applied:
|
|
|
48
58
|
`[preference-audit] account=<id> reworded=<n> merged=<n> retired=<n> restated-to-capability=<n>`
|
|
49
59
|
|
|
50
60
|
Count what the plan proposes on a dry run, and what was applied on a real run.
|
|
61
|
+
|
|
62
|
+
The classification pass emits its own line on the audit's periodic tick, so an invariant-shaped preference accumulating unmigrated surfaces on its own and not only on demand:
|
|
63
|
+
|
|
64
|
+
`[preference-audit] op=classify account=<id8> invariant=<n> soft=<m> unassigned=<k>`
|
|
65
|
+
|
|
66
|
+
`unassigned>0` names a preference the audit could not place under an owning skill.
|
|
@@ -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
|