service-bridge 1.6.0-dev.32 → 1.7.0-dev.34

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/README.md CHANGED
@@ -73,7 +73,7 @@ Node.js SDK for [ServiceBridge](https://servicebridge.dev) — production-ready
73
73
 
74
74
  **Saga / distributed transactions** — DAG workflows with typed steps (`rpc`, `event`, `event_wait`, `sleep`, child workflow). Compensations and rollbacks via workflow step dependencies.
75
75
 
76
- **AI agent orchestration** — Stream LLM tokens via realtime run streams with replay. Orchestrate multi-step AI pipelines as workflows.
76
+ **AI agent orchestration** — Stream LLM tokens via realtime trace streams with replay. Orchestrate multi-step AI pipelines as workflows.
77
77
 
78
78
  **Full-stack observability** — Every RPC call, event delivery, workflow step, and HTTP request traced automatically. One timeline, one dashboard. Prometheus metrics and Loki-compatible log API included.
79
79
 
@@ -236,7 +236,7 @@ Every step above — RPC, event publish, event delivery, workflow execution —
236
236
  - **Metrics** — Prometheus-compatible `/metrics` endpoint (30+ metric families)
237
237
  - **Logs** — structured log ingest with Loki-compatible query API
238
238
  - **Alerts** — runtime alerts for delivery failures, errors, and service health
239
- - **Dashboard** — realtime web UI for runs, events, workflows, jobs, DLQ, service map, and service keys
239
+ - **Dashboard** — realtime web UI for traces, events, workflows, jobs, DLQ, service map, and service keys
240
240
 
241
241
  ---
242
242
 
@@ -262,7 +262,7 @@ Every step above — RPC, event publish, event delivery, workflow execution —
262
262
  ### Cross-SDK parity notes
263
263
 
264
264
  ServiceBridge keeps the core API shape consistent across Node.js, Go, and Python:
265
- constructor, RPC, events, jobs, workflows, `runWorkflow`, streams, serve/stop, and `ServiceBridgeError`.
265
+ constructor, RPC, events, jobs, workflows, `executeWorkflow`, streams, serve/stop, and `ServiceBridgeError`.
266
266
 
267
267
  Constructor-level defaults for `timeout`, `retries`, and `retryDelay` are available
268
268
  across all three SDKs. Parity differences are naming-only (language idioms):
@@ -326,6 +326,15 @@ rpc<T = unknown>(fn: string, payload?: unknown, opts?: RpcOpts): Promise<T>
326
326
 
327
327
  Calls a registered RPC handler on another service. Direct gRPC path, no proxy.
328
328
 
329
+ **Function name formats** — `fn` accepts two formats:
330
+
331
+ | Format | Example | When to use |
332
+ |---|---|---|
333
+ | Plain name | `"charge"` | Function name is globally unique across all services. Resolved automatically via service discovery. |
334
+ | Canonical name | `"payments/charge"` | Multiple services expose a function with the same plain name, or you want to be explicit about the target service. |
335
+
336
+ Both formats are interchangeable when the name is unique globally. Canonical format is recommended in production for clarity and to avoid ambiguity as your service count grows.
337
+
329
338
  `RpcOpts`:
330
339
 
331
340
  | Option | Type | Description |
@@ -338,6 +347,10 @@ Calls a registered RPC handler on another service. Direct gRPC path, no proxy.
338
347
  | `mode` | `"direct" \| "proxy"` | Transport mode. `"direct"` (default) connects directly to the worker. `"proxy"` routes through the control plane when direct connection is unavailable. |
339
348
 
340
349
  ```ts
350
+ // plain name — works when "get" is unique across services
351
+ const user = await sb.rpc<{ id: string; name: string }>("get", { id: "u_1" });
352
+
353
+ // canonical name — explicit service target, always unambiguous
341
354
  const user = await sb.rpc<{ id: string; name: string }>("users/get", { id: "u_1" }, {
342
355
  timeout: 5000,
343
356
  retries: 2,
@@ -378,6 +391,16 @@ await sb.event("orders.created", { orderId: "ord_42" }, {
378
391
 
379
392
  ---
380
393
 
394
+ ### `publishEvent(topic, payload?, opts?)`
395
+
396
+ ```ts
397
+ publishEvent(topic: string, payload?: unknown, opts?: PublishEventOpts): Promise<string>
398
+ ```
399
+
400
+ Publishes an event via the established worker session stream. Requires an active worker session — call after `serve()`. Resolves with `messageId` once the server confirms with `publish_ack`. Times out after 30 s if no ack. Use `event()` when not serving (e.g. caller-only services); use `publishEvent()` from within a worker for lower-latency publishing over the existing session.
401
+
402
+ ---
403
+
381
404
  ### `job(target, opts)`
382
405
 
383
406
  ```ts
@@ -458,44 +481,44 @@ await sb.workflow("checkout.flow", steps, { stepTimeoutMs: 60_000 });
458
481
 
459
482
  ---
460
483
 
461
- ### `runWorkflow(name, input?, opts?)`
484
+ ### `executeWorkflow(name, input?, opts?)`
462
485
 
463
486
  ```ts
464
- runWorkflow(name: string, input?: unknown, opts?: RunWorkflowOpts): Promise<{ runId: string; traceId: string }>
487
+ executeWorkflow(name: string, input?: unknown, opts?: ExecuteWorkflowOpts): Promise<{ traceId: string; groupTraceId: string }>
465
488
  ```
466
489
 
467
- Starts a workflow run on demand. The workflow must be registered first via `workflow()`.
468
- An alternative to scheduling via `job(target, { via: "workflow" })` — triggers the run immediately.
490
+ Starts a workflow execution on demand. The workflow must be registered first via `workflow()`.
491
+ An alternative to scheduling via `job(target, { via: "workflow" })` — triggers the execution immediately.
469
492
 
470
493
  | Parameter | Type | Default | Description |
471
494
  |---|---|---|---|
472
495
  | `name` | `string` | required | Name of a previously registered workflow. |
473
496
  | `input` | `unknown` | `undefined` | Optional JSON-serializable input payload. |
474
497
 
475
- Returns `{ runId, traceId }`. Use `traceId` with `watchRun()` to observe execution in real time.
498
+ Returns `{ traceId, groupTraceId }`. Use `traceId` with `watchTrace()` to observe execution in real time.
476
499
 
477
- `RunWorkflowOpts`:
500
+ `ExecuteWorkflowOpts`:
478
501
 
479
502
  | Option | Type | Description |
480
503
  |---|---|---|
481
- | `traceId` | `string` | Override trace ID for this workflow run. |
504
+ | `traceId` | `string` | Override trace ID for this workflow execution. |
482
505
 
483
506
  ```ts
484
- const { runId, traceId } = await sb.runWorkflow("user.onboarding", { userId: "u_123" });
507
+ const { traceId, groupTraceId } = await sb.executeWorkflow("user.onboarding", { userId: "u_123" });
485
508
  ```
486
509
 
487
510
  ---
488
511
 
489
- ### `cancelWorkflowRun(runId)`
512
+ ### `cancelWorkflow(traceId)`
490
513
 
491
514
  ```ts
492
- cancelWorkflowRun(runId: string): Promise<void>
515
+ cancelWorkflow(traceId: string): Promise<void>
493
516
  ```
494
517
 
495
518
  Cancels a running workflow instance.
496
519
 
497
520
  ```ts
498
- await sb.cancelWorkflowRun("run_01HQ...XYZ");
521
+ await sb.cancelWorkflow("trace_01HQ...XYZ");
499
522
  ```
500
523
 
501
524
  ---
@@ -542,7 +565,7 @@ sb.handleRpc("ai/generate", async (payload: { prompt: string }, ctx) => {
542
565
 
543
566
  | Method | Signature | Description |
544
567
  |---|---|---|
545
- | `write` | `write(data: unknown, key?: string): Promise<void>` | Append a real-time chunk to the run stream. |
568
+ | `write` | `write(data: unknown, key?: string): Promise<void>` | Append a real-time chunk to the trace stream. |
546
569
  | `end` | `end(key?: string): Promise<void>` | No-op placeholder for API symmetry (lifecycle managed by runtime). |
547
570
 
548
571
  ---
@@ -584,7 +607,7 @@ a consumer, the delivery moves to DLQ with reason `delivery_ttl_exceeded`.
584
607
  - `ctx.retry(delayMs?)` — ask for redelivery with optional delay
585
608
  - `ctx.reject(reason)` — move to DLQ immediately, bypassing remaining retries
586
609
  - `ctx.refs` — metadata (`topic`, `groupName`, `messageId`, `attempt`, `headers`)
587
- - `ctx.stream.write(...)` — append real-time chunks to run stream
610
+ - `ctx.stream.write(...)` — append real-time chunks to trace stream
588
611
 
589
612
  ```ts
590
613
  sb.handleEvent("orders.*", async (payload, ctx) => {
@@ -702,32 +725,32 @@ await sb.registerHttpEndpoint({
702
725
 
703
726
  ---
704
727
 
705
- ### `watchRun(runId, opts?)`
728
+ ### `watchTrace(traceId, opts?)`
706
729
 
707
730
  ```ts
708
- watchRun(runId: string, opts?: WatchRunOpts): AsyncIterable<RunStreamEvent>
731
+ watchTrace(traceId: string, opts?: WatchTraceOpts): AsyncIterable<TraceStreamEvent>
709
732
  ```
710
733
 
711
- Subscribes to a run stream with replay and live updates. `runId` is the stream
712
- identifier used by `ctx.stream.write(...)` (typically a trace ID).
734
+ Subscribes to a trace stream with replay and live updates. `traceId` is the stream
735
+ identifier used by `ctx.stream.write(...)`.
713
736
 
714
- `WatchRunOpts`:
737
+ `WatchTraceOpts`:
715
738
 
716
739
  | Option | Type | Default | Description |
717
740
  |---|---|---|---|
718
741
  | `key` | `string` | `""` | Stream key filter (`""` = all keys). |
719
742
  | `fromSequence` | `number` | `0` | Replay from sequence cursor. |
720
743
 
721
- `RunStreamEvent`:
744
+ `TraceStreamEvent`:
722
745
 
723
746
  | Field | Type | Description |
724
747
  |---|---|---|
725
- | `type` | `"chunk" \| "run_complete"` | Event kind. |
726
- | `runId` | `string` | Stream/run identifier being watched. |
748
+ | `type` | `"chunk" \| "trace_complete"` | Event kind. |
749
+ | `traceId` | `string` | Trace identifier being watched. |
727
750
  | `key` | `string` | Stream lane key. |
728
751
  | `sequence` | `number` | Monotonic sequence number. |
729
752
  | `data` | `unknown` | JSON-decoded chunk payload. |
730
- | `runStatus` | `string \| undefined` | Final status on `run_complete`. |
753
+ | `traceStatus` | `string \| undefined` | Final status on `trace_complete`. |
731
754
 
732
755
  Behavior:
733
756
 
@@ -737,11 +760,11 @@ Behavior:
737
760
  - Enforces internal queue limit `256`; overflow is fatal (consumer must drain promptly).
738
761
 
739
762
  ```ts
740
- for await (const evt of sb.watchRun(runId, { key: "output", fromSequence: 0 })) {
763
+ for await (const evt of sb.watchTrace(traceId, { key: "output", fromSequence: 0 })) {
741
764
  if (evt.type === "chunk") {
742
765
  process.stdout.write(String((evt.data as { token?: string }).token ?? ""));
743
766
  }
744
- if (evt.type === "run_complete") break;
767
+ if (evt.type === "trace_complete") break;
745
768
  }
746
769
  ```
747
770
 
@@ -766,18 +789,18 @@ if (tc) {
766
789
  }
767
790
  ```
768
791
 
769
- #### `runWithTraceContext(ctx, fn)`
792
+ #### `withTraceContext(ctx, fn)`
770
793
 
771
794
  ```ts
772
- runWithTraceContext<T>(ctx: { traceId: string; spanId: string }, fn: () => T): T
795
+ withTraceContext<T>(ctx: { traceId: string; spanId: string }, fn: () => T): T
773
796
  ```
774
797
 
775
798
  Runs a function inside an explicit trace context.
776
799
 
777
800
  ```ts
778
- import { runWithTraceContext } from "service-bridge";
801
+ import { withTraceContext } from "service-bridge";
779
802
 
780
- runWithTraceContext({ traceId: "trace-1", spanId: "span-1" }, async () => {
803
+ withTraceContext({ traceId: "trace-1", spanId: "span-1" }, async () => {
781
804
  await sb.event("audit.log", { action: "user.login" });
782
805
  });
783
806
  ```
package/dist/express.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // http/src/express.ts
2
- import { runWithTraceContext } from "service-bridge";
2
+ import { withTraceContext } from "service-bridge";
3
3
 
4
4
  // http/src/trace.ts
5
5
  function parseTraceparent(traceparent) {
@@ -86,7 +86,7 @@ function servicebridgeMiddleware(options) {
86
86
  };
87
87
  res.on("finish", onFinish);
88
88
  res.on("close", onFinish);
89
- runWithTraceContext({ traceId: span.traceId, spanId: span.spanId }, () => {
89
+ withTraceContext({ traceId: span.traceId, spanId: span.spanId }, () => {
90
90
  next();
91
91
  });
92
92
  };
package/dist/fastify.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // http/src/fastify.ts
2
- import { runWithTraceContext } from "service-bridge";
2
+ import { withTraceContext } from "service-bridge";
3
3
 
4
4
  // http/src/trace.ts
5
5
  function parseTraceparent(traceparent) {
@@ -111,7 +111,7 @@ function wrapHandler(handler) {
111
111
  const traceId = request.traceId;
112
112
  const spanId = request.spanId;
113
113
  if (traceId && spanId) {
114
- return runWithTraceContext({ traceId, spanId }, () => handler(request, reply));
114
+ return withTraceContext({ traceId, spanId }, () => handler(request, reply));
115
115
  }
116
116
  return handler(request, reply);
117
117
  };
package/dist/index.js CHANGED
@@ -336,7 +336,7 @@ var traceStorage = new AsyncLocalStorage;
336
336
  function getTraceContext() {
337
337
  return traceStorage.getStore();
338
338
  }
339
- function runWithTraceContext(ctx, fn) {
339
+ function withTraceContext(ctx, fn) {
340
340
  return traceStorage.run(ctx, fn);
341
341
  }
342
342
  var servicebridgePackageDefinition = protoLoader.loadFileDescriptorSetFromBuffer(Buffer.from(DESCRIPTOR_SET_BASE64, "base64"), {
@@ -948,6 +948,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
948
948
  let workerSessionReconnectTimer = null;
949
949
  let workerSessionPingTimer = null;
950
950
  let workerSessionPositionTimer = null;
951
+ const pendingPublishAcks = new Map;
951
952
  let v2Session = null;
952
953
  let serveState = null;
953
954
  let heartbeatTimer = null;
@@ -1012,6 +1013,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1012
1013
  parent_span_id: op.parentSpanId,
1013
1014
  fn: op.fn,
1014
1015
  span_type: op.spanType ?? "",
1016
+ target_service: op.targetService ?? "",
1015
1017
  started_at: op.startedAt,
1016
1018
  input: op.inputBuf,
1017
1019
  attempt: op.attempt
@@ -1033,6 +1035,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1033
1035
  parent_span_id: op.parentSpanId ?? "",
1034
1036
  fn: op.fn,
1035
1037
  span_type: op.spanType ?? "",
1038
+ target_service: op.targetService ?? "",
1036
1039
  started_at: op.startedAt,
1037
1040
  duration_ms: op.durationMs,
1038
1041
  success: op.success,
@@ -1272,7 +1275,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1272
1275
  function normalizeUnknownErrorMessage(error) {
1273
1276
  return error instanceof Error ? error.message : String(error);
1274
1277
  }
1275
- function reportCallStartAsync(traceId, spanId, parentSpanId, fn, startedAt, inputBuf, attempt, spanType) {
1278
+ function reportCallStartAsync(traceId, spanId, parentSpanId, fn, startedAt, inputBuf, attempt, spanType, targetService) {
1276
1279
  if (!isOnline) {
1277
1280
  enqueueOffline({
1278
1281
  type: "reportCallStart",
@@ -1281,6 +1284,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1281
1284
  parentSpanId,
1282
1285
  fn,
1283
1286
  spanType,
1287
+ targetService,
1284
1288
  startedAt,
1285
1289
  inputBuf,
1286
1290
  attempt
@@ -1293,6 +1297,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1293
1297
  parentSpanId,
1294
1298
  fn,
1295
1299
  spanType,
1300
+ targetService,
1296
1301
  startedAt,
1297
1302
  inputBuf,
1298
1303
  attempt
@@ -1310,6 +1315,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1310
1315
  parentSpanId,
1311
1316
  fn,
1312
1317
  spanType,
1318
+ targetService,
1313
1319
  startedAt,
1314
1320
  inputBuf,
1315
1321
  attempt
@@ -1319,7 +1325,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1319
1325
  reportSDKError("report-call-start", err);
1320
1326
  });
1321
1327
  }
1322
- function reportCallAsync(traceId, spanId, fn, startedAt, inputBuf, success, attempt, outputBuf, error = "", spanType, parentSpanId = "") {
1328
+ function reportCallAsync(traceId, spanId, fn, startedAt, inputBuf, success, attempt, outputBuf, error = "", spanType, parentSpanId = "", targetService) {
1323
1329
  const durationMs = computeDurationMs(startedAt);
1324
1330
  if (!isOnline) {
1325
1331
  enqueueOffline({
@@ -1328,6 +1334,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1328
1334
  spanId,
1329
1335
  fn,
1330
1336
  spanType,
1337
+ targetService,
1331
1338
  parentSpanId,
1332
1339
  startedAt,
1333
1340
  durationMs,
@@ -1344,6 +1351,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1344
1351
  spanId,
1345
1352
  fn,
1346
1353
  spanType,
1354
+ targetService,
1347
1355
  parentSpanId,
1348
1356
  startedAt,
1349
1357
  durationMs,
@@ -1365,6 +1373,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1365
1373
  spanId,
1366
1374
  fn,
1367
1375
  spanType,
1376
+ targetService,
1368
1377
  parentSpanId,
1369
1378
  startedAt,
1370
1379
  durationMs,
@@ -1482,7 +1491,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1482
1491
  registrationFollowUpPromise = registrationSyncPromise.catch(() => {}).then(() => {
1483
1492
  registrationFollowUpPromise = null;
1484
1493
  if (serveState)
1485
- return reconcileRegistrations(reason + "-followup");
1494
+ return reconcileRegistrations(`${reason}-followup`);
1486
1495
  });
1487
1496
  }
1488
1497
  return registrationFollowUpPromise;
@@ -1605,12 +1614,14 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1605
1614
  });
1606
1615
  }, delayMs);
1607
1616
  }
1608
- function makeStreamWriter(runId) {
1617
+ function makeStreamWriter(traceId) {
1609
1618
  const append = (data, key = "default") => {
1610
- if (!isOnline || !runId)
1619
+ if (!isOnline || !traceId)
1611
1620
  return Promise.resolve();
1612
1621
  const dataBuf = Buffer.from(JSON.stringify(data));
1613
- if (sessionSend({ stream_append: { run_id: runId, key, data: dataBuf } })) {
1622
+ if (sessionSend({
1623
+ stream_append: { trace_id: traceId, key, data: dataBuf }
1624
+ })) {
1614
1625
  return Promise.resolve();
1615
1626
  }
1616
1627
  return Promise.resolve();
@@ -1628,7 +1639,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1628
1639
  return fnHandlers.get(name.slice(slash + 1));
1629
1640
  return;
1630
1641
  }
1631
- async function invokeRpcHandler(req, runId) {
1642
+ async function invokeRpcHandler(req, traceId) {
1632
1643
  const entry = lookupFnHandler(req.function_name || "");
1633
1644
  if (!entry) {
1634
1645
  return {
@@ -1660,7 +1671,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1660
1671
  const rpcCtx = {
1661
1672
  traceId: traceCtx.traceId,
1662
1673
  spanId: traceCtx.spanId,
1663
- stream: makeStreamWriter(runId)
1674
+ stream: makeStreamWriter(traceId)
1664
1675
  };
1665
1676
  try {
1666
1677
  const result = await entry.handler(parsed, rpcCtx);
@@ -1675,7 +1686,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1675
1686
  }
1676
1687
  });
1677
1688
  }
1678
- async function invokeEventHandler(req, runId) {
1689
+ async function invokeEventHandler(req, traceId) {
1679
1690
  const groupName = typeof req.group_name === "string" ? req.group_name : undefined;
1680
1691
  const entry = groupName ? eventHandlers.get(groupName) : undefined;
1681
1692
  if (!entry) {
@@ -1714,7 +1725,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1714
1725
  reject(reason) {
1715
1726
  rejectReason = reason || "rejected_by_consumer";
1716
1727
  },
1717
- stream: makeStreamWriter(runId)
1728
+ stream: makeStreamWriter(traceId)
1718
1729
  };
1719
1730
  try {
1720
1731
  await entry.handler(parsed, ctx);
@@ -1749,8 +1760,8 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1749
1760
  });
1750
1761
  return;
1751
1762
  }
1752
- const runIdMeta = call.metadata.get("servicebridge-run-id");
1753
- const runId = typeof runIdMeta[0] === "string" ? runIdMeta[0] : "";
1763
+ const traceIdMeta = call.metadata.get("servicebridge-trace-id");
1764
+ const traceId = typeof traceIdMeta[0] === "string" ? traceIdMeta[0] : "";
1754
1765
  const workerTraceId = call.request.trace_id || crypto.randomUUID();
1755
1766
  const workerSpanId = call.request.span_id || crypto.randomUUID();
1756
1767
  const workerParentSpanId = call.request.parent_span_id || "";
@@ -1759,14 +1770,14 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1759
1770
  const rpcStartedAt = Date.now();
1760
1771
  const rpcInputBuf = call.request.payload ?? Buffer.alloc(0);
1761
1772
  reportCallStartAsync(workerTraceId, workerSpanId, workerParentSpanId, fnName, rpcStartedAt, rpcInputBuf, 1, workerSpanType);
1762
- const response = await invokeRpcHandler(call.request, runId);
1773
+ const response = await invokeRpcHandler(call.request, traceId);
1763
1774
  reportCallAsync(workerTraceId, workerSpanId, fnName, rpcStartedAt, rpcInputBuf, response.success ?? true, 1, response.output ?? Buffer.alloc(0), response.error ?? "", workerSpanType, workerParentSpanId);
1764
1775
  cb(null, response);
1765
1776
  }
1766
1777
  async function handleDeliver(call, cb) {
1767
1778
  const req = call.request;
1768
- const runIdMeta = call.metadata.get("servicebridge-run-id");
1769
- const runId = typeof runIdMeta[0] === "string" ? runIdMeta[0] : "";
1779
+ const traceIdMeta = call.metadata.get("servicebridge-trace-id");
1780
+ const traceId = typeof traceIdMeta[0] === "string" ? traceIdMeta[0] : "";
1770
1781
  const result = await invokeEventHandler({
1771
1782
  message_id: String(req.message_id ?? ""),
1772
1783
  group_name: String(req.group_name ?? ""),
@@ -1776,7 +1787,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1776
1787
  trace_id: String(req.trace_id ?? ""),
1777
1788
  parent_span_id: String(req.parent_span_id ?? ""),
1778
1789
  attempt: Number(req.attempt ?? 0)
1779
- }, runId);
1790
+ }, traceId);
1780
1791
  cb(null, {
1781
1792
  ack: result.ack === true,
1782
1793
  error: result.error ?? "",
@@ -1826,6 +1837,71 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1826
1837
  try {
1827
1838
  if (deadlineUnixMs > 0 && Date.now() > deadlineUnixMs) {
1828
1839
  result.error = "command deadline exceeded";
1840
+ } else if (command.task_command) {
1841
+ const tc = command.task_command;
1842
+ if (tc.type === 1) {
1843
+ const fn = tc.meta?.fn ?? "";
1844
+ const tcParentSpanId = String(tc.parent_span_id ?? "");
1845
+ const tcReq = {
1846
+ function_name: fn,
1847
+ payload: tc.payload ? Buffer.from(tc.payload) : Buffer.alloc(0),
1848
+ trace_id: String(tc.trace_id ?? ""),
1849
+ span_id: String(tc.span_id ?? ""),
1850
+ parent_span_id: tcParentSpanId
1851
+ };
1852
+ const tcTraceId = tcReq.trace_id || crypto.randomUUID();
1853
+ const tcSpanId = tcReq.span_id || crypto.randomUUID();
1854
+ const tcStartedAt = Date.now();
1855
+ const tcStreamTraceId = tc.meta?.trace_id ?? "";
1856
+ reportCallStartAsync(tcTraceId, tcSpanId, tcParentSpanId, fn, tcStartedAt, tcReq.payload, tc.attempt ?? 1, "rpc");
1857
+ const tcResult = await invokeRpcHandler(tcReq, tcStreamTraceId);
1858
+ reportCallAsync(tcTraceId, tcSpanId, fn, tcStartedAt, tcReq.payload, tcResult.success, tc.attempt ?? 1, tcResult.output ?? Buffer.alloc(0), tcResult.error ?? "", "rpc", tcParentSpanId);
1859
+ result.success = tcResult.success;
1860
+ result.output = tcResult.output ?? Buffer.alloc(0);
1861
+ result.error = tcResult.error ?? "";
1862
+ } else if (tc.type === 2) {
1863
+ const evTraceId = String(tc.trace_id ?? "");
1864
+ const evParentSpanId = String(tc.parent_span_id ?? "");
1865
+ const evSpanId = String(tc.span_id ?? "") || crypto.randomUUID();
1866
+ const evTopic = tc.meta?.topic ?? "";
1867
+ const evGroupName = tc.meta?.group_name ?? "";
1868
+ const evMessageId = tc.meta?.message_id ?? "";
1869
+ const evStreamTraceId = tc.meta?.trace_id ?? "";
1870
+ const evAttempt = tc.attempt ?? 0;
1871
+ const evPayload = tc.payload ? Buffer.from(tc.payload) : Buffer.alloc(0);
1872
+ const reservedMetaKeys = new Set([
1873
+ "fn",
1874
+ "topic",
1875
+ "group_name",
1876
+ "message_id",
1877
+ "trace_id"
1878
+ ]);
1879
+ const evHeaders = {};
1880
+ for (const [k, v] of Object.entries(tc.meta ?? {})) {
1881
+ if (!reservedMetaKeys.has(k))
1882
+ evHeaders[k] = v;
1883
+ }
1884
+ const evStartedAt = Date.now();
1885
+ reportCallStartAsync(evTraceId, evSpanId, evParentSpanId, evTopic, evStartedAt, evPayload, Number(evAttempt), "event");
1886
+ const evResult = await invokeEventHandler({
1887
+ message_id: evMessageId,
1888
+ group_name: evGroupName,
1889
+ topic: evTopic,
1890
+ payload: evPayload,
1891
+ headers: evHeaders,
1892
+ trace_id: evTraceId,
1893
+ parent_span_id: evParentSpanId,
1894
+ attempt: Number(evAttempt)
1895
+ }, evStreamTraceId);
1896
+ reportCallAsync(evTraceId, evSpanId, evTopic, evStartedAt, evPayload, evResult.ack === true, Number(evAttempt), Buffer.alloc(0), evResult.error ?? "", "event", evParentSpanId);
1897
+ result.success = evResult.ack === true;
1898
+ result.ack = evResult.ack === true;
1899
+ result.retry_after_ms = evResult.retry_after_ms ?? 0;
1900
+ result.reject_reason = evResult.reject_reason ?? "";
1901
+ result.error = evResult.error ?? "";
1902
+ } else {
1903
+ result.error = `unsupported task_command type: ${tc.type}`;
1904
+ }
1829
1905
  } else if (command.rpc) {
1830
1906
  const rpc = command.rpc;
1831
1907
  const rpcParentSpanId = String(rpc.parent_span_id ?? "");
@@ -1842,7 +1918,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1842
1918
  const fnName = rpcReq.function_name;
1843
1919
  const rpcStartedAt = Date.now();
1844
1920
  reportCallStartAsync(workerTraceId, workerSpanId, rpcParentSpanId, fnName, rpcStartedAt, rpcReq.payload, 1, rpcSpanType);
1845
- const rpcResult = await invokeRpcHandler(rpcReq, String(rpc.run_id ?? ""));
1921
+ const rpcResult = await invokeRpcHandler(rpcReq, String(rpc.trace_id ?? ""));
1846
1922
  reportCallAsync(workerTraceId, workerSpanId, fnName, rpcStartedAt, rpcReq.payload, rpcResult.success, 1, rpcResult.output ?? Buffer.alloc(0), rpcResult.error ?? "", rpcSpanType, rpcParentSpanId);
1847
1923
  result.success = rpcResult.success;
1848
1924
  result.output = rpcResult.output ?? Buffer.alloc(0);
@@ -1858,7 +1934,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1858
1934
  trace_id: String(event.trace_id ?? ""),
1859
1935
  parent_span_id: String(event.parent_span_id ?? ""),
1860
1936
  attempt: Number(event.attempt ?? 0)
1861
- }, String(event.run_id ?? ""));
1937
+ }, String(event.trace_id ?? ""));
1862
1938
  result.success = eventResult.ack === true;
1863
1939
  result.ack = eventResult.ack === true;
1864
1940
  result.retry_after_ms = eventResult.retry_after_ms ?? 0;
@@ -1989,6 +2065,20 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
1989
2065
  if (msg.flow_update && v2Session) {
1990
2066
  v2Session.onFlowControlUpdate(msg.flow_update.new_window_size ?? 0, msg.flow_update.reason ?? "");
1991
2067
  }
2068
+ if (msg.publish_ack) {
2069
+ const ack = msg.publish_ack;
2070
+ const reqId = ack.request_id ?? "";
2071
+ const pending = pendingPublishAcks.get(reqId);
2072
+ if (pending) {
2073
+ pendingPublishAcks.delete(reqId);
2074
+ clearTimeout(pending.timer);
2075
+ if (ack.error) {
2076
+ pending.reject(new Error(ack.error));
2077
+ } else {
2078
+ pending.resolve(ack.message_id ?? "");
2079
+ }
2080
+ }
2081
+ }
1992
2082
  if (msg.dispatch) {
1993
2083
  processWorkerSessionCommand(msg.dispatch).catch((err) => {
1994
2084
  reportSDKError("worker-session-command", err);
@@ -2048,11 +2138,14 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2048
2138
  const outputSchema = fmeta?.outputSchema;
2049
2139
  const inputBuf = inputSchema ? encodeWithSchema(inputSchema, payload) : toJsonBuffer(payload);
2050
2140
  const telemetryInputBuf = toJsonBuffer(payload);
2141
+ const _parsedCanonical = parseCanonicalFunctionName(canonical);
2142
+ const rpcFnName = _parsedCanonical?.fnName ?? canonical;
2143
+ const rpcServiceName = _parsedCanonical?.serviceName ?? "";
2051
2144
  const rootSpanId = crypto.randomUUID();
2052
2145
  const parentSpanId = opts?.parentSpanId ?? tc?.spanId ?? "";
2053
2146
  const rootStartedAt = Date.now();
2054
2147
  const hasRetries = maxRetries > 0;
2055
- reportCallStartAsync(traceId, rootSpanId, parentSpanId, canonical, rootStartedAt, telemetryInputBuf, 1, "rpc");
2148
+ reportCallStartAsync(traceId, rootSpanId, parentSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, 1, "rpc", rpcServiceName);
2056
2149
  return traceStorage.run({ traceId, spanId: rootSpanId }, async () => {
2057
2150
  if (opts?.mode === "proxy") {
2058
2151
  return await new Promise((resolve, reject) => {
@@ -2064,15 +2157,15 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2064
2157
  timeout_ms: timeout
2065
2158
  }, meta, { deadline: new Date(Date.now() + timeout) }, (err, res) => {
2066
2159
  if (err) {
2067
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, false, 1, undefined, String(err), "rpc", parentSpanId);
2160
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, false, 1, undefined, String(err), "rpc", parentSpanId, rpcServiceName);
2068
2161
  return reject(normalizeServiceError(err, fn, "control-plane"));
2069
2162
  }
2070
2163
  if (!res?.success) {
2071
2164
  const errMsg = res?.error ?? "proxy call failed";
2072
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, false, 1, undefined, errMsg, "rpc", parentSpanId);
2165
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, false, 1, undefined, errMsg, "rpc", parentSpanId, rpcServiceName);
2073
2166
  return reject(normalizeServiceError(new Error(errMsg), fn, "worker"));
2074
2167
  }
2075
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, true, 1, res.output, "", "rpc", parentSpanId);
2168
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, true, 1, res.output, "", "rpc", parentSpanId, rpcServiceName);
2076
2169
  try {
2077
2170
  const out = res.output?.length ? outputSchema ? decodeWithSchema(outputSchema, res.output) : JSON.parse(res.output.toString()) : undefined;
2078
2171
  resolve(out);
@@ -2089,10 +2182,9 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2089
2182
  for (let attempt = 1;attempt <= maxRetries + 1; attempt++) {
2090
2183
  const attemptSpanId = hasRetries ? crypto.randomUUID() : rootSpanId;
2091
2184
  const attemptParentId = hasRetries ? rootSpanId : parentSpanId;
2092
- const attemptFn = canonical;
2093
2185
  const attemptStartedAt = hasRetries ? Date.now() : rootStartedAt;
2094
2186
  if (hasRetries) {
2095
- reportCallStartAsync(traceId, attemptSpanId, attemptParentId, attemptFn, attemptStartedAt, telemetryInputBuf, attempt, "rpc");
2187
+ reportCallStartAsync(traceId, attemptSpanId, attemptParentId, rpcFnName, attemptStartedAt, telemetryInputBuf, attempt, "rpc", rpcServiceName);
2096
2188
  }
2097
2189
  try {
2098
2190
  const attemptTimeoutMs = timeout;
@@ -2141,22 +2233,22 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2141
2233
  const result = outputSchema ? decodeWithSchema(outputSchema, outputBuf) : outputBuf?.length ? JSON.parse(outputBuf.toString()) : {};
2142
2234
  const encodedOutput = outputBuf?.length ? toJsonBuffer(result) : Buffer.alloc(0);
2143
2235
  if (hasRetries) {
2144
- reportCallAsync(traceId, attemptSpanId, attemptFn, attemptStartedAt, telemetryInputBuf, true, attempt, encodedOutput, "", "rpc", attemptParentId);
2145
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, true, attempt, encodedOutput, "", "rpc", parentSpanId);
2236
+ reportCallAsync(traceId, attemptSpanId, rpcFnName, attemptStartedAt, telemetryInputBuf, true, attempt, encodedOutput, "", "rpc", attemptParentId, rpcServiceName);
2237
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, true, attempt, encodedOutput, "", "rpc", parentSpanId, rpcServiceName);
2146
2238
  } else {
2147
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, true, attempt, encodedOutput, "", "rpc", parentSpanId);
2239
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, true, attempt, encodedOutput, "", "rpc", parentSpanId, rpcServiceName);
2148
2240
  }
2149
2241
  return result;
2150
2242
  } catch (e) {
2151
2243
  lastError = e;
2152
2244
  const errMsg = normalizeUnknownErrorMessage(e);
2153
2245
  if (hasRetries) {
2154
- reportCallAsync(traceId, attemptSpanId, attemptFn, attemptStartedAt, telemetryInputBuf, false, attempt, undefined, errMsg, "rpc", attemptParentId);
2246
+ reportCallAsync(traceId, attemptSpanId, rpcFnName, attemptStartedAt, telemetryInputBuf, false, attempt, undefined, errMsg, "rpc", attemptParentId, rpcServiceName);
2155
2247
  if (attempt > maxRetries) {
2156
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, false, attempt, undefined, errMsg, "rpc", parentSpanId);
2248
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, false, attempt, undefined, errMsg, "rpc", parentSpanId, rpcServiceName);
2157
2249
  }
2158
2250
  } else {
2159
- reportCallAsync(traceId, rootSpanId, canonical, rootStartedAt, telemetryInputBuf, false, attempt, undefined, errMsg, "rpc", parentSpanId);
2251
+ reportCallAsync(traceId, rootSpanId, rpcFnName, rootStartedAt, telemetryInputBuf, false, attempt, undefined, errMsg, "rpc", parentSpanId, rpcServiceName);
2160
2252
  }
2161
2253
  if (attempt <= maxRetries) {
2162
2254
  await new Promise((r) => setTimeout(r, baseDelay * 2 ** (attempt - 1)));
@@ -2184,6 +2276,36 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2184
2276
  }, meta, unaryDeadlineOptions(), (err, res) => err ? reject(normalizeServiceError(err, `publish:${topic}`)) : resolve(res?.message_id ?? ""));
2185
2277
  });
2186
2278
  },
2279
+ publishEvent(topic, payload, opts) {
2280
+ const stream = workerSessionStream;
2281
+ if (!stream) {
2282
+ return Promise.reject(new Error("publishEvent requires an active worker session (call serve() first)"));
2283
+ }
2284
+ const tc = traceStorage.getStore();
2285
+ const requestId = crypto.randomUUID();
2286
+ const msg = {
2287
+ request_id: requestId,
2288
+ topic,
2289
+ payload: toJsonBuffer(payload),
2290
+ headers: toWireStringMap(opts?.headers),
2291
+ trace_id: opts?.traceId ?? tc?.traceId ?? "",
2292
+ parent_span_id: opts?.parentSpanId ?? tc?.spanId ?? ""
2293
+ };
2294
+ return new Promise((resolve, reject) => {
2295
+ const timer = setTimeout(() => {
2296
+ pendingPublishAcks.delete(requestId);
2297
+ reject(new Error(`publishEvent timed out after 30 s (topic="${topic}", request_id="${requestId}")`));
2298
+ }, 30000);
2299
+ pendingPublishAcks.set(requestId, { resolve, reject, timer });
2300
+ try {
2301
+ stream.write({ publish_message: msg });
2302
+ } catch (err) {
2303
+ pendingPublishAcks.delete(requestId);
2304
+ clearTimeout(timer);
2305
+ reject(normalizeServiceError(err, `publish-event:${topic}`));
2306
+ }
2307
+ });
2308
+ },
2187
2309
  handleRpc(fn, handler, opts) {
2188
2310
  fnHandlers.set(fn, { handler, opts: opts ?? {} });
2189
2311
  return svc;
@@ -2343,36 +2465,35 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2343
2465
  }
2344
2466
  return name;
2345
2467
  },
2346
- async runWorkflow(name, input, opts) {
2468
+ async executeWorkflow(name, input, opts) {
2347
2469
  await _controlReady;
2348
2470
  const payload = toJsonBuffer(input ?? {});
2349
2471
  return new Promise((resolve, reject) => {
2350
- stub.RunWorkflow({
2472
+ stub.ExecuteWorkflow({
2351
2473
  name,
2352
2474
  input: payload,
2353
2475
  service_name: service
2354
2476
  }, meta, unaryDeadlineOptions(), (err, res) => {
2355
2477
  if (err) {
2356
- reject(normalizeServiceError(err, "run-workflow"));
2478
+ reject(normalizeServiceError(err, "execute-workflow"));
2357
2479
  return;
2358
2480
  }
2359
2481
  resolve({
2360
- runId: res?.run_id ?? "",
2361
2482
  traceId: res?.trace_id ?? ""
2362
2483
  });
2363
2484
  });
2364
2485
  });
2365
2486
  },
2366
- async cancelWorkflowRun(runId) {
2487
+ async cancelWorkflow(traceId) {
2367
2488
  await _controlReady;
2368
2489
  await new Promise((resolve, reject) => {
2369
- stub.CancelWorkflow({ run_id: runId }, meta, unaryDeadlineOptions(), (err, res) => {
2490
+ stub.CancelWorkflow({ trace_id: traceId }, meta, unaryDeadlineOptions(), (err, res) => {
2370
2491
  if (err) {
2371
- reject(normalizeServiceError(err, "cancel-workflow-run"));
2492
+ reject(normalizeServiceError(err, "cancel-workflow"));
2372
2493
  return;
2373
2494
  }
2374
2495
  if (!res?.cancelled) {
2375
- reject(new Error(`workflow run ${runId} was not cancelled`));
2496
+ reject(new Error(`workflow trace ${traceId} was not cancelled`));
2376
2497
  return;
2377
2498
  }
2378
2499
  resolve();
@@ -2419,12 +2540,12 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2419
2540
  }
2420
2541
  }
2421
2542
  },
2422
- watchRun(runId, opts) {
2543
+ watchTrace(traceId, opts) {
2423
2544
  const key = opts?.key ?? "";
2424
2545
  const fromSeq = opts?.fromSequence ?? 0;
2425
- const WATCH_RUN_QUEUE_LIMIT = 256;
2426
- const WATCH_RUN_RETRY_MIN_MS = 500;
2427
- const WATCH_RUN_RETRY_MAX_MS = 5000;
2546
+ const WATCH_TRACE_QUEUE_LIMIT = 256;
2547
+ const WATCH_TRACE_RETRY_MIN_MS = 500;
2548
+ const WATCH_TRACE_RETRY_MAX_MS = 5000;
2428
2549
  return {
2429
2550
  [Symbol.asyncIterator]() {
2430
2551
  let stream = null;
@@ -2432,7 +2553,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2432
2553
  let cancelled = false;
2433
2554
  let done = false;
2434
2555
  let fatalError = null;
2435
- let reconnectDelay = WATCH_RUN_RETRY_MIN_MS;
2556
+ let reconnectDelay = WATCH_TRACE_RETRY_MIN_MS;
2436
2557
  let lastSequence = fromSeq;
2437
2558
  const queue = [];
2438
2559
  const waiters = [];
@@ -2458,21 +2579,21 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2458
2579
  data = JSON.parse(rawData.toString());
2459
2580
  } catch (cause) {
2460
2581
  throw new ServiceBridgeError({
2461
- message: `watchRun received non-JSON chunk for ${runId}`,
2582
+ message: `watchTrace received non-JSON chunk for ${traceId}`,
2462
2583
  component: "sdk",
2463
- operation: `watch-run:${runId}`,
2584
+ operation: `watch-trace:${traceId}`,
2464
2585
  severity: "fatal",
2465
2586
  cause
2466
2587
  });
2467
2588
  }
2468
2589
  }
2469
2590
  return {
2470
- type: chunk.type === "run_complete" ? "run_complete" : "chunk",
2471
- runId,
2591
+ type: chunk.type === "trace_complete" ? "trace_complete" : "chunk",
2592
+ traceId,
2472
2593
  key: chunk.key || key,
2473
2594
  sequence: Number(chunk.sequence ?? 0),
2474
2595
  data,
2475
- runStatus: chunk.run_status || undefined
2596
+ traceStatus: chunk.trace_status || undefined
2476
2597
  };
2477
2598
  }
2478
2599
  function clearReconnectTimer() {
@@ -2514,11 +2635,11 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2514
2635
  }
2515
2636
  return;
2516
2637
  }
2517
- if (queue.length >= WATCH_RUN_QUEUE_LIMIT) {
2638
+ if (queue.length >= WATCH_TRACE_QUEUE_LIMIT) {
2518
2639
  fail(new ServiceBridgeError({
2519
- message: `watchRun consumer is not draining fast enough for ${runId}`,
2640
+ message: `watchTrace consumer is not draining fast enough for ${traceId}`,
2520
2641
  component: "sdk",
2521
- operation: `watch-run:${runId}`,
2642
+ operation: `watch-trace:${traceId}`,
2522
2643
  severity: "fatal"
2523
2644
  }));
2524
2645
  return;
@@ -2537,14 +2658,14 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2537
2658
  waiters.length = 0;
2538
2659
  }
2539
2660
  function shouldRetry(err) {
2540
- if (metadataValue(err, "servicebridge-run-stream-retryable") === "true") {
2661
+ if (metadataValue(err, "servicebridge-trace-stream-retryable") === "true") {
2541
2662
  return true;
2542
2663
  }
2543
2664
  const code = err?.code;
2544
2665
  return code === grpc.status.UNAVAILABLE || code === grpc.status.UNKNOWN || code === grpc.status.DEADLINE_EXCEEDED || code === grpc.status.RESOURCE_EXHAUSTED;
2545
2666
  }
2546
2667
  function maybeUpdateResumeSequence(err) {
2547
- const parsed = Number(metadataValue(err, "servicebridge-run-stream-resume-from") ?? 0);
2668
+ const parsed = Number(metadataValue(err, "servicebridge-trace-stream-resume-from") ?? 0);
2548
2669
  if (Number.isFinite(parsed) && parsed > lastSequence) {
2549
2670
  lastSequence = parsed;
2550
2671
  }
@@ -2557,7 +2678,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2557
2678
  reconnectTimer = null;
2558
2679
  start();
2559
2680
  }, delay);
2560
- reconnectDelay = Math.min(reconnectDelay * 2, WATCH_RUN_RETRY_MAX_MS);
2681
+ reconnectDelay = Math.min(reconnectDelay * 2, WATCH_TRACE_RETRY_MAX_MS);
2561
2682
  }
2562
2683
  function start() {
2563
2684
  if (cancelled || done || fatalError || stream)
@@ -2565,25 +2686,25 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2565
2686
  if (!dataPlaneStub) {
2566
2687
  fail(new ServiceBridgeError({
2567
2688
  message: "DataPlane service is not available in this runtime version",
2568
- component: "watch-run",
2569
- operation: "watch-run-unavailable",
2689
+ component: "watch-trace",
2690
+ operation: "watch-trace-unavailable",
2570
2691
  severity: "fatal"
2571
2692
  }));
2572
2693
  return;
2573
2694
  }
2574
2695
  try {
2575
- const current = dataPlaneStub.WatchRun({ run_id: runId, key, from_seq: lastSequence }, meta);
2696
+ const current = dataPlaneStub.WatchTrace({ trace_id: traceId, key, from_seq: lastSequence }, meta);
2576
2697
  let endedWithOK = false;
2577
2698
  stream = current;
2578
2699
  current.on("data", (chunk) => {
2579
2700
  if (stream !== current || cancelled || done || fatalError)
2580
2701
  return;
2581
- reconnectDelay = WATCH_RUN_RETRY_MIN_MS;
2702
+ reconnectDelay = WATCH_TRACE_RETRY_MIN_MS;
2582
2703
  let event;
2583
2704
  try {
2584
2705
  event = parseChunk(chunk);
2585
2706
  } catch (err) {
2586
- fail(err instanceof ServiceBridgeError ? err : normalizeServiceError(err, `watch-run:${runId}`));
2707
+ fail(err instanceof ServiceBridgeError ? err : normalizeServiceError(err, `watch-trace:${traceId}`));
2587
2708
  return;
2588
2709
  }
2589
2710
  if (event.sequence > 0) {
@@ -2592,7 +2713,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2592
2713
  lastSequence = event.sequence;
2593
2714
  }
2594
2715
  enqueue(event);
2595
- if (event.type === "run_complete") {
2716
+ if (event.type === "trace_complete") {
2596
2717
  finish();
2597
2718
  }
2598
2719
  });
@@ -2602,10 +2723,10 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2602
2723
  clearStream(false);
2603
2724
  maybeUpdateResumeSequence(err);
2604
2725
  if (!shouldRetry(err)) {
2605
- fail(normalizeServiceError(err, `watch-run:${runId}`));
2726
+ fail(normalizeServiceError(err, `watch-trace:${traceId}`));
2606
2727
  return;
2607
2728
  }
2608
- reconnectDelay = metadataValue(err, "servicebridge-run-stream-disconnect-reason") ? WATCH_RUN_RETRY_MIN_MS : reconnectDelay;
2729
+ reconnectDelay = metadataValue(err, "servicebridge-trace-stream-disconnect-reason") ? WATCH_TRACE_RETRY_MIN_MS : reconnectDelay;
2609
2730
  scheduleReconnect();
2610
2731
  });
2611
2732
  current.on("status", (statusInfo) => {
@@ -2633,7 +2754,7 @@ function servicebridge(url, serviceKey, serviceOrOpts = {}, maybeGlobalOpts = {}
2633
2754
  } catch (err) {
2634
2755
  maybeUpdateResumeSequence(err);
2635
2756
  if (!shouldRetry(err)) {
2636
- fail(normalizeServiceError(err, `watch-run:${runId}`));
2757
+ fail(normalizeServiceError(err, `watch-trace:${traceId}`));
2637
2758
  return;
2638
2759
  }
2639
2760
  scheduleReconnect();
@@ -2728,9 +2849,9 @@ function captureConsole(svc) {
2728
2849
  }
2729
2850
  }
2730
2851
  export {
2852
+ withTraceContext,
2731
2853
  validateV2Config,
2732
2854
  servicebridge,
2733
- runWithTraceContext,
2734
2855
  getTraceContext,
2735
2856
  V2SessionClient,
2736
2857
  ServiceBridgeError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "service-bridge",
3
- "version": "1.6.0-dev.32",
3
+ "version": "1.7.0-dev.34",
4
4
  "type": "module",
5
5
  "description": "ServiceBridge SDK for Node.js — production-ready RPC, durable events, workflows, jobs, and distributed tracing. One Go runtime + PostgreSQL replaces Istio, RabbitMQ, Temporal, and Jaeger.",
6
6
  "keywords": [