@haaaiawd/second-nature 0.1.7 → 0.1.9

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 (184) hide show
  1. package/index.js +429 -96
  2. package/openclaw.plugin.json +1 -1
  3. package/package.json +1 -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 +20 -35
  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/execution-telemetry.d.ts +0 -1
  121. package/runtime/observability/services/governance-audit.d.ts +14 -0
  122. package/runtime/observability/services/governance-audit.js +25 -1
  123. package/runtime/observability/services/governance-plane-recorder.d.ts +47 -0
  124. package/runtime/observability/services/governance-plane-recorder.js +55 -0
  125. package/runtime/observability/services/lived-experience-audit.d.ts +97 -0
  126. package/runtime/observability/services/lived-experience-audit.js +161 -0
  127. package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -0
  128. package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -0
  129. package/runtime/storage/bootstrap/repair-gate.d.ts +17 -0
  130. package/runtime/storage/bootstrap/repair-gate.js +71 -0
  131. package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -0
  132. package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -0
  133. package/runtime/storage/db/index.js +49 -0
  134. package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -0
  135. package/runtime/storage/db/schema/delivery-attempts.js +13 -0
  136. package/runtime/storage/db/schema/index.d.ts +3 -0
  137. package/runtime/storage/db/schema/index.js +3 -0
  138. package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -0
  139. package/runtime/storage/db/schema/life-evidence-index.js +11 -0
  140. package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -0
  141. package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -0
  142. package/runtime/storage/db/schema/policies.d.ts +17 -0
  143. package/runtime/storage/db/schema/policies.js +1 -0
  144. package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -0
  145. package/runtime/storage/delivery/query-delivery-attempts.js +32 -0
  146. package/runtime/storage/delivery/types.d.ts +27 -0
  147. package/runtime/storage/delivery/types.js +1 -0
  148. package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -0
  149. package/runtime/storage/delivery/write-delivery-attempt.js +36 -0
  150. package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -0
  151. package/runtime/storage/fallback/load-operator-fallback.js +47 -0
  152. package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -0
  153. package/runtime/storage/fallback/operator-fallback-types.js +1 -0
  154. package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -0
  155. package/runtime/storage/fallback/operator-fallback-view.js +1 -0
  156. package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -0
  157. package/runtime/storage/fallback/write-operator-fallback.js +21 -0
  158. package/runtime/storage/index.d.ts +21 -0
  159. package/runtime/storage/index.js +14 -0
  160. package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -0
  161. package/runtime/storage/life-evidence/append-life-evidence.js +64 -0
  162. package/runtime/storage/life-evidence/types.d.ts +45 -0
  163. package/runtime/storage/life-evidence/types.js +6 -0
  164. package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -0
  165. package/runtime/storage/quiet/persist-quiet-artifact.js +22 -0
  166. package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -0
  167. package/runtime/storage/quiet/quiet-artifact-types.js +1 -0
  168. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -0
  169. package/runtime/storage/quiet/quiet-artifact-writer.js +56 -0
  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 +46 -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
package/index.js CHANGED
@@ -1,125 +1,432 @@
1
- // @ts-ignore packaged runtime declarations are generated under plugin/runtime
2
- import { createCliRuntimeDeps, createCommandRouter } from "./runtime/cli/index.js";
3
- import { DecisionLedger } from "./runtime/observability/services/decision-ledger.js";
4
- import { ExecutionTelemetry } from "./runtime/observability/services/execution-telemetry.js";
1
+ /**
2
+ * Host-safe Second Nature plugin surface.
3
+ *
4
+ * Core logic:
5
+ * - keep register(api) synchronous so OpenClaw captures services/command/tool before return
6
+ * - avoid importing CLI/runtime DB modules at module-evaluation time because the packaged
7
+ * runtime graph currently contains async sql.js bootstrap that breaks vm sandbox loading
8
+ * - expose a minimal in-memory activation spine so status/lifecycle stay truthful even when
9
+ * the full workspace runtime is not loaded inside the host
10
+ *
11
+ * Dependencies:
12
+ * - only imports runtime lifecycle/service modules that are synchronous at load time
13
+ *
14
+ * Boundaries:
15
+ * - read-only operator flows stay available through command/tool surface
16
+ * - structured mutating flows such as policy set / credential verify remain unavailable here
17
+ * - full evidence-backed workspace runtime can be reintroduced later behind a host-safe boundary
18
+ *
19
+ * Test coverage:
20
+ * - tests/integration/cli/plugin-runtime-registration.test.ts
21
+ * - tests/integration/cli/plugin-packaging-walkthrough.test.ts
22
+ */
5
23
  import { startRuntimeService, } from "./runtime/core/second-nature/runtime/service-entry.js";
6
24
  import { getLifecycleState, recordRegistration, } from "./runtime/core/second-nature/runtime/lifecycle-service.js";
7
- const INTERNAL_RUNTIME_PLATFORM_ID = "second-nature-runtime";
8
25
  const INTERNAL_RUNTIME_TRACE_PREFIX = "sn-runtime-";
9
- const INTERNAL_RUNTIME_CHANNEL = "plugin_host";
26
+ const HOST_SAFE_LIMITATION_MESSAGE = "Host-safe plugin package keeps synchronous register/load semantics, but mutating workspace runtime flows remain unavailable here.";
10
27
  let activationSpine = null;
11
- function createFallbackCommands() {
12
- const commandNames = ["status", "policy", "credential", "quiet", "report", "session", "audit", "explain"];
13
- return commandNames.map((name) => ({
14
- name,
15
- description: `Fallback command shell for ${name}`,
16
- execute: async (_input) => ({
28
+ function trimRuntimeEvidence(spine) {
29
+ if (spine.runtimeEvidence.length > 12) {
30
+ spine.runtimeEvidence.splice(0, spine.runtimeEvidence.length - 12);
31
+ }
32
+ }
33
+ function latestRuntimeEvidence(spine) {
34
+ return spine.runtimeEvidence[spine.runtimeEvidence.length - 1];
35
+ }
36
+ function createUnavailableActionError(code, message, requiredUserInput, nextStep) {
37
+ return {
38
+ ok: false,
39
+ error: {
40
+ code,
41
+ message,
42
+ requiredUserInput,
43
+ nextStep,
44
+ },
45
+ message: HOST_SAFE_LIMITATION_MESSAGE,
46
+ };
47
+ }
48
+ function parseExplainSubject(subjectRaw) {
49
+ const trimmed = subjectRaw.trim();
50
+ if (!trimmed) {
51
+ throw new Error("explain_subject_invalid");
52
+ }
53
+ const separatorIndex = trimmed.indexOf(":");
54
+ if (separatorIndex === -1) {
55
+ throw new Error("explain_subject_requires_id");
56
+ }
57
+ const kind = trimmed.slice(0, separatorIndex).trim();
58
+ const id = trimmed.slice(separatorIndex + 1).trim();
59
+ if (!id) {
60
+ throw new Error("explain_subject_requires_id");
61
+ }
62
+ switch (kind) {
63
+ case "decision":
64
+ return { subjectType: "decision", subjectId: id };
65
+ case "platform":
66
+ case "platform-selection":
67
+ return { subjectType: "platform-selection", subjectId: id };
68
+ case "outreach":
69
+ return { subjectType: "outreach", subjectId: id };
70
+ case "soul":
71
+ case "soul-change":
72
+ return { subjectType: "soul-change", subjectId: id };
73
+ case "fallback":
74
+ return { subjectType: "fallback", subjectId: id };
75
+ case "probe":
76
+ return { subjectType: "probe", subjectId: id };
77
+ case "report":
78
+ return { subjectType: "report", subjectId: id };
79
+ case "delivery":
80
+ return { subjectType: "delivery", subjectId: id };
81
+ case "source":
82
+ case "source_ref":
83
+ return { subjectType: "source_ref", subjectId: id };
84
+ default:
85
+ throw new Error("explain_subject_unsupported");
86
+ }
87
+ }
88
+ function buildStatusPayload(spine) {
89
+ const runtimeEvidence = latestRuntimeEvidence(spine);
90
+ const updatedAt = runtimeEvidence?.createdAt ?? new Date(spine.lifecycleState.lastChangedAt).toISOString();
91
+ return {
92
+ ok: true,
93
+ data: {
94
+ runtime: {
95
+ host: "openclaw-plugin",
96
+ serviceStatus: spine.runtimeHandle.ready ? "running" : "idle",
97
+ updatedAt,
98
+ },
99
+ rhythm: {
100
+ mode: "active",
101
+ windowId: undefined,
102
+ },
103
+ quiet: {
104
+ mode: "unknown",
105
+ lastEvent: runtimeEvidence?.traceId,
106
+ interrupted: undefined,
107
+ },
108
+ connectors: [],
109
+ credentials: [],
110
+ risk: {
111
+ level: "low",
112
+ flags: [],
113
+ },
114
+ },
115
+ };
116
+ }
117
+ function buildQuietPayload(scope) {
118
+ return {
119
+ ok: true,
120
+ data: {
121
+ scope,
122
+ mode: "unknown",
123
+ sourceCount: 0,
124
+ reportCount: 0,
125
+ recentJournalCount: 0,
126
+ },
127
+ };
128
+ }
129
+ function buildReportPayload(day) {
130
+ return {
131
+ ok: true,
132
+ data: {
133
+ day: day && day.trim() ? day : new Date().toISOString().slice(0, 10),
134
+ summary: "",
135
+ highlights: [],
136
+ sourceRefs: [],
137
+ },
138
+ };
139
+ }
140
+ function buildSessionPayload(sessionId) {
141
+ if (!sessionId) {
142
+ return {
17
143
  ok: false,
18
- command: name,
19
- message: "Plugin loaded in packaging fallback mode; reinstall full workspace build for command runtime.",
20
- }),
21
- }));
144
+ error: {
145
+ code: "MISSING_SESSION_ID",
146
+ message: "session show requires sessionId",
147
+ requiredUserInput: ["session_id"],
148
+ nextStep: "reinvoke_session_with_session_id",
149
+ },
150
+ };
151
+ }
152
+ return {
153
+ ok: true,
154
+ data: {
155
+ requestedSessionId: sessionId,
156
+ traceId: sessionId,
157
+ decisionCount: 0,
158
+ attemptCount: 0,
159
+ governanceCount: 0,
160
+ keyFactors: [],
161
+ evidenceRefs: [],
162
+ },
163
+ };
22
164
  }
23
- function createFallbackRouter() {
24
- const commands = createFallbackCommands();
165
+ function buildCredentialPayload(platformId) {
25
166
  return {
26
- commands,
27
- resolve(name) {
28
- return commands.find((command) => command.name === name);
167
+ ok: true,
168
+ data: {
169
+ platformId: platformId && platformId.trim() ? platformId : "unknown",
170
+ status: "missing",
171
+ nextStep: "provide_credential_context",
29
172
  },
30
173
  };
31
174
  }
32
- function ensureActivationSpine() {
33
- if (activationSpine) {
34
- return activationSpine;
175
+ function buildExplainPayload(spine, subjectRaw) {
176
+ if (!subjectRaw?.trim()) {
177
+ return {
178
+ ok: false,
179
+ error: {
180
+ code: "MISSING_EXPLAIN_SUBJECT",
181
+ message: "explain requires subject",
182
+ requiredUserInput: ["subject"],
183
+ nextStep: "reinvoke_explain_with_subject",
184
+ },
185
+ };
35
186
  }
187
+ let subject;
36
188
  try {
37
- const runtimeDeps = createCliRuntimeDeps();
38
- const router = createCommandRouter({ deps: runtimeDeps });
39
- activationSpine = {
40
- runtimeDeps,
41
- router,
42
- decisionLedger: new DecisionLedger(runtimeDeps.observabilityDb),
43
- executionTelemetry: new ExecutionTelemetry(runtimeDeps.observabilityDb),
44
- runtimeHandle: startRuntimeService({ workspaceRoot: process.cwd() }),
45
- lifecycleState: getLifecycleState(),
46
- serviceStartRecorded: false,
189
+ subject = parseExplainSubject(subjectRaw);
190
+ }
191
+ catch (error) {
192
+ const code = error.message;
193
+ if (code === "explain_subject_requires_id") {
194
+ return createUnavailableActionError("EXPLAIN_SUBJECT_REQUIRES_ID", "subject must include identifier", ["subject"], "reinvoke_explain_with_supported_subject");
195
+ }
196
+ if (code === "explain_subject_unsupported") {
197
+ return createUnavailableActionError("EXPLAIN_SUBJECT_UNSUPPORTED", "supported subjects include decision:, platform:, outreach:, soul:, fallback:, delivery:, probe:, report:, source:", ["subject"], "reinvoke_explain_with_supported_subject");
198
+ }
199
+ return createUnavailableActionError("EXPLAIN_SUBJECT_INVALID", "invalid explain subject", ["subject"], "reinvoke_explain_with_supported_subject");
200
+ }
201
+ const runtimeEvidence = latestRuntimeEvidence(spine);
202
+ return {
203
+ ok: true,
204
+ data: {
205
+ subjectType: subject.subjectType,
206
+ conclusion: "Plugin surface is loaded in host-safe mode with a minimal activation spine.",
207
+ keyFactors: [
208
+ "synchronous_register",
209
+ `subject:${subject.subjectId}`,
210
+ runtimeEvidence?.capability ?? "runtime.activate",
211
+ ],
212
+ evidenceRefs: [
213
+ runtimeEvidence?.traceId ?? `${INTERNAL_RUNTIME_TRACE_PREFIX}none`,
214
+ `subject:${subjectRaw.trim()}`,
215
+ "host_safe_mode",
216
+ ],
217
+ nextStep: "use full workspace runtime for evidence-backed explain details",
218
+ },
219
+ };
220
+ }
221
+ async function buildStorageSmokePayload(input) {
222
+ try {
223
+ const mod = await import("./runtime/storage/bootstrap/storage-mode-smoke.js");
224
+ const runRepairFixture = Boolean(input?.runRepairFixture);
225
+ const workspaceRoot = typeof input?.workspaceRoot === "string" ? input.workspaceRoot : undefined;
226
+ const data = await mod.runStorageModeSmoke({ runRepairFixture, workspaceRoot });
227
+ return { ok: true, data };
228
+ }
229
+ catch (error) {
230
+ return {
231
+ ok: false,
232
+ message: error instanceof Error ? error.message : String(error),
233
+ error: {
234
+ code: "STORAGE_SMOKE_LOAD_FAILED",
235
+ message: "Could not load packaged storage-mode smoke module",
236
+ nextStep: "rebuild_plugin_runtime_package",
237
+ },
47
238
  };
48
- return activationSpine;
49
239
  }
50
- catch {
51
- activationSpine = {
52
- router: createFallbackRouter(),
53
- runtimeHandle: { ready: true, version: "0.1.7-minimal", close() { } },
54
- lifecycleState: getLifecycleState(),
55
- serviceStartRecorded: false,
240
+ }
241
+ function buildFallbackHostSafePayload(ref) {
242
+ if (!ref?.trim()) {
243
+ return {
244
+ ok: false,
245
+ error: {
246
+ code: "MISSING_FALLBACK_REF",
247
+ message: "fallback requires ref (e.g. fallback:…)",
248
+ requiredUserInput: ["ref"],
249
+ nextStep: "reinvoke_with_ref",
250
+ },
56
251
  };
57
- return activationSpine;
58
252
  }
253
+ 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");
59
254
  }
60
- function refreshRegistrationState() {
61
- const spine = ensureActivationSpine();
62
- spine.runtimeHandle = startRuntimeService({ workspaceRoot: process.cwd() });
63
- spine.lifecycleState = recordRegistration();
64
- spine.serviceStartRecorded = false;
65
- recordRuntimeEvidence(spine, "register");
255
+ function buildHeartbeatCheckPayload(spine, input) {
256
+ const runtimeEvidence = latestRuntimeEvidence(spine);
257
+ const updatedAt = runtimeEvidence?.createdAt ?? new Date(spine.lifecycleState.lastChangedAt).toISOString();
258
+ const timestamp = typeof input?.timestamp === "string" && input.timestamp.trim().length > 0 ? input.timestamp : updatedAt;
259
+ return {
260
+ ok: true,
261
+ status: "heartbeat_ok",
262
+ heartbeat: "HEARTBEAT_OK",
263
+ scope: "rhythm",
264
+ trigger: "heartbeat_bridge",
265
+ reasons: ["host_safe_bridge_ready"],
266
+ nextAction: "continue",
267
+ message: "Host-safe heartbeat bridge acknowledged the round. No additional action is required from this surface.",
268
+ data: {
269
+ runtime: {
270
+ host: "openclaw-plugin",
271
+ serviceStatus: spine.runtimeHandle.ready ? "running" : "idle",
272
+ updatedAt,
273
+ },
274
+ surface: {
275
+ tool: "second_nature_ops",
276
+ command: "second-nature heartbeat_check",
277
+ },
278
+ bridge: {
279
+ timestamp,
280
+ sessionContextProvided: typeof input?.sessionContext === "string" && input.sessionContext.trim().length > 0,
281
+ heartbeatChecklistProvided: typeof input?.heartbeatChecklist === "string" && input.heartbeatChecklist.trim().length > 0,
282
+ serviceEntryMode: "runtime_carrier_only",
283
+ },
284
+ },
285
+ };
286
+ }
287
+ function createHostSafeRouter(spine) {
288
+ const notImplemented = async (command) => ({
289
+ ok: false,
290
+ command,
291
+ message: HOST_SAFE_LIMITATION_MESSAGE,
292
+ });
293
+ const commands = [
294
+ {
295
+ name: "status",
296
+ description: "Show aggregated Second Nature status",
297
+ execute: async () => buildStatusPayload(spine),
298
+ },
299
+ {
300
+ name: "policy",
301
+ description: "Write or inspect policy state",
302
+ execute: async (input) => {
303
+ const action = typeof input?.action === "string" ? input.action : "show";
304
+ if (action === "set") {
305
+ return createUnavailableActionError("HOST_SAFE_POLICY_SET_UNAVAILABLE", "policy set is unavailable in the host-safe plugin package", ["social_daily_limit", "quiet_enabled"], "run_workspace_runtime_or_reinstall_full_build");
306
+ }
307
+ return notImplemented("policy");
308
+ },
309
+ },
310
+ {
311
+ name: "credential",
312
+ description: "Inspect or recover credential state",
313
+ execute: async (input) => {
314
+ const action = typeof input?.action === "string" ? input.action : "show";
315
+ if (action === "verify") {
316
+ 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");
317
+ }
318
+ const platformId = typeof input?.platformId === "string" ? input.platformId : undefined;
319
+ return buildCredentialPayload(platformId);
320
+ },
321
+ },
322
+ {
323
+ name: "quiet",
324
+ description: "Inspect Quiet lifecycle state",
325
+ execute: async (input) => {
326
+ const scope = typeof input?.scope === "string" ? input.scope : undefined;
327
+ return buildQuietPayload(scope);
328
+ },
329
+ },
330
+ {
331
+ name: "report",
332
+ description: "Show daily report artifacts",
333
+ execute: async (input) => {
334
+ const day = typeof input?.day === "string" ? input.day : undefined;
335
+ return buildReportPayload(day);
336
+ },
337
+ },
338
+ {
339
+ name: "session",
340
+ description: "Inspect continuity session details",
341
+ execute: async (input) => {
342
+ const sessionId = typeof input?.sessionId === "string" ? input.sessionId : undefined;
343
+ return buildSessionPayload(sessionId);
344
+ },
345
+ },
346
+ {
347
+ name: "audit",
348
+ description: "Inspect audit and evidence views",
349
+ execute: async () => notImplemented("audit"),
350
+ },
351
+ {
352
+ name: "explain",
353
+ description: "Answer why-question explain requests",
354
+ execute: async (input) => {
355
+ const subject = typeof input?.subject === "string" ? input.subject : undefined;
356
+ return buildExplainPayload(spine, subject);
357
+ },
358
+ },
359
+ {
360
+ name: "heartbeat_check",
361
+ description: "Acknowledge the shipping heartbeat bridge round",
362
+ execute: async (input) => buildHeartbeatCheckPayload(spine, input),
363
+ },
364
+ {
365
+ name: "fallback",
366
+ description: "Operator-visible delivery fallback view (full workspace runtime required)",
367
+ execute: async (input) => {
368
+ const ref = typeof input?.ref === "string" ? input.ref.trim() : undefined;
369
+ return buildFallbackHostSafePayload(ref);
370
+ },
371
+ },
372
+ {
373
+ name: "storage_smoke",
374
+ description: "T4.1.4 storage mode smoke report (sql.js vs native probe)",
375
+ execute: async (input) => buildStorageSmokePayload(input),
376
+ },
377
+ ];
378
+ return {
379
+ commands,
380
+ resolve(name) {
381
+ return commands.find((command) => command.name === name);
382
+ },
383
+ };
384
+ }
385
+ function createActivationSpine() {
386
+ const spine = {
387
+ router: undefined,
388
+ runtimeHandle: startRuntimeService({ workspaceRoot: process.cwd() }),
389
+ lifecycleState: getLifecycleState(),
390
+ serviceStartRecorded: false,
391
+ runtimeEvidence: [],
392
+ };
393
+ spine.router = createHostSafeRouter(spine);
66
394
  return spine;
67
395
  }
68
- function recordRuntimeEvidence(spine, origin) {
69
- const decisionLedger = spine.decisionLedger;
70
- const executionTelemetry = spine.executionTelemetry;
71
- if (!decisionLedger || !executionTelemetry) {
72
- return;
396
+ function ensureActivationSpine() {
397
+ if (activationSpine) {
398
+ return activationSpine;
73
399
  }
400
+ activationSpine = createActivationSpine();
401
+ return activationSpine;
402
+ }
403
+ function recordRuntimeEvidence(spine, origin) {
74
404
  if (origin === "service_start" && spine.serviceStartRecorded) {
75
405
  return;
76
406
  }
77
407
  if (origin === "service_start") {
78
408
  spine.serviceStartRecorded = true;
79
409
  }
80
- const now = new Date().toISOString();
81
- const traceId = `${INTERNAL_RUNTIME_TRACE_PREFIX}${origin}-${spine.lifecycleState.registerCount}-${Date.now()}`;
82
- const decisionId = `decision-${traceId}`;
83
- const tickId = `tick-${traceId}`;
84
- const intentId = `intent-${traceId}`;
85
- const capability = origin === "register"
86
- ? spine.lifecycleState.registerCount === 1
87
- ? "runtime.activate"
88
- : "runtime.reload"
89
- : "runtime.heartbeat";
90
- void (async () => {
91
- await decisionLedger.recordHeartbeatDecision({
92
- id: decisionId,
93
- tickId,
94
- traceId,
95
- intentId,
96
- runtimeScope: "rhythm",
97
- triggerSource: "heartbeat_bridge",
98
- decisionStatus: "heartbeat_ok",
99
- reasons: [
100
- `origin:${origin}`,
101
- `phase:${spine.lifecycleState.phase}`,
102
- `registrations:${spine.lifecycleState.registerCount}`,
103
- ],
104
- mode: "active",
105
- createdAt: now,
106
- });
107
- await executionTelemetry.startAttempt({
108
- traceId,
109
- decisionId,
110
- intentId,
111
- platformId: INTERNAL_RUNTIME_PLATFORM_ID,
112
- capability,
113
- channel: INTERNAL_RUNTIME_CHANNEL,
114
- status: "started",
115
- startedAt: now,
116
- });
117
- await executionTelemetry.completeAttempt(traceId, "succeeded", "committed");
118
- })().catch(() => {
119
- if (origin === "service_start") {
120
- spine.serviceStartRecorded = false;
121
- }
410
+ spine.runtimeEvidence.push({
411
+ traceId: `${INTERNAL_RUNTIME_TRACE_PREFIX}${origin}-${spine.lifecycleState.registerCount}-${Date.now()}`,
412
+ capability: origin === "register"
413
+ ? spine.lifecycleState.registerCount === 1
414
+ ? "runtime.activate"
415
+ : "runtime.reload"
416
+ : "runtime.heartbeat",
417
+ origin,
418
+ createdAt: new Date().toISOString(),
419
+ status: "succeeded",
122
420
  });
421
+ trimRuntimeEvidence(spine);
422
+ }
423
+ function refreshRegistrationState() {
424
+ const spine = ensureActivationSpine();
425
+ spine.runtimeHandle = startRuntimeService({ workspaceRoot: process.cwd() });
426
+ spine.lifecycleState = recordRegistration();
427
+ spine.serviceStartRecorded = false;
428
+ recordRuntimeEvidence(spine, "register");
429
+ return spine;
123
430
  }
124
431
  function parseCommandInput(rawArgs) {
125
432
  const tokens = rawArgs?.trim().split(/\s+/).filter(Boolean) ?? [];
@@ -176,12 +483,37 @@ function parseCommandInput(rawArgs) {
176
483
  command,
177
484
  input: rest[0] ? { platformId: rest[0] } : undefined,
178
485
  };
486
+ case "heartbeat_check":
487
+ return {
488
+ ok: true,
489
+ command,
490
+ input: rest.length > 0
491
+ ? {
492
+ timestamp: rest[0],
493
+ sessionContext: rest.length > 1 ? rest.slice(1).join(" ") : undefined,
494
+ }
495
+ : undefined,
496
+ };
179
497
  case "explain":
180
498
  return {
181
499
  ok: true,
182
500
  command,
183
501
  input: rest.length > 0 ? { subject: rest.join(" ") } : undefined,
184
502
  };
503
+ case "fallback":
504
+ return {
505
+ ok: true,
506
+ command,
507
+ input: rest.length > 0 ? { ref: rest.join(" ") } : undefined,
508
+ };
509
+ case "storage_smoke": {
510
+ const wantRepair = rest[0] === "repair" || rest.includes("--repair");
511
+ return {
512
+ ok: true,
513
+ command,
514
+ input: wantRepair ? { runRepairFixture: true } : undefined,
515
+ };
516
+ }
185
517
  default:
186
518
  return {
187
519
  ok: true,
@@ -221,7 +553,6 @@ export default {
221
553
  name: "Second Nature",
222
554
  description: "Registers command/tool/service surface with load-reload lifecycle semantics.",
223
555
  register(api) {
224
- const spine = refreshRegistrationState();
225
556
  const runtimeService = createRuntimeService();
226
557
  const lifecycleService = createLifecycleService();
227
558
  api.registerService(runtimeService);
@@ -231,6 +562,7 @@ export default {
231
562
  description: "Route Agent-facing operational commands for Second Nature.",
232
563
  acceptsArgs: true,
233
564
  handler: async (ctx) => {
565
+ const spine = ensureActivationSpine();
234
566
  const parsed = parseCommandInput(ctx.args);
235
567
  if (!parsed.ok) {
236
568
  return {
@@ -262,6 +594,7 @@ export default {
262
594
  required: ["command"],
263
595
  },
264
596
  async execute(_id, params) {
597
+ const spine = ensureActivationSpine();
265
598
  const resolved = spine.router.resolve(params.command);
266
599
  if (!resolved) {
267
600
  return {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "second-nature",
3
3
  "name": "Second Nature",
4
- "version": "0.1.7",
4
+ "version": "0.1.9",
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.7",
3
+ "version": "0.1.9",
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",
@@ -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[];