@ddse/acm-aicoder 0.5.0 → 0.5.2

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 (47) hide show
  1. package/dist/src/capability-map.d.ts +4 -0
  2. package/dist/src/capability-map.d.ts.map +1 -0
  3. package/dist/src/capability-map.js +289 -0
  4. package/dist/src/capability-map.js.map +1 -0
  5. package/dist/src/registries.d.ts +3 -0
  6. package/dist/src/registries.d.ts.map +1 -1
  7. package/dist/src/registries.js +11 -0
  8. package/dist/src/registries.js.map +1 -1
  9. package/dist/tsconfig.tsbuildinfo +1 -1
  10. package/package.json +21 -7
  11. package/.aicoder/index.json +0 -304
  12. package/AICODER_IMPLEMENTATION_PLAN_PHASE2.md +0 -284
  13. package/bin/interactive.tsx +0 -232
  14. package/docs/AICODER.png +0 -0
  15. package/docs/INTERACTIVE_CLI_GUIDE.md +0 -201
  16. package/docs/TUI_MOCKUP.md +0 -180
  17. package/src/config/providers.ts +0 -174
  18. package/src/config/session.ts +0 -143
  19. package/src/context/bm25.ts +0 -173
  20. package/src/context/code-search.ts +0 -188
  21. package/src/context/context-pack.ts +0 -133
  22. package/src/context/dependency-mapper.ts +0 -72
  23. package/src/context/index.ts +0 -8
  24. package/src/context/symbol-extractor.ts +0 -149
  25. package/src/context/test-mapper.ts +0 -77
  26. package/src/context/types.ts +0 -69
  27. package/src/context/workspace-indexer.ts +0 -249
  28. package/src/index.ts +0 -5
  29. package/src/registries.ts +0 -118
  30. package/src/runtime/budget-manager.ts +0 -118
  31. package/src/runtime/interactive-runtime.ts +0 -423
  32. package/src/tasks-v2/analysis-tasks.ts +0 -311
  33. package/src/tasks-v2/developer-tasks.ts +0 -437
  34. package/src/tasks-v2/index.ts +0 -3
  35. package/src/tools-v2/edit-tools.ts +0 -153
  36. package/src/tools-v2/index.ts +0 -6
  37. package/src/tools-v2/read-tools.ts +0 -286
  38. package/src/tools-v2/search-tools.ts +0 -175
  39. package/src/tools-v2/test-tools.ts +0 -147
  40. package/src/tools-v2/workspace-context.ts +0 -428
  41. package/src/ui/App.tsx +0 -392
  42. package/src/ui/components/ChatPane.tsx +0 -84
  43. package/src/ui/components/EventsPane.tsx +0 -81
  44. package/src/ui/components/GoalsTasksPane.tsx +0 -149
  45. package/src/ui/store.ts +0 -362
  46. package/tests/integration.test.ts +0 -537
  47. package/tsconfig.json +0 -22
@@ -1,423 +0,0 @@
1
- // Interactive Runtime Orchestrator
2
- // Connects ACM framework components with the TUI
3
-
4
- import type { LLM } from '@ddse/acm-llm';
5
- import {
6
- type Goal,
7
- type Context,
8
- type Plan,
9
- type StreamSink,
10
- type NucleusConfig,
11
- type LLMCallFn,
12
- type CapabilityRegistry,
13
- ExternalContextProviderAdapter,
14
- } from '@ddse/acm-sdk';
15
- import { type PlannerResult } from '@ddse/acm-planner';
16
- import { ExecutionTranscript, MemoryLedger, type ExecutionTranscriptEvent } from '@ddse/acm-runtime';
17
- import { ACMFramework } from '@ddse/acm-framework';
18
- import type { SessionConfig } from '../config/session.js';
19
- import { BudgetManager } from './budget-manager.js';
20
- import { AppStore } from '../ui/store.js';
21
-
22
- export interface InteractiveRuntimeOptions {
23
- config: SessionConfig;
24
- llm: LLM;
25
- capabilityRegistry: CapabilityRegistry;
26
- toolRegistry: any;
27
- policyEngine: any;
28
- store: AppStore;
29
- contextProvider?: ExternalContextProviderAdapter;
30
- }
31
-
32
- export class InteractiveRuntime {
33
- private config: SessionConfig;
34
- private llm: LLM;
35
- private capabilityRegistry: CapabilityRegistry;
36
- private toolRegistry: any;
37
- private policyEngine: any;
38
- private store: AppStore;
39
- private budgetManager: BudgetManager;
40
- private ledger: MemoryLedger;
41
- private nucleusConfig: {
42
- llmCall: NucleusConfig['llmCall'];
43
- hooks?: NucleusConfig['hooks'];
44
- };
45
- private nucleusLLMCall: LLMCallFn;
46
- private transcript: ExecutionTranscript;
47
- private contextProvider?: ExternalContextProviderAdapter;
48
- private framework: ACMFramework;
49
-
50
- constructor(options: InteractiveRuntimeOptions) {
51
- this.config = options.config;
52
- this.llm = options.llm;
53
- this.capabilityRegistry = options.capabilityRegistry;
54
- this.toolRegistry = options.toolRegistry;
55
- this.policyEngine = options.policyEngine;
56
- this.store = options.store;
57
- this.contextProvider = options.contextProvider;
58
-
59
- this.budgetManager = new BudgetManager(options.config.model);
60
-
61
- this.ledger = new MemoryLedger();
62
-
63
- if (!this.llm.generateWithTools) {
64
- throw new Error('Configured LLM must support structured tool calls to drive Nucleus.');
65
- }
66
-
67
- this.nucleusLLMCall = async (prompt, tools, callConfig) => {
68
- const toolDefs = tools.map(tool => ({
69
- name: tool.name,
70
- description: tool.description ?? 'Interactive runtime tool',
71
- inputSchema: tool.inputSchema ?? { type: 'object', properties: {} },
72
- }));
73
-
74
- const response = await this.llm.generateWithTools!(
75
- [
76
- {
77
- role: 'system',
78
- content: prompt,
79
- },
80
- ],
81
- toolDefs,
82
- {
83
- temperature: callConfig.temperature,
84
- seed: callConfig.seed,
85
- maxTokens: callConfig.maxTokens,
86
- }
87
- );
88
-
89
- return {
90
- reasoning: response.text,
91
- toolCalls: (response.toolCalls ?? []).map(tc => ({
92
- id: tc.id,
93
- name: tc.name,
94
- input: tc.arguments,
95
- })),
96
- raw: response.raw,
97
- };
98
- };
99
-
100
- this.nucleusConfig = {
101
- llmCall: {
102
- provider: this.llm.name(),
103
- model: this.config.model,
104
- temperature: this.config.temperature ?? 0.1,
105
- maxTokens: 512,
106
- },
107
- hooks: {
108
- preflight: true,
109
- postcheck: true,
110
- },
111
- };
112
-
113
- this.framework = ACMFramework.create({
114
- capabilityRegistry: this.capabilityRegistry,
115
- toolRegistry: this.toolRegistry,
116
- policyEngine: this.policyEngine,
117
- contextProvider: this.contextProvider,
118
- verify: async () => true,
119
- nucleus: {
120
- call: this.nucleusLLMCall,
121
- llmConfig: this.nucleusConfig.llmCall,
122
- hooks: this.nucleusConfig.hooks,
123
- },
124
- });
125
-
126
- // Subscribe to ledger events
127
- this.setupLedgerSubscription();
128
-
129
- this.transcript = new ExecutionTranscript({
130
- onEvent: event => this.handleTranscriptEvent(event),
131
- });
132
- this.transcript.attach(this.ledger);
133
- }
134
-
135
- private setupLedgerSubscription(): void {
136
- // Monitor ledger entries and emit to event stream
137
- const originalAppend = this.ledger.append.bind(this.ledger);
138
- this.ledger.append = (type: any, details: Record<string, any>, computeDigest = true) => {
139
- const entry = originalAppend(type, details, computeDigest);
140
-
141
- // Map ledger entry to event
142
- const eventColors: Record<string, any> = {
143
- PLAN_SELECTED: 'green',
144
- TASK_START: 'blue',
145
- TASK_END: 'green',
146
- ERROR: 'red',
147
- POLICY_DECISION: 'yellow',
148
- VERIFICATION: 'blue',
149
- };
150
-
151
- this.store.addEvent(
152
- entry.type,
153
- entry.details,
154
- eventColors[entry.type] || 'gray'
155
- );
156
-
157
- if (entry.type === 'NUCLEUS_INFERENCE') {
158
- const reasoning = (entry.details && (entry.details.reasoning ?? entry.details.nucleus?.reasoning)) as
159
- | string
160
- | undefined;
161
-
162
- if (typeof reasoning === 'string' && reasoning.trim().length > 0) {
163
- this.store.addMessage('nucleus', reasoning.trim());
164
- }
165
- }
166
-
167
- // Update task status based on ledger entry
168
- if (entry.type === 'TASK_START' && entry.details.taskId) {
169
- this.store.updateTaskStatus(entry.details.taskId, 'running');
170
- } else if (entry.type === 'TASK_END' && entry.details.taskId) {
171
- this.store.updateTaskStatus(entry.details.taskId, 'succeeded');
172
- } else if (entry.type === 'ERROR' && entry.details.taskId) {
173
- this.store.updateTaskStatus(
174
- entry.details.taskId,
175
- 'failed',
176
- undefined,
177
- entry.details.message || entry.details.error || 'Unknown error'
178
- );
179
- }
180
-
181
- return entry;
182
- };
183
- }
184
-
185
- private handleTranscriptEvent(event: ExecutionTranscriptEvent): void {
186
- if (event.type === 'task-completed') {
187
- this.store.recordTaskOutput(event.taskId, event.output, event.narrative);
188
- } else if (event.type === 'goal-summary') {
189
- this.store.setGoalSummary(event.summary);
190
- }
191
- }
192
-
193
- async processGoal(goalText: string): Promise<void> {
194
- try {
195
- this.store.setProcessing(true);
196
- this.store.addMessage('user', goalText);
197
-
198
- // Create goal and context
199
- const goal: Goal = {
200
- id: `goal-${Date.now()}`,
201
- intent: goalText,
202
- };
203
-
204
- const context: Context = {
205
- id: `ctx-${Date.now()}`,
206
- facts: {
207
- workspace: this.config.workspace,
208
- timestamp: new Date().toISOString(),
209
- },
210
- };
211
-
212
- this.store.addEvent('GOAL_CREATED', { goalId: goal.id, intent: goalText }, 'green');
213
-
214
- // Create streaming sink for planner
215
- const streamSink: StreamSink = {
216
- attach: (source: string, callback: (chunk: any) => void) => {
217
- // Not used in this implementation
218
- },
219
- emit: (channel, event) => {
220
- if (channel === 'planner') {
221
- const msgs = this.store.getState().messages;
222
- const lastPlanner = msgs.filter(m => m.role === 'planner').pop();
223
-
224
- if ('delta' in event && event.delta) {
225
- // Stream planner reasoning
226
- if (lastPlanner && lastPlanner.streaming) {
227
- this.store.appendToMessage(lastPlanner.id, event.delta);
228
- } else {
229
- this.store.addMessage('planner', event.delta, true);
230
- }
231
- }
232
-
233
- const summaryParts: string[] = [];
234
- if (typeof event.plans === 'number') {
235
- const planLabel = event.plans === 1 ? 'plan' : 'plans';
236
- summaryParts.push(`Generated ${event.plans} ${planLabel}.`);
237
- }
238
- if (event.rationale) {
239
- summaryParts.push(event.rationale);
240
- }
241
-
242
- if (summaryParts.length > 0 && lastPlanner) {
243
- const summary = summaryParts.join('\n\n');
244
- this.store.updateMessage(lastPlanner.id, summary, false);
245
- } else if ('done' in event && event.done && lastPlanner) {
246
- // Mark streaming complete with default message if no summary
247
- const content = lastPlanner.content?.trim().length
248
- ? lastPlanner.content
249
- : 'Planner finished.';
250
- this.store.updateMessage(lastPlanner.id, content, false);
251
- }
252
- }
253
-
254
- if (channel === 'summary') {
255
- this.store.setGoalSummary((event as any)?.summary);
256
- }
257
- },
258
- close: (source: string) => {
259
- // Not used in this implementation
260
- },
261
- };
262
-
263
- // Token allowance check for planning
264
- let planningEstimate;
265
- try {
266
- const promptText = `Goal: ${goalText}\nContext: ${JSON.stringify(context.facts)}`;
267
- planningEstimate = this.budgetManager.checkBudget(promptText, 2000);
268
- this.store.addEvent('TOKEN_BUDGET_CHECK', {
269
- estimatedTokens: planningEstimate.totalTokens,
270
- inputTokens: planningEstimate.inputTokens,
271
- outputTokens: planningEstimate.outputTokens,
272
- }, 'blue');
273
- } catch (err: any) {
274
- this.store.addMessage('system', `Token allowance exceeded: ${err.message}`);
275
- this.store.setProcessing(false);
276
- return;
277
- }
278
-
279
- // Plan with ACM framework wrapper
280
- this.store.addMessage('planner', 'Planner is generating plan(s)...', true);
281
- const planResponse = await this.framework.plan({
282
- goal,
283
- context,
284
- planCount: this.config.planCount || 1,
285
- stream: streamSink,
286
- ledger: this.ledger,
287
- });
288
- const plannerResult = planResponse.result;
289
-
290
- // Record estimated token usage for planning stage
291
- if (planningEstimate) {
292
- this.budgetManager.recordUsage(planningEstimate.inputTokens, planningEstimate.outputTokens);
293
- }
294
- this.store.updateBudgetStatus(this.budgetManager.getStatus());
295
-
296
- if (plannerResult.plans.length === 0) {
297
- this.store.addMessage('system', 'No plans generated. Try rephrasing your goal.');
298
- this.store.setProcessing(false);
299
- return;
300
- }
301
-
302
- const selectedPlan = planResponse.selectedPlan;
303
- this.store.setGoal(goal, context, selectedPlan);
304
- this.store.addEvent('PLAN_SELECTED', {
305
- planId: selectedPlan.id,
306
- tasks: selectedPlan.tasks.length,
307
- }, 'green');
308
-
309
- // Execute plan
310
- await this.executePlan(goal, context, selectedPlan, plannerResult, streamSink);
311
-
312
- } catch (error: any) {
313
- this.store.addMessage('system', `Error: ${error.message}`);
314
- this.store.addEvent('ERROR', { message: error.message }, 'red');
315
- } finally {
316
- this.store.setProcessing(false);
317
- }
318
- }
319
-
320
- private async executePlan(
321
- goal: Goal,
322
- context: Context,
323
- plan: Plan,
324
- plannerResult: PlannerResult,
325
- stream: StreamSink,
326
- ): Promise<void> {
327
- try {
328
- const result = await this.framework.execute({
329
- goal,
330
- context,
331
- stream,
332
- ledger: this.ledger,
333
- runId: `run-${Date.now()}`,
334
- existingPlan: {
335
- plan,
336
- plannerResult,
337
- },
338
- });
339
- const execution = result.execution;
340
-
341
- this.store.addMessage('system', 'Goal completed successfully!');
342
- this.store.addEvent('GOAL_COMPLETED', { goalId: goal.id }, 'green');
343
-
344
- if (execution.goalSummary) {
345
- this.store.setGoalSummary(execution.goalSummary);
346
- }
347
-
348
- const completedTasks = this.store
349
- .getState()
350
- .tasks.filter(task => task.outputSummary && task.status === 'succeeded');
351
-
352
- if (completedTasks.length > 0) {
353
- const summaryLines = completedTasks.map(task => `• ${task.name}: ${task.outputSummary}`);
354
- this.store.addMessage('system', `Summary of task outputs:\n${summaryLines.join('\n')}`);
355
- } else if (Object.keys(execution.outputsByTask || {}).length === 0) {
356
- this.store.addMessage('system', 'Execution completed, but no tasks produced structured outputs.');
357
- }
358
-
359
- // Cleanup after goal
360
- await this.cleanupGoal();
361
-
362
- } catch (error: any) {
363
- this.store.addMessage('system', `Execution error: ${error.message}`);
364
- this.store.addEvent('EXECUTION_ERROR', { message: error.message }, 'red');
365
- }
366
- }
367
-
368
- private async cleanupGoal(): Promise<void> {
369
- // Persist replay bundle
370
- try {
371
- const fs = await import('fs/promises');
372
- const path = await import('path');
373
-
374
- const replayDir = path.join(this.config.workspace, '.aicoder', 'replays');
375
- const timestamp = new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '');
376
- const bundleDir = path.join(replayDir, timestamp);
377
-
378
- // Create directory
379
- await fs.mkdir(bundleDir, { recursive: true });
380
-
381
- // Save session config
382
- await fs.writeFile(
383
- path.join(bundleDir, 'session.json'),
384
- JSON.stringify(this.config, null, 2),
385
- 'utf-8'
386
- );
387
-
388
- // Save ledger entries
389
- const ledgerEntries = this.ledger.getEntries();
390
- await fs.writeFile(
391
- path.join(bundleDir, 'ledger.jsonl'),
392
- ledgerEntries.map(e => JSON.stringify(e)).join('\n'),
393
- 'utf-8'
394
- );
395
-
396
- // Save budget summary
397
- const budgetStatus = this.budgetManager.getStatus();
398
- await fs.writeFile(
399
- path.join(bundleDir, 'budget.json'),
400
- JSON.stringify(budgetStatus, null, 2),
401
- 'utf-8'
402
- );
403
-
404
- this.store.addEvent('REPLAY_SAVED', { path: bundleDir }, 'blue');
405
- this.store.addMessage('system', `Replay bundle saved to: ${bundleDir}`);
406
- } catch (err: any) {
407
- // Non-fatal
408
- this.store.addEvent('REPLAY_SAVE_ERROR', { error: err.message }, 'red');
409
- }
410
-
411
- // Reset budget for next goal
412
- this.budgetManager.reset();
413
- this.store.updateBudgetStatus(this.budgetManager.getStatus());
414
- }
415
-
416
- getBudgetManager(): BudgetManager {
417
- return this.budgetManager;
418
- }
419
-
420
- getLedger(): MemoryLedger {
421
- return this.ledger;
422
- }
423
- }