agentlife 2.6.28 → 2.6.32

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 (143) hide show
  1. package/dist/index.js +912 -352
  2. package/package.json +16 -6
  3. package/web-build/6e23e5428398b92da386.wasm +0 -0
  4. package/web-build/auth-callback.html +40 -0
  5. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_action_plan.xml +10 -0
  6. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_add.xml +9 -0
  7. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_alarm.xml +9 -0
  8. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_amazon.xml +9 -0
  9. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_apple.xml +12 -0
  10. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_clockwise.xml +9 -0
  11. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_counter_clockwise.xml +9 -0
  12. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_down_up.xml +9 -0
  13. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_right.xml +9 -0
  14. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_square_out.xml +9 -0
  15. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrows_out.xml +9 -0
  16. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_audible.xml +9 -0
  17. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_auto_send.xml +9 -0
  18. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_back.xml +9 -0
  19. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_barcode.xml +9 -0
  20. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell.xml +9 -0
  21. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell_ringing.xml +9 -0
  22. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_binoculars.xml +9 -0
  23. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_brain.xml +9 -0
  24. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bulb.xml +9 -0
  25. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar.xml +9 -0
  26. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_blank.xml +9 -0
  27. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_check.xml +9 -0
  28. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_dots.xml +9 -0
  29. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_down.xml +9 -0
  30. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_up.xml +9 -0
  31. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_down.xml +9 -0
  32. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_right.xml +9 -0
  33. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_up.xml +9 -0
  34. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat.xml +9 -0
  35. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle.xml +9 -0
  36. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle_text.xml +9 -0
  37. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_teardrop.xml +9 -0
  38. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_check_simple.xml +9 -0
  39. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_child.xml +9 -0
  40. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_counter_clockwise.xml +9 -0
  41. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_simple.xml +9 -0
  42. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_close.xml +9 -0
  43. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_comment.xml +9 -0
  44. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_community.xml +9 -0
  45. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_copy_simple.xml +9 -0
  46. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_credit_card.xml +9 -0
  47. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_decision_framework.xml +9 -0
  48. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_diff.xml +9 -0
  49. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_dots_three.xml +9 -0
  50. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_edit.xml +9 -0
  51. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_experiment.xml +9 -0
  52. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_facebook.xml +9 -0
  53. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_faders.xml +9 -0
  54. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_dotted.xml +9 -0
  55. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_text.xml +9 -0
  56. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_filter.xml +9 -0
  57. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_flask.xml +9 -0
  58. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_floppy_disk.xml +9 -0
  59. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_open.xml +9 -0
  60. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_simple.xml +9 -0
  61. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_git_merge.xml +9 -0
  62. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google.xml +35 -0
  63. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google_play.xml +22 -0
  64. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_habit_tracker.xml +9 -0
  65. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_1.xml +9 -0
  66. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_2.xml +9 -0
  67. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_3.xml +9 -0
  68. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_4.xml +9 -0
  69. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_hide_pass.xml +9 -0
  70. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_info_simple.xml +9 -0
  71. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_instagram.xml +9 -0
  72. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_keyboard.xml +9 -0
  73. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lightning.xml +9 -0
  74. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_linkedin.xml +9 -0
  75. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lock.xml +9 -0
  76. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_logo.xml +12 -0
  77. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_map_pin_simple.xml +9 -0
  78. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_media.xml +9 -0
  79. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_menu.xml +9 -0
  80. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_microphone_simple.xml +9 -0
  81. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_more_vert.xml +9 -0
  82. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paint_bucket.xml +9 -0
  83. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paper_plane_right.xml +9 -0
  84. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paperclip.xml +9 -0
  85. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_parent.xml +9 -0
  86. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_pause_simple.xml +9 -0
  87. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_play_simple.xml +9 -0
  88. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_plus_circle.xml +9 -0
  89. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin.xml +9 -0
  90. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin_slash.xml +9 -0
  91. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_question.xml +9 -0
  92. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_record.xml +9 -0
  93. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_rocket.xml +9 -0
  94. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_scales.xml +9 -0
  95. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_search.xml +9 -0
  96. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_settings.xml +9 -0
  97. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_share_network.xml +9 -0
  98. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_show_pass.xml +9 -0
  99. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sign_out.xml +9 -0
  100. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_smiley.xml +9 -0
  101. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparkles.xml +9 -0
  102. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparks.xml +9 -0
  103. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_speaker_high.xml +9 -0
  104. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_spotify.xml +9 -0
  105. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_star.xml +9 -0
  106. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_circle.xml +9 -0
  107. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_simple.xml +9 -0
  108. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_swap.xml +9 -0
  109. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tag_simple.xml +9 -0
  110. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_down.xml +9 -0
  111. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_up.xml +9 -0
  112. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tiktok.xml +9 -0
  113. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_complete.xml +9 -0
  114. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_simple.xml +9 -0
  115. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_translate.xml +9 -0
  116. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_trash_simple.xml +9 -0
  117. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tree_structure.xml +9 -0
  118. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_twitch.xml +9 -0
  119. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_upload.xml +9 -0
  120. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_user_circle.xml +9 -0
  121. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_warning.xml +9 -0
  122. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_web.xml +9 -0
  123. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_wrench.xml +9 -0
  124. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x.xml +9 -0
  125. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x_circle.xml +9 -0
  126. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube.xml +9 -0
  127. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube_music.xml +9 -0
  128. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/tenazitas_logo.xml +27 -0
  129. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Caveat-Medium.ttf +0 -0
  130. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Medium.ttf +0 -0
  131. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Regular.ttf +0 -0
  132. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-SemiBold.ttf +0 -0
  133. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Medium.ttf +0 -0
  134. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Regular.ttf +0 -0
  135. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Italic.ttf +0 -0
  136. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Regular.ttf +0 -0
  137. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-SemiBold.ttf +0 -0
  138. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/values/strings.commonMain.cvr +2787 -0
  139. package/web-build/f722462c344efeb18c2f.wasm +0 -0
  140. package/web-build/index.html +23 -0
  141. package/web-build/webApp.js +3 -0
  142. package/web-build/webApp.js.LICENSE.txt +35 -0
  143. package/web-build/webApp.js.map +1 -0
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@ import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
4
  // index.ts
5
- import { homedir as homedir15 } from "node:os";
6
- import * as path18 from "node:path";
7
- import { existsSync as existsSync7 } from "node:fs";
5
+ import { homedir as homedir17 } from "node:os";
6
+ import * as path20 from "node:path";
7
+ import { existsSync as existsSync8 } from "node:fs";
8
8
 
9
9
  // db.ts
10
10
  import * as fsSync from "node:fs";
@@ -988,7 +988,7 @@ If you produce a followup whose residue (digits and parameter values stripped) m
988
988
 
989
989
  ### Guided mode — input surfaces
990
990
 
991
- When you need structured input, push a surface with \`input\` in the header. It renders in the input bar, not on the dashboard. Structure: \`surface <id> input\` → \`card\` → \`column\` → h3 question + 2+ buttons + optional \`textfield placeholder="..."\`.
991
+ When you need structured input, push a surface with \`input\` in the header. It renders in the input bar, not on the dashboard. Structure: \`surface <id> input\` → \`card\` → \`column\` → h3 question + 2+ buttons.
992
992
 
993
993
  **Flow:**
994
994
  1. User clicks a button on a widget → \`[action:*]\`.
@@ -1581,6 +1581,82 @@ ${userSection}`;
1581
1581
  }
1582
1582
 
1583
1583
  // provisioning.ts
1584
+ function providerFromModel(model) {
1585
+ const slash = model.indexOf("/");
1586
+ if (slash <= 0)
1587
+ return null;
1588
+ return model.slice(0, slash).trim() || null;
1589
+ }
1590
+ function configuredProvidersFromConfig(cfg) {
1591
+ const profiles = cfg?.auth?.profiles;
1592
+ if (!profiles || typeof profiles !== "object")
1593
+ return [];
1594
+ const out = [];
1595
+ for (const entry of Object.values(profiles)) {
1596
+ const provider = entry?.provider;
1597
+ if (typeof provider === "string" && provider.trim() && !out.includes(provider.trim())) {
1598
+ out.push(provider.trim());
1599
+ }
1600
+ }
1601
+ return out;
1602
+ }
1603
+ function hasGatewayDefaultModel(cfg) {
1604
+ const modelCfg = cfg?.agents?.defaults?.model;
1605
+ if (!modelCfg || typeof modelCfg !== "object")
1606
+ return false;
1607
+ if (typeof modelCfg.primary === "string" && modelCfg.primary.trim())
1608
+ return true;
1609
+ return Array.isArray(modelCfg.fallbacks) && modelCfg.fallbacks.some((m) => typeof m === "string" && m.trim());
1610
+ }
1611
+ function parseJsonArrayFromStdout(stdout) {
1612
+ const raw = stdout.trim();
1613
+ const jsonStart = raw.indexOf("[");
1614
+ if (jsonStart < 0)
1615
+ return [];
1616
+ try {
1617
+ const parsed = JSON.parse(raw.slice(jsonStart));
1618
+ return Array.isArray(parsed) ? parsed : [];
1619
+ } catch {
1620
+ return [];
1621
+ }
1622
+ }
1623
+ async function resolveProvisionedAgentModel(cfg, currentList, runtime, log) {
1624
+ if (hasGatewayDefaultModel(cfg))
1625
+ return null;
1626
+ const configuredProviders = configuredProvidersFromConfig(cfg);
1627
+ if (configuredProviders.length === 0)
1628
+ return null;
1629
+ const configured = new Set(configuredProviders);
1630
+ const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
1631
+ for (const agent of currentList) {
1632
+ if (!agent || provisionedIds.has(agent.id))
1633
+ continue;
1634
+ const model = typeof agent.model === "string" ? agent.model.trim() : "";
1635
+ const provider = model ? providerFromModel(model) : null;
1636
+ if (model && provider && configured.has(provider)) {
1637
+ return model;
1638
+ }
1639
+ }
1640
+ const run = runtime.system?.runCommandWithTimeout;
1641
+ if (!run)
1642
+ return null;
1643
+ try {
1644
+ const result = await run(["openclaw", "infer", "model", "providers", "--json"], { timeoutMs: 60000 });
1645
+ if ((result?.code ?? 0) !== 0)
1646
+ return null;
1647
+ const catalog = parseJsonArrayFromStdout(result?.stdout ?? "");
1648
+ for (const provider of configuredProviders) {
1649
+ const entry = catalog.find((p) => p?.provider === provider);
1650
+ const defaults = Array.isArray(entry?.defaults) ? entry.defaults : [];
1651
+ const firstDefault = defaults.find((m) => typeof m === "string" && m.trim());
1652
+ if (firstDefault)
1653
+ return `${provider}/${firstDefault.trim()}`;
1654
+ }
1655
+ } catch (err) {
1656
+ log(`[agentlife] provisioned agent model discovery skipped: ${err?.message ?? err}`);
1657
+ }
1658
+ return null;
1659
+ }
1584
1660
  var PROVISIONED_AGENTS = [
1585
1661
  {
1586
1662
  id: "agentlife",
@@ -1671,6 +1747,7 @@ function scheduleEnrichment(state, runtime, targets, log) {
1671
1747
  async function provisionAgents(state, cfg, runtime, log) {
1672
1748
  const home = os.homedir();
1673
1749
  const currentList = [...cfg.agents?.list ?? []];
1750
+ const provisionedModel = await resolveProvisionedAgentModel(cfg, currentList, runtime, log);
1674
1751
  let configChanged = false;
1675
1752
  const newlyProvisioned = [];
1676
1753
  for (const agent of PROVISIONED_AGENTS) {
@@ -1710,12 +1787,15 @@ async function provisionAgents(state, cfg, runtime, log) {
1710
1787
  name: agent.name,
1711
1788
  workspace: workspaceDir
1712
1789
  };
1713
- if (agent.isDefault)
1790
+ const userOwnsDefault = currentList.some((a) => a?.default === true);
1791
+ if (agent.isDefault && !userOwnsDefault)
1714
1792
  entry.default = true;
1715
1793
  if (agent.subagents)
1716
1794
  entry.subagents = agent.subagents;
1717
1795
  if (agent.tools)
1718
1796
  entry.tools = agent.tools;
1797
+ if (provisionedModel)
1798
+ entry.model = provisionedModel;
1719
1799
  if (agent.isDefault) {
1720
1800
  currentList.unshift(entry);
1721
1801
  } else {
@@ -1741,16 +1821,32 @@ async function provisionAgents(state, cfg, runtime, log) {
1741
1821
  configChanged = true;
1742
1822
  log(`[agentlife] added subagents for ${agent.id}`);
1743
1823
  }
1824
+ if (provisionedModel && !existing.model) {
1825
+ existing.model = provisionedModel;
1826
+ configChanged = true;
1827
+ log(`[agentlife] set model for ${agent.id}: ${provisionedModel}`);
1828
+ }
1829
+ if (agent.isDefault && existing.default === true) {
1830
+ const userOwnsDefault = currentList.some((a) => a?.id !== agent.id && a?.default === true);
1831
+ if (userOwnsDefault) {
1832
+ delete existing.default;
1833
+ configChanged = true;
1834
+ log(`[agentlife] surrendered default to user agent (was ${agent.id})`);
1835
+ }
1836
+ }
1744
1837
  }
1745
1838
  }
1746
1839
  }
1747
1840
  const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
1841
+ const backfilledSubagentIds = [];
1748
1842
  for (const agent of currentList) {
1749
1843
  if (provisionedIds.has(agent.id))
1750
1844
  continue;
1751
1845
  if (!agent.subagents) {
1752
1846
  agent.subagents = { allowAgents: ["*"] };
1753
1847
  configChanged = true;
1848
+ if (agent.id)
1849
+ backfilledSubagentIds.push(agent.id);
1754
1850
  log(`[agentlife] backfilled subagents for ${agent.id}`);
1755
1851
  }
1756
1852
  }
@@ -1763,6 +1859,18 @@ async function provisionAgents(state, cfg, runtime, log) {
1763
1859
  const configPath = path4.join(os.homedir(), ".openclaw", "openclaw.json");
1764
1860
  const rawCfgForVisibility = JSON.parse(readFileSync2(configPath, "utf-8"));
1765
1861
  const backupPath = path4.join(os.homedir(), ".openclaw", "agentlife", "config-backup.json");
1862
+ if (backfilledSubagentIds.length > 0) {
1863
+ try {
1864
+ let backup = {};
1865
+ try {
1866
+ backup = JSON.parse(readFileSync2(backupPath, "utf-8"));
1867
+ } catch {}
1868
+ const existing = Array.isArray(backup.subagentsBackfilledIds) ? backup.subagentsBackfilledIds : [];
1869
+ backup.subagentsBackfilledIds = [...new Set([...existing, ...backfilledSubagentIds])];
1870
+ writeFileSync(backupPath, JSON.stringify(backup, null, 2) + `
1871
+ `, "utf-8");
1872
+ } catch {}
1873
+ }
1766
1874
  let visibilityWritten = false;
1767
1875
  const currentVisibility = rawCfgForVisibility?.tools?.sessions?.visibility;
1768
1876
  if (currentVisibility !== "all") {
@@ -2085,7 +2193,6 @@ var KNOWN_BODY_KEYWORDS = new Set([
2085
2193
  "button",
2086
2194
  "image",
2087
2195
  "icon",
2088
- "textfield",
2089
2196
  "checkbox",
2090
2197
  "sparkline",
2091
2198
  "progress",
@@ -2117,7 +2224,6 @@ var INPUT_SURFACE_ACTIONS = new Set([
2117
2224
  var TEXT_LEVELS = new Set(["h1", "h2", "h3", "h4", "h5", "body", "caption"]);
2118
2225
  var TEXT_HINTS = new Set(["icon", "avatar", "smallfeature", "mediumfeature", "largefeature", "header"]);
2119
2226
  var IMG_FITS = new Set(["contain", "cover", "fill", "none", "scale-down"]);
2120
- var TF_TYPES = new Set(["email", "shorttext", "number", "phone", "longtext", "date", "obscured"]);
2121
2227
  var ALIGN_VALUES = new Set(["start", "center", "end", "stretch"]);
2122
2228
  var DISTRIBUTE_VALUES = new Set(["start", "center", "end", "spacebetween", "spacearound", "spaceevenly"]);
2123
2229
  var TREND_VALUES = new Set(["up", "down", "flat"]);
@@ -2172,14 +2278,6 @@ var COMPONENT_CATALOG = {
2172
2278
  }
2173
2279
  },
2174
2280
  icon: { positionalTokens: new Set, positionalFree: true, attrs: {} },
2175
- textfield: {
2176
- positionalTokens: new Set,
2177
- positionalFree: true,
2178
- attrs: {
2179
- type: { kind: "enum", values: TF_TYPES },
2180
- placeholder: { kind: "free" }
2181
- }
2182
- },
2183
2281
  checkbox: {
2184
2282
  positionalTokens: new Set(["checked"]),
2185
2283
  positionalFree: true,
@@ -3194,17 +3292,21 @@ function pollFollowups(state) {
3194
3292
  }
3195
3293
  sweepCounter++;
3196
3294
  if (sweepCounter % 5 === 0 && state.surfaceIndex && state.followupQueue && state.fileSurfaceStorage) {
3197
- sweepExpiredSurfaces({
3198
- index: state.surfaceIndex,
3199
- queue: state.followupQueue,
3200
- storage: state.fileSurfaceStorage
3201
- }).then((r) => {
3202
- for (const sid of r.purged)
3203
- broadcastDelete(sid);
3204
- if (r.purged.length > 0) {
3205
- console.log("[agentlife:sweep] purged %d expired surfaces", r.purged.length);
3206
- }
3207
- }).catch((e) => console.warn("[agentlife:sweep] error: %s", e?.message));
3295
+ try {
3296
+ sweepExpiredSurfaces({
3297
+ index: state.surfaceIndex,
3298
+ queue: state.followupQueue,
3299
+ storage: state.fileSurfaceStorage
3300
+ }).then((r) => {
3301
+ for (const sid of r.purged)
3302
+ broadcastDelete(sid);
3303
+ if (r.purged.length > 0) {
3304
+ console.log("[agentlife:sweep] purged %d expired surfaces", r.purged.length);
3305
+ }
3306
+ }).catch((e) => console.warn("[agentlife:sweep] error: %s", e?.message));
3307
+ } catch (e) {
3308
+ console.warn("[agentlife] sweep invocation failed synchronously: %s", e?.message);
3309
+ }
3208
3310
  }
3209
3311
  }
3210
3312
 
@@ -3473,9 +3575,9 @@ Each turn:
3473
3575
  2. Not enough → \`agentlife_push\` the SAME surfaceId \`welcome-input\` with:
3474
3576
  - h3 question generated from what the user just said
3475
3577
  - 2–4 multichoice buttons \`action=choice\`, options derived from the user's own words (NEVER hardcoded domain lists)
3476
- - \`textfield placeholder="Something else…"\` escape hatch
3477
3578
  - updated \`context: {"phase":"welcome","turn":N+1,"answers":[...]}\`
3478
3579
  - goal + followup unchanged
3580
+ The input bar's text field is always-on, so the user can also type free text instead of picking a button — handle either response.
3479
3581
  Respond \`done\`.
3480
3582
  3. Enough → create the generalist and hand off the dashboard:
3481
3583
  - Workspace under \`$HOME/.openclaw/workspace-{id}\` (id = slugified short name or "me")
@@ -3505,7 +3607,6 @@ function renderWelcomeWidget(state, log) {
3505
3607
  ` column`,
3506
3608
  ` text "What do you want to track or improve?" h3`,
3507
3609
  ` text "One line. Anything — meals, money, a project, a habit." body`,
3508
- ` textfield placeholder="Type anything…"`,
3509
3610
  `goal: Onboard the user to their first agent via a guided interview`,
3510
3611
  `followup: +30m "If unanswered, replace the question with a gentler prompt."`,
3511
3612
  `context: {"phase":"welcome","turn":1,"answers":[]}`
@@ -3533,7 +3634,7 @@ function deleteWelcomeWidget(state, log) {
3533
3634
  }
3534
3635
 
3535
3636
  // services/surfaces-init.ts
3536
- import * as path5 from "node:path";
3637
+ import * as path6 from "node:path";
3537
3638
 
3538
3639
  // storage/reconciler.ts
3539
3640
  var defaultLogger = {
@@ -3781,23 +3882,45 @@ function dispatchRehydrate(state, runtime, log = console.log) {
3781
3882
  log(`[agentlife:rehydrate] dispatched rehydrate to ${orphaned.length} orphaned agent(s): ${orphaned.join(", ")}`);
3782
3883
  }
3783
3884
 
3885
+ // runtime-mode.ts
3886
+ import * as path5 from "node:path";
3887
+ function isPluginInstallOrInspectProcess() {
3888
+ const argv = process.argv.join(" ");
3889
+ const entrypoint = path5.basename(process.argv[1] || "");
3890
+ return entrypoint.includes("openclaw-plugins") || argv.includes("plugins") && argv.includes("install") || argv.includes("plugins") && argv.includes("inspect");
3891
+ }
3892
+ function shouldStartRuntimeServices() {
3893
+ return !isPluginInstallOrInspectProcess();
3894
+ }
3895
+
3784
3896
  // services/surfaces-init.ts
3785
3897
  function registerSurfacesService(api, state) {
3786
3898
  api.registerService({
3787
3899
  id: "agentlife-surfaces",
3788
3900
  start: async (ctx) => {
3789
- const agentlifeDir = path5.join(ctx.stateDir, "agentlife");
3901
+ if (!shouldStartRuntimeServices())
3902
+ return;
3903
+ const agentlifeDir = path6.join(ctx.stateDir, "agentlife");
3904
+ const nextHistoryDbPath = path6.join(agentlifeDir, "agentlife.db");
3905
+ const previousHistoryDbPath = state.historyDbPath;
3906
+ if (state.historyDb && previousHistoryDbPath && previousHistoryDbPath !== nextHistoryDbPath) {
3907
+ try {
3908
+ state.historyDb.close();
3909
+ } catch {}
3910
+ state.historyDb = null;
3911
+ state.surfaceIndex = null;
3912
+ state.followupQueue = null;
3913
+ }
3790
3914
  state.agentlifeStateDir = agentlifeDir;
3791
- state.registryFilePath = path5.join(agentlifeDir, "agent-registry.json");
3792
- state.dbBaseDir = path5.join(agentlifeDir, "db");
3793
- state.historyDbPath = path5.join(agentlifeDir, "agentlife.db");
3915
+ state.registryFilePath = path6.join(agentlifeDir, "agent-registry.json");
3916
+ state.dbBaseDir = path6.join(agentlifeDir, "db");
3917
+ state.historyDbPath = nextHistoryDbPath;
3794
3918
  const db = getOrCreateHistoryDb(state);
3795
3919
  if (!state.surfaceIndex)
3796
3920
  state.surfaceIndex = new SurfaceIndex(db);
3797
3921
  if (!state.followupQueue)
3798
3922
  state.followupQueue = new FollowupQueue(db);
3799
- if (!state.fileSurfaceStorage)
3800
- state.fileSurfaceStorage = new FileSurfaceStorage;
3923
+ state.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: ctx.stateDir });
3801
3924
  await loadRegistryFromDisk(state);
3802
3925
  try {
3803
3926
  const knownAgents = collectKnownAgents(api.runtime);
@@ -3847,11 +3970,11 @@ function collectKnownAgents(runtime) {
3847
3970
 
3848
3971
  // services/config-optimizer.ts
3849
3972
  import * as fs5 from "node:fs/promises";
3850
- import * as path6 from "node:path";
3973
+ import * as path7 from "node:path";
3851
3974
  import * as os2 from "node:os";
3852
3975
  var TARGET_MAX_PING_PONG = 1;
3853
3976
  var TARGET_MAX_CONCURRENT = 10;
3854
- var CONFIG_PATH = path6.join(os2.homedir(), ".openclaw", "openclaw.json");
3977
+ var CONFIG_PATH = process.env.OPENCLAW_CONFIG_PATH || path7.join(os2.homedir(), ".openclaw", "openclaw.json");
3855
3978
  async function readRawConfigFromDisk() {
3856
3979
  const raw = await fs5.readFile(CONFIG_PATH, "utf-8");
3857
3980
  return JSON.parse(raw);
@@ -3879,7 +4002,6 @@ async function restoreConfigFromBackup(api, backupPath) {
3879
4002
  delete fileCfg.agents?.defaults?.maxConcurrent;
3880
4003
  }
3881
4004
  await writeRawConfigToDisk(fileCfg);
3882
- await fs5.unlink(backupPath);
3883
4005
  console.log("[agentlife] restored config: maxPingPongTurns=%s, maxConcurrent=%s", backup.maxPingPongTurns ?? "default", backup.maxConcurrent ?? "default");
3884
4006
  return `config restored`;
3885
4007
  }
@@ -3887,8 +4009,10 @@ function registerConfigOptimizer(api, _state) {
3887
4009
  api.registerService({
3888
4010
  id: "agentlife-config-optimizer",
3889
4011
  start: async (ctx) => {
3890
- const configBackupDir = path6.join(ctx.stateDir, "agentlife");
3891
- const configBackupPath = path6.join(configBackupDir, "config-backup.json");
4012
+ if (!shouldStartRuntimeServices())
4013
+ return;
4014
+ const configBackupDir = path7.join(ctx.stateDir, "agentlife");
4015
+ const configBackupPath = path7.join(configBackupDir, "config-backup.json");
3892
4016
  const cfg = api.runtime.config.loadConfig();
3893
4017
  const currentPPT = cfg.session?.agentToAgent?.maxPingPongTurns;
3894
4018
  const currentMC = cfg.agents?.defaults?.maxConcurrent;
@@ -3899,10 +4023,16 @@ function registerConfigOptimizer(api, _state) {
3899
4023
  return;
3900
4024
  }
3901
4025
  await fs5.mkdir(configBackupDir, { recursive: true });
3902
- await fs5.writeFile(configBackupPath, JSON.stringify({
3903
- maxPingPongTurns: currentPPT ?? null,
3904
- maxConcurrent: currentMC ?? null
3905
- }));
4026
+ let existing = {};
4027
+ try {
4028
+ existing = JSON.parse(await fs5.readFile(configBackupPath, "utf-8"));
4029
+ } catch {}
4030
+ if (!("maxPingPongTurns" in existing))
4031
+ existing.maxPingPongTurns = currentPPT ?? null;
4032
+ if (!("maxConcurrent" in existing))
4033
+ existing.maxConcurrent = currentMC ?? null;
4034
+ await fs5.writeFile(configBackupPath, JSON.stringify(existing, null, 2) + `
4035
+ `);
3906
4036
  const fileCfg = await readRawConfigFromDisk();
3907
4037
  if (needsPPT) {
3908
4038
  fileCfg.session ??= {};
@@ -3924,13 +4054,196 @@ function registerConfigOptimizer(api, _state) {
3924
4054
  },
3925
4055
  stop: async (ctx) => {
3926
4056
  try {
3927
- const backupPath = path6.join(ctx.stateDir, "agentlife", "config-backup.json");
4057
+ const backupPath = path7.join(ctx.stateDir, "agentlife", "config-backup.json");
3928
4058
  await restoreConfigFromBackup(api, backupPath);
3929
4059
  } catch {}
3930
4060
  }
3931
4061
  });
3932
4062
  }
3933
4063
 
4064
+ // services/install-bootstrap.ts
4065
+ import * as fs6 from "node:fs";
4066
+ import * as path8 from "node:path";
4067
+ import * as os3 from "node:os";
4068
+ import { upsertAuthProfile } from "openclaw/plugin-sdk/provider-auth";
4069
+ function resolvePaths(home) {
4070
+ const openclawDir = home ? path8.join(home, ".openclaw") : process.env.OPENCLAW_STATE_DIR || path8.join(os3.homedir(), ".openclaw");
4071
+ const configPath = home ? path8.join(openclawDir, "openclaw.json") : process.env.OPENCLAW_CONFIG_PATH || path8.join(openclawDir, "openclaw.json");
4072
+ const agentlifeDir = path8.join(openclawDir, "agentlife");
4073
+ return {
4074
+ configPath,
4075
+ gatewayTokenFile: path8.join(openclawDir, "gateway-token.txt"),
4076
+ agentlifeDir,
4077
+ configBackupPath: path8.join(agentlifeDir, "config-backup.json"),
4078
+ pairRequestMarker: path8.join(agentlifeDir, "pair-requested")
4079
+ };
4080
+ }
4081
+ function readJsonSafe(p) {
4082
+ try {
4083
+ return JSON.parse(fs6.readFileSync(p, "utf-8"));
4084
+ } catch {
4085
+ return null;
4086
+ }
4087
+ }
4088
+ function writeJson(p, obj) {
4089
+ fs6.writeFileSync(p, JSON.stringify(obj, null, 2) + `
4090
+ `, "utf-8");
4091
+ }
4092
+ function normalizeLegacyCredential(profile) {
4093
+ if (!profile || typeof profile !== "object")
4094
+ return null;
4095
+ if (typeof profile.provider !== "string" || !profile.provider.trim())
4096
+ return null;
4097
+ const credential = { ...profile };
4098
+ if (credential.type === "api_key" && typeof credential.key !== "string" && typeof credential.apiKey === "string") {
4099
+ credential.key = credential.apiKey;
4100
+ delete credential.apiKey;
4101
+ }
4102
+ return credential;
4103
+ }
4104
+ function migrateLegacyAuthProfiles(openclawDir) {
4105
+ const agentsRoot = path8.join(openclawDir, "agents");
4106
+ let entries;
4107
+ try {
4108
+ entries = fs6.readdirSync(agentsRoot, { withFileTypes: true });
4109
+ } catch {
4110
+ return;
4111
+ }
4112
+ let migrated = 0;
4113
+ for (const entry of entries) {
4114
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
4115
+ continue;
4116
+ const agentDir = path8.join(agentsRoot, entry.name, "agent");
4117
+ const legacyPath = path8.join(agentDir, "auth-profiles.json");
4118
+ const legacy = readJsonSafe(legacyPath);
4119
+ const profiles = legacy?.profiles;
4120
+ if (!profiles || typeof profiles !== "object")
4121
+ continue;
4122
+ for (const [profileId, profile] of Object.entries(profiles)) {
4123
+ const credential = normalizeLegacyCredential(profile);
4124
+ if (!credential)
4125
+ continue;
4126
+ try {
4127
+ upsertAuthProfile({ profileId, credential, agentDir });
4128
+ migrated += 1;
4129
+ } catch (err) {
4130
+ console.warn(`[agentlife] install-bootstrap: auth profile migration skipped for ${entry.name}/${profileId}: ${err?.message ?? err}`);
4131
+ }
4132
+ }
4133
+ }
4134
+ if (migrated > 0) {
4135
+ console.log(`[agentlife] install-bootstrap: migrated ${migrated} legacy auth profile(s) into OpenClaw auth store`);
4136
+ }
4137
+ }
4138
+ function runInstallBootstrap(home) {
4139
+ const P = resolvePaths(home);
4140
+ try {
4141
+ fs6.mkdirSync(P.agentlifeDir, { recursive: true, mode: 448 });
4142
+ } catch (err) {
4143
+ console.warn(`[agentlife] install-bootstrap: cannot create ${P.agentlifeDir}: ${err?.message ?? err}`);
4144
+ return;
4145
+ }
4146
+ try {
4147
+ fs6.writeFileSync(P.pairRequestMarker, String(Date.now()), { mode: 384 });
4148
+ } catch (err) {
4149
+ console.warn(`[agentlife] install-bootstrap: cannot write pair-requested marker: ${err?.message ?? err}`);
4150
+ }
4151
+ migrateLegacyAuthProfiles(path8.dirname(P.gatewayTokenFile));
4152
+ if (!fs6.existsSync(P.configPath)) {
4153
+ console.log("[agentlife] install-bootstrap: gateway config missing — deferring config writes");
4154
+ return;
4155
+ }
4156
+ const cfg = readJsonSafe(P.configPath);
4157
+ if (!cfg || typeof cfg !== "object") {
4158
+ console.warn(`[agentlife] install-bootstrap: cannot parse ${P.configPath} — skipping config writes`);
4159
+ return;
4160
+ }
4161
+ const existingBackup = readJsonSafe(P.configBackupPath) ?? {};
4162
+ const originals = [
4163
+ ["sessionsVisibility", cfg?.tools?.sessions?.visibility ?? null],
4164
+ ["agentToAgentEnabled", cfg?.tools?.agentToAgent?.enabled ?? null],
4165
+ ["gatewayBind", cfg?.gateway?.bind ?? null],
4166
+ ["hooksAllowConversationAccess", cfg?.plugins?.entries?.agentlife?.hooks?.allowConversationAccess ?? null],
4167
+ ["maxPingPongTurns", cfg?.session?.agentToAgent?.maxPingPongTurns ?? null],
4168
+ ["maxConcurrent", cfg?.agents?.defaults?.maxConcurrent ?? null]
4169
+ ];
4170
+ let backupChanged = false;
4171
+ for (const [k, orig] of originals) {
4172
+ if (!(k in existingBackup)) {
4173
+ existingBackup[k] = orig;
4174
+ backupChanged = true;
4175
+ }
4176
+ }
4177
+ if (backupChanged) {
4178
+ try {
4179
+ writeJson(P.configBackupPath, existingBackup);
4180
+ } catch (err) {
4181
+ console.warn(`[agentlife] install-bootstrap: cannot write backup: ${err?.message ?? err}`);
4182
+ }
4183
+ }
4184
+ let changed = false;
4185
+ cfg.gateway ??= {};
4186
+ if (cfg.gateway.bind !== "lan") {
4187
+ cfg.gateway.bind = "lan";
4188
+ changed = true;
4189
+ }
4190
+ cfg.gateway.auth ??= {};
4191
+ if (!cfg.gateway.auth.token && fs6.existsSync(P.gatewayTokenFile)) {
4192
+ try {
4193
+ const tok = fs6.readFileSync(P.gatewayTokenFile, "utf-8").trim();
4194
+ if (tok) {
4195
+ cfg.gateway.auth.token = tok;
4196
+ changed = true;
4197
+ console.log(`[agentlife] install-bootstrap: imported gateway token from ${P.gatewayTokenFile}`);
4198
+ }
4199
+ } catch (err) {
4200
+ console.warn(`[agentlife] install-bootstrap: cannot read ${P.gatewayTokenFile}: ${err?.message ?? err}`);
4201
+ }
4202
+ }
4203
+ cfg.tools ??= {};
4204
+ cfg.tools.sessions ??= {};
4205
+ if (cfg.tools.sessions.visibility !== "all") {
4206
+ cfg.tools.sessions.visibility = "all";
4207
+ changed = true;
4208
+ }
4209
+ cfg.tools.agentToAgent ??= {};
4210
+ if (cfg.tools.agentToAgent.enabled !== true) {
4211
+ cfg.tools.agentToAgent.enabled = true;
4212
+ changed = true;
4213
+ }
4214
+ cfg.plugins ??= {};
4215
+ cfg.plugins.entries ??= {};
4216
+ cfg.plugins.entries.agentlife ??= {};
4217
+ cfg.plugins.entries.agentlife.hooks ??= {};
4218
+ if (cfg.plugins.entries.agentlife.hooks.allowConversationAccess !== true) {
4219
+ cfg.plugins.entries.agentlife.hooks.allowConversationAccess = true;
4220
+ changed = true;
4221
+ }
4222
+ cfg.session ??= {};
4223
+ cfg.session.agentToAgent ??= {};
4224
+ if (cfg.session.agentToAgent.maxPingPongTurns !== 1) {
4225
+ cfg.session.agentToAgent.maxPingPongTurns = 1;
4226
+ changed = true;
4227
+ }
4228
+ cfg.agents ??= {};
4229
+ cfg.agents.defaults ??= {};
4230
+ if (!cfg.agents.defaults.maxConcurrent || cfg.agents.defaults.maxConcurrent < 10) {
4231
+ cfg.agents.defaults.maxConcurrent = 10;
4232
+ changed = true;
4233
+ }
4234
+ if (!changed) {
4235
+ console.log("[agentlife] install-bootstrap: config already normalized");
4236
+ return;
4237
+ }
4238
+ try {
4239
+ writeJson(P.configPath, cfg);
4240
+ console.log("[agentlife] install-bootstrap: gateway config normalized (bind=lan, sessions.visibility=all, agentToAgent.enabled=true, hooks.allowConversationAccess=true, maxPingPongTurns=1, maxConcurrent=10)");
4241
+ console.log("[agentlife] install-bootstrap: changes take effect on next `openclaw daemon restart`");
4242
+ } catch (err) {
4243
+ console.warn(`[agentlife] install-bootstrap: cannot write ${P.configPath}: ${err?.message ?? err}`);
4244
+ }
4245
+ }
4246
+
3934
4247
  // services/session-reconcile.ts
3935
4248
  function modelMatches(configured, sessionProvider, sessionModel) {
3936
4249
  if (!sessionModel)
@@ -4093,7 +4406,7 @@ async function reconcileInternalSessions(state, createdInternalSessions) {
4093
4406
  }
4094
4407
 
4095
4408
  // services/supervisor-cleanup.ts
4096
- import * as fs6 from "node:fs";
4409
+ import * as fs7 from "node:fs";
4097
4410
  var STALE_SUPERVISOR_SURFACE = "supervisor-daily";
4098
4411
  var STALE_SUPERVISOR_DIRECT_SESSION = "agent:supervisor:agentlife:direct:operator";
4099
4412
  async function cleanupSupervisorLegacyState(state) {
@@ -4106,7 +4419,7 @@ async function cleanupSupervisorLegacyState(state) {
4106
4419
  removeFollowup(state, STALE_SUPERVISOR_SURFACE);
4107
4420
  if (view2 && !view2.isTransient && view2.path) {
4108
4421
  try {
4109
- fs6.unlinkSync(view2.path);
4422
+ fs7.unlinkSync(view2.path);
4110
4423
  } catch {}
4111
4424
  }
4112
4425
  state.surfaceIndex?.delete(STALE_SUPERVISOR_SURFACE);
@@ -4288,6 +4601,8 @@ function startObservabilityService(state) {
4288
4601
 
4289
4602
  // hooks/activity-hooks.ts
4290
4603
  import * as fsSync2 from "node:fs";
4604
+ import * as nodePath from "node:path";
4605
+ import * as nodeOs from "node:os";
4291
4606
 
4292
4607
  // usage.ts
4293
4608
  var MODEL_COSTS = {
@@ -4936,6 +5251,62 @@ ${parts.join(`
4936
5251
  }
4937
5252
  var QUALITY_CHECK_MAX_WAIT_MS = 5000;
4938
5253
  var TERMINAL_MARKERS = new Set(["NO_REPLY", "ANNOUNCE_SKIP", "REPLY_SKIP", "HEARTBEAT_OK"]);
5254
+ function resolveOpenClawRoot() {
5255
+ const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
5256
+ return fromEnv ? nodePath.resolve(fromEnv) : nodePath.join(nodeOs.homedir(), ".openclaw");
5257
+ }
5258
+ function findSessionModelError(agentId, sessionKey) {
5259
+ const sessionsDir = nodePath.join(resolveOpenClawRoot(), "agents", agentId, "sessions");
5260
+ const indexPath = nodePath.join(sessionsDir, "sessions.json");
5261
+ let sessionFile = null;
5262
+ try {
5263
+ const index = JSON.parse(fsSync2.readFileSync(indexPath, "utf-8"));
5264
+ const entry = index?.[sessionKey];
5265
+ if (typeof entry?.sessionFile === "string") {
5266
+ sessionFile = entry.sessionFile;
5267
+ } else if (typeof entry?.sessionId === "string") {
5268
+ sessionFile = nodePath.join(sessionsDir, `${entry.sessionId}.jsonl`);
5269
+ }
5270
+ } catch {
5271
+ return null;
5272
+ }
5273
+ if (!sessionFile)
5274
+ return null;
5275
+ let provider;
5276
+ let model;
5277
+ let lines;
5278
+ try {
5279
+ lines = fsSync2.readFileSync(sessionFile, "utf-8").split(`
5280
+ `).filter(Boolean);
5281
+ } catch {
5282
+ return null;
5283
+ }
5284
+ for (const line of lines) {
5285
+ try {
5286
+ const entry = JSON.parse(line);
5287
+ if (entry?.type === "model_change") {
5288
+ provider = typeof entry.provider === "string" ? entry.provider : provider;
5289
+ model = typeof entry.modelId === "string" ? entry.modelId : model;
5290
+ }
5291
+ } catch {}
5292
+ }
5293
+ for (let i = lines.length - 1;i >= 0; i--) {
5294
+ try {
5295
+ const entry = JSON.parse(lines[i]);
5296
+ const msg = entry?.message;
5297
+ if (entry?.type !== "message" || msg?.role !== "assistant")
5298
+ continue;
5299
+ if (typeof msg.provider === "string")
5300
+ provider = msg.provider;
5301
+ if (typeof msg.model === "string")
5302
+ model = msg.model;
5303
+ if (typeof msg.errorMessage === "string" && msg.errorMessage.trim()) {
5304
+ return { error: msg.errorMessage, provider, model };
5305
+ }
5306
+ } catch {}
5307
+ }
5308
+ return null;
5309
+ }
4939
5310
  function processPendingQualityChecks(state) {
4940
5311
  if (state.disabled || !state.historyDb)
4941
5312
  return 0;
@@ -4977,6 +5348,21 @@ function processPendingQualityChecks(state) {
4977
5348
  const pushed = db.prepare("SELECT COUNT(*) as c FROM surface_events WHERE event IN ('created','updated') AND createdAt > ? AND (agentId = ? OR agentId IS NULL)").get(row.runStart, row.agentId);
4978
5349
  if ((pushed?.c ?? 0) > 0)
4979
5350
  continue;
5351
+ const modelError = findSessionModelError(row.agentId, row.sessionKey);
5352
+ if (modelError) {
5353
+ recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
5354
+ runId: row.runId,
5355
+ data: JSON.stringify({
5356
+ issue: "model_error_no_widget",
5357
+ trigger: row.trigger,
5358
+ error: modelError.error,
5359
+ provider: modelError.provider,
5360
+ model: modelError.model
5361
+ })
5362
+ });
5363
+ console.warn("[agentlife:quality] %s model error before widget push: %s", row.agentId, modelError.error);
5364
+ continue;
5365
+ }
4980
5366
  recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
4981
5367
  runId: row.runId,
4982
5368
  data: JSON.stringify({ issue: "no_widget_pushed", trigger: row.trigger })
@@ -5075,7 +5461,7 @@ function rescueOrphanedSurfaces(state, agentId, error) {
5075
5461
  }
5076
5462
 
5077
5463
  // services/escalation.ts
5078
- import * as fs7 from "node:fs";
5464
+ import * as fs8 from "node:fs";
5079
5465
  var PATHOLOGY2 = process.env.PATHOLOGY_TEST_MODE === "1";
5080
5466
  var ESCALATION_REVIEW_THRESHOLD_MS = PATHOLOGY2 ? 2 * 60 * 1000 : 48 * 60 * 60 * 1000;
5081
5467
  var TREND_WINDOW_DAYS = 7;
@@ -5141,7 +5527,7 @@ function clearEscalationSurface(state, agentId) {
5141
5527
  const view2 = getSurface(state, surfaceId);
5142
5528
  if (view2 && !view2.isTransient && view2.path) {
5143
5529
  try {
5144
- fs7.unlinkSync(view2.path);
5530
+ fs8.unlinkSync(view2.path);
5145
5531
  } catch {}
5146
5532
  }
5147
5533
  state.surfaceIndex.delete(surfaceId);
@@ -5385,31 +5771,32 @@ function startDailySweepService(state) {
5385
5771
  import * as crypto4 from "node:crypto";
5386
5772
  import * as fsSync3 from "node:fs";
5387
5773
  import { createRequire as createRequire3 } from "node:module";
5388
- import * as os6 from "node:os";
5389
- import * as path10 from "node:path";
5774
+ import * as os7 from "node:os";
5775
+ import * as path12 from "node:path";
5390
5776
 
5391
5777
  // gateway/web-app.ts
5392
5778
  import * as crypto3 from "node:crypto";
5393
- import * as os5 from "node:os";
5394
- import * as path9 from "node:path";
5395
- import * as fs10 from "node:fs";
5779
+ import * as os6 from "node:os";
5780
+ import * as path11 from "node:path";
5781
+ import * as fs11 from "node:fs";
5396
5782
 
5397
5783
  // services/pairing-access-token.ts
5398
5784
  import * as crypto from "node:crypto";
5399
- import * as fs8 from "node:fs";
5400
- import * as os3 from "node:os";
5401
- import * as path7 from "node:path";
5785
+ import * as fs9 from "node:fs";
5786
+ import * as os4 from "node:os";
5787
+ import * as path9 from "node:path";
5402
5788
  var cachedToken = null;
5403
5789
  function pairingAccessPath() {
5404
- return path7.join(os3.homedir(), ".openclaw", "agentlife", "pairing-access.json");
5790
+ const stateDir = process.env.OPENCLAW_STATE_DIR || path9.join(os4.homedir(), ".openclaw");
5791
+ return path9.join(stateDir, "agentlife", "pairing-access.json");
5405
5792
  }
5406
5793
  function loadOrCreatePairingAccessToken() {
5407
5794
  if (cachedToken)
5408
5795
  return cachedToken;
5409
5796
  const filePath = pairingAccessPath();
5410
5797
  try {
5411
- if (fs8.existsSync(filePath)) {
5412
- const raw = fs8.readFileSync(filePath, "utf-8");
5798
+ if (fs9.existsSync(filePath)) {
5799
+ const raw = fs9.readFileSync(filePath, "utf-8");
5413
5800
  const obj = JSON.parse(raw);
5414
5801
  const token2 = typeof obj?.token === "string" ? obj.token : null;
5415
5802
  if (token2 && token2.length >= 32) {
@@ -5418,12 +5805,12 @@ function loadOrCreatePairingAccessToken() {
5418
5805
  }
5419
5806
  }
5420
5807
  const token = crypto.randomBytes(32).toString("base64url");
5421
- const dir = path7.dirname(filePath);
5422
- if (!fs8.existsSync(dir))
5423
- fs8.mkdirSync(dir, { recursive: true, mode: 448 });
5424
- fs8.writeFileSync(filePath, JSON.stringify({ token, createdAtMs: Date.now() }, null, 2), { mode: 384 });
5808
+ const dir = path9.dirname(filePath);
5809
+ if (!fs9.existsSync(dir))
5810
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
5811
+ fs9.writeFileSync(filePath, JSON.stringify({ token, createdAtMs: Date.now() }, null, 2), { mode: 384 });
5425
5812
  try {
5426
- fs8.chmodSync(filePath, 384);
5813
+ fs9.chmodSync(filePath, 384);
5427
5814
  } catch {}
5428
5815
  cachedToken = token;
5429
5816
  return token;
@@ -5436,25 +5823,28 @@ function loadOrCreatePairingAccessToken() {
5436
5823
  // services/cloudflared-supervisor.ts
5437
5824
  import { spawn, spawnSync } from "node:child_process";
5438
5825
  import * as crypto2 from "node:crypto";
5439
- import * as fs9 from "node:fs";
5440
- import * as os4 from "node:os";
5441
- import * as path8 from "node:path";
5442
- var AGENTLIFE_DIR = path8.join(os4.homedir(), ".openclaw", "agentlife");
5443
- var TUNNEL_FILE = path8.join(AGENTLIFE_DIR, "tunnel.json");
5444
- var BIN_DIR = path8.join(AGENTLIFE_DIR, "bin");
5445
- var PAIR_REQUEST_MARKER = path8.join(AGENTLIFE_DIR, "pair-requested");
5446
- var IDENTITY_DIR = path8.join(os4.homedir(), ".agentlife");
5447
- var DEVICE_FILE = path8.join(IDENTITY_DIR, "device.json");
5448
- var LEGACY_DEVICE_FILE = path8.join(AGENTLIFE_DIR, "device.json");
5449
- var CLOUDFLARED_BIN = os4.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
5450
- var CLOUDFLARED_PATH = path8.join(BIN_DIR, CLOUDFLARED_BIN);
5826
+ import * as fs10 from "node:fs";
5827
+ import * as os5 from "node:os";
5828
+ import * as path10 from "node:path";
5829
+ var DEFAULT_OPENCLAW_STATE_DIR = path10.join(os5.homedir(), ".openclaw");
5830
+ var OPENCLAW_STATE_DIR = process.env.OPENCLAW_STATE_DIR || DEFAULT_OPENCLAW_STATE_DIR;
5831
+ var IS_PROFILE_STATE_DIR = OPENCLAW_STATE_DIR !== DEFAULT_OPENCLAW_STATE_DIR;
5832
+ var AGENTLIFE_DIR = path10.join(OPENCLAW_STATE_DIR, "agentlife");
5833
+ var TUNNEL_FILE = path10.join(AGENTLIFE_DIR, "tunnel.json");
5834
+ var BIN_DIR = path10.join(AGENTLIFE_DIR, "bin");
5835
+ var PAIR_REQUEST_MARKER = path10.join(AGENTLIFE_DIR, "pair-requested");
5836
+ var IDENTITY_DIR = IS_PROFILE_STATE_DIR ? path10.join(OPENCLAW_STATE_DIR, ".agentlife") : path10.join(os5.homedir(), ".agentlife");
5837
+ var DEVICE_FILE = path10.join(IDENTITY_DIR, "device.json");
5838
+ var LEGACY_DEVICE_FILE = path10.join(AGENTLIFE_DIR, "device.json");
5839
+ var CLOUDFLARED_BIN = os5.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
5840
+ var CLOUDFLARED_PATH = path10.join(BIN_DIR, CLOUDFLARED_BIN);
5451
5841
  var API_BASE = process.env.AGENTLIFE_API_BASE || "https://api.agentlife.app";
5452
5842
  var RESTART_DELAY_MS = 5000;
5453
5843
  var STABLE_RUNTIME_MS = 60000;
5454
5844
  function createInitialState() {
5455
5845
  let resolver;
5456
- const promise = new Promise((resolve) => {
5457
- resolver = resolve;
5846
+ const promise = new Promise((resolve2) => {
5847
+ resolver = resolve2;
5458
5848
  });
5459
5849
  return {
5460
5850
  tunnelInfo: null,
@@ -5475,7 +5865,7 @@ function getTunnelInfo() {
5475
5865
  function awaitTunnelReady(timeoutMs) {
5476
5866
  return Promise.race([
5477
5867
  state.readyPromise,
5478
- new Promise((resolve) => setTimeout(() => resolve(null), timeoutMs))
5868
+ new Promise((resolve2) => setTimeout(() => resolve2(null), timeoutMs))
5479
5869
  ]);
5480
5870
  }
5481
5871
  function startCloudflaredSupervisor() {
@@ -5567,20 +5957,20 @@ async function doBootstrap() {
5567
5957
  return tunnelInfo;
5568
5958
  }
5569
5959
  function isPairRequested() {
5570
- return fs9.existsSync(PAIR_REQUEST_MARKER);
5960
+ return fs10.existsSync(PAIR_REQUEST_MARKER);
5571
5961
  }
5572
5962
  function requestPair() {
5573
5963
  try {
5574
- if (!fs9.existsSync(AGENTLIFE_DIR))
5575
- fs9.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5576
- fs9.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
5964
+ if (!fs10.existsSync(AGENTLIFE_DIR))
5965
+ fs10.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5966
+ fs10.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
5577
5967
  } catch (err) {
5578
5968
  console.warn(`[cloudflared-supervisor] failed to write pair-request marker: ${err?.message ?? err}`);
5579
5969
  }
5580
5970
  }
5581
5971
  function clearPairRequest() {
5582
5972
  try {
5583
- fs9.unlinkSync(PAIR_REQUEST_MARKER);
5973
+ fs10.unlinkSync(PAIR_REQUEST_MARKER);
5584
5974
  } catch {}
5585
5975
  }
5586
5976
  function parseRetryAfter(value) {
@@ -5604,18 +5994,18 @@ function scheduleProvisionRetry(delayMs) {
5604
5994
  }, delayMs);
5605
5995
  }
5606
5996
  function ensureDirs() {
5607
- if (!fs9.existsSync(AGENTLIFE_DIR))
5608
- fs9.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5609
- if (!fs9.existsSync(BIN_DIR))
5610
- fs9.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
5611
- if (!fs9.existsSync(IDENTITY_DIR))
5612
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5997
+ if (!fs10.existsSync(AGENTLIFE_DIR))
5998
+ fs10.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5999
+ if (!fs10.existsSync(BIN_DIR))
6000
+ fs10.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
6001
+ if (!fs10.existsSync(IDENTITY_DIR))
6002
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5613
6003
  }
5614
6004
  function readIdentityFile(filePath) {
5615
- if (!fs9.existsSync(filePath))
6005
+ if (!fs10.existsSync(filePath))
5616
6006
  return null;
5617
6007
  try {
5618
- const raw = fs9.readFileSync(filePath, "utf-8");
6008
+ const raw = fs10.readFileSync(filePath, "utf-8");
5619
6009
  const parsed = JSON.parse(raw);
5620
6010
  if (typeof parsed.deviceId === "string" && typeof parsed.deviceSecret === "string") {
5621
6011
  return { deviceId: parsed.deviceId, deviceSecret: parsed.deviceSecret };
@@ -5633,14 +6023,14 @@ function loadDeviceIdentity() {
5633
6023
  if (!legacy)
5634
6024
  return null;
5635
6025
  try {
5636
- if (!fs9.existsSync(IDENTITY_DIR))
5637
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5638
- fs9.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
6026
+ if (!fs10.existsSync(IDENTITY_DIR))
6027
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
6028
+ fs10.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
5639
6029
  try {
5640
- fs9.chmodSync(DEVICE_FILE, 384);
6030
+ fs10.chmodSync(DEVICE_FILE, 384);
5641
6031
  } catch {}
5642
6032
  try {
5643
- fs9.unlinkSync(LEGACY_DEVICE_FILE);
6033
+ fs10.unlinkSync(LEGACY_DEVICE_FILE);
5644
6034
  } catch {}
5645
6035
  console.log(`[cloudflared-supervisor] migrated device.json to ${DEVICE_FILE}`);
5646
6036
  } catch (err) {
@@ -5652,25 +6042,25 @@ function loadOrCreateDeviceIdentity() {
5652
6042
  const existing = loadDeviceIdentity();
5653
6043
  if (existing)
5654
6044
  return existing;
5655
- if (!fs9.existsSync(IDENTITY_DIR))
5656
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
6045
+ if (!fs10.existsSync(IDENTITY_DIR))
6046
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5657
6047
  const identity = {
5658
6048
  deviceId: crypto2.randomUUID(),
5659
6049
  deviceSecret: crypto2.randomBytes(32).toString("base64url")
5660
6050
  };
5661
- fs9.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
6051
+ fs10.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
5662
6052
  try {
5663
- fs9.chmodSync(DEVICE_FILE, 384);
6053
+ fs10.chmodSync(DEVICE_FILE, 384);
5664
6054
  } catch {}
5665
6055
  console.log(`[cloudflared-supervisor] generated new device identity (deviceId=${identity.deviceId})`);
5666
6056
  return identity;
5667
6057
  }
5668
6058
  var AGENTLIFE_API_BASE = API_BASE;
5669
6059
  function loadCachedTunnel() {
5670
- if (!fs9.existsSync(TUNNEL_FILE))
6060
+ if (!fs10.existsSync(TUNNEL_FILE))
5671
6061
  return null;
5672
6062
  try {
5673
- const raw = fs9.readFileSync(TUNNEL_FILE, "utf-8");
6063
+ const raw = fs10.readFileSync(TUNNEL_FILE, "utf-8");
5674
6064
  const parsed = JSON.parse(raw);
5675
6065
  if (typeof parsed.subdomain === "string" && typeof parsed.hostname === "string" && typeof parsed.tunnelUrl === "string" && typeof parsed.tunnelToken === "string" && typeof parsed.provisionedAt === "number") {
5676
6066
  return parsed;
@@ -5679,9 +6069,9 @@ function loadCachedTunnel() {
5679
6069
  return null;
5680
6070
  }
5681
6071
  function persistTunnel(info) {
5682
- fs9.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
6072
+ fs10.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
5683
6073
  try {
5684
- fs9.chmodSync(TUNNEL_FILE, 384);
6074
+ fs10.chmodSync(TUNNEL_FILE, 384);
5685
6075
  } catch {}
5686
6076
  }
5687
6077
  async function provisionTunnel(identity) {
@@ -5734,14 +6124,14 @@ async function provisionTunnel(identity) {
5734
6124
  }
5735
6125
  }
5736
6126
  function ensureCloudflaredBinary() {
5737
- if (fs9.existsSync(CLOUDFLARED_PATH)) {
6127
+ if (fs10.existsSync(CLOUDFLARED_PATH)) {
5738
6128
  try {
5739
- fs9.accessSync(CLOUDFLARED_PATH, fs9.constants.X_OK);
6129
+ fs10.accessSync(CLOUDFLARED_PATH, fs10.constants.X_OK);
5740
6130
  return CLOUDFLARED_PATH;
5741
6131
  } catch {}
5742
6132
  }
5743
- const platform2 = os4.platform();
5744
- const arch2 = os4.arch();
6133
+ const platform2 = os5.platform();
6134
+ const arch2 = os5.arch();
5745
6135
  const release = detectCloudflaredRelease(platform2, arch2);
5746
6136
  if (!release) {
5747
6137
  console.warn(`[cloudflared-supervisor] unsupported platform: ${platform2}/${arch2}`);
@@ -5754,28 +6144,28 @@ function ensureCloudflaredBinary() {
5754
6144
  if (release.kind === "tgz") {
5755
6145
  const extracted = extractTgzCloudflared(release.tempPath, BIN_DIR);
5756
6146
  try {
5757
- fs9.unlinkSync(release.tempPath);
6147
+ fs10.unlinkSync(release.tempPath);
5758
6148
  } catch {}
5759
6149
  if (!extracted)
5760
6150
  return null;
5761
6151
  } else {
5762
6152
  try {
5763
- fs9.renameSync(release.tempPath, CLOUDFLARED_PATH);
6153
+ fs10.renameSync(release.tempPath, CLOUDFLARED_PATH);
5764
6154
  } catch (err) {
5765
6155
  console.warn(`[cloudflared-supervisor] rename failed: ${err?.message}`);
5766
6156
  return null;
5767
6157
  }
5768
6158
  }
5769
6159
  try {
5770
- fs9.chmodSync(CLOUDFLARED_PATH, 493);
6160
+ fs10.chmodSync(CLOUDFLARED_PATH, 493);
5771
6161
  } catch {}
5772
- return fs9.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
6162
+ return fs10.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
5773
6163
  }
5774
6164
  function detectCloudflaredRelease(platform2, arch2) {
5775
6165
  const base = "https://github.com/cloudflare/cloudflared/releases/latest/download";
5776
6166
  if (platform2 === "darwin") {
5777
6167
  const asset = arch2 === "arm64" ? "cloudflared-darwin-arm64.tgz" : "cloudflared-darwin-amd64.tgz";
5778
- return { url: `${base}/${asset}`, kind: "tgz", tempPath: path8.join(BIN_DIR, asset) };
6168
+ return { url: `${base}/${asset}`, kind: "tgz", tempPath: path10.join(BIN_DIR, asset) };
5779
6169
  }
5780
6170
  if (platform2 === "linux") {
5781
6171
  const asset = arch2 === "arm64" ? "cloudflared-linux-arm64" : arch2 === "arm" ? "cloudflared-linux-arm" : arch2 === "x64" ? "cloudflared-linux-amd64" : null;
@@ -5799,11 +6189,11 @@ function downloadWithCurl(url, dest) {
5799
6189
  if (result.status !== 0) {
5800
6190
  console.warn(`[cloudflared-supervisor] curl failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
5801
6191
  try {
5802
- fs9.unlinkSync(dest);
6192
+ fs10.unlinkSync(dest);
5803
6193
  } catch {}
5804
6194
  return false;
5805
6195
  }
5806
- return fs9.existsSync(dest) && fs9.statSync(dest).size > 0;
6196
+ return fs10.existsSync(dest) && fs10.statSync(dest).size > 0;
5807
6197
  }
5808
6198
  function extractTgzCloudflared(tgzPath, destDir) {
5809
6199
  const result = spawnSync("tar", ["-xzf", tgzPath, "-C", destDir], { stdio: "pipe" });
@@ -5811,10 +6201,10 @@ function extractTgzCloudflared(tgzPath, destDir) {
5811
6201
  console.warn(`[cloudflared-supervisor] tar extract failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
5812
6202
  return false;
5813
6203
  }
5814
- return fs9.existsSync(CLOUDFLARED_PATH);
6204
+ return fs10.existsSync(CLOUDFLARED_PATH);
5815
6205
  }
5816
6206
  function killOrphanedCloudflareds(binPath) {
5817
- if (os4.platform() === "win32")
6207
+ if (os5.platform() === "win32")
5818
6208
  return;
5819
6209
  const found = spawnSync("pgrep", ["-f", binPath], { stdio: "pipe" });
5820
6210
  if (found.status !== 0)
@@ -5884,13 +6274,16 @@ var MIME_TYPES = {
5884
6274
  ".txt": "text/plain",
5885
6275
  ".map": "application/json"
5886
6276
  };
6277
+ function resolveStateDir() {
6278
+ return process.env.OPENCLAW_STATE_DIR || path11.join(os6.homedir(), ".openclaw");
6279
+ }
5887
6280
  function mintBootstrapToken() {
5888
6281
  const bootstrapToken = crypto3.randomBytes(32).toString("base64url");
5889
- const devicesDir = path9.join(os5.homedir(), ".openclaw", "devices");
5890
- const bootstrapPath = path9.join(devicesDir, "bootstrap.json");
5891
- if (!fs10.existsSync(devicesDir))
5892
- fs10.mkdirSync(devicesDir, { recursive: true });
5893
- const registry = fs10.existsSync(bootstrapPath) ? JSON.parse(fs10.readFileSync(bootstrapPath, "utf-8")) : {};
6282
+ const devicesDir = path11.join(resolveStateDir(), "devices");
6283
+ const bootstrapPath = path11.join(devicesDir, "bootstrap.json");
6284
+ if (!fs11.existsSync(devicesDir))
6285
+ fs11.mkdirSync(devicesDir, { recursive: true });
6286
+ const registry = fs11.existsSync(bootstrapPath) ? JSON.parse(fs11.readFileSync(bootstrapPath, "utf-8")) : {};
5894
6287
  registry[bootstrapToken] = {
5895
6288
  token: bootstrapToken,
5896
6289
  profile: {
@@ -5899,15 +6292,15 @@ function mintBootstrapToken() {
5899
6292
  },
5900
6293
  issuedAtMs: Date.now()
5901
6294
  };
5902
- fs10.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
6295
+ fs11.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
5903
6296
  return bootstrapToken;
5904
6297
  }
5905
6298
  function registerWebApp(api) {
5906
- const pluginRoot = path9.resolve(path9.dirname(api.source), "..");
5907
- const appRoot = path9.join(pluginRoot, "web-build");
5908
- const hasWebBuild = fs10.existsSync(appRoot);
5909
- const indexPath = hasWebBuild ? path9.join(appRoot, "index.html") : null;
5910
- const hasIndex = !!(indexPath && fs10.existsSync(indexPath));
6299
+ const pluginRoot = path11.resolve(path11.dirname(api.source), "..");
6300
+ const appRoot = path11.join(pluginRoot, "web-build");
6301
+ const hasWebBuild = fs11.existsSync(appRoot);
6302
+ const indexPath = hasWebBuild ? path11.join(appRoot, "index.html") : null;
6303
+ const hasIndex = !!(indexPath && fs11.existsSync(indexPath));
5911
6304
  if (!hasWebBuild) {
5912
6305
  api.logger.info("[agentlife] web-build/ not found — /agentlife/pair will still register; static dashboard disabled");
5913
6306
  } else if (!hasIndex) {
@@ -5915,11 +6308,13 @@ function registerWebApp(api) {
5915
6308
  }
5916
6309
  let gatewayToken = "";
5917
6310
  try {
5918
- const configPath = path9.join(__require("node:os").homedir(), ".openclaw", "openclaw.json");
5919
- const raw = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
6311
+ const configPath = process.env.OPENCLAW_CONFIG_PATH || path11.join(resolveStateDir(), "openclaw.json");
6312
+ const raw = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
5920
6313
  gatewayToken = raw?.gateway?.auth?.token || "";
5921
6314
  } catch {}
5922
- loadOrCreatePairingAccessToken();
6315
+ if (shouldStartRuntimeServices()) {
6316
+ loadOrCreatePairingAccessToken();
6317
+ }
5923
6318
  const approveLatest = () => {
5924
6319
  try {
5925
6320
  const { execSync } = __require("node:child_process");
@@ -6034,16 +6429,16 @@ function registerWebApp(api) {
6034
6429
  return true;
6035
6430
  }
6036
6431
  const relative = urlPath.replace(/^\/agentlife\/?/, "") || "index.html";
6037
- const filePath = path9.resolve(appRoot, relative);
6432
+ const filePath = path11.resolve(appRoot, relative);
6038
6433
  if (!filePath.startsWith(appRoot)) {
6039
6434
  res.writeHead(403);
6040
6435
  res.end();
6041
6436
  return true;
6042
6437
  }
6043
- const target = fs10.existsSync(filePath) && fs10.statSync(filePath).isFile() ? filePath : indexPath;
6044
- const ext = path9.extname(target).toLowerCase();
6438
+ const target = fs11.existsSync(filePath) && fs11.statSync(filePath).isFile() ? filePath : indexPath;
6439
+ const ext = path11.extname(target).toLowerCase();
6045
6440
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
6046
- let content = fs10.readFileSync(target);
6441
+ let content = fs11.readFileSync(target);
6047
6442
  if (target === indexPath) {
6048
6443
  setTimeout(approveLatest, 2000);
6049
6444
  setTimeout(approveLatest, 5000);
@@ -6079,62 +6474,29 @@ function registerWebApp(api) {
6079
6474
 
6080
6475
  // services/pairing.ts
6081
6476
  var TUNNEL_READY_TIMEOUT_MS = 90000;
6477
+ function resolveStateDir2(ctx) {
6478
+ return ctx?.stateDir || process.env.OPENCLAW_STATE_DIR || path12.join(os7.homedir(), ".openclaw");
6479
+ }
6082
6480
  function registerPairingServices(api) {
6083
6481
  api.registerService({
6084
6482
  id: "agentlife-pairing-qr",
6085
- start: async (_ctx) => {
6086
- try {
6087
- const cfg = api.runtime.config.loadConfig();
6088
- const port = cfg.gateway?.port ?? 18789;
6089
- const token = cfg.gateway?.auth?.token;
6090
- if (!token)
6091
- return;
6092
- const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
6093
- if (!supervisorTunnel?.tunnelUrl) {
6094
- console.warn(`[agentlife] Cloudflared tunnel did not come up within ${TUNNEL_READY_TIMEOUT_MS / 1000}s — ` + `pairing QR will not be emitted. Check cloudflared-supervisor logs in the gateway output.`);
6095
- return;
6096
- }
6097
- const url = supervisorTunnel.tunnelUrl;
6098
- const bootstrapToken = mintBootstrapToken();
6099
- const payload = JSON.stringify({ url, bootstrapToken });
6100
- const setupCode = Buffer.from(payload).toString("base64url");
6101
- let qrTerminal = null;
6102
- try {
6103
- const realPath = fsSync3.realpathSync(process.argv[1] || "");
6104
- const mainRequire = createRequire3(realPath);
6105
- qrTerminal = mainRequire("qrcode-terminal");
6106
- } catch {}
6107
- console.log(`
6108
- \x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
6109
- `);
6110
- if (qrTerminal?.generate) {
6111
- qrTerminal.generate(setupCode, { small: true }, (qr) => {
6112
- console.log(qr);
6113
- console.log(`
6114
- Setup code: ${setupCode}
6115
- `);
6116
- });
6117
- } else {
6118
- console.log(`Setup code: ${setupCode}`);
6119
- }
6120
- console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
6121
- console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
6122
- console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
6123
- console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
6124
- console.log(`
6125
- Scan the QR from the mobile app to connect.
6126
- `);
6127
- } catch (err) {
6483
+ start: (ctx) => {
6484
+ if (!shouldStartRuntimeServices())
6485
+ return;
6486
+ emitPairingQr(api, ctx).catch((err) => {
6128
6487
  console.warn("[agentlife] QR generation skipped:", err?.message);
6129
- }
6488
+ });
6130
6489
  }
6131
6490
  });
6132
6491
  api.registerService({
6133
6492
  id: "agentlife-auto-pair",
6134
6493
  start: async (ctx) => {
6135
- const devicesDir = path10.join(os6.homedir(), ".openclaw", "devices");
6136
- const pendingPath = path10.join(devicesDir, "pending.json");
6137
- const pairedPath = path10.join(devicesDir, "paired.json");
6494
+ if (!shouldStartRuntimeServices())
6495
+ return;
6496
+ const stateDir = resolveStateDir2(ctx);
6497
+ const devicesDir = path12.join(stateDir, "devices");
6498
+ const pendingPath = path12.join(devicesDir, "pending.json");
6499
+ const pairedPath = path12.join(devicesDir, "paired.json");
6138
6500
  const pollInterval = 3000;
6139
6501
  const withAdmin = (arr) => {
6140
6502
  const base = Array.isArray(arr) ? arr.filter((s) => typeof s === "string") : [];
@@ -6229,6 +6591,52 @@ Setup code: ${setupCode}
6229
6591
  }
6230
6592
  });
6231
6593
  }
6594
+ async function emitPairingQr(api, _ctx) {
6595
+ try {
6596
+ const cfg = api.runtime.config.loadConfig();
6597
+ const port = cfg.gateway?.port ?? 18789;
6598
+ const token = cfg.gateway?.auth?.token;
6599
+ if (!token)
6600
+ return;
6601
+ const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
6602
+ if (!supervisorTunnel?.tunnelUrl) {
6603
+ console.warn(`[agentlife] Cloudflared tunnel did not come up within ${TUNNEL_READY_TIMEOUT_MS / 1000}s — ` + `pairing QR will not be emitted. Check cloudflared-supervisor logs in the gateway output.`);
6604
+ return;
6605
+ }
6606
+ const url = supervisorTunnel.tunnelUrl;
6607
+ const bootstrapToken = mintBootstrapToken();
6608
+ const payload = JSON.stringify({ url, bootstrapToken });
6609
+ const setupCode = Buffer.from(payload).toString("base64url");
6610
+ let qrTerminal = null;
6611
+ try {
6612
+ const realPath = fsSync3.realpathSync(process.argv[1] || "");
6613
+ const mainRequire = createRequire3(realPath);
6614
+ qrTerminal = mainRequire("qrcode-terminal");
6615
+ } catch {}
6616
+ console.log(`
6617
+ \x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
6618
+ `);
6619
+ if (qrTerminal?.generate) {
6620
+ qrTerminal.generate(setupCode, { small: true }, (qr) => {
6621
+ console.log(qr);
6622
+ console.log(`
6623
+ Setup code: ${setupCode}
6624
+ `);
6625
+ });
6626
+ } else {
6627
+ console.log(`Setup code: ${setupCode}`);
6628
+ }
6629
+ console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
6630
+ console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
6631
+ console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
6632
+ console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
6633
+ console.log(`
6634
+ Scan the QR from the mobile app to connect.
6635
+ `);
6636
+ } catch (err) {
6637
+ console.warn("[agentlife] QR generation skipped:", err?.message);
6638
+ }
6639
+ }
6232
6640
 
6233
6641
  // hooks/bootstrap.ts
6234
6642
  var SKIP_GUIDANCE_AGENTS = new Set(["agentlife"]);
@@ -6388,15 +6796,15 @@ function registerBootstrapHookImpl(api, state2) {
6388
6796
  }
6389
6797
 
6390
6798
  // notifications.ts
6391
- import * as fs11 from "node:fs";
6392
- import * as path11 from "node:path";
6799
+ import * as fs12 from "node:fs";
6800
+ import * as path13 from "node:path";
6393
6801
  var config;
6394
6802
  function loadConfig() {
6395
6803
  if (config !== undefined)
6396
6804
  return config;
6397
6805
  try {
6398
- const configPath = path11.join(process.env.HOME ?? "~", ".openclaw", "agentlife", "notification-config.json");
6399
- const raw = fs11.readFileSync(configPath, "utf-8");
6806
+ const configPath = path13.join(process.env.HOME ?? "~", ".openclaw", "agentlife", "notification-config.json");
6807
+ const raw = fs12.readFileSync(configPath, "utf-8");
6400
6808
  const parsed = JSON.parse(raw);
6401
6809
  if (parsed.serverUrl && parsed.apiKey) {
6402
6810
  config = { serverUrl: parsed.serverUrl, apiKey: parsed.apiKey };
@@ -6966,9 +7374,101 @@ function registerPromptStateHook(api, state2) {
6966
7374
  }
6967
7375
 
6968
7376
  // gateway/agents.ts
6969
- import * as fs12 from "node:fs";
6970
- import * as os7 from "node:os";
6971
- import * as path12 from "node:path";
7377
+ import * as fs13 from "node:fs";
7378
+ import * as os8 from "node:os";
7379
+ import * as path14 from "node:path";
7380
+ import { ensureAuthProfileStore, upsertAuthProfile as upsertAuthProfile2 } from "openclaw/plugin-sdk/provider-auth";
7381
+ function resolveOpenClawRoot2() {
7382
+ const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
7383
+ return fromEnv ? path14.resolve(fromEnv) : path14.join(os8.homedir(), ".openclaw");
7384
+ }
7385
+ function providerFromModel2(model) {
7386
+ const slash = model.indexOf("/");
7387
+ if (slash <= 0)
7388
+ return null;
7389
+ return model.slice(0, slash).trim() || null;
7390
+ }
7391
+ function authenticatedProvidersFromConfig(cfg) {
7392
+ const out = new Set;
7393
+ const profiles = cfg?.auth?.profiles;
7394
+ if (!profiles || typeof profiles !== "object")
7395
+ return out;
7396
+ for (const entry of Object.values(profiles)) {
7397
+ const provider = entry?.provider;
7398
+ if (typeof provider === "string" && provider.trim()) {
7399
+ out.add(provider.trim());
7400
+ }
7401
+ }
7402
+ return out;
7403
+ }
7404
+ function findSiblingAuthProfile(provider, targetAgentId) {
7405
+ const agentsRoot = path14.join(resolveOpenClawRoot2(), "agents");
7406
+ let entries;
7407
+ try {
7408
+ entries = fs13.readdirSync(agentsRoot, { withFileTypes: true });
7409
+ } catch {
7410
+ return null;
7411
+ }
7412
+ for (const entry of entries) {
7413
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
7414
+ continue;
7415
+ if (entry.name === targetAgentId)
7416
+ continue;
7417
+ const agentDir = path14.join(agentsRoot, entry.name, "agent");
7418
+ try {
7419
+ const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false, syncExternalCli: false });
7420
+ for (const [profileId, profile] of Object.entries(store?.profiles ?? {})) {
7421
+ if (profile?.provider !== provider)
7422
+ continue;
7423
+ const type = profile?.type;
7424
+ const mode = profile?.mode;
7425
+ if (type === "oauth")
7426
+ continue;
7427
+ if (mode === "oauth")
7428
+ continue;
7429
+ return { profileId, profile };
7430
+ }
7431
+ } catch {}
7432
+ const file = path14.join(agentDir, "auth-profiles.json");
7433
+ let data;
7434
+ try {
7435
+ data = JSON.parse(fs13.readFileSync(file, "utf-8"));
7436
+ } catch {
7437
+ continue;
7438
+ }
7439
+ const profiles = data?.profiles;
7440
+ if (!profiles || typeof profiles !== "object")
7441
+ continue;
7442
+ for (const [profileId, profile] of Object.entries(profiles)) {
7443
+ if (profile?.provider !== provider)
7444
+ continue;
7445
+ const type = profile?.type;
7446
+ const mode = profile?.mode;
7447
+ if (type === "oauth")
7448
+ continue;
7449
+ if (mode === "oauth")
7450
+ continue;
7451
+ return {
7452
+ profileId,
7453
+ profile
7454
+ };
7455
+ }
7456
+ }
7457
+ return null;
7458
+ }
7459
+ function writeAgentAuthProfile(agentId, provider, match) {
7460
+ if (!match)
7461
+ return false;
7462
+ const agentDir = path14.join(resolveOpenClawRoot2(), "agents", agentId, "agent");
7463
+ fs13.mkdirSync(agentDir, { recursive: true });
7464
+ const credential = { ...match.profile };
7465
+ if (credential.type === "api_key" && typeof credential.key !== "string" && typeof credential.apiKey === "string") {
7466
+ credential.key = credential.apiKey;
7467
+ delete credential.apiKey;
7468
+ }
7469
+ upsertAuthProfile2({ profileId: match.profileId, credential, agentDir });
7470
+ return true;
7471
+ }
6972
7472
  function registerAgentGateway(api, state2) {
6973
7473
  api.registerGatewayMethod("agentlife.createAgent", async ({ params, respond }) => {
6974
7474
  const id = typeof params?.id === "string" ? params.id.trim() : "";
@@ -6980,14 +7480,26 @@ function registerAgentGateway(api, state2) {
6980
7480
  const subagents = params?.subagents && typeof params.subagents === "object" ? params.subagents : undefined;
6981
7481
  const identity = params?.identity && typeof params.identity === "object" ? params.identity : undefined;
6982
7482
  if (!id) {
6983
- respond(false, { error: "missing agent id" });
7483
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agent id" });
6984
7484
  return;
6985
7485
  }
6986
7486
  if (!workspace) {
6987
- respond(false, { error: "missing workspace path" });
7487
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing workspace path" });
6988
7488
  return;
6989
7489
  }
6990
7490
  const cfg = api.runtime.config.loadConfig();
7491
+ const provider = model ? providerFromModel2(model) : null;
7492
+ const providerInConfig = provider ? authenticatedProvidersFromConfig(cfg).has(provider) : false;
7493
+ const siblingAuth = provider ? findSiblingAuthProfile(provider, id) : null;
7494
+ if (provider && !providerInConfig && !siblingAuth) {
7495
+ respond(false, undefined, {
7496
+ code: "missing_provider_auth",
7497
+ message: `missing provider credentials for ${provider}; configure ${provider} before creating an agent with ${model}`,
7498
+ provider,
7499
+ model
7500
+ });
7501
+ return;
7502
+ }
6991
7503
  const currentList = cfg.agents?.list ?? [];
6992
7504
  const existing = currentList.find((a) => a.id === id);
6993
7505
  if (!existing) {
@@ -7034,6 +7546,10 @@ function registerAgentGateway(api, state2) {
7034
7546
  console.log("[agentlife] builder patched agent config: %s", id);
7035
7547
  }
7036
7548
  }
7549
+ const authCopied = provider ? writeAgentAuthProfile(id, provider, siblingAuth) : false;
7550
+ if (authCopied) {
7551
+ console.log("[agentlife] copied %s auth profile into agent: %s", provider, id);
7552
+ }
7037
7553
  const hasGoodDescription = description && isUsableDescription(description, id, name);
7038
7554
  state2.agentRegistry.set(id, {
7039
7555
  name: name || id,
@@ -7063,17 +7579,17 @@ function registerAgentGateway(api, state2) {
7063
7579
  console.warn("[agentlife] deleteWelcomeWidget failed for %s: %s", id, e?.message);
7064
7580
  }
7065
7581
  }
7066
- respond(true, { status, id, name, model, workspace, description, ...configFields.length ? { configFields } : {} });
7067
- });
7582
+ respond(true, { status, id, name, model, workspace, description, ...authCopied ? { authCopied: true } : {}, ...configFields.length ? { configFields } : {} });
7583
+ }, { scope: "operator.admin" });
7068
7584
  api.registerGatewayMethod("agentlife.deleteAgent", async ({ params, respond }) => {
7069
7585
  const id = typeof params?.id === "string" ? params.id.trim() : "";
7070
7586
  if (!id) {
7071
- respond(false, { error: "missing agent id" });
7587
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agent id" });
7072
7588
  return;
7073
7589
  }
7074
7590
  const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
7075
7591
  if (provisionedIds.has(id)) {
7076
- respond(false, { error: "cannot delete provisioned agent" });
7592
+ respond(false, undefined, { code: "FAILED", message: "cannot delete provisioned agent" });
7077
7593
  return;
7078
7594
  }
7079
7595
  const cleanup = {
@@ -7156,10 +7672,10 @@ function registerAgentGateway(api, state2) {
7156
7672
  state2.agentDbs.delete(id);
7157
7673
  }
7158
7674
  if (state2.dbBaseDir) {
7159
- const dbPath = path12.join(state2.dbBaseDir, `${id}.db`);
7675
+ const dbPath = path14.join(state2.dbBaseDir, `${id}.db`);
7160
7676
  try {
7161
- if (fs12.existsSync(dbPath)) {
7162
- fs12.unlinkSync(dbPath);
7677
+ if (fs13.existsSync(dbPath)) {
7678
+ fs13.unlinkSync(dbPath);
7163
7679
  cleanup.agentDbDeleted = true;
7164
7680
  }
7165
7681
  } catch (e) {
@@ -7171,24 +7687,24 @@ function registerAgentGateway(api, state2) {
7171
7687
  const existingEntry = currentList.find((a) => a.id === id);
7172
7688
  const filtered = currentList.filter((a) => a.id !== id);
7173
7689
  const removedFromConfig = filtered.length < currentList.length;
7174
- const openclawHome = path12.join(os7.homedir(), ".openclaw");
7175
- const stateDir = path12.join(openclawHome, "agents", id);
7176
- if (stateDir.startsWith(path12.join(openclawHome, "agents") + path12.sep)) {
7690
+ const openclawHome = path14.join(os8.homedir(), ".openclaw");
7691
+ const stateDir = path14.join(openclawHome, "agents", id);
7692
+ if (stateDir.startsWith(path14.join(openclawHome, "agents") + path14.sep)) {
7177
7693
  try {
7178
- if (fs12.existsSync(stateDir)) {
7179
- fs12.rmSync(stateDir, { recursive: true, force: true });
7694
+ if (fs13.existsSync(stateDir)) {
7695
+ fs13.rmSync(stateDir, { recursive: true, force: true });
7180
7696
  cleanup.stateDeleted = true;
7181
7697
  }
7182
7698
  } catch (e) {
7183
7699
  console.warn("[agentlife] deleteAgent: failed to delete state dir:", e?.message);
7184
7700
  }
7185
7701
  }
7186
- const defaultWorkspace = path12.join(openclawHome, `workspace-${id}`);
7187
- const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path12.resolve(existingEntry.workspace) : defaultWorkspace;
7188
- if (workspaceToDelete.startsWith(openclawHome + path12.sep) && workspaceToDelete !== openclawHome) {
7702
+ const defaultWorkspace = path14.join(openclawHome, `workspace-${id}`);
7703
+ const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path14.resolve(existingEntry.workspace) : defaultWorkspace;
7704
+ if (workspaceToDelete.startsWith(openclawHome + path14.sep) && workspaceToDelete !== openclawHome) {
7189
7705
  try {
7190
- if (fs12.existsSync(workspaceToDelete)) {
7191
- fs12.rmSync(workspaceToDelete, { recursive: true, force: true });
7706
+ if (fs13.existsSync(workspaceToDelete)) {
7707
+ fs13.rmSync(workspaceToDelete, { recursive: true, force: true });
7192
7708
  cleanup.workspaceDeleted = true;
7193
7709
  }
7194
7710
  } catch (e) {
@@ -7211,7 +7727,7 @@ function registerAgentGateway(api, state2) {
7211
7727
  }
7212
7728
  console.log("[agentlife] deleted agent: %s (config=%s, registry=%s, surfaces=%d, sessions=%d, cron=%d, historyRows=%d, agentDb=%s, state=%s, workspace=%s)", id, removedFromConfig, removedFromRegistry, cleanup.surfacesCleared, cleanup.sessions, cleanup.cronJobs, cleanup.historyRows, cleanup.agentDbDeleted, cleanup.stateDeleted, cleanup.workspaceDeleted);
7213
7729
  respond(true, { status: "deleted", id, removedFromConfig, removedFromRegistry, cleanup });
7214
- });
7730
+ }, { scope: "operator.admin" });
7215
7731
  api.registerGatewayMethod("agentlife.agents", ({ respond }) => {
7216
7732
  const agents = {};
7217
7733
  for (const [id, entry] of state2.agentRegistry.entries()) {
@@ -7228,11 +7744,11 @@ function registerDbGateway(api, state2) {
7228
7744
  const sql = typeof params?.sql === "string" ? params.sql.trim() : "";
7229
7745
  const sqlParams = Array.isArray(params?.params) ? params.params : [];
7230
7746
  if (!agentId)
7231
- return respond(false, { error: "missing agentId" });
7747
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agentId" });
7232
7748
  if (!sql)
7233
- return respond(false, { error: "missing sql" });
7749
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing sql" });
7234
7750
  if (/^\s*SELECT\b/i.test(sql))
7235
- return respond(false, { error: "use agentlife.db.query for SELECT" });
7751
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "use agentlife.db.query for SELECT" });
7236
7752
  try {
7237
7753
  const db = getOrCreateAgentDb(state2, agentId);
7238
7754
  if (sqlParams.length > 0) {
@@ -7243,19 +7759,19 @@ function registerDbGateway(api, state2) {
7243
7759
  respond(true, { changes: 0, lastInsertRowid: 0 });
7244
7760
  }
7245
7761
  } catch (err) {
7246
- respond(false, { error: err?.message ?? "db error" });
7762
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "db error" });
7247
7763
  }
7248
- });
7764
+ }, { scope: "operator.admin" });
7249
7765
  api.registerGatewayMethod("agentlife.db.query", ({ params, respond }) => {
7250
7766
  const agentId = typeof params?.agentId === "string" ? params.agentId.trim() : "";
7251
7767
  const sql = typeof params?.sql === "string" ? params.sql.trim() : "";
7252
7768
  const sqlParams = Array.isArray(params?.params) ? params.params : [];
7253
7769
  if (!agentId)
7254
- return respond(false, { error: "missing agentId" });
7770
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing agentId" });
7255
7771
  if (!sql)
7256
- return respond(false, { error: "missing sql" });
7772
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing sql" });
7257
7773
  if (!/^\s*SELECT\b/i.test(sql))
7258
- return respond(false, { error: "use agentlife.db.exec for non-SELECT" });
7774
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "use agentlife.db.exec for non-SELECT" });
7259
7775
  try {
7260
7776
  const db = getOrCreateAgentDb(state2, agentId);
7261
7777
  const rows = db.prepare(sql).all(...sqlParams);
@@ -7265,9 +7781,9 @@ function registerDbGateway(api, state2) {
7265
7781
  const columns = result.length > 0 ? Object.keys(result[0]) : [];
7266
7782
  respond(true, { rows: result, columns, count: result.length, truncated });
7267
7783
  } catch (err) {
7268
- respond(false, { error: err?.message ?? "db error" });
7784
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "db error" });
7269
7785
  }
7270
- });
7786
+ }, { scope: "operator.admin" });
7271
7787
  }
7272
7788
 
7273
7789
  // gateway/history.ts
@@ -7308,7 +7824,7 @@ function registerHistoryGateway(api, state2) {
7308
7824
  const rows = db.prepare(`SELECT * FROM surface_events ${where} ORDER BY createdAt DESC LIMIT ? OFFSET ?`).all(...bind);
7309
7825
  respond(true, { events: rows, count: rows.length });
7310
7826
  } catch (err) {
7311
- respond(false, { error: err?.message ?? "history error" });
7827
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "history error" });
7312
7828
  }
7313
7829
  }, { scope: "operator.read" });
7314
7830
  api.registerGatewayMethod("agentlife.history.widgets", ({ params, respond }) => {
@@ -7349,7 +7865,7 @@ function registerHistoryGateway(api, state2) {
7349
7865
  `).all(...bind, limit, offset);
7350
7866
  respond(true, { widgets: rows, count: rows.length, hasMore: rows.length >= limit });
7351
7867
  } catch (err) {
7352
- respond(false, { error: err?.message ?? "history.widgets error" });
7868
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "history.widgets error" });
7353
7869
  }
7354
7870
  }, { scope: "operator.read" });
7355
7871
  api.registerGatewayMethod("agentlife.trace", ({ params, respond }) => {
@@ -7406,7 +7922,7 @@ function registerHistoryGateway(api, state2) {
7406
7922
  }));
7407
7923
  respond(true, { entries, hasMore });
7408
7924
  } catch (err) {
7409
- respond(false, { error: err?.message ?? "trace query failed" });
7925
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "trace query failed" });
7410
7926
  }
7411
7927
  }, { scope: "operator.read" });
7412
7928
  }
@@ -7479,13 +7995,13 @@ function registerUsageGateway(api, state2) {
7479
7995
  bySession: bySessionRows
7480
7996
  });
7481
7997
  } catch (err) {
7482
- respond(false, { error: err?.message ?? "usage query error" });
7998
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "usage query error" });
7483
7999
  }
7484
8000
  }, { scope: "operator.read" });
7485
8001
  api.registerGatewayMethod("agentlife.surfaceUsage", ({ params, respond }) => {
7486
8002
  const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
7487
8003
  if (!surfaceId)
7488
- return respond(false, { error: "missing surfaceId" });
8004
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId" });
7489
8005
  try {
7490
8006
  const db = getOrCreateHistoryDb(state2);
7491
8007
  const rows = db.prepare(`SELECT * FROM surface_usage WHERE surfaceId = ? ORDER BY createdAt DESC`).all(surfaceId);
@@ -7501,13 +8017,13 @@ function registerUsageGateway(api, state2) {
7501
8017
  }, { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, totalTokens: 0, estimatedCost: 0, llmCalls: 0 });
7502
8018
  respond(true, { surfaceId, entries: rows, totals });
7503
8019
  } catch (err) {
7504
- respond(false, { error: err?.message ?? "surface usage error" });
8020
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "surface usage error" });
7505
8021
  }
7506
8022
  }, { scope: "operator.read" });
7507
8023
  }
7508
8024
 
7509
8025
  // gateway/surfaces-gateway.ts
7510
- import * as fs13 from "node:fs";
8026
+ import * as fs14 from "node:fs";
7511
8027
  var guidedDismissSent = new Set;
7512
8028
  var warnedMissingOrigin = new Set;
7513
8029
  function registerSurfacesGateway(api, state2) {
@@ -7550,7 +8066,7 @@ function registerSurfacesGateway(api, state2) {
7550
8066
  api.registerGatewayMethod("agentlife.dismiss", async ({ params, respond }) => {
7551
8067
  const surfaceId = params?.surfaceId;
7552
8068
  if (!surfaceId || typeof surfaceId !== "string") {
7553
- respond(false, { error: "missing surfaceId" });
8069
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId" });
7554
8070
  return;
7555
8071
  }
7556
8072
  const view2 = getSurface(state2, surfaceId);
@@ -7596,7 +8112,7 @@ function registerSurfacesGateway(api, state2) {
7596
8112
  guidedDismissSent.delete(surfaceId);
7597
8113
  if (!view2.isTransient && view2.path) {
7598
8114
  try {
7599
- fs13.unlinkSync(view2.path);
8115
+ fs14.unlinkSync(view2.path);
7600
8116
  } catch (err) {
7601
8117
  if (err?.code !== "ENOENT")
7602
8118
  console.warn("[agentlife] dismiss: file unlink failed for %s: %s", surfaceId, err?.message);
@@ -7621,7 +8137,7 @@ function registerSurfacesGateway(api, state2) {
7621
8137
  const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
7622
8138
  const event = typeof params?.event === "string" ? params.event.trim() : "";
7623
8139
  if (!surfaceId || !event) {
7624
- respond(false, { error: "missing surfaceId or event" });
8140
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId or event" });
7625
8141
  return;
7626
8142
  }
7627
8143
  const agentId = getAgentId(state2, surfaceId) ?? undefined;
@@ -7633,7 +8149,7 @@ function registerSurfacesGateway(api, state2) {
7633
8149
  const message = typeof params?.message === "string" ? params.message : "";
7634
8150
  const sessionKey = typeof params?.sessionKey === "string" ? params.sessionKey : "";
7635
8151
  if (!message) {
7636
- respond(false, { error: "missing message" });
8152
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing message" });
7637
8153
  return;
7638
8154
  }
7639
8155
  broadcastInput(message, sessionKey);
@@ -7642,7 +8158,7 @@ function registerSurfacesGateway(api, state2) {
7642
8158
  api.registerGatewayMethod("agentlife.chain.get", ({ params, respond }) => {
7643
8159
  const root = typeof params?.root === "string" ? params.root.trim() : "";
7644
8160
  if (!root) {
7645
- respond(false, { error: "missing root" });
8161
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing root" });
7646
8162
  return;
7647
8163
  }
7648
8164
  if (!state2.surfaceIndex) {
@@ -7666,7 +8182,7 @@ function registerSurfacesGateway(api, state2) {
7666
8182
  api.registerGatewayMethod("agentlife.action", ({ params, respond }) => {
7667
8183
  const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
7668
8184
  if (!surfaceId) {
7669
- respond(false, { error: "missing surfaceId" });
8185
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing surfaceId" });
7670
8186
  return;
7671
8187
  }
7672
8188
  const view2 = getSurface(state2, surfaceId);
@@ -7757,7 +8273,7 @@ function registerAutomationsGateway(api, state2) {
7757
8273
  const name = typeof params?.name === "string" ? params.name.trim() : "";
7758
8274
  const automationPath = typeof params?.path === "string" ? params.path.trim() : "";
7759
8275
  if (!type || !name || !automationPath) {
7760
- respond(false, { error: "missing required fields: type, name, path" });
8276
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing required fields: type, name, path" });
7761
8277
  return;
7762
8278
  }
7763
8279
  const surfaceId = typeof params?.surfaceId === "string" ? params.surfaceId.trim() : "";
@@ -7765,7 +8281,7 @@ function registerAutomationsGateway(api, state2) {
7765
8281
  const id = registerAutomation(state2, { surfaceId, agentId, type, name, path: automationPath });
7766
8282
  respond(true, { id, surfaceId, agentId, type, name, path: automationPath, status: "running" });
7767
8283
  } catch (err) {
7768
- respond(false, { error: err?.message ?? "register failed" });
8284
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "register failed" });
7769
8285
  }
7770
8286
  }, { scope: "operator.write" });
7771
8287
  api.registerGatewayMethod("agentlife.automations.list", ({ params, respond }) => {
@@ -7806,7 +8322,7 @@ function registerAutomationsGateway(api, state2) {
7806
8322
  }))
7807
8323
  });
7808
8324
  } catch (err) {
7809
- respond(false, { error: err?.message ?? "list failed" });
8325
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "list failed" });
7810
8326
  }
7811
8327
  }, { scope: "operator.read" });
7812
8328
  api.registerGatewayMethod("agentlife.automations.update", ({ params, respond }) => {
@@ -7814,44 +8330,44 @@ function registerAutomationsGateway(api, state2) {
7814
8330
  const id = typeof params?.id === "number" ? params.id : parseInt(params?.id, 10);
7815
8331
  const status = typeof params?.status === "string" ? params.status.trim() : "";
7816
8332
  if (!id || !status || !["running", "stopped", "error"].includes(status)) {
7817
- respond(false, { error: "missing id or invalid status (running|stopped|error)" });
8333
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing id or invalid status (running|stopped|error)" });
7818
8334
  return;
7819
8335
  }
7820
8336
  const db = getOrCreateHistoryDb(state2);
7821
8337
  db.prepare("UPDATE automations SET status = ?, updatedAt = ? WHERE id = ?").run(status, Date.now(), id);
7822
8338
  respond(true, { id, status });
7823
8339
  } catch (err) {
7824
- respond(false, { error: err?.message ?? "update failed" });
8340
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "update failed" });
7825
8341
  }
7826
8342
  }, { scope: "operator.write" });
7827
8343
  api.registerGatewayMethod("agentlife.automations.remove", ({ params, respond }) => {
7828
8344
  try {
7829
8345
  const id = typeof params?.id === "number" ? params.id : parseInt(params?.id, 10);
7830
8346
  if (!id) {
7831
- respond(false, { error: "missing id" });
8347
+ respond(false, undefined, { code: "INVALID_REQUEST", message: "missing id" });
7832
8348
  return;
7833
8349
  }
7834
8350
  const db = getOrCreateHistoryDb(state2);
7835
8351
  db.prepare("UPDATE automations SET status = 'removed', updatedAt = ? WHERE id = ?").run(Date.now(), id);
7836
8352
  respond(true, { id, status: "removed" });
7837
8353
  } catch (err) {
7838
- respond(false, { error: err?.message ?? "remove failed" });
8354
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "remove failed" });
7839
8355
  }
7840
8356
  }, { scope: "operator.write" });
7841
8357
  }
7842
8358
 
7843
8359
  // gateway/admin.ts
7844
- import * as fs14 from "node:fs/promises";
8360
+ import * as fs15 from "node:fs/promises";
7845
8361
  import * as fsSync4 from "node:fs";
7846
- import * as os8 from "node:os";
7847
- import * as path13 from "node:path";
8362
+ import * as os9 from "node:os";
8363
+ import * as path15 from "node:path";
7848
8364
  import { fileURLToPath } from "node:url";
7849
8365
  var PLUGIN_VERSION = (() => {
7850
8366
  try {
7851
- const moduleDir = path13.dirname(fileURLToPath(import.meta.url));
8367
+ const moduleDir = path15.dirname(fileURLToPath(import.meta.url));
7852
8368
  const candidates = [
7853
- path13.join(moduleDir, "..", "package.json"),
7854
- path13.join(moduleDir, "..", "..", "package.json")
8369
+ path15.join(moduleDir, "..", "package.json"),
8370
+ path15.join(moduleDir, "..", "..", "package.json")
7855
8371
  ];
7856
8372
  for (const p of candidates) {
7857
8373
  if (fsSync4.existsSync(p)) {
@@ -7900,7 +8416,7 @@ async function fetchNpmLatestVersion(force = false) {
7900
8416
  return inflightFetch;
7901
8417
  }
7902
8418
  function pluginConfigPath() {
7903
- return path13.join(os8.homedir(), ".openclaw", "agentlife", "plugin-config.json");
8419
+ return path15.join(os9.homedir(), ".openclaw", "agentlife", "plugin-config.json");
7904
8420
  }
7905
8421
  function readPluginConfig() {
7906
8422
  try {
@@ -7910,7 +8426,7 @@ function readPluginConfig() {
7910
8426
  }
7911
8427
  }
7912
8428
  function writePluginConfig(config2) {
7913
- const dir = path13.dirname(pluginConfigPath());
8429
+ const dir = path15.dirname(pluginConfigPath());
7914
8430
  fsSync4.mkdirSync(dir, { recursive: true });
7915
8431
  fsSync4.writeFileSync(pluginConfigPath(), JSON.stringify(config2, null, 2));
7916
8432
  }
@@ -7968,7 +8484,7 @@ function registerAdminGateway(api, state2) {
7968
8484
  cleanup: { pending: cleanupPending, failed: cleanupFailed, exhausted: cleanupExhausted }
7969
8485
  });
7970
8486
  } catch (err) {
7971
- respond(false, { error: err?.message ?? "quality metrics unavailable" });
8487
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "quality metrics unavailable" });
7972
8488
  }
7973
8489
  }, { scope: "operator.read" });
7974
8490
  api.registerGatewayMethod("agentlife.health.list", ({ respond }) => {
@@ -7989,7 +8505,7 @@ function registerAdminGateway(api, state2) {
7989
8505
  const rows = runDailyCycle(db, day);
7990
8506
  respond(true, { day, rows });
7991
8507
  } catch (err) {
7992
- respond(false, { error: err?.message ?? "observability runCycle failed" });
8508
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "observability runCycle failed" });
7993
8509
  }
7994
8510
  }, { scope: "operator.write" });
7995
8511
  api.registerGatewayMethod("agentlife.sweep.run", ({ respond }) => {
@@ -7997,7 +8513,7 @@ function registerAdminGateway(api, state2) {
7997
8513
  const result = runDailySweep(state2);
7998
8514
  respond(true, result);
7999
8515
  } catch (err) {
8000
- respond(false, { error: err?.message ?? "daily sweep failed" });
8516
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "daily sweep failed" });
8001
8517
  }
8002
8518
  }, { scope: "operator.write" });
8003
8519
  api.registerGatewayMethod("agentlife.bootstrap", ({ params, respond }) => {
@@ -8038,7 +8554,7 @@ function registerAdminGateway(api, state2) {
8038
8554
  api.registerGatewayMethod("agentlife.uninstall", async ({ respond }) => {
8039
8555
  try {
8040
8556
  const cleaned = [];
8041
- const baseDir = state2.agentlifeStateDir ?? path13.join(os8.homedir(), ".openclaw", "agentlife");
8557
+ const baseDir = state2.agentlifeStateDir ?? path15.join(os9.homedir(), ".openclaw", "agentlife");
8042
8558
  const identity = loadDeviceIdentity();
8043
8559
  if (!identity) {
8044
8560
  cleaned.push("server deprovision skipped (no device identity)");
@@ -8103,7 +8619,7 @@ function registerAdminGateway(api, state2) {
8103
8619
  } catch {
8104
8620
  cleaned.push("agent config cleanup skipped");
8105
8621
  }
8106
- const backupPath = path13.join(baseDir, "config-backup.json");
8622
+ const backupPath = path15.join(baseDir, "config-backup.json");
8107
8623
  try {
8108
8624
  cleaned.push(await restoreConfigFromBackup(api, backupPath));
8109
8625
  } catch {
@@ -8125,35 +8641,34 @@ function registerAdminGateway(api, state2) {
8125
8641
  dbPath,
8126
8642
  dbPath ? `${dbPath}-wal` : null,
8127
8643
  dbPath ? `${dbPath}-shm` : null,
8128
- path13.join(baseDir, "config-backup.json"),
8129
- path13.join(baseDir, "notification-config.json"),
8130
- path13.join(baseDir, "canvas-node-identity.json")
8644
+ path15.join(baseDir, "notification-config.json"),
8645
+ path15.join(baseDir, "canvas-node-identity.json")
8131
8646
  ].filter(Boolean);
8132
8647
  for (const fp of stateFiles) {
8133
8648
  try {
8134
- await fs14.unlink(fp);
8135
- cleaned.push(`deleted ${path13.basename(fp)}`);
8649
+ await fs15.unlink(fp);
8650
+ cleaned.push(`deleted ${path15.basename(fp)}`);
8136
8651
  } catch {}
8137
8652
  }
8138
8653
  if (state2.dbBaseDir) {
8139
8654
  try {
8140
- await fs14.rm(state2.dbBaseDir, { recursive: true, force: true });
8655
+ await fs15.rm(state2.dbBaseDir, { recursive: true, force: true });
8141
8656
  cleaned.push("deleted agent databases");
8142
8657
  } catch {}
8143
8658
  }
8144
8659
  for (const agent of PROVISIONED_AGENTS) {
8145
8660
  if (agent.existingAgent)
8146
8661
  continue;
8147
- const wsDir = agent.workspaceDir ?? path13.join(os8.homedir(), ".openclaw", `workspace-${agent.id}`);
8662
+ const wsDir = agent.workspaceDir ?? path15.join(os9.homedir(), ".openclaw", `workspace-${agent.id}`);
8148
8663
  try {
8149
- await fs14.rm(wsDir, { recursive: true, force: true });
8664
+ await fs15.rm(wsDir, { recursive: true, force: true });
8150
8665
  cleaned.push(`deleted workspace ${agent.id}`);
8151
8666
  } catch {}
8152
8667
  }
8153
8668
  try {
8154
- const remaining = await fs14.readdir(baseDir);
8669
+ const remaining = await fs15.readdir(baseDir);
8155
8670
  if (remaining.length === 0) {
8156
- await fs14.rmdir(baseDir);
8671
+ await fs15.rmdir(baseDir);
8157
8672
  cleaned.push("deleted agentlife state directory");
8158
8673
  }
8159
8674
  } catch {}
@@ -8164,7 +8679,7 @@ function registerAdminGateway(api, state2) {
8164
8679
  });
8165
8680
  } catch (err) {
8166
8681
  console.error("[agentlife] uninstall failed:", err);
8167
- respond(false, { error: err?.message ?? "unknown error during uninstall" });
8682
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? "unknown error during uninstall" });
8168
8683
  }
8169
8684
  });
8170
8685
  api.registerGatewayMethod("agentlife.config.get", ({ respond }) => {
@@ -8193,11 +8708,11 @@ function registerAdminGateway(api, state2) {
8193
8708
 
8194
8709
  // gateway/evolutions.ts
8195
8710
  import * as fsSync5 from "node:fs";
8196
- import * as os9 from "node:os";
8197
- import * as path14 from "node:path";
8711
+ import * as os10 from "node:os";
8712
+ import * as path16 from "node:path";
8198
8713
  var ALLOWED_FILENAMES = new Set(["AGENTS.md", "USER.md", "SOUL.md", "TOOLS.md", "IDENTITY.md"]);
8199
8714
  function workspaceFilePath(agentId, filename) {
8200
- return path14.join(os9.homedir(), ".openclaw", `workspace-${agentId}`, filename);
8715
+ return path16.join(os10.homedir(), ".openclaw", `workspace-${agentId}`, filename);
8201
8716
  }
8202
8717
  function hasRegressionFlag(db, agentId, filename, snapshotAt) {
8203
8718
  const horizon = snapshotAt + 7 * 24 * 60 * 60 * 1000;
@@ -8572,39 +9087,43 @@ function parseOffset(offset) {
8572
9087
  // gateway/providers.ts
8573
9088
  import {
8574
9089
  upsertApiKeyProfile,
8575
- upsertAuthProfile,
9090
+ upsertAuthProfile as upsertAuthProfile3,
8576
9091
  buildApiKeyCredential,
8577
9092
  applyAuthProfileConfig
8578
9093
  } from "openclaw/plugin-sdk/provider-auth-api-key";
8579
9094
 
8580
9095
  // gateway/config-utils.ts
8581
- import * as fs15 from "node:fs";
8582
- import * as os10 from "node:os";
8583
- import * as path15 from "node:path";
9096
+ import * as fs16 from "node:fs";
9097
+ import * as os11 from "node:os";
9098
+ import * as path17 from "node:path";
8584
9099
  function configPath() {
8585
- return path15.join(os10.homedir(), ".openclaw", "openclaw.json");
9100
+ return path17.join(os11.homedir(), ".openclaw", "openclaw.json");
8586
9101
  }
8587
9102
  function readConfig() {
8588
9103
  try {
8589
- return JSON.parse(fs15.readFileSync(configPath(), "utf-8"));
9104
+ return JSON.parse(fs16.readFileSync(configPath(), "utf-8"));
8590
9105
  } catch {
8591
9106
  return {};
8592
9107
  }
8593
9108
  }
8594
9109
  function writeConfig(cfg) {
8595
- fs15.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
9110
+ fs16.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
8596
9111
  `, "utf-8");
8597
9112
  }
8598
9113
 
8599
9114
  // gateway/providers.ts
8600
- import * as fs16 from "node:fs";
8601
- import * as os11 from "node:os";
8602
- import * as path16 from "node:path";
9115
+ import * as fs17 from "node:fs";
9116
+ import * as os12 from "node:os";
9117
+ import * as path18 from "node:path";
9118
+ function resolveOpenClawRoot3() {
9119
+ const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
9120
+ return fromEnv ? path18.resolve(fromEnv) : path18.join(os12.homedir(), ".openclaw");
9121
+ }
8603
9122
  function siblingAgentDirs() {
8604
- const root = path16.join(os11.homedir(), ".openclaw", "agents");
9123
+ const root = path18.join(resolveOpenClawRoot3(), "agents");
8605
9124
  let entries;
8606
9125
  try {
8607
- entries = fs16.readdirSync(root, { withFileTypes: true });
9126
+ entries = fs17.readdirSync(root, { withFileTypes: true });
8608
9127
  } catch {
8609
9128
  return [];
8610
9129
  }
@@ -8612,9 +9131,9 @@ function siblingAgentDirs() {
8612
9131
  for (const entry of entries) {
8613
9132
  if (!entry.isDirectory() && !entry.isSymbolicLink())
8614
9133
  continue;
8615
- const dir = path16.join(root, entry.name, "agent");
9134
+ const dir = path18.join(root, entry.name, "agent");
8616
9135
  try {
8617
- if (fs16.statSync(dir).isDirectory())
9136
+ if (fs17.statSync(dir).isDirectory())
8618
9137
  out.push(dir);
8619
9138
  } catch {}
8620
9139
  }
@@ -8684,20 +9203,20 @@ async function ensureModels(run) {
8684
9203
  }
8685
9204
  function readOAuthExpiries() {
8686
9205
  const result = new Map;
8687
- const fs17 = __require("node:fs");
8688
- const path17 = __require("node:path");
8689
- const os12 = __require("node:os");
8690
- const agentsDir = path17.join(os12.homedir(), ".openclaw", "agents");
9206
+ const fs18 = __require("node:fs");
9207
+ const path19 = __require("node:path");
9208
+ const os13 = __require("node:os");
9209
+ const agentsDir = path19.join(os13.homedir(), ".openclaw", "agents");
8691
9210
  let entries;
8692
9211
  try {
8693
- entries = fs17.readdirSync(agentsDir);
9212
+ entries = fs18.readdirSync(agentsDir);
8694
9213
  } catch {
8695
9214
  return result;
8696
9215
  }
8697
9216
  for (const name of entries) {
8698
- const filePath = path17.join(agentsDir, name, "agent", "auth-profiles.json");
9217
+ const filePath = path19.join(agentsDir, name, "agent", "auth-profiles.json");
8699
9218
  try {
8700
- const raw = fs17.readFileSync(filePath, "utf-8");
9219
+ const raw = fs18.readFileSync(filePath, "utf-8");
8701
9220
  const data = JSON.parse(raw);
8702
9221
  const profiles = data?.profiles ?? {};
8703
9222
  for (const entry of Object.values(profiles)) {
@@ -8814,20 +9333,20 @@ function registerProvidersGateway(api, state2) {
8814
9333
  }));
8815
9334
  respond(true, { providers });
8816
9335
  } catch (err) {
8817
- respond(false, { error: err?.message ?? String(err) });
9336
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
8818
9337
  }
8819
9338
  }, { scope: "operator.read" });
8820
9339
  api.registerGatewayMethod("agentlife.providers.set", async ({ params, respond }) => {
8821
9340
  const provider = typeof params?.provider === "string" ? params.provider.trim() : "";
8822
9341
  const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
8823
9342
  if (!provider)
8824
- return respond(false, { error: "provider is empty" });
9343
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "provider is empty" });
8825
9344
  if (!apiKey)
8826
- return respond(false, { error: "apiKey is empty" });
9345
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "apiKey is empty" });
8827
9346
  try {
8828
9347
  const catalog = await ensureCatalog(run ?? null);
8829
9348
  if (catalog.length > 0 && !catalog.some((e) => e.provider === provider)) {
8830
- return respond(false, { error: `unknown provider: ${provider}` });
9349
+ return respond(false, undefined, { code: "NOT_FOUND", message: `unknown provider: ${provider}` });
8831
9350
  }
8832
9351
  } catch {}
8833
9352
  try {
@@ -8835,7 +9354,7 @@ function registerProvidersGateway(api, state2) {
8835
9354
  const credential = buildApiKeyCredential(provider, apiKey);
8836
9355
  for (const agentDir of siblingAgentDirs()) {
8837
9356
  try {
8838
- upsertAuthProfile({ profileId, credential, agentDir });
9357
+ upsertAuthProfile3({ profileId, credential, agentDir });
8839
9358
  } catch {}
8840
9359
  }
8841
9360
  const cfg = readConfig();
@@ -8848,13 +9367,13 @@ function registerProvidersGateway(api, state2) {
8848
9367
  writeConfig(nextCfg);
8849
9368
  respond(true, { provider, configured: true, profileId });
8850
9369
  } catch (err) {
8851
- respond(false, { error: err?.message ?? String(err) });
9370
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
8852
9371
  }
8853
9372
  }, { scope: "operator.write" });
8854
9373
  api.registerGatewayMethod("agentlife.providers.delete", ({ params, respond }) => {
8855
9374
  const provider = typeof params?.provider === "string" ? params.provider.trim() : "";
8856
9375
  if (!provider)
8857
- return respond(false, { error: "provider is empty" });
9376
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "provider is empty" });
8858
9377
  try {
8859
9378
  const cfg = readConfig();
8860
9379
  const profiles = cfg?.auth?.profiles;
@@ -8869,9 +9388,9 @@ function registerProvidersGateway(api, state2) {
8869
9388
  }
8870
9389
  writeConfig(cfg);
8871
9390
  for (const agentDir of siblingAgentDirs()) {
8872
- const file = path16.join(agentDir, "auth-profiles.json");
9391
+ const file = path18.join(agentDir, "auth-profiles.json");
8873
9392
  try {
8874
- const raw = fs16.readFileSync(file, "utf-8");
9393
+ const raw = fs17.readFileSync(file, "utf-8");
8875
9394
  const data = JSON.parse(raw);
8876
9395
  const agentProfiles = data?.profiles;
8877
9396
  if (!agentProfiles || typeof agentProfiles !== "object")
@@ -8888,19 +9407,19 @@ function registerProvidersGateway(api, state2) {
8888
9407
  changed = true;
8889
9408
  }
8890
9409
  if (changed)
8891
- fs16.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
9410
+ fs17.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
8892
9411
  } catch {}
8893
9412
  }
8894
9413
  respond(true, { provider, configured: false });
8895
9414
  } catch (err) {
8896
- respond(false, { error: err?.message ?? String(err) });
9415
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
8897
9416
  }
8898
9417
  }, { scope: "operator.write" });
8899
9418
  api.registerGatewayMethod("agentlife.providers.test", async ({ params, respond }) => {
8900
9419
  const provider = typeof params?.provider === "string" ? params.provider.trim() : "";
8901
9420
  const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
8902
9421
  if (!provider)
8903
- return respond(false, { error: "provider is empty" });
9422
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "provider is empty" });
8904
9423
  if (!apiKey)
8905
9424
  return respond(true, { ok: false, error: "apiKey required — type a key before testing" });
8906
9425
  try {
@@ -8926,7 +9445,7 @@ function registerProvidersGateway(api, state2) {
8926
9445
  });
8927
9446
  respond(true, { models: deduped });
8928
9447
  } catch (err) {
8929
- respond(false, { error: err?.message ?? String(err) });
9448
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
8930
9449
  }
8931
9450
  }, { scope: "operator.read" });
8932
9451
  api.registerGatewayMethod("agentlife.providers.health", ({ respond }) => {
@@ -8956,44 +9475,44 @@ function registerProvidersGateway(api, state2) {
8956
9475
  }
8957
9476
  respond(true, { probes });
8958
9477
  } catch (err) {
8959
- respond(false, { error: err?.message ?? String(err) });
9478
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
8960
9479
  }
8961
9480
  }, { scope: "operator.read" });
8962
9481
  api.registerGatewayMethod("agentlife.providers.loginCodex", async ({ respond }) => {
8963
9482
  if (!run)
8964
- return respond(false, { error: "runCommand unavailable" });
9483
+ return respond(false, undefined, { code: "UNAVAILABLE", message: "runCommand unavailable" });
8965
9484
  try {
8966
9485
  const result = await run(["openclaw", "capability", "model", "auth", "login", "--provider", "openai-codex"], { timeoutMs: 180000 });
8967
9486
  const combined = `${result?.stdout ?? ""}
8968
9487
  ${result?.stderr ?? ""}`;
8969
9488
  const urlMatch = combined.match(/https:\/\/\S+/);
8970
9489
  if ((result?.code ?? 0) !== 0 && !urlMatch) {
8971
- return respond(false, { error: (result?.stderr ?? "").trim() || "codex login failed" });
9490
+ return respond(false, undefined, { code: "FAILED", message: (result?.stderr ?? "").trim() || "codex login failed" });
8972
9491
  }
8973
9492
  respond(true, { url: urlMatch?.[0] ?? null, completed: (result?.code ?? 0) === 0 });
8974
9493
  } catch (err) {
8975
- respond(false, { error: err?.message ?? String(err) });
9494
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
8976
9495
  }
8977
9496
  }, { scope: "operator.write" });
8978
9497
  }
8979
9498
 
8980
9499
  // gateway/models-config.ts
8981
- import * as fs17 from "node:fs";
8982
- import * as os12 from "node:os";
8983
- import * as path17 from "node:path";
9500
+ import * as fs18 from "node:fs";
9501
+ import * as os13 from "node:os";
9502
+ import * as path19 from "node:path";
8984
9503
  function pluginConfigPath2() {
8985
- return path17.join(os12.homedir(), ".openclaw", "agentlife", "plugin-config.json");
9504
+ return path19.join(os13.homedir(), ".openclaw", "agentlife", "plugin-config.json");
8986
9505
  }
8987
9506
  function readPluginConfig2() {
8988
9507
  try {
8989
- return JSON.parse(fs17.readFileSync(pluginConfigPath2(), "utf-8"));
9508
+ return JSON.parse(fs18.readFileSync(pluginConfigPath2(), "utf-8"));
8990
9509
  } catch {
8991
9510
  return {};
8992
9511
  }
8993
9512
  }
8994
9513
  function writePluginConfig2(cfg) {
8995
- fs17.mkdirSync(path17.dirname(pluginConfigPath2()), { recursive: true });
8996
- fs17.writeFileSync(pluginConfigPath2(), JSON.stringify(cfg, null, 2) + `
9514
+ fs18.mkdirSync(path19.dirname(pluginConfigPath2()), { recursive: true });
9515
+ fs18.writeFileSync(pluginConfigPath2(), JSON.stringify(cfg, null, 2) + `
8997
9516
  `, "utf-8");
8998
9517
  }
8999
9518
  function registerModelsConfigGateway(api) {
@@ -9008,13 +9527,13 @@ function registerModelsConfigGateway(api) {
9008
9527
  fallbacks: Array.isArray(m.fallbacks) ? m.fallbacks.filter((x) => typeof x === "string") : []
9009
9528
  });
9010
9529
  } catch (err) {
9011
- respond(false, { error: err?.message ?? String(err) });
9530
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9012
9531
  }
9013
9532
  }, { scope: "operator.read" });
9014
9533
  api.registerGatewayMethod("agentlife.models.defaults.setPrimary", ({ params, respond }) => {
9015
9534
  const model = typeof params?.model === "string" ? params.model.trim() : "";
9016
9535
  if (!model)
9017
- return respond(false, { error: "model is required" });
9536
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "model is required" });
9018
9537
  try {
9019
9538
  const cfg = readConfig();
9020
9539
  cfg.agents = cfg.agents ?? {};
@@ -9024,26 +9543,26 @@ function registerModelsConfigGateway(api) {
9024
9543
  writeConfig(cfg);
9025
9544
  respond(true, { ok: true });
9026
9545
  } catch (err) {
9027
- respond(false, { error: err?.message ?? String(err) });
9546
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9028
9547
  }
9029
9548
  }, { scope: "operator.write" });
9030
9549
  api.registerGatewayMethod("agentlife.models.defaults.setInternal", ({ params, respond }) => {
9031
9550
  const model = typeof params?.model === "string" ? params.model.trim() : "";
9032
9551
  if (!model)
9033
- return respond(false, { error: "model is required" });
9552
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "model is required" });
9034
9553
  try {
9035
9554
  const cfg = readPluginConfig2();
9036
9555
  cfg.internalModel = model;
9037
9556
  writePluginConfig2(cfg);
9038
9557
  respond(true, { ok: true });
9039
9558
  } catch (err) {
9040
- respond(false, { error: err?.message ?? String(err) });
9559
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9041
9560
  }
9042
9561
  }, { scope: "operator.write" });
9043
9562
  api.registerGatewayMethod("agentlife.models.fallbacks.set", ({ params, respond }) => {
9044
9563
  const models = Array.isArray(params?.models) ? params.models.filter((x) => typeof x === "string") : null;
9045
9564
  if (!models)
9046
- return respond(false, { error: "models must be an array of strings" });
9565
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "models must be an array of strings" });
9047
9566
  try {
9048
9567
  const cfg = readConfig();
9049
9568
  cfg.agents = cfg.agents ?? {};
@@ -9053,7 +9572,7 @@ function registerModelsConfigGateway(api) {
9053
9572
  writeConfig(cfg);
9054
9573
  respond(true, { ok: true });
9055
9574
  } catch (err) {
9056
- respond(false, { error: err?.message ?? String(err) });
9575
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9057
9576
  }
9058
9577
  }, { scope: "operator.write" });
9059
9578
  api.registerGatewayMethod("agentlife.agents.list", ({ respond }) => {
@@ -9067,23 +9586,23 @@ function registerModelsConfigGateway(api) {
9067
9586
  })).filter((a) => a.id !== null && a.name !== null);
9068
9587
  respond(true, { agents });
9069
9588
  } catch (err) {
9070
- respond(false, { error: err?.message ?? String(err) });
9589
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9071
9590
  }
9072
9591
  }, { scope: "operator.read" });
9073
9592
  api.registerGatewayMethod("agentlife.agents.setModel", ({ params, respond }) => {
9074
9593
  const agentId = typeof params?.agentId === "string" ? params.agentId.trim() : "";
9075
9594
  if (!agentId)
9076
- return respond(false, { error: "agentId is required" });
9595
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "agentId is required" });
9077
9596
  const model = params?.model;
9078
9597
  if (model !== null && typeof model !== "string") {
9079
- return respond(false, { error: "model must be string or null" });
9598
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "model must be string or null" });
9080
9599
  }
9081
9600
  try {
9082
9601
  const cfg = readConfig();
9083
9602
  const list = Array.isArray(cfg?.agents?.list) ? cfg.agents.list : [];
9084
9603
  const idx = list.findIndex((a) => a?.id === agentId);
9085
9604
  if (idx < 0)
9086
- return respond(false, { error: `agent not found: ${agentId}` });
9605
+ return respond(false, undefined, { code: "NOT_FOUND", message: `agent not found: ${agentId}` });
9087
9606
  if (model === null) {
9088
9607
  delete list[idx].model;
9089
9608
  } else {
@@ -9092,19 +9611,19 @@ function registerModelsConfigGateway(api) {
9092
9611
  writeConfig(cfg);
9093
9612
  respond(true, { ok: true });
9094
9613
  } catch (err) {
9095
- respond(false, { error: err?.message ?? String(err) });
9614
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9096
9615
  }
9097
9616
  }, { scope: "operator.write" });
9098
9617
  api.registerGatewayMethod("agentlife.agents.setModel.bulk", ({ params, respond }) => {
9099
9618
  const updates = Array.isArray(params?.updates) ? params.updates : null;
9100
9619
  if (!updates)
9101
- return respond(false, { error: "updates must be an array" });
9620
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "updates must be an array" });
9102
9621
  for (const u of updates) {
9103
9622
  if (!u || typeof u.agentId !== "string" || !u.agentId.trim()) {
9104
- return respond(false, { error: "each update needs agentId (string)" });
9623
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "each update needs agentId (string)" });
9105
9624
  }
9106
9625
  if (u.model !== null && typeof u.model !== "string") {
9107
- return respond(false, { error: "each update model must be string or null" });
9626
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "each update model must be string or null" });
9108
9627
  }
9109
9628
  }
9110
9629
  try {
@@ -9125,7 +9644,7 @@ function registerModelsConfigGateway(api) {
9125
9644
  writeConfig(cfg);
9126
9645
  respond(true, { ok: true, applied });
9127
9646
  } catch (err) {
9128
- respond(false, { error: err?.message ?? String(err) });
9647
+ respond(false, undefined, { code: "INTERNAL", message: err?.message ?? String(err) });
9129
9648
  }
9130
9649
  }, { scope: "operator.write" });
9131
9650
  }
@@ -9137,9 +9656,12 @@ var stopObservability = null;
9137
9656
  var stopDailySweep = null;
9138
9657
  var stopQualityCheckPoller = null;
9139
9658
  var stopCloudflared = null;
9659
+ function resolveOpenClawRoot4() {
9660
+ return process.env.OPENCLAW_STATE_DIR || path20.join(homedir17(), ".openclaw");
9661
+ }
9140
9662
  function resolveInternalModel(api) {
9141
9663
  try {
9142
- const pluginCfgPath = path18.join(homedir15(), ".openclaw", "agentlife", "plugin-config.json");
9664
+ const pluginCfgPath = path20.join(resolveOpenClawRoot4(), "agentlife", "plugin-config.json");
9143
9665
  try {
9144
9666
  const raw = __require("node:fs").readFileSync(pluginCfgPath, "utf-8");
9145
9667
  const pluginCfg = JSON.parse(raw);
@@ -9165,7 +9687,24 @@ function _getState() {
9165
9687
  throw new Error("plugin not registered");
9166
9688
  return currentState;
9167
9689
  }
9690
+ async function startProvisioningServices(api, state2) {
9691
+ const cfg = api.runtime.config.loadConfig();
9692
+ await provisionAgents(state2, cfg, api.runtime, console.log);
9693
+ await reconcileInternalSessions(state2, createdInternalSessions);
9694
+ await cleanupSupervisorLegacyState(state2);
9695
+ stopObservability = startObservabilityService(state2);
9696
+ stopDailySweep = startDailySweepService(state2);
9697
+ stopQualityCheckPoller = startQualityCheckPoller(state2);
9698
+ renderAllAgentWidgets(state2, api.runtime, console.log);
9699
+ }
9168
9700
  function register(api) {
9701
+ if (!shouldStartRuntimeServices()) {
9702
+ console.log("[agentlife] install/inspect mode — runtime registration skipped");
9703
+ return;
9704
+ }
9705
+ if (shouldStartRuntimeServices()) {
9706
+ runInstallBootstrap();
9707
+ }
9169
9708
  if (currentState) {
9170
9709
  registerWidgetPushTool(api, currentState);
9171
9710
  api.registerChannel({ plugin: createAgentlifeChannel(currentState.channelState) });
@@ -9175,7 +9714,8 @@ function register(api) {
9175
9714
  return;
9176
9715
  }
9177
9716
  registered = true;
9178
- const fallbackDir = path18.join(homedir15(), ".openclaw", "agentlife");
9717
+ const fallbackRoot = resolveOpenClawRoot4();
9718
+ const fallbackDir = path20.join(fallbackRoot, "agentlife");
9179
9719
  const state2 = {
9180
9720
  fileSurfaceStorage: null,
9181
9721
  surfaceIndex: null,
@@ -9187,9 +9727,9 @@ function register(api) {
9187
9727
  agentDbs: new Map,
9188
9728
  historyDb: null,
9189
9729
  agentlifeStateDir: fallbackDir,
9190
- registryFilePath: path18.join(fallbackDir, "agent-registry.json"),
9191
- dbBaseDir: path18.join(fallbackDir, "db"),
9192
- historyDbPath: path18.join(fallbackDir, "agentlife.db"),
9730
+ registryFilePath: path20.join(fallbackDir, "agent-registry.json"),
9731
+ dbBaseDir: path20.join(fallbackDir, "db"),
9732
+ historyDbPath: path20.join(fallbackDir, "agentlife.db"),
9193
9733
  runCommand: api.runtime.system?.runCommandWithTimeout ?? null,
9194
9734
  enqueueSystemEvent: null,
9195
9735
  requestHeartbeatNow: null,
@@ -9201,12 +9741,12 @@ function register(api) {
9201
9741
  currentState = state2;
9202
9742
  api.registerChannel({ plugin: createAgentlifeChannel(state2.channelState) });
9203
9743
  console.log("[agentlife] channel registered, session format: %s", buildAgentSessionKey("example"));
9204
- if (existsSync7(fallbackDir)) {
9744
+ if (shouldStartRuntimeServices() && existsSync8(fallbackDir)) {
9205
9745
  try {
9206
9746
  const db = getOrCreateHistoryDb(state2);
9207
9747
  state2.surfaceIndex = new SurfaceIndex(db);
9208
9748
  state2.followupQueue = new FollowupQueue(db);
9209
- state2.fileSurfaceStorage = new FileSurfaceStorage;
9749
+ state2.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: fallbackRoot });
9210
9750
  } catch (e) {
9211
9751
  console.warn("[agentlife] eager surface storage init failed:", e?.message);
9212
9752
  }
@@ -9216,20 +9756,32 @@ function register(api) {
9216
9756
  registerSurfacesService(api, state2);
9217
9757
  api.registerService({
9218
9758
  id: "agentlife-provisioning",
9219
- start: async () => {
9220
- const cfg = api.runtime.config.loadConfig();
9221
- await provisionAgents(state2, cfg, api.runtime, console.log);
9222
- await reconcileInternalSessions(state2, createdInternalSessions);
9223
- await cleanupSupervisorLegacyState(state2);
9224
- stopObservability = startObservabilityService(state2);
9225
- stopDailySweep = startDailySweepService(state2);
9226
- stopQualityCheckPoller = startQualityCheckPoller(state2);
9227
- renderAllAgentWidgets(state2, api.runtime, console.log);
9759
+ start: () => {
9760
+ if (!shouldStartRuntimeServices())
9761
+ return;
9762
+ startProvisioningServices(api, state2).catch((err) => {
9763
+ console.warn("[agentlife] provisioning service failed:", err?.message ?? err);
9764
+ });
9228
9765
  }
9229
9766
  });
9230
9767
  registerConfigOptimizer(api, state2);
9231
9768
  registerPairingServices(api);
9232
- stopCloudflared = startCloudflaredSupervisor();
9769
+ api.registerService({
9770
+ id: "agentlife-cloudflared-supervisor",
9771
+ start: () => {
9772
+ if (!shouldStartRuntimeServices())
9773
+ return;
9774
+ if (!stopCloudflared) {
9775
+ stopCloudflared = startCloudflaredSupervisor();
9776
+ }
9777
+ },
9778
+ stop: () => {
9779
+ if (stopCloudflared) {
9780
+ stopCloudflared();
9781
+ stopCloudflared = null;
9782
+ }
9783
+ }
9784
+ });
9233
9785
  api.registerService({
9234
9786
  id: "agentlife-shutdown",
9235
9787
  start: () => {},
@@ -9254,7 +9806,15 @@ function register(api) {
9254
9806
  closeAllDbs(state2);
9255
9807
  }
9256
9808
  });
9257
- initFollowupSystem(api, state2);
9809
+ api.registerService({
9810
+ id: "agentlife-followups",
9811
+ start: () => {
9812
+ if (!shouldStartRuntimeServices())
9813
+ return;
9814
+ initFollowupSystem(api, state2);
9815
+ },
9816
+ stop: () => stopFollowupSystem(state2)
9817
+ });
9258
9818
  registerPromptStateHook(api, state2);
9259
9819
  registerWidgetPushTool(api, state2);
9260
9820
  registerActivityHooks(api, state2);
@@ -9270,17 +9830,17 @@ function register(api) {
9270
9830
  registerProvidersGateway(api, state2);
9271
9831
  registerModelsConfigGateway(api);
9272
9832
  registerWebApp(api);
9273
- const notifyConfigPath = path18.join(fallbackDir, "notification-config.json");
9833
+ const notifyConfigPath = path20.join(fallbackDir, "notification-config.json");
9274
9834
  api.registerGatewayMethod("agentlife.notifications.register", ({ params, respond }) => {
9275
9835
  const serverUrl = typeof params?.serverUrl === "string" ? params.serverUrl.trim() : "";
9276
9836
  const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
9277
9837
  if (!serverUrl || !apiKey) {
9278
- return respond(false, { error: "missing serverUrl or apiKey" });
9838
+ return respond(false, undefined, { code: "INVALID_REQUEST", message: "missing serverUrl or apiKey" });
9279
9839
  }
9280
9840
  try {
9281
- const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync8 } = __require("node:fs");
9282
- mkdirSync8(path18.dirname(notifyConfigPath), { recursive: true });
9283
- writeFileSync11(notifyConfigPath, JSON.stringify({ serverUrl, apiKey }));
9841
+ const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync10 } = __require("node:fs");
9842
+ mkdirSync10(path20.dirname(notifyConfigPath), { recursive: true });
9843
+ writeFileSync12(notifyConfigPath, JSON.stringify({ serverUrl, apiKey }));
9284
9844
  } catch {}
9285
9845
  respond(true, { registered: true });
9286
9846
  }, { scope: "operator.write" });