@synapsor/client 0.1.4 → 0.1.5

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
@@ -101,6 +101,30 @@ await run.complete({ decision: "waiver_proposed" }, { status: "waiting_approval"
101
101
  const graph = await run.explain();
102
102
  ```
103
103
 
104
+ Find the persisted run/evidence/proposal later by business object, workflow,
105
+ tenant, query fingerprint, or time window:
106
+
107
+ ```js
108
+ const activity = await db.agentActivity.search({
109
+ tenantId: "acme",
110
+ businessObjectId: "T-1042",
111
+ workflow: "support.ticket_refund_flow",
112
+ timeRange: { from: 123, to: 456 },
113
+ limit: 50,
114
+ });
115
+ ```
116
+
117
+ This helper sends the native lookup form:
118
+
119
+ ```sql
120
+ SELECT *
121
+ FROM AGENT ACTIVITY
122
+ WHERE tenant_id = 'acme'
123
+ AND business_object_id = 'T-1042'
124
+ ORDER BY created_at DESC
125
+ LIMIT 50;
126
+ ```
127
+
104
128
  Replay a stored capability run by using the numeric `agent_run_id` returned by
105
129
  Synapsor. Deterministic replay returns the captured persisted run; comparison
106
130
  modes can inspect the original snapshot, current state, a commit version, a
@@ -163,6 +187,7 @@ and reports `applied`, `conflict`, or `failed` back to Synapsor idempotently.
163
187
  - `setSession({...})`
164
188
  - `invokeAgentCapability(name, args, options)`
165
189
  - `agentRuns.start(...)`, `run.invokeCapability(...)`, `run.checkpoint(...)`, `run.complete(...)`, `run.explain(...)`
190
+ - `agentActivity.search({ tenantId, businessObjectId, workflow, timeRange, limit })`
166
191
  - `replayAgentRun(id, { mode, version, timestamp, branchName })`
167
192
  - `externalActions.claim(...)` and `externalActions.confirm(...)`
168
193
  - `createAgentEval(...)` / `evals.create(...)` for run-history or `sourceTable` dataset evals
package/bin/synapsor.mjs CHANGED
@@ -172,6 +172,10 @@ function csvFlag(args, name) {
172
172
  return value ? value.split(",").map((item) => item.trim()).filter(Boolean) : [];
173
173
  }
174
174
 
175
+ function boolFlag(args, name) {
176
+ return args.includes(name);
177
+ }
178
+
175
179
  function resolveSecretRef(value) {
176
180
  const text = String(value || "").trim();
177
181
  if (text.startsWith("env:")) {
@@ -282,24 +286,35 @@ async function main(argv = process.argv.slice(2)) {
282
286
  const envHint = kind === "mysql" ? "APP_MYSQL_URL" : "APP_POSTGRES_URL";
283
287
  const name = flagValue(rest, "--name", defaultName);
284
288
  const url = resolveSecretRef(flagValue(rest, "--url", ""));
289
+ const cdcUrl = resolveSecretRef(flagValue(rest, "--cdc-url", ""));
285
290
  const ssl = flagValue(rest, "--ssl", "require");
286
- const mode = flagValue(rest, "--mode", "read-only");
291
+ const mode = flagValue(rest, "--mode", "live_read");
287
292
  const projectId = scopedProject(globals);
288
293
  const databaseId = scopedDatabase(globals);
289
294
  if (!projectId || !databaseId) throw new Error("PROJECT_AND_DATABASE_REQUIRED: use --project <project_id> --db <database_id> or SYNAPSOR_PROJECT_ID/SYNAPSOR_DATABASE_ID");
290
295
  if (!url) throw new Error(`${kind.toUpperCase()}_URL_REQUIRED: usage \`synapsor sources create ${kind} --name ${defaultName} --url env:${envHint} --project <project> --db <database>\``);
291
- return print(await request(globals, "POST", "/v1/control/external-sources", {
296
+ const body = {
292
297
  project_id: projectId,
293
298
  database_id: databaseId,
294
299
  kind,
295
300
  name,
296
301
  connection: { url, ssl_mode: ssl },
297
302
  mode,
298
- }), globals);
303
+ };
304
+ if (cdcUrl) body.cdc_connection = { url: cdcUrl, ssl_mode: ssl };
305
+ if (mode === "cdc_mirror" || boolFlag(rest, "--cdc-ack")) body.cdc_acknowledged = true;
306
+ return print(await request(globals, "POST", "/v1/control/external-sources", body), globals);
299
307
  }
300
- if (["test", "inspect", "generate", "doctor", "disable"].includes(sub)) {
308
+ if (["test", "inspect", "generate", "doctor", "disable", "cdc-status", "cdc-snapshot", "cdc-poll"].includes(sub)) {
301
309
  const sourceId = await resolveSourceId(globals, third);
302
310
  if (sub === "test") return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/test`, {}), globals);
311
+ if (sub === "cdc-status") return print(await request(globals, "GET", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/cdc/status`), globals);
312
+ if (sub === "cdc-snapshot" || sub === "cdc-poll") {
313
+ const tables = csvFlag(rest, "--tables");
314
+ const body = { cdc_acknowledged: true };
315
+ if (tables.length) body.tables = tables;
316
+ return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/cdc/${sub === "cdc-snapshot" ? "snapshot" : "poll"}`, body), globals);
317
+ }
303
318
  if (sub === "inspect") {
304
319
  const schema = flagValue(rest, "--schema", "public");
305
320
  const database = flagValue(rest, "--database", "");
@@ -340,10 +355,10 @@ async function main(argv = process.argv.slice(2)) {
340
355
  const database = flagValue(rest, "--database", "");
341
356
  const tables = csvFlag(rest, "--tables");
342
357
  const tenantColumn = flagValue(rest, "--tenant-column", "");
343
- const mode = flagValue(rest, "--mode", "live-read");
358
+ const mode = flagValue(rest, "--mode", "live_read");
344
359
  if (!tables.length) throw new Error("TABLES_REQUIRED: pass --tables tickets,customers,policy_chunks");
345
360
  if (!tenantColumn && !rest.includes("--single-tenant")) throw new Error("TENANT_COLUMN_REQUIRED: pass --tenant-column tenant_id or --single-tenant");
346
- return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/import`, {
361
+ const body = {
347
362
  tables: tables.map((table) => ({
348
363
  ...(database ? { database } : {}),
349
364
  schema,
@@ -354,7 +369,9 @@ async function main(argv = process.argv.slice(2)) {
354
369
  single_tenant: rest.includes("--single-tenant"),
355
370
  mode,
356
371
  })),
357
- }), globals);
372
+ };
373
+ if (mode === "cdc_mirror" || boolFlag(rest, "--cdc-ack")) body.cdc_acknowledged = true;
374
+ return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/import`, body), globals);
358
375
  }
359
376
  }
360
377
 
@@ -408,8 +425,8 @@ Usage:
408
425
  synapsor projects create <name>
409
426
  synapsor db list --project <project>
410
427
  synapsor db create <name> --project <project>
411
- synapsor sources create postgres --name app_postgres --url env:APP_POSTGRES_URL --ssl require --mode read-only --project <project> --db <database>
412
- synapsor sources create mysql --name app_mysql --url env:APP_MYSQL_URL --ssl require --mode read-only --project <project> --db <database>
428
+ synapsor sources create postgres --name app_postgres --url env:APP_POSTGRES_URL --ssl require --mode live_read --project <project> --db <database>
429
+ synapsor sources create mysql --name app_mysql --url env:APP_MYSQL_URL --ssl require --mode live_read --project <project> --db <database>
413
430
  synapsor sources test app_postgres --project <project>
414
431
  synapsor sources inspect app_postgres --schema public --project <project>
415
432
  synapsor sources inspect app_mysql --database shopdb --project <project>
@@ -430,6 +447,13 @@ Usage:
430
447
  synapsor proposal reject <proposal-handle> --db <database> --yes
431
448
  synapsor replay <run-id> --db <database>
432
449
 
450
+ Preview/dev CDC commands:
451
+ These are not the current public customer connector path. Use live_read unless you are running a reviewed dev/test CDC smoke.
452
+ synapsor sources import app_postgres --schema public --tables tickets --tenant-column tenant_id --mode cdc_mirror --cdc-ack --project <project>
453
+ synapsor sources cdc-snapshot app_postgres --tables external_support.tickets --project <project>
454
+ synapsor sources cdc-poll app_postgres --tables external_support.tickets --project <project>
455
+ synapsor sources cdc-status app_postgres --project <project>
456
+
433
457
  Environment:
434
458
  SYNAPSOR_API_KEY, SYNAPSOR_BASE_URL
435
459
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synapsor/client",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Node.js SDK for Synapsor, the agent-native database for auditable AI applications",
5
5
  "type": "module",
6
6
  "main": "./synapsor.mjs",
package/synapsor.mjs CHANGED
@@ -80,6 +80,7 @@ export class Synapsor {
80
80
  this.apiKey = apiKey;
81
81
  this.process = process;
82
82
  this.timeoutMs = timeoutMs;
83
+ this.agentActivity = new AgentActivityNamespace(this);
83
84
  this.agentRuns = new AgentRunsNamespace(this);
84
85
  this.externalActions = new ExternalActionsNamespace(this);
85
86
  this.evals = new AgentEvalsNamespace(this);
@@ -268,6 +269,13 @@ export class Synapsor {
268
269
  return results.at(-1).result?.rows ?? [];
269
270
  }
270
271
 
272
+ async searchAgentActivity(filters = {}, options = {}) {
273
+ return this.query(buildAgentActivitySearchSql(filters, options), {
274
+ session: options.session,
275
+ asOf: options.asOf ?? options.as_of,
276
+ });
277
+ }
278
+
271
279
  async invokeAgentCapability(capability, args = {}, {
272
280
  session = undefined,
273
281
  traceId = undefined,
@@ -1131,6 +1139,16 @@ export class Synapsor {
1131
1139
  }
1132
1140
  }
1133
1141
 
1142
+ class AgentActivityNamespace {
1143
+ constructor(client) {
1144
+ this.client = client;
1145
+ }
1146
+
1147
+ async search(filters = {}, options = {}) {
1148
+ return this.client.searchAgentActivity(filters, options);
1149
+ }
1150
+ }
1151
+
1134
1152
  class AgentRunsNamespace {
1135
1153
  constructor(client) {
1136
1154
  this.client = client;
@@ -1396,6 +1414,60 @@ function typedSql(value) {
1396
1414
  return `${identifier(ref.type)}(${sqlLiteral(String(ref.id ?? ""))})`;
1397
1415
  }
1398
1416
 
1417
+ function buildAgentActivitySearchSql(filters = {}, options = {}) {
1418
+ const where = [];
1419
+ const addFilter = (column, ...keys) => {
1420
+ for (const key of keys) {
1421
+ if (filters[key] !== undefined && filters[key] !== null && filters[key] !== "") {
1422
+ where.push(`${column} = ${sqlScalar(filters[key])}`);
1423
+ return;
1424
+ }
1425
+ }
1426
+ };
1427
+ addFilter("activity_kind", "activityKind", "activity_kind");
1428
+ addFilter("tenant_id", "tenantId", "tenant_id");
1429
+ addFilter("principal", "principal");
1430
+ addFilter("workflow", "workflow");
1431
+ addFilter("capability", "capability");
1432
+ addFilter("run_id", "runId", "run_id");
1433
+ addFilter("evidence_bundle_id", "evidenceBundleId", "evidence_bundle_id");
1434
+ addFilter("proposal_id", "proposalId", "proposal_id");
1435
+ addFilter("replay_id", "replayId", "replay_id");
1436
+ addFilter("business_object_type", "businessObjectType", "business_object_type");
1437
+ addFilter("business_object_id", "businessObjectId", "business_object_id");
1438
+ addFilter("query_fingerprint", "queryFingerprint", "query_fingerprint");
1439
+
1440
+ const timeRange = filters.timeRange ?? filters.time_range ?? {};
1441
+ const from = timeRange.from ?? filters.createdFrom ?? filters.created_from;
1442
+ const to = timeRange.to ?? filters.createdTo ?? filters.created_to;
1443
+ if (from !== undefined && from !== null && from !== "") {
1444
+ where.push(`created_at >= ${sqlScalar(from)}`);
1445
+ }
1446
+ if (to !== undefined && to !== null && to !== "") {
1447
+ where.push(`created_at <= ${sqlScalar(to)}`);
1448
+ }
1449
+
1450
+ const requestedLimit = options.limit ?? filters.limit ?? 50;
1451
+ const limit = Number.isInteger(Number(requestedLimit)) && Number(requestedLimit) > 0
1452
+ ? Math.min(Number(requestedLimit), 500)
1453
+ : 50;
1454
+ const whereSql = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
1455
+ return `SELECT * FROM AGENT ACTIVITY${whereSql} ORDER BY created_at DESC LIMIT ${limit};`;
1456
+ }
1457
+
1458
+ function sqlScalar(value) {
1459
+ if (typeof value === "number" && Number.isFinite(value)) {
1460
+ return String(Math.trunc(value));
1461
+ }
1462
+ if (typeof value === "bigint") {
1463
+ return String(value);
1464
+ }
1465
+ if (typeof value === "boolean") {
1466
+ return value ? "TRUE" : "FALSE";
1467
+ }
1468
+ return sqlLiteral(String(value));
1469
+ }
1470
+
1399
1471
  function sqlLiteral(value) {
1400
1472
  return `'${String(value).replaceAll("'", "''")}'`;
1401
1473
  }