orionfold-relay 0.22.1 → 0.24.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 (132) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.js +139 -55
  3. package/drizzle.config.ts +2 -2
  4. package/next.config.mjs +52 -1
  5. package/package.json +2 -1
  6. package/src/app/api/license/route.ts +1 -1
  7. package/src/app/apps/page.tsx +1 -1
  8. package/src/app/inbox/page.tsx +1 -1
  9. package/src/app/layout.tsx +8 -3
  10. package/src/app/packs/page.tsx +255 -40
  11. package/src/app/projects/[id]/page.tsx +0 -11
  12. package/src/components/apps/last-run-card.tsx +1 -1
  13. package/src/components/apps/ledger-hero-panel.tsx +1 -1
  14. package/src/components/charts/time-series-chart.tsx +1 -1
  15. package/src/components/chat/app-materialized-card.tsx +1 -1
  16. package/src/components/chat/app-view-editor-card.tsx +1 -1
  17. package/src/components/chat/chat-command-popover.tsx +2 -2
  18. package/src/components/chat/chat-empty-state.tsx +1 -1
  19. package/src/components/chat/chat-message.tsx +2 -1
  20. package/src/components/chat/chat-session-provider.tsx +2 -2
  21. package/src/components/chat/conversation-template-picker.tsx +1 -1
  22. package/src/components/chat/skill-row.tsx +1 -15
  23. package/src/components/customers/customer-form-sheet.tsx +1 -1
  24. package/src/components/dashboard/greeting.tsx +1 -1
  25. package/src/components/dashboard/welcome-landing.tsx +2 -2
  26. package/src/components/documents/document-upload-dialog.tsx +1 -1
  27. package/src/components/documents/smart-extracted-text.tsx +1 -1
  28. package/src/components/instance/instance-section.tsx +1 -1
  29. package/src/components/instance/upgrade-badge.tsx +1 -1
  30. package/src/components/notifications/batch-proposal-review.tsx +1 -1
  31. package/src/components/notifications/inbox-list.tsx +41 -1
  32. package/src/components/notifications/unread-badge.tsx +28 -2
  33. package/src/components/onboarding/runtime-preference-modal.tsx +32 -3
  34. package/src/components/packs/pack-update-button.tsx +1 -1
  35. package/src/components/profiles/profile-detail-view.tsx +1 -1
  36. package/src/components/projects/project-create-dialog.tsx +1 -1
  37. package/src/components/projects/project-form-sheet.tsx +2 -2
  38. package/src/components/schedules/schedule-create-dialog.tsx +1 -1
  39. package/src/components/schedules/schedule-create-sheet.tsx +1 -1
  40. package/src/components/schedules/schedule-edit-sheet.tsx +1 -1
  41. package/src/components/schedules/schedule-form.tsx +1 -1
  42. package/src/components/settings/data-management-section.tsx +2 -2
  43. package/src/components/settings/database-snapshots-section.tsx +6 -6
  44. package/src/components/settings/ollama-section.tsx +2 -2
  45. package/src/components/settings/presets-section.tsx +2 -2
  46. package/src/components/settings/providers-runtimes-section.tsx +6 -6
  47. package/src/components/settings/web-search-section.tsx +1 -1
  48. package/src/components/shared/command-palette.tsx +2 -2
  49. package/src/components/shared/trust-tier-badge.tsx +3 -3
  50. package/src/components/shell/app-bar.tsx +156 -164
  51. package/src/components/shell/app-shell.tsx +16 -8
  52. package/src/components/shell/nav-items.ts +72 -30
  53. package/src/components/tasks/ai-assist-panel.tsx +2 -2
  54. package/src/components/tasks/task-create-panel.tsx +1 -1
  55. package/src/components/tasks/task-edit-dialog.tsx +1 -1
  56. package/src/components/workflows/blueprint-gallery.tsx +1 -1
  57. package/src/components/workflows/delay-step-body.tsx +1 -1
  58. package/src/components/workflows/workflow-confirmation-view.tsx +1 -1
  59. package/src/components/workflows/workflow-form-view.tsx +1 -1
  60. package/src/components/workspace/discover-workspace-dialog.tsx +1 -1
  61. package/src/instrumentation-node.ts +1 -1
  62. package/src/lib/agents/runtime/anthropic-direct.ts +72 -9
  63. package/src/lib/agents/runtime/openai-codex.ts +1 -1
  64. package/src/lib/apps/apps-events.ts +15 -0
  65. package/src/lib/apps/registry.ts +1 -1
  66. package/src/lib/apps/use-apps.ts +4 -3
  67. package/src/lib/channels/slack-adapter.ts +1 -1
  68. package/src/lib/channels/telegram-adapter.ts +1 -1
  69. package/src/lib/channels/webhook-adapter.ts +2 -2
  70. package/src/lib/chat/system-prompt.ts +4 -4
  71. package/src/lib/chat/tool-catalog.ts +3 -3
  72. package/src/lib/chat/tools/app-view-tools.ts +3 -2
  73. package/src/lib/data/seed-data/environment.ts +2 -2
  74. package/src/lib/db/bootstrap.ts +5 -1
  75. package/src/lib/packs/format.ts +38 -2
  76. package/src/lib/packs/templates/relay-agency/pack.yaml +2 -1
  77. package/src/lib/packs/templates/relay-agency-pro/pack.yaml +16 -7
  78. package/src/lib/plugins/examples/echo-server/plugin.yaml +1 -1
  79. package/src/lib/plugins/examples/finance-pack/plugin.yaml +1 -1
  80. package/src/lib/plugins/examples/reading-radar/plugin.yaml +1 -1
  81. package/src/lib/plugins/registry.ts +1 -1
  82. package/src/lib/plugins/sdk/types.ts +1 -1
  83. package/src/lib/theme.ts +23 -11
  84. package/src/lib/utils/migrate-to-ainative.ts +82 -41
  85. package/src/lib/workflows/blueprints/registry.ts +59 -10
  86. package/src/app/analytics/page.tsx +0 -40
  87. package/src/app/api/environment/artifacts/[id]/route.ts +0 -17
  88. package/src/app/api/environment/artifacts/route.ts +0 -33
  89. package/src/app/api/environment/checkpoints/[id]/route.ts +0 -86
  90. package/src/app/api/environment/checkpoints/route.ts +0 -80
  91. package/src/app/api/environment/profiles/create/route.ts +0 -28
  92. package/src/app/api/environment/profiles/suggest/route.ts +0 -41
  93. package/src/app/api/environment/scan/route.ts +0 -70
  94. package/src/app/api/environment/sync/history/route.ts +0 -20
  95. package/src/app/api/environment/sync/preview/route.ts +0 -27
  96. package/src/app/api/environment/sync/route.ts +0 -43
  97. package/src/app/api/environment/templates/[id]/route.ts +0 -34
  98. package/src/app/api/environment/templates/route.ts +0 -27
  99. package/src/app/environment/compare/page.tsx +0 -20
  100. package/src/app/environment/loading.tsx +0 -56
  101. package/src/app/environment/page.tsx +0 -61
  102. package/src/app/environment/skills/page.tsx +0 -20
  103. package/src/components/analytics/analytics-dashboard.tsx +0 -200
  104. package/src/components/environment/adoption-prompt.tsx +0 -76
  105. package/src/components/environment/artifact-card.tsx +0 -93
  106. package/src/components/environment/artifact-detail-sheet.tsx +0 -136
  107. package/src/components/environment/artifact-presence-cell.tsx +0 -44
  108. package/src/components/environment/category-filter-bar.tsx +0 -132
  109. package/src/components/environment/checkpoint-list.tsx +0 -169
  110. package/src/components/environment/comparison-matrix.tsx +0 -132
  111. package/src/components/environment/environment-dashboard.tsx +0 -248
  112. package/src/components/environment/environment-summary-card.tsx +0 -153
  113. package/src/components/environment/health-score-card.tsx +0 -86
  114. package/src/components/environment/persona-indicator.tsx +0 -43
  115. package/src/components/environment/profile-create-dialog.tsx +0 -178
  116. package/src/components/environment/project-diff-view.tsx +0 -59
  117. package/src/components/environment/project-scan-badge.tsx +0 -36
  118. package/src/components/environment/rollback-confirm-dialog.tsx +0 -105
  119. package/src/components/environment/scan-status-bar.tsx +0 -48
  120. package/src/components/environment/skill-catalog.tsx +0 -117
  121. package/src/components/environment/skill-detail-sheet.tsx +0 -106
  122. package/src/components/environment/skill-drift-indicator.tsx +0 -42
  123. package/src/components/environment/suggested-profiles.tsx +0 -162
  124. package/src/components/environment/summary-cards-row.tsx +0 -91
  125. package/src/components/environment/sync-action-buttons.tsx +0 -63
  126. package/src/components/environment/sync-preview-dialog.tsx +0 -205
  127. package/src/components/environment/template-list.tsx +0 -149
  128. package/src/components/environment/tool-comparison-view.tsx +0 -75
  129. package/src/lib/analytics/queries.ts +0 -207
  130. package/src/lib/environment/comparison.ts +0 -170
  131. package/src/lib/environment/health-scoring.ts +0 -162
  132. package/src/lib/environment/skill-portfolio.ts +0 -97
package/README.md CHANGED
@@ -104,7 +104,7 @@ The governance is *in* the workflow, not bolted on. A blueprint is a fixed step
104
104
  ## Why it stays trustworthy
105
105
 
106
106
  - **Local-first** — SQLite database, no cloud dependency, `npx orionfold-relay` and go
107
- - **Never phones home** — no telemetry, no update checks, no license server; the complete outbound-network inventory is documented and code-linked in [docs/trust/data-flow.md](docs/trust/data-flow.md)
107
+ - **Relay never sends your data to Orionfold** — no telemetry, no update checks, no license server; the complete outbound-network inventory is documented and code-linked in [docs/trust/data-flow.md](docs/trust/data-flow.md)
108
108
  - **Your rules, enforced** — tool permissions, inbox approvals, and audit trails for every agent action
109
109
  - **Your AI team** — 21 specialist profiles ready to deploy, each with instructions, tool policies, and runtime tuning
110
110
  - **Know what you spend** — usage metering, budgets, and per-provider/per-model spend visibility on governed runs
@@ -134,8 +134,8 @@ relay license remove <license-id> # forget a license
134
134
 
135
135
  - **Verification is 100% offline** — an Ed25519 signature check against keys embedded in
136
136
  this repo ([`src/lib/licensing/verify.ts`](src/lib/licensing/verify.ts)). Relay never
137
- phones home: no activation server, no telemetry, no network call of any kind. Works
138
- air-gapped.
137
+ sends your data to Orionfold: no activation server, no telemetry, no network call of
138
+ any kind. Works air-gapped.
139
139
  - **Your packs are yours forever. Renewal gets you the year's new and updated packs +
140
140
  priority support.** An expired or removed license never re-locks content you already
141
141
  installed — it only gates new premium installs and updates.
package/dist/cli.js CHANGED
@@ -464,7 +464,7 @@ function bootstrapAinativeDatabase(sqlite3) {
464
464
  sqlite3.exec(ddl);
465
465
  } catch (err2) {
466
466
  const msg = err2 instanceof Error ? err2.message : String(err2);
467
- if (!msg.includes("duplicate column")) {
467
+ if (!msg.includes("duplicate column") && !msg.includes("no such table")) {
468
468
  console.error("[bootstrap] ALTER TABLE failed:", msg);
469
469
  }
470
470
  }
@@ -1186,7 +1186,7 @@ var CURRENT_PLUGIN_API_VERSION, CAPABILITY_VALUES, ORIGIN_VALUES, PrimitivesBund
1186
1186
  var init_types = __esm({
1187
1187
  "src/lib/plugins/sdk/types.ts"() {
1188
1188
  "use strict";
1189
- CURRENT_PLUGIN_API_VERSION = "0.22";
1189
+ CURRENT_PLUGIN_API_VERSION = "0.24";
1190
1190
  CAPABILITY_VALUES = ["fs", "net", "child_process", "env"];
1191
1191
  ORIGIN_VALUES = ["ainative-internal", "third-party"];
1192
1192
  PrimitivesBundleManifestSchema = z.object({
@@ -3739,9 +3739,25 @@ var init_format = __esm({
3739
3739
  /**
3740
3740
  * Premium display copy (D6). Offline strings rendered on the locked
3741
3741
  * gallery card — the Website still owns actual pricing. Meaningful only
3742
- * alongside `entitlement`; harmless on a free pack.
3742
+ * alongside `entitlement`; harmless on a free pack. Either a flat string
3743
+ * ("$499/year") or a two-phase offer ({ list, intro?, note? }) so a
3744
+ * founding/introductory price can render alongside the list price.
3745
+ * Render sites consume `packPrice()` — never branch on the raw shape.
3743
3746
  */
3744
- price: z3.string().min(1).optional(),
3747
+ price: z3.union([
3748
+ z3.string().min(1),
3749
+ z3.object({
3750
+ list: z3.string().min(1),
3751
+ intro: z3.string().min(1).optional(),
3752
+ note: z3.string().min(1).optional()
3753
+ }).strict()
3754
+ ]).optional(),
3755
+ /**
3756
+ * Card identity token — a lucide icon name rendered on the gallery card
3757
+ * (e.g. "briefcase"). Unknown tokens fall back to the default glyph;
3758
+ * never a remote asset.
3759
+ */
3760
+ icon: z3.string().min(1).optional(),
3745
3761
  /** Get-license CTA target on the locked card. */
3746
3762
  purchaseUrl: z3.url().optional(),
3747
3763
  /**
@@ -5779,6 +5795,13 @@ __export(registry_exports3, {
5779
5795
  import fs11 from "fs";
5780
5796
  import path11 from "path";
5781
5797
  import yaml7 from "js-yaml";
5798
+ function userDirMtimeMs() {
5799
+ try {
5800
+ return fs11.statSync(USER_BLUEPRINTS_DIR).mtimeMs;
5801
+ } catch {
5802
+ return null;
5803
+ }
5804
+ }
5782
5805
  function scanDirectory(dir, isBuiltin2) {
5783
5806
  const blueprints = /* @__PURE__ */ new Map();
5784
5807
  if (!fs11.existsSync(dir)) return blueprints;
@@ -5813,8 +5836,13 @@ function loadAll() {
5813
5836
  return all;
5814
5837
  }
5815
5838
  function ensureLoaded2() {
5839
+ if (blueprintCache && userDirMtimeMs() !== cachedDirMtimeMs) {
5840
+ blueprintCache = null;
5841
+ }
5816
5842
  if (!blueprintCache) {
5843
+ cachedDirMtimeMs = userDirMtimeMs();
5817
5844
  blueprintCache = loadAll();
5845
+ applyPluginBlueprints(blueprintCache);
5818
5846
  }
5819
5847
  return blueprintCache;
5820
5848
  }
@@ -5862,30 +5890,35 @@ function deleteBlueprint(id) {
5862
5890
  function getUserBlueprintsDir() {
5863
5891
  return USER_BLUEPRINTS_DIR;
5864
5892
  }
5893
+ function applyPluginBlueprints(cache) {
5894
+ for (const byId of pluginBlueprints.values()) {
5895
+ for (const [id, bp] of byId) cache.set(id, bp);
5896
+ }
5897
+ }
5865
5898
  function mergePluginBlueprints(entries) {
5866
5899
  const cache = ensureLoaded2();
5867
5900
  for (const entry of entries) {
5868
5901
  cache.set(entry.blueprint.id, entry.blueprint);
5869
- if (!pluginBlueprintIndex.has(entry.pluginId)) {
5870
- pluginBlueprintIndex.set(entry.pluginId, /* @__PURE__ */ new Set());
5902
+ if (!pluginBlueprints.has(entry.pluginId)) {
5903
+ pluginBlueprints.set(entry.pluginId, /* @__PURE__ */ new Map());
5871
5904
  }
5872
- pluginBlueprintIndex.get(entry.pluginId).add(entry.blueprint.id);
5905
+ pluginBlueprints.get(entry.pluginId).set(entry.blueprint.id, entry.blueprint);
5873
5906
  }
5874
5907
  }
5875
5908
  function clearPluginBlueprints(pluginId) {
5876
5909
  const cache = blueprintCache;
5877
- const ids = pluginBlueprintIndex.get(pluginId);
5878
- if (!ids) return;
5879
- if (cache) for (const id of ids) cache.delete(id);
5880
- pluginBlueprintIndex.delete(pluginId);
5910
+ const byId = pluginBlueprints.get(pluginId);
5911
+ if (!byId) return;
5912
+ if (cache) for (const id of byId.keys()) cache.delete(id);
5913
+ pluginBlueprints.delete(pluginId);
5881
5914
  }
5882
5915
  function clearAllPluginBlueprints() {
5883
- for (const pluginId of Array.from(pluginBlueprintIndex.keys())) {
5916
+ for (const pluginId of Array.from(pluginBlueprints.keys())) {
5884
5917
  clearPluginBlueprints(pluginId);
5885
5918
  }
5886
5919
  }
5887
5920
  function listPluginBlueprintIds(pluginId) {
5888
- return Array.from(pluginBlueprintIndex.get(pluginId) ?? []);
5921
+ return Array.from(pluginBlueprints.get(pluginId)?.keys() ?? []);
5889
5922
  }
5890
5923
  function validateBlueprintRefs(bp, opts2) {
5891
5924
  for (const step of bp.steps ?? []) {
@@ -5907,7 +5940,7 @@ function validateBlueprintRefs(bp, opts2) {
5907
5940
  }
5908
5941
  return { ok: true };
5909
5942
  }
5910
- var BUILTINS_DIR2, USER_BLUEPRINTS_DIR, blueprintCache, pluginBlueprintIndex;
5943
+ var BUILTINS_DIR2, USER_BLUEPRINTS_DIR, blueprintCache, cachedDirMtimeMs, pluginBlueprints;
5911
5944
  var init_registry3 = __esm({
5912
5945
  "src/lib/workflows/blueprints/registry.ts"() {
5913
5946
  "use strict";
@@ -5925,7 +5958,8 @@ var init_registry3 = __esm({
5925
5958
  );
5926
5959
  USER_BLUEPRINTS_DIR = getAinativeBlueprintsDir();
5927
5960
  blueprintCache = null;
5928
- pluginBlueprintIndex = /* @__PURE__ */ new Map();
5961
+ cachedDirMtimeMs = null;
5962
+ pluginBlueprints = /* @__PURE__ */ new Map();
5929
5963
  }
5930
5964
  });
5931
5965
 
@@ -12929,7 +12963,7 @@ var init_registry6 = __esm({
12929
12963
  init_registry5();
12930
12964
  init_installer();
12931
12965
  init_schedule_spec();
12932
- SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.21"]);
12966
+ SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.23"]);
12933
12967
  pluginCache = null;
12934
12968
  lastLoadedPluginIds = /* @__PURE__ */ new Set();
12935
12969
  PluginTableSchema = z16.object({
@@ -17601,11 +17635,20 @@ var init_plugin_spec_tools = __esm({
17601
17635
  }
17602
17636
  });
17603
17637
 
17638
+ // src/lib/apps/apps-events.ts
17639
+ var APPS_CHANGED_EVENT;
17640
+ var init_apps_events = __esm({
17641
+ "src/lib/apps/apps-events.ts"() {
17642
+ "use strict";
17643
+ APPS_CHANGED_EVENT = "relay-apps-changed";
17644
+ }
17645
+ });
17646
+
17604
17647
  // src/lib/chat/tools/app-view-tools.ts
17605
17648
  import { z as z28 } from "zod";
17606
17649
  function dispatchAppsChangedFromTool() {
17607
17650
  if (typeof window !== "undefined") {
17608
- window.dispatchEvent(new CustomEvent("ainative-apps-changed"));
17651
+ window.dispatchEvent(new CustomEvent(APPS_CHANGED_EVENT));
17609
17652
  }
17610
17653
  }
17611
17654
  function loadManifestOrError(appId) {
@@ -17742,6 +17785,7 @@ var init_app_view_tools = __esm({
17742
17785
  "src/lib/chat/tools/app-view-tools.ts"() {
17743
17786
  "use strict";
17744
17787
  init_tool_registry();
17788
+ init_apps_events();
17745
17789
  init_helpers2();
17746
17790
  init_registry();
17747
17791
  BindingsSchema = ViewSchema.shape.bindings;
@@ -20139,7 +20183,7 @@ async function handleServerRequest(client, taskId, request) {
20139
20183
  contentItems: [
20140
20184
  {
20141
20185
  type: "inputText",
20142
- text: "Dynamic tool calls are not supported by ainative's Codex runtime yet."
20186
+ text: "Dynamic tool calls are not supported by Relay's Codex runtime yet."
20143
20187
  }
20144
20188
  ]
20145
20189
  });
@@ -20746,6 +20790,24 @@ async function withAnthropicDirectMcpServers(profileServers, browserServers, ext
20746
20790
  relay: relayServer
20747
20791
  };
20748
20792
  }
20793
+ function mcpServersToAnthropicConnectors(mergedServers) {
20794
+ const connectors = [];
20795
+ for (const [name, config] of Object.entries(mergedServers)) {
20796
+ if (name === "relay") continue;
20797
+ const cfg = config;
20798
+ const url = cfg.url ?? cfg.server_url;
20799
+ if (typeof url !== "string" || url.length === 0) {
20800
+ continue;
20801
+ }
20802
+ const connector = { type: "url", url, name };
20803
+ for (const [key, value] of Object.entries(cfg)) {
20804
+ if (["url", "server_url", "command", "args", "env"].includes(key)) continue;
20805
+ if (typeof value === "string") connector[key] = value;
20806
+ }
20807
+ connectors.push(connector);
20808
+ }
20809
+ return connectors;
20810
+ }
20749
20811
  async function getAnthropicSDK() {
20750
20812
  if (!_sdk) {
20751
20813
  _sdk = await import("@anthropic-ai/sdk");
@@ -20812,7 +20874,7 @@ async function callAnthropicModel(client, systemPrompt, messages, tools, signal,
20812
20874
  budget_tokens: options.extendedThinking.budgetTokens ?? 1e4
20813
20875
  };
20814
20876
  }
20815
- if (options.mcpServers && Object.keys(options.mcpServers).length > 0) {
20877
+ if (options.mcpServers && options.mcpServers.length > 0) {
20816
20878
  params.mcp_servers = options.mcpServers;
20817
20879
  }
20818
20880
  const stream = client.messages.stream(params, { signal });
@@ -20917,6 +20979,7 @@ ${outputInstructions}`;
20917
20979
  pluginServers,
20918
20980
  task.projectId
20919
20981
  );
20982
+ const mcpConnectors = mcpServersToAnthropicConnectors(mergedMcpServers);
20920
20983
  let initialMessages;
20921
20984
  if (isResume) {
20922
20985
  const snapshot = await loadSessionSnapshot(taskId);
@@ -20965,7 +21028,7 @@ ${outputInstructions}`;
20965
21028
  enableCaching: true,
20966
21029
  profileInstructions: profile?.skillMd,
20967
21030
  extendedThinking: capOverrides?.extendedThinking,
20968
- mcpServers: mergedMcpServers
21031
+ mcpServers: mcpConnectors
20969
21032
  }
20970
21033
  );
20971
21034
  await saveSessionSnapshot(taskId, agentProfileId, [
@@ -25740,8 +25803,8 @@ import { execFileSync as execFileSync3 } from "child_process";
25740
25803
  import yaml12 from "js-yaml";
25741
25804
  import semver from "semver";
25742
25805
  function relayCoreVersion() {
25743
- if (semver.valid("0.22.1")) {
25744
- return "0.22.1";
25806
+ if (semver.valid("0.24.0")) {
25807
+ return "0.24.0";
25745
25808
  }
25746
25809
  try {
25747
25810
  const root = getAppRoot(import.meta.dirname, 3);
@@ -26984,6 +27047,10 @@ function hasSqliteHeader(path24) {
26984
27047
  return false;
26985
27048
  }
26986
27049
  }
27050
+ var MIGRATION_CHAIN = [
27051
+ { fromDir: ".stagent", toDir: ".ainative", fromDb: "stagent.db", toDb: "ainative.db" },
27052
+ { fromDir: ".ainative", toDir: ".relay", fromDb: "ainative.db", toDb: "relay.db" }
27053
+ ];
26987
27054
  async function migrateLegacyData(options = {}) {
26988
27055
  const home = options.home ?? homedir2();
26989
27056
  const gitDir = options.gitDir ?? join6(process.cwd(), ".git");
@@ -26996,46 +27063,50 @@ async function migrateLegacyData(options = {}) {
26996
27063
  keychainMigrated: false,
26997
27064
  errors: []
26998
27065
  };
26999
- const oldDir = join6(home, ".stagent");
27000
- const newDir = join6(home, ".ainative");
27001
- if (existsSync4(oldDir) && !existsSync4(newDir)) {
27002
- try {
27003
- renameSync(oldDir, newDir);
27004
- report.dirMigrated = true;
27005
- log(`renamed ${oldDir} -> ${newDir}`);
27006
- } catch (err2) {
27007
- const e = err2;
27008
- if (e.code === "EXDEV") {
27009
- try {
27010
- cpSync(oldDir, newDir, { recursive: true });
27011
- rmSync2(oldDir, { recursive: true, force: true });
27012
- report.dirMigrated = true;
27013
- log(`copied ${oldDir} -> ${newDir} (cross-device fallback)`);
27014
- } catch (copyErr) {
27015
- report.errors.push(`dir copy failed: ${String(copyErr)}`);
27066
+ const finalDir = join6(home, MIGRATION_CHAIN[MIGRATION_CHAIN.length - 1].toDir);
27067
+ const finalDbName = MIGRATION_CHAIN[MIGRATION_CHAIN.length - 1].toDb;
27068
+ for (const hop of MIGRATION_CHAIN) {
27069
+ const oldDir = join6(home, hop.fromDir);
27070
+ const newDir = join6(home, hop.toDir);
27071
+ if (existsSync4(oldDir) && !existsSync4(newDir)) {
27072
+ try {
27073
+ renameSync(oldDir, newDir);
27074
+ report.dirMigrated = true;
27075
+ log(`renamed ${oldDir} -> ${newDir}`);
27076
+ } catch (err2) {
27077
+ const e = err2;
27078
+ if (e.code === "EXDEV") {
27079
+ try {
27080
+ cpSync(oldDir, newDir, { recursive: true });
27081
+ rmSync2(oldDir, { recursive: true, force: true });
27082
+ report.dirMigrated = true;
27083
+ log(`copied ${oldDir} -> ${newDir} (cross-device fallback)`);
27084
+ } catch (copyErr) {
27085
+ report.errors.push(`dir copy failed: ${String(copyErr)}`);
27086
+ return report;
27087
+ }
27088
+ } else {
27089
+ report.errors.push(`dir rename failed: ${String(err2)}`);
27016
27090
  return report;
27017
27091
  }
27018
- } else {
27019
- report.errors.push(`dir rename failed: ${String(err2)}`);
27020
- return report;
27021
27092
  }
27022
27093
  }
27023
- }
27024
- if (existsSync4(newDir)) {
27025
- for (const suffix of ["", "-shm", "-wal"]) {
27026
- const oldName = join6(newDir, `stagent.db${suffix}`);
27027
- const newName = join6(newDir, `ainative.db${suffix}`);
27028
- if (existsSync4(oldName) && !existsSync4(newName)) {
27029
- try {
27030
- renameSync(oldName, newName);
27031
- report.dbFilesRenamed++;
27032
- } catch (err2) {
27033
- report.errors.push(`db file rename failed (${suffix}): ${String(err2)}`);
27094
+ if (existsSync4(newDir)) {
27095
+ for (const suffix of ["", "-shm", "-wal"]) {
27096
+ const oldName = join6(newDir, `${hop.fromDb}${suffix}`);
27097
+ const newName = join6(newDir, `${hop.toDb}${suffix}`);
27098
+ if (existsSync4(oldName) && !existsSync4(newName)) {
27099
+ try {
27100
+ renameSync(oldName, newName);
27101
+ report.dbFilesRenamed++;
27102
+ } catch (err2) {
27103
+ report.errors.push(`db file rename failed (${suffix}): ${String(err2)}`);
27104
+ }
27034
27105
  }
27035
27106
  }
27036
27107
  }
27037
27108
  }
27038
- const dbPath4 = join6(newDir, "ainative.db");
27109
+ const dbPath4 = join6(finalDir, finalDbName);
27039
27110
  if (existsSync4(dbPath4) && !hasSqliteHeader(dbPath4)) {
27040
27111
  log(`skipping SQL migration \u2014 ${dbPath4} exists but lacks SQLite header`);
27041
27112
  }
@@ -27104,7 +27175,8 @@ var __dirname = dirname5(fileURLToPath4(import.meta.url));
27104
27175
  var appDir = join21(__dirname, "..");
27105
27176
  var launchCwd2 = process.cwd();
27106
27177
  var _envLocalPath = join21(launchCwd2, ".env.local");
27107
- var _firstRunNeedsEnv = !existsSync13(_envLocalPath) && !process.env.RELAY_DATA_DIR && !isDevMode(launchCwd2);
27178
+ var _hasDataDirFlag = process.argv.slice(2).some((a) => a === "--data-dir" || a.startsWith("--data-dir="));
27179
+ var _firstRunNeedsEnv = !existsSync13(_envLocalPath) && !process.env.RELAY_DATA_DIR && !_hasDataDirFlag && !isDevMode(launchCwd2);
27108
27180
  if (_firstRunNeedsEnv) {
27109
27181
  const folderName = basename6(launchCwd2);
27110
27182
  const autoDataDir = join21(homedir8(), `.${folderName}`);
@@ -27144,6 +27216,18 @@ if (existsSync13(_envLocalPath)) {
27144
27216
  }
27145
27217
  }
27146
27218
  }
27219
+ function scanDataDirFlag(argv) {
27220
+ for (let i = 0; i < argv.length; i++) {
27221
+ const tok = argv[i];
27222
+ if (tok === "--data-dir") return argv[i + 1];
27223
+ if (tok.startsWith("--data-dir=")) return tok.slice("--data-dir=".length);
27224
+ }
27225
+ return void 0;
27226
+ }
27227
+ var _dataDirFlag = scanDataDirFlag(process.argv.slice(2));
27228
+ if (_dataDirFlag) {
27229
+ process.env.RELAY_DATA_DIR = _dataDirFlag;
27230
+ }
27147
27231
  var pkg = JSON.parse(readFileSync9(join21(appDir, "package.json"), "utf-8"));
27148
27232
  function getHelpText() {
27149
27233
  const dir = getAinativeDataDir();
package/drizzle.config.ts CHANGED
@@ -2,13 +2,13 @@ import { defineConfig } from "drizzle-kit";
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
4
 
5
- const dataDir = process.env.AINATIVE_DATA_DIR || join(homedir(), ".ainative");
5
+ const dataDir = process.env.RELAY_DATA_DIR || join(homedir(), ".relay");
6
6
 
7
7
  export default defineConfig({
8
8
  schema: "./src/lib/db/schema.ts",
9
9
  out: "./src/lib/db/migrations",
10
10
  dialect: "sqlite",
11
11
  dbCredentials: {
12
- url: join(dataDir, "ainative.db"),
12
+ url: join(dataDir, "relay.db"),
13
13
  },
14
14
  });
package/next.config.mjs CHANGED
@@ -1,3 +1,9 @@
1
+ import { readFileSync } from "node:fs";
2
+ import {
3
+ PHASE_PRODUCTION_BUILD,
4
+ PHASE_DEVELOPMENT_SERVER,
5
+ } from "next/constants.js";
6
+
1
7
  // When the operator opts into LAN binding (`--hostname` to a non-loopback host,
2
8
  // see bin/cli.ts), the CLI sets RELAY_ALLOW_LAN_ORIGINS=true. In dev mode Next
3
9
  // otherwise blocks cross-origin requests to /_next/* dev assets from the LAN
@@ -16,6 +22,26 @@ const RFC1918_DEV_ORIGINS = [
16
22
  ];
17
23
  const allowLanDevOrigins = process.env.RELAY_ALLOW_LAN_ORIGINS === "true";
18
24
 
25
+ // Build-time core version, mirroring tsup's `define` (tsup.config.ts). tsup
26
+ // only builds the CLI bundle (dist/cli.js), so WITHOUT this the Next.js server
27
+ // leaves `__RELAY_CORE_VERSION__` undefined — relayCoreVersion() then falls to
28
+ // its "0.0.0" default and every /packs UI install is rejected with
29
+ // `requires relay-core >=X, but this install is 0.0.0` (fix-packs-ui-install-
30
+ // core-version). We inject it via `compiler.defineServer` below, which Next
31
+ // applies to BOTH bundlers (webpack for `next build`, Turbopack for `next dev`)
32
+ // so the server resolves the real version exactly as the shipped CLI does.
33
+ // `defineServer` (not `define`) because relayCoreVersion() is server-only —
34
+ // keeps the value out of client bundles. Single source of truth: pkg.version.
35
+ //
36
+ // NOTE: pass the RAW version string, NOT `JSON.stringify(...)`. Next's
37
+ // `compiler.define` takes literal values and quotes them itself, unlike tsup's
38
+ // `define` which takes JS-source text. Stringifying here would inject the
39
+ // double-quoted `"0.23.0"`, which fails `semver.valid()` and silently falls
40
+ // back to the "0.0.0" default — reintroducing the exact bug this fixes.
41
+ const CORE_VERSION_DEFINE = JSON.parse(
42
+ readFileSync(new URL("./package.json", import.meta.url), "utf-8")
43
+ ).version;
44
+
19
45
  /** @type {import('next').NextConfig} */
20
46
  const nextConfig = {
21
47
  serverExternalPackages: ["better-sqlite3", "pdf-parse", "pdfjs-dist"],
@@ -41,4 +67,29 @@ const nextConfig = {
41
67
  },
42
68
  };
43
69
 
44
- export default nextConfig;
70
+ // Phase-aware export. `compiler.defineServer` is a COMPILE-TIME directive — it
71
+ // only does anything while a bundler runs (build + dev). At runtime `next start`
72
+ // re-reads this config but never recompiles, so the define is inert there AND
73
+ // Next's config validator emits a spurious "defineServer.__RELAY_CORE_VERSION__
74
+ // is missing, expected boolean" warning (a false positive from its union-error
75
+ // flattener — the string value is valid; see next/dist/shared/lib/zod.js). That
76
+ // warning would land in every customer's prod server log. So we attach
77
+ // defineServer ONLY in the build/dev phases, keeping the shipped `next start`
78
+ // path warning-free while the version is already baked into `.next`.
79
+ export default function config(phase) {
80
+ if (
81
+ phase === PHASE_PRODUCTION_BUILD ||
82
+ phase === PHASE_DEVELOPMENT_SERVER
83
+ ) {
84
+ return {
85
+ ...nextConfig,
86
+ compiler: {
87
+ ...nextConfig.compiler,
88
+ defineServer: {
89
+ __RELAY_CORE_VERSION__: CORE_VERSION_DEFINE,
90
+ },
91
+ },
92
+ };
93
+ }
94
+ return nextConfig;
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orionfold-relay",
3
- "version": "0.22.1",
3
+ "version": "0.24.0",
4
4
  "description": "Orionfold Relay — a local-first, multi-agent orchestration runtime and builder scaffold for AI-native work.",
5
5
  "keywords": [
6
6
  "ai",
@@ -68,6 +68,7 @@
68
68
  "test:e2e": "vitest run --config vitest.config.e2e.ts",
69
69
  "test:ui": "vitest --ui",
70
70
  "validate:tokens": "npx tsx design-system/validate-tokens.ts",
71
+ "check:price-drift": "node scripts/check-price-drift.mjs",
71
72
  "sync-worktree": "bash bin/sync-worktree.sh",
72
73
  "prepublishOnly": "npm run build:cli"
73
74
  },
@@ -14,7 +14,7 @@ import type { SignedLicense } from "@/lib/licensing/verify";
14
14
  * paste/upload activation path for UI-first users: the browser reads the
15
15
  * fulfilment file client-side and ships the `{ payload, signature }` envelope
16
16
  * as JSON. Verification is offline Ed25519 inside saveLicense; nothing here
17
- * phones home.
17
+ * sends user data to Orionfold.
18
18
  */
19
19
 
20
20
  const BodySchema = z.object({
@@ -100,7 +100,7 @@ function EmptyHero({ starters }: { starters: ReturnType<typeof listStarters> })
100
100
  <Sparkles className="h-10 w-10 text-primary mb-4" aria-hidden="true" />
101
101
  <h2 className="text-lg font-semibold">Teach this instance a new job.</h2>
102
102
  <p className="mt-2 max-w-md text-sm text-muted-foreground">
103
- Describe the thing you do every week. Orionfold Relay composes a profile, blueprint, schedule, and tables into a running app no code, no deploys.
103
+ Describe the thing you do every week. Orionfold Relay composes a profile, blueprint, schedule, and tables into a running app. No code, no deploys.
104
104
  </p>
105
105
  <div className="mt-4 flex items-center gap-4">
106
106
  <Link
@@ -69,7 +69,7 @@ export default async function InboxPage() {
69
69
  return (
70
70
  <PageShell
71
71
  title="Inbox"
72
- description="Governance command center review approvals, questions, and agent activity."
72
+ description="Governance command center. Review approvals, questions, and agent activity."
73
73
  >
74
74
  <GovernanceStats
75
75
  pending={pendingCount}
@@ -12,6 +12,7 @@ import { ChatSessionProvider } from "@/components/chat/chat-session-provider";
12
12
  import { RuntimePreferenceBootstrapper } from "@/components/onboarding/runtime-preference-bootstrapper";
13
13
  import {
14
14
  DEFAULT_THEME,
15
+ LEGACY_THEME_COOKIE,
15
16
  THEME_COOKIE,
16
17
  isResolvedTheme,
17
18
  type ResolvedTheme,
@@ -82,12 +83,16 @@ export default async function RootLayout({
82
83
  }: {
83
84
  children: React.ReactNode;
84
85
  }) {
85
- // Resolve theme server-side from the ainative-theme cookie. Every client-side
86
+ // Resolve theme server-side from the relay-theme cookie. Every client-side
86
87
  // theme toggle writes this cookie (see src/lib/theme.ts), so SSR stays in
87
88
  // sync with the user's preference and there is no FOUC — and no pre-hydration
88
- // <script> tag, which is what React 19 warns about.
89
+ // <script> tag, which is what React 19 warns about. Fall back to the legacy
90
+ // ainative-theme cookie so a returning pre-rebrand user does not flash the
91
+ // default theme before the client re-writes the new cookie.
89
92
  const cookieStore = await cookies();
90
- const cookieValue = cookieStore.get(THEME_COOKIE)?.value;
93
+ const cookieValue =
94
+ cookieStore.get(THEME_COOKIE)?.value ??
95
+ cookieStore.get(LEGACY_THEME_COOKIE)?.value;
91
96
  const theme: ResolvedTheme = isResolvedTheme(cookieValue)
92
97
  ? cookieValue
93
98
  : DEFAULT_THEME;