@cursor/july 0.1.103 → 0.1.105

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.
Files changed (264) hide show
  1. package/dist/channels/change-monitors/change-monitors-channel.d.ts +5 -0
  2. package/dist/channels/change-monitors/change-monitors-channel.d.ts.map +1 -0
  3. package/dist/channels/change-monitors/change-monitors-channel.js +41 -0
  4. package/dist/channels/change-monitors/index.d.ts +4 -0
  5. package/dist/channels/change-monitors/index.d.ts.map +1 -0
  6. package/dist/channels/change-monitors/index.js +2 -0
  7. package/dist/channels/change-monitors/types.d.ts +39 -0
  8. package/dist/channels/change-monitors/types.d.ts.map +1 -0
  9. package/dist/channels/change-monitors/types.js +1 -0
  10. package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
  11. package/dist/channels/deployments/deployments-channel.js +1 -0
  12. package/dist/channels/deployments/types.d.ts +2 -0
  13. package/dist/channels/deployments/types.d.ts.map +1 -1
  14. package/dist/channels/github/defaults.d.ts.map +1 -1
  15. package/dist/channels/github/defaults.js +4 -0
  16. package/dist/channels/github/progress.d.ts +22 -0
  17. package/dist/channels/github/progress.d.ts.map +1 -1
  18. package/dist/channels/github/progress.js +32 -5
  19. package/dist/channels/issues/index.d.ts +4 -0
  20. package/dist/channels/issues/index.d.ts.map +1 -0
  21. package/dist/channels/issues/index.js +2 -0
  22. package/dist/channels/issues/issues-channel.d.ts +5 -0
  23. package/dist/channels/issues/issues-channel.d.ts.map +1 -0
  24. package/dist/channels/issues/issues-channel.js +37 -0
  25. package/dist/channels/issues/types.d.ts +33 -0
  26. package/dist/channels/issues/types.d.ts.map +1 -0
  27. package/dist/channels/issues/types.js +1 -0
  28. package/dist/docs/404.html +2 -2
  29. package/dist/docs/ab.html +4 -4
  30. package/dist/docs/assets/{app.DW9pXkpL.js → app.BVwaffdK.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.CdEIUwDg.js → VPLocalSearchBox.WuWTZ1x8.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.YZMHgPgg.js → theme.BYdI_6sF.js} +2 -2
  34. package/dist/docs/assets/{guides_github.md.DKYiT9do.js → guides_github.md.c0gdGJ-D.js} +1 -1
  35. package/dist/docs/assets/{guides_github.md.DKYiT9do.lean.js → guides_github.md.c0gdGJ-D.lean.js} +1 -1
  36. package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.js +9 -0
  37. package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.lean.js +1 -0
  38. package/dist/docs/assets/index.md.BW_6tOgR.js +5 -0
  39. package/dist/docs/assets/index.md.BW_6tOgR.lean.js +1 -0
  40. package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.js → reference_channels.md.DBGcaB7m.js} +2 -2
  41. package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.lean.js → reference_channels.md.DBGcaB7m.lean.js} +1 -1
  42. package/dist/docs/assets/{reference_cli.md.CJNc_xYK.js → reference_cli.md.C6Lo8SEd.js} +2 -1
  43. package/dist/docs/assets/{reference_extensions.md.DykHW3qL.js → reference_extensions.md.CGmMLblt.js} +1 -1
  44. package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.js +3 -0
  45. package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.lean.js +1 -0
  46. package/dist/docs/building-with-agents.html +4 -4
  47. package/dist/docs/concepts.html +4 -4
  48. package/dist/docs/deployment.html +4 -4
  49. package/dist/docs/design/filesystem.md +376 -0
  50. package/dist/docs/evals.html +4 -4
  51. package/dist/docs/guides/agent-to-agent.html +4 -4
  52. package/dist/docs/guides/cloud-agents.html +5 -5
  53. package/dist/docs/guides/convert-automation.html +4 -4
  54. package/dist/docs/guides/github.html +6 -6
  55. package/dist/docs/guides/github.md +5 -0
  56. package/dist/docs/guides/grokbot-agents.html +35 -0
  57. package/dist/docs/guides/grokbot-agents.md +88 -0
  58. package/dist/docs/guides/human-in-the-loop.html +4 -4
  59. package/dist/docs/guides/mcp-oauth.html +4 -4
  60. package/dist/docs/guides/opentelemetry.html +5 -5
  61. package/dist/docs/guides/slack.html +4 -4
  62. package/dist/docs/guides/webhooks.html +4 -4
  63. package/dist/docs/hashmap.json +1 -1
  64. package/dist/docs/hillclimbing.html +4 -4
  65. package/dist/docs/index.html +6 -6
  66. package/dist/docs/index.md +6 -1
  67. package/dist/docs/llms-full.txt +562 -2
  68. package/dist/docs/llms.txt +3 -0
  69. package/dist/docs/quickstart.html +4 -4
  70. package/dist/docs/reference/agent-config.html +4 -4
  71. package/dist/docs/reference/artifacts.html +4 -4
  72. package/dist/docs/reference/channels.html +6 -6
  73. package/dist/docs/reference/channels.md +20 -0
  74. package/dist/docs/reference/cli.html +7 -6
  75. package/dist/docs/reference/cli.md +2 -1
  76. package/dist/docs/reference/connections.html +4 -4
  77. package/dist/docs/reference/extensions.html +6 -6
  78. package/dist/docs/reference/extensions.md +2 -0
  79. package/dist/docs/reference/hooks.html +4 -4
  80. package/dist/docs/reference/http-api.html +4 -4
  81. package/dist/docs/reference/instructions.html +4 -4
  82. package/dist/docs/reference/playground.html +4 -4
  83. package/dist/docs/reference/project-layout.html +4 -4
  84. package/dist/docs/reference/prompt.html +4 -4
  85. package/dist/docs/reference/result.html +4 -4
  86. package/dist/docs/reference/schedules.html +4 -4
  87. package/dist/docs/reference/sessions.html +4 -4
  88. package/dist/docs/reference/skills.html +4 -4
  89. package/dist/docs/reference/subagents.html +4 -4
  90. package/dist/docs/reference/tools.html +4 -4
  91. package/dist/docs/scaffolding-agents.html +4 -4
  92. package/dist/docs/skills/ab.html +4 -4
  93. package/dist/docs/skills/create-agent.html +4 -4
  94. package/dist/docs/skills/debug.html +4 -4
  95. package/dist/docs/skills/evals.html +4 -4
  96. package/dist/docs/skills/framework-map.html +4 -4
  97. package/dist/docs/skills/github.html +4 -4
  98. package/dist/docs/skills/hillclimb.html +4 -4
  99. package/dist/docs/skills/index.html +4 -4
  100. package/dist/docs/skills/mcp-auth.html +4 -4
  101. package/dist/docs/skills/otel.html +4 -4
  102. package/dist/docs/skills/setup-slack.html +4 -4
  103. package/dist/docs/storage.html +4 -4
  104. package/dist/docs/templates/agentic-owners.html +4 -4
  105. package/dist/docs/templates/agents-md.html +4 -4
  106. package/dist/docs/templates/code-wiki.html +5 -5
  107. package/dist/docs/templates/demo.html +5 -5
  108. package/dist/docs/templates/grokbot-agents.html +29 -0
  109. package/dist/docs/templates/grokbot-agents.md +48 -0
  110. package/dist/docs/templates/pr-autofixer.html +4 -4
  111. package/dist/docs/templates/security-help.html +4 -4
  112. package/dist/docs/templates/security-reviewer.html +4 -4
  113. package/dist/docs/templates/triage.html +4 -4
  114. package/dist/docs/troubleshooting.html +4 -4
  115. package/dist/files.d.ts +7 -2
  116. package/dist/files.d.ts.map +1 -1
  117. package/dist/files.js +7 -2
  118. package/dist/filesystem.d.ts +440 -0
  119. package/dist/filesystem.d.ts.map +1 -0
  120. package/dist/filesystem.js +1694 -0
  121. package/dist/index.d.ts +2 -0
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +1 -0
  124. package/dist/internal/continuation-channel.d.ts +4 -2
  125. package/dist/internal/continuation-channel.d.ts.map +1 -1
  126. package/dist/internal/continuation-channel.js +21 -12
  127. package/dist/internal/continuation-identity.d.ts +13 -5
  128. package/dist/internal/continuation-identity.d.ts.map +1 -1
  129. package/dist/internal/continuation-identity.js +74 -35
  130. package/dist/internal/cursor-agent-template.d.ts +1 -1
  131. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  132. package/dist/internal/cursor-agent-template.js +1 -0
  133. package/dist/internal/discovery/agent.d.ts.map +1 -1
  134. package/dist/internal/discovery/agent.js +80 -8
  135. package/dist/internal/discovery/info.d.ts.map +1 -1
  136. package/dist/internal/discovery/info.js +3 -0
  137. package/dist/internal/factory-resource-channel.d.ts +33 -0
  138. package/dist/internal/factory-resource-channel.d.ts.map +1 -0
  139. package/dist/internal/factory-resource-channel.js +77 -0
  140. package/dist/internal/filesystem/errors.d.ts +64 -0
  141. package/dist/internal/filesystem/errors.d.ts.map +1 -0
  142. package/dist/internal/filesystem/errors.js +175 -0
  143. package/dist/internal/filesystem/format.d.ts +28 -0
  144. package/dist/internal/filesystem/format.d.ts.map +1 -0
  145. package/dist/internal/filesystem/format.js +213 -0
  146. package/dist/internal/filesystem/merge.d.ts +34 -0
  147. package/dist/internal/filesystem/merge.d.ts.map +1 -0
  148. package/dist/internal/filesystem/merge.js +196 -0
  149. package/dist/internal/filesystem/scratch.d.ts +59 -0
  150. package/dist/internal/filesystem/scratch.d.ts.map +1 -0
  151. package/dist/internal/filesystem/scratch.js +212 -0
  152. package/dist/internal/filesystem/sdk-schemas.d.ts +29 -0
  153. package/dist/internal/filesystem/sdk-schemas.d.ts.map +1 -0
  154. package/dist/internal/filesystem/sdk-schemas.js +153 -0
  155. package/dist/internal/filesystem/tools.d.ts +42 -0
  156. package/dist/internal/filesystem/tools.d.ts.map +1 -0
  157. package/dist/internal/filesystem/tools.js +618 -0
  158. package/dist/internal/filesystem/walk.d.ts +74 -0
  159. package/dist/internal/filesystem/walk.d.ts.map +1 -0
  160. package/dist/internal/filesystem/walk.js +339 -0
  161. package/dist/internal/framework-file-storage.d.ts +3 -1
  162. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  163. package/dist/internal/framework-file-storage.js +3 -1
  164. package/dist/internal/guest-network.d.ts +5 -2
  165. package/dist/internal/guest-network.d.ts.map +1 -1
  166. package/dist/internal/guest-network.js +7 -2
  167. package/dist/internal/hosted-delivery.d.ts +7 -0
  168. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  169. package/dist/internal/hosted-delivery.js +41 -3
  170. package/dist/internal/hosted-managed-automation.d.ts.map +1 -1
  171. package/dist/internal/hosted-managed-automation.js +27 -2
  172. package/dist/internal/init-project.d.ts.map +1 -1
  173. package/dist/internal/init-project.js +4 -1
  174. package/dist/internal/sdk-runner.d.ts +13 -1
  175. package/dist/internal/sdk-runner.d.ts.map +1 -1
  176. package/dist/internal/sdk-runner.js +21 -1
  177. package/dist/internal/server.d.ts.map +1 -1
  178. package/dist/internal/server.js +11 -3
  179. package/dist/internal/session-engine.d.ts +30 -0
  180. package/dist/internal/session-engine.d.ts.map +1 -1
  181. package/dist/internal/session-engine.js +484 -82
  182. package/dist/internal/store-mirror.d.ts +2 -2
  183. package/dist/internal/store-mirror.js +2 -2
  184. package/dist/internal/tenant-slack/relay.d.ts +2 -0
  185. package/dist/internal/tenant-slack/relay.d.ts.map +1 -1
  186. package/dist/internal/tenant-slack/relay.js +18 -8
  187. package/dist/internal/testing/factory-resource-fixtures.d.ts +48 -0
  188. package/dist/internal/testing/factory-resource-fixtures.d.ts.map +1 -0
  189. package/dist/internal/testing/factory-resource-fixtures.js +41 -0
  190. package/dist/internal/workspace-sync.d.ts +31 -6
  191. package/dist/internal/workspace-sync.d.ts.map +1 -1
  192. package/dist/internal/workspace-sync.js +67 -12
  193. package/dist/internal/workspace.d.ts +9 -0
  194. package/dist/internal/workspace.d.ts.map +1 -1
  195. package/dist/internal/workspace.js +16 -0
  196. package/dist/playground/assets/{index-59KvgREd.js → index-BaQzjaNK.js} +40 -40
  197. package/dist/playground/assets/index-CceqnLTX.css +1 -0
  198. package/dist/playground/index.html +2 -2
  199. package/dist/types.d.ts +60 -11
  200. package/dist/types.d.ts.map +1 -1
  201. package/docs/README.md +6 -1
  202. package/docs/design/filesystem.md +376 -0
  203. package/docs/guides/github.md +5 -0
  204. package/docs/guides/grokbot-agents.md +93 -0
  205. package/docs/reference/channels.md +20 -0
  206. package/docs/reference/cli.md +2 -1
  207. package/docs/reference/extensions.md +2 -0
  208. package/docs/templates/grokbot-agents.md +53 -0
  209. package/package.json +25 -2
  210. package/src/channels/change-monitors/change-monitors-channel.ts +62 -0
  211. package/src/channels/change-monitors/index.ts +8 -0
  212. package/src/channels/change-monitors/types.ts +44 -0
  213. package/src/channels/deployments/deployments-channel.ts +1 -0
  214. package/src/channels/deployments/types.ts +2 -0
  215. package/src/channels/github/defaults.ts +4 -0
  216. package/src/channels/github/progress.ts +76 -10
  217. package/src/channels/issues/index.ts +9 -0
  218. package/src/channels/issues/issues-channel.ts +51 -0
  219. package/src/channels/issues/types.ts +41 -0
  220. package/src/files.ts +9 -2
  221. package/src/filesystem.ts +2074 -0
  222. package/src/index.ts +28 -0
  223. package/src/internal/continuation-channel.ts +29 -12
  224. package/src/internal/continuation-identity.ts +122 -37
  225. package/src/internal/cursor-agent-template.ts +1 -0
  226. package/src/internal/discovery/agent.ts +87 -1
  227. package/src/internal/discovery/info.ts +3 -0
  228. package/src/internal/factory-resource-channel.ts +122 -0
  229. package/src/internal/filesystem/errors.ts +220 -0
  230. package/src/internal/filesystem/format.ts +276 -0
  231. package/src/internal/filesystem/merge.ts +195 -0
  232. package/src/internal/filesystem/scratch.ts +255 -0
  233. package/src/internal/filesystem/sdk-schemas.ts +204 -0
  234. package/src/internal/filesystem/tools.ts +821 -0
  235. package/src/internal/filesystem/walk.ts +369 -0
  236. package/src/internal/framework-file-storage.ts +3 -1
  237. package/src/internal/guest-network.ts +8 -2
  238. package/src/internal/hosted-delivery.ts +71 -2
  239. package/src/internal/hosted-managed-automation.ts +49 -5
  240. package/src/internal/init-project.ts +4 -1
  241. package/src/internal/sdk-runner.ts +23 -0
  242. package/src/internal/server.ts +16 -0
  243. package/src/internal/session-engine.ts +595 -73
  244. package/src/internal/store-mirror.ts +2 -2
  245. package/src/internal/tenant-slack/relay.ts +28 -4
  246. package/src/internal/testing/factory-resource-fixtures.ts +85 -0
  247. package/src/internal/workspace-sync.ts +84 -12
  248. package/src/internal/workspace.ts +20 -0
  249. package/src/types.ts +58 -11
  250. package/templates/grokbot-agents/README.md +26 -0
  251. package/templates/grokbot-agents/agent/agent.ts +6 -0
  252. package/templates/grokbot-agents/agent/extensions/grokbot.ts +5 -0
  253. package/templates/grokbot-agents/agent/instructions.md +7 -0
  254. package/templates/grokbot-agents/evals/evals.config.ts +5 -0
  255. package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
  256. package/templates/grokbot-agents/init.json +18 -0
  257. package/templates/grokbot-agents/package.json +17 -0
  258. package/templates/grokbot-agents/tsconfig.json +12 -0
  259. package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +0 -1
  260. package/dist/docs/assets/index.md.D75GWhPl.js +0 -5
  261. package/dist/docs/assets/index.md.D75GWhPl.lean.js +0 -1
  262. package/dist/playground/assets/index-MbMxXg-y.css +0 -1
  263. /package/dist/docs/assets/{reference_cli.md.CJNc_xYK.lean.js → reference_cli.md.C6Lo8SEd.lean.js} +0 -0
  264. /package/dist/docs/assets/{reference_extensions.md.DykHW3qL.lean.js → reference_extensions.md.CGmMLblt.lean.js} +0 -0
@@ -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
+ }
@@ -9,7 +9,9 @@
9
9
  * - the **framework-level** store (`AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID`)
10
10
  * holds engine-owned session state — session records, event transcripts,
11
11
  * hosted-execution run logs, the continuation index, and the SDK
12
- * conversation mirror — and is never exposed to authors or models.
12
+ * conversation mirror — and is never mounted into author code or models.
13
+ * The authenticated dashboard may read diagnostic families, but never the
14
+ * continuation index.
13
15
  *
14
16
  * This module puts framework session state on the framework store. The
15
17
  * {@link storageKeys} scheme was designed for exactly this: keys are a
@@ -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 does
18
- * not treat them as restore-window mystery 503s.
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 {
@@ -356,6 +358,13 @@ export function registerHostedDeliveryRoute(input: {
356
358
  flushStartLog?(startLogId: string): Promise<void>;
357
359
  /** Snapshot of the process log ring at persist time. */
358
360
  snapshotLogs?(): LogRingEntry[];
361
+ /**
362
+ * Drop the cached SDK agent handle before the HTTP reply. Hosted v2
363
+ * hibernates this process as soon as the reply lands; leaving the handle
364
+ * in memory freezes a dead Connect/HTTP2 session that the next wake
365
+ * then reuses.
366
+ */
367
+ evictSdkSession?(sessionId: string): void;
359
368
  /**
360
369
  * Wait until guest egress works before authored admission/execution.
361
370
  * Defaults to {@link waitForGuestNetworkReady}. Tests inject a stub.
@@ -461,8 +470,18 @@ export function registerHostedDeliveryRoute(input: {
461
470
  if (unconsumed !== undefined) {
462
471
  return finish(unconsumed);
463
472
  }
464
- const delivery = parseHostedAdmission(await readJsonObject(request));
473
+ const rawAdmission = await readJsonObject(request);
474
+ const delivery = parseHostedAdmission(rawAdmission);
465
475
  if (delivery === undefined) {
476
+ const rawKeys =
477
+ typeof rawAdmission === "object" &&
478
+ rawAdmission !== null &&
479
+ !Array.isArray(rawAdmission)
480
+ ? Object.keys(rawAdmission as Record<string, unknown>)
481
+ : [];
482
+ console.info(
483
+ `[agent-sdk] hosted admission rejected: envelope failed to parse keys=${JSON.stringify(rawKeys)} hasManagedAutomation=${String(rawKeys.includes("managedAutomation"))}`
484
+ );
466
485
  return finish(
467
486
  errorResponse(
468
487
  400,
@@ -471,6 +490,9 @@ export function registerHostedDeliveryRoute(input: {
471
490
  )
472
491
  );
473
492
  }
493
+ console.info(
494
+ `[agent-sdk] hosted admission received deliveryId=${delivery.deliveryId} channelId=${delivery.channelId} channelKind=${delivery.channelKind} eventType=${delivery.eventType} sessionMode=${delivery.sessionMode} managedAutomation=${delivery.managedAutomation === undefined ? "<absent>" : `automationUuid=${delivery.managedAutomation.automationUuid} mcpServerNames=${String(delivery.managedAutomation.mcpServerNames.length)} slackChannels=${delivery.managedAutomation.slackChannels === undefined ? "<absent>" : JSON.stringify(delivery.managedAutomation.slackChannels)}`}`
495
+ );
474
496
  recordHostedManagedAutomation(delivery);
475
497
  const channel = input.channels.find(
476
498
  (candidate) => candidate.id === delivery.channelId
@@ -622,6 +644,7 @@ export function registerHostedDeliveryRoute(input: {
622
644
  sessionId: "",
623
645
  turnStatus: HOSTED_EXECUTION_DIAG_RUNNING_STATUS,
624
646
  });
647
+ let sessionId = "";
625
648
  try {
626
649
  const args = input.buildHandlerArgs(
627
650
  admitted.send.channelId,
@@ -633,10 +656,19 @@ export function registerHostedDeliveryRoute(input: {
633
656
  key: admitted.send.mailboxContinuationKey,
634
657
  },
635
658
  });
659
+ sessionId = session.id;
636
660
  const outcome = await session.waitForCompletion();
637
661
  // Failed turns drain too: their record/continuation updates must
638
662
  // survive the park just like a finished turn's.
639
663
  await input.drainSessionDurability?.(session.id);
664
+ // Evict before the reply: TAS parks and hibernates this process
665
+ // as soon as the POST returns. A leftover SDK handle freezes a
666
+ // dead Connect/HTTP2 session that the next wake then reuses.
667
+ evictHostedSdkSessions(
668
+ input.evictSdkSession,
669
+ session.id,
670
+ execution.sessionPublicId
671
+ );
640
672
  await writeDiag({
641
673
  sessionId: session.id,
642
674
  turnStatus: outcome.status,
@@ -647,11 +679,14 @@ export function registerHostedDeliveryRoute(input: {
647
679
  ),
648
680
  });
649
681
  if (outcome.status !== "finished") {
682
+ // waitForCompletion resolves (does not throw) on a quiet
683
+ // Connect abort. Map that to guest_connect_stalled so TAS
684
+ // retries the same pod. Real runner failures stay turn_failed.
650
685
  return {
651
686
  status: 503,
652
687
  body: {
653
688
  ok: false,
654
- error: "turn_failed",
689
+ error: hostedErrorForUnfinishedTurn(outcome.errorMessage),
655
690
  turnStatus: outcome.status,
656
691
  message: outcome.errorMessage,
657
692
  },
@@ -666,6 +701,11 @@ export function registerHostedDeliveryRoute(input: {
666
701
  },
667
702
  };
668
703
  } catch (error) {
704
+ evictHostedSdkSessions(
705
+ input.evictSdkSession,
706
+ sessionId,
707
+ execution.sessionPublicId
708
+ );
669
709
  await writeDiag({
670
710
  sessionId: "",
671
711
  turnStatus: "error",
@@ -682,6 +722,35 @@ export function registerHostedDeliveryRoute(input: {
682
722
  });
683
723
  }
684
724
 
725
+ function evictHostedSdkSessions(
726
+ evict: ((sessionId: string) => void) | undefined,
727
+ ...ids: Array<string | undefined>
728
+ ): void {
729
+ if (evict === undefined) {
730
+ return;
731
+ }
732
+ const seen = new Set<string>();
733
+ for (const id of ids) {
734
+ if (id === undefined || id === "" || seen.has(id)) {
735
+ continue;
736
+ }
737
+ seen.add(id);
738
+ evict(id);
739
+ }
740
+ }
741
+
742
+ function hostedErrorForUnfinishedTurn(
743
+ errorMessage: string | undefined
744
+ ): typeof GUEST_CONNECT_STALLED | "turn_failed" {
745
+ if (
746
+ errorMessage !== undefined &&
747
+ isQuietGuestConnectStallMessage(errorMessage)
748
+ ) {
749
+ return GUEST_CONNECT_STALLED;
750
+ }
751
+ return "turn_failed";
752
+ }
753
+
685
754
  async function afterHostedWork<T>(
686
755
  flushOtel: (() => Promise<void>) | undefined,
687
756
  work: () => Promise<T>
@@ -17,10 +17,36 @@
17
17
 
18
18
  import type { HostedAdmissionManagedAutomation } from "./hosted-delivery-protocol.js";
19
19
 
20
- const managedAutomationByDelivery = new Map<
21
- string,
22
- { managedAutomation: HostedAdmissionManagedAutomation; expiresAt: number }
23
- >();
20
+ type ManagedAutomationEntry = {
21
+ managedAutomation: HostedAdmissionManagedAutomation;
22
+ expiresAt: number;
23
+ };
24
+
25
+ /**
26
+ * Process-wide slot for the table. Hosted engines run the compiled CLI
27
+ * (`dist/`), which records at admission, while first-party factory agents
28
+ * relative-import this file from `src/` and read at finalize — two module
29
+ * instances in one process. A module-local Map would be recorded in one
30
+ * copy and read from the other (always undefined, so a fork never sees its
31
+ * Slack channels or MCP servers); the shared symbol keeps one table.
32
+ */
33
+ const MANAGED_AUTOMATION_TABLE_KEY = Symbol.for(
34
+ "cursor.agentkit.hostedManagedAutomationByDelivery"
35
+ );
36
+
37
+ function managedAutomationTable(): Map<string, ManagedAutomationEntry> {
38
+ const slots = globalThis as unknown as Record<
39
+ symbol,
40
+ Map<string, ManagedAutomationEntry> | undefined
41
+ >;
42
+ let table = slots[MANAGED_AUTOMATION_TABLE_KEY];
43
+ if (table === undefined) {
44
+ table = new Map();
45
+ slots[MANAGED_AUTOMATION_TABLE_KEY] = table;
46
+ }
47
+ return table;
48
+ }
49
+
24
50
  /** Long enough to outlive a slow review run kicked off by the delivery. */
25
51
  const MANAGED_AUTOMATION_TTL_MS = 60 * 60_000;
26
52
  const MANAGED_AUTOMATION_MAX_ENTRIES = 10_000;
@@ -30,8 +56,12 @@ export function recordHostedManagedAutomation(delivery: {
30
56
  managedAutomation?: HostedAdmissionManagedAutomation;
31
57
  }): void {
32
58
  if (delivery.managedAutomation === undefined) {
59
+ console.info(
60
+ `[agent-sdk] hosted managedAutomation not recorded deliveryId=${delivery.deliveryId}: admission envelope carried no managedAutomation slice (non-managed delivery, or the control plane resolved no MCP servers and no Slack channels)`
61
+ );
33
62
  return;
34
63
  }
64
+ const managedAutomationByDelivery = managedAutomationTable();
35
65
  const now = Date.now();
36
66
  for (const [key, entry] of managedAutomationByDelivery) {
37
67
  if (entry.expiresAt <= now) {
@@ -39,12 +69,19 @@ export function recordHostedManagedAutomation(delivery: {
39
69
  }
40
70
  }
41
71
  if (managedAutomationByDelivery.size >= MANAGED_AUTOMATION_MAX_ENTRIES) {
72
+ console.info(
73
+ `[agent-sdk] hosted managedAutomation table full (${String(managedAutomationByDelivery.size)} entries); clearing before recording deliveryId=${delivery.deliveryId}`
74
+ );
42
75
  managedAutomationByDelivery.clear();
43
76
  }
44
77
  managedAutomationByDelivery.set(delivery.deliveryId, {
45
78
  managedAutomation: delivery.managedAutomation,
46
79
  expiresAt: now + MANAGED_AUTOMATION_TTL_MS,
47
80
  });
81
+ const slackChannels = delivery.managedAutomation.slackChannels;
82
+ console.info(
83
+ `[agent-sdk] hosted managedAutomation recorded deliveryId=${delivery.deliveryId} automationUuid=${delivery.managedAutomation.automationUuid} mcpServerNames=${String(delivery.managedAutomation.mcpServerNames.length)} slackChannels=${slackChannels === undefined ? "<absent>" : JSON.stringify(slackChannels)} ttlMs=${String(MANAGED_AUTOMATION_TTL_MS)} tableSize=${String(managedAutomationByDelivery.size)}`
84
+ );
48
85
  }
49
86
 
50
87
  /**
@@ -55,11 +92,18 @@ export function recordHostedManagedAutomation(delivery: {
55
92
  export function getHostedManagedAutomation(
56
93
  deliveryId: string
57
94
  ): HostedAdmissionManagedAutomation | undefined {
95
+ const managedAutomationByDelivery = managedAutomationTable();
58
96
  const entry = managedAutomationByDelivery.get(deliveryId);
59
97
  if (entry === undefined) {
98
+ console.info(
99
+ `[agent-sdk] hosted managedAutomation lookup miss deliveryId=${deliveryId}: no entry (never recorded in this process, or evicted) tableSize=${String(managedAutomationByDelivery.size)} knownDeliveryIds=${JSON.stringify([...managedAutomationByDelivery.keys()].slice(0, 10))}`
100
+ );
60
101
  return undefined;
61
102
  }
62
103
  if (entry.expiresAt <= Date.now()) {
104
+ console.info(
105
+ `[agent-sdk] hosted managedAutomation lookup expired deliveryId=${deliveryId} automationUuid=${entry.managedAutomation.automationUuid} expiredMsAgo=${String(Date.now() - entry.expiresAt)}`
106
+ );
63
107
  managedAutomationByDelivery.delete(deliveryId);
64
108
  return undefined;
65
109
  }
@@ -68,5 +112,5 @@ export function getHostedManagedAutomation(
68
112
 
69
113
  /** Drop recorded managed-automation slices (tests only). */
70
114
  export function resetHostedManagedAutomationForTest(): void {
71
- managedAutomationByDelivery.clear();
115
+ managedAutomationTable().clear();
72
116
  }
@@ -77,6 +77,7 @@ const TEMPLATES: Readonly<Record<TemplateName, TemplateEntry>> = {
77
77
  "agents-md": { dir: "agents-md" },
78
78
  "code-wiki": { dir: "code-wiki" },
79
79
  demo: { dir: "demo" },
80
+ "grokbot-agents": { dir: "grokbot-agents" },
80
81
  "pr-autofixer": { dir: "pr-autofixer" },
81
82
  "security-help": { dir: "security-help" },
82
83
  "security-reviewer": { dir: "security-reviewer" },
@@ -711,6 +712,7 @@ export function printInitGuide(): void {
711
712
  Scaffold a project, then open the playground:
712
713
  npx ${PACKAGE_NAME} init ./my-agent # installs deps + links agent-sdk onto PATH
713
714
  npx ${PACKAGE_NAME} init ./my-demo --template demo # record a PR walkthrough
715
+ npx ${PACKAGE_NAME} init ./grokbot --template grokbot-agents # talk to Grok Bot agents
714
716
  npx ${PACKAGE_NAME} init ./code-wiki --template code-wiki # keep wiki pages current after merge
715
717
  npx ${PACKAGE_NAME} init ./agents-md --template agents-md # keep AGENTS.md current from last week's PRs and Slack
716
718
  npx ${PACKAGE_NAME} init ./my-reviewer --template security-reviewer # review PRs for security bugs
@@ -719,8 +721,9 @@ Scaffold a project, then open the playground:
719
721
  npx ${PACKAGE_NAME} init ./my-triage --template triage-jira # comment on Jira issues
720
722
  npx ${PACKAGE_NAME} init ./my-owners --template agentic-owners # review PRs via owners policies
721
723
  npx ${PACKAGE_NAME} init ./pr-autofixer --template pr-autofixer # fix PRs on a cloud VM
722
- # TTY asks for GitHub repos. Scripts pass --var:
724
+ # TTY asks the template's questions. Scripts pass --var:
723
725
  # --var repos=acme/widgets,acme/api
726
+ # --var agents="Plan Keeper, Chief of Staff"
724
727
  cd ./my-agent && agent-sdk dev # or: npx ${PACKAGE_NAME} dev
725
728
 
726
729
  Recommended loop once the project exists (from the project directory):