@unblocklabs/unblock-memory 0.3.7 → 0.3.8
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/README.md +28 -44
- package/dist/src/config.d.ts +0 -3
- package/dist/src/config.js +11 -9
- package/dist/src/people-evidence.d.ts +7 -0
- package/dist/src/people-evidence.js +50 -17
- package/dist/src/people-hooks.js +62 -25
- package/dist/src/people-refinement.d.ts +8 -66
- package/dist/src/people-refinement.js +10 -249
- package/dist/src/people-store.d.ts +20 -9
- package/dist/src/people-store.js +137 -64
- package/dist/src/people-tools.d.ts +1 -1
- package/dist/src/people-tools.js +66 -20
- package/dist/src/plugin.js +0 -2
- package/openclaw.plugin.json +6 -14
- package/package.json +1 -1
- package/dist/src/people-cli.d.ts +0 -4
- package/dist/src/people-cli.js +0 -47
package/README.md
CHANGED
|
@@ -139,57 +139,41 @@ directory.
|
|
|
139
139
|
|
|
140
140
|
PeopleSQL is an optional agent-local people store. When `people.enabled` is
|
|
141
141
|
true, incoming Slack messages with a canonical agent session key and exact
|
|
142
|
-
account and sender IDs create or refresh
|
|
142
|
+
account and sender IDs create or refresh an injection-enabled person record.
|
|
143
143
|
Incomplete Slack identities create a bounded, deduplicated todo without storing
|
|
144
144
|
message content. Other channels are ignored.
|
|
145
145
|
|
|
146
|
-
PeopleSQL registers three
|
|
146
|
+
PeopleSQL registers three tools when enabled:
|
|
147
147
|
|
|
148
|
-
- `memory_people_inspect`
|
|
149
|
-
|
|
148
|
+
- `memory_people_inspect` reads one exact person, bounded actionable todos, or
|
|
149
|
+
the next person's unseen exact-attributed interaction evidence;
|
|
150
|
+
- `memory_people_update` replaces or deletes dossiers, consumes evidence,
|
|
151
|
+
toggles one person's injection, and manages company, todo, deletion, or
|
|
150
152
|
restoration state; and
|
|
151
|
-
- `memory_people_sync`
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Codex refinement command. Soft-deleted people can be restored explicitly; restoration
|
|
160
|
-
leaves both policies disabled. The sync is bounded to
|
|
161
|
-
200 normalized directory entries per call and is safe to rerun. With pinned
|
|
162
|
-
OpenClaw `2026.8.1-beta.3`, the directory contract supplies ID, name, and handle;
|
|
163
|
-
Unblock Memory ignores raw provider payloads. Slack requires the
|
|
153
|
+
- the optional `memory_people_sync` enriches one active OpenClaw Slack account;
|
|
154
|
+
its tool input accepts an account ID, not a token.
|
|
155
|
+
|
|
156
|
+
The inspect and update tools are part of the normal agent tool surface; they do
|
|
157
|
+
not depend on sender-owner authorization. Directory sync remains optional and
|
|
158
|
+
may need to be allowed explicitly. The sync is bounded to
|
|
159
|
+
200 normalized directory entries per call and is safe to rerun. Unblock Memory
|
|
160
|
+
keeps only normalized ID, name, handle, and avatar fields. Slack requires the
|
|
164
161
|
`users:read` scope.
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
ephemeral, read-only `codex exec` with structured output. It validates the
|
|
181
|
-
complete result set, person IDs, dossier schema, and evidence locators before
|
|
182
|
-
writing each dossier transactionally. Refinement uses exact-attributed session
|
|
183
|
-
evidence; additional evidence sources can be added when they are implemented.
|
|
184
|
-
The host running the command must already have working Codex CLI authentication.
|
|
185
|
-
The plugin performs no model call on the prompt-injection path.
|
|
186
|
-
|
|
187
|
-
Set both `people.whisperer.enabled` and the person's injection policy to enable
|
|
188
|
-
prompt context. The plugin then prepends only that exact person's stored dossier
|
|
189
|
-
blurb, bounded by `maxChars`, once per session. Unknown, unavailable, or
|
|
190
|
-
incomplete identities produce no context. This non-bundled prompt hook requires
|
|
191
|
-
`plugins.entries.unblock-memory.hooks.allowConversationAccess: true` and remains
|
|
192
|
-
subject to OpenClaw's `allowPromptInjection` policy.
|
|
163
|
+
The agent owns dossier generation and refresh. A cron or isolated agent session
|
|
164
|
+
can repeatedly inspect `refinement_next`, synthesize a dossier from its current
|
|
165
|
+
state and bounded unseen interaction windows, then call `replace_dossier` with
|
|
166
|
+
the evidence locators it consumed. Dossier replacement and evidence consumption
|
|
167
|
+
commit atomically; a no-change run may consume evidence without replacing the
|
|
168
|
+
dossier. The plugin performs no model call for refinement or prompt injection.
|
|
169
|
+
|
|
170
|
+
Set `people.whisperer.enabled` to inject context. For each exact Slack sender,
|
|
171
|
+
the plugin prepends that person's stored dossier blurb, bounded by `maxChars`,
|
|
172
|
+
once per `(Slack thread, person)`. Receipts are durable across retries and
|
|
173
|
+
Gateway restarts, while different people in one thread are handled independently.
|
|
174
|
+
Unthreaded DMs use their OpenClaw session as the conversational scope. Unknown,
|
|
175
|
+
unavailable, disabled, or dossierless people produce no context. Injection
|
|
176
|
+
remains subject to OpenClaw's `allowPromptInjection` policy.
|
|
193
177
|
|
|
194
178
|
Use `sessionFilter` to restrict session results by metadata while leaving file
|
|
195
179
|
corpora searchable. Supported fields are `startedFrom` and `startedTo`
|
package/dist/src/config.d.ts
CHANGED
package/dist/src/config.js
CHANGED
|
@@ -10,7 +10,6 @@ export const DEFAULT_CORPORA = [
|
|
|
10
10
|
];
|
|
11
11
|
export const DEFAULT_PEOPLE_CONFIG = {
|
|
12
12
|
enabled: false,
|
|
13
|
-
refinement: { maxPeoplePerRun: 10 },
|
|
14
13
|
whisperer: { enabled: false, maxChars: 1200 },
|
|
15
14
|
todos: { maxOpen: 1000 },
|
|
16
15
|
};
|
|
@@ -119,12 +118,18 @@ function resolvePeople(value) {
|
|
|
119
118
|
const enabled = people.enabled ?? false;
|
|
120
119
|
if (typeof enabled !== "boolean")
|
|
121
120
|
throw new Error("unblock-memory people.enabled must be a boolean");
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
// Accepted only so existing installations can upgrade without first rewriting config.
|
|
122
|
+
const legacyRefinement = people.refinement;
|
|
123
|
+
if (legacyRefinement !== undefined) {
|
|
124
|
+
if (!legacyRefinement ||
|
|
125
|
+
typeof legacyRefinement !== "object" ||
|
|
126
|
+
Array.isArray(legacyRefinement)) {
|
|
127
|
+
throw new Error("unblock-memory people.refinement must be an object");
|
|
128
|
+
}
|
|
129
|
+
const legacy = legacyRefinement;
|
|
130
|
+
assertOnlyKeys(legacy, ["maxPeoplePerRun"], "people.refinement");
|
|
131
|
+
positiveInteger(legacy.maxPeoplePerRun, 10, "people.refinement.maxPeoplePerRun", 50);
|
|
125
132
|
}
|
|
126
|
-
const refinementRecord = refinement;
|
|
127
|
-
assertOnlyKeys(refinementRecord, ["maxPeoplePerRun"], "people.refinement");
|
|
128
133
|
const whisperer = people.whisperer ?? {};
|
|
129
134
|
if (!whisperer || typeof whisperer !== "object" || Array.isArray(whisperer)) {
|
|
130
135
|
throw new Error("unblock-memory people.whisperer must be an object");
|
|
@@ -143,9 +148,6 @@ function resolvePeople(value) {
|
|
|
143
148
|
assertOnlyKeys(todosRecord, ["maxOpen"], "people.todos");
|
|
144
149
|
return {
|
|
145
150
|
enabled,
|
|
146
|
-
refinement: {
|
|
147
|
-
maxPeoplePerRun: positiveInteger(refinementRecord.maxPeoplePerRun, DEFAULT_PEOPLE_CONFIG.refinement.maxPeoplePerRun, "people.refinement.maxPeoplePerRun", 50),
|
|
148
|
-
},
|
|
149
151
|
whisperer: {
|
|
150
152
|
enabled: whispererEnabled,
|
|
151
153
|
maxChars: positiveInteger(whispererRecord.maxChars, DEFAULT_PEOPLE_CONFIG.whisperer.maxChars, "people.whisperer.maxChars", 4000),
|
|
@@ -3,6 +3,12 @@ export type PersonSessionEvidence = {
|
|
|
3
3
|
locator: string;
|
|
4
4
|
observedAt: string;
|
|
5
5
|
text: string;
|
|
6
|
+
context: Array<{
|
|
7
|
+
locator: string;
|
|
8
|
+
role: string;
|
|
9
|
+
text: string;
|
|
10
|
+
senderId?: string;
|
|
11
|
+
}>;
|
|
6
12
|
};
|
|
7
13
|
export declare function readPersonSessionEvidence(params: {
|
|
8
14
|
databasePath: string;
|
|
@@ -11,4 +17,5 @@ export declare function readPersonSessionEvidence(params: {
|
|
|
11
17
|
externalId: string;
|
|
12
18
|
limit?: number;
|
|
13
19
|
maxMessageChars?: number;
|
|
20
|
+
excludeLocators?: ReadonlySet<string>;
|
|
14
21
|
}): PersonSessionEvidence[];
|
|
@@ -42,9 +42,9 @@ export function readPersonSessionEvidence(params) {
|
|
|
42
42
|
meta.agent_id !== params.agentId) {
|
|
43
43
|
throw new Error("unsupported or mismatched OpenClaw agent database");
|
|
44
44
|
}
|
|
45
|
-
const rows = db
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const rows = db.prepare(`
|
|
46
|
+
SELECT events.session_id, active.active_position, active.event_seq,
|
|
47
|
+
events.event_json, events.created_at
|
|
48
48
|
FROM session_transcript_active_events AS active
|
|
49
49
|
JOIN transcript_events AS events
|
|
50
50
|
ON events.session_id = active.session_id AND events.seq = active.event_seq
|
|
@@ -57,24 +57,57 @@ export function readPersonSessionEvidence(params) {
|
|
|
57
57
|
AND json_extract(events.event_json, '$.message.role') = 'user'
|
|
58
58
|
AND json_extract(events.event_json, '$.message.__openclaw.senderId') = ?
|
|
59
59
|
ORDER BY events.created_at DESC, events.session_id, active.active_position DESC
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
`);
|
|
61
|
+
const contextStatement = db.prepare(`
|
|
62
|
+
SELECT active.event_seq, events.event_json
|
|
63
|
+
FROM session_transcript_active_events AS active
|
|
64
|
+
JOIN transcript_events AS events
|
|
65
|
+
ON events.session_id = active.session_id AND events.seq = active.event_seq
|
|
66
|
+
WHERE active.session_id = ?
|
|
67
|
+
AND active.message_position IS NOT NULL
|
|
68
|
+
AND active.active_position BETWEEN ? AND ?
|
|
69
|
+
AND json_extract(events.event_json, '$.type') = 'message'
|
|
70
|
+
ORDER BY active.active_position
|
|
71
|
+
`);
|
|
72
|
+
const evidence = [];
|
|
73
|
+
for (const row of rows.iterate(params.accountScope, params.externalId)) {
|
|
74
|
+
const locator = `session:${row.session_id}:event:${row.event_seq}`;
|
|
75
|
+
if (params.excludeLocators?.has(locator))
|
|
76
|
+
continue;
|
|
64
77
|
const event = record(JSON.parse(row.event_json));
|
|
65
78
|
const message = record(event?.message);
|
|
66
79
|
const text = messageText(message?.content);
|
|
67
80
|
if (!event || !message || !text)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
continue;
|
|
82
|
+
const context = contextStatement
|
|
83
|
+
.all(row.session_id, row.active_position - 1, row.active_position + 2)
|
|
84
|
+
.flatMap((contextRow) => {
|
|
85
|
+
const candidate = contextRow;
|
|
86
|
+
const contextEvent = record(JSON.parse(candidate.event_json));
|
|
87
|
+
const contextMessage = record(contextEvent?.message);
|
|
88
|
+
const contextText = messageText(contextMessage?.content);
|
|
89
|
+
if (!contextMessage || !contextText || typeof contextMessage.role !== "string")
|
|
90
|
+
return [];
|
|
91
|
+
const metadata = record(contextMessage.__openclaw);
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
locator: `session:${row.session_id}:event:${candidate.event_seq}`,
|
|
95
|
+
role: contextMessage.role,
|
|
96
|
+
text: contextText.slice(0, maxMessageChars),
|
|
97
|
+
...(typeof metadata?.senderId === "string" ? { senderId: metadata.senderId } : {}),
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
});
|
|
101
|
+
evidence.push({
|
|
102
|
+
source: "session",
|
|
103
|
+
locator,
|
|
104
|
+
observedAt: evidenceTimestamp(event, row.created_at),
|
|
105
|
+
text: text.slice(0, maxMessageChars),
|
|
106
|
+
context,
|
|
107
|
+
});
|
|
108
|
+
if (evidence.length === limit)
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
78
111
|
db.exec("COMMIT");
|
|
79
112
|
return evidence;
|
|
80
113
|
}
|
package/dist/src/people-hooks.js
CHANGED
|
@@ -2,6 +2,14 @@ import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
|
2
2
|
function nonBlank(value) {
|
|
3
3
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
4
4
|
}
|
|
5
|
+
function identifier(value) {
|
|
6
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
7
|
+
return String(value);
|
|
8
|
+
return nonBlank(value);
|
|
9
|
+
}
|
|
10
|
+
function promptIdentityKey(sessionKey, accountScope, externalId) {
|
|
11
|
+
return JSON.stringify([sessionKey, accountScope, externalId]);
|
|
12
|
+
}
|
|
5
13
|
function observedAt(timestamp) {
|
|
6
14
|
if (timestamp === undefined || !Number.isFinite(timestamp))
|
|
7
15
|
return undefined;
|
|
@@ -13,18 +21,41 @@ export function renderPeopleWhisper(blurb, maxChars) {
|
|
|
13
21
|
return normalized ? normalized.slice(0, maxChars) : undefined;
|
|
14
22
|
}
|
|
15
23
|
export function registerPeopleHooks(api, stores, config) {
|
|
24
|
+
const threadByRun = new Map();
|
|
25
|
+
const pendingThreadByIdentity = new Map();
|
|
16
26
|
api.on("message_received", (event, context) => {
|
|
17
27
|
if (context.channelId !== "slack")
|
|
18
28
|
return;
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
29
|
+
const parsed = parseAgentSessionKey(context.sessionKey);
|
|
30
|
+
if (!parsed)
|
|
21
31
|
return;
|
|
22
32
|
const accountScope = nonBlank(context.accountId);
|
|
23
33
|
const externalId = nonBlank(context.senderId);
|
|
34
|
+
const conversationId = nonBlank(context.conversationId);
|
|
35
|
+
const runId = nonBlank(event.runId) ?? nonBlank(context.runId);
|
|
36
|
+
const sessionKey = nonBlank(context.sessionKey);
|
|
37
|
+
if (config.whisperer.enabled && accountScope && externalId && conversationId && sessionKey) {
|
|
38
|
+
const threadRootId = identifier(event.threadId) ?? nonBlank(event.replyToId);
|
|
39
|
+
const rootMessageId = threadRootId ?? nonBlank(event.messageId);
|
|
40
|
+
const threadKey = conversationId.startsWith("D") && threadRootId === undefined
|
|
41
|
+
? `slack:${accountScope}:${conversationId}:session:${sessionKey}`
|
|
42
|
+
: rootMessageId
|
|
43
|
+
? `slack:${accountScope}:${conversationId}:${rootMessageId}`
|
|
44
|
+
: undefined;
|
|
45
|
+
if (threadKey) {
|
|
46
|
+
const identityKey = promptIdentityKey(sessionKey, accountScope, externalId);
|
|
47
|
+
if (runId) {
|
|
48
|
+
pendingThreadByIdentity.delete(identityKey);
|
|
49
|
+
threadByRun.set(runId, threadKey);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
pendingThreadByIdentity.set(identityKey, threadKey);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
24
56
|
try {
|
|
25
|
-
const store = stores.get(agentId);
|
|
57
|
+
const store = stores.get(parsed.agentId);
|
|
26
58
|
if (!accountScope || !externalId) {
|
|
27
|
-
const conversationId = nonBlank(context.conversationId);
|
|
28
59
|
store.upsertTodo({
|
|
29
60
|
deduplicationKey: `incomplete-slack-identity:${accountScope ?? "missing"}:${externalId ?? "missing"}:` +
|
|
30
61
|
`${conversationId ?? "unknown"}`,
|
|
@@ -52,50 +83,56 @@ export function registerPeopleHooks(api, stores, config) {
|
|
|
52
83
|
});
|
|
53
84
|
if (!config.whisperer.enabled)
|
|
54
85
|
return;
|
|
55
|
-
const injectedBySession = new Map();
|
|
56
86
|
api.on("before_prompt_build", (_event, context) => {
|
|
57
87
|
if (context.trigger !== "user" || context.messageProvider !== "slack")
|
|
58
88
|
return;
|
|
59
|
-
const
|
|
89
|
+
const sessionKey = nonBlank(context.sessionKey);
|
|
90
|
+
const parsed = parseAgentSessionKey(sessionKey);
|
|
60
91
|
const accountScope = nonBlank(context.accountId);
|
|
61
92
|
const externalId = nonBlank(context.senderId);
|
|
62
|
-
const sessionScope = nonBlank(context.sessionId) ?? nonBlank(context.sessionKey);
|
|
63
93
|
const runId = nonBlank(context.runId);
|
|
64
|
-
if (!
|
|
94
|
+
if (!sessionKey || !parsed || !accountScope || !externalId || !runId)
|
|
95
|
+
return;
|
|
96
|
+
const identityKey = promptIdentityKey(sessionKey, accountScope, externalId);
|
|
97
|
+
const pendingThreadKey = pendingThreadByIdentity.get(identityKey);
|
|
98
|
+
const threadKey = threadByRun.get(runId) ?? pendingThreadKey;
|
|
99
|
+
if (!threadKey)
|
|
65
100
|
return;
|
|
101
|
+
if (pendingThreadKey) {
|
|
102
|
+
pendingThreadByIdentity.delete(identityKey);
|
|
103
|
+
threadByRun.set(runId, pendingThreadKey);
|
|
104
|
+
}
|
|
66
105
|
try {
|
|
67
106
|
const store = stores.get(parsed.agentId);
|
|
68
107
|
const person = store.findPersonByIdentity("slack", accountScope, externalId);
|
|
69
108
|
if (!person || person.status !== "active" || !person.injectionEnabled)
|
|
70
109
|
return;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
110
|
+
const previous = store.getWhisperReceipt(threadKey, person.id);
|
|
111
|
+
if (previous) {
|
|
112
|
+
return previous.runId === runId ? { prependContext: previous.contribution } : undefined;
|
|
113
|
+
}
|
|
75
114
|
const blurb = store.getDossierBlurb(person.id);
|
|
76
115
|
const prependContext = blurb
|
|
77
116
|
? renderPeopleWhisper(blurb, config.whisperer.maxChars)
|
|
78
117
|
: undefined;
|
|
79
118
|
if (!prependContext)
|
|
80
119
|
return;
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return contribution;
|
|
120
|
+
const receipt = store.recordWhisperReceipt({
|
|
121
|
+
threadKey,
|
|
122
|
+
personId: person.id,
|
|
123
|
+
runId,
|
|
124
|
+
contribution: prependContext,
|
|
125
|
+
});
|
|
126
|
+
return receipt.runId === runId ? { prependContext: receipt.contribution } : undefined;
|
|
88
127
|
}
|
|
89
128
|
catch (error) {
|
|
90
129
|
api.logger.warn(`unblock-memory people whisperer lookup failed: ${String(error)}`);
|
|
91
130
|
return;
|
|
92
131
|
}
|
|
93
132
|
});
|
|
94
|
-
api.on("
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
if (context.sessionKey)
|
|
99
|
-
injectedBySession.delete(context.sessionKey);
|
|
133
|
+
api.on("agent_end", (event, context) => {
|
|
134
|
+
const runId = nonBlank(event.runId) ?? nonBlank(context.runId);
|
|
135
|
+
if (runId)
|
|
136
|
+
threadByRun.delete(runId);
|
|
100
137
|
});
|
|
101
138
|
}
|
|
@@ -1,72 +1,14 @@
|
|
|
1
|
-
import { Type } from "typebox";
|
|
2
1
|
import { type PersonSessionEvidence } from "./people-evidence.js";
|
|
3
|
-
import {
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
blurb: Type.TString;
|
|
10
|
-
sections: Type.TArray<Type.TObject<{
|
|
11
|
-
category: Type.TUnion<Type.TLiteral<"role" | "priorities" | "preferences" | "successCriteria" | "workingStyle" | "relationship" | "openLoops">[]>;
|
|
12
|
-
claims: Type.TArray<Type.TObject<{
|
|
13
|
-
statement: Type.TString;
|
|
14
|
-
evidence: Type.TArray<Type.TObject<{
|
|
15
|
-
source: Type.TUnion<[Type.TLiteral<"session">, Type.TLiteral<"memory">, Type.TLiteral<"directory">, Type.TLiteral<"manual">]>;
|
|
16
|
-
locator: Type.TString;
|
|
17
|
-
observedAt: Type.TOptional<Type.TString>;
|
|
18
|
-
}>>;
|
|
19
|
-
epistemicType: Type.TUnion<[Type.TLiteral<"observed">, Type.TLiteral<"reported">, Type.TLiteral<"inferred">, Type.TLiteral<"agent_assessment">]>;
|
|
20
|
-
confidence: Type.TOptional<Type.TUnion<[Type.TLiteral<"low">, Type.TLiteral<"medium">, Type.TLiteral<"high">]>>;
|
|
21
|
-
}>>;
|
|
22
|
-
}>>;
|
|
23
|
-
}>;
|
|
24
|
-
}>>;
|
|
25
|
-
}>;
|
|
26
|
-
type PeopleRefinementInput = {
|
|
27
|
-
people: Array<{
|
|
28
|
-
personId: string;
|
|
29
|
-
displayName: string;
|
|
30
|
-
lastSeenAt: string;
|
|
31
|
-
identities: PersonIdentity[];
|
|
32
|
-
currentDossier?: PersonDossier;
|
|
33
|
-
evidence: PersonSessionEvidence[];
|
|
34
|
-
}>;
|
|
2
|
+
import type { PeopleStore, Person, PersonDossier, PersonIdentity } from "./people-store.js";
|
|
3
|
+
export type PeopleRefinementPacket = {
|
|
4
|
+
person: Person;
|
|
5
|
+
identities: PersonIdentity[];
|
|
6
|
+
currentDossier?: PersonDossier;
|
|
7
|
+
evidence: PersonSessionEvidence[];
|
|
35
8
|
};
|
|
36
|
-
export
|
|
37
|
-
input: PeopleRefinementInput;
|
|
38
|
-
outputSchema: typeof REFINEMENT_OUTPUT_SCHEMA;
|
|
39
|
-
signal?: AbortSignal;
|
|
40
|
-
}) => Promise<unknown>;
|
|
41
|
-
export type PeopleRefinementSummary = {
|
|
42
|
-
status: "ok";
|
|
43
|
-
selected: number;
|
|
44
|
-
refined: number;
|
|
45
|
-
skippedWithoutEvidence: number;
|
|
46
|
-
personIds: string[];
|
|
47
|
-
};
|
|
48
|
-
export declare function refinePeople(params: {
|
|
9
|
+
export declare function nextPeopleRefinement(params: {
|
|
49
10
|
store: PeopleStore;
|
|
50
11
|
agentId: string;
|
|
51
12
|
agentDatabasePath: string;
|
|
52
|
-
maxBlurbChars: number;
|
|
53
|
-
runner: PeopleRefinementRunner;
|
|
54
|
-
candidateLimit?: number;
|
|
55
13
|
evidenceLimit?: number;
|
|
56
|
-
|
|
57
|
-
}): Promise<PeopleRefinementSummary>;
|
|
58
|
-
export type CodexCommandRunner = (params: {
|
|
59
|
-
executable: string;
|
|
60
|
-
args: string[];
|
|
61
|
-
cwd: string;
|
|
62
|
-
input: string;
|
|
63
|
-
env: NodeJS.ProcessEnv;
|
|
64
|
-
signal?: AbortSignal;
|
|
65
|
-
}) => Promise<void>;
|
|
66
|
-
export declare function createCodexPeopleRefinementRunner(runCommand?: CodexCommandRunner, options?: {
|
|
67
|
-
environment?: NodeJS.ProcessEnv;
|
|
68
|
-
timeoutMs?: number;
|
|
69
|
-
maxOutputBytes?: number;
|
|
70
|
-
}): PeopleRefinementRunner;
|
|
71
|
-
export declare const codexPeopleRefinementRunner: PeopleRefinementRunner;
|
|
72
|
-
export {};
|
|
14
|
+
}): PeopleRefinementPacket | undefined;
|