@atomicmail/langchain 0.3.14

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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/esm/_dnt.polyfills.d.ts +101 -0
  4. package/esm/_dnt.polyfills.d.ts.map +1 -0
  5. package/esm/_dnt.polyfills.js +127 -0
  6. package/esm/langchain/mod.d.ts +23 -0
  7. package/esm/langchain/mod.d.ts.map +1 -0
  8. package/esm/langchain/mod.js +157 -0
  9. package/esm/lib/agent/auth/agent-auth-http.d.ts +26 -0
  10. package/esm/lib/agent/auth/agent-auth-http.d.ts.map +1 -0
  11. package/esm/lib/agent/auth/agent-auth-http.js +85 -0
  12. package/esm/lib/agent/auth/agent-jwt.d.ts +12 -0
  13. package/esm/lib/agent/auth/agent-jwt.d.ts.map +1 -0
  14. package/esm/lib/agent/auth/agent-jwt.js +27 -0
  15. package/esm/lib/agent/auth/agent-pow.d.ts +5 -0
  16. package/esm/lib/agent/auth/agent-pow.d.ts.map +1 -0
  17. package/esm/lib/agent/auth/agent-pow.js +49 -0
  18. package/esm/lib/agent/jmap/agent-help-content.d.ts +2 -0
  19. package/esm/lib/agent/jmap/agent-help-content.d.ts.map +1 -0
  20. package/esm/lib/agent/jmap/agent-help-content.js +2 -0
  21. package/esm/lib/agent/jmap/agent-jmap-blob-limits.d.ts +27 -0
  22. package/esm/lib/agent/jmap/agent-jmap-blob-limits.d.ts.map +1 -0
  23. package/esm/lib/agent/jmap/agent-jmap-blob-limits.js +166 -0
  24. package/esm/lib/agent/jmap/agent-jmap-blob-upload.d.ts +24 -0
  25. package/esm/lib/agent/jmap/agent-jmap-blob-upload.d.ts.map +1 -0
  26. package/esm/lib/agent/jmap/agent-jmap-blob-upload.js +104 -0
  27. package/esm/lib/agent/jmap/agent-jmap-email-charset.d.ts +8 -0
  28. package/esm/lib/agent/jmap/agent-jmap-email-charset.d.ts.map +1 -0
  29. package/esm/lib/agent/jmap/agent-jmap-email-charset.js +61 -0
  30. package/esm/lib/agent/jmap/agent-jmap-verify.d.ts +9 -0
  31. package/esm/lib/agent/jmap/agent-jmap-verify.d.ts.map +1 -0
  32. package/esm/lib/agent/jmap/agent-jmap-verify.js +50 -0
  33. package/esm/lib/agent/jmap/agent-jmap.d.ts +89 -0
  34. package/esm/lib/agent/jmap/agent-jmap.d.ts.map +1 -0
  35. package/esm/lib/agent/jmap/agent-jmap.js +373 -0
  36. package/esm/lib/agent/jmap/agent-vars.d.ts +30 -0
  37. package/esm/lib/agent/jmap/agent-vars.d.ts.map +1 -0
  38. package/esm/lib/agent/jmap/agent-vars.js +96 -0
  39. package/esm/lib/agent/jmap/help-content/auth.d.ts +2 -0
  40. package/esm/lib/agent/jmap/help-content/auth.d.ts.map +1 -0
  41. package/esm/lib/agent/jmap/help-content/auth.js +33 -0
  42. package/esm/lib/agent/jmap/help-content/cron.d.ts +6 -0
  43. package/esm/lib/agent/jmap/help-content/cron.d.ts.map +1 -0
  44. package/esm/lib/agent/jmap/help-content/cron.js +159 -0
  45. package/esm/lib/agent/jmap/help-content/index.d.ts +6 -0
  46. package/esm/lib/agent/jmap/help-content/index.d.ts.map +1 -0
  47. package/esm/lib/agent/jmap/help-content/index.js +61 -0
  48. package/esm/lib/agent/jmap/help-content/installation.d.ts +2 -0
  49. package/esm/lib/agent/jmap/help-content/installation.d.ts.map +1 -0
  50. package/esm/lib/agent/jmap/help-content/installation.js +47 -0
  51. package/esm/lib/agent/jmap/help-content/jmap-cheatsheet.d.ts +2 -0
  52. package/esm/lib/agent/jmap/help-content/jmap-cheatsheet.d.ts.map +1 -0
  53. package/esm/lib/agent/jmap/help-content/jmap-cheatsheet.js +230 -0
  54. package/esm/lib/agent/jmap/help-content/multi-account.d.ts +2 -0
  55. package/esm/lib/agent/jmap/help-content/multi-account.d.ts.map +1 -0
  56. package/esm/lib/agent/jmap/help-content/multi-account.js +49 -0
  57. package/esm/lib/agent/jmap/help-content/overview.d.ts +2 -0
  58. package/esm/lib/agent/jmap/help-content/overview.d.ts.map +1 -0
  59. package/esm/lib/agent/jmap/help-content/overview.js +49 -0
  60. package/esm/lib/agent/jmap/help-content/presets.d.ts +2 -0
  61. package/esm/lib/agent/jmap/help-content/presets.d.ts.map +1 -0
  62. package/esm/lib/agent/jmap/help-content/presets.js +51 -0
  63. package/esm/lib/agent/jmap/help-content/tools.d.ts +2 -0
  64. package/esm/lib/agent/jmap/help-content/tools.d.ts.map +1 -0
  65. package/esm/lib/agent/jmap/help-content/tools.js +49 -0
  66. package/esm/lib/agent/jmap/help-content/troubleshooting.d.ts +2 -0
  67. package/esm/lib/agent/jmap/help-content/troubleshooting.d.ts.map +1 -0
  68. package/esm/lib/agent/jmap/help-content/troubleshooting.js +65 -0
  69. package/esm/lib/agent/session/agent-credentials-store.d.ts +45 -0
  70. package/esm/lib/agent/session/agent-credentials-store.d.ts.map +1 -0
  71. package/esm/lib/agent/session/agent-credentials-store.js +121 -0
  72. package/esm/lib/agent/session/agent-resolve-config.d.ts +29 -0
  73. package/esm/lib/agent/session/agent-resolve-config.d.ts.map +1 -0
  74. package/esm/lib/agent/session/agent-resolve-config.js +71 -0
  75. package/esm/lib/agent/session/agent-session-for-dir.d.ts +8 -0
  76. package/esm/lib/agent/session/agent-session-for-dir.d.ts.map +1 -0
  77. package/esm/lib/agent/session/agent-session-for-dir.js +33 -0
  78. package/esm/lib/agent/session/agent-session.d.ts +89 -0
  79. package/esm/lib/agent/session/agent-session.d.ts.map +1 -0
  80. package/esm/lib/agent/session/agent-session.js +320 -0
  81. package/esm/lib/agent/session/inbox-id-to-mailbox-email.d.ts +6 -0
  82. package/esm/lib/agent/session/inbox-id-to-mailbox-email.d.ts.map +1 -0
  83. package/esm/lib/agent/session/inbox-id-to-mailbox-email.js +24 -0
  84. package/esm/lib/core/consts.d.ts +17 -0
  85. package/esm/lib/core/consts.d.ts.map +1 -0
  86. package/esm/lib/core/consts.js +28 -0
  87. package/esm/lib/core/messages.d.ts +6 -0
  88. package/esm/lib/core/messages.d.ts.map +1 -0
  89. package/esm/lib/core/messages.js +19 -0
  90. package/esm/lib/core/read-npm-package-readme.d.ts +6 -0
  91. package/esm/lib/core/read-npm-package-readme.d.ts.map +1 -0
  92. package/esm/lib/core/read-npm-package-readme.js +81 -0
  93. package/esm/lib/core/shared-assets.d.ts +6 -0
  94. package/esm/lib/core/shared-assets.d.ts.map +1 -0
  95. package/esm/lib/core/shared-assets.js +46 -0
  96. package/esm/lib/core/types.d.ts +2 -0
  97. package/esm/lib/core/types.d.ts.map +1 -0
  98. package/esm/lib/core/types.js +2 -0
  99. package/esm/lib/core/utils.d.ts +12 -0
  100. package/esm/lib/core/utils.d.ts.map +1 -0
  101. package/esm/lib/core/utils.js +29 -0
  102. package/esm/lib/mod.d.ts +20 -0
  103. package/esm/lib/mod.d.ts.map +1 -0
  104. package/esm/lib/mod.js +19 -0
  105. package/esm/lib/network/auth-client.d.ts +57 -0
  106. package/esm/lib/network/auth-client.d.ts.map +1 -0
  107. package/esm/lib/network/auth-client.js +210 -0
  108. package/esm/package.json +3 -0
  109. package/package.json +51 -0
  110. package/presets/list_inbox.json +46 -0
  111. package/presets/reply.json +97 -0
  112. package/presets/send_mail.json +70 -0
  113. package/presets/send_mail_attachment.json +92 -0
  114. package/presets/send_mail_blob_attachment.json +74 -0
  115. package/shared/consts.json +11 -0
  116. package/shared/fixtures/pow_vectors.json +32 -0
  117. package/shared/help/fragments/inbox_cron_agent_prompt.md +1 -0
  118. package/shared/help/fragments/post_register_cron_reminder.md +5 -0
  119. package/shared/help/readme_stub.md +3 -0
  120. package/shared/help/topics/auth.md +8 -0
  121. package/shared/help/topics/cron.md +217 -0
  122. package/shared/help/topics/installation.md +35 -0
  123. package/shared/help/topics/jmap_cheatsheet.md +19 -0
  124. package/shared/help/topics/multi_account.md +9 -0
  125. package/shared/help/topics/overview.md +27 -0
  126. package/shared/help/topics/presets.md +12 -0
  127. package/shared/help/topics/tools.md +16 -0
  128. package/shared/help/topics/troubleshooting.md +6 -0
  129. package/shared/manifest.json +31 -0
  130. package/shared/messages/errors.json +68 -0
  131. package/shared/messages/hints.json +8 -0
  132. package/shared/presets/list_inbox.json +46 -0
  133. package/shared/presets/reply.json +97 -0
  134. package/shared/presets/send_mail.json +70 -0
  135. package/shared/presets/send_mail_attachment.json +92 -0
  136. package/shared/presets/send_mail_blob_attachment.json +74 -0
  137. package/shared/skill/SKILL.template.md +202 -0
  138. package/shared/skill/manifest.json +89 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-jmap.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/jmap/agent-jmap.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAIrC,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE,eAAO,MAAM,kBAAkB,qEAGrB,CAAC;AASX,eAAO,MAAM,wBAAwB,6HAM3B,CAAC;AAEX,eAAO,MAAM,aAAa,EAAG,2BAAoC,CAAC;AAElE,wEAAwE;AACxE,eAAO,MAAM,aAAa,EAAG,2BAAoC,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EAAE,EACtB,MAAM,EAAE,MAAM,GACb,YAAY,CAyBd;AAED,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED,wBAAsB,WAAW,CAC/B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAoDD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAcR;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,iBAAiB,CAUnB;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAM1E;AAUD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,EAAE,MAAM,GAChB,oBAAoB,GAAG,IAAI,CA0B7B;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAclC;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,oEAAoE;IACpE,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,wGAAwG;IACxG,6BAA6B,CAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,eAAe,CAAC;IACzB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACpC,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgG5D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,MAAM,CAAC,CA8CjB;AAmCD,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,YAAY,GACrB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAW5D;AAQD,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU5D"}
@@ -0,0 +1,373 @@
1
+ // JMAP envelope parsing, preset paths, $VAR substitution, and HTTP helpers.
2
+ import { readFile } from "node:fs/promises";
3
+ import { dirname, isAbsolute, resolve as resolvePath } from "node:path";
4
+ import { cwd } from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ import { tryReadSharedJson } from "../../core/shared-assets.js";
7
+ import { readCredentials } from "../session/agent-credentials-store.js";
8
+ import { inboxIdToMailboxEmail } from "../session/inbox-id-to-mailbox-email.js";
9
+ import { assertBlobUploadEnvelopeWithinLimits, } from "./agent-jmap-blob-limits.js";
10
+ import { buildVarsFromAttachmentFiles, } from "./agent-jmap-blob-upload.js";
11
+ import { ensureTextCharsetOnEmailSetBlobParts } from "./agent-jmap-email-charset.js";
12
+ import { substituteVars } from "./agent-vars.js";
13
+ export const DEFAULT_JMAP_USING = [
14
+ "urn:ietf:params:jmap:core",
15
+ "urn:ietf:params:jmap:mail",
16
+ ];
17
+ /** Presets shipped with MCP / skill npm packages (for error hints). */
18
+ const sharedManifest = tryReadSharedJson("manifest.json");
19
+ const sharedHints = tryReadSharedJson("messages/hints.json");
20
+ export const BUNDLED_OPS_PRESET_NAMES = [
21
+ "list_inbox.json",
22
+ "reply.json",
23
+ "send_mail.json",
24
+ "send_mail_attachment.json",
25
+ "send_mail_blob_attachment.json",
26
+ ];
27
+ export const JMAP_MAIL_URN = "urn:ietf:params:jmap:mail";
28
+ /** RFC 9404 blob extension URN (Blob/upload, Blob/get, Blob/lookup). */
29
+ export const JMAP_BLOB_URN = "urn:ietf:params:jmap:blob";
30
+ export function parseJmapEnvelope(raw, defaultUsing, source) {
31
+ let value;
32
+ try {
33
+ value = JSON.parse(raw);
34
+ }
35
+ catch (err) {
36
+ throw new Error(`${source} is not valid JSON: ${err.message}`);
37
+ }
38
+ if (Array.isArray(value)) {
39
+ return { using: [...defaultUsing], methodCalls: value };
40
+ }
41
+ if (value !== null &&
42
+ typeof value === "object" &&
43
+ Array.isArray(value.methodCalls)) {
44
+ const obj = value;
45
+ const using = Array.isArray(obj.using)
46
+ ? obj.using.filter((u) => typeof u === "string")
47
+ : [...defaultUsing];
48
+ return { using, methodCalls: obj.methodCalls };
49
+ }
50
+ throw new Error(`${source} must be a methodCalls array, e.g. ` +
51
+ '[["Mailbox/get",{...},"m0"]], or an object with a methodCalls array.');
52
+ }
53
+ export function resolveOpsFilePath(credentialDir, opsFile) {
54
+ return isAbsolute(opsFile) ? opsFile : resolvePath(credentialDir, opsFile);
55
+ }
56
+ export async function readOpsFile(credentialDir, opsFile) {
57
+ const filePath = resolveOpsFilePath(credentialDir, opsFile);
58
+ try {
59
+ return await readFile(filePath, "utf-8");
60
+ }
61
+ catch (err) {
62
+ if (!(err instanceof Error) || !isFileNotFound(err) || isAbsolute(opsFile)) {
63
+ throw err;
64
+ }
65
+ }
66
+ const bundledPath = await resolveBundledPresetPath(opsFile);
67
+ if (!bundledPath) {
68
+ throw new Error(`ops_file '${opsFile}' not found under credential directory (${filePath}) ` +
69
+ "and not among bundled presets: " +
70
+ `${BUNDLED_OPS_PRESET_NAMES.join(", ")}.`);
71
+ }
72
+ return await readFile(bundledPath, "utf-8");
73
+ }
74
+ function isFileNotFound(err) {
75
+ const code = err.code;
76
+ return code === "ENOENT" || code === "ENOTDIR";
77
+ }
78
+ async function resolveBundledPresetPath(opsFile) {
79
+ const moduleDir = dirname(fileURLToPath(globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url));
80
+ let currentDir = moduleDir;
81
+ for (let depth = 0; depth < 8; depth++) {
82
+ const candidates = [
83
+ resolvePath(currentDir, "shared", sharedManifest?.presets_dir ?? "presets", opsFile),
84
+ resolvePath(currentDir, "presets", opsFile),
85
+ resolvePath(currentDir, "agent", "jmap", "presets", opsFile),
86
+ resolvePath(currentDir, "lib", "src", "agent", "jmap", "presets", opsFile),
87
+ ];
88
+ for (const candidate of candidates) {
89
+ try {
90
+ await readFile(candidate, "utf-8");
91
+ return candidate;
92
+ }
93
+ catch (err) {
94
+ if (!(err instanceof Error) || !isFileNotFound(err)) {
95
+ throw err;
96
+ }
97
+ }
98
+ }
99
+ const parent = resolvePath(currentDir, "..");
100
+ if (parent === currentDir)
101
+ break;
102
+ currentDir = parent;
103
+ }
104
+ return undefined;
105
+ }
106
+ export function extractPrimaryMailAccountId(session) {
107
+ const primary = session["primaryAccounts"];
108
+ if (!primary || typeof primary !== "object") {
109
+ throw new Error("JMAP session missing primaryAccounts.");
110
+ }
111
+ const id = primary[JMAP_MAIL_URN];
112
+ if (typeof id !== "string" || id.length === 0) {
113
+ throw new Error(`JMAP session missing primaryAccounts['${JMAP_MAIL_URN}'].`);
114
+ }
115
+ return id;
116
+ }
117
+ export function extractBlobEndpoints(session) {
118
+ const uploadUrl = session["uploadUrl"];
119
+ const downloadUrl = session["downloadUrl"];
120
+ if (typeof uploadUrl !== "string" || uploadUrl.length === 0) {
121
+ throw new Error("JMAP session missing uploadUrl.");
122
+ }
123
+ if (typeof downloadUrl !== "string" || downloadUrl.length === 0) {
124
+ throw new Error("JMAP session missing downloadUrl.");
125
+ }
126
+ return { uploadUrl, downloadUrl };
127
+ }
128
+ /** RFC 8620 §2 / §3.1: POST target for JMAP API calls from the Session object. */
129
+ export function extractJmapApiUrl(session) {
130
+ const u = session["apiUrl"];
131
+ if (typeof u !== "string" || u.length === 0) {
132
+ throw new Error("JMAP session missing apiUrl.");
133
+ }
134
+ return u;
135
+ }
136
+ function asNonNegativeInt(v) {
137
+ if (typeof v !== "number" || !Number.isFinite(v))
138
+ return undefined;
139
+ if (!Number.isInteger(v) || v < 0 || v > Number.MAX_SAFE_INTEGER) {
140
+ return undefined;
141
+ }
142
+ return v;
143
+ }
144
+ /**
145
+ * RFC 9404 §3.1 blob limits for one account from GET /.well-known/jmap JSON.
146
+ * Returns null when the account does not advertise `urn:ietf:params:jmap:blob`.
147
+ */
148
+ export function extractBlobUploadLimits(session, accountId) {
149
+ const accounts = session["accounts"];
150
+ if (!accounts || typeof accounts !== "object")
151
+ return null;
152
+ const acc = accounts[accountId];
153
+ if (!acc || typeof acc !== "object")
154
+ return null;
155
+ const caps = acc["accountCapabilities"];
156
+ if (!caps || typeof caps !== "object")
157
+ return null;
158
+ const blob = caps[JMAP_BLOB_URN];
159
+ if (!blob || typeof blob !== "object")
160
+ return null;
161
+ const b = blob;
162
+ let maxSizeBlobSet = null;
163
+ const rawMax = b["maxSizeBlobSet"];
164
+ if (rawMax === null) {
165
+ maxSizeBlobSet = null;
166
+ }
167
+ else {
168
+ const n = asNonNegativeInt(rawMax);
169
+ maxSizeBlobSet = n === undefined ? null : n;
170
+ }
171
+ const maxDs = asNonNegativeInt(b["maxDataSources"]);
172
+ const out = { maxSizeBlobSet };
173
+ if (maxDs !== undefined) {
174
+ out.maxDataSources = maxDs;
175
+ }
176
+ return out;
177
+ }
178
+ export async function fetchJmapWellKnown(apiUrl, capabilityJwt) {
179
+ const base = apiUrl.replace(/\/+$/, "");
180
+ const res = await fetch(`${base}/.well-known/jmap`, {
181
+ headers: { Authorization: `Bearer ${capabilityJwt}` },
182
+ });
183
+ const text = await res.text();
184
+ if (!res.ok) {
185
+ throw new Error(`JMAP session fetch failed (HTTP ${res.status}): ${text}`);
186
+ }
187
+ try {
188
+ return JSON.parse(text);
189
+ }
190
+ catch {
191
+ throw new Error("JMAP session response is not valid JSON.");
192
+ }
193
+ }
194
+ /**
195
+ * Parse ops JSON, substitute `$VAR_NAME` tokens (session + caller vars), POST to JMAP.
196
+ */
197
+ export async function runJmapRequest(input) {
198
+ if (input.dryRun && input.attachments && input.attachments.length > 0) {
199
+ throw new Error("dryRun cannot be used with attachments: RFC 8620 upload runs first and would create blobs.");
200
+ }
201
+ let mergedVars = input.vars ?? {};
202
+ if (input.attachments && input.attachments.length > 0) {
203
+ const pathBase = input.attachmentPathBase ?? cwd();
204
+ const injected = await buildVarsFromAttachmentFiles(input.session, input.attachments, pathBase);
205
+ mergedVars = { ...injected, ...mergedVars };
206
+ }
207
+ const { text: raw } = await substituteVars({
208
+ raw: input.opsJson,
209
+ vars: mergedVars,
210
+ autoResolvers: {
211
+ ACCOUNT_ID: () => input.session.getPrimaryMailAccountId(),
212
+ INBOX: async () => {
213
+ const raw = input.session.currentInboxId ??
214
+ (input.session.files
215
+ ? (await readCredentials(input.session.files.credentialsFile))
216
+ .inboxId
217
+ : undefined);
218
+ if (!raw) {
219
+ throw new Error("No inbox in session; run register first.");
220
+ }
221
+ return inboxIdToMailboxEmail(raw);
222
+ },
223
+ INBOX_MAILBOX_ID: () => fetchInboxMailboxId(input.session),
224
+ UPLOAD_URL: async () => {
225
+ if (input.session.currentUploadUrl) {
226
+ return input.session.currentUploadUrl;
227
+ }
228
+ if (input.session.files) {
229
+ return (await readCredentials(input.session.files.credentialsFile))
230
+ .uploadUrl;
231
+ }
232
+ throw new Error("JMAP session missing uploadUrl.");
233
+ },
234
+ DOWNLOAD_URL: async () => {
235
+ if (input.session.currentDownloadUrl) {
236
+ return input.session.currentDownloadUrl;
237
+ }
238
+ if (input.session.files) {
239
+ return (await readCredentials(input.session.files.credentialsFile))
240
+ .downloadUrl;
241
+ }
242
+ throw new Error("JMAP session missing downloadUrl.");
243
+ },
244
+ },
245
+ });
246
+ const envelope = parseJmapEnvelope(raw, input.defaultUsing, input.sourceLabel);
247
+ ensureTextCharsetOnEmailSetBlobParts(envelope);
248
+ await enforceJmapBlobUploadLimitsIfApplicable(input.session, envelope);
249
+ const jmapPostUrl = await input.session.getJmapPostUrl();
250
+ if (input.dryRun) {
251
+ return {
252
+ ok: true,
253
+ status: 200,
254
+ bodyText: JSON.stringify({
255
+ dryRun: true,
256
+ url: jmapPostUrl,
257
+ envelope,
258
+ }, null, 2),
259
+ };
260
+ }
261
+ const capabilityJwt = await input.session.getCapabilityToken();
262
+ const { ok, status, bodyText } = await postJmap(jmapPostUrl, capabilityJwt, envelope);
263
+ if (!ok) {
264
+ return { ok, status, bodyText };
265
+ }
266
+ return { ok, status, bodyText: attachJmapNextHints(bodyText) };
267
+ }
268
+ /**
269
+ * Resolves the JMAP `Mailbox` id for the account inbox (`role: "inbox"`).
270
+ * Used for `$INBOX_MAILBOX_ID` substitution (distinct from `$INBOX`, which is
271
+ * the mailbox *email address* — see `inboxIdToMailboxEmail` for normalization).
272
+ */
273
+ export async function fetchInboxMailboxId(port) {
274
+ const accountId = await port.getPrimaryMailAccountId();
275
+ const capabilityJwt = await port.getCapabilityToken();
276
+ const envelope = {
277
+ using: [
278
+ "urn:ietf:params:jmap:core",
279
+ "urn:ietf:params:jmap:mail",
280
+ ],
281
+ methodCalls: [
282
+ [
283
+ "Mailbox/query",
284
+ { accountId, filter: { role: "inbox" } },
285
+ "mq0",
286
+ ],
287
+ ],
288
+ };
289
+ const jmapPostUrl = await port.getJmapPostUrl();
290
+ const { ok, status, bodyText } = await postJmap(jmapPostUrl, capabilityJwt, envelope);
291
+ if (!ok) {
292
+ throw new Error(`Mailbox/query failed (HTTP ${status}): ${bodyText}`);
293
+ }
294
+ let parsed;
295
+ try {
296
+ parsed = JSON.parse(bodyText);
297
+ }
298
+ catch {
299
+ throw new Error("Mailbox/query response is not valid JSON.");
300
+ }
301
+ const responses = parsed
302
+ .methodResponses;
303
+ const first = responses?.[0];
304
+ if (!Array.isArray(first) || first[0] === "error") {
305
+ throw new Error(`Mailbox/query failed: ${bodyText}`);
306
+ }
307
+ if (first[0] !== "Mailbox/query") {
308
+ throw new Error(`Mailbox/query failed: ${bodyText}`);
309
+ }
310
+ const payload = first[1];
311
+ const id = payload.ids?.[0];
312
+ if (typeof id !== "string" || id.length === 0) {
313
+ throw new Error("Mailbox/query returned no inbox mailbox id.");
314
+ }
315
+ return id;
316
+ }
317
+ function collectBlobUploadAccountIds(envelope) {
318
+ const ids = new Set();
319
+ for (const call of envelope.methodCalls) {
320
+ if (!Array.isArray(call) || call[0] !== "Blob/upload")
321
+ continue;
322
+ const arg = call[1];
323
+ if (!arg || typeof arg !== "object")
324
+ continue;
325
+ const aid = arg["accountId"];
326
+ if (typeof aid === "string" && aid.length > 0)
327
+ ids.add(aid);
328
+ }
329
+ return [...ids];
330
+ }
331
+ async function enforceJmapBlobUploadLimitsIfApplicable(session, envelope) {
332
+ if (!envelope.using.includes(JMAP_BLOB_URN))
333
+ return;
334
+ const hasUpload = envelope.methodCalls.some((c) => Array.isArray(c) && c[0] === "Blob/upload");
335
+ if (!hasUpload)
336
+ return;
337
+ const accountIds = collectBlobUploadAccountIds(envelope);
338
+ const limitsByAccount = new Map();
339
+ for (const id of accountIds) {
340
+ limitsByAccount.set(id, await session.getBlobUploadLimitsForAccount(id));
341
+ }
342
+ assertBlobUploadEnvelopeWithinLimits(envelope, limitsByAccount);
343
+ }
344
+ export async function postJmap(jmapPostUrl, capabilityJwt, envelope) {
345
+ const res = await fetch(jmapPostUrl, {
346
+ method: "POST",
347
+ headers: {
348
+ "Content-Type": "application/json",
349
+ Authorization: `Bearer ${capabilityJwt}`,
350
+ },
351
+ body: JSON.stringify(envelope),
352
+ });
353
+ const bodyText = await res.text();
354
+ return { ok: res.ok, status: res.status, bodyText };
355
+ }
356
+ const JMAP_NEXT_HINTS = sharedHints?.jmap_next_hints ?? [
357
+ "Use jmap_request with Mailbox/get or Email/query to work with mail data.",
358
+ "Use presets with $VAR placeholders — $ACCOUNT_ID, $INBOX, and $INBOX_MAILBOX_ID come from the session; pass others via vars / --vars.",
359
+ "Call help for the JMAP cheatsheet and troubleshooting.",
360
+ ];
361
+ /** Attach _next hints to a successful JMAP JSON object when parseable. */
362
+ export function attachJmapNextHints(bodyText) {
363
+ try {
364
+ const obj = JSON.parse(bodyText);
365
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
366
+ return JSON.stringify({ ...obj, _next: [...JMAP_NEXT_HINTS] }, null, 2);
367
+ }
368
+ }
369
+ catch {
370
+ // not JSON — return raw
371
+ }
372
+ return bodyText;
373
+ }
@@ -0,0 +1,30 @@
1
+ /** Keys allowed in MCP `vars` / skill `--vars` (without leading `$`). */
2
+ export declare const USER_VAR_KEY_RE: RegExp;
3
+ /**
4
+ * Parses a JSON object of string values (skill `--vars` / MCP `vars`).
5
+ * Throws `Error` with the same messages the CLI used to emit via `fail(...)`.
6
+ */
7
+ export declare function parseUserVarsJson(jsonString: string): Record<string, string>;
8
+ /** Matches `$FOO_BAR`; excludes JMAP keywords like `$draft` (lowercase). */
9
+ export declare const VAR_PATTERN: RegExp;
10
+ /** Names substituted from JMAP session / credentials when not overridden in `vars`. */
11
+ export declare const SESSION_VAR_NAMES: Set<string>;
12
+ export interface SubstituteVarsInput {
13
+ raw: string;
14
+ /** Caller-supplied values; keys are names without `$` (e.g. `TO`, `SUBJECT`). */
15
+ vars?: Record<string, string>;
16
+ /** Invoked only when the name appears in `raw`, is absent from `vars`, and a resolver exists. */
17
+ autoResolvers?: Record<string, () => Promise<string> | string>;
18
+ }
19
+ export interface SubstituteVarsResult {
20
+ text: string;
21
+ }
22
+ /** Unique variable names in order of first occurrence (without leading `$`). */
23
+ export declare function findVarReferences(raw: string): string[];
24
+ /**
25
+ * Replaces every `$VAR_NAME` in `raw` with the corresponding string.
26
+ * Single pass — values are not scanned for further `$` tokens.
27
+ * Throws if any referenced variable has no value (after vars + autoResolvers).
28
+ */
29
+ export declare function substituteVars(input: SubstituteVarsInput): Promise<SubstituteVarsResult>;
30
+ //# sourceMappingURL=agent-vars.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-vars.d.ts","sourceRoot":"","sources":["../../../../src/lib/agent/jmap/agent-vars.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,eAAO,MAAM,eAAe,QAAsB,CAAC;AAEnD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyB5E;AAED,4EAA4E;AAC5E,eAAO,MAAM,WAAW,QAAyB,CAAC;AAMlD,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,aAI5B,CAAC;AAEH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,iGAAiG;IACjG,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAWvD;AAeD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CA+B/B"}
@@ -0,0 +1,96 @@
1
+ // Variable substitution for JMAP presets / inline ops ($VAR_NAME tokens).
2
+ /** Keys allowed in MCP `vars` / skill `--vars` (without leading `$`). */
3
+ export const USER_VAR_KEY_RE = /^[A-Z][A-Z0-9_]*$/;
4
+ /**
5
+ * Parses a JSON object of string values (skill `--vars` / MCP `vars`).
6
+ * Throws `Error` with the same messages the CLI used to emit via `fail(...)`.
7
+ */
8
+ export function parseUserVarsJson(jsonString) {
9
+ let obj;
10
+ try {
11
+ obj = JSON.parse(jsonString);
12
+ }
13
+ catch (err) {
14
+ throw new Error(`--vars is not valid JSON: ${err.message}`);
15
+ }
16
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
17
+ throw new Error("--vars must be a JSON object of { VAR_NAME: string }.");
18
+ }
19
+ for (const [k, v] of Object.entries(obj)) {
20
+ if (!USER_VAR_KEY_RE.test(k)) {
21
+ throw new Error(`--vars key '${k}' must match /^[A-Z][A-Z0-9_]*$/.`);
22
+ }
23
+ if (typeof v !== "string") {
24
+ throw new Error(`--vars value for '${k}' must be a string.`);
25
+ }
26
+ }
27
+ return obj;
28
+ }
29
+ /** Matches `$FOO_BAR`; excludes JMAP keywords like `$draft` (lowercase). */
30
+ export const VAR_PATTERN = /\$([A-Z][A-Z0-9_]*)/g;
31
+ function varPattern() {
32
+ return new RegExp(VAR_PATTERN.source, VAR_PATTERN.flags);
33
+ }
34
+ /** Names substituted from JMAP session / credentials when not overridden in `vars`. */
35
+ export const SESSION_VAR_NAMES = new Set([
36
+ "ACCOUNT_ID",
37
+ "INBOX",
38
+ "INBOX_MAILBOX_ID",
39
+ ]);
40
+ /** Unique variable names in order of first occurrence (without leading `$`). */
41
+ export function findVarReferences(raw) {
42
+ const seen = new Set();
43
+ const order = [];
44
+ for (const m of raw.matchAll(varPattern())) {
45
+ const name = m[1];
46
+ if (!seen.has(name)) {
47
+ seen.add(name);
48
+ order.push(name);
49
+ }
50
+ }
51
+ return order;
52
+ }
53
+ function formatMissingError(missing) {
54
+ const tokens = missing.map((n) => `$${n}`);
55
+ const hasSession = missing.some((n) => SESSION_VAR_NAMES.has(n));
56
+ let msg = `Missing values for variables: ${tokens.join(", ")}. ` +
57
+ "Pass custom placeholders in vars (MCP) or --vars (skill).";
58
+ if (hasSession) {
59
+ msg +=
60
+ " For $ACCOUNT_ID, $INBOX, and $INBOX_MAILBOX_ID, ensure register completed " +
61
+ "and credentials are valid, or pass overrides in vars.";
62
+ }
63
+ return new Error(msg);
64
+ }
65
+ /**
66
+ * Replaces every `$VAR_NAME` in `raw` with the corresponding string.
67
+ * Single pass — values are not scanned for further `$` tokens.
68
+ * Throws if any referenced variable has no value (after vars + autoResolvers).
69
+ */
70
+ export async function substituteVars(input) {
71
+ const names = findVarReferences(input.raw);
72
+ if (names.length === 0) {
73
+ return { text: input.raw };
74
+ }
75
+ const userVars = input.vars ?? {};
76
+ const resolved = new Map();
77
+ for (const name of names) {
78
+ if (Object.prototype.hasOwnProperty.call(userVars, name)) {
79
+ resolved.set(name, userVars[name]);
80
+ continue;
81
+ }
82
+ const resolver = input.autoResolvers?.[name];
83
+ if (resolver) {
84
+ resolved.set(name, await resolver());
85
+ continue;
86
+ }
87
+ }
88
+ const missing = names.filter((n) => !resolved.has(n));
89
+ if (missing.length > 0) {
90
+ throw formatMissingError(missing);
91
+ }
92
+ const text = input.raw.replace(varPattern(), (_full, name) => {
93
+ return resolved.get(name);
94
+ });
95
+ return { text };
96
+ }
@@ -0,0 +1,2 @@
1
+ export declare const helpTopicAuth = "# Atomic Mail \u2014 Auth flow\n\nAuth is automatic after `register` (or when `credentials.json` + API key\nexist).\n\n1. **Challenge** \u2014 `POST /api/v1/challenge`, read challenge JWT from\n `Authorization: Bearer <challengeJWT>`\n2. **Proof-of-work** \u2014 scrypt until difficulty satisfied\n3. **Session JWT** \u2014 `POST /api/v1/session` with challenge JWT in\n `Authorization: Bearer ...` and PoW fields (`powHex`, `nonce`) in JSON\n body; read session JWT from response `Authorization: Bearer ...` (1h TTL);\n signup returns `apiKey` once\n4. **Capability JWT** \u2014 `POST /api/v1/capability` with session JWT in\n `Authorization: Bearer ...`; read capability JWT from response\n `Authorization: Bearer ...` (2 min TTL) used as the JMAP bearer\n\nJWTs are rotated before expiry and written back to disk.\n\n## Credential files (mode 0600)\n\n`credentials.json` \u2014 `{ apiKey, inboxId, authUrl, apiUrl, scryptSalt, uploadUrl, downloadUrl }` \n`session.jwt` \u2014 session token \n`capability.jwt` \u2014 capability token\n\n## Overriding defaults\n\n- `ATOMIC_MAIL_AUTH_URL` (default: `https://auth.atomicmail.ai`)\n- `ATOMIC_MAIL_API_URL` (default: `https://api.atomicmail.ai`)\n- `ATOMIC_MAIL_SCRYPT_SALT` (optional)\n- `ATOMIC_MAIL_API_KEY` (optional)\n- `ATOMIC_MAIL_CREDENTIALS_DIR` (default: `~/.atomicmail`)";
2
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/lib/agent/jmap/help-content/auth.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,o0CA+BqC,CAAC"}
@@ -0,0 +1,33 @@
1
+ // Help topic: auth (MCP help / AgentSkill help).
2
+ export const helpTopicAuth = `\
3
+ # Atomic Mail — Auth flow
4
+
5
+ Auth is automatic after \`register\` (or when \`credentials.json\` + API key
6
+ exist).
7
+
8
+ 1. **Challenge** — \`POST /api/v1/challenge\`, read challenge JWT from
9
+ \`Authorization: Bearer <challengeJWT>\`
10
+ 2. **Proof-of-work** — scrypt until difficulty satisfied
11
+ 3. **Session JWT** — \`POST /api/v1/session\` with challenge JWT in
12
+ \`Authorization: Bearer ...\` and PoW fields (\`powHex\`, \`nonce\`) in JSON
13
+ body; read session JWT from response \`Authorization: Bearer ...\` (1h TTL);
14
+ signup returns \`apiKey\` once
15
+ 4. **Capability JWT** — \`POST /api/v1/capability\` with session JWT in
16
+ \`Authorization: Bearer ...\`; read capability JWT from response
17
+ \`Authorization: Bearer ...\` (2 min TTL) used as the JMAP bearer
18
+
19
+ JWTs are rotated before expiry and written back to disk.
20
+
21
+ ## Credential files (mode 0600)
22
+
23
+ \`credentials.json\` — \`{ apiKey, inboxId, authUrl, apiUrl, scryptSalt, uploadUrl, downloadUrl }\`
24
+ \`session.jwt\` — session token
25
+ \`capability.jwt\` — capability token
26
+
27
+ ## Overriding defaults
28
+
29
+ - \`ATOMIC_MAIL_AUTH_URL\` (default: \`https://auth.atomicmail.ai\`)
30
+ - \`ATOMIC_MAIL_API_URL\` (default: \`https://api.atomicmail.ai\`)
31
+ - \`ATOMIC_MAIL_SCRYPT_SALT\` (optional)
32
+ - \`ATOMIC_MAIL_API_KEY\` (optional)
33
+ - \`ATOMIC_MAIL_CREDENTIALS_DIR\` (default: \`~/.atomicmail\`)`;
@@ -0,0 +1,6 @@
1
+ /** Prompt for hourly inbox jobs — agent fetches mail and stays available for follow-up. */
2
+ export declare const INBOX_CRON_AGENT_PROMPT: string;
3
+ /** Short block — embed in MCP instructions, register tool text, overview workflow. */
4
+ export declare const postRegisterCronReminder = "AFTER REGISTER \u2014 INBOX POLLING\n Choose the path that matches your runtime (help topic \"cron\"):\n \u2022 Native cron (OpenClaw, Hermes, atomic-agent, \u2026): schedule an hourly AGENT turn with list_inbox.json in the prompt; wire MCP or AgentSkill into that agent. Do NOT cron atomicmail jmap_request alone.\n \u2022 No native cron (Claude, Pi, Cursor, \u2026): do NOT work around this (no wrapper scripts, OS crontab/LaunchAgent, or cross-platform scheduling). Ask your operator to set up polling on a capable host, or remind them to fetch mail manually when needed.\n \u2022 Forbidden: register in one runtime and schedule the cron job on another.";
5
+ export declare const helpTopicCron = "# Inbox polling (after register)\n\nRegistration only creates credentials. For a useful inbox integration, inbound\nmail should be fetched and triaged about once per hour between interactive\nsessions.\n\n**Choose the path that matches your runtime \u2014 do not mix platforms.**\n\n## If your host supports scheduled jobs\n\nIf you have a **native** way to enable cron or recurring agent turns (OpenClaw,\nHermes, atomic-agent, and similar hosts), set up an hourly **agent** job after\nthe first successful `register`. The job must **invoke an AI agent** (not just the\nAtomic Mail CLI) to fetch and triage mail. Wire Atomic Mail MCP or AgentSkill\ninto that agent before scheduling.\n\nThe scheduled turn should call preset **list_inbox.json** (`Email/query` +\n`Email/get`, latest 50; built-ins `$ACCOUNT_ID`, `$INBOX_MAILBOX_ID` \u2014 no extra\n`vars`).\n\n## If your host does **not** support scheduled jobs\n\nExamples: Claude, Pi, Cursor, and other chat or terminal agents without a cron\nAPI.\n\n**Do not spend tokens trying to work around this** \u2014 no wrapper scripts, OS\ncrontab/LaunchAgent setup, or scheduling on a different platform on the agent's\nbehalf.\n\nAfter a successful `register`, do one of the following:\n\n1. **Ask your operator** to set up hourly inbox polling on a host that supports\n it (share the examples below), **or**\n2. **Remind your operator** to fetch mail manually when they need updates\n (`jmap_request` with `list_inbox.json`, or `atomicmail jmap_request\n --ops-file list_inbox.json`).\n\n## Forbidden: cross-platform scheduling\n\nDo **not** register in one runtime and schedule the cron job on another. Example\nof forbidden behavior: Pi runs `register`, then creates an OpenClaw cron job.\nEither the same runtime that registered schedules polling, or the operator\nschedules it themselves on a capable host.\n\n## What not to do (any runtime)\n\n- **Raw CLI in crontab** \u2014 `atomicmail jmap_request --ops-file list_inbox.json`\n alone only fetches JSON; no agent runs and nothing prompts the operator to\n reply.\n- **Headless one-shot CLIs** \u2014 `claude -p`, `pi -p`, `agent -p`, `codex exec`,\n `gemini -p` print and exit; the operator cannot continue the thread to reply\n or forward.\n\n## Agent prompt (use in scheduled jobs or manual fetches)\n\n```text\nUse Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available \u2014 I may ask you to reply, forward, search, or dig into something important.\n```\n\n## Scheduling examples (for capable hosts or operators)\n\n| Your setup | Recommended approach |\n| --- | --- |\n| OpenClaw gateway | Built-in `openclaw cron` |\n| Hermes Agent | Install skill \u2192 `/suggestions` blueprint after `register` (or manual `hermes cron`) |\n| Atomic Bot (atomicbot.ai) | Same as OpenClaw or Hermes host |\n| atomic-agent | Built-in `atomic-agent task create` |\n| No native cron (Claude, Pi, Cursor, \u2026) | Ask operator to schedule on a capable host, or remind them to fetch manually |\n\n### OpenClaw\n\nDocs: https://docs.openclaw.ai/automation/cron-jobs\n\n```bash\nopenclaw cron add \\\n --name \"atomicmail-inbox\" \\\n --cron \"0 * * * *\" \\\n --session isolated \\\n --message \"Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available \u2014 I may ask you to reply, forward, search, or dig into something important.\" \\\n --announce\n```\n\nManage: `openclaw cron list` \u00B7 test: `openclaw cron run <job-id>`\n\n### Hermes Agent\n\nSkill blueprints: https://hermes-agent.nousresearch.com/docs/developer-guide/creating-skills\n\nCron (manual fallback): https://hermes-agent.nousresearch.com/docs/user-guide/features/cron\n\n**Recommended:** Install the Atomic Mail Hermes skill \u2192 after `register`, accept\nthe hourly inbox blueprint via `/suggestions` (`no_agent: false`,\n`list_inbox.json`, `deliver: origin`). Do not cron raw `jmap_request` or use\n`--no-agent`.\n\n**Credentials:** Default `~/.hermes/atomicmail` (not `~/.atomicmail`). The skill\nlauncher sets `ATOMIC_MAIL_CREDENTIALS_DIR` when unset; override via env or\n`atomicmail.credentials_dir` config. Use `--credentials-dir` only for\nmulti-account setups.\n\n**Manual fallback:**\n\n```bash\nhermes cron create \"0 * * * *\" \\\n \"Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available \u2014 I may ask you to reply, forward, search, or dig into something important.\" \\\n --name \"atomicmail-inbox\" \\\n --deliver origin\n```\n\nManage: `hermes cron list` \u00B7 test: `hermes cron run <job-id>`\n\n### atomic-agent\n\n```bash\natomic-agent task create \\\n --cron \"0 * * * *\" \\\n --message \"Use Atomic Mail to fetch my inbox (MCP jmap_request with ops_file list_inbox.json, or atomicmail jmap_request --ops-file list_inbox.json). Summarize new messages, highlight what needs a reply, and stay available \u2014 I may ask you to reply, forward, search, or dig into something important.\"\n```\n\nManage: `atomic-agent task list`\n\n## Verify setup\n\n1. `register` succeeded; Atomic Mail MCP or AgentSkill is available to the agent.\n2. Run the agent prompt **once manually**; confirm inbox fetch and follow-up work.\n3. Confirm the job is registered (`openclaw cron list`, `hermes cron list`,\n `atomic-agent task list`).\n\n## For operators: OS scheduling on terminal hosts\n\nThis section is **operator documentation**, not an agent obligation. Chat agents\nwithout native cron should **not** attempt OS scheduling themselves.\n\nIf you (the operator) run a **terminal CLI agent** without OpenClaw, Hermes, or\nsimilar, the scheduler must **start an interactive session** with the agent\nprompt \u2014 not call `atomicmail` directly.\n\n| Agent | Start interactively | Avoid for inbox polling |\n| --- | --- | --- |\n| Claude Code | `claude \"prompt\"` | `claude -p` |\n| Pi | `pi \"prompt\"` | `pi -p` |\n| Cursor CLI | `agent \"prompt\"` | `agent -p` |\n\nOS options: wrapper script + user crontab, macOS LaunchAgent, or Linux systemd\nuser timer \u2014 launching a terminal emulator with an interactive agent session.";
6
+ //# sourceMappingURL=cron.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../../../../src/lib/agent/jmap/help-content/cron.ts"],"names":[],"mappings":"AAEA,2FAA2F;AAC3F,eAAO,MAAM,uBAAuB,QAIyC,CAAC;AAE9E,sFAAsF;AACtF,eAAO,MAAM,wBAAwB,ypBAKwC,CAAC;AAE9E,eAAO,MAAM,aAAa,y2MAiJoD,CAAC"}