@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.
- 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/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +1 -0
- package/dist/channels/deployments/types.d.ts +2 -0
- package/dist/channels/deployments/types.d.ts.map +1 -1
- 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.BVwaffdK.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CdEIUwDg.js → VPLocalSearchBox.WuWTZ1x8.js} +1 -1
- package/dist/docs/assets/chunks/{theme.YZMHgPgg.js → theme.BYdI_6sF.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/guides_grokbot-agents.md.DVqdhBKb.js +9 -0
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.lean.js +1 -0
- package/dist/docs/assets/index.md.BW_6tOgR.js +5 -0
- package/dist/docs/assets/index.md.BW_6tOgR.lean.js +1 -0
- 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/assets/{reference_cli.md.CJNc_xYK.js → reference_cli.md.C6Lo8SEd.js} +2 -1
- package/dist/docs/assets/{reference_extensions.md.DykHW3qL.js → reference_extensions.md.CGmMLblt.js} +1 -1
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.js +3 -0
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.lean.js +1 -0
- 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 +5 -5
- 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/grokbot-agents.html +35 -0
- package/dist/docs/guides/grokbot-agents.md +88 -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 +5 -5
- 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 +6 -6
- package/dist/docs/index.md +6 -1
- package/dist/docs/llms-full.txt +562 -2
- package/dist/docs/llms.txt +3 -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 +7 -6
- package/dist/docs/reference/cli.md +2 -1
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/extensions.html +6 -6
- package/dist/docs/reference/extensions.md +2 -0
- 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 +5 -5
- package/dist/docs/templates/demo.html +5 -5
- package/dist/docs/templates/grokbot-agents.html +29 -0
- package/dist/docs/templates/grokbot-agents.md +48 -0
- 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/files.d.ts +7 -2
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +7 -2
- 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/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -0
- 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/framework-file-storage.d.ts +3 -1
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +3 -1
- 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 +7 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +41 -3
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -1
- package/dist/internal/hosted-managed-automation.js +27 -2
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +4 -1
- package/dist/internal/sdk-runner.d.ts +13 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +21 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +11 -3
- package/dist/internal/session-engine.d.ts +30 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +484 -82
- package/dist/internal/store-mirror.d.ts +2 -2
- package/dist/internal/store-mirror.js +2 -2
- package/dist/internal/tenant-slack/relay.d.ts +2 -0
- package/dist/internal/tenant-slack/relay.d.ts.map +1 -1
- package/dist/internal/tenant-slack/relay.js +18 -8
- 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/internal/workspace-sync.d.ts +31 -6
- package/dist/internal/workspace-sync.d.ts.map +1 -1
- package/dist/internal/workspace-sync.js +67 -12
- package/dist/internal/workspace.d.ts +9 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +16 -0
- package/dist/playground/assets/{index-59KvgREd.js → index-BaQzjaNK.js} +40 -40
- package/dist/playground/assets/index-CceqnLTX.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +60 -11
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +6 -1
- package/docs/design/filesystem.md +376 -0
- package/docs/guides/github.md +5 -0
- package/docs/guides/grokbot-agents.md +93 -0
- package/docs/reference/channels.md +20 -0
- package/docs/reference/cli.md +2 -1
- package/docs/reference/extensions.md +2 -0
- package/docs/templates/grokbot-agents.md +53 -0
- package/package.json +25 -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/deployments/deployments-channel.ts +1 -0
- package/src/channels/deployments/types.ts +2 -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/files.ts +9 -2
- 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/cursor-agent-template.ts +1 -0
- 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/framework-file-storage.ts +3 -1
- package/src/internal/guest-network.ts +8 -2
- package/src/internal/hosted-delivery.ts +71 -2
- package/src/internal/hosted-managed-automation.ts +49 -5
- package/src/internal/init-project.ts +4 -1
- package/src/internal/sdk-runner.ts +23 -0
- package/src/internal/server.ts +16 -0
- package/src/internal/session-engine.ts +595 -73
- package/src/internal/store-mirror.ts +2 -2
- package/src/internal/tenant-slack/relay.ts +28 -4
- package/src/internal/testing/factory-resource-fixtures.ts +85 -0
- package/src/internal/workspace-sync.ts +84 -12
- package/src/internal/workspace.ts +20 -0
- package/src/types.ts +58 -11
- package/templates/grokbot-agents/README.md +26 -0
- package/templates/grokbot-agents/agent/agent.ts +6 -0
- package/templates/grokbot-agents/agent/extensions/grokbot.ts +5 -0
- package/templates/grokbot-agents/agent/instructions.md +7 -0
- package/templates/grokbot-agents/evals/evals.config.ts +5 -0
- package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
- package/templates/grokbot-agents/init.json +18 -0
- package/templates/grokbot-agents/package.json +17 -0
- package/templates/grokbot-agents/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +0 -1
- package/dist/docs/assets/index.md.D75GWhPl.js +0 -5
- package/dist/docs/assets/index.md.D75GWhPl.lean.js +0 -1
- package/dist/playground/assets/index-MbMxXg-y.css +0 -1
- /package/dist/docs/assets/{reference_cli.md.CJNc_xYK.lean.js → reference_cli.md.C6Lo8SEd.lean.js} +0 -0
- /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
|
|
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
|
|
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 {
|
|
@@ -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
|
|
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:
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
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):
|