@haaaiawd/second-nature 0.1.24 → 0.1.26

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.
Files changed (47) hide show
  1. package/index.js +78 -0
  2. package/openclaw.plugin.json +1 -1
  3. package/package.json +5 -5
  4. package/runtime/cli/commands/goal.d.ts +28 -0
  5. package/runtime/cli/commands/goal.js +163 -0
  6. package/runtime/cli/commands/index.js +38 -3
  7. package/runtime/cli/explain/resolve-subject.js +3 -0
  8. package/runtime/cli/ops/ops-router.d.ts +1 -1
  9. package/runtime/cli/ops/ops-router.js +63 -1
  10. package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +6 -0
  11. package/runtime/cli/ops/workspace-heartbeat-runner.js +35 -1
  12. package/runtime/cli/read-models/index.d.ts +14 -2
  13. package/runtime/cli/read-models/index.js +403 -101
  14. package/runtime/cli/read-models/types.d.ts +90 -3
  15. package/runtime/core/second-nature/feedback/owner-reply-feedback.d.ts +46 -0
  16. package/runtime/core/second-nature/feedback/owner-reply-feedback.js +159 -0
  17. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +11 -1
  18. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +78 -10
  19. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +2 -0
  20. package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +1 -1
  21. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +16 -2
  22. package/runtime/core/second-nature/index.d.ts +1 -0
  23. package/runtime/core/second-nature/index.js +1 -0
  24. package/runtime/core/second-nature/orchestrator/goal-priority.d.ts +16 -3
  25. package/runtime/core/second-nature/orchestrator/goal-priority.js +10 -9
  26. package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +29 -1
  27. package/runtime/core/second-nature/orchestrator/intent-planner.js +154 -79
  28. package/runtime/core/second-nature/orchestrator/narrative-update.js +23 -9
  29. package/runtime/core/second-nature/orchestrator/platform-capability-router.d.ts +34 -0
  30. package/runtime/core/second-nature/orchestrator/platform-capability-router.js +115 -0
  31. package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +3 -1
  32. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +39 -1
  33. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +21 -2
  34. package/runtime/guidance/draft-outreach-message.js +14 -1
  35. package/runtime/guidance/outreach-draft-schema.d.ts +104 -0
  36. package/runtime/guidance/outreach-draft-schema.js +14 -0
  37. package/runtime/observability/audit/audit-envelope.d.ts +1 -1
  38. package/runtime/observability/query/explain-query.d.ts +3 -0
  39. package/runtime/observability/query/explain-query.js +9 -0
  40. package/runtime/observability/services/lived-experience-audit.d.ts +22 -0
  41. package/runtime/observability/services/lived-experience-audit.js +30 -0
  42. package/runtime/shared/types/credential.d.ts +1 -1
  43. package/runtime/storage/chronicle/session-chronicle-store.d.ts +1 -1
  44. package/runtime/storage/db/schema/narrative-state.d.ts +1 -1
  45. package/runtime/storage/db/schema/narrative-state.js +2 -2
  46. package/runtime/storage/services/credential-vault.d.ts +18 -0
  47. package/runtime/storage/services/credential-vault.js +73 -3
@@ -97,6 +97,46 @@ export declare const sceneGuidanceRequestSchema: z.ZodObject<{
97
97
  "zh-CN": "zh-CN";
98
98
  }>>;
99
99
  }, z.core.$strip>;
100
+ export declare const outreachNarrativeContextSchema: z.ZodObject<{
101
+ focus: z.ZodOptional<z.ZodString>;
102
+ progress: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
+ nextIntent: z.ZodOptional<z.ZodString>;
104
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
105
+ id: z.ZodString;
106
+ kind: z.ZodEnum<{
107
+ platform_item: "platform_item";
108
+ workspace_artifact: "workspace_artifact";
109
+ decision_record: "decision_record";
110
+ user_anchor: "user_anchor";
111
+ connector_result: "connector_result";
112
+ host_report: "host_report";
113
+ fallback_artifact: "fallback_artifact";
114
+ }>;
115
+ uri: z.ZodString;
116
+ excerptHash: z.ZodOptional<z.ZodString>;
117
+ observedAt: z.ZodOptional<z.ZodString>;
118
+ }, z.core.$strip>>>;
119
+ }, z.core.$strip>;
120
+ export declare const outreachRelationshipContextSchema: z.ZodObject<{
121
+ tone: z.ZodOptional<z.ZodString>;
122
+ topicAffinities: z.ZodOptional<z.ZodArray<z.ZodString>>;
123
+ avgAffinity: z.ZodOptional<z.ZodNumber>;
124
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
125
+ id: z.ZodString;
126
+ kind: z.ZodEnum<{
127
+ platform_item: "platform_item";
128
+ workspace_artifact: "workspace_artifact";
129
+ decision_record: "decision_record";
130
+ user_anchor: "user_anchor";
131
+ connector_result: "connector_result";
132
+ host_report: "host_report";
133
+ fallback_artifact: "fallback_artifact";
134
+ }>;
135
+ uri: z.ZodString;
136
+ excerptHash: z.ZodOptional<z.ZodString>;
137
+ observedAt: z.ZodOptional<z.ZodString>;
138
+ }, z.core.$strip>>>;
139
+ }, z.core.$strip>;
100
140
  export declare const outreachDraftRequestSchema: z.ZodObject<{
101
141
  requestId: z.ZodString;
102
142
  runtimeScope: z.ZodEnum<{
@@ -177,6 +217,46 @@ export declare const outreachDraftRequestSchema: z.ZodObject<{
177
217
  excerptHash: z.ZodOptional<z.ZodString>;
178
218
  observedAt: z.ZodOptional<z.ZodString>;
179
219
  }, z.core.$strip>>;
220
+ narrativeContext: z.ZodOptional<z.ZodObject<{
221
+ focus: z.ZodOptional<z.ZodString>;
222
+ progress: z.ZodOptional<z.ZodArray<z.ZodString>>;
223
+ nextIntent: z.ZodOptional<z.ZodString>;
224
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
225
+ id: z.ZodString;
226
+ kind: z.ZodEnum<{
227
+ platform_item: "platform_item";
228
+ workspace_artifact: "workspace_artifact";
229
+ decision_record: "decision_record";
230
+ user_anchor: "user_anchor";
231
+ connector_result: "connector_result";
232
+ host_report: "host_report";
233
+ fallback_artifact: "fallback_artifact";
234
+ }>;
235
+ uri: z.ZodString;
236
+ excerptHash: z.ZodOptional<z.ZodString>;
237
+ observedAt: z.ZodOptional<z.ZodString>;
238
+ }, z.core.$strip>>>;
239
+ }, z.core.$strip>>;
240
+ relationshipContext: z.ZodOptional<z.ZodObject<{
241
+ tone: z.ZodOptional<z.ZodString>;
242
+ topicAffinities: z.ZodOptional<z.ZodArray<z.ZodString>>;
243
+ avgAffinity: z.ZodOptional<z.ZodNumber>;
244
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
245
+ id: z.ZodString;
246
+ kind: z.ZodEnum<{
247
+ platform_item: "platform_item";
248
+ workspace_artifact: "workspace_artifact";
249
+ decision_record: "decision_record";
250
+ user_anchor: "user_anchor";
251
+ connector_result: "connector_result";
252
+ host_report: "host_report";
253
+ fallback_artifact: "fallback_artifact";
254
+ }>;
255
+ uri: z.ZodString;
256
+ excerptHash: z.ZodOptional<z.ZodString>;
257
+ observedAt: z.ZodOptional<z.ZodString>;
258
+ }, z.core.$strip>>>;
259
+ }, z.core.$strip>>;
180
260
  }, z.core.$strip>;
181
261
  export type SceneGuidanceRequest = z.infer<typeof sceneGuidanceRequestSchema>;
182
262
  export type OutreachDraftRequest = z.infer<typeof outreachDraftRequestSchema>;
@@ -212,6 +292,30 @@ export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafe
212
292
  fallbackRef?: string | undefined;
213
293
  } | undefined;
214
294
  language?: "en-US" | "zh-CN" | undefined;
295
+ narrativeContext?: {
296
+ focus?: string | undefined;
297
+ progress?: string[] | undefined;
298
+ nextIntent?: string | undefined;
299
+ sourceRefs?: {
300
+ id: string;
301
+ kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
302
+ uri: string;
303
+ excerptHash?: string | undefined;
304
+ observedAt?: string | undefined;
305
+ }[] | undefined;
306
+ } | undefined;
307
+ relationshipContext?: {
308
+ tone?: string | undefined;
309
+ topicAffinities?: string[] | undefined;
310
+ avgAffinity?: number | undefined;
311
+ sourceRefs?: {
312
+ id: string;
313
+ kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
314
+ uri: string;
315
+ excerptHash?: string | undefined;
316
+ observedAt?: string | undefined;
317
+ }[] | undefined;
318
+ } | undefined;
215
319
  }>;
216
320
  /** Async seam for generative outreach copy (implementation lives outside control-plane). */
217
321
  export interface GuidanceDraftPort {
@@ -54,6 +54,18 @@ export const sceneGuidanceRequestSchema = z.object({
54
54
  deliveryContext: deliveryExpressionContextSchema.optional(),
55
55
  language: z.enum(["zh-CN", "en-US"]).optional(),
56
56
  });
57
+ export const outreachNarrativeContextSchema = z.object({
58
+ focus: z.string().optional(),
59
+ progress: z.array(z.string()).optional(),
60
+ nextIntent: z.string().optional(),
61
+ sourceRefs: z.array(guidanceSourceRefSchema).optional(),
62
+ });
63
+ export const outreachRelationshipContextSchema = z.object({
64
+ tone: z.string().optional(),
65
+ topicAffinities: z.array(z.string()).optional(),
66
+ avgAffinity: z.number().optional(),
67
+ sourceRefs: z.array(guidanceSourceRefSchema).optional(),
68
+ });
57
69
  export const outreachDraftRequestSchema = sceneGuidanceRequestSchema
58
70
  .extend({
59
71
  sceneType: z.enum(["outreach", "fallback_candidate"]),
@@ -62,6 +74,8 @@ export const outreachDraftRequestSchema = sceneGuidanceRequestSchema
62
74
  judgmentVerdict: z.enum(["allow", "deny", "defer"]),
63
75
  valueScore: z.number(),
64
76
  interestRefs: z.array(guidanceSourceRefSchema),
77
+ narrativeContext: outreachNarrativeContextSchema.optional(),
78
+ relationshipContext: outreachRelationshipContextSchema.optional(),
65
79
  })
66
80
  .superRefine((val, ctx) => {
67
81
  if (!val.deliveryContext) {
@@ -1,6 +1,6 @@
1
1
  import { type RedactionManifest as FieldRedactionManifest } from "../redaction/manifest.js";
2
2
  export type AuditPlane = "decision" | "delivery" | "source_coverage" | "governance" | "telemetry";
3
- export type AuditEventFamily = "heartbeat.decision" | "delivery" | "source_coverage" | "guidance.grounding" | "host_capability" | "connector.attempt" | "state.governance";
3
+ export type AuditEventFamily = "heartbeat.decision" | "delivery" | "source_coverage" | "guidance.grounding" | "host_capability" | "connector.attempt" | "state.governance" | "narrative.trace" | "dream.trace";
4
4
  export type AuditEnvelopeSensitivity = "public" | "internal" | "private" | "credential" | "sensitive";
5
5
  export interface AuditRedactionManifest {
6
6
  manifestId: string;
@@ -25,6 +25,9 @@ export type ExplainQuery = {
25
25
  } | {
26
26
  kind: "source_ref";
27
27
  sourceRefId: string;
28
+ } | {
29
+ kind: "relationship";
30
+ relationshipId: string;
28
31
  };
29
32
  export interface RedactedExplainEvent {
30
33
  eventId: string;
@@ -60,6 +60,15 @@ function eventMatchesQuery(envelope, query) {
60
60
  return false;
61
61
  }
62
62
  }
63
+ case "relationship": {
64
+ const needle = query.relationshipId;
65
+ try {
66
+ return JSON.stringify(payload).includes(needle);
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }
63
72
  }
64
73
  }
65
74
  function summarizeEnvelope(e) {
@@ -1,5 +1,6 @@
1
1
  import { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
2
2
  import type { SourceRef } from "../../storage/life-evidence/types.js";
3
+ import type { DreamTrace } from "../../dream/types.js";
3
4
  export type RuntimeScope = "rhythm" | "user_task" | "user_reply";
4
5
  export type HeartbeatOutcome = "heartbeat_ok" | "intent_selected" | "denied" | "deferred" | "runtime_carrier_only" | "delivery_unavailable";
5
6
  export type DeliveryAuditStatus = "not_requested" | "target_available" | "target_none" | "channel_missing" | "host_unsupported" | "ack_dropped" | "sent" | "failed" | "not_sent_fallback";
@@ -66,6 +67,21 @@ export interface GuidanceGroundingAuditPayload {
66
67
  deliveryWording?: "sendable" | "not_sent_fallback_candidate";
67
68
  createdAt: string;
68
69
  }
70
+ export interface NarrativeTracePayload {
71
+ traceId: string;
72
+ narrativeId: string;
73
+ revision: number;
74
+ updateSource: "heartbeat" | "dream" | "owner" | "maintenance";
75
+ sourceRefs: Array<{
76
+ id: string;
77
+ kind: string;
78
+ uri?: string;
79
+ }>;
80
+ unsupportedClaims: string[];
81
+ groundingStatus: GroundingStatus;
82
+ goalInfluenceRefs: string[];
83
+ createdAt: string;
84
+ }
69
85
  export interface ExplainLinkageSummary {
70
86
  decisionId: string;
71
87
  summary: string;
@@ -92,6 +108,12 @@ export declare class LivedExperienceAuditRecorder {
92
108
  recordGuidanceGrounding(payload: GuidanceGroundingAuditPayload): {
93
109
  eventId: string;
94
110
  };
111
+ recordNarrativeTrace(payload: NarrativeTracePayload): {
112
+ eventId: string;
113
+ };
114
+ recordDreamTrace(payload: DreamTrace): {
115
+ eventId: string;
116
+ };
95
117
  explainLinkageForDecision(decisionId: string): ExplainLinkageSummary;
96
118
  }
97
119
  export declare function createLivedExperienceAuditRecorder(store?: AppendOnlyAuditStore): LivedExperienceAuditRecorder;
@@ -133,6 +133,36 @@ export class LivedExperienceAuditRecorder {
133
133
  }
134
134
  return { eventId: envelope.eventId };
135
135
  }
136
+ recordNarrativeTrace(payload) {
137
+ const seq = this.bumpSequence();
138
+ const envelope = buildAuditEnvelope({
139
+ family: "narrative.trace",
140
+ plane: "source_coverage",
141
+ traceId: payload.traceId,
142
+ sequence: seq,
143
+ payload,
144
+ previousHash: this.store.lastRecordHash(),
145
+ eventId: crypto.randomUUID(),
146
+ createdAt: payload.createdAt,
147
+ });
148
+ this.store.append(envelope);
149
+ return { eventId: envelope.eventId };
150
+ }
151
+ recordDreamTrace(payload) {
152
+ const seq = this.bumpSequence();
153
+ const envelope = buildAuditEnvelope({
154
+ family: "dream.trace",
155
+ plane: "telemetry",
156
+ traceId: payload.traceId,
157
+ sequence: seq,
158
+ payload,
159
+ previousHash: this.store.lastRecordHash(),
160
+ eventId: crypto.randomUUID(),
161
+ createdAt: payload.finishedAt,
162
+ });
163
+ this.store.append(envelope);
164
+ return { eventId: envelope.eventId };
165
+ }
136
166
  explainLinkageForDecision(decisionId) {
137
167
  const entry = this.explainIndex.get(decisionId);
138
168
  const warnings = [];
@@ -1,4 +1,4 @@
1
- export type CredentialState = "missing" | "pending_verification" | "active" | "expired" | "revoked" | "failed";
1
+ export type CredentialState = "missing" | "pending_verification" | "active" | "expired" | "revoked" | "failed" | "decrypt_failed";
2
2
  export type CredentialType = "api_key" | "oauth_token" | "node_secret" | "verification_code";
3
3
  export interface CredentialContext {
4
4
  platformId: string;
@@ -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;
@@ -99,7 +99,7 @@ export declare const narrativeState: import("drizzle-orm/sqlite-core").SQLiteTab
99
99
  name: "confidence";
100
100
  tableName: "narrative_state";
101
101
  dataType: "number";
102
- columnType: "SQLiteInteger";
102
+ columnType: "SQLiteReal";
103
103
  data: number;
104
104
  driverParam: number;
105
105
  notNull: true;
@@ -1,11 +1,11 @@
1
- import { index, sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
1
+ import { index, sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";
2
2
  export const narrativeState = sqliteTable("narrative_state", {
3
3
  narrativeId: text("narrative_id").primaryKey(),
4
4
  revision: integer("revision").notNull().default(1),
5
5
  focus: text("focus").notNull(),
6
6
  progressJson: text("progress_json").notNull(),
7
7
  nextIntent: text("next_intent").notNull(),
8
- confidence: integer("confidence").notNull().default(0),
8
+ confidence: real("confidence").notNull().default(0),
9
9
  sourceRefsJson: text("source_refs_json").notNull(),
10
10
  unsupportedClaimsJson: text("unsupported_claims_json").notNull(),
11
11
  status: text("status").notNull(),
@@ -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;
@@ -54,6 +54,58 @@ export function decryptCredentialAtRest(ciphertext) {
54
54
  return "";
55
55
  return decryptInternal(ciphertext);
56
56
  }
57
+ /**
58
+ * T1.4.1 — probe a credential record for runtime secret health.
59
+ *
60
+ * Given a raw encrypted value from the DB, this function checks:
61
+ * 1. Is SECOND_NATURE_ENCRYPTION_KEY present and >= 32 chars?
62
+ * 2. Can the ciphertext be decrypted with that key?
63
+ *
64
+ * It never throws; all failures are encoded in the returned state.
65
+ */
66
+ export function probeCredentialHealth(platformId, encryptedValue, baseUrl) {
67
+ // Key availability check
68
+ const rawKey = process.env.SECOND_NATURE_ENCRYPTION_KEY?.trim();
69
+ if (!rawKey || rawKey.length < 32) {
70
+ return {
71
+ platformId,
72
+ state: encryptedValue ? "decrypt_failed" : "missing",
73
+ keyHealth: "missing_key",
74
+ hasBaseUrl: Boolean(baseUrl),
75
+ diagnosticCode: "missing_runtime_secret",
76
+ };
77
+ }
78
+ // No encrypted value to test
79
+ if (!encryptedValue) {
80
+ return {
81
+ platformId,
82
+ state: "missing",
83
+ keyHealth: "ok",
84
+ hasBaseUrl: Boolean(baseUrl),
85
+ diagnosticCode: "ok",
86
+ };
87
+ }
88
+ // Decryption attempt
89
+ try {
90
+ decryptCredentialAtRest(encryptedValue);
91
+ return {
92
+ platformId,
93
+ state: "active",
94
+ keyHealth: "ok",
95
+ hasBaseUrl: Boolean(baseUrl),
96
+ diagnosticCode: "ok",
97
+ };
98
+ }
99
+ catch {
100
+ return {
101
+ platformId,
102
+ state: "decrypt_failed",
103
+ keyHealth: "wrong_key",
104
+ hasBaseUrl: Boolean(baseUrl),
105
+ diagnosticCode: "credential_recovery_required",
106
+ };
107
+ }
108
+ }
57
109
  export function createCredentialVault(db) {
58
110
  return {
59
111
  async saveCredentialContext(input) {
@@ -89,16 +141,34 @@ export function createCredentialVault(db) {
89
141
  if (!record)
90
142
  return null;
91
143
  let plain;
144
+ let status = record.status;
92
145
  if (record.encryptedValue) {
93
146
  if (!isCredentialCiphertext(record.encryptedValue)) {
94
- throw new Error("credential_store_plaintext_or_invalid_legacy_record");
147
+ // Fail-closed: return decrypt_failed so callers do not crash.
148
+ return {
149
+ platformId: record.platformId,
150
+ credentialType: record.credentialType,
151
+ status: "decrypt_failed",
152
+ encryptedValue: undefined,
153
+ verificationCode: record.verificationCode ?? undefined,
154
+ challengeText: record.challengeText ?? undefined,
155
+ verificationDeadline: record.expiresAt ?? undefined,
156
+ attemptsRemaining: record.attemptsRemaining ?? undefined,
157
+ };
158
+ }
159
+ try {
160
+ plain = decryptCredentialAtRest(record.encryptedValue);
161
+ }
162
+ catch {
163
+ // Decryption failure must not break the whole state load.
164
+ status = "decrypt_failed";
165
+ plain = undefined;
95
166
  }
96
- plain = decryptCredentialAtRest(record.encryptedValue);
97
167
  }
98
168
  return {
99
169
  platformId: record.platformId,
100
170
  credentialType: record.credentialType,
101
- status: record.status,
171
+ status,
102
172
  encryptedValue: plain,
103
173
  verificationCode: record.verificationCode ?? undefined,
104
174
  challengeText: record.challengeText ?? undefined,