agentlife 2.6.28 → 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 (143) hide show
  1. package/dist/index.js +815 -272
  2. package/package.json +16 -6
  3. package/web-build/6e23e5428398b92da386.wasm +0 -0
  4. package/web-build/auth-callback.html +40 -0
  5. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_action_plan.xml +10 -0
  6. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_add.xml +9 -0
  7. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_alarm.xml +9 -0
  8. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_amazon.xml +9 -0
  9. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_apple.xml +12 -0
  10. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_clockwise.xml +9 -0
  11. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_counter_clockwise.xml +9 -0
  12. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_down_up.xml +9 -0
  13. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_right.xml +9 -0
  14. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrow_square_out.xml +9 -0
  15. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_arrows_out.xml +9 -0
  16. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_audible.xml +9 -0
  17. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_auto_send.xml +9 -0
  18. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_back.xml +9 -0
  19. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_barcode.xml +9 -0
  20. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell.xml +9 -0
  21. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bell_ringing.xml +9 -0
  22. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_binoculars.xml +9 -0
  23. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_brain.xml +9 -0
  24. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_bulb.xml +9 -0
  25. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar.xml +9 -0
  26. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_blank.xml +9 -0
  27. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_check.xml +9 -0
  28. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_calendar_dots.xml +9 -0
  29. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_down.xml +9 -0
  30. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_double_up.xml +9 -0
  31. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_down.xml +9 -0
  32. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_right.xml +9 -0
  33. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_caret_up.xml +9 -0
  34. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat.xml +9 -0
  35. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle.xml +9 -0
  36. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_circle_text.xml +9 -0
  37. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_chat_teardrop.xml +9 -0
  38. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_check_simple.xml +9 -0
  39. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_child.xml +9 -0
  40. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_counter_clockwise.xml +9 -0
  41. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_clock_simple.xml +9 -0
  42. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_close.xml +9 -0
  43. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_comment.xml +9 -0
  44. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_community.xml +9 -0
  45. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_copy_simple.xml +9 -0
  46. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_credit_card.xml +9 -0
  47. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_decision_framework.xml +9 -0
  48. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_diff.xml +9 -0
  49. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_dots_three.xml +9 -0
  50. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_edit.xml +9 -0
  51. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_experiment.xml +9 -0
  52. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_facebook.xml +9 -0
  53. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_faders.xml +9 -0
  54. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_dotted.xml +9 -0
  55. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_file_text.xml +9 -0
  56. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_filter.xml +9 -0
  57. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_flask.xml +9 -0
  58. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_floppy_disk.xml +9 -0
  59. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_open.xml +9 -0
  60. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_folder_simple.xml +9 -0
  61. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_git_merge.xml +9 -0
  62. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google.xml +35 -0
  63. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_google_play.xml +22 -0
  64. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_habit_tracker.xml +9 -0
  65. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_1.xml +9 -0
  66. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_2.xml +9 -0
  67. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_3.xml +9 -0
  68. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_heading_4.xml +9 -0
  69. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_hide_pass.xml +9 -0
  70. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_info_simple.xml +9 -0
  71. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_instagram.xml +9 -0
  72. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_keyboard.xml +9 -0
  73. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lightning.xml +9 -0
  74. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_linkedin.xml +9 -0
  75. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_lock.xml +9 -0
  76. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_logo.xml +12 -0
  77. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_map_pin_simple.xml +9 -0
  78. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_media.xml +9 -0
  79. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_menu.xml +9 -0
  80. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_microphone_simple.xml +9 -0
  81. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_more_vert.xml +9 -0
  82. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paint_bucket.xml +9 -0
  83. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paper_plane_right.xml +9 -0
  84. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_paperclip.xml +9 -0
  85. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_parent.xml +9 -0
  86. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_pause_simple.xml +9 -0
  87. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_play_simple.xml +9 -0
  88. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_plus_circle.xml +9 -0
  89. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin.xml +9 -0
  90. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_push_pin_slash.xml +9 -0
  91. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_question.xml +9 -0
  92. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_record.xml +9 -0
  93. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_rocket.xml +9 -0
  94. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_scales.xml +9 -0
  95. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_search.xml +9 -0
  96. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_settings.xml +9 -0
  97. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_share_network.xml +9 -0
  98. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_show_pass.xml +9 -0
  99. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sign_out.xml +9 -0
  100. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_smiley.xml +9 -0
  101. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparkles.xml +9 -0
  102. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_sparks.xml +9 -0
  103. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_speaker_high.xml +9 -0
  104. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_spotify.xml +9 -0
  105. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_star.xml +9 -0
  106. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_circle.xml +9 -0
  107. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_stop_simple.xml +9 -0
  108. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_swap.xml +9 -0
  109. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tag_simple.xml +9 -0
  110. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_down.xml +9 -0
  111. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_thumbs_up.xml +9 -0
  112. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tiktok.xml +9 -0
  113. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_complete.xml +9 -0
  114. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_timer_simple.xml +9 -0
  115. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_translate.xml +9 -0
  116. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_trash_simple.xml +9 -0
  117. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_tree_structure.xml +9 -0
  118. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_twitch.xml +9 -0
  119. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_upload.xml +9 -0
  120. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_user_circle.xml +9 -0
  121. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_warning.xml +9 -0
  122. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_web.xml +9 -0
  123. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_wrench.xml +9 -0
  124. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x.xml +9 -0
  125. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_x_circle.xml +9 -0
  126. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube.xml +9 -0
  127. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/ic_youtube_music.xml +9 -0
  128. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/drawable/tenazitas_logo.xml +27 -0
  129. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Caveat-Medium.ttf +0 -0
  130. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Medium.ttf +0 -0
  131. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-Regular.ttf +0 -0
  132. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/Geist-SemiBold.ttf +0 -0
  133. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Medium.ttf +0 -0
  134. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/JetBrainsMono-Regular.ttf +0 -0
  135. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Italic.ttf +0 -0
  136. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-Regular.ttf +0 -0
  137. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/font/SourceSerif4-SemiBold.ttf +0 -0
  138. package/web-build/composeResources/org.acv.agentlife.composeapp.generated.resources/values/strings.commonMain.cvr +2787 -0
  139. package/web-build/f722462c344efeb18c2f.wasm +0 -0
  140. package/web-build/index.html +23 -0
  141. package/web-build/webApp.js +3 -0
  142. package/web-build/webApp.js.LICENSE.txt +35 -0
  143. package/web-build/webApp.js.map +1 -0
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@ import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
4
  // index.ts
5
- import { homedir as homedir15 } from "node:os";
6
- import * as path18 from "node:path";
7
- import { existsSync as existsSync7 } from "node:fs";
5
+ import { homedir as homedir17 } from "node:os";
6
+ import * as path20 from "node:path";
7
+ import { existsSync as existsSync8 } from "node:fs";
8
8
 
9
9
  // db.ts
10
10
  import * as fsSync from "node:fs";
@@ -988,7 +988,7 @@ If you produce a followup whose residue (digits and parameter values stripped) m
988
988
 
989
989
  ### Guided mode — input surfaces
990
990
 
991
- When you need structured input, push a surface with \`input\` in the header. It renders in the input bar, not on the dashboard. Structure: \`surface <id> input\` → \`card\` → \`column\` → h3 question + 2+ buttons + optional \`textfield placeholder="..."\`.
991
+ When you need structured input, push a surface with \`input\` in the header. It renders in the input bar, not on the dashboard. Structure: \`surface <id> input\` → \`card\` → \`column\` → h3 question + 2+ buttons.
992
992
 
993
993
  **Flow:**
994
994
  1. User clicks a button on a widget → \`[action:*]\`.
@@ -1581,6 +1581,82 @@ ${userSection}`;
1581
1581
  }
1582
1582
 
1583
1583
  // provisioning.ts
1584
+ function providerFromModel(model) {
1585
+ const slash = model.indexOf("/");
1586
+ if (slash <= 0)
1587
+ return null;
1588
+ return model.slice(0, slash).trim() || null;
1589
+ }
1590
+ function configuredProvidersFromConfig(cfg) {
1591
+ const profiles = cfg?.auth?.profiles;
1592
+ if (!profiles || typeof profiles !== "object")
1593
+ return [];
1594
+ const out = [];
1595
+ for (const entry of Object.values(profiles)) {
1596
+ const provider = entry?.provider;
1597
+ if (typeof provider === "string" && provider.trim() && !out.includes(provider.trim())) {
1598
+ out.push(provider.trim());
1599
+ }
1600
+ }
1601
+ return out;
1602
+ }
1603
+ function hasGatewayDefaultModel(cfg) {
1604
+ const modelCfg = cfg?.agents?.defaults?.model;
1605
+ if (!modelCfg || typeof modelCfg !== "object")
1606
+ return false;
1607
+ if (typeof modelCfg.primary === "string" && modelCfg.primary.trim())
1608
+ return true;
1609
+ return Array.isArray(modelCfg.fallbacks) && modelCfg.fallbacks.some((m) => typeof m === "string" && m.trim());
1610
+ }
1611
+ function parseJsonArrayFromStdout(stdout) {
1612
+ const raw = stdout.trim();
1613
+ const jsonStart = raw.indexOf("[");
1614
+ if (jsonStart < 0)
1615
+ return [];
1616
+ try {
1617
+ const parsed = JSON.parse(raw.slice(jsonStart));
1618
+ return Array.isArray(parsed) ? parsed : [];
1619
+ } catch {
1620
+ return [];
1621
+ }
1622
+ }
1623
+ async function resolveProvisionedAgentModel(cfg, currentList, runtime, log) {
1624
+ if (hasGatewayDefaultModel(cfg))
1625
+ return null;
1626
+ const configuredProviders = configuredProvidersFromConfig(cfg);
1627
+ if (configuredProviders.length === 0)
1628
+ return null;
1629
+ const configured = new Set(configuredProviders);
1630
+ const provisionedIds = new Set(PROVISIONED_AGENTS.map((a) => a.id));
1631
+ for (const agent of currentList) {
1632
+ if (!agent || provisionedIds.has(agent.id))
1633
+ continue;
1634
+ const model = typeof agent.model === "string" ? agent.model.trim() : "";
1635
+ const provider = model ? providerFromModel(model) : null;
1636
+ if (model && provider && configured.has(provider)) {
1637
+ return model;
1638
+ }
1639
+ }
1640
+ const run = runtime.system?.runCommandWithTimeout;
1641
+ if (!run)
1642
+ return null;
1643
+ try {
1644
+ const result = await run(["openclaw", "infer", "model", "providers", "--json"], { timeoutMs: 60000 });
1645
+ if ((result?.code ?? 0) !== 0)
1646
+ return null;
1647
+ const catalog = parseJsonArrayFromStdout(result?.stdout ?? "");
1648
+ for (const provider of configuredProviders) {
1649
+ const entry = catalog.find((p) => p?.provider === provider);
1650
+ const defaults = Array.isArray(entry?.defaults) ? entry.defaults : [];
1651
+ const firstDefault = defaults.find((m) => typeof m === "string" && m.trim());
1652
+ if (firstDefault)
1653
+ return `${provider}/${firstDefault.trim()}`;
1654
+ }
1655
+ } catch (err) {
1656
+ log(`[agentlife] provisioned agent model discovery skipped: ${err?.message ?? err}`);
1657
+ }
1658
+ return null;
1659
+ }
1584
1660
  var PROVISIONED_AGENTS = [
1585
1661
  {
1586
1662
  id: "agentlife",
@@ -1671,6 +1747,7 @@ function scheduleEnrichment(state, runtime, targets, log) {
1671
1747
  async function provisionAgents(state, cfg, runtime, log) {
1672
1748
  const home = os.homedir();
1673
1749
  const currentList = [...cfg.agents?.list ?? []];
1750
+ const provisionedModel = await resolveProvisionedAgentModel(cfg, currentList, runtime, log);
1674
1751
  let configChanged = false;
1675
1752
  const newlyProvisioned = [];
1676
1753
  for (const agent of PROVISIONED_AGENTS) {
@@ -1710,12 +1787,15 @@ async function provisionAgents(state, cfg, runtime, log) {
1710
1787
  name: agent.name,
1711
1788
  workspace: workspaceDir
1712
1789
  };
1713
- if (agent.isDefault)
1790
+ const userOwnsDefault = currentList.some((a) => a?.default === true);
1791
+ if (agent.isDefault && !userOwnsDefault)
1714
1792
  entry.default = true;
1715
1793
  if (agent.subagents)
1716
1794
  entry.subagents = agent.subagents;
1717
1795
  if (agent.tools)
1718
1796
  entry.tools = agent.tools;
1797
+ if (provisionedModel)
1798
+ entry.model = provisionedModel;
1719
1799
  if (agent.isDefault) {
1720
1800
  currentList.unshift(entry);
1721
1801
  } else {
@@ -1741,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
  }
@@ -2085,7 +2178,6 @@ var KNOWN_BODY_KEYWORDS = new Set([
2085
2178
  "button",
2086
2179
  "image",
2087
2180
  "icon",
2088
- "textfield",
2089
2181
  "checkbox",
2090
2182
  "sparkline",
2091
2183
  "progress",
@@ -2117,7 +2209,6 @@ var INPUT_SURFACE_ACTIONS = new Set([
2117
2209
  var TEXT_LEVELS = new Set(["h1", "h2", "h3", "h4", "h5", "body", "caption"]);
2118
2210
  var TEXT_HINTS = new Set(["icon", "avatar", "smallfeature", "mediumfeature", "largefeature", "header"]);
2119
2211
  var IMG_FITS = new Set(["contain", "cover", "fill", "none", "scale-down"]);
2120
- var TF_TYPES = new Set(["email", "shorttext", "number", "phone", "longtext", "date", "obscured"]);
2121
2212
  var ALIGN_VALUES = new Set(["start", "center", "end", "stretch"]);
2122
2213
  var DISTRIBUTE_VALUES = new Set(["start", "center", "end", "spacebetween", "spacearound", "spaceevenly"]);
2123
2214
  var TREND_VALUES = new Set(["up", "down", "flat"]);
@@ -2172,14 +2263,6 @@ var COMPONENT_CATALOG = {
2172
2263
  }
2173
2264
  },
2174
2265
  icon: { positionalTokens: new Set, positionalFree: true, attrs: {} },
2175
- textfield: {
2176
- positionalTokens: new Set,
2177
- positionalFree: true,
2178
- attrs: {
2179
- type: { kind: "enum", values: TF_TYPES },
2180
- placeholder: { kind: "free" }
2181
- }
2182
- },
2183
2266
  checkbox: {
2184
2267
  positionalTokens: new Set(["checked"]),
2185
2268
  positionalFree: true,
@@ -3473,9 +3556,9 @@ Each turn:
3473
3556
  2. Not enough → \`agentlife_push\` the SAME surfaceId \`welcome-input\` with:
3474
3557
  - h3 question generated from what the user just said
3475
3558
  - 2–4 multichoice buttons \`action=choice\`, options derived from the user's own words (NEVER hardcoded domain lists)
3476
- - \`textfield placeholder="Something else…"\` escape hatch
3477
3559
  - updated \`context: {"phase":"welcome","turn":N+1,"answers":[...]}\`
3478
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.
3479
3562
  Respond \`done\`.
3480
3563
  3. Enough → create the generalist and hand off the dashboard:
3481
3564
  - Workspace under \`$HOME/.openclaw/workspace-{id}\` (id = slugified short name or "me")
@@ -3505,7 +3588,6 @@ function renderWelcomeWidget(state, log) {
3505
3588
  ` column`,
3506
3589
  ` text "What do you want to track or improve?" h3`,
3507
3590
  ` text "One line. Anything — meals, money, a project, a habit." body`,
3508
- ` textfield placeholder="Type anything…"`,
3509
3591
  `goal: Onboard the user to their first agent via a guided interview`,
3510
3592
  `followup: +30m "If unanswered, replace the question with a gentler prompt."`,
3511
3593
  `context: {"phase":"welcome","turn":1,"answers":[]}`
@@ -3533,7 +3615,7 @@ function deleteWelcomeWidget(state, log) {
3533
3615
  }
3534
3616
 
3535
3617
  // services/surfaces-init.ts
3536
- import * as path5 from "node:path";
3618
+ import * as path6 from "node:path";
3537
3619
 
3538
3620
  // storage/reconciler.ts
3539
3621
  var defaultLogger = {
@@ -3781,23 +3863,45 @@ function dispatchRehydrate(state, runtime, log = console.log) {
3781
3863
  log(`[agentlife:rehydrate] dispatched rehydrate to ${orphaned.length} orphaned agent(s): ${orphaned.join(", ")}`);
3782
3864
  }
3783
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
+
3784
3877
  // services/surfaces-init.ts
3785
3878
  function registerSurfacesService(api, state) {
3786
3879
  api.registerService({
3787
3880
  id: "agentlife-surfaces",
3788
3881
  start: async (ctx) => {
3789
- 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
+ }
3790
3895
  state.agentlifeStateDir = agentlifeDir;
3791
- state.registryFilePath = path5.join(agentlifeDir, "agent-registry.json");
3792
- state.dbBaseDir = path5.join(agentlifeDir, "db");
3793
- state.historyDbPath = path5.join(agentlifeDir, "agentlife.db");
3896
+ state.registryFilePath = path6.join(agentlifeDir, "agent-registry.json");
3897
+ state.dbBaseDir = path6.join(agentlifeDir, "db");
3898
+ state.historyDbPath = nextHistoryDbPath;
3794
3899
  const db = getOrCreateHistoryDb(state);
3795
3900
  if (!state.surfaceIndex)
3796
3901
  state.surfaceIndex = new SurfaceIndex(db);
3797
3902
  if (!state.followupQueue)
3798
3903
  state.followupQueue = new FollowupQueue(db);
3799
- if (!state.fileSurfaceStorage)
3800
- state.fileSurfaceStorage = new FileSurfaceStorage;
3904
+ state.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: ctx.stateDir });
3801
3905
  await loadRegistryFromDisk(state);
3802
3906
  try {
3803
3907
  const knownAgents = collectKnownAgents(api.runtime);
@@ -3847,11 +3951,11 @@ function collectKnownAgents(runtime) {
3847
3951
 
3848
3952
  // services/config-optimizer.ts
3849
3953
  import * as fs5 from "node:fs/promises";
3850
- import * as path6 from "node:path";
3954
+ import * as path7 from "node:path";
3851
3955
  import * as os2 from "node:os";
3852
3956
  var TARGET_MAX_PING_PONG = 1;
3853
3957
  var TARGET_MAX_CONCURRENT = 10;
3854
- 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");
3855
3959
  async function readRawConfigFromDisk() {
3856
3960
  const raw = await fs5.readFile(CONFIG_PATH, "utf-8");
3857
3961
  return JSON.parse(raw);
@@ -3887,8 +3991,10 @@ function registerConfigOptimizer(api, _state) {
3887
3991
  api.registerService({
3888
3992
  id: "agentlife-config-optimizer",
3889
3993
  start: async (ctx) => {
3890
- const configBackupDir = path6.join(ctx.stateDir, "agentlife");
3891
- 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");
3892
3998
  const cfg = api.runtime.config.loadConfig();
3893
3999
  const currentPPT = cfg.session?.agentToAgent?.maxPingPongTurns;
3894
4000
  const currentMC = cfg.agents?.defaults?.maxConcurrent;
@@ -3899,10 +4005,16 @@ function registerConfigOptimizer(api, _state) {
3899
4005
  return;
3900
4006
  }
3901
4007
  await fs5.mkdir(configBackupDir, { recursive: true });
3902
- await fs5.writeFile(configBackupPath, JSON.stringify({
3903
- maxPingPongTurns: currentPPT ?? null,
3904
- maxConcurrent: currentMC ?? null
3905
- }));
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
+ `);
3906
4018
  const fileCfg = await readRawConfigFromDisk();
3907
4019
  if (needsPPT) {
3908
4020
  fileCfg.session ??= {};
@@ -3924,13 +4036,196 @@ function registerConfigOptimizer(api, _state) {
3924
4036
  },
3925
4037
  stop: async (ctx) => {
3926
4038
  try {
3927
- const backupPath = path6.join(ctx.stateDir, "agentlife", "config-backup.json");
4039
+ const backupPath = path7.join(ctx.stateDir, "agentlife", "config-backup.json");
3928
4040
  await restoreConfigFromBackup(api, backupPath);
3929
4041
  } catch {}
3930
4042
  }
3931
4043
  });
3932
4044
  }
3933
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
+
3934
4229
  // services/session-reconcile.ts
3935
4230
  function modelMatches(configured, sessionProvider, sessionModel) {
3936
4231
  if (!sessionModel)
@@ -4093,7 +4388,7 @@ async function reconcileInternalSessions(state, createdInternalSessions) {
4093
4388
  }
4094
4389
 
4095
4390
  // services/supervisor-cleanup.ts
4096
- import * as fs6 from "node:fs";
4391
+ import * as fs7 from "node:fs";
4097
4392
  var STALE_SUPERVISOR_SURFACE = "supervisor-daily";
4098
4393
  var STALE_SUPERVISOR_DIRECT_SESSION = "agent:supervisor:agentlife:direct:operator";
4099
4394
  async function cleanupSupervisorLegacyState(state) {
@@ -4106,7 +4401,7 @@ async function cleanupSupervisorLegacyState(state) {
4106
4401
  removeFollowup(state, STALE_SUPERVISOR_SURFACE);
4107
4402
  if (view2 && !view2.isTransient && view2.path) {
4108
4403
  try {
4109
- fs6.unlinkSync(view2.path);
4404
+ fs7.unlinkSync(view2.path);
4110
4405
  } catch {}
4111
4406
  }
4112
4407
  state.surfaceIndex?.delete(STALE_SUPERVISOR_SURFACE);
@@ -4288,6 +4583,8 @@ function startObservabilityService(state) {
4288
4583
 
4289
4584
  // hooks/activity-hooks.ts
4290
4585
  import * as fsSync2 from "node:fs";
4586
+ import * as nodePath from "node:path";
4587
+ import * as nodeOs from "node:os";
4291
4588
 
4292
4589
  // usage.ts
4293
4590
  var MODEL_COSTS = {
@@ -4936,6 +5233,62 @@ ${parts.join(`
4936
5233
  }
4937
5234
  var QUALITY_CHECK_MAX_WAIT_MS = 5000;
4938
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
+ }
4939
5292
  function processPendingQualityChecks(state) {
4940
5293
  if (state.disabled || !state.historyDb)
4941
5294
  return 0;
@@ -4977,6 +5330,21 @@ function processPendingQualityChecks(state) {
4977
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);
4978
5331
  if ((pushed?.c ?? 0) > 0)
4979
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
+ }
4980
5348
  recordActivity(state, "quality_warning", row.sessionKey, row.agentId, {
4981
5349
  runId: row.runId,
4982
5350
  data: JSON.stringify({ issue: "no_widget_pushed", trigger: row.trigger })
@@ -5075,7 +5443,7 @@ function rescueOrphanedSurfaces(state, agentId, error) {
5075
5443
  }
5076
5444
 
5077
5445
  // services/escalation.ts
5078
- import * as fs7 from "node:fs";
5446
+ import * as fs8 from "node:fs";
5079
5447
  var PATHOLOGY2 = process.env.PATHOLOGY_TEST_MODE === "1";
5080
5448
  var ESCALATION_REVIEW_THRESHOLD_MS = PATHOLOGY2 ? 2 * 60 * 1000 : 48 * 60 * 60 * 1000;
5081
5449
  var TREND_WINDOW_DAYS = 7;
@@ -5141,7 +5509,7 @@ function clearEscalationSurface(state, agentId) {
5141
5509
  const view2 = getSurface(state, surfaceId);
5142
5510
  if (view2 && !view2.isTransient && view2.path) {
5143
5511
  try {
5144
- fs7.unlinkSync(view2.path);
5512
+ fs8.unlinkSync(view2.path);
5145
5513
  } catch {}
5146
5514
  }
5147
5515
  state.surfaceIndex.delete(surfaceId);
@@ -5385,31 +5753,32 @@ function startDailySweepService(state) {
5385
5753
  import * as crypto4 from "node:crypto";
5386
5754
  import * as fsSync3 from "node:fs";
5387
5755
  import { createRequire as createRequire3 } from "node:module";
5388
- import * as os6 from "node:os";
5389
- import * as path10 from "node:path";
5756
+ import * as os7 from "node:os";
5757
+ import * as path12 from "node:path";
5390
5758
 
5391
5759
  // gateway/web-app.ts
5392
5760
  import * as crypto3 from "node:crypto";
5393
- import * as os5 from "node:os";
5394
- import * as path9 from "node:path";
5395
- import * as fs10 from "node:fs";
5761
+ import * as os6 from "node:os";
5762
+ import * as path11 from "node:path";
5763
+ import * as fs11 from "node:fs";
5396
5764
 
5397
5765
  // services/pairing-access-token.ts
5398
5766
  import * as crypto from "node:crypto";
5399
- import * as fs8 from "node:fs";
5400
- import * as os3 from "node:os";
5401
- import * as path7 from "node:path";
5767
+ import * as fs9 from "node:fs";
5768
+ import * as os4 from "node:os";
5769
+ import * as path9 from "node:path";
5402
5770
  var cachedToken = null;
5403
5771
  function pairingAccessPath() {
5404
- 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");
5405
5774
  }
5406
5775
  function loadOrCreatePairingAccessToken() {
5407
5776
  if (cachedToken)
5408
5777
  return cachedToken;
5409
5778
  const filePath = pairingAccessPath();
5410
5779
  try {
5411
- if (fs8.existsSync(filePath)) {
5412
- const raw = fs8.readFileSync(filePath, "utf-8");
5780
+ if (fs9.existsSync(filePath)) {
5781
+ const raw = fs9.readFileSync(filePath, "utf-8");
5413
5782
  const obj = JSON.parse(raw);
5414
5783
  const token2 = typeof obj?.token === "string" ? obj.token : null;
5415
5784
  if (token2 && token2.length >= 32) {
@@ -5418,12 +5787,12 @@ function loadOrCreatePairingAccessToken() {
5418
5787
  }
5419
5788
  }
5420
5789
  const token = crypto.randomBytes(32).toString("base64url");
5421
- const dir = path7.dirname(filePath);
5422
- if (!fs8.existsSync(dir))
5423
- fs8.mkdirSync(dir, { recursive: true, mode: 448 });
5424
- fs8.writeFileSync(filePath, JSON.stringify({ token, createdAtMs: Date.now() }, null, 2), { mode: 384 });
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 });
5425
5794
  try {
5426
- fs8.chmodSync(filePath, 384);
5795
+ fs9.chmodSync(filePath, 384);
5427
5796
  } catch {}
5428
5797
  cachedToken = token;
5429
5798
  return token;
@@ -5436,25 +5805,28 @@ function loadOrCreatePairingAccessToken() {
5436
5805
  // services/cloudflared-supervisor.ts
5437
5806
  import { spawn, spawnSync } from "node:child_process";
5438
5807
  import * as crypto2 from "node:crypto";
5439
- import * as fs9 from "node:fs";
5440
- import * as os4 from "node:os";
5441
- import * as path8 from "node:path";
5442
- var AGENTLIFE_DIR = path8.join(os4.homedir(), ".openclaw", "agentlife");
5443
- var TUNNEL_FILE = path8.join(AGENTLIFE_DIR, "tunnel.json");
5444
- var BIN_DIR = path8.join(AGENTLIFE_DIR, "bin");
5445
- var PAIR_REQUEST_MARKER = path8.join(AGENTLIFE_DIR, "pair-requested");
5446
- var IDENTITY_DIR = path8.join(os4.homedir(), ".agentlife");
5447
- var DEVICE_FILE = path8.join(IDENTITY_DIR, "device.json");
5448
- var LEGACY_DEVICE_FILE = path8.join(AGENTLIFE_DIR, "device.json");
5449
- var CLOUDFLARED_BIN = os4.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
5450
- var CLOUDFLARED_PATH = path8.join(BIN_DIR, CLOUDFLARED_BIN);
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);
5451
5823
  var API_BASE = process.env.AGENTLIFE_API_BASE || "https://api.agentlife.app";
5452
5824
  var RESTART_DELAY_MS = 5000;
5453
5825
  var STABLE_RUNTIME_MS = 60000;
5454
5826
  function createInitialState() {
5455
5827
  let resolver;
5456
- const promise = new Promise((resolve) => {
5457
- resolver = resolve;
5828
+ const promise = new Promise((resolve2) => {
5829
+ resolver = resolve2;
5458
5830
  });
5459
5831
  return {
5460
5832
  tunnelInfo: null,
@@ -5475,7 +5847,7 @@ function getTunnelInfo() {
5475
5847
  function awaitTunnelReady(timeoutMs) {
5476
5848
  return Promise.race([
5477
5849
  state.readyPromise,
5478
- new Promise((resolve) => setTimeout(() => resolve(null), timeoutMs))
5850
+ new Promise((resolve2) => setTimeout(() => resolve2(null), timeoutMs))
5479
5851
  ]);
5480
5852
  }
5481
5853
  function startCloudflaredSupervisor() {
@@ -5567,20 +5939,20 @@ async function doBootstrap() {
5567
5939
  return tunnelInfo;
5568
5940
  }
5569
5941
  function isPairRequested() {
5570
- return fs9.existsSync(PAIR_REQUEST_MARKER);
5942
+ return fs10.existsSync(PAIR_REQUEST_MARKER);
5571
5943
  }
5572
5944
  function requestPair() {
5573
5945
  try {
5574
- if (!fs9.existsSync(AGENTLIFE_DIR))
5575
- fs9.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5576
- fs9.writeFileSync(PAIR_REQUEST_MARKER, String(Date.now()), { mode: 384 });
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 });
5577
5949
  } catch (err) {
5578
5950
  console.warn(`[cloudflared-supervisor] failed to write pair-request marker: ${err?.message ?? err}`);
5579
5951
  }
5580
5952
  }
5581
5953
  function clearPairRequest() {
5582
5954
  try {
5583
- fs9.unlinkSync(PAIR_REQUEST_MARKER);
5955
+ fs10.unlinkSync(PAIR_REQUEST_MARKER);
5584
5956
  } catch {}
5585
5957
  }
5586
5958
  function parseRetryAfter(value) {
@@ -5604,18 +5976,18 @@ function scheduleProvisionRetry(delayMs) {
5604
5976
  }, delayMs);
5605
5977
  }
5606
5978
  function ensureDirs() {
5607
- if (!fs9.existsSync(AGENTLIFE_DIR))
5608
- fs9.mkdirSync(AGENTLIFE_DIR, { recursive: true, mode: 448 });
5609
- if (!fs9.existsSync(BIN_DIR))
5610
- fs9.mkdirSync(BIN_DIR, { recursive: true, mode: 448 });
5611
- if (!fs9.existsSync(IDENTITY_DIR))
5612
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
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 });
5613
5985
  }
5614
5986
  function readIdentityFile(filePath) {
5615
- if (!fs9.existsSync(filePath))
5987
+ if (!fs10.existsSync(filePath))
5616
5988
  return null;
5617
5989
  try {
5618
- const raw = fs9.readFileSync(filePath, "utf-8");
5990
+ const raw = fs10.readFileSync(filePath, "utf-8");
5619
5991
  const parsed = JSON.parse(raw);
5620
5992
  if (typeof parsed.deviceId === "string" && typeof parsed.deviceSecret === "string") {
5621
5993
  return { deviceId: parsed.deviceId, deviceSecret: parsed.deviceSecret };
@@ -5633,14 +6005,14 @@ function loadDeviceIdentity() {
5633
6005
  if (!legacy)
5634
6006
  return null;
5635
6007
  try {
5636
- if (!fs9.existsSync(IDENTITY_DIR))
5637
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5638
- fs9.writeFileSync(DEVICE_FILE, JSON.stringify(legacy, null, 2), { mode: 384 });
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 });
5639
6011
  try {
5640
- fs9.chmodSync(DEVICE_FILE, 384);
6012
+ fs10.chmodSync(DEVICE_FILE, 384);
5641
6013
  } catch {}
5642
6014
  try {
5643
- fs9.unlinkSync(LEGACY_DEVICE_FILE);
6015
+ fs10.unlinkSync(LEGACY_DEVICE_FILE);
5644
6016
  } catch {}
5645
6017
  console.log(`[cloudflared-supervisor] migrated device.json to ${DEVICE_FILE}`);
5646
6018
  } catch (err) {
@@ -5652,25 +6024,25 @@ function loadOrCreateDeviceIdentity() {
5652
6024
  const existing = loadDeviceIdentity();
5653
6025
  if (existing)
5654
6026
  return existing;
5655
- if (!fs9.existsSync(IDENTITY_DIR))
5656
- fs9.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
6027
+ if (!fs10.existsSync(IDENTITY_DIR))
6028
+ fs10.mkdirSync(IDENTITY_DIR, { recursive: true, mode: 448 });
5657
6029
  const identity = {
5658
6030
  deviceId: crypto2.randomUUID(),
5659
6031
  deviceSecret: crypto2.randomBytes(32).toString("base64url")
5660
6032
  };
5661
- fs9.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
6033
+ fs10.writeFileSync(DEVICE_FILE, JSON.stringify(identity, null, 2), { mode: 384 });
5662
6034
  try {
5663
- fs9.chmodSync(DEVICE_FILE, 384);
6035
+ fs10.chmodSync(DEVICE_FILE, 384);
5664
6036
  } catch {}
5665
6037
  console.log(`[cloudflared-supervisor] generated new device identity (deviceId=${identity.deviceId})`);
5666
6038
  return identity;
5667
6039
  }
5668
6040
  var AGENTLIFE_API_BASE = API_BASE;
5669
6041
  function loadCachedTunnel() {
5670
- if (!fs9.existsSync(TUNNEL_FILE))
6042
+ if (!fs10.existsSync(TUNNEL_FILE))
5671
6043
  return null;
5672
6044
  try {
5673
- const raw = fs9.readFileSync(TUNNEL_FILE, "utf-8");
6045
+ const raw = fs10.readFileSync(TUNNEL_FILE, "utf-8");
5674
6046
  const parsed = JSON.parse(raw);
5675
6047
  if (typeof parsed.subdomain === "string" && typeof parsed.hostname === "string" && typeof parsed.tunnelUrl === "string" && typeof parsed.tunnelToken === "string" && typeof parsed.provisionedAt === "number") {
5676
6048
  return parsed;
@@ -5679,9 +6051,9 @@ function loadCachedTunnel() {
5679
6051
  return null;
5680
6052
  }
5681
6053
  function persistTunnel(info) {
5682
- fs9.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
6054
+ fs10.writeFileSync(TUNNEL_FILE, JSON.stringify(info, null, 2), { mode: 384 });
5683
6055
  try {
5684
- fs9.chmodSync(TUNNEL_FILE, 384);
6056
+ fs10.chmodSync(TUNNEL_FILE, 384);
5685
6057
  } catch {}
5686
6058
  }
5687
6059
  async function provisionTunnel(identity) {
@@ -5734,14 +6106,14 @@ async function provisionTunnel(identity) {
5734
6106
  }
5735
6107
  }
5736
6108
  function ensureCloudflaredBinary() {
5737
- if (fs9.existsSync(CLOUDFLARED_PATH)) {
6109
+ if (fs10.existsSync(CLOUDFLARED_PATH)) {
5738
6110
  try {
5739
- fs9.accessSync(CLOUDFLARED_PATH, fs9.constants.X_OK);
6111
+ fs10.accessSync(CLOUDFLARED_PATH, fs10.constants.X_OK);
5740
6112
  return CLOUDFLARED_PATH;
5741
6113
  } catch {}
5742
6114
  }
5743
- const platform2 = os4.platform();
5744
- const arch2 = os4.arch();
6115
+ const platform2 = os5.platform();
6116
+ const arch2 = os5.arch();
5745
6117
  const release = detectCloudflaredRelease(platform2, arch2);
5746
6118
  if (!release) {
5747
6119
  console.warn(`[cloudflared-supervisor] unsupported platform: ${platform2}/${arch2}`);
@@ -5754,28 +6126,28 @@ function ensureCloudflaredBinary() {
5754
6126
  if (release.kind === "tgz") {
5755
6127
  const extracted = extractTgzCloudflared(release.tempPath, BIN_DIR);
5756
6128
  try {
5757
- fs9.unlinkSync(release.tempPath);
6129
+ fs10.unlinkSync(release.tempPath);
5758
6130
  } catch {}
5759
6131
  if (!extracted)
5760
6132
  return null;
5761
6133
  } else {
5762
6134
  try {
5763
- fs9.renameSync(release.tempPath, CLOUDFLARED_PATH);
6135
+ fs10.renameSync(release.tempPath, CLOUDFLARED_PATH);
5764
6136
  } catch (err) {
5765
6137
  console.warn(`[cloudflared-supervisor] rename failed: ${err?.message}`);
5766
6138
  return null;
5767
6139
  }
5768
6140
  }
5769
6141
  try {
5770
- fs9.chmodSync(CLOUDFLARED_PATH, 493);
6142
+ fs10.chmodSync(CLOUDFLARED_PATH, 493);
5771
6143
  } catch {}
5772
- return fs9.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
6144
+ return fs10.existsSync(CLOUDFLARED_PATH) ? CLOUDFLARED_PATH : null;
5773
6145
  }
5774
6146
  function detectCloudflaredRelease(platform2, arch2) {
5775
6147
  const base = "https://github.com/cloudflare/cloudflared/releases/latest/download";
5776
6148
  if (platform2 === "darwin") {
5777
6149
  const asset = arch2 === "arm64" ? "cloudflared-darwin-arm64.tgz" : "cloudflared-darwin-amd64.tgz";
5778
- return { url: `${base}/${asset}`, kind: "tgz", tempPath: path8.join(BIN_DIR, asset) };
6150
+ return { url: `${base}/${asset}`, kind: "tgz", tempPath: path10.join(BIN_DIR, asset) };
5779
6151
  }
5780
6152
  if (platform2 === "linux") {
5781
6153
  const asset = arch2 === "arm64" ? "cloudflared-linux-arm64" : arch2 === "arm" ? "cloudflared-linux-arm" : arch2 === "x64" ? "cloudflared-linux-amd64" : null;
@@ -5799,11 +6171,11 @@ function downloadWithCurl(url, dest) {
5799
6171
  if (result.status !== 0) {
5800
6172
  console.warn(`[cloudflared-supervisor] curl failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
5801
6173
  try {
5802
- fs9.unlinkSync(dest);
6174
+ fs10.unlinkSync(dest);
5803
6175
  } catch {}
5804
6176
  return false;
5805
6177
  }
5806
- return fs9.existsSync(dest) && fs9.statSync(dest).size > 0;
6178
+ return fs10.existsSync(dest) && fs10.statSync(dest).size > 0;
5807
6179
  }
5808
6180
  function extractTgzCloudflared(tgzPath, destDir) {
5809
6181
  const result = spawnSync("tar", ["-xzf", tgzPath, "-C", destDir], { stdio: "pipe" });
@@ -5811,10 +6183,10 @@ function extractTgzCloudflared(tgzPath, destDir) {
5811
6183
  console.warn(`[cloudflared-supervisor] tar extract failed (exit ${result.status}): ${result.stderr?.toString().slice(0, 200)}`);
5812
6184
  return false;
5813
6185
  }
5814
- return fs9.existsSync(CLOUDFLARED_PATH);
6186
+ return fs10.existsSync(CLOUDFLARED_PATH);
5815
6187
  }
5816
6188
  function killOrphanedCloudflareds(binPath) {
5817
- if (os4.platform() === "win32")
6189
+ if (os5.platform() === "win32")
5818
6190
  return;
5819
6191
  const found = spawnSync("pgrep", ["-f", binPath], { stdio: "pipe" });
5820
6192
  if (found.status !== 0)
@@ -5884,13 +6256,16 @@ var MIME_TYPES = {
5884
6256
  ".txt": "text/plain",
5885
6257
  ".map": "application/json"
5886
6258
  };
6259
+ function resolveStateDir() {
6260
+ return process.env.OPENCLAW_STATE_DIR || path11.join(os6.homedir(), ".openclaw");
6261
+ }
5887
6262
  function mintBootstrapToken() {
5888
6263
  const bootstrapToken = crypto3.randomBytes(32).toString("base64url");
5889
- const devicesDir = path9.join(os5.homedir(), ".openclaw", "devices");
5890
- const bootstrapPath = path9.join(devicesDir, "bootstrap.json");
5891
- if (!fs10.existsSync(devicesDir))
5892
- fs10.mkdirSync(devicesDir, { recursive: true });
5893
- const registry = fs10.existsSync(bootstrapPath) ? JSON.parse(fs10.readFileSync(bootstrapPath, "utf-8")) : {};
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")) : {};
5894
6269
  registry[bootstrapToken] = {
5895
6270
  token: bootstrapToken,
5896
6271
  profile: {
@@ -5899,15 +6274,15 @@ function mintBootstrapToken() {
5899
6274
  },
5900
6275
  issuedAtMs: Date.now()
5901
6276
  };
5902
- fs10.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
6277
+ fs11.writeFileSync(bootstrapPath, JSON.stringify(registry, null, 2));
5903
6278
  return bootstrapToken;
5904
6279
  }
5905
6280
  function registerWebApp(api) {
5906
- const pluginRoot = path9.resolve(path9.dirname(api.source), "..");
5907
- const appRoot = path9.join(pluginRoot, "web-build");
5908
- const hasWebBuild = fs10.existsSync(appRoot);
5909
- const indexPath = hasWebBuild ? path9.join(appRoot, "index.html") : null;
5910
- const hasIndex = !!(indexPath && fs10.existsSync(indexPath));
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));
5911
6286
  if (!hasWebBuild) {
5912
6287
  api.logger.info("[agentlife] web-build/ not found — /agentlife/pair will still register; static dashboard disabled");
5913
6288
  } else if (!hasIndex) {
@@ -5915,11 +6290,13 @@ function registerWebApp(api) {
5915
6290
  }
5916
6291
  let gatewayToken = "";
5917
6292
  try {
5918
- const configPath = path9.join(__require("node:os").homedir(), ".openclaw", "openclaw.json");
5919
- 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"));
5920
6295
  gatewayToken = raw?.gateway?.auth?.token || "";
5921
6296
  } catch {}
5922
- loadOrCreatePairingAccessToken();
6297
+ if (shouldStartRuntimeServices()) {
6298
+ loadOrCreatePairingAccessToken();
6299
+ }
5923
6300
  const approveLatest = () => {
5924
6301
  try {
5925
6302
  const { execSync } = __require("node:child_process");
@@ -6034,16 +6411,16 @@ function registerWebApp(api) {
6034
6411
  return true;
6035
6412
  }
6036
6413
  const relative = urlPath.replace(/^\/agentlife\/?/, "") || "index.html";
6037
- const filePath = path9.resolve(appRoot, relative);
6414
+ const filePath = path11.resolve(appRoot, relative);
6038
6415
  if (!filePath.startsWith(appRoot)) {
6039
6416
  res.writeHead(403);
6040
6417
  res.end();
6041
6418
  return true;
6042
6419
  }
6043
- const target = fs10.existsSync(filePath) && fs10.statSync(filePath).isFile() ? filePath : indexPath;
6044
- 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();
6045
6422
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
6046
- let content = fs10.readFileSync(target);
6423
+ let content = fs11.readFileSync(target);
6047
6424
  if (target === indexPath) {
6048
6425
  setTimeout(approveLatest, 2000);
6049
6426
  setTimeout(approveLatest, 5000);
@@ -6079,62 +6456,29 @@ function registerWebApp(api) {
6079
6456
 
6080
6457
  // services/pairing.ts
6081
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
+ }
6082
6462
  function registerPairingServices(api) {
6083
6463
  api.registerService({
6084
6464
  id: "agentlife-pairing-qr",
6085
- start: async (_ctx) => {
6086
- try {
6087
- const cfg = api.runtime.config.loadConfig();
6088
- const port = cfg.gateway?.port ?? 18789;
6089
- const token = cfg.gateway?.auth?.token;
6090
- if (!token)
6091
- return;
6092
- const supervisorTunnel = await awaitTunnelReady(TUNNEL_READY_TIMEOUT_MS) ?? getTunnelInfo();
6093
- if (!supervisorTunnel?.tunnelUrl) {
6094
- console.warn(`[agentlife] Cloudflared tunnel did not come up within ${TUNNEL_READY_TIMEOUT_MS / 1000}s — ` + `pairing QR will not be emitted. Check cloudflared-supervisor logs in the gateway output.`);
6095
- return;
6096
- }
6097
- const url = supervisorTunnel.tunnelUrl;
6098
- const bootstrapToken = mintBootstrapToken();
6099
- const payload = JSON.stringify({ url, bootstrapToken });
6100
- const setupCode = Buffer.from(payload).toString("base64url");
6101
- let qrTerminal = null;
6102
- try {
6103
- const realPath = fsSync3.realpathSync(process.argv[1] || "");
6104
- const mainRequire = createRequire3(realPath);
6105
- qrTerminal = mainRequire("qrcode-terminal");
6106
- } catch {}
6107
- console.log(`
6108
- \x1B[36m[agentlife]\x1B[0m \x1B[1mPlugin installed — get the app to see your dashboard.\x1B[0m
6109
- `);
6110
- if (qrTerminal?.generate) {
6111
- qrTerminal.generate(setupCode, { small: true }, (qr) => {
6112
- console.log(qr);
6113
- console.log(`
6114
- Setup code: ${setupCode}
6115
- `);
6116
- });
6117
- } else {
6118
- console.log(`Setup code: ${setupCode}`);
6119
- }
6120
- console.log(" \x1B[32mMac:\x1B[0m https://agentlife.app/desktop");
6121
- console.log(" \x1B[32miOS:\x1B[0m https://agentlife.app/ios");
6122
- console.log(" \x1B[32mAndroid:\x1B[0m https://agentlife.app/android");
6123
- console.log(` \x1B[2mBrowser:\x1B[0m http://localhost:${port}/agentlife/`);
6124
- console.log(`
6125
- Scan the QR from the mobile app to connect.
6126
- `);
6127
- } catch (err) {
6465
+ start: (ctx) => {
6466
+ if (!shouldStartRuntimeServices())
6467
+ return;
6468
+ emitPairingQr(api, ctx).catch((err) => {
6128
6469
  console.warn("[agentlife] QR generation skipped:", err?.message);
6129
- }
6470
+ });
6130
6471
  }
6131
6472
  });
6132
6473
  api.registerService({
6133
6474
  id: "agentlife-auto-pair",
6134
6475
  start: async (ctx) => {
6135
- const devicesDir = path10.join(os6.homedir(), ".openclaw", "devices");
6136
- const pendingPath = path10.join(devicesDir, "pending.json");
6137
- const pairedPath = path10.join(devicesDir, "paired.json");
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");
6138
6482
  const pollInterval = 3000;
6139
6483
  const withAdmin = (arr) => {
6140
6484
  const base = Array.isArray(arr) ? arr.filter((s) => typeof s === "string") : [];
@@ -6229,6 +6573,52 @@ Setup code: ${setupCode}
6229
6573
  }
6230
6574
  });
6231
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
+ }
6232
6622
 
6233
6623
  // hooks/bootstrap.ts
6234
6624
  var SKIP_GUIDANCE_AGENTS = new Set(["agentlife"]);
@@ -6388,15 +6778,15 @@ function registerBootstrapHookImpl(api, state2) {
6388
6778
  }
6389
6779
 
6390
6780
  // notifications.ts
6391
- import * as fs11 from "node:fs";
6392
- import * as path11 from "node:path";
6781
+ import * as fs12 from "node:fs";
6782
+ import * as path13 from "node:path";
6393
6783
  var config;
6394
6784
  function loadConfig() {
6395
6785
  if (config !== undefined)
6396
6786
  return config;
6397
6787
  try {
6398
- const configPath = path11.join(process.env.HOME ?? "~", ".openclaw", "agentlife", "notification-config.json");
6399
- 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");
6400
6790
  const parsed = JSON.parse(raw);
6401
6791
  if (parsed.serverUrl && parsed.apiKey) {
6402
6792
  config = { serverUrl: parsed.serverUrl, apiKey: parsed.apiKey };
@@ -6966,9 +7356,101 @@ function registerPromptStateHook(api, state2) {
6966
7356
  }
6967
7357
 
6968
7358
  // gateway/agents.ts
6969
- import * as fs12 from "node:fs";
6970
- import * as os7 from "node:os";
6971
- 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
+ }
6972
7454
  function registerAgentGateway(api, state2) {
6973
7455
  api.registerGatewayMethod("agentlife.createAgent", async ({ params, respond }) => {
6974
7456
  const id = typeof params?.id === "string" ? params.id.trim() : "";
@@ -6988,6 +7470,18 @@ function registerAgentGateway(api, state2) {
6988
7470
  return;
6989
7471
  }
6990
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
+ }
6991
7485
  const currentList = cfg.agents?.list ?? [];
6992
7486
  const existing = currentList.find((a) => a.id === id);
6993
7487
  if (!existing) {
@@ -7034,6 +7528,10 @@ function registerAgentGateway(api, state2) {
7034
7528
  console.log("[agentlife] builder patched agent config: %s", id);
7035
7529
  }
7036
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
+ }
7037
7535
  const hasGoodDescription = description && isUsableDescription(description, id, name);
7038
7536
  state2.agentRegistry.set(id, {
7039
7537
  name: name || id,
@@ -7063,7 +7561,7 @@ function registerAgentGateway(api, state2) {
7063
7561
  console.warn("[agentlife] deleteWelcomeWidget failed for %s: %s", id, e?.message);
7064
7562
  }
7065
7563
  }
7066
- 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 } : {} });
7067
7565
  });
7068
7566
  api.registerGatewayMethod("agentlife.deleteAgent", async ({ params, respond }) => {
7069
7567
  const id = typeof params?.id === "string" ? params.id.trim() : "";
@@ -7156,10 +7654,10 @@ function registerAgentGateway(api, state2) {
7156
7654
  state2.agentDbs.delete(id);
7157
7655
  }
7158
7656
  if (state2.dbBaseDir) {
7159
- const dbPath = path12.join(state2.dbBaseDir, `${id}.db`);
7657
+ const dbPath = path14.join(state2.dbBaseDir, `${id}.db`);
7160
7658
  try {
7161
- if (fs12.existsSync(dbPath)) {
7162
- fs12.unlinkSync(dbPath);
7659
+ if (fs13.existsSync(dbPath)) {
7660
+ fs13.unlinkSync(dbPath);
7163
7661
  cleanup.agentDbDeleted = true;
7164
7662
  }
7165
7663
  } catch (e) {
@@ -7171,24 +7669,24 @@ function registerAgentGateway(api, state2) {
7171
7669
  const existingEntry = currentList.find((a) => a.id === id);
7172
7670
  const filtered = currentList.filter((a) => a.id !== id);
7173
7671
  const removedFromConfig = filtered.length < currentList.length;
7174
- const openclawHome = path12.join(os7.homedir(), ".openclaw");
7175
- const stateDir = path12.join(openclawHome, "agents", id);
7176
- if (stateDir.startsWith(path12.join(openclawHome, "agents") + path12.sep)) {
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)) {
7177
7675
  try {
7178
- if (fs12.existsSync(stateDir)) {
7179
- fs12.rmSync(stateDir, { recursive: true, force: true });
7676
+ if (fs13.existsSync(stateDir)) {
7677
+ fs13.rmSync(stateDir, { recursive: true, force: true });
7180
7678
  cleanup.stateDeleted = true;
7181
7679
  }
7182
7680
  } catch (e) {
7183
7681
  console.warn("[agentlife] deleteAgent: failed to delete state dir:", e?.message);
7184
7682
  }
7185
7683
  }
7186
- const defaultWorkspace = path12.join(openclawHome, `workspace-${id}`);
7187
- const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path12.resolve(existingEntry.workspace) : defaultWorkspace;
7188
- if (workspaceToDelete.startsWith(openclawHome + path12.sep) && workspaceToDelete !== openclawHome) {
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) {
7189
7687
  try {
7190
- if (fs12.existsSync(workspaceToDelete)) {
7191
- fs12.rmSync(workspaceToDelete, { recursive: true, force: true });
7688
+ if (fs13.existsSync(workspaceToDelete)) {
7689
+ fs13.rmSync(workspaceToDelete, { recursive: true, force: true });
7192
7690
  cleanup.workspaceDeleted = true;
7193
7691
  }
7194
7692
  } catch (e) {
@@ -7507,7 +8005,7 @@ function registerUsageGateway(api, state2) {
7507
8005
  }
7508
8006
 
7509
8007
  // gateway/surfaces-gateway.ts
7510
- import * as fs13 from "node:fs";
8008
+ import * as fs14 from "node:fs";
7511
8009
  var guidedDismissSent = new Set;
7512
8010
  var warnedMissingOrigin = new Set;
7513
8011
  function registerSurfacesGateway(api, state2) {
@@ -7596,7 +8094,7 @@ function registerSurfacesGateway(api, state2) {
7596
8094
  guidedDismissSent.delete(surfaceId);
7597
8095
  if (!view2.isTransient && view2.path) {
7598
8096
  try {
7599
- fs13.unlinkSync(view2.path);
8097
+ fs14.unlinkSync(view2.path);
7600
8098
  } catch (err) {
7601
8099
  if (err?.code !== "ENOENT")
7602
8100
  console.warn("[agentlife] dismiss: file unlink failed for %s: %s", surfaceId, err?.message);
@@ -7841,17 +8339,17 @@ function registerAutomationsGateway(api, state2) {
7841
8339
  }
7842
8340
 
7843
8341
  // gateway/admin.ts
7844
- import * as fs14 from "node:fs/promises";
8342
+ import * as fs15 from "node:fs/promises";
7845
8343
  import * as fsSync4 from "node:fs";
7846
- import * as os8 from "node:os";
7847
- import * as path13 from "node:path";
8344
+ import * as os9 from "node:os";
8345
+ import * as path15 from "node:path";
7848
8346
  import { fileURLToPath } from "node:url";
7849
8347
  var PLUGIN_VERSION = (() => {
7850
8348
  try {
7851
- const moduleDir = path13.dirname(fileURLToPath(import.meta.url));
8349
+ const moduleDir = path15.dirname(fileURLToPath(import.meta.url));
7852
8350
  const candidates = [
7853
- path13.join(moduleDir, "..", "package.json"),
7854
- path13.join(moduleDir, "..", "..", "package.json")
8351
+ path15.join(moduleDir, "..", "package.json"),
8352
+ path15.join(moduleDir, "..", "..", "package.json")
7855
8353
  ];
7856
8354
  for (const p of candidates) {
7857
8355
  if (fsSync4.existsSync(p)) {
@@ -7900,7 +8398,7 @@ async function fetchNpmLatestVersion(force = false) {
7900
8398
  return inflightFetch;
7901
8399
  }
7902
8400
  function pluginConfigPath() {
7903
- return path13.join(os8.homedir(), ".openclaw", "agentlife", "plugin-config.json");
8401
+ return path15.join(os9.homedir(), ".openclaw", "agentlife", "plugin-config.json");
7904
8402
  }
7905
8403
  function readPluginConfig() {
7906
8404
  try {
@@ -7910,7 +8408,7 @@ function readPluginConfig() {
7910
8408
  }
7911
8409
  }
7912
8410
  function writePluginConfig(config2) {
7913
- const dir = path13.dirname(pluginConfigPath());
8411
+ const dir = path15.dirname(pluginConfigPath());
7914
8412
  fsSync4.mkdirSync(dir, { recursive: true });
7915
8413
  fsSync4.writeFileSync(pluginConfigPath(), JSON.stringify(config2, null, 2));
7916
8414
  }
@@ -8038,7 +8536,7 @@ function registerAdminGateway(api, state2) {
8038
8536
  api.registerGatewayMethod("agentlife.uninstall", async ({ respond }) => {
8039
8537
  try {
8040
8538
  const cleaned = [];
8041
- const baseDir = state2.agentlifeStateDir ?? path13.join(os8.homedir(), ".openclaw", "agentlife");
8539
+ const baseDir = state2.agentlifeStateDir ?? path15.join(os9.homedir(), ".openclaw", "agentlife");
8042
8540
  const identity = loadDeviceIdentity();
8043
8541
  if (!identity) {
8044
8542
  cleaned.push("server deprovision skipped (no device identity)");
@@ -8103,7 +8601,7 @@ function registerAdminGateway(api, state2) {
8103
8601
  } catch {
8104
8602
  cleaned.push("agent config cleanup skipped");
8105
8603
  }
8106
- const backupPath = path13.join(baseDir, "config-backup.json");
8604
+ const backupPath = path15.join(baseDir, "config-backup.json");
8107
8605
  try {
8108
8606
  cleaned.push(await restoreConfigFromBackup(api, backupPath));
8109
8607
  } catch {
@@ -8125,35 +8623,35 @@ function registerAdminGateway(api, state2) {
8125
8623
  dbPath,
8126
8624
  dbPath ? `${dbPath}-wal` : null,
8127
8625
  dbPath ? `${dbPath}-shm` : null,
8128
- path13.join(baseDir, "config-backup.json"),
8129
- path13.join(baseDir, "notification-config.json"),
8130
- path13.join(baseDir, "canvas-node-identity.json")
8626
+ path15.join(baseDir, "config-backup.json"),
8627
+ path15.join(baseDir, "notification-config.json"),
8628
+ path15.join(baseDir, "canvas-node-identity.json")
8131
8629
  ].filter(Boolean);
8132
8630
  for (const fp of stateFiles) {
8133
8631
  try {
8134
- await fs14.unlink(fp);
8135
- cleaned.push(`deleted ${path13.basename(fp)}`);
8632
+ await fs15.unlink(fp);
8633
+ cleaned.push(`deleted ${path15.basename(fp)}`);
8136
8634
  } catch {}
8137
8635
  }
8138
8636
  if (state2.dbBaseDir) {
8139
8637
  try {
8140
- await fs14.rm(state2.dbBaseDir, { recursive: true, force: true });
8638
+ await fs15.rm(state2.dbBaseDir, { recursive: true, force: true });
8141
8639
  cleaned.push("deleted agent databases");
8142
8640
  } catch {}
8143
8641
  }
8144
8642
  for (const agent of PROVISIONED_AGENTS) {
8145
8643
  if (agent.existingAgent)
8146
8644
  continue;
8147
- 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}`);
8148
8646
  try {
8149
- await fs14.rm(wsDir, { recursive: true, force: true });
8647
+ await fs15.rm(wsDir, { recursive: true, force: true });
8150
8648
  cleaned.push(`deleted workspace ${agent.id}`);
8151
8649
  } catch {}
8152
8650
  }
8153
8651
  try {
8154
- const remaining = await fs14.readdir(baseDir);
8652
+ const remaining = await fs15.readdir(baseDir);
8155
8653
  if (remaining.length === 0) {
8156
- await fs14.rmdir(baseDir);
8654
+ await fs15.rmdir(baseDir);
8157
8655
  cleaned.push("deleted agentlife state directory");
8158
8656
  }
8159
8657
  } catch {}
@@ -8193,11 +8691,11 @@ function registerAdminGateway(api, state2) {
8193
8691
 
8194
8692
  // gateway/evolutions.ts
8195
8693
  import * as fsSync5 from "node:fs";
8196
- import * as os9 from "node:os";
8197
- import * as path14 from "node:path";
8694
+ import * as os10 from "node:os";
8695
+ import * as path16 from "node:path";
8198
8696
  var ALLOWED_FILENAMES = new Set(["AGENTS.md", "USER.md", "SOUL.md", "TOOLS.md", "IDENTITY.md"]);
8199
8697
  function workspaceFilePath(agentId, filename) {
8200
- return path14.join(os9.homedir(), ".openclaw", `workspace-${agentId}`, filename);
8698
+ return path16.join(os10.homedir(), ".openclaw", `workspace-${agentId}`, filename);
8201
8699
  }
8202
8700
  function hasRegressionFlag(db, agentId, filename, snapshotAt) {
8203
8701
  const horizon = snapshotAt + 7 * 24 * 60 * 60 * 1000;
@@ -8572,39 +9070,43 @@ function parseOffset(offset) {
8572
9070
  // gateway/providers.ts
8573
9071
  import {
8574
9072
  upsertApiKeyProfile,
8575
- upsertAuthProfile,
9073
+ upsertAuthProfile as upsertAuthProfile3,
8576
9074
  buildApiKeyCredential,
8577
9075
  applyAuthProfileConfig
8578
9076
  } from "openclaw/plugin-sdk/provider-auth-api-key";
8579
9077
 
8580
9078
  // gateway/config-utils.ts
8581
- import * as fs15 from "node:fs";
8582
- import * as os10 from "node:os";
8583
- import * as path15 from "node:path";
9079
+ import * as fs16 from "node:fs";
9080
+ import * as os11 from "node:os";
9081
+ import * as path17 from "node:path";
8584
9082
  function configPath() {
8585
- return path15.join(os10.homedir(), ".openclaw", "openclaw.json");
9083
+ return path17.join(os11.homedir(), ".openclaw", "openclaw.json");
8586
9084
  }
8587
9085
  function readConfig() {
8588
9086
  try {
8589
- return JSON.parse(fs15.readFileSync(configPath(), "utf-8"));
9087
+ return JSON.parse(fs16.readFileSync(configPath(), "utf-8"));
8590
9088
  } catch {
8591
9089
  return {};
8592
9090
  }
8593
9091
  }
8594
9092
  function writeConfig(cfg) {
8595
- fs15.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
9093
+ fs16.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + `
8596
9094
  `, "utf-8");
8597
9095
  }
8598
9096
 
8599
9097
  // gateway/providers.ts
8600
- import * as fs16 from "node:fs";
8601
- import * as os11 from "node:os";
8602
- 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
+ }
8603
9105
  function siblingAgentDirs() {
8604
- const root = path16.join(os11.homedir(), ".openclaw", "agents");
9106
+ const root = path18.join(resolveOpenClawRoot3(), "agents");
8605
9107
  let entries;
8606
9108
  try {
8607
- entries = fs16.readdirSync(root, { withFileTypes: true });
9109
+ entries = fs17.readdirSync(root, { withFileTypes: true });
8608
9110
  } catch {
8609
9111
  return [];
8610
9112
  }
@@ -8612,9 +9114,9 @@ function siblingAgentDirs() {
8612
9114
  for (const entry of entries) {
8613
9115
  if (!entry.isDirectory() && !entry.isSymbolicLink())
8614
9116
  continue;
8615
- const dir = path16.join(root, entry.name, "agent");
9117
+ const dir = path18.join(root, entry.name, "agent");
8616
9118
  try {
8617
- if (fs16.statSync(dir).isDirectory())
9119
+ if (fs17.statSync(dir).isDirectory())
8618
9120
  out.push(dir);
8619
9121
  } catch {}
8620
9122
  }
@@ -8684,20 +9186,20 @@ async function ensureModels(run) {
8684
9186
  }
8685
9187
  function readOAuthExpiries() {
8686
9188
  const result = new Map;
8687
- const fs17 = __require("node:fs");
8688
- const path17 = __require("node:path");
8689
- const os12 = __require("node:os");
8690
- const agentsDir = path17.join(os12.homedir(), ".openclaw", "agents");
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");
8691
9193
  let entries;
8692
9194
  try {
8693
- entries = fs17.readdirSync(agentsDir);
9195
+ entries = fs18.readdirSync(agentsDir);
8694
9196
  } catch {
8695
9197
  return result;
8696
9198
  }
8697
9199
  for (const name of entries) {
8698
- const filePath = path17.join(agentsDir, name, "agent", "auth-profiles.json");
9200
+ const filePath = path19.join(agentsDir, name, "agent", "auth-profiles.json");
8699
9201
  try {
8700
- const raw = fs17.readFileSync(filePath, "utf-8");
9202
+ const raw = fs18.readFileSync(filePath, "utf-8");
8701
9203
  const data = JSON.parse(raw);
8702
9204
  const profiles = data?.profiles ?? {};
8703
9205
  for (const entry of Object.values(profiles)) {
@@ -8835,7 +9337,7 @@ function registerProvidersGateway(api, state2) {
8835
9337
  const credential = buildApiKeyCredential(provider, apiKey);
8836
9338
  for (const agentDir of siblingAgentDirs()) {
8837
9339
  try {
8838
- upsertAuthProfile({ profileId, credential, agentDir });
9340
+ upsertAuthProfile3({ profileId, credential, agentDir });
8839
9341
  } catch {}
8840
9342
  }
8841
9343
  const cfg = readConfig();
@@ -8869,9 +9371,9 @@ function registerProvidersGateway(api, state2) {
8869
9371
  }
8870
9372
  writeConfig(cfg);
8871
9373
  for (const agentDir of siblingAgentDirs()) {
8872
- const file = path16.join(agentDir, "auth-profiles.json");
9374
+ const file = path18.join(agentDir, "auth-profiles.json");
8873
9375
  try {
8874
- const raw = fs16.readFileSync(file, "utf-8");
9376
+ const raw = fs17.readFileSync(file, "utf-8");
8875
9377
  const data = JSON.parse(raw);
8876
9378
  const agentProfiles = data?.profiles;
8877
9379
  if (!agentProfiles || typeof agentProfiles !== "object")
@@ -8888,7 +9390,7 @@ function registerProvidersGateway(api, state2) {
8888
9390
  changed = true;
8889
9391
  }
8890
9392
  if (changed)
8891
- fs16.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
9393
+ fs17.writeFileSync(file, JSON.stringify(data, null, 2), "utf-8");
8892
9394
  } catch {}
8893
9395
  }
8894
9396
  respond(true, { provider, configured: false });
@@ -8978,22 +9480,22 @@ ${result?.stderr ?? ""}`;
8978
9480
  }
8979
9481
 
8980
9482
  // gateway/models-config.ts
8981
- import * as fs17 from "node:fs";
8982
- import * as os12 from "node:os";
8983
- import * as path17 from "node:path";
9483
+ import * as fs18 from "node:fs";
9484
+ import * as os13 from "node:os";
9485
+ import * as path19 from "node:path";
8984
9486
  function pluginConfigPath2() {
8985
- return path17.join(os12.homedir(), ".openclaw", "agentlife", "plugin-config.json");
9487
+ return path19.join(os13.homedir(), ".openclaw", "agentlife", "plugin-config.json");
8986
9488
  }
8987
9489
  function readPluginConfig2() {
8988
9490
  try {
8989
- return JSON.parse(fs17.readFileSync(pluginConfigPath2(), "utf-8"));
9491
+ return JSON.parse(fs18.readFileSync(pluginConfigPath2(), "utf-8"));
8990
9492
  } catch {
8991
9493
  return {};
8992
9494
  }
8993
9495
  }
8994
9496
  function writePluginConfig2(cfg) {
8995
- fs17.mkdirSync(path17.dirname(pluginConfigPath2()), { recursive: true });
8996
- 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) + `
8997
9499
  `, "utf-8");
8998
9500
  }
8999
9501
  function registerModelsConfigGateway(api) {
@@ -9137,9 +9639,12 @@ var stopObservability = null;
9137
9639
  var stopDailySweep = null;
9138
9640
  var stopQualityCheckPoller = null;
9139
9641
  var stopCloudflared = null;
9642
+ function resolveOpenClawRoot4() {
9643
+ return process.env.OPENCLAW_STATE_DIR || path20.join(homedir17(), ".openclaw");
9644
+ }
9140
9645
  function resolveInternalModel(api) {
9141
9646
  try {
9142
- const pluginCfgPath = path18.join(homedir15(), ".openclaw", "agentlife", "plugin-config.json");
9647
+ const pluginCfgPath = path20.join(resolveOpenClawRoot4(), "agentlife", "plugin-config.json");
9143
9648
  try {
9144
9649
  const raw = __require("node:fs").readFileSync(pluginCfgPath, "utf-8");
9145
9650
  const pluginCfg = JSON.parse(raw);
@@ -9165,7 +9670,24 @@ function _getState() {
9165
9670
  throw new Error("plugin not registered");
9166
9671
  return currentState;
9167
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
+ }
9168
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
+ }
9169
9691
  if (currentState) {
9170
9692
  registerWidgetPushTool(api, currentState);
9171
9693
  api.registerChannel({ plugin: createAgentlifeChannel(currentState.channelState) });
@@ -9175,7 +9697,8 @@ function register(api) {
9175
9697
  return;
9176
9698
  }
9177
9699
  registered = true;
9178
- const fallbackDir = path18.join(homedir15(), ".openclaw", "agentlife");
9700
+ const fallbackRoot = resolveOpenClawRoot4();
9701
+ const fallbackDir = path20.join(fallbackRoot, "agentlife");
9179
9702
  const state2 = {
9180
9703
  fileSurfaceStorage: null,
9181
9704
  surfaceIndex: null,
@@ -9187,9 +9710,9 @@ function register(api) {
9187
9710
  agentDbs: new Map,
9188
9711
  historyDb: null,
9189
9712
  agentlifeStateDir: fallbackDir,
9190
- registryFilePath: path18.join(fallbackDir, "agent-registry.json"),
9191
- dbBaseDir: path18.join(fallbackDir, "db"),
9192
- 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"),
9193
9716
  runCommand: api.runtime.system?.runCommandWithTimeout ?? null,
9194
9717
  enqueueSystemEvent: null,
9195
9718
  requestHeartbeatNow: null,
@@ -9201,12 +9724,12 @@ function register(api) {
9201
9724
  currentState = state2;
9202
9725
  api.registerChannel({ plugin: createAgentlifeChannel(state2.channelState) });
9203
9726
  console.log("[agentlife] channel registered, session format: %s", buildAgentSessionKey("example"));
9204
- if (existsSync7(fallbackDir)) {
9727
+ if (shouldStartRuntimeServices() && existsSync8(fallbackDir)) {
9205
9728
  try {
9206
9729
  const db = getOrCreateHistoryDb(state2);
9207
9730
  state2.surfaceIndex = new SurfaceIndex(db);
9208
9731
  state2.followupQueue = new FollowupQueue(db);
9209
- state2.fileSurfaceStorage = new FileSurfaceStorage;
9732
+ state2.fileSurfaceStorage = new FileSurfaceStorage({ openclawRoot: fallbackRoot });
9210
9733
  } catch (e) {
9211
9734
  console.warn("[agentlife] eager surface storage init failed:", e?.message);
9212
9735
  }
@@ -9216,20 +9739,32 @@ function register(api) {
9216
9739
  registerSurfacesService(api, state2);
9217
9740
  api.registerService({
9218
9741
  id: "agentlife-provisioning",
9219
- start: async () => {
9220
- const cfg = api.runtime.config.loadConfig();
9221
- await provisionAgents(state2, cfg, api.runtime, console.log);
9222
- await reconcileInternalSessions(state2, createdInternalSessions);
9223
- await cleanupSupervisorLegacyState(state2);
9224
- stopObservability = startObservabilityService(state2);
9225
- stopDailySweep = startDailySweepService(state2);
9226
- stopQualityCheckPoller = startQualityCheckPoller(state2);
9227
- renderAllAgentWidgets(state2, api.runtime, console.log);
9742
+ start: () => {
9743
+ if (!shouldStartRuntimeServices())
9744
+ return;
9745
+ startProvisioningServices(api, state2).catch((err) => {
9746
+ console.warn("[agentlife] provisioning service failed:", err?.message ?? err);
9747
+ });
9228
9748
  }
9229
9749
  });
9230
9750
  registerConfigOptimizer(api, state2);
9231
9751
  registerPairingServices(api);
9232
- 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
+ });
9233
9768
  api.registerService({
9234
9769
  id: "agentlife-shutdown",
9235
9770
  start: () => {},
@@ -9254,7 +9789,15 @@ function register(api) {
9254
9789
  closeAllDbs(state2);
9255
9790
  }
9256
9791
  });
9257
- 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
+ });
9258
9801
  registerPromptStateHook(api, state2);
9259
9802
  registerWidgetPushTool(api, state2);
9260
9803
  registerActivityHooks(api, state2);
@@ -9270,7 +9813,7 @@ function register(api) {
9270
9813
  registerProvidersGateway(api, state2);
9271
9814
  registerModelsConfigGateway(api);
9272
9815
  registerWebApp(api);
9273
- const notifyConfigPath = path18.join(fallbackDir, "notification-config.json");
9816
+ const notifyConfigPath = path20.join(fallbackDir, "notification-config.json");
9274
9817
  api.registerGatewayMethod("agentlife.notifications.register", ({ params, respond }) => {
9275
9818
  const serverUrl = typeof params?.serverUrl === "string" ? params.serverUrl.trim() : "";
9276
9819
  const apiKey = typeof params?.apiKey === "string" ? params.apiKey.trim() : "";
@@ -9278,9 +9821,9 @@ function register(api) {
9278
9821
  return respond(false, { error: "missing serverUrl or apiKey" });
9279
9822
  }
9280
9823
  try {
9281
- const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync8 } = __require("node:fs");
9282
- mkdirSync8(path18.dirname(notifyConfigPath), { recursive: true });
9283
- 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 }));
9284
9827
  } catch {}
9285
9828
  respond(true, { registered: true });
9286
9829
  }, { scope: "operator.write" });