@haaaiawd/second-nature 0.1.8 → 0.1.10

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 (185) hide show
  1. package/index.js +281 -69
  2. package/openclaw.plugin.json +1 -1
  3. package/package.json +2 -1
  4. package/runtime/cli/commands/index.d.ts +2 -0
  5. package/runtime/cli/commands/index.js +61 -6
  6. package/runtime/cli/explain/explain-surface-subject.d.ts +8 -0
  7. package/runtime/cli/explain/explain-surface-subject.js +9 -0
  8. package/runtime/cli/explain/format-explanation.d.ts +2 -0
  9. package/runtime/cli/explain/format-explanation.js +2 -0
  10. package/runtime/cli/explain/resolve-subject.js +15 -0
  11. package/runtime/cli/host-capability/classify-delivery.d.ts +14 -0
  12. package/runtime/cli/host-capability/classify-delivery.js +20 -0
  13. package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -0
  14. package/runtime/cli/host-capability/probe-host-capability.js +58 -0
  15. package/runtime/cli/host-capability/record-host-capability.d.ts +6 -0
  16. package/runtime/cli/host-capability/record-host-capability.js +14 -0
  17. package/runtime/cli/host-capability/types.d.ts +71 -0
  18. package/runtime/cli/host-capability/types.js +6 -0
  19. package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -0
  20. package/runtime/cli/host-smoke/run-host-smoke.js +40 -0
  21. package/runtime/cli/host-smoke/types.d.ts +35 -0
  22. package/runtime/cli/host-smoke/types.js +6 -0
  23. package/runtime/cli/index.js +18 -0
  24. package/runtime/cli/ops/heartbeat-surface.d.ts +35 -0
  25. package/runtime/cli/ops/heartbeat-surface.js +71 -0
  26. package/runtime/cli/ops/ops-router.d.ts +16 -0
  27. package/runtime/cli/ops/ops-router.js +83 -0
  28. package/runtime/cli/ops/show-operator-fallback.d.ts +13 -0
  29. package/runtime/cli/ops/show-operator-fallback.js +22 -0
  30. package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +10 -0
  31. package/runtime/cli/ops/workspace-heartbeat-runner.js +26 -0
  32. package/runtime/cli/read-models/index.d.ts +11 -2
  33. package/runtime/cli/read-models/index.js +50 -0
  34. package/runtime/cli/read-models/operator-explain-map.d.ts +6 -0
  35. package/runtime/cli/read-models/operator-explain-map.js +10 -0
  36. package/runtime/cli/read-models/types.d.ts +5 -1
  37. package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -0
  38. package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -0
  39. package/runtime/connectors/base/contract.d.ts +6 -0
  40. package/runtime/connectors/base/execution-policy.d.ts +47 -0
  41. package/runtime/connectors/base/execution-policy.js +82 -0
  42. package/runtime/connectors/base/index.d.ts +2 -0
  43. package/runtime/connectors/base/index.js +2 -0
  44. package/runtime/connectors/base/manifest.d.ts +55 -2
  45. package/runtime/connectors/base/manifest.js +50 -0
  46. package/runtime/connectors/base/map-life-evidence.d.ts +16 -0
  47. package/runtime/connectors/base/map-life-evidence.js +79 -0
  48. package/runtime/connectors/base/policy-layer.d.ts +2 -0
  49. package/runtime/connectors/base/policy-layer.js +16 -0
  50. package/runtime/connectors/base/route-planner.js +1 -0
  51. package/runtime/connectors/index.d.ts +1 -0
  52. package/runtime/connectors/index.js +1 -0
  53. package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -0
  54. package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -0
  55. package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +2 -0
  56. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +37 -16
  57. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +95 -29
  58. package/runtime/core/second-nature/heartbeat/index.d.ts +4 -1
  59. package/runtime/core/second-nature/heartbeat/index.js +4 -1
  60. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -0
  61. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -0
  62. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -0
  63. package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -0
  64. package/runtime/core/second-nature/heartbeat/signal.d.ts +9 -2
  65. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +19 -1
  66. package/runtime/core/second-nature/index.d.ts +8 -0
  67. package/runtime/core/second-nature/index.js +8 -0
  68. package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +1 -1
  69. package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +9 -4
  70. package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +6 -0
  71. package/runtime/core/second-nature/orchestrator/guard-layer.js +76 -20
  72. package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +10 -0
  73. package/runtime/core/second-nature/orchestrator/intent-planner.js +135 -28
  74. package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +1 -1
  75. package/runtime/core/second-nature/orchestrator/lease-manager.js +1 -1
  76. package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -0
  77. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -0
  78. package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -0
  79. package/runtime/core/second-nature/outreach/delivery-target.js +70 -0
  80. package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -0
  81. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -0
  82. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -0
  83. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -0
  84. package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -0
  85. package/runtime/core/second-nature/outreach/judge-outreach.js +121 -0
  86. package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -0
  87. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -0
  88. package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -0
  89. package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -0
  90. package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -0
  91. package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -0
  92. package/runtime/core/second-nature/types.d.ts +16 -2
  93. package/runtime/guidance/draft-outreach-message.d.ts +7 -0
  94. package/runtime/guidance/draft-outreach-message.js +42 -0
  95. package/runtime/guidance/evidence-guidance.d.ts +40 -0
  96. package/runtime/guidance/evidence-guidance.js +52 -0
  97. package/runtime/guidance/index.d.ts +3 -0
  98. package/runtime/guidance/index.js +3 -0
  99. package/runtime/guidance/outreach-draft-schema.d.ts +228 -0
  100. package/runtime/guidance/outreach-draft-schema.js +80 -0
  101. package/runtime/observability/audit/append-only-audit-store.d.ts +14 -0
  102. package/runtime/observability/audit/append-only-audit-store.js +21 -0
  103. package/runtime/observability/audit/audit-envelope.d.ts +51 -0
  104. package/runtime/observability/audit/audit-envelope.js +130 -0
  105. package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -0
  106. package/runtime/observability/audit/verify-audit-hash-chain.js +83 -0
  107. package/runtime/observability/db/index.js +11 -0
  108. package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -0
  109. package/runtime/observability/db/schema/host-capability-reports.js +12 -0
  110. package/runtime/observability/db/schema/index.d.ts +1 -0
  111. package/runtime/observability/db/schema/index.js +1 -0
  112. package/runtime/observability/index.d.ts +7 -0
  113. package/runtime/observability/index.js +7 -0
  114. package/runtime/observability/query/explain-query.d.ts +48 -0
  115. package/runtime/observability/query/explain-query.js +114 -0
  116. package/runtime/observability/query/export-audit-bundle.d.ts +22 -0
  117. package/runtime/observability/query/export-audit-bundle.js +27 -0
  118. package/runtime/observability/services/decision-ledger.d.ts +1 -1
  119. package/runtime/observability/services/decision-ledger.js +4 -0
  120. package/runtime/observability/services/governance-audit.d.ts +14 -0
  121. package/runtime/observability/services/governance-audit.js +25 -1
  122. package/runtime/observability/services/governance-plane-recorder.d.ts +47 -0
  123. package/runtime/observability/services/governance-plane-recorder.js +55 -0
  124. package/runtime/observability/services/lived-experience-audit.d.ts +97 -0
  125. package/runtime/observability/services/lived-experience-audit.js +162 -0
  126. package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -0
  127. package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -0
  128. package/runtime/storage/bootstrap/repair-gate.d.ts +17 -0
  129. package/runtime/storage/bootstrap/repair-gate.js +71 -0
  130. package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -0
  131. package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -0
  132. package/runtime/storage/db/index.js +49 -0
  133. package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -0
  134. package/runtime/storage/db/schema/delivery-attempts.js +13 -0
  135. package/runtime/storage/db/schema/index.d.ts +3 -0
  136. package/runtime/storage/db/schema/index.js +3 -0
  137. package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -0
  138. package/runtime/storage/db/schema/life-evidence-index.js +11 -0
  139. package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -0
  140. package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -0
  141. package/runtime/storage/db/schema/policies.d.ts +17 -0
  142. package/runtime/storage/db/schema/policies.js +1 -0
  143. package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -0
  144. package/runtime/storage/delivery/query-delivery-attempts.js +32 -0
  145. package/runtime/storage/delivery/types.d.ts +27 -0
  146. package/runtime/storage/delivery/types.js +1 -0
  147. package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -0
  148. package/runtime/storage/delivery/write-delivery-attempt.js +36 -0
  149. package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -0
  150. package/runtime/storage/fallback/load-operator-fallback.js +47 -0
  151. package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -0
  152. package/runtime/storage/fallback/operator-fallback-types.js +1 -0
  153. package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -0
  154. package/runtime/storage/fallback/operator-fallback-view.js +1 -0
  155. package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -0
  156. package/runtime/storage/fallback/write-operator-fallback.js +21 -0
  157. package/runtime/storage/index.d.ts +21 -0
  158. package/runtime/storage/index.js +14 -0
  159. package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -0
  160. package/runtime/storage/life-evidence/append-life-evidence.js +64 -0
  161. package/runtime/storage/life-evidence/types.d.ts +45 -0
  162. package/runtime/storage/life-evidence/types.js +6 -0
  163. package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -0
  164. package/runtime/storage/quiet/persist-quiet-artifact.js +22 -0
  165. package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -0
  166. package/runtime/storage/quiet/quiet-artifact-types.js +1 -0
  167. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -0
  168. package/runtime/storage/quiet/quiet-artifact-writer.js +56 -0
  169. package/runtime/storage/repositories/credential-repository.js +12 -1
  170. package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -0
  171. package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -0
  172. package/runtime/storage/services/credential-vault.d.ts +5 -0
  173. package/runtime/storage/services/credential-vault.js +47 -9
  174. package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -0
  175. package/runtime/storage/snapshots/continuity-snapshot.js +41 -0
  176. package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -0
  177. package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -0
  178. package/runtime/storage/snapshots/types.d.ts +58 -0
  179. package/runtime/storage/snapshots/types.js +1 -0
  180. package/runtime/storage/state-api.js +11 -4
  181. package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -0
  182. package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -0
  183. package/runtime/storage/user-interest/types.d.ts +25 -0
  184. package/runtime/storage/user-interest/types.js +1 -0
  185. package/workspace-ops-bridge.js +78 -0
package/index.js CHANGED
@@ -19,12 +19,102 @@
19
19
  * Test coverage:
20
20
  * - tests/integration/cli/plugin-runtime-registration.test.ts
21
21
  * - tests/integration/cli/plugin-packaging-walkthrough.test.ts
22
+ * - tests/integration/cli/plugin-workspace-ops-bridge.test.ts (T1.1.4)
22
23
  */
23
24
  import { startRuntimeService, } from "./runtime/core/second-nature/runtime/service-entry.js";
24
25
  import { getLifecycleState, recordRegistration, } from "./runtime/core/second-nature/runtime/lifecycle-service.js";
26
+ import { openWorkspaceOpsBridge } from "./workspace-ops-bridge.js";
25
27
  const INTERNAL_RUNTIME_TRACE_PREFIX = "sn-runtime-";
26
28
  const HOST_SAFE_LIMITATION_MESSAGE = "Host-safe plugin package keeps synchronous register/load semantics, but mutating workspace runtime flows remain unavailable here.";
27
29
  let activationSpine = null;
30
+ /** T1.1.4 — lazily opened full read bridge; closed when workspace root / resolution changes. */
31
+ let workspaceOpsBridge = null;
32
+ function disposeWorkspaceOpsBridge() {
33
+ if (workspaceOpsBridge) {
34
+ workspaceOpsBridge.close();
35
+ workspaceOpsBridge = null;
36
+ }
37
+ }
38
+ const WORKSPACE_BRIDGE_COMMANDS = new Set([
39
+ "status",
40
+ "quiet",
41
+ "report",
42
+ "session",
43
+ "explain",
44
+ "heartbeat_check",
45
+ "fallback",
46
+ "storage_smoke",
47
+ ]);
48
+ function isWorkspaceBridgeCommand(command, input) {
49
+ if (command === "credential") {
50
+ const action = typeof input?.action === "string" ? input.action : "show";
51
+ return action !== "verify";
52
+ }
53
+ return WORKSPACE_BRIDGE_COMMANDS.has(command);
54
+ }
55
+ async function ensureWorkspaceOpsBridge(spine) {
56
+ const root = spine.workspaceRootContext.runtimeRoot;
57
+ if (workspaceOpsBridge?.root === root) {
58
+ return { ok: true, dispatch: workspaceOpsBridge.dispatch };
59
+ }
60
+ disposeWorkspaceOpsBridge();
61
+ const opened = await openWorkspaceOpsBridge(root);
62
+ if (!opened.ok) {
63
+ return opened;
64
+ }
65
+ workspaceOpsBridge = { root, close: opened.close, dispatch: opened.dispatch };
66
+ return { ok: true, dispatch: opened.dispatch };
67
+ }
68
+ async function routeSecondNatureCommand(spine, command, input) {
69
+ const wr = spine.workspaceRootContext;
70
+ const useBridge = wr.resolution !== "unknown" && isWorkspaceBridgeCommand(command, input);
71
+ if (useBridge) {
72
+ const bridge = await ensureWorkspaceOpsBridge(spine);
73
+ if (!bridge.ok) {
74
+ return {
75
+ ok: false,
76
+ surfaceMode: "host_safe_carrier",
77
+ workspaceReadModelsEvaluated: false,
78
+ message: HOST_SAFE_LIMITATION_MESSAGE,
79
+ error: bridge.error,
80
+ data: {
81
+ workspaceRootResolution: wr.resolution,
82
+ bridgeAttempted: true,
83
+ declaredRoot: wr.declaredRoot,
84
+ },
85
+ };
86
+ }
87
+ return (await bridge.dispatch(command, input));
88
+ }
89
+ const def = spine.router.resolve(command);
90
+ if (!def) {
91
+ return { ok: false, message: `Unknown Second Nature command: ${command}` };
92
+ }
93
+ return def.execute(input);
94
+ }
95
+ function resolveWorkspaceRoot(toolWorkspaceRoot) {
96
+ const env = process.env.SECOND_NATURE_WORKSPACE_ROOT?.trim();
97
+ if (env) {
98
+ return { resolution: "env", declaredRoot: env, runtimeRoot: env };
99
+ }
100
+ const tool = toolWorkspaceRoot?.trim();
101
+ if (tool) {
102
+ return { resolution: "tool_args", declaredRoot: tool, runtimeRoot: tool };
103
+ }
104
+ return { resolution: "unknown", declaredRoot: undefined, runtimeRoot: process.cwd() };
105
+ }
106
+ function syncWorkspaceRootFromTool(spine, toolWorkspaceRoot) {
107
+ const next = resolveWorkspaceRoot(toolWorkspaceRoot);
108
+ const prev = spine.workspaceRootContext;
109
+ const changed = next.runtimeRoot !== prev.runtimeRoot || next.resolution !== prev.resolution;
110
+ if (changed) {
111
+ disposeWorkspaceOpsBridge();
112
+ }
113
+ spine.workspaceRootContext = next;
114
+ if (changed) {
115
+ spine.runtimeHandle = startRuntimeService({ workspaceRoot: next.runtimeRoot });
116
+ }
117
+ }
28
118
  function trimRuntimeEvidence(spine) {
29
119
  if (spine.runtimeEvidence.length > 12) {
30
120
  spine.runtimeEvidence.splice(0, spine.runtimeEvidence.length - 12);
@@ -70,6 +160,17 @@ function parseExplainSubject(subjectRaw) {
70
160
  case "soul":
71
161
  case "soul-change":
72
162
  return { subjectType: "soul-change", subjectId: id };
163
+ case "fallback":
164
+ return { subjectType: "fallback", subjectId: id };
165
+ case "probe":
166
+ return { subjectType: "probe", subjectId: id };
167
+ case "report":
168
+ return { subjectType: "report", subjectId: id };
169
+ case "delivery":
170
+ return { subjectType: "delivery", subjectId: id };
171
+ case "source":
172
+ case "source_ref":
173
+ return { subjectType: "source_ref", subjectId: id };
73
174
  default:
74
175
  throw new Error("explain_subject_unsupported");
75
176
  }
@@ -77,56 +178,73 @@ function parseExplainSubject(subjectRaw) {
77
178
  function buildStatusPayload(spine) {
78
179
  const runtimeEvidence = latestRuntimeEvidence(spine);
79
180
  const updatedAt = runtimeEvidence?.createdAt ?? new Date(spine.lifecycleState.lastChangedAt).toISOString();
181
+ const wr = spine.workspaceRootContext;
182
+ const needsRootHint = wr.resolution === "unknown";
80
183
  return {
81
- ok: true,
184
+ ok: false,
185
+ surfaceMode: "host_safe_carrier",
186
+ workspaceReadModelsEvaluated: false,
187
+ message: HOST_SAFE_LIMITATION_MESSAGE,
188
+ error: {
189
+ code: "WORKSPACE_READ_SURFACE_UNAVAILABLE",
190
+ message: "Aggregated status requires workspace state; the host-safe plugin does not load persisted read models on this surface.",
191
+ requiredUserInput: needsRootHint ? ["SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot"] : [],
192
+ nextStep: "run_workspace_second_nature_cli_or_full_runtime_package",
193
+ },
82
194
  data: {
83
- runtime: {
195
+ workspaceRootResolution: wr.resolution,
196
+ carrier: {
84
197
  host: "openclaw-plugin",
85
198
  serviceStatus: spine.runtimeHandle.ready ? "running" : "idle",
86
199
  updatedAt,
87
- },
88
- rhythm: {
89
- mode: "active",
90
- windowId: undefined,
91
- },
92
- quiet: {
93
- mode: "unknown",
94
- lastEvent: runtimeEvidence?.traceId,
95
- interrupted: undefined,
96
- },
97
- connectors: [],
98
- credentials: [],
99
- risk: {
100
- level: "low",
101
- flags: [],
200
+ lastRuntimeTraceId: runtimeEvidence?.traceId,
102
201
  },
103
202
  },
104
203
  };
105
204
  }
106
- function buildQuietPayload(scope) {
205
+ function buildQuietPayload(spine, scope) {
206
+ const wr = spine.workspaceRootContext;
107
207
  return {
108
- ok: true,
208
+ ok: false,
209
+ surfaceMode: "host_safe_carrier",
210
+ workspaceReadModelsEvaluated: false,
211
+ message: HOST_SAFE_LIMITATION_MESSAGE,
212
+ error: {
213
+ code: "QUIET_READ_SURFACE_UNAVAILABLE",
214
+ message: "Quiet read surface requires workspace runtime; not evaluated in host-safe carrier mode.",
215
+ requiredUserInput: wr.resolution === "unknown" ? ["SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot"] : [],
216
+ nextStep: "run_workspace_second_nature_cli_or_full_runtime_package",
217
+ },
109
218
  data: {
110
219
  scope,
111
- mode: "unknown",
112
- sourceCount: 0,
113
- reportCount: 0,
114
- recentJournalCount: 0,
220
+ evaluated: false,
221
+ unavailableReason: "host_safe_carrier_no_workspace_db",
222
+ workspaceRootResolution: wr.resolution,
115
223
  },
116
224
  };
117
225
  }
118
- function buildReportPayload(day) {
226
+ function buildReportPayload(spine, day) {
227
+ const wr = spine.workspaceRootContext;
119
228
  return {
120
- ok: true,
229
+ ok: false,
230
+ surfaceMode: "host_safe_carrier",
231
+ workspaceReadModelsEvaluated: false,
232
+ message: HOST_SAFE_LIMITATION_MESSAGE,
233
+ error: {
234
+ code: "REPORT_READ_SURFACE_UNAVAILABLE",
235
+ message: "Daily report artifacts require workspace runtime.",
236
+ requiredUserInput: wr.resolution === "unknown" ? ["SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot"] : [],
237
+ nextStep: "run_workspace_second_nature_cli_or_full_runtime_package",
238
+ },
121
239
  data: {
240
+ evaluated: false,
241
+ unavailableReason: "host_safe_carrier_no_workspace_db",
122
242
  day: day && day.trim() ? day : new Date().toISOString().slice(0, 10),
123
- summary: "",
124
- highlights: [],
125
- sourceRefs: [],
243
+ workspaceRootResolution: wr.resolution,
126
244
  },
127
245
  };
128
246
  }
129
- function buildSessionPayload(sessionId) {
247
+ function buildSessionPayload(spine, sessionId) {
130
248
  if (!sessionId) {
131
249
  return {
132
250
  ok: false,
@@ -138,26 +256,44 @@ function buildSessionPayload(sessionId) {
138
256
  },
139
257
  };
140
258
  }
259
+ const wr = spine.workspaceRootContext;
141
260
  return {
142
- ok: true,
261
+ ok: false,
262
+ surfaceMode: "host_safe_carrier",
263
+ workspaceReadModelsEvaluated: false,
264
+ message: HOST_SAFE_LIMITATION_MESSAGE,
265
+ error: {
266
+ code: "SESSION_READ_SURFACE_UNAVAILABLE",
267
+ message: "Session analytics require workspace state database.",
268
+ requiredUserInput: wr.resolution === "unknown" ? ["SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot"] : [],
269
+ nextStep: "run_workspace_second_nature_cli_or_full_runtime_package",
270
+ },
143
271
  data: {
144
272
  requestedSessionId: sessionId,
145
- traceId: sessionId,
146
- decisionCount: 0,
147
- attemptCount: 0,
148
- governanceCount: 0,
149
- keyFactors: [],
150
- evidenceRefs: [],
273
+ evaluated: false,
274
+ unavailableReason: "host_safe_carrier_no_workspace_db",
275
+ workspaceRootResolution: wr.resolution,
151
276
  },
152
277
  };
153
278
  }
154
- function buildCredentialPayload(platformId) {
279
+ function buildCredentialPayload(spine, platformId) {
280
+ const wr = spine.workspaceRootContext;
155
281
  return {
156
- ok: true,
282
+ ok: false,
283
+ surfaceMode: "host_safe_carrier",
284
+ workspaceReadModelsEvaluated: false,
285
+ message: HOST_SAFE_LIMITATION_MESSAGE,
286
+ error: {
287
+ code: "CREDENTIAL_READ_SURFACE_UNAVAILABLE",
288
+ message: "Credential inspection requires workspace runtime on this surface.",
289
+ requiredUserInput: wr.resolution === "unknown" ? ["SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot"] : [],
290
+ nextStep: "run_workspace_second_nature_cli_or_full_runtime_package",
291
+ },
157
292
  data: {
158
- platformId: platformId && platformId.trim() ? platformId : "unknown",
159
- status: "missing",
160
- nextStep: "provide_credential_context",
293
+ platformId: platformId && platformId.trim() ? platformId : undefined,
294
+ evaluated: false,
295
+ unavailableReason: "host_safe_carrier_no_workspace_db",
296
+ workspaceRootResolution: wr.resolution,
161
297
  },
162
298
  };
163
299
  }
@@ -183,44 +319,79 @@ function buildExplainPayload(spine, subjectRaw) {
183
319
  return createUnavailableActionError("EXPLAIN_SUBJECT_REQUIRES_ID", "subject must include identifier", ["subject"], "reinvoke_explain_with_supported_subject");
184
320
  }
185
321
  if (code === "explain_subject_unsupported") {
186
- return createUnavailableActionError("EXPLAIN_SUBJECT_UNSUPPORTED", "supported subjects are decision:<id>, platform:<id>, outreach:<id>, soul:<id>", ["subject"], "reinvoke_explain_with_supported_subject");
322
+ return createUnavailableActionError("EXPLAIN_SUBJECT_UNSUPPORTED", "supported subjects include decision:, platform:, outreach:, soul:, fallback:, delivery:, probe:, report:, source:", ["subject"], "reinvoke_explain_with_supported_subject");
187
323
  }
188
324
  return createUnavailableActionError("EXPLAIN_SUBJECT_INVALID", "invalid explain subject", ["subject"], "reinvoke_explain_with_supported_subject");
189
325
  }
190
- const runtimeEvidence = latestRuntimeEvidence(spine);
326
+ const wr = spine.workspaceRootContext;
191
327
  return {
192
- ok: true,
328
+ ok: false,
329
+ surfaceMode: "host_safe_carrier",
330
+ workspaceReadModelsEvaluated: false,
331
+ message: HOST_SAFE_LIMITATION_MESSAGE,
332
+ error: {
333
+ code: "EXPLAIN_READ_SURFACE_UNAVAILABLE",
334
+ message: "Evidence-backed explain requires persisted workspace read models; host-safe carrier did not evaluate operator explain (CH-11-02).",
335
+ requiredUserInput: wr.resolution === "unknown" ? ["SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot"] : [],
336
+ nextStep: "run_workspace_second_nature_cli_or_full_runtime_package",
337
+ },
193
338
  data: {
194
339
  subjectType: subject.subjectType,
195
- conclusion: "Plugin surface is loaded in host-safe mode with a minimal activation spine.",
196
- keyFactors: [
197
- "synchronous_register",
198
- `subject:${subject.subjectId}`,
199
- runtimeEvidence?.capability ?? "runtime.activate",
200
- ],
201
- evidenceRefs: [
202
- runtimeEvidence?.traceId ?? `${INTERNAL_RUNTIME_TRACE_PREFIX}none`,
203
- `subject:${subjectRaw.trim()}`,
204
- "host_safe_mode",
205
- ],
206
- nextStep: "use full workspace runtime for evidence-backed explain details",
340
+ evaluated: false,
341
+ workspaceRootResolution: wr.resolution,
207
342
  },
208
343
  };
209
344
  }
345
+ async function buildStorageSmokePayload(input) {
346
+ try {
347
+ const mod = await import("./runtime/storage/bootstrap/storage-mode-smoke.js");
348
+ const runRepairFixture = Boolean(input?.runRepairFixture);
349
+ const workspaceRoot = typeof input?.workspaceRoot === "string" ? input.workspaceRoot : undefined;
350
+ const data = await mod.runStorageModeSmoke({ runRepairFixture, workspaceRoot });
351
+ return { ok: true, data };
352
+ }
353
+ catch (error) {
354
+ return {
355
+ ok: false,
356
+ message: error instanceof Error ? error.message : String(error),
357
+ error: {
358
+ code: "STORAGE_SMOKE_LOAD_FAILED",
359
+ message: "Could not load packaged storage-mode smoke module",
360
+ nextStep: "rebuild_plugin_runtime_package",
361
+ },
362
+ };
363
+ }
364
+ }
365
+ function buildFallbackHostSafePayload(ref) {
366
+ if (!ref?.trim()) {
367
+ return {
368
+ ok: false,
369
+ error: {
370
+ code: "MISSING_FALLBACK_REF",
371
+ message: "fallback requires ref (e.g. fallback:…)",
372
+ requiredUserInput: ["ref"],
373
+ nextStep: "reinvoke_with_ref",
374
+ },
375
+ };
376
+ }
377
+ return createUnavailableActionError("HOST_SAFE_FALLBACK_VIEW_UNAVAILABLE", "Operator fallback view requires workspace state database; host-safe plugin cannot read persisted fallback artifacts.", ["ref"], "run_workspace_second_nature_cli_or_full_runtime_package");
378
+ }
210
379
  function buildHeartbeatCheckPayload(spine, input) {
211
380
  const runtimeEvidence = latestRuntimeEvidence(spine);
212
381
  const updatedAt = runtimeEvidence?.createdAt ?? new Date(spine.lifecycleState.lastChangedAt).toISOString();
213
382
  const timestamp = typeof input?.timestamp === "string" && input.timestamp.trim().length > 0 ? input.timestamp : updatedAt;
383
+ const wr = spine.workspaceRootContext;
214
384
  return {
215
385
  ok: true,
216
- status: "heartbeat_ok",
217
- heartbeat: "HEARTBEAT_OK",
386
+ status: "runtime_carrier_only",
387
+ livedExperienceLoopClaimed: false,
218
388
  scope: "rhythm",
219
389
  trigger: "heartbeat_bridge",
220
- reasons: ["host_safe_bridge_ready"],
221
- nextAction: "continue",
222
- message: "Host-safe heartbeat bridge acknowledged the round. No additional action is required from this surface.",
390
+ reasons: ["runtime_carrier_only", "host_safe_bridge_ack"],
391
+ nextAction: "continue_carrier_surface_only",
392
+ message: "Packaged carrier acknowledged this heartbeat round. This is not a full lived-experience decision loop; use the workspace CLI when read models are required.",
223
393
  data: {
394
+ workspaceRootResolution: wr.resolution,
224
395
  runtime: {
225
396
  host: "openclaw-plugin",
226
397
  serviceStatus: spine.runtimeHandle.ready ? "running" : "idle",
@@ -271,7 +442,7 @@ function createHostSafeRouter(spine) {
271
442
  return createUnavailableActionError("HOST_SAFE_CREDENTIAL_VERIFY_UNAVAILABLE", "credential verify is unavailable in the host-safe plugin package", ["verification_answer"], "run_workspace_runtime_or_reinstall_full_build");
272
443
  }
273
444
  const platformId = typeof input?.platformId === "string" ? input.platformId : undefined;
274
- return buildCredentialPayload(platformId);
445
+ return buildCredentialPayload(spine, platformId);
275
446
  },
276
447
  },
277
448
  {
@@ -279,7 +450,7 @@ function createHostSafeRouter(spine) {
279
450
  description: "Inspect Quiet lifecycle state",
280
451
  execute: async (input) => {
281
452
  const scope = typeof input?.scope === "string" ? input.scope : undefined;
282
- return buildQuietPayload(scope);
453
+ return buildQuietPayload(spine, scope);
283
454
  },
284
455
  },
285
456
  {
@@ -287,7 +458,7 @@ function createHostSafeRouter(spine) {
287
458
  description: "Show daily report artifacts",
288
459
  execute: async (input) => {
289
460
  const day = typeof input?.day === "string" ? input.day : undefined;
290
- return buildReportPayload(day);
461
+ return buildReportPayload(spine, day);
291
462
  },
292
463
  },
293
464
  {
@@ -295,7 +466,7 @@ function createHostSafeRouter(spine) {
295
466
  description: "Inspect continuity session details",
296
467
  execute: async (input) => {
297
468
  const sessionId = typeof input?.sessionId === "string" ? input.sessionId : undefined;
298
- return buildSessionPayload(sessionId);
469
+ return buildSessionPayload(spine, sessionId);
299
470
  },
300
471
  },
301
472
  {
@@ -316,6 +487,19 @@ function createHostSafeRouter(spine) {
316
487
  description: "Acknowledge the shipping heartbeat bridge round",
317
488
  execute: async (input) => buildHeartbeatCheckPayload(spine, input),
318
489
  },
490
+ {
491
+ name: "fallback",
492
+ description: "Operator-visible delivery fallback view (full workspace runtime required)",
493
+ execute: async (input) => {
494
+ const ref = typeof input?.ref === "string" ? input.ref.trim() : undefined;
495
+ return buildFallbackHostSafePayload(ref);
496
+ },
497
+ },
498
+ {
499
+ name: "storage_smoke",
500
+ description: "T4.1.4 storage mode smoke report (sql.js vs native probe)",
501
+ execute: async (input) => buildStorageSmokePayload(input),
502
+ },
319
503
  ];
320
504
  return {
321
505
  commands,
@@ -325,12 +509,14 @@ function createHostSafeRouter(spine) {
325
509
  };
326
510
  }
327
511
  function createActivationSpine() {
512
+ const workspaceRootContext = resolveWorkspaceRoot(undefined);
328
513
  const spine = {
329
514
  router: undefined,
330
- runtimeHandle: startRuntimeService({ workspaceRoot: process.cwd() }),
515
+ runtimeHandle: startRuntimeService({ workspaceRoot: workspaceRootContext.runtimeRoot }),
331
516
  lifecycleState: getLifecycleState(),
332
517
  serviceStartRecorded: false,
333
518
  runtimeEvidence: [],
519
+ workspaceRootContext,
334
520
  };
335
521
  spine.router = createHostSafeRouter(spine);
336
522
  return spine;
@@ -364,7 +550,14 @@ function recordRuntimeEvidence(spine, origin) {
364
550
  }
365
551
  function refreshRegistrationState() {
366
552
  const spine = ensureActivationSpine();
367
- spine.runtimeHandle = startRuntimeService({ workspaceRoot: process.cwd() });
553
+ const workspaceRootContext = resolveWorkspaceRoot(undefined);
554
+ const prev = spine.workspaceRootContext;
555
+ const changed = workspaceRootContext.runtimeRoot !== prev.runtimeRoot || workspaceRootContext.resolution !== prev.resolution;
556
+ if (changed) {
557
+ disposeWorkspaceOpsBridge();
558
+ }
559
+ spine.workspaceRootContext = workspaceRootContext;
560
+ spine.runtimeHandle = startRuntimeService({ workspaceRoot: workspaceRootContext.runtimeRoot });
368
561
  spine.lifecycleState = recordRegistration();
369
562
  spine.serviceStartRecorded = false;
370
563
  recordRuntimeEvidence(spine, "register");
@@ -442,6 +635,20 @@ function parseCommandInput(rawArgs) {
442
635
  command,
443
636
  input: rest.length > 0 ? { subject: rest.join(" ") } : undefined,
444
637
  };
638
+ case "fallback":
639
+ return {
640
+ ok: true,
641
+ command,
642
+ input: rest.length > 0 ? { ref: rest.join(" ") } : undefined,
643
+ };
644
+ case "storage_smoke": {
645
+ const wantRepair = rest[0] === "repair" || rest.includes("--repair");
646
+ return {
647
+ ok: true,
648
+ command,
649
+ input: wantRepair ? { runRepairFixture: true } : undefined,
650
+ };
651
+ }
445
652
  default:
446
653
  return {
447
654
  ok: true,
@@ -503,7 +710,7 @@ export default {
503
710
  text: JSON.stringify({ ok: false, command: parsed.command, message: "Unknown Second Nature command." }),
504
711
  };
505
712
  }
506
- const result = await resolved.execute(parsed.input);
713
+ const result = await routeSecondNatureCommand(spine, parsed.command, parsed.input);
507
714
  return {
508
715
  text: JSON.stringify(result),
509
716
  };
@@ -518,11 +725,16 @@ export default {
518
725
  properties: {
519
726
  command: { type: "string" },
520
727
  args: { type: "object", additionalProperties: true },
728
+ workspaceRoot: {
729
+ type: "string",
730
+ description: "Workspace root for packaged smoke/runtime alignment (optional; prefer SECOND_NATURE_WORKSPACE_ROOT).",
731
+ },
521
732
  },
522
733
  required: ["command"],
523
734
  },
524
735
  async execute(_id, params) {
525
736
  const spine = ensureActivationSpine();
737
+ syncWorkspaceRootFromTool(spine, params.workspaceRoot);
526
738
  const resolved = spine.router.resolve(params.command);
527
739
  if (!resolved) {
528
740
  return {
@@ -534,7 +746,7 @@ export default {
534
746
  ],
535
747
  };
536
748
  }
537
- const result = await resolved.execute(params.args);
749
+ const result = await routeSecondNatureCommand(spine, params.command, params.args);
538
750
  return {
539
751
  content: [
540
752
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "second-nature",
3
3
  "name": "Second Nature",
4
- "version": "0.1.8",
4
+ "version": "0.1.10",
5
5
  "entry": "./index.js",
6
6
  "description": "OpenClaw native plugin package with synchronous surface registration and a bundled runtime spine.",
7
7
  "capabilities": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haaaiawd/second-nature",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "OpenClaw native plugin with synchronous registration, a packaged runtime artifact, and operator-facing status/explain flows.",
5
5
  "keywords": [
6
6
  "openclaw",
@@ -17,6 +17,7 @@
17
17
  "main": "./index.js",
18
18
  "files": [
19
19
  "index.js",
20
+ "workspace-ops-bridge.js",
20
21
  "openclaw.plugin.json",
21
22
  "runtime/"
22
23
  ],
@@ -1,4 +1,5 @@
1
1
  import type { ActionBridge } from "../action-bridge.js";
2
+ import type { OpsRouter } from "../ops/ops-router.js";
2
3
  import type { CliReadModels } from "../read-models/index.js";
3
4
  export interface CliCommandDefinition {
4
5
  name: string;
@@ -8,5 +9,6 @@ export interface CliCommandDefinition {
8
9
  export interface CliCommandDeps {
9
10
  readModels: CliReadModels;
10
11
  actionBridge: ActionBridge;
12
+ opsRouter: OpsRouter;
11
13
  }
12
14
  export declare function createCliCommands(deps: CliCommandDeps): CliCommandDefinition[];
@@ -1,6 +1,8 @@
1
1
  import { credentialVerify } from "./credential.js";
2
2
  import { formatExplanation } from "../explain/format-explanation.js";
3
- import { resolveExplainSubject } from "../explain/resolve-subject.js";
3
+ import { explainSurfaceSubject } from "../explain/explain-surface-subject.js";
4
+ import { showOperatorFallback, OperatorFallbackNotFoundError } from "../ops/show-operator-fallback.js";
5
+ import { runStorageModeSmoke } from "../../storage/bootstrap/storage-mode-smoke.js";
4
6
  import { policySet } from "./policy.js";
5
7
  const notImplemented = async (command) => ({
6
8
  ok: false,
@@ -19,7 +21,7 @@ function explainSubjectError(code, message) {
19
21
  };
20
22
  }
21
23
  export function createCliCommands(deps) {
22
- const { readModels, actionBridge } = deps;
24
+ const { readModels, actionBridge, opsRouter } = deps;
23
25
  return [
24
26
  {
25
27
  name: "status",
@@ -113,9 +115,9 @@ export function createCliCommands(deps) {
113
115
  },
114
116
  };
115
117
  }
116
- let subject;
118
+ let model;
117
119
  try {
118
- subject = resolveExplainSubject(subjectRaw);
120
+ model = await explainSurfaceSubject(subjectRaw, readModels);
119
121
  }
120
122
  catch (error) {
121
123
  const code = error.message;
@@ -123,16 +125,69 @@ export function createCliCommands(deps) {
123
125
  return explainSubjectError("EXPLAIN_SUBJECT_REQUIRES_ID", "subject must include identifier");
124
126
  }
125
127
  if (code === "explain_subject_unsupported") {
126
- return explainSubjectError("EXPLAIN_SUBJECT_UNSUPPORTED", "supported subjects are decision:<id>, platform:<id>, outreach:<id>, soul:<id>");
128
+ return explainSubjectError("EXPLAIN_SUBJECT_UNSUPPORTED", "supported subjects include decision:, platform:, outreach:, soul:, fallback:, delivery:, probe:, report:, source:");
127
129
  }
128
130
  return explainSubjectError("EXPLAIN_SUBJECT_INVALID", "invalid explain subject");
129
131
  }
130
- const model = await readModels.explain(subject);
131
132
  return {
132
133
  ok: true,
133
134
  data: formatExplanation(model),
134
135
  };
135
136
  },
136
137
  },
138
+ {
139
+ name: "heartbeat_check",
140
+ description: "Workspace heartbeat_check ops surface (v5 HeartbeatSurfaceResult)",
141
+ execute: async (input) => {
142
+ const surface = await Promise.resolve(opsRouter.dispatch("heartbeat_check", input));
143
+ return surface;
144
+ },
145
+ },
146
+ {
147
+ name: "storage_smoke",
148
+ description: "T4.1.4 — report sql.js vs native SQLite probe and optional artifact→index repair fixture",
149
+ execute: async (input) => {
150
+ const runRepairFixture = Boolean(input?.runRepairFixture);
151
+ const workspaceRoot = typeof input?.workspaceRoot === "string" ? input.workspaceRoot : undefined;
152
+ const data = await runStorageModeSmoke({ runRepairFixture, workspaceRoot });
153
+ return { ok: true, data };
154
+ },
155
+ },
156
+ {
157
+ name: "fallback",
158
+ description: "Operator-visible delivery fallback view (status always not_sent)",
159
+ execute: async (input) => {
160
+ const ref = typeof input?.ref === "string" ? input.ref.trim() : "";
161
+ if (!ref) {
162
+ return {
163
+ ok: false,
164
+ error: {
165
+ code: "MISSING_FALLBACK_REF",
166
+ message: "fallback requires ref (e.g. fallback:…)",
167
+ requiredUserInput: ["ref"],
168
+ nextStep: "reinvoke_with_ref",
169
+ },
170
+ };
171
+ }
172
+ try {
173
+ const data = await showOperatorFallback(ref, readModels);
174
+ return { ok: true, data };
175
+ }
176
+ catch (error) {
177
+ if (error instanceof OperatorFallbackNotFoundError) {
178
+ return {
179
+ ok: false,
180
+ error: {
181
+ code: error.code,
182
+ message: error.message,
183
+ requiredUserInput: ["ref"],
184
+ nextStep: "verify_fallback_ref_from_delivery_audit",
185
+ },
186
+ };
187
+ }
188
+ throw error;
189
+ }
190
+ },
191
+ },
137
192
  ];
138
193
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Stable operator explain entry matching cli-system.detail §3.8 (T1.2.1).
3
+ *
4
+ * Core logic: parse subject string via resolveExplainSubject, delegate to read models.
5
+ */
6
+ import type { CliReadModels } from "../read-models/index.js";
7
+ import type { ExplainReadModel } from "../read-models/types.js";
8
+ export declare function explainSurfaceSubject(subjectRaw: string, readModels: CliReadModels): Promise<ExplainReadModel>;