@rubytech/create-maxy-code 0.1.456 → 0.1.458

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.
Files changed (104) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/mcp-lifeline/dist/index.d.ts.map +1 -1
  3. package/payload/platform/lib/mcp-lifeline/dist/index.js +22 -1
  4. package/payload/platform/lib/mcp-lifeline/dist/index.js.map +1 -1
  5. package/payload/platform/lib/mcp-lifeline/src/__tests__/lifeline.test.ts +26 -0
  6. package/payload/platform/lib/mcp-lifeline/src/index.ts +24 -1
  7. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +135 -0
  8. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
  9. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
  10. package/payload/platform/lib/storage-broker/dist/cf-exec.js +61 -28
  11. package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
  12. package/payload/platform/lib/storage-broker/dist/house-credential.d.ts.map +1 -1
  13. package/payload/platform/lib/storage-broker/dist/house-credential.js +6 -2
  14. package/payload/platform/lib/storage-broker/dist/house-credential.js.map +1 -1
  15. package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +153 -0
  16. package/payload/platform/lib/storage-broker/src/cf-exec.ts +85 -41
  17. package/payload/platform/lib/storage-broker/src/house-credential.ts +6 -2
  18. package/payload/platform/plugins/cloudflare/bin/__tests__/cf-token.test.sh +145 -4
  19. package/payload/platform/plugins/cloudflare/bin/cf-token.sh +60 -3
  20. package/payload/platform/plugins/cloudflare/references/api.md +1 -1
  21. package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +1 -1
  22. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-erase.test.d.ts +2 -0
  23. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-erase.test.d.ts.map +1 -0
  24. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-erase.test.js +108 -0
  25. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-erase.test.js.map +1 -0
  26. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-export.test.d.ts +2 -0
  27. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-export.test.d.ts.map +1 -0
  28. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-export.test.js +57 -0
  29. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-export.test.js.map +1 -0
  30. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +2 -2
  31. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-export.js +1 -1
  32. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js +16 -3
  33. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js.map +1 -1
  34. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +108 -1
  35. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -1
  36. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +42 -15
  37. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
  38. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +32 -1
  39. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
  40. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts.map +1 -1
  41. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js +3 -5
  42. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js.map +1 -1
  43. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts +7 -0
  44. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts.map +1 -1
  45. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js +17 -3
  46. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js.map +1 -1
  47. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +6 -1
  48. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
  49. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +6 -1
  50. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
  51. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.d.ts.map +1 -1
  52. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.js +3 -4
  53. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.js.map +1 -1
  54. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts.map +1 -1
  55. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js +2 -4
  56. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js.map +1 -1
  57. package/payload/platform/plugins/outlook/references/graph-surfaces.md +14 -2
  58. package/payload/platform/plugins/storage-broker/PLUGIN.md +1 -1
  59. package/payload/platform/services/claude-session-manager/dist/index.js +1 -1
  60. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  61. package/payload/platform/services/claude-session-manager/dist/stuck-turn-recover.d.ts +22 -2
  62. package/payload/platform/services/claude-session-manager/dist/stuck-turn-recover.d.ts.map +1 -1
  63. package/payload/platform/services/claude-session-manager/dist/stuck-turn-recover.js +37 -10
  64. package/payload/platform/services/claude-session-manager/dist/stuck-turn-recover.js.map +1 -1
  65. package/payload/server/{chunk-SDYRKIYY.js → chunk-64FGYKNZ.js} +51 -29
  66. package/payload/server/public/assets/{AdminLoginScreens-Cl7VBbPS.js → AdminLoginScreens-BFbpkEUS.js} +1 -1
  67. package/payload/server/public/assets/{AdminShell-lBaQG3aT.js → AdminShell-CdOGObaZ.js} +1 -1
  68. package/payload/server/public/assets/{Checkbox-P8PNtn8n.js → Checkbox-RFD_F0c0.js} +1 -1
  69. package/payload/server/public/assets/{admin-CB6VgLUJ.js → admin-CYGdL-1O.js} +1 -1
  70. package/payload/server/public/assets/{browser-CKH8YoIg.js → browser-DVfa_plj.js} +1 -1
  71. package/payload/server/public/assets/{calendar-xQ-OEVti.js → calendar-JC_Dxa4n.js} +1 -1
  72. package/payload/server/public/assets/chat-Yf8Nkop8.js +1 -0
  73. package/payload/server/public/assets/chevron-left-CAiAUBxO.js +1 -0
  74. package/payload/server/public/assets/data-hqdYXaEn.js +1 -0
  75. package/payload/server/public/assets/graph-hBUrRSkr.js +51 -0
  76. package/payload/server/public/assets/{graph-labels-DbL7Cxod.js → graph-labels-CHh6zs8t.js} +1 -1
  77. package/payload/server/public/assets/{maximize-2-Up8UP3Iu.js → maximize-2-DjxE4rm9.js} +1 -1
  78. package/payload/server/public/assets/{operator-BE366HH-.js → operator-BqU9SQuu.js} +1 -1
  79. package/payload/server/public/assets/{page-Wvb1S03b.js → page-B4c4_4HP.js} +1 -1
  80. package/payload/server/public/assets/{page-CQjjQz0V.js → page-DiQhU6lD.js} +1 -1
  81. package/payload/server/public/assets/{public-ZkXB-CHy.js → public-Cu_eALgY.js} +1 -1
  82. package/payload/server/public/assets/{rotate-ccw-TTz_MVO0.js → rotate-ccw-F9DsSTSJ.js} +1 -1
  83. package/payload/server/public/assets/{tasks-DNCoSRkP.js → tasks-CE-D0f50.js} +1 -1
  84. package/payload/server/public/assets/{time-entry-format-BooKdDRT.js → time-entry-format-D2rsWhov.js} +1 -1
  85. package/payload/server/public/assets/{useCopyFeedback-C23_n92I.js → useCopyFeedback-QxOmoobV.js} +1 -1
  86. package/payload/server/public/assets/{useSelectionMode-c4oZYAW0.js → useSelectionMode-RIzNqFjV.js} +1 -1
  87. package/payload/server/public/assets/{useSubAccountSwitcher-CHJo5leK.css → useSubAccountSwitcher-BW_fK1Nt.css} +1 -1
  88. package/payload/server/public/assets/{useVoiceRecorder-BkxW-GL1.js → useVoiceRecorder-WrWurSYI.js} +1 -1
  89. package/payload/server/public/browser.html +5 -5
  90. package/payload/server/public/calendar.html +6 -6
  91. package/payload/server/public/chat.html +12 -12
  92. package/payload/server/public/data.html +10 -10
  93. package/payload/server/public/graph.html +11 -11
  94. package/payload/server/public/index.html +13 -13
  95. package/payload/server/public/operator.html +14 -14
  96. package/payload/server/public/public.html +12 -12
  97. package/payload/server/public/tasks.html +5 -5
  98. package/payload/server/server.js +2 -2
  99. package/payload/server/{src-554BYJMN.js → src-S7C4P6TT.js} +1 -1
  100. package/payload/server/public/assets/chat-DH8K5TeZ.js +0 -1
  101. package/payload/server/public/assets/chevron-left-Ceky-ebO.js +0 -1
  102. package/payload/server/public/assets/data-Cvv8Kg9J.js +0 -1
  103. package/payload/server/public/assets/graph-CrEmAyVE.js +0 -51
  104. /package/payload/server/public/assets/{useSubAccountSwitcher-6UoTftbJ.js → useSubAccountSwitcher-wo_qGW-I.js} +0 -0
@@ -0,0 +1,108 @@
1
+ // Task 1310 — the email-source :KnowledgeDocument threads reached by
2
+ // contact-erase are matched by (email, accountId), not email alone. Person
3
+ // email uniqueness became composite (accountId, email) in Task 1304, so two
4
+ // accounts can legitimately hold a :Person with the same email. These tests
5
+ // lock the account-scoped Person match key in both the count query and the
6
+ // irreversible DETACH DELETE query, so a future non-account-scoped writer of
7
+ // FROM/TO/CC edges cannot reintroduce a cross-account leak here.
8
+ import { describe, it, expect, beforeEach, vi } from "vitest";
9
+ const mockTx = {
10
+ run: vi.fn().mockResolvedValue({ records: [] }),
11
+ commit: vi.fn().mockResolvedValue(undefined),
12
+ rollback: vi.fn().mockResolvedValue(undefined),
13
+ };
14
+ const mockSession = {
15
+ run: vi.fn(),
16
+ beginTransaction: vi.fn(() => mockTx),
17
+ close: vi.fn().mockResolvedValue(undefined),
18
+ };
19
+ vi.mock("../../lib/neo4j.js", () => ({
20
+ getSession: () => mockSession,
21
+ }));
22
+ vi.mock("../../lib/resolve-person.js", () => ({
23
+ // Literal accountId — this factory evaluates during import hoisting, before
24
+ // the ACCOUNT_ID const below initializes (TDZ). The person-resolve run is
25
+ // overridden per-test anyway, so this return shape is not asserted on.
26
+ resolvePersonMatch: () => ({
27
+ matchClause: "MATCH (p:Person {email: $email, accountId: $accountId})",
28
+ queryParams: {
29
+ email: "jane@example.com",
30
+ accountId: "20028fb5-db26-4de7-bff6-c5ec2d4fc766",
31
+ },
32
+ }),
33
+ }));
34
+ import { contactErase } from "../contact-erase.js";
35
+ const ACCOUNT_ID = "20028fb5-db26-4de7-bff6-c5ec2d4fc766";
36
+ const PERSON_EMAIL = "jane@example.com";
37
+ // A count record whose numeric getters return 0 regardless of accessor shape.
38
+ function zeroCounts(keys) {
39
+ return {
40
+ records: [
41
+ {
42
+ get: (k) => (keys.includes(k) ? { low: 0, valueOf: () => 0 } : 0),
43
+ },
44
+ ],
45
+ };
46
+ }
47
+ function personResolveRecord() {
48
+ return {
49
+ records: [
50
+ {
51
+ get: (k) => {
52
+ switch (k) {
53
+ case "personNodeId":
54
+ return "4:person:9";
55
+ case "personEmail":
56
+ return PERSON_EMAIL;
57
+ case "personPhone":
58
+ return null;
59
+ case "sessionId":
60
+ return null; // skip the conversation-count branch
61
+ default:
62
+ return null;
63
+ }
64
+ },
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ beforeEach(() => {
70
+ mockSession.run.mockReset();
71
+ mockSession.beginTransaction.mockClear();
72
+ mockTx.run.mockClear();
73
+ mockTx.commit.mockClear();
74
+ mockTx.rollback.mockClear();
75
+ });
76
+ describe("contact-erase — email-thread match is account-scoped (Task 1310)", () => {
77
+ // With sessionId null the run sequence is:
78
+ // [0] person resolve, [1] visitor counts, [2] email counts, [3] grant counts
79
+ function primePreviewRuns() {
80
+ mockSession.run
81
+ .mockResolvedValueOnce(personResolveRecord())
82
+ .mockResolvedValueOnce(zeroCounts(["visitorSessions", "visitorEvents", "anonVisitors"]))
83
+ .mockResolvedValueOnce(zeroCounts(["emailCount"]))
84
+ .mockResolvedValueOnce(zeroCounts(["grantCount"]));
85
+ }
86
+ it("scopes the email-count Person match by accountId", async () => {
87
+ primePreviewRuns();
88
+ await contactErase({ email: PERSON_EMAIL, accountId: ACCOUNT_ID, confirm: false });
89
+ const [emailCypher, emailParams] = mockSession.run.mock.calls[2];
90
+ expect(emailCypher).toContain("MATCH (p:Person {email: $personEmail, accountId: $accountId})");
91
+ expect(emailParams.accountId).toBe(ACCOUNT_ID);
92
+ });
93
+ it("scopes the DETACH DELETE Person match by accountId", async () => {
94
+ // Confirmed erase: preview runs on session, then the cascade runs on the tx.
95
+ mockSession.run
96
+ .mockResolvedValueOnce(personResolveRecord())
97
+ .mockResolvedValueOnce(zeroCounts(["visitorSessions", "visitorEvents", "anonVisitors"]))
98
+ .mockResolvedValueOnce(zeroCounts(["emailCount"]))
99
+ .mockResolvedValueOnce(zeroCounts(["grantCount"]));
100
+ await contactErase({ email: PERSON_EMAIL, accountId: ACCOUNT_ID, confirm: true });
101
+ const deleteCall = mockTx.run.mock.calls.find(([cypher]) => cypher.includes("DETACH DELETE kd"));
102
+ expect(deleteCall, "email-thread DETACH DELETE query should have run").toBeTruthy();
103
+ const [deleteCypher, deleteParams] = deleteCall;
104
+ expect(deleteCypher).toContain("MATCH (p:Person {email: $personEmail, accountId: $accountId})");
105
+ expect(deleteParams.accountId).toBe(ACCOUNT_ID);
106
+ });
107
+ });
108
+ //# sourceMappingURL=contact-erase.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact-erase.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/contact-erase.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contact-export.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact-export.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/contact-export.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ // Task 1310 — the email-source :KnowledgeDocument threads gathered by
2
+ // contact-export are matched by (email, accountId), not email alone. Person
3
+ // email uniqueness became composite (accountId, email) in Task 1304, so two
4
+ // accounts can legitimately hold a :Person with the same email. This test
5
+ // locks the account-scoped Person match key so a future non-account-scoped
6
+ // writer of FROM/TO/CC edges cannot leak another account's email threads into
7
+ // this account's export.
8
+ import { describe, it, expect, beforeEach, vi } from "vitest";
9
+ const mockSession = {
10
+ run: vi.fn(),
11
+ close: vi.fn().mockResolvedValue(undefined),
12
+ };
13
+ vi.mock("../../lib/neo4j.js", () => ({
14
+ getSession: () => mockSession,
15
+ }));
16
+ vi.mock("../../lib/resolve-person.js", () => ({
17
+ resolvePersonMatch: () => ({
18
+ matchClause: "MATCH (p:Person {email: $email, accountId: $accountId})",
19
+ queryParams: {
20
+ email: "jane@example.com",
21
+ accountId: "20028fb5-db26-4de7-bff6-c5ec2d4fc766",
22
+ },
23
+ }),
24
+ }));
25
+ import { contactExport } from "../contact-export.js";
26
+ const ACCOUNT_ID = "20028fb5-db26-4de7-bff6-c5ec2d4fc766";
27
+ const PERSON_EMAIL = "jane@example.com";
28
+ function personResolveRecord() {
29
+ return {
30
+ records: [
31
+ {
32
+ get: (k) => k === "nodeId"
33
+ ? "4:person:9"
34
+ : { properties: { email: PERSON_EMAIL } }, // no sessionId → skip conversation step
35
+ },
36
+ ],
37
+ };
38
+ }
39
+ beforeEach(() => {
40
+ mockSession.run.mockReset();
41
+ mockSession.close.mockClear();
42
+ });
43
+ describe("contact-export — email-thread match is account-scoped (Task 1310)", () => {
44
+ it("scopes the email-gather Person match by accountId", async () => {
45
+ // Run sequence with no sessionId:
46
+ // [0] person resolve, [1] access grants, [2] email gather
47
+ mockSession.run
48
+ .mockResolvedValueOnce(personResolveRecord())
49
+ .mockResolvedValueOnce({ records: [] })
50
+ .mockResolvedValueOnce({ records: [] });
51
+ await contactExport({ email: PERSON_EMAIL, accountId: ACCOUNT_ID });
52
+ const [emailCypher, emailParams] = mockSession.run.mock.calls[2];
53
+ expect(emailCypher).toContain("MATCH (p:Person {email: $personEmail, accountId: $accountId})");
54
+ expect(emailParams.accountId).toBe(ACCOUNT_ID);
55
+ });
56
+ });
57
+ //# sourceMappingURL=contact-export.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact-export.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/contact-export.test.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,4EAA4E;AAC5E,4EAA4E;AAC5E,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,yBAAyB;AAEzB,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,6BAA6B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;QACzB,WAAW,EAAE,yDAAyD;QACtE,WAAW,EAAE;YACX,KAAK,EAAE,kBAAkB;YACzB,SAAS,EAAE,sCAAsC;SAClD;KACF,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,UAAU,GAAG,sCAAsC,CAAC;AAC1D,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC,SAAS,mBAAmB;IAC1B,OAAO;QACL,OAAO,EAAE;YACP;gBACE,GAAG,EAAE,CAAC,CAAS,EAAE,EAAE,CACjB,CAAC,KAAK,QAAQ;oBACZ,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,wCAAwC;aACxF;SACF;KACF,CAAC;AACJ,CAAC;AAED,UAAU,CAAC,GAAG,EAAE;IACd,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;IAC5B,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;AAChC,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mEAAmE,EAAE,GAAG,EAAE;IACjF,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,kCAAkC;QAClC,4DAA4D;QAC5D,WAAW,CAAC,GAAG;aACZ,qBAAqB,CAAC,mBAAmB,EAAE,CAAC;aAC5C,qBAAqB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;aACtC,qBAAqB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAE1C,MAAM,aAAa,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;QAEpE,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,+DAA+D,CAAC,CAAC;QAC/F,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -86,7 +86,7 @@ export async function contactErase(params) {
86
86
  // :KnowledgeDocument {source:'email'} linked to :Person via address edges.
87
87
  if (personEmail) {
88
88
  const emailCountResult = await session.run(`MATCH (kd:KnowledgeDocument {source: 'email', accountId: $accountId})
89
- MATCH (p:Person {email: $personEmail})
89
+ MATCH (p:Person {email: $personEmail, accountId: $accountId})
90
90
  WHERE (kd)-[:FROM]->(p) OR (kd)-[:TO]->(p) OR (kd)-[:CC]->(p)
91
91
  RETURN count(DISTINCT kd) AS emailCount`, { personEmail, accountId });
92
92
  if (emailCountResult.records.length > 0) {
@@ -129,7 +129,7 @@ export async function contactErase(params) {
129
129
  // thread is one :KnowledgeDocument {source:'email'}.
130
130
  if (personEmail) {
131
131
  await tx.run(`MATCH (kd:KnowledgeDocument {source: 'email', accountId: $accountId})
132
- MATCH (p:Person {email: $personEmail})
132
+ MATCH (p:Person {email: $personEmail, accountId: $accountId})
133
133
  WHERE (kd)-[:FROM]->(p) OR (kd)-[:TO]->(p) OR (kd)-[:CC]->(p)
134
134
  DETACH DELETE kd`, { personEmail, accountId });
135
135
  }
@@ -74,7 +74,7 @@ export async function contactExport(params) {
74
74
  const personEmail = personProps.email;
75
75
  if (personEmail) {
76
76
  const emailResult = await session.run(`MATCH (kd:KnowledgeDocument {source: 'email', accountId: $accountId})
77
- MATCH (p:Person {email: $personEmail})
77
+ MATCH (p:Person {email: $personEmail, accountId: $accountId})
78
78
  WHERE (kd)-[:FROM]->(p) OR (kd)-[:TO]->(p) OR (kd)-[:CC]->(p)
79
79
  RETURN kd, elementId(kd) AS eNodeId
80
80
  ORDER BY kd.lastReceivedAt DESC`, { personEmail, accountId });
@@ -31,11 +31,14 @@ function mockGraph(status, bodyObj) {
31
31
  });
32
32
  return { restore: () => { globalThis.fetch = original; }, calls };
33
33
  }
34
- test("outlook-draft-edit: PATCHes only supplied fields", async () => {
35
- const mock = mockGraph(200, { id: "DRAFT9" });
34
+ test("outlook-draft-edit: PATCHes only supplied fields, and reports the id Graph returns", async () => {
35
+ // The mock returns a DIFFERENT id from the one sent. That is the whole point:
36
+ // the previous mock echoed the id it was handed, so the test passed whether or
37
+ // not the code read the response at all (Task 1688).
38
+ const mock = mockGraph(200, { id: "DRAFT9-RESAVED" });
36
39
  try {
37
40
  const r = await runDraftEdit(configWithToken("acct-de-1"), { draftId: "DRAFT9", subject: "Revised", body: "new text" });
38
- assert.deepEqual(r, { draftId: "DRAFT9", updated: true });
41
+ assert.deepEqual(r, { draftId: "DRAFT9-RESAVED", updated: true });
39
42
  assert.equal(mock.calls.length, 1);
40
43
  assert.equal(mock.calls[0].method, "PATCH");
41
44
  assert.match(mock.calls[0].url, /\/me\/messages\/DRAFT9$/);
@@ -47,6 +50,16 @@ test("outlook-draft-edit: PATCHes only supplied fields", async () => {
47
50
  mock.restore();
48
51
  }
49
52
  });
53
+ test("outlook-draft-edit: refuses to claim success when the response carries no id", async () => {
54
+ // Mirrors runDraft: never return a handle we did not read back.
55
+ const mock = mockGraph(200, {});
56
+ try {
57
+ await assert.rejects(runDraftEdit(configWithToken("acct-de-noid"), { draftId: "DRAFT9", subject: "Revised" }), /returned no id/);
58
+ }
59
+ finally {
60
+ mock.restore();
61
+ }
62
+ });
50
63
  test("outlook-draft-edit: isHtml sets HTML content type", async () => {
51
64
  const mock = mockGraph(200, { id: "DRAFT9" });
52
65
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"draft-edit.test.js","sourceRoot":"","sources":["../../src/__tests__/draft-edit.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"draft-edit.test.js","sourceRoot":"","sources":["../../src/__tests__/draft-edit.test.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,36 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { classifyGraphError } from "../lib/graph-client.js";
3
+ import { mkdtempSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { classifyGraphError, createGraphClient, callGraph, patchGraph, IMMUTABLE_ID_PREFER } from "../lib/graph-client.js";
7
+ import { TokenStore } from "../auth/token-store.js";
8
+ /** A config whose TokenStore holds a live (unexpired) access token. */
9
+ function configWithToken(accountId) {
10
+ const dir = mkdtempSync(join(tmpdir(), "outlook-graphclient-"));
11
+ const tokenStore = new TokenStore(accountId, dir);
12
+ tokenStore.store("access-token", "refresh-token", 3600, { scopes: ["Mail.ReadWrite"] });
13
+ return { clientId: "test-client", tenantId: "common", accountId, tokenStore };
14
+ }
15
+ /** Capture the headers of every outbound Graph request. */
16
+ function captureHeaders() {
17
+ const seen = [];
18
+ const original = globalThis.fetch;
19
+ globalThis.fetch = (async (input, init) => {
20
+ const req = input instanceof Request ? input : null;
21
+ const headers = req ? req.headers : new Headers(init?.headers ?? {});
22
+ const flat = {};
23
+ headers.forEach((v, k) => {
24
+ flat[k.toLowerCase()] = v;
25
+ });
26
+ seen.push(flat);
27
+ return new Response(JSON.stringify({ id: "X" }), {
28
+ status: 200,
29
+ headers: { "content-type": "application/json" },
30
+ });
31
+ });
32
+ return { restore: () => { globalThis.fetch = original; }, seen };
33
+ }
4
34
  test("classifyGraphError 401 → auth", () => {
5
35
  const cls = classifyGraphError({
6
36
  statusCode: 401,
@@ -104,4 +134,81 @@ test("classifyGraphError reads Retry-After via fetch-style response.headers.get"
104
134
  assert.equal(cls.retryAfterMs, 10000);
105
135
  }
106
136
  });
137
+ test("every Graph request carries the immutable-id Prefer header", async () => {
138
+ const cap = captureHeaders();
139
+ try {
140
+ const client = createGraphClient(configWithToken("acct-prefer-1"));
141
+ await client.api("/me/messages/X").get();
142
+ }
143
+ finally {
144
+ cap.restore();
145
+ }
146
+ assert.equal(cap.seen.length, 1);
147
+ assert.equal(cap.seen[0]["prefer"], IMMUTABLE_ID_PREFER);
148
+ });
149
+ test("a per-request .header() does not displace the Prefer header", async () => {
150
+ // mail-search sets ConsistencyLevel per request. If that dropped the central
151
+ // Prefer, search alone would keep returning REGULAR ids while every other
152
+ // tool used immutable ones — a silent mixed-namespace break (Task 1688).
153
+ const cap = captureHeaders();
154
+ try {
155
+ const client = createGraphClient(configWithToken("acct-prefer-2"));
156
+ await client.api("/me/messages/X").header("ConsistencyLevel", "eventual").get();
157
+ }
158
+ finally {
159
+ cap.restore();
160
+ }
161
+ assert.equal(cap.seen.length, 1);
162
+ assert.equal(cap.seen[0]["prefer"], IMMUTABLE_ID_PREFER);
163
+ assert.equal(cap.seen[0]["consistencylevel"], "eventual");
164
+ });
165
+ test("graph-error names the target of an id-addressed failure", async () => {
166
+ // Without `target` the log records that SOMETHING 404'd but not what, which
167
+ // is why Task 1688 needed the session JSONL to identify the dead draft.
168
+ const lines = [];
169
+ const origWrite = process.stderr.write.bind(process.stderr);
170
+ process.stderr.write = (s) => {
171
+ lines.push(s);
172
+ return true;
173
+ };
174
+ const origFetch = globalThis.fetch;
175
+ globalThis.fetch = (async () => new Response(JSON.stringify({ error: { code: "ErrorItemNotFound" } }), {
176
+ status: 404,
177
+ headers: { "content-type": "application/json" },
178
+ }));
179
+ try {
180
+ await assert.rejects(() => callGraph(configWithToken("acct-target-1"), async (c) => c.api("/me/messages/DEADBEEF").get(), { tool: "mail-fetch-body", target: "/me/messages/DEADBEEF" }));
181
+ }
182
+ finally {
183
+ globalThis.fetch = origFetch;
184
+ process.stderr.write = origWrite;
185
+ }
186
+ const errLine = lines.find((l) => l.includes("graph-error"));
187
+ assert.ok(errLine, "expected a graph-error line");
188
+ assert.match(errLine, /status=404/);
189
+ assert.match(errLine, /DEADBEEF/);
190
+ });
191
+ test("a write's graph-error names its path without the caller passing target", async () => {
192
+ const lines = [];
193
+ const origWrite = process.stderr.write.bind(process.stderr);
194
+ process.stderr.write = (s) => {
195
+ lines.push(s);
196
+ return true;
197
+ };
198
+ const origFetch = globalThis.fetch;
199
+ globalThis.fetch = (async () => new Response(JSON.stringify({ error: { code: "ErrorItemNotFound" } }), {
200
+ status: 404,
201
+ headers: { "content-type": "application/json" },
202
+ }));
203
+ try {
204
+ await assert.rejects(() => patchGraph(configWithToken("acct-target-2"), "/me/messages/CAFEBABE", { subject: "x" }, { tool: "draft-edit" }));
205
+ }
206
+ finally {
207
+ globalThis.fetch = origFetch;
208
+ process.stderr.write = origWrite;
209
+ }
210
+ const errLine = lines.find((l) => l.includes("graph-error"));
211
+ assert.ok(errLine, "expected a graph-error line");
212
+ assert.match(errLine, /CAFEBABE/);
213
+ });
107
214
  //# sourceMappingURL=graph-client.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-client.test.js","sourceRoot":"","sources":["../../src/__tests__/graph-client.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph-client.test.js","sourceRoot":"","sources":["../../src/__tests__/graph-client.test.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,20 @@ export interface GraphClientConfig {
17
17
  accountId: string;
18
18
  tokenStore: TokenStore;
19
19
  }
20
+ /**
21
+ * Options shared by every Graph call helper.
22
+ *
23
+ * `tool` names the caller, and drives the scope-insufficient and rate-limit
24
+ * messages. `target` is the specific resource path an id-addressed call acts on:
25
+ * without it a `graph-error` records that *something* 404'd but not *what*,
26
+ * which is why identifying the dead draft in Task 1688 required the session
27
+ * JSONL rather than the log. Collection queries address no single item and
28
+ * correctly leave it unset.
29
+ */
30
+ export interface GraphCallOpts {
31
+ tool: string;
32
+ target?: string;
33
+ }
20
34
  export type GraphErrorClass = {
21
35
  kind: "auth";
22
36
  statusCode: number;
@@ -46,6 +60,29 @@ export type GraphErrorClass = {
46
60
  code: string;
47
61
  };
48
62
  export declare function classifyGraphError(err: unknown): GraphErrorClass;
63
+ /**
64
+ * Opt into Graph immutable item ids.
65
+ *
66
+ * Outlook item ids are NOT stable: Graph rotates an item's id when the item is
67
+ * moved or re-saved — including by the operator's own mail client. That is what
68
+ * killed a live draft handle in Task 1688: our PATCH returned 200, and four
69
+ * minutes later the same id returned 404 ErrorItemNotFound, so the agent
70
+ * concluded the draft had vanished and rebuilt it. An immutable id survives that
71
+ * event; a regular id does not.
72
+ *
73
+ * This rides on the one client factory because it is plugin-wide by necessity,
74
+ * not preference: the immutable and default namespaces carry DIFFERENT values
75
+ * for the same item, so a per-tool opt-in would have one tool emitting ids that
76
+ * another cannot address. Never set this per-tool.
77
+ *
78
+ * TRAP for whoever adds the next header: `Prefer` is also how Graph carries
79
+ * `outlook.timezone` and `odata.maxpagesize`. A per-request
80
+ * `.header("Prefer", ...)` REPLACES this one for that call, silently dropping
81
+ * that tool back to regular ids — the mixed-namespace break, invisible until an
82
+ * id passed between tools 404s. `Prefer` is a comma-separated multi-value
83
+ * header: append to this value, never set `Prefer` on its own.
84
+ */
85
+ export declare const IMMUTABLE_ID_PREFER = "IdType=\"ImmutableId\"";
49
86
  /**
50
87
  * Factory wrapping `Client.init`. Exported as a named function so tests can
51
88
  * `vi.spyOn(graphClient, "createGraphClient")` reliably (avoids the
@@ -66,9 +103,7 @@ export declare function getOrRefreshAccessToken(config: GraphClientConfig, force
66
103
  * retry once), and respect for 429 Retry-After. All terminal classes throw
67
104
  * with operator-readable messages.
68
105
  */
69
- export declare function callGraph<T>(config: GraphClientConfig, invoke: (client: Client) => Promise<T>, opts?: {
70
- tool: string;
71
- }): Promise<T>;
106
+ export declare function callGraph<T>(config: GraphClientConfig, invoke: (client: Client) => Promise<T>, opts?: GraphCallOpts): Promise<T>;
72
107
  /** A single page of a Graph collection: the items plus the opaque continuation. */
73
108
  export interface GraphPage<T> {
74
109
  value: T[];
@@ -85,27 +120,19 @@ export interface GraphPage<T> {
85
120
  */
86
121
  export declare function callGraphPaged<T>(config: GraphClientConfig, buildRequest: (client: Client) => {
87
122
  get(): Promise<unknown>;
88
- }, opts?: {
89
- tool: string;
90
- }): Promise<GraphPage<T>>;
123
+ }, opts?: GraphCallOpts): Promise<GraphPage<T>>;
91
124
  /** POST to Graph. See `writeGraph`. */
92
- export declare function postGraph(config: GraphClientConfig, path: string, body: object | undefined, opts?: {
93
- tool: string;
94
- }): Promise<{
125
+ export declare function postGraph(config: GraphClientConfig, path: string, body: object | undefined, opts?: GraphCallOpts): Promise<{
95
126
  status: number;
96
127
  json: unknown | null;
97
128
  }>;
98
129
  /** PATCH to Graph. See `writeGraph`. */
99
- export declare function patchGraph(config: GraphClientConfig, path: string, body: object | undefined, opts?: {
100
- tool: string;
101
- }): Promise<{
130
+ export declare function patchGraph(config: GraphClientConfig, path: string, body: object | undefined, opts?: GraphCallOpts): Promise<{
102
131
  status: number;
103
132
  json: unknown | null;
104
133
  }>;
105
134
  /** DELETE to Graph. See `writeGraph`. */
106
- export declare function deleteGraph(config: GraphClientConfig, path: string, opts?: {
107
- tool: string;
108
- }): Promise<{
135
+ export declare function deleteGraph(config: GraphClientConfig, path: string, opts?: GraphCallOpts): Promise<{
109
136
  status: number;
110
137
  json: unknown | null;
111
138
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"graph-client.d.ts","sourceRoot":"","sources":["../../src/lib/graph-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph-client.d.ts","sourceRoot":"","sources":["../../src/lib/graph-client.ts"],"names":[],"mappings":"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"}
@@ -78,6 +78,29 @@ export function classifyGraphError(err) {
78
78
  }
79
79
  return { kind: "other", statusCode, code: code || "Unknown" };
80
80
  }
81
+ /**
82
+ * Opt into Graph immutable item ids.
83
+ *
84
+ * Outlook item ids are NOT stable: Graph rotates an item's id when the item is
85
+ * moved or re-saved — including by the operator's own mail client. That is what
86
+ * killed a live draft handle in Task 1688: our PATCH returned 200, and four
87
+ * minutes later the same id returned 404 ErrorItemNotFound, so the agent
88
+ * concluded the draft had vanished and rebuilt it. An immutable id survives that
89
+ * event; a regular id does not.
90
+ *
91
+ * This rides on the one client factory because it is plugin-wide by necessity,
92
+ * not preference: the immutable and default namespaces carry DIFFERENT values
93
+ * for the same item, so a per-tool opt-in would have one tool emitting ids that
94
+ * another cannot address. Never set this per-tool.
95
+ *
96
+ * TRAP for whoever adds the next header: `Prefer` is also how Graph carries
97
+ * `outlook.timezone` and `odata.maxpagesize`. A per-request
98
+ * `.header("Prefer", ...)` REPLACES this one for that call, silently dropping
99
+ * that tool back to regular ids — the mixed-namespace break, invisible until an
100
+ * id passed between tools 404s. `Prefer` is a comma-separated multi-value
101
+ * header: append to this value, never set `Prefer` on its own.
102
+ */
103
+ export const IMMUTABLE_ID_PREFER = 'IdType="ImmutableId"';
81
104
  /**
82
105
  * Factory wrapping `Client.init`. Exported as a named function so tests can
83
106
  * `vi.spyOn(graphClient, "createGraphClient")` reliably (avoids the
@@ -85,6 +108,10 @@ export function classifyGraphError(err) {
85
108
  */
86
109
  export function createGraphClient(config) {
87
110
  return Client.init({
111
+ // Applies to every request, and is merged with — not displaced by — a
112
+ // per-request `.header()` (mail-search's ConsistencyLevel). Both properties
113
+ // are asserted in graph-client.test.ts and were verified against live Graph.
114
+ fetchOptions: { headers: { Prefer: IMMUTABLE_ID_PREFER } },
88
115
  authProvider: async (done) => {
89
116
  try {
90
117
  const token = await getOrRefreshAccessToken(config);
@@ -163,6 +190,7 @@ export async function callGraph(config, invoke, opts = { tool: "graph" }) {
163
190
  account: config.accountId,
164
191
  status: cls.statusCode,
165
192
  code: "code" in cls ? cls.code : "",
193
+ target: opts.target ?? "",
166
194
  retryAfterMs: cls.kind === "rate-limit-recoverable" ? cls.retryAfterMs : null,
167
195
  });
168
196
  if (cls.kind === "on-prem") {
@@ -275,7 +303,10 @@ async function writeGraph(config, method, path, body, opts) {
275
303
  json = await raw.json().catch(() => null);
276
304
  }
277
305
  return { status: raw.status, json };
278
- }, opts);
306
+ },
307
+ // Every write already knows its path, so a failing write names its target
308
+ // without the caller threading one through.
309
+ { tool: opts.tool, target: opts.target ?? path });
279
310
  }
280
311
  /** POST to Graph. See `writeGraph`. */
281
312
  export async function postGraph(config, path, body, opts = { tool: "graph" }) {