one-code-extension 0.1.3 → 0.3.0

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 (300) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +200 -172
  3. package/agents/explore.md +9 -6
  4. package/agents/general-purpose.md +1 -1
  5. package/agents/plan.md +6 -4
  6. package/docs/guide/README.md +52 -0
  7. package/docs/guide/bring-your-claude-code-setup.md +126 -0
  8. package/docs/guide/configuration.md +171 -0
  9. package/docs/guide/differences-from-claude-code.md +150 -0
  10. package/docs/guide/doctor.md +86 -0
  11. package/docs/guide/hooks.md +109 -0
  12. package/docs/guide/installation.md +124 -0
  13. package/docs/guide/permissions-modes-and-auto-mode.md +450 -0
  14. package/docs/guide/providers-and-models.md +194 -0
  15. package/docs/guide/reference.md +224 -0
  16. package/docs/guide/sessions-and-context.md +190 -0
  17. package/docs/guide/skills-plugins-and-mcp.md +176 -0
  18. package/docs/guide/subagents-and-workflows.md +193 -0
  19. package/docs/guide/tasks-and-background-work.md +111 -0
  20. package/docs/guide/terminal-interface.md +155 -0
  21. package/docs/guide/tools.md +175 -0
  22. package/docs/guide/troubleshooting.md +175 -0
  23. package/extensions/ask-user/index.ts +65 -71
  24. package/extensions/ask-user/questions.ts +15 -28
  25. package/extensions/ask-user/widget.ts +533 -0
  26. package/extensions/auto-mode/actions.ts +9 -26
  27. package/extensions/auto-mode/classifier-prompt.ts +127 -0
  28. package/extensions/auto-mode/classifier.ts +305 -227
  29. package/extensions/auto-mode/config.ts +279 -77
  30. package/extensions/auto-mode/defaults.ts +17 -60
  31. package/extensions/auto-mode/instructions.ts +2 -1
  32. package/extensions/auto-mode/model-picker.ts +53 -31
  33. package/extensions/auto-mode/model-select.ts +153 -82
  34. package/extensions/auto-mode/paths.ts +13 -1
  35. package/extensions/auto-mode/pause.ts +8 -0
  36. package/extensions/auto-mode/prompt.ts +205 -238
  37. package/extensions/auto-mode/recoverability.ts +229 -0
  38. package/extensions/auto-mode/rules.ts +92 -75
  39. package/extensions/auto-mode/safety-floor.ts +43 -12
  40. package/extensions/auto-mode/sensitive.ts +11 -0
  41. package/extensions/auto-mode/setup-run.ts +245 -0
  42. package/extensions/auto-mode/setup.ts +235 -0
  43. package/extensions/auto-mode/shell-analysis.ts +332 -26
  44. package/extensions/auto-mode/transcript.ts +102 -0
  45. package/extensions/background/index.ts +256 -43
  46. package/extensions/background/monitor-batch.ts +58 -0
  47. package/extensions/background/registry.ts +9 -0
  48. package/extensions/background/wakeup.ts +45 -0
  49. package/extensions/bash/background.ts +47 -22
  50. package/extensions/bash/guards.ts +229 -0
  51. package/extensions/bash/index.ts +81 -19
  52. package/extensions/branding/index.ts +44 -18
  53. package/extensions/branding/startup.ts +23 -8
  54. package/extensions/claude-compat/index.ts +17 -2
  55. package/extensions/claude-context/index.ts +41 -12
  56. package/extensions/clear/index.ts +27 -0
  57. package/extensions/compaction/fit.ts +121 -0
  58. package/extensions/compaction/index.ts +176 -71
  59. package/extensions/compaction/prompt.ts +21 -1
  60. package/extensions/context-budget/budget.ts +61 -0
  61. package/extensions/context-budget/index.ts +66 -0
  62. package/extensions/context-management/index.ts +40 -16
  63. package/extensions/doctor/build.ts +226 -0
  64. package/extensions/doctor/cli.ts +99 -0
  65. package/extensions/doctor/compat.ts +532 -0
  66. package/extensions/doctor/dependencies.ts +186 -0
  67. package/extensions/doctor/fix-prompt.ts +125 -0
  68. package/extensions/doctor/index.ts +308 -0
  69. package/extensions/doctor/models.ts +231 -0
  70. package/extensions/doctor/presets.ts +208 -0
  71. package/extensions/doctor/report.ts +189 -0
  72. package/extensions/doctor/update-lookup.ts +46 -0
  73. package/extensions/doctor/viewer.ts +124 -0
  74. package/extensions/effort/index.ts +41 -4
  75. package/extensions/effort/slider.ts +33 -2
  76. package/extensions/file-tracker/index.ts +143 -20
  77. package/extensions/file-tracker/replay.ts +46 -0
  78. package/extensions/file-tracker/tracker.ts +51 -4
  79. package/extensions/footer/footer-line.ts +207 -0
  80. package/extensions/footer/index.ts +152 -0
  81. package/extensions/footer/pr.ts +53 -0
  82. package/extensions/hooks/executor.ts +57 -20
  83. package/extensions/hooks/formatter-notice.ts +76 -0
  84. package/extensions/hooks/index.ts +314 -73
  85. package/extensions/hooks/matcher.ts +35 -1
  86. package/extensions/hooks/plugin-hooks.ts +3 -3
  87. package/extensions/hooks/protocol.ts +58 -0
  88. package/extensions/hooks/subagent-bridge.ts +77 -0
  89. package/extensions/hooks/trust.ts +15 -4
  90. package/extensions/init/index.ts +26 -0
  91. package/extensions/init/prompt.ts +40 -0
  92. package/extensions/interrupted/index.ts +35 -0
  93. package/extensions/interrupted/line.ts +9 -0
  94. package/extensions/lib/abort.ts +15 -0
  95. package/extensions/lib/agent-loader.ts +183 -0
  96. package/extensions/lib/anthropic-payload.ts +15 -0
  97. package/extensions/lib/atomic-write.ts +56 -0
  98. package/extensions/lib/bridge-watch.ts +20 -0
  99. package/extensions/lib/cache-retention.ts +60 -0
  100. package/extensions/lib/capability-index.ts +466 -0
  101. package/extensions/lib/claude-context.ts +318 -27
  102. package/extensions/lib/claude-settings.ts +64 -0
  103. package/extensions/lib/consent-preview.ts +30 -0
  104. package/extensions/lib/deferred.ts +144 -13
  105. package/extensions/lib/favorites.ts +43 -0
  106. package/extensions/lib/fetch-timeout.ts +15 -0
  107. package/extensions/lib/frontmatter.ts +31 -0
  108. package/extensions/lib/git.ts +44 -2
  109. package/extensions/lib/hook-gate.ts +90 -0
  110. package/extensions/lib/interrupt.ts +94 -0
  111. package/extensions/lib/mcp-overrides.ts +61 -0
  112. package/extensions/lib/mcp-share.ts +25 -0
  113. package/extensions/lib/mcp-status.ts +34 -0
  114. package/extensions/lib/memory.ts +41 -6
  115. package/extensions/lib/model-facts.generated.json +2480 -0
  116. package/extensions/lib/model-facts.ts +162 -0
  117. package/extensions/lib/model-policy.ts +145 -189
  118. package/extensions/lib/model-tier.ts +480 -64
  119. package/extensions/lib/model-unusable.ts +37 -0
  120. package/extensions/lib/notifications.ts +320 -1
  121. package/extensions/lib/one-code-settings.ts +63 -0
  122. package/extensions/lib/original-command.ts +71 -0
  123. package/extensions/lib/package-version.ts +31 -0
  124. package/extensions/lib/paths.ts +39 -4
  125. package/extensions/lib/permission-gate.ts +192 -0
  126. package/extensions/lib/persisted-output.ts +18 -0
  127. package/extensions/lib/pi-ai-estimate.ts +160 -0
  128. package/extensions/lib/pi-version.ts +1 -1
  129. package/extensions/lib/plan-mode-channels.ts +11 -0
  130. package/extensions/lib/plugin-overrides.ts +24 -0
  131. package/extensions/lib/plugin-root.ts +44 -0
  132. package/extensions/lib/plugins.ts +233 -54
  133. package/extensions/lib/prefix-warm-gate.ts +157 -0
  134. package/extensions/lib/process-tree.ts +56 -0
  135. package/extensions/lib/reminders.ts +321 -68
  136. package/extensions/lib/scratchpad.ts +11 -8
  137. package/extensions/lib/session-lifecycle.ts +28 -0
  138. package/extensions/lib/settings-channels.ts +14 -0
  139. package/extensions/lib/shell-tasks.ts +54 -0
  140. package/extensions/lib/skill-overrides.ts +86 -0
  141. package/extensions/lib/skill-scan.ts +110 -0
  142. package/extensions/lib/text-width.ts +267 -0
  143. package/extensions/lib/tui-render.ts +281 -54
  144. package/extensions/lib/usage-bus.ts +75 -0
  145. package/extensions/lib/usage-tracker.ts +67 -0
  146. package/extensions/lib/worktree-isolation.ts +170 -0
  147. package/extensions/lsp/client.ts +69 -12
  148. package/extensions/lsp/format.ts +12 -2
  149. package/extensions/lsp/index.ts +273 -84
  150. package/extensions/lsp/install-hints.ts +43 -0
  151. package/extensions/lsp/keep-alive.ts +19 -0
  152. package/extensions/lsp/plugin-servers.ts +331 -0
  153. package/extensions/lsp/watcher.ts +157 -0
  154. package/extensions/mcp/announce.ts +99 -0
  155. package/extensions/mcp/client.ts +181 -22
  156. package/extensions/mcp/config.ts +55 -24
  157. package/extensions/mcp/index.ts +586 -86
  158. package/extensions/mcp/oauth/browser.ts +26 -0
  159. package/extensions/mcp/oauth/callback.ts +104 -0
  160. package/extensions/mcp/oauth/flow.ts +68 -0
  161. package/extensions/mcp/oauth/provider.ts +147 -0
  162. package/extensions/mcp/oauth/store.ts +86 -0
  163. package/extensions/mcp/panel/keys.ts +34 -0
  164. package/extensions/mcp/panel/model.ts +62 -0
  165. package/extensions/mcp/panel/render.ts +141 -0
  166. package/extensions/mcp/panel/state.ts +108 -0
  167. package/extensions/mcp/schema.ts +83 -6
  168. package/extensions/mcp/trust.ts +340 -0
  169. package/extensions/memory/entries.ts +163 -0
  170. package/extensions/memory/index.ts +119 -2
  171. package/extensions/memory/open-external.ts +97 -0
  172. package/extensions/memory/panel.ts +124 -0
  173. package/extensions/model-default/index.ts +74 -0
  174. package/extensions/notebook/index.ts +36 -18
  175. package/extensions/notebook/notebook.ts +110 -17
  176. package/extensions/permissions/index.ts +1012 -247
  177. package/extensions/permissions/matcher.ts +488 -57
  178. package/extensions/permissions/modes.ts +55 -7
  179. package/extensions/permissions/project-trust.ts +81 -0
  180. package/extensions/permissions/protected-paths.ts +16 -3
  181. package/extensions/permissions/rule-prose.ts +79 -0
  182. package/extensions/permissions/session-grant.ts +91 -0
  183. package/extensions/permissions/settings.ts +112 -33
  184. package/extensions/permissions/subagent-gate.ts +57 -0
  185. package/extensions/plan-mode/index.ts +143 -33
  186. package/extensions/plan-mode/reminder.ts +16 -19
  187. package/extensions/plan-mode/viewer.ts +40 -31
  188. package/extensions/plugins/counts.ts +68 -0
  189. package/extensions/plugins/index.ts +496 -32
  190. package/extensions/plugins/install/install.ts +167 -0
  191. package/extensions/plugins/install/paths.ts +23 -0
  192. package/extensions/plugins/install/registry.ts +72 -0
  193. package/extensions/plugins/marketplace/git.ts +103 -0
  194. package/extensions/plugins/marketplace/official.ts +36 -0
  195. package/extensions/plugins/marketplace/parse.ts +94 -0
  196. package/extensions/plugins/marketplace/registry.ts +60 -0
  197. package/extensions/plugins/marketplace/sync.ts +146 -0
  198. package/extensions/plugins/marketplace/types.ts +128 -0
  199. package/extensions/plugins/panel/keys.ts +52 -0
  200. package/extensions/plugins/panel/render.ts +315 -0
  201. package/extensions/plugins/panel/rows.ts +230 -0
  202. package/extensions/plugins/panel/state.ts +333 -0
  203. package/extensions/recap/index.ts +215 -0
  204. package/extensions/recap/prompt.ts +44 -0
  205. package/extensions/recap/scheduler.ts +87 -0
  206. package/extensions/search-tools/index.ts +0 -0
  207. package/extensions/search-tools/tools.ts +7 -5
  208. package/extensions/skill/index.ts +360 -34
  209. package/extensions/skill/invoke.ts +185 -0
  210. package/extensions/skill/listing.ts +21 -0
  211. package/extensions/skill/panel/keys.ts +41 -0
  212. package/extensions/skill/panel/render.ts +91 -0
  213. package/extensions/skill/panel/state.ts +115 -0
  214. package/extensions/spinner/index.ts +110 -0
  215. package/extensions/spinner/line.ts +51 -0
  216. package/extensions/spinner/verbs.ts +201 -0
  217. package/extensions/subagents/agents.ts +88 -44
  218. package/extensions/subagents/default-model.ts +62 -32
  219. package/extensions/subagents/delegation-steer.ts +23 -0
  220. package/extensions/subagents/hand-back-review.ts +67 -0
  221. package/extensions/subagents/index.ts +1545 -346
  222. package/extensions/subagents/live-runs.ts +293 -0
  223. package/extensions/subagents/model-select.ts +252 -82
  224. package/extensions/subagents/outcome.ts +90 -0
  225. package/extensions/subagents/panel-keys.ts +41 -0
  226. package/extensions/subagents/panel-render.ts +357 -0
  227. package/extensions/subagents/panel-widget.ts +330 -0
  228. package/extensions/subagents/pending-claim.ts +45 -0
  229. package/extensions/subagents/prose.ts +105 -0
  230. package/extensions/subagents/runner.ts +674 -0
  231. package/extensions/subagents/runs.ts +68 -1
  232. package/extensions/subagents/send-to-main-tool.ts +55 -0
  233. package/extensions/subagents/session-turns.ts +138 -0
  234. package/extensions/subagents/shell-panel.ts +198 -0
  235. package/extensions/subagents/usage.ts +16 -4
  236. package/extensions/subagents/worktree.ts +13 -0
  237. package/extensions/system-prompt/environment.ts +5 -3
  238. package/extensions/system-prompt/git-status.ts +93 -0
  239. package/extensions/system-prompt/index.ts +23 -1
  240. package/extensions/system-prompt/template.ts +34 -4
  241. package/extensions/system-prompt/tiers/common.ts +31 -6
  242. package/extensions/system-prompt/tiers/low.ts +13 -3
  243. package/extensions/system-prompt/tiers/mid.ts +21 -8
  244. package/extensions/system-reminder/index.ts +53 -9
  245. package/extensions/tasks/index.ts +65 -16
  246. package/extensions/tasks/store.ts +76 -3
  247. package/extensions/tool-search/announce.ts +42 -0
  248. package/extensions/tool-search/index.ts +136 -13
  249. package/extensions/turn-duration/index.ts +60 -0
  250. package/extensions/turn-duration/line.ts +22 -0
  251. package/extensions/turn-duration/span.ts +41 -0
  252. package/extensions/turn-duration/verbs.ts +24 -0
  253. package/extensions/web/backends.ts +421 -0
  254. package/extensions/web/index.ts +178 -21
  255. package/extensions/web-fetch/index.ts +74 -18
  256. package/extensions/web-fetch/summarize.ts +1 -1
  257. package/extensions/workflow/agent-session.ts +182 -112
  258. package/extensions/workflow/globals.ts +129 -106
  259. package/extensions/workflow/index.ts +343 -28
  260. package/extensions/workflow/records.ts +118 -0
  261. package/extensions/workflow/run-manager.ts +80 -31
  262. package/extensions/workflow/saved-workflows.ts +2 -1
  263. package/extensions/workflow/script-worker.mjs +110 -0
  264. package/extensions/workflow/types.ts +74 -8
  265. package/extensions/workflow/viewer.ts +562 -0
  266. package/extensions/workflow/vm-runtime.ts +118 -22
  267. package/extensions/workflow/widget.ts +126 -18
  268. package/extensions/worktree/guards.ts +240 -0
  269. package/extensions/worktree/index.ts +30 -5
  270. package/extensions/worktree/rewrite.ts +32 -16
  271. package/package.json +20 -10
  272. package/skills/code-review/SKILL.md +165 -0
  273. package/skills/fewer-permission-prompts/SKILL.md +61 -0
  274. package/skills/security-review/SKILL.md +178 -0
  275. package/skills/simplify/SKILL.md +70 -0
  276. package/themes/{one-code-light.json → onecode-light.json} +6 -1
  277. package/themes/{one-code.json → onecode.json} +6 -1
  278. package/types/app.d.ts +8 -1
  279. package/types/community-packages.d.ts +45 -2
  280. package/docs/decisions/auto-mode.md +0 -526
  281. package/docs/decisions/branding.md +0 -127
  282. package/docs/decisions/code-review-remediation.md +0 -106
  283. package/docs/decisions/compaction.md +0 -140
  284. package/docs/decisions/distribution.md +0 -168
  285. package/docs/decisions/hooks.md +0 -64
  286. package/docs/decisions/lsp.md +0 -58
  287. package/docs/decisions/mcp.md +0 -104
  288. package/docs/decisions/memory-state.md +0 -170
  289. package/docs/decisions/model-policy.md +0 -126
  290. package/docs/decisions/modes.md +0 -178
  291. package/docs/decisions/skills-plugins.md +0 -56
  292. package/docs/decisions/subagents-workflows.md +0 -225
  293. package/docs/decisions/system-prompt.md +0 -98
  294. package/docs/decisions/tools.md +0 -407
  295. package/docs/decisions/tui.md +0 -202
  296. package/docs/decisions.md +0 -148
  297. package/extensions/subagents/child.ts +0 -414
  298. package/extensions/subagents/rpc-turns.ts +0 -123
  299. package/extensions/todo/index.ts +0 -133
  300. package/extensions/workflow/permission-gate.ts +0 -71
@@ -1,28 +1,38 @@
1
1
  /**
2
- * Script-visible globals (pure, dependency-injected). Everything the vm
3
- * script can touch — agent/parallel/pipeline/phase/log/console/args/budget —
4
- * is built here around one injected AgentCallFn, so the whole orchestration
5
- * semantics (concurrency, caps, null-on-failure, replay) is unit-testable
6
- * with a fake agent runner.
2
+ * Host-side script globals (pure, dependency-injected). The script itself runs
3
+ * in a worker thread (`script-worker.mjs`, launched by `vm-runtime.ts`); what
4
+ * reaches the host over RPC is built here around one injected AgentCallFn —
5
+ * agent()/workflow() calls, phase/log notices, args and the budget snapshot —
6
+ * so the orchestration semantics that matter for replay and safety
7
+ * (concurrency, caps, budget admission, null-on-failure, journal replay) are
8
+ * unit-testable with a fake agent runner. parallel()/pipeline()/console live
9
+ * in the worker: they take callbacks, which cannot cross the RPC boundary.
7
10
  *
8
11
  * Claude Code semantics implemented here:
9
12
  * - agent() resolves null when the underlying agent fails; caps/budget/abort
10
13
  * violations throw (they must unwind the script).
11
- * - parallel() takes thunks, is a barrier, and maps a throwing thunk to null.
12
- * - pipeline() has no barrier between stages; stage callbacks receive
13
- * (prev, originalItem, index); a throwing stage drops that item to null.
14
14
  * - callIndex is assigned synchronously at invocation time, which is what
15
15
  * makes journal replay deterministic under parallel().
16
16
  */
17
17
 
18
18
  import { hashAgentCall } from "./journal.ts";
19
- import type { AgentCallFn, AgentCallOptions, BudgetSnapshot, JournalEntry, RunProgressEvent } from "./types.ts";
19
+ import { previewValue } from "./records.ts";
20
+ import type {
21
+ AgentCallFn,
22
+ AgentCallOptions,
23
+ AgentRunUpdate,
24
+ BudgetSnapshot,
25
+ JournalEntry,
26
+ RunProgressEvent,
27
+ } from "./types.ts";
20
28
  import { WorkflowScriptError } from "./types.ts";
21
29
  import type { ReplayCursor } from "./journal.ts";
22
30
 
23
31
  export const MAX_AGENTS_PER_RUN = 1000;
24
32
  export const MAX_ITEMS_PER_CALL = 4096;
25
33
  export const MAX_CONCURRENCY = 16;
34
+ /** log()/phase() notices a script may emit between two worker heartbeats (25 ms) before it is judged a flood. */
35
+ export const MAX_NOTICES_PER_TICK = 10_000;
26
36
 
27
37
  /** Promise-queue limiter: at most `limit` callbacks in flight, FIFO overflow. */
28
38
  export function createLimiter(limit: number): <T>(fn: () => Promise<T>) => Promise<T> {
@@ -43,13 +53,61 @@ export function createLimiter(limit: number): <T>(fn: () => Promise<T>) => Promi
43
53
  };
44
54
  }
45
55
 
46
- export interface ScriptGlobalsOptions {
47
- agentCall: AgentCallFn;
48
- args: unknown;
49
- /** null = no budget ceiling. */
56
+ /**
57
+ * One run tree's admission controls and totals: the budget snapshot, the
58
+ * concurrency limiter, the agent cap and the counters they read. Created ONCE
59
+ * per run (`createRunAdmission`) and shared by every nested workflow(), so a
60
+ * child's agents queue behind the same limiter, spend the same budget and
61
+ * count against the same cap as the root's — a per-child copy taken at spawn
62
+ * could not see the parent's later spend and let a tree exceed the run's
63
+ * concurrency (review S9).
64
+ */
65
+ export interface RunAdmission {
66
+ budget: BudgetSnapshot;
67
+ limit: ReturnType<typeof createLimiter>;
68
+ maxAgents: number;
69
+ agentCount: () => number;
70
+ outputTokens: () => number;
71
+ cost: () => number;
72
+ account: (delta: { agents?: number; outputTokens?: number; cost?: number }) => void;
73
+ }
74
+
75
+ export interface RunAdmissionOptions {
76
+ /** null = no budget target. */
50
77
  budgetTotal: number | null;
51
78
  concurrency: number;
52
79
  maxAgents?: number;
80
+ }
81
+
82
+ export function createRunAdmission(options: RunAdmissionOptions): RunAdmission {
83
+ const total = options.budgetTotal;
84
+ let agents = 0;
85
+ let outputTokens = 0;
86
+ let cost = 0;
87
+ return {
88
+ budget: Object.freeze({
89
+ total,
90
+ spent: () => outputTokens,
91
+ remaining: () => (total === null ? Number.POSITIVE_INFINITY : Math.max(0, total - outputTokens)),
92
+ }),
93
+ limit: createLimiter(Math.max(1, Math.min(options.concurrency, MAX_CONCURRENCY))),
94
+ maxAgents: options.maxAgents ?? MAX_AGENTS_PER_RUN,
95
+ agentCount: () => agents,
96
+ outputTokens: () => outputTokens,
97
+ cost: () => cost,
98
+ account: (delta) => {
99
+ agents += delta.agents ?? 0;
100
+ outputTokens += delta.outputTokens ?? 0;
101
+ cost += delta.cost ?? 0;
102
+ },
103
+ };
104
+ }
105
+
106
+ export interface ScriptGlobalsOptions {
107
+ agentCall: AgentCallFn;
108
+ args: unknown;
109
+ /** The run tree's shared controls — the root's own, or inherited by a nested workflow(). */
110
+ admission: RunAdmission;
53
111
  signal: AbortSignal;
54
112
  onEvent: (event: RunProgressEvent) => void;
55
113
  onJournal?: (entry: JournalEntry) => void;
@@ -62,17 +120,16 @@ export interface ScriptGlobalsOptions {
62
120
 
63
121
  export interface ScriptGlobals {
64
122
  agent: (prompt: string, opts?: AgentCallOptions) => Promise<unknown>;
65
- parallel: (thunks: Array<() => Promise<unknown>>) => Promise<unknown[]>;
66
- pipeline: (items: unknown[], ...stages: Array<(prev: unknown, item: unknown, index: number) => unknown>) => Promise<unknown[]>;
67
123
  workflow: (nameOrRef: unknown, childArgs?: unknown) => Promise<unknown>;
68
124
  phase: (title: string) => void;
69
125
  log: (message: unknown) => void;
70
- console: { log: (m: unknown) => void; info: (m: unknown) => void; warn: (m: unknown) => void; error: (m: unknown) => void };
71
126
  args: unknown;
72
127
  budget: BudgetSnapshot;
73
128
  }
74
129
 
130
+ /** What the run manager reads back: tree-wide totals plus this script's phase. */
75
131
  export interface ScriptRunState {
132
+ admission: RunAdmission;
76
133
  agentCount: () => number;
77
134
  outputTokens: () => number;
78
135
  cost: () => number;
@@ -80,25 +137,21 @@ export interface ScriptRunState {
80
137
  }
81
138
 
82
139
  export function createScriptGlobals(options: ScriptGlobalsOptions): { globals: ScriptGlobals; state: ScriptRunState } {
83
- const maxAgents = options.maxAgents ?? MAX_AGENTS_PER_RUN;
84
- const limiter = createLimiter(Math.max(1, Math.min(options.concurrency, MAX_CONCURRENCY)));
140
+ const { admission } = options;
141
+ const { budget, limit: limiter } = admission;
85
142
  const now = options.now ?? (() => Date.now());
86
143
 
87
144
  let callSeq = 0;
88
- let agentCount = 0;
89
- let outputTokens = 0;
90
- let cost = 0;
91
145
  let currentPhase: string | undefined;
92
146
 
93
- const budget: BudgetSnapshot = Object.freeze({
94
- total: options.budgetTotal,
95
- spent: () => outputTokens,
96
- remaining: () => (options.budgetTotal === null ? Number.POSITIVE_INFINITY : Math.max(0, options.budgetTotal - outputTokens)),
97
- });
98
-
99
147
  const throwIfAborted = () => {
100
148
  if (options.signal.aborted) throw new WorkflowScriptError("Workflow run was aborted");
101
149
  };
150
+ const checkBudget = () => {
151
+ if (budget.total !== null && budget.remaining() <= 0) {
152
+ throw new WorkflowScriptError(`Workflow token budget exhausted (${budget.total} output tokens)`);
153
+ }
154
+ };
102
155
 
103
156
  const agent = async (prompt: string, opts: AgentCallOptions = {}): Promise<unknown> => {
104
157
  if (typeof prompt !== "string" || !prompt.trim()) {
@@ -108,41 +161,70 @@ export function createScriptGlobals(options: ScriptGlobalsOptions): { globals: S
108
161
  throw new WorkflowScriptError("agent() schema must be a JSON Schema object");
109
162
  }
110
163
  throwIfAborted();
111
- if (agentCount >= maxAgents) {
112
- throw new WorkflowScriptError(`Workflow agent limit reached (${maxAgents} agents per run)`);
113
- }
114
- if (budget.total !== null && budget.remaining() <= 0) {
115
- throw new WorkflowScriptError(`Workflow token budget exhausted (${budget.total} output tokens)`);
164
+ if (admission.agentCount() >= admission.maxAgents) {
165
+ throw new WorkflowScriptError(`Workflow agent limit reached (${admission.maxAgents} agents per run)`);
116
166
  }
167
+ checkBudget();
117
168
 
118
169
  // Everything up to here — and the index/hash/phase capture — is
119
170
  // synchronous, so invocation order fully determines replay identity.
120
171
  const callIndex = callSeq++;
121
- agentCount++;
172
+ admission.account({ agents: 1 });
122
173
  const hash = hashAgentCall(prompt, opts);
123
174
  const phase = opts.phase ?? currentPhase;
124
175
  const label = opts.label ?? `agent ${callIndex + 1}`;
125
176
 
126
177
  const replayed = options.replay?.match(callIndex, hash);
127
178
  if (replayed !== undefined) {
128
- outputTokens += replayed.tokens?.output ?? 0;
129
- cost += replayed.cost ?? 0;
130
- options.onEvent({ type: "agentEnd", callIndex, label, phase, tokens: replayed.tokens, replayed: true });
179
+ admission.account({ outputTokens: replayed.tokens?.output ?? 0, cost: replayed.cost ?? 0 });
180
+ options.onEvent({
181
+ type: "agentEnd",
182
+ callIndex,
183
+ label,
184
+ phase,
185
+ tokens: replayed.tokens,
186
+ replayed: true,
187
+ prompt,
188
+ preview: previewValue(replayed.value),
189
+ });
131
190
  return replayed.value;
132
191
  }
133
192
 
134
193
  return limiter(async () => {
135
- throwIfAborted();
136
- options.onEvent({ type: "agentStart", callIndex, label, phase });
194
+ // Queued calls were admitted before earlier agents reported usage.
195
+ // Recheck here, before any provider work or agentStart event — and
196
+ // give the admission slot back, since this agent never runs.
197
+ try {
198
+ throwIfAborted();
199
+ checkBudget();
200
+ } catch (error) {
201
+ admission.account({ agents: -1 });
202
+ throw error;
203
+ }
204
+ options.onEvent({ type: "agentStart", callIndex, label, phase, prompt });
137
205
  try {
138
- const result = await options.agentCall(prompt, opts);
139
- outputTokens += result.tokens.output;
140
- cost += result.cost;
206
+ const onUpdate = (update: AgentRunUpdate) =>
207
+ options.onEvent({ type: "agentUpdate", callIndex, label, phase, ...update });
208
+ const result = await options.agentCall(prompt, opts, onUpdate);
209
+ admission.account({ outputTokens: result.tokens.output, cost: result.cost });
141
210
  options.onJournal?.({ callIndex, hash, result, timestamp: now() });
142
- options.onEvent({ type: "agentEnd", callIndex, label, phase, tokens: result.tokens, cost: result.cost });
211
+ options.onEvent({
212
+ type: "agentEnd",
213
+ callIndex,
214
+ label,
215
+ phase,
216
+ tokens: result.tokens,
217
+ cost: result.cost,
218
+ preview: previewValue(result.value),
219
+ });
143
220
  return result.value;
144
221
  } catch (error) {
145
- throwIfAborted();
222
+ if (options.signal.aborted) {
223
+ // Close the record: the viewer would otherwise show a completed or
224
+ // stopped run with this agent still "running".
225
+ options.onEvent({ type: "agentEnd", callIndex, label, phase, text: "aborted" });
226
+ throw new WorkflowScriptError("Workflow run was aborted");
227
+ }
146
228
  // A script-authoring mistake (bad agentType/model, worktree without a
147
229
  // git repo, malformed schema) must unwind the script, not vanish into
148
230
  // a null the way a genuine agent failure does — same rule parallel()
@@ -157,58 +239,6 @@ export function createScriptGlobals(options: ScriptGlobalsOptions): { globals: S
157
239
  });
158
240
  };
159
241
 
160
- const parallel = async (thunks: Array<() => Promise<unknown>>): Promise<unknown[]> => {
161
- if (!Array.isArray(thunks)) throw new WorkflowScriptError("parallel() takes an array of functions");
162
- if (thunks.length > MAX_ITEMS_PER_CALL) {
163
- throw new WorkflowScriptError(`parallel() accepts at most ${MAX_ITEMS_PER_CALL} items (got ${thunks.length})`);
164
- }
165
- // Invoke every thunk synchronously first so agent() callIndexes are
166
- // assigned in array order regardless of completion timing.
167
- const promises = thunks.map((thunk) => {
168
- if (typeof thunk !== "function") {
169
- return Promise.reject(new WorkflowScriptError("parallel() items must be functions returning promises"));
170
- }
171
- try {
172
- return Promise.resolve(thunk());
173
- } catch (error) {
174
- return Promise.reject(error);
175
- }
176
- });
177
- const settled = await Promise.allSettled(promises);
178
- return settled.map((s) => {
179
- if (s.status === "fulfilled") return s.value;
180
- if (s.reason instanceof WorkflowScriptError) throw s.reason;
181
- return null;
182
- });
183
- };
184
-
185
- const pipeline = async (
186
- items: unknown[],
187
- ...stages: Array<(prev: unknown, item: unknown, index: number) => unknown>
188
- ): Promise<unknown[]> => {
189
- if (!Array.isArray(items)) throw new WorkflowScriptError("pipeline() takes an array of items");
190
- if (items.length > MAX_ITEMS_PER_CALL) {
191
- throw new WorkflowScriptError(`pipeline() accepts at most ${MAX_ITEMS_PER_CALL} items (got ${items.length})`);
192
- }
193
- if (stages.some((s) => typeof s !== "function")) {
194
- throw new WorkflowScriptError("pipeline() stages must be functions");
195
- }
196
- return Promise.all(
197
- items.map(async (item, index) => {
198
- let prev: unknown = item;
199
- for (const stage of stages) {
200
- try {
201
- prev = await stage(prev, item, index);
202
- } catch (error) {
203
- if (error instanceof WorkflowScriptError) throw error;
204
- return null;
205
- }
206
- }
207
- return prev;
208
- }),
209
- );
210
- };
211
-
212
242
  const workflowFn = async (nameOrRef: unknown, childArgs?: unknown): Promise<unknown> => {
213
243
  if (!options.runWorkflow) {
214
244
  throw new WorkflowScriptError("workflow() nesting is one level only — this run cannot start another child workflow");
@@ -222,28 +252,21 @@ export function createScriptGlobals(options: ScriptGlobalsOptions): { globals: S
222
252
 
223
253
  const globals: ScriptGlobals = {
224
254
  agent,
225
- parallel,
226
- pipeline,
227
255
  workflow: workflowFn,
228
256
  phase: (title: string) => {
229
257
  currentPhase = String(title);
230
258
  options.onEvent({ type: "phase", phase: currentPhase });
231
259
  },
232
260
  log,
233
- console: {
234
- log,
235
- info: log,
236
- warn: (m) => log(`[warn] ${String(m)}`),
237
- error: (m) => log(`[error] ${String(m)}`),
238
- },
239
261
  args: options.args,
240
262
  budget,
241
263
  };
242
264
 
243
265
  const state: ScriptRunState = {
244
- agentCount: () => agentCount,
245
- outputTokens: () => outputTokens,
246
- cost: () => cost,
266
+ admission,
267
+ agentCount: admission.agentCount,
268
+ outputTokens: admission.outputTokens,
269
+ cost: admission.cost,
247
270
  currentPhase: () => currentPhase,
248
271
  };
249
272