laohuang 0.3.2 → 0.4.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/README.md +142 -16
- package/dist/agent.d.ts +166 -0
- package/dist/agent.js +858 -0
- package/dist/agent.js.map +1 -0
- package/dist/bash-runner.d.ts +79 -0
- package/dist/bash-runner.js +464 -0
- package/dist/bash-runner.js.map +1 -0
- package/dist/cancellation.d.ts +36 -0
- package/dist/cancellation.js +123 -0
- package/dist/cancellation.js.map +1 -0
- package/dist/cli.d.ts +117 -0
- package/dist/cli.js +1307 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.js +17 -0
- package/dist/client.js.map +1 -0
- package/dist/commands.d.ts +123 -0
- package/dist/commands.js +660 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.js +183 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +12 -0
- package/dist/credentials.js +102 -0
- package/dist/credentials.js.map +1 -0
- package/dist/events.d.ts +288 -0
- package/dist/events.js +838 -0
- package/dist/events.js.map +1 -0
- package/dist/model-adapter.d.ts +138 -0
- package/dist/model-adapter.js +244 -0
- package/dist/model-adapter.js.map +1 -0
- package/dist/model-selection.d.ts +67 -0
- package/dist/model-selection.js +148 -0
- package/dist/model-selection.js.map +1 -0
- package/dist/model-stream.d.ts +128 -0
- package/dist/model-stream.js +582 -0
- package/dist/model-stream.js.map +1 -0
- package/dist/project-instructions.d.ts +99 -0
- package/dist/project-instructions.js +348 -0
- package/dist/project-instructions.js.map +1 -0
- package/dist/providers.d.ts +9 -0
- package/dist/providers.js +26 -0
- package/dist/providers.js.map +1 -0
- package/dist/routing.d.ts +170 -0
- package/dist/routing.js +669 -0
- package/dist/routing.js.map +1 -0
- package/dist/semantic-classifier.d.ts +66 -0
- package/dist/semantic-classifier.js +86 -0
- package/dist/semantic-classifier.js.map +1 -0
- package/dist/session.d.ts +162 -0
- package/dist/session.js +871 -0
- package/dist/session.js.map +1 -0
- package/dist/system-prompt.d.ts +16 -0
- package/dist/system-prompt.js +42 -0
- package/dist/system-prompt.js.map +1 -0
- package/dist/terminal/editor.d.ts +161 -0
- package/dist/terminal/editor.js +1060 -0
- package/dist/terminal/editor.js.map +1 -0
- package/dist/terminal/input.d.ts +61 -0
- package/dist/terminal/input.js +276 -0
- package/dist/terminal/input.js.map +1 -0
- package/dist/terminal/markdown.d.ts +20 -0
- package/dist/terminal/markdown.js +621 -0
- package/dist/terminal/markdown.js.map +1 -0
- package/dist/terminal/screen.d.ts +66 -0
- package/dist/terminal/screen.js +624 -0
- package/dist/terminal/screen.js.map +1 -0
- package/dist/terminal/theme.d.ts +23 -0
- package/dist/terminal/theme.js +101 -0
- package/dist/terminal/theme.js.map +1 -0
- package/dist/terminal/ui.d.ts +285 -0
- package/dist/terminal/ui.js +1815 -0
- package/dist/terminal/ui.js.map +1 -0
- package/dist/tools.d.ts +95 -0
- package/dist/tools.js +444 -0
- package/dist/tools.js.map +1 -0
- package/dist/ui-state.d.ts +51 -0
- package/dist/ui-state.js +194 -0
- package/dist/ui-state.js.map +1 -0
- package/dist/web.d.ts +56 -0
- package/dist/web.js +247 -0
- package/dist/web.js.map +1 -0
- package/package.json +24 -21
- package/bin/laohuang.js +0 -10
- package/lib/launcher.js +0 -133
- package/vendor/laohuangcode-0.3.2-py3-none-any.whl +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project instruction discovery and rendering, owned by the agent layer.
|
|
3
|
+
*
|
|
4
|
+
* Discovery walks from the project root to the startup cwd inclusively; each
|
|
5
|
+
* directory may contribute AGENTS.md and/or CLAUDE.md (AGENTS.md first, both
|
|
6
|
+
* only when their trimmed contents differ). Only regular UTF-8 text files
|
|
7
|
+
* inside the project root are read; symlinks resolving outside the root are
|
|
8
|
+
* skipped. The renderer owns the complete `<system-reminder>` wrapper and
|
|
9
|
+
* escapes any literal closing tag inside file content or displayed paths, so
|
|
10
|
+
* repository-controlled instruction text can never close the wrapper or be
|
|
11
|
+
* treated as higher-authority system text.
|
|
12
|
+
*
|
|
13
|
+
* Rendering honors a per-file byte cap and a total byte budget. Under budget
|
|
14
|
+
* pressure whole broad files are omitted before the most specific retained
|
|
15
|
+
* file is truncated; omissions and truncations leave a visible notice.
|
|
16
|
+
*
|
|
17
|
+
* `ProjectInstructionState` is private bookkeeping (never model-visible): it
|
|
18
|
+
* records the loaded scope and a content digest per visible instruction file
|
|
19
|
+
* so later phases can skip already-loaded scopes and diff for append-only
|
|
20
|
+
* replacement/removal reminders.
|
|
21
|
+
*/
|
|
22
|
+
/** Candidate instruction file names, in per-directory precedence order. */
|
|
23
|
+
export declare const INSTRUCTION_FILENAMES: readonly string[];
|
|
24
|
+
export declare const DEFAULT_PER_FILE_CAP_BYTES: number;
|
|
25
|
+
export declare const DEFAULT_TOTAL_BUDGET_BYTES: number;
|
|
26
|
+
export interface ProjectInstructionOptions {
|
|
27
|
+
perFileCapBytes?: number;
|
|
28
|
+
totalBudgetBytes?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Bookkeeping record for one visible instruction file. */
|
|
31
|
+
export interface InstructionFileRecord {
|
|
32
|
+
/** Root-relative directory scope ("" is the project root). */
|
|
33
|
+
readonly scope: string;
|
|
34
|
+
/** Root-relative display path with "/" separators. */
|
|
35
|
+
readonly displayPath: string;
|
|
36
|
+
/** SHA-256 hex digest of the loaded file bytes. */
|
|
37
|
+
readonly digest: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Private bookkeeping for loaded instruction scopes. Never model-visible and
|
|
41
|
+
* never alters committed history; later phases diff against these digests to
|
|
42
|
+
* emit append-only replacement/removal reminders.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ProjectInstructionState {
|
|
45
|
+
private readonly records;
|
|
46
|
+
/** True when any instruction file from this directory scope was loaded. */
|
|
47
|
+
hasScope(scope: string): boolean;
|
|
48
|
+
/** Record a loaded instruction file (keyed by its display path). */
|
|
49
|
+
record(record: InstructionFileRecord): void;
|
|
50
|
+
/** The record for one display path, if that file was loaded. */
|
|
51
|
+
entry(displayPath: string): InstructionFileRecord | undefined;
|
|
52
|
+
get loadedPaths(): readonly string[];
|
|
53
|
+
}
|
|
54
|
+
/** One discovered instruction file, before budget rendering. */
|
|
55
|
+
export interface DiscoveredInstructionFile {
|
|
56
|
+
readonly record: InstructionFileRecord;
|
|
57
|
+
readonly text: string;
|
|
58
|
+
/** True when the file exceeded the per-file cap and was cut. */
|
|
59
|
+
readonly truncated: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** Result of baseline discovery: the rendered message plus bookkeeping. */
|
|
62
|
+
export interface BaselineInstructions {
|
|
63
|
+
/** The complete wrapped reminder, or "" when no instruction file exists. */
|
|
64
|
+
readonly rendered: string;
|
|
65
|
+
readonly state: ProjectInstructionState;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The nearest ancestor of `startCwd` (inclusive) containing a `.git` entry;
|
|
69
|
+
* `fallbackRoot` when no ancestor has one.
|
|
70
|
+
*/
|
|
71
|
+
export declare function findProjectRoot(startCwd: string, fallbackRoot: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Discover instruction files in `scopeDirs` (broad-to-specific order), read
|
|
74
|
+
* them under the per-file cap, and record them in `state` when given.
|
|
75
|
+
* Directories whose scope is already represented in `state` are skipped.
|
|
76
|
+
*/
|
|
77
|
+
export declare function discoverInstructions(root: string, scopeDirs: readonly string[], options?: ProjectInstructionOptions, state?: ProjectInstructionState): DiscoveredInstructionFile[];
|
|
78
|
+
/**
|
|
79
|
+
* Render discovered files under the complete `<system-reminder>` wrapper,
|
|
80
|
+
* broad-to-specific, within `totalBudgetBytes`. Under pressure whole broad
|
|
81
|
+
* files are omitted before the most specific retained file is truncated;
|
|
82
|
+
* both cases leave a visible notice.
|
|
83
|
+
*/
|
|
84
|
+
export declare function renderInstructions(files: readonly DiscoveredInstructionFile[], totalBudgetBytes?: number): string;
|
|
85
|
+
/**
|
|
86
|
+
* Render dynamically discovered files as an additional reminder, appended
|
|
87
|
+
* after paired tool results and before the next model request.
|
|
88
|
+
*/
|
|
89
|
+
export declare function renderAdditionalInstructions(files: readonly DiscoveredInstructionFile[], totalBudgetBytes?: number): string;
|
|
90
|
+
/**
|
|
91
|
+
* Load the startup baseline: discover from `root` to `startupCwd`
|
|
92
|
+
* inclusively, render under the total budget, and return the bookkeeping
|
|
93
|
+
* state. No instruction files means an empty rendered string.
|
|
94
|
+
*/
|
|
95
|
+
export declare function loadBaselineInstructions(root: string, startupCwd: string, options?: ProjectInstructionOptions): BaselineInstructions;
|
|
96
|
+
/** Directories from `root` to `cwd` inclusively; just root when cwd is outside. */
|
|
97
|
+
export declare function scopeChain(root: string, cwd: string): string[];
|
|
98
|
+
/** Canonical realpath of `target`, falling back to path.resolve when missing. */
|
|
99
|
+
export declare function realpathOrSelf(target: string): string;
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project instruction discovery and rendering, owned by the agent layer.
|
|
3
|
+
*
|
|
4
|
+
* Discovery walks from the project root to the startup cwd inclusively; each
|
|
5
|
+
* directory may contribute AGENTS.md and/or CLAUDE.md (AGENTS.md first, both
|
|
6
|
+
* only when their trimmed contents differ). Only regular UTF-8 text files
|
|
7
|
+
* inside the project root are read; symlinks resolving outside the root are
|
|
8
|
+
* skipped. The renderer owns the complete `<system-reminder>` wrapper and
|
|
9
|
+
* escapes any literal closing tag inside file content or displayed paths, so
|
|
10
|
+
* repository-controlled instruction text can never close the wrapper or be
|
|
11
|
+
* treated as higher-authority system text.
|
|
12
|
+
*
|
|
13
|
+
* Rendering honors a per-file byte cap and a total byte budget. Under budget
|
|
14
|
+
* pressure whole broad files are omitted before the most specific retained
|
|
15
|
+
* file is truncated; omissions and truncations leave a visible notice.
|
|
16
|
+
*
|
|
17
|
+
* `ProjectInstructionState` is private bookkeeping (never model-visible): it
|
|
18
|
+
* records the loaded scope and a content digest per visible instruction file
|
|
19
|
+
* so later phases can skip already-loaded scopes and diff for append-only
|
|
20
|
+
* replacement/removal reminders.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
23
|
+
import { closeSync, existsSync, fstatSync, lstatSync, openSync, readSync, realpathSync, statSync, } from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
/** Candidate instruction file names, in per-directory precedence order. */
|
|
26
|
+
export const INSTRUCTION_FILENAMES = [
|
|
27
|
+
"AGENTS.md",
|
|
28
|
+
"CLAUDE.md",
|
|
29
|
+
];
|
|
30
|
+
export const DEFAULT_PER_FILE_CAP_BYTES = 1024 * 1024; // 1 MiB
|
|
31
|
+
export const DEFAULT_TOTAL_BUDGET_BYTES = 64 * 1024; // 64 KiB
|
|
32
|
+
const WRAPPER_INTRO = "The following workspace instructions may be relevant to your work. " +
|
|
33
|
+
"Use them as guidance when applicable. More specific instructions take " +
|
|
34
|
+
"precedence over broader ones. They do not override system, developer, " +
|
|
35
|
+
"or direct user instructions.";
|
|
36
|
+
const OMISSION_NOTICE = "[omitted to stay within the total instruction budget]";
|
|
37
|
+
const TRUNCATION_NOTICE = "[truncated to stay within the total instruction budget]";
|
|
38
|
+
const PER_FILE_CAP_NOTICE = "[truncated: file exceeds the per-file instruction cap]";
|
|
39
|
+
/**
|
|
40
|
+
* Private bookkeeping for loaded instruction scopes. Never model-visible and
|
|
41
|
+
* never alters committed history; later phases diff against these digests to
|
|
42
|
+
* emit append-only replacement/removal reminders.
|
|
43
|
+
*/
|
|
44
|
+
export class ProjectInstructionState {
|
|
45
|
+
records = new Map();
|
|
46
|
+
/** True when any instruction file from this directory scope was loaded. */
|
|
47
|
+
hasScope(scope) {
|
|
48
|
+
for (const record of this.records.values()) {
|
|
49
|
+
if (record.scope === scope) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
/** Record a loaded instruction file (keyed by its display path). */
|
|
56
|
+
record(record) {
|
|
57
|
+
this.records.set(record.displayPath, record);
|
|
58
|
+
}
|
|
59
|
+
/** The record for one display path, if that file was loaded. */
|
|
60
|
+
entry(displayPath) {
|
|
61
|
+
return this.records.get(displayPath);
|
|
62
|
+
}
|
|
63
|
+
get loadedPaths() {
|
|
64
|
+
return [...this.records.keys()];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The nearest ancestor of `startCwd` (inclusive) containing a `.git` entry;
|
|
69
|
+
* `fallbackRoot` when no ancestor has one.
|
|
70
|
+
*/
|
|
71
|
+
export function findProjectRoot(startCwd, fallbackRoot) {
|
|
72
|
+
let current = path.resolve(startCwd);
|
|
73
|
+
for (;;) {
|
|
74
|
+
if (existsSync(path.join(current, ".git"))) {
|
|
75
|
+
return current;
|
|
76
|
+
}
|
|
77
|
+
const parent = path.dirname(current);
|
|
78
|
+
if (parent === current) {
|
|
79
|
+
return fallbackRoot;
|
|
80
|
+
}
|
|
81
|
+
current = parent;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Discover instruction files in `scopeDirs` (broad-to-specific order), read
|
|
86
|
+
* them under the per-file cap, and record them in `state` when given.
|
|
87
|
+
* Directories whose scope is already represented in `state` are skipped.
|
|
88
|
+
*/
|
|
89
|
+
export function discoverInstructions(root, scopeDirs, options = {}, state) {
|
|
90
|
+
const resolved = resolveOptions(options);
|
|
91
|
+
const rootReal = realpathOrSelf(root);
|
|
92
|
+
const found = [];
|
|
93
|
+
for (const directory of scopeDirs) {
|
|
94
|
+
const scope = displayRelative(root, directory);
|
|
95
|
+
if (state?.hasScope(scope)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (const name of INSTRUCTION_FILENAMES) {
|
|
99
|
+
const candidate = path.join(directory, name);
|
|
100
|
+
const file = readInstructionFile(rootReal, candidate, resolved);
|
|
101
|
+
if (file === null) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
// Same-directory dedupe: identical trimmed content keeps only the
|
|
105
|
+
// higher-precedence (earlier) candidate.
|
|
106
|
+
if (found.some((other) => other.text.trim() === file.text.trim())) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const discovered = {
|
|
110
|
+
record: {
|
|
111
|
+
scope,
|
|
112
|
+
displayPath: displayRelative(root, candidate),
|
|
113
|
+
digest: file.digest,
|
|
114
|
+
},
|
|
115
|
+
text: file.text,
|
|
116
|
+
truncated: file.truncated,
|
|
117
|
+
};
|
|
118
|
+
found.push(discovered);
|
|
119
|
+
state?.record(discovered.record);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return found;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Render discovered files under the complete `<system-reminder>` wrapper,
|
|
126
|
+
* broad-to-specific, within `totalBudgetBytes`. Under pressure whole broad
|
|
127
|
+
* files are omitted before the most specific retained file is truncated;
|
|
128
|
+
* both cases leave a visible notice.
|
|
129
|
+
*/
|
|
130
|
+
export function renderInstructions(files, totalBudgetBytes = DEFAULT_TOTAL_BUDGET_BYTES) {
|
|
131
|
+
return renderWrapped(files, totalBudgetBytes, "Instructions from:");
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Render dynamically discovered files as an additional reminder, appended
|
|
135
|
+
* after paired tool results and before the next model request.
|
|
136
|
+
*/
|
|
137
|
+
export function renderAdditionalInstructions(files, totalBudgetBytes = DEFAULT_TOTAL_BUDGET_BYTES) {
|
|
138
|
+
return renderWrapped(files, totalBudgetBytes, "Additional instructions from:");
|
|
139
|
+
}
|
|
140
|
+
function renderWrapped(files, totalBudgetBytes, sectionHeader) {
|
|
141
|
+
if (files.length === 0) {
|
|
142
|
+
return "";
|
|
143
|
+
}
|
|
144
|
+
const sections = files.map((file) => ({
|
|
145
|
+
displayPath: escapeClosingReminder(file.record.displayPath),
|
|
146
|
+
body: escapeClosingReminder(file.text.trim()) +
|
|
147
|
+
(file.truncated ? `\n\n${PER_FILE_CAP_NOTICE}` : ""),
|
|
148
|
+
omitted: false,
|
|
149
|
+
}));
|
|
150
|
+
const render = () => {
|
|
151
|
+
const body = sections
|
|
152
|
+
.map((section) => `${sectionHeader} ${section.displayPath}\n\n` +
|
|
153
|
+
(section.omitted ? OMISSION_NOTICE : section.body))
|
|
154
|
+
.join("\n\n");
|
|
155
|
+
return `<system-reminder>\n${WRAPPER_INTRO}\n\n${body}\n</system-reminder>`;
|
|
156
|
+
};
|
|
157
|
+
let rendered = render();
|
|
158
|
+
while (byteLength(rendered) > totalBudgetBytes) {
|
|
159
|
+
const retained = sections.filter((section) => !section.omitted);
|
|
160
|
+
if (retained.length > 1) {
|
|
161
|
+
const broadest = retained[0];
|
|
162
|
+
if (broadest === undefined) {
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
broadest.omitted = true;
|
|
166
|
+
rendered = render();
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const last = retained[0];
|
|
170
|
+
if (last === undefined) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
const overhead = byteLength(rendered) - byteLength(last.body);
|
|
174
|
+
const notice = `\n\n${TRUNCATION_NOTICE}`;
|
|
175
|
+
const allowed = Math.max(0, totalBudgetBytes - overhead - byteLength(notice));
|
|
176
|
+
last.body = truncateToBytes(last.body, allowed) + notice;
|
|
177
|
+
rendered = render();
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
if (byteLength(rendered) > totalBudgetBytes) {
|
|
181
|
+
// Last-resort clamp for pathologically small budgets: keep the closing
|
|
182
|
+
// wrapper tag intact even if a notice must be cut.
|
|
183
|
+
const closing = "\n</system-reminder>";
|
|
184
|
+
rendered =
|
|
185
|
+
truncateToBytes(rendered, Math.max(0, totalBudgetBytes - byteLength(closing))) + closing;
|
|
186
|
+
}
|
|
187
|
+
return rendered;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Load the startup baseline: discover from `root` to `startupCwd`
|
|
191
|
+
* inclusively, render under the total budget, and return the bookkeeping
|
|
192
|
+
* state. No instruction files means an empty rendered string.
|
|
193
|
+
*/
|
|
194
|
+
export function loadBaselineInstructions(root, startupCwd, options = {}) {
|
|
195
|
+
const resolved = resolveOptions(options);
|
|
196
|
+
const state = new ProjectInstructionState();
|
|
197
|
+
const files = discoverInstructions(root, scopeChain(root, startupCwd), resolved, state);
|
|
198
|
+
return {
|
|
199
|
+
rendered: renderInstructions(files, resolved.totalBudgetBytes),
|
|
200
|
+
state,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// --- Internals ---------------------------------------------------------------
|
|
204
|
+
function resolveOptions(options) {
|
|
205
|
+
return {
|
|
206
|
+
perFileCapBytes: options.perFileCapBytes ?? DEFAULT_PER_FILE_CAP_BYTES,
|
|
207
|
+
totalBudgetBytes: options.totalBudgetBytes ?? DEFAULT_TOTAL_BUDGET_BYTES,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** Directories from `root` to `cwd` inclusively; just root when cwd is outside. */
|
|
211
|
+
export function scopeChain(root, cwd) {
|
|
212
|
+
const relative = path.relative(root, cwd);
|
|
213
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
214
|
+
return [root];
|
|
215
|
+
}
|
|
216
|
+
const dirs = [root];
|
|
217
|
+
let current = root;
|
|
218
|
+
for (const part of relative.split(path.sep)) {
|
|
219
|
+
if (part === "") {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
current = path.join(current, part);
|
|
223
|
+
dirs.push(current);
|
|
224
|
+
}
|
|
225
|
+
return dirs;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Read one candidate as a regular UTF-8 text file inside the root, capped at
|
|
229
|
+
* `options.perFileCapBytes`. Returns null for missing, non-regular,
|
|
230
|
+
* non-UTF-8, or out-of-root-symlink candidates.
|
|
231
|
+
*/
|
|
232
|
+
function readInstructionFile(rootReal, candidate, options) {
|
|
233
|
+
let stats;
|
|
234
|
+
try {
|
|
235
|
+
stats = lstatSync(candidate);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
let resolved = candidate;
|
|
241
|
+
if (stats.isSymbolicLink()) {
|
|
242
|
+
try {
|
|
243
|
+
resolved = realpathSync(candidate);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (!isInsideRoot(rootReal, resolved)) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
if (!statSync(resolved).isFile()) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (!stats.isFile()) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const capped = readCapped(resolved, options.perFileCapBytes);
|
|
264
|
+
if (capped === null) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
let text;
|
|
268
|
+
try {
|
|
269
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(capped.bytes);
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
text,
|
|
276
|
+
truncated: capped.truncated,
|
|
277
|
+
digest: createHash("sha256").update(capped.bytes).digest("hex"),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function readCapped(filePath, cap) {
|
|
281
|
+
let handle;
|
|
282
|
+
try {
|
|
283
|
+
handle = openSync(filePath, "r");
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const size = fstatSync(handle).size;
|
|
290
|
+
const length = Math.min(size, cap);
|
|
291
|
+
const buffer = Buffer.alloc(length);
|
|
292
|
+
let offset = 0;
|
|
293
|
+
while (offset < length) {
|
|
294
|
+
const count = readSync(handle, buffer, offset, length - offset, offset);
|
|
295
|
+
if (count <= 0) {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
offset += count;
|
|
299
|
+
}
|
|
300
|
+
return { bytes: buffer.subarray(0, offset), truncated: size > cap };
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
closeSync(handle);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function isInsideRoot(rootReal, target) {
|
|
310
|
+
const relative = path.relative(rootReal, target);
|
|
311
|
+
return (relative === "" ||
|
|
312
|
+
(!relative.startsWith("..") && !path.isAbsolute(relative)));
|
|
313
|
+
}
|
|
314
|
+
/** Canonical realpath of `target`, falling back to path.resolve when missing. */
|
|
315
|
+
export function realpathOrSelf(target) {
|
|
316
|
+
try {
|
|
317
|
+
return realpathSync(target);
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return path.resolve(target);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/** Root-relative path with "/" separators for display and scope keys. */
|
|
324
|
+
function displayRelative(root, target) {
|
|
325
|
+
return path.relative(root, target).split(path.sep).join("/");
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Neutralize any literal wrapper-closing tag inside repository-controlled
|
|
329
|
+
* text (file content or display paths) so it cannot close the reminder.
|
|
330
|
+
*/
|
|
331
|
+
function escapeClosingReminder(text) {
|
|
332
|
+
return text.split("</system-reminder>").join("<\\/system-reminder>");
|
|
333
|
+
}
|
|
334
|
+
function byteLength(text) {
|
|
335
|
+
return Buffer.byteLength(text, "utf8");
|
|
336
|
+
}
|
|
337
|
+
/** Cut a string to at most `maxBytes` UTF-8 bytes without splitting a character. */
|
|
338
|
+
function truncateToBytes(text, maxBytes) {
|
|
339
|
+
if (byteLength(text) <= maxBytes) {
|
|
340
|
+
return text;
|
|
341
|
+
}
|
|
342
|
+
const sliced = Buffer.from(text, "utf8")
|
|
343
|
+
.subarray(0, maxBytes)
|
|
344
|
+
.toString("utf8");
|
|
345
|
+
// A multi-byte character split at the cut point decodes to U+FFFD.
|
|
346
|
+
return sliced.replace(/�+$/, "");
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=project-instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-instructions.js","sourceRoot":"","sources":["../src/project-instructions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Built-in model provider presets. */
|
|
2
|
+
export interface Provider {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly defaultModel: string | null;
|
|
5
|
+
readonly baseUrl: string | null;
|
|
6
|
+
readonly suggestedModels: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function getProvider(name: string): Provider;
|
|
9
|
+
export declare function providerNames(): string[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Built-in model provider presets. */
|
|
2
|
+
const PROVIDERS = {
|
|
3
|
+
deepseek: {
|
|
4
|
+
name: "deepseek",
|
|
5
|
+
defaultModel: "deepseek-v4-flash",
|
|
6
|
+
baseUrl: "https://api.deepseek.com",
|
|
7
|
+
suggestedModels: ["deepseek-v4-flash", "deepseek-v4-pro"],
|
|
8
|
+
},
|
|
9
|
+
openai: {
|
|
10
|
+
name: "openai",
|
|
11
|
+
defaultModel: null,
|
|
12
|
+
baseUrl: null,
|
|
13
|
+
suggestedModels: [],
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export function getProvider(name) {
|
|
17
|
+
const provider = PROVIDERS[name];
|
|
18
|
+
if (!provider) {
|
|
19
|
+
throw new Error(`Unknown provider: ${name}`);
|
|
20
|
+
}
|
|
21
|
+
return provider;
|
|
22
|
+
}
|
|
23
|
+
export function providerNames() {
|
|
24
|
+
return Object.keys(PROVIDERS).sort();
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA,uCAAuC;AASvC,MAAM,SAAS,GAA6B;IAC1C,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE,mBAAmB;QACjC,OAAO,EAAE,0BAA0B;QACnC,eAAe,EAAE,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;KAC1D;IACD,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,IAAI;QACb,eAAe,EAAE,EAAE;KACpB;CACF,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/** Deterministic routing, task registry, and runtime input queues. */
|
|
2
|
+
import { CancelToken } from "./cancellation.ts";
|
|
3
|
+
import { type AnyEventEnvelope } from "./events.ts";
|
|
4
|
+
export declare const TaskState: {
|
|
5
|
+
readonly RunningModel: "running_model";
|
|
6
|
+
readonly RunningTools: "running_tools";
|
|
7
|
+
readonly Cancelling: "cancelling";
|
|
8
|
+
readonly Cancelled: "cancelled";
|
|
9
|
+
readonly Completed: "completed";
|
|
10
|
+
readonly Failed: "failed";
|
|
11
|
+
};
|
|
12
|
+
export type TaskState = (typeof TaskState)[keyof typeof TaskState];
|
|
13
|
+
export interface TaskRecord {
|
|
14
|
+
readonly taskId: string;
|
|
15
|
+
readonly state: TaskState;
|
|
16
|
+
readonly cancelToken: CancelToken;
|
|
17
|
+
readonly result: string | null;
|
|
18
|
+
readonly error: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface RegisterTaskOptions {
|
|
21
|
+
state?: TaskState;
|
|
22
|
+
cancelToken?: CancelToken;
|
|
23
|
+
activate?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface TransitionOptions {
|
|
26
|
+
result?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Task records with at most one active task. */
|
|
30
|
+
export declare class TaskRegistry {
|
|
31
|
+
private readonly tasks;
|
|
32
|
+
private activeTaskIdValue;
|
|
33
|
+
get activeTaskId(): string | null;
|
|
34
|
+
register(taskId: string, options?: RegisterTaskOptions): TaskRecord;
|
|
35
|
+
get(taskId: string): TaskRecord | null;
|
|
36
|
+
active(): TaskRecord | null;
|
|
37
|
+
transition(taskId: string, state: TaskState, options?: TransitionOptions): TaskRecord;
|
|
38
|
+
records(): TaskRecord[];
|
|
39
|
+
}
|
|
40
|
+
export type RouteStrategy = "execute" | "steer" | "follow_up" | "cancel" | "reject";
|
|
41
|
+
export type RouteDestination = "new_task" | "current_task" | "pending" | "held" | "control" | "drop";
|
|
42
|
+
export type RouteTiming = "immediate" | "safe_point" | "after_cancel";
|
|
43
|
+
export interface RouteDecision {
|
|
44
|
+
readonly taskId: string | null;
|
|
45
|
+
readonly destination: RouteDestination;
|
|
46
|
+
readonly timing: RouteTiming;
|
|
47
|
+
readonly strategy: RouteStrategy;
|
|
48
|
+
readonly confidence: number;
|
|
49
|
+
readonly reason: string;
|
|
50
|
+
readonly layer: number;
|
|
51
|
+
}
|
|
52
|
+
export interface RoutedEvent {
|
|
53
|
+
readonly event: AnyEventEnvelope;
|
|
54
|
+
readonly decision: RouteDecision;
|
|
55
|
+
}
|
|
56
|
+
/** Provider-neutral conservative token estimate without a tokenizer. */
|
|
57
|
+
export declare function estimateTokens(text: string): number;
|
|
58
|
+
export interface DeadLetter {
|
|
59
|
+
readonly event: AnyEventEnvelope;
|
|
60
|
+
readonly reason: string;
|
|
61
|
+
}
|
|
62
|
+
/** Bounded inspection queue for events rejected by routing or capacity. */
|
|
63
|
+
export declare class DeadLetterQueue {
|
|
64
|
+
readonly maxItems: number;
|
|
65
|
+
private items;
|
|
66
|
+
constructor(maxItems?: number);
|
|
67
|
+
put(event: AnyEventEnvelope, reason: string): void;
|
|
68
|
+
drain(): DeadLetter[];
|
|
69
|
+
snapshot(): DeadLetter[];
|
|
70
|
+
clear(): number;
|
|
71
|
+
get size(): number;
|
|
72
|
+
}
|
|
73
|
+
/** Verdict shapes accepted from a semantic classifier function. */
|
|
74
|
+
export type SemanticClassifierVerdict = RouteDecision | RouteStrategy | (string & {}) | null | undefined;
|
|
75
|
+
export type SemanticClassifierFn = (event: AnyEventEnvelope, active: TaskRecord | null) => SemanticClassifierVerdict | Promise<SemanticClassifierVerdict>;
|
|
76
|
+
/** Object-shaped classifier, e.g. SmallModelSemanticClassifier. */
|
|
77
|
+
export interface SemanticClassifierObject {
|
|
78
|
+
classify(event: AnyEventEnvelope, active: TaskRecord | null): RouteDecision | null | Promise<RouteDecision | null>;
|
|
79
|
+
}
|
|
80
|
+
export type SemanticClassifier = SemanticClassifierFn | SemanticClassifierObject;
|
|
81
|
+
export type SafetyPolicy = (event: AnyEventEnvelope, decision: RouteDecision) => RouteDecision | boolean | null | undefined;
|
|
82
|
+
export interface EventRouterOptions {
|
|
83
|
+
semanticClassifier?: SemanticClassifier | null;
|
|
84
|
+
safetyPolicy?: SafetyPolicy | null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Four-layer router with deterministic safety as the final arbiter.
|
|
88
|
+
*
|
|
89
|
+
* Layers one through three short-circuit after obtaining a decision. Layer
|
|
90
|
+
* four always runs, so an injected semantic classifier can never override
|
|
91
|
+
* cancellation, invalid task ownership, or a custom safety policy.
|
|
92
|
+
*/
|
|
93
|
+
export declare class EventRouter {
|
|
94
|
+
readonly taskRegistry: TaskRegistry;
|
|
95
|
+
private readonly semanticClassifier;
|
|
96
|
+
private readonly safetyPolicy;
|
|
97
|
+
constructor(taskRegistry: TaskRegistry, options?: EventRouterOptions);
|
|
98
|
+
route(event: AnyEventEnvelope): Promise<RoutedEvent>;
|
|
99
|
+
private metadataMatch;
|
|
100
|
+
private deterministicRules;
|
|
101
|
+
private semanticClassification;
|
|
102
|
+
private static defaultDecision;
|
|
103
|
+
private safetyArbiter;
|
|
104
|
+
}
|
|
105
|
+
/** Raised when a bounded queue exceeds its item or token budget. */
|
|
106
|
+
export declare class QueueOverflowError extends Error {
|
|
107
|
+
constructor(message: string);
|
|
108
|
+
}
|
|
109
|
+
export interface BoundedQueueOptions {
|
|
110
|
+
maxEstimatedTokens?: number;
|
|
111
|
+
}
|
|
112
|
+
export interface DrainCompatibleOptions {
|
|
113
|
+
taskId?: string | null;
|
|
114
|
+
strategy?: RouteStrategy | null;
|
|
115
|
+
timing?: RouteTiming | null;
|
|
116
|
+
}
|
|
117
|
+
/** FIFO queue with atomic compatible-batch snapshots. */
|
|
118
|
+
export declare class PendingQueue {
|
|
119
|
+
readonly maxItems: number;
|
|
120
|
+
readonly maxEstimatedTokens: number;
|
|
121
|
+
private items;
|
|
122
|
+
private estimatedTokensValue;
|
|
123
|
+
constructor(maxItems?: number, options?: BoundedQueueOptions);
|
|
124
|
+
put(event: RoutedEvent): void;
|
|
125
|
+
/** Atomically remove all currently eligible events in source order. */
|
|
126
|
+
drainCompatible(options?: DrainCompatibleOptions): RoutedEvent[];
|
|
127
|
+
/** Atomically remove every strategy bucket belonging to a task. */
|
|
128
|
+
drainTask(taskId: string): RoutedEvent[];
|
|
129
|
+
get estimatedTokens(): number;
|
|
130
|
+
snapshot(): RoutedEvent[];
|
|
131
|
+
clear(): number;
|
|
132
|
+
get size(): number;
|
|
133
|
+
}
|
|
134
|
+
export interface DrainHeldOptions {
|
|
135
|
+
taskId?: string | null;
|
|
136
|
+
}
|
|
137
|
+
/** Inputs retained while the active task is cancelling. */
|
|
138
|
+
export declare class HeldQueue {
|
|
139
|
+
readonly maxItems: number;
|
|
140
|
+
readonly maxEstimatedTokens: number;
|
|
141
|
+
private items;
|
|
142
|
+
private estimatedTokensValue;
|
|
143
|
+
constructor(maxItems?: number, options?: BoundedQueueOptions);
|
|
144
|
+
put(event: RoutedEvent): void;
|
|
145
|
+
drain(options?: DrainHeldOptions): RoutedEvent[];
|
|
146
|
+
get estimatedTokens(): number;
|
|
147
|
+
clear(): number;
|
|
148
|
+
get size(): number;
|
|
149
|
+
}
|
|
150
|
+
export interface ScheduleResult {
|
|
151
|
+
readonly immediate: readonly RoutedEvent[];
|
|
152
|
+
readonly queued: boolean;
|
|
153
|
+
readonly rejected: boolean;
|
|
154
|
+
readonly reason: string;
|
|
155
|
+
}
|
|
156
|
+
export interface SchedulerOptions {
|
|
157
|
+
pending?: PendingQueue;
|
|
158
|
+
held?: HeldQueue;
|
|
159
|
+
deadLetters?: DeadLetterQueue;
|
|
160
|
+
}
|
|
161
|
+
export declare class Scheduler {
|
|
162
|
+
readonly pending: PendingQueue;
|
|
163
|
+
readonly held: HeldQueue;
|
|
164
|
+
readonly deadLetters: DeadLetterQueue;
|
|
165
|
+
constructor(options?: SchedulerOptions);
|
|
166
|
+
schedule(event: RoutedEvent): ScheduleResult;
|
|
167
|
+
safePoint(taskId: string): RoutedEvent[];
|
|
168
|
+
}
|
|
169
|
+
/** Combine a pending snapshot into one model input without losing IDs. */
|
|
170
|
+
export declare function combineInput(events: Iterable<RoutedEvent>): string;
|