@haaaiawd/second-nature 0.2.9 → 0.2.13
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 +102 -6
- package/openclaw.plugin.json +2 -5
- package/package.json +1 -1
- package/runtime/cli/commands/index.js +85 -11
- package/runtime/cli/host-capability/host-discovery-port.d.ts +85 -0
- package/runtime/cli/host-capability/host-discovery-port.js +137 -0
- package/runtime/cli/host-capability/probe-host-capability.js +1 -1
- package/runtime/cli/host-capability/types.d.ts +2 -7
- package/runtime/cli/host-capability/types.js +0 -5
- package/runtime/cli/ops/heartbeat-surface.d.ts +5 -3
- package/runtime/cli/ops/heartbeat-surface.js +38 -8
- package/runtime/cli/ops/ops-router.d.ts +6 -2
- package/runtime/cli/ops/ops-router.js +1275 -1147
- package/runtime/cli/ops/workspace-heartbeat-runner.js +2 -5
- package/runtime/connectors/base/normalized-evidence-content.d.ts +4 -0
- package/runtime/connectors/base/normalized-evidence-content.js +21 -2
- package/runtime/connectors/evidence-normalizer.js +32 -1
- package/runtime/connectors/manifest/manifest-schema.d.ts +2 -2
- package/runtime/connectors/registry/dynamic-connector-registry.js +16 -1
- package/runtime/connectors/services/connector-executor-adapter.js +54 -35
- package/runtime/core/second-nature/action/action-closure-recorder.d.ts +4 -0
- package/runtime/core/second-nature/action/action-closure-recorder.js +51 -38
- package/runtime/core/second-nature/action/action-proposal-builder.js +8 -34
- package/runtime/core/second-nature/action/policy-bound-dispatch.d.ts +2 -0
- package/runtime/core/second-nature/action/policy-bound-dispatch.js +10 -5
- package/runtime/core/second-nature/control-plane/accepted-projection-loader.js +1 -11
- package/runtime/core/second-nature/control-plane/cycle-finalizer.d.ts +82 -0
- package/runtime/core/second-nature/control-plane/cycle-finalizer.js +187 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +1 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +23 -15
- package/runtime/core/second-nature/control-plane/real-runtime-spine.d.ts +2 -0
- package/runtime/core/second-nature/control-plane/real-runtime-spine.js +2 -1
- package/runtime/core/second-nature/guidance/guidance-proposal-consumer.d.ts +2 -1
- package/runtime/core/second-nature/guidance/guidance-proposal-consumer.js +4 -2
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +4 -3
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +3 -2
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +3 -2
- package/runtime/core/second-nature/orchestrator/intent-planner.js +4 -2
- package/runtime/core/second-nature/orchestrator/narrative-update.js +1 -2
- package/runtime/core/second-nature/orchestrator/platform-capability-router.d.ts +2 -2
- package/runtime/core/second-nature/orchestrator/platform-capability-router.js +1 -1
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +2 -3
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +2 -2
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +6 -1
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +3 -14
- package/runtime/core/second-nature/outreach/judge-outreach.d.ts +6 -5
- package/runtime/core/second-nature/perception/judgment-engine.js +10 -16
- package/runtime/core/second-nature/perception/perception-builder.js +15 -11
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +13 -15
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +40 -16
- package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.d.ts +5 -1
- package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.js +68 -29
- package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +2 -3
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +2 -13
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +1 -0
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +34 -11
- package/runtime/core/second-nature/types.d.ts +2 -9
- package/runtime/dream/dream-engine.js +11 -4
- package/runtime/guidance/outreach-draft-schema.d.ts +12 -12
- package/runtime/guidance/persona-selection.js +5 -0
- package/runtime/guidance/template-registry.js +6 -1
- package/runtime/guidance/types.d.ts +2 -2
- package/runtime/observability/causal-loop-health.d.ts +2 -1
- package/runtime/observability/causal-loop-health.js +7 -0
- package/runtime/observability/living-loop-health-gate.js +2 -8
- package/runtime/observability/loop-stage-event-sink.js +6 -2
- package/runtime/observability/loop-status.d.ts +2 -0
- package/runtime/observability/loop-status.js +14 -1
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +3 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +9 -0
- package/runtime/observability/services/lived-experience-audit.d.ts +7 -7
- package/runtime/shared/degraded-status-classifier.d.ts +16 -0
- package/runtime/shared/degraded-status-classifier.js +68 -0
- package/runtime/shared/evidence-level-classifier.d.ts +61 -0
- package/runtime/shared/evidence-level-classifier.js +116 -0
- package/runtime/shared/provenance-tier.d.ts +37 -0
- package/runtime/shared/provenance-tier.js +97 -0
- package/runtime/shared/serialization.d.ts +17 -0
- package/runtime/shared/serialization.js +27 -0
- package/runtime/shared/setup-ack.d.ts +54 -0
- package/runtime/shared/setup-ack.js +108 -0
- package/runtime/shared/source-ref-compat.d.ts +26 -0
- package/runtime/shared/source-ref-compat.js +64 -0
- package/runtime/shared/types/goal.d.ts +4 -4
- package/runtime/shared/types/goal.js +1 -1
- package/runtime/shared/types/index.d.ts +1 -0
- package/runtime/shared/types/index.js +1 -3
- package/runtime/shared/types/source-ref.d.ts +2 -2
- package/runtime/shared/types/v7-entities.d.ts +5 -5
- package/runtime/shared/types/v7-entities.js +1 -1
- package/runtime/shared/types/v8-contracts.d.ts +13 -2
- package/runtime/storage/db/index.js +60 -12
- package/runtime/storage/db/migrations/index.js +4 -0
- package/runtime/storage/db/migrations/v8-004-schema-closure.d.ts +19 -0
- package/runtime/storage/db/migrations/v8-004-schema-closure.js +74 -0
- package/runtime/storage/db/migrations/v8-005-single-status-schema.d.ts +11 -0
- package/runtime/storage/db/migrations/v8-005-single-status-schema.js +16 -0
- package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.d.ts +9 -0
- package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.js +15 -0
- package/runtime/storage/db/schema/v8-entities.d.ts +65 -84
- package/runtime/storage/db/schema/v8-entities.js +8 -7
- package/runtime/storage/delivery/types.d.ts +2 -2
- package/runtime/storage/fallback/load-operator-fallback.d.ts +2 -2
- package/runtime/storage/fallback/operator-fallback-types.d.ts +2 -2
- package/runtime/storage/fallback/operator-fallback-view.d.ts +2 -2
- package/runtime/storage/index.d.ts +1 -1
- package/runtime/storage/life-evidence/types.d.ts +5 -5
- package/runtime/storage/quiet/quiet-artifact-types.d.ts +4 -4
- package/runtime/storage/quiet/quiet-artifact-writer.d.ts +2 -2
- package/runtime/storage/services/write-validation-gate.d.ts +1 -1
- package/runtime/storage/services/write-validation-gate.js +15 -3
- package/runtime/storage/snapshots/types.d.ts +8 -8
- package/runtime/storage/user-interest/types.d.ts +3 -3
- package/runtime/storage/v8-state-stores.d.ts +15 -3
- package/runtime/storage/v8-state-stores.js +60 -39
|
@@ -4,6 +4,7 @@ import { createAgentGoalStore } from "../../storage/goal/agent-goal-store.js";
|
|
|
4
4
|
import { createNarrativeStateStore } from "../../storage/narrative/narrative-state-store.js";
|
|
5
5
|
import { createRelationshipMemoryStore } from "../../storage/relationship/relationship-memory-store.js";
|
|
6
6
|
import { createIdentityProfileStore } from "../../storage/services/identity-profile-store.js";
|
|
7
|
+
import { toCanonicalSourceRef } from "../../shared/source-ref-compat.js";
|
|
7
8
|
import { generateHeartbeatDigest, } from "../../observability/services/heartbeat-digest-assembler.js";
|
|
8
9
|
import { createHistoryDigestStore } from "../../storage/services/history-digest-store.js";
|
|
9
10
|
export async function loadSnapshotInputsForWorkspaceHeartbeat(readModels, options = {}) {
|
|
@@ -26,11 +27,7 @@ export async function loadSnapshotInputsForWorkspaceHeartbeat(readModels, option
|
|
|
26
27
|
const snapshot = await loadLifeEvidenceSnapshot(options.state, options.workspaceRoot, { limit: 50 },
|
|
27
28
|
// Skip repair gate here — runner is called inside a live cycle; gate ran at startup.
|
|
28
29
|
{ runRepairGate: false });
|
|
29
|
-
lifeEvidenceRefs = snapshot.evidenceRefs.map((ref) => (
|
|
30
|
-
id: ref.id,
|
|
31
|
-
kind: ref.kind,
|
|
32
|
-
uri: ref.uri,
|
|
33
|
-
}));
|
|
30
|
+
lifeEvidenceRefs = snapshot.evidenceRefs.map((ref) => toCanonicalSourceRef(ref));
|
|
34
31
|
platformEventCount = snapshot.platformEvents.length;
|
|
35
32
|
workEventCount = snapshot.workEvents.length;
|
|
36
33
|
if (snapshot.empty) {
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
export declare const NORMALIZED_EVIDENCE_SCHEMA_VERSION = 1;
|
|
24
24
|
export type EvidenceSourceKind = "post" | "comment" | "profile" | "task" | "event" | "game_state" | "notification" | "document" | "unknown";
|
|
25
25
|
export type SummaryProducer = "connector_rules" | "model_assist" | "operator_supplied";
|
|
26
|
+
export type EvidenceContentStatus = "content_present" | "content_missing" | "content_redacted";
|
|
27
|
+
export type EvidenceContentMissingReason = "id_only" | "empty_payload" | "unsupported_shape" | "redacted_private";
|
|
26
28
|
export interface EvidenceActor {
|
|
27
29
|
id?: string;
|
|
28
30
|
displayName?: string;
|
|
@@ -36,6 +38,8 @@ export interface NormalizedEvidenceContent {
|
|
|
36
38
|
externalId?: string;
|
|
37
39
|
title?: string;
|
|
38
40
|
summary: string;
|
|
41
|
+
contentStatus: EvidenceContentStatus;
|
|
42
|
+
contentMissingReason?: EvidenceContentMissingReason;
|
|
39
43
|
excerpt?: string;
|
|
40
44
|
canonicalText?: string;
|
|
41
45
|
actor?: EvidenceActor;
|
|
@@ -226,8 +226,25 @@ function normalizeSingleItem(item, options) {
|
|
|
226
226
|
const tags = extractTags(item);
|
|
227
227
|
const entities = extractEntities(item);
|
|
228
228
|
const metrics = extractMetrics(item);
|
|
229
|
-
const
|
|
230
|
-
const
|
|
229
|
+
const hasReadableContent = isNonEmptyString(bodyText) || isNonEmptyString(title);
|
|
230
|
+
const hasContentMarkers = isNonEmptyString(url) ||
|
|
231
|
+
(actor?.displayName && actor.displayName.length > 0) ||
|
|
232
|
+
tags.length > 0 ||
|
|
233
|
+
entities.length > 0 ||
|
|
234
|
+
(metrics && Object.keys(metrics).length > 0);
|
|
235
|
+
let contentStatus = "content_present";
|
|
236
|
+
let contentMissingReason;
|
|
237
|
+
let summary;
|
|
238
|
+
if (!hasReadableContent && !hasContentMarkers) {
|
|
239
|
+
contentStatus = "content_missing";
|
|
240
|
+
contentMissingReason = externalId ? "id_only" : "empty_payload";
|
|
241
|
+
summary = `Content missing: ${contentMissingReason === "id_only" ? "id-only evidence" : "empty payload"}`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const rawText = bodyText ?? title ?? "[no readable content]";
|
|
245
|
+
summary = truncate(rawText, 160);
|
|
246
|
+
}
|
|
247
|
+
const rawText = bodyText ?? title ?? "";
|
|
231
248
|
const excerpt = rawText.length > summary.length ? truncate(rawText, options.excerptMaxChars ?? 240) : undefined;
|
|
232
249
|
const canonicalText = truncate(rawText, options.canonicalTextMaxChars ?? 2000);
|
|
233
250
|
return {
|
|
@@ -238,6 +255,8 @@ function normalizeSingleItem(item, options) {
|
|
|
238
255
|
externalId,
|
|
239
256
|
title,
|
|
240
257
|
summary,
|
|
258
|
+
contentStatus,
|
|
259
|
+
contentMissingReason,
|
|
241
260
|
excerpt,
|
|
242
261
|
canonicalText,
|
|
243
262
|
actor,
|
|
@@ -31,6 +31,11 @@ import { extractNormalizedEvidenceItems, computeEvidenceContentHashSync, } from
|
|
|
31
31
|
function computeLegacyContentHash(content) {
|
|
32
32
|
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
33
33
|
}
|
|
34
|
+
function truncate(text, maxChars) {
|
|
35
|
+
if (text.length <= maxChars)
|
|
36
|
+
return text;
|
|
37
|
+
return `${text.slice(0, maxChars)}…`;
|
|
38
|
+
}
|
|
34
39
|
function buildSourceRef(platformId, capabilityId, itemId, observedAt) {
|
|
35
40
|
return {
|
|
36
41
|
uri: `sn://connector_result/${platformId}/${capabilityId}/${itemId}`,
|
|
@@ -191,6 +196,32 @@ async function normalizeLegacyConnectorEvidence(db, result, now) {
|
|
|
191
196
|
const observedAt = result.observedAt ?? now;
|
|
192
197
|
const sourceRef = buildSourceRef(result.platformId, result.capabilityId, itemId, observedAt);
|
|
193
198
|
const sensitivityHint = mergeSensitivityHint(inferSensitivityHint(item.content), item.sensitivityHint);
|
|
199
|
+
const trimmedContent = item.content.trim();
|
|
200
|
+
const looksLikeIdOnly = /^[a-z0-9_-]+$/i.test(trimmedContent) && trimmedContent.length < 64;
|
|
201
|
+
const contentStatus = looksLikeIdOnly || trimmedContent.length === 0
|
|
202
|
+
? "content_missing"
|
|
203
|
+
: "content_present";
|
|
204
|
+
const contentMissingReason = contentStatus === "content_missing"
|
|
205
|
+
? trimmedContent.length === 0
|
|
206
|
+
? "empty_payload"
|
|
207
|
+
: "id_only"
|
|
208
|
+
: undefined;
|
|
209
|
+
const normalized = {
|
|
210
|
+
schemaVersion: 1,
|
|
211
|
+
sourceKind: "unknown",
|
|
212
|
+
platformId: result.platformId,
|
|
213
|
+
capabilityId: result.capabilityId,
|
|
214
|
+
externalId: item.id,
|
|
215
|
+
summary: contentStatus === "content_missing"
|
|
216
|
+
? `Content missing: ${contentMissingReason === "id_only" ? "id-only evidence" : "empty payload"}`
|
|
217
|
+
: truncate(trimmedContent, 160),
|
|
218
|
+
contentStatus,
|
|
219
|
+
contentMissingReason,
|
|
220
|
+
excerpt: contentStatus === "content_present" ? truncate(trimmedContent, 240) : undefined,
|
|
221
|
+
canonicalText: contentStatus === "content_present" ? truncate(trimmedContent, 2000) : undefined,
|
|
222
|
+
observedAt,
|
|
223
|
+
summaryProducer: "connector_rules",
|
|
224
|
+
};
|
|
194
225
|
const writeResult = await writeEvidenceItem(db, {
|
|
195
226
|
id: `ev_${result.platformId}_${itemId}_${observedAt.replace(/[:.]/g, "")}`,
|
|
196
227
|
createdAt: now,
|
|
@@ -201,7 +232,7 @@ async function normalizeLegacyConnectorEvidence(db, result, now) {
|
|
|
201
232
|
sourceRefs: [sourceRef],
|
|
202
233
|
redactionClass: sensitivityHint === "sensitive" ? "blocked" : "none",
|
|
203
234
|
lifecycleStatus: "pending",
|
|
204
|
-
payloadJson:
|
|
235
|
+
payloadJson: JSON.stringify(normalized),
|
|
205
236
|
});
|
|
206
237
|
if ("id" in writeResult) {
|
|
207
238
|
evidenceIds.push(writeResult.id);
|
|
@@ -116,7 +116,7 @@ export declare const connectorManifestV6Schema: z.ZodObject<{
|
|
|
116
116
|
export type ConnectorManifestV6 = z.infer<typeof connectorManifestV6Schema>;
|
|
117
117
|
export interface ConnectorConflict {
|
|
118
118
|
platformId: string;
|
|
119
|
-
existingSource: "built_in" | "workspace";
|
|
119
|
+
existingSource: "built_in" | "workspace" | "workspace_shadow";
|
|
120
120
|
attemptedSource: "built_in" | "workspace";
|
|
121
121
|
reason: string;
|
|
122
122
|
}
|
|
@@ -127,7 +127,7 @@ export interface ConnectorManifestValidationError {
|
|
|
127
127
|
}
|
|
128
128
|
export interface ConnectorInventoryEntry {
|
|
129
129
|
platformId: string;
|
|
130
|
-
source: "built_in" | "workspace";
|
|
130
|
+
source: "built_in" | "workspace" | "workspace_shadow";
|
|
131
131
|
manifestPath?: string;
|
|
132
132
|
trustStatus: ConnectorTrustStatus;
|
|
133
133
|
executable: boolean;
|
|
@@ -41,6 +41,12 @@ function manifestToInventoryEntry(manifest, source, manifestPath) {
|
|
|
41
41
|
validationErrors: [],
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
function isSafeBuiltInShadow(manifest) {
|
|
45
|
+
const reason = manifest.trust?.reason?.trim();
|
|
46
|
+
if (!manifest.trust?.override || !reason)
|
|
47
|
+
return false;
|
|
48
|
+
return manifest.runner.kind === "declarative_http" || manifest.runner.kind === "scriptable_node";
|
|
49
|
+
}
|
|
44
50
|
/**
|
|
45
51
|
* DynamicConnectorRegistry scans workspace manifests, validates, classifies trust,
|
|
46
52
|
* merges built-in entries, applies fail-closed conflict policy, and publishes
|
|
@@ -79,11 +85,20 @@ export class DynamicConnectorRegistry {
|
|
|
79
85
|
}
|
|
80
86
|
const manifest = parseResult.manifest;
|
|
81
87
|
const manifestPath = path.relative(workspaceRoot, file.path);
|
|
82
|
-
// Duplicate platformId without override -> fail-closed
|
|
88
|
+
// Duplicate platformId without explicit safe override -> fail-closed.
|
|
89
|
+
// Built-ins may be shadowed only by an auditable workspace manifest with
|
|
90
|
+
// a trusted runner kind, so operators can repair endpoint config without
|
|
91
|
+
// silently replacing native code.
|
|
83
92
|
if (builtInMap.has(manifest.platformId) || dynamicMap.has(manifest.platformId)) {
|
|
84
93
|
const existing = builtInMap.get(manifest.platformId) ?? dynamicMap.get(manifest.platformId);
|
|
85
94
|
const allowOverride = manifest.trust?.override === true;
|
|
86
95
|
const isTrustedSource = existing.source === "built_in";
|
|
96
|
+
if (isTrustedSource && isSafeBuiltInShadow(manifest)) {
|
|
97
|
+
const entry = manifestToInventoryEntry(manifest, "workspace_shadow", manifestPath);
|
|
98
|
+
dynamicMap.set(manifest.platformId, entry);
|
|
99
|
+
registered++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
87
102
|
if (!allowOverride || isTrustedSource) {
|
|
88
103
|
conflicts.push({
|
|
89
104
|
platformId: manifest.platformId,
|
|
@@ -51,7 +51,13 @@ function channelPriorityForRunner(manifest) {
|
|
|
51
51
|
return ["browser"];
|
|
52
52
|
return ["api_rest"];
|
|
53
53
|
}
|
|
54
|
-
function
|
|
54
|
+
function isSafeBuiltInShadow(manifest) {
|
|
55
|
+
const reason = manifest.trust?.reason?.trim();
|
|
56
|
+
if (!manifest.trust?.override || !reason)
|
|
57
|
+
return false;
|
|
58
|
+
return manifest.runner.kind === "declarative_http" || manifest.runner.kind === "scriptable_node";
|
|
59
|
+
}
|
|
60
|
+
function registerWorkspaceManifests(registry, builtInPlatformIds, workspaceRoot) {
|
|
55
61
|
if (!workspaceRoot)
|
|
56
62
|
return;
|
|
57
63
|
for (const file of scanConnectorManifests(workspaceRoot)) {
|
|
@@ -59,6 +65,12 @@ function registerWorkspaceManifests(registry, workspaceRoot) {
|
|
|
59
65
|
if (!parsed.ok)
|
|
60
66
|
continue;
|
|
61
67
|
const manifest = parsed.manifest;
|
|
68
|
+
// Built-in platforms may only be shadowed by auditable, trusted-runner manifests.
|
|
69
|
+
// Unsafe shadows are ignored by execution registry; they remain visible in
|
|
70
|
+
// DynamicConnectorRegistry conflicts for diagnostics.
|
|
71
|
+
if (builtInPlatformIds.has(manifest.platformId) && !isSafeBuiltInShadow(manifest)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
62
74
|
try {
|
|
63
75
|
registry.register({
|
|
64
76
|
platformId: manifest.platformId,
|
|
@@ -201,8 +213,8 @@ function createMoltbookMockRunner(workspaceRoot) {
|
|
|
201
213
|
latencyMs: Date.now() - started,
|
|
202
214
|
success: false,
|
|
203
215
|
error: {
|
|
204
|
-
code: "
|
|
205
|
-
detail: String(err)
|
|
216
|
+
code: "configuration_missing",
|
|
217
|
+
detail: `moltbook mock unreadable: ${err instanceof Error ? err.message : String(err)}`,
|
|
206
218
|
},
|
|
207
219
|
};
|
|
208
220
|
}
|
|
@@ -424,9 +436,13 @@ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
|
|
|
424
436
|
const workspaceManifest = workspaceManifestResult?.manifest;
|
|
425
437
|
const isBuiltInPlatform = platformId === "moltbook" ||
|
|
426
438
|
platformId === "evomap" ||
|
|
427
|
-
platformId === "agent-world"
|
|
439
|
+
platformId === "agent-world" ||
|
|
440
|
+
platformId === "instreet";
|
|
441
|
+
const effectiveWorkspaceManifest = workspaceManifest && (!isBuiltInPlatform || isSafeBuiltInShadow(workspaceManifest))
|
|
442
|
+
? workspaceManifest
|
|
443
|
+
: undefined;
|
|
428
444
|
const requiresCredential = isBuiltInPlatform ||
|
|
429
|
-
Boolean(
|
|
445
|
+
Boolean(effectiveWorkspaceManifest?.credentials.some((credential) => credential.required !== false));
|
|
430
446
|
const credential = requiresCredential
|
|
431
447
|
? await vault.loadCredentialContext(platformId)
|
|
432
448
|
: undefined;
|
|
@@ -448,9 +464,33 @@ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
|
|
|
448
464
|
const activeCredential = credential?.status === "active" && credential.encryptedValue
|
|
449
465
|
? { encryptedValue: credential.encryptedValue }
|
|
450
466
|
: undefined;
|
|
467
|
+
// Safe workspace shadows of built-in platforms take precedence over built-in runners.
|
|
468
|
+
if (effectiveWorkspaceManifest && effectiveWorkspaceManifest.runner.kind === "declarative_http") {
|
|
469
|
+
const httpRunner = createDeclarativeHttpRunner(effectiveWorkspaceManifest, activeCredential);
|
|
470
|
+
return httpRunner.run(_plan, request);
|
|
471
|
+
}
|
|
472
|
+
if (effectiveWorkspaceManifest && effectiveWorkspaceManifest.runner.kind === "scriptable_node") {
|
|
473
|
+
if (!workspaceManifestResult) {
|
|
474
|
+
return {
|
|
475
|
+
platformId,
|
|
476
|
+
channel: request.preferredChannel ?? "api_rest",
|
|
477
|
+
latencyMs: Date.now() - started,
|
|
478
|
+
success: false,
|
|
479
|
+
error: {
|
|
480
|
+
code: "configuration_missing",
|
|
481
|
+
detail: "scriptable_node requires workspace manifest with manifestDir",
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const scriptRunner = createScriptableNodeRunner(effectiveWorkspaceManifest, workspaceManifestResult.manifestDir, activeCredential);
|
|
486
|
+
return scriptRunner.run(_plan, request);
|
|
487
|
+
}
|
|
451
488
|
if (platformId === "moltbook") {
|
|
452
489
|
const baseUrl = process.env.SECOND_NATURE_MOLTBOOK_BASE_URL;
|
|
453
490
|
if (baseUrl) {
|
|
491
|
+
const effectivePlan = request.intent === "feed.read" && _plan.channel !== "api_rest"
|
|
492
|
+
? { ..._plan, channel: "api_rest", endpointMode: "rest_json", degraded: false }
|
|
493
|
+
: _plan;
|
|
454
494
|
const apiClient = createMoltbookApiClient({
|
|
455
495
|
baseUrl,
|
|
456
496
|
accessToken: activeCredential.encryptedValue,
|
|
@@ -461,13 +501,13 @@ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
|
|
|
461
501
|
skillRunner: {
|
|
462
502
|
run: async () => {
|
|
463
503
|
throw {
|
|
464
|
-
code: "
|
|
504
|
+
code: "configuration_missing",
|
|
465
505
|
detail: "moltbook_skill_runner_not_configured",
|
|
466
506
|
};
|
|
467
507
|
},
|
|
468
508
|
},
|
|
469
509
|
});
|
|
470
|
-
return runner.run(
|
|
510
|
+
return runner.run(effectivePlan, request);
|
|
471
511
|
}
|
|
472
512
|
// Mock fallback when real API is not configured
|
|
473
513
|
const mockRunner = createMoltbookMockRunner(workspaceRoot);
|
|
@@ -583,28 +623,6 @@ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
|
|
|
583
623
|
},
|
|
584
624
|
};
|
|
585
625
|
}
|
|
586
|
-
// Wave 83: workspace declarative_http connector fallback
|
|
587
|
-
if (workspaceManifest && workspaceManifest.runner.kind === "declarative_http") {
|
|
588
|
-
const httpRunner = createDeclarativeHttpRunner(workspaceManifest, activeCredential);
|
|
589
|
-
return httpRunner.run(_plan, request);
|
|
590
|
-
}
|
|
591
|
-
// Wave 90: workspace scriptable_node connector
|
|
592
|
-
if (workspaceManifest && workspaceManifest.runner.kind === "scriptable_node") {
|
|
593
|
-
if (!workspaceManifestResult) {
|
|
594
|
-
return {
|
|
595
|
-
platformId,
|
|
596
|
-
channel: request.preferredChannel ?? "api_rest",
|
|
597
|
-
latencyMs: Date.now() - started,
|
|
598
|
-
success: false,
|
|
599
|
-
error: {
|
|
600
|
-
code: "configuration_missing",
|
|
601
|
-
detail: "scriptable_node requires workspace manifest with manifestDir",
|
|
602
|
-
},
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
const scriptRunner = createScriptableNodeRunner(workspaceManifest, workspaceManifestResult.manifestDir, activeCredential);
|
|
606
|
-
return scriptRunner.run(_plan, request);
|
|
607
|
-
}
|
|
608
626
|
return {
|
|
609
627
|
platformId,
|
|
610
628
|
channel: request.preferredChannel ?? "api_rest",
|
|
@@ -621,11 +639,12 @@ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
|
|
|
621
639
|
export function createConnectorExecutorAdapter(options) {
|
|
622
640
|
const vault = createCredentialVault(options.stateDb.db);
|
|
623
641
|
const registry = new CapabilityContractRegistry();
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
642
|
+
const builtInManifests = [moltbookManifest, evomapManifest, agentWorldManifest, instreetManifest];
|
|
643
|
+
const builtInPlatformIds = new Set(builtInManifests.map((m) => m.platformId));
|
|
644
|
+
for (const manifest of builtInManifests) {
|
|
645
|
+
registry.register({ ...manifest });
|
|
646
|
+
}
|
|
647
|
+
registerWorkspaceManifests(registry, builtInPlatformIds, options.workspaceRoot);
|
|
629
648
|
const cooldownPort = createConnectorCooldownPort(options.stateDb);
|
|
630
649
|
const routeContextPort = createCredentialRouteContextPort(vault, options.stateDb);
|
|
631
650
|
const routePlanner = new ConnectorRoutePlanner(registry, routeContextPort, new ChannelHealthStore());
|
|
@@ -641,7 +660,7 @@ export function createConnectorExecutorAdapter(options) {
|
|
|
641
660
|
});
|
|
642
661
|
return {
|
|
643
662
|
async executeEffect(input) {
|
|
644
|
-
registerWorkspaceManifests(registry, options.workspaceRoot);
|
|
663
|
+
registerWorkspaceManifests(registry, builtInPlatformIds, options.workspaceRoot);
|
|
645
664
|
return policy.executeWithPolicy(input.intent, {
|
|
646
665
|
platformId: input.platformId,
|
|
647
666
|
intent: input.intent,
|
|
@@ -38,11 +38,15 @@ export interface ActionClosureRecord {
|
|
|
38
38
|
nextState: string;
|
|
39
39
|
reason: V8ReasonCode;
|
|
40
40
|
sourceRefs: SourceRef[];
|
|
41
|
+
proofRefs?: SourceRef[];
|
|
42
|
+
traceRefs?: SourceRef[];
|
|
41
43
|
memoryReviewCandidate?: MemoryReviewCandidateClosure;
|
|
42
44
|
closedAt: string;
|
|
43
45
|
}
|
|
44
46
|
export interface RecordClosureOptions {
|
|
45
47
|
now?: string;
|
|
48
|
+
platformId?: string;
|
|
49
|
+
capabilityId?: string;
|
|
46
50
|
}
|
|
47
51
|
export type RecordClosureResult = {
|
|
48
52
|
status: "recorded";
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Test coverage: tests/unit/action/action-closure-recorder.test.ts
|
|
22
22
|
*/
|
|
23
23
|
import { writeActionClosureRecord, readActionClosuresByCycle, } from "../../../storage/v8-state-stores.js";
|
|
24
|
+
import { buildClosureProvenance, cycleTraceRef, closureTraceRef, decisionProofRef, } from "../../../shared/provenance-tier.js";
|
|
24
25
|
// ───────────────────────────────────────────────────────────────
|
|
25
26
|
// Helpers
|
|
26
27
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -50,7 +51,8 @@ export async function recordNoActionClosure(db, cycleId, noActionReason, options
|
|
|
50
51
|
status: "no_action",
|
|
51
52
|
reason: noActionReason,
|
|
52
53
|
nextState: "await_next_cycle",
|
|
53
|
-
sourceRefs: [
|
|
54
|
+
sourceRefs: [cycleTraceRef(cycleId)],
|
|
55
|
+
proofRefs: [
|
|
54
56
|
{
|
|
55
57
|
uri: `sn://closure/no_action/${cycleId}`,
|
|
56
58
|
family: "action_closure",
|
|
@@ -59,9 +61,9 @@ export async function recordNoActionClosure(db, cycleId, noActionReason, options
|
|
|
59
61
|
resolveStatus: "resolvable",
|
|
60
62
|
},
|
|
61
63
|
],
|
|
64
|
+
traceRefs: [cycleTraceRef(cycleId)],
|
|
62
65
|
redactionClass: "none",
|
|
63
|
-
|
|
64
|
-
payloadJson: JSON.stringify({ dispatchAttempt: 0, inputSummary: "no-action" }),
|
|
66
|
+
payload: { dispatchAttempt: 0, inputSummary: "no-action" },
|
|
65
67
|
});
|
|
66
68
|
if ("reason" in result) {
|
|
67
69
|
return result;
|
|
@@ -74,21 +76,28 @@ export async function recordNoActionClosure(db, cycleId, noActionReason, options
|
|
|
74
76
|
export async function recordRememberClosure(db, cycleId, memoryReviewCandidate, options) {
|
|
75
77
|
const now = options?.now ?? new Date().toISOString();
|
|
76
78
|
const closureId = `cls_remember_${cycleId}_${now.replace(/[:.]/g, "")}`;
|
|
79
|
+
const provenance = buildClosureProvenance({
|
|
80
|
+
sourceRefs: memoryReviewCandidate.sourceRefs,
|
|
81
|
+
traceRefs: [cycleTraceRef(cycleId)],
|
|
82
|
+
});
|
|
77
83
|
const result = await writeActionClosureRecord(db, {
|
|
78
84
|
id: closureId,
|
|
79
85
|
createdAt: now,
|
|
80
86
|
cycleId,
|
|
87
|
+
platformId: options?.platformId ?? "heartbeat",
|
|
88
|
+
capabilityId: options?.capabilityId,
|
|
81
89
|
status: "completed",
|
|
82
90
|
reason: "remember_for_review",
|
|
83
91
|
nextState: "pending_daily_review",
|
|
84
|
-
sourceRefs:
|
|
92
|
+
sourceRefs: provenance.sourceRefs,
|
|
93
|
+
proofRefs: provenance.proofRefs,
|
|
94
|
+
traceRefs: provenance.traceRefs,
|
|
85
95
|
redactionClass: "none",
|
|
86
|
-
|
|
87
|
-
payloadJson: JSON.stringify({
|
|
96
|
+
payload: {
|
|
88
97
|
memoryReviewCandidate,
|
|
89
98
|
dispatchAttempt: 1,
|
|
90
99
|
inputSummary: `remember_for_review topic=${memoryReviewCandidate.topicKey}`,
|
|
91
|
-
}
|
|
100
|
+
},
|
|
92
101
|
});
|
|
93
102
|
if ("reason" in result) {
|
|
94
103
|
return result;
|
|
@@ -101,24 +110,21 @@ export async function recordRememberClosure(db, cycleId, memoryReviewCandidate,
|
|
|
101
110
|
export async function recordPolicyOutcomeClosure(db, cycleId, closureStatus, reason, params, options) {
|
|
102
111
|
const now = options?.now ?? new Date().toISOString();
|
|
103
112
|
const closureId = `cls_${closureStatus}_${cycleId}_${now.replace(/[:.]/g, "")}`;
|
|
104
|
-
const sourceRefs =
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
const sourceRefs = params.proposalId
|
|
114
|
+
? [
|
|
115
|
+
{
|
|
116
|
+
uri: `sn://proposal/${params.proposalId}`,
|
|
117
|
+
family: "action_closure",
|
|
118
|
+
id: params.proposalId,
|
|
119
|
+
redactionClass: "none",
|
|
120
|
+
resolveStatus: "resolvable",
|
|
121
|
+
},
|
|
122
|
+
]
|
|
123
|
+
: [];
|
|
124
|
+
const proofRefs = [
|
|
125
|
+
closureTraceRef(closureId),
|
|
126
|
+
...(params.decisionId ? [decisionProofRef(params.decisionId)] : []),
|
|
112
127
|
];
|
|
113
|
-
if (params.decisionId) {
|
|
114
|
-
sourceRefs.push({
|
|
115
|
-
uri: `sn://decision/${params.decisionId}`,
|
|
116
|
-
family: "action_closure",
|
|
117
|
-
id: params.decisionId,
|
|
118
|
-
redactionClass: "none",
|
|
119
|
-
resolveStatus: "resolvable",
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
128
|
const result = await writeActionClosureRecord(db, {
|
|
123
129
|
id: closureId,
|
|
124
130
|
createdAt: now,
|
|
@@ -131,14 +137,15 @@ export async function recordPolicyOutcomeClosure(db, cycleId, closureStatus, rea
|
|
|
131
137
|
reason,
|
|
132
138
|
nextState: params.nextState ?? "await_next_cycle",
|
|
133
139
|
sourceRefs,
|
|
140
|
+
proofRefs,
|
|
141
|
+
traceRefs: [cycleTraceRef(cycleId)],
|
|
134
142
|
redactionClass: "none",
|
|
135
|
-
|
|
136
|
-
payloadJson: JSON.stringify({
|
|
143
|
+
payload: {
|
|
137
144
|
dispatchAttempt: 1,
|
|
138
145
|
inputSummary: buildInputSummary(params.proposalId, params.decisionId),
|
|
139
146
|
postProcessing: params.postProcessing ?? [],
|
|
140
147
|
downgradedActionKind: params.downgradedActionKind,
|
|
141
|
-
}
|
|
148
|
+
},
|
|
142
149
|
});
|
|
143
150
|
if ("reason" in result) {
|
|
144
151
|
return result;
|
|
@@ -151,14 +158,19 @@ export async function recordPolicyOutcomeClosure(db, cycleId, closureStatus, rea
|
|
|
151
158
|
export async function recordExecutionClosure(db, cycleId, closureStatus, reason, params, options) {
|
|
152
159
|
const now = options?.now ?? new Date().toISOString();
|
|
153
160
|
const closureId = `cls_exec_${closureStatus}_${cycleId}_${now.replace(/[:.]/g, "")}`;
|
|
154
|
-
const sourceRefs =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
const sourceRefs = params.executionResultRef
|
|
162
|
+
? [
|
|
163
|
+
{
|
|
164
|
+
uri: params.executionResultRef,
|
|
165
|
+
family: "connector_result",
|
|
166
|
+
id: params.executionResultRef,
|
|
167
|
+
redactionClass: "none",
|
|
168
|
+
resolveStatus: "resolvable",
|
|
169
|
+
},
|
|
170
|
+
]
|
|
171
|
+
: [];
|
|
172
|
+
const proofRefs = [
|
|
173
|
+
closureTraceRef(closureId),
|
|
162
174
|
];
|
|
163
175
|
const result = await writeActionClosureRecord(db, {
|
|
164
176
|
id: closureId,
|
|
@@ -172,15 +184,16 @@ export async function recordExecutionClosure(db, cycleId, closureStatus, reason,
|
|
|
172
184
|
reason,
|
|
173
185
|
nextState: params.nextState ?? (closureStatus === "completed" ? "await_next_cycle" : "retryable"),
|
|
174
186
|
sourceRefs,
|
|
187
|
+
proofRefs,
|
|
188
|
+
traceRefs: [cycleTraceRef(cycleId)],
|
|
175
189
|
redactionClass: "none",
|
|
176
|
-
|
|
177
|
-
payloadJson: JSON.stringify({
|
|
190
|
+
payload: {
|
|
178
191
|
dispatchAttempt: 1,
|
|
179
192
|
executionResultRef: params.executionResultRef,
|
|
180
193
|
outputSummary: params.outputSummary,
|
|
181
194
|
inputSummary: buildInputSummary(params.proposalId, params.decisionId),
|
|
182
195
|
retryable: params.retryable ?? closureStatus === "failed",
|
|
183
|
-
}
|
|
196
|
+
},
|
|
184
197
|
});
|
|
185
198
|
if ("reason" in result) {
|
|
186
199
|
return result;
|
|
@@ -22,22 +22,13 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Test coverage: tests/unit/action/action-proposal-builder.test.ts
|
|
24
24
|
*/
|
|
25
|
-
import { readJudgmentVerdictById,
|
|
25
|
+
import { readJudgmentVerdictById, } from "../../../storage/v8-state-stores.js";
|
|
26
|
+
import { parseSourceRefs } from "../../../shared/serialization.js";
|
|
26
27
|
import { ACTION_KIND_REGISTRY } from "../../../shared/types/v8-contracts.js";
|
|
28
|
+
import { classifyDegradedStatus } from "../../../shared/degraded-status-classifier.js";
|
|
27
29
|
// ───────────────────────────────────────────────────────────────
|
|
28
30
|
// Helpers
|
|
29
31
|
// ───────────────────────────────────────────────────────────────
|
|
30
|
-
function parseVerdictSourceRefs(json) {
|
|
31
|
-
if (!json)
|
|
32
|
-
return [];
|
|
33
|
-
try {
|
|
34
|
-
const parsed = JSON.parse(json);
|
|
35
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
32
|
function buildExpectedOutput(actionKind) {
|
|
42
33
|
switch (actionKind) {
|
|
43
34
|
case "ignore":
|
|
@@ -80,7 +71,7 @@ export async function buildActionProposal(db, judgmentVerdictId, options) {
|
|
|
80
71
|
const verdict = readResult.row;
|
|
81
72
|
if (!verdict) {
|
|
82
73
|
return {
|
|
83
|
-
status: "
|
|
74
|
+
status: classifyDegradedStatus("state_unreadable"),
|
|
84
75
|
reason: "state_unreadable",
|
|
85
76
|
ownerStage: "policy",
|
|
86
77
|
sourceRefs: [],
|
|
@@ -99,8 +90,8 @@ export async function buildActionProposal(db, judgmentVerdictId, options) {
|
|
|
99
90
|
judgmentVerdictId,
|
|
100
91
|
};
|
|
101
92
|
}
|
|
102
|
-
const sourceRefs =
|
|
103
|
-
// remember → memory review candidate
|
|
93
|
+
const sourceRefs = parseSourceRefs(verdict.sourceRefsJson);
|
|
94
|
+
// remember → memory review candidate (no direct projection; orchestrator writes closure)
|
|
104
95
|
if (actionKind === "remember") {
|
|
105
96
|
const candidate = {
|
|
106
97
|
closureSubtype: "remember_for_review",
|
|
@@ -131,27 +122,10 @@ export async function buildActionProposal(db, judgmentVerdictId, options) {
|
|
|
131
122
|
},
|
|
132
123
|
]),
|
|
133
124
|
};
|
|
134
|
-
const closureId = `cls_remember_${judgmentVerdictId}_${now.replace(/[:.]/g, "")}`;
|
|
135
|
-
const writeResult = await writeActionClosureRecord(db, {
|
|
136
|
-
id: closureId,
|
|
137
|
-
createdAt: now,
|
|
138
|
-
cycleId,
|
|
139
|
-
platformId: "heartbeat",
|
|
140
|
-
status: "completed",
|
|
141
|
-
reason: "remember_for_review",
|
|
142
|
-
nextState: "pending_daily_review",
|
|
143
|
-
sourceRefs: candidate.sourceRefs,
|
|
144
|
-
redactionClass: "none",
|
|
145
|
-
lifecycleStatus: "closed",
|
|
146
|
-
payloadJson: JSON.stringify({ memoryReviewCandidate: candidate }),
|
|
147
|
-
});
|
|
148
|
-
if ("reason" in writeResult) {
|
|
149
|
-
return writeResult;
|
|
150
|
-
}
|
|
151
125
|
return {
|
|
152
126
|
status: "remember_for_review",
|
|
153
127
|
memoryReviewCandidate: candidate,
|
|
154
|
-
closureId,
|
|
128
|
+
closureId: `cls_remember_${judgmentVerdictId}_${now.replace(/[:.]/g, "")}`,
|
|
155
129
|
};
|
|
156
130
|
}
|
|
157
131
|
// Actionable verdict → build proposal
|
|
@@ -201,7 +175,7 @@ export async function buildActionProposals(db, judgmentVerdictIds, options) {
|
|
|
201
175
|
else if ("status" in result && result.status === "remember_for_review") {
|
|
202
176
|
rememberForReviews.push(result);
|
|
203
177
|
}
|
|
204
|
-
else if ("
|
|
178
|
+
else if ("operatorNextAction" in result) {
|
|
205
179
|
failed.push({
|
|
206
180
|
judgmentVerdictId,
|
|
207
181
|
degraded: result,
|
|
@@ -33,6 +33,7 @@ export interface ConnectorDispatchRequest {
|
|
|
33
33
|
decision: string;
|
|
34
34
|
};
|
|
35
35
|
sourceRefs: string;
|
|
36
|
+
proofRefs: string;
|
|
36
37
|
}
|
|
37
38
|
export interface GuidanceDispatchRequest {
|
|
38
39
|
type: "guidance";
|
|
@@ -43,6 +44,7 @@ export interface GuidanceDispatchRequest {
|
|
|
43
44
|
decision: string;
|
|
44
45
|
};
|
|
45
46
|
sourceRefs: string;
|
|
47
|
+
proofRefs: string;
|
|
46
48
|
}
|
|
47
49
|
export interface NoDispatchResult {
|
|
48
50
|
type: "none";
|