@haaaiawd/second-nature 0.1.26 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +35 -0
- package/agent-inner-guide.md +144 -0
- package/index.js +280 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +4 -1
- package/runtime/cli/commands/connector-behavior.d.ts +20 -0
- package/runtime/cli/commands/connector-behavior.js +160 -0
- package/runtime/cli/commands/index.js +8 -0
- package/runtime/cli/index.js +9 -2
- package/runtime/cli/ops/manual-run-dispatcher.d.ts +79 -0
- package/runtime/cli/ops/manual-run-dispatcher.js +110 -0
- package/runtime/cli/ops/ops-router.d.ts +45 -4
- package/runtime/cli/ops/ops-router.js +543 -2
- package/runtime/cli/read-models/index.js +35 -18
- package/runtime/cli/read-models/types.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.js +2 -2
- package/runtime/connectors/base/contract.d.ts +4 -1
- package/runtime/connectors/base/contract.js +5 -1
- package/runtime/connectors/base/effect-commit-ledger-sqlite.d.ts +31 -0
- package/runtime/connectors/base/effect-commit-ledger-sqlite.js +86 -0
- package/runtime/connectors/base/failure-taxonomy.js +5 -0
- package/runtime/connectors/base/manifest-v7.d.ts +151 -0
- package/runtime/connectors/base/manifest-v7.js +170 -0
- package/runtime/connectors/base/manifest.d.ts +3 -13
- package/runtime/connectors/base/manifest.js +7 -7
- package/runtime/connectors/base/route-planner.js +11 -8
- package/runtime/connectors/base/structured-unavailable-reason.d.ts +59 -0
- package/runtime/connectors/base/structured-unavailable-reason.js +113 -0
- package/runtime/connectors/base/wet-probe-runner.d.ts +40 -0
- package/runtime/connectors/base/wet-probe-runner.js +132 -0
- package/runtime/connectors/manifest/manifest-schema.d.ts +4 -0
- package/runtime/connectors/manifest/manifest-schema.js +2 -0
- package/runtime/connectors/services/connector-executor-adapter.d.ts +1 -0
- package/runtime/connectors/services/connector-executor-adapter.js +132 -26
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.d.ts +45 -0
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.js +132 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.d.ts +60 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.js +174 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.d.ts +38 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.js +60 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.d.ts +51 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.js +129 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.d.ts +30 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.js +92 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.d.ts +34 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.js +67 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.d.ts +37 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.js +62 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.d.ts +29 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.js +28 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.d.ts +54 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.js +164 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.js +60 -0
- package/runtime/core/second-nature/heartbeat/index.d.ts +4 -0
- package/runtime/core/second-nature/heartbeat/index.js +5 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.d.ts +63 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.js +118 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.d.ts +41 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.js +43 -0
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +2 -1
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +2 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.d.ts +31 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.js +102 -0
- package/runtime/core/second-nature/orchestrator/index.d.ts +5 -0
- package/runtime/core/second-nature/orchestrator/index.js +7 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.d.ts +53 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.js +153 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.d.ts +29 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.js +92 -0
- package/runtime/core/second-nature/quiet/index.d.ts +5 -0
- package/runtime/core/second-nature/quiet/index.js +5 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +19 -12
- package/runtime/core/second-nature/types.d.ts +2 -0
- package/runtime/guidance/channel-feedback-ingestion-service.d.ts +88 -0
- package/runtime/guidance/channel-feedback-ingestion-service.js +231 -0
- package/runtime/guidance/guidance-draft-service.d.ts +60 -0
- package/runtime/guidance/guidance-draft-service.js +80 -0
- package/runtime/guidance/index.d.ts +3 -0
- package/runtime/guidance/index.js +3 -0
- package/runtime/guidance/outreach-draft-schema.d.ts +8 -8
- package/runtime/guidance/outreach-strategy-selector.d.ts +77 -0
- package/runtime/guidance/outreach-strategy-selector.js +211 -0
- package/runtime/observability/audit/append-only-audit-store.d.ts +20 -2
- package/runtime/observability/audit/append-only-audit-store.js +32 -6
- package/runtime/observability/audit/audit-envelope.d.ts +2 -1
- package/runtime/observability/audit/audit-envelope.js +8 -7
- package/runtime/observability/audit/audit-family-registry.json +66 -0
- package/runtime/observability/audit/family-registry.d.ts +43 -0
- package/runtime/observability/audit/family-registry.js +70 -0
- package/runtime/observability/index.d.ts +6 -1
- package/runtime/observability/index.js +6 -1
- package/runtime/observability/redaction/policy.d.ts +24 -3
- package/runtime/observability/redaction/policy.js +74 -0
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +152 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +248 -0
- package/runtime/observability/services/lived-experience-audit.js +6 -6
- package/runtime/observability/services/narrative-timeline-query-service.d.ts +136 -0
- package/runtime/observability/services/narrative-timeline-query-service.js +169 -0
- package/runtime/observability/services/restore-audit-service.d.ts +74 -0
- package/runtime/observability/services/restore-audit-service.js +79 -0
- package/runtime/observability/services/runtime-secret-anchor-view.d.ts +77 -0
- package/runtime/observability/services/runtime-secret-anchor-view.js +168 -0
- package/runtime/observability/services/self-health-snapshot.d.ts +92 -0
- package/runtime/observability/services/self-health-snapshot.js +251 -0
- package/runtime/shared/types/goal.d.ts +62 -0
- package/runtime/shared/types/goal.js +20 -0
- package/runtime/shared/types/index.d.ts +3 -0
- package/runtime/shared/types/index.js +3 -0
- package/runtime/shared/types/source-ref.d.ts +14 -0
- package/runtime/shared/types/source-ref.js +1 -0
- package/runtime/shared/types/v7-entities.d.ts +206 -0
- package/runtime/shared/types/v7-entities.js +27 -0
- package/runtime/storage/db/index.js +3 -0
- package/runtime/storage/db/migration-runner.d.ts +30 -0
- package/runtime/storage/db/migration-runner.js +93 -0
- package/runtime/storage/db/migrations/index.d.ts +5 -0
- package/runtime/storage/db/migrations/index.js +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.d.ts +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.js +144 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.d.ts +8 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.js +27 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.js +26 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.js +26 -0
- package/runtime/storage/db/schema/agent-goal.d.ts +38 -0
- package/runtime/storage/db/schema/agent-goal.js +2 -0
- package/runtime/storage/db/transaction-utils.d.ts +14 -0
- package/runtime/storage/db/transaction-utils.js +29 -0
- package/runtime/storage/db/write-queue.d.ts +38 -0
- package/runtime/storage/db/write-queue.js +97 -0
- package/runtime/storage/quiet/persist-quiet-artifact.js +2 -1
- package/runtime/storage/services/credential-vault.js +31 -17
- package/runtime/storage/services/diary-dream-store.d.ts +35 -0
- package/runtime/storage/services/diary-dream-store.js +165 -0
- package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
- package/runtime/storage/services/embodied-context-state-port.js +115 -0
- package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
- package/runtime/storage/services/goal-lifecycle-store.js +181 -0
- package/runtime/storage/services/history-digest-store.d.ts +33 -0
- package/runtime/storage/services/history-digest-store.js +140 -0
- package/runtime/storage/services/identity-profile-store.d.ts +25 -0
- package/runtime/storage/services/identity-profile-store.js +81 -0
- package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
- package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
- package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
- package/runtime/storage/services/restore-snapshot-store.js +193 -0
- package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
- package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
- package/runtime/storage/services/tool-experience-store.d.ts +25 -0
- package/runtime/storage/services/tool-experience-store.js +116 -0
- package/runtime/storage/services/write-validation-gate.d.ts +46 -0
- package/runtime/storage/services/write-validation-gate.js +200 -0
- package/workspace-ops-bridge.js +16 -1
|
@@ -190,6 +190,44 @@ export declare const agentGoal: import("drizzle-orm/sqlite-core").SQLiteTableWit
|
|
|
190
190
|
}, {}, {
|
|
191
191
|
length: number | undefined;
|
|
192
192
|
}>;
|
|
193
|
+
scope: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
194
|
+
name: "scope";
|
|
195
|
+
tableName: "agent_goal";
|
|
196
|
+
dataType: "string";
|
|
197
|
+
columnType: "SQLiteText";
|
|
198
|
+
data: string;
|
|
199
|
+
driverParam: string;
|
|
200
|
+
notNull: false;
|
|
201
|
+
hasDefault: false;
|
|
202
|
+
isPrimaryKey: false;
|
|
203
|
+
isAutoincrement: false;
|
|
204
|
+
hasRuntimeDefault: false;
|
|
205
|
+
enumValues: [string, ...string[]];
|
|
206
|
+
baseColumn: never;
|
|
207
|
+
identity: undefined;
|
|
208
|
+
generated: undefined;
|
|
209
|
+
}, {}, {
|
|
210
|
+
length: number | undefined;
|
|
211
|
+
}>;
|
|
212
|
+
expiresAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
213
|
+
name: "expires_at";
|
|
214
|
+
tableName: "agent_goal";
|
|
215
|
+
dataType: "string";
|
|
216
|
+
columnType: "SQLiteText";
|
|
217
|
+
data: string;
|
|
218
|
+
driverParam: string;
|
|
219
|
+
notNull: false;
|
|
220
|
+
hasDefault: false;
|
|
221
|
+
isPrimaryKey: false;
|
|
222
|
+
isAutoincrement: false;
|
|
223
|
+
hasRuntimeDefault: false;
|
|
224
|
+
enumValues: [string, ...string[]];
|
|
225
|
+
baseColumn: never;
|
|
226
|
+
identity: undefined;
|
|
227
|
+
generated: undefined;
|
|
228
|
+
}, {}, {
|
|
229
|
+
length: number | undefined;
|
|
230
|
+
}>;
|
|
193
231
|
createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
194
232
|
name: "created_at";
|
|
195
233
|
tableName: "agent_goal";
|
|
@@ -10,6 +10,8 @@ export const agentGoal = sqliteTable("agent_goal", {
|
|
|
10
10
|
priorityHint: integer("priority_hint").notNull().default(0),
|
|
11
11
|
sourceRefsJson: text("source_refs_json").notNull(),
|
|
12
12
|
acceptedBy: text("accepted_by"),
|
|
13
|
+
scope: text("scope"),
|
|
14
|
+
expiresAt: text("expires_at"),
|
|
13
15
|
createdAt: text("created_at").notNull(),
|
|
14
16
|
updatedAt: text("updated_at").notNull(),
|
|
15
17
|
}, (table) => [
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction utilities for SQLite exclusive writes (DR-019).
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - Provides `runExclusive` for one-off transactional writes outside the queue.
|
|
6
|
+
* - Uses `BEGIN EXCLUSIVE` for write isolation.
|
|
7
|
+
* - Automatically rolls back on error.
|
|
8
|
+
*
|
|
9
|
+
* Dependencies: sql.js Database.
|
|
10
|
+
* Boundary: Stateless helper; for queued writes prefer WriteQueue.
|
|
11
|
+
* Test coverage: tests/unit/storage/write-queue.test.ts (indirect via queue)
|
|
12
|
+
*/
|
|
13
|
+
import type { Database } from "sql.js";
|
|
14
|
+
export declare function runExclusive<T>(sqlite: Database, fn: (sqlite: Database) => T): T;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction utilities for SQLite exclusive writes (DR-019).
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - Provides `runExclusive` for one-off transactional writes outside the queue.
|
|
6
|
+
* - Uses `BEGIN EXCLUSIVE` for write isolation.
|
|
7
|
+
* - Automatically rolls back on error.
|
|
8
|
+
*
|
|
9
|
+
* Dependencies: sql.js Database.
|
|
10
|
+
* Boundary: Stateless helper; for queued writes prefer WriteQueue.
|
|
11
|
+
* Test coverage: tests/unit/storage/write-queue.test.ts (indirect via queue)
|
|
12
|
+
*/
|
|
13
|
+
export function runExclusive(sqlite, fn) {
|
|
14
|
+
sqlite.exec("BEGIN EXCLUSIVE");
|
|
15
|
+
try {
|
|
16
|
+
const result = fn(sqlite);
|
|
17
|
+
sqlite.exec("COMMIT");
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
try {
|
|
22
|
+
sqlite.exec("ROLLBACK");
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// rollback may fail if no transaction active
|
|
26
|
+
}
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serial write queue with concurrency protection (DR-019).
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - All DB writes are funneled through a single serial queue.
|
|
6
|
+
* - Each write executes inside a `BEGIN EXCLUSIVE` transaction.
|
|
7
|
+
* - On SQLITE_BUSY or equivalent, retries up to 3 times with 50ms backoff.
|
|
8
|
+
* - Flush failure writes to stderr without blocking read paths.
|
|
9
|
+
* - `triggerSource` is preserved through the write pipeline.
|
|
10
|
+
*
|
|
11
|
+
* Dependencies: sql.js Database.
|
|
12
|
+
* Boundary: Wraps raw SQL execution; callers enqueue write operations.
|
|
13
|
+
* Test coverage: tests/unit/storage/write-queue.test.ts
|
|
14
|
+
*/
|
|
15
|
+
import type { Database } from "sql.js";
|
|
16
|
+
export type TriggerSource = "heartbeat" | "manual_run" | "probe" | "idle_curiosity";
|
|
17
|
+
export interface WriteRequest<T = unknown> {
|
|
18
|
+
label: string;
|
|
19
|
+
triggerSource: TriggerSource;
|
|
20
|
+
execute: (sqlite: Database) => T;
|
|
21
|
+
}
|
|
22
|
+
export interface WriteResult<T = unknown> {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
value?: T;
|
|
25
|
+
error?: string;
|
|
26
|
+
triggerSource: TriggerSource;
|
|
27
|
+
}
|
|
28
|
+
export declare class WriteQueue {
|
|
29
|
+
private readonly sqlite;
|
|
30
|
+
private queue;
|
|
31
|
+
private processing;
|
|
32
|
+
constructor(sqlite: Database);
|
|
33
|
+
enqueue<T>(request: WriteRequest<T>): Promise<WriteResult<T>>;
|
|
34
|
+
private drain;
|
|
35
|
+
private executeWithRetry;
|
|
36
|
+
get pending(): number;
|
|
37
|
+
}
|
|
38
|
+
export declare function createWriteQueue(sqlite: Database): WriteQueue;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serial write queue with concurrency protection (DR-019).
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - All DB writes are funneled through a single serial queue.
|
|
6
|
+
* - Each write executes inside a `BEGIN EXCLUSIVE` transaction.
|
|
7
|
+
* - On SQLITE_BUSY or equivalent, retries up to 3 times with 50ms backoff.
|
|
8
|
+
* - Flush failure writes to stderr without blocking read paths.
|
|
9
|
+
* - `triggerSource` is preserved through the write pipeline.
|
|
10
|
+
*
|
|
11
|
+
* Dependencies: sql.js Database.
|
|
12
|
+
* Boundary: Wraps raw SQL execution; callers enqueue write operations.
|
|
13
|
+
* Test coverage: tests/unit/storage/write-queue.test.ts
|
|
14
|
+
*/
|
|
15
|
+
const MAX_RETRIES = 3;
|
|
16
|
+
const BACKOFF_MS = 50;
|
|
17
|
+
function sleep(ms) {
|
|
18
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
19
|
+
}
|
|
20
|
+
function isBusyError(err) {
|
|
21
|
+
if (err instanceof Error) {
|
|
22
|
+
const msg = err.message.toLowerCase();
|
|
23
|
+
return msg.includes("busy") || msg.includes("locked") || msg.includes("database is locked");
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
export class WriteQueue {
|
|
28
|
+
sqlite;
|
|
29
|
+
queue = [];
|
|
30
|
+
processing = false;
|
|
31
|
+
constructor(sqlite) {
|
|
32
|
+
this.sqlite = sqlite;
|
|
33
|
+
}
|
|
34
|
+
enqueue(request) {
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
this.queue.push({
|
|
37
|
+
request: request,
|
|
38
|
+
resolve: resolve,
|
|
39
|
+
});
|
|
40
|
+
this.drain();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async drain() {
|
|
44
|
+
if (this.processing) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.processing = true;
|
|
48
|
+
while (this.queue.length > 0) {
|
|
49
|
+
const item = this.queue.shift();
|
|
50
|
+
const result = await this.executeWithRetry(item.request);
|
|
51
|
+
item.resolve(result);
|
|
52
|
+
}
|
|
53
|
+
this.processing = false;
|
|
54
|
+
}
|
|
55
|
+
async executeWithRetry(request) {
|
|
56
|
+
let lastError;
|
|
57
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
58
|
+
try {
|
|
59
|
+
this.sqlite.exec("BEGIN EXCLUSIVE");
|
|
60
|
+
const value = request.execute(this.sqlite);
|
|
61
|
+
this.sqlite.exec("COMMIT");
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
value,
|
|
65
|
+
triggerSource: request.triggerSource,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
try {
|
|
70
|
+
this.sqlite.exec("ROLLBACK");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// rollback may fail if no transaction active
|
|
74
|
+
}
|
|
75
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76
|
+
lastError = message;
|
|
77
|
+
if (isBusyError(err) && attempt < MAX_RETRIES) {
|
|
78
|
+
await sleep(BACKOFF_MS * (attempt + 1));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
process.stderr.write(`write_queue_flush_failed: ${request.label} [${request.triggerSource}] — ${lastError}\n`);
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
error: lastError,
|
|
88
|
+
triggerSource: request.triggerSource,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
get pending() {
|
|
92
|
+
return this.queue.length;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function createWriteQueue(sqlite) {
|
|
96
|
+
return new WriteQueue(sqlite);
|
|
97
|
+
}
|
|
@@ -6,7 +6,8 @@ import * as path from "node:path";
|
|
|
6
6
|
export async function persistQuietArtifactToWorkspace(workspaceRoot, ack, input) {
|
|
7
7
|
const dir = path.join(workspaceRoot, ".second-nature", "quiet", input.day);
|
|
8
8
|
await fs.mkdir(dir, { recursive: true });
|
|
9
|
-
const
|
|
9
|
+
const fileName = input.kind === "empty_state" ? "empty_state.json" : `${ack.artifactId}.json`;
|
|
10
|
+
const file = path.join(dir, fileName);
|
|
10
11
|
const payload = {
|
|
11
12
|
artifactId: ack.artifactId,
|
|
12
13
|
artifactRef: ack.artifactRef,
|
|
@@ -8,6 +8,11 @@ import * as crypto from "crypto";
|
|
|
8
8
|
import { eq } from "drizzle-orm";
|
|
9
9
|
import { credentialRecords } from "../db/schema/index.js";
|
|
10
10
|
const ALGORITHM = "aes-256-gcm";
|
|
11
|
+
function normalizeCredentialRecord(record) {
|
|
12
|
+
return record && typeof record === "object"
|
|
13
|
+
? record
|
|
14
|
+
: {};
|
|
15
|
+
}
|
|
11
16
|
function resolveKeyBuffer() {
|
|
12
17
|
const raw = process.env.SECOND_NATURE_ENCRYPTION_KEY?.trim();
|
|
13
18
|
if (!raw || raw.length < 32) {
|
|
@@ -140,24 +145,32 @@ export function createCredentialVault(db) {
|
|
|
140
145
|
});
|
|
141
146
|
if (!record)
|
|
142
147
|
return null;
|
|
148
|
+
const row = normalizeCredentialRecord(record);
|
|
149
|
+
const resolvedPlatformId = row.platformId ?? row.platform_id ?? platformId;
|
|
150
|
+
const credentialType = row.credentialType ?? row.credential_type ?? "api_key";
|
|
151
|
+
const encryptedValue = row.encryptedValue ?? row.encrypted_value ?? "";
|
|
152
|
+
const verificationCode = row.verificationCode ?? row.verification_code ?? undefined;
|
|
153
|
+
const challengeText = row.challengeText ?? row.challenge_text ?? undefined;
|
|
154
|
+
const expiresAt = row.expiresAt ?? row.expires_at ?? undefined;
|
|
155
|
+
const attemptsRemaining = row.attemptsRemaining ?? row.attempts_remaining ?? undefined;
|
|
143
156
|
let plain;
|
|
144
|
-
let status =
|
|
145
|
-
if (
|
|
146
|
-
if (!isCredentialCiphertext(
|
|
157
|
+
let status = (row.status ?? "missing");
|
|
158
|
+
if (encryptedValue) {
|
|
159
|
+
if (!isCredentialCiphertext(encryptedValue)) {
|
|
147
160
|
// Fail-closed: return decrypt_failed so callers do not crash.
|
|
148
161
|
return {
|
|
149
|
-
platformId:
|
|
150
|
-
credentialType:
|
|
162
|
+
platformId: resolvedPlatformId,
|
|
163
|
+
credentialType: credentialType,
|
|
151
164
|
status: "decrypt_failed",
|
|
152
165
|
encryptedValue: undefined,
|
|
153
|
-
verificationCode
|
|
154
|
-
challengeText
|
|
155
|
-
verificationDeadline:
|
|
156
|
-
attemptsRemaining
|
|
166
|
+
verificationCode,
|
|
167
|
+
challengeText,
|
|
168
|
+
verificationDeadline: expiresAt,
|
|
169
|
+
attemptsRemaining,
|
|
157
170
|
};
|
|
158
171
|
}
|
|
159
172
|
try {
|
|
160
|
-
plain = decryptCredentialAtRest(
|
|
173
|
+
plain = decryptCredentialAtRest(encryptedValue);
|
|
161
174
|
}
|
|
162
175
|
catch {
|
|
163
176
|
// Decryption failure must not break the whole state load.
|
|
@@ -166,21 +179,22 @@ export function createCredentialVault(db) {
|
|
|
166
179
|
}
|
|
167
180
|
}
|
|
168
181
|
return {
|
|
169
|
-
platformId:
|
|
170
|
-
credentialType:
|
|
182
|
+
platformId: resolvedPlatformId,
|
|
183
|
+
credentialType: credentialType,
|
|
171
184
|
status,
|
|
172
185
|
encryptedValue: plain,
|
|
173
|
-
verificationCode
|
|
174
|
-
challengeText
|
|
175
|
-
verificationDeadline:
|
|
176
|
-
attemptsRemaining
|
|
186
|
+
verificationCode,
|
|
187
|
+
challengeText,
|
|
188
|
+
verificationDeadline: expiresAt,
|
|
189
|
+
attemptsRemaining,
|
|
177
190
|
};
|
|
178
191
|
},
|
|
179
192
|
async getCredentialState(platformId) {
|
|
180
193
|
const record = await db.query.credentialRecords.findFirst({
|
|
181
194
|
where: (tbl) => eq(tbl.platformId, platformId),
|
|
182
195
|
});
|
|
183
|
-
|
|
196
|
+
const row = normalizeCredentialRecord(record);
|
|
197
|
+
return row.status || "missing";
|
|
184
198
|
},
|
|
185
199
|
};
|
|
186
200
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiaryDreamStore — T-SMS.C.7
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - DailyDiary artifact ref + index (observedToday, notableSignals,
|
|
6
|
+
* tomorrowDirection, sourceRefs)
|
|
7
|
+
* - DreamOutput lifecycle: candidate -> accepted -> archived
|
|
8
|
+
* (partial is a terminal state, not a lifecycle stage)
|
|
9
|
+
* - `transitionDreamOutputLifecycle` enforces VALID_TRANSITIONS.
|
|
10
|
+
* - Read path `loadAcceptedDreamProjection` only exposes accepted outputs.
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `StateDatabase` from `../db/index.js`
|
|
14
|
+
* - `DailyDiary`, `DreamOutput`, `DreamOutputStatus` from
|
|
15
|
+
* `../../shared/types/v7-entities.js`
|
|
16
|
+
* - `validateWritePayload` from `./write-validation-gate.js`
|
|
17
|
+
*
|
|
18
|
+
* Boundary:
|
|
19
|
+
* - Candidate outputs are stored but filtered from active read paths.
|
|
20
|
+
* - Accepted transition is initiated by dream-quiet (DR-023).
|
|
21
|
+
* - Write paths pass through WriteValidationGate.
|
|
22
|
+
*
|
|
23
|
+
* Test coverage: tests/unit/storage/diary-dream-store.test.ts
|
|
24
|
+
*/
|
|
25
|
+
import type { StateDatabase } from "../db/index.js";
|
|
26
|
+
import type { DailyDiary, DreamOutput, DreamOutputStatus } from "../../shared/types/v7-entities.js";
|
|
27
|
+
export interface DiaryDreamStore {
|
|
28
|
+
writeDailyDiary(diary: DailyDiary): Promise<void>;
|
|
29
|
+
loadDailyDiary(day: string): Promise<DailyDiary | undefined>;
|
|
30
|
+
appendDreamOutput(output: DreamOutput): Promise<void>;
|
|
31
|
+
transitionDreamOutputLifecycle(outputId: string, newStatus: Exclude<DreamOutputStatus, "candidate" | "partial">): Promise<void>;
|
|
32
|
+
loadAcceptedDreamProjection(limit?: number): Promise<DreamOutput[]>;
|
|
33
|
+
listDreamOutputs(limit?: number): Promise<DreamOutput[]>;
|
|
34
|
+
}
|
|
35
|
+
export declare function createDiaryDreamStore(database: StateDatabase): DiaryDreamStore;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiaryDreamStore — T-SMS.C.7
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - DailyDiary artifact ref + index (observedToday, notableSignals,
|
|
6
|
+
* tomorrowDirection, sourceRefs)
|
|
7
|
+
* - DreamOutput lifecycle: candidate -> accepted -> archived
|
|
8
|
+
* (partial is a terminal state, not a lifecycle stage)
|
|
9
|
+
* - `transitionDreamOutputLifecycle` enforces VALID_TRANSITIONS.
|
|
10
|
+
* - Read path `loadAcceptedDreamProjection` only exposes accepted outputs.
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `StateDatabase` from `../db/index.js`
|
|
14
|
+
* - `DailyDiary`, `DreamOutput`, `DreamOutputStatus` from
|
|
15
|
+
* `../../shared/types/v7-entities.js`
|
|
16
|
+
* - `validateWritePayload` from `./write-validation-gate.js`
|
|
17
|
+
*
|
|
18
|
+
* Boundary:
|
|
19
|
+
* - Candidate outputs are stored but filtered from active read paths.
|
|
20
|
+
* - Accepted transition is initiated by dream-quiet (DR-023).
|
|
21
|
+
* - Write paths pass through WriteValidationGate.
|
|
22
|
+
*
|
|
23
|
+
* Test coverage: tests/unit/storage/diary-dream-store.test.ts
|
|
24
|
+
*/
|
|
25
|
+
import { validateWritePayload } from "./write-validation-gate.js";
|
|
26
|
+
const VALID_TRANSITIONS = {
|
|
27
|
+
candidate: ["accepted", "archived"],
|
|
28
|
+
accepted: ["archived"],
|
|
29
|
+
archived: [],
|
|
30
|
+
partial: [],
|
|
31
|
+
};
|
|
32
|
+
function safeParseJson(json, fallback) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(json);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function createDiaryDreamStore(database) {
|
|
41
|
+
const { sqlite } = database;
|
|
42
|
+
return {
|
|
43
|
+
async writeDailyDiary(diary) {
|
|
44
|
+
const gate = validateWritePayload({
|
|
45
|
+
...diary,
|
|
46
|
+
sourceRefs: diary.sourceRefs,
|
|
47
|
+
});
|
|
48
|
+
if (!gate.ok)
|
|
49
|
+
throw new Error(gate.reason ?? "write_validation_failed");
|
|
50
|
+
sqlite.run(`INSERT INTO daily_diary_index
|
|
51
|
+
(diary_id, day, observed_today_json, notable_signals_json, tomorrow_direction, source_refs_json, created_at)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
53
|
+
ON CONFLICT(day) DO UPDATE SET
|
|
54
|
+
observed_today_json = excluded.observed_today_json,
|
|
55
|
+
notable_signals_json = excluded.notable_signals_json,
|
|
56
|
+
tomorrow_direction = excluded.tomorrow_direction,
|
|
57
|
+
source_refs_json = excluded.source_refs_json,
|
|
58
|
+
created_at = excluded.created_at`, [
|
|
59
|
+
diary.diaryId,
|
|
60
|
+
diary.day,
|
|
61
|
+
JSON.stringify(diary.observedToday),
|
|
62
|
+
JSON.stringify(diary.notableSignals),
|
|
63
|
+
diary.tomorrowDirection,
|
|
64
|
+
JSON.stringify(diary.sourceRefs),
|
|
65
|
+
diary.createdAt,
|
|
66
|
+
]);
|
|
67
|
+
},
|
|
68
|
+
async loadDailyDiary(day) {
|
|
69
|
+
const result = sqlite.exec(`SELECT * FROM daily_diary_index WHERE day = ?`, [day]);
|
|
70
|
+
if (result.length === 0 || result[0].values.length === 0) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const cols = result[0].columns;
|
|
74
|
+
const get = (row, name) => row[cols.indexOf(name)];
|
|
75
|
+
const row = result[0].values[0];
|
|
76
|
+
return {
|
|
77
|
+
diaryId: get(row, "diary_id"),
|
|
78
|
+
day: get(row, "day"),
|
|
79
|
+
observedToday: safeParseJson(get(row, "observed_today_json") ?? "[]", []),
|
|
80
|
+
notableSignals: safeParseJson(get(row, "notable_signals_json") ?? "[]", []),
|
|
81
|
+
tomorrowDirection: get(row, "tomorrow_direction") ?? "",
|
|
82
|
+
sourceRefs: safeParseJson(get(row, "source_refs_json") ?? "[]", ["store:default"]),
|
|
83
|
+
createdAt: get(row, "created_at"),
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
async appendDreamOutput(output) {
|
|
87
|
+
const gate = validateWritePayload({
|
|
88
|
+
...output,
|
|
89
|
+
sourceRefs: ["dream:run"],
|
|
90
|
+
});
|
|
91
|
+
if (!gate.ok)
|
|
92
|
+
throw new Error(gate.reason ?? "write_validation_failed");
|
|
93
|
+
sqlite.run(`INSERT INTO dream_output_index
|
|
94
|
+
(output_id, run_id, status, canonical_entries_json, insights_json,
|
|
95
|
+
narrative_update_json, relationship_update_json, validation_json, created_at)
|
|
96
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
97
|
+
output.outputId,
|
|
98
|
+
output.runId,
|
|
99
|
+
output.status,
|
|
100
|
+
JSON.stringify(output.canonicalEntries),
|
|
101
|
+
JSON.stringify(output.insights),
|
|
102
|
+
output.narrativeUpdate ? JSON.stringify(output.narrativeUpdate) : null,
|
|
103
|
+
output.relationshipUpdate ? JSON.stringify(output.relationshipUpdate) : null,
|
|
104
|
+
JSON.stringify(output.validation),
|
|
105
|
+
output.createdAt ?? new Date().toISOString(),
|
|
106
|
+
]);
|
|
107
|
+
},
|
|
108
|
+
async transitionDreamOutputLifecycle(outputId, newStatus) {
|
|
109
|
+
const currentResult = sqlite.exec(`SELECT status FROM dream_output_index WHERE output_id = ?`, [outputId]);
|
|
110
|
+
if (currentResult.length === 0 ||
|
|
111
|
+
currentResult[0].values.length === 0) {
|
|
112
|
+
throw new Error(`dream_output_not_found:${outputId}`);
|
|
113
|
+
}
|
|
114
|
+
const currentStatus = currentResult[0].values[0][0];
|
|
115
|
+
const allowed = VALID_TRANSITIONS[currentStatus];
|
|
116
|
+
if (!allowed.includes(newStatus)) {
|
|
117
|
+
throw new Error(`invalid_transition:${currentStatus}->${newStatus}`);
|
|
118
|
+
}
|
|
119
|
+
sqlite.run(`UPDATE dream_output_index SET status = ? WHERE output_id = ?`, [newStatus, outputId]);
|
|
120
|
+
},
|
|
121
|
+
async loadAcceptedDreamProjection(limit = 10) {
|
|
122
|
+
const result = sqlite.exec(`SELECT * FROM dream_output_index
|
|
123
|
+
WHERE status = 'accepted'
|
|
124
|
+
ORDER BY created_at DESC
|
|
125
|
+
LIMIT ${limit}`);
|
|
126
|
+
if (result.length === 0 || result[0].values.length === 0) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
const cols = result[0].columns;
|
|
130
|
+
const get = (row, name) => row[cols.indexOf(name)];
|
|
131
|
+
return result[0].values.map((row) => rowToDreamOutput(row, cols));
|
|
132
|
+
},
|
|
133
|
+
async listDreamOutputs(limit = 10) {
|
|
134
|
+
const result = sqlite.exec(`SELECT * FROM dream_output_index
|
|
135
|
+
ORDER BY created_at DESC
|
|
136
|
+
LIMIT ${limit}`);
|
|
137
|
+
if (result.length === 0 || result[0].values.length === 0) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const cols = result[0].columns;
|
|
141
|
+
return result[0].values.map((row) => rowToDreamOutput(row, cols));
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function rowToDreamOutput(row, cols) {
|
|
146
|
+
const get = (name) => row[cols.indexOf(name)];
|
|
147
|
+
return {
|
|
148
|
+
outputId: get("output_id"),
|
|
149
|
+
runId: get("run_id"),
|
|
150
|
+
status: get("status"),
|
|
151
|
+
canonicalEntries: safeParseJson(get("canonical_entries_json") ?? "[]", []),
|
|
152
|
+
insights: safeParseJson(get("insights_json") ?? "[]", []),
|
|
153
|
+
narrativeUpdate: safeParseJson(get("narrative_update_json") ?? "null", undefined),
|
|
154
|
+
relationshipUpdate: safeParseJson(get("relationship_update_json") ?? "null", undefined),
|
|
155
|
+
validation: safeParseJson(get("validation_json") ?? "{}", {
|
|
156
|
+
schemaValid: false,
|
|
157
|
+
sourceGrounded: false,
|
|
158
|
+
sensitivityClean: false,
|
|
159
|
+
unsupportedClaims: [],
|
|
160
|
+
errors: [],
|
|
161
|
+
checkedAt: new Date().toISOString(),
|
|
162
|
+
}),
|
|
163
|
+
createdAt: get("created_at") ?? new Date().toISOString(),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmbodiedContextStatePort — T-SMS.C.2
|
|
3
|
+
*
|
|
4
|
+
* Core logic: 5 read methods for assembling EmbodiedContext slices.
|
|
5
|
+
* Each method supports bounded query (limit/window).
|
|
6
|
+
* Affordance and self-health slices are sourced from body-tool-system
|
|
7
|
+
* and observability-health-system respectively, NOT read directly here.
|
|
8
|
+
* DR-011: loadAcceptedDreamProjection returns accepted projection only.
|
|
9
|
+
* DR-013: bounded context, degraded reason codes on failure.
|
|
10
|
+
* DR-024: empty accepted dream → context_degraded:dream_projection_unavailable.
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - GoalLifecycleStore from `./goal-lifecycle-store.js`
|
|
14
|
+
* - IdentityProfileStore from `./identity-profile-store.js`
|
|
15
|
+
* - InteractionSnapshotProjector from `./interaction-snapshot-projector.js`
|
|
16
|
+
* - ToolExperienceStore from `./tool-experience-store.js`
|
|
17
|
+
* - dream_output_index table (v7-001 migration)
|
|
18
|
+
*
|
|
19
|
+
* Boundary:
|
|
20
|
+
* - This port ONLY reads state-memory tables.
|
|
21
|
+
* - Affordance map and self-health are injected by caller (control-plane)
|
|
22
|
+
* from their respective systems.
|
|
23
|
+
* - All methods return empty array + reason on missing data, never throw.
|
|
24
|
+
*/
|
|
25
|
+
import type { IdentityProfile, RecentInteractionSnapshot, ToolExperience, DreamOutput } from "../../shared/types/v7-entities.js";
|
|
26
|
+
import type { AgentGoal } from "../../shared/types/goal.js";
|
|
27
|
+
import type { GoalLifecycleStore } from "./goal-lifecycle-store.js";
|
|
28
|
+
import type { IdentityProfileStore } from "./identity-profile-store.js";
|
|
29
|
+
import type { InteractionSnapshotProjector } from "./interaction-snapshot-projector.js";
|
|
30
|
+
import type { ToolExperienceStore } from "./tool-experience-store.js";
|
|
31
|
+
import type { StateDatabase } from "../db/index.js";
|
|
32
|
+
export interface EmbodiedContextStatePort {
|
|
33
|
+
loadIdentityProfile(profileId: string): Promise<{
|
|
34
|
+
status: "loaded";
|
|
35
|
+
data: IdentityProfile;
|
|
36
|
+
} | {
|
|
37
|
+
status: "degraded";
|
|
38
|
+
data?: IdentityProfile;
|
|
39
|
+
reason: string;
|
|
40
|
+
}>;
|
|
41
|
+
listActiveGoals(limit?: number): Promise<{
|
|
42
|
+
status: "loaded";
|
|
43
|
+
data: AgentGoal[];
|
|
44
|
+
} | {
|
|
45
|
+
status: "degraded";
|
|
46
|
+
reason: string;
|
|
47
|
+
}>;
|
|
48
|
+
loadRecentInteractionSnapshot(limit?: number): Promise<{
|
|
49
|
+
status: "loaded";
|
|
50
|
+
data: RecentInteractionSnapshot[];
|
|
51
|
+
} | {
|
|
52
|
+
status: "degraded";
|
|
53
|
+
reason: string;
|
|
54
|
+
}>;
|
|
55
|
+
loadToolExperienceSlice(limit?: number): Promise<{
|
|
56
|
+
status: "loaded";
|
|
57
|
+
data: ToolExperience[];
|
|
58
|
+
} | {
|
|
59
|
+
status: "degraded";
|
|
60
|
+
reason: string;
|
|
61
|
+
}>;
|
|
62
|
+
loadAcceptedDreamProjection(limit?: number): Promise<{
|
|
63
|
+
status: "loaded";
|
|
64
|
+
data: DreamOutput[];
|
|
65
|
+
} | {
|
|
66
|
+
status: "degraded";
|
|
67
|
+
reason: string;
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
export interface EmbodiedContextStatePortDeps {
|
|
71
|
+
database: StateDatabase;
|
|
72
|
+
goalStore: GoalLifecycleStore;
|
|
73
|
+
identityStore: IdentityProfileStore;
|
|
74
|
+
interactionProjector: InteractionSnapshotProjector;
|
|
75
|
+
experienceStore: ToolExperienceStore;
|
|
76
|
+
}
|
|
77
|
+
export declare function createEmbodiedContextStatePort(deps: EmbodiedContextStatePortDeps): EmbodiedContextStatePort;
|