@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.
Files changed (186) 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/github/defaults.d.ts.map +1 -1
  11. package/dist/channels/github/defaults.js +4 -0
  12. package/dist/channels/github/progress.d.ts +22 -0
  13. package/dist/channels/github/progress.d.ts.map +1 -1
  14. package/dist/channels/github/progress.js +32 -5
  15. package/dist/channels/issues/index.d.ts +4 -0
  16. package/dist/channels/issues/index.d.ts.map +1 -0
  17. package/dist/channels/issues/index.js +2 -0
  18. package/dist/channels/issues/issues-channel.d.ts +5 -0
  19. package/dist/channels/issues/issues-channel.d.ts.map +1 -0
  20. package/dist/channels/issues/issues-channel.js +37 -0
  21. package/dist/channels/issues/types.d.ts +33 -0
  22. package/dist/channels/issues/types.d.ts.map +1 -0
  23. package/dist/channels/issues/types.js +1 -0
  24. package/dist/docs/404.html +2 -2
  25. package/dist/docs/ab.html +4 -4
  26. package/dist/docs/assets/{app.DW9pXkpL.js → app.DbMWUyGb.js} +1 -1
  27. package/dist/docs/assets/chunks/@localSearchIndexroot.Bqsulb9K.js +1 -0
  28. package/dist/docs/assets/chunks/{VPLocalSearchBox.CdEIUwDg.js → VPLocalSearchBox.DcNeTh9o.js} +1 -1
  29. package/dist/docs/assets/chunks/{theme.YZMHgPgg.js → theme.DMjhY3gl.js} +2 -2
  30. package/dist/docs/assets/{guides_github.md.DKYiT9do.js → guides_github.md.c0gdGJ-D.js} +1 -1
  31. package/dist/docs/assets/{guides_github.md.DKYiT9do.lean.js → guides_github.md.c0gdGJ-D.lean.js} +1 -1
  32. package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.js → reference_channels.md.DBGcaB7m.js} +2 -2
  33. package/dist/docs/assets/{reference_channels.md.Dz8Cnudp.lean.js → reference_channels.md.DBGcaB7m.lean.js} +1 -1
  34. package/dist/docs/building-with-agents.html +4 -4
  35. package/dist/docs/concepts.html +4 -4
  36. package/dist/docs/deployment.html +4 -4
  37. package/dist/docs/design/filesystem.md +376 -0
  38. package/dist/docs/evals.html +4 -4
  39. package/dist/docs/guides/agent-to-agent.html +4 -4
  40. package/dist/docs/guides/cloud-agents.html +4 -4
  41. package/dist/docs/guides/convert-automation.html +4 -4
  42. package/dist/docs/guides/github.html +6 -6
  43. package/dist/docs/guides/github.md +5 -0
  44. package/dist/docs/guides/human-in-the-loop.html +4 -4
  45. package/dist/docs/guides/mcp-oauth.html +4 -4
  46. package/dist/docs/guides/opentelemetry.html +4 -4
  47. package/dist/docs/guides/slack.html +4 -4
  48. package/dist/docs/guides/webhooks.html +4 -4
  49. package/dist/docs/hashmap.json +1 -1
  50. package/dist/docs/hillclimbing.html +4 -4
  51. package/dist/docs/index.html +4 -4
  52. package/dist/docs/llms-full.txt +406 -0
  53. package/dist/docs/llms.txt +1 -0
  54. package/dist/docs/quickstart.html +4 -4
  55. package/dist/docs/reference/agent-config.html +4 -4
  56. package/dist/docs/reference/artifacts.html +4 -4
  57. package/dist/docs/reference/channels.html +6 -6
  58. package/dist/docs/reference/channels.md +20 -0
  59. package/dist/docs/reference/cli.html +4 -4
  60. package/dist/docs/reference/connections.html +4 -4
  61. package/dist/docs/reference/extensions.html +4 -4
  62. package/dist/docs/reference/hooks.html +4 -4
  63. package/dist/docs/reference/http-api.html +4 -4
  64. package/dist/docs/reference/instructions.html +4 -4
  65. package/dist/docs/reference/playground.html +4 -4
  66. package/dist/docs/reference/project-layout.html +4 -4
  67. package/dist/docs/reference/prompt.html +4 -4
  68. package/dist/docs/reference/result.html +4 -4
  69. package/dist/docs/reference/schedules.html +4 -4
  70. package/dist/docs/reference/sessions.html +4 -4
  71. package/dist/docs/reference/skills.html +4 -4
  72. package/dist/docs/reference/subagents.html +4 -4
  73. package/dist/docs/reference/tools.html +4 -4
  74. package/dist/docs/scaffolding-agents.html +4 -4
  75. package/dist/docs/skills/ab.html +4 -4
  76. package/dist/docs/skills/create-agent.html +4 -4
  77. package/dist/docs/skills/debug.html +4 -4
  78. package/dist/docs/skills/evals.html +4 -4
  79. package/dist/docs/skills/framework-map.html +4 -4
  80. package/dist/docs/skills/github.html +4 -4
  81. package/dist/docs/skills/hillclimb.html +4 -4
  82. package/dist/docs/skills/index.html +4 -4
  83. package/dist/docs/skills/mcp-auth.html +4 -4
  84. package/dist/docs/skills/otel.html +4 -4
  85. package/dist/docs/skills/setup-slack.html +4 -4
  86. package/dist/docs/storage.html +4 -4
  87. package/dist/docs/templates/agentic-owners.html +4 -4
  88. package/dist/docs/templates/agents-md.html +4 -4
  89. package/dist/docs/templates/code-wiki.html +4 -4
  90. package/dist/docs/templates/demo.html +4 -4
  91. package/dist/docs/templates/pr-autofixer.html +4 -4
  92. package/dist/docs/templates/security-help.html +4 -4
  93. package/dist/docs/templates/security-reviewer.html +4 -4
  94. package/dist/docs/templates/triage.html +4 -4
  95. package/dist/docs/troubleshooting.html +4 -4
  96. package/dist/filesystem.d.ts +440 -0
  97. package/dist/filesystem.d.ts.map +1 -0
  98. package/dist/filesystem.js +1694 -0
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +1 -0
  102. package/dist/internal/continuation-channel.d.ts +4 -2
  103. package/dist/internal/continuation-channel.d.ts.map +1 -1
  104. package/dist/internal/continuation-channel.js +21 -12
  105. package/dist/internal/continuation-identity.d.ts +13 -5
  106. package/dist/internal/continuation-identity.d.ts.map +1 -1
  107. package/dist/internal/continuation-identity.js +74 -35
  108. package/dist/internal/discovery/agent.d.ts.map +1 -1
  109. package/dist/internal/discovery/agent.js +80 -8
  110. package/dist/internal/discovery/info.d.ts.map +1 -1
  111. package/dist/internal/discovery/info.js +3 -0
  112. package/dist/internal/factory-resource-channel.d.ts +33 -0
  113. package/dist/internal/factory-resource-channel.d.ts.map +1 -0
  114. package/dist/internal/factory-resource-channel.js +77 -0
  115. package/dist/internal/filesystem/errors.d.ts +64 -0
  116. package/dist/internal/filesystem/errors.d.ts.map +1 -0
  117. package/dist/internal/filesystem/errors.js +175 -0
  118. package/dist/internal/filesystem/format.d.ts +28 -0
  119. package/dist/internal/filesystem/format.d.ts.map +1 -0
  120. package/dist/internal/filesystem/format.js +213 -0
  121. package/dist/internal/filesystem/merge.d.ts +34 -0
  122. package/dist/internal/filesystem/merge.d.ts.map +1 -0
  123. package/dist/internal/filesystem/merge.js +196 -0
  124. package/dist/internal/filesystem/scratch.d.ts +59 -0
  125. package/dist/internal/filesystem/scratch.d.ts.map +1 -0
  126. package/dist/internal/filesystem/scratch.js +212 -0
  127. package/dist/internal/filesystem/sdk-schemas.d.ts +29 -0
  128. package/dist/internal/filesystem/sdk-schemas.d.ts.map +1 -0
  129. package/dist/internal/filesystem/sdk-schemas.js +153 -0
  130. package/dist/internal/filesystem/tools.d.ts +42 -0
  131. package/dist/internal/filesystem/tools.d.ts.map +1 -0
  132. package/dist/internal/filesystem/tools.js +618 -0
  133. package/dist/internal/filesystem/walk.d.ts +74 -0
  134. package/dist/internal/filesystem/walk.d.ts.map +1 -0
  135. package/dist/internal/filesystem/walk.js +339 -0
  136. package/dist/internal/guest-network.d.ts +5 -2
  137. package/dist/internal/guest-network.d.ts.map +1 -1
  138. package/dist/internal/guest-network.js +7 -2
  139. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  140. package/dist/internal/hosted-delivery.js +12 -2
  141. package/dist/internal/sdk-runner.d.ts +1 -1
  142. package/dist/internal/sdk-runner.d.ts.map +1 -1
  143. package/dist/internal/sdk-runner.js +3 -1
  144. package/dist/internal/session-engine.d.ts +22 -0
  145. package/dist/internal/session-engine.d.ts.map +1 -1
  146. package/dist/internal/session-engine.js +362 -40
  147. package/dist/internal/testing/factory-resource-fixtures.d.ts +48 -0
  148. package/dist/internal/testing/factory-resource-fixtures.d.ts.map +1 -0
  149. package/dist/internal/testing/factory-resource-fixtures.js +41 -0
  150. package/dist/playground/assets/{index-59KvgREd.js → index-8YiozU89.js} +38 -38
  151. package/dist/playground/index.html +1 -1
  152. package/dist/types.d.ts +51 -3
  153. package/dist/types.d.ts.map +1 -1
  154. package/docs/design/filesystem.md +376 -0
  155. package/docs/guides/github.md +5 -0
  156. package/docs/reference/channels.md +20 -0
  157. package/package.json +16 -2
  158. package/src/channels/change-monitors/change-monitors-channel.ts +62 -0
  159. package/src/channels/change-monitors/index.ts +8 -0
  160. package/src/channels/change-monitors/types.ts +44 -0
  161. package/src/channels/github/defaults.ts +4 -0
  162. package/src/channels/github/progress.ts +76 -10
  163. package/src/channels/issues/index.ts +9 -0
  164. package/src/channels/issues/issues-channel.ts +51 -0
  165. package/src/channels/issues/types.ts +41 -0
  166. package/src/filesystem.ts +2074 -0
  167. package/src/index.ts +28 -0
  168. package/src/internal/continuation-channel.ts +29 -12
  169. package/src/internal/continuation-identity.ts +122 -37
  170. package/src/internal/discovery/agent.ts +87 -1
  171. package/src/internal/discovery/info.ts +3 -0
  172. package/src/internal/factory-resource-channel.ts +122 -0
  173. package/src/internal/filesystem/errors.ts +220 -0
  174. package/src/internal/filesystem/format.ts +276 -0
  175. package/src/internal/filesystem/merge.ts +195 -0
  176. package/src/internal/filesystem/scratch.ts +255 -0
  177. package/src/internal/filesystem/sdk-schemas.ts +204 -0
  178. package/src/internal/filesystem/tools.ts +821 -0
  179. package/src/internal/filesystem/walk.ts +369 -0
  180. package/src/internal/guest-network.ts +8 -2
  181. package/src/internal/hosted-delivery.ts +18 -1
  182. package/src/internal/sdk-runner.ts +4 -0
  183. package/src/internal/session-engine.ts +439 -29
  184. package/src/internal/testing/factory-resource-fixtures.ts +85 -0
  185. package/src/types.ts +49 -3
  186. package/dist/docs/assets/chunks/@localSearchIndexroot.nrl1ifBq.js +0 -1
package/src/index.ts CHANGED
@@ -66,6 +66,34 @@ export {
66
66
  disableSubagent,
67
67
  disableTool,
68
68
  } from "./extensions.js";
69
+ export type {
70
+ DirEntry,
71
+ EditQuery,
72
+ EditResult,
73
+ FileInfo,
74
+ FileSystem,
75
+ GrepQuery,
76
+ GrepResult,
77
+ SearchQuery,
78
+ SearchResult,
79
+ } from "./filesystem.js";
80
+ export {
81
+ DeleteNotSupportedError,
82
+ EditError,
83
+ emptyFs,
84
+ FileNotFoundError,
85
+ hostFs,
86
+ InvalidGrepPatternError,
87
+ isFileNotFoundError,
88
+ NoMountError,
89
+ overlayFs,
90
+ PathError,
91
+ ReadOnlyMountError,
92
+ strReplaceOnFileSystem,
93
+ tempFs,
94
+ unionFs,
95
+ WriteFailedError,
96
+ } from "./filesystem.js";
69
97
  export { defineHook } from "./hooks.js";
70
98
  export {
71
99
  InvalidAsOfError,
@@ -1,7 +1,9 @@
1
1
  import { getBitbucketChannelOptions } from "../channels/bitbucket/bitbucket-channel.js";
2
+ import { getChangeMonitorsChannelOptions } from "../channels/change-monitors/change-monitors-channel.js";
2
3
  import { getDeploymentsChannelOptions } from "../channels/deployments/deployments-channel.js";
3
4
  import { getGitHubChannelOptions } from "../channels/github/github-channel.js";
4
5
  import { getGitLabChannelOptions } from "../channels/gitlab/gitlab-channel.js";
6
+ import { getIssuesChannelOptions } from "../channels/issues/issues-channel.js";
5
7
  import { getOriginChannelOptions } from "../channels/origin/origin-channel.js";
6
8
  import { getSlackChannelOptions } from "../channels/slack/slack-channel.js";
7
9
  import type {
@@ -17,6 +19,7 @@ import {
17
19
  handshakeContinuation,
18
20
  normalizeIdentity,
19
21
  omittedV2MembershipDowngrade,
22
+ omittedV2TypedType,
20
23
  } from "./continuation-identity.js";
21
24
 
22
25
  type ContinuationChannelOptions = {
@@ -24,7 +27,15 @@ type ContinuationChannelOptions = {
24
27
  };
25
28
 
26
29
  const CONTINUATION_CHANNEL_PACKS: ReadonlyArray<{
27
- kind: "github" | "slack" | "origin" | "gitlab" | "bitbucket" | "deployments";
30
+ kind:
31
+ | "github"
32
+ | "slack"
33
+ | "origin"
34
+ | "gitlab"
35
+ | "bitbucket"
36
+ | "deployments"
37
+ | "change_monitors"
38
+ | "issues";
28
39
  getOptions: (definition: object) => ContinuationChannelOptions | undefined;
29
40
  usesDeclaredEvents: boolean;
30
41
  }> = [
@@ -58,6 +69,16 @@ const CONTINUATION_CHANNEL_PACKS: ReadonlyArray<{
58
69
  getOptions: getDeploymentsChannelOptions,
59
70
  usesDeclaredEvents: true,
60
71
  },
72
+ {
73
+ kind: "change_monitors",
74
+ getOptions: getChangeMonitorsChannelOptions,
75
+ usesDeclaredEvents: true,
76
+ },
77
+ {
78
+ kind: "issues",
79
+ getOptions: getIssuesChannelOptions,
80
+ usesDeclaredEvents: true,
81
+ },
61
82
  ];
62
83
 
63
84
  export function channelContinuationIdentity(
@@ -85,8 +106,10 @@ export function httpContinuationIdentity(
85
106
  * Stamp `/v2` continuation defaults onto channel constructors that omitted
86
107
  * `continuation` when the agent deploys onto v2 architecture. GitHub/Origin
87
108
  * type (`pr` / `issue` / `none`) follows declared webhook events.
88
- * Deployments default to `deployments.deployment/v2`. Explicit per-channel
89
- * selectors are left alone. Idempotent.
109
+ * Deployments default to `deployments.deployment/v2`. Change Monitors
110
+ * and issues default to `change_monitors.checkpoint/v2` and
111
+ * `issues.issue/v2`.
112
+ * Explicit per-channel selectors are left alone. Idempotent.
90
113
  */
91
114
  export function applyAgentContinuationDefaults(project: {
92
115
  agent: { architecture?: AgentArchitecture };
@@ -155,14 +178,7 @@ function omittedContinuationDowngradeMessage(
155
178
  unexpected: readonly string[];
156
179
  }
157
180
  ): string {
158
- const membership =
159
- kind === "github"
160
- ? "pr"
161
- : kind === "origin"
162
- ? "origin-pr"
163
- : kind === "deployments"
164
- ? "deployment"
165
- : kind;
181
+ const membership = omittedV2TypedType(kind) ?? kind;
166
182
  return (
167
183
  `omitted continuation stamped ${downgrade.selector} because ` +
168
184
  `${downgrade.unexpected.join(", ")} ${
@@ -205,7 +221,8 @@ export function channelDeclaredEventTypes(
205
221
  meta?.originWebhookEvents ??
206
222
  meta?.gitlabWebhookEvents ??
207
223
  meta?.bitbucketWebhookEvents ??
208
- meta?.deploymentEventTypes
224
+ meta?.deploymentEventTypes ??
225
+ meta?.factoryEventTypes
209
226
  );
210
227
  }
211
228
 
@@ -3,14 +3,16 @@
3
3
  * on a channel constructor stays `/v1` (engine pack minting) so existing
4
4
  * agents do not change: `githubChannel()` → `github.pr/v1`, `slackChannel()`
5
5
  * → `slack.thread/v1`, `originChannel()` → `origin.pr/v1`,
6
- * `deploymentsChannel()` → `deployments.none/v1`, `httpChannel()`
6
+ * `deploymentsChannel()` → `deployments.none/v1`, `changeMonitorsChannel()`
7
+ * → `none/v1`, `issuesChannel()` → `none/v1`, `httpChannel()` →
7
8
  * `none/v1`.
8
9
  *
9
10
  * Opt the whole agent into the v2 control plane with
10
11
  * `defineAgent({ architecture: "v2" })`. That stamps `/v2` defaults from
11
12
  * the channel kind and declared events (`github.pr/v2`, `github.issue/v2`,
12
13
  * `github.none/v2`, `slack.thread/v2`, `origin.pr/v2`,
13
- * `deployments.deployment/v2`, …) onto every channel that omitted a
14
+ * `deployments.deployment/v2`, `change_monitors.checkpoint/v2`,
15
+ * `issues.issue/v2`, …) onto every channel that omitted a
14
16
  * selector. An explicit per-channel selector still overrides. JSONPath
15
17
  * and per-agent format strings are rejected.
16
18
  */
@@ -24,7 +26,9 @@ export type ConversationType =
24
26
  | "origin-pr"
25
27
  | "gitlab-mr"
26
28
  | "bitbucket-pr"
27
- | "deployment";
29
+ | "deployment"
30
+ | "change-monitor-checkpoint"
31
+ | "factory-issue";
28
32
 
29
33
  export type ContinuationKind =
30
34
  | "github"
@@ -33,6 +37,8 @@ export type ContinuationKind =
33
37
  | "gitlab"
34
38
  | "bitbucket"
35
39
  | "deployments"
40
+ | "change_monitors"
41
+ | "issues"
36
42
  | "http";
37
43
 
38
44
  export const CONTINUATION_SELECTORS = [
@@ -54,6 +60,8 @@ export const CONTINUATION_SELECTORS = [
54
60
  "deployments.none/v1",
55
61
  "deployments.none/v2",
56
62
  "deployments.deployment/v2",
63
+ "change_monitors.checkpoint/v2",
64
+ "issues.issue/v2",
57
65
  "none/v1",
58
66
  "none/v2",
59
67
  ] as const;
@@ -76,7 +84,9 @@ export type ContinuationClaim = {
76
84
  | { owner: string; repo: string; number: number }
77
85
  | { channelId: string; threadTs: string }
78
86
  | { fullName: string; number: number }
79
- | { deploymentId: string };
87
+ | { deploymentId: string }
88
+ | { checkpointName: string }
89
+ | { issueName: string };
80
90
  };
81
91
 
82
92
  export type MailboxContinuation = {
@@ -167,6 +177,18 @@ export const BUILTINS: readonly ContinuationBuiltin[] = [
167
177
  events: ["deployment.created", "deployment.succeeded", "deployment.failed"],
168
178
  format: "deployments:{deploymentId}",
169
179
  },
180
+ {
181
+ type: "change-monitor-checkpoint",
182
+ kind: "change_monitors",
183
+ events: ["checkpoint.created"],
184
+ format: "change_monitors:{checkpointName}",
185
+ },
186
+ {
187
+ type: "factory-issue",
188
+ kind: "issues",
189
+ events: ["issue.created"],
190
+ format: "issues:{issueName}",
191
+ },
170
192
  ] as const;
171
193
 
172
194
  export class V2EngineError extends Error {
@@ -201,6 +223,16 @@ const SELECTOR_FIELDS: {
201
223
  type: "deployment",
202
224
  kind: "deployments",
203
225
  },
226
+ "change_monitors.checkpoint/v2": {
227
+ version: 2,
228
+ type: "change-monitor-checkpoint",
229
+ kind: "change_monitors",
230
+ },
231
+ "issues.issue/v2": {
232
+ version: 2,
233
+ type: "factory-issue",
234
+ kind: "issues",
235
+ },
204
236
  "none/v1": { version: 1, type: "none", kind: "http" },
205
237
  "none/v2": { version: 2, type: "none", kind: "http" },
206
238
  };
@@ -234,7 +266,12 @@ export function defaultContinuationSelector(
234
266
  case "slack":
235
267
  return "slack.thread/v2";
236
268
  case "origin":
237
- return originV2Default(eventTypes);
269
+ return membershipOrNone(
270
+ eventTypes,
271
+ "origin-pr",
272
+ "origin.pr/v2",
273
+ "origin.none/v2"
274
+ );
238
275
  case "gitlab":
239
276
  return membershipOrNone(
240
277
  eventTypes,
@@ -250,7 +287,26 @@ export function defaultContinuationSelector(
250
287
  "bitbucket.none/v2"
251
288
  );
252
289
  case "deployments":
253
- return deploymentsV2Default(eventTypes);
290
+ return membershipOrNone(
291
+ eventTypes,
292
+ "deployment",
293
+ "deployments.deployment/v2",
294
+ "deployments.none/v2"
295
+ );
296
+ case "change_monitors":
297
+ return membershipOrNone(
298
+ eventTypes,
299
+ "change-monitor-checkpoint",
300
+ "change_monitors.checkpoint/v2",
301
+ "none/v2"
302
+ );
303
+ case "issues":
304
+ return membershipOrNone(
305
+ eventTypes,
306
+ "factory-issue",
307
+ "issues.issue/v2",
308
+ "none/v2"
309
+ );
254
310
  default:
255
311
  return "none/v2";
256
312
  }
@@ -283,13 +339,17 @@ function githubV2Default(eventTypes?: readonly string[]): ContinuationSelector {
283
339
  return "github.pr/v2";
284
340
  }
285
341
 
286
- function originV2Default(eventTypes?: readonly string[]): ContinuationSelector {
287
- const events = eventTypes ?? [];
288
- const prEvents = new Set(builtinForType("origin-pr")?.events ?? []);
289
- if (events.some((event) => !prEvents.has(event))) {
290
- return "origin.none/v2";
291
- }
292
- return "origin.pr/v2";
342
+ const OMITTED_V2_TYPED_TYPE = {
343
+ github: "pr",
344
+ origin: "origin-pr",
345
+ deployments: "deployment",
346
+ change_monitors: "change-monitor-checkpoint",
347
+ issues: "factory-issue",
348
+ } as const satisfies Partial<Record<string, ConversationType>>;
349
+
350
+ /** Typed membership used when an omitted `/v2` selector becomes type `none`. */
351
+ export function omittedV2TypedType(kind: string): ConversationType | undefined {
352
+ return OMITTED_V2_TYPED_TYPE[kind as keyof typeof OMITTED_V2_TYPED_TYPE];
293
353
  }
294
354
 
295
355
  /**
@@ -312,14 +372,8 @@ export function omittedV2MembershipDowngrade(
312
372
  if (parsed === undefined || parsed.type !== "none") {
313
373
  return undefined;
314
374
  }
315
- const typed =
316
- kind === "github"
317
- ? builtinForType("pr")
318
- : kind === "origin"
319
- ? builtinForType("origin-pr")
320
- : kind === "deployments"
321
- ? builtinForType("deployment")
322
- : undefined;
375
+ const typedType = omittedV2TypedType(kind);
376
+ const typed = typedType === undefined ? undefined : builtinForType(typedType);
323
377
  if (typed === undefined) {
324
378
  return undefined;
325
379
  }
@@ -331,17 +385,6 @@ export function omittedV2MembershipDowngrade(
331
385
  return { selector, unexpected };
332
386
  }
333
387
 
334
- function deploymentsV2Default(
335
- eventTypes?: readonly string[]
336
- ): ContinuationSelector {
337
- const events = eventTypes ?? [];
338
- const deploymentEvents = new Set(builtinForType("deployment")?.events ?? []);
339
- if (events.some((event) => !deploymentEvents.has(event))) {
340
- return "deployments.none/v2";
341
- }
342
- return "deployments.deployment/v2";
343
- }
344
-
345
388
  export function parseContinuationSelector(
346
389
  selector: string
347
390
  ): ParsedContinuation | undefined {
@@ -403,6 +446,8 @@ function formatConversationKey(
403
446
  channelId?: string;
404
447
  threadTs?: string;
405
448
  deploymentId?: string;
449
+ checkpointName?: string;
450
+ issueName?: string;
406
451
  }
407
452
  ): string {
408
453
  switch (type) {
@@ -420,6 +465,10 @@ function formatConversationKey(
420
465
  return `${fields.channelId}:${fields.threadTs}`;
421
466
  case "deployment":
422
467
  return `deployments:${fields.deploymentId}`;
468
+ case "change-monitor-checkpoint":
469
+ return `change_monitors:${fields.checkpointName}`;
470
+ case "factory-issue":
471
+ return `issues:${fields.issueName}`;
423
472
  }
424
473
  }
425
474
 
@@ -441,6 +490,22 @@ export function mintClaim(claim: ContinuationClaim): string {
441
490
  }
442
491
  return `cont:${formatConversationKey("deployment", resource)}`;
443
492
  }
493
+ case "change-monitor-checkpoint": {
494
+ const resource = claim.resource;
495
+ if (!("checkpointName" in resource)) {
496
+ throw new V2EngineError(
497
+ "v2: change-monitor-checkpoint claim requires checkpointName"
498
+ );
499
+ }
500
+ return `cont:${formatConversationKey("change-monitor-checkpoint", resource)}`;
501
+ }
502
+ case "factory-issue": {
503
+ const resource = claim.resource;
504
+ if (!("issueName" in resource)) {
505
+ throw new V2EngineError("v2: factory-issue claim requires issueName");
506
+ }
507
+ return `cont:${formatConversationKey("factory-issue", resource)}`;
508
+ }
444
509
  case "pr":
445
510
  case "issue":
446
511
  case "origin-pr":
@@ -551,7 +616,21 @@ export function extractContinuation(
551
616
  case "bitbucket-pr":
552
617
  return extractBitbucket(payload);
553
618
  case "deployment":
554
- return extractDeployment(payload);
619
+ return extractNamedResource("deployment", "deploymentId", payload);
620
+ case "change-monitor-checkpoint":
621
+ return extractNamedResource(
622
+ "change-monitor-checkpoint",
623
+ "checkpointName",
624
+ payload,
625
+ "checkpoint"
626
+ );
627
+ case "factory-issue":
628
+ return extractNamedResource(
629
+ "factory-issue",
630
+ "issueName",
631
+ payload,
632
+ "issue"
633
+ );
555
634
  }
556
635
  }
557
636
 
@@ -847,13 +926,19 @@ function bitbucketFullName(
847
926
  return `${projectKey}/${slug}`.toLowerCase();
848
927
  }
849
928
 
850
- function extractDeployment(payload: unknown): string | undefined {
929
+ function extractNamedResource(
930
+ type: "deployment" | "change-monitor-checkpoint" | "factory-issue",
931
+ field: "deploymentId" | "checkpointName" | "issueName",
932
+ payload: unknown,
933
+ wrapper?: "checkpoint" | "issue"
934
+ ): string | undefined {
851
935
  const record = asRecord(payload);
852
- const deploymentId = nonEmpty(record?.deploymentId);
853
- if (deploymentId === undefined) {
936
+ const source = wrapper === undefined ? record : asRecord(record?.[wrapper]);
937
+ const value = nonEmpty(source?.[wrapper === undefined ? field : "name"]);
938
+ if (value === undefined) {
854
939
  return undefined;
855
940
  }
856
- return `cont:${formatConversationKey("deployment", { deploymentId })}`;
941
+ return `cont:${formatConversationKey(type, { [field]: value })}`;
857
942
  }
858
943
 
859
944
  function githubFullName(payload: Record<string, unknown>): string | undefined {
@@ -3,6 +3,12 @@
3
3
  */
4
4
 
5
5
  import { join } from "node:path";
6
+ import {
7
+ canonicalizeFileSystemToolName,
8
+ FileSystemConfigError,
9
+ resolveFileSystemTools,
10
+ resolveRootHarnessAllowlist,
11
+ } from "../../filesystem.js";
6
12
  import {
7
13
  type ArtifactsDefinition,
8
14
  DEFAULT_MODEL,
@@ -12,6 +18,7 @@ import {
12
18
  type DiscoveredSchedule,
13
19
  type ResolvedAgent,
14
20
  } from "../../types.js";
21
+ import { synthesizedFileSystemToolNames } from "../filesystem/tools.js";
15
22
  import { withIdentityPreamble } from "../workspace.js";
16
23
  import {
17
24
  isValidModelSetting,
@@ -135,10 +142,88 @@ export async function loadAgent(
135
142
  // second layer (authored tools, mcp-connections, the harness cwd's
136
143
  // ambient config).
137
144
  const authoredAllowlist = config?.tools;
138
- const allowedTools =
145
+ const root = config?.root;
146
+ const hasRoot = root !== undefined;
147
+ if (hasRoot) {
148
+ if (
149
+ config?.local?.cwd !== undefined ||
150
+ config?.local?.workspaceDir !== undefined ||
151
+ config?.local?.sandbox === true
152
+ ) {
153
+ ctx.diagnostics.push({
154
+ severity: "error",
155
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
156
+ message:
157
+ "`root` cannot be combined with `local.cwd`, `local.workspaceDir`, or `local.sandbox`. Those fields configure the host tree the override replaced.",
158
+ });
159
+ }
160
+ if (runtime === "cloud" || runtime === "grokbot") {
161
+ ctx.diagnostics.push({
162
+ severity: "error",
163
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
164
+ message: `\`root\` cannot be combined with runtime: "${runtime}".`,
165
+ });
166
+ }
167
+ if (
168
+ authoredAllowlist?.includes("shell") === true ||
169
+ authoredAllowlist?.includes("Shell") === true
170
+ ) {
171
+ ctx.diagnostics.push({
172
+ severity: "error",
173
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
174
+ message:
175
+ '`root` cannot be combined with `tools` containing `"shell"`. Shell is not a FileSystem method and must not bind the host shell onto a virtual tree.',
176
+ });
177
+ }
178
+ }
179
+ const reservedNames = new Set<string>(
180
+ synthesizedFileSystemToolNames({
181
+ hasRoot,
182
+ tools: authoredAllowlist,
183
+ })
184
+ );
185
+ const clash = tools.find((tool) => {
186
+ if (reservedNames.has(tool.name)) {
187
+ return true;
188
+ }
189
+ const canonical = canonicalizeFileSystemToolName(tool.name);
190
+ return canonical !== undefined && reservedNames.has(canonical);
191
+ });
192
+ if (clash !== undefined) {
193
+ ctx.diagnostics.push({
194
+ severity: "error",
195
+ path: displayPath(ctx, join(agentDir, "tools", `${clash.name}.ts`)),
196
+ message: `agent/tools/${clash.name}.ts: that name is reserved for the synthesized FileSystem tool.`,
197
+ });
198
+ }
199
+ let allowedTools =
139
200
  authoredAllowlist === undefined || authoredAllowlist.includes("mcp")
140
201
  ? authoredAllowlist
141
202
  : [...authoredAllowlist, "mcp" as const];
203
+ if (hasRoot) {
204
+ try {
205
+ if (typeof root !== "function") {
206
+ resolveFileSystemTools({
207
+ root,
208
+ tools: authoredAllowlist,
209
+ });
210
+ } else {
211
+ resolveRootHarnessAllowlist(authoredAllowlist);
212
+ }
213
+ allowedTools = authoredAllowlist;
214
+ } catch (error) {
215
+ ctx.diagnostics.push({
216
+ severity: "error",
217
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
218
+ message:
219
+ error instanceof FileSystemConfigError
220
+ ? error.message
221
+ : error instanceof Error
222
+ ? error.message
223
+ : String(error),
224
+ });
225
+ }
226
+ }
142
227
  // The allowlist states runtime-agnostic intent, but only the local
143
228
  // harness can enforce it (the Cursor SDK rejects tool restrictions on
144
229
  // cloud agents, and the Grok Bot harness has its own fixed toolset) — a
@@ -247,6 +332,7 @@ export async function loadAgent(
247
332
  model: isValidModelSetting(config?.model) ? config.model : DEFAULT_MODEL,
248
333
  instructions,
249
334
  runtime,
335
+ ...(root === undefined ? {} : { root }),
250
336
  cloud,
251
337
  local,
252
338
  ...(allowedTools === undefined ? {} : { allowedTools }),
@@ -137,6 +137,9 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
137
137
  if (meta?.deploymentEventTypes !== undefined) {
138
138
  info.deploymentEventTypes = meta.deploymentEventTypes;
139
139
  }
140
+ if (meta?.factoryEventTypes !== undefined) {
141
+ info.factoryEventTypes = meta.factoryEventTypes;
142
+ }
140
143
  if (meta?.slackChannelPosts !== undefined) {
141
144
  info.slackChannelPosts = meta.slackChannelPosts;
142
145
  }
@@ -0,0 +1,122 @@
1
+ import type { z } from "zod";
2
+ import { defineChannel, localDevStrict, POST } from "../channels.js";
3
+ import type { ChannelDefinition, ChannelHandlerArgs } from "../types.js";
4
+ import { tagOutcome } from "./channel-dispatch.js";
5
+ import type { ContinuationIdentity } from "./continuation-identity.js";
6
+ import { attachHostedAdmissionAdapter } from "./hosted-admission-adapter.js";
7
+ import { bindLocalIngressSend } from "./local-ingress.js";
8
+
9
+ type FactoryResourceChannelOptions<TEvent> = {
10
+ onEvent: (
11
+ event: TEvent,
12
+ args: ChannelHandlerArgs
13
+ ) => Promise<unknown> | unknown;
14
+ continuation?: ContinuationIdentity;
15
+ path?: string;
16
+ auth?: ChannelDefinition["auth"];
17
+ onStart?: ChannelDefinition["onStart"];
18
+ onStop?: ChannelDefinition["onStop"];
19
+ };
20
+
21
+ type FactoryResourceChannelSpec<TEvent extends { eventType: string }> = {
22
+ kind: string;
23
+ eventTypes: readonly string[];
24
+ eventSchema: z.ZodType<TEvent>;
25
+ replayDescription: string;
26
+ invalidError: string;
27
+ };
28
+
29
+ type FactoryResourceChannelPack<TEvent> = {
30
+ channel: (
31
+ options: FactoryResourceChannelOptions<TEvent>
32
+ ) => ChannelDefinition;
33
+ getOptions: (
34
+ definition: object
35
+ ) => FactoryResourceChannelOptions<TEvent> | undefined;
36
+ };
37
+
38
+ /**
39
+ * Shared pack for Factory resource channels that have one event type, no
40
+ * subscription filter, and the same local-replay / hosted-admission path.
41
+ */
42
+ export function createFactoryResourceChannel<
43
+ TEvent extends { eventType: string },
44
+ >(
45
+ spec: FactoryResourceChannelSpec<TEvent>
46
+ ): FactoryResourceChannelPack<TEvent> {
47
+ const optionsByDefinition = new WeakMap<
48
+ object,
49
+ FactoryResourceChannelOptions<TEvent>
50
+ >();
51
+
52
+ async function dispatchEvent(input: {
53
+ options: FactoryResourceChannelOptions<TEvent>;
54
+ event: TEvent;
55
+ args: ChannelHandlerArgs;
56
+ }): Promise<Response> {
57
+ await input.options.onEvent(
58
+ input.event,
59
+ bindLocalIngressSend(input.args, {
60
+ identity: input.options.continuation,
61
+ kind: spec.kind,
62
+ eventType: input.event.eventType,
63
+ payload: input.event,
64
+ })
65
+ );
66
+ return tagOutcome(Response.json({ ok: true }), "task");
67
+ }
68
+
69
+ function channel(
70
+ options: FactoryResourceChannelOptions<TEvent>
71
+ ): ChannelDefinition {
72
+ const routePath = options.path ?? "/";
73
+ const definition = defineChannel({
74
+ auth: options.auth ?? [localDevStrict()],
75
+ meta: {
76
+ kind: spec.kind,
77
+ factoryEventTypes: spec.eventTypes,
78
+ },
79
+ onStart: options.onStart,
80
+ onStop: options.onStop,
81
+ routes: [
82
+ POST(routePath, {
83
+ description: spec.replayDescription,
84
+ bodySchema: spec.eventSchema,
85
+ handler: async (_request, args) =>
86
+ dispatchEvent({
87
+ options,
88
+ event: args.body,
89
+ args,
90
+ }),
91
+ }),
92
+ ],
93
+ });
94
+ optionsByDefinition.set(definition, options);
95
+ attachHostedAdmissionAdapter(definition, {
96
+ kind: spec.kind,
97
+ dispatch: async ({ payload, args }) => {
98
+ const parsed = spec.eventSchema.safeParse(payload);
99
+ if (!parsed.success) {
100
+ return Response.json(
101
+ { ok: false, error: spec.invalidError },
102
+ { status: 400 }
103
+ );
104
+ }
105
+ return dispatchEvent({
106
+ options,
107
+ event: parsed.data,
108
+ args,
109
+ });
110
+ },
111
+ });
112
+ return definition;
113
+ }
114
+
115
+ function getOptions(
116
+ definition: object
117
+ ): FactoryResourceChannelOptions<TEvent> | undefined {
118
+ return optionsByDefinition.get(definition);
119
+ }
120
+
121
+ return { channel, getOptions };
122
+ }