@olane/o-lane 0.8.1 → 0.8.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.
package/README.md CHANGED
@@ -14,12 +14,12 @@ The execution layer that manages AI agent processes through capability-based loo
14
14
  - 📊 **Execution Tracking** - Complete sequence history with cycle-by-cycle audit trails
15
15
  - 🌊 **Streaming Support** - Real-time progress updates to calling agents
16
16
  - 💾 **Persistent State** - Content-addressed storage of lane execution history
17
- - 🎯 **Pre-built Capabilities** - Evaluate, Task, Search, Configure, Error handling included
17
+ - 🎯 **Pre-built Capabilities** - Evaluate and Execute included, with Search, Configure, and Multiple Step planned
18
18
 
19
19
  ## Installation
20
20
 
21
21
  ```bash
22
- npm install @olane/o-lane
22
+ pnpm install @olane/o-lane
23
23
  ```
24
24
 
25
25
  ## Quick Start
@@ -53,7 +53,7 @@ await agent.start();
53
53
 
54
54
  ```typescript
55
55
  // Agent receives an intent and autonomously determines how to execute it
56
- const response = await agent.use({
56
+ const response = await agent.use(agent.address, {
57
57
  method: 'intent',
58
58
  params: {
59
59
  intent: 'Analyze the sales data and create a summary report',
@@ -62,15 +62,15 @@ const response = await agent.use({
62
62
  }
63
63
  });
64
64
 
65
- console.log(response.result);
65
+ console.log(response.result.data);
66
66
  // {
67
67
  // result: "Analysis complete. Created summary report with key insights...",
68
68
  // cycles: 5,
69
69
  // sequence: [
70
70
  // { type: 'EVALUATE', reasoning: '...' },
71
- // { type: 'TASK', result: '...' },
72
- // { type: 'SEARCH', result: '...' },
73
- // { type: 'TASK', result: '...' },
71
+ // { type: 'EXECUTE', result: '...' },
72
+ // { type: 'EVALUATE', result: '...' },
73
+ // { type: 'EXECUTE', result: '...' },
74
74
  // { type: 'STOP', result: '...' }
75
75
  // ]
76
76
  // }
@@ -256,7 +256,7 @@ The heart of o-lane's emergent orchestration:
256
256
  - **Maximum Cycles**: Configurable limit (default: 20) prevents infinite loops
257
257
  - **State Accumulation**: Each cycle builds on previous results
258
258
  - **Emergent Patterns**: Optimal workflows discovered through execution
259
- - **Fault Tolerance**: Errors trigger ERROR capability for recovery
259
+ - **Fault Tolerance**: Errors are handled within capability execution
260
260
 
261
261
  ```typescript
262
262
  // The loop runs automatically in lane.execute()
@@ -289,13 +289,13 @@ console.log(lane.sequence);
289
289
  // id: 'cap-1',
290
290
  // type: 'EVALUATE',
291
291
  // config: { intent: '...', context: '...' },
292
- // result: { reasoning: 'Need to search for information', next: 'SEARCH' }
292
+ // result: { reasoning: 'Need to fetch customer data', next: 'EXECUTE' }
293
293
  // },
294
294
  // {
295
295
  // id: 'cap-2',
296
- // type: 'SEARCH',
297
- // config: { query: 'customer data' },
298
- // result: { data: [...], next: 'TASK' }
296
+ // type: 'EXECUTE',
297
+ // config: { task: { address: 'o://data', method: 'get_customer' } },
298
+ // result: { data: [...], next: 'EVALUATE' }
299
299
  // },
300
300
  // // ... more cycles
301
301
  // ]
@@ -312,13 +312,15 @@ console.log(lane.agentHistory);
312
312
 
313
313
  ## Built-in Capabilities
314
314
 
315
- o-lane includes six core capabilities that handle most agentic workflows:
315
+ o-lane includes two active capabilities, with three additional ones planned:
316
+
317
+ ### Active Capabilities
316
318
 
317
319
  ### 1. Evaluate (`EVALUATE`)
318
320
 
319
321
  **Purpose**: Analyze the intent and determine the next capability to use.
320
322
 
321
- **When Used**:
323
+ **When Used**:
322
324
  - Start of every lane
323
325
  - After completing any other capability
324
326
  - When agent needs to reassess approach
@@ -327,18 +329,20 @@ o-lane includes six core capabilities that handle most agentic workflows:
327
329
  import { oCapabilityEvaluate } from '@olane/o-lane';
328
330
 
329
331
  // Automatically uses AI to evaluate intent and choose next step
330
- // Returns: { type: 'TASK' | 'SEARCH' | 'CONFIGURE' | 'STOP', reasoning: '...' }
332
+ // Returns: { type: 'EXECUTE' | 'STOP', reasoning: '...' }
331
333
  ```
332
334
 
333
- ### 2. Task (`TASK`)
335
+ ### 2. Execute (`EXECUTE`)
334
336
 
335
- **Purpose**: Execute a specific tool method with parameters.
337
+ **Purpose**: Execute a specific tool method with parameters determined by the AI agent.
336
338
 
337
339
  **When Used**: Agent needs to call a tool (API, database, computation, etc.)
338
340
 
339
341
  ```typescript
340
- // Agent decides to execute a task
341
- // Result: { type: 'TASK', config: { task: { address: 'o://tool', payload: {...} } } }
342
+ import { oCapabilityExecute } from '@olane/o-lane';
343
+
344
+ // Agent decides to execute a tool call
345
+ // Result: { type: 'EXECUTE', config: { task: { address: 'o://tool', payload: {...} } } }
342
346
 
343
347
  // Capability executes:
344
348
  const response = await this.node.use(new oAddress('o://analytics'), {
@@ -347,7 +351,9 @@ const response = await this.node.use(new oAddress('o://analytics'), {
347
351
  });
348
352
  ```
349
353
 
350
- ### 3. Search (`SEARCH`)
354
+ ### Planned Capabilities (not yet active)
355
+
356
+ ### 3. Search (`SEARCH`) *(planned)*
351
357
 
352
358
  **Purpose**: Query vector stores, registries, or knowledge bases for information.
353
359
 
@@ -358,7 +364,7 @@ const response = await this.node.use(new oAddress('o://analytics'), {
358
364
  // Can query vector stores, registries, or other search services
359
365
  ```
360
366
 
361
- ### 4. Configure (`CONFIGURE`)
367
+ ### 4. Configure (`CONFIGURE`) *(planned)*
362
368
 
363
369
  **Purpose**: Set up tool parameters, establish connections, or prepare environment.
364
370
 
@@ -369,19 +375,7 @@ const response = await this.node.use(new oAddress('o://analytics'), {
369
375
  // Returns configuration result and proceeds to next capability
370
376
  ```
371
377
 
372
- ### 5. Error (`ERROR`)
373
-
374
- **Purpose**: Handle errors gracefully and attempt recovery.
375
-
376
- **When Used**: Any capability encounters an error
377
-
378
- ```typescript
379
- // Automatically triggered on errors
380
- // Analyzes error, determines recovery strategy
381
- // Can retry, use alternative approach, or escalate
382
- ```
383
-
384
- ### 6. Multiple Step (`MULTIPLE_STEP`)
378
+ ### 5. Multiple Step (`MULTIPLE_STEP`) *(planned)*
385
379
 
386
380
  **Purpose**: Coordinate complex multi-step operations.
387
381
 
@@ -398,25 +392,21 @@ const response = await this.node.use(new oAddress('o://analytics'), {
398
392
  ```
399
393
  Intent: "Analyze Q4 sales and create report"
400
394
 
401
- Cycle 1: EVALUATE → "Need to search for sales data tool"
402
-
403
- Cycle 2: SEARCH → Found 'o://analytics/sales'
395
+ Cycle 1: EVALUATE → "Need to fetch sales data"
404
396
 
405
- Cycle 3: EVALUATE"Ready to fetch data"
397
+ Cycle 2: EXECUTEExecute o://analytics/sales/fetch_q4_data
406
398
 
407
- Cycle 4: TASKExecute o://analytics/sales/fetch_q4_data
399
+ Cycle 3: EVALUATE"Have data, need to analyze"
408
400
 
409
- Cycle 5: EVALUATE"Have data, need to analyze"
401
+ Cycle 4: EXECUTEExecute o://analytics/analyze
410
402
 
411
- Cycle 6: TASKExecute o://analytics/analyze
403
+ Cycle 5: EVALUATE"Analysis complete, create report"
412
404
 
413
- Cycle 7: EVALUATE"Analysis complete, create report"
405
+ Cycle 6: EXECUTEExecute o://reports/create
414
406
 
415
- Cycle 8: TASKExecute o://reports/create
407
+ Cycle 7: EVALUATE"Report created, done"
416
408
 
417
- Cycle 9: EVALUATE"Report created, done"
418
-
419
- Cycle 10: STOP → Return final result
409
+ Cycle 8: STOPReturn final result
420
410
  ```
421
411
 
422
412
  ## API Reference
@@ -434,12 +424,20 @@ new oLane(config: oLaneConfig)
434
424
  **Config Properties:**
435
425
  - `intent: oIntent` - The intent to resolve (required)
436
426
  - `caller: oAddress` - Address of the calling agent (required)
437
- - `currentNode: oLaneTool` - The agent tool executing the lane (required)
427
+ - `currentNode: oToolBase` - The tool executing the lane (required)
428
+ - `promptLoader: PromptLoader` - Prompt loader for capability prompts (required)
438
429
  - `context?: oLaneContext` - Historical or domain context
430
+ - `chatHistory?: string` - Chat history string for context
439
431
  - `streamTo?: oAddress` - Address to stream progress updates
440
- - `capabilities?: oCapability[]` - Custom capability set
432
+ - `capabilities?: oCapability[]` - Custom capability set (overrides `enabledCapabilityTypes`)
433
+ - `enabledCapabilityTypes?: oCapabilityType[]` - Filter which capability types to enable from `ALL_CAPABILITIES`
434
+ - `extraInstructions?: string` - Additional instructions for the AI agent
441
435
  - `maxCycles?: number` - Override default max cycles (20)
442
436
  - `parentLaneId?: string` - Parent lane for sub-lanes
437
+ - `persistToConfig?: boolean` - Persist lane for replay on startup
438
+ - `useStream?: boolean` - Enable streaming mode
439
+ - `onChunk?: (chunk: any) => void` - Callback for streaming chunks
440
+ - `requestId?: string | number` - Request ID for request/response correlation
443
441
 
444
442
  #### Properties
445
443
 
@@ -447,9 +445,11 @@ new oLane(config: oLaneConfig)
447
445
  - `sequence: oCapabilityResult[]` - Execution history
448
446
  - `status: oLaneStatus` - Current lifecycle state
449
447
  - `result: oCapabilityResult` - Final execution result
450
- - `id: string` - Unique lane identifier
448
+ - `id: string` - Unique lane identifier (UUID)
451
449
  - `cid?: CID` - Content identifier for storage
452
450
  - `agentHistory: string` - Formatted execution history
451
+ - `MAX_CYCLES: number` - Maximum capability loop iterations (default: 20)
452
+ - `onChunk?: (chunk: any) => void` - Streaming chunk callback
453
453
 
454
454
  #### Methods
455
455
 
@@ -471,7 +471,7 @@ Internal capability loop execution (called by execute).
471
471
  Add a capability result to the execution sequence.
472
472
 
473
473
  ```typescript
474
- lane.addSequence(new oCapabilityResult({ type: 'TASK', result: data }));
474
+ lane.addSequence(new oCapabilityResult({ type: oCapabilityType.EXECUTE, result: data }));
475
475
  ```
476
476
 
477
477
  **`async store(): Promise<void>`**
@@ -492,6 +492,23 @@ const cid = await lane.toCID();
492
492
  console.log(cid.toString()); // "bafyreib..."
493
493
  ```
494
494
 
495
+ **`getExecutionTrace(): string`**
496
+
497
+ Get a formatted trace of the lane execution for debugging.
498
+
499
+ ```typescript
500
+ const trace = lane.getExecutionTrace();
501
+ console.log(trace);
502
+ ```
503
+
504
+ **`async replay(cid: string): Promise<oCapabilityResult | undefined>`**
505
+
506
+ Replay a previously stored lane execution from its CID.
507
+
508
+ ```typescript
509
+ const result = await lane.replay('bafyreib...');
510
+ ```
511
+
495
512
  **`cancel(): void`**
496
513
 
497
514
  Cancel lane execution.
@@ -505,7 +522,15 @@ console.log(lane.status); // CANCELLED
505
522
 
506
523
  ### `oLaneTool` Class
507
524
 
508
- Extends `oNodeTool` with lane execution capabilities.
525
+ Extends `oNodeTool` with lane execution capabilities via the `withLane` mixin pattern:
526
+
527
+ ```typescript
528
+ // oLaneTool is defined as:
529
+ export class oLaneTool extends withLane(oNodeTool) { }
530
+
531
+ // The withLane mixin can be applied to any tool base class:
532
+ // class MyCustomLaneTool extends withLane(MyBaseClass) { }
533
+ ```
509
534
 
510
535
  #### Built-in Methods
511
536
 
@@ -514,7 +539,7 @@ Extends `oNodeTool` with lane execution capabilities.
514
539
  Perform capability negotiation with other agents.
515
540
 
516
541
  ```typescript
517
- const result = await agent.use({
542
+ const result = await agent.use(agent.address, {
518
543
  method: 'handshake',
519
544
  params: { intent: 'Discover capabilities' }
520
545
  });
@@ -526,7 +551,7 @@ const result = await agent.use({
526
551
  Main entry point for intent resolution.
527
552
 
528
553
  ```typescript
529
- const result = await agent.use({
554
+ const result = await agent.use(agent.address, {
530
555
  method: 'intent',
531
556
  params: {
532
557
  intent: 'Your natural language goal here',
@@ -536,6 +561,22 @@ const result = await agent.use({
536
561
  });
537
562
  ```
538
563
 
564
+ **`async _tool_replay(request: oRequest): Promise<any>`**
565
+
566
+ Replay a stored lane execution from its CID.
567
+
568
+ ```typescript
569
+ const result = await agent.use(agent.address, {
570
+ method: 'replay',
571
+ params: { cid: 'bafyreib...' }
572
+ });
573
+ // Returns: { result, error, cycles, cid }
574
+ ```
575
+
576
+ #### Customization
577
+
578
+ Override `getPromptLoader()` to provide a custom `PromptLoader` for controlling how prompts are loaded for each capability type.
579
+
539
580
  #### Usage Example
540
581
 
541
582
  ```typescript
@@ -550,7 +591,7 @@ class CustomAgent extends oLaneTool {
550
591
  // Add domain-specific tool methods
551
592
  async _tool_domain_action(request: oRequest): Promise<any> {
552
593
  // Your custom logic
553
- return { success: true };
594
+ return { result: 'done' };
554
595
  }
555
596
  }
556
597
  ```
@@ -619,12 +660,32 @@ const manager = new oLaneManager();
619
660
  const lane = await manager.createLane({
620
661
  intent: new oIntent({ intent: 'Your goal' }),
621
662
  currentNode: agentTool,
622
- caller: callerAddress
663
+ caller: callerAddress,
664
+ promptLoader: myPromptLoader
623
665
  });
624
666
 
625
667
  // Lane is automatically tracked
626
668
  ```
627
669
 
670
+ #### Properties
671
+
672
+ - `lanes: oLane[]` - All tracked lanes
673
+ - `activeLanes: oLane[]` - Currently running lanes
674
+ - `staleLanes: oLane[]` - Lanes that are no longer active
675
+ - `maxLanes: number` - Maximum concurrent lanes (default: 100)
676
+
677
+ #### Methods
678
+
679
+ **`async createLane(config: oLaneConfig): Promise<oLane>`** - Create and track a new lane.
680
+
681
+ **`getLane(id: string): oLane | undefined`** - Retrieve a lane by its ID.
682
+
683
+ **`cancelLane(lane: oLane): void`** - Cancel a running lane.
684
+
685
+ **`cleanLanes(): void`** - Remove completed/stale lanes from tracking.
686
+
687
+ **`async teardown(): Promise<void>`** - Cancel all active lanes and clean up.
688
+
628
689
  ---
629
690
 
630
691
  ### `oCapabilityResult` Class
@@ -643,10 +704,12 @@ const result = new oCapabilityResult({
643
704
  ```
644
705
 
645
706
  **Properties:**
707
+ - `id: string` - Unique result identifier
646
708
  - `type: oCapabilityType` - Next capability to execute
647
- - `result: any` - Execution result data
648
- - `config?: any` - Configuration for next capability
709
+ - `result?: any` - Execution result data
710
+ - `config?: oCapabilityConfig` - Configuration for next capability
649
711
  - `error?: string` - Error message if failed
712
+ - `shouldPersist?: boolean` - Whether this result should be persisted to storage
650
713
 
651
714
  ---
652
715
 
@@ -670,14 +733,14 @@ enum oLaneStatus {
670
733
 
671
734
  ```typescript
672
735
  enum oCapabilityType {
673
- EVALUATE = 'evaluate',
674
736
  TASK = 'task',
675
737
  SEARCH = 'search',
676
- CONFIGURE = 'configure',
677
- ERROR = 'error',
678
738
  MULTIPLE_STEP = 'multiple_step',
739
+ CONFIGURE = 'configure',
740
+ EXECUTE = 'execute',
741
+ HANDSHAKE = 'handshake',
742
+ EVALUATE = 'evaluate',
679
743
  STOP = 'stop',
680
- RESULT = 'result',
681
744
  UNKNOWN = 'unknown'
682
745
  }
683
746
  ```
@@ -699,17 +762,17 @@ const agent = new oLaneTool({
699
762
  await agent.start();
700
763
 
701
764
  // Resolve an intent
702
- const response = await agent.use({
765
+ const response = await agent.use(agent.address, {
703
766
  method: 'intent',
704
767
  params: {
705
768
  intent: 'Find the latest sales report and summarize key metrics'
706
769
  }
707
770
  });
708
771
 
709
- console.log('Result:', response.result);
710
- console.log('Cycles used:', response.cycles);
711
- console.log('Execution path:', response.sequence.map(s => s.type));
712
- // ['EVALUATE', 'SEARCH', 'TASK', 'EVALUATE', 'TASK', 'STOP']
772
+ console.log('Result:', response.result.data);
773
+ console.log('Cycles used:', response.result.data.cycles);
774
+ console.log('Execution path:', response.result.data.sequence.map(s => s.type));
775
+ // ['EVALUATE', 'EXECUTE', 'EVALUATE', 'EXECUTE', 'STOP']
713
776
  ```
714
777
 
715
778
  ### Custom Capability
@@ -741,9 +804,9 @@ class EmailCapability extends oCapability {
741
804
  });
742
805
  } catch (error) {
743
806
  return new oCapabilityResult({
744
- type: oCapabilityType.ERROR,
807
+ type: oCapabilityType.EVALUATE,
745
808
  error: error.message,
746
- config: { intent: this.intent, originalError: error }
809
+ config: { intent: this.intent }
747
810
  });
748
811
  }
749
812
  }
@@ -758,10 +821,11 @@ const lane = await manager.createLane({
758
821
  intent: new oIntent({ intent: 'Send status update to team' }),
759
822
  currentNode: agentTool,
760
823
  caller: callerAddress,
824
+ promptLoader: myPromptLoader,
761
825
  capabilities: [
762
826
  new oCapabilityEvaluate(),
763
- new EmailCapability(),
764
- new oCapabilityError()
827
+ new oCapabilityExecute(),
828
+ new EmailCapability()
765
829
  ]
766
830
  });
767
831
 
@@ -787,7 +851,7 @@ const receiver = new StreamReceiver({
787
851
  await receiver.start();
788
852
 
789
853
  // Execute intent with streaming
790
- const response = await agent.use({
854
+ const response = await agent.use(agent.address, {
791
855
  method: 'intent',
792
856
  params: {
793
857
  intent: 'Process large dataset',
@@ -807,8 +871,10 @@ const response = await agent.use({
807
871
  ```typescript
808
872
  import { oLaneContext } from '@olane/o-lane';
809
873
 
810
- // Provide conversation history or domain context
811
- const context = new oLaneContext([
874
+ // Create context and add entries
875
+ const context = new oLaneContext({});
876
+
877
+ context.addAll([
812
878
  '[Previous Conversation]',
813
879
  'User: What were our sales last quarter?',
814
880
  'Agent: Q3 sales were $1.2M, up 15% from Q2.',
@@ -816,7 +882,10 @@ const context = new oLaneContext([
816
882
  '[End Previous Conversation]'
817
883
  ]);
818
884
 
819
- const response = await agent.use({
885
+ // Or add individual entries
886
+ context.add('Additional context here');
887
+
888
+ const response = await agent.use(agent.address, {
820
889
  method: 'intent',
821
890
  params: {
822
891
  intent: 'Calculate Q4 projections',
@@ -831,7 +900,7 @@ const response = await agent.use({
831
900
 
832
901
  ```typescript
833
902
  // Complex intent requiring multiple capabilities
834
- const response = await agent.use({
903
+ const response = await agent.use(agent.address, {
835
904
  method: 'intent',
836
905
  params: {
837
906
  intent: 'Analyze customer feedback from last month, identify common themes, and create action items'
@@ -839,8 +908,9 @@ const response = await agent.use({
839
908
  });
840
909
 
841
910
  // Analyze the execution path
911
+ const data = response.result.data;
842
912
  console.log('Execution Analysis:');
843
- response.sequence.forEach((step, index) => {
913
+ data.sequence.forEach((step, index) => {
844
914
  console.log(`\nCycle ${index + 1}:`);
845
915
  console.log(` Type: ${step.type}`);
846
916
  console.log(` Reasoning: ${step.reasoning}`);
@@ -850,15 +920,15 @@ response.sequence.forEach((step, index) => {
850
920
  });
851
921
 
852
922
  // Output might show:
853
- // Cycle 1: EVALUATE - "Need to search for feedback collection tool"
854
- // Cycle 2: SEARCH - Found o://feedback/collector
855
- // Cycle 3: TASK - Fetched 145 feedback items
856
- // Cycle 4: EVALUATE - "Have data, need sentiment analysis"
857
- // Cycle 5: TASK - Analyzed sentiment across feedback
858
- // Cycle 6: EVALUATE - "Need to identify themes"
859
- // Cycle 7: TASK - Extracted 5 common themes
860
- // Cycle 8: EVALUATE - "Ready to create action items"
861
- // Cycle 9: TASK - Generated 8 action items
923
+ // Cycle 1: EVALUATE - "Need to fetch feedback data"
924
+ // Cycle 2: EXECUTE - Fetched 145 feedback items from o://feedback/collector
925
+ // Cycle 3: EVALUATE - "Have data, need sentiment analysis"
926
+ // Cycle 4: EXECUTE - Analyzed sentiment across feedback
927
+ // Cycle 5: EVALUATE - "Need to identify themes"
928
+ // Cycle 6: EXECUTE - Extracted 5 common themes
929
+ // Cycle 7: EVALUATE - "Ready to create action items"
930
+ // Cycle 8: EXECUTE - Generated 8 action items
931
+ // Cycle 9: EVALUATE - "All tasks complete"
862
932
  // Cycle 10: STOP - Completed successfully
863
933
  ```
864
934
 
@@ -902,7 +972,7 @@ class DatabaseQueryCapability extends oCapability {
902
972
  });
903
973
  } catch (error) {
904
974
  return new oCapabilityResult({
905
- type: oCapabilityType.ERROR,
975
+ type: oCapabilityType.EVALUATE,
906
976
  error: error.message
907
977
  });
908
978
  }
@@ -920,6 +990,20 @@ class DatabaseQueryCapability extends oCapability {
920
990
  - Integration with external systems
921
991
  - Performance-critical operations requiring optimization
922
992
 
993
+ ### Prompt Loading
994
+
995
+ Lanes require a `PromptLoader` to load prompts for each capability type. The `PromptLoader` abstract class provides a `loadPromptForType(type, params?, provider?)` method. A `PromptLoaderDefault` implementation is included for standard use cases.
996
+
997
+ To customize prompt loading in your `oLaneTool`, override `getPromptLoader()`:
998
+
999
+ ```typescript
1000
+ class MyAgent extends oLaneTool {
1001
+ getPromptLoader(): PromptLoader {
1002
+ return new MyCustomPromptLoader();
1003
+ }
1004
+ }
1005
+ ```
1006
+
923
1007
  ### Lane Context
924
1008
 
925
1009
  Provide rich context to help agents make better decisions:
@@ -928,7 +1012,8 @@ Provide rich context to help agents make better decisions:
928
1012
  import { oLaneContext } from '@olane/o-lane';
929
1013
 
930
1014
  // Conversation context
931
- const conversationContext = new oLaneContext([
1015
+ const conversationContext = new oLaneContext({});
1016
+ conversationContext.addAll([
932
1017
  '[Chat History]',
933
1018
  'User: Show me last quarter sales',
934
1019
  'Agent: Q3 sales: $1.2M (↑15%)',
@@ -936,7 +1021,8 @@ const conversationContext = new oLaneContext([
936
1021
  ]);
937
1022
 
938
1023
  // Domain knowledge context
939
- const domainContext = new oLaneContext([
1024
+ const domainContext = new oLaneContext({});
1025
+ domainContext.addAll([
940
1026
  '[Business Rules]',
941
1027
  '- Sales reports require manager approval',
942
1028
  '- Sensitive data must be redacted',
@@ -945,12 +1031,13 @@ const domainContext = new oLaneContext([
945
1031
  ]);
946
1032
 
947
1033
  // Combine contexts
948
- const combinedContext = new oLaneContext([
949
- ...conversationContext.contexts,
950
- ...domainContext.contexts
1034
+ const combinedContext = new oLaneContext({});
1035
+ combinedContext.addAll([
1036
+ conversationContext.toString(),
1037
+ domainContext.toString()
951
1038
  ]);
952
1039
 
953
- const response = await agent.use({
1040
+ const response = await agent.use(agent.address, {
954
1041
  method: 'intent',
955
1042
  params: {
956
1043
  intent: 'Generate Q4 sales report',
@@ -1027,7 +1114,7 @@ const tracker = new ProgressTracker({
1027
1114
  await tracker.start();
1028
1115
 
1029
1116
  // Long-running task with progress updates
1030
- const response = await agent.use({
1117
+ const response = await agent.use(agent.address, {
1031
1118
  method: 'intent',
1032
1119
  params: {
1033
1120
  intent: 'Process 10,000 customer records',
@@ -1044,7 +1131,7 @@ Access historical lane executions:
1044
1131
  import { oAddress } from '@olane/o-core';
1045
1132
 
1046
1133
  // Execute and store lane
1047
- const response = await agent.use({
1134
+ const response = await agent.use(agent.address, {
1048
1135
  method: 'intent',
1049
1136
  params: { intent: 'Analyze sales data' }
1050
1137
  });
@@ -1059,7 +1146,7 @@ const storedLane = await agent.use(new oAddress('o://lane'), {
1059
1146
  params: { key: cid.toString() }
1060
1147
  });
1061
1148
 
1062
- console.log('Historical execution:', storedLane.result);
1149
+ console.log('Historical execution:', storedLane.result.data);
1063
1150
  // {
1064
1151
  // config: { intent: '...', caller: '...' },
1065
1152
  // sequence: [...],
@@ -1419,10 +1506,9 @@ import { ALL_CAPABILITIES } from '@olane/o-lane';
1419
1506
  "Do analysis" → "Calculate average sales per region for Q4 2024"
1420
1507
 
1421
1508
  // 2. Provide context
1422
- context: new oLaneContext([
1423
- 'Available data: sales_q4.csv',
1424
- 'Required format: JSON with region, avg_sales fields'
1425
- ])
1509
+ const ctx = new oLaneContext({});
1510
+ ctx.addAll(['Available data: sales_q4.csv', 'Required format: JSON with region, avg_sales fields']);
1511
+ // then pass: context: ctx
1426
1512
 
1427
1513
  // 3. Add specialized capabilities
1428
1514
  capabilities: [...ALL_CAPABILITIES, new DataAnalysisCapability()]
@@ -6,6 +6,7 @@ export declare class oCapabilityExecute extends oCapabilityIntelligence {
6
6
  config: oCapabilityExecuteConfig;
7
7
  get type(): oCapabilityType;
8
8
  static get type(): oCapabilityType;
9
+ private resolveAddress;
9
10
  loadPrompt({ tools, methods }: any): Promise<string>;
10
11
  handshake(): Promise<oHandshakeResult>;
11
12
  run(): Promise<oCapabilityResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"execute.capability.d.ts","sourceRoot":"","sources":["../../../src/capabilities-execute/execute.capability.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAEvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AAEzF,qBAAa,kBAAmB,SAAQ,uBAAuB;IACtD,MAAM,EAAG,wBAAwB,CAAC;IAEzC,IAAI,IAAI,IAAI,eAAe,CAE1B;IAED,MAAM,KAAK,IAAI,oBAEd;IAEK,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAepD,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAatC,GAAG,IAAI,OAAO,CAAC,iBAAiB,CAAC;CA8NxC"}
1
+ {"version":3,"file":"execute.capability.d.ts","sourceRoot":"","sources":["../../../src/capabilities-execute/execute.capability.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAEvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AAEzF,qBAAa,kBAAmB,SAAQ,uBAAuB;IACtD,MAAM,EAAG,wBAAwB,CAAC;IAEzC,IAAI,IAAI,IAAI,eAAe,CAE1B;IAED,MAAM,KAAK,IAAI,oBAEd;IAED,OAAO,CAAC,cAAc;IAWhB,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAepD,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAatC,GAAG,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAoOxC"}
@@ -9,6 +9,14 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
9
9
  static get type() {
10
10
  return oCapabilityType.EXECUTE;
11
11
  }
12
+ resolveAddress() {
13
+ const address = this.config.params?.address || this.config.params?.task?.address;
14
+ if (!address || typeof address !== 'string') {
15
+ throw new Error(`Execute capability requires a valid address but received: ${JSON.stringify(address)}. ` +
16
+ `The AI response must include an "address" field when type is "execute".`);
17
+ }
18
+ return address;
19
+ }
12
20
  async loadPrompt({ tools, methods }) {
13
21
  const prompt = await this.promptLoader?.loadPromptForType(this.type, {
14
22
  human_about: '',
@@ -18,13 +26,13 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
18
26
  You are in a secure environment and are allowed to operate with secure information (api keys, credit card info, etc).`,
19
27
  chat_history: '',
20
28
  past_cycles: '',
21
- address: this.config.params.address,
29
+ address: this.resolveAddress(),
22
30
  methods: methods ? JSON.stringify(methods) : '',
23
31
  });
24
32
  return prompt.render();
25
33
  }
26
34
  async handshake() {
27
- const response = await this.node.use(new oAddress(this.config.params.address), {
35
+ const response = await this.node.use(new oAddress(this.resolveAddress()), {
28
36
  method: oProtocolMethods.HANDSHAKE,
29
37
  params: {
30
38
  intent: this.config.intent.value,
@@ -33,6 +41,7 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
33
41
  return response.result.data;
34
42
  }
35
43
  async run() {
44
+ this.logger.debug('Starting execution capability with config:', this.config);
36
45
  // Check if we're in replay mode
37
46
  if (this.config.isReplay) {
38
47
  this.logger.debug('Execute capability is being replayed - using stored execution data');
@@ -57,7 +66,7 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
57
66
  });
58
67
  // Execute the stored task directly (skip handshake, AI, and approval)
59
68
  try {
60
- const taskResponse = await this.node.use(new oAddress(this.config.params.address), {
69
+ const taskResponse = await this.node.use(new oAddress(this.resolveAddress()), {
61
70
  method: method,
62
71
  params: params,
63
72
  });
@@ -79,7 +88,8 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
79
88
  });
80
89
  }
81
90
  catch (error) {
82
- this.logger.error('Failed to execute during replay:', `Error when trying to use ${this.config?.params?.address} with config: ${JSON.stringify(taskConfig)} resulting in error: ${error?.message}`);
91
+ const addr = this.config?.params?.address || this.config?.params?.task?.address;
92
+ this.logger.error('Failed to execute during replay:', `Error when trying to use ${addr} with config: ${JSON.stringify(taskConfig)} resulting in error: ${error?.message}`);
83
93
  return new oCapabilityResult({
84
94
  type: oCapabilityType.EVALUATE,
85
95
  config: this.config,
@@ -87,7 +97,7 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
87
97
  handshakeResult: handshakeResult,
88
98
  taskConfig: taskConfig,
89
99
  },
90
- error: `Error when trying to use ${this.config?.params?.address} with config: ${JSON.stringify(taskConfig)} resulting in error: ${error?.message}`,
100
+ error: `Error when trying to use ${addr} with config: ${JSON.stringify(taskConfig)} resulting in error: ${error?.message}`,
91
101
  });
92
102
  }
93
103
  }
@@ -116,7 +126,7 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
116
126
  const approvalResponse = await this.node.use(new oAddress('o://approval'), {
117
127
  method: 'request_approval',
118
128
  params: {
119
- toolAddress: this.config.params.address,
129
+ toolAddress: this.resolveAddress(),
120
130
  method: method,
121
131
  params: params,
122
132
  intent: this.config.intent,
@@ -142,7 +152,7 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
142
152
  }
143
153
  try {
144
154
  // Execute the task
145
- const taskResponse = await this.node.use(new oAddress(this.config.params.address), {
155
+ const taskResponse = await this.node.use(new oAddress(this.resolveAddress()), {
146
156
  method: method,
147
157
  params: params,
148
158
  });
@@ -164,14 +174,15 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
164
174
  method: method,
165
175
  params: params,
166
176
  },
167
- address: this.config.params.address,
177
+ address: this.resolveAddress(),
168
178
  response: taskResponse.result,
169
179
  },
170
180
  shouldPersist,
171
181
  });
172
182
  }
173
183
  catch (error) {
174
- this.logger.error('Failed to execute:', `Error when trying to use ${this.config?.params?.address} with config: ${JSON.stringify({
184
+ const addr = this.config?.params?.address || this.config?.params?.task?.address;
185
+ this.logger.error('Failed to execute:', `Error when trying to use ${addr} with config: ${JSON.stringify({
175
186
  method: method,
176
187
  params: params,
177
188
  })} resulting in error: ${error?.message}`);
@@ -188,7 +199,7 @@ export class oCapabilityExecute extends oCapabilityIntelligence {
188
199
  params: params,
189
200
  },
190
201
  },
191
- error: `Error when trying to use ${this.config?.params?.address} with config: ${JSON.stringify({
202
+ error: `Error when trying to use ${addr} with config: ${JSON.stringify({
192
203
  method: method,
193
204
  params: params,
194
205
  })} resulting in error: ${error?.message}`,
@@ -14,6 +14,10 @@ export interface oCapabilityExecuteConfig extends oCapabilityConfig {
14
14
  method: string;
15
15
  params: any;
16
16
  };
17
+ task?: {
18
+ address: string;
19
+ intent?: string;
20
+ };
17
21
  };
18
22
  }
19
23
  //# sourceMappingURL=o-capability.configure-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-capability.configure-config.d.ts","sourceRoot":"","sources":["../../../../src/capabilities-execute/interfaces/o-capability.configure-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE;YACf,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,OAAO,EAAE;gBAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;aAAE,CAAC;SACrC,CAAC;QACF,UAAU,EAAE;YACV,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,GAAG,CAAC;SACb,CAAC;KACH,CAAC;CACH"}
1
+ {"version":3,"file":"o-capability.configure-config.d.ts","sourceRoot":"","sources":["../../../../src/capabilities-execute/interfaces/o-capability.configure-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE;YACf,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,OAAO,EAAE;gBAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;aAAE,CAAC;SACrC,CAAC;QACF,UAAU,EAAE;YACV,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,GAAG,CAAC;SACb,CAAC;QACF,IAAI,CAAC,EAAE;YACL,OAAO,EAAE,MAAM,CAAC;YAChB,MAAM,CAAC,EAAE,MAAM,CAAC;SACjB,CAAC;KACH,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=execute-capability-address.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-capability-address.spec.d.ts","sourceRoot":"","sources":["../../test/execute-capability-address.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ import { expect } from 'chai';
2
+ import { oCapabilityConfig } from '../src/capabilities/o-capability.config.js';
3
+ import { oCapabilityExecute } from '../src/capabilities-execute/execute.capability.js';
4
+ import { oIntent } from '../src/intent/o-intent.js';
5
+ /**
6
+ * Tests the interface contract between EVALUATE results and the EXECUTE capability.
7
+ *
8
+ * When EVALUATE's AI returns type:"execute", the response may place the address
9
+ * either at the root level or nested inside a `task` object. resultToConfig()
10
+ * passes the AI result as `params`, so the execute capability must handle both shapes.
11
+ */
12
+ describe('EVALUATE → EXECUTE handoff: address resolution', () => {
13
+ // Minimal stub that satisfies oToolBase enough for oCapabilityConfig
14
+ const stubNode = {};
15
+ const stubPromptLoader = {};
16
+ const intent = new oIntent({ intent: 'test intent' });
17
+ /**
18
+ * Simulates what resultToConfig() does: it takes the AI result object
19
+ * and sets it as `params` on the next capability's config.
20
+ */
21
+ function simulateResultToConfig(aiResult) {
22
+ const obj = aiResult;
23
+ return oCapabilityConfig.fromJSON({
24
+ params: typeof obj === 'object' ? obj : {},
25
+ intent,
26
+ node: stubNode,
27
+ });
28
+ }
29
+ /**
30
+ * Creates an oCapabilityExecute instance and sets its config,
31
+ * then calls the private resolveAddress() via the prototype.
32
+ */
33
+ function resolveAddressFrom(config) {
34
+ const capability = new oCapabilityExecute({
35
+ promptLoader: stubPromptLoader,
36
+ node: stubNode,
37
+ });
38
+ // Set config as execute() would
39
+ capability.config = config;
40
+ // Call private method
41
+ return capability.resolveAddress();
42
+ }
43
+ describe('resolveAddress()', () => {
44
+ it('should resolve address from params.address (flat AI response)', () => {
45
+ const aiResult = {
46
+ type: 'execute',
47
+ address: 'o://search',
48
+ summary: 'Searching for something',
49
+ reasoning: 'User wants to search',
50
+ };
51
+ const config = simulateResultToConfig(aiResult);
52
+ const address = resolveAddressFrom(config);
53
+ expect(address).to.equal('o://search');
54
+ });
55
+ it('should resolve address from params.task.address (nested AI response)', () => {
56
+ const aiResult = {
57
+ type: 'execute',
58
+ task: {
59
+ address: 'o://search',
60
+ intent: 'Search for card feed refresh functionality',
61
+ },
62
+ summary: 'Searching for card feed refresh functionality',
63
+ reasoning: 'The intent mentions refreshing',
64
+ };
65
+ const config = simulateResultToConfig(aiResult);
66
+ const address = resolveAddressFrom(config);
67
+ expect(address).to.equal('o://search');
68
+ });
69
+ it('should prefer params.address over params.task.address when both exist', () => {
70
+ const aiResult = {
71
+ type: 'execute',
72
+ address: 'o://primary',
73
+ task: {
74
+ address: 'o://fallback',
75
+ },
76
+ };
77
+ const config = simulateResultToConfig(aiResult);
78
+ const address = resolveAddressFrom(config);
79
+ expect(address).to.equal('o://primary');
80
+ });
81
+ it('should throw when neither address location is present', () => {
82
+ const aiResult = {
83
+ type: 'execute',
84
+ summary: 'Missing address entirely',
85
+ reasoning: 'Bad AI response',
86
+ };
87
+ const config = simulateResultToConfig(aiResult);
88
+ expect(() => resolveAddressFrom(config)).to.throw('Execute capability requires a valid address');
89
+ });
90
+ it('should throw when address is not a string', () => {
91
+ const aiResult = {
92
+ type: 'execute',
93
+ address: 123,
94
+ };
95
+ const config = simulateResultToConfig(aiResult);
96
+ expect(() => resolveAddressFrom(config)).to.throw('Execute capability requires a valid address');
97
+ });
98
+ });
99
+ describe('resultToConfig() params shape', () => {
100
+ it('should pass AI result object as params', () => {
101
+ const aiResult = {
102
+ type: 'execute',
103
+ address: 'o://search',
104
+ task: { address: 'o://search', intent: 'find things' },
105
+ summary: 'test',
106
+ };
107
+ const config = simulateResultToConfig(aiResult);
108
+ expect(config.params).to.deep.equal(aiResult);
109
+ });
110
+ it('should set params to empty object for non-object results', () => {
111
+ const config = simulateResultToConfig('not an object');
112
+ expect(config.params).to.deep.equal({});
113
+ });
114
+ });
115
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/o-lane",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -36,7 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@eslint/eslintrc": "^3.3.1",
38
38
  "@eslint/js": "^9.29.0",
39
- "@olane/o-test": "0.8.1",
39
+ "@olane/o-test": "0.8.3",
40
40
  "@tsconfig/node20": "^20.1.6",
41
41
  "@types/handlebars": "^4.1.0",
42
42
  "@types/jest": "^30.0.0",
@@ -57,15 +57,15 @@
57
57
  "typescript": "5.4.5"
58
58
  },
59
59
  "dependencies": {
60
- "@olane/o-config": "0.8.1",
61
- "@olane/o-core": "0.8.1",
62
- "@olane/o-node": "0.8.1",
63
- "@olane/o-protocol": "0.8.1",
64
- "@olane/o-storage": "0.8.1",
65
- "@olane/o-tool": "0.8.1",
60
+ "@olane/o-config": "0.8.3",
61
+ "@olane/o-core": "0.8.3",
62
+ "@olane/o-node": "0.8.3",
63
+ "@olane/o-protocol": "0.8.3",
64
+ "@olane/o-storage": "0.8.3",
65
+ "@olane/o-tool": "0.8.3",
66
66
  "debug": "^4.4.1",
67
67
  "dotenv": "^16.5.0",
68
68
  "handlebars": "^4.7.8"
69
69
  },
70
- "gitHead": "b84ad7d1c5411d38f3e7ab3c53d336195e7838e9"
70
+ "gitHead": "189c0cf7b6dd9d5d961f5424af21d37978092d9e"
71
71
  }