vibeman 0.0.0 → 0.0.1

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 (220) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +116 -0
  3. package/dist/runtime/api/.tsbuildinfo +1 -0
  4. package/dist/runtime/api/agent/agent-service.d.ts +226 -0
  5. package/dist/runtime/api/agent/agent-service.js +901 -0
  6. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +61 -0
  7. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +373 -0
  8. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +34 -0
  9. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +281 -0
  10. package/dist/runtime/api/agent/ai-providers/index.d.ts +9 -0
  11. package/dist/runtime/api/agent/ai-providers/index.js +7 -0
  12. package/dist/runtime/api/agent/ai-providers/types.d.ts +180 -0
  13. package/dist/runtime/api/agent/ai-providers/types.js +5 -0
  14. package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +1 -0
  15. package/dist/runtime/api/agent/codex-cli-provider.test.js +88 -0
  16. package/dist/runtime/api/agent/core-agent-service.d.ts +119 -0
  17. package/dist/runtime/api/agent/core-agent-service.js +267 -0
  18. package/dist/runtime/api/agent/parsers.d.ts +15 -0
  19. package/dist/runtime/api/agent/parsers.js +241 -0
  20. package/dist/runtime/api/agent/prompt-service.d.ts +17 -0
  21. package/dist/runtime/api/agent/prompt-service.js +340 -0
  22. package/dist/runtime/api/agent/routing-policy.d.ts +188 -0
  23. package/dist/runtime/api/agent/routing-policy.js +246 -0
  24. package/dist/runtime/api/api/router-helpers.d.ts +32 -0
  25. package/dist/runtime/api/api/router-helpers.js +31 -0
  26. package/dist/runtime/api/api/routers/ai.d.ts +188 -0
  27. package/dist/runtime/api/api/routers/ai.js +410 -0
  28. package/dist/runtime/api/api/routers/executions.d.ts +98 -0
  29. package/dist/runtime/api/api/routers/executions.js +103 -0
  30. package/dist/runtime/api/api/routers/git.d.ts +45 -0
  31. package/dist/runtime/api/api/routers/git.js +35 -0
  32. package/dist/runtime/api/api/routers/settings.d.ts +139 -0
  33. package/dist/runtime/api/api/routers/settings.js +113 -0
  34. package/dist/runtime/api/api/routers/tasks.d.ts +141 -0
  35. package/dist/runtime/api/api/routers/tasks.js +238 -0
  36. package/dist/runtime/api/api/routers/workflows.d.ts +268 -0
  37. package/dist/runtime/api/api/routers/workflows.js +308 -0
  38. package/dist/runtime/api/api/routers/worktrees.d.ts +102 -0
  39. package/dist/runtime/api/api/routers/worktrees.js +80 -0
  40. package/dist/runtime/api/api/trpc.d.ts +118 -0
  41. package/dist/runtime/api/api/trpc.js +34 -0
  42. package/dist/runtime/api/index.d.ts +9 -0
  43. package/dist/runtime/api/index.js +125 -0
  44. package/dist/runtime/api/lib/id-generator.d.ts +70 -0
  45. package/dist/runtime/api/lib/id-generator.js +123 -0
  46. package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +26 -0
  47. package/dist/runtime/api/lib/image-paste-drop-extension.js +125 -0
  48. package/dist/runtime/api/lib/logger.d.ts +11 -0
  49. package/dist/runtime/api/lib/logger.js +188 -0
  50. package/dist/runtime/api/lib/markdown-utils.d.ts +8 -0
  51. package/dist/runtime/api/lib/markdown-utils.js +282 -0
  52. package/dist/runtime/api/lib/markdown-utils.test.d.ts +1 -0
  53. package/dist/runtime/api/lib/markdown-utils.test.js +348 -0
  54. package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +6 -0
  55. package/dist/runtime/api/lib/server/agent-service-singleton.js +27 -0
  56. package/dist/runtime/api/lib/server/git-service-singleton.d.ts +6 -0
  57. package/dist/runtime/api/lib/server/git-service-singleton.js +47 -0
  58. package/dist/runtime/api/lib/server/project-root.d.ts +2 -0
  59. package/dist/runtime/api/lib/server/project-root.js +38 -0
  60. package/dist/runtime/api/lib/server/task-service-singleton.d.ts +7 -0
  61. package/dist/runtime/api/lib/server/task-service-singleton.js +58 -0
  62. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +7 -0
  63. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +57 -0
  64. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.d.ts +1 -0
  65. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +27 -0
  66. package/dist/runtime/api/lib/tiptap-utils.d.ts +130 -0
  67. package/dist/runtime/api/lib/tiptap-utils.js +327 -0
  68. package/dist/runtime/api/lib/trpc/client.d.ts +1 -0
  69. package/dist/runtime/api/lib/trpc/client.js +5 -0
  70. package/dist/runtime/api/lib/trpc/server.d.ts +822 -0
  71. package/dist/runtime/api/lib/trpc/server.js +11 -0
  72. package/dist/runtime/api/lib/trpc/ws-server.d.ts +8 -0
  73. package/dist/runtime/api/lib/trpc/ws-server.js +33 -0
  74. package/dist/runtime/api/persistence/database-service.d.ts +14 -0
  75. package/dist/runtime/api/persistence/database-service.js +74 -0
  76. package/dist/runtime/api/persistence/execution-log-persistence.d.ts +90 -0
  77. package/dist/runtime/api/persistence/execution-log-persistence.js +410 -0
  78. package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +1 -0
  79. package/dist/runtime/api/persistence/execution-log-persistence.test.js +170 -0
  80. package/dist/runtime/api/router.d.ts +825 -0
  81. package/dist/runtime/api/router.js +56 -0
  82. package/dist/runtime/api/settings-service.d.ts +110 -0
  83. package/dist/runtime/api/settings-service.js +611 -0
  84. package/dist/runtime/api/tasks/file-watcher.d.ts +23 -0
  85. package/dist/runtime/api/tasks/file-watcher.js +88 -0
  86. package/dist/runtime/api/tasks/task-file-parser.d.ts +13 -0
  87. package/dist/runtime/api/tasks/task-file-parser.js +161 -0
  88. package/dist/runtime/api/tasks/task-service.d.ts +36 -0
  89. package/dist/runtime/api/tasks/task-service.js +173 -0
  90. package/dist/runtime/api/types/index.d.ts +179 -0
  91. package/dist/runtime/api/types/index.js +1 -0
  92. package/dist/runtime/api/types/settings.d.ts +81 -0
  93. package/dist/runtime/api/types/settings.js +2 -0
  94. package/dist/runtime/api/types.d.ts +2 -0
  95. package/dist/runtime/api/types.js +1 -0
  96. package/dist/runtime/api/utils/env.d.ts +6 -0
  97. package/dist/runtime/api/utils/env.js +12 -0
  98. package/dist/runtime/api/utils/stripNextEnv.d.ts +7 -0
  99. package/dist/runtime/api/utils/stripNextEnv.js +22 -0
  100. package/dist/runtime/api/utils/title-slug.d.ts +6 -0
  101. package/dist/runtime/api/utils/title-slug.js +77 -0
  102. package/dist/runtime/api/utils/url.d.ts +2 -0
  103. package/dist/runtime/api/utils/url.js +19 -0
  104. package/dist/runtime/api/vcs/git-history-service.d.ts +57 -0
  105. package/dist/runtime/api/vcs/git-history-service.js +228 -0
  106. package/dist/runtime/api/vcs/git-service.d.ts +127 -0
  107. package/dist/runtime/api/vcs/git-service.js +284 -0
  108. package/dist/runtime/api/vcs/worktree-service.d.ts +93 -0
  109. package/dist/runtime/api/vcs/worktree-service.js +506 -0
  110. package/dist/runtime/api/vcs/worktree-service.test.d.ts +1 -0
  111. package/dist/runtime/api/vcs/worktree-service.test.js +20 -0
  112. package/dist/runtime/api/workflows/quality-pipeline.d.ts +58 -0
  113. package/dist/runtime/api/workflows/quality-pipeline.js +400 -0
  114. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +313 -0
  115. package/dist/runtime/api/workflows/vibing-orchestrator.js +1861 -0
  116. package/dist/runtime/web/.next/BUILD_ID +1 -0
  117. package/dist/runtime/web/.next/app-build-manifest.json +59 -0
  118. package/dist/runtime/web/.next/app-path-routes-manifest.json +7 -0
  119. package/dist/runtime/web/.next/build-manifest.json +33 -0
  120. package/dist/runtime/web/.next/package.json +1 -0
  121. package/dist/runtime/web/.next/prerender-manifest.json +61 -0
  122. package/dist/runtime/web/.next/react-loadable-manifest.json +39 -0
  123. package/dist/runtime/web/.next/required-server-files.json +334 -0
  124. package/dist/runtime/web/.next/routes-manifest.json +62 -0
  125. package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -0
  126. package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  127. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  128. package/dist/runtime/web/.next/server/app/_not-found.html +7 -0
  129. package/dist/runtime/web/.next/server/app/_not-found.meta +8 -0
  130. package/dist/runtime/web/.next/server/app/_not-found.rsc +22 -0
  131. package/dist/runtime/web/.next/server/app/api/health/route.js +1 -0
  132. package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -0
  133. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -0
  134. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -0
  135. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -0
  136. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -0
  137. package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -0
  138. package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -0
  139. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -0
  140. package/dist/runtime/web/.next/server/app/index.html +7 -0
  141. package/dist/runtime/web/.next/server/app/index.meta +7 -0
  142. package/dist/runtime/web/.next/server/app/index.rsc +27 -0
  143. package/dist/runtime/web/.next/server/app/page.js +147 -0
  144. package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -0
  145. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -0
  146. package/dist/runtime/web/.next/server/app-paths-manifest.json +7 -0
  147. package/dist/runtime/web/.next/server/chunks/217.js +1 -0
  148. package/dist/runtime/web/.next/server/chunks/383.js +6 -0
  149. package/dist/runtime/web/.next/server/chunks/458.js +1 -0
  150. package/dist/runtime/web/.next/server/chunks/576.js +18 -0
  151. package/dist/runtime/web/.next/server/chunks/635.js +22 -0
  152. package/dist/runtime/web/.next/server/chunks/761.js +1 -0
  153. package/dist/runtime/web/.next/server/chunks/777.js +3 -0
  154. package/dist/runtime/web/.next/server/chunks/825.js +1 -0
  155. package/dist/runtime/web/.next/server/chunks/838.js +1 -0
  156. package/dist/runtime/web/.next/server/chunks/973.js +15 -0
  157. package/dist/runtime/web/.next/server/functions-config-manifest.json +4 -0
  158. package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -0
  159. package/dist/runtime/web/.next/server/middleware-manifest.json +6 -0
  160. package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -0
  161. package/dist/runtime/web/.next/server/next-font-manifest.js +1 -0
  162. package/dist/runtime/web/.next/server/next-font-manifest.json +1 -0
  163. package/dist/runtime/web/.next/server/pages/404.html +7 -0
  164. package/dist/runtime/web/.next/server/pages/500.html +1 -0
  165. package/dist/runtime/web/.next/server/pages/_app.js +1 -0
  166. package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -0
  167. package/dist/runtime/web/.next/server/pages/_document.js +1 -0
  168. package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -0
  169. package/dist/runtime/web/.next/server/pages/_error.js +19 -0
  170. package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -0
  171. package/dist/runtime/web/.next/server/pages-manifest.json +6 -0
  172. package/dist/runtime/web/.next/server/server-reference-manifest.js +1 -0
  173. package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -0
  174. package/dist/runtime/web/.next/server/webpack-runtime.js +1 -0
  175. package/dist/runtime/web/.next/static/1HR8N0rJkCvFRtbTPJMyH/_buildManifest.js +1 -0
  176. package/dist/runtime/web/.next/static/1HR8N0rJkCvFRtbTPJMyH/_ssgManifest.js +1 -0
  177. package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +1 -0
  178. package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +3 -0
  179. package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +71 -0
  180. package/dist/runtime/web/.next/static/chunks/277-0142a939f08738c3.js +63 -0
  181. package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +1 -0
  182. package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +1 -0
  183. package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +15 -0
  184. package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +1 -0
  185. package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +1 -0
  186. package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +1 -0
  187. package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +1 -0
  188. package/dist/runtime/web/.next/static/chunks/87c73c54-09e1ba5c70e60a51.js +1 -0
  189. package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +1 -0
  190. package/dist/runtime/web/.next/static/chunks/8bb4d8db-3e2aa02b0a2384b9.js +1 -0
  191. package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +1 -0
  192. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +1 -0
  193. package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +1 -0
  194. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +1 -0
  195. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +1 -0
  196. package/dist/runtime/web/.next/static/chunks/app/layout-dc0cfd29075b2160.js +1 -0
  197. package/dist/runtime/web/.next/static/chunks/app/page-f34a8b196b18850b.js +1 -0
  198. package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +1 -0
  199. package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +1 -0
  200. package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +1 -0
  201. package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +1 -0
  202. package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +1 -0
  203. package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +1 -0
  204. package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  205. package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +1 -0
  206. package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +1 -0
  207. package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +3 -0
  208. package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +1 -0
  209. package/dist/runtime/web/.next/static/css/537e22821e101b87.css +1 -0
  210. package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  211. package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  212. package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  213. package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  214. package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  215. package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  216. package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  217. package/dist/runtime/web/package.json +65 -0
  218. package/dist/runtime/web/server.js +44 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -0
  220. package/package.json +80 -7
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Codex CLI Provider
3
+ * Headless wrapper around the Codex CLI `codex exec` command.
4
+ */
5
+ import { spawn } from 'child_process';
6
+ import { getSettingsService } from '../../settings-service.js';
7
+ import { logger } from '../../lib/logger.js';
8
+ export class CodexCliProvider {
9
+ constructor(config = {}) {
10
+ this.config = config;
11
+ this.name = 'codex';
12
+ this.displayName = 'Codex CLI';
13
+ }
14
+ resolveExecutable() {
15
+ if (this.config.codexBinPath)
16
+ return this.config.codexBinPath;
17
+ // Check settings first
18
+ const settingsBinPath = (() => {
19
+ try {
20
+ const svc = getSettingsService();
21
+ const s = svc.getSettings();
22
+ return s?.agents?.providers?.codex?.binPath;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ })();
28
+ if (settingsBinPath?.trim())
29
+ return settingsBinPath.trim();
30
+ // Default to binary name (resolve via PATH)
31
+ return 'codex';
32
+ }
33
+ async *execute(prompt, options) {
34
+ // Effective options
35
+ const cwd = options?.workingDirectory || this.config.defaultWorkingDirectory || process.cwd();
36
+ const mapModel = (m) => {
37
+ if (!m)
38
+ return m;
39
+ // Normalize reasoning-effort variants to the canonical model id
40
+ // Users reported only `gpt-5` is selectable; variants are effort levels in the TUI.
41
+ const lower = m.toLowerCase();
42
+ if (lower === 'gpt-5-minimal' ||
43
+ lower === 'gpt-5-low' ||
44
+ lower === 'gpt-5-medium' ||
45
+ lower === 'gpt-5-high') {
46
+ return 'gpt-5';
47
+ }
48
+ return m;
49
+ };
50
+ const model = mapModel(options?.model || this.config.defaultModel);
51
+ const images = (options?.images || []).filter(Boolean);
52
+ const effort = options?.effort;
53
+ const timeoutMs = options?.timeout ?? this.config.defaultTimeoutMs ?? 10 * 60 * 1000; // 10m
54
+ // Build argv for `codex exec` (non-interactive automation mode)
55
+ const argv = ['exec', prompt];
56
+ if (model) {
57
+ argv.push('--model', model);
58
+ }
59
+ // Prefer spawning with cwd, but also set --cd to make Codex aware of root
60
+ if (cwd) {
61
+ argv.push('--cd', cwd);
62
+ }
63
+ if (images.length) {
64
+ argv.push('--image', images.join(','));
65
+ }
66
+ const cmd = this.resolveExecutable();
67
+ const child = spawn(cmd, argv, {
68
+ cwd,
69
+ env: { ...process.env },
70
+ stdio: ['ignore', 'pipe', 'pipe'],
71
+ });
72
+ // Emit an init/system message with effective command
73
+ const init = {
74
+ type: 'init',
75
+ timestamp: new Date().toISOString(),
76
+ sessionId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
77
+ model: model || 'default',
78
+ provider: this.name,
79
+ };
80
+ yield init;
81
+ yield {
82
+ type: 'system',
83
+ timestamp: new Date().toISOString(),
84
+ content: `Running: ${cmd} ${argv.map((a) => (a.includes(' ') ? '"' + a + '"' : a)).join(' ')} (cwd=${cwd})`,
85
+ metadata: { provider: this.name, images, effort },
86
+ };
87
+ let stdoutBuf = '';
88
+ // let stderrBuf = '';
89
+ let finished = false;
90
+ let lastFlushedIndex = 0;
91
+ let lineCarry = '';
92
+ // Helper to push incremental stdout as assistant log lines
93
+ const flushNewOutput = () => {
94
+ const newChunk = stdoutBuf.slice(lastFlushedIndex);
95
+ if (!newChunk)
96
+ return;
97
+ lastFlushedIndex = stdoutBuf.length;
98
+ // Accumulate and split by line breaks, keep trailing partial
99
+ lineCarry += newChunk;
100
+ const parts = lineCarry.split(/\r?\n/);
101
+ lineCarry = parts.pop() || '';
102
+ return parts.map((l) => l.trimEnd()).filter(Boolean);
103
+ };
104
+ // Provider stays generic: it streams lines and returns raw stdout.
105
+ // Forward parent termination via abortSignal
106
+ // const abortController = new AbortController();
107
+ const onAbort = () => {
108
+ try {
109
+ child.kill('SIGTERM');
110
+ }
111
+ catch {
112
+ /* ignore */
113
+ }
114
+ // Fallback kill after short grace
115
+ setTimeout(() => {
116
+ if (!finished) {
117
+ try {
118
+ child.kill('SIGKILL');
119
+ }
120
+ catch {
121
+ /* ignore */
122
+ }
123
+ }
124
+ }, 3000);
125
+ };
126
+ if (options?.abortSignal) {
127
+ if (options.abortSignal.aborted)
128
+ onAbort();
129
+ else
130
+ options.abortSignal.addEventListener('abort', onAbort, { once: true });
131
+ }
132
+ // Local timeout guard
133
+ const guard = setTimeout(() => {
134
+ onAbort();
135
+ }, timeoutMs);
136
+ // Capture data into buffers
137
+ child.stdout?.on('data', (data) => {
138
+ stdoutBuf += data.toString('utf8');
139
+ });
140
+ // child.stderr?.on('data', (data: Buffer) => {
141
+ // stderrBuf += data.toString('utf8');
142
+ // });
143
+ // Wait for process to finish
144
+ // Stream logs while the process runs by polling for new output
145
+ const exitPromise = new Promise((resolve) => {
146
+ child.on('exit', (code, signal) => resolve({ code, signal }));
147
+ });
148
+ // Poll for output until the process exits
149
+ let exitStatus;
150
+ while (!finished) {
151
+ const tick = new Promise((r) => setTimeout(r, 150));
152
+ const exit = await Promise.race([exitPromise, tick]);
153
+ if (exit && typeof exit.code !== 'undefined') {
154
+ // Process exited; flush any remaining output before breaking
155
+ const lines = flushNewOutput();
156
+ if (lines && lines.length) {
157
+ for (const text of lines) {
158
+ yield {
159
+ type: 'assistant',
160
+ timestamp: new Date().toISOString(),
161
+ content: text,
162
+ };
163
+ }
164
+ }
165
+ finished = true;
166
+ // Record exit info for finalization
167
+ exitStatus = exit;
168
+ break;
169
+ }
170
+ // Flush incremental lines during run
171
+ const lines = flushNewOutput();
172
+ if (lines && lines.length) {
173
+ for (const text of lines) {
174
+ yield {
175
+ type: 'assistant',
176
+ timestamp: new Date().toISOString(),
177
+ content: text,
178
+ };
179
+ }
180
+ }
181
+ }
182
+ // If loop ended without capturing exit status (unlikely), await it now
183
+ const exit = exitStatus ?? (await exitPromise);
184
+ finished = true;
185
+ clearTimeout(guard);
186
+ // Flush any final carry line as a log
187
+ if (lineCarry.trim()) {
188
+ yield {
189
+ type: 'assistant',
190
+ timestamp: new Date().toISOString(),
191
+ content: lineCarry.trimEnd(),
192
+ };
193
+ lineCarry = '';
194
+ }
195
+ // Compose final message
196
+ const success = (exit.code ?? 1) === 0 && !exit.signal;
197
+ const remainingStdout = stdoutBuf.trim();
198
+ const resultMsg = {
199
+ type: 'result',
200
+ timestamp: new Date().toISOString(),
201
+ success,
202
+ // Return raw stdout as result; higher layers decide how to parse it
203
+ result: success ? (remainingStdout || '').toString() : undefined,
204
+ error: success
205
+ ? undefined
206
+ : `codex exited ${exit.signal ? 'by ' + exit.signal : 'with code ' + exit.code}`,
207
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
208
+ };
209
+ yield resultMsg;
210
+ }
211
+ async executeSync(prompt, options) {
212
+ // Convenience: collect from execute()
213
+ let content = '';
214
+ const start = Date.now();
215
+ for await (const msg of this.execute(prompt, options)) {
216
+ if (msg.type === 'assistant' && typeof msg.content === 'string') {
217
+ content += (content ? '\n' : '') + msg.content;
218
+ }
219
+ }
220
+ return {
221
+ id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
222
+ provider: this.name,
223
+ model: options?.model || this.config.defaultModel || 'default',
224
+ content,
225
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
226
+ duration: Date.now() - start,
227
+ };
228
+ }
229
+ async detectAvailableModels() {
230
+ // Current public CLI exposes `--model gpt-5`; effort levels are chosen in-CLI.
231
+ return [
232
+ {
233
+ id: 'gpt-5',
234
+ name: 'gpt-5',
235
+ displayName: 'gpt‑5',
236
+ provider: this.name,
237
+ contextWindow: 256000,
238
+ maxOutputTokens: 16384,
239
+ // Capabilities per Codex docs: supports images via --image and advanced reasoning
240
+ capabilities: ['code', 'analysis', 'tools', 'vision', 'image', 'reasoning'],
241
+ },
242
+ ];
243
+ }
244
+ async validateSetup() {
245
+ try {
246
+ const bin = this.resolveExecutable();
247
+ // Try to spawn `codex --help` quickly to validate presence
248
+ const child = spawn(bin, ['--help'], { stdio: 'ignore' });
249
+ await new Promise((resolve, reject) => {
250
+ child.once('error', reject);
251
+ child.once('close', () => resolve());
252
+ });
253
+ return {
254
+ available: true,
255
+ models: await this.detectAvailableModels(),
256
+ capabilities: this.getCapabilities(),
257
+ };
258
+ }
259
+ catch (error) {
260
+ logger.error(error);
261
+ return {
262
+ available: false,
263
+ error: 'Codex CLI not found. Install from https://github.com/openai/codex (see Getting Started: CLI usage).',
264
+ models: [],
265
+ capabilities: this.getCapabilities(),
266
+ };
267
+ }
268
+ }
269
+ // Note: persistence of detected binary paths is handled by onboarding workflow.
270
+ getCapabilities() {
271
+ return {
272
+ streaming: true,
273
+ tools: true,
274
+ functionCalling: false,
275
+ vision: true,
276
+ codeExecution: true,
277
+ maxContextWindow: 128000,
278
+ maxOutputTokens: 8192,
279
+ };
280
+ }
281
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AI Providers Module
3
+ * Export all provider types and implementations
4
+ */
5
+ export * from './types.js';
6
+ export { ClaudeCodeAdapter } from './claude-code-adapter.js';
7
+ export type { ClaudeCodeConfig } from './claude-code-adapter.js';
8
+ export { CodexCliProvider } from './codex-cli-provider.js';
9
+ export type { CodexCliConfig } from './codex-cli-provider.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * AI Providers Module
3
+ * Export all provider types and implementations
4
+ */
5
+ export * from './types.js';
6
+ export { ClaudeCodeAdapter } from './claude-code-adapter.js';
7
+ export { CodexCliProvider } from './codex-cli-provider.js';
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Core AI Provider Abstraction
3
+ * Defines the contract for AI execution providers (Claude, OpenAI, Local LLMs, etc.)
4
+ */
5
+ /**
6
+ * Model information for available AI models
7
+ */
8
+ export interface ModelInfo {
9
+ id: string;
10
+ name: string;
11
+ displayName: string;
12
+ provider: string;
13
+ contextWindow?: number;
14
+ maxOutputTokens?: number;
15
+ costPerInputToken?: number;
16
+ costPerOutputToken?: number;
17
+ capabilities?: string[];
18
+ }
19
+ /**
20
+ * Tool permissions for AI execution
21
+ * Source: https://docs.anthropic.com/en/docs/claude-code/settings#tools-available-to-claude
22
+ */
23
+ export type ToolPermission = 'Bash' | 'Edit' | 'Glob' | 'Grep' | 'MultiEdit' | 'NotebookEdit' | 'NotebookRead' | 'Read' | 'Task' | 'TodoWrite' | 'WebSearch' | 'WebFetch' | 'Write';
24
+ /**
25
+ * Execution options for AI providers
26
+ */
27
+ export interface ExecutionOptions {
28
+ workingDirectory?: string;
29
+ model?: string;
30
+ temperature?: number;
31
+ maxTokens?: number;
32
+ tools?: ToolPermission[];
33
+ timeout?: number;
34
+ abortSignal?: AbortSignal;
35
+ systemPrompt?: string;
36
+ appendSystemPrompt?: string;
37
+ /**
38
+ * Permission mode for Claude Code SDK (provider-specific hint).
39
+ * - default: standard prompts/permissions
40
+ * - acceptEdits: auto-approve file edits and common FS ops
41
+ * - bypassPermissions: auto-approve all tool uses (dangerous; headless automation)
42
+ */
43
+ permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions';
44
+ /** Optional list of image file paths to attach (Codex CLI: --image) */
45
+ images?: string[];
46
+ /** Optional effort hint for reasoning presets (e.g., 'minimal'|'low'|'medium'|'high'); informational only */
47
+ effort?: string;
48
+ }
49
+ /**
50
+ * Message types for streaming execution
51
+ */
52
+ export interface ExecutionMessage {
53
+ type: 'init' | 'assistant' | 'tool_use' | 'result' | 'error' | 'system';
54
+ timestamp: string;
55
+ content?: string | any;
56
+ metadata?: Record<string, any>;
57
+ }
58
+ /**
59
+ * Initialization message
60
+ */
61
+ export interface InitMessage extends ExecutionMessage {
62
+ type: 'init';
63
+ sessionId: string;
64
+ model: string;
65
+ provider: string;
66
+ }
67
+ /**
68
+ * Assistant message (AI response)
69
+ */
70
+ export interface AssistantMessage extends ExecutionMessage {
71
+ type: 'assistant';
72
+ content: string;
73
+ toolCalls?: ToolCall[];
74
+ }
75
+ /**
76
+ * Tool call information
77
+ */
78
+ export interface ToolCall {
79
+ id: string;
80
+ name: string;
81
+ input?: any;
82
+ output?: any;
83
+ }
84
+ /**
85
+ * Tool use message
86
+ */
87
+ export interface ToolUseMessage extends ExecutionMessage {
88
+ type: 'tool_use';
89
+ tool: string;
90
+ input: any;
91
+ output?: any;
92
+ }
93
+ /**
94
+ * Result message (final output)
95
+ */
96
+ export interface ResultMessage extends ExecutionMessage {
97
+ type: 'result';
98
+ success: boolean;
99
+ result?: string;
100
+ error?: string;
101
+ usage?: {
102
+ promptTokens: number;
103
+ completionTokens: number;
104
+ totalTokens: number;
105
+ cost?: number;
106
+ };
107
+ duration?: number;
108
+ }
109
+ /**
110
+ * Execution result for synchronous execution
111
+ */
112
+ export interface ExecutionResult {
113
+ id: string;
114
+ provider: string;
115
+ model: string;
116
+ content: string;
117
+ usage: {
118
+ promptTokens: number;
119
+ completionTokens: number;
120
+ totalTokens: number;
121
+ cost?: number;
122
+ };
123
+ duration: number;
124
+ error?: string;
125
+ }
126
+ /**
127
+ * Provider capabilities
128
+ */
129
+ export interface ProviderCapabilities {
130
+ streaming: boolean;
131
+ tools: boolean;
132
+ functionCalling: boolean;
133
+ vision: boolean;
134
+ codeExecution: boolean;
135
+ maxContextWindow: number;
136
+ maxOutputTokens: number;
137
+ }
138
+ /**
139
+ * Provider status
140
+ */
141
+ export interface ProviderStatus {
142
+ available: boolean;
143
+ error?: string;
144
+ models: ModelInfo[];
145
+ capabilities: ProviderCapabilities;
146
+ }
147
+ /**
148
+ * Core AI Provider interface
149
+ * All AI providers must implement this interface
150
+ */
151
+ export interface AIProvider {
152
+ /**
153
+ * Provider identifier
154
+ */
155
+ readonly name: string;
156
+ /**
157
+ * Provider display name
158
+ */
159
+ readonly displayName: string;
160
+ /**
161
+ * Execute a prompt and return streaming messages
162
+ */
163
+ execute(prompt: string, options?: ExecutionOptions): AsyncIterableIterator<ExecutionMessage>;
164
+ /**
165
+ * Execute a prompt and return the final result (convenience method)
166
+ */
167
+ executeSync?(prompt: string, options?: ExecutionOptions): Promise<ExecutionResult>;
168
+ /**
169
+ * Detect available models for this provider
170
+ */
171
+ detectAvailableModels(): Promise<ModelInfo[]>;
172
+ /**
173
+ * Validate provider setup and configuration
174
+ */
175
+ validateSetup(): Promise<ProviderStatus>;
176
+ /**
177
+ * Get provider capabilities
178
+ */
179
+ getCapabilities(): ProviderCapabilities;
180
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Core AI Provider Abstraction
3
+ * Defines the contract for AI execution providers (Claude, OpenAI, Local LLMs, etc.)
4
+ */
5
+ export {};
@@ -0,0 +1,88 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+ // Toggle real CLI integration via env
3
+ const USE_REAL = process.env.VITEST_USE_REAL_CODEX === 'true';
4
+ // Note: we dynamically import the provider after (un)mocking per test block
5
+ describe('CodexCliProvider (mocked)', () => {
6
+ afterEach(() => {
7
+ vi.resetAllMocks();
8
+ });
9
+ it('streams stdout, passes images flag, and returns result', async () => {
10
+ vi.resetModules();
11
+ vi.doMock('child_process', () => {
12
+ let captured = [];
13
+ return {
14
+ __captured: () => captured,
15
+ spawn: (cmd, args, opts) => {
16
+ captured = [cmd, args, opts];
17
+ const { EventEmitter } = require('events');
18
+ const stdout = new EventEmitter();
19
+ const stderr = new EventEmitter();
20
+ const proc = new EventEmitter();
21
+ proc.stdout = stdout;
22
+ proc.stderr = stderr;
23
+ setTimeout(() => stdout.emit('data', Buffer.from('Hello from Codex\n')), 10);
24
+ setTimeout(() => proc.emit('exit', 0, null), 20);
25
+ return proc;
26
+ },
27
+ };
28
+ });
29
+ const { CodexCliProvider } = await import('./ai-providers/codex-cli-provider.js');
30
+ const provider = new CodexCliProvider({ defaultTimeoutMs: 1000 });
31
+ const messages = [];
32
+ const iter = provider.execute('What is this project about?', {
33
+ workingDirectory: process.cwd(),
34
+ images: ['a.png', 'b.jpg'],
35
+ effort: 'minimal',
36
+ model: 'gpt-5',
37
+ });
38
+ for await (const m of iter)
39
+ messages.push(m);
40
+ const resultMsg = messages.find((m) => m.type === 'result');
41
+ expect(!!resultMsg).toBe(true);
42
+ const assistantMsg = messages.find((m) => m.type === 'assistant');
43
+ if (assistantMsg && typeof assistantMsg.content === 'string') {
44
+ expect(assistantMsg.content.length).toBeGreaterThan(0);
45
+ }
46
+ else if (resultMsg && typeof resultMsg.result === 'string') {
47
+ expect(resultMsg.result.length).toBeGreaterThan(0);
48
+ }
49
+ const mockSpawn = await import('child_process');
50
+ const captured = mockSpawn.__captured();
51
+ expect(Array.isArray(captured[1])).toBe(true);
52
+ expect(captured[1]).toContain('--image');
53
+ expect(captured[1]).toContain('a.png,b.jpg');
54
+ const modelIdx = captured[1].indexOf('--model');
55
+ expect(modelIdx).toBeGreaterThan(-1);
56
+ expect(captured[1][modelIdx + 1]).toBe('gpt-5');
57
+ });
58
+ });
59
+ // Real integration (opt-in)
60
+ (USE_REAL ? describe : describe.skip)('CodexCliProvider (real CLI)', () => {
61
+ it('answers a simple model question and prints output', { timeout: 120000 }, async () => {
62
+ vi.resetModules();
63
+ vi.doUnmock('child_process');
64
+ const { CodexCliProvider } = await import('./ai-providers/codex-cli-provider.js');
65
+ const provider = new CodexCliProvider({
66
+ defaultWorkingDirectory: process.cwd(),
67
+ defaultTimeoutMs: 120000,
68
+ });
69
+ const prompt = 'Just answer 1 + 1 = ? for testing.';
70
+ let assistant = '';
71
+ let sawResult = false;
72
+ for await (const m of provider.execute(prompt, {
73
+ workingDirectory: process.cwd(),
74
+ timeout: 120000,
75
+ })) {
76
+ console.log('[CODEX]', m.type, typeof m.content === 'string' ? m.content : '');
77
+ if (m.type === 'assistant' && typeof m.content === 'string') {
78
+ if (!assistant)
79
+ assistant = m.content.trim();
80
+ }
81
+ if (m.type === 'result') {
82
+ sawResult = true;
83
+ }
84
+ }
85
+ expect(sawResult).toBe(true);
86
+ expect(assistant.length).toBeGreaterThan(0);
87
+ });
88
+ });
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Core Agent Service - Pure AI Execution Engine
3
+ * Provider-agnostic AI execution service
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { AIProvider, ExecutionOptions, ExecutionMessage, ModelInfo, ProviderStatus } from './ai-providers/index.js';
7
+ /**
8
+ * Execution state tracking
9
+ */
10
+ export interface ExecutionState {
11
+ id: string;
12
+ provider: string;
13
+ status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
14
+ startTime: string;
15
+ endTime?: string;
16
+ error?: string;
17
+ messages: ExecutionMessage[];
18
+ result?: string;
19
+ usage?: {
20
+ promptTokens: number;
21
+ completionTokens: number;
22
+ totalTokens: number;
23
+ cost?: number;
24
+ };
25
+ abortController?: AbortController;
26
+ }
27
+ /**
28
+ * Execution request
29
+ */
30
+ export interface ExecutionRequest {
31
+ prompt: string;
32
+ options?: ExecutionOptions;
33
+ provider?: string;
34
+ metadata?: Record<string, any>;
35
+ executionId?: string;
36
+ }
37
+ /**
38
+ * Execution update event
39
+ */
40
+ export interface ExecutionUpdate {
41
+ executionId: string;
42
+ status: ExecutionState['status'];
43
+ message?: ExecutionMessage;
44
+ error?: string;
45
+ }
46
+ /**
47
+ * Agent Service Configuration
48
+ */
49
+ export interface AgentServiceConfig {
50
+ defaultProvider?: string;
51
+ executionTimeout?: number;
52
+ }
53
+ /**
54
+ * Pure AI Execution Service
55
+ * Manages AI providers and executions without business logic
56
+ */
57
+ export declare class CoreAgentService extends EventEmitter {
58
+ private providers;
59
+ private defaultProvider;
60
+ private executions;
61
+ private config;
62
+ constructor(config?: AgentServiceConfig);
63
+ /**
64
+ * Register an AI provider
65
+ */
66
+ registerProvider(name: string, provider: AIProvider): void;
67
+ /**
68
+ * Set default provider
69
+ */
70
+ setDefaultProvider(name: string): void;
71
+ /**
72
+ * Get default provider name
73
+ */
74
+ getDefaultProvider(): string | null;
75
+ /**
76
+ * Get provider by name
77
+ */
78
+ getProvider(name: string): AIProvider | null;
79
+ /**
80
+ * Get all registered providers
81
+ */
82
+ getProviders(): Map<string, AIProvider>;
83
+ /**
84
+ * Execute a prompt with streaming
85
+ */
86
+ execute(request: ExecutionRequest): Promise<string>;
87
+ /**
88
+ * Get execution status
89
+ */
90
+ getExecutionStatus(executionId: string): ExecutionState | null;
91
+ /**
92
+ * Get execution messages
93
+ */
94
+ getExecutionMessages(executionId: string): ExecutionMessage[];
95
+ /**
96
+ * Stop an execution
97
+ */
98
+ stopExecution(executionId: string): Promise<void>;
99
+ /**
100
+ * Get all executions
101
+ */
102
+ getAllExecutions(): ExecutionState[];
103
+ /**
104
+ * Clear completed executions
105
+ */
106
+ clearCompletedExecutions(): number;
107
+ /**
108
+ * Validate all providers
109
+ */
110
+ validateProviders(): Promise<Map<string, ProviderStatus>>;
111
+ /**
112
+ * Get available models from all providers
113
+ */
114
+ getAvailableModels(): Promise<ModelInfo[]>;
115
+ /**
116
+ * Generate execution ID
117
+ */
118
+ private generateExecutionId;
119
+ }