@threadbase-sh/streamer 1.63.0 → 1.65.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.
- package/dist/agent/agent-client.d.ts +14 -0
- package/dist/agent/agent-client.d.ts.map +1 -0
- package/dist/agent/agent-config.d.ts +24 -0
- package/dist/agent/agent-config.d.ts.map +1 -0
- package/dist/agent/conversation-writer.d.ts +13 -0
- package/dist/agent/conversation-writer.d.ts.map +1 -0
- package/dist/agent/dedupe.d.ts +6 -0
- package/dist/agent/dedupe.d.ts.map +1 -0
- package/dist/agent/errors.d.ts +23 -0
- package/dist/agent/errors.d.ts.map +1 -0
- package/dist/agent/handle-send-agent-input.d.ts +21 -0
- package/dist/agent/handle-send-agent-input.d.ts.map +1 -0
- package/dist/agent/handle-start-agent-session.d.ts +21 -0
- package/dist/agent/handle-start-agent-session.d.ts.map +1 -0
- package/dist/agent/history-mapper.d.ts +4 -0
- package/dist/agent/history-mapper.d.ts.map +1 -0
- package/dist/agent/payload-guard.d.ts +29 -0
- package/dist/agent/payload-guard.d.ts.map +1 -0
- package/dist/api/app.d.ts +28 -0
- package/dist/api/app.d.ts.map +1 -0
- package/dist/api/handlers/conversations.handlers.d.ts +69 -0
- package/dist/api/handlers/conversations.handlers.d.ts.map +1 -0
- package/dist/api/handlers/http-helpers.d.ts +50 -0
- package/dist/api/handlers/http-helpers.d.ts.map +1 -0
- package/dist/api/handlers/sessions.handlers.d.ts +236 -0
- package/dist/api/handlers/sessions.handlers.d.ts.map +1 -0
- package/dist/api/middleware/auth.middleware.d.ts +5 -0
- package/dist/api/middleware/auth.middleware.d.ts.map +1 -0
- package/dist/api/middleware/cors.middleware.d.ts +5 -0
- package/dist/api/middleware/cors.middleware.d.ts.map +1 -0
- package/dist/api/middleware/error.middleware.d.ts +4 -0
- package/dist/api/middleware/error.middleware.d.ts.map +1 -0
- package/dist/api/routes/backup.routes.d.ts +5 -0
- package/dist/api/routes/backup.routes.d.ts.map +1 -0
- package/dist/api/routes/browse.routes.d.ts +5 -0
- package/dist/api/routes/browse.routes.d.ts.map +1 -0
- package/dist/api/routes/cacheAlert.routes.d.ts +5 -0
- package/dist/api/routes/cacheAlert.routes.d.ts.map +1 -0
- package/dist/api/routes/config.routes.d.ts +5 -0
- package/dist/api/routes/config.routes.d.ts.map +1 -0
- package/dist/api/routes/conversations.routes.d.ts +5 -0
- package/dist/api/routes/conversations.routes.d.ts.map +1 -0
- package/dist/api/routes/devices.routes.d.ts +17 -0
- package/dist/api/routes/devices.routes.d.ts.map +1 -0
- package/dist/api/routes/diagnostics.routes.d.ts +5 -0
- package/dist/api/routes/diagnostics.routes.d.ts.map +1 -0
- package/dist/api/routes/health.routes.d.ts +5 -0
- package/dist/api/routes/health.routes.d.ts.map +1 -0
- package/dist/api/routes/logs.routes.d.ts +3 -0
- package/dist/api/routes/logs.routes.d.ts.map +1 -0
- package/dist/api/routes/misc.routes.d.ts +77 -0
- package/dist/api/routes/misc.routes.d.ts.map +1 -0
- package/dist/api/routes/pair.routes.d.ts +5 -0
- package/dist/api/routes/pair.routes.d.ts.map +1 -0
- package/dist/api/routes/progress.routes.d.ts +39 -0
- package/dist/api/routes/progress.routes.d.ts.map +1 -0
- package/dist/api/routes/projects.routes.d.ts +5 -0
- package/dist/api/routes/projects.routes.d.ts.map +1 -0
- package/dist/api/routes/providers.routes.d.ts +19 -0
- package/dist/api/routes/providers.routes.d.ts.map +1 -0
- package/dist/api/routes/scanner.routes.d.ts +5 -0
- package/dist/api/routes/scanner.routes.d.ts.map +1 -0
- package/dist/api/routes/sessions.routes.d.ts +6 -0
- package/dist/api/routes/sessions.routes.d.ts.map +1 -0
- package/dist/api/routes/ws.routes.d.ts +7 -0
- package/dist/api/routes/ws.routes.d.ts.map +1 -0
- package/dist/api/types/api-deps.d.ts +114 -0
- package/dist/api/types/api-deps.d.ts.map +1 -0
- package/dist/auth.d.ts +81 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/browse.d.ts +17 -0
- package/dist/browse.d.ts.map +1 -0
- package/dist/claude-flags.d.ts +76 -0
- package/dist/claude-flags.d.ts.map +1 -0
- package/dist/cli.cjs +121 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/codex-pty-runner.d.ts +90 -0
- package/dist/codex-pty-runner.d.ts.map +1 -0
- package/dist/config/update-config.d.ts +13 -0
- package/dist/config/update-config.d.ts.map +1 -0
- package/dist/conversation-cache.d.ts +336 -0
- package/dist/conversation-cache.d.ts.map +1 -0
- package/dist/db/config.d.ts +11 -0
- package/dist/db/config.d.ts.map +1 -0
- package/dist/db/index.d.ts +5 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/migrations.d.ts +3 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.d.ts.map +1 -0
- package/dist/db/query-timing.d.ts +52 -0
- package/dist/db/query-timing.d.ts.map +1 -0
- package/dist/db/repositories/cacheMetadata.repository.d.ts +12 -0
- package/dist/db/repositories/cacheMetadata.repository.d.ts.map +1 -0
- package/dist/db/repositories/conversations.repository.d.ts +27 -0
- package/dist/db/repositories/conversations.repository.d.ts.map +1 -0
- package/dist/db/repositories/devices.repository.d.ts +137 -0
- package/dist/db/repositories/devices.repository.d.ts.map +1 -0
- package/dist/db/repositories/managed-sessions.repository.d.ts +127 -0
- package/dist/db/repositories/managed-sessions.repository.d.ts.map +1 -0
- package/dist/db/repositories/projects.repository.d.ts +28 -0
- package/dist/db/repositories/projects.repository.d.ts.map +1 -0
- package/dist/db/repositories/push.repository.d.ts +186 -0
- package/dist/db/repositories/push.repository.d.ts.map +1 -0
- package/dist/db/repositories/sessions.repository.d.ts +16 -0
- package/dist/db/repositories/sessions.repository.d.ts.map +1 -0
- package/dist/db/runtime-store.d.ts +80 -0
- package/dist/db/runtime-store.d.ts.map +1 -0
- package/dist/db/sqlite-migrate.d.ts +13 -0
- package/dist/db/sqlite-migrate.d.ts.map +1 -0
- package/dist/db/upload-records.d.ts +11 -0
- package/dist/db/upload-records.d.ts.map +1 -0
- package/dist/e2ee/noise.d.ts +261 -0
- package/dist/e2ee/noise.d.ts.map +1 -0
- package/dist/e2ee/pair-payload.d.ts +60 -0
- package/dist/e2ee/pair-payload.d.ts.map +1 -0
- package/dist/e2ee/pair-request.d.ts +24 -0
- package/dist/e2ee/pair-request.d.ts.map +1 -0
- package/dist/external-tails.d.ts +91 -0
- package/dist/external-tails.d.ts.map +1 -0
- package/dist/feature-flags.d.ts +212 -0
- package/dist/feature-flags.d.ts.map +1 -0
- package/dist/handlers/handleListProjects.d.ts +3 -0
- package/dist/handlers/handleListProjects.d.ts.map +1 -0
- package/dist/index.cjs +101 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +18 -2571
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/constants.d.ts +21 -0
- package/dist/lifecycle/constants.d.ts.map +1 -0
- package/dist/lifecycle/process-liveness.d.ts +7 -0
- package/dist/lifecycle/process-liveness.d.ts.map +1 -0
- package/dist/live-session-manager.d.ts +59 -0
- package/dist/live-session-manager.d.ts.map +1 -0
- package/dist/logger.d.ts +14 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/pair-store.d.ts +60 -0
- package/dist/pair-store.d.ts.map +1 -0
- package/dist/platform.d.ts +25 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/process-discovery.d.ts +35 -0
- package/dist/process-discovery.d.ts.map +1 -0
- package/dist/providers.d.ts +7 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/pty-host/protocol.d.ts +270 -0
- package/dist/pty-host/protocol.d.ts.map +1 -0
- package/dist/pty-host/remote-session-runner.d.ts +133 -0
- package/dist/pty-host/remote-session-runner.d.ts.map +1 -0
- package/dist/pty-host/socket.d.ts +54 -0
- package/dist/pty-host/socket.d.ts.map +1 -0
- package/dist/pty-host/spawn-host.d.ts +30 -0
- package/dist/pty-host/spawn-host.d.ts.map +1 -0
- package/dist/pty-manager.d.ts +65 -0
- package/dist/pty-manager.d.ts.map +1 -0
- package/dist/pty-shared.d.ts +27 -0
- package/dist/pty-shared.d.ts.map +1 -0
- package/dist/scanner-manager.d.ts +126 -0
- package/dist/scanner-manager.d.ts.map +1 -0
- package/dist/schemas/cacheAlert.schema.d.ts +14 -0
- package/dist/schemas/cacheAlert.schema.d.ts.map +1 -0
- package/dist/schemas/claudeFlags.schema.d.ts +7 -0
- package/dist/schemas/claudeFlags.schema.d.ts.map +1 -0
- package/dist/schemas/project.schema.d.ts +18 -0
- package/dist/schemas/project.schema.d.ts.map +1 -0
- package/dist/schemas/updateConfig.schema.d.ts +19 -0
- package/dist/schemas/updateConfig.schema.d.ts.map +1 -0
- package/dist/seal.d.ts +14 -0
- package/dist/seal.d.ts.map +1 -0
- package/dist/server-identity.d.ts +43 -0
- package/dist/server-identity.d.ts.map +1 -0
- package/dist/server-wiring.d.ts +189 -0
- package/dist/server-wiring.d.ts.map +1 -0
- package/dist/server.d.ts +404 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/services/backup/backup.d.ts +103 -0
- package/dist/services/backup/backup.d.ts.map +1 -0
- package/dist/services/cache/cacheMetadata.d.ts +8 -0
- package/dist/services/cache/cacheMetadata.d.ts.map +1 -0
- package/dist/services/cache-integrity/alertStore.d.ts +24 -0
- package/dist/services/cache-integrity/alertStore.d.ts.map +1 -0
- package/dist/services/cache-integrity/backup.d.ts +8 -0
- package/dist/services/cache-integrity/backup.d.ts.map +1 -0
- package/dist/services/cache-integrity/cacheIntegrityMonitor.d.ts +71 -0
- package/dist/services/cache-integrity/cacheIntegrityMonitor.d.ts.map +1 -0
- package/dist/services/conversations/conversationWatcher.d.ts +99 -0
- package/dist/services/conversations/conversationWatcher.d.ts.map +1 -0
- package/dist/services/conversations/findSearchTarget.d.ts +30 -0
- package/dist/services/conversations/findSearchTarget.d.ts.map +1 -0
- package/dist/services/conversations/isAgentConversation.d.ts +8 -0
- package/dist/services/conversations/isAgentConversation.d.ts.map +1 -0
- package/dist/services/conversations/pruneAgentConversations.d.ts +8 -0
- package/dist/services/conversations/pruneAgentConversations.d.ts.map +1 -0
- package/dist/services/conversations/refreshConversationCache.d.ts +27 -0
- package/dist/services/conversations/refreshConversationCache.d.ts.map +1 -0
- package/dist/services/conversations/shouldRefreshProjectsFromHdd.d.ts +33 -0
- package/dist/services/conversations/shouldRefreshProjectsFromHdd.d.ts.map +1 -0
- package/dist/services/diagnostics/diagnostics.d.ts +66 -0
- package/dist/services/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/services/projectChats/deriveProjectChatTitle.d.ts +15 -0
- package/dist/services/projectChats/deriveProjectChatTitle.d.ts.map +1 -0
- package/dist/services/projects/ensureProjectsForConversations.d.ts +15 -0
- package/dist/services/projects/ensureProjectsForConversations.d.ts.map +1 -0
- package/dist/services/providers/capabilities.d.ts +105 -0
- package/dist/services/providers/capabilities.d.ts.map +1 -0
- package/dist/services/providers/providerHealth.d.ts +72 -0
- package/dist/services/providers/providerHealth.d.ts.map +1 -0
- package/dist/services/push/apnsClient.d.ts +105 -0
- package/dist/services/push/apnsClient.d.ts.map +1 -0
- package/dist/services/push/expoPushSender.d.ts +37 -0
- package/dist/services/push/expoPushSender.d.ts.map +1 -0
- package/dist/services/push/liveActivityContentState.d.ts +60 -0
- package/dist/services/push/liveActivityContentState.d.ts.map +1 -0
- package/dist/services/push/liveActivityNotifier.d.ts +49 -0
- package/dist/services/push/liveActivityNotifier.d.ts.map +1 -0
- package/dist/services/push/liveActivityRenewal.d.ts +63 -0
- package/dist/services/push/liveActivityRenewal.d.ts.map +1 -0
- package/dist/services/push/liveActivitySender.d.ts +88 -0
- package/dist/services/push/liveActivitySender.d.ts.map +1 -0
- package/dist/services/push/waitingInputNotifier.d.ts +41 -0
- package/dist/services/push/waitingInputNotifier.d.ts.map +1 -0
- package/dist/services/questions/answersToKeystrokes.d.ts +8 -0
- package/dist/services/questions/answersToKeystrokes.d.ts.map +1 -0
- package/dist/services/questions/codexGateAnswers.d.ts +14 -0
- package/dist/services/questions/codexGateAnswers.d.ts.map +1 -0
- package/dist/services/questions/codexScreen.d.ts +74 -0
- package/dist/services/questions/codexScreen.d.ts.map +1 -0
- package/dist/services/questions/detectAskUserQuestion.d.ts +6 -0
- package/dist/services/questions/detectAskUserQuestion.d.ts.map +1 -0
- package/dist/services/questions/detectPermissionGate.d.ts +62 -0
- package/dist/services/questions/detectPermissionGate.d.ts.map +1 -0
- package/dist/services/questions/detectQuestionFromScreen.d.ts +30 -0
- package/dist/services/questions/detectQuestionFromScreen.d.ts.map +1 -0
- package/dist/services/questions/detectShellPrompt.d.ts +18 -0
- package/dist/services/questions/detectShellPrompt.d.ts.map +1 -0
- package/dist/services/questions/parseAgentPhase.d.ts +12 -0
- package/dist/services/questions/parseAgentPhase.d.ts.map +1 -0
- package/dist/services/questions/parseStatusLine.d.ts +19 -0
- package/dist/services/questions/parseStatusLine.d.ts.map +1 -0
- package/dist/services/questions/questionBroadcast.d.ts +18 -0
- package/dist/services/questions/questionBroadcast.d.ts.map +1 -0
- package/dist/services/questions/resolveAnswer.d.ts +16 -0
- package/dist/services/questions/resolveAnswer.d.ts.map +1 -0
- package/dist/services/search/searchQuery.d.ts +78 -0
- package/dist/services/search/searchQuery.d.ts.map +1 -0
- package/dist/services/security/capabilities.d.ts +50 -0
- package/dist/services/security/capabilities.d.ts.map +1 -0
- package/dist/services/sessions/autoResumeOnBoot.d.ts +28 -0
- package/dist/services/sessions/autoResumeOnBoot.d.ts.map +1 -0
- package/dist/services/sessions/codexRolloutOwner.d.ts +55 -0
- package/dist/services/sessions/codexRolloutOwner.d.ts.map +1 -0
- package/dist/services/sessions/conversationBusy.d.ts +32 -0
- package/dist/services/sessions/conversationBusy.d.ts.map +1 -0
- package/dist/services/sessions/idempotency.d.ts +67 -0
- package/dist/services/sessions/idempotency.d.ts.map +1 -0
- package/dist/services/sessions/reconcileSessions.d.ts +63 -0
- package/dist/services/sessions/reconcileSessions.d.ts.map +1 -0
- package/dist/services/sessions/rehydrateSessions.d.ts +68 -0
- package/dist/services/sessions/rehydrateSessions.d.ts.map +1 -0
- package/dist/services/sessions/resumeIdentity.d.ts +16 -0
- package/dist/services/sessions/resumeIdentity.d.ts.map +1 -0
- package/dist/session-registry-boot.d.ts +142 -0
- package/dist/session-registry-boot.d.ts.map +1 -0
- package/dist/session-store.d.ts +32 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-watchers.d.ts +60 -0
- package/dist/session-watchers.d.ts.map +1 -0
- package/dist/types.d.ts +616 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/uploads.d.ts +16 -0
- package/dist/uploads.d.ts.map +1 -0
- package/dist/utils/bootToken.d.ts +3 -0
- package/dist/utils/bootToken.d.ts.map +1 -0
- package/dist/utils/canonicalizeFilePath.d.ts +75 -0
- package/dist/utils/canonicalizeFilePath.d.ts.map +1 -0
- package/dist/utils/canonicalizeProjectPath.d.ts +13 -0
- package/dist/utils/canonicalizeProjectPath.d.ts.map +1 -0
- package/dist/utils/codexConversationLine.d.ts +38 -0
- package/dist/utils/codexConversationLine.d.ts.map +1 -0
- package/dist/utils/conversationEtag.d.ts +19 -0
- package/dist/utils/conversationEtag.d.ts.map +1 -0
- package/dist/utils/dates.d.ts +12 -0
- package/dist/utils/dates.d.ts.map +1 -0
- package/dist/utils/debounce.d.ts +15 -0
- package/dist/utils/debounce.d.ts.map +1 -0
- package/dist/utils/deriveSessionName.d.ts +10 -0
- package/dist/utils/deriveSessionName.d.ts.map +1 -0
- package/dist/utils/fileIdentity.d.ts +36 -0
- package/dist/utils/fileIdentity.d.ts.map +1 -0
- package/dist/utils/isScannedSnapshotStale.d.ts +16 -0
- package/dist/utils/isScannedSnapshotStale.d.ts.map +1 -0
- package/dist/utils/scanProgressThrottle.d.ts +12 -0
- package/dist/utils/scanProgressThrottle.d.ts.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/ws-hub.d.ts +15 -0
- package/dist/ws-hub.d.ts.map +1 -0
- package/package.json +3 -4
- package/dist/index.d.cts +0 -2571
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { type KeyObject } from "crypto";
|
|
2
|
+
export declare const NOISE_PROTOCOL_NAME = "Noise_IKpsk1_25519_ChaChaPoly_SHA256";
|
|
3
|
+
/**
|
|
4
|
+
* Prologue for the PAIRING handshake, mixed into the transcript before any
|
|
5
|
+
* token (spec §5.3, `MixHash(prologue)`).
|
|
6
|
+
*
|
|
7
|
+
* Not in design.md, and added deliberately. The design has two `IK` handshakes:
|
|
8
|
+
* this one at `/api/pair/exchange` with the pair token as PSK, and a later one
|
|
9
|
+
* at `/api/e2ee/open` against stored static keys with no PSK. Without a
|
|
10
|
+
* prologue the two transcripts differ only by the presence of the PSK, so
|
|
11
|
+
* "could a message from one be replayed into the other" becomes a question you
|
|
12
|
+
* answer by reasoning about the PSK. A prologue answers it by construction, and
|
|
13
|
+
* it costs one hash.
|
|
14
|
+
*
|
|
15
|
+
* **`"threadbase-e2ee/<version> <purpose>"` is a namespace, not one string.**
|
|
16
|
+
* Its sibling is `"threadbase-e2ee/1 open"` for `/api/e2ee/open`, named here
|
|
17
|
+
* rather than left for that phase to invent: half a domain separation is a
|
|
18
|
+
* property nobody has, and a convention chosen once with both instances visible
|
|
19
|
+
* is the one that survives a third handshake. The constant itself is not
|
|
20
|
+
* declared until something uses it.
|
|
21
|
+
*
|
|
22
|
+
* Changing this string is a silent, total incompatibility with tb-mobile — it
|
|
23
|
+
* is hashed into `h` before any token, so the only symptom is "decryption
|
|
24
|
+
* failed". It is pinned by a committed vector for that reason.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PAIR_PROLOGUE: Buffer<ArrayBuffer>;
|
|
27
|
+
/** `e` (32) + `s` sealed (32 + 16) — everything before message 1's payload. */
|
|
28
|
+
export declare const NOISE_MESSAGE_1_OVERHEAD: number;
|
|
29
|
+
/** `e` (32) — everything before message 2's payload. */
|
|
30
|
+
export declare const NOISE_MESSAGE_2_OVERHEAD: number;
|
|
31
|
+
/**
|
|
32
|
+
* Cap on a handshake message, checked BEFORE anything is allocated or parsed.
|
|
33
|
+
*
|
|
34
|
+
* The Noise spec's own transport limit is 65535 bytes. This is far below it
|
|
35
|
+
* because our payloads are a handful of JSON fields, and because
|
|
36
|
+
* `/api/pair/exchange` is a public, unauthenticated endpoint — the same
|
|
37
|
+
* reasoning D-9 applies to the unseal middleware, reaching this far forward.
|
|
38
|
+
*/
|
|
39
|
+
export declare const NOISE_MAX_MESSAGE_BYTES = 4096;
|
|
40
|
+
export declare class NoiseError extends Error {
|
|
41
|
+
constructor(message: string);
|
|
42
|
+
}
|
|
43
|
+
export interface KeyPair {
|
|
44
|
+
publicKey: KeyObject;
|
|
45
|
+
privateKey: KeyObject;
|
|
46
|
+
/** Raw 32 bytes, which is what goes on the wire. */
|
|
47
|
+
publicKeyRaw: Buffer;
|
|
48
|
+
}
|
|
49
|
+
export declare function generateKeyPair(): KeyPair;
|
|
50
|
+
/**
|
|
51
|
+
* A `KeyPair` around a private key this process already holds.
|
|
52
|
+
*
|
|
53
|
+
* The bridge from `loadOrCreateServerIdentity()`, which owns the key file and
|
|
54
|
+
* returns a bare `KeyObject`, to the handshake, which needs the public half
|
|
55
|
+
* alongside it. Kept here rather than in server-identity.ts so that module
|
|
56
|
+
* stays about storing a key rather than about the protocol that uses it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function keyPairFrom(privateKey: KeyObject): KeyPair;
|
|
59
|
+
/**
|
|
60
|
+
* Raw 32 bytes out of a KeyObject. JWK OKP `x` is base64url of exactly that.
|
|
61
|
+
*
|
|
62
|
+
* Accepts either half: `createPublicKey` derives the public key from a private
|
|
63
|
+
* one but rejects a KeyObject that is already public, so the type is checked
|
|
64
|
+
* rather than the call being made unconditionally.
|
|
65
|
+
*/
|
|
66
|
+
export declare function rawPublicKey(key: KeyObject): Buffer;
|
|
67
|
+
/**
|
|
68
|
+
* A KeyObject from raw bytes, or a `NoiseError`.
|
|
69
|
+
*
|
|
70
|
+
* This is a trust boundary: the bytes are the QR's `spk` on the client side and
|
|
71
|
+
* an attacker-supplied handshake message on the server side. A wrong length is
|
|
72
|
+
* rejected here rather than being padded, truncated, or handed to a DH that
|
|
73
|
+
* would fail somewhere less legible.
|
|
74
|
+
*/
|
|
75
|
+
export declare function publicKeyFromRaw(raw: Buffer): KeyObject;
|
|
76
|
+
/**
|
|
77
|
+
* A keypair from a raw 32-byte private scalar.
|
|
78
|
+
*
|
|
79
|
+
* Exists for the committed test vectors: a vector is only a contract if the
|
|
80
|
+
* keys are fixed, and there is no other way to hand Node a chosen X25519
|
|
81
|
+
* private key. Not used by any production path — every real keypair comes from
|
|
82
|
+
* `generateKeyPair()` or the identity key file.
|
|
83
|
+
*
|
|
84
|
+
* Goes through PKCS#8 DER rather than JWK because a JWK OKP private key must
|
|
85
|
+
* carry a matching `x`, and the point here is to supply only `d`. The prefix is
|
|
86
|
+
* the fixed X25519 PrivateKeyInfo header (RFC 8410): SEQUENCE, version 0,
|
|
87
|
+
* AlgorithmIdentifier 1.3.101.110, then an OCTET STRING wrapping the 32-byte
|
|
88
|
+
* scalar.
|
|
89
|
+
*/
|
|
90
|
+
export declare function keyPairFromRawPrivate(raw: Buffer): KeyPair;
|
|
91
|
+
/**
|
|
92
|
+
* The pair token as a 32-byte PSK.
|
|
93
|
+
*
|
|
94
|
+
* The token is `pt_<32 hex chars>` — 35 bytes of ASCII, and the spec requires
|
|
95
|
+
* the PSK to be exactly 32. Hashing with a domain-separating label is the
|
|
96
|
+
* standard way to fit it, and the label means this value can never collide with
|
|
97
|
+
* some other use of the same token elsewhere in the system.
|
|
98
|
+
*
|
|
99
|
+
* Both implementations must compute this identically or the handshake fails
|
|
100
|
+
* with no diagnostic beyond "decryption failed", which is why it is pinned by a
|
|
101
|
+
* test vector rather than left as an obvious detail.
|
|
102
|
+
*/
|
|
103
|
+
export declare function pskFromPairToken(token: string): Buffer;
|
|
104
|
+
/**
|
|
105
|
+
* Noise's `CipherState` (spec §5.1).
|
|
106
|
+
*
|
|
107
|
+
* Exported for two reasons, neither of them scaffolding. Phase 3's record layer
|
|
108
|
+
* has to build a transport cipher from the buffers `split()` returns, so this is
|
|
109
|
+
* the type it will construct. And the §5.1 rule that a failed decryption must
|
|
110
|
+
* not advance `n` is unobservable through the handshake API — every AEAD
|
|
111
|
+
* operation there runs at `n = 0`, because each is preceded by a `MixKey` that
|
|
112
|
+
* calls `initializeKey`. Without a way to reach a cipher state directly, that
|
|
113
|
+
* rule is protected by nothing: a "simplification" that advances before
|
|
114
|
+
* verifying passes every other test in the suite.
|
|
115
|
+
*/
|
|
116
|
+
export declare class CipherState {
|
|
117
|
+
private k;
|
|
118
|
+
private n;
|
|
119
|
+
initializeKey(key: Buffer | null): void;
|
|
120
|
+
hasKey(): boolean;
|
|
121
|
+
encryptWithAd(ad: Buffer, plaintext: Buffer): Buffer;
|
|
122
|
+
decryptWithAd(ad: Buffer, ciphertext: Buffer): Buffer;
|
|
123
|
+
}
|
|
124
|
+
declare class SymmetricState {
|
|
125
|
+
private ck;
|
|
126
|
+
private h;
|
|
127
|
+
private readonly cipher;
|
|
128
|
+
constructor(protocolName: string);
|
|
129
|
+
/**
|
|
130
|
+
* Noise's HKDF (spec §4.3) is RFC 5869 with `salt = chaining_key`,
|
|
131
|
+
* `ikm = input_key_material` and an EMPTY info, so Node's `hkdfSync` is
|
|
132
|
+
* exactly it rather than approximately it. Using the platform's HKDF removes
|
|
133
|
+
* the HMAC chain that would otherwise be the easiest thing here to get
|
|
134
|
+
* subtly wrong.
|
|
135
|
+
*/
|
|
136
|
+
private hkdf;
|
|
137
|
+
mixKey(ikm: Buffer): void;
|
|
138
|
+
mixHash(data: Buffer): void;
|
|
139
|
+
/** Spec §5.2. Used only by the `psk` token. */
|
|
140
|
+
mixKeyAndHash(ikm: Buffer): void;
|
|
141
|
+
encryptAndHash(plaintext: Buffer): Buffer;
|
|
142
|
+
decryptAndHash(ciphertext: Buffer): Buffer;
|
|
143
|
+
/** The transcript hash. Both sides must arrive at the same value. */
|
|
144
|
+
handshakeHash(): Buffer;
|
|
145
|
+
/** Spec §5.2 `Split()`: the two directional transport keys. */
|
|
146
|
+
split(): {
|
|
147
|
+
k1: Buffer;
|
|
148
|
+
k2: Buffer;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export interface HandshakeKeys {
|
|
152
|
+
/** Transcript hash. `ctxId` is derived from it, so both sides agree without a round trip. */
|
|
153
|
+
handshakeHash: Buffer;
|
|
154
|
+
/** Initiator → responder traffic key. */
|
|
155
|
+
clientToServer: Buffer;
|
|
156
|
+
/** Responder → initiator traffic key. */
|
|
157
|
+
serverToClient: Buffer;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Message 1, from the phone.
|
|
161
|
+
*
|
|
162
|
+
* Exported from the *server* module although the server never sends one. It is
|
|
163
|
+
* the only way to drive the responder in a test, and it generates the committed
|
|
164
|
+
* vectors the tb-mobile implementation is checked against — a second, drifting
|
|
165
|
+
* initiator written inside a test file is the precise failure two independent
|
|
166
|
+
* implementations invite.
|
|
167
|
+
*/
|
|
168
|
+
export declare function writeMessage1(args: {
|
|
169
|
+
staticKeyPair: KeyPair;
|
|
170
|
+
responderStaticPub: Buffer;
|
|
171
|
+
psk: Buffer;
|
|
172
|
+
payload: Buffer;
|
|
173
|
+
prologue?: Buffer;
|
|
174
|
+
/** Test seam only. Production always generates a fresh ephemeral. */
|
|
175
|
+
ephemeral?: KeyPair;
|
|
176
|
+
}): {
|
|
177
|
+
message: Buffer;
|
|
178
|
+
state: HandshakeInitiatorState;
|
|
179
|
+
};
|
|
180
|
+
export interface HandshakeInitiatorState {
|
|
181
|
+
symmetric: SymmetricState;
|
|
182
|
+
ephemeral: KeyPair;
|
|
183
|
+
staticKeyPair: KeyPair;
|
|
184
|
+
}
|
|
185
|
+
/** Message 2, read by the phone. Completes the handshake. */
|
|
186
|
+
export declare function readMessage2(state: HandshakeInitiatorState, message: Buffer): {
|
|
187
|
+
payload: Buffer;
|
|
188
|
+
keys: HandshakeKeys;
|
|
189
|
+
};
|
|
190
|
+
export interface ResponderResult {
|
|
191
|
+
/** The initiator's static public key, authenticated by the handshake. */
|
|
192
|
+
initiatorStaticPub: Buffer;
|
|
193
|
+
payload: Buffer;
|
|
194
|
+
message2: Buffer;
|
|
195
|
+
keys: HandshakeKeys;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Half-completed responder state, between reading message 1 and writing 2.
|
|
199
|
+
*
|
|
200
|
+
* Deliberately only ever a local: it is handed straight back into
|
|
201
|
+
* `writeMessage2` within the same synchronous stretch of one request, never
|
|
202
|
+
* stored, never keyed by anything a caller supplies. A responder state that
|
|
203
|
+
* outlived a request would be a thing to allocate and expire on a public
|
|
204
|
+
* endpoint, which is what the single-call shape originally avoided.
|
|
205
|
+
*/
|
|
206
|
+
export interface HandshakeResponderState {
|
|
207
|
+
symmetric: SymmetricState;
|
|
208
|
+
/** The initiator's static public key, authenticated by message 1. */
|
|
209
|
+
initiatorStaticPub: Buffer;
|
|
210
|
+
/** Message 1's decrypted payload. */
|
|
211
|
+
payload: Buffer;
|
|
212
|
+
initiatorEphemeral: KeyObject;
|
|
213
|
+
initiatorStatic: KeyObject;
|
|
214
|
+
staticKeyPair: KeyPair;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Read message 1. Authenticates the initiator and recovers its static key.
|
|
218
|
+
*
|
|
219
|
+
* Split from `writeMessage2` because the server DOES now have something to do
|
|
220
|
+
* between them: message 2's payload carries the `deviceId`, and the device row
|
|
221
|
+
* cannot be written until the pair token has been spent, which cannot happen
|
|
222
|
+
* until this half has succeeded. The original single-call shape assumed nothing
|
|
223
|
+
* sat in the middle; the pairing handler is the caller that proved otherwise.
|
|
224
|
+
*
|
|
225
|
+
* SYNCHRONOUS on purpose, and it must stay that way: the caller runs this
|
|
226
|
+
* between validating the pair token and consuming it, and `PairTokenStore` has
|
|
227
|
+
* no lock. An `await` in that gap would let two concurrent requests with the
|
|
228
|
+
* same token both pass validation.
|
|
229
|
+
*/
|
|
230
|
+
export declare function readMessage1(args: {
|
|
231
|
+
staticKeyPair: KeyPair;
|
|
232
|
+
psk: Buffer;
|
|
233
|
+
message1: Buffer;
|
|
234
|
+
prologue?: Buffer;
|
|
235
|
+
}): HandshakeResponderState;
|
|
236
|
+
/** Write message 2 and derive the transport keys. Completes the handshake. */
|
|
237
|
+
export declare function writeMessage2(state: HandshakeResponderState, responsePayload: Buffer,
|
|
238
|
+
/** Test seam only. Production always generates a fresh ephemeral. */
|
|
239
|
+
ephemeral?: KeyPair): {
|
|
240
|
+
message2: Buffer;
|
|
241
|
+
keys: HandshakeKeys;
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Both halves in one call.
|
|
245
|
+
*
|
|
246
|
+
* Kept because most callers — every test, and any future responder with nothing
|
|
247
|
+
* to do in the middle — want the whole handshake, and because the two halves
|
|
248
|
+
* being separable should not force every one of them to sequence it by hand.
|
|
249
|
+
*/
|
|
250
|
+
export declare function respond(args: {
|
|
251
|
+
staticKeyPair: KeyPair;
|
|
252
|
+
psk: Buffer;
|
|
253
|
+
message1: Buffer;
|
|
254
|
+
/** Built from the authenticated message-1 payload; sealed into message 2. */
|
|
255
|
+
buildPayload: (initiatorStaticPub: Buffer, payload: Buffer) => Buffer;
|
|
256
|
+
prologue?: Buffer;
|
|
257
|
+
/** Test seam only. */
|
|
258
|
+
ephemeral?: KeyPair;
|
|
259
|
+
}): ResponderResult;
|
|
260
|
+
export {};
|
|
261
|
+
//# sourceMappingURL=noise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noise.d.ts","sourceRoot":"","sources":["../../src/e2ee/noise.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The device-registration inputs message 1 authenticates.
|
|
3
|
+
*
|
|
4
|
+
* `deviceName` is the one optional field in the contract, and is normalised to
|
|
5
|
+
* `null` when absent so both pairing paths hand `devicesRepo.register` the same
|
|
6
|
+
* shape. The 100-character bound is the legacy path's, kept identical rather
|
|
7
|
+
* than re-chosen: authenticating a value is not a reason to stop bounding it.
|
|
8
|
+
*/
|
|
9
|
+
export interface E2eePairRegistration {
|
|
10
|
+
version: number;
|
|
11
|
+
deviceName: string | null;
|
|
12
|
+
readOnly: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse message 1's decrypted payload.
|
|
16
|
+
*
|
|
17
|
+
* Throws rather than defaulting. A client that completed the handshake and then
|
|
18
|
+
* sent an unreadable payload is a protocol violation, not an older client — an
|
|
19
|
+
* older client cannot reach this function at all, because it sends no `e2ee`
|
|
20
|
+
* field and no handshake is performed. Defaulting a missing `readOnly` to
|
|
21
|
+
* `false` would silently grant the wider capability preset off a payload the
|
|
22
|
+
* device never actually stated, which is the exact substitution this contract
|
|
23
|
+
* exists to prevent, arrived at through a parser instead of an intermediary.
|
|
24
|
+
*
|
|
25
|
+
* `v` is checked against the same version and with the same ordering as
|
|
26
|
+
* `parseE2eeRequest`: a version mismatch is not a malformed payload, and saying
|
|
27
|
+
* so is what lets a future client fall back deliberately.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseE2eeMsg1Payload(payload: Buffer): E2eePairRegistration;
|
|
30
|
+
/**
|
|
31
|
+
* Every pairing result a new client persists or presents as verified.
|
|
32
|
+
*
|
|
33
|
+
* The outer response still carries compatibility copies of `deviceId`,
|
|
34
|
+
* `deviceToken`, `capabilities`, `publicUrl` and `machineName` for released
|
|
35
|
+
* clients that can read nothing else, and a new client ignores all of them —
|
|
36
|
+
* so this shape is the one that has to be complete. A field missing here is a
|
|
37
|
+
* field the new client would have to take from the unauthenticated outer copy,
|
|
38
|
+
* which is the same as not authenticating it at all.
|
|
39
|
+
*
|
|
40
|
+
* `deviceId` and `deviceToken` are non-nullable on purpose: an E2EE pairing
|
|
41
|
+
* that cannot produce them is a failed pairing, not a success carrying nulls.
|
|
42
|
+
*/
|
|
43
|
+
export interface E2eeMsg2Payload {
|
|
44
|
+
v: number;
|
|
45
|
+
deviceId: string;
|
|
46
|
+
deviceToken: string;
|
|
47
|
+
capabilities: string[];
|
|
48
|
+
publicUrl: string | null;
|
|
49
|
+
machineName: string;
|
|
50
|
+
serverVersion: string;
|
|
51
|
+
/**
|
|
52
|
+
* Always `true`. Completing a handshake is what pins the device, and
|
|
53
|
+
* design.md §6.3 says nothing a client sends ever clears it — so this is a
|
|
54
|
+
* literal here rather than an argument, and there is no call site that can
|
|
55
|
+
* pass `false` by accident.
|
|
56
|
+
*/
|
|
57
|
+
e2eeRequired: true;
|
|
58
|
+
}
|
|
59
|
+
export declare function encodeE2eeMsg2Payload(fields: Omit<E2eeMsg2Payload, "v" | "e2eeRequired">): Buffer;
|
|
60
|
+
//# sourceMappingURL=pair-payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pair-payload.d.ts","sourceRoot":"","sources":["../../src/e2ee/pair-payload.ts"],"names":[],"mappings":"AAoBA;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CA+C1E;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,YAAY,EAAE,IAAI,CAAC;CACpB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,GAAG,cAAc,CAAC,GAAG,MAAM,CAOjG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Envelope version this build speaks. Matches `E2EE_PROTOCOL_VERSION`. */
|
|
2
|
+
export declare const E2EE_EXCHANGE_VERSION = 1;
|
|
3
|
+
export declare class E2eeRequestError extends Error {
|
|
4
|
+
/** Stable code for the client, so a version mismatch is not a parse failure. */
|
|
5
|
+
readonly code: "E2EE_MALFORMED" | "E2EE_VERSION_UNSUPPORTED";
|
|
6
|
+
constructor(code: E2eeRequestError["code"], message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface E2eeExchangeRequest {
|
|
9
|
+
version: number;
|
|
10
|
+
/** Noise `IK` message 1, decoded. */
|
|
11
|
+
message1: Buffer;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* `null` for a client that did not ask for encryption; a parsed request for one
|
|
15
|
+
* that did; a throw for one that asked incoherently.
|
|
16
|
+
*
|
|
17
|
+
* The three are deliberately distinct. Collapsing "absent" into "malformed"
|
|
18
|
+
* breaks every released client, and collapsing "malformed" into "absent" would
|
|
19
|
+
* silently pair a client that believed it was encrypting — which is the
|
|
20
|
+
* downgrade the whole negotiation exists to prevent, arrived at through a
|
|
21
|
+
* parser rather than an attacker.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseE2eeRequest(raw: unknown): E2eeExchangeRequest | null;
|
|
24
|
+
//# sourceMappingURL=pair-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pair-request.d.ts","sourceRoot":"","sources":["../../src/e2ee/pair-request.ts"],"names":[],"mappings":"AAgBA,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAYvC,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,0BAA0B,CAAC;IAE7D,YAAY,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAI1D;CACF;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAsCzE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ConversationCache } from "./conversation-cache";
|
|
2
|
+
import type { CacheIntegrityMonitor } from "./services/cache-integrity/cacheIntegrityMonitor";
|
|
3
|
+
import type { ConversationWatcher } from "./services/conversations/conversationWatcher";
|
|
4
|
+
import type { SessionActivity, SessionResponse } from "./types";
|
|
5
|
+
import type { WSHub } from "./ws-hub";
|
|
6
|
+
export declare const EXTERNAL_TAIL_RECENCY_MS = 120000;
|
|
7
|
+
export declare const EXTERNAL_TAIL_MAX = 32;
|
|
8
|
+
export declare const EXTERNAL_TAIL_IDLE_MS = 300000;
|
|
9
|
+
export declare const EXTERNAL_ACTIVE_WRITING_MS = 30000;
|
|
10
|
+
export type ExternalTailEntry = {
|
|
11
|
+
conversationId: string;
|
|
12
|
+
lastActivityAt: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Everything ExternalTailManager reads from the server. Nullable collaborators
|
|
16
|
+
* are thunks rather than values because they are opened during listen() and
|
|
17
|
+
* rebound by the integrity monitor's reset-and-rescan — the same reason
|
|
18
|
+
* ApiDeps passes `cache: () => ConversationCache | null`.
|
|
19
|
+
*
|
|
20
|
+
* `tails` and `sessionFileMap` are the server's own Maps, held by reference:
|
|
21
|
+
* the server still owns them as instance fields (both are reached directly
|
|
22
|
+
* elsewhere in server.ts, and the tests read `externalTails` off the server),
|
|
23
|
+
* this class only drives them.
|
|
24
|
+
*/
|
|
25
|
+
export type ExternalTailManagerDeps = {
|
|
26
|
+
tails: Map<string, ExternalTailEntry>;
|
|
27
|
+
sessionFileMap: Map<string, string>;
|
|
28
|
+
fileWatcher: ConversationWatcher;
|
|
29
|
+
wsHub: WSHub;
|
|
30
|
+
cache: () => ConversationCache | null;
|
|
31
|
+
cacheMonitor: () => CacheIntegrityMonitor | null;
|
|
32
|
+
broadcastConversationLines: (sessionId: string, lines: string[], seqs?: (number | null)[] | null) => void;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Owns the live tails on JSONLs no PTY session is writing: when one attaches,
|
|
36
|
+
* when it is evicted or swept, the inferred activity it feeds into session
|
|
37
|
+
* responses, and the transcript lines it pushes.
|
|
38
|
+
*
|
|
39
|
+
* Extracted from StreamerServer so external-tail work stops editing the server
|
|
40
|
+
* file, continuing the split in docs/plans/2026-07-12-server-ts-split.md.
|
|
41
|
+
*/
|
|
42
|
+
export declare class ExternalTailManager {
|
|
43
|
+
private deps;
|
|
44
|
+
private log;
|
|
45
|
+
constructor(deps: ExternalTailManagerDeps);
|
|
46
|
+
private get externalTails();
|
|
47
|
+
/** True when a managed (PTY) session owns the tail for this canonical path. */
|
|
48
|
+
isManagedTailPath(key: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Attach a live tail to a JSONL nobody is tailing yet, when it was touched
|
|
51
|
+
* recently enough to look actively written by an external agent. Capped at
|
|
52
|
+
* EXTERNAL_TAIL_MAX with LRU eviction.
|
|
53
|
+
*/
|
|
54
|
+
maybeAttachExternalTail(filePath: string): void;
|
|
55
|
+
/** Stop tailing an external file and drop its bookkeeping. */
|
|
56
|
+
detachExternalTail(key: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Shared unlink path for the per-file watcher and the directory watcher.
|
|
59
|
+
* Detaches any external tail and drops the cache row (unless an integrity
|
|
60
|
+
* alert is freezing deletes).
|
|
61
|
+
*/
|
|
62
|
+
handleJsonlDeleted(filePath: string): void;
|
|
63
|
+
/** Make room for one more tail by evicting the least recently active ones. */
|
|
64
|
+
evictExternalTailsIfNeeded(): void;
|
|
65
|
+
/**
|
|
66
|
+
* INFERRED activity for an externally-owned conversation, derived purely from
|
|
67
|
+
* how recently its JSONL grew (the external tail's bookkeeping). Returns
|
|
68
|
+
* undefined when we hold no tail for it, so a session we know nothing about
|
|
69
|
+
* reports no activity rather than a fabricated "quiet".
|
|
70
|
+
*
|
|
71
|
+
* This can never distinguish a generating agent from one blocked on a
|
|
72
|
+
* permission gate — gates render on the PTY screen and never reach the JSONL —
|
|
73
|
+
* which is why it is a separate field and not folded into `status`.
|
|
74
|
+
*/
|
|
75
|
+
externalActivityFor(conversationId: string, now?: number): SessionActivity | undefined;
|
|
76
|
+
/** Attach inferred `activity` to externally-owned sessions in a response set. */
|
|
77
|
+
withExternalActivity(sessions: readonly SessionResponse[]): readonly SessionResponse[];
|
|
78
|
+
/** Detach external tails idle past EXTERNAL_TAIL_IDLE_MS. */
|
|
79
|
+
sweepIdleExternalTails(now?: number): void;
|
|
80
|
+
/**
|
|
81
|
+
* Push appended lines from an externally-owned conversation. Reuses the exact
|
|
82
|
+
* conversation_events / conversation_event shapes mobile already consumes,
|
|
83
|
+
* keyed by the conversation UUID — an external session has no PTY, so it must
|
|
84
|
+
* never produce terminal_output / terminal_replay / session_ready, and never a
|
|
85
|
+
* session_update whose session.id is a conversation UUID (that would mint a
|
|
86
|
+
* phantom session row in the mobile cache). Question cards are likewise never
|
|
87
|
+
* derived here: with no PTY there is nothing that could deliver an answer.
|
|
88
|
+
*/
|
|
89
|
+
broadcastExternalTailLines(filePath: string, lines: string[], seqs?: (number | null)[] | null): void;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=external-tails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-tails.d.ts","sourceRoot":"","sources":["../src/external-tails.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kDAAkD,CAAC;AAC9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AAExF,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAWtC,eAAO,MAAM,wBAAwB,SAAwB,CAAC;AAK9D,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAKpC,eAAO,MAAM,qBAAqB,SAAU,CAAC;AAI7C,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACtC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,mBAAmB,CAAC;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACtC,YAAY,EAAE,MAAM,qBAAqB,GAAG,IAAI,CAAC;IACjD,0BAA0B,EAAE,CAC1B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,KAC5B,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,mBAAmB;IAGlB,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,GAAG,CAAuB;IAElC,YAAoB,IAAI,EAAE,uBAAuB,EAAI;IAErD,OAAO,KAAK,aAAa,GAExB;IAED,+EAA+E;IAC/E,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAKtC;IAED;;;;OAIG;IACH,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAgC9C;IAED,8DAA8D;IAC9D,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAOpC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAkBzC;IAED,8EAA8E;IAC9E,0BAA0B,IAAI,IAAI,CAmBjC;IAED;;;;;;;;;OASG;IACH,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,eAAe,GAAG,SAAS,CAWzF;IAED,iFAAiF;IACjF,oBAAoB,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,eAAe,EAAE,CAQrF;IAED,6DAA6D;IAC7D,sBAAsB,CAAC,GAAG,SAAa,GAAG,IAAI,CAS7C;IAED;;;;;;;;OAQG;IACH,0BAA0B,CACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAC9B,IAAI,CAyBN;CACF"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/** Spec for one flag in the keyed registry. The id is the object key, not a field. */
|
|
2
|
+
export interface FeatureFlagSpec {
|
|
3
|
+
/** Shipped to clients alongside the values so a UI can render it. */
|
|
4
|
+
description: string;
|
|
5
|
+
default: boolean;
|
|
6
|
+
/** Full env var name. */
|
|
7
|
+
env: string;
|
|
8
|
+
}
|
|
9
|
+
/** Wire shape: the keyed spec plus its id, for GET /api/config/feature-flags. */
|
|
10
|
+
export interface FeatureFlagDefinition extends FeatureFlagSpec {
|
|
11
|
+
/** Stable config/wire key. Used in server.yaml, on the CLI, and over HTTP. */
|
|
12
|
+
id: FeatureFlagId;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Values arriving from a config source. PARTIAL on purpose: server.yaml, the
|
|
16
|
+
* CLI and ServerConfig each speak about the flags they mention and stay silent
|
|
17
|
+
* on the rest, which is what lets the precedence chain fall through.
|
|
18
|
+
*/
|
|
19
|
+
export type FeatureFlagValues = Partial<Record<FeatureFlagId, boolean>>;
|
|
20
|
+
/** Every flag, resolved. Total — see resolveFeatureFlags(). */
|
|
21
|
+
export type ResolvedFeatureFlags = Record<FeatureFlagId, boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Which rung of the precedence chain decided a flag.
|
|
24
|
+
*
|
|
25
|
+
* Kept because the resolved boolean alone cannot answer "why is this on?", and
|
|
26
|
+
* that is the first question asked of a flag that is on when nobody expected it.
|
|
27
|
+
* `override` is the legacy ServerConfig field (see resolveFeatureFlags).
|
|
28
|
+
*/
|
|
29
|
+
export type FeatureFlagSource = "override" | "env" | "cli" | "yaml" | "default";
|
|
30
|
+
export interface FeatureFlagResolution {
|
|
31
|
+
values: ResolvedFeatureFlags;
|
|
32
|
+
sources: Record<FeatureFlagId, FeatureFlagSource>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The registry, keyed by flag name.
|
|
36
|
+
*
|
|
37
|
+
* Prefer `FEATURE_FLAGS.ptyHost` over a string lookup. A typo is a compile
|
|
38
|
+
* error; `findFeatureFlag("…")` is only for untrusted yaml/CLI tokens.
|
|
39
|
+
*
|
|
40
|
+
* `as const` + `keyof` is what makes `flags.ptyHsot` a compile error instead of
|
|
41
|
+
* `undefined`. A TS enum would add a runtime object without a stronger type.
|
|
42
|
+
*/
|
|
43
|
+
export declare const FEATURE_FLAGS: {
|
|
44
|
+
readonly codexSystemPrompt: {
|
|
45
|
+
readonly description: string;
|
|
46
|
+
readonly default: false;
|
|
47
|
+
readonly env: "THREADBASE_FEATURE_CODEX_SYSTEM_PROMPT";
|
|
48
|
+
};
|
|
49
|
+
readonly sessionRehydration: {
|
|
50
|
+
readonly description: string;
|
|
51
|
+
readonly default: true;
|
|
52
|
+
readonly env: "THREADBASE_FEATURE_SESSION_REHYDRATION";
|
|
53
|
+
};
|
|
54
|
+
readonly liveActivityPush: {
|
|
55
|
+
readonly description: string;
|
|
56
|
+
readonly default: false;
|
|
57
|
+
readonly env: "THREADBASE_FEATURE_LIVE_ACTIVITY_PUSH";
|
|
58
|
+
};
|
|
59
|
+
readonly e2ee: {
|
|
60
|
+
readonly description: string;
|
|
61
|
+
readonly default: false;
|
|
62
|
+
readonly env: "THREADBASE_FEATURE_E2EE";
|
|
63
|
+
};
|
|
64
|
+
readonly ptyHost: {
|
|
65
|
+
readonly description: string;
|
|
66
|
+
readonly default: false;
|
|
67
|
+
readonly env: "THREADBASE_FEATURE_PTY_HOST";
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The registry's ids as a union, derived rather than declared.
|
|
72
|
+
*
|
|
73
|
+
* This is what makes `flags.ptyHsot` a compile error instead of `undefined`.
|
|
74
|
+
* Under the old `Record<string, boolean>` an index signature accepted any key,
|
|
75
|
+
* so a typo in a consumer read as falsy and silently disabled the feature it
|
|
76
|
+
* was meant to gate — the exact failure the "total map" contract exists to
|
|
77
|
+
* prevent, reachable through the one door that contract left open.
|
|
78
|
+
*/
|
|
79
|
+
export type FeatureFlagId = keyof typeof FEATURE_FLAGS;
|
|
80
|
+
/**
|
|
81
|
+
* The yaml / `--feature` ids, in registry order.
|
|
82
|
+
*
|
|
83
|
+
* These are the `FEATURE_FLAGS` object keys — not the `THREADBASE_FEATURE_*`
|
|
84
|
+
* env names. `feature_flags: {"ptyHost":true}` is valid;
|
|
85
|
+
* `{"THREADBASE_FEATURE_PTY_HOST":true}` is dropped as unknown.
|
|
86
|
+
*/
|
|
87
|
+
export declare const FEATURE_FLAG_IDS: FeatureFlagId[];
|
|
88
|
+
/**
|
|
89
|
+
* Ordered list for iteration and the HTTP registry.
|
|
90
|
+
*
|
|
91
|
+
* The wire shape stays an array of `{ id, description, default, env }` so a
|
|
92
|
+
* keyed in-process registry is not a breaking GET /api/config/feature-flags
|
|
93
|
+
* change. Each `id` is the matching `FEATURE_FLAGS` key.
|
|
94
|
+
*/
|
|
95
|
+
export declare const FEATURE_FLAG_LIST: readonly FeatureFlagDefinition[];
|
|
96
|
+
export declare function isFeatureFlagId(id: string): id is FeatureFlagId;
|
|
97
|
+
/** Typed lookup. The flag is always present; unknown names do not type-check. */
|
|
98
|
+
export declare function getFeatureFlag<K extends FeatureFlagId>(id: K): {
|
|
99
|
+
id: K;
|
|
100
|
+
} & {
|
|
101
|
+
readonly codexSystemPrompt: {
|
|
102
|
+
readonly description: string;
|
|
103
|
+
readonly default: false;
|
|
104
|
+
readonly env: "THREADBASE_FEATURE_CODEX_SYSTEM_PROMPT";
|
|
105
|
+
};
|
|
106
|
+
readonly sessionRehydration: {
|
|
107
|
+
readonly description: string;
|
|
108
|
+
readonly default: true;
|
|
109
|
+
readonly env: "THREADBASE_FEATURE_SESSION_REHYDRATION";
|
|
110
|
+
};
|
|
111
|
+
readonly liveActivityPush: {
|
|
112
|
+
readonly description: string;
|
|
113
|
+
readonly default: false;
|
|
114
|
+
readonly env: "THREADBASE_FEATURE_LIVE_ACTIVITY_PUSH";
|
|
115
|
+
};
|
|
116
|
+
readonly e2ee: {
|
|
117
|
+
readonly description: string;
|
|
118
|
+
readonly default: false;
|
|
119
|
+
readonly env: "THREADBASE_FEATURE_E2EE";
|
|
120
|
+
};
|
|
121
|
+
readonly ptyHost: {
|
|
122
|
+
readonly description: string;
|
|
123
|
+
readonly default: false;
|
|
124
|
+
readonly env: "THREADBASE_FEATURE_PTY_HOST";
|
|
125
|
+
};
|
|
126
|
+
}[K];
|
|
127
|
+
/**
|
|
128
|
+
* Look a flag up by an unvalidated string (yaml keys, `--feature` tokens).
|
|
129
|
+
*
|
|
130
|
+
* In-process code should use `FEATURE_FLAGS.ptyHost` or `getFeatureFlag("ptyHost")`.
|
|
131
|
+
*/
|
|
132
|
+
export declare function findFeatureFlag(id: string): FeatureFlagDefinition | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* Parse a boolean env var, tri-state.
|
|
135
|
+
*
|
|
136
|
+
* `undefined` means "this variable did not speak" — distinct from `false` — so
|
|
137
|
+
* an unset var lets the next precedence rung (CLI, then yaml) decide instead of
|
|
138
|
+
* silently forcing the flag off.
|
|
139
|
+
*
|
|
140
|
+
* That tri-state return is why this does not reuse one of the existing env
|
|
141
|
+
* parsers (`parseIncludeAgentsEnv` in server.ts, `isTruthy` in
|
|
142
|
+
* agent/agent-config.ts, the inline sets in api/middleware/cors.middleware.ts):
|
|
143
|
+
* all three collapse "absent" into a boolean, which is exactly the distinction
|
|
144
|
+
* the precedence chain needs. Consolidating those three is a separate change.
|
|
145
|
+
*/
|
|
146
|
+
export declare function parseBooleanEnv(raw: string | undefined): boolean | undefined;
|
|
147
|
+
/**
|
|
148
|
+
* Drop everything that isn't a known id carrying a boolean.
|
|
149
|
+
*
|
|
150
|
+
* A TRUST BOUNDARY, mirroring validateFlagValues() in claude-flags.ts: values
|
|
151
|
+
* arrive from a user-editable server.yaml. Unknown ids and ill-typed values are
|
|
152
|
+
* dropped with a warning rather than throwing, so one stale or fat-fingered key
|
|
153
|
+
* can never stop the server from booting.
|
|
154
|
+
*
|
|
155
|
+
* Note the deliberate non-coercion: `"true"` (a string) is dropped, not read as
|
|
156
|
+
* true. A value that isn't already a boolean means the writer misunderstood the
|
|
157
|
+
* format, and guessing at intent is how a flag silently ends up on.
|
|
158
|
+
*/
|
|
159
|
+
export declare function validateFeatureFlagValues(raw: unknown): FeatureFlagValues;
|
|
160
|
+
/**
|
|
161
|
+
* Parse repeatable `--feature <id=bool>` CLI tokens.
|
|
162
|
+
*
|
|
163
|
+
* Errors are returned rather than thrown so the caller owns the exit: a typo on
|
|
164
|
+
* the command line should print one legible message and stop, not surface a
|
|
165
|
+
* stack trace. Unlike the yaml path this is strict — a CLI typo is a mistake the
|
|
166
|
+
* operator is standing right there to fix, whereas a stale yaml key must not
|
|
167
|
+
* block an unattended boot.
|
|
168
|
+
*/
|
|
169
|
+
export declare function parseFeatureFlagArgs(entries: string[]): {
|
|
170
|
+
values: FeatureFlagValues;
|
|
171
|
+
errors: string[];
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Resolve every flag once, at boot. Precedence, highest first:
|
|
175
|
+
*
|
|
176
|
+
* override → env → CLI → server.yaml → registry default
|
|
177
|
+
*
|
|
178
|
+
* Env beats the CLI so an operator can flip a flag on a supervised instance
|
|
179
|
+
* (launchd/systemd/Task Scheduler) whose argv is fixed — the same reason
|
|
180
|
+
* THREADBASE_ALLOW_BROWSER_CORS overrides browser_cors: in server.yaml.
|
|
181
|
+
*
|
|
182
|
+
* `override` is the legacy explicit ServerConfig field (codexSystemPromptEnabled),
|
|
183
|
+
* kept so embedders and tests that set it directly keep working. It is a real
|
|
184
|
+
* rung here rather than a mutation applied to the finished map afterwards: the
|
|
185
|
+
* old shape meant the resolver was not actually the single source of truth, and
|
|
186
|
+
* an override was invisible to anything reporting where a value came from.
|
|
187
|
+
*
|
|
188
|
+
* The returned `values` map is TOTAL: every registry id is present, defaults
|
|
189
|
+
* filled in. Callers therefore index it without a `?? default`, and a flag added
|
|
190
|
+
* later cannot reach a boolean branch as `undefined` just because an older
|
|
191
|
+
* server.yaml predates it. `sources` is total for the same reason.
|
|
192
|
+
*/
|
|
193
|
+
export declare function resolveFeatureFlags(opts?: {
|
|
194
|
+
override?: FeatureFlagValues;
|
|
195
|
+
cli?: FeatureFlagValues;
|
|
196
|
+
yaml?: FeatureFlagValues;
|
|
197
|
+
env?: NodeJS.ProcessEnv;
|
|
198
|
+
}): FeatureFlagResolution;
|
|
199
|
+
/** Registry ids whose resolved value differs from the registry default. */
|
|
200
|
+
export declare function nonDefaultFeatureFlags(values: ResolvedFeatureFlags): FeatureFlagId[];
|
|
201
|
+
/**
|
|
202
|
+
* One line describing the whole resolution: `id=value(source)`, every flag.
|
|
203
|
+
*
|
|
204
|
+
* Replaces a boot log that printed only the ids differing from their defaults
|
|
205
|
+
* under the heading "Feature flags active". That heading was wrong for any
|
|
206
|
+
* flag whose default is ON — disabling `sessionRehydration` made it appear in
|
|
207
|
+
* a list of active flags, stating the opposite of what had happened. Printing
|
|
208
|
+
* the value removes the ambiguity, and printing every flag means the log
|
|
209
|
+
* answers "what was this process running with" instead of only ever hinting.
|
|
210
|
+
*/
|
|
211
|
+
export declare function describeFeatureFlags(resolution: FeatureFlagResolution): string;
|
|
212
|
+
//# sourceMappingURL=feature-flags.d.ts.map
|