kiokuko-dsh 0.2.27 → 0.2.28
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/dist/dsh/agenticreplay-host.d.ts.map +1 -1
- package/dist/dsh/agenticreplay-host.js +5 -1
- package/dist/dsh/agenticreplay-host.js.map +1 -1
- package/dist/dsh/agenticreplay-libraries.d.ts +1 -0
- package/dist/dsh/agenticreplay-libraries.d.ts.map +1 -1
- package/dist/dsh/agenticreplay-libraries.js.map +1 -1
- package/dist/dsh/agenticreplay-recorder.d.ts.map +1 -1
- package/dist/dsh/agenticreplay-recorder.js +22 -10
- package/dist/dsh/agenticreplay-recorder.js.map +1 -1
- package/dist/dsh/agenticreplay-security.d.ts.map +1 -1
- package/dist/dsh/agenticreplay-security.js +40 -6
- package/dist/dsh/agenticreplay-security.js.map +1 -1
- package/dist/dsh/agenticreplay-types.d.ts +3 -0
- package/dist/dsh/agenticreplay-types.d.ts.map +1 -1
- package/dist/dsh/agenticreplay-types.js.map +1 -1
- package/dist/dsh/coding-workflow/host.d.ts +1 -0
- package/dist/dsh/coding-workflow/host.d.ts.map +1 -1
- package/dist/dsh/coding-workflow/host.js +10 -0
- package/dist/dsh/coding-workflow/host.js.map +1 -1
- package/dist/dsh/coding-workflow/service.d.ts +6 -2
- package/dist/dsh/coding-workflow/service.d.ts.map +1 -1
- package/dist/dsh/coding-workflow/service.js +6 -5
- package/dist/dsh/coding-workflow/service.js.map +1 -1
- package/dist/dsh/core/host.d.ts.map +1 -1
- package/dist/dsh/core/host.js +10 -1
- package/dist/dsh/core/host.js.map +1 -1
- package/dist/dsh/execution-support.d.ts.map +1 -1
- package/dist/dsh/execution-support.js +2 -0
- package/dist/dsh/execution-support.js.map +1 -1
- package/dist/dsh/lifecycle-memory.d.ts +1 -0
- package/dist/dsh/lifecycle-memory.d.ts.map +1 -1
- package/dist/dsh/lifecycle-memory.js +7 -0
- package/dist/dsh/lifecycle-memory.js.map +1 -1
- package/dist/dsh/memory-application.d.ts +9 -9
- package/dist/dsh/memory-application.d.ts.map +1 -1
- package/dist/dsh/memory-application.js +448 -147
- package/dist/dsh/memory-application.js.map +1 -1
- package/dist/dsh/on-demand-intake.d.ts +13 -0
- package/dist/dsh/on-demand-intake.d.ts.map +1 -1
- package/dist/dsh/on-demand-intake.js +30 -7
- package/dist/dsh/on-demand-intake.js.map +1 -1
- package/dist/dsh/repository-learning-reflector.d.ts +58 -0
- package/dist/dsh/repository-learning-reflector.d.ts.map +1 -0
- package/dist/dsh/repository-learning-reflector.js +230 -0
- package/dist/dsh/repository-learning-reflector.js.map +1 -0
- package/dist/dsh/repository-learning.d.ts +34 -0
- package/dist/dsh/repository-learning.d.ts.map +1 -0
- package/dist/dsh/repository-learning.js +512 -0
- package/dist/dsh/repository-learning.js.map +1 -0
- package/dist/locale/en.json +1 -1
- package/dist/locale/ja.json +1 -1
- package/dist/locale/ko.json +1 -1
- package/dist/locale/zh-CN.json +1 -1
- package/dist/memory/forget.d.ts +2 -2
- package/dist/memory/forget.d.ts.map +1 -1
- package/dist/memory/forget.js +175 -74
- package/dist/memory/forget.js.map +1 -1
- package/dist/memory/repository-learning.d.ts +106 -0
- package/dist/memory/repository-learning.d.ts.map +1 -0
- package/dist/memory/repository-learning.js +348 -0
- package/dist/memory/repository-learning.js.map +1 -0
- package/docs/agenticreplay-recording.md +20 -2
- package/docs/on-demand-intake.md +8 -2
- package/docs/repository-learning.md +109 -0
- package/migrations/048_repository_learning.sql +65 -0
- package/package.json +5 -1
|
@@ -1,62 +1,123 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
z.object({ action: z.literal(
|
|
20
|
-
z
|
|
1
|
+
import { detectRepositoryRoot } from "../repository/detect-root.js";
|
|
2
|
+
import { readRepositoryLearning, resetRepositoryLearning, } from "../memory/repository-learning.js";
|
|
3
|
+
import { forgetMemory } from "../memory/forget.js";
|
|
4
|
+
import { explainMemory, renderMemoryExplanation } from "../memory/explain.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { realpathSync } from "node:fs";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { beginMemoryExecution, completeMemoryExecution, memoryApplicationReviewSchema, memoryApplicationStatus, } from "../memory/application.js";
|
|
9
|
+
import { autoGlobalizationStatus } from "../memory/auto-globalization.js";
|
|
10
|
+
import { MemoryTimeConstraint } from "../memory/retrieval-contracts.js";
|
|
11
|
+
import { isOwnedExecutionCall } from "./owned-execution.js";
|
|
12
|
+
import { authorizeTaskLease, } from "./task-state.js";
|
|
13
|
+
import { captureExecutionTaskLease } from "./task-authority-context.js";
|
|
14
|
+
import { withImmediateTransaction } from "../db/transaction.js";
|
|
15
|
+
import { correctionDecisionSchema, checkCorrectionDecision, literalVersionGate, recordCorrectionObservation, } from "./correction-decision.js";
|
|
16
|
+
import { canonicalContentHash } from "../serialization/validate.js";
|
|
17
|
+
import { KiokukoError } from "../errors.js";
|
|
18
|
+
const inputSchema = z.discriminatedUnion("action", [
|
|
19
|
+
z.object({ action: z.literal("status") }).strict(),
|
|
20
|
+
z
|
|
21
|
+
.object({
|
|
22
|
+
action: z.literal("review"),
|
|
23
|
+
review: memoryApplicationReviewSchema,
|
|
24
|
+
})
|
|
25
|
+
.strict(),
|
|
26
|
+
z
|
|
27
|
+
.object({
|
|
28
|
+
action: z.literal("review_batch"),
|
|
29
|
+
reviews: z.array(memoryApplicationReviewSchema).min(1).max(32),
|
|
30
|
+
})
|
|
31
|
+
.strict(),
|
|
32
|
+
z
|
|
33
|
+
.object({
|
|
34
|
+
action: z.literal("refresh"),
|
|
35
|
+
query: z.string().trim().min(1).max(4000),
|
|
36
|
+
timeConstraint: MemoryTimeConstraint.optional(),
|
|
37
|
+
})
|
|
38
|
+
.strict(),
|
|
21
39
|
]);
|
|
22
40
|
// Model providers require an object at the root of every tool schema. Keep
|
|
23
41
|
// action-specific validation in inputSchema after the native tool call arrives.
|
|
24
|
-
const transportSchema = z
|
|
25
|
-
|
|
42
|
+
const transportSchema = z
|
|
43
|
+
.object({
|
|
44
|
+
action: z.enum(["status", "review", "review_batch", "refresh"]),
|
|
26
45
|
review: memoryApplicationReviewSchema.optional(),
|
|
27
46
|
reviews: z.array(memoryApplicationReviewSchema).min(1).max(32).optional(),
|
|
28
47
|
query: z.string().trim().min(1).max(4000).optional(),
|
|
29
48
|
timeConstraint: MemoryTimeConstraint.optional(),
|
|
30
|
-
})
|
|
31
|
-
|
|
49
|
+
})
|
|
50
|
+
.strict();
|
|
51
|
+
export const MEMORY_APPLICATION_GUIDANCE = "Memory references and past application status are optional advisory data. Missing, stale or failed memory evidence never blocks tools or task completion. Native permissions, explicit Stop and actual task verification remain authoritative. correction_check checks compiler compatibility and completion judgments against host-delivered corrections and actual verification; self-reported use is not proof. A supported literal conflicting version-only write asks a scoped native question. New memory saving happens only after task completion, permission to organize, and approval of the exact shown revision.";
|
|
32
52
|
// Exact native read tools only; never classify arbitrary shell strings as read-only.
|
|
33
|
-
const READ_TOOLS = new Set([
|
|
34
|
-
|
|
53
|
+
const READ_TOOLS = new Set([
|
|
54
|
+
"Read",
|
|
55
|
+
"Glob",
|
|
56
|
+
"Grep",
|
|
57
|
+
"LS",
|
|
58
|
+
"Skill",
|
|
59
|
+
"read",
|
|
60
|
+
"read_file",
|
|
61
|
+
"glob",
|
|
62
|
+
"grep",
|
|
63
|
+
"skill",
|
|
64
|
+
"observation_read",
|
|
65
|
+
"memory_explain",
|
|
66
|
+
"lisp_status",
|
|
67
|
+
"lisp_hot_status",
|
|
68
|
+
]);
|
|
69
|
+
const CONTROL_TOOLS = new Set([
|
|
70
|
+
"correction_check",
|
|
71
|
+
"task_memory_review",
|
|
72
|
+
"task_completion",
|
|
73
|
+
"memory_checkpoint",
|
|
74
|
+
"curator_check",
|
|
75
|
+
"enno_finish",
|
|
76
|
+
"enno_work_report",
|
|
77
|
+
"enno_plan_review",
|
|
78
|
+
"enno_plan_submit",
|
|
79
|
+
"enno_ideal_submit",
|
|
80
|
+
"enno_meditation_submit",
|
|
81
|
+
]);
|
|
35
82
|
/** Only an exact foreground native Bash invocation at the bound repository root is proof-eligible. */
|
|
36
83
|
export function foregroundNativeCommand(execution, repositoryRoot) {
|
|
37
84
|
const args = execution.arguments;
|
|
38
|
-
if (execution.parent !== undefined ||
|
|
39
|
-
|
|
85
|
+
if (execution.parent !== undefined ||
|
|
86
|
+
!["Bash", "bash"].includes(execution.name) ||
|
|
87
|
+
typeof args?.command !== "string" ||
|
|
88
|
+
args.background === true ||
|
|
89
|
+
args.run_in_background === true)
|
|
40
90
|
return null;
|
|
41
91
|
try {
|
|
42
|
-
const directories = [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
92
|
+
const directories = [
|
|
93
|
+
"cwd",
|
|
94
|
+
"workdir",
|
|
95
|
+
"workingDirectory",
|
|
96
|
+
"working_directory",
|
|
97
|
+
]
|
|
98
|
+
.filter((key) => args[key] !== undefined)
|
|
99
|
+
.map((key) => args[key]);
|
|
100
|
+
return directories.every((directory) => typeof directory === "string" &&
|
|
101
|
+
realpathSync(resolve(repositoryRoot, directory)) === repositoryRoot)
|
|
102
|
+
? args.command
|
|
103
|
+
: null;
|
|
46
104
|
}
|
|
47
105
|
catch {
|
|
48
106
|
return null;
|
|
49
107
|
}
|
|
50
108
|
}
|
|
51
109
|
function isPtcSubcall(execution) {
|
|
52
|
-
return execution.parent !== undefined &&
|
|
53
|
-
|
|
54
|
-
|
|
110
|
+
return (execution.parent !== undefined &&
|
|
111
|
+
typeof execution.rootCallId === "string" &&
|
|
112
|
+
execution.callId.startsWith(`${execution.rootCallId}:ptc:`) &&
|
|
113
|
+
/^[1-9]\d*$/u.test(execution.callId.slice(execution.rootCallId.length + 5)));
|
|
55
114
|
}
|
|
56
115
|
/** Only saved-result paging is a read; ref inspection can run a live worker. */
|
|
57
116
|
export function isSavedLispResultRead(execution) {
|
|
58
|
-
return execution.name ===
|
|
59
|
-
|
|
117
|
+
return (execution.name === "lisp_inspect" &&
|
|
118
|
+
execution.parent === undefined &&
|
|
119
|
+
typeof execution.arguments?.resultOperationId === "string" &&
|
|
120
|
+
execution.arguments.ref === undefined);
|
|
60
121
|
}
|
|
61
122
|
/** Mount on the actual DSH pre-execute/result path; it never grants native permission. */
|
|
62
123
|
export function mountMemoryApplication(ctx, host) {
|
|
@@ -66,227 +127,467 @@ export function mountMemoryApplication(ctx, host) {
|
|
|
66
127
|
const checkDecision = async (execution, decision) => {
|
|
67
128
|
const identity = host.resolve(execution);
|
|
68
129
|
if (!identity)
|
|
69
|
-
throw new KiokukoError(
|
|
130
|
+
throw new KiokukoError("CONFLICT", "No exact native task for correction check");
|
|
70
131
|
const receipt = identity.corrections;
|
|
71
|
-
if (receipt &&
|
|
72
|
-
|
|
73
|
-
|
|
132
|
+
if (receipt &&
|
|
133
|
+
(receipt.runId !== identity.runId ||
|
|
134
|
+
receipt.workspace !== identity.workspace ||
|
|
135
|
+
receipt.repositoryRoot !== realpathSync(identity.repositoryRoot)))
|
|
136
|
+
throw new KiokukoError("CONFLICT", "Correction receipt belongs to another native task");
|
|
137
|
+
const operationDigest = canonicalContentHash({
|
|
138
|
+
name: execution.name,
|
|
139
|
+
arguments: execution.arguments,
|
|
140
|
+
});
|
|
74
141
|
let verdict;
|
|
75
142
|
try {
|
|
76
|
-
verdict = await host.runtime.withDatabase(db => checkCorrectionDecision(db, receipt, decision));
|
|
143
|
+
verdict = await host.runtime.withDatabase((db) => checkCorrectionDecision(db, receipt, decision));
|
|
77
144
|
}
|
|
78
145
|
catch (error) {
|
|
79
146
|
execution.signal.throwIfAborted();
|
|
80
|
-
if (error instanceof KiokukoError &&
|
|
147
|
+
if (error instanceof KiokukoError &&
|
|
148
|
+
[
|
|
149
|
+
"SECURITY_REJECTION",
|
|
150
|
+
"CONFLICT",
|
|
151
|
+
"VALIDATION_ERROR",
|
|
152
|
+
"INTEGRITY_ERROR",
|
|
153
|
+
].includes(error.code))
|
|
81
154
|
throw error;
|
|
82
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
state: "unavailable",
|
|
157
|
+
reason: "correction_observer_unavailable",
|
|
158
|
+
retrieval: receipt?.retrieval ?? "not_requested",
|
|
159
|
+
delivery: receipt?.delivery ?? "not_delivered",
|
|
160
|
+
verification: "unverified",
|
|
161
|
+
allowed: true,
|
|
162
|
+
authorizationGranted: false,
|
|
163
|
+
recorded: false,
|
|
164
|
+
};
|
|
83
165
|
}
|
|
84
|
-
const knownGate = decision.kind ===
|
|
85
|
-
|
|
86
|
-
|
|
166
|
+
const knownGate = decision.kind === "compatibility" &&
|
|
167
|
+
decision.basis === "version_only" &&
|
|
168
|
+
receipt?.sources.some((source) => source.rule === "capability_compatibility");
|
|
169
|
+
const conflict = verdict.state === "contradiction" ||
|
|
170
|
+
(verdict.state === "handoff_missing" && knownGate);
|
|
171
|
+
let confirmation = "not_needed";
|
|
87
172
|
if (conflict) {
|
|
88
|
-
const key = canonicalContentHash({
|
|
173
|
+
const key = canonicalContentHash({
|
|
174
|
+
runId: identity.runId,
|
|
175
|
+
request: receipt?.requestDigest,
|
|
176
|
+
sources: receipt?.sources,
|
|
177
|
+
decision,
|
|
178
|
+
});
|
|
89
179
|
const previous = answered.get(key);
|
|
90
|
-
confirmation = previous ??
|
|
180
|
+
confirmation = previous ?? "unavailable";
|
|
91
181
|
if (!previous && host.questions && execution.agent?.id) {
|
|
92
|
-
const result = await host.questions.ask({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
182
|
+
const result = await host.questions.ask({
|
|
183
|
+
agent: execution.agent,
|
|
184
|
+
signal: execution.signal,
|
|
185
|
+
questions: [
|
|
186
|
+
{
|
|
187
|
+
id: `correction-${key.slice(0, 24)}`,
|
|
188
|
+
header: "訂正との矛盾",
|
|
189
|
+
question: verdict.reason === "verified_work_marked_incomplete_for_version"
|
|
190
|
+
? "対象の試験は成功しています。版数だけを理由に未完了とする判断を見直しますか?"
|
|
191
|
+
: "この操作は、能力で互換性を確認するという訂正と矛盾する完全一致チェックを含みます。どう扱いますか?",
|
|
96
192
|
detail: `対象: ${decision.target}`,
|
|
97
|
-
options: [
|
|
193
|
+
options: [
|
|
194
|
+
{ label: "見直す" },
|
|
195
|
+
{ label: "この操作だけ許可" },
|
|
196
|
+
{ label: "この操作を中止" },
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
98
201
|
execution.signal.throwIfAborted();
|
|
99
202
|
const current = host.resolve(execution);
|
|
100
|
-
if (current?.runId !== identity.runId ||
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
throw new KiokukoError(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
throw new KiokukoError(
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
203
|
+
if (current?.runId !== identity.runId ||
|
|
204
|
+
canonicalContentHash(current.corrections ?? null) !==
|
|
205
|
+
canonicalContentHash(receipt ?? null))
|
|
206
|
+
throw new KiokukoError("CONFLICT", "Correction request changed during confirmation");
|
|
207
|
+
if (canonicalContentHash({
|
|
208
|
+
name: execution.name,
|
|
209
|
+
arguments: execution.arguments,
|
|
210
|
+
}) !== operationDigest)
|
|
211
|
+
throw new KiokukoError("CONFLICT", "Correction operation changed during confirmation");
|
|
212
|
+
const fresh = await host.runtime.withDatabase((db) => checkCorrectionDecision(db, receipt, decision));
|
|
213
|
+
if (fresh.state === "stale")
|
|
214
|
+
throw new KiokukoError("CONFLICT", "Correction source changed during confirmation");
|
|
215
|
+
if (fresh.targetDigest !== verdict.targetDigest ||
|
|
216
|
+
fresh.state !== verdict.state ||
|
|
217
|
+
fresh.verification !== verdict.verification)
|
|
218
|
+
throw new KiokukoError("CONFLICT", "Correction target or verification changed during confirmation");
|
|
219
|
+
const answer = result.answers.find((answer) => answer.id === `correction-${key.slice(0, 24)}`)?.selected[0];
|
|
220
|
+
confirmation =
|
|
221
|
+
answer === "この操作だけ許可"
|
|
222
|
+
? "approve"
|
|
223
|
+
: answer === "見直す"
|
|
224
|
+
? "revise"
|
|
225
|
+
: "cancel";
|
|
226
|
+
if (confirmation !== "approve") {
|
|
112
227
|
if (answered.size >= 128)
|
|
113
228
|
answered.delete(answered.keys().next().value);
|
|
114
229
|
answered.set(key, confirmation);
|
|
115
230
|
}
|
|
116
231
|
}
|
|
117
232
|
}
|
|
118
|
-
const result = {
|
|
233
|
+
const result = {
|
|
234
|
+
...verdict,
|
|
235
|
+
allowed: !conflict || confirmation === "approve",
|
|
236
|
+
authorizationGranted: false,
|
|
237
|
+
confirmation,
|
|
238
|
+
operationDigest,
|
|
239
|
+
decisionDigest: canonicalContentHash(decision),
|
|
240
|
+
};
|
|
119
241
|
let recorded = false;
|
|
120
242
|
if (receipt)
|
|
121
243
|
try {
|
|
122
|
-
recorded = await host.runtime.withDatabase(db => recordCorrectionObservation(db, receipt, {
|
|
244
|
+
recorded = await host.runtime.withDatabase((db) => recordCorrectionObservation(db, receipt, {
|
|
245
|
+
decision: decision.kind,
|
|
246
|
+
target: decision.target,
|
|
247
|
+
...result,
|
|
248
|
+
}));
|
|
123
249
|
}
|
|
124
250
|
catch {
|
|
125
251
|
execution.signal.throwIfAborted();
|
|
126
252
|
}
|
|
127
253
|
return { ...result, recorded };
|
|
128
254
|
};
|
|
129
|
-
disposers.push(ctx.tools.register({
|
|
130
|
-
|
|
255
|
+
disposers.push(ctx.tools.register({
|
|
256
|
+
name: "correction_check",
|
|
257
|
+
modelFacing: true,
|
|
258
|
+
description: "Check a compiler compatibility or completion judgment against host-retrieved corrections and actual current verification. A model claim never proves application. Only a concrete conflicting operation asks a scoped native question.",
|
|
131
259
|
parameters: JSON.parse(JSON.stringify(z.toJSONSchema(z.object({ decision: correctionDecisionSchema }).strict()))),
|
|
132
|
-
output: {
|
|
260
|
+
output: {
|
|
261
|
+
schema: {},
|
|
262
|
+
render: (_, value) => [
|
|
263
|
+
{ type: "text", text: JSON.stringify(value) },
|
|
264
|
+
],
|
|
265
|
+
},
|
|
133
266
|
execute: async (args, execution) => {
|
|
134
|
-
if (execution.name !==
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
267
|
+
if (execution.name !== "correction_check" ||
|
|
268
|
+
execution.parent !== undefined)
|
|
269
|
+
throw new KiokukoError("CONFLICT", "Correction check requires a root native call");
|
|
270
|
+
return checkDecision(execution, z.object({ decision: correctionDecisionSchema }).strict().parse(args)
|
|
271
|
+
.decision);
|
|
272
|
+
},
|
|
273
|
+
}));
|
|
138
274
|
// Existing authorization hooks still own the operation. This guard never grants
|
|
139
275
|
// permission, executes a verifier, or blocks unrelated/missing-memory work.
|
|
140
|
-
disposers.push(ctx.on(
|
|
276
|
+
disposers.push(ctx.on("tools/pre-execute", async (execution, next) => {
|
|
141
277
|
const identity = host.resolve(execution);
|
|
142
|
-
if (!identity?.corrections?.sources.some(source => source.rule ===
|
|
278
|
+
if (!identity?.corrections?.sources.some((source) => source.rule === "capability_compatibility"))
|
|
143
279
|
return next();
|
|
144
280
|
const decision = literalVersionGate(execution.name, execution.arguments, identity.repositoryRoot);
|
|
145
281
|
if (!decision)
|
|
146
282
|
return next();
|
|
147
283
|
const result = await checkDecision(execution, decision);
|
|
148
284
|
if (!result.allowed)
|
|
149
|
-
throw new KiokukoError(
|
|
285
|
+
throw new KiokukoError("CONFLICT", `Correction conflict: ${result.reason}; only this operation is held`);
|
|
150
286
|
return next();
|
|
151
287
|
}, { prepend: true }));
|
|
152
288
|
if (ctx.commands && host.session)
|
|
153
|
-
disposers.push(ctx.commands.register({
|
|
154
|
-
|
|
289
|
+
disposers.push(ctx.commands.register({
|
|
290
|
+
name: "kioku-memory-application",
|
|
291
|
+
description: "Show memory application and regression verification status for this native session.",
|
|
292
|
+
input: { hint: "status [--json]" },
|
|
155
293
|
handler: async (invocation) => {
|
|
156
294
|
invocation.signal.throwIfAborted();
|
|
157
|
-
if (![
|
|
158
|
-
return {
|
|
295
|
+
if (!["", "status", "status --json"].includes(invocation.rawInput.trim()))
|
|
296
|
+
return {
|
|
297
|
+
kind: "error",
|
|
298
|
+
text: "Use /kioku-memory-application status [--json].",
|
|
299
|
+
};
|
|
159
300
|
const session = host.session(invocation.agent);
|
|
160
301
|
if (!session)
|
|
161
|
-
return {
|
|
162
|
-
|
|
163
|
-
|
|
302
|
+
return {
|
|
303
|
+
kind: "error",
|
|
304
|
+
text: "Native session identity is unavailable.",
|
|
305
|
+
};
|
|
306
|
+
const status = await host.runtime.withDatabase((db) => {
|
|
307
|
+
const row = db
|
|
308
|
+
.prepare("SELECT run_id FROM task_memory_bindings WHERE session_id=? AND repository_root=? ORDER BY rowid DESC LIMIT 1")
|
|
164
309
|
.get(session.sessionId, session.repositoryRoot);
|
|
165
|
-
const application = row
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
310
|
+
const application = row
|
|
311
|
+
? memoryApplicationStatus(db, row.run_id)
|
|
312
|
+
: {
|
|
313
|
+
supported: false,
|
|
314
|
+
ready: true,
|
|
315
|
+
workMayContinue: true,
|
|
316
|
+
verification: "unobserved",
|
|
317
|
+
};
|
|
318
|
+
const globalization = application.supported && "items" in application
|
|
319
|
+
? application.items.map((item) => ({
|
|
320
|
+
entryId: item.entryId,
|
|
321
|
+
revision: item.revision,
|
|
322
|
+
...autoGlobalizationStatus(db, item.entryId, item.revision),
|
|
323
|
+
}))
|
|
324
|
+
: [];
|
|
325
|
+
return {
|
|
326
|
+
integration: "native_active",
|
|
327
|
+
...application,
|
|
328
|
+
globalization,
|
|
329
|
+
};
|
|
170
330
|
});
|
|
171
|
-
if (invocation.rawInput.includes(
|
|
172
|
-
return { kind:
|
|
173
|
-
return {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
331
|
+
if (invocation.rawInput.includes("--json"))
|
|
332
|
+
return { kind: "success", text: JSON.stringify(status, null, 2) };
|
|
333
|
+
return {
|
|
334
|
+
kind: "success",
|
|
335
|
+
text: !status.supported
|
|
336
|
+
? "記憶適用の連携は稼働中です。この会話で準備済みの依頼はありません。"
|
|
337
|
+
: `記憶適用: ${status.ready ? "未処理なし" : "確認が必要"}\n取得: ${"retrieval" in status ? status.retrieval : "unavailable"}\n検証: ${status.verification}\n未処理: ${"pending" in status ? status.pending.length : 0}\n実行中・終了未確認: ${"running" in status ? status.running : 0}\n自動Global化: ${status.globalization.map((item) => `${item.entryId}@${item.revision}: ${"successfulRuns" in item ? item.successfulRuns : 0}/3, ${"reason" in item ? (item.reason ?? item.state) : "未対応"}, ${"globalEntryId" in item ? (item.globalEntryId ?? "未生成") : "未生成"}`).join(" / ") || "対象なし"}\n詳細は status --json で確認できます。`,
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
}));
|
|
341
|
+
disposers.push(ctx.tools.register({
|
|
342
|
+
name: "memory_explain",
|
|
343
|
+
modelFacing: true,
|
|
344
|
+
description: "Inspect stored memory claims, source excerpts, revision history and current eligibility. Source attachment does not prove truth.",
|
|
345
|
+
parameters: JSON.parse(JSON.stringify(z.toJSONSchema(z
|
|
346
|
+
.object({
|
|
347
|
+
entryId: z.string().min(1).max(256),
|
|
348
|
+
revision: z.number().int().positive().optional(),
|
|
349
|
+
})
|
|
350
|
+
.strict()))),
|
|
351
|
+
output: {
|
|
352
|
+
schema: {},
|
|
353
|
+
render: (_, value) => [
|
|
354
|
+
{ type: "text", text: JSON.stringify(value) },
|
|
355
|
+
],
|
|
356
|
+
},
|
|
180
357
|
execute: async (args, execution) => {
|
|
181
358
|
const identity = host.resolve(execution);
|
|
182
|
-
if (!identity ||
|
|
183
|
-
|
|
184
|
-
|
|
359
|
+
if (!identity ||
|
|
360
|
+
execution.parent !== undefined ||
|
|
361
|
+
execution.name !== "memory_explain")
|
|
362
|
+
throw new Error("No active native task");
|
|
363
|
+
const input = z
|
|
364
|
+
.object({
|
|
365
|
+
entryId: z.string().min(1).max(256),
|
|
366
|
+
revision: z.number().int().positive().optional(),
|
|
367
|
+
})
|
|
368
|
+
.strict()
|
|
369
|
+
.parse(args);
|
|
185
370
|
execution.signal.throwIfAborted();
|
|
186
|
-
return host.runtime.withDatabase(db => {
|
|
371
|
+
return host.runtime.withDatabase((db) => {
|
|
187
372
|
if (host.resolve(execution)?.runId !== identity.runId)
|
|
188
|
-
throw new Error(
|
|
189
|
-
const value = explainMemory(db, {
|
|
190
|
-
|
|
373
|
+
throw new Error("Native task changed");
|
|
374
|
+
const value = explainMemory(db, {
|
|
375
|
+
workspace: identity.workspace,
|
|
376
|
+
runId: identity.runId,
|
|
377
|
+
...input,
|
|
378
|
+
});
|
|
379
|
+
db.prepare("INSERT OR IGNORE INTO memory_explain_receipts VALUES(?,?,?,?)").run(identity.sessionId, execution.callId, value.entryId, value.revision);
|
|
191
380
|
return value;
|
|
192
381
|
});
|
|
193
|
-
}
|
|
382
|
+
},
|
|
194
383
|
}));
|
|
195
384
|
if (ctx.commands && host.session)
|
|
196
|
-
disposers.push(ctx.commands.register({
|
|
385
|
+
disposers.push(ctx.commands.register({
|
|
386
|
+
name: "kioku-memory",
|
|
387
|
+
description: "Explain memory evidence or forget all revisions and dependent memories. Native logs remain. explain ENTRY_ID [--revision N] [--json] | forget ENTRY_ID --revision N | learning status | learning reset",
|
|
388
|
+
input: {
|
|
389
|
+
hint: "explain ENTRY_ID [--revision N] [--json] | forget ENTRY_ID --revision N | learning status | learning reset",
|
|
390
|
+
},
|
|
197
391
|
handler: async (invocation) => {
|
|
198
392
|
try {
|
|
199
393
|
const session = host.session(invocation.agent);
|
|
200
394
|
if (!session)
|
|
201
|
-
throw new Error(
|
|
395
|
+
throw new Error("Native session identity unavailable");
|
|
202
396
|
const args = invocation.rawInput.trim().split(/\s+/u);
|
|
203
|
-
if (
|
|
204
|
-
|
|
397
|
+
if (args[0] === "learning") {
|
|
398
|
+
if (args.length !== 2 || !["status", "reset"].includes(args[1]))
|
|
399
|
+
throw new Error("Use /kioku-memory learning status | learning reset (current repository only).");
|
|
400
|
+
invocation.signal.throwIfAborted();
|
|
401
|
+
const value = await host.runtime.withDatabase((db) => {
|
|
402
|
+
invocation.signal.throwIfAborted();
|
|
403
|
+
const current = host.session(invocation.agent);
|
|
404
|
+
if (!current ||
|
|
405
|
+
current.sessionId !== session.sessionId ||
|
|
406
|
+
current.repositoryRoot !== session.repositoryRoot)
|
|
407
|
+
throw new Error("Native session identity changed");
|
|
408
|
+
const root = detectRepositoryRoot({
|
|
409
|
+
cwd: current.repositoryRoot,
|
|
410
|
+
allowDirectory: true,
|
|
411
|
+
}).root;
|
|
412
|
+
if (args[1] === "reset")
|
|
413
|
+
return {
|
|
414
|
+
action: "reset",
|
|
415
|
+
...resetRepositoryLearning(db, root),
|
|
416
|
+
};
|
|
417
|
+
const snapshot = readRepositoryLearning(db, root);
|
|
418
|
+
// No lesson text, model tags, source excerpts or filesystem paths in status.
|
|
419
|
+
return {
|
|
420
|
+
action: "status",
|
|
421
|
+
repositoryKey: snapshot.repositoryKey,
|
|
422
|
+
revision: snapshot.revision,
|
|
423
|
+
turnCount: snapshot.turnCount,
|
|
424
|
+
retainedPoints: snapshot.points.length,
|
|
425
|
+
outcomeCounts: snapshot.outcomeCounts,
|
|
426
|
+
assessment: snapshot.assessment,
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
return {
|
|
430
|
+
kind: "success",
|
|
431
|
+
text: JSON.stringify(value, null, 2) +
|
|
432
|
+
(args[1] === "reset"
|
|
433
|
+
? "\nCurrent repository learning was reset; pending pre-reset writes are invalidated. Automatic learning remains enabled. Native logs, already-delivered context and backups are not erased."
|
|
434
|
+
: "\nCounts describe stored learning, not verified correctness or model use."),
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (!["explain", "forget"].includes(args[0]) || !args[1])
|
|
438
|
+
throw new Error("Use explain ENTRY_ID [--revision N] [--json]");
|
|
205
439
|
let revision;
|
|
206
440
|
for (let i = 2; i < args.length; i++) {
|
|
207
|
-
if (args[i] ===
|
|
441
|
+
if (args[i] === "--json")
|
|
208
442
|
continue;
|
|
209
|
-
if (args[i] ===
|
|
443
|
+
if (args[i] === "--revision" &&
|
|
444
|
+
revision === undefined &&
|
|
445
|
+
/^[1-9]\d*$/.test(args[i + 1] ?? "")) {
|
|
210
446
|
revision = Number(args[++i]);
|
|
211
447
|
if (!Number.isSafeInteger(revision))
|
|
212
|
-
throw new Error(
|
|
448
|
+
throw new Error("Invalid revision");
|
|
213
449
|
continue;
|
|
214
450
|
}
|
|
215
|
-
throw new Error(
|
|
451
|
+
throw new Error("Invalid memory command arguments");
|
|
216
452
|
}
|
|
217
|
-
if (args[0] ===
|
|
218
|
-
throw new Error(
|
|
453
|
+
if (args[0] === "forget" && revision === undefined)
|
|
454
|
+
throw new Error("forget requires --revision N; it erases all revisions and dependent memories");
|
|
219
455
|
invocation.signal.throwIfAborted();
|
|
220
|
-
const value = await host.runtime.withDatabase(db => {
|
|
221
|
-
const binding = db
|
|
222
|
-
|
|
456
|
+
const value = await host.runtime.withDatabase((db) => {
|
|
457
|
+
const binding = db
|
|
458
|
+
.prepare("SELECT workspace,run_id FROM task_memory_bindings WHERE session_id=? AND repository_root=? ORDER BY rowid DESC LIMIT 1")
|
|
459
|
+
.get(session.sessionId, session.repositoryRoot);
|
|
460
|
+
const project = db
|
|
461
|
+
.prepare("SELECT workspace FROM repository_locations JOIN repositories USING(repository_id) WHERE canonical_root=?")
|
|
462
|
+
.get(session.repositoryRoot);
|
|
223
463
|
const workspace = binding?.workspace ?? project?.workspace;
|
|
224
464
|
if (!workspace)
|
|
225
|
-
throw new Error(
|
|
226
|
-
return args[0] ===
|
|
465
|
+
throw new Error("Current native project is unavailable");
|
|
466
|
+
return args[0] === "forget"
|
|
467
|
+
? forgetMemory(db, {
|
|
468
|
+
workspace,
|
|
469
|
+
entryId: args[1],
|
|
470
|
+
expectedRevision: revision,
|
|
471
|
+
operationId: `forget:${session.sessionId}:${invocation.commandId ?? args[1] + ":" + revision}`,
|
|
472
|
+
})
|
|
473
|
+
: explainMemory(db, {
|
|
474
|
+
workspace,
|
|
475
|
+
entryId: args[1],
|
|
476
|
+
revision,
|
|
477
|
+
...(binding ? { runId: binding.run_id } : {}),
|
|
478
|
+
});
|
|
227
479
|
});
|
|
228
|
-
return {
|
|
480
|
+
return {
|
|
481
|
+
kind: "success",
|
|
482
|
+
text: args.includes("--json")
|
|
483
|
+
? JSON.stringify(value, null, 2)
|
|
484
|
+
: args[0] === "forget"
|
|
485
|
+
? `記憶 ${args[1]} と依存する記憶を忘却しました。native会話ログ、外部への送信済み情報、バックアップは対象外です。`
|
|
486
|
+
: renderMemoryExplanation(value),
|
|
487
|
+
};
|
|
229
488
|
}
|
|
230
489
|
catch (error) {
|
|
231
|
-
return {
|
|
490
|
+
return {
|
|
491
|
+
kind: "error",
|
|
492
|
+
text: error instanceof Error
|
|
493
|
+
? error.message
|
|
494
|
+
: "Memory inspection failed",
|
|
495
|
+
};
|
|
232
496
|
}
|
|
233
|
-
}
|
|
497
|
+
},
|
|
234
498
|
}));
|
|
235
|
-
disposers.push(ctx.tools.register({
|
|
499
|
+
disposers.push(ctx.tools.register({
|
|
500
|
+
name: "task_memory_review",
|
|
501
|
+
modelFacing: true,
|
|
236
502
|
description: MEMORY_APPLICATION_GUIDANCE,
|
|
237
503
|
// Zod attaches non-enumerable ~standard metadata; DSH requires plain JSON.
|
|
238
|
-
parameters: JSON.parse(JSON.stringify(z.toJSONSchema(transportSchema))),
|
|
504
|
+
parameters: JSON.parse(JSON.stringify(z.toJSONSchema(transportSchema))),
|
|
505
|
+
output: {
|
|
506
|
+
schema: {},
|
|
507
|
+
render: (_, value) => [
|
|
508
|
+
{ type: "text", text: JSON.stringify(value) },
|
|
509
|
+
],
|
|
510
|
+
},
|
|
239
511
|
execute: async (args, execution) => {
|
|
240
512
|
const identity = host.resolve(execution);
|
|
241
|
-
if (!identity ||
|
|
242
|
-
|
|
513
|
+
if (!identity ||
|
|
514
|
+
(execution.parent !== undefined && !isPtcSubcall(execution)) ||
|
|
515
|
+
execution.name !== "task_memory_review")
|
|
516
|
+
throw new Error("No active native task for memory application");
|
|
243
517
|
execution.signal.throwIfAborted();
|
|
244
518
|
const input = inputSchema.parse(args);
|
|
245
|
-
if (input.action !==
|
|
246
|
-
return {
|
|
247
|
-
|
|
519
|
+
if (input.action !== "status")
|
|
520
|
+
return {
|
|
521
|
+
recorded: false,
|
|
522
|
+
workMayContinue: true,
|
|
523
|
+
reason: "post_task_memory_review_required",
|
|
524
|
+
};
|
|
525
|
+
return host.runtime.withDatabase((db) => {
|
|
248
526
|
execution.signal.throwIfAborted();
|
|
249
527
|
if (host.resolve(execution)?.runId !== identity.runId)
|
|
250
|
-
throw new Error(
|
|
528
|
+
throw new Error("Native task changed");
|
|
251
529
|
return memoryApplicationStatus(db, identity.runId);
|
|
252
530
|
});
|
|
253
|
-
}
|
|
254
|
-
|
|
531
|
+
},
|
|
532
|
+
}));
|
|
533
|
+
disposers.push(ctx.on("tools/pre-execute", async (execution, next) => {
|
|
255
534
|
const identity = host.resolve(execution);
|
|
256
|
-
if (!identity ||
|
|
535
|
+
if (!identity ||
|
|
536
|
+
isOwnedExecutionCall(execution) ||
|
|
537
|
+
READ_TOOLS.has(execution.name) ||
|
|
538
|
+
CONTROL_TOOLS.has(execution.name) ||
|
|
539
|
+
isSavedLispResultRead(execution) ||
|
|
540
|
+
host.preparationOnly?.(execution) === true)
|
|
257
541
|
return next();
|
|
258
542
|
// A child without its own admitted binding cannot borrow its parent's proof.
|
|
259
543
|
const command = foregroundNativeCommand(execution, identity.repositoryRoot);
|
|
260
|
-
const tracked = await host.runtime
|
|
544
|
+
const tracked = await host.runtime
|
|
545
|
+
.withDatabase((db) => {
|
|
261
546
|
// Empty memory applications remain observational and take no writer lock.
|
|
262
547
|
if (!memoryApplicationStatus(db, identity.runId).items?.length)
|
|
263
548
|
return undefined;
|
|
264
549
|
return withImmediateTransaction(db, () => {
|
|
265
550
|
execution.signal.throwIfAborted();
|
|
266
551
|
if (host.resolve(execution)?.runId !== identity.runId)
|
|
267
|
-
throw new Error(
|
|
268
|
-
const lease = captureExecutionTaskLease(db, {
|
|
269
|
-
|
|
552
|
+
throw new Error("Native task changed");
|
|
553
|
+
const lease = captureExecutionTaskLease(db, {
|
|
554
|
+
taskId: identity.runId,
|
|
555
|
+
sessionId: identity.sessionId,
|
|
556
|
+
repositoryRoot: identity.repositoryRoot,
|
|
557
|
+
}, execution);
|
|
558
|
+
return beginMemoryExecution(db, identity, execution.callId, command)
|
|
559
|
+
? lease
|
|
560
|
+
: undefined;
|
|
270
561
|
});
|
|
271
|
-
})
|
|
562
|
+
})
|
|
563
|
+
.catch(() => undefined);
|
|
272
564
|
if (tracked)
|
|
273
|
-
pending.set(execution, {
|
|
565
|
+
pending.set(execution, {
|
|
566
|
+
identity: Object.freeze({ ...identity }),
|
|
567
|
+
lease: tracked,
|
|
568
|
+
});
|
|
274
569
|
return next();
|
|
275
570
|
}, { prepend: true }));
|
|
276
|
-
disposers.push(ctx.on(
|
|
571
|
+
disposers.push(ctx.on("tools/result", (execution, result) => {
|
|
277
572
|
const tracked = pending.get(execution);
|
|
278
573
|
if (!tracked)
|
|
279
574
|
return;
|
|
280
575
|
const { identity, lease } = tracked;
|
|
281
576
|
pending.delete(execution);
|
|
282
|
-
return host.runtime
|
|
577
|
+
return host.runtime
|
|
578
|
+
.withDatabase((db) => withImmediateTransaction(db, () => {
|
|
283
579
|
if (host.resolve(execution)?.runId !== identity.runId)
|
|
284
580
|
return;
|
|
285
581
|
authorizeTaskLease(db, lease);
|
|
286
582
|
completeMemoryExecution(db, identity, execution.callId, result);
|
|
287
|
-
}))
|
|
583
|
+
}))
|
|
584
|
+
.catch(() => {
|
|
585
|
+
/* Stale completion cannot adopt evidence into a resumed task. */
|
|
586
|
+
});
|
|
288
587
|
}));
|
|
289
|
-
return () => {
|
|
290
|
-
dispose()
|
|
588
|
+
return () => {
|
|
589
|
+
for (const dispose of disposers.reverse())
|
|
590
|
+
dispose();
|
|
591
|
+
};
|
|
291
592
|
}
|
|
292
593
|
//# sourceMappingURL=memory-application.js.map
|