@rivet-dev/pi 0.5.4 → 0.5.6

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/tracing.js CHANGED
@@ -1,4 +1,255 @@
1
- import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
1
+ import { withTelemetry } from "@earendil-works/chord/context";
2
+ import { context, createTraceState, isSpanContextValid, ROOT_CONTEXT, SpanKind, SpanStatusCode, trace, } from "@opentelemetry/api";
3
+ const TRACER_NAME = "@rivet-dev/pi";
4
+ const TRACEPARENT = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
5
+ /** OpenTelemetry must never break a run, so a call into it that throws does nothing. */
6
+ function guarded(call) {
7
+ try {
8
+ return call();
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ /** Runs `body` with `active` as the active context, and without it if entering it throws. */
15
+ function runActive(active, body) {
16
+ let entered = false;
17
+ try {
18
+ return context.with(active, () => {
19
+ entered = true;
20
+ return body();
21
+ });
22
+ }
23
+ catch (error) {
24
+ if (entered)
25
+ throw error;
26
+ return body();
27
+ }
28
+ }
29
+ /** The spans Pi's model layer emits. Users see each as `chat <model>`. */
30
+ const MODEL_SPANS = new Set([
31
+ "pi.ai.request",
32
+ "pi.ai.poll",
33
+ ]);
34
+ /**
35
+ * Pi's model span attributes under the names users build dashboards on, from
36
+ * OpenTelemetry's GenAI conventions. Input tokens include cached ones, as those
37
+ * conventions ask. A deferred or pending response has no usage to report yet.
38
+ */
39
+ function modelAttributes(attributes = {}) {
40
+ const hasUsage = attributes.stop !== "deferred" && attributes.stop !== "pending";
41
+ const input = attributes["usage.input"];
42
+ return {
43
+ "gen_ai.provider.name": attributes.provider,
44
+ "gen_ai.request.model": attributes.model,
45
+ ...(hasUsage && {
46
+ "gen_ai.usage.input_tokens": typeof input === "number"
47
+ ? input +
48
+ Number(attributes["usage.cache_read"] ?? 0) +
49
+ Number(attributes["usage.cache_write"] ?? 0)
50
+ : undefined,
51
+ "gen_ai.usage.output_tokens": attributes["usage.output"],
52
+ "gen_ai.usage.cost_usd": attributes["cost.total"],
53
+ }),
54
+ };
55
+ }
56
+ function attributesOf(attributes) {
57
+ const defined = {};
58
+ for (const [name, value] of Object.entries(attributes ?? {})) {
59
+ if (value === undefined)
60
+ continue;
61
+ defined[name] = (Array.isArray(value) ? [...value] : value);
62
+ }
63
+ return defined;
64
+ }
65
+ /** A parent for new spans: the root, or a span extracted from a stored carrier. */
66
+ class OtelTelemetry {
67
+ adapter;
68
+ parent;
69
+ propagation;
70
+ constructor(adapter, parent) {
71
+ this.adapter = adapter;
72
+ this.parent = parent;
73
+ this.propagation = adapter.propagation;
74
+ }
75
+ get settled() {
76
+ return false;
77
+ }
78
+ async startSpan(options, callback) {
79
+ const { adapter, parent } = this;
80
+ const isModelSpan = MODEL_SPANS.has(options.name);
81
+ const native = this.settled
82
+ ? undefined
83
+ : guarded(() => adapter.tracer().startSpan(isModelSpan ? `chat ${options.attributes?.model}` : options.name, {
84
+ attributes: {
85
+ ...adapter.attributes,
86
+ ...attributesOf(isModelSpan
87
+ ? {
88
+ "gen_ai.operation.name": "chat",
89
+ ...modelAttributes(options.attributes),
90
+ }
91
+ : options.attributes),
92
+ },
93
+ links: options.links?.flatMap(linksOf),
94
+ },
95
+ // Never the active context, so a background phase does not nest under an unrelated action.
96
+ parent));
97
+ const span = new OtelSpan(adapter, native, isModelSpan);
98
+ try {
99
+ const result = await runActive(native ? trace.setSpan(parent, native) : parent, () => callback(span));
100
+ span.finish();
101
+ return result;
102
+ }
103
+ catch (error) {
104
+ span.finish({
105
+ status: "error",
106
+ error: {
107
+ name: error instanceof Error ? error.name : "Error",
108
+ message: error instanceof Error ? error.message : "",
109
+ },
110
+ });
111
+ throw error;
112
+ }
113
+ }
114
+ }
115
+ class OtelSpan extends OtelTelemetry {
116
+ native;
117
+ isModelSpan;
118
+ #settled = false;
119
+ #status;
120
+ constructor(adapter, native, isModelSpan = false) {
121
+ super(adapter, native ? trace.setSpan(ROOT_CONTEXT, native) : ROOT_CONTEXT);
122
+ this.native = native;
123
+ this.isModelSpan = isModelSpan;
124
+ }
125
+ get settled() {
126
+ return this.#settled;
127
+ }
128
+ #record(call) {
129
+ const { native } = this;
130
+ if (!this.#settled && native)
131
+ guarded(() => call(native));
132
+ }
133
+ addLink(link) {
134
+ for (const otelLink of linksOf(link)) {
135
+ this.#record((native) => native.addLink(otelLink));
136
+ }
137
+ }
138
+ addEvent(name, attributes) {
139
+ this.#record((native) => native.addEvent(name, attributesOf(attributes)));
140
+ }
141
+ setAttributes(attributes) {
142
+ const recorded = this.isModelSpan
143
+ ? modelAttributes(attributes)
144
+ : attributes;
145
+ this.#record((native) => native.setAttributes(attributesOf(recorded)));
146
+ }
147
+ setStatus(status) {
148
+ if (!this.#settled)
149
+ this.#status = status;
150
+ }
151
+ /**
152
+ * Ends the span with the last status set, or else with `thrown`. Success stays
153
+ * Unset. A failure records its name and drops its message, because Pi's
154
+ * diagnostics can contain caller content.
155
+ */
156
+ finish(thrown) {
157
+ const status = this.#status ?? thrown;
158
+ this.#record((native) => {
159
+ if (status?.status !== "error")
160
+ return;
161
+ native.setAttribute("error.type", status.error?.name ?? "_OTHER");
162
+ native.setStatus({
163
+ code: SpanStatusCode.ERROR,
164
+ message: this.adapter.statusMessage?.(status),
165
+ });
166
+ });
167
+ this.#record((native) => native.end());
168
+ this.#settled = true;
169
+ }
170
+ }
171
+ function spanContextOf(telemetry) {
172
+ if (!(telemetry instanceof OtelTelemetry))
173
+ return undefined;
174
+ return guarded(() => {
175
+ const spanContext = trace.getSpanContext(telemetry.parent);
176
+ return spanContext && isSpanContextValid(spanContext)
177
+ ? spanContext
178
+ : undefined;
179
+ });
180
+ }
181
+ function linksOf(link) {
182
+ const spanContext = spanContextOf(link.context);
183
+ return spanContext
184
+ ? [{ context: spanContext, attributes: attributesOf(link.attributes) }]
185
+ : [];
186
+ }
187
+ /**
188
+ * Writes the carrier from the span context and not through the global
189
+ * propagator, which needs an SDK. With no SDK, spans have no valid context, so
190
+ * Pi stores nothing.
191
+ */
192
+ function injectCarrier(telemetry) {
193
+ const spanContext = spanContextOf(telemetry);
194
+ if (!spanContext)
195
+ return undefined;
196
+ return guarded(() => {
197
+ const flags = spanContext.traceFlags.toString(16).padStart(2, "0");
198
+ const tracestate = spanContext.traceState?.serialize();
199
+ return {
200
+ traceparent: `00-${spanContext.traceId}-${spanContext.spanId}-${flags}`,
201
+ ...(tracestate ? { tracestate } : {}),
202
+ };
203
+ });
204
+ }
205
+ function extractParent(carrier) {
206
+ const match = carrier?.traceparent?.match(TRACEPARENT);
207
+ if (!match)
208
+ return ROOT_CONTEXT;
209
+ const parent = guarded(() => ({
210
+ traceId: match[1],
211
+ spanId: match[2],
212
+ traceFlags: Number.parseInt(match[3], 16),
213
+ isRemote: true,
214
+ traceState: carrier?.tracestate
215
+ ? createTraceState(carrier.tracestate)
216
+ : undefined,
217
+ }));
218
+ return parent && isSpanContextValid(parent)
219
+ ? trace.setSpanContext(ROOT_CONTEXT, parent)
220
+ : ROOT_CONTEXT;
221
+ }
222
+ /**
223
+ * Pi Durable's telemetry over `@opentelemetry/api`. Without an SDK, every call
224
+ * does nothing. `statusMessage` exists for Pi's conformance suite, which
225
+ * compares messages. Nothing else sets it, so traces hold no diagnostics.
226
+ */
227
+ export function createPiTelemetry(actorId, options = {}) {
228
+ const adapter = {
229
+ // Read per span, so an SDK started after the actor opened still gets its spans.
230
+ tracer: () => options.tracer ?? trace.getTracer(TRACER_NAME),
231
+ attributes: { "rivet.actor.id": actorId },
232
+ statusMessage: options.statusMessage,
233
+ propagation: {
234
+ inject: injectCarrier,
235
+ extract: (carrier) => new OtelTelemetry(adapter, extractParent(carrier)),
236
+ },
237
+ };
238
+ return new OtelTelemetry(adapter, ROOT_CONTEXT);
239
+ }
240
+ /**
241
+ * `parent` with the active span as its telemetry, so a run's root span links to
242
+ * its caller. Each run is its own trace, like a scheduled action in RivetKit.
243
+ */
244
+ export function withCallerSpan(actorId, parent) {
245
+ const caller = guarded(() => {
246
+ const span = trace.getSpan(context.active());
247
+ return span && isSpanContextValid(span.spanContext()) ? span : undefined;
248
+ });
249
+ return caller
250
+ ? withTelemetry(new OtelSpan(createPiTelemetry(actorId).adapter, caller), parent)
251
+ : parent;
252
+ }
2
253
  /**
3
254
  * Runs one Pi run inside an `invoke_agent pi` span, a child of the active span,
4
255
  * which is the `prompt` action span when RivetKit tracing is on. The action
@@ -8,7 +259,7 @@ import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
8
259
  * results are not recorded.
9
260
  */
10
261
  export async function traceRun(actorId, conversationId, model, run) {
11
- const span = trace.getTracer("@rivet-dev/pi").startSpan("invoke_agent pi", {
262
+ const span = guarded(() => trace.getTracer(TRACER_NAME).startSpan("invoke_agent pi", {
12
263
  kind: SpanKind.INTERNAL,
13
264
  attributes: {
14
265
  "gen_ai.operation.name": "invoke_agent",
@@ -17,22 +268,92 @@ export async function traceRun(actorId, conversationId, model, run) {
17
268
  "gen_ai.request.model": model,
18
269
  "rivet.actor.id": actorId,
19
270
  },
271
+ }));
272
+ const fail = (errorType, message) => guarded(() => {
273
+ span?.setAttribute("error.type", errorType);
274
+ span?.setStatus({ code: SpanStatusCode.ERROR, message });
20
275
  });
21
276
  try {
22
- const result = await context.with(trace.setSpan(context.active(), span), run);
23
- if (result.status === "unanswered") {
24
- span.setAttribute("error.type", "unanswered");
25
- span.setStatus({ code: SpanStatusCode.ERROR, message: result.reason });
26
- }
277
+ const result = await (span
278
+ ? runActive(trace.setSpan(context.active(), span), run)
279
+ : run());
280
+ if (result.status === "unanswered")
281
+ fail("unanswered", result.reason);
27
282
  return result;
28
283
  }
29
284
  catch (error) {
30
- span.setAttribute("error.type", "run_error");
31
- span.setStatus({ code: SpanStatusCode.ERROR });
285
+ fail("run_error");
32
286
  throw error;
33
287
  }
34
288
  finally {
35
- span.end();
289
+ guarded(() => span?.end());
36
290
  }
37
291
  }
292
+ const MODEL_CALL_FAILED = {
293
+ status: "error",
294
+ error: { name: "provider_error", message: "" },
295
+ };
296
+ /** Records what a finished model call reports, never its content or its error text. */
297
+ function recordModelResult(span, message) {
298
+ const { input, output, cacheRead, cacheWrite, cost } = message.usage;
299
+ span.setAttributes({
300
+ stop: message.stopReason,
301
+ "usage.input": input,
302
+ "usage.output": output,
303
+ "usage.cache_read": cacheRead,
304
+ "usage.cache_write": cacheWrite,
305
+ "cost.total": cost.total,
306
+ });
307
+ if (message.stopReason === "error")
308
+ span.setStatus(MODEL_CALL_FAILED);
309
+ }
310
+ /** Runs one model call in a span of Pi's model vocabulary, ending when its stream does. */
311
+ function traceModelCall(spanName, model, telemetry, call) {
312
+ if (!telemetry)
313
+ return call();
314
+ let stream;
315
+ let thrown;
316
+ void telemetry.startSpan({
317
+ name: spanName,
318
+ attributes: { provider: model.provider, model: model.id },
319
+ }, async (span) => {
320
+ try {
321
+ stream = call();
322
+ recordModelResult(span, await stream.result());
323
+ }
324
+ catch (error) {
325
+ thrown = error;
326
+ span.setStatus(MODEL_CALL_FAILED);
327
+ }
328
+ });
329
+ if (!stream)
330
+ throw thrown;
331
+ return stream;
332
+ }
333
+ /**
334
+ * The default model runtime with a span per model call. `ModelRuntime` skips
335
+ * the `observeModelRequest` that pi-ai's own `Models` runs, and pi-ai does not
336
+ * export it, so this emits the same spans. An app's own `models` stays as it is,
337
+ * so a call never has two spans. tests/tracing-models.test.ts fails when Pi
338
+ * fixes `ModelRuntime`. Remove this wrapper then: users see the same spans.
339
+ *
340
+ * A Proxy and not patched methods: `completeSimple` calls `streamSimple`
341
+ * inside `ModelRuntime`, so patching both would give that call two spans.
342
+ */
343
+ export function traceModelRequests(runtime) {
344
+ const traced = {
345
+ streamSimple: ((model, context, options) => traceModelCall("pi.ai.request", model, options?.telemetry, () => runtime.streamSimple(model, context, options))),
346
+ completeSimple: ((model, context, options) => traceModelCall("pi.ai.request", model, options?.telemetry, () => runtime.streamSimple(model, context, options)).result()),
347
+ fetchDeferred: ((model, handle, options) => traceModelCall("pi.ai.poll", model, options?.telemetry, () => runtime.streamDeferred(model, handle, options)).result()),
348
+ };
349
+ return new Proxy(runtime, {
350
+ get: (target, name) => {
351
+ const method = traced[name];
352
+ if (method)
353
+ return method;
354
+ const value = Reflect.get(target, name, target);
355
+ return typeof value === "function" ? value.bind(target) : value;
356
+ },
357
+ });
358
+ }
38
359
  //# sourceMappingURL=tracing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC7B,OAAe,EACf,cAAsB,EACtB,KAAyB,EACzB,GAAqB;IAErB,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,iBAAiB,EAAE;QAC1E,IAAI,EAAE,QAAQ,CAAC,QAAQ;QACvB,UAAU,EAAE;YACX,uBAAuB,EAAE,cAAc;YACvC,mBAAmB,EAAE,IAAI;YACzB,wBAAwB,EAAE,MAAM,CAAC,cAAc,CAAC;YAChD,sBAAsB,EAAE,KAAK;YAC7B,gBAAgB,EAAE,OAAO;SACzB;KACD,CAAC,CAAC;IACH,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAChC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,EACrC,GAAG,CACH,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACpC,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;YAC9C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC;IACb,CAAC;YAAS,CAAC;QACV,IAAI,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"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"}
package/dist/watches.d.ts CHANGED
@@ -1,14 +1,12 @@
1
- import type { Op } from "@earendil-works/chord/delta";
2
- import { type AgentEvent, type ConversationDocToken, type ConversationId, type ConversationView, type Harness, type JsonObject, type TaskGraph } from "@earendil-works/pi-durable";
1
+ import { type Op } from "@earendil-works/chord/delta";
2
+ import type { ConversationDocToken, ConversationId, ConversationView, Harness, JsonObject, TaskGraph } from "@earendil-works/pi-durable";
3
3
  import type { PiContext } from "./runtime.js";
4
4
  import { type WatchSpec } from "./storage.js";
5
- /** One batch of agent events, one commit per batch. */
6
- export type PiEventsFrame = {
7
- conversationId: ConversationId;
8
- seq: number;
9
- events: readonly AgentEvent[];
10
- };
11
- /** `seq` 0 carries the whole view; later frames carry Chord operations. */
5
+ /**
6
+ * `seq` 0 carries the view; later frames carry Chord operations. `value.entries`
7
+ * holds the newest entries, not the whole transcript. Page older ones with
8
+ * `conversation.entries`.
9
+ */
12
10
  export type PiViewFrame = {
13
11
  conversationId: ConversationId;
14
12
  seq: number;
@@ -38,7 +36,6 @@ export type PiDocFrame = {
38
36
  * client that sees a gap calls the watch action again.
39
37
  */
40
38
  export type PiWatchEvents = {
41
- "pi.events": PiEventsFrame;
42
39
  "pi.view": PiViewFrame;
43
40
  "pi.taskGraph": PiTaskGraphFrame;
44
41
  "pi.doc": PiDocFrame;
@@ -59,7 +56,7 @@ type Documents = ReadonlyMap<string, ConversationDocToken<any>>;
59
56
  * Starts a watch for a connection, stored so it comes back after sleep, and
60
57
  * returns the value it starts from. Watching the same thing again restarts
61
58
  * it with a fresh value. `requireTarget` runs in the queue too, so a watch
62
- * and an unwatch sent right after it take effect in the order they arrive.
59
+ * and an unwatch take effect in the order they reach this queue.
63
60
  */
64
61
  export declare function watch(actor: PiContext, watches: ConnectionWatches, harness: Harness, documents: Documents, connId: string, spec: WatchSpec, requireTarget: () => Promise<void>): Promise<unknown>;
65
62
  export declare function unwatch(actor: PiContext, watches: ConnectionWatches, connId: string, spec: WatchSpec): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"watches.d.ts","sourceRoot":"","sources":["../src/watches.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"watches.d.ts","sourceRoot":"","sources":["../src/watches.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,EAAE,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EACX,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,OAAO,EACP,UAAU,EACV,SAAS,EAET,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAKN,KAAK,SAAS,EACd,MAAM,cAAc,CAAC;AAEtB;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC;CACpB,CAAC;AACF,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC;CACpB,CAAC;AACF;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,cAAc,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC3B,SAAS,EAAE,WAAW,CAAC;IACvB,cAAc,EAAE,gBAAgB,CAAC;IACjC,QAAQ,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,KAAK,WAAW,GAAG;IAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAE7C,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,mDAAmD;IACnD,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACpD,sGAAsG;IACtG,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAgB,uBAAuB,IAAI,iBAAiB,CAE3D;AAWD,KAAK,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;AAahE;;;;;GAKG;AACH,wBAAsB,KAAK,CAC1B,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAChC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,OAAO,CAC5B,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,kDAAkD;AAClD,wBAAsB,iBAAiB,CACtC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,oGAAoG;AACpG,wBAAsB,cAAc,CACnC,OAAO,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACpC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,GAClB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
package/dist/watches.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
- import { watchEvents, } from "@earendil-works/pi-durable";
2
+ import { isReplace } from "@earendil-works/chord/delta";
3
3
  import { deleteConnectionWatches, deleteWatch, loadWatches, saveWatch, } from "./storage.js";
4
4
  export function createConnectionWatches() {
5
5
  return { byConnection: new Map(), queue: Promise.resolve() };
@@ -11,7 +11,6 @@ function serially(watches, change) {
11
11
  }
12
12
  function watchKey(spec) {
13
13
  switch (spec.kind) {
14
- case "events":
15
14
  case "view":
16
15
  return `${spec.kind}:${spec.conversationId}`;
17
16
  case "taskGraph":
@@ -24,7 +23,7 @@ function watchKey(spec) {
24
23
  * Starts a watch for a connection, stored so it comes back after sleep, and
25
24
  * returns the value it starts from. Watching the same thing again restarts
26
25
  * it with a fresh value. `requireTarget` runs in the queue too, so a watch
27
- * and an unwatch sent right after it take effect in the order they arrive.
26
+ * and an unwatch take effect in the order they reach this queue.
28
27
  */
29
28
  export async function watch(actor, watches, harness, documents, connId, spec, requireTarget) {
30
29
  const key = watchKey(spec);
@@ -71,7 +70,7 @@ export async function reattachWatches(actor, watches, harness, documents) {
71
70
  await serially(watches, async () => {
72
71
  const open = new Set(actor.conns.keys());
73
72
  for (const stored of await loadWatches(actor.db)) {
74
- if (!open.has(stored.connId)) {
73
+ if (!open.has(stored.connId) || stored.spec.kind === "events") {
75
74
  await deleteWatch(actor.db, stored.connId, stored.key);
76
75
  continue;
77
76
  }
@@ -114,20 +113,6 @@ async function attach(actor, watches, harness, documents, connId, key, spec, res
114
113
  async function start(log, harness, documents, spec, send, resend) {
115
114
  const context = BACKGROUND_CONTEXT;
116
115
  switch (spec.kind) {
117
- case "events": {
118
- const conversationId = spec.conversationId;
119
- const stream = await watchEvents(harness, conversationId, context);
120
- if (resend)
121
- send("pi.events", {
122
- conversationId,
123
- events: [stream.snapshot],
124
- });
125
- stream.start(async (events) => send("pi.events", { conversationId, events }));
126
- return {
127
- initial: { snapshot: stream.snapshot },
128
- stop: async () => void (await stream.stop()),
129
- };
130
- }
131
116
  case "view": {
132
117
  const conversationId = spec.conversationId;
133
118
  const conversation = await harness.conversation(conversationId, context);
@@ -136,7 +121,10 @@ async function start(log, harness, documents, spec, send, resend) {
136
121
  const view = await conversation.watch(context);
137
122
  if (resend)
138
123
  send("pi.view", { conversationId, value: view.value });
139
- view.start(async (_value, ops) => send("pi.view", { conversationId, ops }));
124
+ view.start(async (call) => {
125
+ if (call.kind === "value")
126
+ send("pi.view", { conversationId, ops: call.ops });
127
+ });
140
128
  return {
141
129
  initial: { value: view.value },
142
130
  stop: async () => void (await view.stop()),
@@ -146,7 +134,10 @@ async function start(log, harness, documents, spec, send, resend) {
146
134
  const graph = await harness.watchTaskGraph(context);
147
135
  if (resend)
148
136
  send("pi.taskGraph", { value: graph.value });
149
- graph.start(async (_value, ops) => send("pi.taskGraph", { ops }));
137
+ graph.start(async (call) => {
138
+ if (call.kind === "value")
139
+ send("pi.taskGraph", { ops: call.ops });
140
+ });
150
141
  return {
151
142
  initial: { value: graph.value },
152
143
  stop: async () => void (await graph.stop()),
@@ -157,9 +148,9 @@ async function start(log, harness, documents, spec, send, resend) {
157
148
  }
158
149
  }
159
150
  /**
160
- * Pi's `watchDoc` never creates a document, and its watch ends when the
161
- * document is retired. While the document does not exist, the watch attaches
162
- * when a commit creates it, and its value is the next frame.
151
+ * Pi's `watchDoc` never creates a document. While the document does not exist,
152
+ * the watch attaches when a commit creates it, and its value is the next frame.
153
+ * Once attached, the watch follows the document through retirements.
163
154
  */
164
155
  async function startDoc(log, harness, documents, spec, send, resend) {
165
156
  const token = documents.get(spec.docKind);
@@ -167,20 +158,22 @@ async function startDoc(log, harness, documents, spec, send, resend) {
167
158
  throw new Error(`document ${spec.docKind} is not listed in pi({ documents })`);
168
159
  const { docKind: kind, conversationId } = spec;
169
160
  let docWatch;
170
- let unsubscribe;
161
+ let observation;
171
162
  let stopped = false;
172
163
  const follow = (handle, sendFirst) => {
173
164
  docWatch = handle;
174
165
  if (sendFirst)
175
166
  send("pi.doc", { kind, conversationId, value: handle.value });
176
- handle.start(async (value) => {
177
- send("pi.doc", { kind, conversationId, value });
178
- if (value !== null || stopped)
167
+ handle.start(async (call) => {
168
+ if (call.kind !== "value")
179
169
  return;
180
- // A retired document's watch ends, so wait for a commit that creates it again.
181
- docWatch = undefined;
182
- void handle.stop();
183
- awaitCreation();
170
+ // A retirement keeps the last value and adds no ops. A later document at this address arrives as a root replacement.
171
+ const retired = call.retired && !call.ops.some(isReplace);
172
+ send("pi.doc", {
173
+ kind,
174
+ conversationId,
175
+ value: retired ? null : call.value,
176
+ });
184
177
  });
185
178
  };
186
179
  const awaitCreation = () => {
@@ -196,8 +189,8 @@ async function startDoc(log, harness, documents, spec, send, resend) {
196
189
  attaching = false;
197
190
  return;
198
191
  }
199
- unsubscribe?.();
200
- unsubscribe = undefined;
192
+ observation?.detach();
193
+ observation = undefined;
201
194
  follow(handle, true);
202
195
  }
203
196
  catch (error) {
@@ -211,16 +204,19 @@ async function startDoc(log, harness, documents, spec, send, resend) {
211
204
  });
212
205
  }
213
206
  };
214
- unsubscribe = harness.subscribeCommits((publication) => {
215
- const created = publication.changes.some((change) => change.type === "document" &&
216
- change.record.kind === kind &&
217
- change.conversationId === conversationId &&
218
- change.value !== null);
219
- if (!created || attaching)
220
- return;
221
- attaching = true;
222
- // Commit listeners must not call Pi Durable, so the watch attaches after the listener returns.
223
- queueMicrotask(() => void attachCreated());
207
+ observation = harness.observeCommits({
208
+ advance: (publication) => {
209
+ const created = publication.changes.some((change) => change.type === "document" &&
210
+ change.kind === "change" &&
211
+ change.record.kind === kind &&
212
+ change.conversationId === conversationId);
213
+ if (!created || attaching)
214
+ return;
215
+ attaching = true;
216
+ // Commit observers must not call Pi Durable, so the watch attaches after this returns.
217
+ queueMicrotask(() => void attachCreated());
218
+ },
219
+ end: () => { },
224
220
  });
225
221
  };
226
222
  const existing = await harness.watchDoc(token, conversationId, BACKGROUND_CONTEXT);
@@ -236,7 +232,7 @@ async function startDoc(log, harness, documents, spec, send, resend) {
236
232
  initial: { value: existing?.value },
237
233
  stop: async () => {
238
234
  stopped = true;
239
- unsubscribe?.();
235
+ observation?.detach();
240
236
  await docWatch?.stop();
241
237
  },
242
238
  };