harnery 0.45.2 → 0.46.1

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 (227) hide show
  1. package/dist/commander.d.ts +13 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commands/admission.d.ts.map +1 -1
  4. package/dist/commands/admission.js +214 -27
  5. package/dist/commands/agents.d.ts +4 -0
  6. package/dist/commands/agents.d.ts.map +1 -1
  7. package/dist/commands/agents.js +102 -25
  8. package/dist/commands/browse.d.ts +3 -0
  9. package/dist/commands/browse.d.ts.map +1 -1
  10. package/dist/commands/browse.js +172 -8
  11. package/dist/commands/fetch.js +1 -0
  12. package/dist/commands/ledger-v3.d.ts.map +1 -1
  13. package/dist/commands/ledger-v3.js +8 -2
  14. package/dist/commands/presence.d.ts.map +1 -1
  15. package/dist/commands/presence.js +40 -0
  16. package/dist/commands/qa-run.d.ts.map +1 -1
  17. package/dist/commands/qa-run.js +4 -3
  18. package/dist/commands/review-pack.d.ts.map +1 -1
  19. package/dist/commands/review-pack.js +33 -6
  20. package/dist/core/agents/index.d.ts +1 -0
  21. package/dist/core/agents/index.d.ts.map +1 -1
  22. package/dist/core/agents/index.js +1 -0
  23. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  24. package/dist/core/agents/live-authority-v3.js +11 -4
  25. package/dist/core/agents/render/session-context.d.ts +0 -6
  26. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  27. package/dist/core/agents/render/session-context.js +1 -6
  28. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  29. package/dist/core/agents/rules/claim-conflict.js +36 -16
  30. package/dist/core/agents/rules/stop-hook.d.ts +6 -0
  31. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  32. package/dist/core/agents/rules/stop-hook.js +20 -1
  33. package/dist/core/agents/session-events.d.ts.map +1 -1
  34. package/dist/core/agents/session-events.js +12 -4
  35. package/dist/core/agents/state/heartbeat-reader.d.ts +3 -0
  36. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  37. package/dist/core/agents/state/live-coordination-view.d.ts +3 -1
  38. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  39. package/dist/core/agents/state/live-coordination-view.js +6 -2
  40. package/dist/core/agents/state/parent-reference-contract.d.ts +16 -0
  41. package/dist/core/agents/state/parent-reference-contract.d.ts.map +1 -0
  42. package/dist/core/agents/state/parent-reference-contract.js +1 -0
  43. package/dist/core/agents/state/parent-reference.d.ts +18 -0
  44. package/dist/core/agents/state/parent-reference.d.ts.map +1 -0
  45. package/dist/core/agents/state/parent-reference.js +119 -0
  46. package/dist/core/config.d.ts +39 -0
  47. package/dist/core/config.d.ts.map +1 -1
  48. package/dist/core/config.js +63 -0
  49. package/dist/core/diagnostics/path-identity.d.ts +1 -1
  50. package/dist/core/diagnostics/path-identity.d.ts.map +1 -1
  51. package/dist/core/diagnostics/path-identity.js +51 -6
  52. package/dist/core/events/v3/canonical.js +43 -14
  53. package/dist/core/events/v3/index.d.ts +1 -1
  54. package/dist/core/events/v3/index.d.ts.map +1 -1
  55. package/dist/core/events/v3/index.js +1 -1
  56. package/dist/core/events/v3/producers/recorder.d.ts +34 -0
  57. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  58. package/dist/core/events/v3/producers/recorder.js +117 -13
  59. package/dist/core/events/v3/reader.d.ts.map +1 -1
  60. package/dist/core/events/v3/reader.js +53 -13
  61. package/dist/core/events/v3/validate.d.ts.map +1 -1
  62. package/dist/core/events/v3/validate.js +36 -1
  63. package/dist/core/hooks/adapter/native-parent.d.ts +23 -0
  64. package/dist/core/hooks/adapter/native-parent.d.ts.map +1 -0
  65. package/dist/core/hooks/adapter/native-parent.js +91 -0
  66. package/dist/core/hooks/cli.js +103 -5
  67. package/dist/core/hooks/resolve/owner.d.ts +0 -11
  68. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  69. package/dist/core/hooks/resolve/owner.js +0 -23
  70. package/dist/core/hooks/resolve/transcript.d.ts +12 -0
  71. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  72. package/dist/core/hooks/resolve/transcript.js +54 -0
  73. package/dist/core/hooks/shell-admission.d.ts +84 -0
  74. package/dist/core/hooks/shell-admission.d.ts.map +1 -0
  75. package/dist/core/hooks/shell-admission.js +622 -0
  76. package/dist/core/hooks/shell-commit-tree.d.ts +2 -0
  77. package/dist/core/hooks/shell-commit-tree.d.ts.map +1 -0
  78. package/dist/core/hooks/shell-commit-tree.js +104 -0
  79. package/dist/core/inbox/contract.d.ts +12 -0
  80. package/dist/core/inbox/contract.d.ts.map +1 -1
  81. package/dist/core/inbox/contract.js +16 -0
  82. package/dist/core/inbox/peer-messages.d.ts +41 -16
  83. package/dist/core/inbox/peer-messages.d.ts.map +1 -1
  84. package/dist/core/inbox/peer-messages.js +372 -15
  85. package/dist/core/inbox/service.d.ts +8 -1
  86. package/dist/core/inbox/service.d.ts.map +1 -1
  87. package/dist/core/inbox/service.js +83 -3
  88. package/dist/core/inbox/targets.d.ts +17 -3
  89. package/dist/core/inbox/targets.d.ts.map +1 -1
  90. package/dist/core/inbox/targets.js +77 -2
  91. package/dist/core/presence/git.d.ts +18 -12
  92. package/dist/core/presence/git.d.ts.map +1 -1
  93. package/dist/core/presence/git.js +32 -38
  94. package/dist/core/presence/index.d.ts +2 -2
  95. package/dist/core/presence/index.d.ts.map +1 -1
  96. package/dist/core/presence/index.js +8 -6
  97. package/dist/core/presence/mail.d.ts +133 -0
  98. package/dist/core/presence/mail.d.ts.map +1 -0
  99. package/dist/core/presence/mail.js +464 -0
  100. package/dist/core/semantic/contract.d.ts +5 -5
  101. package/dist/core/storage/modes.d.ts +4 -0
  102. package/dist/core/storage/modes.d.ts.map +1 -1
  103. package/dist/core/storage/modes.js +4 -0
  104. package/dist/core/storage/segments.d.ts.map +1 -1
  105. package/dist/core/storage/segments.js +5 -4
  106. package/dist/lib/admission-history.d.ts +96 -0
  107. package/dist/lib/admission-history.d.ts.map +1 -0
  108. package/dist/lib/admission-history.js +196 -0
  109. package/dist/lib/admission.d.ts +56 -7
  110. package/dist/lib/admission.d.ts.map +1 -1
  111. package/dist/lib/admission.js +161 -19
  112. package/dist/lib/browser/client.d.ts +18 -0
  113. package/dist/lib/browser/client.d.ts.map +1 -1
  114. package/dist/lib/browser/client.js +100 -12
  115. package/dist/lib/browser/content-checks.d.ts +3 -1
  116. package/dist/lib/browser/content-checks.d.ts.map +1 -1
  117. package/dist/lib/browser/content-checks.js +10 -2
  118. package/dist/lib/browser/critique.d.ts +32 -0
  119. package/dist/lib/browser/critique.d.ts.map +1 -1
  120. package/dist/lib/browser/critique.js +72 -3
  121. package/dist/lib/browser/geometry.d.ts +2 -0
  122. package/dist/lib/browser/geometry.d.ts.map +1 -1
  123. package/dist/lib/browser/geometry.js +61 -15
  124. package/dist/lib/browser/index.d.ts +1 -1
  125. package/dist/lib/browser/index.d.ts.map +1 -1
  126. package/dist/lib/browser/index.js +1 -1
  127. package/dist/lib/browser/page-review-contracts.d.ts +6 -1
  128. package/dist/lib/browser/page-review-contracts.d.ts.map +1 -1
  129. package/dist/lib/browser/page-review-contracts.js +6 -1
  130. package/dist/lib/browser/page-review-judge.d.ts +3 -1
  131. package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
  132. package/dist/lib/browser/page-review-judge.js +41 -11
  133. package/dist/lib/browser/page-review-pack.d.ts +3 -0
  134. package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
  135. package/dist/lib/browser/page-review-pack.js +5 -2
  136. package/dist/lib/browser/qa-run-contracts.d.ts +11 -0
  137. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  138. package/dist/lib/browser/qa-run-contracts.js +11 -4
  139. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  140. package/dist/lib/browser/qa-run.js +73 -16
  141. package/dist/lib/browser/runts.d.ts +4 -2
  142. package/dist/lib/browser/runts.d.ts.map +1 -1
  143. package/dist/lib/browser/runts.js +84 -33
  144. package/dist/lib/browser/windows-chrome.d.ts +14 -0
  145. package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
  146. package/dist/lib/browser/windows-chrome.js +42 -0
  147. package/dist/lib/browser/windows-login-sync.d.ts +110 -0
  148. package/dist/lib/browser/windows-login-sync.d.ts.map +1 -0
  149. package/dist/lib/browser/windows-login-sync.js +171 -0
  150. package/dist/lib/cookies/client.d.ts +30 -5
  151. package/dist/lib/cookies/client.d.ts.map +1 -1
  152. package/dist/lib/cookies/client.js +55 -10
  153. package/dist/lib/cookies/index.d.ts +1 -1
  154. package/dist/lib/cookies/index.d.ts.map +1 -1
  155. package/dist/lib/cookies/index.js +1 -1
  156. package/dist/lib/docs-metadata-sync.d.ts.map +1 -1
  157. package/dist/lib/docs-metadata-sync.js +41 -2
  158. package/dist/lib/durable-job.d.ts +8 -0
  159. package/dist/lib/durable-job.d.ts.map +1 -1
  160. package/dist/lib/durable-job.js +10 -0
  161. package/dist/lib/http/client.d.ts +9 -8
  162. package/dist/lib/http/client.d.ts.map +1 -1
  163. package/dist/lib/http/client.js +44 -2
  164. package/dist/lib/instructions/templates.js +4 -4
  165. package/package.json +2 -2
  166. package/src/commander.ts +13 -0
  167. package/src/commands/admission.ts +272 -29
  168. package/src/commands/agents.ts +132 -27
  169. package/src/commands/browse.ts +208 -6
  170. package/src/commands/fetch.ts +1 -0
  171. package/src/commands/ledger-v3.ts +8 -2
  172. package/src/commands/presence.ts +49 -0
  173. package/src/commands/qa-run.ts +4 -3
  174. package/src/commands/review-pack.ts +41 -5
  175. package/src/core/agents/index.ts +6 -0
  176. package/src/core/agents/live-authority-v3.ts +11 -4
  177. package/src/core/agents/render/session-context.ts +1 -7
  178. package/src/core/agents/rules/claim-conflict.ts +50 -18
  179. package/src/core/agents/rules/stop-hook.ts +28 -1
  180. package/src/core/agents/session-events.ts +18 -3
  181. package/src/core/agents/state/heartbeat-reader.ts +3 -0
  182. package/src/core/agents/state/live-coordination-view.ts +13 -6
  183. package/src/core/agents/state/parent-reference-contract.ts +14 -0
  184. package/src/core/agents/state/parent-reference.ts +179 -0
  185. package/src/core/config.ts +85 -0
  186. package/src/core/diagnostics/path-identity.ts +52 -6
  187. package/src/core/events/v3/canonical.ts +46 -13
  188. package/src/core/events/v3/index.ts +3 -0
  189. package/src/core/events/v3/producers/recorder.ts +151 -13
  190. package/src/core/events/v3/reader.ts +55 -11
  191. package/src/core/events/v3/validate.ts +42 -1
  192. package/src/core/hooks/adapter/native-parent.ts +109 -0
  193. package/src/core/hooks/cli.ts +110 -4
  194. package/src/core/hooks/resolve/owner.ts +0 -23
  195. package/src/core/hooks/resolve/transcript.ts +61 -0
  196. package/src/core/hooks/shell-admission.ts +634 -0
  197. package/src/core/hooks/shell-commit-tree.ts +102 -0
  198. package/src/core/inbox/contract.ts +18 -0
  199. package/src/core/inbox/peer-messages.ts +501 -15
  200. package/src/core/inbox/service.ts +108 -3
  201. package/src/core/inbox/targets.ts +94 -3
  202. package/src/core/presence/git.ts +45 -37
  203. package/src/core/presence/index.ts +16 -8
  204. package/src/core/presence/mail.ts +620 -0
  205. package/src/core/storage/modes.ts +6 -0
  206. package/src/core/storage/segments.ts +5 -4
  207. package/src/lib/admission-history.ts +267 -0
  208. package/src/lib/admission.ts +217 -19
  209. package/src/lib/browser/client.ts +147 -35
  210. package/src/lib/browser/content-checks.ts +14 -3
  211. package/src/lib/browser/critique.ts +113 -3
  212. package/src/lib/browser/geometry.ts +63 -14
  213. package/src/lib/browser/index.ts +5 -0
  214. package/src/lib/browser/page-review-contracts.ts +6 -1
  215. package/src/lib/browser/page-review-judge.ts +58 -13
  216. package/src/lib/browser/page-review-pack.ts +13 -2
  217. package/src/lib/browser/qa-run-contracts.ts +30 -4
  218. package/src/lib/browser/qa-run.ts +78 -17
  219. package/src/lib/browser/runts.ts +85 -37
  220. package/src/lib/browser/windows-chrome.ts +50 -0
  221. package/src/lib/browser/windows-login-sync.ts +275 -0
  222. package/src/lib/cookies/client.ts +62 -10
  223. package/src/lib/cookies/index.ts +3 -0
  224. package/src/lib/docs-metadata-sync.ts +44 -2
  225. package/src/lib/durable-job.ts +15 -0
  226. package/src/lib/http/client.ts +63 -1
  227. package/src/lib/instructions/templates.ts +4 -4
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Cross-machine peer messages (ADR 0222). A ping whose recipient lives on
3
+ * another machine is queued here and published as part of this machine's
4
+ * mail ref, `refs/harnery/mail/<machine>`, on the same origin the presence
5
+ * refs use. Each machine writes only its own ref, so force-pushing it never
6
+ * races another writer.
7
+ *
8
+ * The ref is a parentless commit whose message is one JSON blob:
9
+ * outbox — messages this machine sent that the recipient machine has not
10
+ * acknowledged yet;
11
+ * acks — ids of messages this machine imported from other machines.
12
+ *
13
+ * The recipient machine imports envelopes addressed to it into the ordinary
14
+ * per-instance inbox (keeping the sender's message id, so a re-import is a
15
+ * no-op) and acknowledges them in its own ref. The sender drops an
16
+ * acknowledged message from its outbox; the recipient stops acknowledging
17
+ * once the sender's ref no longer lists it. Delivery is therefore
18
+ * at-least-once into a deduplicating inbox.
19
+ *
20
+ * Local state is one append-only log, `.harnery/mail/log.jsonl`, updated
21
+ * under the durable-history lease so concurrent hooks on one machine agree.
22
+ * Repo access is the trust boundary, exactly as for presence: anyone who can
23
+ * push to origin can publish a mail ref.
24
+ */
25
+ import type { HarneryInboxService } from "../inbox/service.js";
26
+ export declare const HARNERY_MAIL_ENVELOPE_SCHEMA: "harnery.mail-envelope/v1";
27
+ /** An unacknowledged message stops being offered after this long. */
28
+ export declare const MAIL_TTL_MS: number;
29
+ export interface HarneryMailEnvelopeV1 {
30
+ schema: typeof HARNERY_MAIL_ENVELOPE_SCHEMA;
31
+ message_id: string;
32
+ from_machine: string;
33
+ to_machine: string;
34
+ sender_instance_id: string | null;
35
+ sender_display_name: string;
36
+ recipient_instance_id: string;
37
+ recipient_display_name: string;
38
+ created_at: string;
39
+ body: string;
40
+ body_bytes: number;
41
+ }
42
+ export interface HarneryMailBlobV1 {
43
+ v: 1;
44
+ machine: string;
45
+ published_at: string;
46
+ outbox: HarneryMailEnvelopeV1[];
47
+ acks: string[];
48
+ }
49
+ /** This machine's mail identity: the same sanitized label as its ref. */
50
+ export declare function mailMachine(): string;
51
+ export interface QueueMailInput {
52
+ messageId: string;
53
+ replayOnly?: boolean;
54
+ toMachine: string;
55
+ senderInstanceId: string | null;
56
+ senderName: string;
57
+ recipientInstanceId: string;
58
+ recipientName: string;
59
+ body: string;
60
+ maxBodyBytes: number;
61
+ now?: Date;
62
+ }
63
+ export interface HarneryMailQueueReceipt {
64
+ envelope: HarneryMailEnvelopeV1;
65
+ state: "queued" | "duplicate";
66
+ delivery_state: "pending" | "delivered" | "expired";
67
+ synced: true;
68
+ }
69
+ /** Queue one message for another machine. The caller publishes afterwards. */
70
+ export declare function queueMail(coordRoot: string, input: QueueMailInput): HarneryMailQueueReceipt;
71
+ export type MailPublishResult = {
72
+ status: "published";
73
+ machine: string;
74
+ outbox: number;
75
+ acks: number;
76
+ sha: string;
77
+ } | {
78
+ status: "skipped";
79
+ reason: string;
80
+ } | {
81
+ status: "error";
82
+ error: string;
83
+ };
84
+ /**
85
+ * Publish this machine's mail ref when its content changed, or as a keepalive
86
+ * while messages are still unacknowledged. A machine that never sent or
87
+ * received cross-machine mail never creates the ref.
88
+ */
89
+ export declare function publishMail(coordRoot: string, opts?: {
90
+ force?: boolean;
91
+ sync?: boolean;
92
+ now?: Date;
93
+ }): MailPublishResult;
94
+ export interface MailImportResult {
95
+ received: number;
96
+ delivered: number;
97
+ released: number;
98
+ rejected: number;
99
+ published?: MailPublishResult["status"];
100
+ }
101
+ /**
102
+ * Apply every locally-fetched peer mail ref: import envelopes addressed to
103
+ * this machine, record acknowledgements of this machine's outbox, and stop
104
+ * acknowledging messages their sender has dropped. Reads only local refs;
105
+ * `fetchPresence` brings them in. Never throws.
106
+ */
107
+ export declare function importMail(coordRoot: string, inbox: HarneryInboxService, opts?: {
108
+ now?: Date;
109
+ publish?: boolean;
110
+ throttle?: boolean;
111
+ }): MailImportResult;
112
+ export interface RemoteSender {
113
+ instance_id: string;
114
+ name: string;
115
+ machine: string;
116
+ }
117
+ /** Senders this machine has received mail from, so a reply can find them even
118
+ * after their machine's presence goes quiet. */
119
+ export declare function knownRemoteSenders(coordRoot: string): RemoteSender[];
120
+ export interface MailStatus {
121
+ machine: string;
122
+ outbox: Array<{
123
+ message_id: string;
124
+ to_machine: string;
125
+ recipient: string;
126
+ queued_at: string;
127
+ }>;
128
+ acks: number;
129
+ delivered: number;
130
+ expired: number;
131
+ }
132
+ export declare function mailStatus(coordRoot: string): MailStatus;
133
+ //# sourceMappingURL=mail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.d.ts","sourceRoot":"","sources":["../../../src/core/presence/mail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,464 @@
1
+ /**
2
+ * Cross-machine peer messages (ADR 0222). A ping whose recipient lives on
3
+ * another machine is queued here and published as part of this machine's
4
+ * mail ref, `refs/harnery/mail/<machine>`, on the same origin the presence
5
+ * refs use. Each machine writes only its own ref, so force-pushing it never
6
+ * races another writer.
7
+ *
8
+ * The ref is a parentless commit whose message is one JSON blob:
9
+ * outbox — messages this machine sent that the recipient machine has not
10
+ * acknowledged yet;
11
+ * acks — ids of messages this machine imported from other machines.
12
+ *
13
+ * The recipient machine imports envelopes addressed to it into the ordinary
14
+ * per-instance inbox (keeping the sender's message id, so a re-import is a
15
+ * no-op) and acknowledges them in its own ref. The sender drops an
16
+ * acknowledged message from its outbox; the recipient stops acknowledging
17
+ * once the sender's ref no longer lists it. Delivery is therefore
18
+ * at-least-once into a deduplicating inbox.
19
+ *
20
+ * Local state is one append-only log, `.harnery/mail/log.jsonl`, updated
21
+ * under the durable-history lease so concurrent hooks on one machine agree.
22
+ * Repo access is the trust boundary, exactly as for presence: anyone who can
23
+ * push to origin can publish a mail ref.
24
+ */
25
+ import { createHash } from "node:crypto";
26
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, utimesSync, writeFileSync, } from "node:fs";
27
+ import { join } from "node:path";
28
+ import { resolveMachineLabel } from "../../lib/machine.js";
29
+ import { presenceEnabled } from "../config.js";
30
+ import { HARNERY_INBOX_RECORD_SCHEMA, HarneryInboxCapacityError, HarneryInboxMessageConflictError, HarneryInboxMessageHistoryMissingError, } from "../inbox/contract.js";
31
+ import { readCrashSafeJsonlFile, updateCrashSafeJsonlFile } from "../storage/durable-history.js";
32
+ import { hasOrigin, MAIL_REF_PREFIX, pushMachineRef, readMachineRefs, sanitizeRefComponent, writePresenceCommit, } from "./git.js";
33
+ export const HARNERY_MAIL_ENVELOPE_SCHEMA = "harnery.mail-envelope/v1";
34
+ const MAIL_LOG_SCHEMA = "harnery.mail-log/v1";
35
+ /** An unacknowledged message stops being offered after this long. */
36
+ export const MAIL_TTL_MS = 14 * 24 * 60 * 60_000;
37
+ /** While a message is still unacknowledged, re-push at least this often, so a
38
+ * detached push that failed heals without anyone noticing. */
39
+ const MAIL_KEEPALIVE_SECS = 600;
40
+ const MAX_OUTBOX = 100;
41
+ /** Hook-driven imports run at most this often (the presence fetch is 60s). */
42
+ const IMPORT_INTERVAL_SECS = 30;
43
+ const MAX_LOG_RECORDS = 20_000;
44
+ const MAX_LOG_RECORD_BYTES = 16_384;
45
+ /** This machine's mail identity: the same sanitized label as its ref. */
46
+ export function mailMachine() {
47
+ return sanitizeRefComponent(resolveMachineLabel());
48
+ }
49
+ function mailDir(coordRoot) {
50
+ return join(coordRoot, ".harnery", "mail");
51
+ }
52
+ function logPath(coordRoot) {
53
+ return join(mailDir(coordRoot), "log.jsonl");
54
+ }
55
+ const LOG_OPTIONS = { max_record_bytes: MAX_LOG_RECORD_BYTES, max_records: MAX_LOG_RECORDS };
56
+ function stamp(now) {
57
+ return now.toISOString();
58
+ }
59
+ function projectMail(records) {
60
+ const sent = new Map();
61
+ const outbound = new Map();
62
+ const acks = new Map();
63
+ const imported = new Set();
64
+ const senders = new Map();
65
+ let delivered = 0;
66
+ let expired = 0;
67
+ for (const record of records) {
68
+ if (record?.schema !== MAIL_LOG_SCHEMA)
69
+ continue;
70
+ switch (record.kind) {
71
+ case "outbound":
72
+ outbound.set(record.envelope.message_id, { at: record.at, envelope: record.envelope });
73
+ sent.set(record.envelope.message_id, {
74
+ envelope: record.envelope,
75
+ delivery_state: "pending",
76
+ });
77
+ break;
78
+ case "delivered":
79
+ if (outbound.delete(record.message_id))
80
+ delivered++;
81
+ if (sent.has(record.message_id))
82
+ sent.get(record.message_id).delivery_state = "delivered";
83
+ break;
84
+ case "expired":
85
+ if (outbound.delete(record.message_id))
86
+ expired++;
87
+ if (sent.has(record.message_id))
88
+ sent.get(record.message_id).delivery_state = "expired";
89
+ break;
90
+ case "imported":
91
+ imported.add(record.message_id);
92
+ acks.set(record.message_id, { at: record.at, from_machine: record.from_machine });
93
+ if (record.sender_instance_id) {
94
+ senders.set(record.sender_instance_id, {
95
+ name: record.sender_display_name,
96
+ machine: record.from_machine,
97
+ at: record.at,
98
+ });
99
+ }
100
+ break;
101
+ case "released":
102
+ acks.delete(record.message_id);
103
+ break;
104
+ }
105
+ }
106
+ return { sent, outbox: [...outbound.values()], acks, imported, senders, delivered, expired };
107
+ }
108
+ function readMailLog(coordRoot) {
109
+ try {
110
+ return readCrashSafeJsonlFile(logPath(coordRoot), LOG_OPTIONS);
111
+ }
112
+ catch {
113
+ return [];
114
+ }
115
+ }
116
+ function updateMailLog(coordRoot, update) {
117
+ mkdirSync(mailDir(coordRoot), { recursive: true });
118
+ return updateCrashSafeJsonlFile(logPath(coordRoot), LOG_OPTIONS, (records) => update(projectMail(records)));
119
+ }
120
+ /** Queue one message for another machine. The caller publishes afterwards. */
121
+ export function queueMail(coordRoot, input) {
122
+ if (input.replayOnly !== undefined && typeof input.replayOnly !== "boolean") {
123
+ throw new Error("invalid replayOnly flag");
124
+ }
125
+ if (typeof input.messageId !== "string" ||
126
+ !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(input.messageId)) {
127
+ throw new Error("invalid message id");
128
+ }
129
+ const bodyBytes = Buffer.byteLength(input.body);
130
+ if (bodyBytes === 0 || bodyBytes > input.maxBodyBytes) {
131
+ throw new HarneryInboxCapacityError("message_body_limit", bodyBytes, input.maxBodyBytes);
132
+ }
133
+ const now = input.now ?? new Date();
134
+ const envelope = {
135
+ schema: HARNERY_MAIL_ENVELOPE_SCHEMA,
136
+ message_id: input.messageId,
137
+ from_machine: mailMachine(),
138
+ to_machine: sanitizeRefComponent(input.toMachine),
139
+ sender_instance_id: input.senderInstanceId,
140
+ sender_display_name: input.senderName,
141
+ recipient_instance_id: input.recipientInstanceId,
142
+ recipient_display_name: input.recipientName,
143
+ created_at: stamp(now),
144
+ body: input.body,
145
+ body_bytes: bodyBytes,
146
+ };
147
+ return updateMailLog(coordRoot, (projection) => {
148
+ const existing = projection.sent.get(envelope.message_id);
149
+ if (existing) {
150
+ const fields = [
151
+ "from_machine",
152
+ "to_machine",
153
+ "sender_instance_id",
154
+ "sender_display_name",
155
+ "recipient_instance_id",
156
+ "recipient_display_name",
157
+ "body",
158
+ "body_bytes",
159
+ ];
160
+ if (fields.some((field) => existing.envelope[field] !== envelope[field])) {
161
+ throw new HarneryInboxMessageConflictError(envelope.message_id);
162
+ }
163
+ return {
164
+ result: {
165
+ envelope: existing.envelope,
166
+ state: "duplicate",
167
+ delivery_state: existing.delivery_state,
168
+ synced: true,
169
+ },
170
+ };
171
+ }
172
+ if (input.replayOnly) {
173
+ throw new HarneryInboxMessageHistoryMissingError(envelope.message_id);
174
+ }
175
+ if (projection.outbox.length >= MAX_OUTBOX) {
176
+ throw new HarneryInboxCapacityError("pending_count_limit", projection.outbox.length, MAX_OUTBOX);
177
+ }
178
+ return {
179
+ append: [{ schema: MAIL_LOG_SCHEMA, kind: "outbound", at: stamp(now), envelope }],
180
+ result: { envelope, state: "queued", delivery_state: "pending", synced: true },
181
+ };
182
+ });
183
+ }
184
+ function statePath(coordRoot) {
185
+ return join(mailDir(coordRoot), "publish-state.json");
186
+ }
187
+ function readState(coordRoot) {
188
+ try {
189
+ return JSON.parse(readFileSync(statePath(coordRoot), "utf8"));
190
+ }
191
+ catch {
192
+ return {};
193
+ }
194
+ }
195
+ function writeState(coordRoot, state) {
196
+ try {
197
+ mkdirSync(mailDir(coordRoot), { recursive: true });
198
+ const p = statePath(coordRoot);
199
+ const tmp = `${p}.tmp.${process.pid}`;
200
+ writeFileSync(tmp, JSON.stringify(state), "utf8");
201
+ renameSync(tmp, p);
202
+ }
203
+ catch {
204
+ /* best-effort: the keepalive re-push covers a lost state write */
205
+ }
206
+ }
207
+ /** Expire stale outbound mail, then return the blob this machine should publish. */
208
+ function currentBlob(coordRoot, now) {
209
+ const projection = updateMailLog(coordRoot, (p) => {
210
+ const cutoff = now.getTime() - MAIL_TTL_MS;
211
+ const stale = p.outbox.filter(({ at }) => Date.parse(at) < cutoff);
212
+ return {
213
+ append: stale.map(({ envelope }) => ({
214
+ schema: MAIL_LOG_SCHEMA,
215
+ kind: "expired",
216
+ at: stamp(now),
217
+ message_id: envelope.message_id,
218
+ })),
219
+ result: { ...p, outbox: p.outbox.filter(({ at }) => Date.parse(at) >= cutoff) },
220
+ };
221
+ });
222
+ return {
223
+ v: 1,
224
+ machine: mailMachine(),
225
+ published_at: stamp(now),
226
+ outbox: projection.outbox.map(({ envelope }) => envelope),
227
+ acks: [...projection.acks.keys()].sort(),
228
+ };
229
+ }
230
+ /**
231
+ * Publish this machine's mail ref when its content changed, or as a keepalive
232
+ * while messages are still unacknowledged. A machine that never sent or
233
+ * received cross-machine mail never creates the ref.
234
+ */
235
+ export function publishMail(coordRoot, opts = {}) {
236
+ try {
237
+ if (!presenceEnabled(coordRoot))
238
+ return { status: "skipped", reason: "disabled" };
239
+ // A machine that never sent or received cross-machine mail has no log;
240
+ // the hook path stops here without spawning git.
241
+ if (!existsSync(logPath(coordRoot)))
242
+ return { status: "skipped", reason: "no mail" };
243
+ if (!hasOrigin(coordRoot))
244
+ return { status: "skipped", reason: "no origin remote" };
245
+ const now = opts.now ?? new Date();
246
+ const blob = currentBlob(coordRoot, now);
247
+ const empty = blob.outbox.length === 0 && blob.acks.length === 0;
248
+ const hash = createHash("sha256")
249
+ .update(JSON.stringify([blob.outbox.map((e) => e.message_id), blob.acks]))
250
+ .digest("hex");
251
+ const state = readState(coordRoot);
252
+ if (!opts.force) {
253
+ if (empty && (state.empty ?? true))
254
+ return { status: "skipped", reason: "nothing to carry" };
255
+ const pushedAt = Date.parse(state.pushed_at ?? "");
256
+ const sincePush = Number.isFinite(pushedAt)
257
+ ? (now.getTime() - pushedAt) / 1000
258
+ : Number.POSITIVE_INFINITY;
259
+ if (state.hash === hash && (blob.outbox.length === 0 || sincePush < MAIL_KEEPALIVE_SECS)) {
260
+ return { status: "skipped", reason: "unchanged" };
261
+ }
262
+ }
263
+ const sha = writePresenceCommit(coordRoot, JSON.stringify(blob));
264
+ if (!sha)
265
+ return { status: "error", error: "commit-tree failed" };
266
+ const push = pushMachineRef(coordRoot, MAIL_REF_PREFIX, blob.machine, sha, {
267
+ sync: opts.sync,
268
+ });
269
+ if (!push.ok)
270
+ return { status: "error", error: push.error ?? "push failed" };
271
+ writeState(coordRoot, { hash, pushed_at: stamp(now), empty });
272
+ return {
273
+ status: "published",
274
+ machine: blob.machine,
275
+ outbox: blob.outbox.length,
276
+ acks: blob.acks.length,
277
+ sha,
278
+ };
279
+ }
280
+ catch (e) {
281
+ return { status: "error", error: e instanceof Error ? e.message : String(e) };
282
+ }
283
+ }
284
+ function parseBlob(machine, message) {
285
+ try {
286
+ const blob = JSON.parse(message);
287
+ if (blob?.v !== 1 || sanitizeRefComponent(String(blob.machine)) !== machine)
288
+ return null;
289
+ if (!Number.isFinite(Date.parse(blob.published_at)))
290
+ return null;
291
+ if (!Array.isArray(blob.outbox) || !Array.isArray(blob.acks))
292
+ return null;
293
+ return blob;
294
+ }
295
+ catch {
296
+ return null;
297
+ }
298
+ }
299
+ function validEnvelope(envelope, from, self) {
300
+ return (envelope?.schema === HARNERY_MAIL_ENVELOPE_SCHEMA &&
301
+ envelope.from_machine === from &&
302
+ envelope.to_machine === self &&
303
+ typeof envelope.message_id === "string" &&
304
+ typeof envelope.recipient_instance_id === "string" &&
305
+ typeof envelope.body === "string" &&
306
+ Buffer.byteLength(envelope.body) === envelope.body_bytes);
307
+ }
308
+ /**
309
+ * Apply every locally-fetched peer mail ref: import envelopes addressed to
310
+ * this machine, record acknowledgements of this machine's outbox, and stop
311
+ * acknowledging messages their sender has dropped. Reads only local refs;
312
+ * `fetchPresence` brings them in. Never throws.
313
+ */
314
+ export function importMail(coordRoot, inbox, opts = {}) {
315
+ const result = { received: 0, delivered: 0, released: 0, rejected: 0 };
316
+ try {
317
+ if (!presenceEnabled(coordRoot))
318
+ return result;
319
+ const now = opts.now ?? new Date();
320
+ if (opts.throttle && !importDue(coordRoot, now))
321
+ return result;
322
+ const self = mailMachine();
323
+ const peers = readMachineRefs(coordRoot, MAIL_REF_PREFIX)
324
+ .filter(({ machine }) => machine !== self)
325
+ .map(({ machine, message }) => ({ machine, blob: parseBlob(machine, message) }))
326
+ .filter((peer) => peer.blob !== null);
327
+ if (peers.length === 0)
328
+ return result;
329
+ updateMailLog(coordRoot, (projection) => {
330
+ const append = [];
331
+ const pending = new Map(projection.outbox.map((o) => [o.envelope.message_id, o.envelope]));
332
+ for (const { machine, blob } of peers) {
333
+ const listed = new Set();
334
+ for (const envelope of blob.outbox) {
335
+ if (!validEnvelope(envelope, machine, self))
336
+ continue;
337
+ listed.add(envelope.message_id);
338
+ if (projection.imported.has(envelope.message_id))
339
+ continue;
340
+ if (!receive(inbox, envelope, result))
341
+ continue;
342
+ append.push({
343
+ schema: MAIL_LOG_SCHEMA,
344
+ kind: "imported",
345
+ at: stamp(now),
346
+ message_id: envelope.message_id,
347
+ from_machine: machine,
348
+ sender_instance_id: envelope.sender_instance_id,
349
+ sender_display_name: envelope.sender_display_name,
350
+ });
351
+ }
352
+ for (const id of blob.acks) {
353
+ if (pending.get(id)?.to_machine !== machine)
354
+ continue;
355
+ pending.delete(id);
356
+ append.push({
357
+ schema: MAIL_LOG_SCHEMA,
358
+ kind: "delivered",
359
+ at: stamp(now),
360
+ message_id: id,
361
+ });
362
+ result.delivered++;
363
+ }
364
+ const publishedAt = Date.parse(blob.published_at);
365
+ for (const [id, ack] of projection.acks) {
366
+ if (ack.from_machine !== machine || listed.has(id))
367
+ continue;
368
+ if (publishedAt <= Date.parse(ack.at))
369
+ continue;
370
+ append.push({
371
+ schema: MAIL_LOG_SCHEMA,
372
+ kind: "released",
373
+ at: stamp(now),
374
+ message_id: id,
375
+ });
376
+ result.released++;
377
+ }
378
+ }
379
+ return { append, result: undefined };
380
+ });
381
+ if (opts.publish !== false && result.received + result.delivered + result.released > 0) {
382
+ result.published = publishMail(coordRoot, { now }).status;
383
+ }
384
+ }
385
+ catch {
386
+ /* mail must never break a hook */
387
+ }
388
+ return result;
389
+ }
390
+ /** Hooks import at most once per interval: refs only change when the
391
+ * throttled presence fetch lands, so checking every prompt spawns git for
392
+ * nothing. The stamp is touched first so concurrent hooks do not stampede. */
393
+ function importDue(coordRoot, now) {
394
+ const path = join(mailDir(coordRoot), "import-stamp");
395
+ try {
396
+ if ((now.getTime() - statSync(path).mtimeMs) / 1000 < IMPORT_INTERVAL_SECS)
397
+ return false;
398
+ }
399
+ catch {
400
+ /* no stamp yet → import */
401
+ }
402
+ try {
403
+ mkdirSync(mailDir(coordRoot), { recursive: true });
404
+ writeFileSync(path, "", "utf8");
405
+ utimesSync(path, now, now);
406
+ }
407
+ catch {
408
+ /* best-effort */
409
+ }
410
+ return true;
411
+ }
412
+ /** Inbox append for one envelope. A full inbox leaves the message unacknowledged
413
+ * so the sender keeps offering it; a malformed one is acknowledged and dropped. */
414
+ function receive(inbox, envelope, result) {
415
+ const record = {
416
+ schema: HARNERY_INBOX_RECORD_SCHEMA,
417
+ kind: "message",
418
+ message_id: envelope.message_id,
419
+ sender_instance_id: envelope.sender_instance_id,
420
+ sender_display_name: envelope.sender_display_name,
421
+ sender_machine: envelope.from_machine,
422
+ recipient_instance_id: envelope.recipient_instance_id,
423
+ recipient_display_name: envelope.recipient_display_name,
424
+ created_at: envelope.created_at,
425
+ body: envelope.body,
426
+ body_bytes: envelope.body_bytes,
427
+ };
428
+ try {
429
+ if (inbox.receive(record) === "received")
430
+ result.received++;
431
+ return true;
432
+ }
433
+ catch (error) {
434
+ if (error instanceof HarneryInboxCapacityError && error.reason_code !== "message_body_limit") {
435
+ return false;
436
+ }
437
+ result.rejected++;
438
+ return true;
439
+ }
440
+ }
441
+ /** Senders this machine has received mail from, so a reply can find them even
442
+ * after their machine's presence goes quiet. */
443
+ export function knownRemoteSenders(coordRoot) {
444
+ return [...projectMail(readMailLog(coordRoot)).senders].map(([instance_id, s]) => ({
445
+ instance_id,
446
+ name: s.name,
447
+ machine: s.machine,
448
+ }));
449
+ }
450
+ export function mailStatus(coordRoot) {
451
+ const projection = projectMail(readMailLog(coordRoot));
452
+ return {
453
+ machine: mailMachine(),
454
+ outbox: projection.outbox.map(({ at, envelope }) => ({
455
+ message_id: envelope.message_id,
456
+ to_machine: envelope.to_machine,
457
+ recipient: envelope.recipient_display_name,
458
+ queued_at: at,
459
+ })),
460
+ acks: projection.acks.size,
461
+ delivered: projection.delivered,
462
+ expired: projection.expired,
463
+ };
464
+ }
@@ -265,7 +265,7 @@ export declare const SemanticMeaningV2Schema: import("@sinclair/typebox").TObjec
265
265
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
266
266
  }>>;
267
267
  tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
268
- value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "delivery" | "documentation" | "infrastructure">[]>>;
268
+ value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "delivery" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "documentation" | "infrastructure">[]>>;
269
269
  basis: import("@sinclair/typebox").TLiteral<"model-synthesis" | "prediction">;
270
270
  confidence: import("@sinclair/typebox").TLiteral<"low"> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]>;
271
271
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -325,7 +325,7 @@ export declare const SemanticModelReplyV2Schema: import("@sinclair/typebox").TOb
325
325
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
326
326
  }>>;
327
327
  tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
328
- value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "delivery" | "documentation" | "infrastructure">[]>>;
328
+ value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "delivery" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "documentation" | "infrastructure">[]>>;
329
329
  basis: import("@sinclair/typebox").TLiteral<"model-synthesis" | "prediction">;
330
330
  confidence: import("@sinclair/typebox").TLiteral<"low"> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]>;
331
331
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -391,7 +391,7 @@ export declare function semanticModelReplyV2SchemaFor(expected: {
391
391
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<string>[]>>;
392
392
  }>>;
393
393
  tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
394
- value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "delivery" | "documentation" | "infrastructure">[]>>;
394
+ value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "delivery" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "documentation" | "infrastructure">[]>>;
395
395
  basis: import("@sinclair/typebox").TLiteral<"model-synthesis" | "prediction">;
396
396
  confidence: import("@sinclair/typebox").TLiteral<"low"> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]>;
397
397
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<string>[]>>;
@@ -516,7 +516,7 @@ export declare const SemanticAcceptedReadModelV2Schema: import("@sinclair/typebo
516
516
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
517
517
  }>>;
518
518
  tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
519
- value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "delivery" | "documentation" | "infrastructure">[]>>;
519
+ value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "delivery" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "documentation" | "infrastructure">[]>>;
520
520
  basis: import("@sinclair/typebox").TLiteral<"model-synthesis" | "prediction">;
521
521
  confidence: import("@sinclair/typebox").TLiteral<"low"> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]>;
522
522
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -844,7 +844,7 @@ export declare const SemanticAgentReadModelV2Schema: import("@sinclair/typebox")
844
844
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
845
845
  }>>;
846
846
  tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
847
- value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "delivery" | "documentation" | "infrastructure">[]>>;
847
+ value: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "research" | "delivery" | "waiting" | "planning" | "implementation" | "verification" | "coordination" | "documentation" | "infrastructure">[]>>;
848
848
  basis: import("@sinclair/typebox").TLiteral<"model-synthesis" | "prediction">;
849
849
  confidence: import("@sinclair/typebox").TLiteral<"low"> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]> | import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"low">]>;
850
850
  evidence_event_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -1,5 +1,9 @@
1
1
  /** The sharing mode for this process, resolved once. Tests reset it with `resetStorageSharing`. */
2
2
  export declare function stateSharing(): "private" | "group";
3
+ /** Directory mode for personal state in the user's home, which never follows project sharing. */
4
+ export declare const PERSONAL_DIR_MODE = 448;
5
+ /** File mode for personal state in the user's home, such as a per-install secret key. */
6
+ export declare const PERSONAL_FILE_MODE = 384;
3
7
  export declare function resetStorageSharing(): void;
4
8
  /**
5
9
  * Mode for a directory of project state. Group mode includes setgid: Harnery chmods directories
@@ -1 +1 @@
1
- {"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../src/core/storage/modes.ts"],"names":[],"mappings":"AAgBA,mGAAmG;AACnG,wBAAgB,YAAY,IAAI,SAAS,GAAG,OAAO,CASlD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
1
+ {"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../src/core/storage/modes.ts"],"names":[],"mappings":"AAgBA,mGAAmG;AACnG,wBAAgB,YAAY,IAAI,SAAS,GAAG,OAAO,CASlD;AAED,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,MAAQ,CAAC;AAEvC,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,MAAQ,CAAC;AAExC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}