@haaaiawd/second-nature 0.1.51 → 0.2.0
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/openclaw.plugin.json +29 -29
- package/package.json +55 -55
- package/runtime/cli/commands/index.js +326 -325
- package/runtime/cli/ops/heartbeat-surface.d.ts +84 -84
- package/runtime/cli/ops/heartbeat-surface.js +100 -100
- package/runtime/cli/ops/ops-router.js +1555 -1482
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +85 -85
- package/runtime/cli/ops/workspace-heartbeat-runner.js +242 -242
- package/runtime/connectors/base/contract.d.ts +111 -111
- package/runtime/connectors/base/failure-taxonomy.d.ts +13 -13
- package/runtime/connectors/base/failure-taxonomy.js +186 -186
- package/runtime/connectors/base/map-life-evidence.js +137 -137
- package/runtime/connectors/base/policy-layer.js +202 -202
- package/runtime/connectors/evidence-normalizer.d.ts +45 -0
- package/runtime/connectors/evidence-normalizer.js +115 -0
- package/runtime/connectors/manifest/manifest-schema.d.ts +152 -152
- package/runtime/connectors/manifest/manifest-schema.js +54 -54
- package/runtime/connectors/services/connector-executor-adapter.d.ts +20 -20
- package/runtime/connectors/services/connector-executor-adapter.js +645 -645
- package/runtime/core/second-nature/action/action-closure-recorder.d.ts +70 -0
- package/runtime/core/second-nature/action/action-closure-recorder.js +184 -0
- package/runtime/core/second-nature/action/action-proposal-builder.d.ts +70 -0
- package/runtime/core/second-nature/action/action-proposal-builder.js +217 -0
- package/runtime/core/second-nature/action/autonomy-policy-evaluator.d.ts +43 -0
- package/runtime/core/second-nature/action/autonomy-policy-evaluator.js +213 -0
- package/runtime/core/second-nature/action/policy-bound-dispatch.d.ts +69 -0
- package/runtime/core/second-nature/action/policy-bound-dispatch.js +112 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-side-effect.d.ts +49 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-side-effect.js +100 -0
- package/runtime/core/second-nature/control-plane/accepted-projection-loader.d.ts +45 -0
- package/runtime/core/second-nature/control-plane/accepted-projection-loader.js +85 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +38 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +165 -0
- package/runtime/core/second-nature/guidance/guidance-proposal-consumer.d.ts +51 -0
- package/runtime/core/second-nature/guidance/guidance-proposal-consumer.js +113 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +24 -24
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -61
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +97 -97
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +397 -397
- package/runtime/core/second-nature/orchestrator/platform-capability-router.js +149 -149
- package/runtime/core/second-nature/perception/judgment-engine.d.ts +53 -0
- package/runtime/core/second-nature/perception/judgment-engine.js +239 -0
- package/runtime/core/second-nature/perception/perception-builder.d.ts +62 -0
- package/runtime/core/second-nature/perception/perception-builder.js +208 -0
- package/runtime/core/second-nature/perception/sensitivity-classifier.d.ts +37 -0
- package/runtime/core/second-nature/perception/sensitivity-classifier.js +87 -0
- package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.d.ts +44 -0
- package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.js +180 -0
- package/runtime/core/second-nature/quiet-dream/dream-scheduler.d.ts +36 -0
- package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +105 -0
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.d.ts +36 -0
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +151 -0
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +46 -0
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +123 -0
- package/runtime/observability/causal-loop-health.d.ts +44 -0
- package/runtime/observability/causal-loop-health.js +118 -0
- package/runtime/observability/diagnostic-redaction.d.ts +43 -0
- package/runtime/observability/diagnostic-redaction.js +114 -0
- package/runtime/observability/loop-stage-event-sink.d.ts +43 -0
- package/runtime/observability/loop-stage-event-sink.js +148 -0
- package/runtime/observability/loop-status.d.ts +46 -0
- package/runtime/observability/loop-status.js +85 -0
- package/runtime/shared/types/index.js +3 -0
- package/runtime/shared/types/v8-contracts.d.ts +86 -0
- package/runtime/shared/types/v8-contracts.js +84 -0
- package/runtime/storage/db/schema/index.d.ts +1 -0
- package/runtime/storage/db/schema/index.js +1 -0
- package/runtime/storage/db/schema/v8-entities.d.ts +1973 -0
- package/runtime/storage/db/schema/v8-entities.js +160 -0
- package/runtime/storage/v8-state-stores.d.ts +147 -0
- package/runtime/storage/v8-state-stores.js +491 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiagnosticRedaction — Attribute sensitivity blocks and redact diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Given a diagnostic payload, classify its sensitivity,
|
|
5
|
+
* redact credential-shaped values, preserve public technical summaries,
|
|
6
|
+
* and attribute the block to the responsible system.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §3.4`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/shared/types/v8-contracts.js` (SourceRef, RedactionClass, SensitivityClass)
|
|
14
|
+
*
|
|
15
|
+
* Boundary:
|
|
16
|
+
* - Pure function; no DB access.
|
|
17
|
+
* - Does not modify source data; returns redacted copy.
|
|
18
|
+
* - Public technical vocabulary is preserved.
|
|
19
|
+
*
|
|
20
|
+
* Test coverage: tests/unit/observability/diagnostic-redaction.test.ts
|
|
21
|
+
*/
|
|
22
|
+
// ───────────────────────────────────────────────────────────────
|
|
23
|
+
// Helpers
|
|
24
|
+
// ───────────────────────────────────────────────────────────────
|
|
25
|
+
function containsCredentialValue(text) {
|
|
26
|
+
// Match both key=value and Bearer token formats
|
|
27
|
+
return /\b(?:Bearer|token|secret|password|key|credential)\s*[:=\s]\s*[a-zA-Z0-9+/=_-]{8,}\b/i.test(text);
|
|
28
|
+
}
|
|
29
|
+
function containsPrivateMarker(text) {
|
|
30
|
+
return /\b(?:DM|private message|confidential|internal only)\b/i.test(text);
|
|
31
|
+
}
|
|
32
|
+
function redactCredentialValues(text) {
|
|
33
|
+
// Redact both key=value and Bearer token formats
|
|
34
|
+
return text.replace(/\b((?:Bearer|token|secret|password|key|credential)\s*[:=\s]\s*)[a-zA-Z0-9+/=_-]{8,}\b/gi, "$1[REDACTED]");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Classify sensitivity-related diagnostic attribution based on source system
|
|
38
|
+
* and reason code. Maps to deterministic attribution categories per
|
|
39
|
+
* observability-health-system.detail.md §4.2.
|
|
40
|
+
*/
|
|
41
|
+
export function classifyDiagnosticAttribution(sourceSystem, reasonCode) {
|
|
42
|
+
// Storage validation blocks
|
|
43
|
+
if (reasonCode === "state_unreadable" || reasonCode === "quiet_validation_failed") {
|
|
44
|
+
return "storage_validation_block";
|
|
45
|
+
}
|
|
46
|
+
// Dream redaction blocks
|
|
47
|
+
if (reasonCode === "dream_blocked_redaction") {
|
|
48
|
+
return "dream_redaction_block";
|
|
49
|
+
}
|
|
50
|
+
// Perception risk blocks
|
|
51
|
+
if (reasonCode === "perception_rules_only" || reasonCode === "evidence_batch_truncated") {
|
|
52
|
+
return "perception_risk_block";
|
|
53
|
+
}
|
|
54
|
+
// Policy denial
|
|
55
|
+
if (reasonCode?.startsWith("policy_denied") || reasonCode === "policy_downgraded_to_draft") {
|
|
56
|
+
return "policy_denial";
|
|
57
|
+
}
|
|
58
|
+
return "no_redaction_needed";
|
|
59
|
+
}
|
|
60
|
+
// ───────────────────────────────────────────────────────────────
|
|
61
|
+
// Public API
|
|
62
|
+
// ───────────────────────────────────────────────────────────────
|
|
63
|
+
export function projectDiagnosticRedaction(payload) {
|
|
64
|
+
const summary = payload.summary;
|
|
65
|
+
const detail = payload.detail;
|
|
66
|
+
// Credential value shape → blocked
|
|
67
|
+
if (containsCredentialValue(summary) || (detail && containsCredentialValue(detail))) {
|
|
68
|
+
return {
|
|
69
|
+
summary: redactCredentialValues(summary),
|
|
70
|
+
detail: detail ? redactCredentialValues(detail) : undefined,
|
|
71
|
+
redactionClass: "blocked",
|
|
72
|
+
attribution: `${payload.sourceSystem}:credential_shape_detected`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Classify attribution from source system + reason code
|
|
76
|
+
const attribution = classifyDiagnosticAttribution(payload.sourceSystem, payload.reasonCode);
|
|
77
|
+
// Private context → redacted
|
|
78
|
+
if (payload.sensitivityHint === "private_context" ||
|
|
79
|
+
containsPrivateMarker(summary) ||
|
|
80
|
+
(detail && containsPrivateMarker(detail))) {
|
|
81
|
+
return {
|
|
82
|
+
summary: "[redacted: private context]",
|
|
83
|
+
detail: undefined,
|
|
84
|
+
redactionClass: "redacted",
|
|
85
|
+
attribution: `${payload.sourceSystem}:${attribution}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Public technical → preserve with light redaction
|
|
89
|
+
if (payload.sensitivityHint === "public_technical") {
|
|
90
|
+
return {
|
|
91
|
+
summary,
|
|
92
|
+
detail,
|
|
93
|
+
redactionClass: "none",
|
|
94
|
+
attribution: `${payload.sourceSystem}:public_technical_preserved`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Source-system-specific attribution for blocked/redacted cases
|
|
98
|
+
if (attribution !== "no_redaction_needed") {
|
|
99
|
+
const isBlocked = attribution === "policy_denial" || attribution === "dream_redaction_block";
|
|
100
|
+
return {
|
|
101
|
+
summary: isBlocked ? `[blocked: ${attribution.replace(/_/g, " ")}]` : summary,
|
|
102
|
+
detail: isBlocked ? undefined : detail,
|
|
103
|
+
redactionClass: isBlocked ? "blocked" : "redacted",
|
|
104
|
+
attribution: `${payload.sourceSystem}:${attribution}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Default: preserve
|
|
108
|
+
return {
|
|
109
|
+
summary,
|
|
110
|
+
detail,
|
|
111
|
+
redactionClass: "none",
|
|
112
|
+
attribution: `${payload.sourceSystem}:no_redaction_needed`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoopStageEventSink — v8 observability stage event recorder.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Validate, redact, and append LoopStageEvent rows.
|
|
5
|
+
* Malformed events produce degraded diagnostics without blocking the
|
|
6
|
+
* heartbeat main loop.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §3.1`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (writeLoopStageEvent)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (LoopStageEvent, SourceRef, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does NOT make semantic decisions about stage health.
|
|
18
|
+
* - Does NOT block callers on DB failure; returns degraded result.
|
|
19
|
+
* - Redacts credential-shaped values before persistence.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/observability/loop-stage-event-sink.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import type { StateDatabase } from "../storage/db/index.js";
|
|
24
|
+
import type { LoopStageEvent, DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
25
|
+
export interface RecordLoopStageEventOptions {
|
|
26
|
+
now?: string;
|
|
27
|
+
}
|
|
28
|
+
export type RecordLoopStageEventResult = {
|
|
29
|
+
ok: true;
|
|
30
|
+
id: string;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
degraded: DegradedOperationResult;
|
|
34
|
+
};
|
|
35
|
+
export declare function recordLoopStageEvent(db: StateDatabase, event: Partial<LoopStageEvent>, options?: RecordLoopStageEventOptions): Promise<RecordLoopStageEventResult>;
|
|
36
|
+
export interface BatchRecordResult {
|
|
37
|
+
succeeded: string[];
|
|
38
|
+
failed: {
|
|
39
|
+
id?: string;
|
|
40
|
+
degraded: DegradedOperationResult;
|
|
41
|
+
}[];
|
|
42
|
+
}
|
|
43
|
+
export declare function recordLoopStageEvents(db: StateDatabase, events: Partial<LoopStageEvent>[], options?: RecordLoopStageEventOptions): Promise<BatchRecordResult>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoopStageEventSink — v8 observability stage event recorder.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Validate, redact, and append LoopStageEvent rows.
|
|
5
|
+
* Malformed events produce degraded diagnostics without blocking the
|
|
6
|
+
* heartbeat main loop.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §3.1`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (writeLoopStageEvent)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (LoopStageEvent, SourceRef, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does NOT make semantic decisions about stage health.
|
|
18
|
+
* - Does NOT block callers on DB failure; returns degraded result.
|
|
19
|
+
* - Redacts credential-shaped values before persistence.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/observability/loop-stage-event-sink.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import { writeLoopStageEvent } from "../storage/v8-state-stores.js";
|
|
24
|
+
function validateEvent(event) {
|
|
25
|
+
if (!event.cycleId || event.cycleId.trim().length === 0) {
|
|
26
|
+
return { ok: false, reason: "cycle_id_required", field: "cycleId" };
|
|
27
|
+
}
|
|
28
|
+
if (!event.stage || event.stage.trim().length === 0) {
|
|
29
|
+
return { ok: false, reason: "stage_required", field: "stage" };
|
|
30
|
+
}
|
|
31
|
+
if (!event.status || event.status.trim().length === 0) {
|
|
32
|
+
return { ok: false, reason: "status_required", field: "status" };
|
|
33
|
+
}
|
|
34
|
+
if (!event.occurredAt || event.occurredAt.trim().length === 0) {
|
|
35
|
+
return { ok: false, reason: "occurred_at_required", field: "occurredAt" };
|
|
36
|
+
}
|
|
37
|
+
const validStages = [
|
|
38
|
+
"ingestion",
|
|
39
|
+
"perception",
|
|
40
|
+
"judgment",
|
|
41
|
+
"policy",
|
|
42
|
+
"execution",
|
|
43
|
+
"closure",
|
|
44
|
+
"quiet",
|
|
45
|
+
"dream",
|
|
46
|
+
"projection",
|
|
47
|
+
];
|
|
48
|
+
if (!validStages.includes(event.stage)) {
|
|
49
|
+
return { ok: false, reason: "stage_invalid", field: "stage" };
|
|
50
|
+
}
|
|
51
|
+
const validStatuses = [
|
|
52
|
+
"started",
|
|
53
|
+
"completed",
|
|
54
|
+
"skipped",
|
|
55
|
+
"blocked",
|
|
56
|
+
"failed",
|
|
57
|
+
];
|
|
58
|
+
if (!validStatuses.includes(event.status)) {
|
|
59
|
+
return { ok: false, reason: "status_invalid", field: "status" };
|
|
60
|
+
}
|
|
61
|
+
return { ok: true };
|
|
62
|
+
}
|
|
63
|
+
// ───────────────────────────────────────────────────────────────
|
|
64
|
+
// Redaction
|
|
65
|
+
// ───────────────────────────────────────────────────────────────
|
|
66
|
+
function redactSourceRefs(refs) {
|
|
67
|
+
let hasRedacted = false;
|
|
68
|
+
let hasBlocked = false;
|
|
69
|
+
const redacted = refs.map((ref) => {
|
|
70
|
+
if (ref.sensitivityClass === "sensitive") {
|
|
71
|
+
hasBlocked = true;
|
|
72
|
+
return {
|
|
73
|
+
...ref,
|
|
74
|
+
redactionClass: "blocked",
|
|
75
|
+
resolveStatus: "redacted",
|
|
76
|
+
resolveFailureReason: "sensitivity_class_blocked",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (ref.sensitivityClass === "private_context") {
|
|
80
|
+
hasRedacted = true;
|
|
81
|
+
return {
|
|
82
|
+
...ref,
|
|
83
|
+
redactionClass: "redacted",
|
|
84
|
+
resolveStatus: "redacted",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return ref;
|
|
88
|
+
});
|
|
89
|
+
const redactionClass = hasBlocked
|
|
90
|
+
? "blocked"
|
|
91
|
+
: hasRedacted
|
|
92
|
+
? "redacted"
|
|
93
|
+
: "none";
|
|
94
|
+
return { redacted, redactionClass };
|
|
95
|
+
}
|
|
96
|
+
// ───────────────────────────────────────────────────────────────
|
|
97
|
+
// Public API
|
|
98
|
+
// ───────────────────────────────────────────────────────────────
|
|
99
|
+
export async function recordLoopStageEvent(db, event, options) {
|
|
100
|
+
const validation = validateEvent(event);
|
|
101
|
+
if (!validation.ok) {
|
|
102
|
+
const degraded = {
|
|
103
|
+
status: "degraded",
|
|
104
|
+
reason: "stage_event_missing",
|
|
105
|
+
ownerStage: event.stage || "ingestion",
|
|
106
|
+
sourceRefs: event.sourceRefs || [],
|
|
107
|
+
operatorNextAction: `Fix missing ${validation.field} in stage event`,
|
|
108
|
+
retryable: false,
|
|
109
|
+
};
|
|
110
|
+
return { ok: false, degraded };
|
|
111
|
+
}
|
|
112
|
+
const now = options?.now ?? new Date().toISOString();
|
|
113
|
+
const sourceRefs = event.sourceRefs ?? [];
|
|
114
|
+
const { redacted: redactedRefs, redactionClass } = redactSourceRefs(sourceRefs);
|
|
115
|
+
const record = {
|
|
116
|
+
id: event.id ?? `evt_${now.replace(/[:.]/g, "")}_${event.cycleId}_${event.stage}`,
|
|
117
|
+
cycleId: event.cycleId,
|
|
118
|
+
cycleSequence: event.cycleSequence ?? 0,
|
|
119
|
+
stage: event.stage,
|
|
120
|
+
status: event.status,
|
|
121
|
+
reason: event.reason,
|
|
122
|
+
sourceRefs: redactedRefs,
|
|
123
|
+
redactionClass,
|
|
124
|
+
occurredAt: event.occurredAt,
|
|
125
|
+
expectedDownstreamByCycle: event.expectedDownstreamByCycle,
|
|
126
|
+
payloadJson: event.payloadJson ?? null,
|
|
127
|
+
lifecycleStatus: "completed",
|
|
128
|
+
};
|
|
129
|
+
const result = await writeLoopStageEvent(db, record);
|
|
130
|
+
if ("id" in result) {
|
|
131
|
+
return { ok: true, id: result.id };
|
|
132
|
+
}
|
|
133
|
+
return { ok: false, degraded: result };
|
|
134
|
+
}
|
|
135
|
+
export async function recordLoopStageEvents(db, events, options) {
|
|
136
|
+
const succeeded = [];
|
|
137
|
+
const failed = [];
|
|
138
|
+
for (const event of events) {
|
|
139
|
+
const result = await recordLoopStageEvent(db, event, options);
|
|
140
|
+
if (result.ok) {
|
|
141
|
+
succeeded.push(result.id);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
failed.push({ id: event.id ?? "unknown", degraded: result.degraded });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { succeeded, failed };
|
|
148
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoopStatus — Expose loop health and stalled stage diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Call assembleLoopStatus, format into v8 loop_status shape,
|
|
5
|
+
* and include policy-denied closure counts.
|
|
6
|
+
*
|
|
7
|
+
* Design authority:
|
|
8
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/runtime-ops-system.md`
|
|
10
|
+
*
|
|
11
|
+
* Dependencies:
|
|
12
|
+
* - `src/observability/causal-loop-health.js` (assembleLoopStatus)
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readLoopStageEventsByStage)
|
|
14
|
+
*
|
|
15
|
+
* Boundary:
|
|
16
|
+
* - Read-only diagnostic query; does not modify state.
|
|
17
|
+
* - Returns degraded envelope when state unreadable.
|
|
18
|
+
*
|
|
19
|
+
* Test coverage: tests/unit/observability/loop-status.test.ts
|
|
20
|
+
*/
|
|
21
|
+
import type { StateDatabase } from "../storage/db/index.js";
|
|
22
|
+
import type { DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
23
|
+
export interface LoopStatusReadModel {
|
|
24
|
+
ok: true;
|
|
25
|
+
overallStatus: string;
|
|
26
|
+
stalledAt?: string;
|
|
27
|
+
lastCycleSequence: number;
|
|
28
|
+
lastHeartbeatAt?: string;
|
|
29
|
+
stageSummaries: StageSummary[];
|
|
30
|
+
policyDeniedCount: number;
|
|
31
|
+
nextAction: string;
|
|
32
|
+
}
|
|
33
|
+
export interface StageSummary {
|
|
34
|
+
stage: string;
|
|
35
|
+
eventCount: number;
|
|
36
|
+
stalled: boolean;
|
|
37
|
+
lastEventAt?: string;
|
|
38
|
+
}
|
|
39
|
+
export type LoopStatusResult = {
|
|
40
|
+
ok: true;
|
|
41
|
+
status: LoopStatusReadModel;
|
|
42
|
+
} | {
|
|
43
|
+
ok: false;
|
|
44
|
+
degraded: DegradedOperationResult;
|
|
45
|
+
};
|
|
46
|
+
export declare function readLoopStatus(db: StateDatabase): Promise<LoopStatusResult>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoopStatus — Expose loop health and stalled stage diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Call assembleLoopStatus, format into v8 loop_status shape,
|
|
5
|
+
* and include policy-denied closure counts.
|
|
6
|
+
*
|
|
7
|
+
* Design authority:
|
|
8
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/runtime-ops-system.md`
|
|
10
|
+
*
|
|
11
|
+
* Dependencies:
|
|
12
|
+
* - `src/observability/causal-loop-health.js` (assembleLoopStatus)
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readLoopStageEventsByStage)
|
|
14
|
+
*
|
|
15
|
+
* Boundary:
|
|
16
|
+
* - Read-only diagnostic query; does not modify state.
|
|
17
|
+
* - Returns degraded envelope when state unreadable.
|
|
18
|
+
*
|
|
19
|
+
* Test coverage: tests/unit/observability/loop-status.test.ts
|
|
20
|
+
*/
|
|
21
|
+
import { assembleLoopStatus } from "./causal-loop-health.js";
|
|
22
|
+
// ───────────────────────────────────────────────────────────────
|
|
23
|
+
// Helpers
|
|
24
|
+
// ───────────────────────────────────────────────────────────────
|
|
25
|
+
function computeNextAction(overallStatus, stalledAt) {
|
|
26
|
+
if (overallStatus === "healthy") {
|
|
27
|
+
return "No operator action required. Loop is progressing normally.";
|
|
28
|
+
}
|
|
29
|
+
if (overallStatus === "no_data") {
|
|
30
|
+
return "Run a heartbeat cycle or check connector configuration to generate initial evidence.";
|
|
31
|
+
}
|
|
32
|
+
if (overallStatus === "degraded") {
|
|
33
|
+
return "Check state database connectivity and retry. If persistent, review logs for state_unreadable errors.";
|
|
34
|
+
}
|
|
35
|
+
if (overallStatus === "stalled" && stalledAt) {
|
|
36
|
+
const actions = {
|
|
37
|
+
ingestion: "Verify connector credentials and platform availability. Check ingestion_connector_failed events.",
|
|
38
|
+
perception: "Check perception model availability or rules-only fallback. Review evidence_batch_empty vs perception_blocked_redaction.",
|
|
39
|
+
judgment: "Review judgment_low_confidence or judgment_missing_source_refs. Ensure perception cards have valid source refs.",
|
|
40
|
+
policy: "Review policy_denied_high_risk or policy_denied_missing_permission. Check affordance map and breaker status.",
|
|
41
|
+
execution: "Verify connector executor and guidance port availability. Check execution_unavailable or execution_timeout events.",
|
|
42
|
+
closure: "Check closure_missing or closure_failed. Verify state write validation and idempotency key uniqueness.",
|
|
43
|
+
quiet: "Quiet review may be empty or failed. Check quiet_empty_input or quiet_failed events. Daily review triggers after 36h window.",
|
|
44
|
+
dream: "Dream scheduler may be unavailable or redaction blocked. Check dream_scheduler_unavailable or dream_blocked_redaction events.",
|
|
45
|
+
projection: "Projection may be rejected or missing source refs. Check projection_rejected events and candidate validation.",
|
|
46
|
+
};
|
|
47
|
+
return actions[stalledAt] ?? `Review ${stalledAt} stage events for blocked or failed status.`;
|
|
48
|
+
}
|
|
49
|
+
return "Review loop stage events and state database health.";
|
|
50
|
+
}
|
|
51
|
+
// ───────────────────────────────────────────────────────────────
|
|
52
|
+
// Public API
|
|
53
|
+
// ───────────────────────────────────────────────────────────────
|
|
54
|
+
export async function readLoopStatus(db) {
|
|
55
|
+
const health = await assembleLoopStatus(db, { limit: 50 });
|
|
56
|
+
if ("status" in health && health.status === "degraded") {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
degraded: health,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const snapshot = health;
|
|
63
|
+
const stageSummaries = snapshot.stages.map((s) => ({
|
|
64
|
+
stage: s.stage,
|
|
65
|
+
eventCount: s.eventCount,
|
|
66
|
+
stalled: s.stalled,
|
|
67
|
+
lastEventAt: s.lastEventAt,
|
|
68
|
+
}));
|
|
69
|
+
// Policy denied count is a placeholder; real implementation would query action closures
|
|
70
|
+
const policyDeniedCount = 0;
|
|
71
|
+
const nextAction = computeNextAction(snapshot.overallStatus, snapshot.stalledAt);
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
status: {
|
|
75
|
+
ok: true,
|
|
76
|
+
overallStatus: snapshot.overallStatus,
|
|
77
|
+
stalledAt: snapshot.stalledAt,
|
|
78
|
+
lastCycleSequence: snapshot.lastCycleSequence,
|
|
79
|
+
lastHeartbeatAt: snapshot.lastHeartbeatAt,
|
|
80
|
+
stageSummaries,
|
|
81
|
+
policyDeniedCount,
|
|
82
|
+
nextAction,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -4,3 +4,6 @@ export * from "./outreach.js";
|
|
|
4
4
|
export * from "./source-ref.js";
|
|
5
5
|
export * from "./goal.js";
|
|
6
6
|
export * from "./v7-entities.js";
|
|
7
|
+
// v8-contracts intentionally NOT re-exported from index to avoid SourceRef
|
|
8
|
+
// name collision with v7 tuple type. v8 consumers import directly from
|
|
9
|
+
// `./v8-contracts.js`.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v8 Shared Contracts — Cross-system value contracts for Living Perception Loop.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Single source of truth for types that would otherwise be
|
|
5
|
+
* duplicated across perception-judgment, action-closure-policy,
|
|
6
|
+
* dream-quiet-memory, observability-health, and control-plane systems.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/action-closure-policy-system.detail.md §1`
|
|
11
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §2`
|
|
12
|
+
*
|
|
13
|
+
* Dependencies: none (primitive shared types).
|
|
14
|
+
* Boundary: Type definitions only; no runtime logic.
|
|
15
|
+
* Test coverage: tests/unit/contracts/v8-shared-contracts.test.ts
|
|
16
|
+
*/
|
|
17
|
+
export type PlatformNeutralActionKind = "ignore" | "watch" | "remember" | "notify_owner" | "draft_reply" | "auto_reply" | "draft_publish" | "auto_publish" | "run_connector";
|
|
18
|
+
export type ActionSideEffectClass = "none" | "local_state" | "owner_attention" | "external_write" | "external_read" | "capability_declared";
|
|
19
|
+
export type ActionAttentionClass = "none" | "owner_visible" | "external_visible" | "depends_on_capability";
|
|
20
|
+
export interface ActionKindMetadata {
|
|
21
|
+
kind: PlatformNeutralActionKind;
|
|
22
|
+
sideEffectClass: ActionSideEffectClass;
|
|
23
|
+
attentionClass: ActionAttentionClass;
|
|
24
|
+
requiresPolicyDecision: boolean;
|
|
25
|
+
allowedDowngrades: PlatformNeutralActionKind[];
|
|
26
|
+
}
|
|
27
|
+
export type ConnectorCapabilitySideEffect = "external_read" | "external_write" | "local_state" | "unknown";
|
|
28
|
+
export type SourceRefFamily = "evidence" | "perception" | "judgment" | "action_closure" | "quiet_review" | "dream_run" | "memory_projection" | "tool_experience" | "connector_result" | "audit";
|
|
29
|
+
export type RedactionClass = "none" | "redacted" | "blocked";
|
|
30
|
+
export type SensitivityClass = "public_technical" | "public_general" | "private_context" | "sensitive";
|
|
31
|
+
export type SourceResolveStatus = "resolvable" | "missing" | "redacted" | "permission_denied";
|
|
32
|
+
export interface SourceRef {
|
|
33
|
+
uri: string;
|
|
34
|
+
family: SourceRefFamily;
|
|
35
|
+
id: string;
|
|
36
|
+
redactionClass: RedactionClass;
|
|
37
|
+
sensitivityClass?: SensitivityClass;
|
|
38
|
+
resolveStatus?: SourceResolveStatus;
|
|
39
|
+
resolveFailureReason?: string;
|
|
40
|
+
}
|
|
41
|
+
export type HeartbeatCycleStatus = "started" | "completed" | "failed" | "degraded";
|
|
42
|
+
export interface HeartbeatCycleTrace {
|
|
43
|
+
cycleId: string;
|
|
44
|
+
cycleSequence: number;
|
|
45
|
+
heartbeatStartedAt: string;
|
|
46
|
+
heartbeatCompletedAt?: string;
|
|
47
|
+
inputCount: number;
|
|
48
|
+
outputCount: number;
|
|
49
|
+
expectedDownstreamByCycle?: number;
|
|
50
|
+
status: HeartbeatCycleStatus;
|
|
51
|
+
}
|
|
52
|
+
export type LoopStage = "ingestion" | "perception" | "judgment" | "policy" | "execution" | "closure" | "quiet" | "dream" | "projection";
|
|
53
|
+
export type LoopStageEventStatus = "started" | "completed" | "skipped" | "blocked" | "failed";
|
|
54
|
+
export interface LoopStageEvent {
|
|
55
|
+
id: string;
|
|
56
|
+
cycleId: string;
|
|
57
|
+
cycleSequence: number;
|
|
58
|
+
stage: LoopStage;
|
|
59
|
+
status: LoopStageEventStatus;
|
|
60
|
+
reason?: V8ReasonCode;
|
|
61
|
+
sourceRefs: SourceRef[];
|
|
62
|
+
redactionClass: RedactionClass;
|
|
63
|
+
occurredAt: string;
|
|
64
|
+
expectedDownstreamByCycle?: number;
|
|
65
|
+
payloadJson?: string;
|
|
66
|
+
}
|
|
67
|
+
export type MemoryReviewClosureSubtype = "remember_for_review";
|
|
68
|
+
export interface MemoryReviewCandidateClosure {
|
|
69
|
+
closureSubtype: MemoryReviewClosureSubtype;
|
|
70
|
+
perceptionRef: SourceRef;
|
|
71
|
+
judgmentVerdictRef: SourceRef;
|
|
72
|
+
topicKey: string;
|
|
73
|
+
memoryIntentReason: string;
|
|
74
|
+
reviewPriority: "low" | "medium" | "high";
|
|
75
|
+
sourceRefs: [SourceRef, ...SourceRef[]];
|
|
76
|
+
}
|
|
77
|
+
export interface DegradedOperationResult {
|
|
78
|
+
status: "degraded" | "blocked";
|
|
79
|
+
reason: V8ReasonCode;
|
|
80
|
+
ownerStage: LoopStage;
|
|
81
|
+
sourceRefs: SourceRef[];
|
|
82
|
+
operatorNextAction: string;
|
|
83
|
+
retryable: boolean;
|
|
84
|
+
}
|
|
85
|
+
export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "dream_scheduled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "evidence_batch_empty" | "evidence_batch_truncated" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
|
|
86
|
+
export declare const ACTION_KIND_REGISTRY: Readonly<Record<PlatformNeutralActionKind, ActionKindMetadata>>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v8 Shared Contracts — Cross-system value contracts for Living Perception Loop.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Single source of truth for types that would otherwise be
|
|
5
|
+
* duplicated across perception-judgment, action-closure-policy,
|
|
6
|
+
* dream-quiet-memory, observability-health, and control-plane systems.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/action-closure-policy-system.detail.md §1`
|
|
11
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §2`
|
|
12
|
+
*
|
|
13
|
+
* Dependencies: none (primitive shared types).
|
|
14
|
+
* Boundary: Type definitions only; no runtime logic.
|
|
15
|
+
* Test coverage: tests/unit/contracts/v8-shared-contracts.test.ts
|
|
16
|
+
*/
|
|
17
|
+
// ───────────────────────────────────────────────────────────────
|
|
18
|
+
// 7. Action Kind Registry Metadata Table
|
|
19
|
+
// ───────────────────────────────────────────────────────────────
|
|
20
|
+
export const ACTION_KIND_REGISTRY = {
|
|
21
|
+
ignore: {
|
|
22
|
+
kind: "ignore",
|
|
23
|
+
sideEffectClass: "none",
|
|
24
|
+
attentionClass: "none",
|
|
25
|
+
requiresPolicyDecision: false,
|
|
26
|
+
allowedDowngrades: [],
|
|
27
|
+
},
|
|
28
|
+
watch: {
|
|
29
|
+
kind: "watch",
|
|
30
|
+
sideEffectClass: "local_state",
|
|
31
|
+
attentionClass: "none",
|
|
32
|
+
requiresPolicyDecision: false,
|
|
33
|
+
allowedDowngrades: [],
|
|
34
|
+
},
|
|
35
|
+
remember: {
|
|
36
|
+
kind: "remember",
|
|
37
|
+
sideEffectClass: "local_state",
|
|
38
|
+
attentionClass: "none",
|
|
39
|
+
requiresPolicyDecision: true,
|
|
40
|
+
allowedDowngrades: ["watch"],
|
|
41
|
+
},
|
|
42
|
+
notify_owner: {
|
|
43
|
+
kind: "notify_owner",
|
|
44
|
+
sideEffectClass: "owner_attention",
|
|
45
|
+
attentionClass: "owner_visible",
|
|
46
|
+
requiresPolicyDecision: true,
|
|
47
|
+
allowedDowngrades: ["watch"],
|
|
48
|
+
},
|
|
49
|
+
draft_reply: {
|
|
50
|
+
kind: "draft_reply",
|
|
51
|
+
sideEffectClass: "local_state",
|
|
52
|
+
attentionClass: "owner_visible",
|
|
53
|
+
requiresPolicyDecision: true,
|
|
54
|
+
allowedDowngrades: ["notify_owner", "watch"],
|
|
55
|
+
},
|
|
56
|
+
auto_reply: {
|
|
57
|
+
kind: "auto_reply",
|
|
58
|
+
sideEffectClass: "external_write",
|
|
59
|
+
attentionClass: "external_visible",
|
|
60
|
+
requiresPolicyDecision: true,
|
|
61
|
+
allowedDowngrades: ["draft_reply", "notify_owner", "watch"],
|
|
62
|
+
},
|
|
63
|
+
draft_publish: {
|
|
64
|
+
kind: "draft_publish",
|
|
65
|
+
sideEffectClass: "local_state",
|
|
66
|
+
attentionClass: "owner_visible",
|
|
67
|
+
requiresPolicyDecision: true,
|
|
68
|
+
allowedDowngrades: ["notify_owner", "watch"],
|
|
69
|
+
},
|
|
70
|
+
auto_publish: {
|
|
71
|
+
kind: "auto_publish",
|
|
72
|
+
sideEffectClass: "external_write",
|
|
73
|
+
attentionClass: "external_visible",
|
|
74
|
+
requiresPolicyDecision: true,
|
|
75
|
+
allowedDowngrades: ["draft_publish", "notify_owner", "watch"],
|
|
76
|
+
},
|
|
77
|
+
run_connector: {
|
|
78
|
+
kind: "run_connector",
|
|
79
|
+
sideEffectClass: "capability_declared",
|
|
80
|
+
attentionClass: "depends_on_capability",
|
|
81
|
+
requiresPolicyDecision: true,
|
|
82
|
+
allowedDowngrades: ["notify_owner", "watch"],
|
|
83
|
+
},
|
|
84
|
+
};
|