@canonry/canonry 5.10.0 → 5.12.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.
Files changed (33) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +13 -2
  2. package/assets/assets/{AuditHistoryPanel-C1mws7FA.js → AuditHistoryPanel-DL_NSpfm.js} +1 -1
  3. package/assets/assets/{BacklinksPage-DP9BUn0y.js → BacklinksPage-BtcBybtX.js} +1 -1
  4. package/assets/assets/{HistoryPage-Bmo40fKZ.js → HistoryPage-jXj-qi9c.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-DyqZVFEx.js → MeasurementPropertyPage-DKw401ok.js} +1 -1
  6. package/assets/assets/{ProjectPage-DBJvI-H1.js → ProjectPage-og4qfFFD.js} +1 -1
  7. package/assets/assets/{RunRow-BSZcQ0v6.js → RunRow-nK5iaZe1.js} +1 -1
  8. package/assets/assets/{RunsPage-CIkLV-XY.js → RunsPage-CMgr3YSF.js} +1 -1
  9. package/assets/assets/{SettingsPage-TJ6l-L5i.js → SettingsPage-QtElCcew.js} +1 -1
  10. package/assets/assets/{SiteHealthSection-CKvDNoSp.js → SiteHealthSection-DNdVVl30.js} +3 -3
  11. package/assets/assets/{TrafficPage-BNaCL4v4.js → TrafficPage-C0WgvfRj.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-WQu7Tf56.js → TrafficSourceDetailPage-jOTedQtt.js} +1 -1
  13. package/assets/assets/{extract-error-message-DMgow2EC.js → extract-error-message-DO_053o0.js} +1 -1
  14. package/assets/assets/index-CnVwWE3V.js +86 -0
  15. package/assets/assets/{index-DQw9KQwS.css → index-EJC8PSbh.css} +1 -1
  16. package/assets/assets/{react-sigma_core.esm.min-YECPUDRj.js → react-sigma_core.esm.min-DJttq2UK.js} +1 -1
  17. package/assets/assets/{v2-overview-adapter-BFf9y-xb.js → v2-overview-adapter-BC_oa__K.js} +1 -1
  18. package/assets/assets/{vendor-lucide-2cuPDw3H.js → vendor-lucide-N6vOpZmh.js} +1 -1
  19. package/assets/index.html +3 -3
  20. package/dist/{chunk-KBPSOB55.js → chunk-DOC4QQLS.js} +2110 -1871
  21. package/dist/{chunk-W67ORZUR.js → chunk-KBSAEALE.js} +123 -32
  22. package/dist/{chunk-S7D23FEE.js → chunk-LWGYRN3T.js} +2 -2
  23. package/dist/{chunk-KEMMVSAQ.js → chunk-NYRRWWEE.js} +50 -2
  24. package/dist/{chunk-HF6MAKWE.js → chunk-S4BIBUCK.js} +2 -2
  25. package/dist/{chunk-GQ37ALYP.js → chunk-WJGUBZSW.js} +209 -52
  26. package/dist/cli.js +106 -14
  27. package/dist/{demo-server-DKH73GRD.js → demo-server-OTXS6ZHS.js} +3 -3
  28. package/dist/index.d.ts +28 -1
  29. package/dist/index.js +5 -5
  30. package/dist/{intelligence-service-7PVUFJTX.js → intelligence-service-K4U55A2J.js} +2 -2
  31. package/dist/mcp.js +3 -3
  32. package/package.json +13 -13
  33. package/assets/assets/index-BHIRnvQi.js +0 -86
@@ -4,6 +4,7 @@ import {
4
4
  import {
5
5
  AGENT_MEMORY_KEY_MAX_LENGTH,
6
6
  AGENT_MEMORY_VALUE_MAX_BYTES,
7
+ AGENT_PROVIDER_IDS,
7
8
  CANONRY_NPM_PACKAGE_URL,
8
9
  CodingAgents,
9
10
  DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
@@ -26,7 +27,9 @@ import {
26
27
  adsOperationReconcileRequestSchema,
27
28
  adsPauseRequestSchema,
28
29
  adsUnresolvedOperationListQuerySchema,
30
+ agentConversationCreateSchema,
29
31
  agentPluginClientLabel,
32
+ agentProviderIdSchema,
30
33
  apiKeyDtoSchema,
31
34
  backlinkSourceSchema,
32
35
  canonicalizeGtmAccountId,
@@ -130,7 +133,7 @@ import {
130
133
  upgradeCaveatFor,
131
134
  upgradeCommandFor,
132
135
  visibilityReportRequestSchema
133
- } from "./chunk-KEMMVSAQ.js";
136
+ } from "./chunk-NYRRWWEE.js";
134
137
 
135
138
  // src/cli-error.ts
136
139
  function isMachineFormat(format) {
@@ -205,6 +208,8 @@ var dashboardManagedSweepsSchema = z.boolean().nullish();
205
208
  var researchAllowViewersSchema = z.boolean().nullish();
206
209
  var researchViewerDailyRunLimitSchema = z.number().int().positive().nullish();
207
210
  var dashboardManagedRunKindsSchema = z.array(schedulableRunKindSchema).nullish();
211
+ var agentProviderSchema = agentProviderIdSchema.nullish();
212
+ var agentModelSchema = z.string().trim().min(1).nullish();
208
213
  function resolveOperatorApiKeyIds(source) {
209
214
  const raw = source.CANONRY_OPERATOR_KEY_IDS?.trim();
210
215
  if (!raw) return [];
@@ -459,6 +464,24 @@ Do not use "canonry init --force" for recovery. It replaces credentials without
459
464
  message: `Invalid config at ${configPath}: research.viewerDailyRunLimit must be a positive integer or left blank.`
460
465
  });
461
466
  }
467
+ if (!agentProviderSchema.safeParse(parsed.agent?.provider).success) {
468
+ throw new CliError({
469
+ code: "CONFIG_INVALID",
470
+ message: `Invalid config at ${configPath}: agent.provider must be one of ${AGENT_PROVIDER_IDS.join(", ")}, or left blank.`
471
+ });
472
+ }
473
+ if (!agentModelSchema.safeParse(parsed.agent?.model).success) {
474
+ throw new CliError({
475
+ code: "CONFIG_INVALID",
476
+ message: `Invalid config at ${configPath}: agent.model must be a non-empty string or left blank.`
477
+ });
478
+ }
479
+ if (parsed.agent?.model && !parsed.agent?.provider) {
480
+ throw new CliError({
481
+ code: "CONFIG_INVALID",
482
+ message: `Invalid config at ${configPath}: agent.model requires agent.provider \u2014 a model id is only meaningful against the provider that serves it.`
483
+ });
484
+ }
462
485
  if (parsed.geminiApiKey && !parsed.providers?.gemini) {
463
486
  parsed.providers = {
464
487
  ...parsed.providers,
@@ -624,56 +647,6 @@ function configExists() {
624
647
  return fs.existsSync(getConfigPath());
625
648
  }
626
649
 
627
- // src/client.ts
628
- import { AsyncLocalStorage } from "async_hooks";
629
-
630
- // src/client-reliability.ts
631
- function parseRetryAfterMs(header, now = Date.now()) {
632
- if (!header) return void 0;
633
- const value = header.trim();
634
- if (!value) return void 0;
635
- if (/^\d+(?:\.\d+)?$/.test(value)) {
636
- const delayMs2 = Number(value) * 1e3;
637
- return Number.isFinite(delayMs2) && delayMs2 >= 0 && delayMs2 <= Number.MAX_SAFE_INTEGER ? delayMs2 : void 0;
638
- }
639
- if (!/^[a-z]{3},\s/i.test(value)) return void 0;
640
- const target = Date.parse(value);
641
- if (!Number.isFinite(target)) return void 0;
642
- const delayMs = Math.max(0, target - now);
643
- return Number.isSafeInteger(delayMs) ? delayMs : void 0;
644
- }
645
- function httpErrorDetails(serverDetails, response) {
646
- const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
647
- const requestId = response.headers.get("x-request-id") ?? response.headers.get("request-id");
648
- return {
649
- ...serverDetails && typeof serverDetails === "object" && !Array.isArray(serverDetails) ? serverDetails : {},
650
- httpStatus: response.status,
651
- ...retryAfterMs === void 0 ? {} : { retryAfterMs },
652
- ...requestId ? { requestId } : {}
653
- };
654
- }
655
- function isConnectionFailure(message) {
656
- return message.includes("fetch failed") || message.includes("ECONNREFUSED") || message.includes("connect ECONNREFUSED");
657
- }
658
- function connectionFailureMessage(target) {
659
- return `Could not connect to canonry server at ${redactRequestTarget(target)}. Check that this URL is reachable and the server is running. For a local Canonry instance, start it with "canonry start" (a background daemon, stopped with "canonry stop"), or "canonry serve" to run it in the foreground.`;
660
- }
661
- function redactRequestTarget(target) {
662
- try {
663
- const url = new URL(target);
664
- url.username = "";
665
- url.password = "";
666
- for (const key of [...url.searchParams.keys()]) {
667
- if (isSensitiveDiagnosticQueryKey(key)) {
668
- url.searchParams.set(key, "<redacted>");
669
- }
670
- }
671
- return url.toString();
672
- } catch {
673
- return target.replace(/\/\/[^/@]*@/, "//");
674
- }
675
- }
676
-
677
650
  // ../api-client-generated/src/generated/core/bodySerializer.gen.ts
678
651
  var jsonBodySerializer = {
679
652
  bodySerializer: (body) => JSON.stringify(
@@ -5939,6 +5912,70 @@ var getApiV1ProjectsByNameTechnicalAeoRunsByRunIdProgress = (options) => {
5939
5912
  ...options
5940
5913
  });
5941
5914
  };
5915
+ var getApiV1ProjectsByNameAgentConversations = (options) => {
5916
+ return (options.client ?? client).get({
5917
+ security: [
5918
+ {
5919
+ scheme: "bearer",
5920
+ type: "http"
5921
+ }
5922
+ ],
5923
+ url: "/api/v1/projects/{name}/agent/conversations",
5924
+ ...options
5925
+ });
5926
+ };
5927
+ var postApiV1ProjectsByNameAgentConversations = (options) => {
5928
+ return (options.client ?? client).post({
5929
+ security: [
5930
+ {
5931
+ scheme: "bearer",
5932
+ type: "http"
5933
+ }
5934
+ ],
5935
+ url: "/api/v1/projects/{name}/agent/conversations",
5936
+ ...options,
5937
+ headers: {
5938
+ "Content-Type": "application/json",
5939
+ ...options.headers
5940
+ }
5941
+ });
5942
+ };
5943
+ var deleteApiV1ProjectsByNameAgentConversationsById = (options) => {
5944
+ return (options.client ?? client).delete({
5945
+ security: [
5946
+ {
5947
+ scheme: "bearer",
5948
+ type: "http"
5949
+ }
5950
+ ],
5951
+ url: "/api/v1/projects/{name}/agent/conversations/{id}",
5952
+ ...options
5953
+ });
5954
+ };
5955
+ var getApiV1ProjectsByNameAgentConversationsById = (options) => {
5956
+ return (options.client ?? client).get({
5957
+ security: [
5958
+ {
5959
+ scheme: "bearer",
5960
+ type: "http"
5961
+ }
5962
+ ],
5963
+ url: "/api/v1/projects/{name}/agent/conversations/{id}",
5964
+ ...options
5965
+ });
5966
+ };
5967
+ var postApiV1ProjectsByNameAgentConversationsByIdResume = (options) => {
5968
+ return (options.client ?? client).post({
5969
+ security: [
5970
+ {
5971
+ scheme: "bearer",
5972
+ type: "http"
5973
+ }
5974
+ ],
5975
+ url: "/api/v1/projects/{name}/agent/conversations/{id}/resume",
5976
+ ...options
5977
+ });
5978
+ };
5942
5979
  var deleteApiV1ProjectsByNameAgentTranscript = (options) => {
5943
5980
  return (options.client ?? client).delete({
5944
5981
  security: [
@@ -6033,6 +6070,56 @@ function createClient2(opts) {
6033
6070
  return createClient(config);
6034
6071
  }
6035
6072
 
6073
+ // src/client.ts
6074
+ import { AsyncLocalStorage } from "async_hooks";
6075
+
6076
+ // src/client-reliability.ts
6077
+ function parseRetryAfterMs(header, now = Date.now()) {
6078
+ if (!header) return void 0;
6079
+ const value = header.trim();
6080
+ if (!value) return void 0;
6081
+ if (/^\d+(?:\.\d+)?$/.test(value)) {
6082
+ const delayMs2 = Number(value) * 1e3;
6083
+ return Number.isFinite(delayMs2) && delayMs2 >= 0 && delayMs2 <= Number.MAX_SAFE_INTEGER ? delayMs2 : void 0;
6084
+ }
6085
+ if (!/^[a-z]{3},\s/i.test(value)) return void 0;
6086
+ const target = Date.parse(value);
6087
+ if (!Number.isFinite(target)) return void 0;
6088
+ const delayMs = Math.max(0, target - now);
6089
+ return Number.isSafeInteger(delayMs) ? delayMs : void 0;
6090
+ }
6091
+ function httpErrorDetails(serverDetails, response) {
6092
+ const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
6093
+ const requestId = response.headers.get("x-request-id") ?? response.headers.get("request-id");
6094
+ return {
6095
+ ...serverDetails && typeof serverDetails === "object" && !Array.isArray(serverDetails) ? serverDetails : {},
6096
+ httpStatus: response.status,
6097
+ ...retryAfterMs === void 0 ? {} : { retryAfterMs },
6098
+ ...requestId ? { requestId } : {}
6099
+ };
6100
+ }
6101
+ function isConnectionFailure(message) {
6102
+ return message.includes("fetch failed") || message.includes("ECONNREFUSED") || message.includes("connect ECONNREFUSED");
6103
+ }
6104
+ function connectionFailureMessage(target) {
6105
+ return `Could not connect to canonry server at ${redactRequestTarget(target)}. Check that this URL is reachable and the server is running. For a local Canonry instance, start it with "canonry start" (a background daemon, stopped with "canonry stop"), or "canonry serve" to run it in the foreground.`;
6106
+ }
6107
+ function redactRequestTarget(target) {
6108
+ try {
6109
+ const url = new URL(target);
6110
+ url.username = "";
6111
+ url.password = "";
6112
+ for (const key of [...url.searchParams.keys()]) {
6113
+ if (isSensitiveDiagnosticQueryKey(key)) {
6114
+ url.searchParams.set(key, "<redacted>");
6115
+ }
6116
+ }
6117
+ return url.toString();
6118
+ } catch {
6119
+ return target.replace(/\/\/[^/@]*@/, "//");
6120
+ }
6121
+ }
6122
+
6036
6123
  // src/client.ts
6037
6124
  function parseServerUpdateAvailable(value) {
6038
6125
  if (!value || typeof value !== "object") return null;
@@ -6212,6 +6299,21 @@ var ApiClient = class {
6212
6299
  // generated SDK and we route them through `invoke()` like every other
6213
6300
  // endpoint. The SSE `POST /agent/prompt` is intentionally not here — it
6214
6301
  // stays on `streamPost()` below since the SDK can't represent SSE cleanly.
6302
+ async listAgentConversations(project, query) {
6303
+ return this.invoke(() => getApiV1ProjectsByNameAgentConversations({ client: this.heyClient, path: { name: project }, query }));
6304
+ }
6305
+ async getAgentConversation(project, id) {
6306
+ return this.invoke(() => getApiV1ProjectsByNameAgentConversationsById({ client: this.heyClient, path: { name: project, id } }));
6307
+ }
6308
+ async createAgentConversation(project, id) {
6309
+ return this.invoke(() => postApiV1ProjectsByNameAgentConversations({ client: this.heyClient, path: { name: project }, body: { id } }));
6310
+ }
6311
+ async resumeAgentConversation(project, id) {
6312
+ return this.invoke(() => postApiV1ProjectsByNameAgentConversationsByIdResume({ client: this.heyClient, path: { name: project, id } }));
6313
+ }
6314
+ async deleteAgentConversation(project, id) {
6315
+ return this.invoke(() => deleteApiV1ProjectsByNameAgentConversationsById({ client: this.heyClient, path: { name: project, id } }));
6316
+ }
6215
6317
  async getAgentTranscript(project) {
6216
6318
  return this.invoke(
6217
6319
  () => getApiV1ProjectsByNameAgentTranscript({ client: this.heyClient, path: { name: project } })
@@ -11784,10 +11886,65 @@ var canonryMcpTools = [
11784
11886
  openApiOperations: ["DELETE /api/v1/projects/{name}/agent/memory"],
11785
11887
  handler: (client2, input) => client2.forgetAgentMemory(input.project, input.key)
11786
11888
  }),
11889
+ defineTool({
11890
+ name: "canonry_agent_conversations_list",
11891
+ title: "List Aero conversations",
11892
+ description: "List Aero conversations. Instance administrators only. New and resume preserve the previous conversation; shared project notes are retained. Creation requires a UUID that must be reused on retries.",
11893
+ access: "read",
11894
+ tier: "agent",
11895
+ inputSchema: projectInputSchema.extend({ offset: z4.number().int().min(0).optional(), limit: z4.number().int().min(1).max(100).optional() }),
11896
+ annotations: readAnnotations(),
11897
+ openApiOperations: ["GET /api/v1/projects/{name}/agent/conversations"],
11898
+ handler: (client2, input) => client2.listAgentConversations(input.project, { offset: input.offset, limit: input.limit })
11899
+ }),
11900
+ defineTool({
11901
+ name: "canonry_agent_conversations_get",
11902
+ title: "Read an Aero conversation",
11903
+ description: "Read an Aero conversation. Instance administrators only. New and resume preserve the previous conversation; shared project notes are retained. Creation requires a UUID that must be reused on retries.",
11904
+ access: "read",
11905
+ tier: "agent",
11906
+ inputSchema: projectInputSchema.extend({ id: z4.string().min(1) }),
11907
+ annotations: readAnnotations(),
11908
+ openApiOperations: ["GET /api/v1/projects/{name}/agent/conversations/{id}"],
11909
+ handler: (client2, input) => client2.getAgentConversation(input.project, input.id)
11910
+ }),
11911
+ defineTool({
11912
+ name: "canonry_agent_conversations_new",
11913
+ title: "Start a new Aero conversation",
11914
+ description: "Start a new Aero conversation. Instance administrators only. New and resume preserve the previous conversation; shared project notes are retained. Creation requires a UUID that must be reused on retries.",
11915
+ access: "write",
11916
+ tier: "agent",
11917
+ inputSchema: projectInputSchema.extend(agentConversationCreateSchema.shape),
11918
+ annotations: writeAnnotations({ idempotentHint: true }),
11919
+ openApiOperations: ["POST /api/v1/projects/{name}/agent/conversations"],
11920
+ handler: (client2, input) => client2.createAgentConversation(input.project, input.id)
11921
+ }),
11922
+ defineTool({
11923
+ name: "canonry_agent_conversations_resume",
11924
+ title: "Resume an Aero conversation",
11925
+ description: "Resume an Aero conversation. Instance administrators only. New and resume preserve the previous conversation; shared project notes are retained. Creation requires a UUID that must be reused on retries.",
11926
+ access: "write",
11927
+ tier: "agent",
11928
+ inputSchema: projectInputSchema.extend({ id: z4.string().min(1) }),
11929
+ annotations: writeAnnotations({ idempotentHint: true }),
11930
+ openApiOperations: ["POST /api/v1/projects/{name}/agent/conversations/{id}/resume"],
11931
+ handler: (client2, input) => client2.resumeAgentConversation(input.project, input.id)
11932
+ }),
11933
+ defineTool({
11934
+ name: "canonry_agent_conversations_delete",
11935
+ title: "Delete an Aero conversation",
11936
+ description: "Delete an Aero conversation. Instance administrators only. New and resume preserve the previous conversation; shared project notes are retained. Creation requires a UUID that must be reused on retries.",
11937
+ access: "write",
11938
+ tier: "agent",
11939
+ inputSchema: projectInputSchema.extend({ id: z4.string().min(1) }),
11940
+ annotations: writeAnnotations({ idempotentHint: true, destructiveHint: true }),
11941
+ openApiOperations: ["DELETE /api/v1/projects/{name}/agent/conversations/{id}"],
11942
+ handler: (client2, input) => client2.deleteAgentConversation(input.project, input.id)
11943
+ }),
11787
11944
  defineTool({
11788
11945
  name: "canonry_agent_clear",
11789
11946
  title: "Clear agent transcript",
11790
- description: "Clear the rolling Aero conversation for a project \u2014 wipes the transcript, the in-memory pending follow-up buffer, and the persisted follow-up queue. Memory entries (canonry_memory_*) are preserved. Use when starting a fresh dialogue or when the operator wants to reset context.",
11947
+ description: "Clear the rolling Aero conversation for a project \u2014 wipes the transcript, the in-memory pending follow-up buffer, and the persisted follow-up queue. Memory entries (canonry_memory_*) are preserved. Destructive legacy operation. Prefer canonry_agent_conversations_new to start fresh while preserving history.",
11791
11948
  access: "write",
11792
11949
  tier: "agent",
11793
11950
  inputSchema: projectInputSchema,
package/dist/cli.js CHANGED
@@ -35,11 +35,11 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-W67ORZUR.js";
38
+ } from "./chunk-KBSAEALE.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
42
- } from "./chunk-HF6MAKWE.js";
42
+ } from "./chunk-S4BIBUCK.js";
43
43
  import {
44
44
  CliError,
45
45
  EXIT_SYSTEM_ERROR,
@@ -66,13 +66,13 @@ import {
66
66
  shouldPrintServeSkillsNudge,
67
67
  systemError,
68
68
  usageError
69
- } from "./chunk-GQ37ALYP.js";
69
+ } from "./chunk-WJGUBZSW.js";
70
70
  import {
71
71
  DEFAULT_DEMO_TRUSTED_PROXIES,
72
72
  trustedProxiesProblem,
73
73
  trustedProxyProblem
74
74
  } from "./chunk-RB2F4D3Z.js";
75
- import "./chunk-S7D23FEE.js";
75
+ import "./chunk-LWGYRN3T.js";
76
76
  import "./chunk-AOJU5LKX.js";
77
77
  import {
78
78
  CLOUDFLARE_WORKER_BINDINGS,
@@ -88,7 +88,7 @@ import {
88
88
  queries,
89
89
  renderReportHtml,
90
90
  runs
91
- } from "./chunk-KBPSOB55.js";
91
+ } from "./chunk-DOC4QQLS.js";
92
92
  import {
93
93
  AdsDeliverySnapshotStatuses,
94
94
  AdsHistoricalCampaignRollupStatuses,
@@ -123,6 +123,7 @@ import {
123
123
  adsGeoSearchQuerySchema,
124
124
  adsImageUploadRequestSchema,
125
125
  adsPauseRequestSchema,
126
+ agentConversationListQuerySchema,
126
127
  agentTurnLimitsSchema,
127
128
  agentViewContextSchema,
128
129
  backlinkSourceSchema,
@@ -172,7 +173,7 @@ import {
172
173
  snapshotProviderModeSchema,
173
174
  visibilityReportRequestSchema,
174
175
  winnabilityClassSchema
175
- } from "./chunk-KEMMVSAQ.js";
176
+ } from "./chunk-NYRRWWEE.js";
176
177
 
177
178
  // src/cli.ts
178
179
  import { pathToFileURL } from "url";
@@ -437,7 +438,7 @@ function parseDemoTrustedProxies(values) {
437
438
  async function demoCommand(options) {
438
439
  const { host, port } = parseDemoListenOptions(options);
439
440
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
440
- const { createDemoServer } = await import("./demo-server-DKH73GRD.js");
441
+ const { createDemoServer } = await import("./demo-server-OTXS6ZHS.js");
441
442
  const app = await createDemoServer({ trustProxy });
442
443
  let stopping = false;
443
444
  const stop = () => {
@@ -12943,18 +12944,33 @@ async function listSchedules(project, format) {
12943
12944
  function getClient24() {
12944
12945
  return createApiClient();
12945
12946
  }
12947
+ async function readCurrentSchedule(client, project, kind) {
12948
+ try {
12949
+ return await client.getSchedule(project, kind);
12950
+ } catch {
12951
+ return void 0;
12952
+ }
12953
+ }
12946
12954
  async function setSchedule(project, opts) {
12947
12955
  const client = getClient24();
12956
+ const current = await readCurrentSchedule(client, project, opts.kind);
12948
12957
  const body = {};
12949
12958
  if (opts.kind) body.kind = opts.kind;
12950
- if (opts.sourceId) body.sourceId = opts.sourceId;
12951
- if (opts.preset) body.preset = opts.preset;
12952
- if (opts.cron) body.cron = opts.cron;
12959
+ else if (current) body.kind = current.kind;
12953
12960
  if (opts.everyDays || opts.startDate || opts.at) {
12954
12961
  body.recurrence = { everyDays: Number(opts.everyDays), startDate: opts.startDate, time: opts.at };
12955
- }
12962
+ } else if (opts.preset) body.preset = opts.preset;
12963
+ else if (opts.cron) body.cron = opts.cron;
12964
+ else if (current?.recurrence) body.recurrence = current.recurrence;
12965
+ else if (current?.preset) body.preset = current.preset;
12966
+ else if (current?.cronExpr) body.cron = current.cronExpr;
12956
12967
  if (opts.timezone) body.timezone = opts.timezone;
12968
+ else if (current) body.timezone = current.timezone;
12957
12969
  if (opts.providers?.length) body.providers = opts.providers;
12970
+ else if (current?.providers.length) body.providers = current.providers;
12971
+ if (opts.sourceId) body.sourceId = opts.sourceId;
12972
+ else if (current?.sourceId) body.sourceId = current.sourceId;
12973
+ if (current) body.expectedUpdatedAt = current.updatedAt;
12958
12974
  const result = await client.putSchedule(project, body);
12959
12975
  if (isMachineFormat(opts.format)) {
12960
12976
  console.log(JSON.stringify(result, null, 2));
@@ -13014,6 +13030,29 @@ async function removeSchedule(project, format, kind) {
13014
13030
  }
13015
13031
  console.log(`Schedule removed for "${project}" (kind: ${resolvedKind})`);
13016
13032
  }
13033
+ function formatNextRun(nextRunAt, timezone) {
13034
+ const at = new Date(nextRunAt);
13035
+ if (Number.isNaN(at.getTime())) return nextRunAt;
13036
+ try {
13037
+ const parts = new Intl.DateTimeFormat("en-CA", {
13038
+ timeZone: timezone,
13039
+ year: "numeric",
13040
+ month: "2-digit",
13041
+ day: "2-digit",
13042
+ hour: "2-digit",
13043
+ minute: "2-digit",
13044
+ hour12: false,
13045
+ timeZoneName: "longOffset"
13046
+ }).formatToParts(at);
13047
+ const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
13048
+ const offset = get("timeZoneName").replace("GMT", "") || "+00:00";
13049
+ const local = `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}`;
13050
+ const utc = `${String(at.getUTCHours()).padStart(2, "0")}:${String(at.getUTCMinutes()).padStart(2, "0")}Z`;
13051
+ return `${local} (${offset}) / ${utc}`;
13052
+ } catch {
13053
+ return nextRunAt;
13054
+ }
13055
+ }
13017
13056
  function printSchedule(s) {
13018
13057
  console.log(` Kind: ${s.kind}`);
13019
13058
  if (s.preset) {
@@ -13038,7 +13077,7 @@ function printSchedule(s) {
13038
13077
  console.log(` Last run: ${s.lastRunAt}`);
13039
13078
  }
13040
13079
  if (s.nextRunAt) {
13041
- console.log(` Next run: ${s.nextRunAt}`);
13080
+ console.log(` Next run: ${formatNextRun(s.nextRunAt, s.timezone)}`);
13042
13081
  }
13043
13082
  }
13044
13083
 
@@ -18461,6 +18500,43 @@ var WORDPRESS_CLI_COMMANDS = [
18461
18500
  }
18462
18501
  ];
18463
18502
 
18503
+ // src/commands/agent-conversations.ts
18504
+ import { randomUUID as randomUUID2 } from "crypto";
18505
+ async function agentConversations(opts) {
18506
+ try {
18507
+ const client = createApiClient();
18508
+ const result = await (() => {
18509
+ switch (opts.action) {
18510
+ case "list":
18511
+ return client.listAgentConversations(opts.project, { offset: opts.offset, limit: opts.limit });
18512
+ case "new":
18513
+ return client.createAgentConversation(opts.project, opts.id ?? randomUUID2());
18514
+ case "show":
18515
+ return client.getAgentConversation(opts.project, opts.id);
18516
+ case "resume":
18517
+ return client.resumeAgentConversation(opts.project, opts.id);
18518
+ case "delete":
18519
+ return client.deleteAgentConversation(opts.project, opts.id);
18520
+ }
18521
+ })();
18522
+ if (isMachineFormat(opts.format)) {
18523
+ console.log(JSON.stringify(result, null, 2));
18524
+ return;
18525
+ }
18526
+ if ("conversations" in result) {
18527
+ if (!result.conversations.length) console.log("No Aero conversations yet.");
18528
+ for (const conversation of result.conversations) console.log(`${conversation.active ? "* " : " "}${conversation.id} ${conversation.title} ${conversation.updatedAt}`);
18529
+ if (result.nextOffset !== null) console.log(`More conversations: --offset ${result.nextOffset}`);
18530
+ } else if ("messages" in result) {
18531
+ console.log(`${result.title} \u2014 ${result.id}${result.active ? " (current)" : ""}`);
18532
+ for (const message of result.messages) console.log(`[${message.role}] ${typeof message.content === "string" ? message.content : JSON.stringify(message.content)}`);
18533
+ } else console.log(`Conversation deleted: ${result.id}. Shared project notes kept.`);
18534
+ } catch (error) {
18535
+ printCliError(error, opts.format);
18536
+ process.exitCode = error instanceof CliError ? error.exitCode : 2;
18537
+ }
18538
+ }
18539
+
18464
18540
  // src/agent-webhook.ts
18465
18541
  var AGENT_WEBHOOK_EVENTS = ["run.completed", "insight.critical", "insight.high", "citation.gained"];
18466
18542
 
@@ -18529,6 +18605,7 @@ async function agentAsk(opts) {
18529
18605
  `/projects/${encodeURIComponent(opts.project)}/agent/prompt`,
18530
18606
  {
18531
18607
  prompt: opts.prompt,
18608
+ ...opts.conversationId ? { conversationId: opts.conversationId } : {},
18532
18609
  provider: opts.provider,
18533
18610
  modelId: opts.modelId,
18534
18611
  scope: opts.scope ?? "all",
@@ -18805,20 +18882,34 @@ async function agentMemoryForget(opts) {
18805
18882
  // src/cli-commands/agent.ts
18806
18883
  var AGENT_ASK_SCOPES = ["all", "read-only"];
18807
18884
  var AGENT_CLI_COMMANDS = [
18885
+ ...["list", "new", "show", "resume", "delete"].map((action) => ({
18886
+ path: ["agent", "conversations", action],
18887
+ usage: `canonry agent conversations ${action} <project> [--id <conversation-id>] [--offset <n>] [--limit <n>] [--format json]`,
18888
+ options: { id: stringOption(), offset: stringOption(), limit: stringOption() },
18889
+ run: async (input) => {
18890
+ const usage = `canonry agent conversations ${action} <project>`;
18891
+ const project = requireProject(input, `agent.conversations.${action}`, usage);
18892
+ const id = action === "new" || action === "list" ? getString(input.values, "id") : requireStringOption(input, "id", { command: `agent.conversations.${action}`, usage, message: "--id is required" });
18893
+ const query = agentConversationListQuerySchema.safeParse({ offset: getString(input.values, "offset"), limit: getString(input.values, "limit") });
18894
+ if (!query.success) throw usageError("Invalid history pagination", { message: query.error.message });
18895
+ await agentConversations({ project, action, id, ...query.data, format: input.format });
18896
+ }
18897
+ })),
18808
18898
  {
18809
18899
  path: ["agent", "ask"],
18810
- usage: `canonry agent ask <project> "<prompt>" [--provider ${listAgentProviders().join("|")}] [--model <id>] [--scope all|read-only] [--profile default|ads-operator] [--context <json>] [--max-tool-calls <n>] [--timeout-ms <ms>] [--format json]`,
18900
+ usage: `canonry agent ask <project> "<prompt>" [--provider ${listAgentProviders().join("|")}] [--model <id>] [--scope all|read-only] [--profile default|ads-operator] [--context <json>] [--conversation <id>] [--max-tool-calls <n>] [--timeout-ms <ms>] [--format json]`,
18811
18901
  options: {
18812
18902
  provider: stringOption(),
18813
18903
  model: stringOption(),
18814
18904
  scope: stringOption(),
18815
18905
  profile: stringOption(),
18816
18906
  context: stringOption(),
18907
+ conversation: stringOption(),
18817
18908
  "max-tool-calls": stringOption(),
18818
18909
  "timeout-ms": stringOption()
18819
18910
  },
18820
18911
  run: async (input) => {
18821
- const usage = `canonry agent ask <project> "<prompt>" [--provider ${listAgentProviders().join("|")}] [--model <id>] [--scope all|read-only] [--profile default|ads-operator] [--context <json>] [--max-tool-calls <n>] [--timeout-ms <ms>] [--format json]`;
18912
+ const usage = `canonry agent ask <project> "<prompt>" [--provider ${listAgentProviders().join("|")}] [--model <id>] [--scope all|read-only] [--profile default|ads-operator] [--context <json>] [--conversation <id>] [--max-tool-calls <n>] [--timeout-ms <ms>] [--format json]`;
18822
18913
  const project = requireProject(input, "agent.ask", usage);
18823
18914
  const prompt2 = input.positionals.slice(1).join(" ").trim();
18824
18915
  if (!prompt2) {
@@ -18884,6 +18975,7 @@ Usage: ${usage}`, {
18884
18975
  if (!parsedLimits.success) throw usageError("Invalid Aero turn limits", { message: parsedLimits.error.message });
18885
18976
  await agentAsk({
18886
18977
  context,
18978
+ conversationId: getString(input.values, "conversation"),
18887
18979
  limits: parsedLimits.data,
18888
18980
  project,
18889
18981
  prompt: prompt2,
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  layoutSiteCrawlGraphInput,
6
6
  persistSiteCrawlGraphLayout
7
- } from "./chunk-S7D23FEE.js";
7
+ } from "./chunk-LWGYRN3T.js";
8
8
  import {
9
9
  PACKAGE_VERSION
10
10
  } from "./chunk-AOJU5LKX.js";
@@ -83,7 +83,7 @@ import {
83
83
  siteCrawlPages,
84
84
  siteCrawlSnapshots,
85
85
  trafficSources
86
- } from "./chunk-KBPSOB55.js";
86
+ } from "./chunk-DOC4QQLS.js";
87
87
  import {
88
88
  RunKinds,
89
89
  SiteCrawlFetchStates,
@@ -104,7 +104,7 @@ import {
104
104
  measurementPlanV2Schema,
105
105
  placementLinkDecision,
106
106
  siteAuditPageFactorSchema
107
- } from "./chunk-KEMMVSAQ.js";
107
+ } from "./chunk-NYRRWWEE.js";
108
108
 
109
109
  // src/demo-server.ts
110
110
  import { dirname, join as join2 } from "path";
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FastifyInstance } from 'fastify';
2
2
  import { DatabaseClient } from '@ainyc/canonry-db';
3
- import { ProviderQuotaPolicy, SchedulableRunKind, EmbedConfigEntry, AgentPluginState, GoogleAdsCampaignMetricsQuery, GoogleAdsRawSnapshotDto, GoogleAdsEffectiveGoalGraphDto, GoogleMarketingProvider, GoogleAdsAccessibleCustomersResponse, GoogleAdsAccessibleCustomerDto, GtmAccountsResponse, GtmContainerListResponse, GtmWorkspaceListResponse, GtmRawSnapshotDto } from '@ainyc/canonry-contracts';
3
+ import { ProviderQuotaPolicy, SchedulableRunKind, AgentProviderId, EmbedConfigEntry, AgentPluginState, GoogleAdsCampaignMetricsQuery, GoogleAdsRawSnapshotDto, GoogleAdsEffectiveGoalGraphDto, GoogleMarketingProvider, GoogleAdsAccessibleCustomersResponse, GoogleAdsAccessibleCustomerDto, GtmAccountsResponse, GtmContainerListResponse, GtmWorkspaceListResponse, GtmRawSnapshotDto } from '@ainyc/canonry-contracts';
4
4
  import { GoogleTokenResponse } from '@ainyc/canonry-integration-google';
5
5
 
6
6
  type GoogleConnectionType = 'gsc' | 'ga4' | 'gbp';
@@ -289,6 +289,33 @@ interface AgentConfigEntry {
289
289
  * Both resolvers live in agent-config.ts.
290
290
  */
291
291
  mode?: 'disabled' | 'prompt-only';
292
+ /**
293
+ * Pin the LLM provider Aero reasons with.
294
+ *
295
+ * Absent (the default) keeps the historical behaviour: the first provider
296
+ * holding a usable key wins, by `autoDetectPriority`. That default is an
297
+ * accident of which ANSWER-ENGINE keys the install happens to hold -- an
298
+ * install that sweeps with Claude silently gets Claude for Aero too, at that
299
+ * provider's agent-tier pricing -- so any deployment that cares which model
300
+ * answers has to say so here rather than rely on detection order.
301
+ *
302
+ * Ranked by `resolveSessionProviderAndModel`: an explicit per-request
303
+ * provider beats this pin, and this pin beats both the persisted session row
304
+ * and auto-detection. Because it outranks the row, a pin survives the
305
+ * conversation-delete path that drops that row entirely. An explicit request
306
+ * lasts one turn: the next turn that names no provider returns to the pin.
307
+ *
308
+ * Removing the pin does not move an existing session back to detection:
309
+ * without a pin a session keeps its stored provider, as it always has, until
310
+ * its conversation is deleted. Starting a new conversation keeps it too.
311
+ */
312
+ provider?: AgentProviderId;
313
+ /**
314
+ * Model id for `provider`. Ignored unless `provider` is set: a model slug is
315
+ * only meaningful against the provider that serves it. Absent uses that
316
+ * provider's agent-tier default.
317
+ */
318
+ model?: string;
292
319
  }
293
320
  interface DashboardConfigEntry {
294
321
  /**
package/dist/index.js CHANGED
@@ -3,14 +3,14 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-W67ORZUR.js";
6
+ } from "./chunk-KBSAEALE.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-GQ37ALYP.js";
10
- import "./chunk-S7D23FEE.js";
9
+ } from "./chunk-WJGUBZSW.js";
10
+ import "./chunk-LWGYRN3T.js";
11
11
  import "./chunk-AOJU5LKX.js";
12
- import "./chunk-KBPSOB55.js";
13
- import "./chunk-KEMMVSAQ.js";
12
+ import "./chunk-DOC4QQLS.js";
13
+ import "./chunk-NYRRWWEE.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
16
16
  createGoogleMarketingCredentialStore,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-KBPSOB55.js";
5
- import "./chunk-KEMMVSAQ.js";
4
+ } from "./chunk-DOC4QQLS.js";
5
+ import "./chunk-NYRRWWEE.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
8
8
  IntelligenceService