koneck 2.68.0 → 2.69.0
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/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +98 -9
- package/dist/ink-chat.js.map +1 -1
- package/dist/mention-resolve.d.ts +64 -0
- package/dist/mention-resolve.d.ts.map +1 -0
- package/dist/mention-resolve.js +139 -0
- package/dist/mention-resolve.js.map +1 -0
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +63 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/events.d.ts +13 -0
- package/dist/web/events.d.ts.map +1 -1
- package/dist/web/events.js.map +1 -1
- package/dist/web/mention-signals.d.ts +41 -0
- package/dist/web/mention-signals.d.ts.map +1 -0
- package/dist/web/mention-signals.js +117 -0
- package/dist/web/mention-signals.js.map +1 -0
- package/dist/web/mention.d.ts +121 -0
- package/dist/web/mention.d.ts.map +1 -0
- package/dist/web/mention.js +211 -0
- package/dist/web/mention.js.map +1 -0
- package/dist/web/sessions.d.ts.map +1 -1
- package/dist/web/sessions.js +23 -2
- package/dist/web/sessions.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +270 -1
- package/dist/web/ui-client.js.map +1 -1
- package/dist/web/ui-css.d.ts.map +1 -1
- package/dist/web/ui-css.js +39 -0
- package/dist/web/ui-css.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +9 -0
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/web/events.d.ts
CHANGED
|
@@ -28,6 +28,19 @@ export type WebEvent =
|
|
|
28
28
|
seq: number;
|
|
29
29
|
text: string;
|
|
30
30
|
images?: number;
|
|
31
|
+
/**
|
|
32
|
+
* What an `@` in the message actually attached.
|
|
33
|
+
*
|
|
34
|
+
* Reported rather than assumed, because attaching a file spends tokens and a spend nobody can
|
|
35
|
+
* see is a spend nobody can question. The transcript shows the sentence as typed; this says
|
|
36
|
+
* what was added beneath it, and what could not be.
|
|
37
|
+
*/
|
|
38
|
+
refs?: Array<{
|
|
39
|
+
ref: string;
|
|
40
|
+
kind: string;
|
|
41
|
+
lines: number | null;
|
|
42
|
+
note?: string;
|
|
43
|
+
}>;
|
|
31
44
|
}
|
|
32
45
|
/** A piece of the reply as it streams. */
|
|
33
46
|
| {
|
package/dist/web/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/web/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ;AAClB,wCAAwC;AACtC;IAAE,CAAC,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC5G,gFAAgF;GAC9E;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/web/events.ts"],"names":[],"mappings":"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"}
|
package/dist/web/events.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/web/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/web/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAmEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAalC,MAAM,OAAO,QAAQ;IACF,GAAG,GAAe,EAAE,CAAC;IACrB,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IACpD,GAAG,GAAG,CAAC,CAAC;IAEhB,oEAAoE;IACpE,IAAI,CAAC,KAAc;QACjB,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,EAAc,CAAC;QACnF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,YAAY;YAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QACrD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,4EAA4E;YAC5E,IAAI,CAAC;gBAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,sCAAsC,CAAC,CAAC;QACxE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,KAAK,GAAG,CAAC;QACb,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC;IAC1E,CAAC;IAED,SAAS,CAAC,EAAyB;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,QAAQ,KAAa,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,IAAI,OAAO,KAAa,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the workspace knows about which file you meant.
|
|
3
|
+
*
|
|
4
|
+
* `rankMentions` takes three lists and stays pure so it can be tested against fixed input. This is
|
|
5
|
+
* where the lists come from, and each one is cheap on purpose: a picker that reads the ledger and
|
|
6
|
+
* shells out to git on every keystroke stops being a picker and becomes a wait. So the signals are
|
|
7
|
+
* gathered once when the dropdown opens and reused for the rest of the typing.
|
|
8
|
+
*/
|
|
9
|
+
import type { TrajectoryEvent } from '../trajectory.js';
|
|
10
|
+
import type { Signals } from './mention.js';
|
|
11
|
+
/**
|
|
12
|
+
* Files this session has actually handled, most recent first.
|
|
13
|
+
*
|
|
14
|
+
* Read from the trajectory rather than tracked separately, because the trajectory is already the
|
|
15
|
+
* record of what happened and a second list of the same thing is a second list to get wrong. Only
|
|
16
|
+
* the tools whose detail is a path: `execute_command` puts a command line there and `grep_search`
|
|
17
|
+
* puts a pattern, and either one offered as a filename is noise.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sessionPaths(events: readonly TrajectoryEvent[], limit?: number): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Files git has touched lately, uncommitted first.
|
|
22
|
+
*
|
|
23
|
+
* Uncommitted first because that is a stronger statement about what the project is in the middle of
|
|
24
|
+
* than a commit from yesterday: work in the tree is work in progress. Then the recent commits,
|
|
25
|
+
* which is how a file you finished this morning stays near the top.
|
|
26
|
+
*
|
|
27
|
+
* Failure is silence. A directory that is not a repository is a perfectly ordinary place to work,
|
|
28
|
+
* and a picker that reported an error for it would be reporting its own assumption.
|
|
29
|
+
*/
|
|
30
|
+
export declare function gitRecent(cwd: string, limit?: number): Promise<string[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Files the ledger holds findings about, newest first.
|
|
33
|
+
*
|
|
34
|
+
* This is the signal no other picker has, because no other picker has the ledger: it is where the
|
|
35
|
+
* things that cost somebody an afternoon were written down. A file with a finding against it is a
|
|
36
|
+
* file where surprises live, and it belongs near the top of a list of files worth showing the model.
|
|
37
|
+
*/
|
|
38
|
+
export declare function notedPaths(cwd: string, limit?: number): Promise<string[]>;
|
|
39
|
+
/** All three, gathered together. */
|
|
40
|
+
export declare function gatherSignals(cwd: string, events?: readonly TrajectoryEvent[]): Promise<Signals>;
|
|
41
|
+
//# sourceMappingURL=mention-signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-signals.d.ts","sourceRoot":"","sources":["../../src/web/mention-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAO5C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,EAAE,KAAK,SAAK,GAAG,MAAM,EAAE,CAgBrF;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA8B1E;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB3E;AAED,oCAAoC;AACpC,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,SAAS,eAAe,EAAO,GACtC,OAAO,CAAC,OAAO,CAAC,CAGlB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the workspace knows about which file you meant.
|
|
3
|
+
*
|
|
4
|
+
* `rankMentions` takes three lists and stays pure so it can be tested against fixed input. This is
|
|
5
|
+
* where the lists come from, and each one is cheap on purpose: a picker that reads the ledger and
|
|
6
|
+
* shells out to git on every keystroke stops being a picker and becomes a wait. So the signals are
|
|
7
|
+
* gathered once when the dropdown opens and reused for the rest of the typing.
|
|
8
|
+
*/
|
|
9
|
+
import { readAll } from '../ledger.js';
|
|
10
|
+
/** Tools whose detail is a path rather than a command or a pattern. */
|
|
11
|
+
const PATH_TOOLS = new Set([
|
|
12
|
+
'read_files', 'write_file', 'search_replace', 'apply_diff', 'delete_file', 'move_file', 'list_dir',
|
|
13
|
+
]);
|
|
14
|
+
/**
|
|
15
|
+
* Files this session has actually handled, most recent first.
|
|
16
|
+
*
|
|
17
|
+
* Read from the trajectory rather than tracked separately, because the trajectory is already the
|
|
18
|
+
* record of what happened and a second list of the same thing is a second list to get wrong. Only
|
|
19
|
+
* the tools whose detail is a path: `execute_command` puts a command line there and `grep_search`
|
|
20
|
+
* puts a pattern, and either one offered as a filename is noise.
|
|
21
|
+
*/
|
|
22
|
+
export function sessionPaths(events, limit = 40) {
|
|
23
|
+
const out = [];
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (let i = events.length - 1; i >= 0 && out.length < limit; i--) {
|
|
26
|
+
const event = events[i];
|
|
27
|
+
if (event.t !== 'tool' || !PATH_TOOLS.has(event.name))
|
|
28
|
+
continue;
|
|
29
|
+
const detail = String(event.detail ?? '').trim();
|
|
30
|
+
// Absolute paths and traversal are outside the workspace listing, so they can never match a
|
|
31
|
+
// candidate and only take up a slot.
|
|
32
|
+
if (detail === '' || detail.startsWith('/') || detail.includes('..'))
|
|
33
|
+
continue;
|
|
34
|
+
const it = detail.replace(/^\.\//, '');
|
|
35
|
+
if (seen.has(it))
|
|
36
|
+
continue;
|
|
37
|
+
seen.add(it);
|
|
38
|
+
out.push(it);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Files git has touched lately, uncommitted first.
|
|
44
|
+
*
|
|
45
|
+
* Uncommitted first because that is a stronger statement about what the project is in the middle of
|
|
46
|
+
* than a commit from yesterday: work in the tree is work in progress. Then the recent commits,
|
|
47
|
+
* which is how a file you finished this morning stays near the top.
|
|
48
|
+
*
|
|
49
|
+
* Failure is silence. A directory that is not a repository is a perfectly ordinary place to work,
|
|
50
|
+
* and a picker that reported an error for it would be reporting its own assumption.
|
|
51
|
+
*/
|
|
52
|
+
export async function gitRecent(cwd, limit = 40) {
|
|
53
|
+
const { execa } = await import('execa');
|
|
54
|
+
const run = async (args) => {
|
|
55
|
+
try {
|
|
56
|
+
const done = await execa('git', args, { cwd, reject: false, timeout: 4_000 });
|
|
57
|
+
if (done.exitCode !== 0)
|
|
58
|
+
return [];
|
|
59
|
+
return String(done.stdout ?? '').split('\n').map(line => line.trim()).filter(Boolean);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
// --porcelain lines are "XY path", and a rename is "R old -> new", where the new name is the one
|
|
66
|
+
// that exists to be referenced.
|
|
67
|
+
const dirty = (await run(['status', '--porcelain', '--no-renames']))
|
|
68
|
+
.map(line => line.slice(3).trim())
|
|
69
|
+
.map(p => (p.includes(' -> ') ? p.split(' -> ').pop().trim() : p));
|
|
70
|
+
const committed = await run(['log', '-n', '12', '--name-only', '--pretty=format:', '--no-renames']);
|
|
71
|
+
const out = [];
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
for (const p of [...dirty, ...committed]) {
|
|
74
|
+
const it = p.replace(/^"|"$/g, '').replace(/^\.\//, '');
|
|
75
|
+
if (it === '' || seen.has(it))
|
|
76
|
+
continue;
|
|
77
|
+
seen.add(it);
|
|
78
|
+
out.push(it);
|
|
79
|
+
if (out.length >= limit)
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Files the ledger holds findings about, newest first.
|
|
86
|
+
*
|
|
87
|
+
* This is the signal no other picker has, because no other picker has the ledger: it is where the
|
|
88
|
+
* things that cost somebody an afternoon were written down. A file with a finding against it is a
|
|
89
|
+
* file where surprises live, and it belongs near the top of a list of files worth showing the model.
|
|
90
|
+
*/
|
|
91
|
+
export async function notedPaths(cwd, limit = 40) {
|
|
92
|
+
let findings;
|
|
93
|
+
try {
|
|
94
|
+
findings = await readAll(cwd);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const out = [];
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
for (let i = findings.length - 1; i >= 0 && out.length < limit; i--) {
|
|
102
|
+
for (const p of findings[i].paths ?? []) {
|
|
103
|
+
const it = String(p).replace(/^\.\//, '');
|
|
104
|
+
if (it === '' || seen.has(it))
|
|
105
|
+
continue;
|
|
106
|
+
seen.add(it);
|
|
107
|
+
out.push(it);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/** All three, gathered together. */
|
|
113
|
+
export async function gatherSignals(cwd, events = []) {
|
|
114
|
+
const [recent, noted] = await Promise.all([gitRecent(cwd), notedPaths(cwd)]);
|
|
115
|
+
return { recent, noted, session: sessionPaths(events) };
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=mention-signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-signals.js","sourceRoot":"","sources":["../../src/web/mention-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,uEAAuE;AACvE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU;CACnG,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,MAAkC,EAAE,KAAK,GAAG,EAAE;IACzE,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QACzB,IAAI,KAAK,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,4FAA4F;QAC5F,qCAAqC;QACrC,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAS;QAC/E,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,KAAK,GAAG,EAAE;IACrD,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,KAAK,EAAE,IAAc,EAAqB,EAAE;QACtD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9E,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YACnC,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;IAEF,kGAAkG;IAClG,gCAAgC;IAChC,MAAM,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;SACjE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtE,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC,CAAC;IAEpG,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QACxC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACb,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK;YAAE,MAAM;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,KAAK,GAAG,EAAE;IACtD,IAAI,QAA6C,CAAC;IAClD,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QACpE,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACzC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC1C,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YACxC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,SAAqC,EAAE;IAEvC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Referencing a file by name, and knowing what it costs before you do.
|
|
3
|
+
*
|
|
4
|
+
* The terminal has had `@` for a while: type it, get a subsequence match over every path in the
|
|
5
|
+
* workspace, pick one. The browser never got it, which is the gap this closes. But copying the
|
|
6
|
+
* terminal's version would have missed the two things that make this worth building rather than
|
|
7
|
+
* ticking off.
|
|
8
|
+
*
|
|
9
|
+
* ## Ranked by what this project is doing, not alphabetically
|
|
10
|
+
*
|
|
11
|
+
* A fuzzy match over four thousand paths answers "which files contain these letters". It does not
|
|
12
|
+
* answer "which file did you mean", and the difference is most of the value. KONECK already knows
|
|
13
|
+
* things no generic picker does:
|
|
14
|
+
*
|
|
15
|
+
* - which files git has touched recently — what the project is in the middle of;
|
|
16
|
+
* - which files this session has already read or written — what *you* are in the middle of;
|
|
17
|
+
* - which files the project ledger holds findings about — where the sharp edges were.
|
|
18
|
+
*
|
|
19
|
+
* A file carrying all three is almost certainly the one being reached for, and it is offered first
|
|
20
|
+
* with the reason attached, because a ranked list that will not say why it ranked something is a
|
|
21
|
+
* list you cannot correct.
|
|
22
|
+
*
|
|
23
|
+
* ## The cost is shown before the attachment is made
|
|
24
|
+
*
|
|
25
|
+
* Every candidate carries an estimate of what including it would spend. This matters here more than
|
|
26
|
+
* it might elsewhere: the person this was built for has run into truncated prompts and exhausted
|
|
27
|
+
* windows repeatedly, and "src/engine.ts ~48k tokens" is the difference between an informed choice
|
|
28
|
+
* and a surprise three turns later. It is an estimate and is labelled as one — four characters to
|
|
29
|
+
* the token is the usual rough figure and it is rough — but a rough number in front of the decision
|
|
30
|
+
* beats an exact one after it.
|
|
31
|
+
*
|
|
32
|
+
* A directory is offered as an outline rather than a concatenation, for the same reason: naming
|
|
33
|
+
* twenty files costs a few hundred tokens and pasting them costs a hundred thousand.
|
|
34
|
+
*/
|
|
35
|
+
/** Why a candidate is where it is in the list. Shown, because an unexplained ranking cannot be trusted. */
|
|
36
|
+
export type Because =
|
|
37
|
+
/** Changed in git recently — what the project is in the middle of. */
|
|
38
|
+
'recently changed'
|
|
39
|
+
/** Read or written by this session — what you are in the middle of. */
|
|
40
|
+
| 'used in this session'
|
|
41
|
+
/** The ledger holds a finding about it — where something was learned the hard way. */
|
|
42
|
+
| 'has notes';
|
|
43
|
+
export interface Candidate {
|
|
44
|
+
/** Repo-relative path. */
|
|
45
|
+
path: string;
|
|
46
|
+
kind: 'file' | 'dir';
|
|
47
|
+
/** Roughly what including it would spend, or null for a directory, which is offered as an outline. */
|
|
48
|
+
tokens: number | null;
|
|
49
|
+
/** Bytes on disk, when known. */
|
|
50
|
+
bytes: number | null;
|
|
51
|
+
/** Every reason this ranked where it did, in the order they were applied. */
|
|
52
|
+
because: Because[];
|
|
53
|
+
}
|
|
54
|
+
/** What the workspace knows that a filename match does not. */
|
|
55
|
+
export interface Signals {
|
|
56
|
+
/** Files git has touched recently, newest first. */
|
|
57
|
+
recent?: readonly string[];
|
|
58
|
+
/** Files this session has read or written. */
|
|
59
|
+
session?: readonly string[];
|
|
60
|
+
/** Files the ledger holds findings about. */
|
|
61
|
+
noted?: readonly string[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Four characters to the token.
|
|
65
|
+
*
|
|
66
|
+
* The usual rough figure for English and code alike. Deliberately not a real tokeniser: loading one
|
|
67
|
+
* to put an advisory number beside forty filenames would cost more than the number is worth, and
|
|
68
|
+
* being out by a fifth does not change the decision anybody makes with it.
|
|
69
|
+
*/
|
|
70
|
+
export declare const CHARS_PER_TOKEN = 4;
|
|
71
|
+
export declare function tokensForBytes(bytes: number): number;
|
|
72
|
+
/**
|
|
73
|
+
* The reasons a path carries, in a fixed order.
|
|
74
|
+
*
|
|
75
|
+
* Fixed so the list reads the same way every time — a reason that moves around between keystrokes
|
|
76
|
+
* makes the ranking look arbitrary even when it is not.
|
|
77
|
+
*/
|
|
78
|
+
export declare function reasonsFor(p: string, signals: Signals): Because[];
|
|
79
|
+
/**
|
|
80
|
+
* The candidates for a query, best first.
|
|
81
|
+
*
|
|
82
|
+
* An empty query is not an empty answer: it offers what the project is doing — recent, in-session,
|
|
83
|
+
* noted — which is the list somebody wants when they type `@` and pause. A generic picker shows the
|
|
84
|
+
* alphabet at that moment, which is never what was meant.
|
|
85
|
+
*/
|
|
86
|
+
export declare function rankMentions(entries: readonly string[], query: string, signals?: Signals, limit?: number): Array<{
|
|
87
|
+
path: string;
|
|
88
|
+
score: number;
|
|
89
|
+
because: Because[];
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Fills in size and cost, and says which are directories.
|
|
93
|
+
*
|
|
94
|
+
* Done for the shown candidates only. Sizing four thousand paths to display forty is the kind of
|
|
95
|
+
* work that makes a picker feel slow for no reason anybody can see.
|
|
96
|
+
*/
|
|
97
|
+
export declare function describeCandidates(cwd: string, ranked: ReadonlyArray<{
|
|
98
|
+
path: string;
|
|
99
|
+
because: Because[];
|
|
100
|
+
}>): Promise<Candidate[]>;
|
|
101
|
+
/** A size a person reads, rather than a byte count. */
|
|
102
|
+
export declare function humanBytes(bytes: number | null): string;
|
|
103
|
+
/** A token count a person reads. Approximate, and never dressed up as exact. */
|
|
104
|
+
export declare function humanTokens(tokens: number | null): string;
|
|
105
|
+
/**
|
|
106
|
+
* The short forms shown in a row, in place of the full reason.
|
|
107
|
+
*
|
|
108
|
+
* A row is a row, not a sentence: "used in this session" reads as prose beside a filename and
|
|
109
|
+
* "in session" reads as a label. The wording is shared so the terminal and the browser cannot drift
|
|
110
|
+
* into saying different things about the same signal.
|
|
111
|
+
*/
|
|
112
|
+
export declare function shortReason(because: Because): string;
|
|
113
|
+
/**
|
|
114
|
+
* What a picker row says after the path: why it is there, and what it will cost.
|
|
115
|
+
*
|
|
116
|
+
* Shared by both surfaces. `bytes` is undefined while the size is still being read, which is the
|
|
117
|
+
* normal first state rather than an error — the rows appear at once and the numbers arrive behind
|
|
118
|
+
* them, because a picker that waits for forty stat calls before showing anything feels broken.
|
|
119
|
+
*/
|
|
120
|
+
export declare function rowDetail(because: readonly Because[], bytes: number | undefined, kind?: 'file' | 'dir'): string;
|
|
121
|
+
//# sourceMappingURL=mention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../src/web/mention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAMH,2GAA2G;AAC3G,MAAM,MAAM,OAAO;AACjB,sEAAsE;AACpE,kBAAkB;AACpB,uEAAuE;GACrE,sBAAsB;AACxB,sFAAsF;GACpF,WAAW,CAAC;AAEhB,MAAM,WAAW,SAAS;IACxB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,sGAAsG;IACtG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iCAAiC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6EAA6E;IAC7E,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,+DAA+D;AAC/D,MAAM,WAAW,OAAO;IACtB,oDAAoD;IACpD,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAcD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAOjE;AA0BD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAY,EACrB,KAAK,SAAK,GACT,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,CAgC5D;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,GAC1D,OAAO,CAAC,SAAS,EAAE,CAAC,CAmBtB;AAED,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAKvD;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAIzD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAIpD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,GAAE,MAAM,GAAG,KAAc,GAC5B,MAAM,CAKR"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Referencing a file by name, and knowing what it costs before you do.
|
|
3
|
+
*
|
|
4
|
+
* The terminal has had `@` for a while: type it, get a subsequence match over every path in the
|
|
5
|
+
* workspace, pick one. The browser never got it, which is the gap this closes. But copying the
|
|
6
|
+
* terminal's version would have missed the two things that make this worth building rather than
|
|
7
|
+
* ticking off.
|
|
8
|
+
*
|
|
9
|
+
* ## Ranked by what this project is doing, not alphabetically
|
|
10
|
+
*
|
|
11
|
+
* A fuzzy match over four thousand paths answers "which files contain these letters". It does not
|
|
12
|
+
* answer "which file did you mean", and the difference is most of the value. KONECK already knows
|
|
13
|
+
* things no generic picker does:
|
|
14
|
+
*
|
|
15
|
+
* - which files git has touched recently — what the project is in the middle of;
|
|
16
|
+
* - which files this session has already read or written — what *you* are in the middle of;
|
|
17
|
+
* - which files the project ledger holds findings about — where the sharp edges were.
|
|
18
|
+
*
|
|
19
|
+
* A file carrying all three is almost certainly the one being reached for, and it is offered first
|
|
20
|
+
* with the reason attached, because a ranked list that will not say why it ranked something is a
|
|
21
|
+
* list you cannot correct.
|
|
22
|
+
*
|
|
23
|
+
* ## The cost is shown before the attachment is made
|
|
24
|
+
*
|
|
25
|
+
* Every candidate carries an estimate of what including it would spend. This matters here more than
|
|
26
|
+
* it might elsewhere: the person this was built for has run into truncated prompts and exhausted
|
|
27
|
+
* windows repeatedly, and "src/engine.ts ~48k tokens" is the difference between an informed choice
|
|
28
|
+
* and a surprise three turns later. It is an estimate and is labelled as one — four characters to
|
|
29
|
+
* the token is the usual rough figure and it is rough — but a rough number in front of the decision
|
|
30
|
+
* beats an exact one after it.
|
|
31
|
+
*
|
|
32
|
+
* A directory is offered as an outline rather than a concatenation, for the same reason: naming
|
|
33
|
+
* twenty files costs a few hundred tokens and pasting them costs a hundred thousand.
|
|
34
|
+
*/
|
|
35
|
+
import path from 'path';
|
|
36
|
+
import { stat } from 'fs/promises';
|
|
37
|
+
import { scoreEntry } from '../workspace-files.js';
|
|
38
|
+
/**
|
|
39
|
+
* Four characters to the token.
|
|
40
|
+
*
|
|
41
|
+
* The usual rough figure for English and code alike. Deliberately not a real tokeniser: loading one
|
|
42
|
+
* to put an advisory number beside forty filenames would cost more than the number is worth, and
|
|
43
|
+
* being out by a fifth does not change the decision anybody makes with it.
|
|
44
|
+
*/
|
|
45
|
+
export const CHARS_PER_TOKEN = 4;
|
|
46
|
+
export function tokensForBytes(bytes) {
|
|
47
|
+
return Math.max(1, Math.round(bytes / CHARS_PER_TOKEN));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Normalised for comparison: a path is the same path however it was written.
|
|
51
|
+
*
|
|
52
|
+
* The trailing slash goes too. The workspace listing marks directories with one, and carrying it
|
|
53
|
+
* through meant the candidate was `src/` — which the client then completed to `@src//`, and which
|
|
54
|
+
* matched nothing when compared against the same directory named by git or by the session.
|
|
55
|
+
*/
|
|
56
|
+
function key(p) {
|
|
57
|
+
const cleaned = p.replace(/^\.\//, '').replace(/\\/g, '/').replace(/\/+$/, '');
|
|
58
|
+
return cleaned;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The reasons a path carries, in a fixed order.
|
|
62
|
+
*
|
|
63
|
+
* Fixed so the list reads the same way every time — a reason that moves around between keystrokes
|
|
64
|
+
* makes the ranking look arbitrary even when it is not.
|
|
65
|
+
*/
|
|
66
|
+
export function reasonsFor(p, signals) {
|
|
67
|
+
const it = key(p);
|
|
68
|
+
const out = [];
|
|
69
|
+
if (signals.recent?.some(r => key(r) === it))
|
|
70
|
+
out.push('recently changed');
|
|
71
|
+
if (signals.session?.some(r => key(r) === it))
|
|
72
|
+
out.push('used in this session');
|
|
73
|
+
if (signals.noted?.some(r => key(r) === it))
|
|
74
|
+
out.push('has notes');
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* How much being known about is worth, against how well the name matches.
|
|
79
|
+
*
|
|
80
|
+
* The match still decides: somebody typing `engine` wants engine.ts even if they have never opened
|
|
81
|
+
* it. But among candidates that match comparably, the one the project is working on wins — and the
|
|
82
|
+
* weights are small enough that a strong name match cannot be displaced by mere familiarity.
|
|
83
|
+
*/
|
|
84
|
+
const WORTH = {
|
|
85
|
+
'used in this session': 8,
|
|
86
|
+
'recently changed': 5,
|
|
87
|
+
'has notes': 2,
|
|
88
|
+
};
|
|
89
|
+
/*
|
|
90
|
+
* Where those numbers came from.
|
|
91
|
+
*
|
|
92
|
+
* Measured, not chosen. The matcher scores a hit in the filename against a hit spread across
|
|
93
|
+
* directories about six points apart — `src/engine.ts` and `tests/engine.test.ts` for the query
|
|
94
|
+
* "enginets" come out 1008 and 1014 — while a genuinely different file is hundreds away or does not
|
|
95
|
+
* match at all. So eight is enough to turn a near miss into a win, and nowhere near enough to
|
|
96
|
+
* promote a file that merely happens to be open. The first version used six, which made the near
|
|
97
|
+
* miss an exact tie and left a length comparison to decide it, which is not a decision at all.
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* The candidates for a query, best first.
|
|
101
|
+
*
|
|
102
|
+
* An empty query is not an empty answer: it offers what the project is doing — recent, in-session,
|
|
103
|
+
* noted — which is the list somebody wants when they type `@` and pause. A generic picker shows the
|
|
104
|
+
* alphabet at that moment, which is never what was meant.
|
|
105
|
+
*/
|
|
106
|
+
export function rankMentions(entries, query, signals = {}, limit = 40) {
|
|
107
|
+
const asked = query.trim();
|
|
108
|
+
if (asked === '') {
|
|
109
|
+
const seen = new Set();
|
|
110
|
+
const suggested = [];
|
|
111
|
+
// In the order they answer "what am I working on": this session, then the project, then history.
|
|
112
|
+
for (const source of [signals.session ?? [], signals.recent ?? [], signals.noted ?? []]) {
|
|
113
|
+
for (const p of source) {
|
|
114
|
+
const it = key(p);
|
|
115
|
+
if (seen.has(it))
|
|
116
|
+
continue;
|
|
117
|
+
seen.add(it);
|
|
118
|
+
const because = reasonsFor(it, signals);
|
|
119
|
+
suggested.push({ path: it, score: -because.reduce((n, b) => n + WORTH[b], 0), because });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
suggested.sort((a, b) => a.score - b.score);
|
|
123
|
+
return suggested.slice(0, limit);
|
|
124
|
+
}
|
|
125
|
+
const scored = [];
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
const base = scoreEntry(entry, asked);
|
|
128
|
+
if (base === null)
|
|
129
|
+
continue;
|
|
130
|
+
const because = reasonsFor(entry, signals);
|
|
131
|
+
// Lower is better in scoreEntry, so being known about subtracts.
|
|
132
|
+
const bonus = because.reduce((n, b) => n + WORTH[b], 0);
|
|
133
|
+
scored.push({ path: key(entry), score: base - bonus, because });
|
|
134
|
+
}
|
|
135
|
+
scored.sort((a, b) => (a.score - b.score) || a.path.length - b.path.length
|
|
136
|
+
|| a.path.localeCompare(b.path));
|
|
137
|
+
return scored.slice(0, limit);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Fills in size and cost, and says which are directories.
|
|
141
|
+
*
|
|
142
|
+
* Done for the shown candidates only. Sizing four thousand paths to display forty is the kind of
|
|
143
|
+
* work that makes a picker feel slow for no reason anybody can see.
|
|
144
|
+
*/
|
|
145
|
+
export async function describeCandidates(cwd, ranked) {
|
|
146
|
+
return Promise.all(ranked.map(async (entry) => {
|
|
147
|
+
const full = path.join(cwd, entry.path);
|
|
148
|
+
let bytes = null;
|
|
149
|
+
let kind = 'file';
|
|
150
|
+
try {
|
|
151
|
+
const info = await stat(full);
|
|
152
|
+
kind = info.isDirectory() ? 'dir' : 'file';
|
|
153
|
+
bytes = info.isDirectory() ? null : info.size;
|
|
154
|
+
}
|
|
155
|
+
catch { /* it was listed and has since gone; offered without a size */ }
|
|
156
|
+
return {
|
|
157
|
+
path: entry.path,
|
|
158
|
+
kind,
|
|
159
|
+
bytes,
|
|
160
|
+
// A directory is offered as an outline, so its cost is not the sum of its files.
|
|
161
|
+
tokens: kind === 'dir' || bytes === null ? null : tokensForBytes(bytes),
|
|
162
|
+
because: entry.because,
|
|
163
|
+
};
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
/** A size a person reads, rather than a byte count. */
|
|
167
|
+
export function humanBytes(bytes) {
|
|
168
|
+
if (bytes === null)
|
|
169
|
+
return '';
|
|
170
|
+
if (bytes < 1024)
|
|
171
|
+
return `${bytes} B`;
|
|
172
|
+
if (bytes < 1024 * 1024)
|
|
173
|
+
return `${Math.round(bytes / 1024)} KB`;
|
|
174
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
175
|
+
}
|
|
176
|
+
/** A token count a person reads. Approximate, and never dressed up as exact. */
|
|
177
|
+
export function humanTokens(tokens) {
|
|
178
|
+
if (tokens === null)
|
|
179
|
+
return '';
|
|
180
|
+
if (tokens < 1000)
|
|
181
|
+
return `~${tokens} tok`;
|
|
182
|
+
return `~${Math.round(tokens / 100) / 10}k tok`;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The short forms shown in a row, in place of the full reason.
|
|
186
|
+
*
|
|
187
|
+
* A row is a row, not a sentence: "used in this session" reads as prose beside a filename and
|
|
188
|
+
* "in session" reads as a label. The wording is shared so the terminal and the browser cannot drift
|
|
189
|
+
* into saying different things about the same signal.
|
|
190
|
+
*/
|
|
191
|
+
export function shortReason(because) {
|
|
192
|
+
return because === 'used in this session' ? 'in session'
|
|
193
|
+
: because === 'recently changed' ? 'changed'
|
|
194
|
+
: 'noted';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* What a picker row says after the path: why it is there, and what it will cost.
|
|
198
|
+
*
|
|
199
|
+
* Shared by both surfaces. `bytes` is undefined while the size is still being read, which is the
|
|
200
|
+
* normal first state rather than an error — the rows appear at once and the numbers arrive behind
|
|
201
|
+
* them, because a picker that waits for forty stat calls before showing anything feels broken.
|
|
202
|
+
*/
|
|
203
|
+
export function rowDetail(because, bytes, kind = 'file') {
|
|
204
|
+
const parts = because.map(shortReason);
|
|
205
|
+
if (kind === 'dir')
|
|
206
|
+
parts.push('folder');
|
|
207
|
+
else if (bytes !== undefined)
|
|
208
|
+
parts.push(humanTokens(tokensForBytes(bytes)));
|
|
209
|
+
return parts.join(' · ');
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=mention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention.js","sourceRoot":"","sources":["../../src/web/mention.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/web/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAE9D,OAAO,EAAE,QAAQ,EAAiB,MAAM,aAAa,CAAC;AACtD,OAAO,EAAqB,SAAS,IAAI,gBAAgB,EACjC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/web/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAE9D,OAAO,EAAE,QAAQ,EAAiB,MAAM,aAAa,CAAC;AACtD,OAAO,EAAqB,SAAS,IAAI,gBAAgB,EACjC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAoC,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAIpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,IAAI,EAAE,QAAQ,CAAC;IACf,0DAA0D;IAC1D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAClB,+FAA+F;IAC/F,QAAQ,EAAE,MAAM,CAAC;IACjB,6FAA6F;IAC7F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3D;AAUD,qBAAa,UAAU;IAuFT,QAAQ,CAAC,EAAE,EAAE,MAAM;IAAE,OAAO,CAAC,MAAM;IAtF/C,QAAQ,CAAC,GAAG,WAAkB;IAC9B,QAAQ,CAAC,SAAS,SAAc;IAChC;;;;;;OAMG;IACH,KAAK,SAAK;IACV,KAAK,SAAiB;IACtB,KAAK,SAAK;IACV,WAAW,SAAK;IAChB,IAAI,UAAS;IAEb,OAAO,CAAC,KAAK,CAA+D;IAC5E,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAgC;IAEhD,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAuB;IAC9C,gEAAgE;IAChE,OAAO,CAAC,WAAW,CAAuB;IAC1C,2EAA2E;IAC3E,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,CAA6B;IAE3D,gFAAgF;IAChF,IAAI,EAAE,OAAO,WAAW,EAAE,SAAS,GAAG,IAAI,CAAQ;IAClD,8EAA8E;IAC9E,OAAO,CAAC,QAAQ,CAAuB;IAEvC,0FAA0F;IAC1F,OAAO,CAAC,IAAI,CACiD;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAK;IACxB,gGAAgG;IAChG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,QAAQ,CAAyB;IACzC,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,CAAM;IAEzC;;;;;;;;OAQG;IACH,IAAI,EAAE,QAAQ,CAAU;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3B,MAAM,EAAE,MAAM,CAAY;IAC1B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC9B,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAuB;gBAElB,EAAE,EAAE,MAAM,EAAU,MAAM,EAAE,WAAW;IAO5D,IAAI,IAAI,cAAc;IA0BtB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAQ,GAAG,OAAO;IAUhE,uFAAuF;IACvF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IAO3C;;;;;;;OAOG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAS/B,qDAAqD;IACrD,IAAI,SAAS,IAAI,MAAM,GAAG,IAAI,CAA0B;IAExD;;;;;;OAMG;IACH,MAAM,IAAI,MAAM,GAAG,IAAI;IAYvB,oDAAoD;IACpD,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAM7B,iFAAiF;IACjF,eAAe,IAAI,MAAM,EAAE;IAE3B;;;;;OAKG;IACH,UAAU,IAAI,SAAS,eAAe,EAAE;YAI1B,WAAW;IAuKzB;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;QAClE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IASxD;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtC,2FAA2F;IACrF,iBAAiB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IAOjH;;;;;;OAMG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,SAAS,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0F3E,iFAAiF;IACjF;;;;;;OAMG;IAYH,OAAO,CAAC,aAAa,CAAS;YAEhB,YAAY;IA6B1B,gFAAgF;IAChF,OAAO,CAAC,QAAQ;IAOV,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YA6BhB,iBAAiB;IAa/B,0DAA0D;IAC1D;;;;;;OAMG;IACH,KAAK,IAAI,OAAO;IAQhB;;;;;;;;;OASG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,UAAO,GACrD;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAqDtF,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI9C;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAItC;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAUzC,kFAAkF;IAClF,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IA4C9C,oGAAoG;IACpG,QAAQ,IAAI,MAAM;CAInB;AAED,iCAAiC;AACjC,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,OAAO,CAAK;IAEpB,sFAAsF;IACtF,OAAO,IAAI,MAAM,EAAE;IAInB,0FAA0F;IAC1F,aAAa,IAAI,MAAM,EAAE;IAIzB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU;IAUvC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IACvC,IAAI,IAAI,cAAc,EAAE;IAKxB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAC3B,IAAI,IAAI,IAAI,MAAM,CAA+B;CAClD;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD"}
|
package/dist/web/sessions.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createAgentSession, toolDetailFor } from '../engine.js';
|
|
|
13
13
|
import { estimateCost } from '../pricing.js';
|
|
14
14
|
import { EventBus } from './events.js';
|
|
15
15
|
import { snapshot, compare, summarise as summariseChanges } from './changes.js';
|
|
16
|
+
import { attachReferences, linesIn } from '../mention-resolve.js';
|
|
16
17
|
import { decide, modeSpec } from '../permissions.js';
|
|
17
18
|
import { redactSecrets } from '../redact.js';
|
|
18
19
|
import { keyEnvFor, resolveProvider } from '../providers.js';
|
|
@@ -438,7 +439,27 @@ export class WebSession {
|
|
|
438
439
|
if (this.title === 'New session')
|
|
439
440
|
this.title = redactSecrets(tidyTitle(text, 72)) || 'New session';
|
|
440
441
|
this.lastText = text;
|
|
441
|
-
|
|
442
|
+
/*
|
|
443
|
+
* An `@` becomes the file it names, before the model sees the message.
|
|
444
|
+
*
|
|
445
|
+
* The picker showed what each reference would cost, so this is the spend that was agreed to. The
|
|
446
|
+
* transcript keeps the sentence as typed — nobody wants to scroll past eight hundred lines of
|
|
447
|
+
* their own attachment to find what they asked — and the list of what was attached rides
|
|
448
|
+
* alongside it so the spend is visible rather than inferred.
|
|
449
|
+
*/
|
|
450
|
+
const referenced = await attachReferences(this.config.cwd, text)
|
|
451
|
+
.catch(() => ({ text, attached: [] }));
|
|
452
|
+
this.bus.emit({
|
|
453
|
+
t: 'user', text, images: imagePaths.length,
|
|
454
|
+
...(referenced.attached.length > 0 ? {
|
|
455
|
+
refs: referenced.attached.map(a => ({
|
|
456
|
+
ref: a.ref,
|
|
457
|
+
kind: a.kind,
|
|
458
|
+
lines: a.content === undefined ? null : linesIn(a.content),
|
|
459
|
+
...(a.note ? { note: a.note } : {}),
|
|
460
|
+
})),
|
|
461
|
+
} : {}),
|
|
462
|
+
});
|
|
442
463
|
try {
|
|
443
464
|
const agent = await this.ensureAgent();
|
|
444
465
|
// The same prefix the terminal builds: the standing goal, the mode's own instruction, and
|
|
@@ -451,7 +472,7 @@ export class WebSession {
|
|
|
451
472
|
// AbortSignal — so passing undefined here meant stop had nothing to act on: the button
|
|
452
473
|
// called an optional method that did not exist, and silently did nothing at all.
|
|
453
474
|
this.stopping = new AbortController();
|
|
454
|
-
const completed = await agent.send(prefix + text, this.stopping.signal, imagePaths.length ? imagePaths : undefined);
|
|
475
|
+
const completed = await agent.send(prefix + referenced.text, this.stopping.signal, imagePaths.length ? imagePaths : undefined);
|
|
455
476
|
this.turns++;
|
|
456
477
|
const ms = Date.now() - started;
|
|
457
478
|
// What this turn changed, taken while it is still true. Comparing at the end of the session
|