orionfold-relay 0.23.0 → 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 (122) hide show
  1. package/dist/cli.js +60 -19
  2. package/next.config.mjs +52 -1
  3. package/package.json +2 -1
  4. package/src/app/apps/page.tsx +1 -1
  5. package/src/app/inbox/page.tsx +1 -1
  6. package/src/app/layout.tsx +8 -3
  7. package/src/app/packs/page.tsx +1 -1
  8. package/src/app/projects/[id]/page.tsx +0 -11
  9. package/src/components/apps/last-run-card.tsx +1 -1
  10. package/src/components/apps/ledger-hero-panel.tsx +1 -1
  11. package/src/components/charts/time-series-chart.tsx +1 -1
  12. package/src/components/chat/app-materialized-card.tsx +1 -1
  13. package/src/components/chat/app-view-editor-card.tsx +1 -1
  14. package/src/components/chat/chat-command-popover.tsx +2 -2
  15. package/src/components/chat/chat-empty-state.tsx +1 -1
  16. package/src/components/chat/chat-message.tsx +2 -1
  17. package/src/components/chat/chat-session-provider.tsx +2 -2
  18. package/src/components/chat/conversation-template-picker.tsx +1 -1
  19. package/src/components/chat/skill-row.tsx +1 -15
  20. package/src/components/customers/customer-form-sheet.tsx +1 -1
  21. package/src/components/dashboard/greeting.tsx +1 -1
  22. package/src/components/dashboard/welcome-landing.tsx +2 -2
  23. package/src/components/documents/document-upload-dialog.tsx +1 -1
  24. package/src/components/documents/smart-extracted-text.tsx +1 -1
  25. package/src/components/instance/instance-section.tsx +1 -1
  26. package/src/components/instance/upgrade-badge.tsx +1 -1
  27. package/src/components/notifications/batch-proposal-review.tsx +1 -1
  28. package/src/components/onboarding/runtime-preference-modal.tsx +32 -3
  29. package/src/components/packs/pack-update-button.tsx +1 -1
  30. package/src/components/profiles/profile-detail-view.tsx +1 -1
  31. package/src/components/projects/project-create-dialog.tsx +1 -1
  32. package/src/components/projects/project-form-sheet.tsx +2 -2
  33. package/src/components/schedules/schedule-create-dialog.tsx +1 -1
  34. package/src/components/schedules/schedule-create-sheet.tsx +1 -1
  35. package/src/components/schedules/schedule-edit-sheet.tsx +1 -1
  36. package/src/components/schedules/schedule-form.tsx +1 -1
  37. package/src/components/settings/data-management-section.tsx +2 -2
  38. package/src/components/settings/database-snapshots-section.tsx +6 -6
  39. package/src/components/settings/ollama-section.tsx +2 -2
  40. package/src/components/settings/presets-section.tsx +2 -2
  41. package/src/components/settings/providers-runtimes-section.tsx +6 -6
  42. package/src/components/settings/web-search-section.tsx +1 -1
  43. package/src/components/shared/command-palette.tsx +2 -2
  44. package/src/components/shared/trust-tier-badge.tsx +3 -3
  45. package/src/components/shell/app-bar.tsx +156 -164
  46. package/src/components/shell/app-shell.tsx +16 -8
  47. package/src/components/shell/nav-items.ts +72 -30
  48. package/src/components/tasks/ai-assist-panel.tsx +2 -2
  49. package/src/components/tasks/task-create-panel.tsx +1 -1
  50. package/src/components/tasks/task-edit-dialog.tsx +1 -1
  51. package/src/components/workflows/blueprint-gallery.tsx +1 -1
  52. package/src/components/workflows/delay-step-body.tsx +1 -1
  53. package/src/components/workflows/workflow-confirmation-view.tsx +1 -1
  54. package/src/components/workflows/workflow-form-view.tsx +1 -1
  55. package/src/components/workspace/discover-workspace-dialog.tsx +1 -1
  56. package/src/lib/agents/runtime/openai-codex.ts +1 -1
  57. package/src/lib/apps/apps-events.ts +15 -0
  58. package/src/lib/apps/use-apps.ts +4 -3
  59. package/src/lib/channels/slack-adapter.ts +1 -1
  60. package/src/lib/channels/telegram-adapter.ts +1 -1
  61. package/src/lib/channels/webhook-adapter.ts +2 -2
  62. package/src/lib/chat/system-prompt.ts +4 -4
  63. package/src/lib/chat/tool-catalog.ts +3 -3
  64. package/src/lib/chat/tools/app-view-tools.ts +3 -2
  65. package/src/lib/data/seed-data/environment.ts +2 -2
  66. package/src/lib/db/bootstrap.ts +5 -1
  67. package/src/lib/packs/templates/relay-agency/pack.yaml +1 -1
  68. package/src/lib/packs/templates/relay-agency-pro/pack.yaml +6 -6
  69. package/src/lib/plugins/examples/echo-server/plugin.yaml +1 -1
  70. package/src/lib/plugins/examples/finance-pack/plugin.yaml +1 -1
  71. package/src/lib/plugins/examples/reading-radar/plugin.yaml +1 -1
  72. package/src/lib/plugins/registry.ts +1 -1
  73. package/src/lib/plugins/sdk/types.ts +1 -1
  74. package/src/lib/theme.ts +23 -11
  75. package/src/lib/workflows/blueprints/registry.ts +59 -10
  76. package/src/app/analytics/page.tsx +0 -40
  77. package/src/app/api/environment/artifacts/[id]/route.ts +0 -17
  78. package/src/app/api/environment/artifacts/route.ts +0 -33
  79. package/src/app/api/environment/checkpoints/[id]/route.ts +0 -86
  80. package/src/app/api/environment/checkpoints/route.ts +0 -80
  81. package/src/app/api/environment/profiles/create/route.ts +0 -28
  82. package/src/app/api/environment/profiles/suggest/route.ts +0 -41
  83. package/src/app/api/environment/scan/route.ts +0 -70
  84. package/src/app/api/environment/sync/history/route.ts +0 -20
  85. package/src/app/api/environment/sync/preview/route.ts +0 -27
  86. package/src/app/api/environment/sync/route.ts +0 -43
  87. package/src/app/api/environment/templates/[id]/route.ts +0 -34
  88. package/src/app/api/environment/templates/route.ts +0 -27
  89. package/src/app/environment/compare/page.tsx +0 -20
  90. package/src/app/environment/loading.tsx +0 -56
  91. package/src/app/environment/page.tsx +0 -61
  92. package/src/app/environment/skills/page.tsx +0 -20
  93. package/src/components/analytics/analytics-dashboard.tsx +0 -200
  94. package/src/components/environment/adoption-prompt.tsx +0 -76
  95. package/src/components/environment/artifact-card.tsx +0 -93
  96. package/src/components/environment/artifact-detail-sheet.tsx +0 -136
  97. package/src/components/environment/artifact-presence-cell.tsx +0 -44
  98. package/src/components/environment/category-filter-bar.tsx +0 -132
  99. package/src/components/environment/checkpoint-list.tsx +0 -169
  100. package/src/components/environment/comparison-matrix.tsx +0 -132
  101. package/src/components/environment/environment-dashboard.tsx +0 -248
  102. package/src/components/environment/environment-summary-card.tsx +0 -153
  103. package/src/components/environment/health-score-card.tsx +0 -86
  104. package/src/components/environment/persona-indicator.tsx +0 -43
  105. package/src/components/environment/profile-create-dialog.tsx +0 -178
  106. package/src/components/environment/project-diff-view.tsx +0 -59
  107. package/src/components/environment/project-scan-badge.tsx +0 -36
  108. package/src/components/environment/rollback-confirm-dialog.tsx +0 -105
  109. package/src/components/environment/scan-status-bar.tsx +0 -48
  110. package/src/components/environment/skill-catalog.tsx +0 -117
  111. package/src/components/environment/skill-detail-sheet.tsx +0 -106
  112. package/src/components/environment/skill-drift-indicator.tsx +0 -42
  113. package/src/components/environment/suggested-profiles.tsx +0 -162
  114. package/src/components/environment/summary-cards-row.tsx +0 -91
  115. package/src/components/environment/sync-action-buttons.tsx +0 -63
  116. package/src/components/environment/sync-preview-dialog.tsx +0 -205
  117. package/src/components/environment/template-list.tsx +0 -149
  118. package/src/components/environment/tool-comparison-view.tsx +0 -75
  119. package/src/lib/analytics/queries.ts +0 -207
  120. package/src/lib/environment/comparison.ts +0 -170
  121. package/src/lib/environment/health-scoring.ts +0 -162
  122. package/src/lib/environment/skill-portfolio.ts +0 -97
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.23";
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({
@@ -5795,6 +5795,13 @@ __export(registry_exports3, {
5795
5795
  import fs11 from "fs";
5796
5796
  import path11 from "path";
5797
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
+ }
5798
5805
  function scanDirectory(dir, isBuiltin2) {
5799
5806
  const blueprints = /* @__PURE__ */ new Map();
5800
5807
  if (!fs11.existsSync(dir)) return blueprints;
@@ -5829,8 +5836,13 @@ function loadAll() {
5829
5836
  return all;
5830
5837
  }
5831
5838
  function ensureLoaded2() {
5839
+ if (blueprintCache && userDirMtimeMs() !== cachedDirMtimeMs) {
5840
+ blueprintCache = null;
5841
+ }
5832
5842
  if (!blueprintCache) {
5843
+ cachedDirMtimeMs = userDirMtimeMs();
5833
5844
  blueprintCache = loadAll();
5845
+ applyPluginBlueprints(blueprintCache);
5834
5846
  }
5835
5847
  return blueprintCache;
5836
5848
  }
@@ -5878,30 +5890,35 @@ function deleteBlueprint(id) {
5878
5890
  function getUserBlueprintsDir() {
5879
5891
  return USER_BLUEPRINTS_DIR;
5880
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
+ }
5881
5898
  function mergePluginBlueprints(entries) {
5882
5899
  const cache = ensureLoaded2();
5883
5900
  for (const entry of entries) {
5884
5901
  cache.set(entry.blueprint.id, entry.blueprint);
5885
- if (!pluginBlueprintIndex.has(entry.pluginId)) {
5886
- pluginBlueprintIndex.set(entry.pluginId, /* @__PURE__ */ new Set());
5902
+ if (!pluginBlueprints.has(entry.pluginId)) {
5903
+ pluginBlueprints.set(entry.pluginId, /* @__PURE__ */ new Map());
5887
5904
  }
5888
- pluginBlueprintIndex.get(entry.pluginId).add(entry.blueprint.id);
5905
+ pluginBlueprints.get(entry.pluginId).set(entry.blueprint.id, entry.blueprint);
5889
5906
  }
5890
5907
  }
5891
5908
  function clearPluginBlueprints(pluginId) {
5892
5909
  const cache = blueprintCache;
5893
- const ids = pluginBlueprintIndex.get(pluginId);
5894
- if (!ids) return;
5895
- if (cache) for (const id of ids) cache.delete(id);
5896
- 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);
5897
5914
  }
5898
5915
  function clearAllPluginBlueprints() {
5899
- for (const pluginId of Array.from(pluginBlueprintIndex.keys())) {
5916
+ for (const pluginId of Array.from(pluginBlueprints.keys())) {
5900
5917
  clearPluginBlueprints(pluginId);
5901
5918
  }
5902
5919
  }
5903
5920
  function listPluginBlueprintIds(pluginId) {
5904
- return Array.from(pluginBlueprintIndex.get(pluginId) ?? []);
5921
+ return Array.from(pluginBlueprints.get(pluginId)?.keys() ?? []);
5905
5922
  }
5906
5923
  function validateBlueprintRefs(bp, opts2) {
5907
5924
  for (const step of bp.steps ?? []) {
@@ -5923,7 +5940,7 @@ function validateBlueprintRefs(bp, opts2) {
5923
5940
  }
5924
5941
  return { ok: true };
5925
5942
  }
5926
- var BUILTINS_DIR2, USER_BLUEPRINTS_DIR, blueprintCache, pluginBlueprintIndex;
5943
+ var BUILTINS_DIR2, USER_BLUEPRINTS_DIR, blueprintCache, cachedDirMtimeMs, pluginBlueprints;
5927
5944
  var init_registry3 = __esm({
5928
5945
  "src/lib/workflows/blueprints/registry.ts"() {
5929
5946
  "use strict";
@@ -5941,7 +5958,8 @@ var init_registry3 = __esm({
5941
5958
  );
5942
5959
  USER_BLUEPRINTS_DIR = getAinativeBlueprintsDir();
5943
5960
  blueprintCache = null;
5944
- pluginBlueprintIndex = /* @__PURE__ */ new Map();
5961
+ cachedDirMtimeMs = null;
5962
+ pluginBlueprints = /* @__PURE__ */ new Map();
5945
5963
  }
5946
5964
  });
5947
5965
 
@@ -12945,7 +12963,7 @@ var init_registry6 = __esm({
12945
12963
  init_registry5();
12946
12964
  init_installer();
12947
12965
  init_schedule_spec();
12948
- SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.22"]);
12966
+ SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.23"]);
12949
12967
  pluginCache = null;
12950
12968
  lastLoadedPluginIds = /* @__PURE__ */ new Set();
12951
12969
  PluginTableSchema = z16.object({
@@ -17617,11 +17635,20 @@ var init_plugin_spec_tools = __esm({
17617
17635
  }
17618
17636
  });
17619
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
+
17620
17647
  // src/lib/chat/tools/app-view-tools.ts
17621
17648
  import { z as z28 } from "zod";
17622
17649
  function dispatchAppsChangedFromTool() {
17623
17650
  if (typeof window !== "undefined") {
17624
- window.dispatchEvent(new CustomEvent("ainative-apps-changed"));
17651
+ window.dispatchEvent(new CustomEvent(APPS_CHANGED_EVENT));
17625
17652
  }
17626
17653
  }
17627
17654
  function loadManifestOrError(appId) {
@@ -17758,6 +17785,7 @@ var init_app_view_tools = __esm({
17758
17785
  "src/lib/chat/tools/app-view-tools.ts"() {
17759
17786
  "use strict";
17760
17787
  init_tool_registry();
17788
+ init_apps_events();
17761
17789
  init_helpers2();
17762
17790
  init_registry();
17763
17791
  BindingsSchema = ViewSchema.shape.bindings;
@@ -20155,7 +20183,7 @@ async function handleServerRequest(client, taskId, request) {
20155
20183
  contentItems: [
20156
20184
  {
20157
20185
  type: "inputText",
20158
- 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."
20159
20187
  }
20160
20188
  ]
20161
20189
  });
@@ -25775,8 +25803,8 @@ import { execFileSync as execFileSync3 } from "child_process";
25775
25803
  import yaml12 from "js-yaml";
25776
25804
  import semver from "semver";
25777
25805
  function relayCoreVersion() {
25778
- if (semver.valid("0.23.0")) {
25779
- return "0.23.0";
25806
+ if (semver.valid("0.24.0")) {
25807
+ return "0.24.0";
25780
25808
  }
25781
25809
  try {
25782
25810
  const root = getAppRoot(import.meta.dirname, 3);
@@ -27147,7 +27175,8 @@ var __dirname = dirname5(fileURLToPath4(import.meta.url));
27147
27175
  var appDir = join21(__dirname, "..");
27148
27176
  var launchCwd2 = process.cwd();
27149
27177
  var _envLocalPath = join21(launchCwd2, ".env.local");
27150
- 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);
27151
27180
  if (_firstRunNeedsEnv) {
27152
27181
  const folderName = basename6(launchCwd2);
27153
27182
  const autoDataDir = join21(homedir8(), `.${folderName}`);
@@ -27187,6 +27216,18 @@ if (existsSync13(_envLocalPath)) {
27187
27216
  }
27188
27217
  }
27189
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
+ }
27190
27231
  var pkg = JSON.parse(readFileSync9(join21(appDir, "package.json"), "utf-8"));
27191
27232
  function getHelpText() {
27192
27233
  const dir = getAinativeDataDir();
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.23.0",
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
  },
@@ -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;
@@ -81,7 +81,7 @@ export default async function PacksPage({
81
81
  return (
82
82
  <PageShell
83
83
  title="Packs"
84
- description="Vertical content bundles an app, profiles, blueprints, tables, and seed data installed in one step."
84
+ description="Vertical content bundles. An app, profiles, blueprints, tables, and seed data installed in one step."
85
85
  filters={
86
86
  templates.length > 1 ? (
87
87
  <FilterChips active={filter} counts={counts} />
@@ -11,7 +11,6 @@ import Link from "next/link";
11
11
  import { FileText } from "lucide-react";
12
12
  import { Sparkline } from "@/components/charts/sparkline";
13
13
  import { getProjectCompletionTrend } from "@/lib/queries/chart-data";
14
- import { EnvironmentSummaryCard } from "@/components/environment/environment-summary-card";
15
14
 
16
15
  export const dynamic = "force-dynamic";
17
16
 
@@ -160,16 +159,6 @@ export default async function ProjectDetailPage({
160
159
  </div>
161
160
  )}
162
161
 
163
- {/* Environment summary */}
164
- {project.workingDirectory && (
165
- <div className="mb-6">
166
- <EnvironmentSummaryCard
167
- projectId={id}
168
- workingDirectory={project.workingDirectory}
169
- />
170
- </div>
171
- )}
172
-
173
162
  {/* Recent documents */}
174
163
  {recentDocs.length > 0 && (
175
164
  <div className="mb-6">
@@ -100,7 +100,7 @@ function HeroVariant({ task, previousRuns }: HeroProps) {
100
100
  if (!task) {
101
101
  return (
102
102
  <div className="surface-card rounded-xl p-6 text-center text-muted-foreground border">
103
- No digest yet click <strong>Run now</strong> to generate the first one.
103
+ No digest yet. Click <strong>Run now</strong> to generate the first one.
104
104
  </div>
105
105
  );
106
106
  }
@@ -15,7 +15,7 @@ export function LedgerHeroPanel({ series, categories, period }: LedgerHeroPanelP
15
15
  if (series.length === 0 && categories.length === 0) {
16
16
  return (
17
17
  <div className="surface-card rounded-xl p-12 text-center text-muted-foreground border">
18
- No data yet add transactions or click <strong>Run now</strong> to ingest a CSV.
18
+ No data yet. Add transactions or click <strong>Run now</strong> to ingest a CSV.
19
19
  </div>
20
20
  );
21
21
  }
@@ -39,7 +39,7 @@ export function TimeSeriesChart({
39
39
  style={{ height }}
40
40
  data-chart-height={String(height)}
41
41
  >
42
- No data yet runs will populate this chart
42
+ No data yet. Runs will populate this chart.
43
43
  </div>
44
44
  );
45
45
  }
@@ -137,7 +137,7 @@ export function AppMaterializedCard({
137
137
  ))}
138
138
  </ul>
139
139
  <p className="text-[11px] text-muted-foreground/60 pt-1">
140
- Informational these files are written under your control. No approval required.
140
+ Informational. These files are written under your control. No approval required.
141
141
  </p>
142
142
  </div>
143
143
  )}
@@ -193,7 +193,7 @@ export function AppViewEditorCard({
193
193
  </p>
194
194
  )}
195
195
  {status === "cancelled" && (
196
- <p className="text-xs text-muted-foreground">Cancelled no changes written.</p>
196
+ <p className="text-xs text-muted-foreground">Cancelled. No changes written.</p>
197
197
  )}
198
198
  {status === "failed" && errorMessage && (
199
199
  <p className="text-xs text-destructive">Failed: {errorMessage}</p>
@@ -176,7 +176,7 @@ export function ChatCommandPopover({
176
176
  const activeCount = Array.isArray(body.activeSkillIds)
177
177
  ? (body.activeSkillIds as string[]).length
178
178
  : 1;
179
- toast.success(`Added ${skillName} ${activeCount} skill${activeCount !== 1 ? "s" : ""} active`);
179
+ toast.success(`Added ${skillName}. ${activeCount} skill${activeCount !== 1 ? "s" : ""} active`);
180
180
  },
181
181
  [conversationId, refetchActive]
182
182
  );
@@ -494,7 +494,7 @@ function ToolCatalogItems({
494
494
  const showDisabled = !isActive && !canAdd && !!onAddSkill;
495
495
  const disabledReason = atCapacity
496
496
  ? `Max ${resolvedMax} skills active`
497
- : "Single skill only on this runtime switch runtime to compose";
497
+ : "Single skill only on this runtime. Switch runtime to compose";
498
498
 
499
499
  return (
500
500
  <SkillRow
@@ -99,7 +99,7 @@ export function ChatEmptyState({
99
99
  </div>
100
100
  <h2 className="text-lg font-semibold">Describe an app. Orionfold Relay builds it.</h2>
101
101
  <p className="text-sm text-muted-foreground text-center max-w-md">
102
- Profiles, blueprints, tables, and schedules composed from a single prompt.
102
+ Profiles, blueprints, tables, and schedules, composed from a single prompt.
103
103
  Or ask anything about your workspace.
104
104
  </p>
105
105
  </div>
@@ -18,6 +18,7 @@ import {
18
18
  import { AlertCircle } from "lucide-react";
19
19
  import { resolveModelLabel, type ChatQuestion, type QuickAccessItem, type ScreenshotAttachment } from "@/lib/chat/types";
20
20
  import type { ComposedAppSummary } from "@/lib/apps/composition-detector";
21
+ import { APPS_CHANGED_EVENT } from "@/lib/apps/apps-events";
21
22
 
22
23
  interface ExtensionFallbackSummary {
23
24
  plugin: CreatePluginSpecInputForCard;
@@ -56,7 +57,7 @@ function ComposedAppCard({ app }: { app: ComposedAppSummary }) {
56
57
  });
57
58
  if (res.ok) {
58
59
  setAppStatus("undone");
59
- window.dispatchEvent(new CustomEvent("ainative-apps-changed"));
60
+ window.dispatchEvent(new CustomEvent(APPS_CHANGED_EVENT));
60
61
  }
61
62
  }, [app.appId]);
62
63
 
@@ -331,7 +331,7 @@ export function ChatSessionProvider({ children }: { children: ReactNode }) {
331
331
  void createConversation();
332
332
  };
333
333
  const handleCompact = () => {
334
- toast.info("Compact is not wired yet coming soon.");
334
+ toast.info("Compact is not wired yet. Coming soon.");
335
335
  };
336
336
  const handleExport = async () => {
337
337
  const activeConversationId = activeIdRef.current;
@@ -339,7 +339,7 @@ export function ChatSessionProvider({ children }: { children: ReactNode }) {
339
339
  ? messagesByConversationRef.current[activeConversationId]
340
340
  : undefined;
341
341
  if (!msgs || msgs.length === 0) {
342
- toast.error("Nothing to export this conversation is empty.");
342
+ toast.error("Nothing to export. This conversation is empty.");
343
343
  return;
344
344
  }
345
345
  const title = `Chat — ${new Date().toISOString().slice(0, 10)}`;
@@ -325,7 +325,7 @@ function ParameterForm({
325
325
  if (blueprint.variables.length === 0) {
326
326
  return (
327
327
  <p className="text-sm text-muted-foreground">
328
- This blueprint has no parameters starting a conversation now.
328
+ This blueprint has no parameters. Starting a conversation now.
329
329
  </p>
330
330
  );
331
331
  }
@@ -49,11 +49,6 @@ export function SkillRow({
49
49
  addButton,
50
50
  onDeactivate,
51
51
  }: SkillRowProps) {
52
- const syncHref =
53
- skill.syncStatus !== "synced"
54
- ? `/environment?skill=${encodeURIComponent(skill.name)}`
55
- : null;
56
-
57
52
  return (
58
53
  <CommandItem
59
54
  key={skill.id}
@@ -112,7 +107,7 @@ export function SkillRow({
112
107
  </Badge>
113
108
  </div>
114
109
  </div>
115
- {/* Right-side slot: either the env link or the Add/Deactivate button. */}
110
+ {/* Right-side slot: the Add or Deactivate button. */}
116
111
  {isActive && onDeactivate ? (
117
112
  <button
118
113
  type="button"
@@ -132,15 +127,6 @@ export function SkillRow({
132
127
  </button>
133
128
  ) : addButton ? (
134
129
  addButton
135
- ) : syncHref ? (
136
- <a
137
- href={syncHref}
138
- aria-label={`Open ${skill.name} in environment dashboard`}
139
- className="ml-auto shrink-0 text-muted-foreground hover:text-foreground"
140
- onClick={(e) => e.stopPropagation()}
141
- >
142
-
143
- </a>
144
130
  ) : null}
145
131
  </CommandItem>
146
132
  );
@@ -140,7 +140,7 @@ export function CustomerFormSheet({
140
140
  id="customer-slug"
141
141
  value={slug}
142
142
  onChange={(e) => setSlug(e.target.value)}
143
- placeholder="meridian-cre derived from name if blank"
143
+ placeholder="meridian-cre (derived from name if blank)"
144
144
  />
145
145
  <p className="text-xs text-muted-foreground">
146
146
  Lowercase letters, numbers, and hyphens. Immutable once set.
@@ -21,7 +21,7 @@ export function Greeting({ runningCount, awaitingCount, failedCount, activeWorkf
21
21
 
22
22
  const summary = parts.length > 0
23
23
  ? `You have ${parts.join(", ")}.`
24
- : "All clear no tasks need your attention.";
24
+ : "All clear. No tasks need your attention.";
25
25
 
26
26
  return (
27
27
  <div className="surface-card rounded-lg p-5 mb-6">
@@ -9,7 +9,7 @@ const pillars = [
9
9
  {
10
10
  icon: Sparkles,
11
11
  title: "Apps from a sentence",
12
- description: "Describe what you do every week. Orionfold Relay composes the profile, blueprint, schedule, and tables into a running app no code.",
12
+ description: "Describe what you do every week. Orionfold Relay composes the profile, blueprint, schedule, and tables into a running app. No code.",
13
13
  },
14
14
  {
15
15
  icon: Shield,
@@ -44,7 +44,7 @@ export function WelcomeLanding({ starters = [] }: WelcomeLandingProps) {
44
44
  Welcome
45
45
  </h1>
46
46
  <p className="text-base text-muted-foreground mb-8 max-w-lg">
47
- Your AI Business Operating System. Describe an app, Orionfold Relay builds it and runs it on your rules, your budget, your data.
47
+ Your AI Business Operating System. Describe an app, Orionfold Relay builds it, and runs it on your rules, your budget, your data.
48
48
  </p>
49
49
 
50
50
  <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 w-full mb-8">
@@ -86,7 +86,7 @@ export function DocumentUploadDialog({
86
86
  <div
87
87
  role="button"
88
88
  tabIndex={0}
89
- aria-label="Upload files click or drag and drop"
89
+ aria-label="Upload files. Click or drag and drop"
90
90
  className="border-2 border-dashed border-border rounded-lg p-8 text-center cursor-pointer hover:bg-accent/50 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
91
91
  onClick={() => inputRef.current?.click()}
92
92
  onKeyDown={(e) => {
@@ -35,7 +35,7 @@ export function SmartExtractedText({ text }: SmartExtractedTextProps) {
35
35
  return (
36
36
  <div className="space-y-3">
37
37
  <p className="text-xs text-muted-foreground italic">
38
- Extracted text original formatting may differ
38
+ Extracted text. Original formatting may differ
39
39
  </p>
40
40
  <div className={PROSE_READER}>
41
41
  <ReactMarkdown remarkPlugins={[remarkGfm]}>
@@ -269,7 +269,7 @@ export function InstanceSection() {
269
269
  const startUpgradeDisabled = busy !== null || !upgradeAvailable;
270
270
  const startUpgradeTitle = upgradeAvailable
271
271
  ? `Merge ${upgradeCount} upstream commit${upgradeCount === 1 ? "" : "s"} into ${config!.branchName}`
272
- : "No upgrades available click 'Check for upgrades' to refresh";
272
+ : "No upgrades available. Click 'Check for upgrades' to refresh";
273
273
  const statusMessage = pollFailing && upgrade?.lastPollError
274
274
  ? upgrade.lastPollError
275
275
  : message;
@@ -131,7 +131,7 @@ export function UpgradeBadge() {
131
131
  ? "Check failing"
132
132
  : `${count} update${count === 1 ? "" : "s"}`;
133
133
  const tooltip = failing
134
- ? "Upgrade check failing click to retry"
134
+ ? "Upgrade check failing. Click to retry"
135
135
  : `${count} upstream update${count === 1 ? "" : "s"} ready to merge`;
136
136
  const buttonClass = failing
137
137
  ? "h-7 px-2 rounded-md border border-amber-500/40 bg-amber-500/10 text-[11px] font-medium text-amber-700 dark:text-amber-400 hover:bg-amber-500/20 transition-colors cursor-pointer inline-flex items-center gap-1.5 group-data-[collapsible=icon]:hidden"
@@ -121,7 +121,7 @@ export function BatchProposalReview({
121
121
  <CardHeader className="pb-3">
122
122
  <CardTitle className="text-sm flex items-center gap-2">
123
123
  <Brain className="h-4 w-4" />
124
- Workflow Learning {proposalIds.length} Proposals
124
+ Workflow Learning: {proposalIds.length} Proposals
125
125
  </CardTitle>
126
126
  </CardHeader>
127
127
  <CardContent className="space-y-4">