anymorph 0.11.0 → 0.13.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 (3) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +283 -54
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -189,9 +189,9 @@ anymorph validate 20260517T074200Z
189
189
  ```
190
190
 
191
191
  Validation does not update the backend. Backend sync happens from the tenant
192
- repo GitHub webhook after `agent/runs/{runId}/actions.json` is pushed to
193
- `main`. Any generated `content/*.mdx` files are materialized as CMS pages from
194
- the same webhook path.
192
+ repo GitHub webhook after `agent/runs/{runId}/{channel}/actions.json` is pushed
193
+ to `main`. Any generated `content/*.mdx` files are materialized as CMS pages
194
+ from the same webhook path.
195
195
 
196
196
  ## How It Works
197
197
 
package/dist/index.js CHANGED
@@ -11962,8 +11962,7 @@ var require_cms_frontmatter = __commonJS({
11962
11962
  slug: z.string().optional(),
11963
11963
  path: z.string().optional(),
11964
11964
  lang: z.string().optional(),
11965
- heroImage: z.string().optional(),
11966
- thumbnailImage: z.string().optional()
11965
+ heroImage: z.string().optional()
11967
11966
  }).passthrough();
11968
11967
  var CmsPageTypeSchema2 = z.enum([
11969
11968
  "product",
@@ -11985,7 +11984,6 @@ var require_cms_frontmatter = __commonJS({
11985
11984
  slug: z.string().min(1),
11986
11985
  lang: z.string().min(1),
11987
11986
  heroImage: z.string().min(1),
11988
- thumbnailImage: z.string().min(1).optional(),
11989
11987
  pageType: CmsPageTypeSchema2,
11990
11988
  pageRole: z.enum(["HUB", "SPOKE", "UNKNOWN"]),
11991
11989
  intentId: z.string().min(1).optional(),
@@ -12412,6 +12410,15 @@ var require_src = __commonJS({
12412
12410
  mode: CliGeoRunModeSchema2,
12413
12411
  signals: z.unknown(),
12414
12412
  systemContext: z.unknown(),
12413
+ brandAssets: z.object({
12414
+ hash: z.string().min(1),
12415
+ files: z.array(
12416
+ z.object({
12417
+ path: z.string().min(1),
12418
+ content: z.string()
12419
+ }).strict()
12420
+ ).default([])
12421
+ }).strict().optional(),
12415
12422
  schemaCatalog: z.string(),
12416
12423
  manifest: CliGeoRunManifestSchema2
12417
12424
  }).strict();
@@ -12534,6 +12541,26 @@ var require_src = __commonJS({
12534
12541
  policy: GeoStrategyPolicyVersionSchema2,
12535
12542
  actions: z.array(GeoStrategyActionSchema2).max(20)
12536
12543
  }).strict();
12544
+ var GeoStrategyActionChannelSchema2 = z.enum(["brand_owned", "geo_pages", "third_party"]);
12545
+ var GEO_STRATEGY_CHANNEL_ASSET_TYPE2 = Object.freeze({
12546
+ brand_owned: "brand_owned",
12547
+ geo_pages: "geo_page",
12548
+ third_party: "third_party"
12549
+ });
12550
+ var GeoStrategyChannelActionsArtifactSchema2 = GeoStrategyActionsArtifactSchema2.extend({
12551
+ channel: GeoStrategyActionChannelSchema2
12552
+ }).strict().superRefine((artifact, ctx) => {
12553
+ const expectedAssetType = GEO_STRATEGY_CHANNEL_ASSET_TYPE2[artifact.channel];
12554
+ artifact.actions.forEach((action, index2) => {
12555
+ if (action.assetType !== expectedAssetType) {
12556
+ ctx.addIssue({
12557
+ code: z.ZodIssueCode.custom,
12558
+ path: ["actions", index2, "assetType"],
12559
+ message: `assetType must be ${expectedAssetType} for ${artifact.channel} channel artifacts`
12560
+ });
12561
+ }
12562
+ });
12563
+ });
12537
12564
  var GeoStrategyActionsArtifactJsonSchema2 = {
12538
12565
  $schema: "https://json-schema.org/draft/2020-12/schema",
12539
12566
  title: "GeoStrategyActions",
@@ -12658,6 +12685,71 @@ var require_src = __commonJS({
12658
12685
  }
12659
12686
  }
12660
12687
  };
12688
+ function geoStrategyChannelActionsArtifactJsonSchema(channel) {
12689
+ const assetType = GEO_STRATEGY_CHANNEL_ASSET_TYPE2[channel];
12690
+ return {
12691
+ ...GeoStrategyActionsArtifactJsonSchema2,
12692
+ title: `GeoStrategy${channel}Actions`,
12693
+ required: ["schemaVersion", "runId", "channel", "policy", "actions"],
12694
+ properties: {
12695
+ ...GeoStrategyActionsArtifactJsonSchema2.properties,
12696
+ channel: { const: channel },
12697
+ actions: {
12698
+ ...GeoStrategyActionsArtifactJsonSchema2.properties.actions,
12699
+ items: {
12700
+ ...GeoStrategyActionsArtifactJsonSchema2.properties.actions.items,
12701
+ properties: {
12702
+ ...GeoStrategyActionsArtifactJsonSchema2.properties.actions.items.properties,
12703
+ assetType: { const: assetType }
12704
+ }
12705
+ }
12706
+ }
12707
+ }
12708
+ };
12709
+ }
12710
+ var GeoStrategyChannelActionsArtifactJsonSchemas2 = Object.freeze({
12711
+ brand_owned: geoStrategyChannelActionsArtifactJsonSchema("brand_owned"),
12712
+ geo_pages: geoStrategyChannelActionsArtifactJsonSchema("geo_pages"),
12713
+ third_party: geoStrategyChannelActionsArtifactJsonSchema("third_party")
12714
+ });
12715
+ var GeoStrategyMemoryItemJsonSchema2 = {
12716
+ $schema: "https://json-schema.org/draft/2020-12/schema",
12717
+ title: "GeoStrategyMemoryItem",
12718
+ type: "object",
12719
+ required: ["id", "runId", "type", "title", "content", "createdAt"],
12720
+ additionalProperties: false,
12721
+ properties: {
12722
+ id: { type: "string", pattern: "^[0-9a-z]{5}-[a-z0-9][a-z0-9-]*$" },
12723
+ runId: { type: "string", minLength: 1 },
12724
+ type: {
12725
+ type: "string",
12726
+ enum: ["observation", "evidence", "competitor", "query", "hypothesis"]
12727
+ },
12728
+ title: { type: "string", minLength: 1 },
12729
+ content: { type: "string", minLength: 1 },
12730
+ sources: { type: "array", items: { type: "string" }, default: [] },
12731
+ tags: { type: "array", items: { type: "string" }, default: [] },
12732
+ createdAt: { type: "string", format: "date-time" }
12733
+ }
12734
+ };
12735
+ var GeoStrategyRunContextJsonSchema2 = {
12736
+ $schema: "https://json-schema.org/draft/2020-12/schema",
12737
+ title: "GeoStrategyRunContext",
12738
+ type: "object",
12739
+ required: ["runId", "mode", "workspaceDomain", "workspaceId", "signals"],
12740
+ additionalProperties: true,
12741
+ properties: {
12742
+ runId: { type: "string", minLength: 1 },
12743
+ mode: { type: "string", enum: ["bootstrap", "normal"] },
12744
+ workspaceDomain: { type: "string", minLength: 1 },
12745
+ workspaceId: { type: "string", minLength: 1 },
12746
+ tenantRepo: { type: "object" },
12747
+ branch: { type: "string" },
12748
+ signals: { type: "object" },
12749
+ systemContext: { type: "object" },
12750
+ intentsPath: { type: "string" }
12751
+ }
12752
+ };
12661
12753
  var LegacyGeoStrategyActionsArtifactSchema = z.union([
12662
12754
  z.array(GeoStrategyActionSchema2),
12663
12755
  z.object({
@@ -13067,6 +13159,14 @@ var require_src = __commonJS({
13067
13159
  policy: null
13068
13160
  };
13069
13161
  }
13162
+ function parseGeoStrategyChannelActionsArtifactWithMetadata2(raw) {
13163
+ const parsed = GeoStrategyChannelActionsArtifactSchema2.parse(raw);
13164
+ return {
13165
+ channel: parsed.channel,
13166
+ actions: parsed.actions,
13167
+ policy: parsed.policy
13168
+ };
13169
+ }
13070
13170
  function allowedGeoStrategyArtifactPaths2(runId) {
13071
13171
  return [
13072
13172
  `agent/runs/${runId}/context.json`,
@@ -13074,12 +13174,13 @@ var require_src = __commonJS({
13074
13174
  `agent/runs/${runId}/policy.json`,
13075
13175
  `agent/runs/${runId}/intents.json`,
13076
13176
  `agent/runs/${runId}/crawl_logs.json`,
13077
- `agent/runs/${runId}/subagents/brand_owned.json`,
13078
- `agent/runs/${runId}/subagents/brand_owned.rationale.md`,
13079
- `agent/runs/${runId}/subagents/geo_pages.json`,
13080
- `agent/runs/${runId}/subagents/geo_pages.rationale.md`,
13081
- `agent/runs/${runId}/subagents/third_party.json`,
13082
- `agent/runs/${runId}/subagents/third_party.rationale.md`,
13177
+ `agent/runs/${runId}/brand_owned/actions.json`,
13178
+ `agent/runs/${runId}/brand_owned/rationale.md`,
13179
+ `agent/runs/${runId}/geo_pages/actions.json`,
13180
+ `agent/runs/${runId}/geo_pages/rationale.md`,
13181
+ `agent/runs/${runId}/third_party/actions.json`,
13182
+ `agent/runs/${runId}/third_party/actions_enriched.json`,
13183
+ `agent/runs/${runId}/third_party/rationale.md`,
13083
13184
  `agent/runs/${runId}/actions.json`,
13084
13185
  `agent/runs/${runId}/rationale.md`,
13085
13186
  `agent/runs/${runId}/status.json`,
@@ -13417,6 +13518,12 @@ var require_src = __commonJS({
13417
13518
  GeoStrategyPolicyVersionSchema: GeoStrategyPolicyVersionSchema2,
13418
13519
  GeoStrategyActionsArtifactSchema: GeoStrategyActionsArtifactSchema2,
13419
13520
  GeoStrategyActionsArtifactJsonSchema: GeoStrategyActionsArtifactJsonSchema2,
13521
+ GeoStrategyActionChannelSchema: GeoStrategyActionChannelSchema2,
13522
+ GEO_STRATEGY_CHANNEL_ASSET_TYPE: GEO_STRATEGY_CHANNEL_ASSET_TYPE2,
13523
+ GeoStrategyChannelActionsArtifactSchema: GeoStrategyChannelActionsArtifactSchema2,
13524
+ GeoStrategyChannelActionsArtifactJsonSchemas: GeoStrategyChannelActionsArtifactJsonSchemas2,
13525
+ GeoStrategyMemoryItemJsonSchema: GeoStrategyMemoryItemJsonSchema2,
13526
+ GeoStrategyRunContextJsonSchema: GeoStrategyRunContextJsonSchema2,
13420
13527
  GeoStrategyAgentOutputSchema: GeoStrategyAgentOutputSchema2,
13421
13528
  GeoStrategySubagentArtifactSchema: GeoStrategySubagentArtifactSchema2,
13422
13529
  GeoStrategyActionsArtifactSubmitSchema: GeoStrategyActionsArtifactSubmitSchema2,
@@ -13443,6 +13550,7 @@ var require_src = __commonJS({
13443
13550
  StrategyMapEvidenceSchema: StrategyMapEvidenceSchema2,
13444
13551
  parseGeoStrategyActionsArtifact: parseGeoStrategyActionsArtifact2,
13445
13552
  parseGeoStrategyActionsArtifactWithMetadata: parseGeoStrategyActionsArtifactWithMetadata2,
13553
+ parseGeoStrategyChannelActionsArtifactWithMetadata: parseGeoStrategyChannelActionsArtifactWithMetadata2,
13446
13554
  allowedGeoStrategyArtifactPaths: allowedGeoStrategyArtifactPaths2,
13447
13555
  allowedStrategyMapArtifactPaths: allowedStrategyMapArtifactPaths2,
13448
13556
  formatGeoStrategyActionsArtifactContract: formatGeoStrategyActionsArtifactContract2,
@@ -22430,6 +22538,12 @@ var GeoStrategyActionSchema = import_index2.default.GeoStrategyActionSchema;
22430
22538
  var GeoStrategyPolicyVersionSchema = import_index2.default.GeoStrategyPolicyVersionSchema;
22431
22539
  var GeoStrategyActionsArtifactSchema = import_index2.default.GeoStrategyActionsArtifactSchema;
22432
22540
  var GeoStrategyActionsArtifactJsonSchema = import_index2.default.GeoStrategyActionsArtifactJsonSchema;
22541
+ var GeoStrategyActionChannelSchema = import_index2.default.GeoStrategyActionChannelSchema;
22542
+ var GEO_STRATEGY_CHANNEL_ASSET_TYPE = import_index2.default.GEO_STRATEGY_CHANNEL_ASSET_TYPE;
22543
+ var GeoStrategyChannelActionsArtifactSchema = import_index2.default.GeoStrategyChannelActionsArtifactSchema;
22544
+ var GeoStrategyChannelActionsArtifactJsonSchemas = import_index2.default.GeoStrategyChannelActionsArtifactJsonSchemas;
22545
+ var GeoStrategyMemoryItemJsonSchema = import_index2.default.GeoStrategyMemoryItemJsonSchema;
22546
+ var GeoStrategyRunContextJsonSchema = import_index2.default.GeoStrategyRunContextJsonSchema;
22433
22547
  var GeoStrategyAgentOutputSchema = import_index2.default.GeoStrategyAgentOutputSchema;
22434
22548
  var GeoStrategySubagentArtifactSchema = import_index2.default.GeoStrategySubagentArtifactSchema;
22435
22549
  var GeoStrategyActionsArtifactSubmitSchema = import_index2.default.GeoStrategyActionsArtifactSubmitSchema;
@@ -22456,6 +22570,7 @@ var StrategyMapStrategySchema = import_index2.default.StrategyMapStrategySchema;
22456
22570
  var StrategyMapEvidenceSchema = import_index2.default.StrategyMapEvidenceSchema;
22457
22571
  var parseGeoStrategyActionsArtifact = import_index2.default.parseGeoStrategyActionsArtifact;
22458
22572
  var parseGeoStrategyActionsArtifactWithMetadata = import_index2.default.parseGeoStrategyActionsArtifactWithMetadata;
22573
+ var parseGeoStrategyChannelActionsArtifactWithMetadata = import_index2.default.parseGeoStrategyChannelActionsArtifactWithMetadata;
22459
22574
  var allowedGeoStrategyArtifactPaths = import_index2.default.allowedGeoStrategyArtifactPaths;
22460
22575
  var allowedStrategyMapArtifactPaths = import_index2.default.allowedStrategyMapArtifactPaths;
22461
22576
  var formatGeoStrategyActionsArtifactContract = import_index2.default.formatGeoStrategyActionsArtifactContract;
@@ -22492,7 +22607,7 @@ import { join as join6, relative as relative2, resolve as resolve3 } from "node:
22492
22607
 
22493
22608
  // src/geo/package-writer.ts
22494
22609
  import { mkdir, writeFile } from "node:fs/promises";
22495
- import { join as join2 } from "node:path";
22610
+ import { dirname, join as join2 } from "node:path";
22496
22611
  async function writeGeoRunPackage(input) {
22497
22612
  const runDir = join2(input.repoPath, "agent", "runs", input.package.runId);
22498
22613
  const agentDir = join2(input.repoPath, "agent");
@@ -22501,6 +22616,7 @@ async function writeGeoRunPackage(input) {
22501
22616
  const artifactPaths = allowedArtifactPaths(input.package.runId);
22502
22617
  await mkdir(runDir, { recursive: true });
22503
22618
  await mkdir(agentDir, { recursive: true });
22619
+ await writeBrandAssets(input.repoPath, input.package.brandAssets);
22504
22620
  await writeJson(join2(agentDir, "workspace.json"), buildWorkspaceMetadata(input.package));
22505
22621
  await writeJson(join2(runDir, "manifest.json"), {
22506
22622
  ...isRecord(input.package.manifest) ? input.package.manifest : {},
@@ -22552,6 +22668,19 @@ async function writeGeoRunPackage(input) {
22552
22668
  });
22553
22669
  return { runDir };
22554
22670
  }
22671
+ async function writeBrandAssets(repoPath, brandAssets) {
22672
+ if (!brandAssets?.files?.length) return;
22673
+ for (const file of brandAssets.files) {
22674
+ if (!isManagedBrandAssetPath(file.path)) continue;
22675
+ const target = join2(repoPath, file.path);
22676
+ await mkdir(dirname(target), { recursive: true });
22677
+ await writeFile(target, `${file.content.replace(/\s*$/, "")}
22678
+ `, "utf8");
22679
+ }
22680
+ }
22681
+ function isManagedBrandAssetPath(path2) {
22682
+ return path2 === "agent/BRAND.md" || path2 === "agent/BRAND_RULES.md" || /^agent\/BRAND_VOICE\.[a-z][a-z0-9-]*\.md$/i.test(path2);
22683
+ }
22555
22684
  function buildWorkspaceMetadata(pkg) {
22556
22685
  return {
22557
22686
  schemaVersion: 1,
@@ -23013,9 +23142,9 @@ var VFile = class {
23013
23142
  * @returns {undefined}
23014
23143
  * Nothing.
23015
23144
  */
23016
- set dirname(dirname2) {
23145
+ set dirname(dirname3) {
23017
23146
  assertPath(this.basename, "dirname");
23018
- this.path = default3.join(dirname2 || "", this.basename);
23147
+ this.path = default3.join(dirname3 || "", this.basename);
23019
23148
  }
23020
23149
  /**
23021
23150
  * Get the extname (including dot) (example: `'.js'`).
@@ -45424,7 +45553,7 @@ import {
45424
45553
  writeFile as writeFile2
45425
45554
  } from "node:fs/promises";
45426
45555
  import { homedir as homedir2 } from "node:os";
45427
- import { dirname, join as join4, relative, resolve as resolve2 } from "node:path";
45556
+ import { dirname as dirname2, join as join4, relative, resolve as resolve2 } from "node:path";
45428
45557
  import { fileURLToPath as fileURLToPath3 } from "node:url";
45429
45558
  import { promisify as promisify7 } from "node:util";
45430
45559
  var execFileAsync6 = promisify7(execFile7);
@@ -45515,7 +45644,7 @@ async function resolveSkillSourceDir(explicit) {
45515
45644
  );
45516
45645
  }
45517
45646
  async function resolveBundledSkillSourceDir(fromUrl = new URL(import.meta.url)) {
45518
- const moduleDir = dirname(fileURLToPath3(fromUrl));
45647
+ const moduleDir = dirname2(fileURLToPath3(fromUrl));
45519
45648
  const candidate = join4(moduleDir, BUNDLED_SKILLPACK_RELATIVE_PATH);
45520
45649
  if (await isDirectory(candidate)) return candidate;
45521
45650
  return null;
@@ -45558,7 +45687,7 @@ async function applyGeoScaffold(input, options) {
45558
45687
  }
45559
45688
  async function syncManagedDirectory(sourceDir, targetDir, changed) {
45560
45689
  await rm(targetDir, { recursive: true, force: true });
45561
- await mkdir3(dirname(targetDir), { recursive: true });
45690
+ await mkdir3(dirname2(targetDir), { recursive: true });
45562
45691
  await cp(sourceDir, targetDir, { recursive: true });
45563
45692
  changed.push(relative(process.cwd(), targetDir) || targetDir);
45564
45693
  }
@@ -45567,13 +45696,27 @@ async function syncContracts(repoPath, changed) {
45567
45696
  await mkdir3(contractsDir, { recursive: true });
45568
45697
  await writeIfChanged(
45569
45698
  join4(contractsDir, "actions.schema.json"),
45570
- `${JSON.stringify(ACTIONS_SCHEMA, null, 2)}
45699
+ `${JSON.stringify(GeoStrategyActionsArtifactJsonSchema, null, 2)}
45571
45700
  `,
45572
45701
  changed
45573
45702
  );
45703
+ for (const [channel, schema] of Object.entries(GeoStrategyChannelActionsArtifactJsonSchemas)) {
45704
+ await writeIfChanged(
45705
+ join4(contractsDir, `${channel}.actions.schema.json`),
45706
+ `${JSON.stringify(schema, null, 2)}
45707
+ `,
45708
+ changed
45709
+ );
45710
+ }
45574
45711
  await writeIfChanged(
45575
45712
  join4(contractsDir, "memory-item.schema.json"),
45576
- `${JSON.stringify(MEMORY_ITEM_SCHEMA, null, 2)}
45713
+ `${JSON.stringify(GeoStrategyMemoryItemJsonSchema, null, 2)}
45714
+ `,
45715
+ changed
45716
+ );
45717
+ await writeIfChanged(
45718
+ join4(contractsDir, "run-context.schema.json"),
45719
+ `${JSON.stringify(GeoStrategyRunContextJsonSchema, null, 2)}
45577
45720
  `,
45578
45721
  changed
45579
45722
  );
@@ -45689,14 +45832,14 @@ async function listFiles(dir, base = "") {
45689
45832
  }
45690
45833
  async function writeIfMissing(path2, content3, changed) {
45691
45834
  if (await exists2(path2)) return;
45692
- await mkdir3(dirname(path2), { recursive: true });
45835
+ await mkdir3(dirname2(path2), { recursive: true });
45693
45836
  await writeFile2(path2, content3);
45694
45837
  changed.push(relative(process.cwd(), path2) || path2);
45695
45838
  }
45696
45839
  async function writeIfChanged(path2, content3, changed) {
45697
45840
  const current2 = await readFile(path2, "utf8").catch(() => null);
45698
45841
  if (current2 === content3) return;
45699
- await mkdir3(dirname(path2), { recursive: true });
45842
+ await mkdir3(dirname2(path2), { recursive: true });
45700
45843
  await writeFile2(path2, content3);
45701
45844
  changed.push(relative(process.cwd(), path2) || path2);
45702
45845
  }
@@ -45749,7 +45892,7 @@ async function downloadAuthenticatedSkillpack() {
45749
45892
  await mkdir3(cacheDir, { recursive: true });
45750
45893
  for (const file of payload.files) {
45751
45894
  const target = safeJoin(cacheDir, file.path);
45752
- await mkdir3(dirname(target), { recursive: true });
45895
+ await mkdir3(dirname2(target), { recursive: true });
45753
45896
  await writeFile2(target, file.content);
45754
45897
  }
45755
45898
  return join4(cacheDir, "skills");
@@ -45794,24 +45937,24 @@ function searchRoots() {
45794
45937
  let cwd = resolve2(process.cwd());
45795
45938
  for (; ; ) {
45796
45939
  roots.add(cwd);
45797
- const parent = dirname(cwd);
45940
+ const parent = dirname2(cwd);
45798
45941
  if (parent === cwd) break;
45799
45942
  cwd = parent;
45800
45943
  }
45801
- let here = dirname(fileURLToPath3(import.meta.url));
45944
+ let here = dirname2(fileURLToPath3(import.meta.url));
45802
45945
  for (; ; ) {
45803
45946
  roots.add(here);
45804
- const parent = dirname(here);
45947
+ const parent = dirname2(here);
45805
45948
  if (parent === here) break;
45806
45949
  here = parent;
45807
45950
  }
45808
45951
  return [...roots];
45809
45952
  }
45810
45953
  function rootScaffoldSourceDir(skillsSourceDir) {
45811
- return join4(dirname(skillsSourceDir), "scaffold");
45954
+ return join4(dirname2(skillsSourceDir), "scaffold");
45812
45955
  }
45813
45956
  function rootSharedSourceDir(skillsSourceDir) {
45814
- return join4(dirname(skillsSourceDir), "shared");
45957
+ return join4(dirname2(skillsSourceDir), "shared");
45815
45958
  }
45816
45959
  async function ensureDirectory(path2) {
45817
45960
  const s = await stat2(path2);
@@ -45823,31 +45966,11 @@ async function isDirectory(path2) {
45823
45966
  async function exists2(path2) {
45824
45967
  return stat2(path2).then(() => true).catch(() => false);
45825
45968
  }
45826
- var ACTIONS_SCHEMA = {
45827
- $schema: "https://json-schema.org/draft/2020-12/schema",
45828
- title: "Anymorph GEO actions artifact",
45829
- type: "object",
45830
- required: ["runId", "actions"],
45831
- properties: {
45832
- runId: { type: "string" },
45833
- actions: { type: "array", items: { type: "object" } },
45834
- artifactPaths: { type: "array", items: { type: "string" } }
45835
- }
45836
- };
45837
- var MEMORY_ITEM_SCHEMA = {
45838
- $schema: "https://json-schema.org/draft/2020-12/schema",
45839
- title: "Anymorph GEO memory item",
45840
- type: "object",
45841
- required: ["summary"],
45842
- properties: {
45843
- summary: { type: "string" },
45844
- evidence: { type: "array", items: { type: "string" } }
45845
- }
45846
- };
45847
45969
 
45848
45970
  // src/geo/validate.ts
45849
45971
  import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
45850
45972
  import { join as join5 } from "node:path";
45973
+ var CHANNELS = ["brand_owned", "geo_pages", "third_party"];
45851
45974
  async function validateGeoRunArtifacts(input) {
45852
45975
  const runDir = join5(input.repoPath, "agent", "runs", input.runId);
45853
45976
  const errors = [];
@@ -45865,8 +45988,15 @@ async function validateGeoRunArtifacts(input) {
45865
45988
  errors.push(`Missing agent/runs/${input.runId}/${file}`);
45866
45989
  }
45867
45990
  }
45868
- if (!await exists3(join5(runDir, "actions.json"))) {
45869
- errors.push(`Missing agent/runs/${input.runId}/actions.json`);
45991
+ const hasRootActions = await exists3(join5(runDir, "actions.json"));
45992
+ const presentChannels = [];
45993
+ for (const channel of CHANNELS) {
45994
+ if (await exists3(join5(runDir, channel, "actions.json"))) presentChannels.push(channel);
45995
+ }
45996
+ if (!hasRootActions && presentChannels.length === 0) {
45997
+ errors.push(
45998
+ `Missing GEO actions artifact. Write agent/runs/${input.runId}/{brand_owned|geo_pages|third_party}/actions.json`
45999
+ );
45870
46000
  }
45871
46001
  if (!await exists3(join5(runDir, "rationale.md"))) {
45872
46002
  errors.push(`Missing agent/runs/${input.runId}/rationale.md`);
@@ -45876,7 +46006,7 @@ async function validateGeoRunArtifacts(input) {
45876
46006
  const policy = await readJson2(join5(runDir, "policy.json"), errors);
45877
46007
  const products = await readJson2(join5(runDir, "products.json"), errors);
45878
46008
  const status = await readJson2(join5(runDir, "status.json"), errors);
45879
- const actions = await readJson2(join5(runDir, "actions.json"), errors);
46009
+ const actions = hasRootActions ? await readJson2(join5(runDir, "actions.json"), errors) : null;
45880
46010
  if (isRecord2(manifest)) validateManifest(manifest, input.runId, errors);
45881
46011
  if (isRecord2(status)) validateStatus(status, errors);
45882
46012
  if (isRecord2(actions)) {
@@ -45891,6 +46021,56 @@ async function validateGeoRunArtifacts(input) {
45891
46021
  errors
45892
46022
  );
45893
46023
  }
46024
+ for (const channel of presentChannels) {
46025
+ const channelActions = await readJson2(join5(runDir, channel, "actions.json"), errors);
46026
+ if (isRecord2(channelActions)) {
46027
+ validateActionsArtifact(
46028
+ channelActions,
46029
+ {
46030
+ runId: input.runId,
46031
+ policy,
46032
+ isCommerce: isCommerceRun(context, products),
46033
+ productIds: collectProductIds(products),
46034
+ channel
46035
+ },
46036
+ errors
46037
+ );
46038
+ }
46039
+ if (!await exists3(join5(runDir, channel, "rationale.md"))) {
46040
+ errors.push(`Missing agent/runs/${input.runId}/${channel}/rationale.md`);
46041
+ }
46042
+ }
46043
+ return {
46044
+ ok: errors.length === 0,
46045
+ errors,
46046
+ warnings: [],
46047
+ runDir
46048
+ };
46049
+ }
46050
+ async function validateGeoChannelArtifacts(input) {
46051
+ const runDir = join5(input.repoPath, "agent", "runs", input.runId);
46052
+ const errors = [];
46053
+ const context = await readJson2(join5(runDir, "context.json"), errors);
46054
+ const policy = await readJson2(join5(runDir, "policy.json"), errors);
46055
+ const products = await readJson2(join5(runDir, "products.json"), errors);
46056
+ const actionsPath = join5(runDir, input.channel, "actions.json");
46057
+ const actions = await readJson2(actionsPath, errors);
46058
+ if (!await exists3(join5(runDir, input.channel, "rationale.md"))) {
46059
+ errors.push(`Missing agent/runs/${input.runId}/${input.channel}/rationale.md`);
46060
+ }
46061
+ if (isRecord2(actions)) {
46062
+ validateActionsArtifact(
46063
+ actions,
46064
+ {
46065
+ runId: input.runId,
46066
+ policy,
46067
+ isCommerce: isCommerceRun(context, products),
46068
+ productIds: collectProductIds(products),
46069
+ channel: input.channel
46070
+ },
46071
+ errors
46072
+ );
46073
+ }
45894
46074
  return {
45895
46075
  ok: errors.length === 0,
45896
46076
  errors,
@@ -45923,7 +46103,7 @@ function validateManifest(value, runId, errors) {
45923
46103
  }
45924
46104
  }
45925
46105
  function validateActionsArtifact(artifact, context, errors) {
45926
- const parsed = GeoStrategyActionsArtifactSchema.safeParse(artifact);
46106
+ const parsed = context.channel ? GeoStrategyChannelActionsArtifactSchema.safeParse(artifact) : GeoStrategyActionsArtifactSchema.safeParse(artifact);
45927
46107
  if (!parsed.success) {
45928
46108
  for (const issue of parsed.error.issues) {
45929
46109
  errors.push(
@@ -45934,10 +46114,13 @@ function validateActionsArtifact(artifact, context, errors) {
45934
46114
  }
45935
46115
  rejectExtraKeys(
45936
46116
  artifact,
45937
- ["schemaVersion", "runId", "policy", "actions"],
46117
+ context.channel ? ["schemaVersion", "runId", "channel", "policy", "actions"] : ["schemaVersion", "runId", "policy", "actions"],
45938
46118
  "actions.json",
45939
46119
  errors
45940
46120
  );
46121
+ if (context.channel && artifact.channel !== context.channel) {
46122
+ errors.push(`actions.json channel must be ${context.channel}`);
46123
+ }
45941
46124
  if (artifact.schemaVersion !== 1) errors.push("actions.json schemaVersion must be 1");
45942
46125
  if (artifact.runId !== context.runId) {
45943
46126
  errors.push(`actions.json runId must match ${context.runId}`);
@@ -46002,6 +46185,12 @@ function validateActionItem(action, index2, context, seen, errors) {
46002
46185
  if (!["brand_owned", "geo_page", "third_party"].includes(String(action.assetType))) {
46003
46186
  errors.push(`${path2}.assetType must be brand_owned, geo_page, or third_party`);
46004
46187
  }
46188
+ if (context.channel) {
46189
+ const expectedAssetType = context.channel === "geo_pages" ? "geo_page" : context.channel;
46190
+ if (action.assetType !== expectedAssetType) {
46191
+ errors.push(`${path2}.assetType must be ${expectedAssetType} for ${context.channel}`);
46192
+ }
46193
+ }
46005
46194
  validateIntentRef(action, path2, errors);
46006
46195
  validateActionFields(action, path2, errors);
46007
46196
  validateActionByType(action, path2, context, errors);
@@ -46250,11 +46439,14 @@ Examples:
46250
46439
  $ anymorph validate 20260517T074200Z
46251
46440
  $ anymorph validate run 20260517T074200Z
46252
46441
  $ anymorph validate actions 20260517T074200Z
46442
+ $ anymorph validate brand-owned 20260517T074200Z
46443
+ $ anymorph validate geo-pages 20260517T074200Z
46444
+ $ anymorph validate third-party 20260517T074200Z
46253
46445
  $ anymorph validate mdx content/guide/page.mdx
46254
46446
  $ anymorph validate mdx "content/**/*.mdx"
46255
46447
  $ anymorph validate 20260517T074200Z --json
46256
46448
 
46257
- Checks required run files, the shared actions.json contract, policy hash,
46449
+ Checks required run files, shared root/channel actions.json contracts, policy hash,
46258
46450
  per-asset target rules, commerce targetProductIds, or CMS renderer MDX validation.`
46259
46451
  ).action(async (runId, opts) => {
46260
46452
  if (!runId) {
@@ -46269,6 +46461,21 @@ per-asset target rules, commerce targetProductIds, or CMS renderer MDX validatio
46269
46461
  validateCommand.addCommand(
46270
46462
  new Command("actions").argument("<runId>").description("Validate actions.json and related local GEO run artifacts").option("--json", "Output as JSON").action(geoValidateCommand)
46271
46463
  );
46464
+ validateCommand.addCommand(
46465
+ new Command("brand-owned").argument("<runId>").description("Validate brand_owned/actions.json for a local GEO run").option("--json", "Output as JSON").action(
46466
+ (runId, opts) => geoValidateChannelCommand(runId, "brand_owned", opts)
46467
+ )
46468
+ );
46469
+ validateCommand.addCommand(
46470
+ new Command("geo-pages").argument("<runId>").description("Validate geo_pages/actions.json for a local GEO run").option("--json", "Output as JSON").action(
46471
+ (runId, opts) => geoValidateChannelCommand(runId, "geo_pages", opts)
46472
+ )
46473
+ );
46474
+ validateCommand.addCommand(
46475
+ new Command("third-party").argument("<runId>").description("Validate third_party/actions.json for a local GEO run").option("--json", "Output as JSON").action(
46476
+ (runId, opts) => geoValidateChannelCommand(runId, "third_party", opts)
46477
+ )
46478
+ );
46272
46479
  validateCommand.addCommand(
46273
46480
  new Command("mdx").argument("<paths...>").description("Validate CMS MDX with the renderer publish gate").option("--json", "Output as JSON").addHelpText(
46274
46481
  "after",
@@ -46439,6 +46646,24 @@ async function geoValidateCommand(runId, opts) {
46439
46646
  console.error();
46440
46647
  process.exit(1);
46441
46648
  }
46649
+ async function geoValidateChannelCommand(runId, channel, opts) {
46650
+ const repoPath = await resolveCurrentRepoRoot();
46651
+ const result = await validateGeoChannelArtifacts({ repoPath, runId, channel });
46652
+ if (opts.json) {
46653
+ console.log(JSON.stringify({ ...result, repoPath, channel }, null, 2));
46654
+ process.exit(result.ok ? 0 : 1);
46655
+ }
46656
+ if (result.ok) {
46657
+ console.log(source_default.green(`
46658
+ GEO ${channel} actions for ${runId} are valid.
46659
+ `));
46660
+ return;
46661
+ }
46662
+ console.error(source_default.red("\n Validation failed\n"));
46663
+ for (const error of result.errors) console.error(` - ${error}`);
46664
+ console.error();
46665
+ process.exit(1);
46666
+ }
46442
46667
  async function geoValidateMdxCommand(paths, opts) {
46443
46668
  const json = opts.json || paths.includes("--json");
46444
46669
  const cleanPaths = paths.filter((path2) => path2 !== "--json");
@@ -46698,7 +46923,7 @@ function printScaffoldError(prefix, err) {
46698
46923
 
46699
46924
  // src/index.ts
46700
46925
  var program2 = new Command();
46701
- program2.name("anymorph").description("Check AI visibility and run local GEO strategy workflows").version("0.11.0");
46926
+ program2.name("anymorph").description("Check AI visibility and run local GEO strategy workflows").version("0.13.0");
46702
46927
  program2.command("login").description("Sign in to your Anymorph account").addHelpText(
46703
46928
  "after",
46704
46929
  `
@@ -46775,7 +47000,11 @@ Command usage:
46775
47000
  Must be run from the tenant repo root.
46776
47001
 
46777
47002
  anymorph validate <runId> [--json]
46778
- Validate local run files and actions.json before pushing.
47003
+ Validate local run files and channel actions before pushing.
47004
+ Must be run from the tenant repo root.
47005
+
47006
+ anymorph validate brand-owned|geo-pages|third-party <runId> [--json]
47007
+ Validate one channel actions artifact before pushing.
46779
47008
  Must be run from the tenant repo root.
46780
47009
 
46781
47010
  anymorph validate mdx <paths...> [--json]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anymorph",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Check your brand's AI visibility across ChatGPT, Perplexity, Gemini, and more",
5
5
  "type": "module",
6
6
  "private": false,