@unblocklabs/unblock-memory 0.3.15 → 0.3.16
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 +198 -0
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +4 -1
- package/dist/src/plugin.js +2 -0
- package/dist/src/response-audit.d.ts +87 -0
- package/dist/src/response-audit.js +193 -0
- package/dist/src/response-config.d.ts +13 -0
- package/dist/src/response-config.js +43 -0
- package/dist/src/response-episodes.d.ts +68 -0
- package/dist/src/response-episodes.js +242 -0
- package/dist/src/response-identity.d.ts +15 -0
- package/dist/src/response-identity.js +34 -0
- package/dist/src/response-judge.d.ts +224 -0
- package/dist/src/response-judge.js +248 -0
- package/dist/src/response-memory.d.ts +8 -0
- package/dist/src/response-memory.js +25 -0
- package/dist/src/response-outcome.d.ts +30 -0
- package/dist/src/response-outcome.js +51 -0
- package/dist/src/response-reviews.d.ts +27 -0
- package/dist/src/response-reviews.js +116 -0
- package/dist/src/response-runtime.d.ts +3 -0
- package/dist/src/response-runtime.js +150 -0
- package/dist/src/response-stages.d.ts +184 -0
- package/dist/src/response-stages.js +38 -0
- package/dist/src/response-store.d.ts +180 -0
- package/dist/src/response-store.js +411 -0
- package/dist/src/response-text.d.ts +6 -0
- package/dist/src/response-text.js +37 -0
- package/dist/src/typesafe-review.d.ts +5 -0
- package/dist/src/typesafe-review.js +6 -5
- package/openclaw.plugin.json +20 -1
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { resolveAgentDir, resolveAgentWorkspaceDir, resolveStateDir } from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
|
|
4
|
+
import { listAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
5
|
+
import { normalizeAgentIdStrict } from "openclaw/plugin-sdk/routing";
|
|
6
|
+
import { resolveSources } from "./sources.js";
|
|
7
|
+
import { auditResponses, responseCohort } from "./response-audit.js";
|
|
8
|
+
import { ResponseAuditStore } from "./response-store.js";
|
|
9
|
+
function dateOption(value, fallback) {
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return fallback;
|
|
12
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value) {
|
|
13
|
+
throw new Error("Dates must be valid YYYY-MM-DD UTC dates");
|
|
14
|
+
}
|
|
15
|
+
return Date.parse(value);
|
|
16
|
+
}
|
|
17
|
+
export function registerResponseAudit(api, config) {
|
|
18
|
+
const options = (cfg, id) => {
|
|
19
|
+
const normalized = normalizeAgentIdStrict(id);
|
|
20
|
+
if (!normalized.ok || !listAgentIds(cfg).includes(normalized.value))
|
|
21
|
+
throw new Error("Unknown response-audit agent");
|
|
22
|
+
const agentId = normalized.value;
|
|
23
|
+
const state = join(resolveStateDir(), "agents", agentId, "unblock-memory");
|
|
24
|
+
return { agentId, config, databasePath: join(resolveAgentDir(cfg, agentId), "openclaw-agent.sqlite"),
|
|
25
|
+
storePath: join(state, "response-audit.sqlite"), indexPath: join(state, "index.sqlite"),
|
|
26
|
+
peoplePath: join(state, "people.sqlite"),
|
|
27
|
+
sources: resolveSources(resolveAgentWorkspaceDir(cfg, agentId), config.corpora.filter(c => c.kind === "files")
|
|
28
|
+
.filter(c => config.responseAudit.memoryCorpora.includes(c.name))) };
|
|
29
|
+
};
|
|
30
|
+
api.registerCli(({ program, config: cfg }) => {
|
|
31
|
+
const root = program.command("memory-responses").description("Operator-only response quality audits and evidence-linked trend reports");
|
|
32
|
+
root.command("audit").option("--agent <id>", "Agent id", "main").option("--dry-run", "Preview bounded coverage without inference or audit writes")
|
|
33
|
+
.action(async (opts) => {
|
|
34
|
+
const result = await auditResponses({ ...options(cfg, opts.agent), dryRun: opts.dryRun });
|
|
35
|
+
console.log(JSON.stringify(result, null, 2));
|
|
36
|
+
if (result.status === "unavailable")
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
});
|
|
39
|
+
root.command("report").option("--agent <id>", "Agent id", "main").option("--episode <id>", "Include one episode's judgments and source event references")
|
|
40
|
+
.option("--since <date>", "Inclusive UTC date YYYY-MM-DD").option("--until <date>", "Exclusive UTC date YYYY-MM-DD")
|
|
41
|
+
.option("--bucket <period>", "day or week", "week").option("--sender <id>", "Human sender ID; requires --account")
|
|
42
|
+
.option("--account <scope>", "Provider account scope; requires --sender").option("--person <id>", "Linked people-store person ID")
|
|
43
|
+
.option("--task-type <type>", "Task classification").option("--model <id>", "Agent model")
|
|
44
|
+
.action((opts) => {
|
|
45
|
+
if (!config.responseAudit.enabled) {
|
|
46
|
+
console.log(JSON.stringify({ status: "disabled" }));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const { storePath } = options(cfg, opts.agent);
|
|
50
|
+
if (!existsSync(storePath)) {
|
|
51
|
+
console.log(JSON.stringify({ status: "not_run" }));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const store = new ResponseAuditStore(storePath);
|
|
55
|
+
if (opts.bucket !== "day" && opts.bucket !== "week") {
|
|
56
|
+
store.close();
|
|
57
|
+
throw new Error("Bucket must be day or week");
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
console.log(JSON.stringify(store.report(responseCohort(config.responseAudit), dateOption(opts.since, Date.now() - config.responseAudit.lookbackDays * 86400_000), opts.episode, { until: dateOption(opts.until, Number.MAX_SAFE_INTEGER), bucket: opts.bucket, senderId: opts.sender,
|
|
61
|
+
accountScope: opts.account, personId: opts.person, taskType: opts.taskType, agentModel: opts.model }), null, 2));
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
store.close();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const withStore = (agent, fn) => {
|
|
68
|
+
if (!config.responseAudit.enabled)
|
|
69
|
+
throw new Error("Response audit is disabled");
|
|
70
|
+
const { storePath } = options(cfg, agent);
|
|
71
|
+
if (!existsSync(storePath))
|
|
72
|
+
throw new Error("Response audit has not run");
|
|
73
|
+
const store = new ResponseAuditStore(storePath);
|
|
74
|
+
try {
|
|
75
|
+
console.log(JSON.stringify(fn(store, responseCohort(config.responseAudit)), null, 2));
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
store.close();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
root.command("tasks").description("Operator-only response reviews; never automatically writes memories or preferences")
|
|
82
|
+
.option("--agent <id>", "Agent id", "main").option("--id <id>", "One task with evidence")
|
|
83
|
+
.action((opts) => withStore(opts.agent, (store, cohort) => {
|
|
84
|
+
const tasks = store.reviews.list(cohort, opts.id);
|
|
85
|
+
return { tasks: tasks.slice(0, 1000), capped: tasks.length > 1000 };
|
|
86
|
+
}));
|
|
87
|
+
root.command("review").requiredOption("--id <id>", "Task ID").requiredOption("--status <status>", "pending, resolved, dismissed or deferred")
|
|
88
|
+
.requiredOption("--reviewer <kind>", "human or agent: provenance, not an authorization grant")
|
|
89
|
+
.requiredOption("--note <text>", "Evidence-based decision; no automatic whisperer write")
|
|
90
|
+
.option("--agent <id>", "Agent id", "main")
|
|
91
|
+
.action((opts) => withStore(opts.agent, (store, cohort) => {
|
|
92
|
+
store.reviews.decide(cohort, opts.id, opts.status, opts.reviewer, opts.note);
|
|
93
|
+
return store.reviews.list(cohort, opts.id);
|
|
94
|
+
}));
|
|
95
|
+
root.command("annotate").option("--agent <id>", "Agent id", "main").requiredOption("--date <date>", "UTC date YYYY-MM-DD")
|
|
96
|
+
.requiredOption("--kind <kind>", "model, prompt, deployment or other").requiredOption("--note <text>", "Known change; correlation is not causation")
|
|
97
|
+
.action((opts) => withStore(opts.agent, store => ({ id: store.reviews.annotate(dateOption(opts.date, NaN), opts.kind, opts.note) })));
|
|
98
|
+
root.command("retry-failed").description("Reset failed stage retries without discarding successful judgments; next audit performs the work")
|
|
99
|
+
.option("--agent <id>", "Agent id", "main")
|
|
100
|
+
.action((opts) => withStore(opts.agent, (store, cohort) => store.retryFailed(cohort)));
|
|
101
|
+
}, { descriptors: [{ name: "memory-responses", description: "Audit human-agent exchanges and report quality signals", hasSubcommands: true }] });
|
|
102
|
+
if (api.registrationMode === "cli-metadata" || !config.responseAudit.enabled || !config.typesafe.enabled || !config.responseAudit.intervalMinutes)
|
|
103
|
+
return;
|
|
104
|
+
let timer, running, lifetime = new AbortController();
|
|
105
|
+
const tick = () => {
|
|
106
|
+
if (running || lifetime.signal.aborted)
|
|
107
|
+
return;
|
|
108
|
+
running = (async () => {
|
|
109
|
+
for (const id of listAgentIds(api.config)) {
|
|
110
|
+
if (lifetime.signal.aborted)
|
|
111
|
+
break;
|
|
112
|
+
try {
|
|
113
|
+
const params = options(api.config, id);
|
|
114
|
+
const store = new ResponseAuditStore(params.storePath);
|
|
115
|
+
let due;
|
|
116
|
+
try {
|
|
117
|
+
due = store.claimScheduled(Date.now(), config.responseAudit.intervalMinutes * 60_000);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
store.close();
|
|
121
|
+
}
|
|
122
|
+
if (!due)
|
|
123
|
+
continue;
|
|
124
|
+
const result = await auditResponses({ ...params, signal: lifetime.signal });
|
|
125
|
+
if (result.status === "unavailable")
|
|
126
|
+
api.logger.warn("unblock-memory response audit unavailable; inspect operator report");
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
api.logger.warn("unblock-memory response audit unavailable");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})().catch(() => { api.logger.warn("unblock-memory response audit unavailable"); }).finally(() => { running = undefined; });
|
|
133
|
+
};
|
|
134
|
+
api.on("gateway_start", () => {
|
|
135
|
+
if (timer)
|
|
136
|
+
clearInterval(timer);
|
|
137
|
+
lifetime = new AbortController();
|
|
138
|
+
// A minute poll honors durable due times without relying on process uptime.
|
|
139
|
+
timer = setInterval(tick, 60_000);
|
|
140
|
+
timer.unref();
|
|
141
|
+
tick(); // New schedules wait one interval; overdue schedules get one bounded attempt.
|
|
142
|
+
});
|
|
143
|
+
api.on("gateway_stop", async () => {
|
|
144
|
+
if (timer)
|
|
145
|
+
clearInterval(timer);
|
|
146
|
+
timer = undefined;
|
|
147
|
+
lifetime.abort();
|
|
148
|
+
await running;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { ResponseEpisode } from "./response-episodes.js";
|
|
2
|
+
import type { ResponseAuditStore } from "./response-store.js";
|
|
3
|
+
export declare function assessResponseStages(store: ResponseAuditStore, cohort: string, e: ResponseEpisode, params: {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
timeoutMs: number;
|
|
6
|
+
signal: AbortSignal;
|
|
7
|
+
}, sentimentEnabled: boolean, usage: {
|
|
8
|
+
stageAttempts: number;
|
|
9
|
+
stageCacheHits: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
retrospective: {
|
|
12
|
+
status: ResponseEpisode["followup"]["status"];
|
|
13
|
+
judgment: import("typebox").Static<import("typebox").TObject<{
|
|
14
|
+
answers: import("typebox").TObject<{
|
|
15
|
+
correction: import("typebox").TObject<{
|
|
16
|
+
type: import("typebox").TLiteral<"noul">;
|
|
17
|
+
noul: import("typebox").TNumber;
|
|
18
|
+
}>;
|
|
19
|
+
deliveryAdmission: import("typebox").TObject<{
|
|
20
|
+
type: import("typebox").TLiteral<"noul">;
|
|
21
|
+
noul: import("typebox").TNumber;
|
|
22
|
+
}>;
|
|
23
|
+
regression: import("typebox").TObject<{
|
|
24
|
+
type: import("typebox").TLiteral<"noul">;
|
|
25
|
+
noul: import("typebox").TNumber;
|
|
26
|
+
}>;
|
|
27
|
+
scopeClarification: import("typebox").TObject<{
|
|
28
|
+
type: import("typebox").TLiteral<"noul">;
|
|
29
|
+
noul: import("typebox").TNumber;
|
|
30
|
+
}>;
|
|
31
|
+
outcome: import("typebox").TObject<{
|
|
32
|
+
type: import("typebox").TLiteral<"choice">;
|
|
33
|
+
choice: import("typebox").TEnum<("unknown" | "reported_shortfall" | "acknowledged_success")[]>;
|
|
34
|
+
confidence: import("typebox").TNumber;
|
|
35
|
+
probabilities: import("typebox").TObject<{
|
|
36
|
+
[k: string]: import("typebox").TNumber;
|
|
37
|
+
}>;
|
|
38
|
+
}>;
|
|
39
|
+
reason: import("typebox").TObject<{
|
|
40
|
+
type: import("typebox").TLiteral<"choice">;
|
|
41
|
+
choice: import("typebox").TEnum<("none_or_unclear" | "missing_requested_work" | "unnecessary_deferral" | "regression" | "incorrect_claim" | "wrong_scope" | "failed_delivery")[]>;
|
|
42
|
+
confidence: import("typebox").TNumber;
|
|
43
|
+
probabilities: import("typebox").TObject<{
|
|
44
|
+
[k: string]: import("typebox").TNumber;
|
|
45
|
+
}>;
|
|
46
|
+
}>;
|
|
47
|
+
}>;
|
|
48
|
+
}>>["answers"] | null;
|
|
49
|
+
};
|
|
50
|
+
quality: {
|
|
51
|
+
taskType: {
|
|
52
|
+
type: "choice";
|
|
53
|
+
choice: "action" | "question" | "artifact" | "discussion" | "other";
|
|
54
|
+
confidence: number;
|
|
55
|
+
probabilities: {
|
|
56
|
+
[x: string]: number;
|
|
57
|
+
[x: number]: number;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
assessability: {
|
|
61
|
+
type: "choice";
|
|
62
|
+
choice: "assessable" | "not_assessable";
|
|
63
|
+
confidence: number;
|
|
64
|
+
probabilities: {
|
|
65
|
+
[x: string]: number;
|
|
66
|
+
[x: number]: number;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
fitAssessability: {
|
|
70
|
+
type: "choice";
|
|
71
|
+
choice: "assessable" | "not_assessable";
|
|
72
|
+
confidence: number;
|
|
73
|
+
probabilities: {
|
|
74
|
+
[x: string]: number;
|
|
75
|
+
[x: number]: number;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
underdelivery: {
|
|
79
|
+
type: "noul";
|
|
80
|
+
noul: number;
|
|
81
|
+
};
|
|
82
|
+
failureReason: {
|
|
83
|
+
type: "choice";
|
|
84
|
+
choice: "none_or_unclear" | "missing_requested_work" | "wrong_deliverable" | "missed_constraint" | "insufficient_answer_depth" | "unnecessary_deferral";
|
|
85
|
+
confidence: number;
|
|
86
|
+
probabilities: {
|
|
87
|
+
[x: string]: number;
|
|
88
|
+
[x: number]: number;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
fulfillment: {
|
|
92
|
+
type: "score";
|
|
93
|
+
confidence: number;
|
|
94
|
+
probabilities: {
|
|
95
|
+
"0": number;
|
|
96
|
+
"1": number;
|
|
97
|
+
"2": number;
|
|
98
|
+
"3": number;
|
|
99
|
+
};
|
|
100
|
+
score: number;
|
|
101
|
+
};
|
|
102
|
+
deliverableFit: {
|
|
103
|
+
type: "score";
|
|
104
|
+
confidence: number;
|
|
105
|
+
probabilities: {
|
|
106
|
+
"0": number;
|
|
107
|
+
"1": number;
|
|
108
|
+
"2": number;
|
|
109
|
+
"3": number;
|
|
110
|
+
};
|
|
111
|
+
score: number;
|
|
112
|
+
};
|
|
113
|
+
consistency: {
|
|
114
|
+
type: "choice";
|
|
115
|
+
choice: "not_assessable" | "consistent" | "contradicted";
|
|
116
|
+
confidence: number;
|
|
117
|
+
probabilities: {
|
|
118
|
+
[x: string]: number;
|
|
119
|
+
[x: number]: number;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
feedback: {
|
|
124
|
+
feedbackType: {
|
|
125
|
+
type: "choice";
|
|
126
|
+
choice: "mixed" | "unrelated" | "unclear" | "acceptance" | "correction" | "continuation";
|
|
127
|
+
confidence: number;
|
|
128
|
+
probabilities: {
|
|
129
|
+
[x: string]: number;
|
|
130
|
+
[x: number]: number;
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
target: {
|
|
134
|
+
type: "choice";
|
|
135
|
+
choice: "delivery" | "mixed" | "unclear" | "current_answer" | "earlier_behavior" | "proactive_action" | "external" | "new_work";
|
|
136
|
+
confidence: number;
|
|
137
|
+
probabilities: {
|
|
138
|
+
[x: string]: number;
|
|
139
|
+
[x: number]: number;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
avoidableRework: {
|
|
143
|
+
type: "noul";
|
|
144
|
+
noul: number;
|
|
145
|
+
};
|
|
146
|
+
repeatedConstraint: {
|
|
147
|
+
type: "noul";
|
|
148
|
+
noul: number;
|
|
149
|
+
};
|
|
150
|
+
memoryGap: {
|
|
151
|
+
type: "noul";
|
|
152
|
+
noul: number;
|
|
153
|
+
};
|
|
154
|
+
} & Partial<{
|
|
155
|
+
sentiment: {
|
|
156
|
+
type: "choice";
|
|
157
|
+
choice: "satisfied" | "dissatisfied" | "mixed" | "neutral" | "unrelated" | "unclear";
|
|
158
|
+
confidence: number;
|
|
159
|
+
probabilities: {
|
|
160
|
+
[x: string]: number;
|
|
161
|
+
[x: number]: number;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
annoyance: {
|
|
165
|
+
type: "noul";
|
|
166
|
+
noul: number;
|
|
167
|
+
};
|
|
168
|
+
frustration: {
|
|
169
|
+
type: "noul";
|
|
170
|
+
noul: number;
|
|
171
|
+
};
|
|
172
|
+
dissatisfactionIntensity: {
|
|
173
|
+
type: "score";
|
|
174
|
+
confidence: number;
|
|
175
|
+
probabilities: {
|
|
176
|
+
"0": number;
|
|
177
|
+
"1": number;
|
|
178
|
+
"2": number;
|
|
179
|
+
"3": number;
|
|
180
|
+
};
|
|
181
|
+
score: number;
|
|
182
|
+
};
|
|
183
|
+
}>;
|
|
184
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { RESPONSE_EXTRACTOR_VERSION } from "./response-episodes.js";
|
|
3
|
+
import { judgeResponse, judgeResponseFollowup, RESPONSE_STAGE_VERSIONS } from "./response-judge.js";
|
|
4
|
+
import { TYPESAFE_REVIEW_MODEL } from "./typesafe-review.js";
|
|
5
|
+
export async function assessResponseStages(store, cohort, e, params, sentimentEnabled, usage) {
|
|
6
|
+
const original = { before: e.before, request: e.request, answer: e.answer, contextLimited: e.contextLimited };
|
|
7
|
+
const feedback = { ...original, feedback: e.feedback };
|
|
8
|
+
const evidence = { quality: original, feedback, sentiment: feedback, retrospective: { ...feedback, followup: e.followup } };
|
|
9
|
+
const keys = Object.fromEntries(Object.keys(evidence).map(stage => [stage,
|
|
10
|
+
createHash("sha256").update(JSON.stringify([RESPONSE_EXTRACTOR_VERSION, TYPESAFE_REVIEW_MODEL, RESPONSE_STAGE_VERSIONS[stage],
|
|
11
|
+
e.id, e.session, e.senderId, e.thread, evidence[stage]])).digest("hex"),
|
|
12
|
+
]));
|
|
13
|
+
const cache = {
|
|
14
|
+
quality: store.stage(cohort, e, "quality", keys.quality),
|
|
15
|
+
feedback: store.stage(cohort, e, "feedback", keys.feedback),
|
|
16
|
+
sentiment: sentimentEnabled ? store.stage(cohort, e, "sentiment", keys.sentiment) : undefined,
|
|
17
|
+
begin(stages) {
|
|
18
|
+
params.signal.throwIfAborted();
|
|
19
|
+
store.stageBegin(stages.map(stage => keys[stage]), Date.now());
|
|
20
|
+
usage.stageAttempts += stages.length;
|
|
21
|
+
},
|
|
22
|
+
save(stage, result) {
|
|
23
|
+
params.signal.throwIfAborted();
|
|
24
|
+
store.stageSave(keys[stage], result, Date.now());
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
usage.stageCacheHits += Number(!!cache.quality) + Number(!!cache.feedback) + Number(!!cache.sentiment);
|
|
28
|
+
const judgment = await judgeResponse(e, params, sentimentEnabled, cache);
|
|
29
|
+
let retrospective = store.stage(cohort, e, "retrospective", keys.retrospective);
|
|
30
|
+
if (retrospective)
|
|
31
|
+
usage.stageCacheHits++;
|
|
32
|
+
if (!retrospective) {
|
|
33
|
+
cache.begin(["retrospective"]);
|
|
34
|
+
retrospective = await judgeResponseFollowup(e, params);
|
|
35
|
+
cache.save("retrospective", retrospective);
|
|
36
|
+
}
|
|
37
|
+
return { ...judgment, retrospective };
|
|
38
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { ResponseEpisode } from "./response-episodes.js";
|
|
2
|
+
import type { ResponseJudgment, ResponseStages, judgeMemoryOpportunity, judgeResponseFollowup } from "./response-judge.js";
|
|
3
|
+
import { responseOutcome } from "./response-outcome.js";
|
|
4
|
+
import type { ResponseHuman } from "./response-identity.js";
|
|
5
|
+
import { ResponseReviews } from "./response-reviews.js";
|
|
6
|
+
export type ResponseResult = ResponseJudgment & {
|
|
7
|
+
references: {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
request: number[];
|
|
10
|
+
answer: number[];
|
|
11
|
+
feedback: number[];
|
|
12
|
+
followup: number[];
|
|
13
|
+
inputHash: string;
|
|
14
|
+
};
|
|
15
|
+
retrospective: Awaited<ReturnType<typeof judgeResponseFollowup>>;
|
|
16
|
+
agentModel: string;
|
|
17
|
+
human?: ResponseHuman;
|
|
18
|
+
contextLimited: boolean;
|
|
19
|
+
memorySearchCalls: number;
|
|
20
|
+
memory: {
|
|
21
|
+
status: "not_requested" | "unavailable" | "checked";
|
|
22
|
+
candidates: Awaited<ReturnType<typeof judgeMemoryOpportunity>>;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export type ResponseReportOptions = {
|
|
26
|
+
until?: number;
|
|
27
|
+
bucket?: "day" | "week";
|
|
28
|
+
senderId?: string;
|
|
29
|
+
accountScope?: string;
|
|
30
|
+
personId?: string;
|
|
31
|
+
taskType?: string;
|
|
32
|
+
agentModel?: string;
|
|
33
|
+
};
|
|
34
|
+
/** Separate operator-only database: not a memory corpus and never injected into agent prompts. */
|
|
35
|
+
export declare class ResponseAuditStore {
|
|
36
|
+
#private;
|
|
37
|
+
readonly reviews: ResponseReviews;
|
|
38
|
+
constructor(path: string);
|
|
39
|
+
/** Claim one bounded scheduled attempt, never replay every missed interval. */
|
|
40
|
+
claimScheduled(now: number, intervalMs: number): boolean;
|
|
41
|
+
acquire(now: number): string | undefined;
|
|
42
|
+
release(token: string): void;
|
|
43
|
+
activeSessions(cohort: string, since: number): string[];
|
|
44
|
+
observe(cohort: string, sessionId: string, episodes: readonly ResponseEpisode[]): void;
|
|
45
|
+
needsJudgment(cohort: string, e: ResponseEpisode, now: number): boolean;
|
|
46
|
+
attempted(cohort: string, e: ResponseEpisode, now: number): void;
|
|
47
|
+
stale(cohort: string, e: ResponseEpisode): void;
|
|
48
|
+
save(cohort: string, e: ResponseEpisode, result: ResponseResult, now: number): void;
|
|
49
|
+
cursor(cohort: string): string;
|
|
50
|
+
advance(cohort: string, cursor: string): void;
|
|
51
|
+
checkpoint(cohort: string, session: string): {
|
|
52
|
+
revision: string;
|
|
53
|
+
coverage: ReturnType<typeof import("./response-episodes.js").responseEpisodes>["coverage"];
|
|
54
|
+
} | undefined;
|
|
55
|
+
checkpointSave(cohort: string, session: string, revision: string, coverage: object): void;
|
|
56
|
+
checkpointForget(cohort: string, session: string): void;
|
|
57
|
+
sessionWork(cohort: string, session: string, since: number, now: number): {
|
|
58
|
+
total: number;
|
|
59
|
+
due: number;
|
|
60
|
+
};
|
|
61
|
+
pendingWork(cohort: string, since: number, now: number): number;
|
|
62
|
+
stage<K extends keyof ResponseStages>(cohort: string, e: ResponseEpisode, stage: K, key: string): ResponseStages[K] | undefined;
|
|
63
|
+
stageBegin(keys: string[], now: number): void;
|
|
64
|
+
stageSave<K extends keyof ResponseStages>(key: string, result: ResponseStages[K], now: number): void;
|
|
65
|
+
retryFailed(cohort: string): {
|
|
66
|
+
stages: number | bigint;
|
|
67
|
+
episodes: number | bigint;
|
|
68
|
+
};
|
|
69
|
+
scan(cohort: string, coverage: object, now: number): void;
|
|
70
|
+
report(cohort: string, since: number, id?: string, options?: ResponseReportOptions): {
|
|
71
|
+
limitations: string[];
|
|
72
|
+
episode?: {
|
|
73
|
+
result: any;
|
|
74
|
+
} | null | undefined;
|
|
75
|
+
cohort: string;
|
|
76
|
+
reportVersion: string;
|
|
77
|
+
reviewPolicy: string;
|
|
78
|
+
bucket: "day" | "week";
|
|
79
|
+
since: number;
|
|
80
|
+
until: number;
|
|
81
|
+
annotations: Record<string, import("node:sqlite").SQLOutputValue>[];
|
|
82
|
+
stages: Record<string, import("node:sqlite").SQLOutputValue>[];
|
|
83
|
+
stageCountsScope: string;
|
|
84
|
+
rubricScope: string;
|
|
85
|
+
coverage: any;
|
|
86
|
+
stored: number;
|
|
87
|
+
reportCapped: boolean;
|
|
88
|
+
failedOrPending: number;
|
|
89
|
+
groups: {
|
|
90
|
+
periodStart: string;
|
|
91
|
+
fulfillmentMean: number | null;
|
|
92
|
+
deliverableFitMean: number | null;
|
|
93
|
+
sentimentNotAssessed: number;
|
|
94
|
+
sentimentUnknown: number;
|
|
95
|
+
sentimentCoverage: number;
|
|
96
|
+
dissatisfactionRate: number | null;
|
|
97
|
+
sentimentUnknownRate: number | null;
|
|
98
|
+
annoyanceRate: number | null;
|
|
99
|
+
frustrationRate: number | null;
|
|
100
|
+
dissatisfactionIntensityMean: number | null;
|
|
101
|
+
observedSuccessRate: number | null;
|
|
102
|
+
observedSuccessRate95Interval: number[] | null;
|
|
103
|
+
outcomeUnknown: number;
|
|
104
|
+
outcomeCoverage: number;
|
|
105
|
+
acknowledgedRate: number;
|
|
106
|
+
reportedShortfallRate: number;
|
|
107
|
+
unknownRate: number;
|
|
108
|
+
underdeliveryRate: number | null;
|
|
109
|
+
underdeliveryRate95Interval: number[] | null;
|
|
110
|
+
reworkRate: number | null;
|
|
111
|
+
reworkRate95Interval: number[] | null;
|
|
112
|
+
smallSample: boolean;
|
|
113
|
+
week: string;
|
|
114
|
+
human: ResponseHuman | null;
|
|
115
|
+
taskType: string;
|
|
116
|
+
agentModel: string;
|
|
117
|
+
evaluated: number;
|
|
118
|
+
assessable: number;
|
|
119
|
+
fitAssessable: number;
|
|
120
|
+
fulfillmentScored: number;
|
|
121
|
+
deliverableFitScored: number;
|
|
122
|
+
feedbackCertain: number;
|
|
123
|
+
accepted: number;
|
|
124
|
+
reworkCertain: number;
|
|
125
|
+
rework: number;
|
|
126
|
+
memoryGap: number;
|
|
127
|
+
dissatisfied: number;
|
|
128
|
+
sentimentAssessed: number;
|
|
129
|
+
sentimentCertain: number;
|
|
130
|
+
emotionAssessed: number;
|
|
131
|
+
annoyed: number;
|
|
132
|
+
frustrated: number;
|
|
133
|
+
annoyanceUncertain: number;
|
|
134
|
+
frustrationUncertain: number;
|
|
135
|
+
dissatisfactionIntensityScored: number;
|
|
136
|
+
underdeliveryCertain: number;
|
|
137
|
+
underdelivery: number;
|
|
138
|
+
failureReasons: Record<string, number>;
|
|
139
|
+
feedbackTargets: Record<string, number>;
|
|
140
|
+
retrospectiveAssessed: number;
|
|
141
|
+
laterCorrections: number;
|
|
142
|
+
deliveryAdmissions: number;
|
|
143
|
+
outcomeKnown: number;
|
|
144
|
+
acknowledgedSuccess: number;
|
|
145
|
+
reportedShortfall: number;
|
|
146
|
+
outcomeReasons: Record<string, number>;
|
|
147
|
+
}[];
|
|
148
|
+
trends: {
|
|
149
|
+
fromWeek: string;
|
|
150
|
+
toWeek: string;
|
|
151
|
+
taskType: string;
|
|
152
|
+
agentModel: string;
|
|
153
|
+
fromPeriod: string;
|
|
154
|
+
toPeriod: string;
|
|
155
|
+
human: ResponseHuman | null;
|
|
156
|
+
dimension: "fulfillment" | "deliverableFit" | "dissatisfactionIntensity" | "acknowledgedRate" | "reportedShortfallRate" | "unknownRate" | "dissatisfactionRate" | "sentimentUnknownRate" | "annoyanceRate" | "frustrationRate";
|
|
157
|
+
beforeN: number;
|
|
158
|
+
afterN: number;
|
|
159
|
+
status: string;
|
|
160
|
+
before: number | null;
|
|
161
|
+
after: number | null;
|
|
162
|
+
beforeEvaluated: number;
|
|
163
|
+
afterEvaluated: number;
|
|
164
|
+
beforeCoverage: number;
|
|
165
|
+
afterCoverage: number;
|
|
166
|
+
coverageChanged: boolean;
|
|
167
|
+
denominator: string;
|
|
168
|
+
scale: string;
|
|
169
|
+
delta: number | null;
|
|
170
|
+
}[];
|
|
171
|
+
examples: {
|
|
172
|
+
id: string;
|
|
173
|
+
episodeAt: number;
|
|
174
|
+
assessedAt: number;
|
|
175
|
+
signals: string[];
|
|
176
|
+
outcome: ReturnType<typeof responseOutcome>;
|
|
177
|
+
}[];
|
|
178
|
+
};
|
|
179
|
+
close(): void;
|
|
180
|
+
}
|