@aj-archipelago/cortex 1.4.22 → 1.4.24

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 (30) hide show
  1. package/FILE_SYSTEM_DOCUMENTATION.md +116 -48
  2. package/config.js +9 -0
  3. package/lib/fileUtils.js +329 -214
  4. package/package.json +1 -1
  5. package/pathways/system/entity/files/sys_read_file_collection.js +22 -11
  6. package/pathways/system/entity/files/sys_update_file_metadata.js +18 -8
  7. package/pathways/system/entity/sys_entity_agent.js +8 -6
  8. package/pathways/system/entity/tools/sys_tool_codingagent.js +4 -4
  9. package/pathways/system/entity/tools/sys_tool_editfile.js +35 -24
  10. package/pathways/system/entity/tools/sys_tool_file_collection.js +93 -36
  11. package/pathways/system/entity/tools/sys_tool_image.js +1 -1
  12. package/pathways/system/entity/tools/sys_tool_image_gemini.js +1 -1
  13. package/pathways/system/entity/tools/sys_tool_readfile.js +4 -4
  14. package/pathways/system/entity/tools/sys_tool_slides_gemini.js +1 -1
  15. package/pathways/system/entity/tools/sys_tool_video_veo.js +1 -1
  16. package/pathways/system/entity/tools/sys_tool_view_image.js +10 -5
  17. package/pathways/system/workspaces/run_workspace_agent.js +4 -1
  18. package/pathways/video_seedance.js +2 -0
  19. package/server/executeWorkspace.js +45 -2
  20. package/server/pathwayResolver.js +18 -0
  21. package/server/plugins/replicateApiPlugin.js +18 -0
  22. package/server/typeDef.js +10 -1
  23. package/tests/integration/features/tools/fileCollection.test.js +254 -248
  24. package/tests/integration/features/tools/fileOperations.test.js +131 -81
  25. package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +3 -4
  26. package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +3 -4
  27. package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +3 -4
  28. package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +5 -5
  29. package/tests/unit/core/fileCollection.test.js +86 -25
  30. package/pathways/system/workspaces/run_workspace_research_agent.js +0 -27
@@ -21,14 +21,17 @@ test.after.always('cleanup', async () => {
21
21
  }
22
22
  });
23
23
 
24
- // Helper to create a test context
24
+ // Helper to create a test context (returns agentContext array)
25
25
  const createTestContext = () => {
26
26
  const contextId = `test-fileops-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
27
- return contextId;
27
+ return {
28
+ contextId,
29
+ agentContext: [{ contextId, contextKey: null, default: true }]
30
+ };
28
31
  };
29
32
 
30
33
  // Helper to clean up test data
31
- const cleanup = async (contextId, contextKey = null) => {
34
+ const cleanup = async (contextId) => {
32
35
  try {
33
36
  const { getRedisClient } = await import('../../../../lib/fileUtils.js');
34
37
  const redisClient = await getRedisClient();
@@ -44,14 +47,14 @@ const cleanup = async (contextId, contextKey = null) => {
44
47
  // ========== WriteFile Tests ==========
45
48
 
46
49
  test('WriteFile: Write and upload text file', async t => {
47
- const contextId = createTestContext();
50
+ const { contextId, agentContext } = createTestContext();
48
51
 
49
52
  try {
50
53
  const content = 'Hello, world!\nThis is a test file.';
51
54
  const filename = 'test.txt';
52
55
 
53
56
  const result = await callPathway('sys_tool_writefile', {
54
- contextId,
57
+ agentContext,
55
58
  content,
56
59
  filename,
57
60
  userMessage: 'Writing test file'
@@ -77,14 +80,14 @@ test('WriteFile: Write and upload text file', async t => {
77
80
  });
78
81
 
79
82
  test('WriteFile: Write JSON file', async t => {
80
- const contextId = createTestContext();
83
+ const { contextId, agentContext } = createTestContext();
81
84
 
82
85
  try {
83
86
  const content = JSON.stringify({ name: 'Test', value: 42 }, null, 2);
84
87
  const filename = 'data.json';
85
88
 
86
89
  const result = await callPathway('sys_tool_writefile', {
87
- contextId,
90
+ agentContext,
88
91
  content,
89
92
  filename,
90
93
  userMessage: 'Writing JSON file'
@@ -110,13 +113,13 @@ test('WriteFile: Write JSON file', async t => {
110
113
  // ========== ReadFile Tests ==========
111
114
 
112
115
  test('ReadFile: Read entire file', async t => {
113
- const contextId = createTestContext();
116
+ const { contextId, agentContext } = createTestContext();
114
117
 
115
118
  try {
116
119
  // First write a file
117
120
  const content = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
118
121
  const writeResult = await callPathway('sys_tool_writefile', {
119
- contextId,
122
+ agentContext,
120
123
  content,
121
124
  filename: 'readtest.txt',
122
125
  userMessage: 'Writing file for read test'
@@ -135,7 +138,7 @@ test('ReadFile: Read entire file', async t => {
135
138
 
136
139
  // Now read it
137
140
  const readResult = await callPathway('sys_tool_readfile', {
138
- contextId,
141
+ agentContext,
139
142
  file: writeParsed.fileId || 'readtest.txt',
140
143
  userMessage: 'Reading entire file'
141
144
  });
@@ -151,13 +154,13 @@ test('ReadFile: Read entire file', async t => {
151
154
  });
152
155
 
153
156
  test('ReadFile: Read line range', async t => {
154
- const contextId = createTestContext();
157
+ const { contextId, agentContext } = createTestContext();
155
158
 
156
159
  try {
157
160
  // First write a file
158
161
  const content = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
159
162
  const writeResult = await callPathway('sys_tool_writefile', {
160
- contextId,
163
+ agentContext,
161
164
  content,
162
165
  filename: 'rangetest.txt',
163
166
  userMessage: 'Writing file for range read test'
@@ -175,7 +178,7 @@ test('ReadFile: Read line range', async t => {
175
178
 
176
179
  // Read lines 2-4
177
180
  const readResult = await callPathway('sys_tool_readfile', {
178
- contextId,
181
+ agentContext,
179
182
  file: writeParsed.fileId || 'rangetest.txt',
180
183
  startLine: 2,
181
184
  endLine: 4,
@@ -195,7 +198,7 @@ test('ReadFile: Read line range', async t => {
195
198
  });
196
199
 
197
200
  test('ReadFile: Read with line range limit', async t => {
198
- const contextId = createTestContext();
201
+ const { contextId, agentContext } = createTestContext();
199
202
 
200
203
  try {
201
204
  // Write a large file
@@ -203,7 +206,7 @@ test('ReadFile: Read with line range limit', async t => {
203
206
  const content = lines.join('\n');
204
207
 
205
208
  const writeResult = await callPathway('sys_tool_writefile', {
206
- contextId,
209
+ agentContext,
207
210
  content,
208
211
  filename: 'largetest.txt',
209
212
  userMessage: 'Writing large file'
@@ -221,7 +224,7 @@ test('ReadFile: Read with line range limit', async t => {
221
224
 
222
225
  // Read with endLine limit (first 10 lines)
223
226
  const readResult = await callPathway('sys_tool_readfile', {
224
- contextId,
227
+ agentContext,
225
228
  file: writeParsed.fileId || 'largetest.txt',
226
229
  startLine: 1,
227
230
  endLine: 10,
@@ -241,13 +244,13 @@ test('ReadFile: Read with line range limit', async t => {
241
244
  // ========== EditFileByLine Tests ==========
242
245
 
243
246
  test('EditFileByLine: Replace single line', async t => {
244
- const contextId = createTestContext();
247
+ const { contextId, agentContext } = createTestContext();
245
248
 
246
249
  try {
247
250
  // Write initial file
248
251
  const initialContent = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
249
252
  const writeResult = await callPathway('sys_tool_writefile', {
250
- contextId,
253
+ agentContext,
251
254
  content: initialContent,
252
255
  filename: 'modifytest.txt',
253
256
  userMessage: 'Writing file for modify test'
@@ -269,7 +272,7 @@ test('EditFileByLine: Replace single line', async t => {
269
272
 
270
273
  // Modify line 3
271
274
  const modifyResult = await callPathway('sys_tool_editfile', {
272
- contextId,
275
+ agentContext,
273
276
  file: writeParsed.fileId || 'modifytest.txt',
274
277
  startLine: 3,
275
278
  endLine: 3,
@@ -285,7 +288,7 @@ test('EditFileByLine: Replace single line', async t => {
285
288
  // Read back to verify
286
289
  await new Promise(resolve => setTimeout(resolve, 500));
287
290
  const readResult = await callPathway('sys_tool_readfile', {
288
- contextId,
291
+ agentContext,
289
292
  file: modifyParsed.fileId || 'modifytest.txt',
290
293
  userMessage: 'Reading modified file'
291
294
  });
@@ -300,13 +303,13 @@ test('EditFileByLine: Replace single line', async t => {
300
303
  });
301
304
 
302
305
  test('EditFileByLine: Replace multiple lines', async t => {
303
- const contextId = createTestContext();
306
+ const { contextId, agentContext } = createTestContext();
304
307
 
305
308
  try {
306
309
  // Write initial file
307
310
  const initialContent = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
308
311
  const writeResult = await callPathway('sys_tool_writefile', {
309
- contextId,
312
+ agentContext,
310
313
  content: initialContent,
311
314
  filename: 'multimodify.txt',
312
315
  userMessage: 'Writing file for multi-line modify'
@@ -324,7 +327,7 @@ test('EditFileByLine: Replace multiple lines', async t => {
324
327
 
325
328
  // Replace lines 2-4 with new content
326
329
  const modifyResult = await callPathway('sys_tool_editfile', {
327
- contextId,
330
+ agentContext,
328
331
  file: writeParsed.fileId || 'multimodify.txt',
329
332
  startLine: 2,
330
333
  endLine: 4,
@@ -340,7 +343,7 @@ test('EditFileByLine: Replace multiple lines', async t => {
340
343
  // Read back to verify
341
344
  await new Promise(resolve => setTimeout(resolve, 500));
342
345
  const readResult = await callPathway('sys_tool_readfile', {
343
- contextId,
346
+ agentContext,
344
347
  file: modifyParsed.fileId || 'multimodify.txt',
345
348
  userMessage: 'Reading modified file'
346
349
  });
@@ -359,13 +362,13 @@ test('EditFileByLine: Replace multiple lines', async t => {
359
362
  });
360
363
 
361
364
  test('EditFileByLine: Insert content (replace with more lines)', async t => {
362
- const contextId = createTestContext();
365
+ const { contextId, agentContext } = createTestContext();
363
366
 
364
367
  try {
365
368
  // Write initial file
366
369
  const initialContent = 'Line 1\nLine 2\nLine 3';
367
370
  const writeResult = await callPathway('sys_tool_writefile', {
368
- contextId,
371
+ agentContext,
369
372
  content: initialContent,
370
373
  filename: 'inserttest.txt',
371
374
  userMessage: 'Writing file for insert test'
@@ -383,7 +386,7 @@ test('EditFileByLine: Insert content (replace with more lines)', async t => {
383
386
 
384
387
  // Replace line 2 with 3 new lines
385
388
  const modifyResult = await callPathway('sys_tool_editfile', {
386
- contextId,
389
+ agentContext,
387
390
  file: writeParsed.fileId || 'inserttest.txt',
388
391
  startLine: 2,
389
392
  endLine: 2,
@@ -400,7 +403,7 @@ test('EditFileByLine: Insert content (replace with more lines)', async t => {
400
403
  // Read back to verify
401
404
  await new Promise(resolve => setTimeout(resolve, 500));
402
405
  const readResult = await callPathway('sys_tool_readfile', {
403
- contextId,
406
+ agentContext,
404
407
  file: modifyParsed.fileId || 'inserttest.txt',
405
408
  userMessage: 'Reading modified file'
406
409
  });
@@ -420,13 +423,13 @@ test('EditFileByLine: Insert content (replace with more lines)', async t => {
420
423
  });
421
424
 
422
425
  test('EditFileByLine: Delete content (replace with fewer lines)', async t => {
423
- const contextId = createTestContext();
426
+ const { contextId, agentContext } = createTestContext();
424
427
 
425
428
  try {
426
429
  // Write initial file
427
430
  const initialContent = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
428
431
  const writeResult = await callPathway('sys_tool_writefile', {
429
- contextId,
432
+ agentContext,
430
433
  content: initialContent,
431
434
  filename: 'deletetest.txt',
432
435
  userMessage: 'Writing file for delete test'
@@ -444,7 +447,7 @@ test('EditFileByLine: Delete content (replace with fewer lines)', async t => {
444
447
 
445
448
  // Replace lines 2-4 with a single line
446
449
  const modifyResult = await callPathway('sys_tool_editfile', {
447
- contextId,
450
+ agentContext,
448
451
  file: writeParsed.fileId || 'deletetest.txt',
449
452
  startLine: 2,
450
453
  endLine: 4,
@@ -461,7 +464,7 @@ test('EditFileByLine: Delete content (replace with fewer lines)', async t => {
461
464
  // Read back to verify
462
465
  await new Promise(resolve => setTimeout(resolve, 500));
463
466
  const readResult = await callPathway('sys_tool_readfile', {
464
- contextId,
467
+ agentContext,
465
468
  file: modifyParsed.fileId || 'deletetest.txt',
466
469
  userMessage: 'Reading modified file'
467
470
  });
@@ -479,11 +482,11 @@ test('EditFileByLine: Delete content (replace with fewer lines)', async t => {
479
482
  });
480
483
 
481
484
  test('EditFileByLine: Error handling - file not found', async t => {
482
- const contextId = createTestContext();
485
+ const { contextId, agentContext } = createTestContext();
483
486
 
484
487
  try {
485
488
  const result = await callPathway('sys_tool_editfile', {
486
- contextId,
489
+ agentContext,
487
490
  file: 'nonexistent.txt',
488
491
  startLine: 1,
489
492
  endLine: 1,
@@ -500,12 +503,12 @@ test('EditFileByLine: Error handling - file not found', async t => {
500
503
  });
501
504
 
502
505
  test('EditFileByLine: Error handling - invalid line range', async t => {
503
- const contextId = createTestContext();
506
+ const { contextId, agentContext } = createTestContext();
504
507
 
505
508
  try {
506
509
  // Write a file first
507
510
  const writeResult = await callPathway('sys_tool_writefile', {
508
- contextId,
511
+ agentContext,
509
512
  content: 'Line 1\nLine 2',
510
513
  filename: 'rangetest.txt',
511
514
  userMessage: 'Writing test file'
@@ -523,7 +526,7 @@ test('EditFileByLine: Error handling - invalid line range', async t => {
523
526
 
524
527
  // Try invalid range (endLine < startLine)
525
528
  const result = await callPathway('sys_tool_editfile', {
526
- contextId,
529
+ agentContext,
527
530
  file: writeParsed.fileId || 'rangetest.txt',
528
531
  startLine: 5,
529
532
  endLine: 3,
@@ -540,13 +543,13 @@ test('EditFileByLine: Error handling - invalid line range', async t => {
540
543
  });
541
544
 
542
545
  test('EditFileByLine: Works after prior SearchAndReplace edit', async t => {
543
- const contextId = createTestContext();
546
+ const { contextId, agentContext } = createTestContext();
544
547
 
545
548
  try {
546
549
  // Write initial file
547
550
  const initialContent = 'Version: v1\nLine2: alpha\nLine3: bravo\nLine4: charlie';
548
551
  const writeResult = await callPathway('sys_tool_writefile', {
549
- contextId,
552
+ agentContext,
550
553
  content: initialContent,
551
554
  filename: 'smoketest-tools.txt',
552
555
  userMessage: 'Writing file for sequential edit test'
@@ -567,7 +570,7 @@ test('EditFileByLine: Works after prior SearchAndReplace edit', async t => {
567
570
 
568
571
  // First edit: SearchAndReplace (changes hash)
569
572
  const searchReplaceResult = await callPathway('sys_tool_editfile', {
570
- contextId,
573
+ agentContext,
571
574
  file: fileId,
572
575
  oldString: 'Version: v1',
573
576
  newString: 'Version: v2',
@@ -585,7 +588,7 @@ test('EditFileByLine: Works after prior SearchAndReplace edit', async t => {
585
588
 
586
589
  // Second edit: EditFileByLine (should work after hash change)
587
590
  const editByLineResult = await callPathway('sys_tool_editfile', {
588
- contextId,
591
+ agentContext,
589
592
  file: fileId, // Use same fileId - should resolve correctly after hash change
590
593
  startLine: 3,
591
594
  endLine: 3,
@@ -601,7 +604,7 @@ test('EditFileByLine: Works after prior SearchAndReplace edit', async t => {
601
604
  // Verify final content
602
605
  await new Promise(resolve => setTimeout(resolve, 500));
603
606
  const readResult = await callPathway('sys_tool_readfile', {
604
- contextId,
607
+ agentContext,
605
608
  file: fileId,
606
609
  userMessage: 'Reading final file content'
607
610
  });
@@ -616,13 +619,13 @@ test('EditFileByLine: Works after prior SearchAndReplace edit', async t => {
616
619
  });
617
620
 
618
621
  test('ReadTextFile: Gets fresh content after EditFileByLine', async t => {
619
- const contextId = createTestContext();
622
+ const { contextId, agentContext } = createTestContext();
620
623
 
621
624
  try {
622
625
  // Write initial file
623
626
  const initialContent = 'Line1: alpha\nLine2: bravo\nLine3: charlie';
624
627
  const writeResult = await callPathway('sys_tool_writefile', {
625
- contextId,
628
+ agentContext,
626
629
  content: initialContent,
627
630
  filename: 'read-after-edit.txt',
628
631
  userMessage: 'Writing file for read-after-edit test'
@@ -643,7 +646,7 @@ test('ReadTextFile: Gets fresh content after EditFileByLine', async t => {
643
646
 
644
647
  // Edit the file
645
648
  const editResult = await callPathway('sys_tool_editfile', {
646
- contextId,
649
+ agentContext,
647
650
  file: fileId,
648
651
  startLine: 2,
649
652
  endLine: 2,
@@ -659,7 +662,7 @@ test('ReadTextFile: Gets fresh content after EditFileByLine', async t => {
659
662
 
660
663
  // Read file - should get fresh content (not cached)
661
664
  const readResult = await callPathway('sys_tool_readfile', {
662
- contextId,
665
+ agentContext,
663
666
  file: fileId,
664
667
  userMessage: 'Reading file after edit'
665
668
  });
@@ -674,12 +677,12 @@ test('ReadTextFile: Gets fresh content after EditFileByLine', async t => {
674
677
  });
675
678
 
676
679
  test('EditFileByLine: Error handling - line out of range', async t => {
677
- const contextId = createTestContext();
680
+ const { contextId, agentContext } = createTestContext();
678
681
 
679
682
  try {
680
683
  // Write a file with 2 lines
681
684
  const writeResult = await callPathway('sys_tool_writefile', {
682
- contextId,
685
+ agentContext,
683
686
  content: 'Line 1\nLine 2',
684
687
  filename: 'rangetest2.txt',
685
688
  userMessage: 'Writing test file'
@@ -697,7 +700,7 @@ test('EditFileByLine: Error handling - line out of range', async t => {
697
700
 
698
701
  // Try to modify line 10 (doesn't exist)
699
702
  const result = await callPathway('sys_tool_editfile', {
700
- contextId,
703
+ agentContext,
701
704
  file: writeParsed.fileId || 'rangetest2.txt',
702
705
  startLine: 10,
703
706
  endLine: 10,
@@ -716,13 +719,13 @@ test('EditFileByLine: Error handling - line out of range', async t => {
716
719
  // ========== EditFileBySearchAndReplace Tests ==========
717
720
 
718
721
  test('EditFileBySearchAndReplace: Replace first occurrence', async t => {
719
- const contextId = createTestContext();
722
+ const { contextId, agentContext } = createTestContext();
720
723
 
721
724
  try {
722
725
  // Write initial file
723
726
  const initialContent = 'Hello world\nThis is a test\nHello again';
724
727
  const writeResult = await callPathway('sys_tool_writefile', {
725
- contextId,
728
+ agentContext,
726
729
  content: initialContent,
727
730
  filename: 'searchreplace.txt',
728
731
  userMessage: 'Writing file for search replace test'
@@ -740,7 +743,7 @@ test('EditFileBySearchAndReplace: Replace first occurrence', async t => {
740
743
 
741
744
  // Replace first occurrence of "Hello"
742
745
  const modifyResult = await callPathway('sys_tool_editfile', {
743
- contextId,
746
+ agentContext,
744
747
  file: writeParsed.fileId || 'searchreplace.txt',
745
748
  oldString: 'Hello',
746
749
  newString: 'Hi',
@@ -758,7 +761,7 @@ test('EditFileBySearchAndReplace: Replace first occurrence', async t => {
758
761
  // Read back to verify
759
762
  await new Promise(resolve => setTimeout(resolve, 500));
760
763
  const readResult = await callPathway('sys_tool_readfile', {
761
- contextId,
764
+ agentContext,
762
765
  file: modifyParsed.fileId || 'searchreplace.txt',
763
766
  userMessage: 'Reading modified file'
764
767
  });
@@ -772,13 +775,13 @@ test('EditFileBySearchAndReplace: Replace first occurrence', async t => {
772
775
  });
773
776
 
774
777
  test('EditFileBySearchAndReplace: Replace all occurrences', async t => {
775
- const contextId = createTestContext();
778
+ const { contextId, agentContext } = createTestContext();
776
779
 
777
780
  try {
778
781
  // Write initial file
779
782
  const initialContent = 'Hello world\nThis is a test\nHello again';
780
783
  const writeResult = await callPathway('sys_tool_writefile', {
781
- contextId,
784
+ agentContext,
782
785
  content: initialContent,
783
786
  filename: 'searchreplaceall.txt',
784
787
  userMessage: 'Writing file for search replace all test'
@@ -796,7 +799,7 @@ test('EditFileBySearchAndReplace: Replace all occurrences', async t => {
796
799
 
797
800
  // Replace all occurrences of "Hello"
798
801
  const modifyResult = await callPathway('sys_tool_editfile', {
799
- contextId,
802
+ agentContext,
800
803
  file: writeParsed.fileId || 'searchreplaceall.txt',
801
804
  oldString: 'Hello',
802
805
  newString: 'Hi',
@@ -813,7 +816,7 @@ test('EditFileBySearchAndReplace: Replace all occurrences', async t => {
813
816
  // Read back to verify
814
817
  await new Promise(resolve => setTimeout(resolve, 500));
815
818
  const readResult = await callPathway('sys_tool_readfile', {
816
- contextId,
819
+ agentContext,
817
820
  file: modifyParsed.fileId || 'searchreplaceall.txt',
818
821
  userMessage: 'Reading modified file'
819
822
  });
@@ -827,13 +830,13 @@ test('EditFileBySearchAndReplace: Replace all occurrences', async t => {
827
830
  });
828
831
 
829
832
  test('EditFileBySearchAndReplace: Replace multiline string', async t => {
830
- const contextId = createTestContext();
833
+ const { contextId, agentContext } = createTestContext();
831
834
 
832
835
  try {
833
836
  // Write initial file
834
837
  const initialContent = 'Line 1\nLine 2\nLine 3\nLine 2\nLine 4';
835
838
  const writeResult = await callPathway('sys_tool_writefile', {
836
- contextId,
839
+ agentContext,
837
840
  content: initialContent,
838
841
  filename: 'multiline.txt',
839
842
  userMessage: 'Writing file for multiline replace test'
@@ -851,7 +854,7 @@ test('EditFileBySearchAndReplace: Replace multiline string', async t => {
851
854
 
852
855
  // Replace multiline string
853
856
  const modifyResult = await callPathway('sys_tool_editfile', {
854
- contextId,
857
+ agentContext,
855
858
  file: writeParsed.fileId || 'multiline.txt',
856
859
  oldString: 'Line 2\nLine 3',
857
860
  newString: 'Replaced 2\nReplaced 3',
@@ -865,7 +868,7 @@ test('EditFileBySearchAndReplace: Replace multiline string', async t => {
865
868
  // Read back to verify
866
869
  await new Promise(resolve => setTimeout(resolve, 500));
867
870
  const readResult = await callPathway('sys_tool_readfile', {
868
- contextId,
871
+ agentContext,
869
872
  file: modifyParsed.fileId || 'multiline.txt',
870
873
  userMessage: 'Reading modified file'
871
874
  });
@@ -879,12 +882,12 @@ test('EditFileBySearchAndReplace: Replace multiline string', async t => {
879
882
  });
880
883
 
881
884
  test('EditFileBySearchAndReplace: Error handling - string not found', async t => {
882
- const contextId = createTestContext();
885
+ const { contextId, agentContext } = createTestContext();
883
886
 
884
887
  try {
885
888
  // Write a file
886
889
  const writeResult = await callPathway('sys_tool_writefile', {
887
- contextId,
890
+ agentContext,
888
891
  content: 'Line 1\nLine 2',
889
892
  filename: 'notfound.txt',
890
893
  userMessage: 'Writing test file'
@@ -902,7 +905,7 @@ test('EditFileBySearchAndReplace: Error handling - string not found', async t =>
902
905
 
903
906
  // Try to replace a string that doesn't exist
904
907
  const result = await callPathway('sys_tool_editfile', {
905
- contextId,
908
+ agentContext,
906
909
  file: writeParsed.fileId || 'notfound.txt',
907
910
  oldString: 'This string does not exist',
908
911
  newString: 'replacement',
@@ -920,13 +923,13 @@ test('EditFileBySearchAndReplace: Error handling - string not found', async t =>
920
923
  // ========== Data Integrity Tests ==========
921
924
 
922
925
  test('EditFile: Old file preserved if upload fails (data integrity)', async t => {
923
- const contextId = createTestContext();
926
+ const { contextId, agentContext } = createTestContext();
924
927
 
925
928
  try {
926
929
  // Write initial file
927
930
  const initialContent = 'Original content\nLine 2\nLine 3';
928
931
  const writeResult = await callPathway('sys_tool_writefile', {
929
- contextId,
932
+ agentContext,
930
933
  content: initialContent,
931
934
  filename: 'integrity-test.txt',
932
935
  userMessage: 'Writing file for integrity test'
@@ -948,7 +951,7 @@ test('EditFile: Old file preserved if upload fails (data integrity)', async t =>
948
951
  // Verify original file is readable
949
952
  await new Promise(resolve => setTimeout(resolve, 500));
950
953
  const readOriginal = await callPathway('sys_tool_readfile', {
951
- contextId,
954
+ agentContext,
952
955
  file: originalFileId,
953
956
  userMessage: 'Reading original file'
954
957
  });
@@ -958,7 +961,7 @@ test('EditFile: Old file preserved if upload fails (data integrity)', async t =>
958
961
 
959
962
  // Edit the file (this should upload first, then delete old file)
960
963
  const modifyResult = await callPathway('sys_tool_editfile', {
961
- contextId,
964
+ agentContext,
962
965
  file: originalFileId,
963
966
  startLine: 1,
964
967
  endLine: 1,
@@ -978,7 +981,7 @@ test('EditFile: Old file preserved if upload fails (data integrity)', async t =>
978
981
  // Verify new file has correct content
979
982
  await new Promise(resolve => setTimeout(resolve, 500));
980
983
  const readModified = await callPathway('sys_tool_readfile', {
981
- contextId,
984
+ agentContext,
982
985
  file: modifyParsed.fileId || originalFileId,
983
986
  userMessage: 'Reading modified file'
984
987
  });
@@ -989,7 +992,7 @@ test('EditFile: Old file preserved if upload fails (data integrity)', async t =>
989
992
 
990
993
  // Verify file collection was updated with new URL (proves upload happened first)
991
994
  const listResult = await callPathway('sys_tool_file_collection', {
992
- contextId,
995
+ agentContext,
993
996
  userMessage: 'List files'
994
997
  });
995
998
  const listParsed = JSON.parse(listResult);
@@ -1016,13 +1019,13 @@ test('EditFile: Old file preserved if upload fails (data integrity)', async t =>
1016
1019
  // ========== Serialization Tests ==========
1017
1020
 
1018
1021
  test('EditFile: Concurrent edits are serialized (no race conditions)', async t => {
1019
- const contextId = createTestContext();
1022
+ const { contextId, agentContext } = createTestContext();
1020
1023
 
1021
1024
  try {
1022
1025
  // Write initial file with numbered lines
1023
1026
  const initialContent = 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5';
1024
1027
  const writeResult = await callPathway('sys_tool_writefile', {
1025
- contextId,
1028
+ agentContext,
1026
1029
  content: initialContent,
1027
1030
  filename: 'serialization-test.txt',
1028
1031
  userMessage: 'Writing file for serialization test'
@@ -1092,7 +1095,7 @@ test('EditFile: Concurrent edits are serialized (no race conditions)', async t =
1092
1095
 
1093
1096
  // Read the final file content
1094
1097
  const readResult = await callPathway('sys_tool_readfile', {
1095
- contextId,
1098
+ agentContext,
1096
1099
  file: fileId,
1097
1100
  userMessage: 'Reading final file after concurrent edits'
1098
1101
  });
@@ -1116,13 +1119,13 @@ test('EditFile: Concurrent edits are serialized (no race conditions)', async t =
1116
1119
  });
1117
1120
 
1118
1121
  test('EditFile: Sequential edits maintain order (serialization verification)', async t => {
1119
- const contextId = createTestContext();
1122
+ const { contextId, agentContext } = createTestContext();
1120
1123
 
1121
1124
  try {
1122
1125
  // Write initial file
1123
1126
  const initialContent = 'Version: 0';
1124
1127
  const writeResult = await callPathway('sys_tool_writefile', {
1125
- contextId,
1128
+ agentContext,
1126
1129
  content: initialContent,
1127
1130
  filename: 'order-test.txt',
1128
1131
  userMessage: 'Writing file for order test'
@@ -1184,7 +1187,7 @@ test('EditFile: Sequential edits maintain order (serialization verification)', a
1184
1187
 
1185
1188
  // Read final content
1186
1189
  const readResult = await callPathway('sys_tool_readfile', {
1187
- contextId,
1190
+ agentContext,
1188
1191
  file: fileId,
1189
1192
  userMessage: 'Reading final file'
1190
1193
  });
@@ -1208,12 +1211,12 @@ test('EditFile: Sequential edits maintain order (serialization verification)', a
1208
1211
  // ========== Integration Tests ==========
1209
1212
 
1210
1213
  test('File Operations: Write, Read, Modify workflow', async t => {
1211
- const contextId = createTestContext();
1214
+ const { contextId, agentContext } = createTestContext();
1212
1215
 
1213
1216
  try {
1214
1217
  // 1. Write a file
1215
1218
  const writeResult = await callPathway('sys_tool_writefile', {
1216
- contextId,
1219
+ agentContext,
1217
1220
  content: 'Initial content\nLine 2\nLine 3',
1218
1221
  filename: 'workflow.txt',
1219
1222
  userMessage: 'Writing initial file'
@@ -1234,7 +1237,7 @@ test('File Operations: Write, Read, Modify workflow', async t => {
1234
1237
 
1235
1238
  // 2. Read the file
1236
1239
  const readResult = await callPathway('sys_tool_readfile', {
1237
- contextId,
1240
+ agentContext,
1238
1241
  file: fileId,
1239
1242
  userMessage: 'Reading file'
1240
1243
  });
@@ -1247,7 +1250,7 @@ test('File Operations: Write, Read, Modify workflow', async t => {
1247
1250
 
1248
1251
  // 3. Modify the file
1249
1252
  const modifyResult = await callPathway('sys_tool_editfile', {
1250
- contextId,
1253
+ agentContext,
1251
1254
  file: fileId,
1252
1255
  startLine: 2,
1253
1256
  endLine: 2,
@@ -1262,7 +1265,7 @@ test('File Operations: Write, Read, Modify workflow', async t => {
1262
1265
 
1263
1266
  // 4. Read again to verify modification
1264
1267
  const readResult2 = await callPathway('sys_tool_readfile', {
1265
- contextId,
1268
+ agentContext,
1266
1269
  file: fileId,
1267
1270
  userMessage: 'Reading modified file'
1268
1271
  });
@@ -1276,3 +1279,50 @@ test('File Operations: Write, Read, Modify workflow', async t => {
1276
1279
  }
1277
1280
  });
1278
1281
 
1282
+ // ========== Backward Compatibility Test ==========
1283
+
1284
+ test('Backward compat: contextId without agentContext still works', async t => {
1285
+ // Test that passing contextId directly (without agentContext) still works
1286
+ // The pathwayResolver should automatically create agentContext from contextId
1287
+ const contextId = `test-backcompat-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1288
+
1289
+ try {
1290
+ const content = 'Backward compatibility test content';
1291
+ const filename = 'backcompat.txt';
1292
+
1293
+ // Use contextId directly instead of agentContext
1294
+ const result = await callPathway('sys_tool_writefile', {
1295
+ contextId, // Legacy format - no agentContext
1296
+ content,
1297
+ filename,
1298
+ userMessage: 'Testing backward compatibility'
1299
+ });
1300
+
1301
+ const parsed = JSON.parse(result);
1302
+
1303
+ // Skip test if file handler is not configured
1304
+ if (!parsed.success && parsed.error?.includes('WHISPER_MEDIA_API_URL')) {
1305
+ t.log('Test skipped - file handler URL not configured');
1306
+ t.pass();
1307
+ return;
1308
+ }
1309
+
1310
+ t.is(parsed.success, true, 'Write with legacy contextId should succeed');
1311
+ t.is(parsed.filename, filename);
1312
+ t.truthy(parsed.url);
1313
+
1314
+ // Also test read with legacy format
1315
+ const readResult = await callPathway('sys_tool_readfile', {
1316
+ contextId, // Legacy format
1317
+ file: parsed.fileId || filename,
1318
+ userMessage: 'Reading with legacy contextId'
1319
+ });
1320
+
1321
+ const readParsed = JSON.parse(readResult);
1322
+ t.is(readParsed.success, true, 'Read with legacy contextId should succeed');
1323
+ t.is(readParsed.content, content);
1324
+ } finally {
1325
+ await cleanup(contextId);
1326
+ }
1327
+ });
1328
+