@dench.com/cli 2.3.1 → 2.4.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/crm.ts CHANGED
@@ -88,6 +88,7 @@ type CrmCliContext = {
88
88
  * organization context.
89
89
  */
90
90
  sessionToken?: string;
91
+ runId?: string;
91
92
  enrichmentGateway?: Pick<EnrichmentGatewayOptions, "apiKey" | "baseUrl">;
92
93
  };
93
94
 
@@ -296,6 +297,7 @@ async function applyBatchOpsInChunks(
296
297
  const chunkKey = chunkIdempotencyKey(idempotencyKey, chunkIndex);
297
298
  await callMutation(ctx, api.batch.apply, {
298
299
  ops: chunk,
300
+ ...runContextArgs(ctx),
299
301
  ...(chunkKey ? { idempotencyKey: chunkKey } : {}),
300
302
  ...(allowUnknown ? { allowUnknown: true } : {}),
301
303
  });
@@ -303,6 +305,10 @@ async function applyBatchOpsInChunks(
303
305
  return chunks.length;
304
306
  }
305
307
 
308
+ function runContextArgs(ctx: CrmCliContext): Record<string, string> {
309
+ return ctx.runId ? { runId: ctx.runId } : {};
310
+ }
311
+
306
312
  /**
307
313
  * Fetch the canonical field names for an object. Best-effort: returns
308
314
  * `null` (rather than throwing) on any failure so the caller falls back
@@ -574,6 +580,11 @@ const api = {
574
580
  "functions/crm/entries:bulkDelete",
575
581
  ),
576
582
  },
583
+ history: {
584
+ listForEntry: makeFunctionReference<"query">(
585
+ "functions/crm/history:listForEntry",
586
+ ),
587
+ },
577
588
  cells: {
578
589
  get: makeFunctionReference<"query">("functions/crm/cells:get"),
579
590
  set: makeFunctionReference<"mutation">("functions/crm/cells:set"),
@@ -641,6 +652,7 @@ export async function runCrmCommand(opts: {
641
652
  args,
642
653
  jsonOutput,
643
654
  sessionToken: opts.sessionToken,
655
+ runId: process.env.DENCH_RUN_ID?.trim() || undefined,
644
656
  enrichmentGateway: opts.enrichmentGateway,
645
657
  };
646
658
  const subcommand = args.shift();
@@ -657,6 +669,8 @@ export async function runCrmCommand(opts: {
657
669
  return await runFieldsCommand(ctx);
658
670
  case "entries":
659
671
  return await runEntriesCommand(ctx);
672
+ case "history":
673
+ return await runHistoryCommand(ctx);
660
674
  case "cells":
661
675
  return await runCellsCommand(ctx);
662
676
  case "query":
@@ -1335,6 +1349,7 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
1335
1349
  await callMutation(ctx, api.entries.create, {
1336
1350
  objectName,
1337
1351
  data,
1352
+ ...runContextArgs(ctx),
1338
1353
  ...(allowUnknown ? { allowUnknown: true } : {}),
1339
1354
  }),
1340
1355
  );
@@ -1392,6 +1407,7 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
1392
1407
  objectName,
1393
1408
  entryId: entryId as any,
1394
1409
  data,
1410
+ ...runContextArgs(ctx),
1395
1411
  ...(allowUnknown ? { allowUnknown: true } : {}),
1396
1412
  }),
1397
1413
  );
@@ -1438,6 +1454,7 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
1438
1454
  ctx,
1439
1455
  await callMutation(ctx, api.entries.remove, {
1440
1456
  entryId: entryId as any,
1457
+ ...runContextArgs(ctx),
1441
1458
  }),
1442
1459
  );
1443
1460
  return;
@@ -1452,6 +1469,7 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
1452
1469
  ctx,
1453
1470
  await callMutation(ctx, api.entries.bulkDelete, {
1454
1471
  entryIds: entryIds as any,
1472
+ ...runContextArgs(ctx),
1455
1473
  }),
1456
1474
  );
1457
1475
  return;
@@ -1461,6 +1479,27 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
1461
1479
  }
1462
1480
  }
1463
1481
 
1482
+ async function runHistoryCommand(ctx: CrmCliContext): Promise<void> {
1483
+ const objectName = shift(ctx.args, "object name");
1484
+ const entryId = shift(ctx.args, "entry id");
1485
+ const includeSystem = hasFlag(ctx.args, "--include-system");
1486
+ const limit = parseInt(getFlag(ctx.args, "--limit") ?? "50", 10);
1487
+ const cursor = getFlag(ctx.args, "--cursor");
1488
+ assertNoUnknownFlags(ctx.args, "crm history");
1489
+ out(
1490
+ ctx,
1491
+ await callQuery(ctx, api.history.listForEntry, {
1492
+ objectName,
1493
+ entryId,
1494
+ includeSystem,
1495
+ paginationOpts: {
1496
+ numItems: Number.isFinite(limit) ? Math.min(Math.max(limit, 1), 100) : 50,
1497
+ cursor: cursor ?? null,
1498
+ },
1499
+ }),
1500
+ );
1501
+ }
1502
+
1464
1503
  async function runCellsCommand(ctx: CrmCliContext): Promise<void> {
1465
1504
  const verb = ctx.args.shift();
1466
1505
  switch (verb) {
@@ -1492,6 +1531,7 @@ async function runCellsCommand(ctx: CrmCliContext): Promise<void> {
1492
1531
  entryId: entryId as any,
1493
1532
  fieldName,
1494
1533
  value,
1534
+ ...runContextArgs(ctx),
1495
1535
  }),
1496
1536
  );
1497
1537
  return;
@@ -1538,6 +1578,7 @@ async function runCellsCommand(ctx: CrmCliContext): Promise<void> {
1538
1578
  entryId: entryId as any,
1539
1579
  fieldName,
1540
1580
  value,
1581
+ ...runContextArgs(ctx),
1541
1582
  }),
1542
1583
  );
1543
1584
  return;
@@ -1664,6 +1705,7 @@ async function runBatchCommand(ctx: CrmCliContext): Promise<void> {
1664
1705
  ctx,
1665
1706
  await callMutation(ctx, api.batch.apply, {
1666
1707
  ops: ops as any,
1708
+ ...runContextArgs(ctx),
1667
1709
  ...(idempotencyKey ? { idempotencyKey } : {}),
1668
1710
  } as any),
1669
1711
  );
@@ -1804,6 +1846,7 @@ async function runTransactionCommand(ctx: CrmCliContext): Promise<void> {
1804
1846
  ctx,
1805
1847
  await callMutation(ctx, api.transaction.commit, {
1806
1848
  txnId: txnId as any,
1849
+ ...runContextArgs(ctx),
1807
1850
  }),
1808
1851
  );
1809
1852
  return;
@@ -1859,6 +1902,7 @@ async function runTransactionCommand(ctx: CrmCliContext): Promise<void> {
1859
1902
  ctx,
1860
1903
  await callMutation(ctx, api.transaction.commit, {
1861
1904
  txnId: begun.txnId as any,
1905
+ ...runContextArgs(ctx),
1862
1906
  }),
1863
1907
  );
1864
1908
  } else {
@@ -2637,6 +2681,7 @@ async function runPeopleCommand(ctx: CrmCliContext): Promise<void> {
2637
2681
  await callMutation(ctx, api.entries.create, {
2638
2682
  objectName: "people",
2639
2683
  data,
2684
+ ...runContextArgs(ctx),
2640
2685
  }),
2641
2686
  );
2642
2687
  return;
@@ -2701,6 +2746,7 @@ async function runCompaniesCommand(ctx: CrmCliContext): Promise<void> {
2701
2746
  await callMutation(ctx, api.entries.create, {
2702
2747
  objectName: "company",
2703
2748
  data,
2749
+ ...runContextArgs(ctx),
2704
2750
  }),
2705
2751
  );
2706
2752
  return;
@@ -2895,6 +2941,7 @@ Fields (columns):
2895
2941
  Entries (rows):
2896
2942
  dench crm entries list <object> [--limit N] [--with-meta] [--json]
2897
2943
  dench crm entries get <object> <entryId>
2944
+ dench crm history <object> <entryId> [--include-system] [--limit N] [--cursor CURSOR] [--json]
2898
2945
  dench crm entries create <object> --data '{"Field":"Value",...}' (alias: --fields) [--allow-unknown]
2899
2946
  dench crm entries create-many <object> --data '[{"Field":"Value",...}]' [--file rows.json] [--idempotency-key <key>] [--allow-unknown]
2900
2947
  dench crm entries update <object> <entryId> --data '{...}' (alias: --fields) [--allow-unknown]
package/dench.ts CHANGED
@@ -163,6 +163,14 @@ const api = {
163
163
  "functions/agentWorkspace:whatCanIDoHere",
164
164
  ),
165
165
  },
166
+ agentConfig: {
167
+ workspaceInstructions: makeFunctionReference<"query">(
168
+ "functions/agentConfig:workspaceInstructions",
169
+ ),
170
+ devWorkspaceInstructions: makeFunctionReference<"query">(
171
+ "functions/agentConfig:devWorkspaceInstructions",
172
+ ),
173
+ },
166
174
  files: {
167
175
  listTree: makeFunctionReference<"query">("functions/files:listTree"),
168
176
  deleteFile: makeFunctionReference<"mutation">(
@@ -450,7 +458,8 @@ Usage:
450
458
  dench use <session-key-or-workspace-slug> [--host <host>] [--json]
451
459
  dench logout [session-key-or-workspace-slug] [--session <key-or-scope>] [--host <host>] [--all] [--json]
452
460
  dench backend [show|set|clear] [<production|staging|local|<url>>] [--json]
453
- dench context [--json]
461
+ dench context [--instructions] [--json]
462
+ dench instructions [--json]
454
463
  dench status [--mine|--self] [--json]
455
464
  dench memory search "query" [--limit 8] [--json]
456
465
  dench memory save <key> "text" [--kind fact|decision|preference|goal|tool_note|other] [--tags a,b] [--sensitivity normal|broad|sensitive] [--scope user|org] [--json]
@@ -682,10 +691,14 @@ function contextHelp() {
682
691
  console.log(`Dench context
683
692
 
684
693
  Usage:
685
- dench context [--json]
694
+ dench context [--instructions] [--json]
695
+ dench instructions [--json]
686
696
 
687
697
  Shows the current workspace, current agent, assigned tasks, pending approvals
688
698
  requested by this agent, connected apps when available, and useful next commands.
699
+
700
+ Use --instructions (or dench instructions) to print the standing workspace
701
+ instructions external agents should treat as system-level guidance.
689
702
  `);
690
703
  }
691
704
 
@@ -2900,6 +2913,24 @@ function formatContext(context: JsonRecord) {
2900
2913
  return lines.join("\n");
2901
2914
  }
2902
2915
 
2916
+ async function buildWorkspaceInstructionsContext(
2917
+ runtime: Awaited<ReturnType<typeof getRuntime>>,
2918
+ ) {
2919
+ if (runtime.mode === "dev") {
2920
+ return (await runtime.client.query(
2921
+ api.functions.agentConfig.devWorkspaceInstructions,
2922
+ runtime.workspaceArgs,
2923
+ )) as JsonRecord;
2924
+ }
2925
+ if (runtime.mode !== "session" && runtime.mode !== "apiKey") {
2926
+ throw loginRequiredError("dench context --instructions");
2927
+ }
2928
+ return (await runtime.client.query(
2929
+ api.functions.agentConfig.workspaceInstructions,
2930
+ { sessionToken: runtime.sessionToken },
2931
+ )) as JsonRecord;
2932
+ }
2933
+
2903
2934
  async function devOverview(runtime: Awaited<ReturnType<typeof getRuntime>>) {
2904
2935
  if (runtime.mode !== "dev") {
2905
2936
  throw new Error("Not in dev mode");
@@ -4355,6 +4386,22 @@ async function main() {
4355
4386
  return;
4356
4387
  }
4357
4388
 
4389
+ if (command === "context" && args.includes("--instructions")) {
4390
+ const instructions = await buildWorkspaceInstructionsContext(runtime);
4391
+ print(
4392
+ json ? instructions : (stringField(instructions, "instructions") ?? ""),
4393
+ );
4394
+ return;
4395
+ }
4396
+
4397
+ if (command === "instructions") {
4398
+ const instructions = await buildWorkspaceInstructionsContext(runtime);
4399
+ print(
4400
+ json ? instructions : (stringField(instructions, "instructions") ?? ""),
4401
+ );
4402
+ return;
4403
+ }
4404
+
4358
4405
  if (command === "context") {
4359
4406
  const context = await buildContext(runtime);
4360
4407
  print(json ? context : formatContext(context));
@@ -340,6 +340,18 @@ const workspaceStatusPayload = z
340
340
  "Workspace orientation payload shared by /context, /status, /agents, and /approvals.",
341
341
  );
342
342
 
343
+ const workspaceInstructionsResponse = z.object({
344
+ instructions: z
345
+ .string()
346
+ .describe(
347
+ "Markdown standing instructions for external agents connected to this workspace.",
348
+ ),
349
+ workspace: z.object({
350
+ name: z.string(),
351
+ slug: z.string().optional(),
352
+ }),
353
+ });
354
+
343
355
  const memoryDoc = z
344
356
  .object({
345
357
  _id: z.string(),
@@ -1183,6 +1195,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1183
1195
 
1184
1196
  // Workspace
1185
1197
  "workspace.context": noInput,
1198
+ "workspace.instructions": noInput,
1186
1199
  "workspace.status": noInput,
1187
1200
  "workspace.agents": noInput,
1188
1201
  "workspace.approvals.list": noInput,
@@ -1909,6 +1922,7 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
1909
1922
 
1910
1923
  // Workspace orientation
1911
1924
  "workspace.context": workspaceStatusPayload,
1925
+ "workspace.instructions": workspaceInstructionsResponse,
1912
1926
  "workspace.status": workspaceStatusPayload,
1913
1927
  "workspace.agents": workspaceStatusPayload,
1914
1928
  "workspace.approvals.list": workspaceStatusPayload,
@@ -2757,6 +2771,11 @@ export const responseExamples: Record<string, unknown> = {
2757
2771
  },
2758
2772
  },
2759
2773
  "workspace.context": workspaceStatusExample,
2774
+ "workspace.instructions": {
2775
+ workspace: { name: "Acme", slug: "acme" },
2776
+ instructions:
2777
+ "# Dench Workspace Instructions\n\nWorkspace: Acme (acme)\n\n## Identity\nYou are Dench...",
2778
+ },
2760
2779
  "workspace.status": workspaceStatusExample,
2761
2780
  "workspace.agents": workspaceStatusExample,
2762
2781
  "workspace.approvals.list": workspaceStatusExample,
@@ -245,6 +245,18 @@ const rawApiOperations = [
245
245
  responseSchema: anyResponse,
246
246
  backend: convex("query", "functions/agentWorkspace:whatCanIDoHere"),
247
247
  }),
248
+ op({
249
+ id: "workspace.instructions",
250
+ group: "workspace",
251
+ summary:
252
+ "Return the current workspace's standing instructions for external agents.",
253
+ cli: "dench context --instructions",
254
+ method: "GET",
255
+ path: "/context/instructions",
256
+ requestSchema: noBody,
257
+ responseSchema: anyResponse,
258
+ backend: convex("query", "functions/agentConfig:workspaceInstructions"),
259
+ }),
248
260
  op({
249
261
  id: "workspace.status",
250
262
  group: "workspace",
@@ -704,6 +716,37 @@ const rawApiOperations = [
704
716
  responseSchema: anyResponse,
705
717
  backend: convex("query", "functions/crm/entries:get"),
706
718
  }),
719
+ op({
720
+ id: "crm.entries.history",
721
+ group: "crm",
722
+ summary: "List a CRM entry's audit history.",
723
+ cli: "dench crm history",
724
+ method: "GET",
725
+ path: "/crm/objects/{objectName}/entries/{entryId}/history",
726
+ requestSchema: noBody,
727
+ responseSchema: anyResponse,
728
+ backend: {
729
+ ...convex("query", "functions/crm/history:listForEntry"),
730
+ transformArgs: (input) => {
731
+ const limit =
732
+ typeof input.limit === "number"
733
+ ? Math.min(Math.max(Math.floor(input.limit), 1), 100)
734
+ : 50;
735
+ return {
736
+ objectName: input.objectName,
737
+ entryId: input.entryId,
738
+ includeSystem: input.includeSystem === true,
739
+ paginationOpts: {
740
+ numItems: limit,
741
+ cursor:
742
+ typeof input.cursor === "string" && input.cursor.length > 0
743
+ ? input.cursor
744
+ : null,
745
+ },
746
+ };
747
+ },
748
+ },
749
+ }),
707
750
  op({
708
751
  id: "crm.entries.create",
709
752
  group: "crm",
@@ -713,7 +756,9 @@ const rawApiOperations = [
713
756
  path: "/crm/objects/{objectName}/entries",
714
757
  requestSchema: anyObject,
715
758
  responseSchema: anyResponse,
716
- backend: convex("mutation", "functions/crm/entries:create"),
759
+ backend: convex("mutation", "functions/crm/entries:create", "bearer", {
760
+ includeRunId: true,
761
+ }),
717
762
  }),
718
763
  op({
719
764
  id: "crm.entries.createMany",
@@ -724,7 +769,9 @@ const rawApiOperations = [
724
769
  path: "/crm/objects/{objectName}/entries/batch",
725
770
  requestSchema: anyObject,
726
771
  responseSchema: anyResponse,
727
- backend: convex("mutation", "functions/crm/entries:createMany"),
772
+ backend: convex("mutation", "functions/crm/entries:createMany", "bearer", {
773
+ includeRunId: true,
774
+ }),
728
775
  }),
729
776
  op({
730
777
  id: "crm.entries.update",
@@ -735,7 +782,9 @@ const rawApiOperations = [
735
782
  path: "/crm/objects/{objectName}/entries/{entryId}",
736
783
  requestSchema: anyObject,
737
784
  responseSchema: anyResponse,
738
- backend: convex("mutation", "functions/crm/entries:update"),
785
+ backend: convex("mutation", "functions/crm/entries:update", "bearer", {
786
+ includeRunId: true,
787
+ }),
739
788
  }),
740
789
  op({
741
790
  id: "crm.entries.updateMany",
@@ -746,7 +795,9 @@ const rawApiOperations = [
746
795
  path: "/crm/objects/{objectName}/entries/batch",
747
796
  requestSchema: anyObject,
748
797
  responseSchema: anyResponse,
749
- backend: convex("mutation", "functions/crm/batch:apply"),
798
+ backend: convex("mutation", "functions/crm/batch:apply", "bearer", {
799
+ includeRunId: true,
800
+ }),
750
801
  }),
751
802
  op({
752
803
  id: "crm.entries.delete",
@@ -757,7 +808,9 @@ const rawApiOperations = [
757
808
  path: "/crm/objects/{objectName}/entries/{entryId}",
758
809
  requestSchema: anyObject,
759
810
  responseSchema: anyResponse,
760
- backend: convex("mutation", "functions/crm/entries:remove"),
811
+ backend: convex("mutation", "functions/crm/entries:remove", "bearer", {
812
+ includeRunId: true,
813
+ }),
761
814
  }),
762
815
  op({
763
816
  id: "crm.entries.bulkDelete",
@@ -768,7 +821,9 @@ const rawApiOperations = [
768
821
  path: "/crm/objects/{objectName}/entries/delete",
769
822
  requestSchema: anyObject,
770
823
  responseSchema: anyResponse,
771
- backend: convex("mutation", "functions/crm/entries:bulkDelete"),
824
+ backend: convex("mutation", "functions/crm/entries:bulkDelete", "bearer", {
825
+ includeRunId: true,
826
+ }),
772
827
  }),
773
828
 
774
829
  op({
@@ -791,7 +846,9 @@ const rawApiOperations = [
791
846
  path: "/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName}",
792
847
  requestSchema: anyObject,
793
848
  responseSchema: anyResponse,
794
- backend: convex("mutation", "functions/crm/cells:set"),
849
+ backend: convex("mutation", "functions/crm/cells:set", "bearer", {
850
+ includeRunId: true,
851
+ }),
795
852
  }),
796
853
  op({
797
854
  id: "crm.cells.setMany",
@@ -802,7 +859,9 @@ const rawApiOperations = [
802
859
  path: "/crm/cells/batch",
803
860
  requestSchema: anyObject,
804
861
  responseSchema: anyResponse,
805
- backend: convex("mutation", "functions/crm/cells:setMany"),
862
+ backend: convex("mutation", "functions/crm/cells:setMany", "bearer", {
863
+ includeRunId: true,
864
+ }),
806
865
  }),
807
866
  op({
808
867
  id: "crm.cells.append",
@@ -813,7 +872,9 @@ const rawApiOperations = [
813
872
  path: "/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName}/append",
814
873
  requestSchema: anyObject,
815
874
  responseSchema: anyResponse,
816
- backend: convex("mutation", "functions/crm/cells:append"),
875
+ backend: convex("mutation", "functions/crm/cells:append", "bearer", {
876
+ includeRunId: true,
877
+ }),
817
878
  }),
818
879
 
819
880
  op({
@@ -891,7 +952,9 @@ const rawApiOperations = [
891
952
  path: "/crm/batch",
892
953
  requestSchema: anyObject,
893
954
  responseSchema: anyResponse,
894
- backend: convex("mutation", "functions/crm/batch:apply"),
955
+ backend: convex("mutation", "functions/crm/batch:apply", "bearer", {
956
+ includeRunId: true,
957
+ }),
895
958
  }),
896
959
  op({
897
960
  id: "crm.transaction.begin",
@@ -924,7 +987,9 @@ const rawApiOperations = [
924
987
  path: "/crm/transactions/{txnId}/commit",
925
988
  requestSchema: anyObject,
926
989
  responseSchema: anyResponse,
927
- backend: convex("mutation", "functions/crm/transaction:commit"),
990
+ backend: convex("mutation", "functions/crm/transaction:commit", "bearer", {
991
+ includeRunId: true,
992
+ }),
928
993
  }),
929
994
  op({
930
995
  id: "crm.transaction.abort",
@@ -1325,6 +1325,17 @@ async function buildGatewayError(
1325
1325
  { status: response.status, code },
1326
1326
  );
1327
1327
  }
1328
+ if (
1329
+ response.status === 402 ||
1330
+ code === "on_demand_disabled" ||
1331
+ code === "insufficient_credits" ||
1332
+ code === "spend_limit_reached"
1333
+ ) {
1334
+ return new EnrichmentGatewayError(
1335
+ upstreamMessage ?? "AI credits exhausted.",
1336
+ { status: response.status, code },
1337
+ );
1338
+ }
1328
1339
 
1329
1340
  const detail = upstreamMessage || rawText || response.statusText;
1330
1341
  const truncated = detail.length > 500 ? `${detail.slice(0, 500)}...` : detail;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dench.com/cli",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Dench agent workspace CLI. v2 unifies auth behind `dench signin`; the legacy `dench login` / `dench onboard` / `dench setup` / `dench what-can-i-do` / `dench register` commands now error with a redirect.",
5
5
  "type": "module",
6
6
  "bin": {