@telemai/sdk 0.3.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -207,18 +207,35 @@ results of each search step.
207
207
 
208
208
  ## Monitors
209
209
 
210
- A monitor runs a query on a schedule. Poll it to get the results that are new since
211
- your last poll. `poll` returns a `SearchResponse`:
210
+ A monitor runs a query on a schedule, and its agent checks every result against the
211
+ query and keeps the ones that match. A monitor has the agent unless you create it with
212
+ `agent: false`, and a monitor with the agent needs a `ttl`, at most `"30d"`: without
213
+ one, `create` throws a `TelemError` with the code `invalid_request`.
212
214
 
213
215
  ```ts
214
- const monitor = await telem.monitors.create("Nvidia files a new 8-K with the SEC", { frequency: "1h", ttl: "7d" })
215
- const response = await telem.monitors.poll(monitor.id)
216
+ const monitor = await telem.monitors.create("Nvidia files a new 8-K with the SEC", { frequency: "1h", ttl: "30d" })
217
+ console.log(monitor.agentState?.status) // what its agent does: compiling, then ready
218
+ for await (const change of telem.monitors.iterateChanges(monitor.id)) {
219
+ console.log(change.seq, change.kind, change.subject.name)
220
+ }
221
+ const agent = await telem.monitors.agent(monitor.id)
216
222
  await telem.monitors.update(monitor.id, { status: "paused" })
223
+ await telem.monitors.update(monitor.id, { ttl: "30d" }) // renew: it ends 30 days from now
217
224
  await telem.monitors.delete(monitor.id)
218
225
  ```
219
226
 
220
- `telem.monitors` also has `get` and `list`. See [Monitors](https://docs.telem.ai/resources/monitors/)
221
- for the fields and the errors.
227
+ `iterateChanges` reads what the agent kept, oldest first, until you are caught up,
228
+ and then stops. Keep the `seq` of the last change you handled and pass it as `cursor`
229
+ next time: no change comes twice, and none is skipped. `changes` returns one page
230
+ (`nextCursor`, `watermark`) and takes `cursor`, `since`, `kinds` and `limit`. `agent`
231
+ returns the agent itself: its status, query, frequency and plan version.
232
+ `monitor.agentState` shows it on the monitor; when the agent did not start, the
233
+ create's `monitor.agentNote` says why. Read `iterateChanges` with `for await`.
234
+
235
+ `telem.monitors` also has `get` and `list`. `poll` is deprecated and warns once:
236
+ read the agent's changes with `iterateChanges` instead.
237
+ See [Monitors](https://docs.telem.ai/resources/monitors/) for the fields, the errors and
238
+ the older reads.
222
239
 
223
240
  ## Vercel AI SDK
224
241
 
@@ -369,7 +386,7 @@ try {
369
386
 
370
387
  | Situation | Result |
371
388
  |---|---|
372
- | Bad input (empty query, empty URL list) | `TelemError`, `code: "invalid_request"`, nothing sent |
389
+ | Bad input (empty query, empty URL list, a monitor with an agent and no `ttl`) | `TelemError`, `code: "invalid_request"`, nothing sent |
373
390
  | Network failure | `TelemError`, `code: "connection_error"`, after the retries |
374
391
  | 5xx or 429 | Retried with backoff (`Retry-After` applies), then `TelemError` with the status |
375
392
  | Other 4xx | `TelemError` with the status and the code from the server, not retried |
@@ -80,8 +80,10 @@ function defaultMessage(status) {
80
80
  if (status === 404) return "Not found";
81
81
  return `Unexpected status code ${status}`;
82
82
  }
83
+ var BLOCKED_MESSAGE = "The request was blocked by a firewall before it reached Telem; the text may have characters that are not allowed.";
83
84
  function statusError(status, body) {
84
- return new TelemError(detailFromBody(body) ?? defaultMessage(status), {
85
+ const fallback = status === 403 && !isObject(body) ? BLOCKED_MESSAGE : defaultMessage(status);
86
+ return new TelemError(detailFromBody(body) ?? fallback, {
85
87
  status,
86
88
  code: codeFromBody(body),
87
89
  details: body
package/dist/index.cjs CHANGED
@@ -60,7 +60,7 @@ var TimeoutError = class extends TelemError {
60
60
  };
61
61
 
62
62
  // src/version.ts
63
- var VERSION = "0.3.3";
63
+ var VERSION = "0.5.0";
64
64
 
65
65
  // src/config.ts
66
66
  var DEFAULT_BASE_URL = "https://router.telem.ai";
@@ -120,6 +120,7 @@ function resolveConfig(options) {
120
120
  if (typeof send !== "function") {
121
121
  throw invalidOption("no fetch on this runtime: pass one with the `fetch` option");
122
122
  }
123
+ if (typeof opts.apiKey === "string" && !opts.apiKey.trim()) throw invalidOption("apiKey is empty");
123
124
  let apiKey = opts.apiKey || env.TELEM_API_KEY || void 0;
124
125
  let baseUrl = opts.baseUrl || env.TELEM_BASE_URL || void 0;
125
126
  if (apiKey === void 0 || baseUrl === void 0) {
@@ -232,8 +233,10 @@ function defaultMessage(status) {
232
233
  if (status === 404) return "Not found";
233
234
  return `Unexpected status code ${status}`;
234
235
  }
236
+ var BLOCKED_MESSAGE = "The request was blocked by a firewall before it reached Telem; the text may have characters that are not allowed.";
235
237
  function statusError(status, body) {
236
- return new TelemError(detailFromBody(body) ?? defaultMessage(status), {
238
+ const fallback = status === 403 && !isObject(body) ? BLOCKED_MESSAGE : defaultMessage(status);
239
+ return new TelemError(detailFromBody(body) ?? fallback, {
237
240
  status,
238
241
  code: codeFromBody(body),
239
242
  details: body
@@ -698,7 +701,8 @@ function providerRun(run) {
698
701
  related: envelope.related ?? null,
699
702
  verticals: envelope.verticals ?? null,
700
703
  usage: envelope.usage ?? null,
701
- warnings: asArray(envelope.warnings)
704
+ warnings: asArray(envelope.warnings),
705
+ sameStory: isObject(envelope.same_story) ? envelope.same_story : null
702
706
  };
703
707
  }
704
708
  function buildSearchResponse(interaction) {
@@ -722,9 +726,33 @@ async function search(config, query, options) {
722
726
  }
723
727
 
724
728
  // src/methods/monitors.ts
729
+ var NEEDS_TTL = 'A monitor with an agent must end: pass ttl, such as ttl: "30d" (at most 30 days), or agent: false for a monitor without an agent.';
730
+ var POLL_DEPRECATED = "@telemai/sdk: monitors.poll() is deprecated: read what the monitor agent finds with monitors.iterateChanges() or monitors.changes().";
731
+ var pollWarned = false;
732
+ function warnPollDeprecated() {
733
+ if (pollWarned) return;
734
+ pollWarned = true;
735
+ console.warn(POLL_DEPRECATED);
736
+ }
737
+ function agentStateFrom(data) {
738
+ return {
739
+ engine: String(data.engine ?? ""),
740
+ status: String(data.status ?? ""),
741
+ needsAttention: data.needs_attention === true,
742
+ planVersion: typeof data.plan_version === "number" ? data.plan_version : null,
743
+ nextRunAt: typeof data.next_run_at === "string" ? data.next_run_at : null,
744
+ watermark: Number(data.watermark ?? 0),
745
+ // No compile job yet is null, not an object of empty strings.
746
+ compile: isObject(data.compile) ? {
747
+ jobId: String(data.compile.job_id ?? ""),
748
+ status: String(data.compile.status ?? ""),
749
+ error: typeof data.compile.error === "string" ? data.compile.error : null
750
+ } : null
751
+ };
752
+ }
725
753
  function monitorFrom(body) {
726
754
  const data = asObject(body);
727
- return {
755
+ const monitor = {
728
756
  id: String(data.id ?? ""),
729
757
  query: data.query ?? "",
730
758
  frequency: data.frequency ?? "",
@@ -735,51 +763,141 @@ function monitorFrom(body) {
735
763
  expiresAt: data.expires_at ?? null,
736
764
  webhookUrl: data.webhook_url ?? null,
737
765
  webhookSecret: data.webhook_secret ?? null,
766
+ agentNote: typeof data.agent_note === "string" ? data.agent_note : null,
738
767
  raw: data
739
768
  };
769
+ if (data.agent_state !== void 0) {
770
+ monitor.agentState = isObject(data.agent_state) ? agentStateFrom(data.agent_state) : null;
771
+ }
772
+ return monitor;
773
+ }
774
+ function agentDocumentFrom(body) {
775
+ const data = asObject(body);
776
+ const document = { raw: data };
777
+ if (typeof data.status === "string") document.status = data.status;
778
+ if (typeof data.query === "string") document.query = data.query;
779
+ if (typeof data.frequency === "string") document.frequency = data.frequency;
780
+ if (typeof data.spec_version === "number") document.specVersion = data.spec_version;
781
+ if (data.budget !== void 0) document.budget = isObject(data.budget) ? data.budget : null;
782
+ if (typeof data.spend_usd === "number") document.spendUsd = data.spend_usd;
783
+ return document;
784
+ }
785
+ function changeRecordFrom(entry) {
786
+ const subject = asObject(entry.subject);
787
+ return {
788
+ seq: Number(entry.seq ?? 0),
789
+ type: String(entry.type ?? ""),
790
+ kind: String(entry.kind ?? ""),
791
+ monitorId: String(entry.monitor_id ?? ""),
792
+ subject: {
793
+ entityId: typeof subject.entity_id === "string" ? subject.entity_id : null,
794
+ name: typeof subject.name === "string" ? subject.name : null
795
+ },
796
+ // The values depend on the kind (an object, a status string, nothing): as sent.
797
+ before: entry.before ?? null,
798
+ after: entry.after ?? null,
799
+ factIds: asArray(entry.fact_ids).map(String),
800
+ at: String(entry.at ?? ""),
801
+ raw: entry
802
+ };
803
+ }
804
+ function changesPageFrom(body) {
805
+ const data = asObject(body);
806
+ return {
807
+ changes: asArray(data.changes).filter(isObject).map(changeRecordFrom),
808
+ nextCursor: Number(data.next_cursor ?? 0),
809
+ watermark: Number(data.watermark ?? 0)
810
+ };
811
+ }
812
+ function listQuery(options) {
813
+ const params = new URLSearchParams();
814
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
815
+ if (options.cursor !== void 0) params.set("cursor", options.cursor);
816
+ const query = params.toString();
817
+ return query ? `?${query}` : "";
818
+ }
819
+ function sinceParam(since) {
820
+ if (typeof since === "string") return since;
821
+ if (Number.isNaN(since.getTime())) throw invalidRequest("since is not a valid date");
822
+ return since.toISOString();
823
+ }
824
+ function changesQuery(options) {
825
+ const params = new URLSearchParams();
826
+ if (options.cursor !== void 0) params.set("cursor", String(options.cursor));
827
+ if (options.since !== void 0) params.set("since", sinceParam(options.since));
828
+ if (options.kinds !== void 0) params.set("kinds", options.kinds.join(","));
829
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
830
+ const query = params.toString();
831
+ return query ? `?${query}` : "";
740
832
  }
741
833
  function createMonitors(config) {
742
834
  const path = (monitorId, suffix = "") => {
743
835
  if (!monitorId || /^\.+$/.test(monitorId)) throw invalidRequest("a monitor id is required");
744
836
  return `/v1/monitors/${encodeURIComponent(monitorId)}${suffix}`;
745
837
  };
838
+ const listPage = async (options = {}) => {
839
+ const route = `/v1/monitors${listQuery(options)}`;
840
+ const data = asObject(await request(config, "GET", route, void 0, options.signal));
841
+ const nextCursor = typeof data.next_cursor === "string" ? data.next_cursor : null;
842
+ return { monitors: asArray(data.monitors).map(monitorFrom), nextCursor };
843
+ };
844
+ const changes = async (monitorId, options = {}) => {
845
+ const route = `${path(monitorId, "/agent/changes")}${changesQuery(options)}`;
846
+ return changesPageFrom(await request(config, "GET", route, void 0, options.signal));
847
+ };
746
848
  return {
747
- /** Create a monitor. Telem runs `query` on the schedule. */
748
- async create(query, options = {}) {
849
+ /** Create a monitor. Telem runs `query` on the schedule, and its agent keeps what matches. */
850
+ async create(query, options) {
851
+ const given = options ?? {};
852
+ const agent = given.agent == null ? true : Boolean(given.agent);
853
+ if (agent && !given.ttl) throw invalidRequest(NEEDS_TTL);
749
854
  const monitorOptions = {};
750
- if (options.frequency !== void 0) monitorOptions.frequency = options.frequency;
751
- if (options.includeDomains !== void 0) {
752
- monitorOptions.include_domains = options.includeDomains;
855
+ if (given.frequency !== void 0) monitorOptions.frequency = given.frequency;
856
+ if (given.includeDomains !== void 0) {
857
+ monitorOptions.include_domains = given.includeDomains;
753
858
  }
754
- if (options.search !== void 0) monitorOptions.search = searchBlock({}, options.search);
859
+ if (given.search !== void 0) monitorOptions.search = searchBlock({}, given.search);
755
860
  const body = { query, options: monitorOptions };
756
- if (options.metadata !== void 0) body.metadata = options.metadata;
757
- if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
758
- if (options.ttl !== void 0) body.ttl = options.ttl;
759
- const data = await request(config, "POST", "/v1/monitors", body, options.signal, "rate_limit");
861
+ if (given.metadata !== void 0) body.metadata = given.metadata;
862
+ if (given.webhookUrl !== void 0) body.webhook_url = given.webhookUrl;
863
+ if (given.ttl !== void 0) body.ttl = given.ttl;
864
+ body.agent = agent;
865
+ const data = await request(config, "POST", "/v1/monitors", body, given.signal, "rate_limit").catch(
866
+ (error) => {
867
+ if (error instanceof TimeoutError) {
868
+ throw new TimeoutError(
869
+ `${error.message}. The monitor may exist: list monitors before you create it again.`,
870
+ error.cause
871
+ );
872
+ }
873
+ throw error;
874
+ }
875
+ );
760
876
  return monitorFrom(data);
761
877
  },
762
- /** List your monitors, newest first (server default 50, maximum 200). */
878
+ /** List your monitors, newest first. `listPage` also gives the next cursor. */
763
879
  async list(options = {}) {
764
- const query = options.limit === void 0 ? "" : `?limit=${options.limit}`;
765
- const body = await request(config, "GET", `/v1/monitors${query}`, void 0, options.signal);
766
- return asArray(asObject(body).monitors).map(monitorFrom);
880
+ return (await listPage(options)).monitors;
767
881
  },
882
+ listPage,
768
883
  /** Get one monitor. */
769
884
  async get(monitorId, options = {}) {
770
885
  return monitorFrom(await request(config, "GET", path(monitorId), void 0, options.signal));
771
886
  },
772
- /** Pause, resume or change a monitor. Only the fields given are sent. */
887
+ /** Pause, resume, renew or change a monitor. Only the fields given are sent. */
773
888
  async update(monitorId, options) {
774
889
  const body = {};
775
890
  if (options.status !== void 0) body.status = options.status;
776
891
  if (options.frequency !== void 0) body.frequency = options.frequency;
777
- if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
892
+ if (options.ttl !== void 0) body.ttl = options.ttl;
893
+ if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl || null;
778
894
  const route = path(monitorId);
779
895
  return monitorFrom(await request(config, "PATCH", route, body, options.signal));
780
896
  },
781
- /** Get the results that are new since the last poll. */
897
+ /** Get the monitor's own results that are new since the last poll. Deprecated: it
898
+ * warns once per process. */
782
899
  async poll(monitorId, options = {}) {
900
+ warnPollDeprecated();
783
901
  const body = {};
784
902
  if (options.session !== void 0) body.session_id = options.session;
785
903
  if (options.metadata !== void 0) body.metadata = options.metadata;
@@ -790,6 +908,23 @@ function createMonitors(config) {
790
908
  /** Delete a monitor. A repeat deletes what a failed delete left. */
791
909
  async delete(monitorId, options = {}) {
792
910
  await request(config, "DELETE", path(monitorId), void 0, options.signal);
911
+ },
912
+ /** Read the monitor agent's document. A monitor without an agent answers 404. */
913
+ async agent(monitorId, options = {}) {
914
+ const route = path(monitorId, "/agent");
915
+ return agentDocumentFrom(await request(config, "GET", route, void 0, options.signal));
916
+ },
917
+ /** One page of the agent's changes. Keep `nextCursor` and pass it back as `cursor`. */
918
+ changes,
919
+ /** Every change after `cursor`, page by page, until you are caught up. */
920
+ async *iterateChanges(monitorId, options = {}) {
921
+ let cursor = options.cursor ?? 0;
922
+ for (; ; ) {
923
+ const page = await changes(monitorId, { cursor, signal: options.signal });
924
+ for (const change of page.changes) yield change;
925
+ if (!page.changes.length || page.nextCursor <= cursor || page.nextCursor >= page.watermark) return;
926
+ cursor = page.nextCursor;
927
+ }
793
928
  }
794
929
  };
795
930
  }
@@ -892,7 +1027,8 @@ var Telem = class {
892
1027
  maxRetries;
893
1028
  /** The session read surface: `list` and `results(id)`. */
894
1029
  sessions;
895
- /** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
1030
+ /** Monitors: `create`, `list`, `listPage`, `get`, `update` and `delete`, the agent reads
1031
+ * `agent`, `changes` and `iterateChanges`, and the deprecated `poll`. */
896
1032
  monitors;
897
1033
  #config;
898
1034
  /**
package/dist/index.d.cts CHANGED
@@ -157,6 +157,8 @@ type ProviderRun = {
157
157
  verticals: Json | null;
158
158
  usage: Json | unknown[] | null;
159
159
  warnings: Json[];
160
+ /** Monitor polls: results folded as the same story, `{leadUrl: [{url, title}]}`. */
161
+ sameStory: Record<string, Json[]> | null;
160
162
  raw: Json | null;
161
163
  };
162
164
  type SearchResponse = {
@@ -207,7 +209,7 @@ type ProviderInfo = {
207
209
  url: string | null;
208
210
  normalized: boolean;
209
211
  tiers: string[];
210
- /** Whether auto-routing can pick it for a ranked topic; `null` from a router that does not say. */
212
+ /** Whether auto-routing can pick it; `null` from a router that does not say. */
211
213
  autoRouted: boolean | null;
212
214
  };
213
215
  /** One session as `sessions.list` reports it. */
@@ -236,34 +238,55 @@ type SessionsResource = {
236
238
  };
237
239
  /** The search options a monitor can run on its schedule (`MonitorCreateOptions.search`). */
238
240
  type MonitorSearchOptions = Pick<SearchOptions, "tier" | "numResults">;
239
- /** Options for `monitors.create`. */
240
- type MonitorCreateOptions = {
241
+ /** The options of `monitors.create` that a monitor takes with the agent or without. */
242
+ type MonitorCreateCommonOptions = {
241
243
  /** `"1h"`, `"6h"`, `"12h"`, `"1d"` or `"7d"`. If you leave it out, the monitor runs once a day. */
242
244
  frequency?: string;
243
245
  /** Also run a web search for the query on the schedule, with these options. `{}`
244
- * uses the defaults. Client search defaults do not apply. */
246
+ * uses the defaults. Client search defaults do not apply. It adds to what a poll returns. */
245
247
  search?: MonitorSearchOptions;
246
- /** Only results from these sites and their subdomains, such as `["sec.gov"]`,
247
- * from every source of the monitor. */
248
+ /** Only results from these sites and their subdomains, such as `["sec.gov"]`, in what
249
+ * a poll returns. */
248
250
  includeDomains?: string[];
249
- /** Request metadata (query lineage keys), sent as given. */
251
+ /** Query lineage keys, sent as given. They choose the session that records the polls of
252
+ * the monitor. */
250
253
  metadata?: Json;
251
254
  /** An `https` URL that receives the new results. */
252
255
  webhookUrl?: string;
253
- /** Delete the monitor after this long, such as `"12h"` or `"7d"` (at most `"30d"`). Without it, the monitor never expires. */
254
- ttl?: string;
255
256
  signal?: AbortSignal;
256
257
  };
258
+ /** Options for `monitors.create`. A monitor has the agent unless `agent` is `false`,
259
+ * and a monitor with the agent must end: then `ttl` is required. */
260
+ type MonitorCreateOptions = MonitorCreateCommonOptions & ({
261
+ /** `false` creates a monitor without an agent, which you read with the deprecated
262
+ * `monitors.poll`. */
263
+ agent: false;
264
+ /** End the monitor after this long, such as `"7d"` or `"30d"` (at most `"30d"`).
265
+ * Without it, a monitor without an agent never ends. */
266
+ ttl?: string;
267
+ } | {
268
+ /** Start the monitor agent with the monitor, the default: it checks every result
269
+ * against the query and keeps the ones that match. Read what it keeps with
270
+ * `monitors.iterateChanges` or `monitors.changes`. */
271
+ agent?: true;
272
+ /** End the monitor after this long, such as `"7d"` or `"30d"` (at most `"30d"`).
273
+ * Required with the agent. */
274
+ ttl: string;
275
+ });
257
276
  /** Options for `monitors.update`. Send at least one field. */
258
277
  type MonitorUpdateOptions = {
259
278
  /** `"paused"` or `"active"`. */
260
279
  status?: string;
261
280
  frequency?: string;
262
- /** A new webhook URL, with a new `webhookSecret` in the response. `null` removes it. */
281
+ /** A new webhook URL, with a new `webhookSecret` in the response. `null` or `""` removes it. */
263
282
  webhookUrl?: string | null;
283
+ /** A new end, this long from now, such as `"30d"` (at most `"30d"`). It renews a monitor
284
+ * before it ends; an ended monitor cannot be renewed. */
285
+ ttl?: string;
264
286
  signal?: AbortSignal;
265
287
  };
266
- /** Options for `monitors.poll`. */
288
+ /** Options for `monitors.poll`.
289
+ * @deprecated `poll` is deprecated: read the agent with `monitors.iterateChanges`. */
267
290
  type MonitorPollOptions = {
268
291
  /** Record this poll in another session (default: the monitor's own). */
269
292
  session?: string;
@@ -276,33 +299,169 @@ type Monitor = {
276
299
  id: string;
277
300
  query: string;
278
301
  frequency: string;
279
- /** `active` or `paused` (you paused it). */
302
+ /** `active`, `paused` (you paused it) or `ended` (past `expiresAt`). */
280
303
  status: string;
281
304
  sessionId: string;
282
305
  createdAt: string;
283
306
  lastPolledAt: string | null;
284
- /** When Telem deletes the monitor; `null`: never. */
307
+ /** When the monitor ends; `null`: never. */
285
308
  expiresAt: string | null;
286
309
  webhookUrl: string | null;
287
310
  /** Signs the webhook deliveries. Only in the response that set `webhookUrl`. */
288
311
  webhookSecret: string | null;
312
+ /** The monitor agent's state. `null`: the monitor has no agent. Absent when the router
313
+ * sends none, as a router from before the agent reads does. */
314
+ agentState?: AgentState | null;
315
+ /** Only in the answer to a create: why the monitor agent did not start, or not yet.
316
+ * `null` when it started, and in every other answer. */
317
+ agentNote: string | null;
318
+ raw: Json;
319
+ };
320
+ /** The latest compile of an agent's plan (`AgentState.compile`). */
321
+ type AgentCompileState = {
322
+ jobId: string;
323
+ /** The compile job's status, such as `done` or `failed`. */
324
+ status: string;
325
+ /** Why the compile failed; `null` when it did not. */
326
+ error: string | null;
327
+ };
328
+ /** The state of a monitor's agent: `monitor.agentState`. */
329
+ type AgentState = {
330
+ /** The agent engine, `"v4"` today. */
331
+ engine: string;
332
+ /** Such as `compiling`, `ready`, `paused` or `failed`. */
333
+ status: string;
334
+ /** `true` when `status` is `failed`. */
335
+ needsAttention: boolean;
336
+ /** The version of the agent's plan; `null` before it has one. */
337
+ planVersion: number | null;
338
+ /** When the agent runs next; `null` when no run is scheduled. */
339
+ nextRunAt: string | null;
340
+ /** The `seq` of the newest change `monitors.changes` can serve now. */
341
+ watermark: number;
342
+ /** The latest compile of the plan; `null` when there has been none. */
343
+ compile: AgentCompileState | null;
344
+ };
345
+ /** The monitor agent's document: `monitors.agent`. A field is there only when the
346
+ * router sent it. The rest of the document (the plan, the counts, the runs) is in `raw`. */
347
+ type AgentDocument = {
348
+ /** Such as `compiling`, `ready`, `paused`, `failed` or `ended`. */
349
+ status?: string;
350
+ query?: string;
351
+ frequency?: string;
352
+ /** The version of the plan; 0 while the agent compiles. */
353
+ specVersion?: number;
354
+ /** The agent's spending limits; `null` when none are set. */
355
+ budget?: Json | null;
356
+ /** What the agent has spent, in US dollars. */
357
+ spendUsd?: number;
358
+ raw: Json;
359
+ };
360
+ /** The item a change is about. `entityId` is `null` for a change to the whole monitor,
361
+ * such as a run or a plan change. */
362
+ type ChangeSubject = {
363
+ entityId: string | null;
364
+ name: string | null;
365
+ };
366
+ /** One change the monitor agent made. */
367
+ type ChangeRecord = {
368
+ /** The change's place in the stream. It grows with every change and is never reused.
369
+ * Pass the last one you handled as `cursor` to read on from there. */
370
+ seq: number;
371
+ /** The change's own type, such as `new_object`. */
372
+ type: string;
373
+ /** What happened: one of the six kinds `entity_added`, `value_changed`,
374
+ * `fact_status_changed`, `alert`, `run` or `plan_changed`. */
375
+ kind: string;
376
+ monitorId: string;
377
+ subject: ChangeSubject;
378
+ /** What it was before, as the router sent it; `null` when there is no before. Its shape
379
+ * depends on `kind`. */
380
+ before: unknown;
381
+ /** What it is now, as the router sent it. */
382
+ after: unknown;
383
+ /** The facts behind the change; empty when the router names none. */
384
+ factIds: string[];
385
+ /** When it happened, ISO 8601. */
386
+ at: string;
289
387
  raw: Json;
290
388
  };
389
+ /** One page of `monitors.changes`. */
390
+ type ChangesPage = {
391
+ changes: ChangeRecord[];
392
+ /** Keep this and pass it as `cursor` to read the changes after this page. On a full
393
+ * page (`limit` changes) it is the `seq` of the last change. On a shorter page, an
394
+ * empty one included, it is `watermark`: the read got that far, past the changes
395
+ * that `kinds` left out too. */
396
+ nextCursor: number;
397
+ /** The `seq` of the newest change that can be read now. You are caught up when
398
+ * `nextCursor` equals it, or when a page is empty. */
399
+ watermark: number;
400
+ };
401
+ /** Options for `monitors.changes`. */
402
+ type MonitorChangesOptions = {
403
+ /** Read the changes after this `seq`. If you leave it out, the page starts at the
404
+ * first change. Do not send it with `since`. */
405
+ cursor?: number;
406
+ /** Read from this time on: a `Date`, or an ISO 8601 time with an offset such as
407
+ * `"2026-11-03T12:00:00Z"`. Do not send it with `cursor`. */
408
+ since?: string | Date;
409
+ /** Only these kinds of change: `entity_added`, `value_changed`, `fact_status_changed`,
410
+ * `alert`, `run` or `plan_changed`, for example `["alert", "run"]`. Another name is a
411
+ * 400 (`kinds_invalid`). An empty list is no filter. */
412
+ kinds?: string[];
413
+ /** How many changes to return (server default 200, maximum 500). */
414
+ limit?: number;
415
+ signal?: AbortSignal;
416
+ };
417
+ /** Options for `monitors.iterateChanges`. */
418
+ type MonitorIterateChangesOptions = {
419
+ /** Read the changes after this `seq`. If you leave it out, the walk starts at the
420
+ * first change. */
421
+ cursor?: number;
422
+ signal?: AbortSignal;
423
+ };
424
+ /** Options for `monitors.list` and `monitors.listPage`. */
425
+ type MonitorListOptions = {
426
+ /** How many to return (server default 50, maximum 200). */
427
+ limit?: number;
428
+ /** The `nextCursor` of the page before. */
429
+ cursor?: string;
430
+ signal?: AbortSignal;
431
+ };
432
+ /** One page of `monitors.listPage`. */
433
+ type MonitorPage = {
434
+ monitors: Monitor[];
435
+ /** Pass as `cursor` to get the next page; `null` on the last page. */
436
+ nextCursor: string | null;
437
+ };
291
438
  /** The `telem.monitors` namespace. */
292
439
  type MonitorsResource = {
293
- create(query: string, options?: MonitorCreateOptions): Promise<Monitor>;
294
- list(options?: {
295
- limit?: number;
296
- signal?: AbortSignal;
297
- }): Promise<Monitor[]>;
440
+ /** Create a monitor. It has the agent unless `agent` is `false`, and with the agent
441
+ * `ttl` is required: a create without it throws a `TelemError` (`invalid_request`)
442
+ * before anything is sent. */
443
+ create(query: string, options: MonitorCreateOptions): Promise<Monitor>;
444
+ list(options?: MonitorListOptions): Promise<Monitor[]>;
445
+ listPage(options?: MonitorListOptions): Promise<MonitorPage>;
298
446
  get(monitorId: string, options?: {
299
447
  signal?: AbortSignal;
300
448
  }): Promise<Monitor>;
301
449
  update(monitorId: string, options: MonitorUpdateOptions): Promise<Monitor>;
450
+ /** Get the monitor's own results that are new since the last poll.
451
+ * @deprecated Read what the monitor agent finds with `iterateChanges` or `changes`.
452
+ * A poll still works, and warns once per process with `console.warn`. */
302
453
  poll(monitorId: string, options?: MonitorPollOptions): Promise<SearchResponse>;
303
454
  delete(monitorId: string, options?: {
304
455
  signal?: AbortSignal;
305
456
  }): Promise<void>;
457
+ /** Read the monitor agent's document. A monitor without an agent answers 404. */
458
+ agent(monitorId: string, options?: {
459
+ signal?: AbortSignal;
460
+ }): Promise<AgentDocument>;
461
+ /** One page of the agent's changes. Keep `nextCursor` and pass it back as `cursor`. */
462
+ changes(monitorId: string, options?: MonitorChangesOptions): Promise<ChangesPage>;
463
+ /** Every change after `cursor`, page by page, until you are caught up. */
464
+ iterateChanges(monitorId: string, options?: MonitorIterateChangesOptions): AsyncGenerator<ChangeRecord, void, undefined>;
306
465
  };
307
466
 
308
467
  /**
@@ -446,7 +605,8 @@ declare class Telem {
446
605
  readonly maxRetries: number;
447
606
  /** The session read surface: `list` and `results(id)`. */
448
607
  readonly sessions: SessionsResource;
449
- /** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
608
+ /** Monitors: `create`, `list`, `listPage`, `get`, `update` and `delete`, the agent reads
609
+ * `agent`, `changes` and `iterateChanges`, and the deprecated `poll`. */
450
610
  readonly monitors: MonitorsResource;
451
611
  /**
452
612
  * @param options - An API key string, or an options object. Anything left unset
@@ -479,4 +639,4 @@ declare class Telem {
479
639
  providers(): Promise<ProviderInfo[]>;
480
640
  }
481
641
 
482
- export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type Monitor, type MonitorCreateOptions, type MonitorPollOptions, type MonitorUpdateOptions, type MonitorsResource, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, verifyWebhook, windowAnchor, withTrajectory };
642
+ export { type AgentCompileState, type AgentDocument, type AgentState, type ChangeRecord, type ChangeSubject, type ChangesPage, type FetchOptions, type FetchResponse, type FetchResult, type Json, type Monitor, type MonitorChangesOptions, type MonitorCreateOptions, type MonitorIterateChangesOptions, type MonitorListOptions, type MonitorPage, type MonitorPollOptions, type MonitorUpdateOptions, type MonitorsResource, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, verifyWebhook, windowAnchor, withTrajectory };
package/dist/index.d.ts CHANGED
@@ -157,6 +157,8 @@ type ProviderRun = {
157
157
  verticals: Json | null;
158
158
  usage: Json | unknown[] | null;
159
159
  warnings: Json[];
160
+ /** Monitor polls: results folded as the same story, `{leadUrl: [{url, title}]}`. */
161
+ sameStory: Record<string, Json[]> | null;
160
162
  raw: Json | null;
161
163
  };
162
164
  type SearchResponse = {
@@ -207,7 +209,7 @@ type ProviderInfo = {
207
209
  url: string | null;
208
210
  normalized: boolean;
209
211
  tiers: string[];
210
- /** Whether auto-routing can pick it for a ranked topic; `null` from a router that does not say. */
212
+ /** Whether auto-routing can pick it; `null` from a router that does not say. */
211
213
  autoRouted: boolean | null;
212
214
  };
213
215
  /** One session as `sessions.list` reports it. */
@@ -236,34 +238,55 @@ type SessionsResource = {
236
238
  };
237
239
  /** The search options a monitor can run on its schedule (`MonitorCreateOptions.search`). */
238
240
  type MonitorSearchOptions = Pick<SearchOptions, "tier" | "numResults">;
239
- /** Options for `monitors.create`. */
240
- type MonitorCreateOptions = {
241
+ /** The options of `monitors.create` that a monitor takes with the agent or without. */
242
+ type MonitorCreateCommonOptions = {
241
243
  /** `"1h"`, `"6h"`, `"12h"`, `"1d"` or `"7d"`. If you leave it out, the monitor runs once a day. */
242
244
  frequency?: string;
243
245
  /** Also run a web search for the query on the schedule, with these options. `{}`
244
- * uses the defaults. Client search defaults do not apply. */
246
+ * uses the defaults. Client search defaults do not apply. It adds to what a poll returns. */
245
247
  search?: MonitorSearchOptions;
246
- /** Only results from these sites and their subdomains, such as `["sec.gov"]`,
247
- * from every source of the monitor. */
248
+ /** Only results from these sites and their subdomains, such as `["sec.gov"]`, in what
249
+ * a poll returns. */
248
250
  includeDomains?: string[];
249
- /** Request metadata (query lineage keys), sent as given. */
251
+ /** Query lineage keys, sent as given. They choose the session that records the polls of
252
+ * the monitor. */
250
253
  metadata?: Json;
251
254
  /** An `https` URL that receives the new results. */
252
255
  webhookUrl?: string;
253
- /** Delete the monitor after this long, such as `"12h"` or `"7d"` (at most `"30d"`). Without it, the monitor never expires. */
254
- ttl?: string;
255
256
  signal?: AbortSignal;
256
257
  };
258
+ /** Options for `monitors.create`. A monitor has the agent unless `agent` is `false`,
259
+ * and a monitor with the agent must end: then `ttl` is required. */
260
+ type MonitorCreateOptions = MonitorCreateCommonOptions & ({
261
+ /** `false` creates a monitor without an agent, which you read with the deprecated
262
+ * `monitors.poll`. */
263
+ agent: false;
264
+ /** End the monitor after this long, such as `"7d"` or `"30d"` (at most `"30d"`).
265
+ * Without it, a monitor without an agent never ends. */
266
+ ttl?: string;
267
+ } | {
268
+ /** Start the monitor agent with the monitor, the default: it checks every result
269
+ * against the query and keeps the ones that match. Read what it keeps with
270
+ * `monitors.iterateChanges` or `monitors.changes`. */
271
+ agent?: true;
272
+ /** End the monitor after this long, such as `"7d"` or `"30d"` (at most `"30d"`).
273
+ * Required with the agent. */
274
+ ttl: string;
275
+ });
257
276
  /** Options for `monitors.update`. Send at least one field. */
258
277
  type MonitorUpdateOptions = {
259
278
  /** `"paused"` or `"active"`. */
260
279
  status?: string;
261
280
  frequency?: string;
262
- /** A new webhook URL, with a new `webhookSecret` in the response. `null` removes it. */
281
+ /** A new webhook URL, with a new `webhookSecret` in the response. `null` or `""` removes it. */
263
282
  webhookUrl?: string | null;
283
+ /** A new end, this long from now, such as `"30d"` (at most `"30d"`). It renews a monitor
284
+ * before it ends; an ended monitor cannot be renewed. */
285
+ ttl?: string;
264
286
  signal?: AbortSignal;
265
287
  };
266
- /** Options for `monitors.poll`. */
288
+ /** Options for `monitors.poll`.
289
+ * @deprecated `poll` is deprecated: read the agent with `monitors.iterateChanges`. */
267
290
  type MonitorPollOptions = {
268
291
  /** Record this poll in another session (default: the monitor's own). */
269
292
  session?: string;
@@ -276,33 +299,169 @@ type Monitor = {
276
299
  id: string;
277
300
  query: string;
278
301
  frequency: string;
279
- /** `active` or `paused` (you paused it). */
302
+ /** `active`, `paused` (you paused it) or `ended` (past `expiresAt`). */
280
303
  status: string;
281
304
  sessionId: string;
282
305
  createdAt: string;
283
306
  lastPolledAt: string | null;
284
- /** When Telem deletes the monitor; `null`: never. */
307
+ /** When the monitor ends; `null`: never. */
285
308
  expiresAt: string | null;
286
309
  webhookUrl: string | null;
287
310
  /** Signs the webhook deliveries. Only in the response that set `webhookUrl`. */
288
311
  webhookSecret: string | null;
312
+ /** The monitor agent's state. `null`: the monitor has no agent. Absent when the router
313
+ * sends none, as a router from before the agent reads does. */
314
+ agentState?: AgentState | null;
315
+ /** Only in the answer to a create: why the monitor agent did not start, or not yet.
316
+ * `null` when it started, and in every other answer. */
317
+ agentNote: string | null;
318
+ raw: Json;
319
+ };
320
+ /** The latest compile of an agent's plan (`AgentState.compile`). */
321
+ type AgentCompileState = {
322
+ jobId: string;
323
+ /** The compile job's status, such as `done` or `failed`. */
324
+ status: string;
325
+ /** Why the compile failed; `null` when it did not. */
326
+ error: string | null;
327
+ };
328
+ /** The state of a monitor's agent: `monitor.agentState`. */
329
+ type AgentState = {
330
+ /** The agent engine, `"v4"` today. */
331
+ engine: string;
332
+ /** Such as `compiling`, `ready`, `paused` or `failed`. */
333
+ status: string;
334
+ /** `true` when `status` is `failed`. */
335
+ needsAttention: boolean;
336
+ /** The version of the agent's plan; `null` before it has one. */
337
+ planVersion: number | null;
338
+ /** When the agent runs next; `null` when no run is scheduled. */
339
+ nextRunAt: string | null;
340
+ /** The `seq` of the newest change `monitors.changes` can serve now. */
341
+ watermark: number;
342
+ /** The latest compile of the plan; `null` when there has been none. */
343
+ compile: AgentCompileState | null;
344
+ };
345
+ /** The monitor agent's document: `monitors.agent`. A field is there only when the
346
+ * router sent it. The rest of the document (the plan, the counts, the runs) is in `raw`. */
347
+ type AgentDocument = {
348
+ /** Such as `compiling`, `ready`, `paused`, `failed` or `ended`. */
349
+ status?: string;
350
+ query?: string;
351
+ frequency?: string;
352
+ /** The version of the plan; 0 while the agent compiles. */
353
+ specVersion?: number;
354
+ /** The agent's spending limits; `null` when none are set. */
355
+ budget?: Json | null;
356
+ /** What the agent has spent, in US dollars. */
357
+ spendUsd?: number;
358
+ raw: Json;
359
+ };
360
+ /** The item a change is about. `entityId` is `null` for a change to the whole monitor,
361
+ * such as a run or a plan change. */
362
+ type ChangeSubject = {
363
+ entityId: string | null;
364
+ name: string | null;
365
+ };
366
+ /** One change the monitor agent made. */
367
+ type ChangeRecord = {
368
+ /** The change's place in the stream. It grows with every change and is never reused.
369
+ * Pass the last one you handled as `cursor` to read on from there. */
370
+ seq: number;
371
+ /** The change's own type, such as `new_object`. */
372
+ type: string;
373
+ /** What happened: one of the six kinds `entity_added`, `value_changed`,
374
+ * `fact_status_changed`, `alert`, `run` or `plan_changed`. */
375
+ kind: string;
376
+ monitorId: string;
377
+ subject: ChangeSubject;
378
+ /** What it was before, as the router sent it; `null` when there is no before. Its shape
379
+ * depends on `kind`. */
380
+ before: unknown;
381
+ /** What it is now, as the router sent it. */
382
+ after: unknown;
383
+ /** The facts behind the change; empty when the router names none. */
384
+ factIds: string[];
385
+ /** When it happened, ISO 8601. */
386
+ at: string;
289
387
  raw: Json;
290
388
  };
389
+ /** One page of `monitors.changes`. */
390
+ type ChangesPage = {
391
+ changes: ChangeRecord[];
392
+ /** Keep this and pass it as `cursor` to read the changes after this page. On a full
393
+ * page (`limit` changes) it is the `seq` of the last change. On a shorter page, an
394
+ * empty one included, it is `watermark`: the read got that far, past the changes
395
+ * that `kinds` left out too. */
396
+ nextCursor: number;
397
+ /** The `seq` of the newest change that can be read now. You are caught up when
398
+ * `nextCursor` equals it, or when a page is empty. */
399
+ watermark: number;
400
+ };
401
+ /** Options for `monitors.changes`. */
402
+ type MonitorChangesOptions = {
403
+ /** Read the changes after this `seq`. If you leave it out, the page starts at the
404
+ * first change. Do not send it with `since`. */
405
+ cursor?: number;
406
+ /** Read from this time on: a `Date`, or an ISO 8601 time with an offset such as
407
+ * `"2026-11-03T12:00:00Z"`. Do not send it with `cursor`. */
408
+ since?: string | Date;
409
+ /** Only these kinds of change: `entity_added`, `value_changed`, `fact_status_changed`,
410
+ * `alert`, `run` or `plan_changed`, for example `["alert", "run"]`. Another name is a
411
+ * 400 (`kinds_invalid`). An empty list is no filter. */
412
+ kinds?: string[];
413
+ /** How many changes to return (server default 200, maximum 500). */
414
+ limit?: number;
415
+ signal?: AbortSignal;
416
+ };
417
+ /** Options for `monitors.iterateChanges`. */
418
+ type MonitorIterateChangesOptions = {
419
+ /** Read the changes after this `seq`. If you leave it out, the walk starts at the
420
+ * first change. */
421
+ cursor?: number;
422
+ signal?: AbortSignal;
423
+ };
424
+ /** Options for `monitors.list` and `monitors.listPage`. */
425
+ type MonitorListOptions = {
426
+ /** How many to return (server default 50, maximum 200). */
427
+ limit?: number;
428
+ /** The `nextCursor` of the page before. */
429
+ cursor?: string;
430
+ signal?: AbortSignal;
431
+ };
432
+ /** One page of `monitors.listPage`. */
433
+ type MonitorPage = {
434
+ monitors: Monitor[];
435
+ /** Pass as `cursor` to get the next page; `null` on the last page. */
436
+ nextCursor: string | null;
437
+ };
291
438
  /** The `telem.monitors` namespace. */
292
439
  type MonitorsResource = {
293
- create(query: string, options?: MonitorCreateOptions): Promise<Monitor>;
294
- list(options?: {
295
- limit?: number;
296
- signal?: AbortSignal;
297
- }): Promise<Monitor[]>;
440
+ /** Create a monitor. It has the agent unless `agent` is `false`, and with the agent
441
+ * `ttl` is required: a create without it throws a `TelemError` (`invalid_request`)
442
+ * before anything is sent. */
443
+ create(query: string, options: MonitorCreateOptions): Promise<Monitor>;
444
+ list(options?: MonitorListOptions): Promise<Monitor[]>;
445
+ listPage(options?: MonitorListOptions): Promise<MonitorPage>;
298
446
  get(monitorId: string, options?: {
299
447
  signal?: AbortSignal;
300
448
  }): Promise<Monitor>;
301
449
  update(monitorId: string, options: MonitorUpdateOptions): Promise<Monitor>;
450
+ /** Get the monitor's own results that are new since the last poll.
451
+ * @deprecated Read what the monitor agent finds with `iterateChanges` or `changes`.
452
+ * A poll still works, and warns once per process with `console.warn`. */
302
453
  poll(monitorId: string, options?: MonitorPollOptions): Promise<SearchResponse>;
303
454
  delete(monitorId: string, options?: {
304
455
  signal?: AbortSignal;
305
456
  }): Promise<void>;
457
+ /** Read the monitor agent's document. A monitor without an agent answers 404. */
458
+ agent(monitorId: string, options?: {
459
+ signal?: AbortSignal;
460
+ }): Promise<AgentDocument>;
461
+ /** One page of the agent's changes. Keep `nextCursor` and pass it back as `cursor`. */
462
+ changes(monitorId: string, options?: MonitorChangesOptions): Promise<ChangesPage>;
463
+ /** Every change after `cursor`, page by page, until you are caught up. */
464
+ iterateChanges(monitorId: string, options?: MonitorIterateChangesOptions): AsyncGenerator<ChangeRecord, void, undefined>;
306
465
  };
307
466
 
308
467
  /**
@@ -446,7 +605,8 @@ declare class Telem {
446
605
  readonly maxRetries: number;
447
606
  /** The session read surface: `list` and `results(id)`. */
448
607
  readonly sessions: SessionsResource;
449
- /** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
608
+ /** Monitors: `create`, `list`, `listPage`, `get`, `update` and `delete`, the agent reads
609
+ * `agent`, `changes` and `iterateChanges`, and the deprecated `poll`. */
450
610
  readonly monitors: MonitorsResource;
451
611
  /**
452
612
  * @param options - An API key string, or an options object. Anything left unset
@@ -479,4 +639,4 @@ declare class Telem {
479
639
  providers(): Promise<ProviderInfo[]>;
480
640
  }
481
641
 
482
- export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type Monitor, type MonitorCreateOptions, type MonitorPollOptions, type MonitorUpdateOptions, type MonitorsResource, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, verifyWebhook, windowAnchor, withTrajectory };
642
+ export { type AgentCompileState, type AgentDocument, type AgentState, type ChangeRecord, type ChangeSubject, type ChangesPage, type FetchOptions, type FetchResponse, type FetchResult, type Json, type Monitor, type MonitorChangesOptions, type MonitorCreateOptions, type MonitorIterateChangesOptions, type MonitorListOptions, type MonitorPage, type MonitorPollOptions, type MonitorUpdateOptions, type MonitorsResource, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, verifyWebhook, windowAnchor, withTrajectory };
package/dist/index.js CHANGED
@@ -19,10 +19,10 @@ import {
19
19
  windowAnchor,
20
20
  wireMetadata,
21
21
  withTrajectory
22
- } from "./chunk-DKTEG7EY.js";
22
+ } from "./chunk-SQ3ZZA72.js";
23
23
 
24
24
  // src/version.ts
25
- var VERSION = "0.3.3";
25
+ var VERSION = "0.5.0";
26
26
 
27
27
  // src/config.ts
28
28
  var DEFAULT_BASE_URL = "https://router.telem.ai";
@@ -82,6 +82,7 @@ function resolveConfig(options) {
82
82
  if (typeof send !== "function") {
83
83
  throw invalidOption("no fetch on this runtime: pass one with the `fetch` option");
84
84
  }
85
+ if (typeof opts.apiKey === "string" && !opts.apiKey.trim()) throw invalidOption("apiKey is empty");
85
86
  let apiKey = opts.apiKey || env.TELEM_API_KEY || void 0;
86
87
  let baseUrl = opts.baseUrl || env.TELEM_BASE_URL || void 0;
87
88
  if (apiKey === void 0 || baseUrl === void 0) {
@@ -331,7 +332,8 @@ function providerRun(run) {
331
332
  related: envelope.related ?? null,
332
333
  verticals: envelope.verticals ?? null,
333
334
  usage: envelope.usage ?? null,
334
- warnings: asArray(envelope.warnings)
335
+ warnings: asArray(envelope.warnings),
336
+ sameStory: isObject(envelope.same_story) ? envelope.same_story : null
335
337
  };
336
338
  }
337
339
  function buildSearchResponse(interaction) {
@@ -355,9 +357,33 @@ async function search(config, query, options) {
355
357
  }
356
358
 
357
359
  // src/methods/monitors.ts
360
+ var NEEDS_TTL = 'A monitor with an agent must end: pass ttl, such as ttl: "30d" (at most 30 days), or agent: false for a monitor without an agent.';
361
+ var POLL_DEPRECATED = "@telemai/sdk: monitors.poll() is deprecated: read what the monitor agent finds with monitors.iterateChanges() or monitors.changes().";
362
+ var pollWarned = false;
363
+ function warnPollDeprecated() {
364
+ if (pollWarned) return;
365
+ pollWarned = true;
366
+ console.warn(POLL_DEPRECATED);
367
+ }
368
+ function agentStateFrom(data) {
369
+ return {
370
+ engine: String(data.engine ?? ""),
371
+ status: String(data.status ?? ""),
372
+ needsAttention: data.needs_attention === true,
373
+ planVersion: typeof data.plan_version === "number" ? data.plan_version : null,
374
+ nextRunAt: typeof data.next_run_at === "string" ? data.next_run_at : null,
375
+ watermark: Number(data.watermark ?? 0),
376
+ // No compile job yet is null, not an object of empty strings.
377
+ compile: isObject(data.compile) ? {
378
+ jobId: String(data.compile.job_id ?? ""),
379
+ status: String(data.compile.status ?? ""),
380
+ error: typeof data.compile.error === "string" ? data.compile.error : null
381
+ } : null
382
+ };
383
+ }
358
384
  function monitorFrom(body) {
359
385
  const data = asObject(body);
360
- return {
386
+ const monitor = {
361
387
  id: String(data.id ?? ""),
362
388
  query: data.query ?? "",
363
389
  frequency: data.frequency ?? "",
@@ -368,51 +394,141 @@ function monitorFrom(body) {
368
394
  expiresAt: data.expires_at ?? null,
369
395
  webhookUrl: data.webhook_url ?? null,
370
396
  webhookSecret: data.webhook_secret ?? null,
397
+ agentNote: typeof data.agent_note === "string" ? data.agent_note : null,
371
398
  raw: data
372
399
  };
400
+ if (data.agent_state !== void 0) {
401
+ monitor.agentState = isObject(data.agent_state) ? agentStateFrom(data.agent_state) : null;
402
+ }
403
+ return monitor;
404
+ }
405
+ function agentDocumentFrom(body) {
406
+ const data = asObject(body);
407
+ const document = { raw: data };
408
+ if (typeof data.status === "string") document.status = data.status;
409
+ if (typeof data.query === "string") document.query = data.query;
410
+ if (typeof data.frequency === "string") document.frequency = data.frequency;
411
+ if (typeof data.spec_version === "number") document.specVersion = data.spec_version;
412
+ if (data.budget !== void 0) document.budget = isObject(data.budget) ? data.budget : null;
413
+ if (typeof data.spend_usd === "number") document.spendUsd = data.spend_usd;
414
+ return document;
415
+ }
416
+ function changeRecordFrom(entry) {
417
+ const subject = asObject(entry.subject);
418
+ return {
419
+ seq: Number(entry.seq ?? 0),
420
+ type: String(entry.type ?? ""),
421
+ kind: String(entry.kind ?? ""),
422
+ monitorId: String(entry.monitor_id ?? ""),
423
+ subject: {
424
+ entityId: typeof subject.entity_id === "string" ? subject.entity_id : null,
425
+ name: typeof subject.name === "string" ? subject.name : null
426
+ },
427
+ // The values depend on the kind (an object, a status string, nothing): as sent.
428
+ before: entry.before ?? null,
429
+ after: entry.after ?? null,
430
+ factIds: asArray(entry.fact_ids).map(String),
431
+ at: String(entry.at ?? ""),
432
+ raw: entry
433
+ };
434
+ }
435
+ function changesPageFrom(body) {
436
+ const data = asObject(body);
437
+ return {
438
+ changes: asArray(data.changes).filter(isObject).map(changeRecordFrom),
439
+ nextCursor: Number(data.next_cursor ?? 0),
440
+ watermark: Number(data.watermark ?? 0)
441
+ };
442
+ }
443
+ function listQuery(options) {
444
+ const params = new URLSearchParams();
445
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
446
+ if (options.cursor !== void 0) params.set("cursor", options.cursor);
447
+ const query = params.toString();
448
+ return query ? `?${query}` : "";
449
+ }
450
+ function sinceParam(since) {
451
+ if (typeof since === "string") return since;
452
+ if (Number.isNaN(since.getTime())) throw invalidRequest("since is not a valid date");
453
+ return since.toISOString();
454
+ }
455
+ function changesQuery(options) {
456
+ const params = new URLSearchParams();
457
+ if (options.cursor !== void 0) params.set("cursor", String(options.cursor));
458
+ if (options.since !== void 0) params.set("since", sinceParam(options.since));
459
+ if (options.kinds !== void 0) params.set("kinds", options.kinds.join(","));
460
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
461
+ const query = params.toString();
462
+ return query ? `?${query}` : "";
373
463
  }
374
464
  function createMonitors(config) {
375
465
  const path = (monitorId, suffix = "") => {
376
466
  if (!monitorId || /^\.+$/.test(monitorId)) throw invalidRequest("a monitor id is required");
377
467
  return `/v1/monitors/${encodeURIComponent(monitorId)}${suffix}`;
378
468
  };
469
+ const listPage = async (options = {}) => {
470
+ const route = `/v1/monitors${listQuery(options)}`;
471
+ const data = asObject(await request(config, "GET", route, void 0, options.signal));
472
+ const nextCursor = typeof data.next_cursor === "string" ? data.next_cursor : null;
473
+ return { monitors: asArray(data.monitors).map(monitorFrom), nextCursor };
474
+ };
475
+ const changes = async (monitorId, options = {}) => {
476
+ const route = `${path(monitorId, "/agent/changes")}${changesQuery(options)}`;
477
+ return changesPageFrom(await request(config, "GET", route, void 0, options.signal));
478
+ };
379
479
  return {
380
- /** Create a monitor. Telem runs `query` on the schedule. */
381
- async create(query, options = {}) {
480
+ /** Create a monitor. Telem runs `query` on the schedule, and its agent keeps what matches. */
481
+ async create(query, options) {
482
+ const given = options ?? {};
483
+ const agent = given.agent == null ? true : Boolean(given.agent);
484
+ if (agent && !given.ttl) throw invalidRequest(NEEDS_TTL);
382
485
  const monitorOptions = {};
383
- if (options.frequency !== void 0) monitorOptions.frequency = options.frequency;
384
- if (options.includeDomains !== void 0) {
385
- monitorOptions.include_domains = options.includeDomains;
486
+ if (given.frequency !== void 0) monitorOptions.frequency = given.frequency;
487
+ if (given.includeDomains !== void 0) {
488
+ monitorOptions.include_domains = given.includeDomains;
386
489
  }
387
- if (options.search !== void 0) monitorOptions.search = searchBlock({}, options.search);
490
+ if (given.search !== void 0) monitorOptions.search = searchBlock({}, given.search);
388
491
  const body = { query, options: monitorOptions };
389
- if (options.metadata !== void 0) body.metadata = options.metadata;
390
- if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
391
- if (options.ttl !== void 0) body.ttl = options.ttl;
392
- const data = await request(config, "POST", "/v1/monitors", body, options.signal, "rate_limit");
492
+ if (given.metadata !== void 0) body.metadata = given.metadata;
493
+ if (given.webhookUrl !== void 0) body.webhook_url = given.webhookUrl;
494
+ if (given.ttl !== void 0) body.ttl = given.ttl;
495
+ body.agent = agent;
496
+ const data = await request(config, "POST", "/v1/monitors", body, given.signal, "rate_limit").catch(
497
+ (error) => {
498
+ if (error instanceof TimeoutError) {
499
+ throw new TimeoutError(
500
+ `${error.message}. The monitor may exist: list monitors before you create it again.`,
501
+ error.cause
502
+ );
503
+ }
504
+ throw error;
505
+ }
506
+ );
393
507
  return monitorFrom(data);
394
508
  },
395
- /** List your monitors, newest first (server default 50, maximum 200). */
509
+ /** List your monitors, newest first. `listPage` also gives the next cursor. */
396
510
  async list(options = {}) {
397
- const query = options.limit === void 0 ? "" : `?limit=${options.limit}`;
398
- const body = await request(config, "GET", `/v1/monitors${query}`, void 0, options.signal);
399
- return asArray(asObject(body).monitors).map(monitorFrom);
511
+ return (await listPage(options)).monitors;
400
512
  },
513
+ listPage,
401
514
  /** Get one monitor. */
402
515
  async get(monitorId, options = {}) {
403
516
  return monitorFrom(await request(config, "GET", path(monitorId), void 0, options.signal));
404
517
  },
405
- /** Pause, resume or change a monitor. Only the fields given are sent. */
518
+ /** Pause, resume, renew or change a monitor. Only the fields given are sent. */
406
519
  async update(monitorId, options) {
407
520
  const body = {};
408
521
  if (options.status !== void 0) body.status = options.status;
409
522
  if (options.frequency !== void 0) body.frequency = options.frequency;
410
- if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
523
+ if (options.ttl !== void 0) body.ttl = options.ttl;
524
+ if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl || null;
411
525
  const route = path(monitorId);
412
526
  return monitorFrom(await request(config, "PATCH", route, body, options.signal));
413
527
  },
414
- /** Get the results that are new since the last poll. */
528
+ /** Get the monitor's own results that are new since the last poll. Deprecated: it
529
+ * warns once per process. */
415
530
  async poll(monitorId, options = {}) {
531
+ warnPollDeprecated();
416
532
  const body = {};
417
533
  if (options.session !== void 0) body.session_id = options.session;
418
534
  if (options.metadata !== void 0) body.metadata = options.metadata;
@@ -423,6 +539,23 @@ function createMonitors(config) {
423
539
  /** Delete a monitor. A repeat deletes what a failed delete left. */
424
540
  async delete(monitorId, options = {}) {
425
541
  await request(config, "DELETE", path(monitorId), void 0, options.signal);
542
+ },
543
+ /** Read the monitor agent's document. A monitor without an agent answers 404. */
544
+ async agent(monitorId, options = {}) {
545
+ const route = path(monitorId, "/agent");
546
+ return agentDocumentFrom(await request(config, "GET", route, void 0, options.signal));
547
+ },
548
+ /** One page of the agent's changes. Keep `nextCursor` and pass it back as `cursor`. */
549
+ changes,
550
+ /** Every change after `cursor`, page by page, until you are caught up. */
551
+ async *iterateChanges(monitorId, options = {}) {
552
+ let cursor = options.cursor ?? 0;
553
+ for (; ; ) {
554
+ const page = await changes(monitorId, { cursor, signal: options.signal });
555
+ for (const change of page.changes) yield change;
556
+ if (!page.changes.length || page.nextCursor <= cursor || page.nextCursor >= page.watermark) return;
557
+ cursor = page.nextCursor;
558
+ }
426
559
  }
427
560
  };
428
561
  }
@@ -525,7 +658,8 @@ var Telem = class {
525
658
  maxRetries;
526
659
  /** The session read surface: `list` and `results(id)`. */
527
660
  sessions;
528
- /** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
661
+ /** Monitors: `create`, `list`, `listPage`, `get`, `update` and `delete`, the agent reads
662
+ * `agent`, `changes` and `iterateChanges`, and the deprecated `poll`. */
529
663
  monitors;
530
664
  #config;
531
665
  /**
@@ -9,7 +9,7 @@ import {
9
9
  toolMarker,
10
10
  windowAnchor,
11
11
  withTrajectory
12
- } from "../chunk-DKTEG7EY.js";
12
+ } from "../chunk-SQ3ZZA72.js";
13
13
 
14
14
  // src/vercel/index.ts
15
15
  import { jsonSchema } from "ai";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telemai/sdk",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "Telem web search and fetch for JavaScript and TypeScript.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",