@antiphony/shared 0.5.0 → 0.5.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.
@@ -94,6 +94,30 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
94
94
  * (i.e. trim). Absent when the variant's duration matches the original.
95
95
  */
96
96
  processedDurationMs: zod.z.number().int().min(0).optional(),
97
+ /**
98
+ * Which denoiser produced the variant's denoise contribution — provenance,
99
+ * the counterpart to a transcript record's `model`.
100
+ *
101
+ * Lives here because a cleaned variant, unlike a transcript, has no record
102
+ * of its own to carry it: it is a blob CID on this state. Without it,
103
+ * changing denoisers leaves no way to tell which variants predate the
104
+ * switch, so nothing can identify what to re-run.
105
+ *
106
+ * Named for the STAGE, not the variant (`processedModel`), because it
107
+ * describes one link of the byte-mutating chain rather than the composed
108
+ * artifact. Trim contributes to the same variant and has no model, and a
109
+ * later external link would want its own field rather than to overwrite
110
+ * this one.
111
+ *
112
+ * Written on every successful denoise, never cleared — it moves with
113
+ * `processedBlobCid`, which is only ever set, never reset. A denoise that
114
+ * FAILS leaves both alone, which is correct: the variant still holds the
115
+ * previous denoiser's output, so the previous model still describes it.
116
+ *
117
+ * Internal, like the other variant fields — `toProcessingView` projects
118
+ * stages only, so this never reaches a client.
119
+ */
120
+ denoiseModel: zod.z.string().optional(),
97
121
  /**
98
122
  * Peaks for the processed variant, once the `waveform` stage completes.
99
123
  * Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
@@ -291,10 +315,15 @@ var ViewerStateSchema = zod.z.object({
291
315
  replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
292
316
  });
293
317
  var PostRecordPublicSchema = zod.z.object({
294
- text: zod.z.string(),
295
- title: zod.z.string().optional(),
318
+ // `text`, `title` and `langs` keep the record's bounds, for the same reason
319
+ // `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
320
+ // larger payload than the record it projects. The write path already
321
+ // enforces these, so no stored post can exceed them — stating them here
322
+ // keeps the published contract honest rather than adding a new constraint.
323
+ text: zod.z.string().max(3e3),
324
+ title: zod.z.string().max(3e3).optional(),
296
325
  reply: ReplyRefSchema.optional(),
297
- langs: zod.z.array(zod.z.string()).optional(),
326
+ langs: zod.z.array(zod.z.string()).max(3).optional(),
298
327
  selfLabels: zod.z.array(zod.z.string()).optional(),
299
328
  createdAt: FirestoreTimestampSchema
300
329
  });
@@ -111,6 +111,30 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
111
111
  * (i.e. trim). Absent when the variant's duration matches the original.
112
112
  */
113
113
  processedDurationMs: zod.z.number().int().min(0).optional(),
114
+ /**
115
+ * Which denoiser produced the variant's denoise contribution — provenance,
116
+ * the counterpart to a transcript record's `model`.
117
+ *
118
+ * Lives here because a cleaned variant, unlike a transcript, has no record
119
+ * of its own to carry it: it is a blob CID on this state. Without it,
120
+ * changing denoisers leaves no way to tell which variants predate the
121
+ * switch, so nothing can identify what to re-run.
122
+ *
123
+ * Named for the STAGE, not the variant (`processedModel`), because it
124
+ * describes one link of the byte-mutating chain rather than the composed
125
+ * artifact. Trim contributes to the same variant and has no model, and a
126
+ * later external link would want its own field rather than to overwrite
127
+ * this one.
128
+ *
129
+ * Written on every successful denoise, never cleared — it moves with
130
+ * `processedBlobCid`, which is only ever set, never reset. A denoise that
131
+ * FAILS leaves both alone, which is correct: the variant still holds the
132
+ * previous denoiser's output, so the previous model still describes it.
133
+ *
134
+ * Internal, like the other variant fields — `toProcessingView` projects
135
+ * stages only, so this never reaches a client.
136
+ */
137
+ denoiseModel: zod.z.string().optional(),
114
138
  /**
115
139
  * Peaks for the processed variant, once the `waveform` stage completes.
116
140
  * Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
@@ -326,10 +350,15 @@ var ViewerStateSchema = zod.z.object({
326
350
  replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
327
351
  });
328
352
  var PostRecordPublicSchema = zod.z.object({
329
- text: zod.z.string(),
330
- title: zod.z.string().optional(),
353
+ // `text`, `title` and `langs` keep the record's bounds, for the same reason
354
+ // `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
355
+ // larger payload than the record it projects. The write path already
356
+ // enforces these, so no stored post can exceed them — stating them here
357
+ // keeps the published contract honest rather than adding a new constraint.
358
+ text: zod.z.string().max(3e3),
359
+ title: zod.z.string().max(3e3).optional(),
331
360
  reply: ReplyRefSchema.optional(),
332
- langs: zod.z.array(zod.z.string()).optional(),
361
+ langs: zod.z.array(zod.z.string()).max(3).optional(),
333
362
  selfLabels: zod.z.array(zod.z.string()).optional(),
334
363
  createdAt: FirestoreTimestampSchema
335
364
  });
@@ -401,6 +430,15 @@ var EMBED_NSID = {
401
430
  Audio: "dev.antiphony.embed.audio",
402
431
  RecordWithAudio: "dev.antiphony.embed.recordWithAudio"
403
432
  };
433
+ var XRPC_NSID = {
434
+ // Queries (GET).
435
+ GetPost: "dev.antiphony.audio.getPost",
436
+ GetThread: "dev.antiphony.audio.getThread",
437
+ GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl",
438
+ // Procedures (POST).
439
+ CreatePost: "dev.antiphony.audio.createPost",
440
+ ReprocessPost: "dev.antiphony.audio.reprocessPost"
441
+ };
404
442
  var COLLECTIONS = {
405
443
  // One post collection + the transcript enrichment namespace.
406
444
  [NSID.AudioPost]: "posts",
@@ -532,6 +570,7 @@ exports.TranscriptSegmentSchema = TranscriptSegmentSchema;
532
570
  exports.UnauthorizedError = UnauthorizedError;
533
571
  exports.ValidationError = ValidationError;
534
572
  exports.ViewerStateSchema = ViewerStateSchema;
573
+ exports.XRPC_NSID = XRPC_NSID;
535
574
  exports.buildReportedErrorEvent = buildReportedErrorEvent;
536
575
  exports.isFirestoreTimestamp = isFirestoreTimestamp;
537
576
  exports.reportError = reportError;
@@ -1,314 +1,10 @@
1
1
  export { CreateAudioPostRequest, CreateAudioPostRequestSchema, PatchAudioPostRequest, PatchAudioPostRequestSchema } from './api-codecs.cjs';
2
2
  export { FirestoreTimestamp, FirestoreTimestampSchema } from './types/records.cjs';
3
3
  export { ActorProfileRecord, ActorProfileRecordSchema, AudioEmbed, AudioEmbedSchema, AudioEmbedView, AudioEmbedViewSchema, AudioPostRecord, AudioPostRecordSchema, AudioPostView, AudioPostViewSchema, PostRecordPublic, PostRecordPublicSchema, ReplyRef, ReplyRefSchema, StrongRef, StrongRefSchema, TimedTranscript, TimedTranscriptSchema, TranscriptEnrichmentRecord, TranscriptEnrichmentRecordSchema, TranscriptSegment, TranscriptSegmentSchema, ViewerState, ViewerStateSchema } from './types/audio.cjs';
4
- import { z } from 'zod';
4
+ export { BYTE_MUTATING_STAGES, CanonicalAudioFields, DERIVED_STAGES, PROCESSING_STAGES, ProcessingRequest, ProcessingRequestSchema, ProcessingStage, ProcessingStageMap, ProcessingStageMapSchema, ProcessingStageSchema, ProcessingStageStatus, ProcessingStageStatusSchema, ProcessingState, ProcessingStateSchema, ProcessingView, ProcessingViewSchema, ResolvedProcessing, ResolvedProcessingSchema, resolveAudioVariant, toProcessingView } from './types/processing.cjs';
5
5
  export { BlobRef, BlobRefSchema } from './types/blob.cjs';
6
- export { COLLECTIONS, EMBED_NSID, NSID, NsidValue, StoredNsidValue } from './nsid.cjs';
6
+ export { COLLECTIONS, EMBED_NSID, NSID, NsidValue, StoredNsidValue, XRPC_NSID, XrpcNsidValue } from './nsid.cjs';
7
7
  export { ConflictError, ForbiddenError, NotFoundError, RateLimitError, ServiceError, UnauthorizedError, ValidationError } from './errors/index.cjs';
8
8
  export { isFirestoreTimestamp } from './utils/index.cjs';
9
9
  export { ReportedErrorEvent, buildReportedErrorEvent, reportError } from './observability/report-error.cjs';
10
-
11
- /**
12
- * Audio hygiene / enrichment processing (B5).
13
- *
14
- * Antiphony can, when the calling app opts in, run audio processing on a
15
- * post's audio. Four stages, classified on two axes (see
16
- * `specs/enrichment-pipeline.md`):
17
- *
18
- * - **Byte-mutating** (`denoise`, `trim`) produce new audio. They compose in
19
- * order into a SINGLE processed variant — trimmed-and-denoised audio is one
20
- * artifact, not two — addressed by `processedBlobCid`.
21
- * - **Derived** (`transcribe`, `waveform`) are pure analysis over the final
22
- * variant and modify no audio. Because they are a function of the variant,
23
- * recomputation is always the correct response to their input changing.
24
- *
25
- * Every stage is OFF by default — the app asks for them per post via
26
- * `CreateAudioPostRequest.processing`, or after the fact via the `processing`
27
- * opt-in on `PATCH /api/v1/posts/{postId}`.
28
- *
29
- * The work runs asynchronously (outside the create request), so a post
30
- * carries a mutable `processing` state that starts `pending` and settles to
31
- * `ready`/`failed`/`skipped` per stage. That state is a storage-layer field —
32
- * it is NOT part of the canonical lexicon record and never enters the record
33
- * CID (like `kind` and `threadParticipants`), so processing can update it
34
- * without changing the post's content address.
35
- *
36
- * That immutability is why stage OUTPUT lives here rather than on the embed:
37
- * `embed.audio.ref.$link`, `embed.durationMs`, and `embed.waveform` are inside
38
- * the CID and can never be rewritten. The read-time view resolves per field
39
- * between the record's canonical values and the variant values below.
40
- */
41
- /**
42
- * Per-stage status.
43
- * - `pending` — requested, not yet done (the worker acts on these).
44
- * - `ready` — completed.
45
- * - `failed` — attempted and errored.
46
- * - `skipped` — requested but this deployment has no provider for it.
47
- *
48
- * A stage returning to `pending` after having been `ready` is NORMAL, not a
49
- * regression: a byte-mutating stage completing invalidates derived artifacts,
50
- * which are then recomputed. Clients treat any `pending` stage as "still
51
- * working", which already covers this.
52
- */
53
- declare const ProcessingStageStatusSchema: z.ZodEnum<["pending", "ready", "failed", "skipped"]>;
54
- type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>;
55
- /**
56
- * The stage names, in the order a multi-stage request runs them:
57
- * denoise → trim → (transcribe, waveform).
58
- *
59
- * All byte-mutating stages run first; the two derived stages then consume the
60
- * final variant and are mutually independent. Denoise precedes trim
61
- * deliberately — silence detection keys off a noise floor, so on noisy input
62
- * the "silence" is not actually quiet and trim under-cuts.
63
- */
64
- declare const PROCESSING_STAGES: readonly ["denoise", "trim", "transcribe", "waveform"];
65
- declare const ProcessingStageSchema: z.ZodEnum<["denoise", "trim", "transcribe", "waveform"]>;
66
- type ProcessingStage = z.infer<typeof ProcessingStageSchema>;
67
- /** Stages that produce new audio bytes, composing into one processed variant. */
68
- declare const BYTE_MUTATING_STAGES: readonly ["denoise", "trim"];
69
- /** Stages that are pure analysis over the final variant, modifying no audio. */
70
- declare const DERIVED_STAGES: readonly ["transcribe", "waveform"];
71
- /**
72
- * What the calling app opts into, on `CreateAudioPostRequest`. All default
73
- * off; only `true` values request a stage.
74
- */
75
- declare const ProcessingRequestSchema: z.ZodObject<{
76
- transcribe: z.ZodOptional<z.ZodBoolean>;
77
- denoise: z.ZodOptional<z.ZodBoolean>;
78
- trim: z.ZodOptional<z.ZodBoolean>;
79
- waveform: z.ZodOptional<z.ZodBoolean>;
80
- /**
81
- * Whether a completed byte-mutating stage should invalidate and recompute
82
- * the derived artifacts that describe the old audio. Defaults to **true**
83
- * — a transcript of superseded audio is wrong, not merely stale.
84
- *
85
- * `false` opts out, for an app that would rather keep the existing
86
- * transcript than pay to regenerate it. It does NOT name a stage, so a
87
- * request carrying only `reprocess` requests no work.
88
- */
89
- reprocess: z.ZodOptional<z.ZodBoolean>;
90
- }, "strip", z.ZodTypeAny, {
91
- denoise?: boolean | undefined;
92
- trim?: boolean | undefined;
93
- transcribe?: boolean | undefined;
94
- waveform?: boolean | undefined;
95
- reprocess?: boolean | undefined;
96
- }, {
97
- denoise?: boolean | undefined;
98
- trim?: boolean | undefined;
99
- transcribe?: boolean | undefined;
100
- waveform?: boolean | undefined;
101
- reprocess?: boolean | undefined;
102
- }>;
103
- type ProcessingRequest = z.infer<typeof ProcessingRequestSchema>;
104
- /**
105
- * Per-stage status across all stages — the shape shared by the stored state,
106
- * the hydrated view, and the resolved-initial-state handoff between the route
107
- * and the service. A key is present iff that stage was requested.
108
- */
109
- declare const ProcessingStageMapSchema: z.ZodObject<{
110
- transcribe: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
111
- denoise: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
112
- trim: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
113
- waveform: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
114
- }, "strip", z.ZodTypeAny, {
115
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
116
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
117
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
118
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
119
- }, {
120
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
121
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
122
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
123
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
124
- }>;
125
- type ProcessingStageMap = z.infer<typeof ProcessingStageMapSchema>;
126
- /**
127
- * An opt-in request resolved against a deployment's capabilities: the initial
128
- * per-stage state plus the settings the async worker needs to honour it.
129
- *
130
- * `reprocess` is carried here — and persisted — rather than passed to the
131
- * worker as an argument, because the request that asks for the work and the
132
- * pass that performs it are separated by a queue (step 8). Written on every
133
- * request including the default, because the stored state is MERGED onto —
134
- * absent means true only for posts written before this field existed.
135
- */
136
- declare const ResolvedProcessingSchema: z.ZodObject<{
137
- transcribe: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
138
- denoise: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
139
- trim: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
140
- waveform: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
141
- } & {
142
- reprocess: z.ZodOptional<z.ZodBoolean>;
143
- }, "strip", z.ZodTypeAny, {
144
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
145
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
146
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
147
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
148
- reprocess?: boolean | undefined;
149
- }, {
150
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
151
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
152
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
153
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
154
- reprocess?: boolean | undefined;
155
- }>;
156
- type ResolvedProcessing = z.infer<typeof ResolvedProcessingSchema>;
157
- /**
158
- * Stored processing state on the post record (storage-layer; not in the CID):
159
- * the per-stage statuses plus the output of the stages themselves.
160
- *
161
- * The variant fields below all exist for the same reason — their canonical
162
- * counterparts live inside the record CID and cannot be updated:
163
- *
164
- * - `processedBlobCid` ↔ `embed.audio.ref.$link`
165
- * - `processedMimeType` ↔ `embed.audio.mimeType` (providers may transcode)
166
- * - `processedDurationMs` ↔ `embed.durationMs` (trim changes duration)
167
- * - `waveformPeaks` ↔ `embed.waveform` (the client's peaks describe the original)
168
- */
169
- declare const ProcessingStateSchema: z.ZodObject<{
170
- transcribe: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
171
- denoise: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
172
- trim: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
173
- waveform: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
174
- } & {
175
- reprocess: z.ZodOptional<z.ZodBoolean>;
176
- } & {
177
- /**
178
- * Content CID of the processed audio variant — the composed output of every
179
- * byte-mutating stage that has completed. The record's own
180
- * `embed.audio.ref.$link` stays the ORIGINAL CID (immutable content
181
- * address); only the read-time view swaps playback to this variant.
182
- */
183
- processedBlobCid: z.ZodOptional<z.ZodString>;
184
- /**
185
- * MIME type of the processed variant. Present because providers may
186
- * TRANSCODE — the ElevenLabs Voice Isolator returns MP3 regardless of what
187
- * it is given — so the variant's type cannot be assumed to match
188
- * `embed.audio.mimeType`. Anything reading the variant's bytes must use
189
- * this, not the embed's.
190
- */
191
- processedMimeType: z.ZodOptional<z.ZodString>;
192
- /**
193
- * Duration of the processed variant, when a byte-mutating stage changed it
194
- * (i.e. trim). Absent when the variant's duration matches the original.
195
- */
196
- processedDurationMs: z.ZodOptional<z.ZodNumber>;
197
- /**
198
- * Peaks for the processed variant, once the `waveform` stage completes.
199
- * Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
200
- * a view can never carry a larger payload than the record allows.
201
- */
202
- waveformPeaks: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
203
- /**
204
- * When the current runner's exclusive claim on this post expires.
205
- *
206
- * Queue delivery is at-least-once, so the same job can arrive twice and
207
- * run CONCURRENTLY. `process()` is idempotent under sequential retry — it
208
- * acts on `pending` and re-does nothing already settled — but two passes
209
- * interleaved is a different failure: both read the same `pending` state,
210
- * both bill the provider for the same stage, and both write
211
- * `processedBlobCid`, so the surviving variant is whichever finished last
212
- * and the other's blob is orphaned.
213
- *
214
- * A runner claims this field transactionally before doing any work and
215
- * clears it when finished; a second runner finding it unexpired declines
216
- * and returns. It is an EXPIRY, not a boolean lock, because the holder can
217
- * die mid-run (instance recycled, process killed) with no chance to
218
- * release — a plain flag would strand the post permanently, where a lapsed
219
- * lease lets the next delivery pick it up.
220
- *
221
- * Internal, like the variant fields above: `toProcessingView` projects
222
- * stages only, so this never reaches a client.
223
- */
224
- leaseUntil: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodType<unknown, z.ZodTypeDef, unknown>, z.ZodString, z.ZodNumber, z.ZodDate]>, Date, unknown>>;
225
- updatedAt: z.ZodEffects<z.ZodUnion<[z.ZodType<unknown, z.ZodTypeDef, unknown>, z.ZodString, z.ZodNumber, z.ZodDate]>, Date, unknown>;
226
- }, "strip", z.ZodTypeAny, {
227
- updatedAt: Date;
228
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
229
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
230
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
231
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
232
- reprocess?: boolean | undefined;
233
- processedBlobCid?: string | undefined;
234
- processedMimeType?: string | undefined;
235
- processedDurationMs?: number | undefined;
236
- waveformPeaks?: number[] | undefined;
237
- leaseUntil?: Date | undefined;
238
- }, {
239
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
240
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
241
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
242
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
243
- reprocess?: boolean | undefined;
244
- processedBlobCid?: string | undefined;
245
- processedMimeType?: string | undefined;
246
- processedDurationMs?: number | undefined;
247
- waveformPeaks?: number[] | undefined;
248
- leaseUntil?: unknown;
249
- updatedAt?: unknown;
250
- }>;
251
- type ProcessingState = z.infer<typeof ProcessingStateSchema>;
252
- /**
253
- * The processing status surfaced on the hydrated view — the per-stage status
254
- * only (no internal storage fields: variant CID, duration, peaks, timestamps).
255
- * Absent when no processing was requested.
256
- */
257
- declare const ProcessingViewSchema: z.ZodObject<{
258
- transcribe: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
259
- denoise: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
260
- trim: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
261
- waveform: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
262
- }, "strip", z.ZodTypeAny, {
263
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
264
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
265
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
266
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
267
- }, {
268
- denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
269
- trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
270
- transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
271
- waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
272
- }>;
273
- type ProcessingView = z.infer<typeof ProcessingViewSchema>;
274
- /**
275
- * Project the stored state onto the view — drops every internal field.
276
- *
277
- * Derived from `PROCESSING_STAGES` rather than listing the stages by hand, so
278
- * a stage added to the set cannot be silently omitted from the view (which
279
- * would leave clients unable to tell "not requested" from "in progress").
280
- */
281
- declare function toProcessingView(state: ProcessingState): ProcessingView;
282
- /** The record's own audio fields — canonical, inside the CID, never rewritten. */
283
- interface CanonicalAudioFields {
284
- blobCid: string;
285
- durationMs?: number;
286
- waveform?: number[];
287
- }
288
- /**
289
- * Resolve the audio fields a reader should see: canonical from the record,
290
- * variant from `ProcessingState` wherever processing has superseded it.
291
- *
292
- * The three fields have to move together. Peaks are rendered ACROSS a duration
293
- * and a duration describes a specific set of bytes, so serving a processed URL
294
- * beside the original duration puts a scrubber out of alignment with the audio
295
- * under it — the failure this function exists to prevent.
296
- *
297
- * Resolution is not a uniform `??` per field, because the state's fields do not
298
- * all mean the same thing when absent:
299
- *
300
- * - `processedDurationMs` absent is DEFINED as "the variant's duration equals
301
- * the original" (denoise transcodes without retiming), so falling back to
302
- * the record there is correct, not a guess.
303
- * - `waveformPeaks` carries no such guarantee. Recompute marks the stage
304
- * `pending` without clearing the field, so between a variant change and the
305
- * recomputed peaks landing it holds peaks for the SUPERSEDED variant. Hence
306
- * the status gate rather than a presence check.
307
- *
308
- * Peaks do not key off `processedBlobCid`: `waveform` always runs over the
309
- * final variant, so when it is `ready` its peaks describe whatever playback
310
- * resolves to here, variant or original alike.
311
- */
312
- declare function resolveAudioVariant(canonical: CanonicalAudioFields, state: ProcessingState | undefined): CanonicalAudioFields;
313
-
314
- export { BYTE_MUTATING_STAGES, type CanonicalAudioFields, DERIVED_STAGES, PROCESSING_STAGES, type ProcessingRequest, ProcessingRequestSchema, type ProcessingStage, type ProcessingStageMap, ProcessingStageMapSchema, ProcessingStageSchema, type ProcessingStageStatus, ProcessingStageStatusSchema, type ProcessingState, ProcessingStateSchema, type ProcessingView, ProcessingViewSchema, type ResolvedProcessing, ResolvedProcessingSchema, resolveAudioVariant, toProcessingView };
10
+ import 'zod';
package/dist/cjs/nsid.cjs CHANGED
@@ -16,6 +16,15 @@ var EMBED_NSID = {
16
16
  Audio: "dev.antiphony.embed.audio",
17
17
  RecordWithAudio: "dev.antiphony.embed.recordWithAudio"
18
18
  };
19
+ var XRPC_NSID = {
20
+ // Queries (GET).
21
+ GetPost: "dev.antiphony.audio.getPost",
22
+ GetThread: "dev.antiphony.audio.getThread",
23
+ GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl",
24
+ // Procedures (POST).
25
+ CreatePost: "dev.antiphony.audio.createPost",
26
+ ReprocessPost: "dev.antiphony.audio.reprocessPost"
27
+ };
19
28
  var COLLECTIONS = {
20
29
  // One post collection + the transcript enrichment namespace.
21
30
  [NSID.AudioPost]: "posts",
@@ -25,3 +34,4 @@ var COLLECTIONS = {
25
34
  exports.COLLECTIONS = COLLECTIONS;
26
35
  exports.EMBED_NSID = EMBED_NSID;
27
36
  exports.NSID = NSID;
37
+ exports.XRPC_NSID = XRPC_NSID;
@@ -23,6 +23,30 @@ declare const EMBED_NSID: {
23
23
  readonly Audio: "dev.antiphony.embed.audio";
24
24
  readonly RecordWithAudio: "dev.antiphony.embed.recordWithAudio";
25
25
  };
26
+ /**
27
+ * XRPC method NSIDs — the `/xrpc/<nsid>` surface (see
28
+ * specs/xrpc-and-atproto-lex-strategy.md).
29
+ *
30
+ * These are **siblings** of the record NSIDs in `NSID`, not children of them.
31
+ * A method shares the authority segment (`dev.antiphony.audio`) with the record
32
+ * but never nests under the record's own name: the record is
33
+ * `dev.antiphony.audio.post` and the query that fetches it is
34
+ * `dev.antiphony.audio.getPost`. Deriving one by appending to the other
35
+ * produces `dev.antiphony.audio.post.getPost`, a different and undefined
36
+ * namespace — hence a separate map rather than a helper over `NSID`.
37
+ *
38
+ * Queries are `GET`, procedures are `POST`; the grouping below follows that
39
+ * split because it is also the auth split (procedures always require an acting
40
+ * actor, queries may be viewer-less).
41
+ */
42
+ declare const XRPC_NSID: {
43
+ readonly GetPost: "dev.antiphony.audio.getPost";
44
+ readonly GetThread: "dev.antiphony.audio.getThread";
45
+ readonly GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl";
46
+ readonly CreatePost: "dev.antiphony.audio.createPost";
47
+ readonly ReprocessPost: "dev.antiphony.audio.reprocessPost";
48
+ };
49
+ type XrpcNsidValue = typeof XRPC_NSID[keyof typeof XRPC_NSID];
26
50
  /**
27
51
  * Maps the STORED AT Protocol record-type NSIDs to Firestore collection
28
52
  * names. When migrating to a PDS, this mapping becomes the adapter layer.
@@ -30,4 +54,4 @@ declare const EMBED_NSID: {
30
54
  */
31
55
  declare const COLLECTIONS: Record<StoredNsidValue, string>;
32
56
 
33
- export { COLLECTIONS, EMBED_NSID, NSID, type NsidValue, type StoredNsidValue };
57
+ export { COLLECTIONS, EMBED_NSID, NSID, type NsidValue, type StoredNsidValue, XRPC_NSID, type XrpcNsidValue };
@@ -94,6 +94,30 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
94
94
  * (i.e. trim). Absent when the variant's duration matches the original.
95
95
  */
96
96
  processedDurationMs: zod.z.number().int().min(0).optional(),
97
+ /**
98
+ * Which denoiser produced the variant's denoise contribution — provenance,
99
+ * the counterpart to a transcript record's `model`.
100
+ *
101
+ * Lives here because a cleaned variant, unlike a transcript, has no record
102
+ * of its own to carry it: it is a blob CID on this state. Without it,
103
+ * changing denoisers leaves no way to tell which variants predate the
104
+ * switch, so nothing can identify what to re-run.
105
+ *
106
+ * Named for the STAGE, not the variant (`processedModel`), because it
107
+ * describes one link of the byte-mutating chain rather than the composed
108
+ * artifact. Trim contributes to the same variant and has no model, and a
109
+ * later external link would want its own field rather than to overwrite
110
+ * this one.
111
+ *
112
+ * Written on every successful denoise, never cleared — it moves with
113
+ * `processedBlobCid`, which is only ever set, never reset. A denoise that
114
+ * FAILS leaves both alone, which is correct: the variant still holds the
115
+ * previous denoiser's output, so the previous model still describes it.
116
+ *
117
+ * Internal, like the other variant fields — `toProcessingView` projects
118
+ * stages only, so this never reaches a client.
119
+ */
120
+ denoiseModel: zod.z.string().optional(),
97
121
  /**
98
122
  * Peaks for the processed variant, once the `waveform` stage completes.
99
123
  * Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
@@ -291,10 +315,15 @@ var ViewerStateSchema = zod.z.object({
291
315
  replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
292
316
  });
293
317
  var PostRecordPublicSchema = zod.z.object({
294
- text: zod.z.string(),
295
- title: zod.z.string().optional(),
318
+ // `text`, `title` and `langs` keep the record's bounds, for the same reason
319
+ // `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
320
+ // larger payload than the record it projects. The write path already
321
+ // enforces these, so no stored post can exceed them — stating them here
322
+ // keeps the published contract honest rather than adding a new constraint.
323
+ text: zod.z.string().max(3e3),
324
+ title: zod.z.string().max(3e3).optional(),
296
325
  reply: ReplyRefSchema.optional(),
297
- langs: zod.z.array(zod.z.string()).optional(),
326
+ langs: zod.z.array(zod.z.string()).max(3).optional(),
298
327
  selfLabels: zod.z.array(zod.z.string()).optional(),
299
328
  createdAt: FirestoreTimestampSchema
300
329
  });
@@ -402,6 +402,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
402
402
  processedBlobCid: z.ZodOptional<z.ZodString>;
403
403
  processedMimeType: z.ZodOptional<z.ZodString>;
404
404
  processedDurationMs: z.ZodOptional<z.ZodNumber>;
405
+ denoiseModel: z.ZodOptional<z.ZodString>;
405
406
  waveformPeaks: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
406
407
  leaseUntil: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodType<unknown, z.ZodTypeDef, unknown>, z.ZodString, z.ZodNumber, z.ZodDate]>, Date, unknown>>;
407
408
  updatedAt: z.ZodEffects<z.ZodUnion<[z.ZodType<unknown, z.ZodTypeDef, unknown>, z.ZodString, z.ZodNumber, z.ZodDate]>, Date, unknown>;
@@ -415,6 +416,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
415
416
  processedBlobCid?: string | undefined;
416
417
  processedMimeType?: string | undefined;
417
418
  processedDurationMs?: number | undefined;
419
+ denoiseModel?: string | undefined;
418
420
  waveformPeaks?: number[] | undefined;
419
421
  leaseUntil?: Date | undefined;
420
422
  }, {
@@ -426,6 +428,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
426
428
  processedBlobCid?: string | undefined;
427
429
  processedMimeType?: string | undefined;
428
430
  processedDurationMs?: number | undefined;
431
+ denoiseModel?: string | undefined;
429
432
  waveformPeaks?: number[] | undefined;
430
433
  leaseUntil?: unknown;
431
434
  updatedAt?: unknown;
@@ -562,6 +565,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
562
565
  processedBlobCid?: string | undefined;
563
566
  processedMimeType?: string | undefined;
564
567
  processedDurationMs?: number | undefined;
568
+ denoiseModel?: string | undefined;
565
569
  waveformPeaks?: number[] | undefined;
566
570
  leaseUntil?: Date | undefined;
567
571
  } | undefined;
@@ -612,6 +616,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
612
616
  processedBlobCid?: string | undefined;
613
617
  processedMimeType?: string | undefined;
614
618
  processedDurationMs?: number | undefined;
619
+ denoiseModel?: string | undefined;
615
620
  waveformPeaks?: number[] | undefined;
616
621
  leaseUntil?: unknown;
617
622
  updatedAt?: unknown;
@@ -666,6 +671,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
666
671
  processedBlobCid?: string | undefined;
667
672
  processedMimeType?: string | undefined;
668
673
  processedDurationMs?: number | undefined;
674
+ denoiseModel?: string | undefined;
669
675
  waveformPeaks?: number[] | undefined;
670
676
  leaseUntil?: Date | undefined;
671
677
  } | undefined;
@@ -716,6 +722,7 @@ declare const AudioPostRecordSchema: z.ZodEffects<z.ZodObject<{
716
722
  processedBlobCid?: string | undefined;
717
723
  processedMimeType?: string | undefined;
718
724
  processedDurationMs?: number | undefined;
725
+ denoiseModel?: string | undefined;
719
726
  waveformPeaks?: number[] | undefined;
720
727
  leaseUntil?: unknown;
721
728
  updatedAt?: unknown;