agentlife 2.6.27 → 2.6.31

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 (144) hide show
  1. package/dist/index.js +832 -273
  2. package/openclaw.plugin.json +8 -0
  3. package/package.json +16 -5
  4. package/web-build/6e23e5428398b92da386.wasm +0 -0
  5. package/web-build/auth-callback.html +40 -0
  6. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_action_plan.xml +10 -0
  7. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_add.xml +9 -0
  8. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_alarm.xml +9 -0
  9. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_amazon.xml +9 -0
  10. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_apple.xml +12 -0
  11. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_clockwise.xml +9 -0
  12. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_counter_clockwise.xml +9 -0
  13. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_down_up.xml +9 -0
  14. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_right.xml +9 -0
  15. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_square_out.xml +9 -0
  16. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrows_out.xml +9 -0
  17. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_audible.xml +9 -0
  18. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_auto_send.xml +9 -0
  19. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_back.xml +9 -0
  20. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_barcode.xml +9 -0
  21. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell.xml +9 -0
  22. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell_ringing.xml +9 -0
  23. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_binoculars.xml +9 -0
  24. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_brain.xml +9 -0
  25. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bulb.xml +9 -0
  26. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar.xml +9 -0
  27. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_blank.xml +9 -0
  28. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_check.xml +9 -0
  29. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_dots.xml +9 -0
  30. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_down.xml +9 -0
  31. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_up.xml +9 -0
  32. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_down.xml +9 -0
  33. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_right.xml +9 -0
  34. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_up.xml +9 -0
  35. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat.xml +9 -0
  36. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle.xml +9 -0
  37. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle_text.xml +9 -0
  38. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_teardrop.xml +9 -0
  39. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_check_simple.xml +9 -0
  40. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_child.xml +9 -0
  41. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_counter_clockwise.xml +9 -0
  42. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_simple.xml +9 -0
  43. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_close.xml +9 -0
  44. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_comment.xml +9 -0
  45. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_community.xml +9 -0
  46. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_copy_simple.xml +9 -0
  47. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_credit_card.xml +9 -0
  48. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_decision_framework.xml +9 -0
  49. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_diff.xml +9 -0
  50. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_dots_three.xml +9 -0
  51. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_edit.xml +9 -0
  52. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_experiment.xml +9 -0
  53. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_facebook.xml +9 -0
  54. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_faders.xml +9 -0
  55. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_dotted.xml +9 -0
  56. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_text.xml +9 -0
  57. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_filter.xml +9 -0
  58. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_flask.xml +9 -0
  59. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_floppy_disk.xml +9 -0
  60. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_open.xml +9 -0
  61. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_simple.xml +9 -0
  62. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_git_merge.xml +9 -0
  63. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google.xml +35 -0
  64. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google_play.xml +22 -0
  65. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_habit_tracker.xml +9 -0
  66. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_1.xml +9 -0
  67. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_2.xml +9 -0
  68. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_3.xml +9 -0
  69. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_4.xml +9 -0
  70. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_hide_pass.xml +9 -0
  71. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_info_simple.xml +9 -0
  72. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_instagram.xml +9 -0
  73. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_keyboard.xml +9 -0
  74. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lightning.xml +9 -0
  75. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_linkedin.xml +9 -0
  76. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lock.xml +9 -0
  77. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_logo.xml +12 -0
  78. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_map_pin_simple.xml +9 -0
  79. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_media.xml +9 -0
  80. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_menu.xml +9 -0
  81. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_microphone_simple.xml +9 -0
  82. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_more_vert.xml +9 -0
  83. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paint_bucket.xml +9 -0
  84. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paper_plane_right.xml +9 -0
  85. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paperclip.xml +9 -0
  86. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_parent.xml +9 -0
  87. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_pause_simple.xml +9 -0
  88. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_play_simple.xml +9 -0
  89. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_plus_circle.xml +9 -0
  90. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin.xml +9 -0
  91. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin_slash.xml +9 -0
  92. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_question.xml +9 -0
  93. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_record.xml +9 -0
  94. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_rocket.xml +9 -0
  95. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_scales.xml +9 -0
  96. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_search.xml +9 -0
  97. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_settings.xml +9 -0
  98. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_share_network.xml +9 -0
  99. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_show_pass.xml +9 -0
  100. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sign_out.xml +9 -0
  101. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_smiley.xml +9 -0
  102. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparkles.xml +9 -0
  103. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparks.xml +9 -0
  104. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_speaker_high.xml +9 -0
  105. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_spotify.xml +9 -0
  106. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_star.xml +9 -0
  107. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_circle.xml +9 -0
  108. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_simple.xml +9 -0
  109. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_swap.xml +9 -0
  110. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tag_simple.xml +9 -0
  111. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_down.xml +9 -0
  112. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_up.xml +9 -0
  113. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tiktok.xml +9 -0
  114. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_complete.xml +9 -0
  115. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_simple.xml +9 -0
  116. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_translate.xml +9 -0
  117. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_trash_simple.xml +9 -0
  118. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tree_structure.xml +9 -0
  119. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_twitch.xml +9 -0
  120. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_upload.xml +9 -0
  121. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_user_circle.xml +9 -0
  122. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_warning.xml +9 -0
  123. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_web.xml +9 -0
  124. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_wrench.xml +9 -0
  125. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x.xml +9 -0
  126. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x_circle.xml +9 -0
  127. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube.xml +9 -0
  128. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube_music.xml +9 -0
  129. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/tenazitas_logo.xml +27 -0
  130. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Caveat-Medium.ttf +0 -0
  131. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Medium.ttf +0 -0
  132. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Regular.ttf +0 -0
  133. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-SemiBold.ttf +0 -0
  134. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Medium.ttf +0 -0
  135. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Regular.ttf +0 -0
  136. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Italic.ttf +0 -0
  137. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Regular.ttf +0 -0
  138. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-SemiBold.ttf +0 -0
  139. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/values/strings.commonMain.cvr +2787 -0
  140. package/web-build/f722462c344efeb18c2f.wasm +0 -0
  141. package/web-build/index.html +23 -0
  142. package/web-build/webApp.js +3 -0
  143. package/web-build/webApp.js.LICENSE.txt +35 -0
  144. 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,6 +1821,19 @@ 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
  }
@@ -1823,7 +1916,21 @@ async function provisionAgents(state, cfg, runtime, log) {
1823
1916
  globalAllowWritten = true;
1824
1917
  }
1825
1918
  }
1826
- if (visibilityWritten || a2aWritten || globalAllowWritten) {
1919
+ const currentHooksAccess = rawCfgForVisibility?.plugins?.entries?.agentlife?.hooks?.allowConversationAccess;
1920
+ let hooksAccessWritten = false;
1921
+ if (currentHooksAccess !== true) {
1922
+ if (!rawCfgForVisibility.plugins)
1923
+ rawCfgForVisibility.plugins = {};
1924
+ if (!rawCfgForVisibility.plugins.entries)
1925
+ rawCfgForVisibility.plugins.entries = {};
1926
+ if (!rawCfgForVisibility.plugins.entries.agentlife)
1927
+ rawCfgForVisibility.plugins.entries.agentlife = {};
1928
+ if (!rawCfgForVisibility.plugins.entries.agentlife.hooks)
1929
+ rawCfgForVisibility.plugins.entries.agentlife.hooks = {};
1930
+ rawCfgForVisibility.plugins.entries.agentlife.hooks.allowConversationAccess = true;
1931
+ hooksAccessWritten = true;
1932
+ }
1933
+ if (visibilityWritten || a2aWritten || globalAllowWritten || hooksAccessWritten) {
1827
1934
  writeFileSync(configPath, JSON.stringify(rawCfgForVisibility, null, 2) + `
1828
1935
  `, "utf-8");
1829
1936
  configChanged = true;
@@ -1833,6 +1940,8 @@ async function provisionAgents(state, cfg, runtime, log) {
1833
1940
  log("[agentlife] set tools.agentToAgent.enabled=true (cross-agent sends)");
1834
1941
  if (globalAllowWritten)
1835
1942
  log('[agentlife] added "*" to tools.allow (unblock exec/read/write for provisioned agents)');
1943
+ if (hooksAccessWritten)
1944
+ log("[agentlife] set plugins.entries.agentlife.hooks.allowConversationAccess=true (typed hooks)");
1836
1945
  }
1837
1946
  if (configChanged) {
1838
1947
  const configPath2 = path4.join(os.homedir(), ".openclaw", "openclaw.json");
@@ -2069,7 +2178,6 @@ var KNOWN_BODY_KEYWORDS = new Set([
2069
2178
  "button",
2070
2179
  "image",
2071
2180
  "icon",
2072
- "textfield",
2073
2181
  "checkbox",
2074
2182
  "sparkline",
2075
2183
  "progress",
@@ -2101,7 +2209,6 @@ var INPUT_SURFACE_ACTIONS = new Set([
2101
2209
  var TEXT_LEVELS = new Set(["h1", "h2", "h3", "h4", "h5", "body", "caption"]);
2102
2210
  var TEXT_HINTS = new Set(["icon", "avatar", "smallfeature", "mediumfeature", "largefeature", "header"]);
2103
2211
  var IMG_FITS = new Set(["contain", "cover", "fill", "none", "scale-down"]);
2104
- var TF_TYPES = new Set(["email", "shorttext", "number", "phone", "longtext", "date", "obscured"]);
2105
2212
  var ALIGN_VALUES = new Set(["start", "center", "end", "stretch"]);
2106
2213
  var DISTRIBUTE_VALUES = new Set(["start", "center", "end", "spacebetween", "spacearound", "spaceevenly"]);
2107
2214
  var TREND_VALUES = new Set(["up", "down", "flat"]);
@@ -2156,14 +2263,6 @@ var COMPONENT_CATALOG = {
2156
2263
  }
2157
2264
  },
2158
2265
  icon: { positionalTokens: new Set, positionalFree: true, attrs: {} },
2159
- textfield: {
2160
- positionalTokens: new Set,
2161
- positionalFree: true,
2162
- attrs: {
2163
- type: { kind: "enum", values: TF_TYPES },
2164
- placeholder: { kind: "free" }
2165
- }
2166
- },
2167
2266
  checkbox: {
2168
2267
  positionalTokens: new Set(["checked"]),
2169
2268
  positionalFree: true,
@@ -3457,9 +3556,9 @@ Each turn:
3457
3556
  2. Not enough → \`agentlife_push\` the SAME surfaceId \`welcome-input\` with:
3458
3557
  - h3 question generated from what the user just said
3459
3558
  - 2–4 multichoice buttons \`action=choice\`, options derived from the user's own words (NEVER hardcoded domain lists)
3460
- - \`textfield placeholder="Something else…"\` escape hatch
3461
3559
  - updated \`context: {"phase":"welcome","turn":N+1,"answers":[...]}\`
3462
3560
  - goal + followup unchanged
3561
+ 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.
3463
3562
  Respond \`done\`.
3464
3563
  3. Enough → create the generalist and hand off the dashboard:
3465
3564
  - Workspace under \`$HOME/.openclaw/workspace-{id}\` (id = slugified short name or "me")
@@ -3489,7 +3588,6 @@ function renderWelcomeWidget(state, log) {
3489
3588
  ` column`,
3490
3589
  ` text "What do you want to track or improve?" h3`,
3491
3590
  ` text "One line. Anything — meals, money, a project, a habit." body`,
3492
- ` textfield placeholder="Type anything…"`,
3493
3591
  `goal: Onboard the user to their first agent via a guided interview`,
3494
3592
  `followup: +30m "If unanswered, replace the question with a gentler prompt."`,
3495
3593
  `context: {"phase":"welcome","turn":1,"answers":[]}`
@@ -3517,7 +3615,7 @@ function deleteWelcomeWidget(state, log) {
3517
3615
  }
3518
3616
 
3519
3617
  // services/surfaces-init.ts
3520
- import * as path5 from "node:path";
3618
+ import * as path6 from "node:path";
3521
3619
 
3522
3620
  // storage/reconciler.ts
3523
3621
  var defaultLogger = {
@@ -3765,23 +3863,45 @@ function dispatchRehydrate(state, runtime, log = console.log) {
3765
3863
  log(`[agentlife:rehydrate] dispatched rehydrate to ${orphaned.length} orphaned agent(s): ${orphaned.join(", ")}`);
3766
3864
  }
3767
3865
 
3866
+ // runtime-mode.ts
3867
+ import * as path5 from "node:path";
3868
+ function isPluginInstallOrInspectProcess() {
3869
+ const argv = process.argv.join(" ");
3870
+ const entrypoint = path5.basename(process.argv[1] || "");
3871
+ return entrypoint.includes("openclaw-plugins") || argv.includes("plugins") && argv.includes("install") || argv.includes("plugins") && argv.includes("inspect");
3872
+ }
3873
+ function shouldStartRuntimeServices() {
3874
+ return !isPluginInstallOrInspectProcess();
3875
+ }
3876
+
3768
3877
  // services/surfaces-init.ts
3769
3878
  function registerSurfacesService(api, state) {
3770
3879
  api.registerService({
3771
3880
  id: "agentlife-surfaces",
3772
3881
  start: async (ctx) => {
3773
- const agentlifeDir = path5.join(ctx.stateDir, "agentlife");
3882
+ if (!shouldStartRuntimeServices())
3883
+ return;
3884
+ const agentlifeDir = path6.join(ctx.stateDir, "agentlife");
3885
+ const nextHistoryDbPath = path6.join(agentlifeDir, "agentlife.db");
3886
+ const previousHistoryDbPath = state.historyDbPath;
3887
+ if (state.historyDb && previousHistoryDbPath && previousHistoryDbPath !== nextHistoryDbPath) {
3888
+ try {
3889
+ state.historyDb.close();
3890
+ } catch {}
3891
+ state.historyDb = null;
3892
+ state.surfaceIndex = null;
3893
+ state.followupQueue = null;
3894
+ }
3774
3895
  state.agentlifeStateDir = agentlifeDir;
3775
- state.registryFilePath = path5.join(agentlifeDir, "agent-registry.json");
3776
- state.dbBaseDir = path5.join(agentlifeDir, "db");
3777
- state.historyDbPath = path5.join(agentlifeDir, "agentlife.db");
3896
+ state.registryFilePath = path6.join(agentlifeDir, "agent-registry.json");
3897
+ state.dbBaseDir = path6.join(agentlifeDir, "db");
3898
+ state.historyDbPath = nextHistoryDbPath;
3778
3899
  const db = getOrCreateHistoryDb(state);
3779
3900
  if (!state.surfaceIndex)
3780
3901
  state.surfaceIndex = new SurfaceIndex(db);
3781
3902
  if (!state.followupQueue)
3782
3903
  state.followupQueue = new FollowupQueue(db);
3783
- if (!state.fileSurfaceStorage)
3784
- state.fileSurfaceStorage = new FileSurfaceStorage;
3904
+ state.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: ctx.stateDir });
3785
3905
  await loadRegistryFromDisk(state);
3786
3906
  try {
3787
3907
  const knownAgents = collectKnownAgents(api.runtime);
@@ -3831,11 +3951,11 @@ function collectKnownAgents(runtime) {
3831
3951
 
3832
3952
  // services/config-optimizer.ts
3833
3953
  import * as fs5 from "node:fs/promises";
3834
- import * as path6 from "node:path";
3954
+ import * as path7 from "node:path";
3835
3955
  import * as os2 from "node:os";
3836
3956
  var TARGET_MAX_PING_PONG = 1;
3837
3957
  var TARGET_MAX_CONCURRENT = 10;
3838
- var CONFIG_PATH = path6.join(os2.homedir(), ".openclaw", "openclaw.json");
3958
+ var CONFIG_PATH = process.env.OPENCLAW_CONFIG_PATH || path7.join(os2.homedir(), ".openclaw", "openclaw.json");
3839
3959
  async function readRawConfigFromDisk() {
3840
3960
  const raw = await fs5.readFile(CONFIG_PATH, "utf-8");
3841
3961
  return JSON.parse(raw);
@@ -3871,8 +3991,10 @@ function registerConfigOptimizer(api, _state) {
3871
3991
  api.registerService({
3872
3992
  id: "agentlife-config-optimizer",
3873
3993
  start: async (ctx) => {
3874
- const configBackupDir = path6.join(ctx.stateDir, "agentlife");
3875
- const configBackupPath = path6.join(configBackupDir, "config-backup.json");
3994
+ if (!shouldStartRuntimeServices())
3995
+ return;
3996
+ const configBackupDir = path7.join(ctx.stateDir, "agentlife");
3997
+ const configBackupPath = path7.join(configBackupDir, "config-backup.json");
3876
3998
  const cfg = api.runtime.config.loadConfig();
3877
3999
  const currentPPT = cfg.session?.agentToAgent?.maxPingPongTurns;
3878
4000
  const currentMC = cfg.agents?.defaults?.maxConcurrent;
@@ -3883,10 +4005,16 @@ function registerConfigOptimizer(api, _state) {
3883
4005
  return;
3884
4006
  }
3885
4007
  await fs5.mkdir(configBackupDir, { recursive: true });
3886
- await fs5.writeFile(configBackupPath, JSON.stringify({
3887
- maxPingPongTurns: currentPPT ?? null,
3888
- maxConcurrent: currentMC ?? null
3889
- }));
4008
+ let existing = {};
4009
+ try {
4010
+ existing = JSON.parse(await fs5.readFile(configBackupPath, "utf-8"));
4011
+ } catch {}
4012
+ if (!("maxPingPongTurns" in existing))
4013
+ existing.maxPingPongTurns = currentPPT ?? null;
4014
+ if (!("maxConcurrent" in existing))
4015
+ existing.maxConcurrent = currentMC ?? null;
4016
+ await fs5.writeFile(configBackupPath, JSON.stringify(existing, null, 2) + `
4017
+ `);
3890
4018
  const fileCfg = await readRawConfigFromDisk();
3891
4019
  if (needsPPT) {
3892
4020
  fileCfg.session ??= {};
@@ -3908,13 +4036,196 @@ function registerConfigOptimizer(api, _state) {
3908
4036
  },
3909
4037
  stop: async (ctx) => {
3910
4038
  try {
3911
- const backupPath = path6.join(ctx.stateDir, "agentlife", "config-backup.json");
4039
+ const backupPath = path7.join(ctx.stateDir, "agentlife", "config-backup.json");
3912
4040
  await restoreConfigFromBackup(api, backupPath);
3913
4041
  } catch {}
3914
4042
  }
3915
4043
  });
3916
4044
  }
3917
4045
 
4046
+ // services/install-bootstrap.ts
4047
+ import * as fs6 from "node:fs";
4048
+ import * as path8 from "node:path";
4049
+ import * as os3 from "node:os";
4050
+ import { upsertAuthProfile } from "openclaw/plugin-sdk/provider-auth";
4051
+ function resolvePaths(home) {
4052
+ const openclawDir = home ? path8.join(home, ".openclaw") : process.env.OPENCLAW_STATE_DIR || path8.join(os3.homedir(), ".openclaw");
4053
+ const configPath = home ? path8.join(openclawDir, "openclaw.json") : process.env.OPENCLAW_CONFIG_PATH || path8.join(openclawDir, "openclaw.json");
4054
+ const agentlifeDir = path8.join(openclawDir, "agentlife");
4055
+ return {
4056
+ configPath,
4057
+ gatewayTokenFile: path8.join(openclawDir, "gateway-token.txt"),
4058
+ agentlifeDir,
4059
+ configBackupPath: path8.join(agentlifeDir, "config-backup.json"),
4060
+ pairRequestMarker: path8.join(agentlifeDir, "pair-requested")
4061
+ };
4062
+ }
4063
+ function readJsonSafe(p) {
4064
+ try {
4065
+ return JSON.parse(fs6.readFileSync(p, "utf-8"));
4066
+ } catch {
4067
+ return null;
4068
+ }
4069
+ }
4070
+ function writeJson(p, obj) {
4071
+ fs6.writeFileSync(p, JSON.stringify(obj, null, 2) + `
4072
+ `, "utf-8");
4073
+ }
4074
+ function normalizeLegacyCredential(profile) {
4075
+ if (!profile || typeof profile !== "object")
4076
+ return null;
4077
+ if (typeof profile.provider !== "string" || !profile.provider.trim())
4078
+ return null;
4079
+ const credential = { ...profile };
4080
+ if (credential.type === "api_key" && typeof credential.key !== "string" && typeof credential.apiKey === "string") {
4081
+ credential.key = credential.apiKey;
4082
+ delete credential.apiKey;
4083
+ }
4084
+ return credential;
4085
+ }
4086
+ function migrateLegacyAuthProfiles(openclawDir) {
4087
+ const agentsRoot = path8.join(openclawDir, "agents");
4088
+ let entries;
4089
+ try {
4090
+ entries = fs6.readdirSync(agentsRoot, { withFileTypes: true });
4091
+ } catch {
4092
+ return;
4093
+ }
4094
+ let migrated = 0;
4095
+ for (const entry of entries) {
4096
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
4097
+ continue;
4098
+ const agentDir = path8.join(agentsRoot, entry.name, "agent");
4099
+ const legacyPath = path8.join(agentDir, "auth-profiles.json");
4100
+ const legacy = readJsonSafe(legacyPath);
4101
+ const profiles = legacy?.profiles;
4102
+ if (!profiles || typeof profiles !== "object")
4103
+ continue;
4104
+ for (const [profileId, profile] of Object.entries(profiles)) {
4105
+ const credential = normalizeLegacyCredential(profile);
4106
+ if (!credential)
4107
+ continue;
4108
+ try {
4109
+ upsertAuthProfile({ profileId, credential, agentDir });
4110
+ migrated += 1;
4111
+ } catch (err) {
4112
+ console.warn(`[agentlife] install-bootstrap: auth profile migration skipped for ${entry.name}/${profileId}: ${err?.message ?? err}`);
4113
+ }
4114
+ }
4115
+ }
4116
+ if (migrated > 0) {
4117
+ console.log(`[agentlife] install-bootstrap: migrated ${migrated} legacy auth profile(s) into OpenClaw auth store`);
4118
+ }
4119
+ }
4120
+ function runInstallBootstrap(home) {
4121
+ const P = resolvePaths(home);
4122
+ try {
4123
+ fs6.mkdirSync(P.agentlifeDir, { recursive: true, mode: 448 });
4124
+ } catch (err) {
4125
+ console.warn(`[agentlife] install-bootstrap: cannot create ${P.agentlifeDir}: ${err?.message ?? err}`);
4126
+ return;
4127
+ }
4128
+ try {
4129
+ fs6.writeFileSync(P.pairRequestMarker, String(Date.now()), { mode: 384 });
4130
+ } catch (err) {
4131
+ console.warn(`[agentlife] install-bootstrap: cannot write pair-requested marker: ${err?.message ?? err}`);
4132
+ }
4133
+ migrateLegacyAuthProfiles(path8.dirname(P.gatewayTokenFile));
4134
+ if (!fs6.existsSync(P.configPath)) {
4135
+ console.log("[agentlife] install-bootstrap: gateway config missing — deferring config writes");
4136
+ return;
4137
+ }
4138
+ const cfg = readJsonSafe(P.configPath);
4139
+ if (!cfg || typeof cfg !== "object") {
4140
+ console.warn(`[agentlife] install-bootstrap: cannot parse ${P.configPath} — skipping config writes`);
4141
+ return;
4142
+ }
4143
+ const existingBackup = readJsonSafe(P.configBackupPath) ?? {};
4144
+ const originals = [
4145
+ ["sessionsVisibility", cfg?.tools?.sessions?.visibility ?? null],
4146
+ ["agentToAgentEnabled", cfg?.tools?.agentToAgent?.enabled ?? null],
4147
+ ["gatewayBind", cfg?.gateway?.bind ?? null],
4148
+ ["hooksAllowConversationAccess", cfg?.plugins?.entries?.agentlife?.hooks?.allowConversationAccess ?? null],
4149
+ ["maxPingPongTurns", cfg?.session?.agentToAgent?.maxPingPongTurns ?? null],
4150
+ ["maxConcurrent", cfg?.agents?.defaults?.maxConcurrent ?? null]
4151
+ ];
4152
+ let backupChanged = false;
4153
+ for (const [k, orig] of originals) {
4154
+ if (!(k in existingBackup)) {
4155
+ existingBackup[k] = orig;
4156
+ backupChanged = true;
4157
+ }
4158
+ }
4159
+ if (backupChanged) {
4160
+ try {
4161
+ writeJson(P.configBackupPath, existingBackup);
4162
+ } catch (err) {
4163
+ console.warn(`[agentlife] install-bootstrap: cannot write backup: ${err?.message ?? err}`);
4164
+ }
4165
+ }
4166
+ let changed = false;
4167
+ cfg.gateway ??= {};
4168
+ if (cfg.gateway.bind !== "lan") {
4169
+ cfg.gateway.bind = "lan";
4170
+ changed = true;
4171
+ }
4172
+ cfg.gateway.auth ??= {};
4173
+ if (!cfg.gateway.auth.token && fs6.existsSync(P.gatewayTokenFile)) {
4174
+ try {
4175
+ const tok = fs6.readFileSync(P.gatewayTokenFile, "utf-8").trim();
4176
+ if (tok) {
4177
+ cfg.gateway.auth.token = tok;
4178
+ changed = true;
4179
+ console.log(`[agentlife] install-bootstrap: imported gateway token from ${P.gatewayTokenFile}`);
4180
+ }
4181
+ } catch (err) {
4182
+ console.warn(`[agentlife] install-bootstrap: cannot read ${P.gatewayTokenFile}: ${err?.message ?? err}`);
4183
+ }
4184
+ }
4185
+ cfg.tools ??= {};
4186
+ cfg.tools.sessions ??= {};
4187
+ if (cfg.tools.sessions.visibility !== "all") {
4188
+ cfg.tools.sessions.visibility = "all";
4189
+ changed = true;
4190
+ }
4191
+ cfg.tools.agentToAgent ??= {};
4192
+ if (cfg.tools.agentToAgent.enabled !== true) {
4193
+ cfg.tools.agentToAgent.enabled = true;
4194
+ changed = true;
4195
+ }
4196
+ cfg.plugins ??= {};
4197
+ cfg.plugins.entries ??= {};
4198
+ cfg.plugins.entries.agentlife ??= {};
4199
+ cfg.plugins.entries.agentlife.hooks ??= {};
4200
+ if (cfg.plugins.entries.agentlife.hooks.allowConversationAccess !== true) {
4201
+ cfg.plugins.entries.agentlife.hooks.allowConversationAccess = true;
4202
+ changed = true;
4203
+ }
4204
+ cfg.session ??= {};
4205
+ cfg.session.agentToAgent ??= {};
4206
+ if (cfg.session.agentToAgent.maxPingPongTurns !== 1) {
4207
+ cfg.session.agentToAgent.maxPingPongTurns = 1;
4208
+ changed = true;
4209
+ }
4210
+ cfg.agents ??= {};
4211
+ cfg.agents.defaults ??= {};
4212
+ if (!cfg.agents.defaults.maxConcurrent || cfg.agents.defaults.maxConcurrent < 10) {
4213
+ cfg.agents.defaults.maxConcurrent = 10;
4214
+ changed = true;
4215
+ }
4216
+ if (!changed) {
4217
+ console.log("[agentlife] install-bootstrap: config already normalized");
4218
+ return;
4219
+ }
4220
+ try {
4221
+ writeJson(P.configPath, cfg);
4222
+ console.log("[agentlife] install-bootstrap: gateway config normalized (bind=lan, sessions.visibility=all, agentToAgent.enabled=true, hooks.allowConversationAccess=true, maxPingPongTurns=1, maxConcurrent=10)");
4223
+ console.log("[agentlife] install-bootstrap: changes take effect on next `openclaw daemon restart`");
4224
+ } catch (err) {
4225
+ console.warn(`[agentlife] install-bootstrap: cannot write ${P.configPath}: ${err?.message ?? err}`);
4226
+ }
4227
+ }
4228
+
3918
4229
  // services/session-reconcile.ts
3919
4230
  function modelMatches(configured, sessionProvider, sessionModel) {
3920
4231
  if (!sessionModel)
@@ -4077,7 +4388,7 @@ async function reconcileInternalSessions(state, createdInternalSessions) {
4077
4388
  }
4078
4389
 
4079
4390
  // services/supervisor-cleanup.ts
4080
- import * as fs6 from "node:fs";
4391
+ import * as fs7 from "node:fs";
4081
4392
  var STALE_SUPERVISOR_SURFACE = "supervisor-daily";
4082
4393
  var STALE_SUPERVISOR_DIRECT_SESSION = "agent:supervisor:agentlife:direct:operator";
4083
4394
  async function cleanupSupervisorLegacyState(state) {
@@ -4090,7 +4401,7 @@ async function cleanupSupervisorLegacyState(state) {
4090
4401
  removeFollowup(state, STALE_SUPERVISOR_SURFACE);
4091
4402
  if (view2 && !view2.isTransient && view2.path) {
4092
4403
  try {
4093
- fs6.unlinkSync(view2.path);
4404
+ fs7.unlinkSync(view2.path);
4094
4405
  } catch {}
4095
4406
  }
4096
4407
  state.surfaceIndex?.delete(STALE_SUPERVISOR_SURFACE);
@@ -4272,6 +4583,8 @@ function startObservabilityService(state) {
4272
4583
 
4273
4584
  // hooks/activity-hooks.ts
4274
4585
  import * as fsSync2 from "node:fs";
4586
+ import * as nodePath from "node:path";
4587
+ import * as nodeOs from "node:os";
4275
4588
 
4276
4589
  // usage.ts
4277
4590
  var MODEL_COSTS = {
@@ -4920,6 +5233,62 @@ ${parts.join(`
4920
5233
  }
4921
5234
  var QUALITY_CHECK_MAX_WAIT_MS = 5000;
4922
5235
  var TERMINAL_MARKERS = new Set(["NO_REPLY", "ANNOUNCE_SKIP", "REPLY_SKIP", "HEARTBEAT_OK"]);
5236
+ function resolveOpenClawRoot() {
5237
+ const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
5238
+ return fromEnv ? nodePath.resolve(fromEnv) : nodePath.join(nodeOs.homedir(), ".openclaw");
5239
+ }
5240
+ function findSessionModelError(agentId, sessionKey) {
5241
+ const sessionsDir = nodePath.join(resolveOpenClawRoot(), "agents", agentId, "sessions");
5242
+ const indexPath = nodePath.join(sessionsDir, "sessions.json");
5243
+ let sessionFile = null;
5244
+ try {
5245
+ const index = JSON.parse(fsSync2.readFileSync(indexPath, "utf-8"));
5246
+ const entry = index?.[sessionKey];
5247
+ if (typeof entry?.sessionFile === "string") {
5248
+ sessionFile = entry.sessionFile;
5249
+ } else if (typeof entry?.sessionId === "string") {
5250
+ sessionFile = nodePath.join(sessionsDir, `${entry.sessionId}.jsonl`);
5251
+ }
5252
+ } catch {
5253
+ return null;
5254
+ }
5255
+ if (!sessionFile)
5256
+ return null;
5257
+ let provider;
5258
+ let model;
5259
+ let lines;
5260
+ try {
5261
+ lines = fsSync2.readFileSync(sessionFile, "utf-8").split(`
5262
+ `).filter(Boolean);
5263
+ } catch {
5264
+ return null;
5265
+ }
5266
+ for (const line of lines) {
5267
+ try {
5268
+ const entry = JSON.parse(line);
5269
+ if (entry?.type === "model_change") {
5270
+ provider = typeof entry.provider === "string" ? entry.provider : provider;
5271
+ model = typeof entry.modelId === "string" ? entry.modelId : model;
5272
+ }
5273
+ } catch {}
5274
+ }
5275
+ for (let i = lines.length - 1;i >= 0; i--) {
5276
+ try {
5277
+ const entry = JSON.parse(lines[i]);
5278
+ const msg = entry?.message;
5279
+ if (entry?.type !== "message" || msg?.role !== "assistant")
5280
+ continue;
5281
+ if (typeof msg.provider === "string")
5282
+ provider = msg.provider;
5283
+ if (typeof msg.model === "string")
5284
+ model = msg.model;
5285
+ if (typeof msg.errorMessage === "string" && msg.errorMessage.trim()) {
5286
+ return { error: msg.errorMessage, provider, model };
5287
+ }
5288
+ } catch {}
5289
+ }
5290
+ return null;
5291
+ }
4923
5292
  function processPendingQualityChecks(state) {
4924
5293
  if (state.disabled || !state.historyDb)
4925
5294
  return 0;
@@ -4961,6 +5330,21 @@ function processPendingQualityChecks(state) {
4961
5330
  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);
4962
5331
  if ((pushed?.c ?? 0) > 0)
4963
5332
  continue;
5333
+ const modelError = findSessionModelError(row.agentId, row.sessionKey);
5334
+ if (modelError) {
5335
+ recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
5336
+ runId: row.runId,
5337
+ data: JSON.stringify({
5338
+ issue: "model_error_no_widget",
5339
+ trigger: row.trigger,
5340
+ error: modelError.error,
5341
+ provider: modelError.provider,
5342
+ model: modelError.model
5343
+ })
5344
+ });
5345
+ console.warn("[agentlife:quality] %s model error before widget push: %s", row.agentId, modelError.error);
5346
+ continue;
5347
+ }
4964
5348
  recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
4965
5349
  runId: row.runId,
4966
5350
  data: JSON.stringify({ issue: "no_widget_pushed", trigger: row.trigger })
@@ -5059,7 +5443,7 @@ function rescueOrphanedSurfaces(state, agentId, error) {
5059
5443
  }
5060
5444
 
5061
5445
  // services/escalation.ts
5062
- import * as fs7 from "node:fs";
5446
+ import * as fs8 from "node:fs";
5063
5447
  var PATHOLOGY2 = process.env.PATHOLOGY_TEST_MODE === "1";
5064
5448
  var ESCALATION_REVIEW_THRESHOLD_MS = PATHOLOGY2 ? 2 * 60 * 1000 : 48 * 60 * 60 * 1000;
5065
5449
  var TREND_WINDOW_DAYS = 7;
@@ -5125,7 +5509,7 @@ function clearEscalationSurface(state, agentId) {
5125
5509
  const view2 = getSurface(state, surfaceId);
5126
5510
  if (view2 && !view2.isTransient && view2.path) {
5127
5511
  try {
5128
- fs7.unlinkSync(view2.path);
5512
+ fs8.unlinkSync(view2.path);
5129
5513
  } catch {}
5130
5514
  }
5131
5515
  state.surfaceIndex.delete(surfaceId);
@@ -5369,31 +5753,32 @@ function startDailySweepService(state) {
5369
5753
  import * as crypto4 from "node:crypto";
5370
5754
  import * as fsSync3 from "node:fs";
5371
5755
  import { createRequire as createRequire3 } from "node:module";
5372
- import * as os6 from "node:os";
5373
- import * as path10 from "node:path";
5756
+ import * as os7 from "node:os";
5757
+ import * as path12 from "node:path";
5374
5758
 
5375
5759
  // gateway/web-app.ts
5376
5760
  import * as crypto3 from "node:crypto";
5377
- import * as os5 from "node:os";
5378
- import * as path9 from "node:path";
5379
- import * as fs10 from "node:fs";
5761
+ import * as os6 from "node:os";
5762
+ import * as path11 from "node:path";
5763
+ import * as fs11 from "node:fs";
5380
5764
 
5381
5765
  // services/pairing-access-token.ts
5382
5766
  import * as crypto from "node:crypto";
5383
- import * as fs8 from "node:fs";
5384
- import * as os3 from "node:os";
5385
- import * as path7 from "node:path";
5767
+ import * as fs9 from "node:fs";
5768
+ import * as os4 from "node:os";
5769
+ import * as path9 from "node:path";
5386
5770
  var cachedToken = null;
5387
5771
  function pairingAccessPath() {
5388
- return path7.join(os3.homedir(), ".openclaw", "agentlife", "pairing-access.json");
5772
+ const stateDir = process.env.OPENCLAW_STATE_DIR || path9.join(os4.homedir(), ".openclaw");
5773
+ return path9.join(stateDir, "agentlife", "pairing-access.json");
5389
5774
  }
5390
5775
  function loadOrCreatePairingAccessToken() {
5391
5776
  if (cachedToken)
5392
5777
  return cachedToken;
5393
5778
  const filePath = pairingAccessPath();
5394
5779
  try {
5395
- if (fs8.existsSync(filePath)) {
5396
- const raw = fs8.readFileSync(filePath, "utf-8");
5780
+ if (fs9.existsSync(filePath)) {
5781
+ const raw = fs9.readFileSync(filePath, "utf-8");
5397
5782
  const obj = JSON.parse(raw);
5398
5783
  const token2 = typeof obj?.token === "string" ? obj.token : null;
5399
5784
  if (token2 && token2.length >= 32) {
@@ -5402,12 +5787,12 @@ function loadOrCreatePairingAccessToken() {
5402
5787
  }
5403
5788
  }
5404
5789
  const token = crypto.randomBytes(32).toString("base64url");
5405
- const dir = path7.dirname(filePath);
5406
- if (!fs8.existsSync(dir))
5407
- fs8.mkdirSync(dir, { recursive: true, mode: 448 });
5408
- fs8.writeFileSync(filePath, JSON.stringify({ token, createdAtMs: Date.now() }, null, 2), { mode: 384 });
5790
+ const dir = path9.dirname(filePath);
5791
+ if (!fs9.existsSync(dir))
5792
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
5793
+ fs9.writeFileSync(filePath, JSON.stringify({ token, createdAtMs: Date.now() }, null, 2), { mode: 384 });
5409
5794
  try {
5410
- fs8.chmodSync(filePath, 384);
5795
+ fs9.chmodSync(filePath, 384);
5411
5796
  } catch {}
5412
5797
  cachedToken = token;
5413
5798
  return token;
@@ -5420,25 +5805,28 @@ function loadOrCreatePairingAccessToken() {
5420
5805
  // services/cloudflared-supervisor.ts
5421
5806
  import { spawn, spawnSync } from "node:child_process";
5422
5807
  import * as crypto2 from "node:crypto";
5423
- import * as fs9 from "node:fs";
5424
- import * as os4 from "node:os";
5425
- import * as path8 from "node:path";
5426
- var AGENTLIFE_DIR = path8.join(os4.homedir(), ".openclaw", "agentlife");
5427
- var TUNNEL_FILE = path8.join(AGENTLIFE_DIR, "tunnel.json");
5428
- var BIN_DIR = path8.join(AGENTLIFE_DIR, "bin");
5429
- var PAIR_REQUEST_MARKER = path8.join(AGENTLIFE_DIR, "pair-requested");
5430
- var IDENTITY_DIR = path8.join(os4.homedir(), ".agentlife");
5431
- var DEVICE_FILE = path8.join(IDENTITY_DIR, "device.json");
5432
- var LEGACY_DEVICE_FILE = path8.join(AGENTLIFE_DIR, "device.json");
5433
- var CLOUDFLARED_BIN = os4.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
5434
- var CLOUDFLARED_PATH = path8.join(BIN_DIR, CLOUDFLARED_BIN);
5808
+ import * as fs10 from "node:fs";
5809
+ import * as os5 from "node:os";
5810
+ import * as path10 from "node:path";
5811
+ var DEFAULT_OPENCLAW_STATE_DIR = path10.join(os5.homedir(), ".openclaw");
5812
+ var OPENCLAW_STATE_DIR = process.env.OPENCLAW_STATE_DIR || DEFAULT_OPENCLAW_STATE_DIR;
5813
+ var IS_PROFILE_STATE_DIR = OPENCLAW_STATE_DIR !== DEFAULT_OPENCLAW_STATE_DIR;
5814
+ var AGENTLIFE_DIR = path10.join(OPENCLAW_STATE_DIR, "agentlife");
5815
+ var TUNNEL_FILE = path10.join(AGENTLIFE_DIR, "tunnel.json");
5816
+ var BIN_DIR = path10.join(AGENTLIFE_DIR, "bin");
5817
+ var PAIR_REQUEST_MARKER = path10.join(AGENTLIFE_DIR, "pair-requested");
5818
+ var IDENTITY_DIR = IS_PROFILE_STATE_DIR ? path10.join(OPENCLAW_STATE_DIR, ".agentlife") : path10.join(os5.homedir(), ".agentlife");
5819
+ var DEVICE_FILE = path10.join(IDENTITY_DIR, "device.json");
5820
+ var LEGACY_DEVICE_FILE = path10.join(AGENTLIFE_DIR, "device.json");
5821
+ var CLOUDFLARED_BIN = os5.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
5822
+ var CLOUDFLARED_PATH = path10.join(BIN_DIR, CLOUDFLARED_BIN);
5435
5823
  var API_BASE = process.env.AGENTLIFE_API_BASE || "https://api.agentlife.app";
5436
5824
  var RESTART_DELAY_MS = 5000;
5437
5825
  var STABLE_RUNTIME_MS = 60000;
5438
5826
  function createInitialState() {
5439
5827
  let resolver;
5440
- const promise = new Promise((resolve) => {
5441
- resolver = resolve;
5828
+ const promise = new Promise((resolve2) => {
5829
+ resolver = resolve2;
5442
5830
  });
5443
5831
  return {
5444
5832
  tunnelInfo: null,
@@ -5459,7 +5847,7 @@ function getTunnelInfo() {
5459
5847
  function awaitTunnelReady(timeoutMs) {
5460
5848
  return Promise.race([
5461
5849
  state.readyPromise,
5462
- new Promise((resolve) => setTimeout(() => resolve(null), timeoutMs))
5850
+ new Promise((resolve2) => setTimeout(() => resolve2(null), timeoutMs))
5463
5851
  ]);
5464
5852
  }
5465
5853
  function startCloudflaredSupervisor() {
@@ -5551,20 +5939,20 @@ async function doBootstrap() {
5551
5939
  return tunnelInfo;
5552
5940
  }
5553
5941
  function isPairRequested() {
5554
- return fs9.existsSync(PAIR_REQUEST_MARKER);
5942
+ return fs10.existsSync(PAIR_REQUEST_MARKER);
5555
5943
  }
5556
5944
  function requestPair() {
5557
5945
  try {
5558
- if (!fs9.existsSync(AGENTLIFE_DIR))
5559
- fs9.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5560
- fs9.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
5946
+ if (!fs10.existsSync(AGENTLIFE_DIR))
5947
+ fs10.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5948
+ fs10.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
5561
5949
  } catch (err) {
5562
5950
  console.warn(`[cloudflared-supervisor] failed to write pair-request marker: ${err?.message ?? err}`);
5563
5951
  }
5564
5952
  }
5565
5953
  function clearPairRequest() {
5566
5954
  try {
5567
- fs9.unlinkSync(PAIR_REQUEST_MARKER);
5955
+ fs10.unlinkSync(PAIR_REQUEST_MARKER);
5568
5956
  } catch {}
5569
5957
  }
5570
5958
  function parseRetryAfter(value) {
@@ -5588,18 +5976,18 @@ function scheduleProvisionRetry(delayMs) {
5588
5976
  }, delayMs);
5589
5977
  }
5590
5978
  function ensureDirs() {
5591
- if (!fs9.existsSync(AGENTLIFE_DIR))
5592
- fs9.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5593
- if (!fs9.existsSync(BIN_DIR))
5594
- fs9.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
5595
- if (!fs9.existsSync(IDENTITY_DIR))
5596
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5979
+ if (!fs10.existsSync(AGENTLIFE_DIR))
5980
+ fs10.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5981
+ if (!fs10.existsSync(BIN_DIR))
5982
+ fs10.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
5983
+ if (!fs10.existsSync(IDENTITY_DIR))
5984
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5597
5985
  }
5598
5986
  function readIdentityFile(filePath) {
5599
- if (!fs9.existsSync(filePath))
5987
+ if (!fs10.existsSync(filePath))
5600
5988
  return null;
5601
5989
  try {
5602
- const raw = fs9.readFileSync(filePath, "utf-8");
5990
+ const raw = fs10.readFileSync(filePath, "utf-8");
5603
5991
  const parsed = JSON.parse(raw);
5604
5992
  if (typeof parsed.deviceId === "string" && typeof parsed.deviceSecret === "string") {
5605
5993
  return { deviceId: parsed.deviceId, deviceSecret: parsed.deviceSecret };
@@ -5617,14 +6005,14 @@ function loadDeviceIdentity() {
5617
6005
  if (!legacy)
5618
6006
  return null;
5619
6007
  try {
5620
- if (!fs9.existsSync(IDENTITY_DIR))
5621
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5622
- fs9.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
6008
+ if (!fs10.existsSync(IDENTITY_DIR))
6009
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
6010
+ fs10.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
5623
6011
  try {
5624
- fs9.chmodSync(DEVICE_FILE, 384);
6012
+ fs10.chmodSync(DEVICE_FILE, 384);
5625
6013
  } catch {}
5626
6014
  try {
5627
- fs9.unlinkSync(LEGACY_DEVICE_FILE);
6015
+ fs10.unlinkSync(LEGACY_DEVICE_FILE);
5628
6016
  } catch {}
5629
6017
  console.log(`[cloudflared-supervisor] migrated device.json to ${DEVICE_FILE}`);
5630
6018
  } catch (err) {
@@ -5636,25 +6024,25 @@ function loadOrCreateDeviceIdentity() {
5636
6024
  const existing = loadDeviceIdentity();
5637
6025
  if (existing)
5638
6026
  return existing;
5639
- if (!fs9.existsSync(IDENTITY_DIR))
5640
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
6027
+ if (!fs10.existsSync(IDENTITY_DIR))
6028
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5641
6029
  const identity = {
5642
6030
  deviceId: crypto2.randomUUID(),
5643
6031
  deviceSecret: crypto2.randomBytes(32).toString("base64url")
5644
6032
  };
5645
- fs9.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
6033
+ fs10.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
5646
6034
  try {
5647
- fs9.chmodSync(DEVICE_FILE, 384);
6035
+ fs10.chmodSync(DEVICE_FILE, 384);
5648
6036
  } catch {}
5649
6037
  console.log(`[cloudflared-supervisor] generated new device identity (deviceId=${identity.deviceId})`);
5650
6038
  return identity;
5651
6039
  }
5652
6040
  var AGENTLIFE_API_BASE = API_BASE;
5653
6041
  function loadCachedTunnel() {
5654
- if (!fs9.existsSync(TUNNEL_FILE))
6042
+ if (!fs10.existsSync(TUNNEL_FILE))
5655
6043
  return null;
5656
6044
  try {
5657
- const raw = fs9.readFileSync(TUNNEL_FILE, "utf-8");
6045
+ const raw = fs10.readFileSync(TUNNEL_FILE, "utf-8");
5658
6046
  const parsed = JSON.parse(raw);
5659
6047
  if (typeof parsed.subdomain === "string" && typeof parsed.hostname === "string" && typeof parsed.tunnelUrl === "string" && typeof parsed.tunnelToken === "string" && typeof parsed.provisionedAt === "number") {
5660
6048
  return parsed;
@@ -5663,9 +6051,9 @@ function loadCachedTunnel() {
5663
6051
  return null;
5664
6052
  }
5665
6053
  function persistTunnel(info) {
5666
- fs9.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
6054
+ fs10.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
5667
6055
  try {
5668
- fs9.chmodSync(TUNNEL_FILE, 384);
6056
+ fs10.chmodSync(TUNNEL_FILE, 384);
5669
6057
  } catch {}
5670
6058
  }
5671
6059
  async function provisionTunnel(identity) {
@@ -5718,14 +6106,14 @@ async function provisionTunnel(identity) {
5718
6106
  }
5719
6107
  }
5720
6108
  function ensureCloudflaredBinary() {
5721
- if (fs9.existsSync(CLOUDFLARED_PATH)) {
6109
+ if (fs10.existsSync(CLOUDFLARED_PATH)) {
5722
6110
  try {
5723
- fs9.accessSync(CLOUDFLARED_PATH, fs9.constants.X_OK);
6111
+ fs10.accessSync(CLOUDFLARED_PATH, fs10.constants.X_OK);
5724
6112
  return CLOUDFLARED_PATH;
5725
6113
  } catch {}
5726
6114
  }
5727
- const platform2 = os4.platform();
5728
- const arch2 = os4.arch();
6115
+ const platform2 = os5.platform();
6116
+ const arch2 = os5.arch();
5729
6117
  const release = detectCloudflaredRelease(platform2, arch2);
5730
6118
  if (!release) {
5731
6119
  console.warn(`[cloudflared-supervisor] unsupported platform: ${platform2}/${arch2}`);
@@ -5738,28 +6126,28 @@ function ensureCloudflaredBinary() {
5738
6126
  if (release.kind === "tgz") {
5739
6127
  const extracted = extractTgzCloudflared(release.tempPath, BIN_DIR);
5740
6128
  try {
5741
- fs9.unlinkSync(release.tempPath);
6129
+ fs10.unlinkSync(release.tempPath);
5742
6130
  } catch {}
5743
6131
  if (!extracted)
5744
6132
  return null;
5745
6133
  } else {
5746
6134
  try {
5747
- fs9.renameSync(release.tempPath, CLOUDFLARED_PATH);
6135
+ fs10.renameSync(release.tempPath, CLOUDFLARED_PATH);
5748
6136
  } catch (err) {
5749
6137
  console.warn(`[cloudflared-supervisor] rename failed: ${err?.message}`);
5750
6138
  return null;
5751
6139
  }
5752
6140
  }
5753
6141
  try {
5754
- fs9.chmodSync(CLOUDFLARED_PATH, 493);
6142
+ fs10.chmodSync(CLOUDFLARED_PATH, 493);
5755
6143
  } catch {}
5756
- return fs9.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
6144
+ return fs10.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
5757
6145
  }
5758
6146
  function detectCloudflaredRelease(platform2, arch2) {
5759
6147
  const base = "https://github.com/cloudflare/cloudflared/releases/latest/download";
5760
6148
  if (platform2 === "darwin") {
5761
6149
  const asset = arch2 === "arm64" ? "cloudflared-darwin-arm64.tgz" : "cloudflared-darwin-amd64.tgz";
5762
- return { url: `${base}/${asset}`, kind: "tgz", tempPath: path8.join(BIN_DIR, asset) };
6150
+ return { url: `${base}/${asset}`, kind: "tgz", tempPath: path10.join(BIN_DIR, asset) };
5763
6151
  }
5764
6152
  if (platform2 === "linux") {
5765
6153
  const asset = arch2 === "arm64" ? "cloudflared-linux-arm64" : arch2 === "arm" ? "cloudflared-linux-arm" : arch2 === "x64" ? "cloudflared-linux-amd64" : null;
@@ -5783,11 +6171,11 @@ function downloadWithCurl(url, dest) {
5783
6171
  if (result.status !== 0) {
5784
6172
  console.warn(`[cloudflared-supervisor] curl failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
5785
6173
  try {
5786
- fs9.unlinkSync(dest);
6174
+ fs10.unlinkSync(dest);
5787
6175
  } catch {}
5788
6176
  return false;
5789
6177
  }
5790
- return fs9.existsSync(dest) && fs9.statSync(dest).size > 0;
6178
+ return fs10.existsSync(dest) && fs10.statSync(dest).size > 0;
5791
6179
  }
5792
6180
  function extractTgzCloudflared(tgzPath, destDir) {
5793
6181
  const result = spawnSync("tar", ["-xzf", tgzPath, "-C", destDir], { stdio: "pipe" });
@@ -5795,10 +6183,10 @@ function extractTgzCloudflared(tgzPath, destDir) {
5795
6183
  console.warn(`[cloudflared-supervisor] tar extract failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
5796
6184
  return false;
5797
6185
  }
5798
- return fs9.existsSync(CLOUDFLARED_PATH);
6186
+ return fs10.existsSync(CLOUDFLARED_PATH);
5799
6187
  }
5800
6188
  function killOrphanedCloudflareds(binPath) {
5801
- if (os4.platform() === "win32")
6189
+ if (os5.platform() === "win32")
5802
6190
  return;
5803
6191
  const found = spawnSync("pgrep", ["-f", binPath], { stdio: "pipe" });
5804
6192
  if (found.status !== 0)
@@ -5868,13 +6256,16 @@ var MIME_TYPES = {
5868
6256
  ".txt": "text/plain",
5869
6257
  ".map": "application/json"
5870
6258
  };
6259
+ function resolveStateDir() {
6260
+ return process.env.OPENCLAW_STATE_DIR || path11.join(os6.homedir(), ".openclaw");
6261
+ }
5871
6262
  function mintBootstrapToken() {
5872
6263
  const bootstrapToken = crypto3.randomBytes(32).toString("base64url");
5873
- const devicesDir = path9.join(os5.homedir(), ".openclaw", "devices");
5874
- const bootstrapPath = path9.join(devicesDir, "bootstrap.json");
5875
- if (!fs10.existsSync(devicesDir))
5876
- fs10.mkdirSync(devicesDir, { recursive: true });
5877
- const registry = fs10.existsSync(bootstrapPath) ? JSON.parse(fs10.readFileSync(bootstrapPath, "utf-8")) : {};
6264
+ const devicesDir = path11.join(resolveStateDir(), "devices");
6265
+ const bootstrapPath = path11.join(devicesDir, "bootstrap.json");
6266
+ if (!fs11.existsSync(devicesDir))
6267
+ fs11.mkdirSync(devicesDir, { recursive: true });
6268
+ const registry = fs11.existsSync(bootstrapPath) ? JSON.parse(fs11.readFileSync(bootstrapPath, "utf-8")) : {};
5878
6269
  registry[bootstrapToken] = {
5879
6270
  token: bootstrapToken,
5880
6271
  profile: {
@@ -5883,15 +6274,15 @@ function mintBootstrapToken() {
5883
6274
  },
5884
6275
  issuedAtMs: Date.now()
5885
6276
  };
5886
- fs10.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
6277
+ fs11.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
5887
6278
  return bootstrapToken;
5888
6279
  }
5889
6280
  function registerWebApp(api) {
5890
- const pluginRoot = path9.resolve(path9.dirname(api.source), "..");
5891
- const appRoot = path9.join(pluginRoot, "web-build");
5892
- const hasWebBuild = fs10.existsSync(appRoot);
5893
- const indexPath = hasWebBuild ? path9.join(appRoot, "index.html") : null;
5894
- const hasIndex = !!(indexPath && fs10.existsSync(indexPath));
6281
+ const pluginRoot = path11.resolve(path11.dirname(api.source), "..");
6282
+ const appRoot = path11.join(pluginRoot, "web-build");
6283
+ const hasWebBuild = fs11.existsSync(appRoot);
6284
+ const indexPath = hasWebBuild ? path11.join(appRoot, "index.html") : null;
6285
+ const hasIndex = !!(indexPath && fs11.existsSync(indexPath));
5895
6286
  if (!hasWebBuild) {
5896
6287
  api.logger.info("[agentlife] web-build/ not found — /agentlife/pair will still register; static dashboard disabled");
5897
6288
  } else if (!hasIndex) {
@@ -5899,11 +6290,13 @@ function registerWebApp(api) {
5899
6290
  }
5900
6291
  let gatewayToken = "";
5901
6292
  try {
5902
- const configPath = path9.join(__require("node:os").homedir(), ".openclaw", "openclaw.json");
5903
- const raw = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
6293
+ const configPath = process.env.OPENCLAW_CONFIG_PATH || path11.join(resolveStateDir(), "openclaw.json");
6294
+ const raw = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
5904
6295
  gatewayToken = raw?.gateway?.auth?.token || "";
5905
6296
  } catch {}
5906
- loadOrCreatePairingAccessToken();
6297
+ if (shouldStartRuntimeServices()) {
6298
+ loadOrCreatePairingAccessToken();
6299
+ }
5907
6300
  const approveLatest = () => {
5908
6301
  try {
5909
6302
  const { execSync } = __require("node:child_process");
@@ -6018,16 +6411,16 @@ function registerWebApp(api) {
6018
6411
  return true;
6019
6412
  }
6020
6413
  const relative = urlPath.replace(/^\/agentlife\/?/, "") || "index.html";
6021
- const filePath = path9.resolve(appRoot, relative);
6414
+ const filePath = path11.resolve(appRoot, relative);
6022
6415
  if (!filePath.startsWith(appRoot)) {
6023
6416
  res.writeHead(403);
6024
6417
  res.end();
6025
6418
  return true;
6026
6419
  }
6027
- const target = fs10.existsSync(filePath) && fs10.statSync(filePath).isFile() ? filePath : indexPath;
6028
- const ext = path9.extname(target).toLowerCase();
6420
+ const target = fs11.existsSync(filePath) && fs11.statSync(filePath).isFile() ? filePath : indexPath;
6421
+ const ext = path11.extname(target).toLowerCase();
6029
6422
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
6030
- let content = fs10.readFileSync(target);
6423
+ let content = fs11.readFileSync(target);
6031
6424
  if (target === indexPath) {
6032
6425
  setTimeout(approveLatest, 2000);
6033
6426
  setTimeout(approveLatest, 5000);
@@ -6063,62 +6456,29 @@ function registerWebApp(api) {
6063
6456
 
6064
6457
  // services/pairing.ts
6065
6458
  var TUNNEL_READY_TIMEOUT_MS = 90000;
6459
+ function resolveStateDir2(ctx) {
6460
+ return ctx?.stateDir || process.env.OPENCLAW_STATE_DIR || path12.join(os7.homedir(), ".openclaw");
6461
+ }
6066
6462
  function registerPairingServices(api) {
6067
6463
  api.registerService({
6068
6464
  id: "agentlife-pairing-qr",
6069
- start: async (_ctx) => {
6070
- try {
6071
- const cfg = api.runtime.config.loadConfig();
6072
- const port = cfg.gateway?.port ?? 18789;
6073
- const token = cfg.gateway?.auth?.token;
6074
- if (!token)
6075
- return;
6076
- const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
6077
- if (!supervisorTunnel?.tunnelUrl) {
6078
- 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.`);
6079
- return;
6080
- }
6081
- const url = supervisorTunnel.tunnelUrl;
6082
- const bootstrapToken = mintBootstrapToken();
6083
- const payload = JSON.stringify({ url, bootstrapToken });
6084
- const setupCode = Buffer.from(payload).toString("base64url");
6085
- let qrTerminal = null;
6086
- try {
6087
- const realPath = fsSync3.realpathSync(process.argv[1] || "");
6088
- const mainRequire = createRequire3(realPath);
6089
- qrTerminal = mainRequire("qrcode-terminal");
6090
- } catch {}
6091
- console.log(`
6092
- \x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
6093
- `);
6094
- if (qrTerminal?.generate) {
6095
- qrTerminal.generate(setupCode, { small: true }, (qr) => {
6096
- console.log(qr);
6097
- console.log(`
6098
- Setup code: ${setupCode}
6099
- `);
6100
- });
6101
- } else {
6102
- console.log(`Setup code: ${setupCode}`);
6103
- }
6104
- console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
6105
- console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
6106
- console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
6107
- console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
6108
- console.log(`
6109
- Scan the QR from the mobile app to connect.
6110
- `);
6111
- } catch (err) {
6465
+ start: (ctx) => {
6466
+ if (!shouldStartRuntimeServices())
6467
+ return;
6468
+ emitPairingQr(api, ctx).catch((err) => {
6112
6469
  console.warn("[agentlife] QR generation skipped:", err?.message);
6113
- }
6470
+ });
6114
6471
  }
6115
6472
  });
6116
6473
  api.registerService({
6117
6474
  id: "agentlife-auto-pair",
6118
6475
  start: async (ctx) => {
6119
- const devicesDir = path10.join(os6.homedir(), ".openclaw", "devices");
6120
- const pendingPath = path10.join(devicesDir, "pending.json");
6121
- const pairedPath = path10.join(devicesDir, "paired.json");
6476
+ if (!shouldStartRuntimeServices())
6477
+ return;
6478
+ const stateDir = resolveStateDir2(ctx);
6479
+ const devicesDir = path12.join(stateDir, "devices");
6480
+ const pendingPath = path12.join(devicesDir, "pending.json");
6481
+ const pairedPath = path12.join(devicesDir, "paired.json");
6122
6482
  const pollInterval = 3000;
6123
6483
  const withAdmin = (arr) => {
6124
6484
  const base = Array.isArray(arr) ? arr.filter((s) => typeof s === "string") : [];
@@ -6213,6 +6573,52 @@ Setup code: ${setupCode}
6213
6573
  }
6214
6574
  });
6215
6575
  }
6576
+ async function emitPairingQr(api, _ctx) {
6577
+ try {
6578
+ const cfg = api.runtime.config.loadConfig();
6579
+ const port = cfg.gateway?.port ?? 18789;
6580
+ const token = cfg.gateway?.auth?.token;
6581
+ if (!token)
6582
+ return;
6583
+ const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
6584
+ if (!supervisorTunnel?.tunnelUrl) {
6585
+ 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.`);
6586
+ return;
6587
+ }
6588
+ const url = supervisorTunnel.tunnelUrl;
6589
+ const bootstrapToken = mintBootstrapToken();
6590
+ const payload = JSON.stringify({ url, bootstrapToken });
6591
+ const setupCode = Buffer.from(payload).toString("base64url");
6592
+ let qrTerminal = null;
6593
+ try {
6594
+ const realPath = fsSync3.realpathSync(process.argv[1] || "");
6595
+ const mainRequire = createRequire3(realPath);
6596
+ qrTerminal = mainRequire("qrcode-terminal");
6597
+ } catch {}
6598
+ console.log(`
6599
+ \x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
6600
+ `);
6601
+ if (qrTerminal?.generate) {
6602
+ qrTerminal.generate(setupCode, { small: true }, (qr) => {
6603
+ console.log(qr);
6604
+ console.log(`
6605
+ Setup code: ${setupCode}
6606
+ `);
6607
+ });
6608
+ } else {
6609
+ console.log(`Setup code: ${setupCode}`);
6610
+ }
6611
+ console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
6612
+ console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
6613
+ console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
6614
+ console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
6615
+ console.log(`
6616
+ Scan the QR from the mobile app to connect.
6617
+ `);
6618
+ } catch (err) {
6619
+ console.warn("[agentlife] QR generation skipped:", err?.message);
6620
+ }
6621
+ }
6216
6622
 
6217
6623
  // hooks/bootstrap.ts
6218
6624
  var SKIP_GUIDANCE_AGENTS = new Set(["agentlife"]);
@@ -6372,15 +6778,15 @@ function registerBootstrapHookImpl(api, state2) {
6372
6778
  }
6373
6779
 
6374
6780
  // notifications.ts
6375
- import * as fs11 from "node:fs";
6376
- import * as path11 from "node:path";
6781
+ import * as fs12 from "node:fs";
6782
+ import * as path13 from "node:path";
6377
6783
  var config;
6378
6784
  function loadConfig() {
6379
6785
  if (config !== undefined)
6380
6786
  return config;
6381
6787
  try {
6382
- const configPath = path11.join(process.env.HOME ?? "~", ".openclaw", "agentlife", "notification-config.json");
6383
- const raw = fs11.readFileSync(configPath, "utf-8");
6788
+ const configPath = path13.join(process.env.HOME ?? "~", ".openclaw", "agentlife", "notification-config.json");
6789
+ const raw = fs12.readFileSync(configPath, "utf-8");
6384
6790
  const parsed = JSON.parse(raw);
6385
6791
  if (parsed.serverUrl && parsed.apiKey) {
6386
6792
  config = { serverUrl: parsed.serverUrl, apiKey: parsed.apiKey };
@@ -6950,9 +7356,101 @@ function registerPromptStateHook(api, state2) {
6950
7356
  }
6951
7357
 
6952
7358
  // gateway/agents.ts
6953
- import * as fs12 from "node:fs";
6954
- import * as os7 from "node:os";
6955
- import * as path12 from "node:path";
7359
+ import * as fs13 from "node:fs";
7360
+ import * as os8 from "node:os";
7361
+ import * as path14 from "node:path";
7362
+ import { ensureAuthProfileStore, upsertAuthProfile as upsertAuthProfile2 } from "openclaw/plugin-sdk/provider-auth";
7363
+ function resolveOpenClawRoot2() {
7364
+ const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
7365
+ return fromEnv ? path14.resolve(fromEnv) : path14.join(os8.homedir(), ".openclaw");
7366
+ }
7367
+ function providerFromModel2(model) {
7368
+ const slash = model.indexOf("/");
7369
+ if (slash <= 0)
7370
+ return null;
7371
+ return model.slice(0, slash).trim() || null;
7372
+ }
7373
+ function authenticatedProvidersFromConfig(cfg) {
7374
+ const out = new Set;
7375
+ const profiles = cfg?.auth?.profiles;
7376
+ if (!profiles || typeof profiles !== "object")
7377
+ return out;
7378
+ for (const entry of Object.values(profiles)) {
7379
+ const provider = entry?.provider;
7380
+ if (typeof provider === "string" && provider.trim()) {
7381
+ out.add(provider.trim());
7382
+ }
7383
+ }
7384
+ return out;
7385
+ }
7386
+ function findSiblingAuthProfile(provider, targetAgentId) {
7387
+ const agentsRoot = path14.join(resolveOpenClawRoot2(), "agents");
7388
+ let entries;
7389
+ try {
7390
+ entries = fs13.readdirSync(agentsRoot, { withFileTypes: true });
7391
+ } catch {
7392
+ return null;
7393
+ }
7394
+ for (const entry of entries) {
7395
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
7396
+ continue;
7397
+ if (entry.name === targetAgentId)
7398
+ continue;
7399
+ const agentDir = path14.join(agentsRoot, entry.name, "agent");
7400
+ try {
7401
+ const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false, syncExternalCli: false });
7402
+ for (const [profileId, profile] of Object.entries(store?.profiles ?? {})) {
7403
+ if (profile?.provider !== provider)
7404
+ continue;
7405
+ const type = profile?.type;
7406
+ const mode = profile?.mode;
7407
+ if (type === "oauth")
7408
+ continue;
7409
+ if (mode === "oauth")
7410
+ continue;
7411
+ return { profileId, profile };
7412
+ }
7413
+ } catch {}
7414
+ const file = path14.join(agentDir, "auth-profiles.json");
7415
+ let data;
7416
+ try {
7417
+ data = JSON.parse(fs13.readFileSync(file, "utf-8"));
7418
+ } catch {
7419
+ continue;
7420
+ }
7421
+ const profiles = data?.profiles;
7422
+ if (!profiles || typeof profiles !== "object")
7423
+ continue;
7424
+ for (const [profileId, profile] of Object.entries(profiles)) {
7425
+ if (profile?.provider !== provider)
7426
+ continue;
7427
+ const type = profile?.type;
7428
+ const mode = profile?.mode;
7429
+ if (type === "oauth")
7430
+ continue;
7431
+ if (mode === "oauth")
7432
+ continue;
7433
+ return {
7434
+ profileId,
7435
+ profile
7436
+ };
7437
+ }
7438
+ }
7439
+ return null;
7440
+ }
7441
+ function writeAgentAuthProfile(agentId, provider, match) {
7442
+ if (!match)
7443
+ return false;
7444
+ const agentDir = path14.join(resolveOpenClawRoot2(), "agents", agentId, "agent");
7445
+ fs13.mkdirSync(agentDir, { recursive: true });
7446
+ const credential = { ...match.profile };
7447
+ if (credential.type === "api_key" && typeof credential.key !== "string" && typeof credential.apiKey === "string") {
7448
+ credential.key = credential.apiKey;
7449
+ delete credential.apiKey;
7450
+ }
7451
+ upsertAuthProfile2({ profileId: match.profileId, credential, agentDir });
7452
+ return true;
7453
+ }
6956
7454
  function registerAgentGateway(api, state2) {
6957
7455
  api.registerGatewayMethod("agentlife.createAgent", async ({ params, respond }) => {
6958
7456
  const id = typeof params?.id === "string" ? params.id.trim() : "";
@@ -6972,6 +7470,18 @@ function registerAgentGateway(api, state2) {
6972
7470
  return;
6973
7471
  }
6974
7472
  const cfg = api.runtime.config.loadConfig();
7473
+ const provider = model ? providerFromModel2(model) : null;
7474
+ const providerInConfig = provider ? authenticatedProvidersFromConfig(cfg).has(provider) : false;
7475
+ const siblingAuth = provider ? findSiblingAuthProfile(provider, id) : null;
7476
+ if (provider && !providerInConfig && !siblingAuth) {
7477
+ respond(false, {
7478
+ error: `missing provider credentials for ${provider}; configure ${provider} before creating an agent with ${model}`,
7479
+ code: "missing_provider_auth",
7480
+ provider,
7481
+ model
7482
+ });
7483
+ return;
7484
+ }
6975
7485
  const currentList = cfg.agents?.list ?? [];
6976
7486
  const existing = currentList.find((a) => a.id === id);
6977
7487
  if (!existing) {
@@ -7018,6 +7528,10 @@ function registerAgentGateway(api, state2) {
7018
7528
  console.log("[agentlife] builder patched agent config: %s", id);
7019
7529
  }
7020
7530
  }
7531
+ const authCopied = provider ? writeAgentAuthProfile(id, provider, siblingAuth) : false;
7532
+ if (authCopied) {
7533
+ console.log("[agentlife] copied %s auth profile into agent: %s", provider, id);
7534
+ }
7021
7535
  const hasGoodDescription = description && isUsableDescription(description, id, name);
7022
7536
  state2.agentRegistry.set(id, {
7023
7537
  name: name || id,
@@ -7047,7 +7561,7 @@ function registerAgentGateway(api, state2) {
7047
7561
  console.warn("[agentlife] deleteWelcomeWidget failed for %s: %s", id, e?.message);
7048
7562
  }
7049
7563
  }
7050
- respond(true, { status, id, name, model, workspace, description, ...configFields.length ? { configFields } : {} });
7564
+ respond(true, { status, id, name, model, workspace, description, ...authCopied ? { authCopied: true } : {}, ...configFields.length ? { configFields } : {} });
7051
7565
  });
7052
7566
  api.registerGatewayMethod("agentlife.deleteAgent", async ({ params, respond }) => {
7053
7567
  const id = typeof params?.id === "string" ? params.id.trim() : "";
@@ -7140,10 +7654,10 @@ function registerAgentGateway(api, state2) {
7140
7654
  state2.agentDbs.delete(id);
7141
7655
  }
7142
7656
  if (state2.dbBaseDir) {
7143
- const dbPath = path12.join(state2.dbBaseDir, `${id}.db`);
7657
+ const dbPath = path14.join(state2.dbBaseDir, `${id}.db`);
7144
7658
  try {
7145
- if (fs12.existsSync(dbPath)) {
7146
- fs12.unlinkSync(dbPath);
7659
+ if (fs13.existsSync(dbPath)) {
7660
+ fs13.unlinkSync(dbPath);
7147
7661
  cleanup.agentDbDeleted = true;
7148
7662
  }
7149
7663
  } catch (e) {
@@ -7155,24 +7669,24 @@ function registerAgentGateway(api, state2) {
7155
7669
  const existingEntry = currentList.find((a) => a.id === id);
7156
7670
  const filtered = currentList.filter((a) => a.id !== id);
7157
7671
  const removedFromConfig = filtered.length < currentList.length;
7158
- const openclawHome = path12.join(os7.homedir(), ".openclaw");
7159
- const stateDir = path12.join(openclawHome, "agents", id);
7160
- if (stateDir.startsWith(path12.join(openclawHome, "agents") + path12.sep)) {
7672
+ const openclawHome = path14.join(os8.homedir(), ".openclaw");
7673
+ const stateDir = path14.join(openclawHome, "agents", id);
7674
+ if (stateDir.startsWith(path14.join(openclawHome, "agents") + path14.sep)) {
7161
7675
  try {
7162
- if (fs12.existsSync(stateDir)) {
7163
- fs12.rmSync(stateDir, { recursive: true, force: true });
7676
+ if (fs13.existsSync(stateDir)) {
7677
+ fs13.rmSync(stateDir, { recursive: true, force: true });
7164
7678
  cleanup.stateDeleted = true;
7165
7679
  }
7166
7680
  } catch (e) {
7167
7681
  console.warn("[agentlife] deleteAgent: failed to delete state dir:", e?.message);
7168
7682
  }
7169
7683
  }
7170
- const defaultWorkspace = path12.join(openclawHome, `workspace-${id}`);
7171
- const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path12.resolve(existingEntry.workspace) : defaultWorkspace;
7172
- if (workspaceToDelete.startsWith(openclawHome + path12.sep) && workspaceToDelete !== openclawHome) {
7684
+ const defaultWorkspace = path14.join(openclawHome, `workspace-${id}`);
7685
+ const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path14.resolve(existingEntry.workspace) : defaultWorkspace;
7686
+ if (workspaceToDelete.startsWith(openclawHome + path14.sep) && workspaceToDelete !== openclawHome) {
7173
7687
  try {
7174
- if (fs12.existsSync(workspaceToDelete)) {
7175
- fs12.rmSync(workspaceToDelete, { recursive: true, force: true });
7688
+ if (fs13.existsSync(workspaceToDelete)) {
7689
+ fs13.rmSync(workspaceToDelete, { recursive: true, force: true });
7176
7690
  cleanup.workspaceDeleted = true;
7177
7691
  }
7178
7692
  } catch (e) {
@@ -7491,7 +8005,7 @@ function registerUsageGateway(api, state2) {
7491
8005
  }
7492
8006
 
7493
8007
  // gateway/surfaces-gateway.ts
7494
- import * as fs13 from "node:fs";
8008
+ import * as fs14 from "node:fs";
7495
8009
  var guidedDismissSent = new Set;
7496
8010
  var warnedMissingOrigin = new Set;
7497
8011
  function registerSurfacesGateway(api, state2) {
@@ -7580,7 +8094,7 @@ function registerSurfacesGateway(api, state2) {
7580
8094
  guidedDismissSent.delete(surfaceId);
7581
8095
  if (!view2.isTransient && view2.path) {
7582
8096
  try {
7583
- fs13.unlinkSync(view2.path);
8097
+ fs14.unlinkSync(view2.path);
7584
8098
  } catch (err) {
7585
8099
  if (err?.code !== "ENOENT")
7586
8100
  console.warn("[agentlife] dismiss: file unlink failed for %s: %s", surfaceId, err?.message);
@@ -7825,17 +8339,17 @@ function registerAutomationsGateway(api, state2) {
7825
8339
  }
7826
8340
 
7827
8341
  // gateway/admin.ts
7828
- import * as fs14 from "node:fs/promises";
8342
+ import * as fs15 from "node:fs/promises";
7829
8343
  import * as fsSync4 from "node:fs";
7830
- import * as os8 from "node:os";
7831
- import * as path13 from "node:path";
8344
+ import * as os9 from "node:os";
8345
+ import * as path15 from "node:path";
7832
8346
  import { fileURLToPath } from "node:url";
7833
8347
  var PLUGIN_VERSION = (() => {
7834
8348
  try {
7835
- const moduleDir = path13.dirname(fileURLToPath(import.meta.url));
8349
+ const moduleDir = path15.dirname(fileURLToPath(import.meta.url));
7836
8350
  const candidates = [
7837
- path13.join(moduleDir, "..", "package.json"),
7838
- path13.join(moduleDir, "..", "..", "package.json")
8351
+ path15.join(moduleDir, "..", "package.json"),
8352
+ path15.join(moduleDir, "..", "..", "package.json")
7839
8353
  ];
7840
8354
  for (const p of candidates) {
7841
8355
  if (fsSync4.existsSync(p)) {
@@ -7884,7 +8398,7 @@ async function fetchNpmLatestVersion(force = false) {
7884
8398
  return inflightFetch;
7885
8399
  }
7886
8400
  function pluginConfigPath() {
7887
- return path13.join(os8.homedir(), ".openclaw", "agentlife", "plugin-config.json");
8401
+ return path15.join(os9.homedir(), ".openclaw", "agentlife", "plugin-config.json");
7888
8402
  }
7889
8403
  function readPluginConfig() {
7890
8404
  try {
@@ -7894,7 +8408,7 @@ function readPluginConfig() {
7894
8408
  }
7895
8409
  }
7896
8410
  function writePluginConfig(config2) {
7897
- const dir = path13.dirname(pluginConfigPath());
8411
+ const dir = path15.dirname(pluginConfigPath());
7898
8412
  fsSync4.mkdirSync(dir, { recursive: true });
7899
8413
  fsSync4.writeFileSync(pluginConfigPath(), JSON.stringify(config2, null, 2));
7900
8414
  }
@@ -8022,7 +8536,7 @@ function registerAdminGateway(api, state2) {
8022
8536
  api.registerGatewayMethod("agentlife.uninstall", async ({ respond }) => {
8023
8537
  try {
8024
8538
  const cleaned = [];
8025
- const baseDir = state2.agentlifeStateDir ?? path13.join(os8.homedir(), ".openclaw", "agentlife");
8539
+ const baseDir = state2.agentlifeStateDir ?? path15.join(os9.homedir(), ".openclaw", "agentlife");
8026
8540
  const identity = loadDeviceIdentity();
8027
8541
  if (!identity) {
8028
8542
  cleaned.push("server deprovision skipped (no device identity)");
@@ -8087,7 +8601,7 @@ function registerAdminGateway(api, state2) {
8087
8601
  } catch {
8088
8602
  cleaned.push("agent config cleanup skipped");
8089
8603
  }
8090
- const backupPath = path13.join(baseDir, "config-backup.json");
8604
+ const backupPath = path15.join(baseDir, "config-backup.json");
8091
8605
  try {
8092
8606
  cleaned.push(await restoreConfigFromBackup(api, backupPath));
8093
8607
  } catch {
@@ -8109,35 +8623,35 @@ function registerAdminGateway(api, state2) {
8109
8623
  dbPath,
8110
8624
  dbPath ? `${dbPath}-wal` : null,
8111
8625
  dbPath ? `${dbPath}-shm` : null,
8112
- path13.join(baseDir, "config-backup.json"),
8113
- path13.join(baseDir, "notification-config.json"),
8114
- path13.join(baseDir, "canvas-node-identity.json")
8626
+ path15.join(baseDir, "config-backup.json"),
8627
+ path15.join(baseDir, "notification-config.json"),
8628
+ path15.join(baseDir, "canvas-node-identity.json")
8115
8629
  ].filter(Boolean);
8116
8630
  for (const fp of stateFiles) {
8117
8631
  try {
8118
- await fs14.unlink(fp);
8119
- cleaned.push(`deleted ${path13.basename(fp)}`);
8632
+ await fs15.unlink(fp);
8633
+ cleaned.push(`deleted ${path15.basename(fp)}`);
8120
8634
  } catch {}
8121
8635
  }
8122
8636
  if (state2.dbBaseDir) {
8123
8637
  try {
8124
- await fs14.rm(state2.dbBaseDir, { recursive: true, force: true });
8638
+ await fs15.rm(state2.dbBaseDir, { recursive: true, force: true });
8125
8639
  cleaned.push("deleted agent databases");
8126
8640
  } catch {}
8127
8641
  }
8128
8642
  for (const agent of PROVISIONED_AGENTS) {
8129
8643
  if (agent.existingAgent)
8130
8644
  continue;
8131
- const wsDir = agent.workspaceDir ?? path13.join(os8.homedir(), ".openclaw", `workspace-${agent.id}`);
8645
+ const wsDir = agent.workspaceDir ?? path15.join(os9.homedir(), ".openclaw", `workspace-${agent.id}`);
8132
8646
  try {
8133
- await fs14.rm(wsDir, { recursive: true, force: true });
8647
+ await fs15.rm(wsDir, { recursive: true, force: true });
8134
8648
  cleaned.push(`deleted workspace ${agent.id}`);
8135
8649
  } catch {}
8136
8650
  }
8137
8651
  try {
8138
- const remaining = await fs14.readdir(baseDir);
8652
+ const remaining = await fs15.readdir(baseDir);
8139
8653
  if (remaining.length === 0) {
8140
- await fs14.rmdir(baseDir);
8654
+ await fs15.rmdir(baseDir);
8141
8655
  cleaned.push("deleted agentlife state directory");
8142
8656
  }
8143
8657
  } catch {}
@@ -8177,11 +8691,11 @@ function registerAdminGateway(api, state2) {
8177
8691
 
8178
8692
  // gateway/evolutions.ts
8179
8693
  import * as fsSync5 from "node:fs";
8180
- import * as os9 from "node:os";
8181
- import * as path14 from "node:path";
8694
+ import * as os10 from "node:os";
8695
+ import * as path16 from "node:path";
8182
8696
  var ALLOWED_FILENAMES = new Set(["AGENTS.md", "USER.md", "SOUL.md", "TOOLS.md", "IDENTITY.md"]);
8183
8697
  function workspaceFilePath(agentId, filename) {
8184
- return path14.join(os9.homedir(), ".openclaw", `workspace-${agentId}`, filename);
8698
+ return path16.join(os10.homedir(), ".openclaw", `workspace-${agentId}`, filename);
8185
8699
  }
8186
8700
  function hasRegressionFlag(db, agentId, filename, snapshotAt) {
8187
8701
  const horizon = snapshotAt + 7 * 24 * 60 * 60 * 1000;
@@ -8556,39 +9070,43 @@ function parseOffset(offset) {
8556
9070
  // gateway/providers.ts
8557
9071
  import {
8558
9072
  upsertApiKeyProfile,
8559
- upsertAuthProfile,
9073
+ upsertAuthProfile as upsertAuthProfile3,
8560
9074
  buildApiKeyCredential,
8561
9075
  applyAuthProfileConfig
8562
9076
  } from "openclaw/plugin-sdk/provider-auth-api-key";
8563
9077
 
8564
9078
  // gateway/config-utils.ts
8565
- import * as fs15 from "node:fs";
8566
- import * as os10 from "node:os";
8567
- import * as path15 from "node:path";
9079
+ import * as fs16 from "node:fs";
9080
+ import * as os11 from "node:os";
9081
+ import * as path17 from "node:path";
8568
9082
  function configPath() {
8569
- return path15.join(os10.homedir(), ".openclaw", "openclaw.json");
9083
+ return path17.join(os11.homedir(), ".openclaw", "openclaw.json");
8570
9084
  }
8571
9085
  function readConfig() {
8572
9086
  try {
8573
- return JSON.parse(fs15.readFileSync(configPath(), "utf-8"));
9087
+ return JSON.parse(fs16.readFileSync(configPath(), "utf-8"));
8574
9088
  } catch {
8575
9089
  return {};
8576
9090
  }
8577
9091
  }
8578
9092
  function writeConfig(cfg) {
8579
- fs15.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
9093
+ fs16.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
8580
9094
  `, "utf-8");
8581
9095
  }
8582
9096
 
8583
9097
  // gateway/providers.ts
8584
- import * as fs16 from "node:fs";
8585
- import * as os11 from "node:os";
8586
- import * as path16 from "node:path";
9098
+ import * as fs17 from "node:fs";
9099
+ import * as os12 from "node:os";
9100
+ import * as path18 from "node:path";
9101
+ function resolveOpenClawRoot3() {
9102
+ const fromEnv = process.env.OPENCLAW_STATE_DIR?.trim();
9103
+ return fromEnv ? path18.resolve(fromEnv) : path18.join(os12.homedir(), ".openclaw");
9104
+ }
8587
9105
  function siblingAgentDirs() {
8588
- const root = path16.join(os11.homedir(), ".openclaw", "agents");
9106
+ const root = path18.join(resolveOpenClawRoot3(), "agents");
8589
9107
  let entries;
8590
9108
  try {
8591
- entries = fs16.readdirSync(root, { withFileTypes: true });
9109
+ entries = fs17.readdirSync(root, { withFileTypes: true });
8592
9110
  } catch {
8593
9111
  return [];
8594
9112
  }
@@ -8596,9 +9114,9 @@ function siblingAgentDirs() {
8596
9114
  for (const entry of entries) {
8597
9115
  if (!entry.isDirectory() && !entry.isSymbolicLink())
8598
9116
  continue;
8599
- const dir = path16.join(root, entry.name, "agent");
9117
+ const dir = path18.join(root, entry.name, "agent");
8600
9118
  try {
8601
- if (fs16.statSync(dir).isDirectory())
9119
+ if (fs17.statSync(dir).isDirectory())
8602
9120
  out.push(dir);
8603
9121
  } catch {}
8604
9122
  }
@@ -8668,20 +9186,20 @@ async function ensureModels(run) {
8668
9186
  }
8669
9187
  function readOAuthExpiries() {
8670
9188
  const result = new Map;
8671
- const fs17 = __require("node:fs");
8672
- const path17 = __require("node:path");
8673
- const os12 = __require("node:os");
8674
- const agentsDir = path17.join(os12.homedir(), ".openclaw", "agents");
9189
+ const fs18 = __require("node:fs");
9190
+ const path19 = __require("node:path");
9191
+ const os13 = __require("node:os");
9192
+ const agentsDir = path19.join(os13.homedir(), ".openclaw", "agents");
8675
9193
  let entries;
8676
9194
  try {
8677
- entries = fs17.readdirSync(agentsDir);
9195
+ entries = fs18.readdirSync(agentsDir);
8678
9196
  } catch {
8679
9197
  return result;
8680
9198
  }
8681
9199
  for (const name of entries) {
8682
- const filePath = path17.join(agentsDir, name, "agent", "auth-profiles.json");
9200
+ const filePath = path19.join(agentsDir, name, "agent", "auth-profiles.json");
8683
9201
  try {
8684
- const raw = fs17.readFileSync(filePath, "utf-8");
9202
+ const raw = fs18.readFileSync(filePath, "utf-8");
8685
9203
  const data = JSON.parse(raw);
8686
9204
  const profiles = data?.profiles ?? {};
8687
9205
  for (const entry of Object.values(profiles)) {
@@ -8819,7 +9337,7 @@ function registerProvidersGateway(api, state2) {
8819
9337
  const credential = buildApiKeyCredential(provider, apiKey);
8820
9338
  for (const agentDir of siblingAgentDirs()) {
8821
9339
  try {
8822
- upsertAuthProfile({ profileId, credential, agentDir });
9340
+ upsertAuthProfile3({ profileId, credential, agentDir });
8823
9341
  } catch {}
8824
9342
  }
8825
9343
  const cfg = readConfig();
@@ -8853,9 +9371,9 @@ function registerProvidersGateway(api, state2) {
8853
9371
  }
8854
9372
  writeConfig(cfg);
8855
9373
  for (const agentDir of siblingAgentDirs()) {
8856
- const file = path16.join(agentDir, "auth-profiles.json");
9374
+ const file = path18.join(agentDir, "auth-profiles.json");
8857
9375
  try {
8858
- const raw = fs16.readFileSync(file, "utf-8");
9376
+ const raw = fs17.readFileSync(file, "utf-8");
8859
9377
  const data = JSON.parse(raw);
8860
9378
  const agentProfiles = data?.profiles;
8861
9379
  if (!agentProfiles || typeof agentProfiles !== "object")
@@ -8872,7 +9390,7 @@ function registerProvidersGateway(api, state2) {
8872
9390
  changed = true;
8873
9391
  }
8874
9392
  if (changed)
8875
- fs16.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
9393
+ fs17.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
8876
9394
  } catch {}
8877
9395
  }
8878
9396
  respond(true, { provider, configured: false });
@@ -8962,22 +9480,22 @@ ${result?.stderr ?? ""}`;
8962
9480
  }
8963
9481
 
8964
9482
  // gateway/models-config.ts
8965
- import * as fs17 from "node:fs";
8966
- import * as os12 from "node:os";
8967
- import * as path17 from "node:path";
9483
+ import * as fs18 from "node:fs";
9484
+ import * as os13 from "node:os";
9485
+ import * as path19 from "node:path";
8968
9486
  function pluginConfigPath2() {
8969
- return path17.join(os12.homedir(), ".openclaw", "agentlife", "plugin-config.json");
9487
+ return path19.join(os13.homedir(), ".openclaw", "agentlife", "plugin-config.json");
8970
9488
  }
8971
9489
  function readPluginConfig2() {
8972
9490
  try {
8973
- return JSON.parse(fs17.readFileSync(pluginConfigPath2(), "utf-8"));
9491
+ return JSON.parse(fs18.readFileSync(pluginConfigPath2(), "utf-8"));
8974
9492
  } catch {
8975
9493
  return {};
8976
9494
  }
8977
9495
  }
8978
9496
  function writePluginConfig2(cfg) {
8979
- fs17.mkdirSync(path17.dirname(pluginConfigPath2()), { recursive: true });
8980
- fs17.writeFileSync(pluginConfigPath2(), JSON.stringify(cfg, null, 2) + `
9497
+ fs18.mkdirSync(path19.dirname(pluginConfigPath2()), { recursive: true });
9498
+ fs18.writeFileSync(pluginConfigPath2(), JSON.stringify(cfg, null, 2) + `
8981
9499
  `, "utf-8");
8982
9500
  }
8983
9501
  function registerModelsConfigGateway(api) {
@@ -9121,9 +9639,12 @@ var stopObservability = null;
9121
9639
  var stopDailySweep = null;
9122
9640
  var stopQualityCheckPoller = null;
9123
9641
  var stopCloudflared = null;
9642
+ function resolveOpenClawRoot4() {
9643
+ return process.env.OPENCLAW_STATE_DIR || path20.join(homedir17(), ".openclaw");
9644
+ }
9124
9645
  function resolveInternalModel(api) {
9125
9646
  try {
9126
- const pluginCfgPath = path18.join(homedir15(), ".openclaw", "agentlife", "plugin-config.json");
9647
+ const pluginCfgPath = path20.join(resolveOpenClawRoot4(), "agentlife", "plugin-config.json");
9127
9648
  try {
9128
9649
  const raw = __require("node:fs").readFileSync(pluginCfgPath, "utf-8");
9129
9650
  const pluginCfg = JSON.parse(raw);
@@ -9149,7 +9670,24 @@ function _getState() {
9149
9670
  throw new Error("plugin not registered");
9150
9671
  return currentState;
9151
9672
  }
9673
+ async function startProvisioningServices(api, state2) {
9674
+ const cfg = api.runtime.config.loadConfig();
9675
+ await provisionAgents(state2, cfg, api.runtime, console.log);
9676
+ await reconcileInternalSessions(state2, createdInternalSessions);
9677
+ await cleanupSupervisorLegacyState(state2);
9678
+ stopObservability = startObservabilityService(state2);
9679
+ stopDailySweep = startDailySweepService(state2);
9680
+ stopQualityCheckPoller = startQualityCheckPoller(state2);
9681
+ renderAllAgentWidgets(state2, api.runtime, console.log);
9682
+ }
9152
9683
  function register(api) {
9684
+ if (!shouldStartRuntimeServices()) {
9685
+ console.log("[agentlife] install/inspect mode — runtime registration skipped");
9686
+ return;
9687
+ }
9688
+ if (shouldStartRuntimeServices()) {
9689
+ runInstallBootstrap();
9690
+ }
9153
9691
  if (currentState) {
9154
9692
  registerWidgetPushTool(api, currentState);
9155
9693
  api.registerChannel({ plugin: createAgentlifeChannel(currentState.channelState) });
@@ -9159,7 +9697,8 @@ function register(api) {
9159
9697
  return;
9160
9698
  }
9161
9699
  registered = true;
9162
- const fallbackDir = path18.join(homedir15(), ".openclaw", "agentlife");
9700
+ const fallbackRoot = resolveOpenClawRoot4();
9701
+ const fallbackDir = path20.join(fallbackRoot, "agentlife");
9163
9702
  const state2 = {
9164
9703
  fileSurfaceStorage: null,
9165
9704
  surfaceIndex: null,
@@ -9171,9 +9710,9 @@ function register(api) {
9171
9710
  agentDbs: new Map,
9172
9711
  historyDb: null,
9173
9712
  agentlifeStateDir: fallbackDir,
9174
- registryFilePath: path18.join(fallbackDir, "agent-registry.json"),
9175
- dbBaseDir: path18.join(fallbackDir, "db"),
9176
- historyDbPath: path18.join(fallbackDir, "agentlife.db"),
9713
+ registryFilePath: path20.join(fallbackDir, "agent-registry.json"),
9714
+ dbBaseDir: path20.join(fallbackDir, "db"),
9715
+ historyDbPath: path20.join(fallbackDir, "agentlife.db"),
9177
9716
  runCommand: api.runtime.system?.runCommandWithTimeout ?? null,
9178
9717
  enqueueSystemEvent: null,
9179
9718
  requestHeartbeatNow: null,
@@ -9185,12 +9724,12 @@ function register(api) {
9185
9724
  currentState = state2;
9186
9725
  api.registerChannel({ plugin: createAgentlifeChannel(state2.channelState) });
9187
9726
  console.log("[agentlife] channel registered, session format: %s", buildAgentSessionKey("example"));
9188
- if (existsSync7(fallbackDir)) {
9727
+ if (shouldStartRuntimeServices() && existsSync8(fallbackDir)) {
9189
9728
  try {
9190
9729
  const db = getOrCreateHistoryDb(state2);
9191
9730
  state2.surfaceIndex = new SurfaceIndex(db);
9192
9731
  state2.followupQueue = new FollowupQueue(db);
9193
- state2.fileSurfaceStorage = new FileSurfaceStorage;
9732
+ state2.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: fallbackRoot });
9194
9733
  } catch (e) {
9195
9734
  console.warn("[agentlife] eager surface storage init failed:", e?.message);
9196
9735
  }
@@ -9200,20 +9739,32 @@ function register(api) {
9200
9739
  registerSurfacesService(api, state2);
9201
9740
  api.registerService({
9202
9741
  id: "agentlife-provisioning",
9203
- start: async () => {
9204
- const cfg = api.runtime.config.loadConfig();
9205
- await provisionAgents(state2, cfg, api.runtime, console.log);
9206
- await reconcileInternalSessions(state2, createdInternalSessions);
9207
- await cleanupSupervisorLegacyState(state2);
9208
- stopObservability = startObservabilityService(state2);
9209
- stopDailySweep = startDailySweepService(state2);
9210
- stopQualityCheckPoller = startQualityCheckPoller(state2);
9211
- renderAllAgentWidgets(state2, api.runtime, console.log);
9742
+ start: () => {
9743
+ if (!shouldStartRuntimeServices())
9744
+ return;
9745
+ startProvisioningServices(api, state2).catch((err) => {
9746
+ console.warn("[agentlife] provisioning service failed:", err?.message ?? err);
9747
+ });
9212
9748
  }
9213
9749
  });
9214
9750
  registerConfigOptimizer(api, state2);
9215
9751
  registerPairingServices(api);
9216
- stopCloudflared = startCloudflaredSupervisor();
9752
+ api.registerService({
9753
+ id: "agentlife-cloudflared-supervisor",
9754
+ start: () => {
9755
+ if (!shouldStartRuntimeServices())
9756
+ return;
9757
+ if (!stopCloudflared) {
9758
+ stopCloudflared = startCloudflaredSupervisor();
9759
+ }
9760
+ },
9761
+ stop: () => {
9762
+ if (stopCloudflared) {
9763
+ stopCloudflared();
9764
+ stopCloudflared = null;
9765
+ }
9766
+ }
9767
+ });
9217
9768
  api.registerService({
9218
9769
  id: "agentlife-shutdown",
9219
9770
  start: () => {},
@@ -9238,7 +9789,15 @@ function register(api) {
9238
9789
  closeAllDbs(state2);
9239
9790
  }
9240
9791
  });
9241
- initFollowupSystem(api, state2);
9792
+ api.registerService({
9793
+ id: "agentlife-followups",
9794
+ start: () => {
9795
+ if (!shouldStartRuntimeServices())
9796
+ return;
9797
+ initFollowupSystem(api, state2);
9798
+ },
9799
+ stop: () => stopFollowupSystem(state2)
9800
+ });
9242
9801
  registerPromptStateHook(api, state2);
9243
9802
  registerWidgetPushTool(api, state2);
9244
9803
  registerActivityHooks(api, state2);
@@ -9254,7 +9813,7 @@ function register(api) {
9254
9813
  registerProvidersGateway(api, state2);
9255
9814
  registerModelsConfigGateway(api);
9256
9815
  registerWebApp(api);
9257
- const notifyConfigPath = path18.join(fallbackDir, "notification-config.json");
9816
+ const notifyConfigPath = path20.join(fallbackDir, "notification-config.json");
9258
9817
  api.registerGatewayMethod("agentlife.notifications.register", ({ params, respond }) => {
9259
9818
  const serverUrl = typeof params?.serverUrl === "string" ? params.serverUrl.trim() : "";
9260
9819
  const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
@@ -9262,9 +9821,9 @@ function register(api) {
9262
9821
  return respond(false, { error: "missing serverUrl or apiKey" });
9263
9822
  }
9264
9823
  try {
9265
- const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync8 } = __require("node:fs");
9266
- mkdirSync8(path18.dirname(notifyConfigPath), { recursive: true });
9267
- writeFileSync11(notifyConfigPath, JSON.stringify({ serverUrl, apiKey }));
9824
+ const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync10 } = __require("node:fs");
9825
+ mkdirSync10(path20.dirname(notifyConfigPath), { recursive: true });
9826
+ writeFileSync12(notifyConfigPath, JSON.stringify({ serverUrl, apiKey }));
9268
9827
  } catch {}
9269
9828
  respond(true, { registered: true });
9270
9829
  }, { scope: "operator.write" });