@keystrokehq/cli 0.0.66 → 0.0.69

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.
Files changed (33) hide show
  1. package/dist/{cat-TSFMZVYS-RwFYGRgl.mjs → cat-TSFMZVYS-Bp6ZgHzP.mjs} +2 -2
  2. package/dist/{cat-TSFMZVYS-RwFYGRgl.mjs.map → cat-TSFMZVYS-Bp6ZgHzP.mjs.map} +1 -1
  3. package/dist/{cut-OKARJCCV-7cebiTlb.mjs → cut-OKARJCCV-DLzvnQ0X.mjs} +2 -2
  4. package/dist/{cut-OKARJCCV-7cebiTlb.mjs.map → cut-OKARJCCV-DLzvnQ0X.mjs.map} +1 -1
  5. package/dist/{dist-DRPUD4yJ-3gMinGsn.mjs → dist-BNJUEQuz-C9tkfUCE.mjs} +124 -3
  6. package/dist/dist-BNJUEQuz-C9tkfUCE.mjs.map +1 -0
  7. package/dist/{dist-CrZVpQSd.mjs → dist-nRx_8hnk.mjs} +6 -8
  8. package/dist/dist-nRx_8hnk.mjs.map +1 -0
  9. package/dist/{du-572XNP42-RgaK1Rqq.mjs → du-572XNP42-DCcv0kSr.mjs} +2 -2
  10. package/dist/{du-572XNP42-RgaK1Rqq.mjs.map → du-572XNP42-DCcv0kSr.mjs.map} +1 -1
  11. package/dist/{emit-route-manifest-DRcNXHCP-DN1BpGOi.mjs → emit-route-manifest-DRcNXHCP-BkSmGkhV.mjs} +14 -13
  12. package/dist/{emit-route-manifest-DRcNXHCP-DN1BpGOi.mjs.map → emit-route-manifest-DRcNXHCP-BkSmGkhV.mjs.map} +1 -1
  13. package/dist/index.mjs +903 -94
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/{jq-4XLYLOS5-DNvJFpkY.mjs → jq-4XLYLOS5-CgTVHGto.mjs} +2 -2
  16. package/dist/{jq-4XLYLOS5-DNvJFpkY.mjs.map → jq-4XLYLOS5-CgTVHGto.mjs.map} +1 -1
  17. package/dist/{ls-ZJGQER7M-DHpAibRp.mjs → ls-ZJGQER7M-DbHjJAJ5.mjs} +2 -2
  18. package/dist/{ls-ZJGQER7M-DHpAibRp.mjs.map → ls-ZJGQER7M-DbHjJAJ5.mjs.map} +1 -1
  19. package/dist/{mistral-R2FX6jgL-C3TQNHh7.mjs → mistral-D7a1LQ-6-CFFRHspK.mjs} +3 -3
  20. package/dist/{mistral-R2FX6jgL-C3TQNHh7.mjs.map → mistral-D7a1LQ-6-CFFRHspK.mjs.map} +1 -1
  21. package/dist/{sort-SW2YEO5B-BD_P9Q6n.mjs → sort-SW2YEO5B-DM3UAdjd.mjs} +2 -2
  22. package/dist/{sort-SW2YEO5B-BD_P9Q6n.mjs.map → sort-SW2YEO5B-DM3UAdjd.mjs.map} +1 -1
  23. package/dist/{tree-YLD52CNT-C9IR00tM.mjs → tree-YLD52CNT-Bv1j-DWN.mjs} +2 -2
  24. package/dist/{tree-YLD52CNT-C9IR00tM.mjs.map → tree-YLD52CNT-Bv1j-DWN.mjs.map} +1 -1
  25. package/dist/{uniq-XSIZR6PB-gU1BpRGX.mjs → uniq-XSIZR6PB-d6Kx_H64.mjs} +2 -2
  26. package/dist/{uniq-XSIZR6PB-gU1BpRGX.mjs.map → uniq-XSIZR6PB-d6Kx_H64.mjs.map} +1 -1
  27. package/dist/{wc-LF7NU4LA-DwSj8_V4.mjs → wc-LF7NU4LA-B6PXzeyp.mjs} +2 -2
  28. package/dist/{wc-LF7NU4LA-DwSj8_V4.mjs.map → wc-LF7NU4LA-B6PXzeyp.mjs.map} +1 -1
  29. package/dist/{xan-Y6WF3IRG-CAgH4sE_.mjs → xan-Y6WF3IRG-CnmH45qB.mjs} +2 -2
  30. package/dist/{xan-Y6WF3IRG-CAgH4sE_.mjs.map → xan-Y6WF3IRG-CnmH45qB.mjs.map} +1 -1
  31. package/package.json +3 -21
  32. package/dist/dist-CrZVpQSd.mjs.map +0 -1
  33. package/dist/dist-DRPUD4yJ-3gMinGsn.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1460,7 +1460,7 @@ const propertyKeyTypes = /* @__PURE__ */ new Set([
1460
1460
  function escapeRegex(str) {
1461
1461
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1462
1462
  }
1463
- function clone(inst, def, params) {
1463
+ function clone$1(inst, def, params) {
1464
1464
  const cl = new inst._zod.constr(def ?? inst._zod.def);
1465
1465
  if (!def || params?.parent) cl._zod.parent = inst;
1466
1466
  return cl;
@@ -1496,7 +1496,7 @@ function pick(schema, mask) {
1496
1496
  const currDef = schema._zod.def;
1497
1497
  const checks = currDef.checks;
1498
1498
  if (checks && checks.length > 0) throw new Error(".pick() cannot be used on object schemas containing refinements");
1499
- return clone(schema, mergeDefs(schema._zod.def, {
1499
+ return clone$1(schema, mergeDefs(schema._zod.def, {
1500
1500
  get shape() {
1501
1501
  const newShape = {};
1502
1502
  for (const key in mask) {
@@ -1514,7 +1514,7 @@ function omit(schema, mask) {
1514
1514
  const currDef = schema._zod.def;
1515
1515
  const checks = currDef.checks;
1516
1516
  if (checks && checks.length > 0) throw new Error(".omit() cannot be used on object schemas containing refinements");
1517
- return clone(schema, mergeDefs(schema._zod.def, {
1517
+ return clone$1(schema, mergeDefs(schema._zod.def, {
1518
1518
  get shape() {
1519
1519
  const newShape = { ...schema._zod.def.shape };
1520
1520
  for (const key in mask) {
@@ -1535,7 +1535,7 @@ function extend(schema, shape) {
1535
1535
  const existingShape = schema._zod.def.shape;
1536
1536
  for (const key in shape) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
1537
1537
  }
1538
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
1538
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
1539
1539
  const _shape = {
1540
1540
  ...schema._zod.def.shape,
1541
1541
  ...shape
@@ -1546,7 +1546,7 @@ function extend(schema, shape) {
1546
1546
  }
1547
1547
  function safeExtend(schema, shape) {
1548
1548
  if (!isPlainObject$1(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
1549
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
1549
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
1550
1550
  const _shape = {
1551
1551
  ...schema._zod.def.shape,
1552
1552
  ...shape
@@ -1557,7 +1557,7 @@ function safeExtend(schema, shape) {
1557
1557
  }
1558
1558
  function merge(a, b) {
1559
1559
  if (a._zod.def.checks?.length) throw new Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.");
1560
- return clone(a, mergeDefs(a._zod.def, {
1560
+ return clone$1(a, mergeDefs(a._zod.def, {
1561
1561
  get shape() {
1562
1562
  const _shape = {
1563
1563
  ...a._zod.def.shape,
@@ -1575,7 +1575,7 @@ function merge(a, b) {
1575
1575
  function partial(Class, schema, mask) {
1576
1576
  const checks = schema._zod.def.checks;
1577
1577
  if (checks && checks.length > 0) throw new Error(".partial() cannot be used on object schemas containing refinements");
1578
- return clone(schema, mergeDefs(schema._zod.def, {
1578
+ return clone$1(schema, mergeDefs(schema._zod.def, {
1579
1579
  get shape() {
1580
1580
  const oldShape = schema._zod.def.shape;
1581
1581
  const shape = { ...oldShape };
@@ -1598,7 +1598,7 @@ function partial(Class, schema, mask) {
1598
1598
  }));
1599
1599
  }
1600
1600
  function required(Class, schema, mask) {
1601
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
1601
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
1602
1602
  const oldShape = schema._zod.def.shape;
1603
1603
  const shape = { ...oldShape };
1604
1604
  if (mask) for (const key in mask) {
@@ -5354,7 +5354,7 @@ const ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
5354
5354
  return this.check(...chks);
5355
5355
  },
5356
5356
  clone(def, params) {
5357
- return clone(this, def, params);
5357
+ return clone$1(this, def, params);
5358
5358
  },
5359
5359
  brand() {
5360
5360
  return this;
@@ -6795,6 +6795,126 @@ function listenPortFromPublicUrl(value, fallback) {
6795
6795
  return fallback;
6796
6796
  }
6797
6797
  }
6798
+ const serializedRouteManifestEntrySchema = discriminatedUnion("kind", [
6799
+ object({
6800
+ kind: literal("health"),
6801
+ method: literal("GET"),
6802
+ path: literal("/health")
6803
+ }),
6804
+ object({
6805
+ kind: literal("agent"),
6806
+ method: literal("POST"),
6807
+ path: string(),
6808
+ moduleFile: string(),
6809
+ requestSchema: record(string(), unknown()),
6810
+ responseSchema: record(string(), unknown())
6811
+ }),
6812
+ object({
6813
+ kind: literal("agent-sessions-list"),
6814
+ method: literal("GET"),
6815
+ path: string(),
6816
+ agentId: string(),
6817
+ moduleFile: string()
6818
+ }),
6819
+ object({
6820
+ kind: literal("agent-session-detail"),
6821
+ method: literal("GET"),
6822
+ path: string(),
6823
+ agentId: string(),
6824
+ moduleFile: string()
6825
+ }),
6826
+ object({
6827
+ kind: literal("workflow"),
6828
+ method: literal("POST"),
6829
+ path: string(),
6830
+ workflowId: string(),
6831
+ workflowName: string().optional(),
6832
+ moduleFile: string(),
6833
+ requestSchema: record(string(), unknown()),
6834
+ responseSchema: record(string(), unknown())
6835
+ }),
6836
+ object({
6837
+ kind: literal("workflow-runs-list"),
6838
+ method: literal("GET"),
6839
+ path: string(),
6840
+ workflowId: string(),
6841
+ workflowName: string().optional(),
6842
+ moduleFile: string()
6843
+ }),
6844
+ object({
6845
+ kind: literal("workflow-run-detail"),
6846
+ method: literal("GET"),
6847
+ path: string(),
6848
+ workflowId: string(),
6849
+ workflowName: string().optional(),
6850
+ moduleFile: string()
6851
+ }),
6852
+ object({
6853
+ kind: literal("trigger-webhook"),
6854
+ method: literal("POST"),
6855
+ path: string(),
6856
+ attachmentIds: array(string()),
6857
+ moduleFile: string(),
6858
+ requestSchema: record(string(), unknown()),
6859
+ responseSchema: record(string(), unknown())
6860
+ }),
6861
+ object({
6862
+ kind: literal("trigger-poll"),
6863
+ method: literal("POST"),
6864
+ path: string(),
6865
+ attachmentId: string(),
6866
+ moduleFile: string(),
6867
+ schedule: string(),
6868
+ responseSchema: record(string(), unknown())
6869
+ }),
6870
+ object({
6871
+ kind: literal("trigger-poll-group"),
6872
+ method: literal("POST"),
6873
+ path: string(),
6874
+ pollId: string(),
6875
+ attachmentIds: array(string()),
6876
+ moduleFile: string(),
6877
+ schedule: string(),
6878
+ responseSchema: record(string(), unknown())
6879
+ }),
6880
+ object({
6881
+ kind: literal("trigger-runs-list"),
6882
+ method: literal("GET"),
6883
+ path: string(),
6884
+ attachmentId: string(),
6885
+ moduleFile: string()
6886
+ }),
6887
+ object({
6888
+ kind: literal("trigger-run-detail"),
6889
+ method: literal("GET"),
6890
+ path: string(),
6891
+ attachmentId: string(),
6892
+ moduleFile: string()
6893
+ }),
6894
+ object({
6895
+ kind: literal("cron-schedule"),
6896
+ attachmentId: string(),
6897
+ moduleFile: string(),
6898
+ schedule: string()
6899
+ }),
6900
+ object({
6901
+ kind: literal("plugin"),
6902
+ method: _enum([
6903
+ "GET",
6904
+ "POST",
6905
+ "PUT",
6906
+ "PATCH",
6907
+ "DELETE"
6908
+ ]),
6909
+ path: string(),
6910
+ plugin: string()
6911
+ })
6912
+ ]);
6913
+ object({
6914
+ version: literal(1),
6915
+ entries: array(serializedRouteManifestEntrySchema),
6916
+ integrations: array(string()).optional()
6917
+ });
6798
6918
  /** How a credential instance is stored (`credential_instances.auth_kind`). */
6799
6919
  const CredentialAuthKindSchema = _enum(["api_key", "oauth_managed"]);
6800
6920
  /** Where a credential instance is stored (`credential_instances.scope_type`). */
@@ -8063,8 +8183,8 @@ const DEFAULT_PROJECT_SETTINGS = {
8063
8183
  defaultRole: "editor",
8064
8184
  invitePermission: "admin"
8065
8185
  };
8066
- const MOCK_STORAGE_KEY$3 = "keystroke:mock-project-settings:v1";
8067
- function getBrowserLocalStorage$3() {
8186
+ const MOCK_STORAGE_KEY$4 = "keystroke:mock-project-settings:v1";
8187
+ function getBrowserLocalStorage$4() {
8068
8188
  if (typeof globalThis !== "object") return null;
8069
8189
  return globalThis.localStorage ?? null;
8070
8190
  }
@@ -8090,10 +8210,10 @@ function mergeProjectSettings(current, patch) {
8090
8210
  });
8091
8211
  }
8092
8212
  function readSettingsStore() {
8093
- const localStorage = getBrowserLocalStorage$3();
8213
+ const localStorage = getBrowserLocalStorage$4();
8094
8214
  if (!localStorage) return {};
8095
8215
  try {
8096
- const raw = localStorage.getItem(MOCK_STORAGE_KEY$3);
8216
+ const raw = localStorage.getItem(MOCK_STORAGE_KEY$4);
8097
8217
  if (!raw) return {};
8098
8218
  const parsed = JSON.parse(raw);
8099
8219
  if (!parsed || typeof parsed !== "object") return {};
@@ -8103,10 +8223,10 @@ function readSettingsStore() {
8103
8223
  }
8104
8224
  }
8105
8225
  function writeSettingsStore(store) {
8106
- const localStorage = getBrowserLocalStorage$3();
8226
+ const localStorage = getBrowserLocalStorage$4();
8107
8227
  if (!localStorage) return;
8108
8228
  try {
8109
- localStorage.setItem(MOCK_STORAGE_KEY$3, JSON.stringify(store));
8229
+ localStorage.setItem(MOCK_STORAGE_KEY$4, JSON.stringify(store));
8110
8230
  } catch {}
8111
8231
  }
8112
8232
  function requireProjectId(projectId) {
@@ -9210,7 +9330,6 @@ const agentSeed = [{
9210
9330
  updatedAt: "2026-06-02T14:20:00Z",
9211
9331
  description: "Triage support requests and draft helpful replies.",
9212
9332
  sourcePath: "src/agents/support-agent.ts",
9213
- mode: "sandbox",
9214
9333
  model: "claude-4-sonnet",
9215
9334
  toolCount: 4,
9216
9335
  credentialCount: 2,
@@ -9222,7 +9341,6 @@ const agentSeed = [{
9222
9341
  updatedAt: "2026-06-01T09:15:00Z",
9223
9342
  description: "Summarize docs and pull answers from connected sources.",
9224
9343
  sourcePath: "src/agents/research-agent.ts",
9225
- mode: "worker",
9226
9344
  model: "claude-4-sonnet",
9227
9345
  toolCount: 6,
9228
9346
  credentialCount: 3,
@@ -9235,11 +9353,8 @@ const workflowSeed = [{
9235
9353
  updatedAt: "2026-06-02T11:05:00Z",
9236
9354
  description: "Collect project updates and send a daily summary.",
9237
9355
  sourcePath: "src/workflows/daily-digest.ts",
9238
- trigger: "schedule.daily",
9239
9356
  stepCount: 2,
9240
- integrationCount: 1,
9241
- lastRunAt: "2026-06-02T11:05:00Z",
9242
- runState: "Succeeded"
9357
+ lastRunAt: "2026-06-02T11:05:00Z"
9243
9358
  }, {
9244
9359
  id: "workflow-email-triage",
9245
9360
  name: "Email Triage",
@@ -9247,11 +9362,8 @@ const workflowSeed = [{
9247
9362
  updatedAt: "2026-05-31T16:40:00Z",
9248
9363
  description: "Label, prioritize, and draft replies for inbound email.",
9249
9364
  sourcePath: "src/workflows/email-triage.ts",
9250
- trigger: "gmail.new_message",
9251
9365
  stepCount: 4,
9252
- integrationCount: 2,
9253
- lastRunAt: "2026-05-31T16:40:00Z",
9254
- runState: "Running"
9366
+ lastRunAt: "2026-05-31T16:40:00Z"
9255
9367
  }];
9256
9368
  const skillSeed = [{
9257
9369
  id: "skill-attio-sync",
@@ -9259,20 +9371,14 @@ const skillSeed = [{
9259
9371
  projectId: "project_personal_repo",
9260
9372
  updatedAt: "2026-06-01T18:05:00Z",
9261
9373
  description: "Enrich company and contact records from Attio.",
9262
- sourcePath: ".agents/skills/attio-sync/SKILL.md",
9263
- category: "CRM",
9264
- availability: "Private",
9265
- lastUsedAt: "2026-06-02T08:30:00Z"
9374
+ sourcePath: ".agents/skills/attio-sync/SKILL.md"
9266
9375
  }, {
9267
9376
  id: "skill-doc-coauthor",
9268
9377
  name: "Doc Co-author",
9269
9378
  projectId: "project_personal_repo",
9270
9379
  updatedAt: "2026-05-30T10:24:00Z",
9271
9380
  description: "Collaborative writing, editing, and version tracking for docs.",
9272
- sourcePath: ".agents/skills/doc-coauthor/SKILL.md",
9273
- category: "Writing",
9274
- availability: "Private",
9275
- lastUsedAt: "2026-05-29T14:12:00Z"
9381
+ sourcePath: ".agents/skills/doc-coauthor/SKILL.md"
9276
9382
  }];
9277
9383
  const deploymentSeed = [{
9278
9384
  id: "deployment-v002",
@@ -9283,11 +9389,7 @@ const deploymentSeed = [{
9283
9389
  deployedBy: "Dallin Bentley",
9284
9390
  deployedByEmail: "dallin@keystroke.ai",
9285
9391
  createdAt: "2026-04-20T15:44:00Z",
9286
- active: true,
9287
- summary: "Added support-agent and daily digest workflow.",
9288
- changedFiles: 4,
9289
- lineAdditions: 182,
9290
- lineDeletions: 24
9392
+ active: true
9291
9393
  }, {
9292
9394
  id: "deployment-v001",
9293
9395
  projectId: "project_personal_repo",
@@ -9297,11 +9399,7 @@ const deploymentSeed = [{
9297
9399
  deployedBy: "Dallin Bentley",
9298
9400
  deployedByEmail: "dallin@keystroke.ai",
9299
9401
  createdAt: "2026-04-18T15:44:00Z",
9300
- active: false,
9301
- summary: "Initial deployed project snapshot.",
9302
- changedFiles: 2,
9303
- lineAdditions: 96,
9304
- lineDeletions: 0
9402
+ active: false
9305
9403
  }];
9306
9404
  const projectFileSeed = [
9307
9405
  {
@@ -9495,7 +9593,7 @@ function buildRecentResourceSeed() {
9495
9593
  });
9496
9594
  const skillRecents = skillSeed.map((skill, index) => {
9497
9595
  const lastModifiedAt = skill.updatedAt;
9498
- const lastOpenedAt = skill.lastUsedAt ?? hoursBefore(lastModifiedAt, index * 3 + 1);
9596
+ const lastOpenedAt = hoursBefore(lastModifiedAt, index * 3 + 1);
9499
9597
  return {
9500
9598
  id: `recent-skill-${skill.id}`,
9501
9599
  resourceKind: "skill",
@@ -9733,6 +9831,57 @@ function createRecentsResource() {
9733
9831
  }, async () => buildRecentResourceSeed()) };
9734
9832
  }
9735
9833
  const historyProjectNameById = { project_personal_repo: "Blake's Project" };
9834
+ const MOCK_ACTOR = {
9835
+ userId: "user_blake",
9836
+ name: "Blake Rouse",
9837
+ avatarUrl: null
9838
+ };
9839
+ function buildCompletedUsage(durationMs) {
9840
+ return {
9841
+ runDurationMs: durationMs,
9842
+ lineItems: [
9843
+ {
9844
+ id: "claude-opus",
9845
+ label: "Claude Opus 4.8",
9846
+ amountUsd: .413
9847
+ },
9848
+ {
9849
+ id: "gemini-flash",
9850
+ label: "Gemini Flash 3.5",
9851
+ amountUsd: .001
9852
+ },
9853
+ {
9854
+ id: "daytona",
9855
+ label: "Daytona Sandbox",
9856
+ amountUsd: .188
9857
+ }
9858
+ ],
9859
+ totalExcludingDurationUsd: .603
9860
+ };
9861
+ }
9862
+ function buildRunningUsage() {
9863
+ return {
9864
+ runDurationMs: null,
9865
+ lineItems: [],
9866
+ totalExcludingDurationUsd: null
9867
+ };
9868
+ }
9869
+ function buildFailedUsage(durationMs) {
9870
+ if (durationMs == null) return {
9871
+ runDurationMs: null,
9872
+ lineItems: [],
9873
+ totalExcludingDurationUsd: null
9874
+ };
9875
+ return {
9876
+ runDurationMs: durationMs,
9877
+ lineItems: [{
9878
+ id: "claude-opus",
9879
+ label: "Claude Opus 4.8",
9880
+ amountUsd: .021
9881
+ }],
9882
+ totalExcludingDurationUsd: .021
9883
+ };
9884
+ }
9736
9885
  function workflowKeyFromId(workflowId) {
9737
9886
  const row = workflowSeed.find((workflow) => workflow.id === workflowId);
9738
9887
  if (!row?.sourcePath) return workflowId;
@@ -9754,11 +9903,17 @@ const historyRunSeed = [
9754
9903
  resourceId: "agent-support",
9755
9904
  resourceName: "Support Agent",
9756
9905
  status: "completed",
9906
+ triggeredAt: "2026-04-20T15:44:00Z",
9907
+ timeInQueueMs: 85,
9757
9908
  startedAt: "2026-04-20T15:44:00Z",
9758
9909
  finishedAt: "2026-04-20T15:44:42Z",
9759
9910
  durationMs: 42e3,
9760
9911
  triggerLabel: "Gateway",
9761
9912
  triggerRaw: "gateway",
9913
+ ranAs: MOCK_ACTOR,
9914
+ modelsUsed: ["Claude Opus 4.8", "Gemini Flash 3.5"],
9915
+ otherServicesUsed: ["Slack", "Daytona Sandbox"],
9916
+ totalCostUsd: .603,
9762
9917
  messageCount: 8
9763
9918
  },
9764
9919
  {
@@ -9771,11 +9926,17 @@ const historyRunSeed = [
9771
9926
  resourceId: "workflow-daily-digest",
9772
9927
  resourceName: "Daily Digest",
9773
9928
  status: "running",
9929
+ triggeredAt: "2026-04-20T15:12:00Z",
9930
+ timeInQueueMs: null,
9774
9931
  startedAt: "2026-04-20T15:12:00Z",
9775
9932
  finishedAt: null,
9776
9933
  durationMs: null,
9777
9934
  triggerLabel: "Cron",
9778
9935
  triggerRaw: "cron",
9936
+ ranAs: MOCK_ACTOR,
9937
+ modelsUsed: [],
9938
+ otherServicesUsed: [],
9939
+ totalCostUsd: null,
9779
9940
  stepCount: 1
9780
9941
  },
9781
9942
  {
@@ -9788,11 +9949,17 @@ const historyRunSeed = [
9788
9949
  resourceId: "agent-support",
9789
9950
  resourceName: "Support Agent",
9790
9951
  status: "failed",
9952
+ triggeredAt: "2026-04-19T18:22:00Z",
9953
+ timeInQueueMs: 45,
9791
9954
  startedAt: "2026-04-19T18:22:00Z",
9792
9955
  finishedAt: "2026-04-19T18:22:12Z",
9793
9956
  durationMs: 12e3,
9794
9957
  triggerLabel: "Gateway",
9795
9958
  triggerRaw: "gateway",
9959
+ ranAs: MOCK_ACTOR,
9960
+ modelsUsed: ["Claude Opus 4.8"],
9961
+ otherServicesUsed: ["Slack"],
9962
+ totalCostUsd: .021,
9796
9963
  messageCount: 2
9797
9964
  },
9798
9965
  {
@@ -9805,11 +9972,17 @@ const historyRunSeed = [
9805
9972
  resourceId: "workflow-email-triage",
9806
9973
  resourceName: "Email Triage",
9807
9974
  status: "failed",
9975
+ triggeredAt: "2026-05-31T16:40:05Z",
9976
+ timeInQueueMs: 120,
9808
9977
  startedAt: "2026-05-31T16:40:05Z",
9809
9978
  finishedAt: "2026-05-31T16:40:18Z",
9810
9979
  durationMs: 13e3,
9811
9980
  triggerLabel: "Webhook",
9812
9981
  triggerRaw: "webhook",
9982
+ ranAs: MOCK_ACTOR,
9983
+ modelsUsed: ["Claude Opus 4.8"],
9984
+ otherServicesUsed: ["Gmail"],
9985
+ totalCostUsd: .021,
9813
9986
  stepCount: 2
9814
9987
  },
9815
9988
  {
@@ -9822,15 +9995,59 @@ const historyRunSeed = [
9822
9995
  resourceId: "agent-research",
9823
9996
  resourceName: "Research Agent",
9824
9997
  status: "completed",
9998
+ triggeredAt: "2026-06-01T09:15:00Z",
9999
+ timeInQueueMs: 210,
9825
10000
  startedAt: "2026-06-01T09:15:00Z",
9826
10001
  finishedAt: "2026-06-01T09:16:24Z",
9827
10002
  durationMs: 84e3,
9828
10003
  triggerLabel: "API",
9829
10004
  triggerRaw: "api",
10005
+ ranAs: MOCK_ACTOR,
10006
+ modelsUsed: ["Claude Opus 4.8"],
10007
+ otherServicesUsed: ["Exa"],
10008
+ totalCostUsd: .603,
9830
10009
  messageCount: 12
9831
10010
  }
9832
10011
  ];
10012
+ const historyRunById = Object.fromEntries(historyRunSeed.map((run) => [run.id, run]));
10013
+ /** Resolve a seed run by id so fixtures stay consistent if the seed is reordered. */
10014
+ function runById(id) {
10015
+ const run = historyRunById[id];
10016
+ if (!run) throw new Error(`Unknown history run: ${id}`);
10017
+ return run;
10018
+ }
10019
+ let historyRuns = historyRunSeed.map((run) => ({ ...run }));
10020
+ const historyRunStatusOverrides = /* @__PURE__ */ new Map();
10021
+ function listHistoryRuns() {
10022
+ return historyRuns;
10023
+ }
10024
+ /**
10025
+ * Cancel a running/queued run. Terminal runs are returned unchanged. Updates the
10026
+ * list row and records an override so {@link getHistoryRunDetail} stays in sync.
10027
+ */
10028
+ function cancelHistoryRun(runId) {
10029
+ const run = historyRuns.find((entry) => entry.id === runId);
10030
+ if (!run) return null;
10031
+ if (run.status !== "running" && run.status !== "pending") return run;
10032
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
10033
+ const durationMs = run.startedAt ? Math.max(0, new Date(finishedAt).getTime() - new Date(run.startedAt).getTime()) : run.durationMs;
10034
+ const canceled = {
10035
+ ...run,
10036
+ status: "canceled",
10037
+ finishedAt,
10038
+ durationMs
10039
+ };
10040
+ historyRuns = historyRuns.map((entry) => entry.id === runId ? canceled : entry);
10041
+ historyRunStatusOverrides.set(runId, {
10042
+ status: "canceled",
10043
+ finishedAt
10044
+ });
10045
+ return canceled;
10046
+ }
9833
10047
  function buildSupportAgentTrace(runId, failed) {
10048
+ const run = runById(runId);
10049
+ const startedAt = run.startedAt;
10050
+ const finishedAt = run.finishedAt ?? run.startedAt;
9834
10051
  const sessionSpanId = `span-session-${runId}`;
9835
10052
  return {
9836
10053
  traceId: runId,
@@ -9838,13 +10055,17 @@ function buildSupportAgentTrace(runId, failed) {
9838
10055
  triggerType: "gateway",
9839
10056
  triggerRunId: `trigger-${runId}`,
9840
10057
  agentKey: "support-agent",
10058
+ attachmentId: "attach-slack-incoming",
9841
10059
  attachmentKey: "slack-incoming",
10060
+ route: "/agents/support-agent",
9842
10061
  sourcePath: "src/triggers/slack-message.ts"
9843
10062
  },
9844
10063
  gateway: {
10064
+ gatewayAttachmentId: "attach-slack-incoming",
9845
10065
  gatewayAttachmentKey: "slack-incoming",
9846
10066
  threadId: "C01234567",
9847
- channelId: "support-escalations"
10067
+ channelId: "support-escalations",
10068
+ mode: "thread"
9848
10069
  },
9849
10070
  spans: [{
9850
10071
  id: sessionSpanId,
@@ -9854,8 +10075,8 @@ function buildSupportAgentTrace(runId, failed) {
9854
10075
  refId: runId,
9855
10076
  name: "Support Agent",
9856
10077
  status: failed ? "error" : "ok",
9857
- startedAt: "2026-04-20T15:44:00Z",
9858
- finishedAt: failed ? "2026-04-19T18:22:12Z" : "2026-04-20T15:44:42Z",
10078
+ startedAt,
10079
+ finishedAt,
9859
10080
  metadata: { messageCount: failed ? 2 : 8 },
9860
10081
  error: failed ? { message: "Credential expired" } : null
9861
10082
  }, {
@@ -9866,8 +10087,8 @@ function buildSupportAgentTrace(runId, failed) {
9866
10087
  refId: `tool-${runId}`,
9867
10088
  name: "post_slack_reply",
9868
10089
  status: failed ? "error" : "ok",
9869
- startedAt: "2026-04-20T15:44:30Z",
9870
- finishedAt: failed ? "2026-04-19T18:22:11Z" : "2026-04-20T15:44:40Z",
10090
+ startedAt,
10091
+ finishedAt,
9871
10092
  metadata: null,
9872
10093
  error: failed ? { message: "oauth_token_expired" } : null
9873
10094
  }],
@@ -9880,7 +10101,7 @@ function buildSupportAgentTrace(runId, failed) {
9880
10101
  message: failed ? "Received Slack message" : "Session started from gateway thread",
9881
10102
  data: null,
9882
10103
  source: "system",
9883
- createdAt: "2026-04-20T15:44:01Z"
10104
+ createdAt: startedAt
9884
10105
  }, {
9885
10106
  id: `log-${runId}-2`,
9886
10107
  traceId: runId,
@@ -9890,7 +10111,7 @@ function buildSupportAgentTrace(runId, failed) {
9890
10111
  message: failed ? "Credential expired before response could be posted." : "Drafted three support replies.",
9891
10112
  data: null,
9892
10113
  source: "system",
9893
- createdAt: failed ? "2026-04-19T18:22:12Z" : "2026-04-20T15:44:42Z"
10114
+ createdAt: finishedAt
9894
10115
  }]
9895
10116
  };
9896
10117
  }
@@ -9901,7 +10122,9 @@ function buildDailyDigestTrace() {
9901
10122
  triggerType: "cron",
9902
10123
  triggerRunId: "trigger-daily-digest-001",
9903
10124
  workflowKey: "daily-digest",
10125
+ attachmentId: "attach-schedule-daily",
9904
10126
  attachmentKey: "schedule-daily",
10127
+ route: "/workflows/daily-digest",
9905
10128
  sourcePath: "src/triggers/daily-digest.ts"
9906
10129
  },
9907
10130
  gateway: null,
@@ -9996,7 +10219,9 @@ function buildEmailTriageDetail() {
9996
10219
  triggerType: "webhook",
9997
10220
  triggerRunId: "trigger-email-002",
9998
10221
  workflowKey: "email-triage",
9999
- attachmentKey: "gmail-new-message"
10222
+ attachmentId: "attach-gmail-new-message",
10223
+ attachmentKey: "gmail-new-message",
10224
+ route: "/workflows/email-triage"
10000
10225
  },
10001
10226
  gateway: null,
10002
10227
  spans: [{
@@ -10023,7 +10248,9 @@ function buildEmailTriageDetail() {
10023
10248
  source: "system",
10024
10249
  createdAt: "2026-05-31T16:40:18Z"
10025
10250
  }]
10026
- }
10251
+ },
10252
+ ranAs: MOCK_ACTOR,
10253
+ usage: buildFailedUsage(13e3)
10027
10254
  };
10028
10255
  }
10029
10256
  const historyDetailById = {
@@ -10050,7 +10277,9 @@ const historyDetailById = {
10050
10277
  role: "assistant",
10051
10278
  content: "Drafted reply with prorated credit explanation."
10052
10279
  }],
10053
- trace: buildSupportAgentTrace("run-support-agent-001", false)
10280
+ trace: buildSupportAgentTrace("run-support-agent-001", false),
10281
+ ranAs: MOCK_ACTOR,
10282
+ usage: buildCompletedUsage(42e3)
10054
10283
  },
10055
10284
  "run-support-agent-000": {
10056
10285
  kind: "agent",
@@ -10072,7 +10301,9 @@ const historyDetailById = {
10072
10301
  role: "user",
10073
10302
  content: "Urgent refund request in #support."
10074
10303
  }],
10075
- trace: buildSupportAgentTrace("run-support-agent-000", true)
10304
+ trace: buildSupportAgentTrace("run-support-agent-000", true),
10305
+ ranAs: MOCK_ACTOR,
10306
+ usage: buildFailedUsage(12e3)
10076
10307
  },
10077
10308
  "run-daily-digest-001": {
10078
10309
  kind: "workflow",
@@ -10102,7 +10333,9 @@ const historyDetailById = {
10102
10333
  output: { projects: 3 },
10103
10334
  completedAt: "2026-04-20T15:12:45Z"
10104
10335
  }],
10105
- trace: buildDailyDigestTrace()
10336
+ trace: buildDailyDigestTrace(),
10337
+ ranAs: MOCK_ACTOR,
10338
+ usage: buildRunningUsage()
10106
10339
  },
10107
10340
  "run-email-triage-002": buildEmailTriageDetail(),
10108
10341
  "run-research-agent-003": {
@@ -10130,7 +10363,8 @@ const historyDetailById = {
10130
10363
  trigger: {
10131
10364
  triggerType: "api",
10132
10365
  triggerRunId: "trigger-research-003",
10133
- agentKey: "research-agent"
10366
+ agentKey: "research-agent",
10367
+ route: "/agents/research-agent"
10134
10368
  },
10135
10369
  gateway: null,
10136
10370
  spans: [{
@@ -10157,11 +10391,34 @@ const historyDetailById = {
10157
10391
  source: "system",
10158
10392
  createdAt: "2026-06-01T09:16:24Z"
10159
10393
  }]
10160
- }
10394
+ },
10395
+ ranAs: MOCK_ACTOR,
10396
+ usage: buildCompletedUsage(84e3)
10161
10397
  }
10162
10398
  };
10399
+ function applyRunStatusOverride(detail, override) {
10400
+ if (detail.kind === "workflow") return {
10401
+ ...detail,
10402
+ run: {
10403
+ ...detail.run,
10404
+ status: override.status,
10405
+ finishedAt: override.finishedAt
10406
+ }
10407
+ };
10408
+ return {
10409
+ ...detail,
10410
+ session: {
10411
+ ...detail.session,
10412
+ status: override.status,
10413
+ updatedAt: override.finishedAt ?? detail.session.updatedAt
10414
+ }
10415
+ };
10416
+ }
10163
10417
  function getHistoryRunDetail(runId) {
10164
- return historyDetailById[runId] ?? null;
10418
+ const detail = historyDetailById[runId];
10419
+ if (!detail) return null;
10420
+ const override = historyRunStatusOverrides.get(runId);
10421
+ return override ? applyRunStatusOverride(detail, override) : detail;
10165
10422
  }
10166
10423
  function createHistoryResource() {
10167
10424
  return {
@@ -10170,16 +10427,22 @@ function createHistoryResource() {
10170
10427
  domain: "history",
10171
10428
  method: "list",
10172
10429
  plannedEndpoint: "GET /api/orgs/:orgSlug/history"
10173
- }, async () => [...historyRunSeed].sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())),
10430
+ }, async () => [...listHistoryRuns()].sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())),
10174
10431
  get: mock({
10175
10432
  domain: "history",
10176
10433
  method: "get",
10177
10434
  plannedEndpoint: "GET /api/orgs/:orgSlug/history/:runId"
10178
- }, async (runId) => getHistoryRunDetail(runId))
10435
+ }, async (runId) => getHistoryRunDetail(runId)),
10436
+ /** Cancel a running/queued run. Returns the updated run (or null if unknown). */
10437
+ cancel: mock({
10438
+ domain: "history",
10439
+ method: "cancel",
10440
+ plannedEndpoint: "POST /api/orgs/:orgSlug/history/:runId/cancel"
10441
+ }, async (runId) => cancelHistoryRun(runId))
10179
10442
  };
10180
10443
  }
10181
- const MOCK_STORAGE_KEY$2 = "keystroke:mock-organization-sidebar-branding:v1";
10182
- function getBrowserLocalStorage$2() {
10444
+ const MOCK_STORAGE_KEY$3 = "keystroke:mock-organization-sidebar-branding:v1";
10445
+ function getBrowserLocalStorage$3() {
10183
10446
  if (typeof globalThis !== "object") return null;
10184
10447
  return globalThis.localStorage ?? null;
10185
10448
  }
@@ -10222,10 +10485,10 @@ function mergeOrganizationSidebarBranding(current, patch) {
10222
10485
  });
10223
10486
  }
10224
10487
  function readBrandingStore() {
10225
- const localStorage = getBrowserLocalStorage$2();
10488
+ const localStorage = getBrowserLocalStorage$3();
10226
10489
  if (!localStorage) return {};
10227
10490
  try {
10228
- const raw = localStorage.getItem(MOCK_STORAGE_KEY$2);
10491
+ const raw = localStorage.getItem(MOCK_STORAGE_KEY$3);
10229
10492
  if (!raw) return {};
10230
10493
  const parsed = JSON.parse(raw);
10231
10494
  if (!parsed || typeof parsed !== "object") return {};
@@ -10235,10 +10498,10 @@ function readBrandingStore() {
10235
10498
  }
10236
10499
  }
10237
10500
  function writeBrandingStore(store) {
10238
- const localStorage = getBrowserLocalStorage$2();
10501
+ const localStorage = getBrowserLocalStorage$3();
10239
10502
  if (!localStorage) return;
10240
10503
  try {
10241
- localStorage.setItem(MOCK_STORAGE_KEY$2, JSON.stringify(store));
10504
+ localStorage.setItem(MOCK_STORAGE_KEY$3, JSON.stringify(store));
10242
10505
  } catch {}
10243
10506
  }
10244
10507
  function resolveOrganizationId(getActiveOrganizationId) {
@@ -10272,8 +10535,8 @@ function createOrganizationSidebarBrandingResource(options) {
10272
10535
  })
10273
10536
  };
10274
10537
  }
10275
- const MOCK_STORAGE_KEY$1 = "keystroke:mock-user-preferences:v1";
10276
- function getBrowserLocalStorage$1() {
10538
+ const MOCK_STORAGE_KEY$2 = "keystroke:mock-user-preferences:v1";
10539
+ function getBrowserLocalStorage$2() {
10277
10540
  if (typeof globalThis !== "object") return null;
10278
10541
  return globalThis.localStorage ?? null;
10279
10542
  }
@@ -10381,10 +10644,10 @@ function mergeUserPreferences(current, patch) {
10381
10644
  });
10382
10645
  }
10383
10646
  function readPreferencesStore() {
10384
- const localStorage = getBrowserLocalStorage$1();
10647
+ const localStorage = getBrowserLocalStorage$2();
10385
10648
  if (!localStorage) return {};
10386
10649
  try {
10387
- const raw = localStorage.getItem(MOCK_STORAGE_KEY$1);
10650
+ const raw = localStorage.getItem(MOCK_STORAGE_KEY$2);
10388
10651
  if (!raw) return {};
10389
10652
  const parsed = JSON.parse(raw);
10390
10653
  if (!parsed || typeof parsed !== "object") return {};
@@ -10395,10 +10658,10 @@ function readPreferencesStore() {
10395
10658
  }
10396
10659
  }
10397
10660
  function writePreferencesStore(store) {
10398
- const localStorage = getBrowserLocalStorage$1();
10661
+ const localStorage = getBrowserLocalStorage$2();
10399
10662
  if (!localStorage) return;
10400
10663
  try {
10401
- localStorage.setItem(MOCK_STORAGE_KEY$1, JSON.stringify(store));
10664
+ localStorage.setItem(MOCK_STORAGE_KEY$2, JSON.stringify(store));
10402
10665
  } catch {}
10403
10666
  }
10404
10667
  function resolveUserId$1(getCurrentUserId) {
@@ -10433,8 +10696,8 @@ function createUserPreferencesResource(options) {
10433
10696
  };
10434
10697
  }
10435
10698
  const DEFAULT_USER_AVATAR = { dataUrl: null };
10436
- const MOCK_STORAGE_KEY = "keystroke:mock-user-avatar:v1";
10437
- function getBrowserLocalStorage() {
10699
+ const MOCK_STORAGE_KEY$1 = "keystroke:mock-user-avatar:v1";
10700
+ function getBrowserLocalStorage$1() {
10438
10701
  if (typeof globalThis !== "object") return null;
10439
10702
  return globalThis.localStorage ?? null;
10440
10703
  }
@@ -10444,10 +10707,10 @@ function normalizeUserAvatar(value) {
10444
10707
  return { dataUrl: typeof dataUrl === "string" && dataUrl.length > 0 ? dataUrl : null };
10445
10708
  }
10446
10709
  function readAvatarStore() {
10447
- const localStorage = getBrowserLocalStorage();
10710
+ const localStorage = getBrowserLocalStorage$1();
10448
10711
  if (!localStorage) return {};
10449
10712
  try {
10450
- const raw = localStorage.getItem(MOCK_STORAGE_KEY);
10713
+ const raw = localStorage.getItem(MOCK_STORAGE_KEY$1);
10451
10714
  if (!raw) return {};
10452
10715
  const parsed = JSON.parse(raw);
10453
10716
  if (!parsed || typeof parsed !== "object") return {};
@@ -10457,10 +10720,10 @@ function readAvatarStore() {
10457
10720
  }
10458
10721
  }
10459
10722
  function writeAvatarStore(store) {
10460
- const localStorage = getBrowserLocalStorage();
10723
+ const localStorage = getBrowserLocalStorage$1();
10461
10724
  if (!localStorage) return;
10462
10725
  try {
10463
- localStorage.setItem(MOCK_STORAGE_KEY, JSON.stringify(store));
10726
+ localStorage.setItem(MOCK_STORAGE_KEY$1, JSON.stringify(store));
10464
10727
  } catch {}
10465
10728
  }
10466
10729
  function resolveUserId(getCurrentUserId) {
@@ -10494,6 +10757,558 @@ function createUserAvatarResource(options) {
10494
10757
  })
10495
10758
  };
10496
10759
  }
10760
+ const CHANNEL_PLATFORMS = [
10761
+ {
10762
+ key: "slack",
10763
+ name: "Slack",
10764
+ appLogoId: "slack",
10765
+ fallbackDomain: "slack.com",
10766
+ description: "Connect a Slack workspace so this agent can respond in channels, threads, and DMs.",
10767
+ tagline: "Respond in channels, threads, and DMs.",
10768
+ authKind: "oauth",
10769
+ credentialFields: [],
10770
+ capabilities: {
10771
+ mentions: true,
10772
+ reactions: "full",
10773
+ cards: "full",
10774
+ modals: true,
10775
+ streaming: "native",
10776
+ dms: true
10777
+ },
10778
+ supportedModes: [
10779
+ "mention",
10780
+ "all",
10781
+ "dm"
10782
+ ],
10783
+ resourceNoun: {
10784
+ singular: "channel",
10785
+ plural: "channels"
10786
+ },
10787
+ webhookPathHint: "/gateways/slack",
10788
+ supportsChannelDirectory: true,
10789
+ docsUrl: "https://chat-sdk.dev/adapters/official/slack"
10790
+ },
10791
+ {
10792
+ key: "discord",
10793
+ name: "Discord",
10794
+ appLogoId: "discord",
10795
+ fallbackDomain: "discord.com",
10796
+ description: "Connect a Discord application so this agent can respond in servers and channels.",
10797
+ tagline: "Respond in servers and channels.",
10798
+ authKind: "credentials",
10799
+ credentialFields: [
10800
+ {
10801
+ key: "botToken",
10802
+ label: "Bot token",
10803
+ placeholder: "Bot token from the Discord developer portal",
10804
+ helpText: "Bot → Reset Token. Shown only once.",
10805
+ type: "password",
10806
+ required: true
10807
+ },
10808
+ {
10809
+ key: "applicationId",
10810
+ label: "Application ID",
10811
+ placeholder: "Application ID",
10812
+ type: "text",
10813
+ required: true
10814
+ },
10815
+ {
10816
+ key: "publicKey",
10817
+ label: "Public key",
10818
+ placeholder: "Public key",
10819
+ helpText: "General Information → Public Key, used to verify interactions.",
10820
+ type: "text",
10821
+ required: true
10822
+ }
10823
+ ],
10824
+ capabilities: {
10825
+ mentions: true,
10826
+ reactions: "full",
10827
+ cards: "full",
10828
+ modals: false,
10829
+ streaming: "post-edit",
10830
+ dms: true
10831
+ },
10832
+ supportedModes: [
10833
+ "mention",
10834
+ "all",
10835
+ "dm"
10836
+ ],
10837
+ resourceNoun: {
10838
+ singular: "channel",
10839
+ plural: "channels"
10840
+ },
10841
+ webhookPathHint: "/gateways/discord",
10842
+ supportsChannelDirectory: true,
10843
+ docsUrl: "https://chat-sdk.dev/adapters/official/discord"
10844
+ },
10845
+ {
10846
+ key: "telegram",
10847
+ name: "Telegram",
10848
+ appLogoId: "telegram",
10849
+ fallbackDomain: "telegram.org",
10850
+ description: "Connect a Telegram bot so this agent can respond in chats and groups.",
10851
+ tagline: "Respond in chats and groups.",
10852
+ authKind: "bot-token",
10853
+ credentialFields: [{
10854
+ key: "botToken",
10855
+ label: "Bot token",
10856
+ placeholder: "123456:ABC-DEF…",
10857
+ helpText: "Create a bot with @BotFather and paste its token.",
10858
+ type: "password",
10859
+ required: true
10860
+ }, {
10861
+ key: "botUsername",
10862
+ label: "Bot username",
10863
+ placeholder: "my_agent_bot",
10864
+ type: "text",
10865
+ required: false,
10866
+ prefix: "@"
10867
+ }],
10868
+ capabilities: {
10869
+ mentions: true,
10870
+ reactions: "full",
10871
+ cards: "partial",
10872
+ modals: false,
10873
+ streaming: "drafts",
10874
+ dms: true
10875
+ },
10876
+ supportedModes: [
10877
+ "mention",
10878
+ "all",
10879
+ "dm"
10880
+ ],
10881
+ resourceNoun: {
10882
+ singular: "chat",
10883
+ plural: "chats"
10884
+ },
10885
+ webhookPathHint: "/gateways/telegram",
10886
+ supportsChannelDirectory: false,
10887
+ docsUrl: "https://chat-sdk.dev/adapters/official/telegram"
10888
+ },
10889
+ {
10890
+ key: "microsoft-teams",
10891
+ name: "Microsoft Teams",
10892
+ appLogoId: "microsoft-teams",
10893
+ fallbackDomain: "teams.microsoft.com",
10894
+ description: "Connect a Microsoft Teams app so this agent can respond in channels and chats.",
10895
+ tagline: "Respond in channels and chats.",
10896
+ authKind: "credentials",
10897
+ credentialFields: [
10898
+ {
10899
+ key: "appId",
10900
+ label: "App ID",
10901
+ placeholder: "Azure AD application (client) ID",
10902
+ type: "text",
10903
+ required: true
10904
+ },
10905
+ {
10906
+ key: "appPassword",
10907
+ label: "App password",
10908
+ placeholder: "Client secret",
10909
+ type: "password",
10910
+ required: true
10911
+ },
10912
+ {
10913
+ key: "tenantId",
10914
+ label: "Tenant ID",
10915
+ placeholder: "Azure AD tenant ID",
10916
+ type: "text",
10917
+ required: true
10918
+ }
10919
+ ],
10920
+ capabilities: {
10921
+ mentions: true,
10922
+ reactions: "read-only",
10923
+ cards: "full",
10924
+ modals: true,
10925
+ streaming: "buffered",
10926
+ dms: true
10927
+ },
10928
+ supportedModes: [
10929
+ "mention",
10930
+ "all",
10931
+ "dm"
10932
+ ],
10933
+ resourceNoun: {
10934
+ singular: "channel",
10935
+ plural: "channels"
10936
+ },
10937
+ webhookPathHint: "/gateways/teams",
10938
+ supportsChannelDirectory: true,
10939
+ docsUrl: "https://chat-sdk.dev/adapters/official/teams"
10940
+ },
10941
+ {
10942
+ key: "google-chat",
10943
+ name: "Google Chat",
10944
+ appLogoId: "google-chat",
10945
+ fallbackDomain: "chat.google.com",
10946
+ description: "Connect a Google Chat app so this agent can respond in spaces and direct messages.",
10947
+ tagline: "Respond in spaces and DMs.",
10948
+ authKind: "credentials",
10949
+ credentialFields: [{
10950
+ key: "serviceAccountJson",
10951
+ label: "Service account JSON",
10952
+ placeholder: "Paste the service account key JSON",
10953
+ helpText: "IAM → Service Accounts → Keys → Add key → JSON.",
10954
+ type: "textarea",
10955
+ required: true
10956
+ }, {
10957
+ key: "projectNumber",
10958
+ label: "GCP project number",
10959
+ placeholder: "Project number used to verify webhooks",
10960
+ type: "text",
10961
+ required: true
10962
+ }],
10963
+ capabilities: {
10964
+ mentions: true,
10965
+ reactions: "full",
10966
+ cards: "full",
10967
+ modals: false,
10968
+ streaming: "post-edit",
10969
+ dms: true
10970
+ },
10971
+ supportedModes: [
10972
+ "mention",
10973
+ "all",
10974
+ "dm"
10975
+ ],
10976
+ resourceNoun: {
10977
+ singular: "space",
10978
+ plural: "spaces"
10979
+ },
10980
+ webhookPathHint: "/gateways/gchat",
10981
+ supportsChannelDirectory: true,
10982
+ docsUrl: "https://chat-sdk.dev/adapters/official/google-chat"
10983
+ }
10984
+ ];
10985
+ function getChannelPlatform(key) {
10986
+ return CHANNEL_PLATFORMS.find((platform) => platform.key === key);
10987
+ }
10988
+ const CHANNEL_DIRECTORY = {
10989
+ slack: [
10990
+ {
10991
+ id: "C0SUPPORT",
10992
+ name: "support",
10993
+ kind: "public",
10994
+ memberCount: 48
10995
+ },
10996
+ {
10997
+ id: "C0GENERAL",
10998
+ name: "general",
10999
+ kind: "public",
11000
+ memberCount: 132
11001
+ },
11002
+ {
11003
+ id: "C0ENG",
11004
+ name: "engineering",
11005
+ kind: "public",
11006
+ memberCount: 27
11007
+ },
11008
+ {
11009
+ id: "C0INCIDENTS",
11010
+ name: "incidents",
11011
+ kind: "private",
11012
+ memberCount: 12
11013
+ },
11014
+ {
11015
+ id: "C0RANDOM",
11016
+ name: "random",
11017
+ kind: "public",
11018
+ memberCount: 118
11019
+ },
11020
+ {
11021
+ id: "C0ARCHIVE",
11022
+ name: "launch-2025",
11023
+ kind: "public",
11024
+ memberCount: 9,
11025
+ isArchived: true
11026
+ }
11027
+ ],
11028
+ discord: [
11029
+ {
11030
+ id: "d-general",
11031
+ name: "general",
11032
+ kind: "public",
11033
+ memberCount: 540
11034
+ },
11035
+ {
11036
+ id: "d-help",
11037
+ name: "help",
11038
+ kind: "public",
11039
+ memberCount: 312
11040
+ },
11041
+ {
11042
+ id: "d-dev",
11043
+ name: "dev",
11044
+ kind: "public",
11045
+ memberCount: 76
11046
+ },
11047
+ {
11048
+ id: "d-staff",
11049
+ name: "staff",
11050
+ kind: "private",
11051
+ memberCount: 8
11052
+ }
11053
+ ],
11054
+ telegram: [],
11055
+ "microsoft-teams": [
11056
+ {
11057
+ id: "t-general",
11058
+ name: "General",
11059
+ kind: "public",
11060
+ memberCount: 64
11061
+ },
11062
+ {
11063
+ id: "t-support",
11064
+ name: "Customer Support",
11065
+ kind: "public",
11066
+ memberCount: 22
11067
+ },
11068
+ {
11069
+ id: "t-leads",
11070
+ name: "Leadership",
11071
+ kind: "private",
11072
+ memberCount: 6
11073
+ }
11074
+ ],
11075
+ "google-chat": [
11076
+ {
11077
+ id: "spaces/AAA-support",
11078
+ name: "Support",
11079
+ kind: "space",
11080
+ memberCount: 31
11081
+ },
11082
+ {
11083
+ id: "spaces/AAA-eng",
11084
+ name: "Engineering",
11085
+ kind: "space",
11086
+ memberCount: 44
11087
+ },
11088
+ {
11089
+ id: "spaces/AAA-ops",
11090
+ name: "Ops On-call",
11091
+ kind: "space",
11092
+ memberCount: 10
11093
+ }
11094
+ ]
11095
+ };
11096
+ const MOCK_STORAGE_KEY = "keystroke:mock-channels:v1";
11097
+ function getBrowserLocalStorage() {
11098
+ if (typeof globalThis !== "object") return null;
11099
+ return globalThis.localStorage ?? null;
11100
+ }
11101
+ /** A connected Slack workspace with two bindings, so the panel is reviewable. */
11102
+ function seedStore() {
11103
+ return { "agent-support": [{
11104
+ id: "chan-conn-slack-support",
11105
+ platform: "slack",
11106
+ agentId: "agent-support",
11107
+ accountLabel: "Keystroke HQ",
11108
+ accountId: "T0KEYSTROKE",
11109
+ connectedAt: "2026-05-28T15:30:00Z",
11110
+ bindings: [{
11111
+ id: "chan-bind-support",
11112
+ channelId: "C0SUPPORT",
11113
+ channelName: "support",
11114
+ channelKind: "public",
11115
+ mode: "all",
11116
+ subscribeOnMention: true,
11117
+ createdAt: "2026-05-28T15:31:00Z",
11118
+ updatedAt: "2026-05-28T15:31:00Z"
11119
+ }, {
11120
+ id: "chan-bind-incidents",
11121
+ channelId: "C0INCIDENTS",
11122
+ channelName: "incidents",
11123
+ channelKind: "private",
11124
+ mode: "mention",
11125
+ subscribeOnMention: true,
11126
+ createdAt: "2026-05-29T09:05:00Z",
11127
+ updatedAt: "2026-05-29T09:05:00Z"
11128
+ }]
11129
+ }] };
11130
+ }
11131
+ function readStore() {
11132
+ const localStorage = getBrowserLocalStorage();
11133
+ if (!localStorage) return seedStore();
11134
+ try {
11135
+ const raw = localStorage.getItem(MOCK_STORAGE_KEY);
11136
+ if (!raw) {
11137
+ const seeded = seedStore();
11138
+ localStorage.setItem(MOCK_STORAGE_KEY, JSON.stringify(seeded));
11139
+ return seeded;
11140
+ }
11141
+ const parsed = JSON.parse(raw);
11142
+ if (!parsed || typeof parsed !== "object") return {};
11143
+ return parsed;
11144
+ } catch {
11145
+ return {};
11146
+ }
11147
+ }
11148
+ function writeStore(store) {
11149
+ const localStorage = getBrowserLocalStorage();
11150
+ if (!localStorage) return;
11151
+ try {
11152
+ localStorage.setItem(MOCK_STORAGE_KEY, JSON.stringify(store));
11153
+ } catch {}
11154
+ }
11155
+ function newId(prefix) {
11156
+ const cryptoRef = globalThis.crypto;
11157
+ if (cryptoRef?.randomUUID) return `${prefix}-${cryptoRef.randomUUID()}`;
11158
+ return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
11159
+ }
11160
+ function requireAgentId(agentId) {
11161
+ const trimmed = agentId.trim();
11162
+ if (!trimmed) throw new Error("Channels require an agent id");
11163
+ return trimmed;
11164
+ }
11165
+ function clone(value) {
11166
+ return JSON.parse(JSON.stringify(value));
11167
+ }
11168
+ function defaultAccountLabel(platform) {
11169
+ const meta = getChannelPlatform(platform);
11170
+ return meta ? `${meta.name} workspace` : platform;
11171
+ }
11172
+ function findConnection(connections, connectionId) {
11173
+ const connection = connections.find((entry) => entry.id === connectionId);
11174
+ if (!connection) throw new Error(`Channel connection not found: ${connectionId}`);
11175
+ return connection;
11176
+ }
11177
+ function toBinding(input, now) {
11178
+ return {
11179
+ id: newId("chan-bind"),
11180
+ channelId: input.channelId,
11181
+ channelName: input.channelName,
11182
+ channelKind: input.channelKind ?? "public",
11183
+ mode: input.mode ?? "mention",
11184
+ subscribeOnMention: input.subscribeOnMention ?? true,
11185
+ createdAt: now,
11186
+ updatedAt: now
11187
+ };
11188
+ }
11189
+ function createChannelsResource() {
11190
+ return {
11191
+ /** The supported-platform catalog (static; backend may derive from the adapter registry). */
11192
+ listPlatforms: mock({
11193
+ domain: "channels",
11194
+ method: "listPlatforms",
11195
+ plannedEndpoint: "GET /api/channels/platforms"
11196
+ }, async () => clone(CHANNEL_PLATFORMS)),
11197
+ /** Connections (with their channel bindings) for one agent. */
11198
+ listForAgent: mock({
11199
+ domain: "channels",
11200
+ method: "listForAgent",
11201
+ plannedEndpoint: "GET /api/agents/:agentId/channels"
11202
+ }, async (agentId) => {
11203
+ const id = requireAgentId(agentId);
11204
+ return clone(readStore()[id] ?? []);
11205
+ }),
11206
+ /**
11207
+ * Channel directory for a platform — the picker source. NET-NEW vs. the
11208
+ * current backend (no channel-list endpoint exists). The runtime team needs
11209
+ * to expose this (e.g. Slack `conversations.list`) for a real picker.
11210
+ */
11211
+ listChannels: mock({
11212
+ domain: "channels",
11213
+ method: "listChannels",
11214
+ plannedEndpoint: "GET /api/channels/:platform/directory"
11215
+ }, async (platform) => clone(CHANNEL_DIRECTORY[platform] ?? [])),
11216
+ /** Connect a platform for an agent (OAuth install or credential entry), optionally with a first binding. */
11217
+ connect: mock({
11218
+ domain: "channels",
11219
+ method: "connect",
11220
+ plannedEndpoint: "POST /api/agents/:agentId/channels"
11221
+ }, async (input) => {
11222
+ const agentId = requireAgentId(input.agentId);
11223
+ const store = readStore();
11224
+ const now = (/* @__PURE__ */ new Date()).toISOString();
11225
+ const connection = {
11226
+ id: newId("chan-conn"),
11227
+ platform: input.platform,
11228
+ agentId,
11229
+ accountLabel: input.accountLabel ?? defaultAccountLabel(input.platform),
11230
+ accountId: newId("acct").toUpperCase(),
11231
+ connectedAt: now,
11232
+ bindings: input.initialBinding ? [toBinding(input.initialBinding, now)] : []
11233
+ };
11234
+ writeStore({
11235
+ ...store,
11236
+ [agentId]: [...store[agentId] ?? [], connection]
11237
+ });
11238
+ return clone(connection);
11239
+ }),
11240
+ /** Add a channel binding to an existing connection. */
11241
+ addBinding: mock({
11242
+ domain: "channels",
11243
+ method: "addBinding",
11244
+ plannedEndpoint: "PUT /gateways/:platform/attachments/:channelId"
11245
+ }, async (input) => {
11246
+ const agentId = requireAgentId(input.agentId);
11247
+ const store = readStore();
11248
+ const connections = store[agentId] ?? [];
11249
+ const connection = findConnection(connections, input.connectionId);
11250
+ const now = (/* @__PURE__ */ new Date()).toISOString();
11251
+ connection.bindings = [...connection.bindings, toBinding(input.binding, now)];
11252
+ writeStore({
11253
+ ...store,
11254
+ [agentId]: connections
11255
+ });
11256
+ return clone(connection);
11257
+ }),
11258
+ /** Update a binding's listen mode / subscribe flag. */
11259
+ updateBinding: mock({
11260
+ domain: "channels",
11261
+ method: "updateBinding",
11262
+ plannedEndpoint: "PUT /gateways/:platform/attachments/:channelId"
11263
+ }, async (input) => {
11264
+ const agentId = requireAgentId(input.agentId);
11265
+ const store = readStore();
11266
+ const connections = store[agentId] ?? [];
11267
+ const connection = findConnection(connections, input.connectionId);
11268
+ const binding = connection.bindings.find((entry) => entry.id === input.bindingId);
11269
+ if (!binding) throw new Error(`Channel binding not found: ${input.bindingId}`);
11270
+ if (input.patch.mode !== void 0) binding.mode = input.patch.mode;
11271
+ if (input.patch.subscribeOnMention !== void 0) binding.subscribeOnMention = input.patch.subscribeOnMention;
11272
+ binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
11273
+ writeStore({
11274
+ ...store,
11275
+ [agentId]: connections
11276
+ });
11277
+ return clone(connection);
11278
+ }),
11279
+ /** Remove a channel binding. */
11280
+ removeBinding: mock({
11281
+ domain: "channels",
11282
+ method: "removeBinding",
11283
+ plannedEndpoint: "DELETE /gateways/:platform/attachments/:channelId"
11284
+ }, async (input) => {
11285
+ const agentId = requireAgentId(input.agentId);
11286
+ const store = readStore();
11287
+ const connections = store[agentId] ?? [];
11288
+ const connection = findConnection(connections, input.connectionId);
11289
+ connection.bindings = connection.bindings.filter((entry) => entry.id !== input.bindingId);
11290
+ writeStore({
11291
+ ...store,
11292
+ [agentId]: connections
11293
+ });
11294
+ return clone(connection);
11295
+ }),
11296
+ /** Disconnect the agent from a platform (removes the connection + its bindings). */
11297
+ disconnect: mock({
11298
+ domain: "channels",
11299
+ method: "disconnect",
11300
+ plannedEndpoint: "DELETE /api/agents/:agentId/channels/:connectionId"
11301
+ }, async (input) => {
11302
+ const agentId = requireAgentId(input.agentId);
11303
+ const store = readStore();
11304
+ const next = (store[agentId] ?? []).filter((entry) => entry.id !== input.connectionId);
11305
+ writeStore({
11306
+ ...store,
11307
+ [agentId]: next
11308
+ });
11309
+ })
11310
+ };
11311
+ }
10497
11312
  function createPlatformClient(options) {
10498
11313
  const auth = options.auth ?? { type: "none" };
10499
11314
  const base = normalizeBaseUrl(options.baseUrl);
@@ -10530,6 +11345,7 @@ function createPlatformClient(options) {
10530
11345
  deployments: createDeploymentsResource(),
10531
11346
  projectMetrics: createProjectMetricsResource(),
10532
11347
  projectFiles: createProjectFilesResource(),
11348
+ channels: createChannelsResource(),
10533
11349
  members: createMembersResource(http),
10534
11350
  invitations: createInvitationsResource(http),
10535
11351
  apiKeys: createApiKeysResource(),
@@ -12857,11 +13673,8 @@ function registerBuildCommand(program) {
12857
13673
  program.command("build").description("Build the keystroke project for production").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
12858
13674
  try {
12859
13675
  const root = resolveProjectRoot(options.dir);
12860
- const { buildApp } = await import("./dist-CrZVpQSd.mjs");
12861
- await buildApp({
12862
- root,
12863
- emitRouteManifest: false
12864
- });
13676
+ const { buildApp } = await import("./dist-nRx_8hnk.mjs");
13677
+ await buildApp({ root });
12865
13678
  process.stdout.write(`Built ${root}\n`);
12866
13679
  } catch (error) {
12867
13680
  const message = error instanceof Error ? error.message : "Build failed";
@@ -12916,11 +13729,8 @@ async function runDeploy(options) {
12916
13729
  const config = createCliConfig();
12917
13730
  const client = createCliPlatformClient(config);
12918
13731
  if (!options.skipBuild) {
12919
- const { buildApp } = await import("./dist-CrZVpQSd.mjs");
12920
- await buildApp({
12921
- root,
12922
- emitRouteManifest: false
12923
- });
13732
+ const { buildApp } = await import("./dist-nRx_8hnk.mjs");
13733
+ await buildApp({ root });
12924
13734
  }
12925
13735
  const archive = packProjectArtifact(root);
12926
13736
  let active = await client.organizations.getActive();
@@ -13010,7 +13820,7 @@ function runtimeChildEnv(parentEnv, overrides) {
13010
13820
  //#region src/project/bootstrap-run.ts
13011
13821
  /** Node args + env for `@keystrokehq/build` bootstrap (shared by start + dev). */
13012
13822
  async function resolveBootstrapRun(options) {
13013
- const { resolveRuntimeBuildArtifact } = await import("./dist-CrZVpQSd.mjs");
13823
+ const { resolveRuntimeBuildArtifact } = await import("./dist-nRx_8hnk.mjs");
13014
13824
  const loader = pathToFileURL(resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/runtime-loader.mjs")).href;
13015
13825
  const bootstrap = resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/bootstrap.mjs");
13016
13826
  const args = [`--import=${loader}`];
@@ -13175,7 +13985,7 @@ async function runDev(options) {
13175
13985
  process.on("SIGINT", shutdown);
13176
13986
  process.on("SIGTERM", shutdown);
13177
13987
  try {
13178
- const { watchApp } = await import("./dist-CrZVpQSd.mjs");
13988
+ const { watchApp } = await import("./dist-nRx_8hnk.mjs");
13179
13989
  await watchApp({
13180
13990
  root,
13181
13991
  clean: false,
@@ -13562,11 +14372,10 @@ async function runStart(options) {
13562
14372
  const apiPort = Number(new URL(serverUrl).port || 80);
13563
14373
  const runtimeNodeModules = resolveCliRuntimeNodeModules(resolveCliRoot(import.meta.url));
13564
14374
  ensureNativeDeps(runtimeNodeModules);
13565
- const { buildApp } = await import("./dist-CrZVpQSd.mjs");
14375
+ const { buildApp } = await import("./dist-nRx_8hnk.mjs");
13566
14376
  await buildApp({
13567
14377
  root,
13568
- clean: false,
13569
- emitRouteManifest: false
14378
+ clean: false
13570
14379
  });
13571
14380
  const processes = [{
13572
14381
  name: "api",