@haaaiawd/second-nature 0.1.33 → 0.1.34
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.
package/index.js
CHANGED
|
@@ -71,7 +71,7 @@ process.stderr.write("[second-nature] module evaluated\n");
|
|
|
71
71
|
const INTERNAL_RUNTIME_TRACE_PREFIX = "sn-runtime-";
|
|
72
72
|
const HOST_SAFE_LIMITATION_MESSAGE = "Host-safe plugin package keeps synchronous register/load semantics, but mutating workspace runtime flows remain unavailable here.";
|
|
73
73
|
const SETUP_MARKER_RELATIVE_PATH = path.join(".second-nature", "setup", "agent-inner-guide-ack.json");
|
|
74
|
-
const SETUP_GUIDE_VERSION = "0.1.
|
|
74
|
+
const SETUP_GUIDE_VERSION = "0.1.34";
|
|
75
75
|
const SETUP_COMMANDS = new Set(["setup_hint", "setup_ack"]);
|
|
76
76
|
let activationSpine = null;
|
|
77
77
|
/** T1.1.4 — lazily opened full read bridge; closed when workspace root / resolution changes. */
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "second-nature",
|
|
3
3
|
"name": "Second Nature",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.34",
|
|
5
5
|
"description": "OpenClaw native plugin with synchronous surface registration and bundled runtime spine. Set SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot to the same path as the agent workspace. Agent inner guide is packaged as agent-inner-guide.md. v7 ops surface: self_health, tool_affordance, heartbeat_digest, snapshot:capture, narrative:diff, timeline, restore, runtime_secret_bootstrap.",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
package/package.json
CHANGED
|
@@ -154,6 +154,96 @@ function registerConnectorForWetProbe(input) {
|
|
|
154
154
|
credentialTypes: ["runtime_ops_probe"],
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
|
+
async function captureRuntimeSnapshot(deps, input) {
|
|
158
|
+
const generatedAt = new Date().toISOString();
|
|
159
|
+
if (!deps.state || !deps.restoreSnapshotStore) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
command: "snapshot:capture",
|
|
163
|
+
runtimeMode: "unavailable",
|
|
164
|
+
surfaceMode: "cli",
|
|
165
|
+
generatedAt,
|
|
166
|
+
error: {
|
|
167
|
+
code: "SNAPSHOT_CAPTURE_DEPS_UNAVAILABLE",
|
|
168
|
+
message: "snapshot:capture requires state DB and RestoreSnapshotStore in OpsRouterDeps",
|
|
169
|
+
nextStep: "wire_state_and_restore_snapshot_store_into_ops_router",
|
|
170
|
+
},
|
|
171
|
+
warnings: [],
|
|
172
|
+
sourceRefs: [],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const snapshotId = textInput(input, "snapshotId") ??
|
|
176
|
+
`snapshot:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
|
|
177
|
+
const requestedKinds = coerceRestorableKinds(input?.entityWhitelist) ?? [...DEFAULT_SNAPSHOT_KINDS];
|
|
178
|
+
const rowCounts = {};
|
|
179
|
+
const warnings = [];
|
|
180
|
+
for (const kind of requestedKinds) {
|
|
181
|
+
const table = SNAPSHOT_TABLE_BY_KIND[kind];
|
|
182
|
+
if (!tableExists(deps.state, table)) {
|
|
183
|
+
rowCounts[kind] = 0;
|
|
184
|
+
warnings.push(`table_missing:${kind}:${table}`);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
rowCounts[kind] = readRowsFromTable(deps.state, table).length;
|
|
188
|
+
}
|
|
189
|
+
const historyStore = createHistoryDigestStore(deps.state);
|
|
190
|
+
const previousHash = (await historyStore.listNarrativeTimeline({ limit: 1 }))[0]?.currentHash ?? "";
|
|
191
|
+
const delta = buildSnapshotNarrativeDelta(input, snapshotId, rowCounts);
|
|
192
|
+
const currentHash = hashNarrativeSnapshot({
|
|
193
|
+
previousHash,
|
|
194
|
+
snapshotId,
|
|
195
|
+
delta,
|
|
196
|
+
createdAt: generatedAt,
|
|
197
|
+
});
|
|
198
|
+
await historyStore.appendNarrativeTimeline({
|
|
199
|
+
timelineId: snapshotId,
|
|
200
|
+
entryType: "owner.override",
|
|
201
|
+
subjectId: textInput(input, "subjectId") ?? snapshotId,
|
|
202
|
+
delta,
|
|
203
|
+
previousHash,
|
|
204
|
+
currentHash,
|
|
205
|
+
createdAt: generatedAt,
|
|
206
|
+
});
|
|
207
|
+
const payload = {};
|
|
208
|
+
const capturedKinds = [];
|
|
209
|
+
for (const kind of requestedKinds) {
|
|
210
|
+
const table = SNAPSHOT_TABLE_BY_KIND[kind];
|
|
211
|
+
if (!tableExists(deps.state, table))
|
|
212
|
+
continue;
|
|
213
|
+
const rows = readRowsFromTable(deps.state, table);
|
|
214
|
+
rowCounts[kind] = rows.length;
|
|
215
|
+
if (rows.length > 0) {
|
|
216
|
+
payload[kind] = rows;
|
|
217
|
+
capturedKinds.push(kind);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const snapshot = await deps.restoreSnapshotStore.captureSnapshot({
|
|
221
|
+
snapshotId,
|
|
222
|
+
entityWhitelist: requestedKinds,
|
|
223
|
+
payload,
|
|
224
|
+
capturedAt: generatedAt,
|
|
225
|
+
});
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
command: "snapshot:capture",
|
|
229
|
+
runtimeMode: "workspace_full_runtime",
|
|
230
|
+
surfaceMode: "cli",
|
|
231
|
+
generatedAt,
|
|
232
|
+
data: {
|
|
233
|
+
snapshotId: snapshot.snapshotId,
|
|
234
|
+
capturedAt: snapshot.capturedAt,
|
|
235
|
+
entityWhitelist: snapshot.entityWhitelist,
|
|
236
|
+
capturedKinds,
|
|
237
|
+
rowCounts,
|
|
238
|
+
narrativeVersion: snapshotId,
|
|
239
|
+
},
|
|
240
|
+
warnings,
|
|
241
|
+
sourceRefs: [
|
|
242
|
+
"storage/services/restore-snapshot-store.ts",
|
|
243
|
+
"storage/services/history-digest-store.ts",
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
157
247
|
/**
|
|
158
248
|
* T1.2.8 — static local adapter: all checks return `unknown` when no real host is available.
|
|
159
249
|
* Allows `capability_probe` to be called from CLI / workspace bridge without requiring a live host.
|
|
@@ -194,7 +284,7 @@ export function createOpsRouter(deps) {
|
|
|
194
284
|
const runtimeAvailable = typeof input?.runtimeAvailable === "boolean"
|
|
195
285
|
? input.runtimeAvailable
|
|
196
286
|
: deps.runtimeAvailable;
|
|
197
|
-
|
|
287
|
+
const result = await heartbeatCheck({
|
|
198
288
|
probeOnly: coerceProbeOnlyFlag(input),
|
|
199
289
|
runtimeAvailable,
|
|
200
290
|
fakeControlPlanePassthrough: input?.fakeControlPlanePassthrough &&
|
|
@@ -219,6 +309,34 @@ export function createOpsRouter(deps) {
|
|
|
219
309
|
connectorRegistry: input
|
|
220
310
|
?.connectorRegistry ?? deps.connectorRegistry,
|
|
221
311
|
});
|
|
312
|
+
if (result.ok &&
|
|
313
|
+
result.surfaceMode === "workspace_full_runtime" &&
|
|
314
|
+
!coerceProbeOnlyFlag(input) &&
|
|
315
|
+
deps.state &&
|
|
316
|
+
deps.restoreSnapshotStore) {
|
|
317
|
+
try {
|
|
318
|
+
const capture = await captureRuntimeSnapshot(deps, {
|
|
319
|
+
snapshotId: `heartbeat:${result.decisionId ?? "cycle"}:${Date.now()}`,
|
|
320
|
+
subjectId: result.decisionId ?? "heartbeat_check",
|
|
321
|
+
reasonCode: "heartbeat_check",
|
|
322
|
+
summaryText: `Heartbeat ${result.status} captured bounded restore snapshot`,
|
|
323
|
+
focus: result.status,
|
|
324
|
+
progress: result.reasons.join(",") || "heartbeat_completed",
|
|
325
|
+
nextIntent: "continue_runtime_loop",
|
|
326
|
+
sourceRefs: result.decisionId
|
|
327
|
+
? [`heartbeat:${result.decisionId}`]
|
|
328
|
+
: ["heartbeat:runtime"],
|
|
329
|
+
});
|
|
330
|
+
if (capture.ok) {
|
|
331
|
+
result.reasons = [...result.reasons, "restore_snapshot_captured"];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
336
|
+
result.reasons = [...result.reasons, `restore_snapshot_capture_failed:${msg}`];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
222
340
|
}
|
|
223
341
|
if (command === "fallback") {
|
|
224
342
|
const ref = typeof input?.ref === "string" ? input.ref.trim() : "";
|
|
@@ -737,96 +855,7 @@ export function createOpsRouter(deps) {
|
|
|
737
855
|
* NarrativeTimeline. This gives restore and narrative:diff real state to consume.
|
|
738
856
|
*/
|
|
739
857
|
if (command === "snapshot:capture") {
|
|
740
|
-
|
|
741
|
-
if (!deps.state || !deps.restoreSnapshotStore) {
|
|
742
|
-
const envelope = {
|
|
743
|
-
ok: false,
|
|
744
|
-
command: "snapshot:capture",
|
|
745
|
-
runtimeMode: "unavailable",
|
|
746
|
-
surfaceMode: "cli",
|
|
747
|
-
generatedAt,
|
|
748
|
-
error: {
|
|
749
|
-
code: "SNAPSHOT_CAPTURE_DEPS_UNAVAILABLE",
|
|
750
|
-
message: "snapshot:capture requires state DB and RestoreSnapshotStore in OpsRouterDeps",
|
|
751
|
-
nextStep: "wire_state_and_restore_snapshot_store_into_ops_router",
|
|
752
|
-
},
|
|
753
|
-
warnings: [],
|
|
754
|
-
sourceRefs: [],
|
|
755
|
-
};
|
|
756
|
-
return envelope;
|
|
757
|
-
}
|
|
758
|
-
const snapshotId = textInput(input, "snapshotId") ??
|
|
759
|
-
`snapshot:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
|
|
760
|
-
const requestedKinds = coerceRestorableKinds(input?.entityWhitelist) ?? [...DEFAULT_SNAPSHOT_KINDS];
|
|
761
|
-
const rowCounts = {};
|
|
762
|
-
const warnings = [];
|
|
763
|
-
for (const kind of requestedKinds) {
|
|
764
|
-
const table = SNAPSHOT_TABLE_BY_KIND[kind];
|
|
765
|
-
if (!tableExists(deps.state, table)) {
|
|
766
|
-
rowCounts[kind] = 0;
|
|
767
|
-
warnings.push(`table_missing:${kind}:${table}`);
|
|
768
|
-
continue;
|
|
769
|
-
}
|
|
770
|
-
rowCounts[kind] = readRowsFromTable(deps.state, table).length;
|
|
771
|
-
}
|
|
772
|
-
const historyStore = createHistoryDigestStore(deps.state);
|
|
773
|
-
const previousHash = (await historyStore.listNarrativeTimeline({ limit: 1 }))[0]?.currentHash ?? "";
|
|
774
|
-
const delta = buildSnapshotNarrativeDelta(input, snapshotId, rowCounts);
|
|
775
|
-
const currentHash = hashNarrativeSnapshot({
|
|
776
|
-
previousHash,
|
|
777
|
-
snapshotId,
|
|
778
|
-
delta,
|
|
779
|
-
createdAt: generatedAt,
|
|
780
|
-
});
|
|
781
|
-
await historyStore.appendNarrativeTimeline({
|
|
782
|
-
timelineId: snapshotId,
|
|
783
|
-
entryType: "owner.override",
|
|
784
|
-
subjectId: textInput(input, "subjectId") ?? snapshotId,
|
|
785
|
-
delta,
|
|
786
|
-
previousHash,
|
|
787
|
-
currentHash,
|
|
788
|
-
createdAt: generatedAt,
|
|
789
|
-
});
|
|
790
|
-
const payload = {};
|
|
791
|
-
const capturedKinds = [];
|
|
792
|
-
for (const kind of requestedKinds) {
|
|
793
|
-
const table = SNAPSHOT_TABLE_BY_KIND[kind];
|
|
794
|
-
if (!tableExists(deps.state, table))
|
|
795
|
-
continue;
|
|
796
|
-
const rows = readRowsFromTable(deps.state, table);
|
|
797
|
-
rowCounts[kind] = rows.length;
|
|
798
|
-
if (rows.length > 0) {
|
|
799
|
-
payload[kind] = rows;
|
|
800
|
-
capturedKinds.push(kind);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
const snapshot = await deps.restoreSnapshotStore.captureSnapshot({
|
|
804
|
-
snapshotId,
|
|
805
|
-
entityWhitelist: requestedKinds,
|
|
806
|
-
payload,
|
|
807
|
-
capturedAt: generatedAt,
|
|
808
|
-
});
|
|
809
|
-
const envelope = {
|
|
810
|
-
ok: true,
|
|
811
|
-
command: "snapshot:capture",
|
|
812
|
-
runtimeMode: "workspace_full_runtime",
|
|
813
|
-
surfaceMode: "cli",
|
|
814
|
-
generatedAt,
|
|
815
|
-
data: {
|
|
816
|
-
snapshotId: snapshot.snapshotId,
|
|
817
|
-
capturedAt: snapshot.capturedAt,
|
|
818
|
-
entityWhitelist: snapshot.entityWhitelist,
|
|
819
|
-
capturedKinds,
|
|
820
|
-
rowCounts,
|
|
821
|
-
narrativeVersion: snapshotId,
|
|
822
|
-
},
|
|
823
|
-
warnings,
|
|
824
|
-
sourceRefs: [
|
|
825
|
-
"storage/services/restore-snapshot-store.ts",
|
|
826
|
-
"storage/services/history-digest-store.ts",
|
|
827
|
-
],
|
|
828
|
-
};
|
|
829
|
-
return envelope;
|
|
858
|
+
return captureRuntimeSnapshot(deps, input);
|
|
830
859
|
}
|
|
831
860
|
/**
|
|
832
861
|
* [G6] narrative:diff — queryNarrativeDiff between two versions.
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
function extractSourceRefs(platformId, data, observedAt) {
|
|
2
2
|
if (data && typeof data === "object") {
|
|
3
3
|
const record = data;
|
|
4
|
+
if (record.data && typeof record.data === "object") {
|
|
5
|
+
const nested = extractSourceRefs(platformId, record.data, observedAt);
|
|
6
|
+
if (nested.length > 0)
|
|
7
|
+
return nested;
|
|
8
|
+
}
|
|
4
9
|
if (Array.isArray(record.sourceRefs)) {
|
|
5
10
|
const out = [];
|
|
6
11
|
for (const item of record.sourceRefs) {
|
|
@@ -78,10 +78,18 @@ export function createCapabilityProbeResultStore(database) {
|
|
|
78
78
|
const { sqlite } = database;
|
|
79
79
|
return {
|
|
80
80
|
async appendProbeResult(result) {
|
|
81
|
-
sqlite.run(`INSERT INTO capability_probe_result
|
|
82
|
-
(probe_result_id, capability_id, connector_id, actual_status,
|
|
83
|
-
http_status, sample_response_ref, probe_config_ref, created_at)
|
|
84
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
81
|
+
sqlite.run(`INSERT INTO capability_probe_result
|
|
82
|
+
(probe_result_id, capability_id, connector_id, actual_status,
|
|
83
|
+
http_status, sample_response_ref, probe_config_ref, created_at)
|
|
84
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
85
|
+
ON CONFLICT(probe_result_id) DO UPDATE SET
|
|
86
|
+
capability_id = excluded.capability_id,
|
|
87
|
+
connector_id = excluded.connector_id,
|
|
88
|
+
actual_status = excluded.actual_status,
|
|
89
|
+
http_status = excluded.http_status,
|
|
90
|
+
sample_response_ref = excluded.sample_response_ref,
|
|
91
|
+
probe_config_ref = excluded.probe_config_ref,
|
|
92
|
+
created_at = excluded.created_at`, [
|
|
85
93
|
result.probeResultId,
|
|
86
94
|
result.capabilityId,
|
|
87
95
|
result.connectorId,
|