@cursor/july 0.1.10 → 0.1.11

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 (194) hide show
  1. package/AGENTS.md +1 -0
  2. package/dist/bin/agent-serve.js +17 -3
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +2 -1
  5. package/dist/channels/github/types.d.ts +4 -0
  6. package/dist/channels/github/types.d.ts.map +1 -1
  7. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  8. package/dist/channels/slack/cursor-account.js +1 -1
  9. package/dist/channels/slack/dispatch.d.ts +13 -0
  10. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  11. package/dist/channels/slack/dispatch.js +36 -11
  12. package/dist/channels/slack/eval-directive.d.ts +63 -0
  13. package/dist/channels/slack/eval-directive.d.ts.map +1 -0
  14. package/dist/channels/slack/eval-directive.js +283 -0
  15. package/dist/channels/slack/index.d.ts +1 -0
  16. package/dist/channels/slack/index.d.ts.map +1 -1
  17. package/dist/channels/slack/index.js +1 -0
  18. package/dist/channels/slack/post-update-delivery.js +318 -0
  19. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  20. package/dist/channels/slack/slack-channel.js +4 -2
  21. package/dist/channels/slack/types.d.ts +7 -0
  22. package/dist/channels/slack/types.d.ts.map +1 -1
  23. package/dist/connections.d.ts +10 -5
  24. package/dist/connections.d.ts.map +1 -1
  25. package/dist/connections.js +10 -5
  26. package/dist/docs/404.html +2 -2
  27. package/dist/docs/ab.html +4 -4
  28. package/dist/docs/assets/{app.CY2buaEE.js → app.BcXUGca7.js} +1 -1
  29. package/dist/docs/assets/chunks/@localSearchIndexroot.BIbvK8M1.js +1 -0
  30. package/dist/docs/assets/chunks/{VPLocalSearchBox.Ix954SFJ.js → VPLocalSearchBox.CWur6XGZ.js} +1 -1
  31. package/dist/docs/assets/chunks/{theme.PDwuJbyi.js → theme.BnvulOX2.js} +2 -2
  32. package/dist/docs/assets/{deployment.md.DC5-ImcM.js → deployment.md.TecHo0_2.js} +3 -3
  33. package/dist/docs/assets/{deployment.md.DC5-ImcM.lean.js → deployment.md.TecHo0_2.lean.js} +1 -1
  34. package/dist/docs/assets/guides_mcp-oauth.md.DqmEOQY1.js +29 -0
  35. package/dist/docs/assets/guides_mcp-oauth.md.DqmEOQY1.lean.js +1 -0
  36. package/dist/docs/assets/{index.md.COiu-1jL.js → index.md.t0TM2Qzz.js} +3 -3
  37. package/dist/docs/assets/{index.md.COiu-1jL.lean.js → index.md.t0TM2Qzz.lean.js} +1 -1
  38. package/dist/docs/assets/{reference_cli.md.CjfiV2Q-.js → reference_cli.md.B74NrVzb.js} +3 -3
  39. package/dist/docs/assets/{reference_cli.md.CjfiV2Q-.lean.js → reference_cli.md.B74NrVzb.lean.js} +1 -1
  40. package/dist/docs/assets/{reference_connections.md.FbWupyzW.js → reference_connections.md.CsSr3rDz.js} +8 -3
  41. package/dist/docs/assets/{reference_connections.md.FbWupyzW.lean.js → reference_connections.md.CsSr3rDz.lean.js} +1 -1
  42. package/dist/docs/assets/reference_prompt.md.BaiweQxE.js +1 -0
  43. package/dist/docs/assets/reference_prompt.md.BaiweQxE.lean.js +1 -0
  44. package/dist/docs/assets/{reference_tools.md.CqgJroI0.js → reference_tools.md.BRSDnTbN.js} +16 -3
  45. package/dist/docs/assets/{reference_tools.md.CqgJroI0.lean.js → reference_tools.md.BRSDnTbN.lean.js} +1 -1
  46. package/dist/docs/assets/{troubleshooting.md.DkovjS7h.js → troubleshooting.md.CmQkmnzC.js} +1 -1
  47. package/dist/docs/assets/{troubleshooting.md.DkovjS7h.lean.js → troubleshooting.md.CmQkmnzC.lean.js} +1 -1
  48. package/dist/docs/building-with-agents.html +4 -4
  49. package/dist/docs/concepts.html +4 -4
  50. package/dist/docs/deployment.html +7 -7
  51. package/dist/docs/evals.html +4 -4
  52. package/dist/docs/example-agents/approval-buddy.html +4 -4
  53. package/dist/docs/example-agents/benny.html +4 -4
  54. package/dist/docs/example-agents/bugbot.html +4 -4
  55. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  56. package/dist/docs/example-agents/codeowners-review.html +4 -4
  57. package/dist/docs/example-agents/concierge.html +4 -4
  58. package/dist/docs/example-agents/fsd.html +4 -4
  59. package/dist/docs/example-agents/index.html +4 -4
  60. package/dist/docs/example-agents/knowledge-base.html +4 -4
  61. package/dist/docs/example-agents/oncall.html +4 -4
  62. package/dist/docs/example-agents/security-reviewer.html +4 -4
  63. package/dist/docs/example-agents/slack-agent.html +4 -4
  64. package/dist/docs/example-agents/weather-agent.html +4 -4
  65. package/dist/docs/guides/agent-to-agent.html +4 -4
  66. package/dist/docs/guides/cloud-runtime.html +4 -4
  67. package/dist/docs/guides/github.html +4 -4
  68. package/dist/docs/guides/human-in-the-loop.html +4 -4
  69. package/dist/docs/guides/mcp-oauth.html +53 -0
  70. package/dist/docs/guides/slack.html +4 -4
  71. package/dist/docs/guides/webhooks.html +4 -4
  72. package/dist/docs/hashmap.json +1 -1
  73. package/dist/docs/hillclimbing.html +4 -4
  74. package/dist/docs/index.html +6 -6
  75. package/dist/docs/quickstart.html +4 -4
  76. package/dist/docs/reference/agent-config.html +4 -4
  77. package/dist/docs/reference/channels.html +4 -4
  78. package/dist/docs/reference/cli.html +6 -6
  79. package/dist/docs/reference/connections.html +12 -7
  80. package/dist/docs/reference/hooks.html +4 -4
  81. package/dist/docs/reference/http-api.html +4 -4
  82. package/dist/docs/reference/instructions.html +4 -4
  83. package/dist/docs/reference/playground.html +4 -4
  84. package/dist/docs/reference/project-layout.html +4 -4
  85. package/dist/docs/reference/prompt.html +33 -0
  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 +5 -5
  89. package/dist/docs/reference/subagents.html +4 -4
  90. package/dist/docs/reference/tools.html +20 -7
  91. package/dist/docs/scaffolding-agents.html +4 -4
  92. package/dist/docs/storage.html +4 -4
  93. package/dist/docs/troubleshooting.html +5 -5
  94. package/dist/evals.d.ts +39 -0
  95. package/dist/evals.d.ts.map +1 -1
  96. package/dist/index.d.ts +3 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +2 -1
  99. package/dist/internal/cli-mcp-oauth.d.ts +16 -0
  100. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -0
  101. package/dist/internal/cli-mcp-oauth.js +194 -0
  102. package/dist/internal/cli-mcp.d.ts +4 -1
  103. package/dist/internal/cli-mcp.d.ts.map +1 -1
  104. package/dist/internal/cli-mcp.js +19 -1
  105. package/dist/internal/corp-ca.d.ts +9 -0
  106. package/dist/internal/corp-ca.d.ts.map +1 -0
  107. package/dist/internal/corp-ca.js +44 -0
  108. package/dist/internal/cursor/account-mcp.d.ts +17 -4
  109. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  110. package/dist/internal/cursor/account-mcp.js +105 -17
  111. package/dist/internal/discovery.d.ts.map +1 -1
  112. package/dist/internal/discovery.js +64 -10
  113. package/dist/internal/distribution.d.ts.map +1 -1
  114. package/dist/internal/distribution.js +1 -0
  115. package/dist/internal/json-dir-store.js +100 -0
  116. package/dist/internal/logs-client.js +1 -1
  117. package/dist/internal/mcp-host.d.ts.map +1 -1
  118. package/dist/internal/mcp-host.js +49 -26
  119. package/dist/internal/mcp-oauth.d.ts +88 -0
  120. package/dist/internal/mcp-oauth.d.ts.map +1 -0
  121. package/dist/internal/mcp-oauth.js +385 -0
  122. package/dist/internal/resolved-connections.d.ts.map +1 -1
  123. package/dist/internal/resolved-connections.js +4 -1
  124. package/dist/internal/sdk-runner.d.ts +9 -0
  125. package/dist/internal/sdk-runner.d.ts.map +1 -1
  126. package/dist/internal/sdk-runner.js +28 -2
  127. package/dist/internal/server.d.ts.map +1 -1
  128. package/dist/internal/server.js +13 -5
  129. package/dist/internal/session-engine.d.ts +7 -9
  130. package/dist/internal/session-engine.d.ts.map +1 -1
  131. package/dist/internal/session-engine.js +96 -64
  132. package/dist/internal/sessions-client.d.ts.map +1 -1
  133. package/dist/internal/sessions-client.js +2 -1
  134. package/dist/internal/stream-progress.d.ts.map +1 -1
  135. package/dist/internal/stream-progress.js +8 -4
  136. package/dist/internal/turn-governor.d.ts +54 -0
  137. package/dist/internal/turn-governor.d.ts.map +1 -0
  138. package/dist/internal/turn-governor.js +111 -0
  139. package/dist/playground/assets/{index-C0-QEG4O.js → index-BIGLubwK.js} +46 -46
  140. package/dist/playground/assets/{index-z3JY9O2x.css → index-C17ZQKDG.css} +1 -1
  141. package/dist/playground/index.html +2 -2
  142. package/dist/prompt.d.ts +39 -0
  143. package/dist/prompt.d.ts.map +1 -0
  144. package/dist/prompt.js +70 -0
  145. package/dist/types.d.ts +78 -5
  146. package/dist/types.d.ts.map +1 -1
  147. package/dist/types.js +6 -0
  148. package/docs/.vitepress/cache/deps/package.json +3 -0
  149. package/docs/README.md +4 -0
  150. package/docs/deployment.md +6 -1
  151. package/docs/guides/mcp-oauth.md +157 -0
  152. package/docs/reference/cli.md +28 -0
  153. package/docs/reference/connections.md +28 -0
  154. package/docs/reference/prompt.md +47 -0
  155. package/docs/reference/tools.md +21 -0
  156. package/docs/troubleshooting.md +11 -0
  157. package/package.json +9 -2
  158. package/skills/create-agent/SKILL.md +7 -3
  159. package/skills/framework-map/SKILL.md +1 -0
  160. package/skills/mcp-auth/SKILL.md +138 -0
  161. package/src/bin/agent-serve.ts +20 -3
  162. package/src/channels/github/github-channel.ts +2 -0
  163. package/src/channels/github/types.ts +4 -0
  164. package/src/channels/slack/cursor-account.ts +2 -0
  165. package/src/channels/slack/dispatch.ts +49 -0
  166. package/src/channels/slack/eval-directive.ts +348 -0
  167. package/src/channels/slack/index.ts +1 -0
  168. package/src/channels/slack/slack-channel.ts +3 -0
  169. package/src/channels/slack/types.ts +7 -0
  170. package/src/connections.ts +10 -5
  171. package/src/evals.ts +43 -0
  172. package/src/index.ts +3 -0
  173. package/src/internal/cli-mcp-oauth.ts +254 -0
  174. package/src/internal/cli-mcp.ts +25 -2
  175. package/src/internal/cursor/account-mcp.ts +153 -41
  176. package/src/internal/discovery.ts +70 -8
  177. package/src/internal/distribution.ts +1 -0
  178. package/src/internal/logs-client.ts +1 -1
  179. package/src/internal/mcp-host.ts +33 -9
  180. package/src/internal/mcp-oauth.ts +496 -0
  181. package/src/internal/resolved-connections.ts +6 -1
  182. package/src/internal/sdk-runner.ts +37 -1
  183. package/src/internal/server.ts +13 -5
  184. package/src/internal/session-engine.ts +115 -74
  185. package/src/internal/sessions-client.ts +2 -1
  186. package/src/internal/stream-progress.ts +8 -0
  187. package/src/internal/turn-governor.ts +145 -0
  188. package/src/prompt.ts +83 -0
  189. package/src/types.ts +76 -6
  190. package/dist/docs/assets/chunks/@localSearchIndexroot.OfDZ5CXg.js +0 -1
  191. package/dist/internal/persistence-coordinator.d.ts +0 -127
  192. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  193. package/dist/persistence.d.ts +0 -184
  194. package/dist/persistence.d.ts.map +0 -1
@@ -51,6 +51,7 @@ import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections
51
51
  import { SessionStore } from "./session-store.js";
52
52
  import { StorageCoordinator } from "./storage-coordinator.js";
53
53
  import { normalizeToolResult, toolCallErrorMessage } from "./tool-result.js";
54
+ import { TurnGovernor } from "./turn-governor.js";
54
55
  import { buildAgentsMdContent, isNestedInGitRepo, materializeWorkspace, writeWorkspaceFiles, } from "./workspace.js";
55
56
  export const HTTP_CHANNEL_ID = "http";
56
57
  export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
@@ -143,6 +144,7 @@ export class SessionEngine {
143
144
  this.stateRoot = options.stateRoot;
144
145
  this.runner = options.runner;
145
146
  this.logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (() => { });
147
+ this.turnGovernor = new TurnGovernor(options.project.agent.concurrency);
146
148
  this.sessions = new SessionStore(join(options.stateRoot, "sessions"));
147
149
  this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
148
150
  this.askDedupe = new SlackAskDedupeStore(join(options.stateRoot, "slack-asks"));
@@ -186,13 +188,14 @@ export class SessionEngine {
186
188
  });
187
189
  }
188
190
  }
189
- /** Shared host services (MCP / GitHub / Slack / reminders / kv / Slack nudges). */
191
+ /** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / Slack nudges). */
190
192
  get host() {
191
193
  return {
192
194
  mcp: this.mcpHost,
193
195
  github: this.platforms.github,
194
196
  slack: this.platforms.slack,
195
197
  reminders: this.reminderApi,
198
+ evals: this.evalsApi,
196
199
  kv: this.kvApi,
197
200
  slackNudges: this.slackNudgeHostApi(),
198
201
  };
@@ -212,6 +215,10 @@ export class SessionEngine {
212
215
  setReminders(api) {
213
216
  this.reminderApi = api;
214
217
  }
218
+ /** Attach the serve-host playground evals API (called from startServer). */
219
+ setEvals(api) {
220
+ this.evalsApi = api;
221
+ }
215
222
  // ==========================================================================
216
223
  // Sending messages
217
224
  // ==========================================================================
@@ -520,11 +527,8 @@ export class SessionEngine {
520
527
  turnId,
521
528
  data: { text: message },
522
529
  });
523
- yield this.appendEvent(sessionId, {
524
- type: "turn.started",
525
- turnId,
526
- data: {},
527
- });
530
+ // `turn.started` is appended by executeTurn once the turn holds a
531
+ // governor slot; saturated engines emit `turn.queued` first.
528
532
  const abort = new AbortController();
529
533
  this.turnAborts.set(sessionId, abort);
530
534
  // Lifecycle promise includes post-turn coalesce drain so waiters and
@@ -707,54 +711,77 @@ export class SessionEngine {
707
711
  }
708
712
  };
709
713
  const mapper = new TurnEventMapper(emit);
714
+ // Take a turn slot before any work happens. When every slot is busy the
715
+ // turn waits FIFO — durable `turn.queued` marks the wait — and a
716
+ // preempting follow-up (or interrupt) resolves the wait as cancelled
717
+ // without the turn ever running.
718
+ const permit = yield this.turnGovernor.acquire({
719
+ sessionId,
720
+ signal,
721
+ onQueued: (position) => {
722
+ var _a;
723
+ const maxRunningTurns = (_a = this.turnGovernor.maxRunningTurns) !== null && _a !== void 0 ? _a : 0;
724
+ emit({ type: "turn.queued", data: { position, maxRunningTurns } });
725
+ this.logger(`[session] ${sessionId} ${turnId} queued at position ${position} (${maxRunningTurns} turn slot(s) busy)`);
726
+ },
727
+ });
710
728
  let outcome;
711
- try {
712
- // Scaffold agent files into the session workspace even when the harness
713
- // cwd is overridden (e.g. a PR worktree). A/B variant instruction
714
- // overlays are applied via agentForSession.
715
- const agent = this.agentForSession(record);
716
- yield materializeWorkspace({
717
- agent,
718
- workspaceDir,
719
- seed: isFirstTurn,
720
- });
721
- const pendingFiles = record.pendingWorkspaceFiles;
722
- if (pendingFiles !== undefined && Object.keys(pendingFiles).length > 0) {
723
- yield writeWorkspaceFiles(harnessCwd, pendingFiles);
724
- yield this.updateRecord(sessionId, (current) => {
725
- const { pendingWorkspaceFiles: _cleared } = current, rest = __rest(current, ["pendingWorkspaceFiles"]);
726
- return rest;
729
+ if (permit === undefined) {
730
+ // Preempted while waiting for a slot; settle as an interrupted turn.
731
+ outcome = { status: "cancelled" };
732
+ }
733
+ else {
734
+ emit({ type: "turn.started", data: {} });
735
+ try {
736
+ // Scaffold agent files into the session workspace even when the harness
737
+ // cwd is overridden (e.g. a PR worktree). A/B variant instruction
738
+ // overlays are applied via agentForSession.
739
+ const agent = this.agentForSession(record);
740
+ yield materializeWorkspace({
741
+ agent,
742
+ workspaceDir,
743
+ seed: isFirstTurn,
727
744
  });
728
- }
729
- // The harness only loads the scaffolded AGENTS.md when the session
730
- // workspace is its own project root. With a per-session cwd override
731
- // (PR worktree) the scaffold lands elsewhere, and a workspace nested
732
- // inside a git repo re-roots harness rules at that repo — in both
733
- // cases deliver instructions inline on the first turn instead.
734
- const instructionsInvisibleToHarness = isFirstTurn &&
735
- (record.workspaceOverride !== undefined ||
736
- (yield isNestedInGitRepo(harnessCwd)));
737
- const request = this.buildTurnRequest(record, agent, turnId, message, isFirstTurn, instructionsInvisibleToHarness, emit, (update) => __awaiter(this, void 0, void 0, function* () {
738
- mapper.handleUpdate(update);
739
- // Yield so the HTTP NDJSON writer can flush tool start/result
740
- // events while sibling parallel tools are still executing.
741
- if (update.type === "tool-call-started" ||
742
- update.type === "tool-call-completed" ||
743
- // Nested subagent progress (newer SDKs); string check keeps
744
- // typecheck green against older published `@cursor/sdk` pins.
745
- update.type === "tool-call-delta") {
746
- yield yieldEventLoop();
745
+ const pendingFiles = record.pendingWorkspaceFiles;
746
+ if (pendingFiles !== undefined &&
747
+ Object.keys(pendingFiles).length > 0) {
748
+ yield writeWorkspaceFiles(harnessCwd, pendingFiles);
749
+ yield this.updateRecord(sessionId, (current) => {
750
+ const { pendingWorkspaceFiles: _cleared } = current, rest = __rest(current, ["pendingWorkspaceFiles"]);
751
+ return rest;
752
+ });
747
753
  }
748
- }), signal);
749
- outcome = yield this.runner.runTurn(request);
750
- }
751
- catch (error) {
752
- this.approvals.cancelSession(sessionId, "deny");
753
- outcome = { status: "error", errorMessage: describeError(error) };
754
- }
755
- finally {
756
- this.approvals.cancelSession(sessionId, "deny");
757
- yield this.clearPendingApprovals(sessionId);
754
+ // The harness only loads the scaffolded AGENTS.md when the session
755
+ // workspace is its own project root. With a per-session cwd override
756
+ // (PR worktree) the scaffold lands elsewhere, and a workspace nested
757
+ // inside a git repo re-roots harness rules at that repo — in both
758
+ // cases deliver instructions inline on the first turn instead.
759
+ const instructionsInvisibleToHarness = isFirstTurn &&
760
+ (record.workspaceOverride !== undefined ||
761
+ (yield isNestedInGitRepo(harnessCwd)));
762
+ const request = this.buildTurnRequest(record, agent, turnId, message, isFirstTurn, instructionsInvisibleToHarness, emit, (update) => __awaiter(this, void 0, void 0, function* () {
763
+ mapper.handleUpdate(update);
764
+ // Yield so the HTTP NDJSON writer can flush tool start/result
765
+ // events while sibling parallel tools are still executing.
766
+ if (update.type === "tool-call-started" ||
767
+ update.type === "tool-call-completed" ||
768
+ // Nested subagent progress (newer SDKs); string check keeps
769
+ // typecheck green against older published `@cursor/sdk` pins.
770
+ update.type === "tool-call-delta") {
771
+ yield yieldEventLoop();
772
+ }
773
+ }), signal);
774
+ outcome = yield this.runner.runTurn(request);
775
+ }
776
+ catch (error) {
777
+ this.approvals.cancelSession(sessionId, "deny");
778
+ outcome = { status: "error", errorMessage: describeError(error) };
779
+ }
780
+ finally {
781
+ permit.release();
782
+ this.approvals.cancelSession(sessionId, "deny");
783
+ yield this.clearPendingApprovals(sessionId);
784
+ }
758
785
  }
759
786
  mapper.finish();
760
787
  if (mapper.title !== undefined) {
@@ -867,34 +894,37 @@ export class SessionEngine {
867
894
  });
868
895
  }) });
869
896
  }
870
- /**
871
- * MCP servers for a turn: authored concrete connections plus resolved
872
- * serve-time resolved connections (peers, Cursor account bridge).
873
- * Resolved connections pick their URL by runtime — loopback for local
874
- * turns, `cloudUrl` for cloud turns — and are omitted from cloud turns
875
- * when no cloud URL exists unless a `servers` filter requires the bridge
876
- * (see {@link assertCloudCanHonorAccountServersFilters}).
877
- */
897
+ /** Authored + resolved MCP servers for a turn; skips `hostOnly`. */
878
898
  buildMcpServers(runtime) {
899
+ const hostOnlyNames = new Set(this.project.agent.connections
900
+ .filter((connection) => connection.hostOnly === true)
901
+ .map((connection) => connection.name));
879
902
  assertCloudCanHonorAccountServersFilters({
880
903
  runtime,
881
- resolvedConnections: this.resolvedConnections,
904
+ resolvedConnections: this.resolvedConnections.filter((resolved) => !hostOnlyNames.has(resolved.name)),
882
905
  });
883
906
  const servers = {};
884
907
  for (const connection of this.project.agent.connections) {
908
+ if (hostOnlyNames.has(connection.name)) {
909
+ continue;
910
+ }
885
911
  if (isSymbolicConnectionTransport(connection.transport)) {
886
912
  continue;
887
913
  }
888
914
  servers[connection.name] = connection.transport;
889
915
  }
890
916
  for (const resolved of this.resolvedConnections) {
917
+ if (hostOnlyNames.has(resolved.name)) {
918
+ continue;
919
+ }
891
920
  const url = runtime === "cloud" ? resolved.cloudUrl : resolved.localUrl;
892
921
  if (url === undefined) {
893
922
  continue;
894
923
  }
895
- servers[resolved.name] = Object.assign({ url }, (resolved.headers === undefined
896
- ? {}
897
- : { headers: resolved.headers }));
924
+ servers[resolved.name] = {
925
+ url,
926
+ headers: resolved.headers,
927
+ };
898
928
  }
899
929
  return servers;
900
930
  }
@@ -1434,7 +1464,9 @@ export class SessionEngine {
1434
1464
  .map((record) => {
1435
1465
  const externalUrl = externalUrlForSession(record);
1436
1466
  return Object.assign(Object.assign(Object.assign({ sessionId: record.sessionId, channelId: record.channelId, mode: record.mode }, (record.title === undefined ? {} : { title: record.title })), { turnCount: record.turnCount, running: this.activeTurns.has(record.sessionId) ||
1437
- this.pendingTurns.has(record.sessionId), createdAt: record.createdAt, updatedAt: record.updatedAt, continuationToken: record.continuationKey === null
1467
+ this.pendingTurns.has(record.sessionId), queued: this.turnGovernor.limited
1468
+ ? this.turnGovernor.isQueued(record.sessionId)
1469
+ : undefined, createdAt: record.createdAt, updatedAt: record.updatedAt, continuationToken: record.continuationKey === null
1438
1470
  ? null
1439
1471
  : formatContinuationToken(record.channelId, record.continuationKey) }), (externalUrl === undefined ? {} : { externalUrl }));
1440
1472
  });
@@ -1 +1 @@
1
- {"version":3,"file":"sessions-client.d.ts","sourceRoot":"","sources":["../../src/internal/sessions-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE7E,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,4DAA4D;IAC5D,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAE5B;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,cAAc,CAAC,CAiC1B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAuE1B;AAgCD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAClC,cAAc,GAAG,SAAS,CAU5B;AAED,+DAA+D;AAC/D,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO9B;AAED,+DAA+D;AAC/D,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAIzE;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAE,GACvC,MAAM,CAkBR"}
1
+ {"version":3,"file":"sessions-client.d.ts","sourceRoot":"","sources":["../../src/internal/sessions-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE7E,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,4DAA4D;IAC5D,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAE5B;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,cAAc,CAAC,CAiC1B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAuE1B;AAiCD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAClC,cAAc,GAAG,SAAS,CAU5B;AAED,+DAA+D;AAC/D,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO9B;AAED,+DAA+D;AAC/D,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAIzE;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAE,GACvC,MAAM,CAkBR"}
@@ -143,6 +143,7 @@ function parseSessionSummary(value) {
143
143
  title: typeof row.title === "string" ? row.title : undefined,
144
144
  turnCount: typeof row.turnCount === "number" ? row.turnCount : 0,
145
145
  running: row.running === true,
146
+ queued: typeof row.queued === "boolean" ? row.queued : undefined,
146
147
  createdAt: typeof row.createdAt === "string" ? row.createdAt : "",
147
148
  updatedAt: typeof row.updatedAt === "string" ? row.updatedAt : "",
148
149
  continuationToken,
@@ -194,7 +195,7 @@ export function formatSessionsTable(sessions, options) {
194
195
  s.channelId,
195
196
  s.mode,
196
197
  String(s.turnCount),
197
- s.running ? "yes" : "no",
198
+ s.queued === true ? "queued" : s.running ? "yes" : "no",
198
199
  s.updatedAt,
199
200
  ]);
200
201
  return `${sourceLine}${renderTable(header, rows)}\n`;
@@ -1 +1 @@
1
- {"version":3,"file":"stream-progress.d.ts","sourceRoot":"","sources":["../../src/internal/stream-progress.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAC7C,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,cAAc,CAqNjB"}
1
+ {"version":3,"file":"stream-progress.d.ts","sourceRoot":"","sources":["../../src/internal/stream-progress.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAC7C,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,cAAc,CA6NjB"}
@@ -21,7 +21,7 @@ export function createStreamProgress(options) {
21
21
  const elapsed = (since) => `${Date.now() - since}ms`;
22
22
  return {
23
23
  onEvent(event) {
24
- var _a, _b, _c;
24
+ var _a, _b, _c, _d;
25
25
  switch (event.type) {
26
26
  case "session.started": {
27
27
  write(c.dim(`session ${event.sessionId}\n`));
@@ -35,12 +35,16 @@ export function createStreamProgress(options) {
35
35
  }
36
36
  break;
37
37
  }
38
+ case "turn.queued": {
39
+ write(c.dim(` turn ${(_a = event.turnId) !== null && _a !== void 0 ? _a : "?"} queued (position ${event.data.position}, max ${event.data.maxRunningTurns} running)\n`));
40
+ break;
41
+ }
38
42
  case "turn.started": {
39
43
  turnClock = Date.now();
40
44
  if (event.turnId !== undefined) {
41
45
  turnStartedAt.set(event.turnId, turnClock);
42
46
  }
43
- write(c.dim(` turn ${(_a = event.turnId) !== null && _a !== void 0 ? _a : "?"} started\n`));
47
+ write(c.dim(` turn ${(_b = event.turnId) !== null && _b !== void 0 ? _b : "?"} started\n`));
44
48
  break;
45
49
  }
46
50
  case "message.received": {
@@ -131,11 +135,11 @@ export function createStreamProgress(options) {
131
135
  const detail = event.data.description
132
136
  ? ` — ${truncate(event.data.description, 60)}`
133
137
  : "";
134
- write(` ${c.magenta("task")} ${c.bold((_b = event.data.name) !== null && _b !== void 0 ? _b : "subagent")}${c.dim(detail)}\n`);
138
+ write(` ${c.magenta("task")} ${c.bold((_c = event.data.name) !== null && _c !== void 0 ? _c : "subagent")}${c.dim(detail)}\n`);
135
139
  break;
136
140
  }
137
141
  case "subagent.completed": {
138
- write(` ${c.magenta("✓")} ${c.bold((_c = event.data.name) !== null && _c !== void 0 ? _c : "subagent")} ${c.dim("done")}\n`);
142
+ write(` ${c.magenta("✓")} ${c.bold((_d = event.data.name) !== null && _d !== void 0 ? _d : "subagent")} ${c.dim("done")}\n`);
139
143
  break;
140
144
  }
141
145
  case "turn.completed": {
@@ -0,0 +1,54 @@
1
+ /**
2
+ * FIFO admission of model turns under `concurrency.maxRunningTurns`
3
+ * (see {@link AgentConcurrencyOptions}). One governor per session engine:
4
+ * every turn acquires a slot before its SDK run starts and releases it when
5
+ * the run settles, so at most `maxRunningTurns` turns execute at once and
6
+ * the rest wait in arrival order instead of failing.
7
+ *
8
+ * Waiting is cancellable: the engine passes the turn's abort signal, so a
9
+ * follow-up that preempts a queued turn (or an explicit `interrupt()`)
10
+ * removes the waiter without ever running it.
11
+ */
12
+ import type { AgentConcurrencyOptions } from "../types.js";
13
+ export interface TurnPermit {
14
+ /** Return the slot. Idempotent; wakes the oldest waiter, if any. */
15
+ release(): void;
16
+ }
17
+ export interface AcquireTurnSlotArgs {
18
+ sessionId: string;
19
+ /** Abort waiting (preemption / interrupt). Ignored once a slot is held. */
20
+ signal?: AbortSignal;
21
+ /**
22
+ * Called synchronously when the turn must wait, with its 1-based
23
+ * position among waiting turns at enqueue time.
24
+ */
25
+ onQueued?: (position: number) => void;
26
+ }
27
+ export declare class TurnGovernor {
28
+ /** The configured ceiling, or undefined for unlimited. */
29
+ readonly maxRunningTurns: number | undefined;
30
+ private running;
31
+ private readonly waiters;
32
+ /** Sessions currently waiting for a slot (at most one turn per session). */
33
+ private readonly queuedSessions;
34
+ constructor(concurrency: AgentConcurrencyOptions | undefined);
35
+ /** True when a `maxRunningTurns` ceiling is configured. */
36
+ get limited(): boolean;
37
+ /** True while `sessionId` has a turn waiting for a slot. */
38
+ isQueued(sessionId: string): boolean;
39
+ /** Turns currently waiting for a slot. */
40
+ get queuedCount(): number;
41
+ /** Turns currently holding a slot. */
42
+ get runningCount(): number;
43
+ /**
44
+ * Acquire a turn slot. Resolves with a permit once a slot is free (FIFO
45
+ * behind earlier waiters), or with `undefined` when the abort signal
46
+ * fires while waiting — the turn must then settle as cancelled without
47
+ * running. Unlimited governors grant immediately.
48
+ */
49
+ acquire(args: AcquireTurnSlotArgs): Promise<TurnPermit | undefined>;
50
+ /** Take a slot and build its (idempotent) release handle. */
51
+ private grantSlot;
52
+ private wakeNextWaiter;
53
+ }
54
+ //# sourceMappingURL=turn-governor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-governor.d.ts","sourceRoot":"","sources":["../../src/internal/turn-governor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AASD,qBAAa,YAAY;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IAEpD,YAAY,WAAW,EAAE,uBAAuB,GAAG,SAAS,EAE3D;IAED,2DAA2D;IAC3D,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEnC;IAED,0CAA0C;IAC1C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,sCAAsC;IACtC,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAoClE;IAED,6DAA6D;IAC7D,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,cAAc;CAYvB"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * FIFO admission of model turns under `concurrency.maxRunningTurns`
3
+ * (see {@link AgentConcurrencyOptions}). One governor per session engine:
4
+ * every turn acquires a slot before its SDK run starts and releases it when
5
+ * the run settles, so at most `maxRunningTurns` turns execute at once and
6
+ * the rest wait in arrival order instead of failing.
7
+ *
8
+ * Waiting is cancellable: the engine passes the turn's abort signal, so a
9
+ * follow-up that preempts a queued turn (or an explicit `interrupt()`)
10
+ * removes the waiter without ever running it.
11
+ */
12
+ export class TurnGovernor {
13
+ constructor(concurrency) {
14
+ this.running = 0;
15
+ this.waiters = [];
16
+ /** Sessions currently waiting for a slot (at most one turn per session). */
17
+ this.queuedSessions = new Set();
18
+ this.maxRunningTurns = concurrency === null || concurrency === void 0 ? void 0 : concurrency.maxRunningTurns;
19
+ }
20
+ /** True when a `maxRunningTurns` ceiling is configured. */
21
+ get limited() {
22
+ return this.maxRunningTurns !== undefined;
23
+ }
24
+ /** True while `sessionId` has a turn waiting for a slot. */
25
+ isQueued(sessionId) {
26
+ return this.queuedSessions.has(sessionId);
27
+ }
28
+ /** Turns currently waiting for a slot. */
29
+ get queuedCount() {
30
+ return this.waiters.length;
31
+ }
32
+ /** Turns currently holding a slot. */
33
+ get runningCount() {
34
+ return this.running;
35
+ }
36
+ /**
37
+ * Acquire a turn slot. Resolves with a permit once a slot is free (FIFO
38
+ * behind earlier waiters), or with `undefined` when the abort signal
39
+ * fires while waiting — the turn must then settle as cancelled without
40
+ * running. Unlimited governors grant immediately.
41
+ */
42
+ acquire(args) {
43
+ var _a;
44
+ const limit = this.maxRunningTurns;
45
+ if (limit === undefined) {
46
+ return Promise.resolve(NO_OP_PERMIT);
47
+ }
48
+ if (this.running < limit && this.waiters.length === 0) {
49
+ return Promise.resolve(this.grantSlot());
50
+ }
51
+ // Saturated. A turn that is already preempted never enqueues.
52
+ if (((_a = args.signal) === null || _a === void 0 ? void 0 : _a.aborted) === true) {
53
+ return Promise.resolve(undefined);
54
+ }
55
+ return new Promise((resolve) => {
56
+ var _a, _b;
57
+ const waiter = {
58
+ sessionId: args.sessionId,
59
+ grant: (permit) => resolve(permit),
60
+ cancel: () => resolve(undefined),
61
+ cleanup: () => {
62
+ var _a;
63
+ this.queuedSessions.delete(args.sessionId);
64
+ (_a = args.signal) === null || _a === void 0 ? void 0 : _a.removeEventListener("abort", onAbort);
65
+ },
66
+ };
67
+ const onAbort = () => {
68
+ const index = this.waiters.indexOf(waiter);
69
+ if (index === -1) {
70
+ return;
71
+ }
72
+ this.waiters.splice(index, 1);
73
+ waiter.cleanup();
74
+ waiter.cancel();
75
+ };
76
+ this.waiters.push(waiter);
77
+ this.queuedSessions.add(args.sessionId);
78
+ (_a = args.signal) === null || _a === void 0 ? void 0 : _a.addEventListener("abort", onAbort, { once: true });
79
+ (_b = args.onQueued) === null || _b === void 0 ? void 0 : _b.call(args, this.waiters.length);
80
+ });
81
+ }
82
+ /** Take a slot and build its (idempotent) release handle. */
83
+ grantSlot() {
84
+ this.running += 1;
85
+ let released = false;
86
+ return {
87
+ release: () => {
88
+ if (released) {
89
+ return;
90
+ }
91
+ released = true;
92
+ this.running -= 1;
93
+ this.wakeNextWaiter();
94
+ },
95
+ };
96
+ }
97
+ wakeNextWaiter() {
98
+ const limit = this.maxRunningTurns;
99
+ if (limit === undefined || this.running >= limit) {
100
+ return;
101
+ }
102
+ const waiter = this.waiters.shift();
103
+ if (waiter === undefined) {
104
+ return;
105
+ }
106
+ waiter.cleanup();
107
+ waiter.grant(this.grantSlot());
108
+ }
109
+ }
110
+ /** Permit handed out by unlimited governors. */
111
+ const NO_OP_PERMIT = { release: () => { } };