@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,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DreamScheduler — Schedule Dream consolidation after Quiet completion.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Read a QuietDailyReview, create a DreamConsolidationRun
|
|
5
|
+
* with lifecycle trace, and write it to state. Handles unavailable
|
|
6
|
+
* scheduler by recording degraded state.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.detail.md §3.2`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.md §4.2`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readQuietDailyReviewById, writeDreamConsolidationRun)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (SourceRef, DegradedOperationResult, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does not run consolidation; only schedules and records lifecycle.
|
|
18
|
+
* - Does not form long-term memory; Dream runner does that.
|
|
19
|
+
* - Degrades gracefully on missing review or unreadable state.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/dream/dream-scheduler-lifecycle.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import { readQuietDailyReviewById, writeDreamConsolidationRun, } from "../../../storage/v8-state-stores.js";
|
|
24
|
+
// ───────────────────────────────────────────────────────────────
|
|
25
|
+
// Public API
|
|
26
|
+
// ───────────────────────────────────────────────────────────────
|
|
27
|
+
export async function scheduleDreamAfterQuiet(db, quietReviewId, options) {
|
|
28
|
+
const now = options?.now ?? new Date().toISOString();
|
|
29
|
+
const readResult = await readQuietDailyReviewById(db, quietReviewId);
|
|
30
|
+
if (readResult.degraded) {
|
|
31
|
+
return readResult.degraded;
|
|
32
|
+
}
|
|
33
|
+
const review = readResult.row;
|
|
34
|
+
if (!review) {
|
|
35
|
+
return {
|
|
36
|
+
status: "degraded",
|
|
37
|
+
reason: "state_unreadable",
|
|
38
|
+
ownerStage: "dream",
|
|
39
|
+
sourceRefs: [],
|
|
40
|
+
operatorNextAction: `QuietDailyReview ${quietReviewId} not found`,
|
|
41
|
+
retryable: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const runId = `dream_${quietReviewId}_${now.replace(/[:.]/g, "")}`;
|
|
45
|
+
// Scheduler unavailable → record blocked state
|
|
46
|
+
if (options?.schedulerAvailable === false) {
|
|
47
|
+
const writeResult = await writeDreamConsolidationRun(db, {
|
|
48
|
+
id: runId,
|
|
49
|
+
createdAt: now,
|
|
50
|
+
quietReviewId,
|
|
51
|
+
status: "blocked",
|
|
52
|
+
reason: "dream_scheduler_unavailable",
|
|
53
|
+
sourceRefs: [
|
|
54
|
+
{
|
|
55
|
+
uri: `sn://dream/scheduler_unavailable/${quietReviewId}`,
|
|
56
|
+
family: "dream_run",
|
|
57
|
+
id: quietReviewId,
|
|
58
|
+
redactionClass: "none",
|
|
59
|
+
resolveStatus: "resolvable",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
redactionClass: "none",
|
|
63
|
+
lifecycleStatus: "pending",
|
|
64
|
+
payloadJson: JSON.stringify({ scheduledAt: now, blocked: true }),
|
|
65
|
+
});
|
|
66
|
+
if ("reason" in writeResult) {
|
|
67
|
+
return writeResult;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
id: runId,
|
|
71
|
+
quietReviewId,
|
|
72
|
+
status: "blocked",
|
|
73
|
+
reason: "dream_scheduler_unavailable",
|
|
74
|
+
createdAt: now,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Normal schedule
|
|
78
|
+
const writeResult = await writeDreamConsolidationRun(db, {
|
|
79
|
+
id: runId,
|
|
80
|
+
createdAt: now,
|
|
81
|
+
quietReviewId,
|
|
82
|
+
status: "scheduled",
|
|
83
|
+
sourceRefs: [
|
|
84
|
+
{
|
|
85
|
+
uri: `sn://dream/scheduled/${quietReviewId}`,
|
|
86
|
+
family: "dream_run",
|
|
87
|
+
id: quietReviewId,
|
|
88
|
+
redactionClass: "none",
|
|
89
|
+
resolveStatus: "resolvable",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
redactionClass: "none",
|
|
93
|
+
lifecycleStatus: "pending",
|
|
94
|
+
payloadJson: JSON.stringify({ scheduledAt: now }),
|
|
95
|
+
});
|
|
96
|
+
if ("reason" in writeResult) {
|
|
97
|
+
return writeResult;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
id: runId,
|
|
101
|
+
quietReviewId,
|
|
102
|
+
status: "scheduled",
|
|
103
|
+
createdAt: now,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryProjectionLifecycle — Manage accepted long-term memory projections.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Accept, activate, supersede, and reject projections.
|
|
5
|
+
* When accepting a projection on a topic with existing active projection,
|
|
6
|
+
* supersede the old one.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.detail.md §3.4`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.md §4.2`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readMemoryProjectionsByTopic, writeLongTermMemoryProjection)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (SourceRef, DegradedOperationResult, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Only accepts projections with source refs.
|
|
18
|
+
* - Supersedes old active projections on same topic automatically.
|
|
19
|
+
* - Does not delete projections; only transitions status.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/dream/memory-projection-lifecycle.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import type { StateDatabase } from "../../../storage/db/index.js";
|
|
24
|
+
import type { SourceRef, DegradedOperationResult, V8ReasonCode } from "../../../shared/types/v8-contracts.js";
|
|
25
|
+
export interface ProjectionLifecycleResult {
|
|
26
|
+
projectionId: string;
|
|
27
|
+
status: "accepted" | "rejected" | "superseded" | "retired";
|
|
28
|
+
reason: V8ReasonCode;
|
|
29
|
+
supersedesProjectionId?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AcceptMemoryProjectionOptions {
|
|
32
|
+
now?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function acceptMemoryProjection(db: StateDatabase, candidateId: string, topicKey: string, memoryText: string, sourceRefs: SourceRef[], options?: AcceptMemoryProjectionOptions): Promise<ProjectionLifecycleResult | DegradedOperationResult>;
|
|
35
|
+
export declare function rejectMemoryProjection(db: StateDatabase, projectionId: string, candidateId: string, topicKey: string, sourceRefs: SourceRef[], reason?: V8ReasonCode, options?: AcceptMemoryProjectionOptions): Promise<ProjectionLifecycleResult | DegradedOperationResult>;
|
|
36
|
+
export declare function retireMemoryProjection(db: StateDatabase, projectionId: string, candidateId: string, topicKey: string, sourceRefs: SourceRef[], options?: AcceptMemoryProjectionOptions): Promise<ProjectionLifecycleResult | DegradedOperationResult>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryProjectionLifecycle — Manage accepted long-term memory projections.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Accept, activate, supersede, and reject projections.
|
|
5
|
+
* When accepting a projection on a topic with existing active projection,
|
|
6
|
+
* supersede the old one.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.detail.md §3.4`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.md §4.2`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readMemoryProjectionsByTopic, writeLongTermMemoryProjection)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (SourceRef, DegradedOperationResult, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Only accepts projections with source refs.
|
|
18
|
+
* - Supersedes old active projections on same topic automatically.
|
|
19
|
+
* - Does not delete projections; only transitions status.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/dream/memory-projection-lifecycle.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import { readMemoryProjectionsByTopic, writeLongTermMemoryProjection, } from "../../../storage/v8-state-stores.js";
|
|
24
|
+
// ───────────────────────────────────────────────────────────────
|
|
25
|
+
// Public API
|
|
26
|
+
// ───────────────────────────────────────────────────────────────
|
|
27
|
+
export async function acceptMemoryProjection(db, candidateId, topicKey, memoryText, sourceRefs, options) {
|
|
28
|
+
const now = options?.now ?? new Date().toISOString();
|
|
29
|
+
if (sourceRefs.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
status: "degraded",
|
|
32
|
+
reason: "source_refs_unresolved",
|
|
33
|
+
ownerStage: "projection",
|
|
34
|
+
sourceRefs: [],
|
|
35
|
+
operatorNextAction: "Acceptance requires source refs",
|
|
36
|
+
retryable: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const existing = await readMemoryProjectionsByTopic(db, topicKey);
|
|
40
|
+
if (existing.degraded) {
|
|
41
|
+
return existing.degraded;
|
|
42
|
+
}
|
|
43
|
+
// Find active projection to supersede
|
|
44
|
+
const activeProjection = existing.rows.find((r) => r.status === "active" || r.status === "accepted");
|
|
45
|
+
let supersedesId;
|
|
46
|
+
if (activeProjection) {
|
|
47
|
+
// Supersede existing active projection
|
|
48
|
+
const supersedeResult = await writeLongTermMemoryProjection(db, {
|
|
49
|
+
id: activeProjection.id,
|
|
50
|
+
createdAt: activeProjection.createdAt,
|
|
51
|
+
candidateId: activeProjection.candidateId,
|
|
52
|
+
topicKey: activeProjection.topicKey,
|
|
53
|
+
status: "superseded",
|
|
54
|
+
sourceRefs: parseSourceRefs(activeProjection.sourceRefsJson),
|
|
55
|
+
redactionClass: activeProjection.redactionClass,
|
|
56
|
+
lifecycleStatus: "superseded",
|
|
57
|
+
payloadJson: JSON.stringify({
|
|
58
|
+
supersededAt: now,
|
|
59
|
+
supersededBy: candidateId,
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
if ("reason" in supersedeResult) {
|
|
63
|
+
return supersedeResult;
|
|
64
|
+
}
|
|
65
|
+
supersedesId = activeProjection.id;
|
|
66
|
+
}
|
|
67
|
+
// Create new accepted/active projection
|
|
68
|
+
const projectionId = `proj_${candidateId}_${now.replace(/[:.]/g, "")}`;
|
|
69
|
+
const writeResult = await writeLongTermMemoryProjection(db, {
|
|
70
|
+
id: projectionId,
|
|
71
|
+
createdAt: now,
|
|
72
|
+
candidateId,
|
|
73
|
+
topicKey,
|
|
74
|
+
status: "active",
|
|
75
|
+
sourceRefs,
|
|
76
|
+
redactionClass: "none",
|
|
77
|
+
lifecycleStatus: "active",
|
|
78
|
+
payloadJson: JSON.stringify({
|
|
79
|
+
memoryText,
|
|
80
|
+
acceptedAt: now,
|
|
81
|
+
supersedesProjectionId: supersedesId,
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
if ("reason" in writeResult) {
|
|
85
|
+
return writeResult;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
projectionId,
|
|
89
|
+
status: "accepted",
|
|
90
|
+
reason: "projection_accepted",
|
|
91
|
+
supersedesProjectionId: supersedesId,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export async function rejectMemoryProjection(db, projectionId, candidateId, topicKey, sourceRefs, reason = "projection_rejected", options) {
|
|
95
|
+
const now = options?.now ?? new Date().toISOString();
|
|
96
|
+
const writeResult = await writeLongTermMemoryProjection(db, {
|
|
97
|
+
id: projectionId,
|
|
98
|
+
createdAt: now,
|
|
99
|
+
candidateId,
|
|
100
|
+
topicKey,
|
|
101
|
+
status: "rejected",
|
|
102
|
+
sourceRefs,
|
|
103
|
+
redactionClass: "none",
|
|
104
|
+
lifecycleStatus: "rejected",
|
|
105
|
+
payloadJson: JSON.stringify({ rejectedAt: now, reason }),
|
|
106
|
+
});
|
|
107
|
+
if ("reason" in writeResult) {
|
|
108
|
+
return writeResult;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
projectionId,
|
|
112
|
+
status: "rejected",
|
|
113
|
+
reason,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export async function retireMemoryProjection(db, projectionId, candidateId, topicKey, sourceRefs, options) {
|
|
117
|
+
const now = options?.now ?? new Date().toISOString();
|
|
118
|
+
const writeResult = await writeLongTermMemoryProjection(db, {
|
|
119
|
+
id: projectionId,
|
|
120
|
+
createdAt: now,
|
|
121
|
+
candidateId,
|
|
122
|
+
topicKey,
|
|
123
|
+
status: "retired",
|
|
124
|
+
sourceRefs,
|
|
125
|
+
redactionClass: "none",
|
|
126
|
+
lifecycleStatus: "retired",
|
|
127
|
+
payloadJson: JSON.stringify({ retiredAt: now }),
|
|
128
|
+
});
|
|
129
|
+
if ("reason" in writeResult) {
|
|
130
|
+
return writeResult;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
projectionId,
|
|
134
|
+
status: "retired",
|
|
135
|
+
reason: "projection_rejected",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// ───────────────────────────────────────────────────────────────
|
|
139
|
+
// Helpers
|
|
140
|
+
// ───────────────────────────────────────────────────────────────
|
|
141
|
+
function parseSourceRefs(json) {
|
|
142
|
+
if (!json)
|
|
143
|
+
return [];
|
|
144
|
+
try {
|
|
145
|
+
const parsed = JSON.parse(json);
|
|
146
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuietDailyReviewBuilder — Aggregate daily closures, perceptions, and
|
|
3
|
+
* memory-review candidates into a source-backed QuietDailyReview.
|
|
4
|
+
*
|
|
5
|
+
* Core logic: Read ActionClosureRecords by day, collect memory-review
|
|
6
|
+
* candidates, build summary, and write QuietDailyReview row.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.detail.md §3.1`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.md §4.2`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readActionClosuresByDay, writeQuietDailyReview)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (SourceRef, DegradedOperationResult, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does not form long-term memory; only emits review input for Dream.
|
|
18
|
+
* - Does not judge importance; reads closure status and risk flags.
|
|
19
|
+
* - Degrades gracefully on unreadable state.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/quiet/quiet-daily-review-builder.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import type { StateDatabase } from "../../../storage/db/index.js";
|
|
24
|
+
import type { SourceRef, DegradedOperationResult, V8ReasonCode } from "../../../shared/types/v8-contracts.js";
|
|
25
|
+
export interface QuietDailyReviewResult {
|
|
26
|
+
id: string;
|
|
27
|
+
day: string;
|
|
28
|
+
closureCount: number;
|
|
29
|
+
memoryCandidateCount: number;
|
|
30
|
+
sourceRefs: SourceRef[];
|
|
31
|
+
reviewSummary: string;
|
|
32
|
+
importanceSignals: string[];
|
|
33
|
+
createdAt: string;
|
|
34
|
+
}
|
|
35
|
+
export interface BuildQuietDailyReviewOptions {
|
|
36
|
+
day?: string;
|
|
37
|
+
now?: string;
|
|
38
|
+
}
|
|
39
|
+
export type BuildQuietDailyReviewOutput = {
|
|
40
|
+
status: "completed";
|
|
41
|
+
review: QuietDailyReviewResult;
|
|
42
|
+
} | {
|
|
43
|
+
status: "empty";
|
|
44
|
+
reason: V8ReasonCode;
|
|
45
|
+
} | DegradedOperationResult;
|
|
46
|
+
export declare function buildQuietDailyReview(db: StateDatabase, options?: BuildQuietDailyReviewOptions): Promise<BuildQuietDailyReviewOutput>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuietDailyReviewBuilder — Aggregate daily closures, perceptions, and
|
|
3
|
+
* memory-review candidates into a source-backed QuietDailyReview.
|
|
4
|
+
*
|
|
5
|
+
* Core logic: Read ActionClosureRecords by day, collect memory-review
|
|
6
|
+
* candidates, build summary, and write QuietDailyReview row.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.detail.md §3.1`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/dream-quiet-memory-system.md §4.2`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readActionClosuresByDay, writeQuietDailyReview)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (SourceRef, DegradedOperationResult, V8ReasonCode)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does not form long-term memory; only emits review input for Dream.
|
|
18
|
+
* - Does not judge importance; reads closure status and risk flags.
|
|
19
|
+
* - Degrades gracefully on unreadable state.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/quiet/quiet-daily-review-builder.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import { readActionClosuresByDay, writeQuietDailyReview, } from "../../../storage/v8-state-stores.js";
|
|
24
|
+
// ───────────────────────────────────────────────────────────────
|
|
25
|
+
// Config
|
|
26
|
+
// ───────────────────────────────────────────────────────────────
|
|
27
|
+
const QUIET_MAX_CLOSURES_PER_DAY = 200;
|
|
28
|
+
// ───────────────────────────────────────────────────────────────
|
|
29
|
+
// Helpers
|
|
30
|
+
// ───────────────────────────────────────────────────────────────
|
|
31
|
+
function todayString(now) {
|
|
32
|
+
return now.slice(0, 10);
|
|
33
|
+
}
|
|
34
|
+
function parsePayloadJson(json) {
|
|
35
|
+
if (!json)
|
|
36
|
+
return {};
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(json);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function buildSourceRefFromClosure(closure) {
|
|
45
|
+
return {
|
|
46
|
+
uri: `sn://closure/${closure.id}`,
|
|
47
|
+
family: "action_closure",
|
|
48
|
+
id: closure.id,
|
|
49
|
+
redactionClass: "none",
|
|
50
|
+
resolveStatus: "resolvable",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// ───────────────────────────────────────────────────────────────
|
|
54
|
+
// Public API
|
|
55
|
+
// ───────────────────────────────────────────────────────────────
|
|
56
|
+
export async function buildQuietDailyReview(db, options) {
|
|
57
|
+
const now = options?.now ?? new Date().toISOString();
|
|
58
|
+
const day = options?.day ?? todayString(now);
|
|
59
|
+
const readResult = await readActionClosuresByDay(db, day);
|
|
60
|
+
if (readResult.degraded) {
|
|
61
|
+
return readResult.degraded;
|
|
62
|
+
}
|
|
63
|
+
const closures = readResult.rows.slice(0, QUIET_MAX_CLOSURES_PER_DAY);
|
|
64
|
+
if (closures.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
status: "empty",
|
|
67
|
+
reason: "quiet_empty_input",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const sourceRefs = closures.map(buildSourceRefFromClosure);
|
|
71
|
+
// Collect memory-review candidates from closure payloads
|
|
72
|
+
const memoryCandidates = [];
|
|
73
|
+
for (const closure of closures) {
|
|
74
|
+
const payload = parsePayloadJson(closure.payloadJson);
|
|
75
|
+
if (payload.memoryReviewCandidate) {
|
|
76
|
+
memoryCandidates.push(payload.memoryReviewCandidate);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Build summary
|
|
80
|
+
const completedCount = closures.filter((c) => c.status === "completed").length;
|
|
81
|
+
const deniedCount = closures.filter((c) => c.status === "denied").length;
|
|
82
|
+
const failedCount = closures.filter((c) => c.status === "failed").length;
|
|
83
|
+
const reviewSummary = `Day ${day}: ${closures.length} closures (${completedCount} completed, ${deniedCount} denied, ${failedCount} failed)`;
|
|
84
|
+
const importanceSignals = [];
|
|
85
|
+
if (memoryCandidates.length > 0) {
|
|
86
|
+
importanceSignals.push(`${memoryCandidates.length} memory-review candidates`);
|
|
87
|
+
}
|
|
88
|
+
if (failedCount > 0) {
|
|
89
|
+
importanceSignals.push(`${failedCount} failed actions`);
|
|
90
|
+
}
|
|
91
|
+
const reviewId = `quiet_${day}`;
|
|
92
|
+
const writeResult = await writeQuietDailyReview(db, {
|
|
93
|
+
id: reviewId,
|
|
94
|
+
createdAt: now,
|
|
95
|
+
day,
|
|
96
|
+
closureCount: closures.length,
|
|
97
|
+
memoryCandidateCount: memoryCandidates.length,
|
|
98
|
+
sourceRefs,
|
|
99
|
+
redactionClass: "none",
|
|
100
|
+
lifecycleStatus: "pending",
|
|
101
|
+
payloadJson: JSON.stringify({
|
|
102
|
+
reviewSummary,
|
|
103
|
+
importanceSignals,
|
|
104
|
+
memoryCandidates,
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
if ("reason" in writeResult) {
|
|
108
|
+
return writeResult;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
status: "completed",
|
|
112
|
+
review: {
|
|
113
|
+
id: reviewId,
|
|
114
|
+
day,
|
|
115
|
+
closureCount: closures.length,
|
|
116
|
+
memoryCandidateCount: memoryCandidates.length,
|
|
117
|
+
sourceRefs,
|
|
118
|
+
reviewSummary,
|
|
119
|
+
importanceSignals,
|
|
120
|
+
createdAt: now,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CausalLoopHealth — Assemble loop health snapshot from cycle traces and stage events.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Read recent HeartbeatCycleTrace and LoopStageEvent rows,
|
|
5
|
+
* compute stage freshness, identify stalled stages, and return
|
|
6
|
+
* CausalLoopHealthSnapshot.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §3.2`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readHeartbeatCycleTraces, readLoopStageEventsByStage)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (LoopStage, DegradedOperationResult)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does not judge action correctness; only measures loop progression.
|
|
18
|
+
* - Does not block heartbeat; returns degraded diagnostics.
|
|
19
|
+
* - Stall detection uses cycle-sequence gaps, not wall-clock only.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/observability/causal-loop-health.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import type { StateDatabase } from "../storage/db/index.js";
|
|
24
|
+
import type { LoopStage, DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
25
|
+
export interface StageHealth {
|
|
26
|
+
stage: LoopStage;
|
|
27
|
+
lastEventAt?: string;
|
|
28
|
+
lastCycleSequence?: number;
|
|
29
|
+
eventCount: number;
|
|
30
|
+
stalled: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface CausalLoopHealthSnapshot {
|
|
33
|
+
overallStatus: "healthy" | "degraded" | "stalled" | "no_data";
|
|
34
|
+
stalledAt?: LoopStage;
|
|
35
|
+
lastCycleSequence: number;
|
|
36
|
+
lastHeartbeatAt?: string;
|
|
37
|
+
stages: StageHealth[];
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface AssembleLoopStatusOptions {
|
|
41
|
+
stallThresholdCycles?: number;
|
|
42
|
+
limit?: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function assembleLoopStatus(db: StateDatabase, options?: AssembleLoopStatusOptions): Promise<CausalLoopHealthSnapshot | DegradedOperationResult>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CausalLoopHealth — Assemble loop health snapshot from cycle traces and stage events.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Read recent HeartbeatCycleTrace and LoopStageEvent rows,
|
|
5
|
+
* compute stage freshness, identify stalled stages, and return
|
|
6
|
+
* CausalLoopHealthSnapshot.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.detail.md §3.2`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §5`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readHeartbeatCycleTraces, readLoopStageEventsByStage)
|
|
14
|
+
* - `src/shared/types/v8-contracts.js` (LoopStage, DegradedOperationResult)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Does not judge action correctness; only measures loop progression.
|
|
18
|
+
* - Does not block heartbeat; returns degraded diagnostics.
|
|
19
|
+
* - Stall detection uses cycle-sequence gaps, not wall-clock only.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/observability/causal-loop-health.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import { readHeartbeatCycleTraces, readLoopStageEventsByStage, } from "../storage/v8-state-stores.js";
|
|
24
|
+
// ───────────────────────────────────────────────────────────────
|
|
25
|
+
// Config
|
|
26
|
+
// ───────────────────────────────────────────────────────────────
|
|
27
|
+
const DEFAULT_STALL_THRESHOLD_CYCLES = 2;
|
|
28
|
+
const LOOP_STAGES = [
|
|
29
|
+
"ingestion",
|
|
30
|
+
"perception",
|
|
31
|
+
"judgment",
|
|
32
|
+
"policy",
|
|
33
|
+
"execution",
|
|
34
|
+
"closure",
|
|
35
|
+
"quiet",
|
|
36
|
+
"dream",
|
|
37
|
+
"projection",
|
|
38
|
+
];
|
|
39
|
+
// ───────────────────────────────────────────────────────────────
|
|
40
|
+
// Helpers
|
|
41
|
+
// ───────────────────────────────────────────────────────────────
|
|
42
|
+
function maxCycleSequence(stages) {
|
|
43
|
+
return Math.max(0, ...stages.map((s) => s.lastCycleSequence ?? 0));
|
|
44
|
+
}
|
|
45
|
+
function findStalledStage(stages, threshold) {
|
|
46
|
+
const maxSeq = maxCycleSequence(stages);
|
|
47
|
+
if (maxSeq === 0)
|
|
48
|
+
return undefined;
|
|
49
|
+
for (const stage of stages) {
|
|
50
|
+
if (stage.lastCycleSequence === undefined) {
|
|
51
|
+
// No events for this stage → stalled if other stages have progressed
|
|
52
|
+
if (maxSeq > 0)
|
|
53
|
+
return stage.stage;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const gap = maxSeq - stage.lastCycleSequence;
|
|
57
|
+
if (gap >= threshold) {
|
|
58
|
+
return stage.stage;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
// ───────────────────────────────────────────────────────────────
|
|
64
|
+
// Public API
|
|
65
|
+
// ───────────────────────────────────────────────────────────────
|
|
66
|
+
export async function assembleLoopStatus(db, options) {
|
|
67
|
+
const threshold = options?.stallThresholdCycles ?? DEFAULT_STALL_THRESHOLD_CYCLES;
|
|
68
|
+
const limit = options?.limit ?? 100;
|
|
69
|
+
const cycleResult = await readHeartbeatCycleTraces(db, limit);
|
|
70
|
+
if (cycleResult.degraded) {
|
|
71
|
+
return cycleResult.degraded;
|
|
72
|
+
}
|
|
73
|
+
const cycles = cycleResult.rows;
|
|
74
|
+
if (cycles.length === 0) {
|
|
75
|
+
return {
|
|
76
|
+
overallStatus: "no_data",
|
|
77
|
+
lastCycleSequence: 0,
|
|
78
|
+
stages: [],
|
|
79
|
+
reason: "no heartbeat cycles recorded",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const lastCycle = cycles[0];
|
|
83
|
+
// Gather stage health
|
|
84
|
+
const stages = [];
|
|
85
|
+
for (const stage of LOOP_STAGES) {
|
|
86
|
+
const eventResult = await readLoopStageEventsByStage(db, stage, limit);
|
|
87
|
+
if (eventResult.degraded) {
|
|
88
|
+
return eventResult.degraded;
|
|
89
|
+
}
|
|
90
|
+
const events = eventResult.rows;
|
|
91
|
+
const lastEvent = events[0];
|
|
92
|
+
stages.push({
|
|
93
|
+
stage,
|
|
94
|
+
lastEventAt: lastEvent?.occurredAt ?? undefined,
|
|
95
|
+
lastCycleSequence: lastEvent?.cycleSequence ?? undefined,
|
|
96
|
+
eventCount: events.length,
|
|
97
|
+
stalled: false, // computed below
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const stalledAt = findStalledStage(stages, threshold);
|
|
101
|
+
// Mark stalled stages
|
|
102
|
+
for (const stage of stages) {
|
|
103
|
+
if (stalledAt === stage.stage) {
|
|
104
|
+
stage.stalled = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const overallStatus = stalledAt
|
|
108
|
+
? "stalled"
|
|
109
|
+
: "healthy";
|
|
110
|
+
return {
|
|
111
|
+
overallStatus,
|
|
112
|
+
stalledAt,
|
|
113
|
+
lastCycleSequence: lastCycle.cycleSequence,
|
|
114
|
+
lastHeartbeatAt: lastCycle.heartbeatStartedAt,
|
|
115
|
+
stages,
|
|
116
|
+
reason: stalledAt ? `stage ${stalledAt} stalled for >=${threshold} cycles` : undefined,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
import type { RedactionClass, SensitivityClass, V8ReasonCode } from "../shared/types/v8-contracts.js";
|
|
23
|
+
export interface DiagnosticPayload {
|
|
24
|
+
summary: string;
|
|
25
|
+
detail?: string;
|
|
26
|
+
sourceSystem: "perception" | "judgment" | "dream" | "policy" | "storage" | "unknown";
|
|
27
|
+
sensitivityHint?: SensitivityClass;
|
|
28
|
+
reasonCode?: V8ReasonCode;
|
|
29
|
+
}
|
|
30
|
+
export interface RedactedDiagnostic {
|
|
31
|
+
summary: string;
|
|
32
|
+
detail?: string;
|
|
33
|
+
redactionClass: RedactionClass;
|
|
34
|
+
attribution: string;
|
|
35
|
+
}
|
|
36
|
+
export type DiagnosticAttribution = "storage_validation_block" | "dream_redaction_block" | "perception_risk_block" | "policy_denial" | "credential_shape_detected" | "private_context" | "public_technical_preserved" | "no_redaction_needed";
|
|
37
|
+
/**
|
|
38
|
+
* Classify sensitivity-related diagnostic attribution based on source system
|
|
39
|
+
* and reason code. Maps to deterministic attribution categories per
|
|
40
|
+
* observability-health-system.detail.md §4.2.
|
|
41
|
+
*/
|
|
42
|
+
export declare function classifyDiagnosticAttribution(sourceSystem: DiagnosticPayload["sourceSystem"], reasonCode?: V8ReasonCode): DiagnosticAttribution;
|
|
43
|
+
export declare function projectDiagnosticRedaction(payload: DiagnosticPayload): RedactedDiagnostic;
|