@rubytech/create-maxy-code 0.1.498 → 0.1.499
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/skills/platform-architecture/SKILL.md +3 -3
- package/payload/platform/plugins/docs/references/internals.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/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/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/server/public/assets/{chat-qtOiJX-Y.js → chat-MvnxPJZ5.js} +1 -1
- package/payload/server/public/assets/{operator-icJwMkZF.js → operator-DCNWUKP_.js} +1 -1
- package/payload/server/public/assets/{page-iG57nKOg.js → page-C4TrTU6r.js} +1 -1
- package/payload/server/public/assets/{public-CkOumzpf.js → public-DXJXQPwe.js} +1 -1
- package/payload/server/public/chat.html +2 -2
- package/payload/server/public/operator.html +2 -2
- package/payload/server/public/public.html +2 -2
- package/payload/server/server.js +47 -57
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/signature.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { mkdtempSync, writeFileSync, rmSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { loadSignature, applySignature } from '../lib/signature.js';
|
|
6
|
+
describe('Task 1952 — email signature load + apply', () => {
|
|
7
|
+
let dir = '';
|
|
8
|
+
beforeEach(() => { dir = realpathSync(mkdtempSync(join(tmpdir(), 'sig-'))); });
|
|
9
|
+
afterEach(() => { if (dir)
|
|
10
|
+
rmSync(dir, { recursive: true, force: true }); });
|
|
11
|
+
it('loadSignature returns null fields when no files exist', () => {
|
|
12
|
+
expect(loadSignature(dir)).toEqual({ text: null, html: null, font: null });
|
|
13
|
+
});
|
|
14
|
+
it('loadSignature returns null fields when accountDir is undefined', () => {
|
|
15
|
+
expect(loadSignature(undefined)).toEqual({ text: null, html: null, font: null });
|
|
16
|
+
});
|
|
17
|
+
it('loadSignature reads text and html verbatim and trims font', () => {
|
|
18
|
+
writeFileSync(join(dir, 'email-signature.txt'), 'Regards,\nDale');
|
|
19
|
+
writeFileSync(join(dir, 'email-signature.html'), '<p>Regards,<br>Dale</p>');
|
|
20
|
+
writeFileSync(join(dir, 'email-signature.font'), 'Georgia, serif\n');
|
|
21
|
+
expect(loadSignature(dir)).toEqual({
|
|
22
|
+
text: 'Regards,\nDale',
|
|
23
|
+
html: '<p>Regards,<br>Dale</p>',
|
|
24
|
+
font: 'Georgia, serif',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('applySignature appends text to body byte-for-byte', () => {
|
|
28
|
+
const out = applySignature({ body: 'Hello.' }, { text: 'Regards,\nDale', html: null, font: null });
|
|
29
|
+
expect(out.body).toBe('Hello.\n\nRegards,\nDale');
|
|
30
|
+
expect(out.html).toBeUndefined();
|
|
31
|
+
expect(out.applied).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('applySignature appends html sig then wraps in font when html present', () => {
|
|
34
|
+
const out = applySignature({ body: 'Hello.', html: '<p>Hello.</p>' }, { text: null, html: '<p>Regards</p>', font: 'Georgia, serif' });
|
|
35
|
+
expect(out.html).toBe('<div style="font-family: Georgia, serif"><p>Hello.</p><p>Regards</p></div>');
|
|
36
|
+
expect(out.applied).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('applySignature leaves html unset when caller gave no html', () => {
|
|
39
|
+
const out = applySignature({ body: 'Hi' }, { text: null, html: '<p>Sig</p>', font: 'Arial' });
|
|
40
|
+
expect(out.html).toBeUndefined();
|
|
41
|
+
expect(out.applied).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it('applySignature applied=false when no signature files present', () => {
|
|
44
|
+
const out = applySignature({ body: 'Hi', html: '<p>Hi</p>' }, { text: null, html: null, font: null });
|
|
45
|
+
expect(out.body).toBe('Hi');
|
|
46
|
+
expect(out.html).toBe('<p>Hi</p>');
|
|
47
|
+
expect(out.applied).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=signature.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.test.js","sourceRoot":"","sources":["../../src/__tests__/signature.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -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.
|
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.
|
package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts
CHANGED
|
@@ -7,24 +7,24 @@ export interface AuditSessionLike {
|
|
|
7
7
|
}
|
|
8
8
|
export interface AuditFinding {
|
|
9
9
|
accountId: string;
|
|
10
|
-
/** Active (non-trashed, not-declined, above-floor) preference rows
|
|
11
|
-
*
|
|
12
|
-
* owner
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
/** Rows injection
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
duplicateClusters: number;
|
|
10
|
+
/** Active (non-trashed, not-declined, above-floor) owner preference rows
|
|
11
|
+
* stored — the count injection is expected to surface verbatim. Task 1564:
|
|
12
|
+
* owner-scoped, so rows stranded on a non-owner profile are neither counted
|
|
13
|
+
* nor injected. */
|
|
14
|
+
stored: number;
|
|
15
|
+
/** Rows injection actually surfaces (resolveActiveRules, the exact injection
|
|
16
|
+
* path). Equal to `stored` unless injection dropped rows. */
|
|
17
|
+
injected: number;
|
|
19
18
|
}
|
|
20
|
-
/** One
|
|
21
|
-
* the
|
|
19
|
+
/** One reconcile line per flagged account. accountId truncated to 8 for
|
|
20
|
+
* readability; the two counts are greppable fields. */
|
|
22
21
|
export declare function formatAuditLine(f: AuditFinding): string;
|
|
23
|
-
/** For every account that holds active preferences,
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
/** For every account that holds active preferences, compare the stored active
|
|
23
|
+
* count against the count injection surfaces. Returns a finding ONLY where the
|
|
24
|
+
* two differ — injection is meant to surface every stored active preference
|
|
25
|
+
* verbatim (Task 1951), so a gap is a dropped-row regression. Measures against
|
|
26
|
+
* the exact injection path (resolveActiveRules), so the audit and injection can
|
|
27
|
+
* never disagree about what "injected" means. */
|
|
28
28
|
export declare function auditPreferenceReconciliation(session: AuditSessionLike): Promise<AuditFinding[]>;
|
|
29
29
|
export interface PreferenceAuditDeps {
|
|
30
30
|
neo4jUri: string;
|
|
@@ -32,10 +32,10 @@ export interface PreferenceAuditDeps {
|
|
|
32
32
|
neo4jPassword: string;
|
|
33
33
|
logger: (line: string) => void;
|
|
34
34
|
}
|
|
35
|
-
/** Run one audit pass and log a
|
|
35
|
+
/** Run one audit pass and log a reconcile line per flagged account. Best-effort
|
|
36
36
|
* Neo4j — absent env or an unreachable/erroring read logs `unavailable` and
|
|
37
|
-
* never throws. A clean pass logs a single `op=
|
|
38
|
-
* liveness is visible. */
|
|
37
|
+
* never throws. A clean pass logs a single `op=reconcile drifted=0` line so the
|
|
38
|
+
* audit's own liveness is visible. */
|
|
39
39
|
export declare function emitPreferenceAudit(deps: PreferenceAuditDeps): Promise<void>;
|
|
40
40
|
export interface PreferenceAudit {
|
|
41
41
|
start(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preference-reconciliation-audit.d.ts","sourceRoot":"","sources":["../src/preference-reconciliation-audit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preference-reconciliation-audit.d.ts","sourceRoot":"","sources":["../src/preference-reconciliation-audit.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CACD,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAA;CAC9D;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB;;;wBAGoB;IACpB,MAAM,EAAE,MAAM,CAAA;IACd;kEAC8D;IAC9D,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;wDACwD;AACxD,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAEvD;AAED;;;;;kDAKkD;AAClD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B;AAED;;;uCAGuC;AACvC,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBlF;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;CACb;AAED;sCACsC;AACtC,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,mBAAmB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GACjD,eAAe,CAiBjB"}
|
package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js
CHANGED
|
@@ -1,30 +1,26 @@
|
|
|
1
|
-
// Task 1486 — standing preference-reconciliation audit.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// against the de-conflicted count that injection would actually surface (via the
|
|
11
|
-
// same @maxy/active-rules definition). A positive gap is unreconciled
|
|
12
|
-
// same-subject duplicates. Best-effort by construction: a Neo4j-unreachable read
|
|
13
|
-
// logs and returns nothing rather than crashing the manager (Neo4j may not be up
|
|
14
|
-
// at boot). Shape mirrors rootless-client-audit.ts.
|
|
1
|
+
// Task 1486 / 1951 — standing preference-reconciliation audit. Injection now
|
|
2
|
+
// surfaces every stored active preference verbatim (Task 1951), so the invariant
|
|
3
|
+
// is stored == injected. This audit is the tripwire: for each account it compares
|
|
4
|
+
// the stored active-candidate count against the count injection actually surfaces
|
|
5
|
+
// (resolveActiveRules, the exact injection path). A gap means injection dropped
|
|
6
|
+
// rows — a reintroduced cap, filter, or truncation — caught here without
|
|
7
|
+
// reproducing a turn. Best-effort by construction: a Neo4j-unreachable read logs
|
|
8
|
+
// and returns nothing rather than crashing the manager (Neo4j may not be up at
|
|
9
|
+
// boot). Shape mirrors rootless-client-audit.ts.
|
|
15
10
|
import neo4j from 'neo4j-driver';
|
|
16
|
-
import { queryActiveRuleCandidates,
|
|
17
|
-
const TAG = '[preference-
|
|
18
|
-
/** One
|
|
19
|
-
* the
|
|
11
|
+
import { queryActiveRuleCandidates, resolveActiveRules, } from '../../../lib/active-rules/dist/index.js';
|
|
12
|
+
const TAG = '[preference-inject]';
|
|
13
|
+
/** One reconcile line per flagged account. accountId truncated to 8 for
|
|
14
|
+
* readability; the two counts are greppable fields. */
|
|
20
15
|
export function formatAuditLine(f) {
|
|
21
|
-
return `${TAG} op=
|
|
16
|
+
return `${TAG} op=reconcile accountId=${f.accountId.slice(0, 8)} stored=${f.stored} injected=${f.injected}`;
|
|
22
17
|
}
|
|
23
|
-
/** For every account that holds active preferences,
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
18
|
+
/** For every account that holds active preferences, compare the stored active
|
|
19
|
+
* count against the count injection surfaces. Returns a finding ONLY where the
|
|
20
|
+
* two differ — injection is meant to surface every stored active preference
|
|
21
|
+
* verbatim (Task 1951), so a gap is a dropped-row regression. Measures against
|
|
22
|
+
* the exact injection path (resolveActiveRules), so the audit and injection can
|
|
23
|
+
* never disagree about what "injected" means. */
|
|
28
24
|
export async function auditPreferenceReconciliation(session) {
|
|
29
25
|
const accountsRes = await session.run(`MATCH (pref:Preference {scope: 'admin'})
|
|
30
26
|
WHERE NOT pref:Trashed AND pref.deletedAt IS NULL
|
|
@@ -33,20 +29,18 @@ export async function auditPreferenceReconciliation(session) {
|
|
|
33
29
|
const findings = [];
|
|
34
30
|
for (const rec of accountsRes.records) {
|
|
35
31
|
const accountId = String(rec.get('accountId'));
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (duplicateClusters > 0) {
|
|
41
|
-
findings.push({ accountId, storedActive, injectedActive, duplicateClusters });
|
|
32
|
+
const stored = (await queryActiveRuleCandidates(session, accountId)).length;
|
|
33
|
+
const injected = (await resolveActiveRules(session, accountId)).rules.length;
|
|
34
|
+
if (stored !== injected) {
|
|
35
|
+
findings.push({ accountId, stored, injected });
|
|
42
36
|
}
|
|
43
37
|
}
|
|
44
38
|
return findings;
|
|
45
39
|
}
|
|
46
|
-
/** Run one audit pass and log a
|
|
40
|
+
/** Run one audit pass and log a reconcile line per flagged account. Best-effort
|
|
47
41
|
* Neo4j — absent env or an unreachable/erroring read logs `unavailable` and
|
|
48
|
-
* never throws. A clean pass logs a single `op=
|
|
49
|
-
* liveness is visible. */
|
|
42
|
+
* never throws. A clean pass logs a single `op=reconcile drifted=0` line so the
|
|
43
|
+
* audit's own liveness is visible. */
|
|
50
44
|
export async function emitPreferenceAudit(deps) {
|
|
51
45
|
if (!deps.neo4jUri || !deps.neo4jPassword) {
|
|
52
46
|
deps.logger(`${TAG} op=unavailable reason=no-neo4j-env`);
|
|
@@ -57,7 +51,7 @@ export async function emitPreferenceAudit(deps) {
|
|
|
57
51
|
try {
|
|
58
52
|
const findings = await auditPreferenceReconciliation(session);
|
|
59
53
|
if (findings.length === 0) {
|
|
60
|
-
deps.logger(`${TAG} op=
|
|
54
|
+
deps.logger(`${TAG} op=reconcile drifted=0`);
|
|
61
55
|
return;
|
|
62
56
|
}
|
|
63
57
|
for (const f of findings)
|
package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preference-reconciliation-audit.js","sourceRoot":"","sources":["../src/preference-reconciliation-audit.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"preference-reconciliation-audit.js","sourceRoot":"","sources":["../src/preference-reconciliation-audit.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAClF,gFAAgF;AAChF,yEAAyE;AACzE,iFAAiF;AACjF,+EAA+E;AAC/E,iDAAiD;AAEjD,OAAO,KAAK,MAAM,cAAc,CAAA;AAChC,OAAO,EACL,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,yCAAyC,CAAA;AAEhD,MAAM,GAAG,GAAG,qBAAqB,CAAA;AAqBjC;wDACwD;AACxD,MAAM,UAAU,eAAe,CAAC,CAAe;IAC7C,OAAO,GAAG,GAAG,2BAA2B,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC,QAAQ,EAAE,CAAA;AAC7G,CAAC;AAED;;;;;kDAKkD;AAClD,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAyB;IAEzB,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC;;;iDAG6C,EAC7C,EAAE,CACH,CAAA;IACD,MAAM,QAAQ,GAAmB,EAAE,CAAA;IACnC,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAG,CAAC,MAAM,yBAAyB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAA;QAC3E,MAAM,QAAQ,GAAG,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAA;QAC5E,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AASD;;;uCAGuC;AACvC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAyB;IACjE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,qCAAqC,CAAC,CAAA;QACxD,OAAM;IACR,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAA;IAChG,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;IAChC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,OAAO,CAAC,CAAA;QAC7D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,yBAAyB,CAAC,CAAA;YAC5C,OAAM;QACR,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,QAAQ;YAAE,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACjG,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QACrB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;AACH,CAAC;AAOD;sCACsC;AACtC,MAAM,UAAU,qBAAqB,CACnC,IAAkD;IAElD,IAAI,KAAK,GAA0B,IAAI,CAAA;IACvC,OAAO;QACL,KAAK;YACH,IAAI,KAAK;gBAAE,OAAM;YACjB,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;gBACvB,KAAK,mBAAmB,CAAC,IAAI,CAAC,CAAA;YAChC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;YACnB,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU;gBAAE,KAAK,CAAC,KAAK,EAAE,CAAA;QACtD,CAAC;QACD,IAAI;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,aAAa,CAAC,KAAK,CAAC,CAAA;gBACpB,KAAK,GAAG,IAAI,CAAA;YACd,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,U as t}from"./useSubAccountSwitcher-Bc7XHr0L.js";import"./useVoiceRecorder-Cu6zvuFx.js";import"./AdminShell-BFVVkWFC.js";import{n}from"./page-
|
|
1
|
+
import{B as e,U as t}from"./useSubAccountSwitcher-Bc7XHr0L.js";import"./useVoiceRecorder-Cu6zvuFx.js";import"./AdminShell-BFVVkWFC.js";import{n}from"./page-C4TrTU6r.js";import"./useCopyFeedback-DHCtz8qX.js";var r=t(),i=e();(0,r.createRoot)(document.getElementById(`root`)).render((0,i.jsx)(n,{}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./chunk-fQC6bEKA.js";import{B as t,G as n,U as r}from"./useSubAccountSwitcher-Bc7XHr0L.js";import{n as i,t as a}from"./AdminLoginScreens-Ce5aBBWF.js";import"./useVoiceRecorder-Cu6zvuFx.js";import"./AdminShell-BFVVkWFC.js";import{t as o}from"./page-
|
|
1
|
+
import{o as e}from"./chunk-fQC6bEKA.js";import{B as t,G as n,U as r}from"./useSubAccountSwitcher-Bc7XHr0L.js";import{n as i,t as a}from"./AdminLoginScreens-Ce5aBBWF.js";import"./useVoiceRecorder-Cu6zvuFx.js";import"./AdminShell-BFVVkWFC.js";import{t as o}from"./page-C4TrTU6r.js";import"./useCopyFeedback-DHCtz8qX.js";var s=r(),c=e(n(),1),l=t();function u(){let e=i(`operator`),t=(0,c.useRef)(null);return(0,c.useEffect)(()=>{e.appState!==`chat`&&t.current!==e.appState&&(t.current=e.appState,console.info(`[operator-ui] op=auth-gate state=${e.appState}`))},[e.appState]),e.appState===`chat`?(0,l.jsx)(o,{variant:`operator`,forceCanonicalSession:window.location.pathname!==`/chat`}):(0,l.jsx)(a,{auth:e})}(0,s.createRoot)(document.getElementById(`root`)).render((0,l.jsx)(u,{}));
|