@cursor/july 0.1.103 → 0.1.104
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/channels/change-monitors/change-monitors-channel.d.ts +5 -0
- package/dist/channels/change-monitors/change-monitors-channel.d.ts.map +1 -0
- package/dist/channels/change-monitors/change-monitors-channel.js +41 -0
- package/dist/channels/change-monitors/index.d.ts +4 -0
- package/dist/channels/change-monitors/index.d.ts.map +1 -0
- package/dist/channels/change-monitors/index.js +2 -0
- package/dist/channels/change-monitors/types.d.ts +39 -0
- package/dist/channels/change-monitors/types.d.ts.map +1 -0
- package/dist/channels/change-monitors/types.js +1 -0
- package/dist/channels/github/defaults.d.ts.map +1 -1
- package/dist/channels/github/defaults.js +4 -0
- package/dist/channels/github/progress.d.ts +22 -0
- package/dist/channels/github/progress.d.ts.map +1 -1
- package/dist/channels/github/progress.js +32 -5
- package/dist/channels/issues/index.d.ts +4 -0
- package/dist/channels/issues/index.d.ts.map +1 -0
- package/dist/channels/issues/index.js +2 -0
- package/dist/channels/issues/issues-channel.d.ts +5 -0
- package/dist/channels/issues/issues-channel.d.ts.map +1 -0
- package/dist/channels/issues/issues-channel.js +37 -0
- package/dist/channels/issues/types.d.ts +33 -0
- package/dist/channels/issues/types.d.ts.map +1 -0
- package/dist/channels/issues/types.js +1 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DW9pXkpL.js → app.DbMWUyGb.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.Bqsulb9K.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CdEIUwDg.js → VPLocalSearchBox.DcNeTh9o.js} +1 -1
- package/dist/docs/assets/chunks/{theme.YZMHgPgg.js → theme.DMjhY3gl.js} +2 -2
- package/dist/docs/assets/{guides_github.md.DKYiT9do.js → guides_github.md.c0gdGJ-D.js} +1 -1
- package/dist/docs/assets/{guides_github.md.DKYiT9do.lean.js → guides_github.md.c0gdGJ-D.lean.js} +1 -1
- package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.js → reference_channels.md.DBGcaB7m.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.lean.js → reference_channels.md.DBGcaB7m.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/design/filesystem.md +376 -0
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-agents.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/github.md +5 -0
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +4 -4
- package/dist/docs/llms-full.txt +406 -0
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/channels.md +20 -0
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/extensions.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/result.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/skills/ab.html +4 -4
- package/dist/docs/skills/create-agent.html +4 -4
- package/dist/docs/skills/debug.html +4 -4
- package/dist/docs/skills/evals.html +4 -4
- package/dist/docs/skills/framework-map.html +4 -4
- package/dist/docs/skills/github.html +4 -4
- package/dist/docs/skills/hillclimb.html +4 -4
- package/dist/docs/skills/index.html +4 -4
- package/dist/docs/skills/mcp-auth.html +4 -4
- package/dist/docs/skills/otel.html +4 -4
- package/dist/docs/skills/setup-slack.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +4 -4
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/filesystem.d.ts +440 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +1694 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/continuation-channel.d.ts +4 -2
- package/dist/internal/continuation-channel.d.ts.map +1 -1
- package/dist/internal/continuation-channel.js +21 -12
- package/dist/internal/continuation-identity.d.ts +13 -5
- package/dist/internal/continuation-identity.d.ts.map +1 -1
- package/dist/internal/continuation-identity.js +74 -35
- package/dist/internal/discovery/agent.d.ts.map +1 -1
- package/dist/internal/discovery/agent.js +80 -8
- package/dist/internal/discovery/info.d.ts.map +1 -1
- package/dist/internal/discovery/info.js +3 -0
- package/dist/internal/factory-resource-channel.d.ts +33 -0
- package/dist/internal/factory-resource-channel.d.ts.map +1 -0
- package/dist/internal/factory-resource-channel.js +77 -0
- package/dist/internal/filesystem/errors.d.ts +64 -0
- package/dist/internal/filesystem/errors.d.ts.map +1 -0
- package/dist/internal/filesystem/errors.js +175 -0
- package/dist/internal/filesystem/format.d.ts +28 -0
- package/dist/internal/filesystem/format.d.ts.map +1 -0
- package/dist/internal/filesystem/format.js +213 -0
- package/dist/internal/filesystem/merge.d.ts +34 -0
- package/dist/internal/filesystem/merge.d.ts.map +1 -0
- package/dist/internal/filesystem/merge.js +196 -0
- package/dist/internal/filesystem/scratch.d.ts +59 -0
- package/dist/internal/filesystem/scratch.d.ts.map +1 -0
- package/dist/internal/filesystem/scratch.js +212 -0
- package/dist/internal/filesystem/sdk-schemas.d.ts +29 -0
- package/dist/internal/filesystem/sdk-schemas.d.ts.map +1 -0
- package/dist/internal/filesystem/sdk-schemas.js +153 -0
- package/dist/internal/filesystem/tools.d.ts +42 -0
- package/dist/internal/filesystem/tools.d.ts.map +1 -0
- package/dist/internal/filesystem/tools.js +618 -0
- package/dist/internal/filesystem/walk.d.ts +74 -0
- package/dist/internal/filesystem/walk.d.ts.map +1 -0
- package/dist/internal/filesystem/walk.js +339 -0
- package/dist/internal/guest-network.d.ts +5 -2
- package/dist/internal/guest-network.d.ts.map +1 -1
- package/dist/internal/guest-network.js +7 -2
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +12 -2
- package/dist/internal/sdk-runner.d.ts +1 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +3 -1
- package/dist/internal/session-engine.d.ts +22 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +362 -40
- package/dist/internal/testing/factory-resource-fixtures.d.ts +48 -0
- package/dist/internal/testing/factory-resource-fixtures.d.ts.map +1 -0
- package/dist/internal/testing/factory-resource-fixtures.js +41 -0
- package/dist/playground/assets/{index-59KvgREd.js → index-8YiozU89.js} +38 -38
- package/dist/playground/index.html +1 -1
- package/dist/types.d.ts +51 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/design/filesystem.md +376 -0
- package/docs/guides/github.md +5 -0
- package/docs/reference/channels.md +20 -0
- package/package.json +16 -2
- package/src/channels/change-monitors/change-monitors-channel.ts +62 -0
- package/src/channels/change-monitors/index.ts +8 -0
- package/src/channels/change-monitors/types.ts +44 -0
- package/src/channels/github/defaults.ts +4 -0
- package/src/channels/github/progress.ts +76 -10
- package/src/channels/issues/index.ts +9 -0
- package/src/channels/issues/issues-channel.ts +51 -0
- package/src/channels/issues/types.ts +41 -0
- package/src/filesystem.ts +2074 -0
- package/src/index.ts +28 -0
- package/src/internal/continuation-channel.ts +29 -12
- package/src/internal/continuation-identity.ts +122 -37
- package/src/internal/discovery/agent.ts +87 -1
- package/src/internal/discovery/info.ts +3 -0
- package/src/internal/factory-resource-channel.ts +122 -0
- package/src/internal/filesystem/errors.ts +220 -0
- package/src/internal/filesystem/format.ts +276 -0
- package/src/internal/filesystem/merge.ts +195 -0
- package/src/internal/filesystem/scratch.ts +255 -0
- package/src/internal/filesystem/sdk-schemas.ts +204 -0
- package/src/internal/filesystem/tools.ts +821 -0
- package/src/internal/filesystem/walk.ts +369 -0
- package/src/internal/guest-network.ts +8 -2
- package/src/internal/hosted-delivery.ts +18 -1
- package/src/internal/sdk-runner.ts +4 -0
- package/src/internal/session-engine.ts +439 -29
- package/src/internal/testing/factory-resource-fixtures.ts +85 -0
- package/src/types.ts +49 -3
- package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +0 -1
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive listing, existence probes, and native LS trees. Matches the
|
|
3
|
+
* change-monitor VFS contract on origin/main: `list` is files-only and
|
|
4
|
+
* recursive; `stat` tells file / directory / missing apart; composers
|
|
5
|
+
* merge already-sorted streams with a k-way heap.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { posix } from "node:path";
|
|
9
|
+
import { minimatch } from "minimatch";
|
|
10
|
+
import type { DirEntry, FileInfo, FileSystem } from "../../filesystem.js";
|
|
11
|
+
import { normalizeFsPath } from "../../filesystem.js";
|
|
12
|
+
import { mergeSorted } from "./merge.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Native LS `directoryTreeRoot` (LocalLsExecutor ingest tree).
|
|
16
|
+
* Synthesis builds this from `list` + `stat` via `buildLsTree`.
|
|
17
|
+
* Not a FileSystem method result and not part of the public
|
|
18
|
+
* Agent SDK surface.
|
|
19
|
+
*/
|
|
20
|
+
export interface LsDirectoryTreeNode {
|
|
21
|
+
absPath: string;
|
|
22
|
+
childrenDirs: LsDirectoryTreeNode[];
|
|
23
|
+
childrenFiles: Array<{ name: string }>;
|
|
24
|
+
childrenWereProcessed: boolean;
|
|
25
|
+
fullSubtreeExtensionCounts: Record<string, number>;
|
|
26
|
+
numFiles: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Filename-character ingest budget; matches LocalLsExecutor. */
|
|
30
|
+
export const LS_INGEST_CHARACTER_BUDGET = 2_500;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Yield the files under `prefix`, sorted. A file whose path equals
|
|
34
|
+
* `prefix` is not under itself.
|
|
35
|
+
*/
|
|
36
|
+
export async function* listFilesFromPaths(
|
|
37
|
+
files: Iterable<DirEntry>,
|
|
38
|
+
prefix?: string
|
|
39
|
+
): AsyncGenerator<DirEntry> {
|
|
40
|
+
const scope = normalizeListPrefix(prefix);
|
|
41
|
+
const withSlash = scope === "/" ? "/" : `${scope}/`;
|
|
42
|
+
const matches: DirEntry[] = [];
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
const filePath = normalizeFsPath(file.path);
|
|
45
|
+
if (scope === "/") {
|
|
46
|
+
matches.push(
|
|
47
|
+
file.size === undefined
|
|
48
|
+
? { path: filePath }
|
|
49
|
+
: { path: filePath, size: file.size }
|
|
50
|
+
);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (filePath === scope || !filePath.startsWith(withSlash)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
matches.push(
|
|
57
|
+
file.size === undefined
|
|
58
|
+
? { path: filePath }
|
|
59
|
+
: { path: filePath, size: file.size }
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
matches.sort((left, right) => left.path.localeCompare(right.path));
|
|
63
|
+
yield* matches;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function normalizeListPrefix(prefix?: string): string {
|
|
67
|
+
if (prefix === undefined || prefix === "" || prefix === "/") {
|
|
68
|
+
return "/";
|
|
69
|
+
}
|
|
70
|
+
return normalizeFsPath(prefix);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Existence probe over a flat file-path index. Exact key → file; any
|
|
75
|
+
* key with this prefix → directory; `""` / `/` → directory; else missing.
|
|
76
|
+
*/
|
|
77
|
+
export function statFromFilePaths(
|
|
78
|
+
files: Iterable<string>,
|
|
79
|
+
path: string
|
|
80
|
+
): FileInfo {
|
|
81
|
+
const normalized = normalizeListPrefix(path);
|
|
82
|
+
if (normalized === "/") {
|
|
83
|
+
return { kind: "directory" };
|
|
84
|
+
}
|
|
85
|
+
const prefix = `${normalized}/`;
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const filePath = normalizeFsPath(file);
|
|
88
|
+
if (filePath === normalized) {
|
|
89
|
+
return { kind: "file" };
|
|
90
|
+
}
|
|
91
|
+
if (filePath.startsWith(prefix)) {
|
|
92
|
+
return { kind: "directory" };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { kind: "missing" };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function collectList(
|
|
99
|
+
root: Pick<FileSystem, "list">,
|
|
100
|
+
prefix?: string
|
|
101
|
+
): Promise<DirEntry[]> {
|
|
102
|
+
const entries: DirEntry[] = [];
|
|
103
|
+
if (root.list === undefined) {
|
|
104
|
+
return entries;
|
|
105
|
+
}
|
|
106
|
+
for await (const entry of root.list(prefix)) {
|
|
107
|
+
entries.push(entry);
|
|
108
|
+
}
|
|
109
|
+
return entries;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function collectGlob(
|
|
113
|
+
root: Pick<FileSystem, "glob">,
|
|
114
|
+
options: { pattern: string; dir?: string }
|
|
115
|
+
): Promise<DirEntry[]> {
|
|
116
|
+
const entries: DirEntry[] = [];
|
|
117
|
+
if (root.glob === undefined) {
|
|
118
|
+
return entries;
|
|
119
|
+
}
|
|
120
|
+
for await (const entry of root.glob(options.pattern, options.dir)) {
|
|
121
|
+
entries.push(entry);
|
|
122
|
+
}
|
|
123
|
+
return entries;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* List, then `stat` only when nothing came back. A non-empty listing is
|
|
128
|
+
* already a directory (a file is not under itself).
|
|
129
|
+
*/
|
|
130
|
+
export async function collectListOrStat(
|
|
131
|
+
root: Pick<FileSystem, "list" | "stat">,
|
|
132
|
+
path?: string
|
|
133
|
+
): Promise<{ kind: FileInfo["kind"]; files: DirEntry[] }> {
|
|
134
|
+
const files = await collectList(root, path);
|
|
135
|
+
if (files.length > 0) {
|
|
136
|
+
return { kind: "directory", files };
|
|
137
|
+
}
|
|
138
|
+
if (root.stat === undefined) {
|
|
139
|
+
return { kind: "missing", files };
|
|
140
|
+
}
|
|
141
|
+
const probe = path === undefined || path === "" ? "." : path;
|
|
142
|
+
return { kind: (await root.stat(probe)).kind, files };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function* mergeSortedByPath(
|
|
146
|
+
sources: Array<Iterable<DirEntry> | AsyncIterable<DirEntry>>,
|
|
147
|
+
options?: { unique?: boolean }
|
|
148
|
+
): AsyncGenerator<DirEntry> {
|
|
149
|
+
yield* mergeSorted(sources, {
|
|
150
|
+
compare: (left, right) => left.path.localeCompare(right.path),
|
|
151
|
+
unique: options?.unique === true,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function emptyLsNode(absPath: string): LsDirectoryTreeNode {
|
|
156
|
+
return {
|
|
157
|
+
absPath,
|
|
158
|
+
childrenDirs: [],
|
|
159
|
+
childrenFiles: [],
|
|
160
|
+
childrenWereProcessed: true,
|
|
161
|
+
fullSubtreeExtensionCounts: {},
|
|
162
|
+
numFiles: 0,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Build the native LS tree from a recursive file listing. Applies the
|
|
168
|
+
* LocalLsExecutor ingest budget so a huge directory truncates the same way.
|
|
169
|
+
*/
|
|
170
|
+
export function buildLsTree(
|
|
171
|
+
dir: string,
|
|
172
|
+
files: readonly DirEntry[]
|
|
173
|
+
): LsDirectoryTreeNode {
|
|
174
|
+
const rootPath = normalizeListPrefix(dir);
|
|
175
|
+
const root: LsDirectoryTreeNode = emptyLsNode(rootPath);
|
|
176
|
+
const dirNodes = new Map<string, LsDirectoryTreeNode>([[rootPath, root]]);
|
|
177
|
+
const state = {
|
|
178
|
+
charactersUsed: rootPath.length,
|
|
179
|
+
budgetExceeded: false,
|
|
180
|
+
};
|
|
181
|
+
for (const file of files) {
|
|
182
|
+
addPathToTree(
|
|
183
|
+
file.path,
|
|
184
|
+
rootPath,
|
|
185
|
+
dirNodes,
|
|
186
|
+
state,
|
|
187
|
+
LS_INGEST_CHARACTER_BUDGET
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return root;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function addPathToTree(
|
|
194
|
+
filePath: string,
|
|
195
|
+
rootPath: string,
|
|
196
|
+
dirNodes: Map<string, LsDirectoryTreeNode>,
|
|
197
|
+
state: { charactersUsed: number; budgetExceeded: boolean },
|
|
198
|
+
characterBudget: number
|
|
199
|
+
): void {
|
|
200
|
+
const fileName = posix.basename(filePath);
|
|
201
|
+
const dirPath = posixParent(filePath);
|
|
202
|
+
ensureDirectoryPath(dirPath, rootPath, dirNodes, state.budgetExceeded);
|
|
203
|
+
const parentNode = dirNodes.get(dirPath);
|
|
204
|
+
if (parentNode === undefined) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const fileCharCost = fileName.length;
|
|
208
|
+
if (
|
|
209
|
+
!state.budgetExceeded &&
|
|
210
|
+
state.charactersUsed + fileCharCost <= characterBudget
|
|
211
|
+
) {
|
|
212
|
+
parentNode.childrenFiles.push({ name: fileName });
|
|
213
|
+
state.charactersUsed += fileCharCost;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!state.budgetExceeded) {
|
|
217
|
+
state.budgetExceeded = true;
|
|
218
|
+
let currentDir = dirPath;
|
|
219
|
+
while (currentDir.startsWith(rootPath)) {
|
|
220
|
+
const node = dirNodes.get(currentDir);
|
|
221
|
+
if (node !== undefined) {
|
|
222
|
+
node.childrenWereProcessed = false;
|
|
223
|
+
}
|
|
224
|
+
const parentDir = posixParent(currentDir);
|
|
225
|
+
if (parentDir === currentDir) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
currentDir = parentDir;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const extension = posix.extname(filePath);
|
|
232
|
+
let currentDir = dirPath;
|
|
233
|
+
while (currentDir.startsWith(rootPath)) {
|
|
234
|
+
const node = dirNodes.get(currentDir);
|
|
235
|
+
if (node !== undefined) {
|
|
236
|
+
node.fullSubtreeExtensionCounts[extension] =
|
|
237
|
+
(node.fullSubtreeExtensionCounts[extension] ?? 0) + 1;
|
|
238
|
+
node.numFiles += 1;
|
|
239
|
+
}
|
|
240
|
+
const parentDir = posixParent(currentDir);
|
|
241
|
+
if (parentDir === currentDir) {
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
currentDir = parentDir;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function ensureDirectoryPath(
|
|
249
|
+
dirPath: string,
|
|
250
|
+
rootPath: string,
|
|
251
|
+
dirNodes: Map<string, LsDirectoryTreeNode>,
|
|
252
|
+
budgetExceeded: boolean
|
|
253
|
+
): void {
|
|
254
|
+
if (dirNodes.has(dirPath) || dirPath === rootPath) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const parentPath = posixParent(dirPath);
|
|
258
|
+
if (parentPath !== dirPath && pathIsUnderRoot(parentPath, rootPath)) {
|
|
259
|
+
ensureDirectoryPath(parentPath, rootPath, dirNodes, budgetExceeded);
|
|
260
|
+
}
|
|
261
|
+
const dirNode: LsDirectoryTreeNode = {
|
|
262
|
+
...emptyLsNode(dirPath),
|
|
263
|
+
childrenWereProcessed: !budgetExceeded,
|
|
264
|
+
};
|
|
265
|
+
dirNodes.set(dirPath, dirNode);
|
|
266
|
+
const parentNode = dirNodes.get(parentPath);
|
|
267
|
+
if (parentNode !== undefined) {
|
|
268
|
+
parentNode.childrenDirs.push(dirNode);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function pathIsUnderRoot(path: string, rootPath: string): boolean {
|
|
273
|
+
return (
|
|
274
|
+
path === rootPath ||
|
|
275
|
+
path.startsWith(rootPath === "/" ? "/" : `${rootPath}/`)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Native LS ignore_globs: a pattern that is not already recursive is
|
|
281
|
+
* matched at any depth (the same `** /` prefix the Cursor SDK native uses).
|
|
282
|
+
*/
|
|
283
|
+
export function applyIgnores(
|
|
284
|
+
entries: readonly DirEntry[],
|
|
285
|
+
ignoreGlobs?: readonly string[]
|
|
286
|
+
): DirEntry[] {
|
|
287
|
+
if (ignoreGlobs === undefined || ignoreGlobs.length === 0) {
|
|
288
|
+
return [...entries];
|
|
289
|
+
}
|
|
290
|
+
return entries.filter(
|
|
291
|
+
(entry) => !ignoreGlobs.some((glob) => matchesIgnoreGlob(entry.path, glob))
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function matchesIgnoreGlob(filePath: string, glob: string): boolean {
|
|
296
|
+
const recursivePrefix = "**".concat("/");
|
|
297
|
+
const pattern = glob.startsWith(recursivePrefix)
|
|
298
|
+
? glob
|
|
299
|
+
: recursivePrefix + glob;
|
|
300
|
+
const relative = filePath.replace(/^\//, "");
|
|
301
|
+
return (
|
|
302
|
+
minimatch(relative, pattern, { dot: true }) ||
|
|
303
|
+
minimatch(filePath, pattern, { dot: true })
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export async function lsFromList(
|
|
308
|
+
root: Pick<FileSystem, "list" | "stat">,
|
|
309
|
+
path?: string,
|
|
310
|
+
ignoreGlobs?: readonly string[]
|
|
311
|
+
): Promise<
|
|
312
|
+
| { status: "success"; value: { directoryTreeRoot: LsDirectoryTreeNode } }
|
|
313
|
+
| { status: "error"; error: { message: string } }
|
|
314
|
+
> {
|
|
315
|
+
const listed = await collectListOrStat(root, path);
|
|
316
|
+
const treePath = path ?? inferListedRoot(listed.files);
|
|
317
|
+
switch (listed.kind) {
|
|
318
|
+
case "missing":
|
|
319
|
+
return {
|
|
320
|
+
status: "error",
|
|
321
|
+
error: { message: `Path does not exist: ${treePath}` },
|
|
322
|
+
};
|
|
323
|
+
case "file":
|
|
324
|
+
return {
|
|
325
|
+
status: "error",
|
|
326
|
+
error: { message: `Path is not a directory: ${treePath}` },
|
|
327
|
+
};
|
|
328
|
+
case "directory":
|
|
329
|
+
return {
|
|
330
|
+
status: "success",
|
|
331
|
+
value: {
|
|
332
|
+
directoryTreeRoot: buildLsTree(
|
|
333
|
+
treePath,
|
|
334
|
+
applyIgnores(listed.files, ignoreGlobs)
|
|
335
|
+
),
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function inferListedRoot(files: readonly DirEntry[]): string {
|
|
342
|
+
if (files.length === 0) {
|
|
343
|
+
return "/";
|
|
344
|
+
}
|
|
345
|
+
let prefix = posixParent(files[0]!.path);
|
|
346
|
+
for (const file of files) {
|
|
347
|
+
while (
|
|
348
|
+
prefix !== "/" &&
|
|
349
|
+
file.path !== prefix &&
|
|
350
|
+
!file.path.startsWith(`${prefix}/`)
|
|
351
|
+
) {
|
|
352
|
+
const parent = posixParent(prefix);
|
|
353
|
+
if (parent === prefix) {
|
|
354
|
+
return "/";
|
|
355
|
+
}
|
|
356
|
+
prefix = parent;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return prefix;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* `path.posix.dirname` returns `.` for slash-free paths. Treat that as
|
|
364
|
+
* the virtual root so LS walks terminate and relative files attach to `/`.
|
|
365
|
+
*/
|
|
366
|
+
function posixParent(path: string): string {
|
|
367
|
+
const dir = posix.dirname(path);
|
|
368
|
+
return dir === "." || dir === path ? "/" : dir;
|
|
369
|
+
}
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Quiet guest Connect (`TypeError: fetch failed` with no errno, Connect
|
|
16
16
|
* `[unknown]`/`[canceled]`/`[unavailable]`, headers/body timeout) is a
|
|
17
|
-
* stall, not a dead NIC. Those become `guest_connect_stalled` so TAS
|
|
18
|
-
*
|
|
17
|
+
* stall, not a dead NIC. Those become `guest_connect_stalled` so TAS
|
|
18
|
+
* retries the same pod — including when `waitForCompletion` resolves
|
|
19
|
+
* with that message instead of throwing.
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
22
|
export const GUEST_NETWORK_UNAVAILABLE = "guest_network_unavailable";
|
|
@@ -112,6 +113,11 @@ export function isQuietGuestConnectStall(error: unknown): boolean {
|
|
|
112
113
|
return isQuietSignatureAtDepth(error, 0);
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
/** Message-only form for `waitForCompletion` outcomes that do not throw. */
|
|
117
|
+
export function isQuietGuestConnectStallMessage(message: string): boolean {
|
|
118
|
+
return QUIET_CONNECT_MESSAGE.test(message);
|
|
119
|
+
}
|
|
120
|
+
|
|
115
121
|
function errorCode(error: Error): string | undefined {
|
|
116
122
|
const code = (error as NodeJS.ErrnoException).code;
|
|
117
123
|
return typeof code === "string" && code.length > 0 ? code : undefined;
|
|
@@ -15,10 +15,12 @@ import {
|
|
|
15
15
|
} from "./deferred-channel-session.js";
|
|
16
16
|
import {
|
|
17
17
|
formatGuestNetworkServerTiming,
|
|
18
|
+
GUEST_CONNECT_STALLED,
|
|
18
19
|
GUEST_NETWORK_UNAVAILABLE,
|
|
19
20
|
GuestNetworkUnavailableError,
|
|
20
21
|
type GuestNetworkWaitResult,
|
|
21
22
|
type GuestNetworkWaitStats,
|
|
23
|
+
isQuietGuestConnectStallMessage,
|
|
22
24
|
waitForGuestNetworkReady,
|
|
23
25
|
} from "./guest-network.js";
|
|
24
26
|
import {
|
|
@@ -647,11 +649,14 @@ export function registerHostedDeliveryRoute(input: {
|
|
|
647
649
|
),
|
|
648
650
|
});
|
|
649
651
|
if (outcome.status !== "finished") {
|
|
652
|
+
// waitForCompletion resolves (does not throw) on a quiet
|
|
653
|
+
// Connect abort. Map that to guest_connect_stalled so TAS
|
|
654
|
+
// retries the same pod. Real runner failures stay turn_failed.
|
|
650
655
|
return {
|
|
651
656
|
status: 503,
|
|
652
657
|
body: {
|
|
653
658
|
ok: false,
|
|
654
|
-
error:
|
|
659
|
+
error: hostedErrorForUnfinishedTurn(outcome.errorMessage),
|
|
655
660
|
turnStatus: outcome.status,
|
|
656
661
|
message: outcome.errorMessage,
|
|
657
662
|
},
|
|
@@ -682,6 +687,18 @@ export function registerHostedDeliveryRoute(input: {
|
|
|
682
687
|
});
|
|
683
688
|
}
|
|
684
689
|
|
|
690
|
+
function hostedErrorForUnfinishedTurn(
|
|
691
|
+
errorMessage: string | undefined
|
|
692
|
+
): typeof GUEST_CONNECT_STALLED | "turn_failed" {
|
|
693
|
+
if (
|
|
694
|
+
errorMessage !== undefined &&
|
|
695
|
+
isQuietGuestConnectStallMessage(errorMessage)
|
|
696
|
+
) {
|
|
697
|
+
return GUEST_CONNECT_STALLED;
|
|
698
|
+
}
|
|
699
|
+
return "turn_failed";
|
|
700
|
+
}
|
|
701
|
+
|
|
685
702
|
async function afterHostedWork<T>(
|
|
686
703
|
flushOtel: (() => Promise<void>) | undefined,
|
|
687
704
|
work: () => Promise<T>
|
|
@@ -67,6 +67,7 @@ export type RunnerPrewarmRequest = Pick<
|
|
|
67
67
|
| "runnerStateDir"
|
|
68
68
|
| "sandbox"
|
|
69
69
|
| "tools"
|
|
70
|
+
| "disallowedTools"
|
|
70
71
|
| "mcpServers"
|
|
71
72
|
| "perSendMcpServers"
|
|
72
73
|
| "subagents"
|
|
@@ -542,6 +543,9 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
542
543
|
// tool restrictions on cloud agents. Passed on create *and* resume
|
|
543
544
|
// (both flow through here) — the SDK does not persist it across runs.
|
|
544
545
|
...(request.tools === undefined ? {} : { tools: request.tools }),
|
|
546
|
+
...(request.disallowedTools === undefined
|
|
547
|
+
? {}
|
|
548
|
+
: { disallowedTools: request.disallowedTools }),
|
|
545
549
|
local: {
|
|
546
550
|
cwd: request.workspaceDir,
|
|
547
551
|
settingSources: ["project"],
|