@haaaiawd/second-nature 0.1.25 → 0.1.27
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 +33 -0
- package/agent-inner-guide.md +124 -0
- package/index.js +206 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +3 -1
- package/runtime/cli/commands/goal.d.ts +2 -0
- package/runtime/cli/commands/goal.js +5 -1
- package/runtime/cli/commands/index.js +1 -1
- package/runtime/cli/explain/resolve-subject.js +3 -0
- package/runtime/cli/ops/ops-router.js +13 -5
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +6 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.js +35 -1
- package/runtime/cli/read-models/index.js +81 -10
- package/runtime/cli/read-models/types.d.ts +10 -3
- package/runtime/connectors/base/manifest.d.ts +77 -77
- package/runtime/core/second-nature/feedback/owner-reply-feedback.d.ts +46 -0
- package/runtime/core/second-nature/feedback/owner-reply-feedback.js +159 -0
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +8 -1
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +45 -4
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +2 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +1 -1
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +16 -2
- package/runtime/core/second-nature/index.d.ts +1 -0
- package/runtime/core/second-nature/index.js +1 -0
- package/runtime/core/second-nature/orchestrator/goal-priority.d.ts +14 -2
- package/runtime/core/second-nature/orchestrator/goal-priority.js +2 -2
- package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +29 -1
- package/runtime/core/second-nature/orchestrator/intent-planner.js +154 -79
- package/runtime/core/second-nature/orchestrator/narrative-update.js +23 -9
- package/runtime/core/second-nature/orchestrator/platform-capability-router.d.ts +34 -0
- package/runtime/core/second-nature/orchestrator/platform-capability-router.js +115 -0
- package/runtime/observability/query/explain-query.d.ts +3 -0
- package/runtime/observability/query/explain-query.js +9 -0
- package/runtime/shared/types/credential.d.ts +1 -1
- package/runtime/storage/chronicle/session-chronicle-store.d.ts +1 -1
- package/runtime/storage/services/credential-vault.d.ts +18 -0
- package/runtime/storage/services/credential-vault.js +96 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StateDatabase } from "../db/index.js";
|
|
2
|
-
export type ChronicleEventKind = "heartbeat" | "connector_action" | "outreach" | "owner_reply" | "dream_run" | "maintenance";
|
|
2
|
+
export type ChronicleEventKind = "heartbeat" | "connector_action" | "outreach" | "owner_reply" | "dream_run" | "maintenance" | "system_notice";
|
|
3
3
|
export interface SourceRef {
|
|
4
4
|
sourceId: string;
|
|
5
5
|
kind: string;
|
|
@@ -10,4 +10,22 @@ export interface CredentialVault {
|
|
|
10
10
|
loadCredentialContext(platformId: string): Promise<CredentialContext | null>;
|
|
11
11
|
getCredentialState(platformId: string): Promise<CredentialState>;
|
|
12
12
|
}
|
|
13
|
+
/** T1.4.1 — runtime secret health probe result for a single credential row. */
|
|
14
|
+
export interface CredentialHealthProbe {
|
|
15
|
+
platformId: string;
|
|
16
|
+
state: CredentialState | "decrypt_failed";
|
|
17
|
+
keyHealth: "missing_key" | "wrong_key" | "ok";
|
|
18
|
+
hasBaseUrl: boolean;
|
|
19
|
+
diagnosticCode: "missing_runtime_secret" | "credential_recovery_required" | "ok";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* T1.4.1 — probe a credential record for runtime secret health.
|
|
23
|
+
*
|
|
24
|
+
* Given a raw encrypted value from the DB, this function checks:
|
|
25
|
+
* 1. Is SECOND_NATURE_ENCRYPTION_KEY present and >= 32 chars?
|
|
26
|
+
* 2. Can the ciphertext be decrypted with that key?
|
|
27
|
+
*
|
|
28
|
+
* It never throws; all failures are encoded in the returned state.
|
|
29
|
+
*/
|
|
30
|
+
export declare function probeCredentialHealth(platformId: string, encryptedValue: string | undefined | null, baseUrl: string | undefined | null): CredentialHealthProbe;
|
|
13
31
|
export declare function createCredentialVault(db: StateDatabase["db"]): CredentialVault;
|
|
@@ -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) {
|
|
@@ -54,6 +59,58 @@ export function decryptCredentialAtRest(ciphertext) {
|
|
|
54
59
|
return "";
|
|
55
60
|
return decryptInternal(ciphertext);
|
|
56
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* T1.4.1 — probe a credential record for runtime secret health.
|
|
64
|
+
*
|
|
65
|
+
* Given a raw encrypted value from the DB, this function checks:
|
|
66
|
+
* 1. Is SECOND_NATURE_ENCRYPTION_KEY present and >= 32 chars?
|
|
67
|
+
* 2. Can the ciphertext be decrypted with that key?
|
|
68
|
+
*
|
|
69
|
+
* It never throws; all failures are encoded in the returned state.
|
|
70
|
+
*/
|
|
71
|
+
export function probeCredentialHealth(platformId, encryptedValue, baseUrl) {
|
|
72
|
+
// Key availability check
|
|
73
|
+
const rawKey = process.env.SECOND_NATURE_ENCRYPTION_KEY?.trim();
|
|
74
|
+
if (!rawKey || rawKey.length < 32) {
|
|
75
|
+
return {
|
|
76
|
+
platformId,
|
|
77
|
+
state: encryptedValue ? "decrypt_failed" : "missing",
|
|
78
|
+
keyHealth: "missing_key",
|
|
79
|
+
hasBaseUrl: Boolean(baseUrl),
|
|
80
|
+
diagnosticCode: "missing_runtime_secret",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// No encrypted value to test
|
|
84
|
+
if (!encryptedValue) {
|
|
85
|
+
return {
|
|
86
|
+
platformId,
|
|
87
|
+
state: "missing",
|
|
88
|
+
keyHealth: "ok",
|
|
89
|
+
hasBaseUrl: Boolean(baseUrl),
|
|
90
|
+
diagnosticCode: "ok",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Decryption attempt
|
|
94
|
+
try {
|
|
95
|
+
decryptCredentialAtRest(encryptedValue);
|
|
96
|
+
return {
|
|
97
|
+
platformId,
|
|
98
|
+
state: "active",
|
|
99
|
+
keyHealth: "ok",
|
|
100
|
+
hasBaseUrl: Boolean(baseUrl),
|
|
101
|
+
diagnosticCode: "ok",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {
|
|
106
|
+
platformId,
|
|
107
|
+
state: "decrypt_failed",
|
|
108
|
+
keyHealth: "wrong_key",
|
|
109
|
+
hasBaseUrl: Boolean(baseUrl),
|
|
110
|
+
diagnosticCode: "credential_recovery_required",
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
57
114
|
export function createCredentialVault(db) {
|
|
58
115
|
return {
|
|
59
116
|
async saveCredentialContext(input) {
|
|
@@ -88,29 +145,56 @@ export function createCredentialVault(db) {
|
|
|
88
145
|
});
|
|
89
146
|
if (!record)
|
|
90
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;
|
|
91
156
|
let plain;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
157
|
+
let status = (row.status ?? "missing");
|
|
158
|
+
if (encryptedValue) {
|
|
159
|
+
if (!isCredentialCiphertext(encryptedValue)) {
|
|
160
|
+
// Fail-closed: return decrypt_failed so callers do not crash.
|
|
161
|
+
return {
|
|
162
|
+
platformId: resolvedPlatformId,
|
|
163
|
+
credentialType: credentialType,
|
|
164
|
+
status: "decrypt_failed",
|
|
165
|
+
encryptedValue: undefined,
|
|
166
|
+
verificationCode,
|
|
167
|
+
challengeText,
|
|
168
|
+
verificationDeadline: expiresAt,
|
|
169
|
+
attemptsRemaining,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
plain = decryptCredentialAtRest(encryptedValue);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Decryption failure must not break the whole state load.
|
|
177
|
+
status = "decrypt_failed";
|
|
178
|
+
plain = undefined;
|
|
95
179
|
}
|
|
96
|
-
plain = decryptCredentialAtRest(record.encryptedValue);
|
|
97
180
|
}
|
|
98
181
|
return {
|
|
99
|
-
platformId:
|
|
100
|
-
credentialType:
|
|
101
|
-
status
|
|
182
|
+
platformId: resolvedPlatformId,
|
|
183
|
+
credentialType: credentialType,
|
|
184
|
+
status,
|
|
102
185
|
encryptedValue: plain,
|
|
103
|
-
verificationCode
|
|
104
|
-
challengeText
|
|
105
|
-
verificationDeadline:
|
|
106
|
-
attemptsRemaining
|
|
186
|
+
verificationCode,
|
|
187
|
+
challengeText,
|
|
188
|
+
verificationDeadline: expiresAt,
|
|
189
|
+
attemptsRemaining,
|
|
107
190
|
};
|
|
108
191
|
},
|
|
109
192
|
async getCredentialState(platformId) {
|
|
110
193
|
const record = await db.query.credentialRecords.findFirst({
|
|
111
194
|
where: (tbl) => eq(tbl.platformId, platformId),
|
|
112
195
|
});
|
|
113
|
-
|
|
196
|
+
const row = normalizeCredentialRecord(record);
|
|
197
|
+
return row.status || "missing";
|
|
114
198
|
},
|
|
115
199
|
};
|
|
116
200
|
}
|