@ddse/acm-aicoder 0.5.0 → 0.5.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 (43) 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/tsconfig.tsbuildinfo +1 -1
  6. package/package.json +21 -7
  7. package/.aicoder/index.json +0 -304
  8. package/AICODER_IMPLEMENTATION_PLAN_PHASE2.md +0 -284
  9. package/bin/interactive.tsx +0 -232
  10. package/docs/AICODER.png +0 -0
  11. package/docs/INTERACTIVE_CLI_GUIDE.md +0 -201
  12. package/docs/TUI_MOCKUP.md +0 -180
  13. package/src/config/providers.ts +0 -174
  14. package/src/config/session.ts +0 -143
  15. package/src/context/bm25.ts +0 -173
  16. package/src/context/code-search.ts +0 -188
  17. package/src/context/context-pack.ts +0 -133
  18. package/src/context/dependency-mapper.ts +0 -72
  19. package/src/context/index.ts +0 -8
  20. package/src/context/symbol-extractor.ts +0 -149
  21. package/src/context/test-mapper.ts +0 -77
  22. package/src/context/types.ts +0 -69
  23. package/src/context/workspace-indexer.ts +0 -249
  24. package/src/index.ts +0 -5
  25. package/src/registries.ts +0 -118
  26. package/src/runtime/budget-manager.ts +0 -118
  27. package/src/runtime/interactive-runtime.ts +0 -423
  28. package/src/tasks-v2/analysis-tasks.ts +0 -311
  29. package/src/tasks-v2/developer-tasks.ts +0 -437
  30. package/src/tasks-v2/index.ts +0 -3
  31. package/src/tools-v2/edit-tools.ts +0 -153
  32. package/src/tools-v2/index.ts +0 -6
  33. package/src/tools-v2/read-tools.ts +0 -286
  34. package/src/tools-v2/search-tools.ts +0 -175
  35. package/src/tools-v2/test-tools.ts +0 -147
  36. package/src/tools-v2/workspace-context.ts +0 -428
  37. package/src/ui/App.tsx +0 -392
  38. package/src/ui/components/ChatPane.tsx +0 -84
  39. package/src/ui/components/EventsPane.tsx +0 -81
  40. package/src/ui/components/GoalsTasksPane.tsx +0 -149
  41. package/src/ui/store.ts +0 -362
  42. package/tests/integration.test.ts +0 -537
  43. 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
- }