@twelvehart/orcats 0.0.0-seed.0 → 0.2.3

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 (275) hide show
  1. package/LICENSE +48 -0
  2. package/NOTICE +5 -0
  3. package/README.md +399 -0
  4. package/bin/orcats +4 -0
  5. package/dist/backends/acp-client.d.ts +74 -0
  6. package/dist/backends/acp-client.d.ts.map +1 -0
  7. package/dist/backends/acp-run.d.ts +31 -0
  8. package/dist/backends/acp-run.d.ts.map +1 -0
  9. package/dist/backends/claude-run.d.ts +35 -0
  10. package/dist/backends/claude-run.d.ts.map +1 -0
  11. package/dist/backends/claude-stream-json.d.ts +15 -0
  12. package/dist/backends/claude-stream-json.d.ts.map +1 -0
  13. package/dist/backends/codex-jsonl.d.ts +34 -0
  14. package/dist/backends/codex-jsonl.d.ts.map +1 -0
  15. package/dist/backends/codex-run.d.ts +40 -0
  16. package/dist/backends/codex-run.d.ts.map +1 -0
  17. package/dist/backends/codex.d.ts +5 -0
  18. package/dist/backends/codex.d.ts.map +1 -0
  19. package/dist/backends/conversation-config.d.ts +12 -0
  20. package/dist/backends/conversation-config.d.ts.map +1 -0
  21. package/dist/backends/index.d.ts +14 -0
  22. package/dist/backends/index.d.ts.map +1 -0
  23. package/dist/backends/opencode-run.d.ts +48 -0
  24. package/dist/backends/opencode-run.d.ts.map +1 -0
  25. package/dist/backends/opencode-sse.d.ts +35 -0
  26. package/dist/backends/opencode-sse.d.ts.map +1 -0
  27. package/dist/backends/pi-rpc.d.ts +21 -0
  28. package/dist/backends/pi-rpc.d.ts.map +1 -0
  29. package/dist/backends/pi-run.d.ts +21 -0
  30. package/dist/backends/pi-run.d.ts.map +1 -0
  31. package/dist/backends/select.d.ts +17 -0
  32. package/dist/backends/select.d.ts.map +1 -0
  33. package/dist/backends/subprocess-run.d.ts +68 -0
  34. package/dist/backends/subprocess-run.d.ts.map +1 -0
  35. package/dist/backends/types.d.ts +17 -0
  36. package/dist/backends/types.d.ts.map +1 -0
  37. package/dist/backends/unsupported.d.ts +9 -0
  38. package/dist/backends/unsupported.d.ts.map +1 -0
  39. package/dist/baseline/index.d.ts +56 -0
  40. package/dist/baseline/index.d.ts.map +1 -0
  41. package/dist/cli/args.d.ts +22 -0
  42. package/dist/cli/args.d.ts.map +1 -0
  43. package/dist/cli/embedded.d.ts +7 -0
  44. package/dist/cli/embedded.d.ts.map +1 -0
  45. package/dist/cli/main.d.ts +6 -0
  46. package/dist/cli/main.d.ts.map +1 -0
  47. package/dist/cli/version.d.ts +2 -0
  48. package/dist/cli/version.d.ts.map +1 -0
  49. package/dist/conversation/ask-user.d.ts +17 -0
  50. package/dist/conversation/ask-user.d.ts.map +1 -0
  51. package/dist/conversation/conversation.d.ts +46 -0
  52. package/dist/conversation/conversation.d.ts.map +1 -0
  53. package/dist/conversation/harness.d.ts +14 -0
  54. package/dist/conversation/harness.d.ts.map +1 -0
  55. package/dist/conversation/index.d.ts +5 -0
  56. package/dist/conversation/index.d.ts.map +1 -0
  57. package/dist/conversation/queue.d.ts +15 -0
  58. package/dist/conversation/queue.d.ts.map +1 -0
  59. package/dist/flow/accessors.d.ts +13 -0
  60. package/dist/flow/accessors.d.ts.map +1 -0
  61. package/dist/flow/approvals.d.ts +6 -0
  62. package/dist/flow/approvals.d.ts.map +1 -0
  63. package/dist/flow/args.d.ts +4 -0
  64. package/dist/flow/args.d.ts.map +1 -0
  65. package/dist/flow/context.d.ts +36 -0
  66. package/dist/flow/context.d.ts.map +1 -0
  67. package/dist/flow/index.d.ts +5 -0
  68. package/dist/flow/index.d.ts.map +1 -0
  69. package/dist/index.d.ts +16 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/loop/builder/index.d.ts +17 -0
  72. package/dist/loop/builder/index.d.ts.map +1 -0
  73. package/dist/loop/builder/types.d.ts +97 -0
  74. package/dist/loop/builder/types.d.ts.map +1 -0
  75. package/dist/loop/context/compaction.d.ts +38 -0
  76. package/dist/loop/context/compaction.d.ts.map +1 -0
  77. package/dist/loop/context/index.d.ts +4 -0
  78. package/dist/loop/context/index.d.ts.map +1 -0
  79. package/dist/loop/context/offload.d.ts +34 -0
  80. package/dist/loop/context/offload.d.ts.map +1 -0
  81. package/dist/loop/context/types.d.ts +13 -0
  82. package/dist/loop/context/types.d.ts.map +1 -0
  83. package/dist/loop/engine/bridge.d.ts +21 -0
  84. package/dist/loop/engine/bridge.d.ts.map +1 -0
  85. package/dist/loop/engine/index.d.ts +73 -0
  86. package/dist/loop/engine/index.d.ts.map +1 -0
  87. package/dist/loop/execution.d.ts +68 -0
  88. package/dist/loop/execution.d.ts.map +1 -0
  89. package/dist/loop/fanout.d.ts +148 -0
  90. package/dist/loop/fanout.d.ts.map +1 -0
  91. package/dist/loop/firing.d.ts +54 -0
  92. package/dist/loop/firing.d.ts.map +1 -0
  93. package/dist/loop/graph/analyze.d.ts +8 -0
  94. package/dist/loop/graph/analyze.d.ts.map +1 -0
  95. package/dist/loop/graph/index.d.ts +3 -0
  96. package/dist/loop/graph/index.d.ts.map +1 -0
  97. package/dist/loop/graph/types.d.ts +35 -0
  98. package/dist/loop/graph/types.d.ts.map +1 -0
  99. package/dist/loop/index.d.ts +7 -0
  100. package/dist/loop/index.d.ts.map +1 -0
  101. package/dist/loop/io/index.d.ts +4 -0
  102. package/dist/loop/io/index.d.ts.map +1 -0
  103. package/dist/loop/io/linear.d.ts +142 -0
  104. package/dist/loop/io/linear.d.ts.map +1 -0
  105. package/dist/loop/io/sink.d.ts +47 -0
  106. package/dist/loop/io/sink.d.ts.map +1 -0
  107. package/dist/loop/io/source.d.ts +64 -0
  108. package/dist/loop/io/source.d.ts.map +1 -0
  109. package/dist/loop/serve.d.ts +97 -0
  110. package/dist/loop/serve.d.ts.map +1 -0
  111. package/dist/loop/state/index.d.ts +5 -0
  112. package/dist/loop/state/index.d.ts.map +1 -0
  113. package/dist/loop/state/manifest.d.ts +36 -0
  114. package/dist/loop/state/manifest.d.ts.map +1 -0
  115. package/dist/loop/state/port.d.ts +33 -0
  116. package/dist/loop/state/port.d.ts.map +1 -0
  117. package/dist/loop/state/snapshot.d.ts +15 -0
  118. package/dist/loop/state/snapshot.d.ts.map +1 -0
  119. package/dist/loop/state/sqlite.d.ts +31 -0
  120. package/dist/loop/state/sqlite.d.ts.map +1 -0
  121. package/dist/loop/termination-contract.d.ts +28 -0
  122. package/dist/loop/termination-contract.d.ts.map +1 -0
  123. package/dist/loop/termination.d.ts +33 -0
  124. package/dist/loop/termination.d.ts.map +1 -0
  125. package/dist/model/backend-config.d.ts +30 -0
  126. package/dist/model/backend-config.d.ts.map +1 -0
  127. package/dist/model/brand.d.ts +9 -0
  128. package/dist/model/brand.d.ts.map +1 -0
  129. package/dist/model/errors.d.ts +11 -0
  130. package/dist/model/errors.d.ts.map +1 -0
  131. package/dist/model/index.d.ts +8 -0
  132. package/dist/model/index.d.ts.map +1 -0
  133. package/dist/model/json-schema.d.ts +4 -0
  134. package/dist/model/json-schema.d.ts.map +1 -0
  135. package/dist/model/result.d.ts +3 -0
  136. package/dist/model/result.d.ts.map +1 -0
  137. package/dist/model/schemas.d.ts +435 -0
  138. package/dist/model/schemas.d.ts.map +1 -0
  139. package/dist/model/structured.d.ts +9 -0
  140. package/dist/model/structured.d.ts.map +1 -0
  141. package/dist/monitor/index.d.ts +139 -0
  142. package/dist/monitor/index.d.ts.map +1 -0
  143. package/dist/plan/index.d.ts +3 -0
  144. package/dist/plan/index.d.ts.map +1 -0
  145. package/dist/plan/persistent.d.ts +23 -0
  146. package/dist/plan/persistent.d.ts.map +1 -0
  147. package/dist/plan/verdict.d.ts +9 -0
  148. package/dist/plan/verdict.d.ts.map +1 -0
  149. package/dist/review/index.d.ts +4 -0
  150. package/dist/review/index.d.ts.map +1 -0
  151. package/dist/review/loop.d.ts +117 -0
  152. package/dist/review/loop.d.ts.map +1 -0
  153. package/dist/review/reviewers.d.ts +13 -0
  154. package/dist/review/reviewers.d.ts.map +1 -0
  155. package/dist/review/tool.d.ts +20 -0
  156. package/dist/review/tool.d.ts.map +1 -0
  157. package/dist/run-output/index.d.ts +112 -0
  158. package/dist/run-output/index.d.ts.map +1 -0
  159. package/dist/runner/index.d.ts +2 -0
  160. package/dist/runner/index.d.ts.map +1 -0
  161. package/dist/runner/typecheck.d.ts +23 -0
  162. package/dist/runner/typecheck.d.ts.map +1 -0
  163. package/dist/test-utils/fakes.d.ts +76 -0
  164. package/dist/test-utils/fakes.d.ts.map +1 -0
  165. package/dist/test-utils/fixtures.d.ts +4 -0
  166. package/dist/test-utils/fixtures.d.ts.map +1 -0
  167. package/dist/test-utils/index.d.ts +4 -0
  168. package/dist/test-utils/index.d.ts.map +1 -0
  169. package/dist/test-utils/tier2.d.ts +8 -0
  170. package/dist/test-utils/tier2.d.ts.map +1 -0
  171. package/dist/tools/fs.d.ts +11 -0
  172. package/dist/tools/fs.d.ts.map +1 -0
  173. package/dist/tools/git.d.ts +10 -0
  174. package/dist/tools/git.d.ts.map +1 -0
  175. package/dist/tools/github.d.ts +13 -0
  176. package/dist/tools/github.d.ts.map +1 -0
  177. package/dist/tools/index.d.ts +7 -0
  178. package/dist/tools/index.d.ts.map +1 -0
  179. package/dist/tools/linear.d.ts +117 -0
  180. package/dist/tools/linear.d.ts.map +1 -0
  181. package/dist/tools/process.d.ts +40 -0
  182. package/dist/tools/process.d.ts.map +1 -0
  183. package/dist/tools/terminal.d.ts +23 -0
  184. package/dist/tools/terminal.d.ts.map +1 -0
  185. package/package.json +89 -9
  186. package/src/backends/acp-client.ts +324 -0
  187. package/src/backends/acp-run.ts +636 -0
  188. package/src/backends/claude-run.ts +226 -0
  189. package/src/backends/claude-stream-json.ts +273 -0
  190. package/src/backends/codex-jsonl.ts +339 -0
  191. package/src/backends/codex-run.ts +177 -0
  192. package/src/backends/codex.ts +84 -0
  193. package/src/backends/conversation-config.ts +26 -0
  194. package/src/backends/index.ts +13 -0
  195. package/src/backends/opencode-run.ts +432 -0
  196. package/src/backends/opencode-sse.ts +360 -0
  197. package/src/backends/pi-rpc.ts +242 -0
  198. package/src/backends/pi-run.ts +154 -0
  199. package/src/backends/select.ts +77 -0
  200. package/src/backends/subprocess-run.ts +305 -0
  201. package/src/backends/types.ts +29 -0
  202. package/src/backends/unsupported.ts +48 -0
  203. package/src/baseline/index.ts +437 -0
  204. package/src/cli/args.ts +109 -0
  205. package/src/cli/embedded.ts +206 -0
  206. package/src/cli/main.ts +332 -0
  207. package/src/cli/version.ts +1 -0
  208. package/src/conversation/ask-user.ts +105 -0
  209. package/src/conversation/conversation.ts +109 -0
  210. package/src/conversation/harness.ts +39 -0
  211. package/src/conversation/index.ts +4 -0
  212. package/src/conversation/queue.ts +86 -0
  213. package/src/flow/accessors.ts +43 -0
  214. package/src/flow/approvals.ts +10 -0
  215. package/src/flow/args.ts +26 -0
  216. package/src/flow/context.ts +106 -0
  217. package/src/flow/index.ts +4 -0
  218. package/src/index.ts +17 -0
  219. package/src/loop/builder/index.ts +346 -0
  220. package/src/loop/builder/types.ts +131 -0
  221. package/src/loop/context/compaction.ts +175 -0
  222. package/src/loop/context/index.ts +3 -0
  223. package/src/loop/context/offload.ts +79 -0
  224. package/src/loop/context/types.ts +17 -0
  225. package/src/loop/engine/bridge.ts +61 -0
  226. package/src/loop/engine/index.ts +166 -0
  227. package/src/loop/execution.ts +453 -0
  228. package/src/loop/fanout.ts +444 -0
  229. package/src/loop/firing.ts +188 -0
  230. package/src/loop/graph/analyze.ts +128 -0
  231. package/src/loop/graph/index.ts +2 -0
  232. package/src/loop/graph/types.ts +45 -0
  233. package/src/loop/index.ts +11 -0
  234. package/src/loop/io/index.ts +3 -0
  235. package/src/loop/io/linear.ts +714 -0
  236. package/src/loop/io/sink.ts +170 -0
  237. package/src/loop/io/source.ts +255 -0
  238. package/src/loop/serve.ts +307 -0
  239. package/src/loop/state/index.ts +4 -0
  240. package/src/loop/state/manifest.ts +56 -0
  241. package/src/loop/state/port.ts +43 -0
  242. package/src/loop/state/snapshot.ts +129 -0
  243. package/src/loop/state/sqlite.ts +265 -0
  244. package/src/loop/termination-contract.ts +89 -0
  245. package/src/loop/termination.ts +156 -0
  246. package/src/model/backend-config.ts +34 -0
  247. package/src/model/brand.ts +11 -0
  248. package/src/model/errors.ts +28 -0
  249. package/src/model/index.ts +7 -0
  250. package/src/model/json-schema.ts +14 -0
  251. package/src/model/result.ts +28 -0
  252. package/src/model/schemas.ts +218 -0
  253. package/src/model/structured.ts +26 -0
  254. package/src/monitor/index.ts +367 -0
  255. package/src/plan/index.ts +2 -0
  256. package/src/plan/persistent.ts +98 -0
  257. package/src/plan/verdict.ts +3 -0
  258. package/src/review/index.ts +3 -0
  259. package/src/review/loop.ts +365 -0
  260. package/src/review/reviewers.ts +92 -0
  261. package/src/review/tool.ts +41 -0
  262. package/src/run-output/index.ts +319 -0
  263. package/src/runner/index.ts +1 -0
  264. package/src/runner/typecheck.ts +72 -0
  265. package/src/test-utils/fakes.ts +260 -0
  266. package/src/test-utils/fixtures.ts +33 -0
  267. package/src/test-utils/index.ts +3 -0
  268. package/src/test-utils/tier2.ts +12 -0
  269. package/src/tools/fs.ts +44 -0
  270. package/src/tools/git.ts +34 -0
  271. package/src/tools/github.ts +25 -0
  272. package/src/tools/index.ts +6 -0
  273. package/src/tools/linear.ts +428 -0
  274. package/src/tools/process.ts +156 -0
  275. package/src/tools/terminal.ts +116 -0
@@ -0,0 +1,109 @@
1
+ import { err, ok, type Result } from "neverthrow";
2
+ import {
3
+ type BackendResult,
4
+ type BackendTag,
5
+ type ConversationEvent,
6
+ type RuntimeError,
7
+ unsupportedFeature
8
+ } from "../model/index.ts";
9
+ import { BoundedAsyncQueue } from "./queue.ts";
10
+
11
+ export type Outcome<B extends BackendTag = BackendTag> =
12
+ | { readonly type: "success"; readonly result: BackendResult<B> }
13
+ | { readonly type: "cancelled"; readonly reason?: string }
14
+ | { readonly type: "failed"; readonly error: RuntimeError };
15
+
16
+ export interface Conversation<B extends BackendTag = BackendTag> {
17
+ readonly backend: B;
18
+ readonly canAskUser: boolean;
19
+ readonly signal: AbortSignal;
20
+ events(): AsyncIterable<ConversationEvent>;
21
+ awaitResult(): Promise<Outcome<B>>;
22
+ cancel(reason?: string): Promise<void>;
23
+ }
24
+
25
+ export interface StreamConversationOptions<B extends BackendTag> {
26
+ readonly backend: B;
27
+ readonly capacity?: number;
28
+ readonly canAskUser?: boolean;
29
+ readonly onCancel?: (reason?: string) => Promise<void> | void;
30
+ }
31
+
32
+ export class StreamConversation<B extends BackendTag> implements Conversation<B> {
33
+ private readonly abortController = new AbortController();
34
+
35
+ readonly canAskUser: boolean;
36
+ readonly signal = this.abortController.signal;
37
+
38
+ private readonly queue: BoundedAsyncQueue<ConversationEvent>;
39
+ private readonly outcome: Promise<Outcome<B>>;
40
+ private settle!: (outcome: Outcome<B>) => void;
41
+ private settled = false;
42
+
43
+ constructor(private readonly options: StreamConversationOptions<B>) {
44
+ this.queue = new BoundedAsyncQueue(options.capacity ?? 32);
45
+ this.canAskUser = options.canAskUser ?? false;
46
+ this.outcome = new Promise((resolve) => {
47
+ this.settle = resolve;
48
+ });
49
+ }
50
+
51
+ get backend(): B {
52
+ return this.options.backend;
53
+ }
54
+
55
+ events(): AsyncIterable<ConversationEvent> {
56
+ return this.queue;
57
+ }
58
+
59
+ awaitResult(): Promise<Outcome<B>> {
60
+ return this.outcome;
61
+ }
62
+
63
+ async emit(event: ConversationEvent): Promise<Result<void, RuntimeError>> {
64
+ let unsupportedReason: string | undefined;
65
+
66
+ if (event.type === "user_question" && !this.canAskUser) {
67
+ unsupportedReason = "Human interaction events require an explicit interactive conversation";
68
+ } else if (event.type === "approve_tool") {
69
+ unsupportedReason = "Live approval events are unsupported for autonomous execution";
70
+ }
71
+
72
+ if (unsupportedReason !== undefined) {
73
+ const error = unsupportedFeature(event.type, unsupportedReason);
74
+ this.fail(error);
75
+ return err(error);
76
+ }
77
+
78
+ await this.queue.push(event);
79
+ return ok(undefined);
80
+ }
81
+
82
+ succeed(result: BackendResult<B>): void {
83
+ this.complete({ type: "success", result });
84
+ }
85
+
86
+ fail(error: RuntimeError): void {
87
+ this.complete({ type: "failed", error });
88
+ }
89
+
90
+ async cancel(reason?: string): Promise<void> {
91
+ if (this.settled) {
92
+ return;
93
+ }
94
+
95
+ this.abortController.abort(reason);
96
+ await this.options.onCancel?.(reason);
97
+ this.complete(reason === undefined ? { type: "cancelled" } : { type: "cancelled", reason });
98
+ }
99
+
100
+ private complete(outcome: Outcome<B>): void {
101
+ if (this.settled) {
102
+ return;
103
+ }
104
+
105
+ this.settled = true;
106
+ this.queue.close();
107
+ this.settle(outcome);
108
+ }
109
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ backendFailed,
3
+ type BackendTag,
4
+ type ConversationEvent
5
+ } from "../model/index.ts";
6
+ import { StreamConversation, type Outcome } from "./conversation.ts";
7
+
8
+ export interface ConversationCapture<B extends BackendTag> {
9
+ readonly events: readonly ConversationEvent[];
10
+ readonly outcome: Outcome<B>;
11
+ }
12
+
13
+ export interface ConversationHarnessOptions<B extends BackendTag> {
14
+ readonly backend: B;
15
+ readonly capacity?: number;
16
+ readonly canAskUser?: boolean;
17
+ readonly consume: (conversation: StreamConversation<B>) => Promise<void> | void;
18
+ }
19
+
20
+ export async function collectConversation<B extends BackendTag>(
21
+ options: ConversationHarnessOptions<B>
22
+ ): Promise<ConversationCapture<B>> {
23
+ const { backend, consume, ...conversationOptions } = options;
24
+ const conversation = new StreamConversation({ backend, ...conversationOptions });
25
+
26
+ try {
27
+ await consume(conversation);
28
+ } catch (error) {
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ conversation.fail(backendFailed(backend, message));
31
+ }
32
+
33
+ const events: ConversationEvent[] = [];
34
+ for await (const event of conversation.events()) {
35
+ events.push(event);
36
+ }
37
+
38
+ return { events, outcome: await conversation.awaitResult() };
39
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./ask-user.ts";
2
+ export * from "./conversation.ts";
3
+ export * from "./harness.ts";
4
+ export * from "./queue.ts";
@@ -0,0 +1,86 @@
1
+ export class BoundedAsyncQueue<T> implements AsyncIterable<T> {
2
+ private readonly items: T[] = [];
3
+ private readonly takers: Array<(value: IteratorResult<T>) => void> = [];
4
+ private readonly pushWaiters: Array<() => void> = [];
5
+ private closed = false;
6
+ private iteratorStarted = false;
7
+
8
+ constructor(private readonly capacity: number) {
9
+ if (capacity < 1) {
10
+ throw new Error("Queue capacity must be at least 1");
11
+ }
12
+ }
13
+
14
+ async push(item: T): Promise<void> {
15
+ this.ensureOpen();
16
+
17
+ while (this.items.length >= this.capacity) {
18
+ // Result-only callers never iterate, so a full queue would park the
19
+ // producer (and the backend turn loop behind it) forever: without a
20
+ // consumer, evict the oldest event instead of blocking.
21
+ if (!this.iteratorStarted) {
22
+ this.items.shift();
23
+ break;
24
+ }
25
+ await new Promise<void>((resolve) => this.pushWaiters.push(resolve));
26
+ this.ensureOpen();
27
+ }
28
+
29
+ const taker = this.takers.shift();
30
+ if (taker) {
31
+ taker({ value: item, done: false });
32
+ return;
33
+ }
34
+
35
+ this.items.push(item);
36
+ }
37
+
38
+ close(): void {
39
+ if (this.closed) {
40
+ return;
41
+ }
42
+
43
+ this.closed = true;
44
+ for (const resolve of this.pushWaiters) {
45
+ resolve();
46
+ }
47
+ this.pushWaiters.length = 0;
48
+ for (const taker of this.takers) {
49
+ taker({ value: undefined, done: true });
50
+ }
51
+ this.takers.length = 0;
52
+ }
53
+
54
+ [Symbol.asyncIterator](): AsyncIterator<T> {
55
+ if (this.iteratorStarted) {
56
+ throw new Error("Queue supports a single consumer");
57
+ }
58
+
59
+ this.iteratorStarted = true;
60
+
61
+ return {
62
+ next: () => this.next()
63
+ };
64
+ }
65
+
66
+ private async next(): Promise<IteratorResult<T>> {
67
+ const item = this.items.shift();
68
+ if (item !== undefined) {
69
+ const waiter = this.pushWaiters.shift();
70
+ waiter?.();
71
+ return { value: item, done: false };
72
+ }
73
+
74
+ if (this.closed) {
75
+ return { value: undefined, done: true };
76
+ }
77
+
78
+ return new Promise<IteratorResult<T>>((resolve) => this.takers.push(resolve));
79
+ }
80
+
81
+ private ensureOpen(): void {
82
+ if (this.closed) {
83
+ throw new Error("Queue is closed");
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,43 @@
1
+ import { currentFlowContext } from "./context.ts";
2
+
3
+ export const flowContext = currentFlowContext;
4
+
5
+ export function fs() {
6
+ return currentFlowContext().fs;
7
+ }
8
+
9
+ export function git() {
10
+ return currentFlowContext().git;
11
+ }
12
+
13
+ export function gh() {
14
+ return currentFlowContext().gh;
15
+ }
16
+
17
+ export function linear() {
18
+ return currentFlowContext().linear;
19
+ }
20
+
21
+ export function terminal() {
22
+ return currentFlowContext().terminal;
23
+ }
24
+
25
+ export function command() {
26
+ return currentFlowContext().command;
27
+ }
28
+
29
+ export function llm() {
30
+ return currentFlowContext().llm;
31
+ }
32
+
33
+ export function plan() {
34
+ return currentFlowContext().plan;
35
+ }
36
+
37
+ export function review() {
38
+ return currentFlowContext().review;
39
+ }
40
+
41
+ export function reporter() {
42
+ return currentFlowContext().reporter;
43
+ }
@@ -0,0 +1,10 @@
1
+ export interface ToolApprovalRequest {
2
+ readonly toolName: string;
3
+ readonly input: unknown;
4
+ }
5
+
6
+ export function requestToolApproval(request: ToolApprovalRequest): never {
7
+ throw new Error(
8
+ `Tool approval for ${request.toolName} is intentionally unsupported in v1`
9
+ );
10
+ }
@@ -0,0 +1,26 @@
1
+ export const FLOW_ARGS_ENV = "ORCA_FLOW_ARGS";
2
+
3
+ // User task tokens are everything after a standalone `--`, so CLI flags and the
4
+ // flow path never leak into a flow's task input. No `--` means no task args.
5
+ export function extractFlowArgs(argv: readonly string[]): string[] {
6
+ const separator = argv.indexOf("--");
7
+ return separator === -1 ? [] : argv.slice(separator + 1);
8
+ }
9
+
10
+ // The arguments a flow should treat as its task input. The orcats CLI captures
11
+ // the post-`--` tokens and forwards them via ORCA_FLOW_ARGS; a flow run directly
12
+ // (e.g. `bun flow.ts -- foo`) falls back to parsing argv.
13
+ export function flowArgs(): string[] {
14
+ const raw = process.env[FLOW_ARGS_ENV];
15
+ if (raw !== undefined) {
16
+ try {
17
+ const parsed: unknown = JSON.parse(raw);
18
+ if (Array.isArray(parsed)) {
19
+ return parsed.filter((arg): arg is string => typeof arg === "string");
20
+ }
21
+ } catch {
22
+ // malformed env — fall back to argv parsing below
23
+ }
24
+ }
25
+ return extractFlowArgs(process.argv.slice(2));
26
+ }
@@ -0,0 +1,106 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { createFsTool, type FsTool } from "../tools/fs.ts";
3
+ import { createGitTool, type GitTool } from "../tools/git.ts";
4
+ import { createGitHubTool, type GitHubTool } from "../tools/github.ts";
5
+ import { createLinearTool, type LinearTool } from "../tools/linear.ts";
6
+ import { createCommandTool, type CommandTool } from "../tools/process.ts";
7
+ import { createTerminalTool, type TerminalTool } from "../tools/terminal.ts";
8
+ import type { LlmTool } from "../backends/index.ts";
9
+ import {
10
+ defaultPlanPath,
11
+ implementTaskLoop,
12
+ recoverPlan,
13
+ writePlan
14
+ } from "../plan/index.ts";
15
+ import { createReviewTool, type ReviewTool } from "../review/index.ts";
16
+ import {
17
+ activeRunReporter,
18
+ createRunReporter,
19
+ type RunReporter,
20
+ } from "../run-output/index.ts";
21
+
22
+ export interface PlanTool {
23
+ readonly defaultPath: typeof defaultPlanPath;
24
+ readonly write: typeof writePlan;
25
+ readonly recover: typeof recoverPlan;
26
+ readonly implementTaskLoop: typeof implementTaskLoop;
27
+ interactive(): never;
28
+ }
29
+
30
+ export interface FlowContext {
31
+ readonly args: readonly string[];
32
+ readonly cwd: string;
33
+ readonly fs: FsTool;
34
+ readonly git: GitTool;
35
+ readonly gh: GitHubTool;
36
+ readonly linear: LinearTool;
37
+ readonly terminal: TerminalTool;
38
+ readonly command: CommandTool;
39
+ readonly llm: LlmTool;
40
+ readonly plan: PlanTool;
41
+ readonly review: ReviewTool;
42
+ readonly reporter: RunReporter;
43
+ }
44
+
45
+ export type FlowOverrides = Partial<Omit<FlowContext, "args">>;
46
+
47
+ const storage = new AsyncLocalStorage<FlowContext>();
48
+
49
+ export function flow<T>(
50
+ args: readonly string[] = [],
51
+ overrides: FlowOverrides = {}
52
+ ): (fn: () => Promise<T> | T) => Promise<T> {
53
+ const context = createDefaultFlowContext(args, overrides);
54
+ return async (fn) => await storage.run(context, async () => await fn());
55
+ }
56
+
57
+ export function currentFlowContext(): FlowContext {
58
+ const context = storage.getStore();
59
+ if (!context) {
60
+ throw new Error("No active Orca flow context");
61
+ }
62
+ return context;
63
+ }
64
+
65
+ export function createDefaultFlowContext(
66
+ args: readonly string[],
67
+ overrides: FlowOverrides = {}
68
+ ): FlowContext {
69
+ const cwd = overrides.cwd ?? process.cwd();
70
+ const reporter = overrides.reporter ?? activeRunReporter() ?? createRunReporter();
71
+
72
+ return {
73
+ args,
74
+ cwd,
75
+ fs: overrides.fs ?? createFsTool(),
76
+ git: overrides.git ?? createGitTool(cwd),
77
+ gh: overrides.gh ?? createGitHubTool(cwd),
78
+ linear: overrides.linear ?? createLinearTool(),
79
+ terminal: overrides.terminal ?? createTerminalTool({ reporter }),
80
+ command: overrides.command ?? createCommandTool(cwd),
81
+ llm: overrides.llm ?? createDefaultLlmTool(),
82
+ plan: overrides.plan ?? createDefaultPlanTool(),
83
+ review: overrides.review ?? createReviewTool(),
84
+ reporter
85
+ };
86
+ }
87
+
88
+ function createDefaultLlmTool(): LlmTool {
89
+ return {
90
+ autonomous(backend, request) {
91
+ return backend.autonomous(request);
92
+ }
93
+ };
94
+ }
95
+
96
+ function createDefaultPlanTool(): PlanTool {
97
+ return {
98
+ defaultPath: defaultPlanPath,
99
+ write: writePlan,
100
+ recover: recoverPlan,
101
+ implementTaskLoop,
102
+ interactive() {
103
+ throw new Error("Plan.interactive is intentionally unsupported in v1");
104
+ }
105
+ };
106
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./accessors.ts";
2
+ export * from "./approvals.ts";
3
+ export * from "./args.ts";
4
+ export * from "./context.ts";
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ export * from "./backends/index.ts";
2
+ export * from "./baseline/index.ts";
3
+ export * from "./conversation/index.ts";
4
+ export * from "./flow/index.ts";
5
+ export * from "./loop/index.ts";
6
+ export * from "./model/index.ts";
7
+ export * from "./monitor/index.ts";
8
+ export * from "./plan/index.ts";
9
+ export * from "./review/index.ts";
10
+ export * from "./runner/index.ts";
11
+ export * from "./run-output/index.ts";
12
+ export * from "./tools/index.ts";
13
+ export { z } from "zod";
14
+ // Re-exported so flows (including standalone runs that only get the embedded
15
+ // @twelvehart/orcats surface) can build fixLoop/Result values without a neverthrow dep.
16
+ export { err, ok } from "neverthrow";
17
+ export type { Result } from "neverthrow";