@haaaiawd/second-nature 0.2.5 → 0.2.7
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 +1 -1
- package/package.json +1 -1
- package/runtime/cli/commands/index.d.ts +4 -0
- package/runtime/cli/commands/index.js +188 -5
- package/runtime/cli/index.js +2 -0
- package/runtime/cli/ops/ops-router.js +23 -17
- package/runtime/connectors/base/normalized-evidence-content.d.ts +71 -0
- package/runtime/connectors/base/normalized-evidence-content.js +273 -0
- package/runtime/connectors/evidence-normalizer.d.ts +4 -3
- package/runtime/connectors/evidence-normalizer.js +128 -23
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +15 -0
- package/runtime/core/second-nature/perception/perception-builder.d.ts +3 -1
- package/runtime/core/second-nature/perception/perception-builder.js +98 -44
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.d.ts +6 -3
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +198 -22
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +19 -3
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +189 -12
- package/runtime/observability/db/index.d.ts +2 -0
- package/runtime/observability/db/index.js +6 -0
- package/runtime/shared/types/v8-contracts.d.ts +1 -1
- package/runtime/storage/db/index.d.ts +2 -0
- package/runtime/storage/db/index.js +16 -8
- package/runtime/storage/db/migrations/v8-003-quiet-closure-refs.js +2 -1
- package/runtime/storage/services/write-validation-gate.d.ts +2 -0
- package/runtime/storage/services/write-validation-gate.js +69 -17
- package/runtime/storage/v8-state-stores.d.ts +25 -0
- package/runtime/storage/v8-state-stores.js +87 -1
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "second-nature",
|
|
3
3
|
"name": "Second Nature",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.7",
|
|
5
5
|
"description": "OpenClaw native plugin with synchronous surface registration and bundled runtime spine. Set SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot to the same path as the agent workspace. Agent inner guide is packaged as agent-inner-guide.md. v7 ops surface: self_health, tool_affordance, heartbeat_digest, snapshot:capture, narrative:diff, timeline, restore, runtime_secret_bootstrap, connector:run, guidance_payload.",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ActionBridge } from "../action-bridge.js";
|
|
2
2
|
import type { OpsRouter } from "../ops/ops-router.js";
|
|
3
|
+
import type { StateDatabase } from "../../storage/db/index.js";
|
|
4
|
+
import type { ObservabilityDatabase } from "../../observability/db/index.js";
|
|
3
5
|
import type { CliReadModels } from "../read-models/index.js";
|
|
4
6
|
export interface CliCommandDefinition {
|
|
5
7
|
name: string;
|
|
@@ -10,5 +12,7 @@ export interface CliCommandDeps {
|
|
|
10
12
|
readModels: CliReadModels;
|
|
11
13
|
actionBridge: ActionBridge;
|
|
12
14
|
opsRouter: OpsRouter;
|
|
15
|
+
stateDb?: StateDatabase;
|
|
16
|
+
observabilityDb?: ObservabilityDatabase;
|
|
13
17
|
}
|
|
14
18
|
export declare function createCliCommands(deps: CliCommandDeps): CliCommandDefinition[];
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { credentialVerify } from "./credential.js";
|
|
2
4
|
import { connectorInit } from "./connector-init.js";
|
|
3
5
|
import { formatExplanation } from "../explain/format-explanation.js";
|
|
@@ -5,6 +7,139 @@ import { explainSurfaceSubject } from "../explain/explain-surface-subject.js";
|
|
|
5
7
|
import { showOperatorFallback, OperatorFallbackNotFoundError, } from "../ops/show-operator-fallback.js";
|
|
6
8
|
import { runStorageModeSmoke } from "../../storage/bootstrap/storage-mode-smoke.js";
|
|
7
9
|
import { policySet } from "./policy.js";
|
|
10
|
+
const SETUP_MARKER_RELATIVE_PATH = path.join(".second-nature", "setup", "agent-inner-guide-ack.json");
|
|
11
|
+
function safeShortText(value, maxLen) {
|
|
12
|
+
if (typeof value !== "string")
|
|
13
|
+
return undefined;
|
|
14
|
+
const trimmed = value.trim();
|
|
15
|
+
if (trimmed.length === 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
return trimmed.slice(0, maxLen);
|
|
18
|
+
}
|
|
19
|
+
function resolveWorkspaceRoot(input) {
|
|
20
|
+
if (typeof input?.workspaceRoot === "string" && input.workspaceRoot.trim().length > 0) {
|
|
21
|
+
return path.resolve(input.workspaceRoot);
|
|
22
|
+
}
|
|
23
|
+
if (typeof process.env.SECOND_NATURE_WORKSPACE_ROOT === "string" && process.env.SECOND_NATURE_WORKSPACE_ROOT.trim().length > 0) {
|
|
24
|
+
return path.resolve(process.env.SECOND_NATURE_WORKSPACE_ROOT);
|
|
25
|
+
}
|
|
26
|
+
return process.cwd();
|
|
27
|
+
}
|
|
28
|
+
function readSetupText(fileName) {
|
|
29
|
+
const candidates = fileName === "SKILL.md"
|
|
30
|
+
? [path.resolve(process.cwd(), "SKILL.md"), path.resolve(process.cwd(), "..", "SKILL.md")]
|
|
31
|
+
: [path.resolve(process.cwd(), "plugin", "agent-inner-guide.md"), path.resolve(process.cwd(), "..", "plugin", "agent-inner-guide.md")];
|
|
32
|
+
for (const candidate of candidates) {
|
|
33
|
+
try {
|
|
34
|
+
const content = fs.readFileSync(candidate, "utf-8");
|
|
35
|
+
return { ok: true, path: candidate, content };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// try next candidate
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { ok: false, path: candidates[0] ?? fileName, error: `Could not read ${fileName}` };
|
|
42
|
+
}
|
|
43
|
+
function summarizeSetupText(content) {
|
|
44
|
+
const lines = content.split("\n");
|
|
45
|
+
const nonEmpty = lines.filter((line) => line.trim().length > 0);
|
|
46
|
+
const first = nonEmpty.slice(0, 3).join("\n");
|
|
47
|
+
const marker = content.length > first.length ? "\n\n[...]" : "";
|
|
48
|
+
return `${first}${marker}`;
|
|
49
|
+
}
|
|
50
|
+
function readSetupAckMarker(workspaceRoot) {
|
|
51
|
+
const markerPath = path.join(workspaceRoot, SETUP_MARKER_RELATIVE_PATH);
|
|
52
|
+
try {
|
|
53
|
+
const raw = fs.readFileSync(markerPath, "utf-8");
|
|
54
|
+
const marker = JSON.parse(raw);
|
|
55
|
+
if (marker.status === "acknowledged") {
|
|
56
|
+
return {
|
|
57
|
+
status: "acknowledged",
|
|
58
|
+
markerPath,
|
|
59
|
+
acknowledgedAt: typeof marker.acknowledgedAt === "string" ? marker.acknowledgedAt : undefined,
|
|
60
|
+
placedIn: typeof marker.placedIn === "string" ? marker.placedIn : undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// marker missing or unreadable
|
|
66
|
+
}
|
|
67
|
+
return { status: "pending", markerPath };
|
|
68
|
+
}
|
|
69
|
+
async function buildSetupHintPayload(input) {
|
|
70
|
+
const format = input?.format === "full" ? "full" : "summary";
|
|
71
|
+
const includeSkill = input?.includeSkill !== false;
|
|
72
|
+
const includeGuide = input?.includeGuide !== false;
|
|
73
|
+
const workspaceRoot = resolveWorkspaceRoot(input);
|
|
74
|
+
const ack = readSetupAckMarker(workspaceRoot);
|
|
75
|
+
const data = {
|
|
76
|
+
status: ack.status,
|
|
77
|
+
workspaceRoot,
|
|
78
|
+
markerPath: ack.markerPath,
|
|
79
|
+
acknowledgedAt: ack.acknowledgedAt,
|
|
80
|
+
placedIn: ack.placedIn,
|
|
81
|
+
recommendedPlacement: [
|
|
82
|
+
"agent prompt",
|
|
83
|
+
"workspace/IDENTITY.md",
|
|
84
|
+
"workspace/USER.md",
|
|
85
|
+
],
|
|
86
|
+
nextStep: ack.status === "acknowledged"
|
|
87
|
+
? "setup_already_acknowledged"
|
|
88
|
+
: "read_returned_guidance_then_run_setup_ack",
|
|
89
|
+
};
|
|
90
|
+
if (includeSkill) {
|
|
91
|
+
const skill = readSetupText("SKILL.md");
|
|
92
|
+
data.skill = skill.ok
|
|
93
|
+
? {
|
|
94
|
+
path: skill.path,
|
|
95
|
+
content: format === "full" ? skill.content : summarizeSetupText(skill.content),
|
|
96
|
+
}
|
|
97
|
+
: skill;
|
|
98
|
+
}
|
|
99
|
+
if (includeGuide) {
|
|
100
|
+
const guide = readSetupText("agent-inner-guide.md");
|
|
101
|
+
data.guide = guide.ok
|
|
102
|
+
? {
|
|
103
|
+
path: guide.path,
|
|
104
|
+
content: format === "full" ? guide.content : summarizeSetupText(guide.content),
|
|
105
|
+
}
|
|
106
|
+
: guide;
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
command: "setup_hint",
|
|
111
|
+
surfaceMode: "workspace_full_runtime",
|
|
112
|
+
message: "Read the SKILL and guide as a friendly setup note, then place the guidance where the agent naturally checks its working anchors.",
|
|
113
|
+
data,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function buildSetupAckPayload(input) {
|
|
117
|
+
const workspaceRoot = resolveWorkspaceRoot(input);
|
|
118
|
+
const markerPath = path.join(workspaceRoot, SETUP_MARKER_RELATIVE_PATH);
|
|
119
|
+
const marker = {
|
|
120
|
+
acknowledgedAt: new Date().toISOString(),
|
|
121
|
+
acceptedBy: safeShortText(input?.acceptedBy, 80) ?? "agent",
|
|
122
|
+
placedIn: safeShortText(input?.placedIn, 160) ?? "unspecified",
|
|
123
|
+
note: safeShortText(input?.note, 240),
|
|
124
|
+
guideVersion: "0.2.5",
|
|
125
|
+
source: "second-nature-cli",
|
|
126
|
+
skillPath: "SKILL.md",
|
|
127
|
+
guidePath: "plugin/agent-inner-guide.md",
|
|
128
|
+
status: "acknowledged",
|
|
129
|
+
};
|
|
130
|
+
fs.mkdirSync(path.dirname(markerPath), { recursive: true });
|
|
131
|
+
fs.writeFileSync(markerPath, `${JSON.stringify(marker, null, 2)}\n`, "utf-8");
|
|
132
|
+
return {
|
|
133
|
+
ok: true,
|
|
134
|
+
command: "setup_ack",
|
|
135
|
+
surfaceMode: "workspace_full_runtime",
|
|
136
|
+
message: "Setup guide acknowledgement persisted; setup nudge is now silent for this workspace.",
|
|
137
|
+
data: {
|
|
138
|
+
markerPath,
|
|
139
|
+
...marker,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
8
143
|
const notImplemented = async (command) => ({
|
|
9
144
|
ok: false,
|
|
10
145
|
command,
|
|
@@ -22,16 +157,45 @@ function explainSubjectError(code, message) {
|
|
|
22
157
|
};
|
|
23
158
|
}
|
|
24
159
|
export function createCliCommands(deps) {
|
|
25
|
-
const { readModels, actionBridge, opsRouter } = deps;
|
|
160
|
+
const { readModels, actionBridge, opsRouter, stateDb, observabilityDb } = deps;
|
|
161
|
+
const flush = () => {
|
|
162
|
+
try {
|
|
163
|
+
stateDb?.flush();
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// ignore flush errors to avoid masking command results
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
observabilityDb?.flush();
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// ignore flush errors to avoid masking command results
|
|
173
|
+
}
|
|
174
|
+
};
|
|
26
175
|
const opsCommand = (name, description) => ({
|
|
27
176
|
name,
|
|
28
177
|
description,
|
|
29
178
|
execute: async (input) => {
|
|
30
179
|
const surface = await Promise.resolve(opsRouter.dispatch(name, input));
|
|
180
|
+
flush();
|
|
31
181
|
return surface;
|
|
32
182
|
},
|
|
33
183
|
});
|
|
34
184
|
return [
|
|
185
|
+
{
|
|
186
|
+
name: "setup_hint",
|
|
187
|
+
description: "Return the packaged setup SKILL and agent inner guide for first-run onboarding",
|
|
188
|
+
execute: async (input) => buildSetupHintPayload(input),
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "setup_ack",
|
|
192
|
+
description: "Persist that the packaged setup guide was read and placed into working anchors",
|
|
193
|
+
execute: async (input) => {
|
|
194
|
+
const result = await buildSetupAckPayload(input);
|
|
195
|
+
flush();
|
|
196
|
+
return result;
|
|
197
|
+
},
|
|
198
|
+
},
|
|
35
199
|
{
|
|
36
200
|
name: "status",
|
|
37
201
|
description: "T1.2.6 — Show v6 aggregated Second Nature status (narrative + dream + cycles + runtime)",
|
|
@@ -47,7 +211,9 @@ export function createCliCommands(deps) {
|
|
|
47
211
|
execute: async (input) => {
|
|
48
212
|
const action = typeof input?.action === "string" ? input.action : "show";
|
|
49
213
|
if (action === "set") {
|
|
50
|
-
|
|
214
|
+
const result = await policySet(actionBridge, input);
|
|
215
|
+
flush();
|
|
216
|
+
return result;
|
|
51
217
|
}
|
|
52
218
|
// T1.2.6 (SN-CODE-01): `policy show` (default) returns the current rhythm policy
|
|
53
219
|
// snapshot. Returns workspace defaults when no policy row has been persisted yet.
|
|
@@ -159,6 +325,7 @@ export function createCliCommands(deps) {
|
|
|
159
325
|
description: "Workspace heartbeat_check ops surface (v5 HeartbeatSurfaceResult)",
|
|
160
326
|
execute: async (input) => {
|
|
161
327
|
const surface = await Promise.resolve(opsRouter.dispatch("heartbeat_check", input));
|
|
328
|
+
flush();
|
|
162
329
|
return surface;
|
|
163
330
|
},
|
|
164
331
|
},
|
|
@@ -174,6 +341,7 @@ export function createCliCommands(deps) {
|
|
|
174
341
|
runRepairFixture,
|
|
175
342
|
workspaceRoot,
|
|
176
343
|
});
|
|
344
|
+
flush();
|
|
177
345
|
return { ok: true, data };
|
|
178
346
|
},
|
|
179
347
|
},
|
|
@@ -218,6 +386,7 @@ export function createCliCommands(deps) {
|
|
|
218
386
|
description: "T1.2.8 — probe host capabilities and persist report (static unknown adapter in CLI context)",
|
|
219
387
|
execute: async (input) => {
|
|
220
388
|
const surface = await Promise.resolve(opsRouter.dispatch("capability_probe", input));
|
|
389
|
+
flush();
|
|
221
390
|
return surface;
|
|
222
391
|
},
|
|
223
392
|
},
|
|
@@ -226,6 +395,7 @@ export function createCliCommands(deps) {
|
|
|
226
395
|
description: "T3.3.2 — run near-real connector smoke (sentinel Moltbook + EvoMap, no live HTTP)",
|
|
227
396
|
execute: async (input) => {
|
|
228
397
|
const surface = await Promise.resolve(opsRouter.dispatch("near_real_smoke", input));
|
|
398
|
+
flush();
|
|
229
399
|
return surface;
|
|
230
400
|
},
|
|
231
401
|
},
|
|
@@ -249,6 +419,7 @@ export function createCliCommands(deps) {
|
|
|
249
419
|
? input.workspaceRoot
|
|
250
420
|
: undefined,
|
|
251
421
|
});
|
|
422
|
+
flush();
|
|
252
423
|
return result;
|
|
253
424
|
},
|
|
254
425
|
},
|
|
@@ -261,10 +432,11 @@ export function createCliCommands(deps) {
|
|
|
261
432
|
},
|
|
262
433
|
},
|
|
263
434
|
{
|
|
264
|
-
name: "
|
|
265
|
-
description: "T1.
|
|
435
|
+
name: "credential",
|
|
436
|
+
description: "T1.4.1 — inspect or verify credential health without exposing plaintext",
|
|
266
437
|
execute: async (input) => {
|
|
267
|
-
const surface = await Promise.resolve(opsRouter.dispatch("
|
|
438
|
+
const surface = await Promise.resolve(opsRouter.dispatch("credential", input));
|
|
439
|
+
flush();
|
|
268
440
|
return surface;
|
|
269
441
|
},
|
|
270
442
|
},
|
|
@@ -273,6 +445,16 @@ export function createCliCommands(deps) {
|
|
|
273
445
|
description: "T1.2.3 — dry-run test a connector by platformId (default dry-run)",
|
|
274
446
|
execute: async (input) => {
|
|
275
447
|
const surface = await Promise.resolve(opsRouter.dispatch("connector_test", input));
|
|
448
|
+
flush();
|
|
449
|
+
return surface;
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: "connector_status",
|
|
454
|
+
description: "T1.2.5 — show connector inventory status, trust, and health conflicts",
|
|
455
|
+
execute: async (input) => {
|
|
456
|
+
const surface = await Promise.resolve(opsRouter.dispatch("connector_status", input));
|
|
457
|
+
flush();
|
|
276
458
|
return surface;
|
|
277
459
|
},
|
|
278
460
|
},
|
|
@@ -292,6 +474,7 @@ export function createCliCommands(deps) {
|
|
|
292
474
|
description: "T1.2.4 — owner-governed goal operations: set, list, accept, reject",
|
|
293
475
|
execute: async (input) => {
|
|
294
476
|
const surface = await Promise.resolve(opsRouter.dispatch("goal", input));
|
|
477
|
+
flush();
|
|
295
478
|
return surface;
|
|
296
479
|
},
|
|
297
480
|
},
|
package/runtime/cli/index.js
CHANGED
|
@@ -290,6 +290,8 @@ export function createCommandRouter(options = {}) {
|
|
|
290
290
|
readModels: runtime.readModels,
|
|
291
291
|
actionBridge: runtime.actionBridge,
|
|
292
292
|
opsRouter,
|
|
293
|
+
stateDb: runtime.stateDb,
|
|
294
|
+
observabilityDb: runtime.observabilityDb,
|
|
293
295
|
});
|
|
294
296
|
return {
|
|
295
297
|
commands,
|
|
@@ -1144,24 +1144,30 @@ export function createOpsRouter(deps) {
|
|
|
1144
1144
|
};
|
|
1145
1145
|
return envelope;
|
|
1146
1146
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1147
|
+
let fromVersion = typeof input?.from === "string" ? input.from : "";
|
|
1148
|
+
let toVersion = typeof input?.to === "string" ? input.to : "";
|
|
1149
1149
|
if (!fromVersion || !toVersion) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1150
|
+
// Auto-resolve the two most recent narrative timeline versions when not provided.
|
|
1151
|
+
const recent = await deps.narrativeTimelineDeps.stateMemoryPort.listNarrativeTimeline(new Date(0).toISOString(), new Date().toISOString(), { limit: 2 });
|
|
1152
|
+
if (recent.length < 2) {
|
|
1153
|
+
const envelope = {
|
|
1154
|
+
ok: false,
|
|
1155
|
+
command: "narrative:diff",
|
|
1156
|
+
runtimeMode: "workspace_full_runtime",
|
|
1157
|
+
surfaceMode: "cli",
|
|
1158
|
+
generatedAt,
|
|
1159
|
+
error: {
|
|
1160
|
+
code: "NARRATIVE_DIFF_REQUIRES_TWO_VERSIONS",
|
|
1161
|
+
message: `narrative:diff requires at least two timeline versions; found ${recent.length}. Pass explicit 'from' and 'to', or run snapshot:capture twice.`,
|
|
1162
|
+
nextStep: "run_snapshot_capture_twice_or_pass_from_and_to",
|
|
1163
|
+
},
|
|
1164
|
+
warnings: [],
|
|
1165
|
+
sourceRefs: [],
|
|
1166
|
+
};
|
|
1167
|
+
return envelope;
|
|
1168
|
+
}
|
|
1169
|
+
fromVersion = recent[1].version;
|
|
1170
|
+
toVersion = recent[0].version;
|
|
1165
1171
|
}
|
|
1166
1172
|
try {
|
|
1167
1173
|
const diff = await queryNarrativeDiff(fromVersion, toVersion, deps.narrativeTimelineDeps);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NormalizedEvidenceContent — Cross-platform content-bearing evidence envelope.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Map arbitrary connector read payloads into a stable, source-backed
|
|
5
|
+
* summary structure that perception, Quiet, and Dream can consume. This is a
|
|
6
|
+
* schema boundary, not a judgment layer.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/connector-system.md`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/perception-judgment-system.md`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - none (pure extraction)
|
|
14
|
+
*
|
|
15
|
+
* Boundary:
|
|
16
|
+
* - Does not classify sensitivity.
|
|
17
|
+
* - Does not redact; callers run redaction separately.
|
|
18
|
+
* - Does not persist; callers write EvidenceItem.
|
|
19
|
+
* - Preserves raw values in canonicalText/excerpt so downstream can decide what to keep.
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/connectors/normalized-evidence-content.test.ts
|
|
22
|
+
*/
|
|
23
|
+
export declare const NORMALIZED_EVIDENCE_SCHEMA_VERSION = 1;
|
|
24
|
+
export type EvidenceSourceKind = "post" | "comment" | "profile" | "task" | "event" | "game_state" | "notification" | "document" | "unknown";
|
|
25
|
+
export type SummaryProducer = "connector_rules" | "model_assist" | "operator_supplied";
|
|
26
|
+
export interface EvidenceActor {
|
|
27
|
+
id?: string;
|
|
28
|
+
displayName?: string;
|
|
29
|
+
role?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface NormalizedEvidenceContent {
|
|
32
|
+
schemaVersion: typeof NORMALIZED_EVIDENCE_SCHEMA_VERSION;
|
|
33
|
+
sourceKind: EvidenceSourceKind;
|
|
34
|
+
platformId: string;
|
|
35
|
+
capabilityId: string;
|
|
36
|
+
externalId?: string;
|
|
37
|
+
title?: string;
|
|
38
|
+
summary: string;
|
|
39
|
+
excerpt?: string;
|
|
40
|
+
canonicalText?: string;
|
|
41
|
+
actor?: EvidenceActor;
|
|
42
|
+
url?: string;
|
|
43
|
+
occurredAt?: string;
|
|
44
|
+
observedAt: string;
|
|
45
|
+
tags?: string[];
|
|
46
|
+
entities?: string[];
|
|
47
|
+
metrics?: Record<string, number | string | boolean>;
|
|
48
|
+
rawContentRef?: string;
|
|
49
|
+
summaryProducer: SummaryProducer;
|
|
50
|
+
}
|
|
51
|
+
export interface ExtractEvidenceOptions {
|
|
52
|
+
platformId: string;
|
|
53
|
+
capabilityId: string;
|
|
54
|
+
observedAt?: string;
|
|
55
|
+
summaryProducer?: SummaryProducer;
|
|
56
|
+
/** Max characters for excerpt. Default 240. */
|
|
57
|
+
excerptMaxChars?: number;
|
|
58
|
+
/** Max characters for canonicalText. Default 2000. */
|
|
59
|
+
canonicalTextMaxChars?: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extract a list of content-bearing evidence items from a connector success payload.
|
|
63
|
+
* Returns empty array when data is not an object/array or contains no extractable items.
|
|
64
|
+
*/
|
|
65
|
+
export declare function extractNormalizedEvidenceItems(data: unknown, options: ExtractEvidenceOptions): NormalizedEvidenceContent[];
|
|
66
|
+
/**
|
|
67
|
+
* Compute a stable content hash for deduplication across connector runs.
|
|
68
|
+
* Prefer externalId-based identity; this hash is the fallback.
|
|
69
|
+
*/
|
|
70
|
+
export declare function computeEvidenceContentHash(content: NormalizedEvidenceContent): string;
|
|
71
|
+
export declare function computeEvidenceContentHashSync(content: NormalizedEvidenceContent): string;
|