ftown 0.21.0 → 0.24.0

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 (79) hide show
  1. package/dist/attention-events.d.ts +1 -1
  2. package/dist/attention-store.d.ts +3 -0
  3. package/dist/attention-store.js +25 -1
  4. package/dist/attention-store.js.map +1 -1
  5. package/dist/centrifugo-client.d.ts +14 -1
  6. package/dist/centrifugo-client.js +23 -2
  7. package/dist/centrifugo-client.js.map +1 -1
  8. package/dist/command-rpc.d.ts +2 -0
  9. package/dist/command-rpc.js +55 -9
  10. package/dist/command-rpc.js.map +1 -1
  11. package/dist/identity-reset.d.ts +3 -1
  12. package/dist/identity-reset.js +27 -5
  13. package/dist/identity-reset.js.map +1 -1
  14. package/dist/index.js +15 -5
  15. package/dist/index.js.map +1 -1
  16. package/dist/ledger-pairing-client.d.ts +1 -1
  17. package/dist/ledger-pairing-client.js +5 -3
  18. package/dist/ledger-pairing-client.js.map +1 -1
  19. package/dist/ledger-relay.d.ts +42 -7
  20. package/dist/ledger-relay.js +175 -33
  21. package/dist/ledger-relay.js.map +1 -1
  22. package/dist/ledger-reoffer.d.ts +56 -0
  23. package/dist/ledger-reoffer.js +210 -0
  24. package/dist/ledger-reoffer.js.map +1 -0
  25. package/dist/ledger-startup.d.ts +30 -5
  26. package/dist/ledger-startup.js +40 -10
  27. package/dist/ledger-startup.js.map +1 -1
  28. package/dist/local-api-server.d.ts +3 -0
  29. package/dist/local-api-server.js +68 -6
  30. package/dist/local-api-server.js.map +1 -1
  31. package/dist/mail-delivery.d.ts +26 -3
  32. package/dist/mail-delivery.js +65 -7
  33. package/dist/mail-delivery.js.map +1 -1
  34. package/dist/preview-protocol.d.ts +19 -3
  35. package/dist/preview-protocol.js +14 -0
  36. package/dist/preview-protocol.js.map +1 -1
  37. package/dist/relay-token.d.ts +6 -0
  38. package/dist/relay-token.js +9 -1
  39. package/dist/relay-token.js.map +1 -1
  40. package/dist/security/bridge/ledger-rpc.d.ts +34 -0
  41. package/dist/security/bridge/ledger-rpc.js +58 -0
  42. package/dist/security/bridge/ledger-rpc.js.map +1 -0
  43. package/dist/security/bridge/ledger-store.d.ts +82 -13
  44. package/dist/security/bridge/ledger-store.js +394 -54
  45. package/dist/security/bridge/ledger-store.js.map +1 -1
  46. package/dist/security/bridge/permissions.js +2 -1
  47. package/dist/security/bridge/permissions.js.map +1 -1
  48. package/dist/security/bridge/policy.d.ts +2 -1
  49. package/dist/security/bridge/policy.js.map +1 -1
  50. package/dist/security/bridge/security.d.ts +138 -10
  51. package/dist/security/bridge/security.js +252 -54
  52. package/dist/security/bridge/security.js.map +1 -1
  53. package/dist/security/ledger/announce.d.ts +32 -0
  54. package/dist/security/ledger/announce.js +101 -0
  55. package/dist/security/ledger/announce.js.map +1 -0
  56. package/dist/security/ledger/ledger.d.ts +107 -14
  57. package/dist/security/ledger/ledger.js +364 -83
  58. package/dist/security/ledger/ledger.js.map +1 -1
  59. package/dist/security/ledger/proof.d.ts +12 -0
  60. package/dist/security/ledger/proof.js +40 -8
  61. package/dist/security/ledger/proof.js.map +1 -1
  62. package/dist/security/ledger/protocol.d.ts +14 -1
  63. package/dist/security/ledger/protocol.js.map +1 -1
  64. package/dist/session-controller.d.ts +9 -3
  65. package/dist/session-controller.js +120 -20
  66. package/dist/session-controller.js.map +1 -1
  67. package/dist/session-inbox-protocol.d.ts +2 -0
  68. package/dist/session-preview.d.ts +20 -5
  69. package/dist/session-preview.js +185 -37
  70. package/dist/session-preview.js.map +1 -1
  71. package/dist/session-store.js +19 -1
  72. package/dist/session-store.js.map +1 -1
  73. package/dist/sessions-commands.js +41 -7
  74. package/dist/sessions-commands.js.map +1 -1
  75. package/dist/types.d.ts +1 -1
  76. package/opencode-plugin/ftown.js +15 -2
  77. package/package.json +2 -2
  78. package/skills/ftown/SKILL.md +18 -7
  79. package/skills/ftown/references/sessions.md +35 -10
@@ -0,0 +1,210 @@
1
+ export const REOFFER_PAIR_WINDOW_MS = 30_000;
2
+ /** At most one POST per this interval, whatever the (our head, server head) pair. */
3
+ export const REOFFER_MIN_POST_SPACING_MS = 30_000;
4
+ /** After a 404 (the server does not know the account), nothing is posted for this long unless a read finds the account again. */
5
+ export const REOFFER_UNKNOWN_ACCOUNT_PAUSE_MS = 3_600_000;
6
+ export const REOFFER_FIRST_BACKOFF_MS = 30_000;
7
+ export const REOFFER_MAX_BACKOFF_MS = 600_000;
8
+ export const REOFFER_MAX_RETRY_AFTER_MS = 3_600_000;
9
+ /** Each backoff wait is multiplied by 1 - REOFFER_JITTER + 2 * REOFFER_JITTER * random(). */
10
+ export const REOFFER_JITTER = 0.2;
11
+ /** The backoff wait after `failures` failed attempts in a row (>= 1), for a given random() draw. */
12
+ export function reofferBackoffMs(failures, random) {
13
+ const base = Math.min(REOFFER_FIRST_BACKOFF_MS * 2 ** Math.max(0, failures - 1), REOFFER_MAX_BACKOFF_MS);
14
+ return Math.round(base * (1 - REOFFER_JITTER + 2 * REOFFER_JITTER * random));
15
+ }
16
+ const messageOf = (error) => error instanceof Error ? error.message : String(error);
17
+ const field = (body, key) => typeof body === 'object' && body !== null && !Array.isArray(body) ? body[key] : undefined;
18
+ export function createLedgerReoffer(deps) {
19
+ let running = null;
20
+ let failures = 0;
21
+ /** Monotonic time before which no attempt starts (after a failure). */
22
+ let readyAt = Number.NEGATIVE_INFINITY;
23
+ let cancelTimer = null;
24
+ let stopped = false;
25
+ /** The server said it does not know this account: no attempt before this monotonic time (unless a read finds the account). */
26
+ let pausedUntil = Number.NEGATIVE_INFINITY;
27
+ /** The last POST (any pair), for the global floor. */
28
+ let lastPostAt = Number.NEGATIVE_INFINITY;
29
+ /** The last POST was answered 2xx: the next read must no longer show the server behind, else that counts as a failure. */
30
+ let confirming = false;
31
+ /** (our head, server head) -> when it was last posted. */
32
+ const sent = new Map();
33
+ /** Local heads whose ledger_rejected answer was logged. */
34
+ const rejectedLogged = new Set();
35
+ const cancel = () => { cancelTimer?.(); cancelTimer = null; };
36
+ const paused = () => deps.now() < pausedUntil;
37
+ /** (Re)arms the one retry timer: an attempt `ms` from now, unless one is running then. */
38
+ const arm = (ms) => {
39
+ cancel();
40
+ if (stopped)
41
+ return;
42
+ cancelTimer = deps.schedule(() => { cancelTimer = null; if (running === null)
43
+ void start(); }, Math.max(0, ms));
44
+ };
45
+ const settled = (outcome) => {
46
+ failures = 0;
47
+ readyAt = Number.NEGATIVE_INFINITY;
48
+ cancel();
49
+ return outcome;
50
+ };
51
+ const failed = (reason, retryAfterMs = null, quiet = false) => {
52
+ failures += 1;
53
+ let wait = reofferBackoffMs(failures, deps.random());
54
+ if (retryAfterMs !== null && retryAfterMs > wait)
55
+ wait = Math.min(retryAfterMs, REOFFER_MAX_RETRY_AFTER_MS);
56
+ readyAt = deps.now() + wait;
57
+ // The first failure at each larger step is logged; once the wait stops growing, the bridge stays quiet.
58
+ const grew = failures === 1 || reofferBackoffMs(failures, 0.5) > reofferBackoffMs(failures - 1, 0.5);
59
+ if (!quiet && grew)
60
+ deps.log(`[ledger] could not send our account history to the server (${reason}); retrying in ${Math.round(wait / 1000)} s`);
61
+ arm(wait);
62
+ return 'failed';
63
+ };
64
+ const unknownAccount = () => {
65
+ if (!paused())
66
+ deps.log('[ledger] the server does not know this account (404); this computer will not send its account history to it for an hour');
67
+ pausedUntil = deps.now() + REOFFER_UNKNOWN_ACCOUNT_PAUSE_MS;
68
+ confirming = false;
69
+ failures = 0;
70
+ readyAt = Number.NEGATIVE_INFINITY;
71
+ cancel();
72
+ return 'stopped';
73
+ };
74
+ const offerQuietly = async (ledger) => {
75
+ try {
76
+ return await deps.offer(ledger);
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ };
82
+ const send = async (serverHead) => {
83
+ const local = await deps.local();
84
+ if (local === null)
85
+ return settled('not_behind');
86
+ const now = deps.now();
87
+ for (const [key, at] of sent)
88
+ if (now < at || now - at >= REOFFER_PAIR_WINDOW_MS)
89
+ sent.delete(key);
90
+ const pair = `${local.head}\n${serverHead}`;
91
+ const pairAt = sent.get(pair);
92
+ // Both floors, after the fresh read: this pair at most once per 30 s, and any POST at most once per 30 s.
93
+ const floorAt = now >= lastPostAt ? lastPostAt + REOFFER_MIN_POST_SPACING_MS : Number.NEGATIVE_INFINITY;
94
+ const until = Math.max(pairAt === undefined ? Number.NEGATIVE_INFINITY : pairAt + REOFFER_PAIR_WINDOW_MS, floorAt);
95
+ if (until > now) {
96
+ // A retry after a failure can come back before a floor ends (the first wait may be 24 s): try again when it does.
97
+ if (failures > 0)
98
+ arm(until - now);
99
+ return 'skipped';
100
+ }
101
+ sent.set(pair, now);
102
+ lastPostAt = now;
103
+ let answer;
104
+ try {
105
+ answer = await deps.post(local.ledger, serverHead);
106
+ }
107
+ catch (error) {
108
+ return failed(messageOf(error));
109
+ }
110
+ if (stopped)
111
+ return 'stopped';
112
+ const { status, body } = answer;
113
+ const error = field(body, 'error');
114
+ const chain = field(body, 'ledger');
115
+ const answered = typeof chain === 'object' && chain !== null;
116
+ if (status >= 200 && status < 300) {
117
+ if (answered)
118
+ await offerQuietly(chain);
119
+ // Not settled yet: the next read must show the server holding our chain.
120
+ confirming = true;
121
+ return 'posted';
122
+ }
123
+ if (status === 404)
124
+ return unknownAccount();
125
+ if (status === 409 && error === 'head_changed') {
126
+ // Another write landed since our read: judge the server's chain; if it is still a prefix of ours, the retry reads and sends again.
127
+ const outcome = answered ? await offerQuietly(chain) : null;
128
+ if (outcome !== null && outcome.decision !== 'reject' && outcome.relation !== 'behind')
129
+ return settled('not_behind');
130
+ return failed('head_changed');
131
+ }
132
+ if (status === 409 && error === 'diverged') {
133
+ if (answered)
134
+ await offerQuietly(chain);
135
+ return failed('the server holds a different history');
136
+ }
137
+ if (status === 409 && error === 'ledger_rejected') {
138
+ const first = !rejectedLogged.has(local.head);
139
+ rejectedLogged.add(local.head);
140
+ if (first)
141
+ deps.log('[ledger] the server rejected our account history; retrying later');
142
+ return failed('ledger_rejected', null, true);
143
+ }
144
+ if (status === 429)
145
+ return failed('rate limited', answer.retryAfterMs);
146
+ return failed(`HTTP ${status}`);
147
+ };
148
+ const attempt = async () => {
149
+ if (stopped)
150
+ return 'stopped';
151
+ if (paused())
152
+ return 'stopped';
153
+ const read = await deps.read();
154
+ if (stopped)
155
+ return 'stopped';
156
+ if (read.kind === 'unknown_account')
157
+ return unknownAccount();
158
+ if (read.kind === 'failed')
159
+ return failed(`read failed: ${read.reason}`);
160
+ // The account is there again.
161
+ pausedUntil = Number.NEGATIVE_INFINITY;
162
+ const { relation, head } = read.outcome;
163
+ const wasConfirming = confirming;
164
+ confirming = false;
165
+ if (relation !== 'behind' || head === null)
166
+ return settled('not_behind');
167
+ // The server answered 2xx to our last POST, yet still lacks our records: a hostile or broken server, not progress.
168
+ if (wasConfirming)
169
+ return failed('the server accepted our history but still lacks it');
170
+ return send(head);
171
+ };
172
+ const start = () => {
173
+ const run = attempt().catch((error) => failed(messageOf(error)));
174
+ running = run.finally(() => { running = null; });
175
+ return running;
176
+ };
177
+ return {
178
+ noteServerBehind() {
179
+ if (stopped)
180
+ return;
181
+ // The caller's read found the account: a 404 pause is over.
182
+ pausedUntil = Number.NEGATIVE_INFINITY;
183
+ if (running !== null || cancelTimer !== null)
184
+ return;
185
+ const wait = readyAt - deps.now();
186
+ if (wait > 0)
187
+ arm(wait);
188
+ else
189
+ void start();
190
+ },
191
+ check() {
192
+ if (stopped || paused())
193
+ return Promise.resolve('stopped');
194
+ if (running !== null)
195
+ return running;
196
+ if (readyAt - deps.now() > 0) {
197
+ if (cancelTimer === null)
198
+ arm(readyAt - deps.now());
199
+ return Promise.resolve('deferred');
200
+ }
201
+ cancel();
202
+ return start();
203
+ },
204
+ stop() {
205
+ stopped = true;
206
+ cancel();
207
+ },
208
+ };
209
+ }
210
+ //# sourceMappingURL=ledger-reoffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-reoffer.js","sourceRoot":"","sources":["../src/ledger-reoffer.ts"],"names":[],"mappings":"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"}
@@ -6,9 +6,22 @@ import type { EndpointPublic } from './security/protocol.js';
6
6
  export declare const LEGACY_ACCOUNT_RESET_MESSAGE = "[Bridge] Old account data from a previous ftown version was cleared. Pair this computer again with the link below.";
7
7
  export declare const NOT_IN_LEDGER_MESSAGE = "This computer is not in your account's ledger. Run `ftown start --new-identity` to pair it again.";
8
8
  export declare const PAIRING_REJECTED_HINT = "Pairing did not complete. Run `ftown start` to try again.";
9
+ export declare const PAIRING_REVOKED_MESSAGE: string;
9
10
  export declare const SERVER_LEDGER_MISMATCH_MESSAGE = "[Bridge] The server's account ledger does not list this computer. If it was removed from your account, run `ftown start --new-identity` to pair it again.";
10
11
  export declare const UPDATE_RESTART_UNPAIRED_MESSAGE = "[Bridge] An update restart cannot pair this computer. Run `ftown start` to pair it.";
11
12
  export declare const UPDATED_TO_LEDGER_MESSAGE = "[Bridge] ftown was updated to the passkey ledger; run `ftown start` to pair this computer.";
13
+ export declare const DO_NOT_RESET_LINE = "[Bridge] Do NOT run --new-identity: this computer may hold the only copy. Open Security in the dashboard to choose a history.";
14
+ export declare const HELD_OFFLINE_LINE = "[Bridge] Starting offline: local sessions keep working, and the relay is retried in the background.";
15
+ export declare const HELD_NO_RELAY_URL_LINE = "[Bridge] No relay URL was saved by an earlier start, so this computer cannot start offline. Run `ftown start` again later.";
16
+ /** What this computer holds that the server lacks (BridgeSecurityService.uniqueHistory), for the held-history notice. */
17
+ export interface HeldHistory {
18
+ afterRevision: number;
19
+ records: number;
20
+ removals: number;
21
+ passkeys: number;
22
+ }
23
+ /** Printed once when the server refuses the key of a computer that is an active member of its own history (step 5): never reset advice. */
24
+ export declare function heldHistoryNotice(unique: HeldHistory | null): string[];
12
25
  /** Only account bridges use the passkey ledger (legacy reset, pairing, ledger replica, relay token by proof). */
13
26
  export declare function usesLedger(mode: BridgeMode): boolean;
14
27
  export interface IdentityResetDeps {
@@ -65,7 +78,8 @@ export interface AccountStartupDeps {
65
78
  /** False during an update restart: it must come back with its existing identity and never pair. */
66
79
  allowPairing: boolean;
67
80
  /** One pairing attempt (runLedgerPairing) with the unbound identity and the empty store; binds both on success. Throws
68
- * Error('pairing_expired') (its hint already printed) or Error('pairing_rejected'). */
81
+ * Error('pairing_expired') (its hint already printed), Error('pairing_revoked') (the account already revokes this bridge id) or
82
+ * Error('pairing_rejected'). */
69
83
  pair: (identity: LedgerBridgeIdentity, store: BridgeLedgerStore) => Promise<unknown>;
70
84
  /** The server's ledger for this account (GET /api/account/ledger?accountId=…, bounded). */
71
85
  fetchLedger: (accountId: string) => Promise<unknown>;
@@ -96,23 +110,31 @@ export type RelayStartup = {
96
110
  token: string;
97
111
  relayUrl: string;
98
112
  }
99
- /** The server is unreachable for now: start with the saved relay URL and keep retrying in the background. */
113
+ /** The server is unreachable for now, or refused a computer that may hold the only copy of its history (`notice`, printed once instead
114
+ * of the unreachable line): start with the saved relay URL and keep retrying in the background. */
100
115
  | {
101
116
  kind: 'offline';
102
117
  relayUrl: string;
103
118
  reason: string;
119
+ notice?: string[];
104
120
  };
105
121
  export declare function offlineWithoutRelayUrlMessage(reason: string): string;
106
122
  /**
107
- * Step 5, the first relay token. A proven revocation (the token hooks offer the server's evidence first) wins over everything; a
108
- * refusal (401/403) exits; a temporary failure (network, timeout, 5xx, cooldown) starts offline with the saved relay URL, and exits
109
- * only when there is none. After a successful token the revocation is checked again: the token response's ledger may prove it.
123
+ * Step 5, the first relay token. A proven revocation (the token hooks offer the server's evidence first) wins over everything. A refusal
124
+ * (401/403) of a computer that is still an active member of its own history (`held` returns the notice: a divergence with the server, a
125
+ * server behind us, a failed re-offer, or any refusal without proof of our removal) starts OFFLINE and prints the notice once, never the
126
+ * reset advice: this computer may hold the only copy. Any other refusal (this computer is not an active member: unpaired/absent) exits
127
+ * with the reset advice. A temporary failure (network, timeout, 5xx, cooldown) starts offline with the saved relay URL. Without a saved
128
+ * relay URL there is no offline start: exit. After a successful token the revocation is checked again (the token response's ledger may
129
+ * prove it).
110
130
  */
111
131
  export declare function startRelay(input: {
112
132
  getToken: () => Promise<string>;
113
133
  relayUrl: () => string | null;
114
134
  bridgeRevoked: () => boolean;
115
135
  apiUrl: string;
136
+ /** The held-history notice when this computer is an active member of its own history (heldHistoryNotice), else null. */
137
+ held?: () => string[] | null;
116
138
  }): Promise<RelayStartup>;
117
139
  export declare function endpointRefusedMessage(reason: string): string;
118
140
  export type RegistrationAttempt = {
@@ -133,6 +155,9 @@ export interface EndpointRegistrarDeps {
133
155
  cooldownRemainingMs(): number;
134
156
  };
135
157
  bridgeRevoked: () => boolean;
158
+ /** This computer is an active member of its own history: a refusal is then not final (it may hold the only copy; the startup notice
159
+ * said so): no reset advice, keep retrying with the backoff until the server accepts the key again. */
160
+ held?: () => boolean;
136
161
  log: (line: string) => void;
137
162
  /** Returns a cancel function. Defaults to an unref'd setTimeout. */
138
163
  schedule?: (run: () => void, ms: number) => () => void;
@@ -12,9 +12,23 @@ import { MISSING_RELAY_URL_MESSAGE, isRefusal, relayTokenFailureMessage } from '
12
12
  export const LEGACY_ACCOUNT_RESET_MESSAGE = '[Bridge] Old account data from a previous ftown version was cleared. Pair this computer again with the link below.';
13
13
  export const NOT_IN_LEDGER_MESSAGE = "This computer is not in your account's ledger. Run `ftown start --new-identity` to pair it again.";
14
14
  export const PAIRING_REJECTED_HINT = 'Pairing did not complete. Run `ftown start` to try again.';
15
+ export const PAIRING_REVOKED_MESSAGE = "[Bridge] This computer's id was removed from the account before pairing completed. "
16
+ + 'Run `ftown start --new-identity` to pair it with a new id.';
15
17
  export const SERVER_LEDGER_MISMATCH_MESSAGE = "[Bridge] The server's account ledger does not list this computer. If it was removed from your account, run `ftown start --new-identity` to pair it again.";
16
18
  export const UPDATE_RESTART_UNPAIRED_MESSAGE = '[Bridge] An update restart cannot pair this computer. Run `ftown start` to pair it.';
17
19
  export const UPDATED_TO_LEDGER_MESSAGE = '[Bridge] ftown was updated to the passkey ledger; run `ftown start` to pair this computer.';
20
+ export const DO_NOT_RESET_LINE = '[Bridge] Do NOT run --new-identity: this computer may hold the only copy. Open Security in the dashboard to choose a history.';
21
+ export const HELD_OFFLINE_LINE = '[Bridge] Starting offline: local sessions keep working, and the relay is retried in the background.';
22
+ export const HELD_NO_RELAY_URL_LINE = '[Bridge] No relay URL was saved by an earlier start, so this computer cannot start offline. Run `ftown start` again later.';
23
+ const plural = (count, word) => `${count} ${word}${count === 1 ? '' : 's'}`;
24
+ /** Printed once when the server refuses the key of a computer that is an active member of its own history (step 5): never reset advice. */
25
+ export function heldHistoryNotice(unique) {
26
+ const detail = unique === null || unique.records === 0
27
+ ? 'this computer holds changes the server does not have'
28
+ : `this computer holds account history the server does not have: ${plural(unique.removals, 'removal')} and ${plural(unique.passkeys, 'passkey')} `
29
+ + `among ${plural(unique.records, 'change')} after revision ${unique.afterRevision}`;
30
+ return [`[Bridge] The ftown server refused this computer's key, and ${detail}.`, DO_NOT_RESET_LINE];
31
+ }
18
32
  /** Only account bridges use the passkey ledger (legacy reset, pairing, ledger replica, relay token by proof). */
19
33
  export function usesLedger(mode) {
20
34
  return mode === 'account';
@@ -74,7 +88,7 @@ export async function startAccountIdentity(deps) {
74
88
  if (!deps.allowPairing)
75
89
  return { kind: 'exit', message: UPDATE_RESTART_UNPAIRED_MESSAGE };
76
90
  try {
77
- await deps.pair(keys, new BridgeLedgerStore(deps.dataDir));
91
+ await deps.pair(keys, new BridgeLedgerStore(deps.dataDir, { bridgeId: deps.bridgeId }));
78
92
  }
79
93
  catch (error) {
80
94
  const code = messageOf(error);
@@ -82,13 +96,16 @@ export async function startAccountIdentity(deps) {
82
96
  return { kind: 'exit', message: null };
83
97
  if (code === 'pairing_rejected')
84
98
  return { kind: 'exit', message: PAIRING_REJECTED_HINT };
99
+ // Not a removal of a paired bridge: nothing is reset; the owner picks a new id.
100
+ if (code === 'pairing_revoked')
101
+ return { kind: 'exit', message: PAIRING_REVOKED_MESSAGE };
85
102
  throw error;
86
103
  }
87
104
  }
88
105
  const identity = await readIdentity(deps.dataDir);
89
106
  if (!bound(identity))
90
107
  return { kind: 'exit', message: PAIRING_REJECTED_HINT };
91
- const store = new BridgeLedgerStore(deps.dataDir, { accountId: identity.accountId });
108
+ const store = new BridgeLedgerStore(deps.dataDir, { accountId: identity.accountId, bridgeId: deps.bridgeId });
92
109
  if (reconciled === 'needs_ledger') {
93
110
  try {
94
111
  await store.adoptBound(await deps.fetchLedger(identity.accountId));
@@ -99,7 +116,9 @@ export async function startAccountIdentity(deps) {
99
116
  return { kind: 'exit', message: ledgerUnavailableMessage(messageOf(error)) };
100
117
  }
101
118
  }
102
- const action = ownStatusAction(await store.ownBridgeStatus(deps.bridgeId, identity.authPublicKey));
119
+ const action = ownStatusAction(await store.ownBridgeStatus({
120
+ bridgeId: deps.bridgeId, endpointId: identity.endpoint.id, endpointPublicKey: identity.endpoint.publicKey, authPublicKey: identity.authPublicKey,
121
+ }));
103
122
  if (action === 'revoked_restart')
104
123
  return { kind: 'revoked' };
105
124
  if (action === 'exit_absent')
@@ -110,9 +129,13 @@ export function offlineWithoutRelayUrlMessage(reason) {
110
129
  return `[Bridge] Could not reach the ftown server (${reason}) and no relay URL was saved by an earlier start. Check the connection and run \`ftown start\` again.`;
111
130
  }
112
131
  /**
113
- * Step 5, the first relay token. A proven revocation (the token hooks offer the server's evidence first) wins over everything; a
114
- * refusal (401/403) exits; a temporary failure (network, timeout, 5xx, cooldown) starts offline with the saved relay URL, and exits
115
- * only when there is none. After a successful token the revocation is checked again: the token response's ledger may prove it.
132
+ * Step 5, the first relay token. A proven revocation (the token hooks offer the server's evidence first) wins over everything. A refusal
133
+ * (401/403) of a computer that is still an active member of its own history (`held` returns the notice: a divergence with the server, a
134
+ * server behind us, a failed re-offer, or any refusal without proof of our removal) starts OFFLINE and prints the notice once, never the
135
+ * reset advice: this computer may hold the only copy. Any other refusal (this computer is not an active member: unpaired/absent) exits
136
+ * with the reset advice. A temporary failure (network, timeout, 5xx, cooldown) starts offline with the saved relay URL. Without a saved
137
+ * relay URL there is no offline start: exit. After a successful token the revocation is checked again (the token response's ledger may
138
+ * prove it).
116
139
  */
117
140
  export async function startRelay(input) {
118
141
  let token;
@@ -122,8 +145,15 @@ export async function startRelay(input) {
122
145
  catch (error) {
123
146
  if (input.bridgeRevoked())
124
147
  return { kind: 'revoked' };
125
- if (isRefusal(error))
126
- return { kind: 'exit', message: relayTokenFailureMessage(error, input.apiUrl) };
148
+ if (isRefusal(error)) {
149
+ const notice = input.held?.() ?? null;
150
+ if (notice === null)
151
+ return { kind: 'exit', message: relayTokenFailureMessage(error, input.apiUrl) };
152
+ const savedUrl = input.relayUrl();
153
+ if (savedUrl === null)
154
+ return { kind: 'exit', message: [...notice, HELD_NO_RELAY_URL_LINE].join('\n') };
155
+ return { kind: 'offline', relayUrl: savedUrl, reason: messageOf(error), notice: [...notice, HELD_OFFLINE_LINE] };
156
+ }
127
157
  const reason = messageOf(error);
128
158
  const relayUrl = input.relayUrl();
129
159
  return relayUrl === null ? { kind: 'exit', message: offlineWithoutRelayUrlMessage(reason) } : { kind: 'offline', relayUrl, reason };
@@ -161,7 +191,7 @@ export function createEndpointRegistrar(deps) {
161
191
  catch (error) {
162
192
  if (stopped)
163
193
  return;
164
- if (isRefusal(error)) {
194
+ if (isRefusal(error) && !deps.held?.()) {
165
195
  // A verified revocation restarts the bridge on its own; anything else needs the owner.
166
196
  if (!deps.bridgeRevoked())
167
197
  deps.log(endpointRefusedMessage(messageOf(error)));
@@ -181,7 +211,7 @@ export function createEndpointRegistrar(deps) {
181
211
  catch (error) {
182
212
  if (deps.bridgeRevoked())
183
213
  return { kind: 'revoked' };
184
- if (isRefusal(error))
214
+ if (isRefusal(error) && !deps.held?.())
185
215
  return { kind: 'exit', message: endpointRefusedMessage(messageOf(error)) };
186
216
  deps.log(`[Bridge] Could not register this computer's encrypted endpoint yet (${messageOf(error)}); retrying in the background.`);
187
217
  return { kind: 'retry' };
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-startup.js","sourceRoot":"","sources":["../src/ledger-startup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ledger-startup.js","sourceRoot":"","sources":["../src/ledger-startup.ts"],"names":[],"mappings":"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"}
@@ -73,6 +73,9 @@ export declare class LocalApiServer extends EventEmitter<HookServerEvents> {
73
73
  private localEventBytes;
74
74
  private readonly localEventWaiters;
75
75
  setSecurity(security: BridgeSecurityService, info: BridgeSecurityInfo, command: (message: VerifiedApplication) => Promise<ApplicationJson>, observer?: SecureExecutionObserver): void;
76
+ /** GET /api/security/info: the static info, plus in account mode `ledger: { divergence, serverBehind, removedOnOtherHistory }` (CONTRACT §5: a computer
77
+ * holding a different account history is shown with a warning). */
78
+ private securityInfoPayload;
76
79
  recordPublication(channel: string, data: unknown): void;
77
80
  dispatchSecure(message: VerifiedApplication, clientGone?: AbortSignal): Promise<ApplicationJson>;
78
81
  private port;
@@ -137,6 +137,15 @@ export class LocalApiServer extends EventEmitter {
137
137
  this.secureCommand = command;
138
138
  this.secureObserver = observer;
139
139
  }
140
+ /** GET /api/security/info: the static info, plus in account mode `ledger: { divergence, serverBehind, removedOnOtherHistory }` (CONTRACT §5: a computer
141
+ * holding a different account history is shown with a warning). */
142
+ securityInfoPayload() {
143
+ const info = this.securityInfo;
144
+ if (!this.security?.options.ledger)
145
+ return info;
146
+ const status = this.security.ledgerStatus();
147
+ return { ...info, ledger: { divergence: status.divergence, serverBehind: status.serverBehind, removedOnOtherHistory: status.removedOnOtherHistory } };
148
+ }
140
149
  recordPublication(channel, data) {
141
150
  const item = { channel, data: applicationJson(data), cursor: ++this.localEventSequence };
142
151
  this.localEvents.push(item);
@@ -459,7 +468,7 @@ export class LocalApiServer extends EventEmitter {
459
468
  return;
460
469
  }
461
470
  if (path === '/api/security/info' && req.method === 'GET') {
462
- jsonResponse(res, 200, this.securityInfo);
471
+ jsonResponse(res, 200, this.securityInfoPayload());
463
472
  return;
464
473
  }
465
474
  if (req.method === 'POST' && (path === '/api/security/envelope' || path === '/api/security/events')) {
@@ -830,11 +839,42 @@ export class LocalApiServer extends EventEmitter {
830
839
  return;
831
840
  }
832
841
  const body = await parseBody(req);
833
- if (req.method === 'PUT' ? Object.keys(body).length !== 1 || typeof body.path !== 'string' : Object.keys(body).length !== 0) {
842
+ if (req.method === 'PUT') {
843
+ if (Object.keys(body).some((key) => key !== 'paths' && key !== 'mode') || !('paths' in body) || !Array.isArray(body.paths) || body.paths.length < 1 || body.paths.length > 12 || body.paths.some((entry) => typeof entry !== 'string' || entry.length === 0)) {
844
+ jsonResponse(res, 400, { error: 'Invalid preview request' });
845
+ return;
846
+ }
847
+ const mode = body.mode;
848
+ if (mode !== undefined && mode !== 'replace' && mode !== 'add') {
849
+ jsonResponse(res, 400, { error: 'Invalid preview request' });
850
+ return;
851
+ }
852
+ const result = mode === 'add' ? await controller.addPreview(sessionPreviewMatch[1], body.paths) : await controller.setPreview(sessionPreviewMatch[1], body.paths);
853
+ if (!result.ok) {
854
+ jsonResponse(res, result.code === 'not_found' ? 404 : result.code === 'too_large' ? 413 : 400, { error: result.message });
855
+ return;
856
+ }
857
+ jsonResponse(res, 200, { session: toWireSession(result.session) });
858
+ return;
859
+ }
860
+ if (Object.keys(body).some((key) => key !== 'names')) {
861
+ jsonResponse(res, 400, { error: 'Invalid preview request' });
862
+ return;
863
+ }
864
+ if (body.names === undefined || (Array.isArray(body.names) && body.names.length === 0)) {
865
+ const result = await controller.clearPreview(sessionPreviewMatch[1]);
866
+ if (!result.ok) {
867
+ jsonResponse(res, result.code === 'not_found' ? 404 : 400, { error: result.message });
868
+ return;
869
+ }
870
+ jsonResponse(res, 200, { session: toWireSession(result.session) });
871
+ return;
872
+ }
873
+ if (!Array.isArray(body.names) || body.names.some((entry) => typeof entry !== 'string' || entry.length === 0)) {
834
874
  jsonResponse(res, 400, { error: 'Invalid preview request' });
835
875
  return;
836
876
  }
837
- const result = req.method === 'PUT' ? await controller.setPreview(sessionPreviewMatch[1], body.path) : await controller.clearPreview(sessionPreviewMatch[1]);
877
+ const result = await controller.removePreview(sessionPreviewMatch[1], body.names);
838
878
  if (!result.ok) {
839
879
  jsonResponse(res, result.code === 'not_found' ? 404 : result.code === 'too_large' ? 413 : 400, { error: result.message });
840
880
  return;
@@ -895,6 +935,7 @@ export class LocalApiServer extends EventEmitter {
895
935
  return;
896
936
  }
897
937
  await controller.remove(sessionId);
938
+ this.mail.markSessionEnded(sessionId);
898
939
  jsonResponse(res, 200, { removed: true, sessionId });
899
940
  return;
900
941
  }
@@ -1357,6 +1398,18 @@ export class LocalApiServer extends EventEmitter {
1357
1398
  }
1358
1399
  return;
1359
1400
  }
1401
+ if (payload.hook_event_name === 'FtownWakeSubmitted') {
1402
+ // Opencode announces the wake before submitting it (same 200 either way).
1403
+ // Record only: never forwarded to the dashboard, never attention.
1404
+ const submittedId = typeof payload.ftown_session_id === 'string' ? payload.ftown_session_id : undefined;
1405
+ const submittedSource = typeof payload.ftown_session_source === 'string' ? payload.ftown_session_source : undefined;
1406
+ if (submittedId && submittedSource !== 'workspace' && /^[a-zA-Z0-9_-]+$/.test(submittedId)
1407
+ && await this.store?.loadSession(submittedId)) {
1408
+ this.mail.notePluginHandoff(submittedId);
1409
+ }
1410
+ jsonResponse(res, 200, { ok: true });
1411
+ return;
1412
+ }
1360
1413
  const hookEventName = typeof payload.hook_event_name === 'string' ? payload.hook_event_name : undefined;
1361
1414
  let ftownSessionId = typeof payload.ftown_session_id === 'string' ? payload.ftown_session_id : undefined;
1362
1415
  let source = typeof payload.ftown_session_source === 'string' ? payload.ftown_session_source : undefined;
@@ -1369,22 +1422,31 @@ export class LocalApiServer extends EventEmitter {
1369
1422
  jsonResponse(res, 200, { ok: true });
1370
1423
  return;
1371
1424
  }
1372
- const { ftown_session_id: _, ftown_session_source: __, attentionId: _attentionId, attentionSequence: _attentionSequence, attentionCreatedAt: _attentionCreatedAt, ...rest } = payload;
1425
+ const { ftown_session_id: _, ftown_session_source: __, attentionId: _attentionId, attentionSequence: _attentionSequence, attentionCreatedAt: _attentionCreatedAt, ...restWithInjected } = payload;
1426
+ // Never trust a harness-supplied injected flag.
1427
+ const { injected: _incomingInjected, ...rest } = restWithInjected;
1428
+ const isPromptSubmit = hookEventName === 'UserPromptSubmit' || hookEventName === 'beforeSubmitPrompt';
1429
+ const injected = isPromptSubmit && this.mail.isInjectedPrompt(ftownSessionId, rest);
1373
1430
  let committedAttention = undefined;
1374
1431
  // Workspace fallback can identify a foreign agent in the same directory.
1375
1432
  // Only known explicit/native-attributed sessions may produce authoritative records.
1376
1433
  if (this.attention && source !== 'workspace' && /^[a-zA-Z0-9_-]+$/.test(ftownSessionId) && await this.store?.loadSession(ftownSessionId)) {
1377
- committedAttention = await this.attention.record(ftownSessionId, hookEventName, rest);
1434
+ committedAttention = await this.attention.record(ftownSessionId, hookEventName, rest, isPromptSubmit ? { promptOrigin: injected ? 'injected' : 'human' } : undefined);
1378
1435
  }
1379
1436
  if (hookEventName === 'UserPromptSubmit' || hookEventName === 'PreToolUse' || hookEventName === 'PostToolUse')
1380
1437
  this.mail.markAgentBusy(ftownSessionId);
1381
- else if (hookEventName === 'Stop' || hookEventName === 'SessionEnd')
1438
+ else if (hookEventName === 'Stop')
1382
1439
  this.mail.markAgentIdle(ftownSessionId);
1440
+ else if (hookEventName === 'SessionEnd') {
1441
+ this.mail.markAgentIdle(ftownSessionId);
1442
+ this.mail.markSessionEnded(ftownSessionId);
1443
+ }
1383
1444
  const conversationId = payload.conversation_id;
1384
1445
  if (typeof conversationId === 'string' && conversationId && source !== 'workspace')
1385
1446
  registerSessionConversation(ftownSessionId, conversationId);
1386
1447
  const hookEvent = { sessionId: ftownSessionId, eventName: hookEventName, data: {
1387
1448
  ...rest,
1449
+ ...(injected ? { injected: true } : {}),
1388
1450
  ...(committedAttention ? { attentionId: committedAttention.id, attentionSequence: committedAttention.sequence, attentionCreatedAt: committedAttention.createdAt } : {}),
1389
1451
  }, source };
1390
1452
  // Persistence is complete before listeners can publish and before HTTP success.