@haaaiawd/second-nature 0.1.27 → 0.1.30
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/SKILL.md +35 -33
- package/agent-inner-guide.md +144 -124
- package/index.js +83 -20
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -1
- package/runtime/cli/commands/connector-behavior.d.ts +20 -0
- package/runtime/cli/commands/connector-behavior.js +160 -0
- package/runtime/cli/commands/index.js +8 -0
- package/runtime/cli/index.js +9 -2
- package/runtime/cli/ops/manual-run-dispatcher.d.ts +79 -0
- package/runtime/cli/ops/manual-run-dispatcher.js +110 -0
- package/runtime/cli/ops/ops-router.d.ts +45 -4
- package/runtime/cli/ops/ops-router.js +543 -2
- package/runtime/cli/read-models/index.js +35 -18
- package/runtime/cli/read-models/types.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.js +2 -2
- package/runtime/connectors/base/contract.d.ts +4 -1
- package/runtime/connectors/base/contract.js +5 -1
- package/runtime/connectors/base/effect-commit-ledger-sqlite.d.ts +31 -0
- package/runtime/connectors/base/effect-commit-ledger-sqlite.js +86 -0
- package/runtime/connectors/base/failure-taxonomy.js +5 -0
- package/runtime/connectors/base/manifest-v7.d.ts +151 -0
- package/runtime/connectors/base/manifest-v7.js +170 -0
- package/runtime/connectors/base/manifest.d.ts +67 -77
- package/runtime/connectors/base/manifest.js +7 -7
- package/runtime/connectors/base/route-planner.js +11 -8
- package/runtime/connectors/base/structured-unavailable-reason.d.ts +59 -0
- package/runtime/connectors/base/structured-unavailable-reason.js +113 -0
- package/runtime/connectors/base/wet-probe-runner.d.ts +40 -0
- package/runtime/connectors/base/wet-probe-runner.js +132 -0
- package/runtime/connectors/manifest/manifest-schema.d.ts +4 -0
- package/runtime/connectors/manifest/manifest-schema.js +2 -0
- package/runtime/connectors/services/connector-executor-adapter.d.ts +1 -0
- package/runtime/connectors/services/connector-executor-adapter.js +132 -26
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.d.ts +45 -0
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.js +132 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.d.ts +60 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.js +174 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.d.ts +38 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.js +60 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.d.ts +51 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.js +129 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.d.ts +30 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.js +92 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.d.ts +34 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.js +67 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.d.ts +37 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.js +62 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.d.ts +29 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.js +28 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.d.ts +54 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.js +164 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.js +60 -0
- package/runtime/core/second-nature/heartbeat/index.d.ts +4 -0
- package/runtime/core/second-nature/heartbeat/index.js +5 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.d.ts +63 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.js +118 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.d.ts +41 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.js +43 -0
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +2 -1
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +2 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.d.ts +31 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.js +102 -0
- package/runtime/core/second-nature/orchestrator/index.d.ts +5 -0
- package/runtime/core/second-nature/orchestrator/index.js +7 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.d.ts +53 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.js +153 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.d.ts +29 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.js +92 -0
- package/runtime/core/second-nature/quiet/index.d.ts +5 -0
- package/runtime/core/second-nature/quiet/index.js +5 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +19 -12
- package/runtime/core/second-nature/types.d.ts +2 -0
- package/runtime/guidance/channel-feedback-ingestion-service.d.ts +88 -0
- package/runtime/guidance/channel-feedback-ingestion-service.js +231 -0
- package/runtime/guidance/guidance-draft-service.d.ts +60 -0
- package/runtime/guidance/guidance-draft-service.js +80 -0
- package/runtime/guidance/index.d.ts +3 -0
- package/runtime/guidance/index.js +3 -0
- package/runtime/guidance/outreach-draft-schema.d.ts +8 -8
- package/runtime/guidance/outreach-strategy-selector.d.ts +77 -0
- package/runtime/guidance/outreach-strategy-selector.js +211 -0
- package/runtime/observability/audit/append-only-audit-store.d.ts +20 -2
- package/runtime/observability/audit/append-only-audit-store.js +32 -6
- package/runtime/observability/audit/audit-envelope.d.ts +2 -1
- package/runtime/observability/audit/audit-envelope.js +8 -7
- package/runtime/observability/audit/audit-family-registry.json +66 -0
- package/runtime/observability/audit/family-registry.d.ts +43 -0
- package/runtime/observability/audit/family-registry.js +70 -0
- package/runtime/observability/index.d.ts +6 -1
- package/runtime/observability/index.js +6 -1
- package/runtime/observability/redaction/policy.d.ts +24 -3
- package/runtime/observability/redaction/policy.js +74 -0
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +152 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +248 -0
- package/runtime/observability/services/lived-experience-audit.js +6 -6
- package/runtime/observability/services/narrative-timeline-query-service.d.ts +136 -0
- package/runtime/observability/services/narrative-timeline-query-service.js +169 -0
- package/runtime/observability/services/restore-audit-service.d.ts +74 -0
- package/runtime/observability/services/restore-audit-service.js +79 -0
- package/runtime/observability/services/runtime-secret-anchor-view.d.ts +77 -0
- package/runtime/observability/services/runtime-secret-anchor-view.js +168 -0
- package/runtime/observability/services/self-health-snapshot.d.ts +92 -0
- package/runtime/observability/services/self-health-snapshot.js +251 -0
- package/runtime/shared/types/goal.d.ts +62 -0
- package/runtime/shared/types/goal.js +20 -0
- package/runtime/shared/types/index.d.ts +3 -0
- package/runtime/shared/types/index.js +3 -0
- package/runtime/shared/types/source-ref.d.ts +14 -0
- package/runtime/shared/types/source-ref.js +1 -0
- package/runtime/shared/types/v7-entities.d.ts +206 -0
- package/runtime/shared/types/v7-entities.js +27 -0
- package/runtime/storage/db/index.js +3 -0
- package/runtime/storage/db/migration-runner.d.ts +30 -0
- package/runtime/storage/db/migration-runner.js +93 -0
- package/runtime/storage/db/migrations/index.d.ts +5 -0
- package/runtime/storage/db/migrations/index.js +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.d.ts +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.js +144 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.d.ts +8 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.js +27 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.js +26 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.js +26 -0
- package/runtime/storage/db/schema/agent-goal.d.ts +38 -0
- package/runtime/storage/db/schema/agent-goal.js +2 -0
- package/runtime/storage/db/transaction-utils.d.ts +14 -0
- package/runtime/storage/db/transaction-utils.js +29 -0
- package/runtime/storage/db/write-queue.d.ts +38 -0
- package/runtime/storage/db/write-queue.js +97 -0
- package/runtime/storage/quiet/persist-quiet-artifact.js +2 -1
- package/runtime/storage/services/diary-dream-store.d.ts +35 -0
- package/runtime/storage/services/diary-dream-store.js +165 -0
- package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
- package/runtime/storage/services/embodied-context-state-port.js +115 -0
- package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
- package/runtime/storage/services/goal-lifecycle-store.js +181 -0
- package/runtime/storage/services/history-digest-store.d.ts +33 -0
- package/runtime/storage/services/history-digest-store.js +140 -0
- package/runtime/storage/services/identity-profile-store.d.ts +25 -0
- package/runtime/storage/services/identity-profile-store.js +81 -0
- package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
- package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
- package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
- package/runtime/storage/services/restore-snapshot-store.js +193 -0
- package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
- package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
- package/runtime/storage/services/tool-experience-store.d.ts +25 -0
- package/runtime/storage/services/tool-experience-store.js +116 -0
- package/runtime/storage/services/write-validation-gate.d.ts +46 -0
- package/runtime/storage/services/write-validation-gate.js +200 -0
- package/workspace-ops-bridge.js +16 -1
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared ops command dispatch for CLI + tool surfaces (T1.1.3, T1.2.2).
|
|
3
|
+
*
|
|
4
|
+
* v7 additions (T-ROS.C.1): self_health, tool_affordance, connector_test --wet,
|
|
5
|
+
* heartbeat_digest, narrative:diff, timeline, restore, runtime_secret_bootstrap.
|
|
6
|
+
* All commands return RuntimeOpsEnvelope.
|
|
3
7
|
*/
|
|
8
|
+
import fs from "node:fs";
|
|
4
9
|
import { heartbeatCheck, } from "./heartbeat-surface.js";
|
|
5
10
|
import { showOperatorFallback, OperatorFallbackNotFoundError, } from "./show-operator-fallback.js";
|
|
6
11
|
import { probeHostCapability } from "../host-capability/probe-host-capability.js";
|
|
7
12
|
import { recordHostCapability } from "../host-capability/record-host-capability.js";
|
|
8
13
|
import { runNearRealConnectorSmoke } from "../../connectors/near-real/near-real-connector-smoke.js";
|
|
9
14
|
import { connectorInit } from "../commands/connector-init.js";
|
|
15
|
+
import { connectorBehaviorAdd } from "../commands/connector-behavior.js";
|
|
10
16
|
import { connectorStatus, connectorTest } from "../commands/connector-status.js";
|
|
11
17
|
import { goalCommand } from "../commands/goal.js";
|
|
18
|
+
// v7 observability services (T-ROS.C.1)
|
|
19
|
+
import { getSelfHealthSnapshot, ensureMinimumProbes, } from "../../observability/services/self-health-snapshot.js";
|
|
20
|
+
import { generateHeartbeatDigest, } from "../../observability/services/heartbeat-digest-assembler.js";
|
|
21
|
+
import { queryNarrativeTimeline, queryNarrativeDiff, } from "../../observability/services/narrative-timeline-query-service.js";
|
|
22
|
+
import { viewSecretAnchor, } from "../../observability/services/runtime-secret-anchor-view.js";
|
|
23
|
+
import { writeRestoreAudit, } from "../../observability/services/restore-audit-service.js";
|
|
24
|
+
// T-ROS.C.3: ManualRunDispatcher and its deps
|
|
25
|
+
import { createManualRunDispatcher, } from "./manual-run-dispatcher.js";
|
|
26
|
+
import { createExperienceWriter } from "../../core/second-nature/body/tool-experience/experience-writer.js";
|
|
27
|
+
import { createToolExperienceStore } from "../../storage/services/tool-experience-store.js";
|
|
28
|
+
import { createWetProbeRunner } from "../../connectors/base/wet-probe-runner.js";
|
|
29
|
+
import { CapabilityContractRegistryV7 } from "../../connectors/base/manifest-v7.js";
|
|
12
30
|
function coerceProbeOnlyFlag(input) {
|
|
13
31
|
const v = input?.probeOnly;
|
|
14
32
|
return v === true || v === "true" || v === 1 || v === "1";
|
|
@@ -200,6 +218,23 @@ export function createOpsRouter(deps) {
|
|
|
200
218
|
return result;
|
|
201
219
|
})();
|
|
202
220
|
}
|
|
221
|
+
if (command === "connector_behavior_add") {
|
|
222
|
+
return connectorBehaviorAdd({
|
|
223
|
+
platformId: typeof input?.platformId === "string" ? input.platformId : "",
|
|
224
|
+
behaviorId: typeof input?.behaviorId === "string"
|
|
225
|
+
? input.behaviorId
|
|
226
|
+
: typeof input?.capabilityId === "string"
|
|
227
|
+
? input.capabilityId
|
|
228
|
+
: "",
|
|
229
|
+
description: typeof input?.description === "string" ? input.description : undefined,
|
|
230
|
+
channel: typeof input?.channel === "string" ? input.channel : undefined,
|
|
231
|
+
sourceRefs: input?.sourceRefs,
|
|
232
|
+
observedCount: typeof input?.observedCount === "number" ? input.observedCount : undefined,
|
|
233
|
+
workspaceRoot: typeof input?.workspaceRoot === "string"
|
|
234
|
+
? input.workspaceRoot
|
|
235
|
+
: deps.workspaceRoot,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
203
238
|
if (command === "connector_status") {
|
|
204
239
|
return connectorStatus(deps.registry, undefined, {
|
|
205
240
|
includeHealth: Boolean(input?.includeHealth),
|
|
@@ -209,9 +244,79 @@ export function createOpsRouter(deps) {
|
|
|
209
244
|
});
|
|
210
245
|
}
|
|
211
246
|
if (command === "connector_test") {
|
|
212
|
-
|
|
247
|
+
// v7 T-ROS.C.1: --wet flag (wet=true) sets dryRun=false + marks triggerSource:"manual_run"
|
|
248
|
+
const isWet = input?.wet === true || input?.wet === "true";
|
|
249
|
+
const result = await connectorTest(deps.registry, {
|
|
213
250
|
platformId: typeof input?.platformId === "string" ? input.platformId : "",
|
|
214
|
-
dryRun: input?.dryRun === false ? false : true,
|
|
251
|
+
dryRun: isWet ? false : (input?.dryRun === false ? false : true),
|
|
252
|
+
});
|
|
253
|
+
if (isWet && result.ok) {
|
|
254
|
+
// Annotate result with manual trigger context (DR-038 / T-ROS.C.3)
|
|
255
|
+
result.triggerSource = "manual_run";
|
|
256
|
+
result.affectsHeartbeatCadence = false;
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
if (command === "connector:run") {
|
|
261
|
+
// T-ROS.C.3: manual connector execution — isolated from heartbeat cadence
|
|
262
|
+
const platformId = typeof input?.platformId === "string" ? input.platformId : "";
|
|
263
|
+
const capabilityId = typeof input?.capabilityId === "string" ? input.capabilityId : "";
|
|
264
|
+
if (!platformId || !capabilityId) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
command: "connector:run",
|
|
268
|
+
error: {
|
|
269
|
+
code: "MISSING_PLATFORM_OR_CAPABILITY_ID",
|
|
270
|
+
message: "connector:run requires platformId and capabilityId",
|
|
271
|
+
requiredUserInput: ["platformId", "capabilityId"],
|
|
272
|
+
nextStep: "reinvoke_with_platform_and_capability_id",
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (!deps.connectorExecutor || !deps.state) {
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
command: "connector:run",
|
|
280
|
+
error: {
|
|
281
|
+
code: "MANUAL_RUN_DEPS_UNAVAILABLE",
|
|
282
|
+
message: "connector:run requires connectorExecutor and state database",
|
|
283
|
+
nextStep: "wire_connector_executor_and_state_into_ops_router",
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const toolExperienceStore = createToolExperienceStore(deps.state);
|
|
288
|
+
const experienceWriter = createExperienceWriter(toolExperienceStore);
|
|
289
|
+
const wetProbeRunner = createWetProbeRunner();
|
|
290
|
+
const registryV7 = new CapabilityContractRegistryV7();
|
|
291
|
+
// Populate V7 registry from dynamic registry if available (best-effort)
|
|
292
|
+
if (deps.registry) {
|
|
293
|
+
for (const entry of deps.registry.listConnectors()) {
|
|
294
|
+
if (entry.manifestPath) {
|
|
295
|
+
try {
|
|
296
|
+
const manifestText = fs.readFileSync(entry.manifestPath, "utf-8");
|
|
297
|
+
const manifest = JSON.parse(manifestText);
|
|
298
|
+
registryV7.register(manifest);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Skip manifests that can't be read or don't validate as V7
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const dispatcher = createManualRunDispatcher({
|
|
307
|
+
connectorExecutor: deps.connectorExecutor,
|
|
308
|
+
experienceWriter,
|
|
309
|
+
wetProbeRunner,
|
|
310
|
+
registryV7,
|
|
311
|
+
});
|
|
312
|
+
return dispatcher.runConnector({
|
|
313
|
+
platformId,
|
|
314
|
+
capabilityId,
|
|
315
|
+
payload: typeof input?.payload === "object" && input?.payload !== null
|
|
316
|
+
? input.payload
|
|
317
|
+
: undefined,
|
|
318
|
+
caller: typeof input?.caller === "string" ? input.caller : undefined,
|
|
319
|
+
reason: typeof input?.reason === "string" ? input.reason : undefined,
|
|
215
320
|
});
|
|
216
321
|
}
|
|
217
322
|
if (command === "goal") {
|
|
@@ -275,6 +380,442 @@ export function createOpsRouter(deps) {
|
|
|
275
380
|
const data = await deps.readModels.loadCycleRecent(limit);
|
|
276
381
|
return { ok: true, data };
|
|
277
382
|
}
|
|
383
|
+
// ─── v7 commands (T-ROS.C.1) ─────────────────────────────────────────
|
|
384
|
+
/** [G2] self_health — transparent pass-through from SelfHealthSnapshot (DR-042). */
|
|
385
|
+
if (command === "self_health") {
|
|
386
|
+
const generatedAt = new Date().toISOString();
|
|
387
|
+
try {
|
|
388
|
+
ensureMinimumProbes();
|
|
389
|
+
const snap = await getSelfHealthSnapshot();
|
|
390
|
+
const degraded_dimensions = Object.entries(snap.dimensions)
|
|
391
|
+
.filter(([, d]) => d.status === "degraded")
|
|
392
|
+
.map(([k]) => k);
|
|
393
|
+
const envelope = {
|
|
394
|
+
ok: true,
|
|
395
|
+
command: "self_health",
|
|
396
|
+
runtimeMode: "workspace_full_runtime",
|
|
397
|
+
surfaceMode: "cli",
|
|
398
|
+
generatedAt,
|
|
399
|
+
data: {
|
|
400
|
+
overall: snap.overall,
|
|
401
|
+
generatedAt: snap.generatedAt,
|
|
402
|
+
degraded_dimensions,
|
|
403
|
+
dimensions: snap.dimensions,
|
|
404
|
+
},
|
|
405
|
+
warnings: [],
|
|
406
|
+
sourceRefs: ["observability/services/self-health-snapshot.ts"],
|
|
407
|
+
};
|
|
408
|
+
return envelope;
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
412
|
+
const envelope = {
|
|
413
|
+
ok: false,
|
|
414
|
+
command: "self_health",
|
|
415
|
+
runtimeMode: "unavailable",
|
|
416
|
+
surfaceMode: "cli",
|
|
417
|
+
generatedAt,
|
|
418
|
+
error: { code: "SELF_HEALTH_PROBE_FAILED", message: msg },
|
|
419
|
+
warnings: [],
|
|
420
|
+
sourceRefs: [],
|
|
421
|
+
};
|
|
422
|
+
return envelope;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* [G3] tool_affordance — body-tool AffordanceMap pass-through.
|
|
427
|
+
* Port not yet wired in this wave; returns degraded view with clear next-step.
|
|
428
|
+
*/
|
|
429
|
+
if (command === "tool_affordance") {
|
|
430
|
+
const generatedAt = new Date().toISOString();
|
|
431
|
+
const envelope = {
|
|
432
|
+
ok: false,
|
|
433
|
+
command: "tool_affordance",
|
|
434
|
+
runtimeMode: "unavailable",
|
|
435
|
+
surfaceMode: "cli",
|
|
436
|
+
generatedAt,
|
|
437
|
+
error: {
|
|
438
|
+
code: "TOOL_AFFORDANCE_PORT_UNWIRED",
|
|
439
|
+
message: "tool_affordance requires body-tool AffordanceMap port (T-BTS.C.1) to be wired into OpsRouterDeps",
|
|
440
|
+
nextStep: "wire_body_tool_port_into_ops_router_deps",
|
|
441
|
+
},
|
|
442
|
+
warnings: [],
|
|
443
|
+
sourceRefs: [],
|
|
444
|
+
};
|
|
445
|
+
return envelope;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* [G6] heartbeat_digest — wraps generateHeartbeatDigest.
|
|
449
|
+
* Requires auditStore in deps; degrades if unavailable.
|
|
450
|
+
*/
|
|
451
|
+
if (command === "heartbeat_digest") {
|
|
452
|
+
const generatedAt = new Date().toISOString();
|
|
453
|
+
if (!deps.auditStore) {
|
|
454
|
+
const envelope = {
|
|
455
|
+
ok: false,
|
|
456
|
+
command: "heartbeat_digest",
|
|
457
|
+
runtimeMode: "unavailable",
|
|
458
|
+
surfaceMode: "cli",
|
|
459
|
+
generatedAt,
|
|
460
|
+
error: {
|
|
461
|
+
code: "AUDIT_STORE_UNAVAILABLE",
|
|
462
|
+
message: "heartbeat_digest requires auditStore in OpsRouterDeps",
|
|
463
|
+
nextStep: "wire_audit_store_into_ops_router",
|
|
464
|
+
},
|
|
465
|
+
warnings: [],
|
|
466
|
+
sourceRefs: [],
|
|
467
|
+
};
|
|
468
|
+
return envelope;
|
|
469
|
+
}
|
|
470
|
+
const date = typeof input?.date === "string" && input.date
|
|
471
|
+
? input.date
|
|
472
|
+
: new Date().toISOString().slice(0, 10);
|
|
473
|
+
try {
|
|
474
|
+
const digestDeps = {
|
|
475
|
+
auditStore: deps.auditStore,
|
|
476
|
+
...deps.heartbeatDigestDeps,
|
|
477
|
+
};
|
|
478
|
+
const digest = await generateHeartbeatDigest(date, digestDeps);
|
|
479
|
+
const envelope = {
|
|
480
|
+
ok: true,
|
|
481
|
+
command: "heartbeat_digest",
|
|
482
|
+
runtimeMode: "workspace_full_runtime",
|
|
483
|
+
surfaceMode: "cli",
|
|
484
|
+
generatedAt,
|
|
485
|
+
data: digest,
|
|
486
|
+
warnings: [],
|
|
487
|
+
sourceRefs: ["observability/services/heartbeat-digest-assembler.ts"],
|
|
488
|
+
};
|
|
489
|
+
return envelope;
|
|
490
|
+
}
|
|
491
|
+
catch (err) {
|
|
492
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
493
|
+
const envelope = {
|
|
494
|
+
ok: false,
|
|
495
|
+
command: "heartbeat_digest",
|
|
496
|
+
runtimeMode: "unavailable",
|
|
497
|
+
surfaceMode: "cli",
|
|
498
|
+
generatedAt,
|
|
499
|
+
error: { code: "DIGEST_GENERATION_FAILED", message: msg },
|
|
500
|
+
warnings: [],
|
|
501
|
+
sourceRefs: [],
|
|
502
|
+
};
|
|
503
|
+
return envelope;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* [G6] narrative:diff — queryNarrativeDiff between two versions.
|
|
508
|
+
* Requires narrativeTimelineDeps in OpsRouterDeps.
|
|
509
|
+
*/
|
|
510
|
+
if (command === "narrative:diff") {
|
|
511
|
+
const generatedAt = new Date().toISOString();
|
|
512
|
+
if (!deps.narrativeTimelineDeps) {
|
|
513
|
+
const envelope = {
|
|
514
|
+
ok: false,
|
|
515
|
+
command: "narrative:diff",
|
|
516
|
+
runtimeMode: "unavailable",
|
|
517
|
+
surfaceMode: "cli",
|
|
518
|
+
generatedAt,
|
|
519
|
+
error: {
|
|
520
|
+
code: "NARRATIVE_TIMELINE_PORT_UNAVAILABLE",
|
|
521
|
+
message: "narrative:diff requires narrativeTimelineDeps in OpsRouterDeps",
|
|
522
|
+
nextStep: "wire_narrative_timeline_deps_into_ops_router",
|
|
523
|
+
},
|
|
524
|
+
warnings: [],
|
|
525
|
+
sourceRefs: [],
|
|
526
|
+
};
|
|
527
|
+
return envelope;
|
|
528
|
+
}
|
|
529
|
+
const fromVersion = typeof input?.from === "string" ? input.from : "";
|
|
530
|
+
const toVersion = typeof input?.to === "string" ? input.to : "";
|
|
531
|
+
if (!fromVersion || !toVersion) {
|
|
532
|
+
const envelope = {
|
|
533
|
+
ok: false,
|
|
534
|
+
command: "narrative:diff",
|
|
535
|
+
runtimeMode: "workspace_full_runtime",
|
|
536
|
+
surfaceMode: "cli",
|
|
537
|
+
generatedAt,
|
|
538
|
+
error: {
|
|
539
|
+
code: "MISSING_VERSIONS",
|
|
540
|
+
message: "narrative:diff requires 'from' and 'to' version arguments",
|
|
541
|
+
nextStep: "reinvoke_with_from_and_to",
|
|
542
|
+
},
|
|
543
|
+
warnings: [],
|
|
544
|
+
sourceRefs: [],
|
|
545
|
+
};
|
|
546
|
+
return envelope;
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const diff = await queryNarrativeDiff(fromVersion, toVersion, deps.narrativeTimelineDeps);
|
|
550
|
+
const envelope = {
|
|
551
|
+
ok: true,
|
|
552
|
+
command: "narrative:diff",
|
|
553
|
+
runtimeMode: "workspace_full_runtime",
|
|
554
|
+
surfaceMode: "cli",
|
|
555
|
+
generatedAt,
|
|
556
|
+
data: diff,
|
|
557
|
+
warnings: [],
|
|
558
|
+
sourceRefs: ["observability/services/narrative-timeline-query-service.ts"],
|
|
559
|
+
};
|
|
560
|
+
return envelope;
|
|
561
|
+
}
|
|
562
|
+
catch (err) {
|
|
563
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
564
|
+
const envelope = {
|
|
565
|
+
ok: false,
|
|
566
|
+
command: "narrative:diff",
|
|
567
|
+
runtimeMode: "unavailable",
|
|
568
|
+
surfaceMode: "cli",
|
|
569
|
+
generatedAt,
|
|
570
|
+
error: { code: "NARRATIVE_DIFF_FAILED", message: msg },
|
|
571
|
+
warnings: [],
|
|
572
|
+
sourceRefs: [],
|
|
573
|
+
};
|
|
574
|
+
return envelope;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* [G6] timeline — queryNarrativeTimeline with cursor pagination.
|
|
579
|
+
* Requires narrativeTimelineDeps in OpsRouterDeps.
|
|
580
|
+
*/
|
|
581
|
+
if (command === "timeline") {
|
|
582
|
+
const generatedAt = new Date().toISOString();
|
|
583
|
+
if (!deps.narrativeTimelineDeps) {
|
|
584
|
+
const envelope = {
|
|
585
|
+
ok: false,
|
|
586
|
+
command: "timeline",
|
|
587
|
+
runtimeMode: "unavailable",
|
|
588
|
+
surfaceMode: "cli",
|
|
589
|
+
generatedAt,
|
|
590
|
+
error: {
|
|
591
|
+
code: "NARRATIVE_TIMELINE_PORT_UNAVAILABLE",
|
|
592
|
+
message: "timeline requires narrativeTimelineDeps in OpsRouterDeps",
|
|
593
|
+
nextStep: "wire_narrative_timeline_deps_into_ops_router",
|
|
594
|
+
},
|
|
595
|
+
warnings: [],
|
|
596
|
+
sourceRefs: [],
|
|
597
|
+
};
|
|
598
|
+
return envelope;
|
|
599
|
+
}
|
|
600
|
+
const now = new Date();
|
|
601
|
+
const to = typeof input?.to === "string" ? input.to : now.toISOString();
|
|
602
|
+
const from = typeof input?.from === "string"
|
|
603
|
+
? input.from
|
|
604
|
+
: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
605
|
+
const limit = typeof input?.limit === "number" ? input.limit : 20;
|
|
606
|
+
const cursor = typeof input?.cursor === "string" ? input.cursor : undefined;
|
|
607
|
+
try {
|
|
608
|
+
const page = await queryNarrativeTimeline(from, to, { limit, cursor }, deps.narrativeTimelineDeps);
|
|
609
|
+
const envelope = {
|
|
610
|
+
ok: true,
|
|
611
|
+
command: "timeline",
|
|
612
|
+
runtimeMode: "workspace_full_runtime",
|
|
613
|
+
surfaceMode: "cli",
|
|
614
|
+
generatedAt,
|
|
615
|
+
data: page,
|
|
616
|
+
warnings: [],
|
|
617
|
+
sourceRefs: ["observability/services/narrative-timeline-query-service.ts"],
|
|
618
|
+
};
|
|
619
|
+
return envelope;
|
|
620
|
+
}
|
|
621
|
+
catch (err) {
|
|
622
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
623
|
+
const code = err.name === "NarrativeQueryRangeError"
|
|
624
|
+
? "NARRATIVE_RANGE_EXCEEDED"
|
|
625
|
+
: "TIMELINE_QUERY_FAILED";
|
|
626
|
+
const envelope = {
|
|
627
|
+
ok: false,
|
|
628
|
+
command: "timeline",
|
|
629
|
+
runtimeMode: "unavailable",
|
|
630
|
+
surfaceMode: "cli",
|
|
631
|
+
generatedAt,
|
|
632
|
+
error: { code, message: msg },
|
|
633
|
+
warnings: [],
|
|
634
|
+
sourceRefs: [],
|
|
635
|
+
};
|
|
636
|
+
return envelope;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* [G6] restore — bounded state restoration via RestoreSnapshotStore + audit (T-ROS.C.1, T-OBS.C.6).
|
|
641
|
+
* When restoreSnapshotStore is wired, attempts to apply the snapshot payload back to state.
|
|
642
|
+
* Always writes RestoreAudit. Never restores credential fields.
|
|
643
|
+
*/
|
|
644
|
+
if (command === "restore") {
|
|
645
|
+
const generatedAt = new Date().toISOString();
|
|
646
|
+
if (!deps.auditStore) {
|
|
647
|
+
const envelope = {
|
|
648
|
+
ok: false,
|
|
649
|
+
command: "restore",
|
|
650
|
+
runtimeMode: "unavailable",
|
|
651
|
+
surfaceMode: "cli",
|
|
652
|
+
generatedAt,
|
|
653
|
+
error: {
|
|
654
|
+
code: "AUDIT_STORE_UNAVAILABLE",
|
|
655
|
+
message: "restore requires auditStore in OpsRouterDeps",
|
|
656
|
+
nextStep: "wire_audit_store_into_ops_router",
|
|
657
|
+
},
|
|
658
|
+
warnings: [],
|
|
659
|
+
sourceRefs: [],
|
|
660
|
+
};
|
|
661
|
+
return envelope;
|
|
662
|
+
}
|
|
663
|
+
const missingFields = [];
|
|
664
|
+
if (typeof input?.restoreTarget !== "string")
|
|
665
|
+
missingFields.push("restoreTarget");
|
|
666
|
+
if (typeof input?.fromVersion !== "string")
|
|
667
|
+
missingFields.push("fromVersion");
|
|
668
|
+
if (typeof input?.toVersion !== "string")
|
|
669
|
+
missingFields.push("toVersion");
|
|
670
|
+
if (missingFields.length > 0) {
|
|
671
|
+
const envelope = {
|
|
672
|
+
ok: false,
|
|
673
|
+
command: "restore",
|
|
674
|
+
runtimeMode: "workspace_full_runtime",
|
|
675
|
+
surfaceMode: "cli",
|
|
676
|
+
generatedAt,
|
|
677
|
+
error: {
|
|
678
|
+
code: "MISSING_RESTORE_FIELDS",
|
|
679
|
+
message: `restore requires: ${missingFields.join(", ")}`,
|
|
680
|
+
nextStep: "reinvoke_with_required_fields",
|
|
681
|
+
},
|
|
682
|
+
warnings: [],
|
|
683
|
+
sourceRefs: [],
|
|
684
|
+
};
|
|
685
|
+
return envelope;
|
|
686
|
+
}
|
|
687
|
+
// [NEW] Invoke bounded restore via RestoreSnapshotStore when wired
|
|
688
|
+
let restoreResult = {
|
|
689
|
+
ok: false,
|
|
690
|
+
completedEntities: [],
|
|
691
|
+
failedEntities: [],
|
|
692
|
+
warnings: ["restore_snapshot_store_unavailable"],
|
|
693
|
+
};
|
|
694
|
+
if (deps.restoreSnapshotStore) {
|
|
695
|
+
restoreResult = await deps.restoreSnapshotStore.applyBoundedRestore({
|
|
696
|
+
restoreTarget: input.restoreTarget,
|
|
697
|
+
fromVersion: input.fromVersion,
|
|
698
|
+
toVersion: input.toVersion,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
const event = {
|
|
702
|
+
id: `restore-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
703
|
+
restoreTarget: input.restoreTarget,
|
|
704
|
+
fromVersion: input.fromVersion,
|
|
705
|
+
toVersion: input.toVersion,
|
|
706
|
+
triggeredBy: input?.triggeredBy ?? "operator",
|
|
707
|
+
reason: typeof input?.reason === "string" ? input.reason : "manual_restore",
|
|
708
|
+
completedEntities: restoreResult.completedEntities,
|
|
709
|
+
failedEntities: restoreResult.failedEntities,
|
|
710
|
+
// credentials are always excluded from restore audit
|
|
711
|
+
excludedFields: Array.isArray(input?.excludedFields)
|
|
712
|
+
? input.excludedFields.filter((f) => typeof f === "string")
|
|
713
|
+
: ["credential", "encryptionKey"],
|
|
714
|
+
restoredFieldCount: restoreResult.completedEntities.length,
|
|
715
|
+
createdAt: generatedAt,
|
|
716
|
+
traceId: typeof input?.traceId === "string" ? input.traceId : `trace-restore-${Date.now()}`,
|
|
717
|
+
};
|
|
718
|
+
const auditResult = await writeRestoreAudit(event, deps.auditStore);
|
|
719
|
+
const envelope = {
|
|
720
|
+
ok: restoreResult.ok && auditResult.ok,
|
|
721
|
+
command: "restore",
|
|
722
|
+
runtimeMode: "workspace_full_runtime",
|
|
723
|
+
surfaceMode: "cli",
|
|
724
|
+
generatedAt,
|
|
725
|
+
data: {
|
|
726
|
+
auditWritten: auditResult.warnings.length === 0,
|
|
727
|
+
fromVersion: event.fromVersion,
|
|
728
|
+
toVersion: event.toVersion,
|
|
729
|
+
restoreTarget: event.restoreTarget,
|
|
730
|
+
isPartialRestore: event.failedEntities.length > 0,
|
|
731
|
+
failedEntities: event.failedEntities,
|
|
732
|
+
completedEntities: event.completedEntities,
|
|
733
|
+
restoreSnapshotStoreAvailable: !!deps.restoreSnapshotStore,
|
|
734
|
+
},
|
|
735
|
+
warnings: [...restoreResult.warnings, ...auditResult.warnings],
|
|
736
|
+
sourceRefs: [
|
|
737
|
+
"observability/services/restore-audit-service.ts",
|
|
738
|
+
"storage/services/restore-snapshot-store.ts",
|
|
739
|
+
],
|
|
740
|
+
};
|
|
741
|
+
return envelope;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* [G7] runtime_secret_bootstrap — RuntimeSecretAnchorView pass-through.
|
|
745
|
+
* Requires secretAnchorDeps in OpsRouterDeps; never returns key plaintext.
|
|
746
|
+
*/
|
|
747
|
+
if (command === "runtime_secret_bootstrap") {
|
|
748
|
+
const generatedAt = new Date().toISOString();
|
|
749
|
+
if (!deps.secretAnchorDeps) {
|
|
750
|
+
const envelope = {
|
|
751
|
+
ok: false,
|
|
752
|
+
command: "runtime_secret_bootstrap",
|
|
753
|
+
runtimeMode: "unavailable",
|
|
754
|
+
surfaceMode: "cli",
|
|
755
|
+
generatedAt,
|
|
756
|
+
error: {
|
|
757
|
+
code: "SECRET_ANCHOR_DEPS_UNAVAILABLE",
|
|
758
|
+
message: "runtime_secret_bootstrap requires secretAnchorDeps in OpsRouterDeps",
|
|
759
|
+
nextStep: "wire_secret_anchor_deps_into_ops_router",
|
|
760
|
+
},
|
|
761
|
+
warnings: [],
|
|
762
|
+
sourceRefs: [],
|
|
763
|
+
};
|
|
764
|
+
return envelope;
|
|
765
|
+
}
|
|
766
|
+
try {
|
|
767
|
+
const view = await viewSecretAnchor(deps.secretAnchorDeps);
|
|
768
|
+
// Map to RuntimeSecretBootstrapView (design model §6.1)
|
|
769
|
+
const data = {
|
|
770
|
+
status: view.status === "verified" || view.status === "ok"
|
|
771
|
+
? "ok"
|
|
772
|
+
: view.status === "missing"
|
|
773
|
+
? "runtime_secret_anchor_missing"
|
|
774
|
+
: view.status === "wrong_key"
|
|
775
|
+
? "credential_recovery_required"
|
|
776
|
+
: view.status === "decryption_failed"
|
|
777
|
+
? "runtime_secret_unavailable"
|
|
778
|
+
: "unknown",
|
|
779
|
+
keyHealth: view.status === "verified" || view.status === "ok"
|
|
780
|
+
? "ok"
|
|
781
|
+
: view.status === "missing"
|
|
782
|
+
? "missing_key"
|
|
783
|
+
: view.status === "wrong_key"
|
|
784
|
+
? "wrong_key"
|
|
785
|
+
: "unknown",
|
|
786
|
+
anchorLocation: view.keyPath,
|
|
787
|
+
recoveryPrincipleRef: view.recoveryDocRef,
|
|
788
|
+
plaintextKeyExposed: false,
|
|
789
|
+
reasonCode: view.reasonCode,
|
|
790
|
+
recoverySteps: view.recoverySteps,
|
|
791
|
+
};
|
|
792
|
+
const envelope = {
|
|
793
|
+
ok: true,
|
|
794
|
+
command: "runtime_secret_bootstrap",
|
|
795
|
+
runtimeMode: "workspace_full_runtime",
|
|
796
|
+
surfaceMode: "cli",
|
|
797
|
+
generatedAt,
|
|
798
|
+
data,
|
|
799
|
+
warnings: [],
|
|
800
|
+
sourceRefs: ["observability/services/runtime-secret-anchor-view.ts"],
|
|
801
|
+
};
|
|
802
|
+
return envelope;
|
|
803
|
+
}
|
|
804
|
+
catch (err) {
|
|
805
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
806
|
+
const envelope = {
|
|
807
|
+
ok: false,
|
|
808
|
+
command: "runtime_secret_bootstrap",
|
|
809
|
+
runtimeMode: "unavailable",
|
|
810
|
+
surfaceMode: "cli",
|
|
811
|
+
generatedAt,
|
|
812
|
+
error: { code: "SECRET_ANCHOR_PROBE_FAILED", message: msg },
|
|
813
|
+
warnings: [],
|
|
814
|
+
sourceRefs: [],
|
|
815
|
+
};
|
|
816
|
+
return envelope;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
278
819
|
return {
|
|
279
820
|
ok: false,
|
|
280
821
|
error: {
|