eklavya 1.47.2 → 1.47.4
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/assets/tutor/SKILL.md +1 -0
- package/dist/assets/tutor/references/focus-and-level.md +3 -1
- package/dist/assets/tutor/references/grading.md +17 -0
- package/dist/assets/tutor/references/writing-mcq.md +56 -6
- package/dist/eval/question-checks.js +31 -3
- package/dist/eval/question-checks.js.map +1 -1
- package/dist/hooks/capture-tool.js +13 -11
- package/dist/hooks/capture-tool.js.map +1 -1
- package/dist/memory/capture.js +7 -3
- package/dist/memory/capture.js.map +1 -1
- package/dist/memory/privacy.js +74 -11
- package/dist/memory/privacy.js.map +1 -1
- package/dist/memory/replay.js +6 -5
- package/dist/memory/replay.js.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/agents/tutor.md +3 -1
- package/dist/plugin/skills/tutor/SKILL.md +1 -0
- package/dist/plugin/skills/tutor/references/focus-and-level.md +3 -1
- package/dist/plugin/skills/tutor/references/grading.md +17 -0
- package/dist/plugin/skills/tutor/references/writing-mcq.md +56 -6
- package/package.json +1 -1
|
@@ -27,6 +27,7 @@ yourself thinking one, the right-hand column is what is actually happening.
|
|
|
27
27
|
| "I'll reword the question they got wrong." | A question in `asked_before` is spent, not recyclable. |
|
|
28
28
|
| "A definition is the transferable version of this." | `concept` focus wants the general rule. "What is X" is tier-1 recall wearing a hat. |
|
|
29
29
|
| "Too easy to be worth asking — I'll add a 'why'." | `easy` is tiers 1–2 and it was earned. A smuggled "why" is a question they cannot answer honestly. |
|
|
30
|
+
| "They know what 'Task 6' means." | They saw none of it. Define the name or drop it. |
|
|
30
31
|
| "I'll put the dials above the stem for context." | The dials are in the status bar. A line you assemble is one the server cannot keep consistent. |
|
|
31
32
|
| "Grade 5 — they picked the right option." | Multiple choice caps at 4. One in four is a coin. |
|
|
32
33
|
|
|
@@ -50,7 +50,9 @@ The code is the subject. Name the file, the line, the decision.
|
|
|
50
50
|
The same subject matter, asked so the answer transfers to a different codebase.
|
|
51
51
|
**This does not mean textbook questions.** The diff stops being the *subject*
|
|
52
52
|
and becomes the *motivation*: open from what was just written, then ask for the
|
|
53
|
-
general rule, the class of problem, or where else it applies.
|
|
53
|
+
general rule, the class of problem, or where else it applies. Open from it in
|
|
54
|
+
words the learner can follow without having read it — they usually have not
|
|
55
|
+
(`writing-mcq.md`, *Write for a cold reader*).
|
|
54
56
|
|
|
55
57
|
- Right: *"We gave the profile cache a 60s TTL in `profile.ts`. TTL is one
|
|
56
58
|
answer to cache invalidation — what problem is it actually solving, and what
|
|
@@ -114,6 +114,23 @@ it rebuilds footing. If they want to stop, they will say so; wait to be told.
|
|
|
114
114
|
**Never dump the remaining answers as a list.** If the quiz ends early, it ends.
|
|
115
115
|
A wall of four explanations at the door is not teaching, it is a receipt.
|
|
116
116
|
|
|
117
|
+
## When they say the question lacks context
|
|
118
|
+
|
|
119
|
+
"This question does not have enough context" is a verdict on the question, not
|
|
120
|
+
a decline. The question assumed something they never saw, so the miss is
|
|
121
|
+
yours. Never drop it untaught:
|
|
122
|
+
|
|
123
|
+
1. **Own it** in a few words — the question leaned on something you had not
|
|
124
|
+
shown them.
|
|
125
|
+
2. **Give the answer and one line of why**, defining the name or situation the
|
|
126
|
+
stem skipped.
|
|
127
|
+
3. **Record it as a blank**: `grade: 0`, `outcome: "dont_know"`, with that
|
|
128
|
+
answer in `feedback`. It is not `declined` — a decline is never offered
|
|
129
|
+
again, and they did not ask to be left alone.
|
|
130
|
+
|
|
131
|
+
The next question about it, now or later, starts with the situation sentence
|
|
132
|
+
the first one was missing (`writing-mcq.md`, *Write for a cold reader*).
|
|
133
|
+
|
|
117
134
|
## `already_taught`
|
|
118
135
|
|
|
119
136
|
When it is true on a plan item, they blanked on this before and you explained
|
|
@@ -3,16 +3,54 @@
|
|
|
3
3
|
Required reading before you write a question. `SKILL.md` decides *whether* to
|
|
4
4
|
ask and *at what tier*; this is how to build the thing itself.
|
|
5
5
|
|
|
6
|
+
## Write for a cold reader
|
|
7
|
+
|
|
8
|
+
Assume the learner saw none of it: not the code, not the plan, not the task
|
|
9
|
+
list, not the conversation. A background agent usually writes the code, so the
|
|
10
|
+
diff you are looking at is one they have never opened. The question has to work
|
|
11
|
+
from the stem and the four options alone.
|
|
12
|
+
|
|
13
|
+
- **Every project-specific name gets a defining clause, or goes.** "Task 6",
|
|
14
|
+
"the brief", "the plan", "the island", a component nickname, a file they never
|
|
15
|
+
opened: each one is a puzzle the learner must solve before the real question.
|
|
16
|
+
Say what it is — *"the hook that runs when a session starts"* — or ask
|
|
17
|
+
without it.
|
|
18
|
+
- **The key follows from the concept, never from something only you
|
|
19
|
+
observed.** A finding in a write-up they never read, the data from one
|
|
20
|
+
session, your own experiment, the keyframes in a file they never opened: a
|
|
21
|
+
question whose answer depends on these is unfair, not hard.
|
|
22
|
+
- **Situations beat abstractions.** A stem with no host, no situation and no
|
|
23
|
+
example cannot be placed. Name the setting in plain words.
|
|
24
|
+
- **Drop the name, keep the constraint.** The project's details are often what
|
|
25
|
+
made the key the only right answer: *no stored state*, *runs on every prompt*,
|
|
26
|
+
*one writer*. When you replace a name with a general situation, carry those
|
|
27
|
+
constraints into it, or a distractor that was wrong in the project becomes
|
|
28
|
+
right in the abstract.
|
|
29
|
+
|
|
30
|
+
> Opaque: *Task 6 moves the instruction from SessionStart to UserPromptSubmit.
|
|
31
|
+
> What does that change?*
|
|
32
|
+
>
|
|
33
|
+
> Cold: *A Claude Code hook can inject text once when a session starts, or on
|
|
34
|
+
> every prompt the user sends. Why move a reminder to the per-prompt hook?*
|
|
35
|
+
|
|
6
36
|
## Build it in this order
|
|
7
37
|
|
|
8
38
|
Six parts, then the call. Build them in the order below — not the order they
|
|
9
39
|
appear on screen — because each one constrains the next.
|
|
10
40
|
|
|
11
|
-
**1. The stem.**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
41
|
+
**1. The stem.** Two parts, grounded per the plan's `framing`:
|
|
42
|
+
|
|
43
|
+
- **The situation** — one plain sentence of 25 words or fewer, saying what is
|
|
44
|
+
happening and defining any name the question uses. One sentence, not two:
|
|
45
|
+
if it will not fit, the question is carrying more setup than one idea needs.
|
|
46
|
+
Leave it out when the question already stands on its own.
|
|
47
|
+
- **The question** — one question about one idea, 25 words or fewer. The
|
|
48
|
+
answer lives in the options, so the question asks and stops.
|
|
49
|
+
|
|
50
|
+
Ordinary words: "sent with the request" rather than "transmitted alongside the
|
|
51
|
+
request context". Expand an acronym the first time it appears for this learner —
|
|
52
|
+
CSRF once, then CSRF. The situation sets the scene and never hints at the
|
|
53
|
+
answer.
|
|
16
54
|
|
|
17
55
|
Ask the positive form, and ask it once: negation makes the stem a reading test
|
|
18
56
|
rather than a question about the idea, and a stem needing a second clause to be
|
|
@@ -23,6 +61,12 @@ precise is doing two jobs — ask the first one and keep the other for later.
|
|
|
23
61
|
**2. The correct option.** Write it before the distractors. It sets the length
|
|
24
62
|
and the grammar the other three have to match.
|
|
25
63
|
|
|
64
|
+
Each option is a complete claim on its own: read alone, it says something that
|
|
65
|
+
is true or false. "Re-point from an ancestor" or "How often, and how recent" is
|
|
66
|
+
a riddle, not an option. When the options differ in length, meet parity by
|
|
67
|
+
lengthening the short ones, never by cutting every option down until none of
|
|
68
|
+
them says anything.
|
|
69
|
+
|
|
26
70
|
**3. Three distractors**, each drawn from one of these:
|
|
27
71
|
|
|
28
72
|
| Source | Written against the stem above |
|
|
@@ -92,8 +136,14 @@ quietly drops one of its fields is the other case, and it is what
|
|
|
92
136
|
Read yours against this. Each line is a property of the question, so a "no" is
|
|
93
137
|
telling you which part to rebuild.
|
|
94
138
|
|
|
95
|
-
-
|
|
139
|
+
- Someone who saw none of the code, plan or conversation could answer it: every
|
|
140
|
+
project-specific name is defined, and the key follows from the concept.
|
|
141
|
+
- The stem asks one thing: at most one sentence of situation, then a question,
|
|
142
|
+
each 25 words or fewer.
|
|
143
|
+
- Read without the project, every wrong option is still wrong: the situation
|
|
144
|
+
kept the constraints the names carried.
|
|
96
145
|
- The answer appears among the options and nowhere in the stem.
|
|
146
|
+
- Each option is a complete claim when read on its own.
|
|
97
147
|
- All four options are within a few words of the same length and use the same
|
|
98
148
|
grammar. A visibly longer or more careful option reads as the correct one, and
|
|
99
149
|
gets picked without engaging — the same leak as always answering first.
|
|
@@ -63,8 +63,34 @@ export function longestSharedRun(a, b) {
|
|
|
63
63
|
}
|
|
64
64
|
return best;
|
|
65
65
|
}
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* The question sentence: this long and it stops being read and starts being
|
|
68
|
+
* pattern-matched.
|
|
69
|
+
*/
|
|
67
70
|
export const STEM_WORD_LIMIT = 25;
|
|
71
|
+
/**
|
|
72
|
+
* The situation that may come before the question. A stem is one plain
|
|
73
|
+
* sentence of setup and then the question, because a learner who never saw the
|
|
74
|
+
* code needs somewhere to be told what "the plan" or "this hook" is. Before
|
|
75
|
+
* this existed, the single 25-word limit squeezed that setup into a bare name.
|
|
76
|
+
*/
|
|
77
|
+
export const SETUP_WORD_LIMIT = 25;
|
|
78
|
+
/**
|
|
79
|
+
* A stem split into its setup and the question that ends it.
|
|
80
|
+
*
|
|
81
|
+
* The question is the last sentence; everything before it is setup. Sentences
|
|
82
|
+
* end at `.`, `!` or `?` followed by whitespace, so `auth.ts` and `res.json()`
|
|
83
|
+
* stay whole.
|
|
84
|
+
*/
|
|
85
|
+
export function splitStem(stem) {
|
|
86
|
+
const sentences = stem
|
|
87
|
+
.trim()
|
|
88
|
+
.split(/(?<=[.!?])\s+/)
|
|
89
|
+
.filter((s) => s.length > 0);
|
|
90
|
+
if (sentences.length <= 1)
|
|
91
|
+
return { setup: '', question: stem.trim() };
|
|
92
|
+
return { setup: sentences.slice(0, -1).join(' '), question: sentences[sentences.length - 1] };
|
|
93
|
+
}
|
|
68
94
|
/** A shared run this long means the stem contains the answer's sentence. */
|
|
69
95
|
export const ANSWER_RUN_LIMIT = 4;
|
|
70
96
|
/**
|
|
@@ -128,8 +154,10 @@ export function checkQuestion(q) {
|
|
|
128
154
|
push('answer_at_position', obeyed, obeyed
|
|
129
155
|
? `correct option at ${q.answer_position}, as asked`
|
|
130
156
|
: `plan said ${q.answer_position}, model used ${q.correct}`);
|
|
131
|
-
const
|
|
132
|
-
|
|
157
|
+
const { setup, question } = splitStem(q.stem);
|
|
158
|
+
const setupWords = words(setup).length;
|
|
159
|
+
const questionWords = words(question).length;
|
|
160
|
+
push('stem_length', setupWords <= SETUP_WORD_LIMIT && questionWords <= STEM_WORD_LIMIT, `setup ${setupWords} words (limit ${SETUP_WORD_LIMIT}), question ${questionWords} words (limit ${STEM_WORD_LIMIT})`);
|
|
133
161
|
// Indexed into `q.options`, never `labels`. `labels` drops empty options, so
|
|
134
162
|
// one blank before the answer shifts every later index by one -- and the
|
|
135
163
|
// conspicuousness check below then compared the correct option against
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-checks.js","sourceRoot":"","sources":["../../src/eval/question-checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA0BH,sEAAsE;AACtE,SAAS,KAAK,CAAC,IAAY;IACzB,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS,EAAE,CAAS;IACnD,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE/C,wEAAwE;IACxE,+CAA+C;IAC/C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,IAAI,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;YACvB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrD,QAAQ,GAAG,KAAK,CAAC;YACjB,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;YACd,IAAI,GAAG,GAAG,IAAI;gBAAE,IAAI,GAAG,GAAG,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"question-checks.js","sourceRoot":"","sources":["../../src/eval/question-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { run, openExisting, config, cwdOf, sessionId } from './lib.js';
|
|
13
13
|
import { batchIfFull, identityOf, record } from './capture-lib.js';
|
|
14
|
-
import {
|
|
14
|
+
import { policyFrom } from '../memory/capture.js';
|
|
15
|
+
import { clip, clipTail } from '../memory/privacy.js';
|
|
15
16
|
/** Tools whose result is a file's contents rather than a change to one. */
|
|
16
17
|
const READ_TOOLS = new Set(['Read', 'NotebookRead', 'Glob', 'Grep']);
|
|
17
18
|
const EDIT_TOOLS = new Set(['Edit', 'Write', 'NotebookEdit', 'MultiEdit']);
|
|
@@ -60,10 +61,11 @@ function resultText(response) {
|
|
|
60
61
|
* What the result showed, head and tail — the tail is where a test summary or
|
|
61
62
|
* a stack trace ends up. This is what an observation is made from: without it
|
|
62
63
|
* the summariser saw that `git log` ran, never what it showed, and could record
|
|
63
|
-
* that work happened but not what was found.
|
|
64
|
-
*
|
|
64
|
+
* that work happened but not what was found. Each piece is redacted before it
|
|
65
|
+
* is cut, over a bounded window: a cut first could leave half a secret that the
|
|
66
|
+
* redaction of the stored body no longer recognises.
|
|
65
67
|
*/
|
|
66
|
-
function excerpt(tool, response) {
|
|
68
|
+
function excerpt(tool, response, policy) {
|
|
67
69
|
if (NO_RESULT.has(tool))
|
|
68
70
|
return '';
|
|
69
71
|
const text = resultText(response).trim();
|
|
@@ -71,7 +73,7 @@ function excerpt(tool, response) {
|
|
|
71
73
|
return '';
|
|
72
74
|
if (text.length <= RESULT_HEAD + RESULT_TAIL)
|
|
73
75
|
return text;
|
|
74
|
-
return `${text
|
|
76
|
+
return `${clip(text, RESULT_HEAD, policy)}\n…\n${clipTail(text, RESULT_TAIL, policy)}`;
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
79
|
* What the event's body says: the arguments, then what came back.
|
|
@@ -80,7 +82,7 @@ function excerpt(tool, response) {
|
|
|
80
82
|
* for reads and edits: a `Read` returning a whole file would make the corpus a
|
|
81
83
|
* second copy of the repository, and an edit's result only echoes its input.
|
|
82
84
|
*/
|
|
83
|
-
function bodyFor(input, failed) {
|
|
85
|
+
function bodyFor(input, failed, policy) {
|
|
84
86
|
const toolInput = input.tool_input ?? {};
|
|
85
87
|
const parts = [];
|
|
86
88
|
if (typeof toolInput.command === 'string')
|
|
@@ -90,7 +92,7 @@ function bodyFor(input, failed) {
|
|
|
90
92
|
if (typeof toolInput.prompt === 'string')
|
|
91
93
|
parts.push(toolInput.prompt);
|
|
92
94
|
if (typeof toolInput.old_string === 'string' && typeof toolInput.new_string === 'string') {
|
|
93
|
-
parts.push(`- ${clip(toolInput.old_string, 600)}`, `+ ${clip(toolInput.new_string, 600)}`);
|
|
95
|
+
parts.push(`- ${clip(toolInput.old_string, 600, policy)}`, `+ ${clip(toolInput.new_string, 600, policy)}`);
|
|
94
96
|
}
|
|
95
97
|
if (!parts.length) {
|
|
96
98
|
const keys = Object.keys(toolInput).filter((k) => k !== 'content');
|
|
@@ -99,12 +101,12 @@ function bodyFor(input, failed) {
|
|
|
99
101
|
// `questions=[object Object] answers=[object Object]`.
|
|
100
102
|
const text = (v) => (typeof v === 'string' ? v : JSON.stringify(v));
|
|
101
103
|
if (keys.length)
|
|
102
|
-
parts.push(keys.map((k) => `${k}=${clip(text(toolInput[k]), 200)}`).join(' '));
|
|
104
|
+
parts.push(keys.map((k) => `${k}=${clip(text(toolInput[k]), 200, policy)}`).join(' '));
|
|
103
105
|
}
|
|
104
106
|
if (failed)
|
|
105
|
-
parts.push(clip(String(errorText(input.tool_response)), 800));
|
|
107
|
+
parts.push(clip(String(errorText(input.tool_response)), 800, policy));
|
|
106
108
|
else {
|
|
107
|
-
const result = excerpt(input.tool_name, input.tool_response);
|
|
109
|
+
const result = excerpt(input.tool_name, input.tool_response, policy);
|
|
108
110
|
if (result)
|
|
109
111
|
parts.push(`→ ${result}`);
|
|
110
112
|
}
|
|
@@ -150,7 +152,7 @@ await run(async (input) => {
|
|
|
150
152
|
const sid = sessionId(input, db);
|
|
151
153
|
const identity = identityOf(input, cwd, sid);
|
|
152
154
|
const isError = failed(input.tool_response);
|
|
153
|
-
const body = bodyFor(input, isError);
|
|
155
|
+
const body = bodyFor(input, isError, policyFrom(resolved.config));
|
|
154
156
|
if (!body.trim())
|
|
155
157
|
return 0;
|
|
156
158
|
record(db, resolved, identity, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture-tool.js","sourceRoot":"","sources":["../../src/hooks/capture-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"capture-tool.js","sourceRoot":"","sources":["../../src/hooks/capture-tool.ts"],"names":[],"mappings":"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"}
|
package/dist/memory/capture.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nowIso } from '../time.js';
|
|
2
2
|
import { eventUid, relativeToProject } from './identity.js';
|
|
3
|
-
import { DEFAULT_PRIVACY, isOwnTraffic, pathExcluded, redact, toolExcluded } from './privacy.js';
|
|
3
|
+
import { DEFAULT_PRIVACY, isOwnTraffic, pathExcluded, redact, redactHead, toolExcluded } from './privacy.js';
|
|
4
4
|
import { appendEvent } from './store.js';
|
|
5
5
|
import { spoolEvent, takeSpooled } from './spool.js';
|
|
6
6
|
/** Per-event body cap. Evidence is a pointer to work, not a copy of the repo. */
|
|
@@ -26,9 +26,13 @@ export function policyFrom(config) {
|
|
|
26
26
|
redactPatterns: config.privacy.redact_patterns,
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* A body as evidence stores it: redacted over the window, then capped. The
|
|
31
|
+
* window's own cut is handled by `redactHead`, so a secret straddling it leaves
|
|
32
|
+
* no recognisable-only-when-whole prefix behind.
|
|
33
|
+
*/
|
|
30
34
|
export function redactBody(body, policy = DEFAULT_PRIVACY) {
|
|
31
|
-
const redacted =
|
|
35
|
+
const redacted = redactHead(body, REDACT_WINDOW, policy);
|
|
32
36
|
return { ...redacted, text: redacted.text.slice(0, MAX_BODY) };
|
|
33
37
|
}
|
|
34
38
|
/** Everything but the write: pure, so the exclusion rules are testable alone. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/memory/capture.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAyB,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAsB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/memory/capture.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAyB,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAsB,MAAM,cAAc,CAAC;AACjI,OAAO,EAAE,WAAW,EAAsB,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAiCrD,iFAAiF;AACjF,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,QAAQ,GAAG,CAAC,CAAC;AAEnC;;;;GAIG;AACH,MAAM,aAAa,GAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAEhF,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,OAAO;QACL,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;QAChF,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,YAAY,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;QAChF,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,eAAe;KAC/C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,SAAwB,eAAe;IAC9E,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IACzD,OAAO,EAAE,GAAG,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,OAAO,CACrB,MAAqB,EACrB,QAA0B,EAC1B,KAAgB;IAEhB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAC3E,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5F,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtD,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,iBAAiB;IACjB,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4EAA4E;IAC5E,0EAA0E;IAC1E,yEAAyE;IACzE,6EAA6E;IAC7E,kEAAkE;IAClE,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,MAAM,EAAE,CAAC;IAEhD,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC;YACjB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU;YACV,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC;QACF,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QAC5D,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/D,UAAU;QACV,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,EAAM,EACN,MAAqB,EACrB,QAA0B,EAC1B,KAAgB;IAEhB,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC;IAC9B,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,EAAa,EACb,MAAqB,EACrB,QAA0B,EAC1B,KAAgB;IAEhB,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC;IAC9B,IAAI,EAAE,EAAE,CAAC;QACP,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5C,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,sBAAsB;QACxB,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,EAAM;IAC/B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IAC1C,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAuB,CAAC;QACtC,4EAA4E;QAC5E,4EAA4E;QAC5E,oEAAoE;QACpE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YAC7E,OAAO,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5C,IAAI,QAAQ;gBAAE,QAAQ,EAAE,CAAC;;gBACpB,OAAO,EAAE,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,2EAA2E;YAC3E,qEAAqE;YACrE,uEAAuE;YACvE,wEAAwE;YACxE,8DAA8D;YAC9D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC;QAChF,CAAC;IACH,CAAC;IACD,uEAAuE;IACvE,oCAAoC;IACpC,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
|
package/dist/memory/privacy.js
CHANGED
|
@@ -64,12 +64,22 @@ const BUILT_IN = [
|
|
|
64
64
|
* Group 1 is everything kept (key, separator, an auth scheme); group 2 the
|
|
65
65
|
* quote, group 3 a quoted value, group 4 a bare one.
|
|
66
66
|
*/
|
|
67
|
-
const
|
|
67
|
+
const ASSIGNED_KEPT = String.raw `((?<![A-Za-z0-9_.-])[A-Za-z0-9_.-]*?` +
|
|
68
68
|
String.raw `(?:password|passwd|passphrase|secret|credentials?|token|api[_-]?key|access[_-]?key|private[_-]?key|authorization)` +
|
|
69
69
|
String.raw `(?:[_.-]?key)?(?:[_.-]base)?["']?\]?[ \t]*(?::|=>?)(?![=:])[ \t]*` +
|
|
70
|
-
String.raw `(?:(?:basic|bearer|token|digest)[ \t]+)?)
|
|
70
|
+
String.raw `(?:(?:basic|bearer|token|digest)[ \t]+)?)`;
|
|
71
|
+
const ASSIGNED = new RegExp(ASSIGNED_KEPT +
|
|
71
72
|
String.raw `(?:(["'])(?!\[redacted)([^"'\n]{4,})\2` +
|
|
72
73
|
String.raw `|(?!\[redacted)([^\s"'\x60,;&(){}\[\]<>$][^\s"'\x60,;&<>)}\]]{5,}))`, 'gi');
|
|
74
|
+
/**
|
|
75
|
+
* A quoted assignment whose value runs to the end of the text without its
|
|
76
|
+
* closing quote: `password="hunter2` once a cut has taken the quote `ASSIGNED`
|
|
77
|
+
* needs. Any length and no state-word exemption, because an unterminated value
|
|
78
|
+
* is only a prefix — `expired` may be the start of `expired2024`. Anchored to
|
|
79
|
+
* the end of the text, not of a line, so a complete string that merely spans a
|
|
80
|
+
* line break is left to the rules above.
|
|
81
|
+
*/
|
|
82
|
+
const UNTERMINATED = new RegExp(ASSIGNED_KEPT + String.raw `(["'])(?!\[redacted)([^"'\n]+)$`, 'i');
|
|
73
83
|
/**
|
|
74
84
|
* Bare values that are code, not secrets. Judged only for an unquoted value: a
|
|
75
85
|
* quoted one is a literal whatever it says.
|
|
@@ -145,6 +155,12 @@ export function redact(text, policy = DEFAULT_PRIVACY) {
|
|
|
145
155
|
assigned = true;
|
|
146
156
|
return quoted !== undefined ? `${kept}${quote}[redacted:secret]${quote}` : `${kept}[redacted:secret]`;
|
|
147
157
|
});
|
|
158
|
+
// Linear: the value class stops at a newline, so each candidate key scans
|
|
159
|
+
// at most the rest of its own line before the end anchor fails.
|
|
160
|
+
out = out.replace(new RegExp(UNTERMINATED.source, UNTERMINATED.flags), (_m, kept, quote) => {
|
|
161
|
+
assigned = true;
|
|
162
|
+
return `${kept}${quote}[redacted:secret]`;
|
|
163
|
+
});
|
|
148
164
|
if (assigned)
|
|
149
165
|
kinds.push('assigned-secret');
|
|
150
166
|
for (const source of policy.redactPatterns) {
|
|
@@ -165,6 +181,62 @@ export function redact(text, policy = DEFAULT_PRIVACY) {
|
|
|
165
181
|
}
|
|
166
182
|
return { text: out, redacted: kinds.length > 0, kinds: [...new Set(kinds)] };
|
|
167
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* How far back from a cut a partial word is dropped. Longer than every vendor
|
|
186
|
+
* token shape above, so a key the cut left too short to recognise goes whole;
|
|
187
|
+
* bounded, so a cut through a long unbroken dump costs this much and no more.
|
|
188
|
+
*/
|
|
189
|
+
const CUT_GUARD = 256;
|
|
190
|
+
/** `text` without the word a cut at its end went through. */
|
|
191
|
+
function dropCutHead(text) {
|
|
192
|
+
let i = text.length;
|
|
193
|
+
const stop = Math.max(0, text.length - CUT_GUARD);
|
|
194
|
+
while (i > stop && !/\s/.test(text[i - 1]))
|
|
195
|
+
i--;
|
|
196
|
+
return text.slice(0, i > stop ? i : stop);
|
|
197
|
+
}
|
|
198
|
+
/** `text` without the word a cut at its start went through. */
|
|
199
|
+
function dropCutTail(text) {
|
|
200
|
+
let i = 0;
|
|
201
|
+
const stop = Math.min(text.length, CUT_GUARD);
|
|
202
|
+
while (i < stop && !/\s/.test(text[i]))
|
|
203
|
+
i++;
|
|
204
|
+
return text.slice(i < stop ? i : stop);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The first `window` characters of `text`, redacted, handling the cut.
|
|
208
|
+
*
|
|
209
|
+
* A cut can land inside a secret and leave a piece no pattern recognises: a
|
|
210
|
+
* quoted value without its closing quote, a bare value under the six
|
|
211
|
+
* characters `ASSIGNED` wants, or a vendor key under its minimum length. So
|
|
212
|
+
* when the window did cut, the partial word at the cut is dropped, and an
|
|
213
|
+
* unterminated quoted value before it is redacted by `redact` itself.
|
|
214
|
+
*/
|
|
215
|
+
export function redactHead(text, window, policy = DEFAULT_PRIVACY) {
|
|
216
|
+
return redact(text.length > window ? dropCutHead(text.slice(0, window)) : text, policy);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The last `window` characters of `text`, redacted, handling the cut: the
|
|
220
|
+
* partial word at the start goes, since a suffix of a secret has lost the key
|
|
221
|
+
* or prefix that would identify it.
|
|
222
|
+
*/
|
|
223
|
+
export function redactTail(text, window, policy = DEFAULT_PRIVACY) {
|
|
224
|
+
return redact(text.length > window ? dropCutTail(text.slice(-window)) : text, policy);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* `text` cut to `max` characters, redacted first. A cut made before redaction
|
|
228
|
+
* can land inside a secret and leave a head no pattern recognises
|
|
229
|
+
* (`DB_PASSWORD="hunter2` has lost the quote the rule needs), so every place
|
|
230
|
+
* that trims a piece of a body goes through here, not `.slice()`. Pass the
|
|
231
|
+
* configured policy so configured patterns see the text before the cut too.
|
|
232
|
+
*/
|
|
233
|
+
export function clip(text, max, policy = DEFAULT_PRIVACY) {
|
|
234
|
+
return redactHead(text, max * 4, policy).text.slice(0, max);
|
|
235
|
+
}
|
|
236
|
+
/** The last `max` characters of `text`, redacted before the cut. */
|
|
237
|
+
export function clipTail(text, max, policy = DEFAULT_PRIVACY) {
|
|
238
|
+
return redactTail(text, max * 4, policy).text.slice(-max);
|
|
239
|
+
}
|
|
168
240
|
/**
|
|
169
241
|
* True when this path must never be captured at all.
|
|
170
242
|
*
|
|
@@ -178,15 +250,6 @@ export function redact(text, policy = DEFAULT_PRIVACY) {
|
|
|
178
250
|
* common case costs nothing, and a path that does not exist keeps the answer
|
|
179
251
|
* the literal comparison gave.
|
|
180
252
|
*/
|
|
181
|
-
/**
|
|
182
|
-
* `text` cut to `max` characters, redacted first. A cut made before redaction
|
|
183
|
-
* can land inside a secret and leave a head no pattern recognises
|
|
184
|
-
* (`DB_PASSWORD="hunter2` has lost the quote the rule needs), so every place
|
|
185
|
-
* that trims a piece of a body goes through here, not `.slice()`.
|
|
186
|
-
*/
|
|
187
|
-
export function clip(text, max) {
|
|
188
|
-
return redact(text.slice(0, max * 4)).text.slice(0, max);
|
|
189
|
-
}
|
|
190
253
|
export function pathExcluded(file, policy = DEFAULT_PRIVACY) {
|
|
191
254
|
const matches = (candidate) => {
|
|
192
255
|
const normalised = candidate.replace(/\\/g, '/').toLowerCase();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAwBzB,MAAM,CAAC,MAAM,eAAe,GAAkB;IAC5C,YAAY,EAAE;QACZ,MAAM;QACN,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,YAAY;QACZ,MAAM;QACN,MAAM;QACN,WAAW;QACX,kBAAkB;QAClB,QAAQ;QACR,QAAQ;QACR,UAAU;QACV,uBAAuB;KACxB;IACD,YAAY,EAAE,EAAE;IAChB,cAAc,EAAE,EAAE;CACnB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,QAAQ,GAAmC;IAC/C,+EAA+E;IAC/E,uEAAuE;IACvE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,mFAAmF,EAAE;IAChH,uEAAuE;IACvE,wEAAwE;IACxE,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAE,mEAAmE,EAAE;IACpG,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,8BAA8B,EAAE;IAC7D,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,sEAAsE,EAAE;IAClG,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,yCAAyC,EAAE;IACrE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,yDAAyD,EAAE;IACvF,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,6BAA6B,EAAE;IAC3D,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,0BAA0B,EAAE;IACrD,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,gCAAgC,EAAE;IAC5D,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,iCAAiC,EAAE;IAC9D,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,4BAA4B,EAAE;IACxD,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,kEAAkE,EAAE;IACvF,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,wCAAwC,EAAE;CACjE,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"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"}
|
package/dist/memory/replay.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { identityFor } from './identity.js';
|
|
5
|
-
import { prepare } from './capture.js';
|
|
5
|
+
import { policyFrom, prepare } from './capture.js';
|
|
6
6
|
import { clip } from './privacy.js';
|
|
7
7
|
import { appendEvent } from './store.js';
|
|
8
8
|
/**
|
|
@@ -64,19 +64,19 @@ function kindFor(tool) {
|
|
|
64
64
|
return 'file_read';
|
|
65
65
|
return 'tool_use';
|
|
66
66
|
}
|
|
67
|
-
function bodyFor(input) {
|
|
67
|
+
function bodyFor(input, policy) {
|
|
68
68
|
const parts = [];
|
|
69
69
|
for (const key of ['command', 'description', 'prompt', 'pattern', 'query']) {
|
|
70
70
|
if (typeof input[key] === 'string')
|
|
71
71
|
parts.push(input[key]);
|
|
72
72
|
}
|
|
73
73
|
if (typeof input.old_string === 'string' && typeof input.new_string === 'string') {
|
|
74
|
-
parts.push(`- ${clip(input.old_string, 600)}`, `+ ${clip(input.new_string, 600)}`);
|
|
74
|
+
parts.push(`- ${clip(input.old_string, 600, policy)}`, `+ ${clip(input.new_string, 600, policy)}`);
|
|
75
75
|
}
|
|
76
76
|
if (!parts.length) {
|
|
77
77
|
const keys = Object.keys(input).filter((k) => k !== 'content');
|
|
78
78
|
if (keys.length)
|
|
79
|
-
parts.push(keys.map((k) => `${k}=${clip(String(input[k]), 200)}`).join(' '));
|
|
79
|
+
parts.push(keys.map((k) => `${k}=${clip(String(input[k]), 200, policy)}`).join(' '));
|
|
80
80
|
}
|
|
81
81
|
return parts.join('\n');
|
|
82
82
|
}
|
|
@@ -100,6 +100,7 @@ export function replayTranscript(db, config, file, opts = {}) {
|
|
|
100
100
|
catch {
|
|
101
101
|
return result;
|
|
102
102
|
}
|
|
103
|
+
const policy = policyFrom(config);
|
|
103
104
|
const lines = text.split('\n').filter(Boolean).slice(0, opts.maxLines ?? 20_000);
|
|
104
105
|
for (const raw of lines) {
|
|
105
106
|
let line;
|
|
@@ -133,7 +134,7 @@ export function replayTranscript(db, config, file, opts = {}) {
|
|
|
133
134
|
}
|
|
134
135
|
else {
|
|
135
136
|
for (const use of toolUsesOf(line.message?.content)) {
|
|
136
|
-
const body = bodyFor(use.input);
|
|
137
|
+
const body = bodyFor(use.input, policy);
|
|
137
138
|
if (!body.trim())
|
|
138
139
|
continue;
|
|
139
140
|
events.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/memory/replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAkB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/memory/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eklavya",
|
|
3
3
|
"displayName": "Eklavya",
|
|
4
|
-
"version": "1.47.
|
|
4
|
+
"version": "1.47.4",
|
|
5
5
|
"description": "Learn while your agent works. Turns coding-agent generation time into adaptive, Socratic learning grounded in the code being written.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ajay Kumar"
|
|
@@ -72,13 +72,15 @@ The implementation is happening in files you can read. Use `Read` and `Grep` to
|
|
|
72
72
|
|
|
73
73
|
Do not guess at code you have not read. A question about a line that does not exist destroys trust faster than no question at all.
|
|
74
74
|
|
|
75
|
+
You read the code; the developer usually has not. Write every question for a reader who saw none of the code, the plan or the conversation: give each project-specific name a defining clause or drop it, and make sure the right answer follows from the concept, not from something only you looked at. `writing-mcq.md`, *Write for a cold reader*, has the rule. If they reply that a question lacks context, give the answer and one line of why, and record it as `dont_know` — `grading.md` has the steps.
|
|
76
|
+
|
|
75
77
|
## Session
|
|
76
78
|
|
|
77
79
|
You share the knowledge database with the session that spawned you. Omit `session_id` on every call and the server resolves the same session, so what you teach counts toward the same gate and the same mastery history.
|
|
78
80
|
|
|
79
81
|
## Pacing
|
|
80
82
|
|
|
81
|
-
You are competing for attention with an agent that is producing code. Ask one question, then wait. If the developer does not answer, do not chase them — they are
|
|
83
|
+
You are competing for attention with an agent that is producing code. Ask one question, then wait. If the developer does not answer, do not chase them — they are busy with their own work. Silence is a legitimate answer and costs nothing.
|
|
82
84
|
|
|
83
85
|
Never record a grade for a question that was not answered. Record a decline (`outcome: "declined"`) only when they actually say so.
|
|
84
86
|
|
|
@@ -27,6 +27,7 @@ yourself thinking one, the right-hand column is what is actually happening.
|
|
|
27
27
|
| "I'll reword the question they got wrong." | A question in `asked_before` is spent, not recyclable. |
|
|
28
28
|
| "A definition is the transferable version of this." | `concept` focus wants the general rule. "What is X" is tier-1 recall wearing a hat. |
|
|
29
29
|
| "Too easy to be worth asking — I'll add a 'why'." | `easy` is tiers 1–2 and it was earned. A smuggled "why" is a question they cannot answer honestly. |
|
|
30
|
+
| "They know what 'Task 6' means." | They saw none of it. Define the name or drop it. |
|
|
30
31
|
| "I'll put the dials above the stem for context." | The dials are in the status bar. A line you assemble is one the server cannot keep consistent. |
|
|
31
32
|
| "Grade 5 — they picked the right option." | Multiple choice caps at 4. One in four is a coin. |
|
|
32
33
|
|
|
@@ -50,7 +50,9 @@ The code is the subject. Name the file, the line, the decision.
|
|
|
50
50
|
The same subject matter, asked so the answer transfers to a different codebase.
|
|
51
51
|
**This does not mean textbook questions.** The diff stops being the *subject*
|
|
52
52
|
and becomes the *motivation*: open from what was just written, then ask for the
|
|
53
|
-
general rule, the class of problem, or where else it applies.
|
|
53
|
+
general rule, the class of problem, or where else it applies. Open from it in
|
|
54
|
+
words the learner can follow without having read it — they usually have not
|
|
55
|
+
(`writing-mcq.md`, *Write for a cold reader*).
|
|
54
56
|
|
|
55
57
|
- Right: *"We gave the profile cache a 60s TTL in `profile.ts`. TTL is one
|
|
56
58
|
answer to cache invalidation — what problem is it actually solving, and what
|
|
@@ -114,6 +114,23 @@ it rebuilds footing. If they want to stop, they will say so; wait to be told.
|
|
|
114
114
|
**Never dump the remaining answers as a list.** If the quiz ends early, it ends.
|
|
115
115
|
A wall of four explanations at the door is not teaching, it is a receipt.
|
|
116
116
|
|
|
117
|
+
## When they say the question lacks context
|
|
118
|
+
|
|
119
|
+
"This question does not have enough context" is a verdict on the question, not
|
|
120
|
+
a decline. The question assumed something they never saw, so the miss is
|
|
121
|
+
yours. Never drop it untaught:
|
|
122
|
+
|
|
123
|
+
1. **Own it** in a few words — the question leaned on something you had not
|
|
124
|
+
shown them.
|
|
125
|
+
2. **Give the answer and one line of why**, defining the name or situation the
|
|
126
|
+
stem skipped.
|
|
127
|
+
3. **Record it as a blank**: `grade: 0`, `outcome: "dont_know"`, with that
|
|
128
|
+
answer in `feedback`. It is not `declined` — a decline is never offered
|
|
129
|
+
again, and they did not ask to be left alone.
|
|
130
|
+
|
|
131
|
+
The next question about it, now or later, starts with the situation sentence
|
|
132
|
+
the first one was missing (`writing-mcq.md`, *Write for a cold reader*).
|
|
133
|
+
|
|
117
134
|
## `already_taught`
|
|
118
135
|
|
|
119
136
|
When it is true on a plan item, they blanked on this before and you explained
|
|
@@ -3,16 +3,54 @@
|
|
|
3
3
|
Required reading before you write a question. `SKILL.md` decides *whether* to
|
|
4
4
|
ask and *at what tier*; this is how to build the thing itself.
|
|
5
5
|
|
|
6
|
+
## Write for a cold reader
|
|
7
|
+
|
|
8
|
+
Assume the learner saw none of it: not the code, not the plan, not the task
|
|
9
|
+
list, not the conversation. A background agent usually writes the code, so the
|
|
10
|
+
diff you are looking at is one they have never opened. The question has to work
|
|
11
|
+
from the stem and the four options alone.
|
|
12
|
+
|
|
13
|
+
- **Every project-specific name gets a defining clause, or goes.** "Task 6",
|
|
14
|
+
"the brief", "the plan", "the island", a component nickname, a file they never
|
|
15
|
+
opened: each one is a puzzle the learner must solve before the real question.
|
|
16
|
+
Say what it is — *"the hook that runs when a session starts"* — or ask
|
|
17
|
+
without it.
|
|
18
|
+
- **The key follows from the concept, never from something only you
|
|
19
|
+
observed.** A finding in a write-up they never read, the data from one
|
|
20
|
+
session, your own experiment, the keyframes in a file they never opened: a
|
|
21
|
+
question whose answer depends on these is unfair, not hard.
|
|
22
|
+
- **Situations beat abstractions.** A stem with no host, no situation and no
|
|
23
|
+
example cannot be placed. Name the setting in plain words.
|
|
24
|
+
- **Drop the name, keep the constraint.** The project's details are often what
|
|
25
|
+
made the key the only right answer: *no stored state*, *runs on every prompt*,
|
|
26
|
+
*one writer*. When you replace a name with a general situation, carry those
|
|
27
|
+
constraints into it, or a distractor that was wrong in the project becomes
|
|
28
|
+
right in the abstract.
|
|
29
|
+
|
|
30
|
+
> Opaque: *Task 6 moves the instruction from SessionStart to UserPromptSubmit.
|
|
31
|
+
> What does that change?*
|
|
32
|
+
>
|
|
33
|
+
> Cold: *A Claude Code hook can inject text once when a session starts, or on
|
|
34
|
+
> every prompt the user sends. Why move a reminder to the per-prompt hook?*
|
|
35
|
+
|
|
6
36
|
## Build it in this order
|
|
7
37
|
|
|
8
38
|
Six parts, then the call. Build them in the order below — not the order they
|
|
9
39
|
appear on screen — because each one constrains the next.
|
|
10
40
|
|
|
11
|
-
**1. The stem.**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
41
|
+
**1. The stem.** Two parts, grounded per the plan's `framing`:
|
|
42
|
+
|
|
43
|
+
- **The situation** — one plain sentence of 25 words or fewer, saying what is
|
|
44
|
+
happening and defining any name the question uses. One sentence, not two:
|
|
45
|
+
if it will not fit, the question is carrying more setup than one idea needs.
|
|
46
|
+
Leave it out when the question already stands on its own.
|
|
47
|
+
- **The question** — one question about one idea, 25 words or fewer. The
|
|
48
|
+
answer lives in the options, so the question asks and stops.
|
|
49
|
+
|
|
50
|
+
Ordinary words: "sent with the request" rather than "transmitted alongside the
|
|
51
|
+
request context". Expand an acronym the first time it appears for this learner —
|
|
52
|
+
CSRF once, then CSRF. The situation sets the scene and never hints at the
|
|
53
|
+
answer.
|
|
16
54
|
|
|
17
55
|
Ask the positive form, and ask it once: negation makes the stem a reading test
|
|
18
56
|
rather than a question about the idea, and a stem needing a second clause to be
|
|
@@ -23,6 +61,12 @@ precise is doing two jobs — ask the first one and keep the other for later.
|
|
|
23
61
|
**2. The correct option.** Write it before the distractors. It sets the length
|
|
24
62
|
and the grammar the other three have to match.
|
|
25
63
|
|
|
64
|
+
Each option is a complete claim on its own: read alone, it says something that
|
|
65
|
+
is true or false. "Re-point from an ancestor" or "How often, and how recent" is
|
|
66
|
+
a riddle, not an option. When the options differ in length, meet parity by
|
|
67
|
+
lengthening the short ones, never by cutting every option down until none of
|
|
68
|
+
them says anything.
|
|
69
|
+
|
|
26
70
|
**3. Three distractors**, each drawn from one of these:
|
|
27
71
|
|
|
28
72
|
| Source | Written against the stem above |
|
|
@@ -92,8 +136,14 @@ quietly drops one of its fields is the other case, and it is what
|
|
|
92
136
|
Read yours against this. Each line is a property of the question, so a "no" is
|
|
93
137
|
telling you which part to rebuild.
|
|
94
138
|
|
|
95
|
-
-
|
|
139
|
+
- Someone who saw none of the code, plan or conversation could answer it: every
|
|
140
|
+
project-specific name is defined, and the key follows from the concept.
|
|
141
|
+
- The stem asks one thing: at most one sentence of situation, then a question,
|
|
142
|
+
each 25 words or fewer.
|
|
143
|
+
- Read without the project, every wrong option is still wrong: the situation
|
|
144
|
+
kept the constraints the names carried.
|
|
96
145
|
- The answer appears among the options and nowhere in the stem.
|
|
146
|
+
- Each option is a complete claim when read on its own.
|
|
97
147
|
- All four options are within a few words of the same length and use the same
|
|
98
148
|
grammar. A visibly longer or more careful option reads as the correct one, and
|
|
99
149
|
gets picked without engaging — the same leak as always answering first.
|
package/package.json
CHANGED