@renseiai/agentfactory 0.8.0
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/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/src/config/index.d.ts +3 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +1 -0
- package/dist/src/config/repository-config.d.ts +44 -0
- package/dist/src/config/repository-config.d.ts.map +1 -0
- package/dist/src/config/repository-config.js +88 -0
- package/dist/src/config/repository-config.test.d.ts +2 -0
- package/dist/src/config/repository-config.test.d.ts.map +1 -0
- package/dist/src/config/repository-config.test.js +249 -0
- package/dist/src/deployment/deployment-checker.d.ts +110 -0
- package/dist/src/deployment/deployment-checker.d.ts.map +1 -0
- package/dist/src/deployment/deployment-checker.js +242 -0
- package/dist/src/deployment/index.d.ts +3 -0
- package/dist/src/deployment/index.d.ts.map +1 -0
- package/dist/src/deployment/index.js +2 -0
- package/dist/src/frontend/index.d.ts +2 -0
- package/dist/src/frontend/index.d.ts.map +1 -0
- package/dist/src/frontend/index.js +1 -0
- package/dist/src/frontend/types.d.ts +106 -0
- package/dist/src/frontend/types.d.ts.map +1 -0
- package/dist/src/frontend/types.js +11 -0
- package/dist/src/governor/decision-engine.d.ts +52 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -0
- package/dist/src/governor/decision-engine.js +220 -0
- package/dist/src/governor/decision-engine.test.d.ts +2 -0
- package/dist/src/governor/decision-engine.test.d.ts.map +1 -0
- package/dist/src/governor/decision-engine.test.js +629 -0
- package/dist/src/governor/event-bus.d.ts +43 -0
- package/dist/src/governor/event-bus.d.ts.map +1 -0
- package/dist/src/governor/event-bus.js +8 -0
- package/dist/src/governor/event-deduplicator.d.ts +43 -0
- package/dist/src/governor/event-deduplicator.d.ts.map +1 -0
- package/dist/src/governor/event-deduplicator.js +53 -0
- package/dist/src/governor/event-driven-governor.d.ts +131 -0
- package/dist/src/governor/event-driven-governor.d.ts.map +1 -0
- package/dist/src/governor/event-driven-governor.js +379 -0
- package/dist/src/governor/event-driven-governor.test.d.ts +2 -0
- package/dist/src/governor/event-driven-governor.test.d.ts.map +1 -0
- package/dist/src/governor/event-driven-governor.test.js +673 -0
- package/dist/src/governor/event-types.d.ts +78 -0
- package/dist/src/governor/event-types.d.ts.map +1 -0
- package/dist/src/governor/event-types.js +32 -0
- package/dist/src/governor/governor-types.d.ts +82 -0
- package/dist/src/governor/governor-types.d.ts.map +1 -0
- package/dist/src/governor/governor-types.js +21 -0
- package/dist/src/governor/governor.d.ts +100 -0
- package/dist/src/governor/governor.d.ts.map +1 -0
- package/dist/src/governor/governor.js +262 -0
- package/dist/src/governor/governor.test.d.ts +2 -0
- package/dist/src/governor/governor.test.d.ts.map +1 -0
- package/dist/src/governor/governor.test.js +514 -0
- package/dist/src/governor/human-touchpoints.d.ts +131 -0
- package/dist/src/governor/human-touchpoints.d.ts.map +1 -0
- package/dist/src/governor/human-touchpoints.js +251 -0
- package/dist/src/governor/human-touchpoints.test.d.ts +2 -0
- package/dist/src/governor/human-touchpoints.test.d.ts.map +1 -0
- package/dist/src/governor/human-touchpoints.test.js +366 -0
- package/dist/src/governor/in-memory-event-bus.d.ts +29 -0
- package/dist/src/governor/in-memory-event-bus.d.ts.map +1 -0
- package/dist/src/governor/in-memory-event-bus.js +79 -0
- package/dist/src/governor/index.d.ts +14 -0
- package/dist/src/governor/index.d.ts.map +1 -0
- package/dist/src/governor/index.js +13 -0
- package/dist/src/governor/override-parser.d.ts +60 -0
- package/dist/src/governor/override-parser.d.ts.map +1 -0
- package/dist/src/governor/override-parser.js +98 -0
- package/dist/src/governor/override-parser.test.d.ts +2 -0
- package/dist/src/governor/override-parser.test.d.ts.map +1 -0
- package/dist/src/governor/override-parser.test.js +312 -0
- package/dist/src/governor/platform-adapter.d.ts +69 -0
- package/dist/src/governor/platform-adapter.d.ts.map +1 -0
- package/dist/src/governor/platform-adapter.js +11 -0
- package/dist/src/governor/processing-state.d.ts +66 -0
- package/dist/src/governor/processing-state.d.ts.map +1 -0
- package/dist/src/governor/processing-state.js +43 -0
- package/dist/src/governor/processing-state.test.d.ts +2 -0
- package/dist/src/governor/processing-state.test.d.ts.map +1 -0
- package/dist/src/governor/processing-state.test.js +96 -0
- package/dist/src/governor/top-of-funnel.d.ts +118 -0
- package/dist/src/governor/top-of-funnel.d.ts.map +1 -0
- package/dist/src/governor/top-of-funnel.js +168 -0
- package/dist/src/governor/top-of-funnel.test.d.ts +2 -0
- package/dist/src/governor/top-of-funnel.test.d.ts.map +1 -0
- package/dist/src/governor/top-of-funnel.test.js +331 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +10 -0
- package/dist/src/linear-cli.d.ts +38 -0
- package/dist/src/linear-cli.d.ts.map +1 -0
- package/dist/src/linear-cli.js +674 -0
- package/dist/src/logger.d.ts +117 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +430 -0
- package/dist/src/manifest/generate.d.ts +20 -0
- package/dist/src/manifest/generate.d.ts.map +1 -0
- package/dist/src/manifest/generate.js +65 -0
- package/dist/src/manifest/index.d.ts +4 -0
- package/dist/src/manifest/index.d.ts.map +1 -0
- package/dist/src/manifest/index.js +2 -0
- package/dist/src/manifest/route-manifest.d.ts +34 -0
- package/dist/src/manifest/route-manifest.d.ts.map +1 -0
- package/dist/src/manifest/route-manifest.js +148 -0
- package/dist/src/orchestrator/activity-emitter.d.ts +119 -0
- package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -0
- package/dist/src/orchestrator/activity-emitter.js +306 -0
- package/dist/src/orchestrator/api-activity-emitter.d.ts +167 -0
- package/dist/src/orchestrator/api-activity-emitter.d.ts.map +1 -0
- package/dist/src/orchestrator/api-activity-emitter.js +417 -0
- package/dist/src/orchestrator/heartbeat-writer.d.ts +57 -0
- package/dist/src/orchestrator/heartbeat-writer.d.ts.map +1 -0
- package/dist/src/orchestrator/heartbeat-writer.js +137 -0
- package/dist/src/orchestrator/index.d.ts +20 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -0
- package/dist/src/orchestrator/index.js +22 -0
- package/dist/src/orchestrator/log-analyzer.d.ts +160 -0
- package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -0
- package/dist/src/orchestrator/log-analyzer.js +572 -0
- package/dist/src/orchestrator/log-config.d.ts +39 -0
- package/dist/src/orchestrator/log-config.d.ts.map +1 -0
- package/dist/src/orchestrator/log-config.js +45 -0
- package/dist/src/orchestrator/orchestrator.d.ts +316 -0
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/src/orchestrator/orchestrator.js +3290 -0
- package/dist/src/orchestrator/parse-work-result.d.ts +16 -0
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -0
- package/dist/src/orchestrator/parse-work-result.js +135 -0
- package/dist/src/orchestrator/parse-work-result.test.d.ts +2 -0
- package/dist/src/orchestrator/parse-work-result.test.d.ts.map +1 -0
- package/dist/src/orchestrator/parse-work-result.test.js +234 -0
- package/dist/src/orchestrator/progress-logger.d.ts +72 -0
- package/dist/src/orchestrator/progress-logger.d.ts.map +1 -0
- package/dist/src/orchestrator/progress-logger.js +135 -0
- package/dist/src/orchestrator/session-logger.d.ts +159 -0
- package/dist/src/orchestrator/session-logger.d.ts.map +1 -0
- package/dist/src/orchestrator/session-logger.js +275 -0
- package/dist/src/orchestrator/state-recovery.d.ts +96 -0
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -0
- package/dist/src/orchestrator/state-recovery.js +302 -0
- package/dist/src/orchestrator/state-types.d.ts +165 -0
- package/dist/src/orchestrator/state-types.d.ts.map +1 -0
- package/dist/src/orchestrator/state-types.js +7 -0
- package/dist/src/orchestrator/stream-parser.d.ts +151 -0
- package/dist/src/orchestrator/stream-parser.d.ts.map +1 -0
- package/dist/src/orchestrator/stream-parser.js +137 -0
- package/dist/src/orchestrator/types.d.ts +232 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -0
- package/dist/src/orchestrator/types.js +4 -0
- package/dist/src/orchestrator/validate-git-remote.test.d.ts +2 -0
- package/dist/src/orchestrator/validate-git-remote.test.d.ts.map +1 -0
- package/dist/src/orchestrator/validate-git-remote.test.js +61 -0
- package/dist/src/providers/a2a-auth.d.ts +81 -0
- package/dist/src/providers/a2a-auth.d.ts.map +1 -0
- package/dist/src/providers/a2a-auth.js +188 -0
- package/dist/src/providers/a2a-auth.test.d.ts +2 -0
- package/dist/src/providers/a2a-auth.test.d.ts.map +1 -0
- package/dist/src/providers/a2a-auth.test.js +232 -0
- package/dist/src/providers/a2a-provider.d.ts +254 -0
- package/dist/src/providers/a2a-provider.d.ts.map +1 -0
- package/dist/src/providers/a2a-provider.integration.test.d.ts +9 -0
- package/dist/src/providers/a2a-provider.integration.test.d.ts.map +1 -0
- package/dist/src/providers/a2a-provider.integration.test.js +665 -0
- package/dist/src/providers/a2a-provider.js +811 -0
- package/dist/src/providers/a2a-provider.test.d.ts +2 -0
- package/dist/src/providers/a2a-provider.test.d.ts.map +1 -0
- package/dist/src/providers/a2a-provider.test.js +681 -0
- package/dist/src/providers/amp-provider.d.ts +20 -0
- package/dist/src/providers/amp-provider.d.ts.map +1 -0
- package/dist/src/providers/amp-provider.js +24 -0
- package/dist/src/providers/claude-provider.d.ts +18 -0
- package/dist/src/providers/claude-provider.d.ts.map +1 -0
- package/dist/src/providers/claude-provider.js +437 -0
- package/dist/src/providers/codex-provider.d.ts +133 -0
- package/dist/src/providers/codex-provider.d.ts.map +1 -0
- package/dist/src/providers/codex-provider.js +381 -0
- package/dist/src/providers/codex-provider.test.d.ts +2 -0
- package/dist/src/providers/codex-provider.test.d.ts.map +1 -0
- package/dist/src/providers/codex-provider.test.js +387 -0
- package/dist/src/providers/index.d.ts +44 -0
- package/dist/src/providers/index.d.ts.map +1 -0
- package/dist/src/providers/index.js +85 -0
- package/dist/src/providers/spring-ai-provider.d.ts +90 -0
- package/dist/src/providers/spring-ai-provider.d.ts.map +1 -0
- package/dist/src/providers/spring-ai-provider.integration.test.d.ts +13 -0
- package/dist/src/providers/spring-ai-provider.integration.test.d.ts.map +1 -0
- package/dist/src/providers/spring-ai-provider.integration.test.js +351 -0
- package/dist/src/providers/spring-ai-provider.js +317 -0
- package/dist/src/providers/spring-ai-provider.test.d.ts +2 -0
- package/dist/src/providers/spring-ai-provider.test.d.ts.map +1 -0
- package/dist/src/providers/spring-ai-provider.test.js +200 -0
- package/dist/src/providers/types.d.ts +165 -0
- package/dist/src/providers/types.d.ts.map +1 -0
- package/dist/src/providers/types.js +13 -0
- package/dist/src/templates/adapters.d.ts +51 -0
- package/dist/src/templates/adapters.d.ts.map +1 -0
- package/dist/src/templates/adapters.js +104 -0
- package/dist/src/templates/adapters.test.d.ts +2 -0
- package/dist/src/templates/adapters.test.d.ts.map +1 -0
- package/dist/src/templates/adapters.test.js +165 -0
- package/dist/src/templates/agent-definition.d.ts +85 -0
- package/dist/src/templates/agent-definition.d.ts.map +1 -0
- package/dist/src/templates/agent-definition.js +97 -0
- package/dist/src/templates/agent-definition.test.d.ts +2 -0
- package/dist/src/templates/agent-definition.test.d.ts.map +1 -0
- package/dist/src/templates/agent-definition.test.js +209 -0
- package/dist/src/templates/index.d.ts +14 -0
- package/dist/src/templates/index.d.ts.map +1 -0
- package/dist/src/templates/index.js +11 -0
- package/dist/src/templates/loader.d.ts +41 -0
- package/dist/src/templates/loader.d.ts.map +1 -0
- package/dist/src/templates/loader.js +114 -0
- package/dist/src/templates/registry.d.ts +80 -0
- package/dist/src/templates/registry.d.ts.map +1 -0
- package/dist/src/templates/registry.js +177 -0
- package/dist/src/templates/registry.test.d.ts +2 -0
- package/dist/src/templates/registry.test.d.ts.map +1 -0
- package/dist/src/templates/registry.test.js +198 -0
- package/dist/src/templates/renderer.d.ts +29 -0
- package/dist/src/templates/renderer.d.ts.map +1 -0
- package/dist/src/templates/renderer.js +35 -0
- package/dist/src/templates/strategy-templates.test.d.ts +2 -0
- package/dist/src/templates/strategy-templates.test.d.ts.map +1 -0
- package/dist/src/templates/strategy-templates.test.js +619 -0
- package/dist/src/templates/types.d.ts +233 -0
- package/dist/src/templates/types.d.ts.map +1 -0
- package/dist/src/templates/types.js +127 -0
- package/dist/src/templates/types.test.d.ts +2 -0
- package/dist/src/templates/types.test.d.ts.map +1 -0
- package/dist/src/templates/types.test.js +232 -0
- package/dist/src/tools/index.d.ts +6 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +3 -0
- package/dist/src/tools/linear-runner.d.ts +34 -0
- package/dist/src/tools/linear-runner.d.ts.map +1 -0
- package/dist/src/tools/linear-runner.js +700 -0
- package/dist/src/tools/plugins/linear.d.ts +9 -0
- package/dist/src/tools/plugins/linear.d.ts.map +1 -0
- package/dist/src/tools/plugins/linear.js +138 -0
- package/dist/src/tools/registry.d.ts +9 -0
- package/dist/src/tools/registry.d.ts.map +1 -0
- package/dist/src/tools/registry.js +18 -0
- package/dist/src/tools/types.d.ts +18 -0
- package/dist/src/tools/types.d.ts.map +1 -0
- package/dist/src/tools/types.js +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2a-provider.test.d.ts","sourceRoot":"","sources":["../../../src/providers/a2a-provider.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mapA2aTaskEvent, fetchAgentCard, } from './a2a-provider.js';
|
|
3
|
+
function freshState() {
|
|
4
|
+
return {
|
|
5
|
+
sessionId: null,
|
|
6
|
+
taskId: null,
|
|
7
|
+
totalInputTokens: 0,
|
|
8
|
+
totalOutputTokens: 0,
|
|
9
|
+
turnCount: 0,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
describe('mapA2aTaskEvent', () => {
|
|
13
|
+
// -----------------------------------------------------------------------
|
|
14
|
+
// TaskStatusUpdate — submitted
|
|
15
|
+
// -----------------------------------------------------------------------
|
|
16
|
+
it('maps submitted status to init event', () => {
|
|
17
|
+
const state = freshState();
|
|
18
|
+
const event = {
|
|
19
|
+
type: 'TaskStatusUpdate',
|
|
20
|
+
id: 'task-123',
|
|
21
|
+
sessionId: 'sess-456',
|
|
22
|
+
status: { state: 'submitted' },
|
|
23
|
+
};
|
|
24
|
+
const result = mapA2aTaskEvent(event, state);
|
|
25
|
+
expect(result).toHaveLength(1);
|
|
26
|
+
expect(result[0]).toMatchObject({
|
|
27
|
+
type: 'init',
|
|
28
|
+
sessionId: 'sess-456',
|
|
29
|
+
});
|
|
30
|
+
expect(state.sessionId).toBe('sess-456');
|
|
31
|
+
expect(state.taskId).toBe('task-123');
|
|
32
|
+
});
|
|
33
|
+
it('uses task id as sessionId when sessionId is not present', () => {
|
|
34
|
+
const state = freshState();
|
|
35
|
+
const event = {
|
|
36
|
+
type: 'TaskStatusUpdate',
|
|
37
|
+
id: 'task-789',
|
|
38
|
+
status: { state: 'submitted' },
|
|
39
|
+
};
|
|
40
|
+
const result = mapA2aTaskEvent(event, state);
|
|
41
|
+
expect(result[0]).toMatchObject({
|
|
42
|
+
type: 'init',
|
|
43
|
+
sessionId: 'task-789',
|
|
44
|
+
});
|
|
45
|
+
expect(state.sessionId).toBe('task-789');
|
|
46
|
+
});
|
|
47
|
+
// -----------------------------------------------------------------------
|
|
48
|
+
// TaskStatusUpdate — working
|
|
49
|
+
// -----------------------------------------------------------------------
|
|
50
|
+
it('maps working status with message to assistant_text', () => {
|
|
51
|
+
const state = freshState();
|
|
52
|
+
const event = {
|
|
53
|
+
type: 'TaskStatusUpdate',
|
|
54
|
+
id: 'task-123',
|
|
55
|
+
status: {
|
|
56
|
+
state: 'working',
|
|
57
|
+
message: {
|
|
58
|
+
role: 'agent',
|
|
59
|
+
parts: [{ type: 'text', text: 'Analyzing the codebase...' }],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const result = mapA2aTaskEvent(event, state);
|
|
64
|
+
expect(result).toHaveLength(1);
|
|
65
|
+
expect(result[0]).toMatchObject({
|
|
66
|
+
type: 'assistant_text',
|
|
67
|
+
text: 'Analyzing the codebase...',
|
|
68
|
+
});
|
|
69
|
+
expect(state.turnCount).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
it('maps working status without message to system event', () => {
|
|
72
|
+
const state = freshState();
|
|
73
|
+
const event = {
|
|
74
|
+
type: 'TaskStatusUpdate',
|
|
75
|
+
id: 'task-123',
|
|
76
|
+
status: { state: 'working' },
|
|
77
|
+
};
|
|
78
|
+
const result = mapA2aTaskEvent(event, state);
|
|
79
|
+
expect(result).toHaveLength(1);
|
|
80
|
+
expect(result[0]).toMatchObject({
|
|
81
|
+
type: 'system',
|
|
82
|
+
subtype: 'working',
|
|
83
|
+
message: 'Task task-123 is working (turn 1)',
|
|
84
|
+
});
|
|
85
|
+
expect(state.turnCount).toBe(1);
|
|
86
|
+
});
|
|
87
|
+
it('increments turn count on each working event', () => {
|
|
88
|
+
const state = freshState();
|
|
89
|
+
const event = {
|
|
90
|
+
type: 'TaskStatusUpdate',
|
|
91
|
+
id: 'task-123',
|
|
92
|
+
status: { state: 'working' },
|
|
93
|
+
};
|
|
94
|
+
mapA2aTaskEvent(event, state);
|
|
95
|
+
expect(state.turnCount).toBe(1);
|
|
96
|
+
mapA2aTaskEvent(event, state);
|
|
97
|
+
expect(state.turnCount).toBe(2);
|
|
98
|
+
const result = mapA2aTaskEvent(event, state);
|
|
99
|
+
expect(state.turnCount).toBe(3);
|
|
100
|
+
expect(result[0]).toMatchObject({
|
|
101
|
+
message: 'Task task-123 is working (turn 3)',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
// -----------------------------------------------------------------------
|
|
105
|
+
// TaskStatusUpdate — input-required
|
|
106
|
+
// -----------------------------------------------------------------------
|
|
107
|
+
it('maps input-required status to system event with input_required subtype', () => {
|
|
108
|
+
const state = freshState();
|
|
109
|
+
const event = {
|
|
110
|
+
type: 'TaskStatusUpdate',
|
|
111
|
+
id: 'task-123',
|
|
112
|
+
status: {
|
|
113
|
+
state: 'input-required',
|
|
114
|
+
message: {
|
|
115
|
+
role: 'agent',
|
|
116
|
+
parts: [{ type: 'text', text: 'Please provide the API key' }],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
const result = mapA2aTaskEvent(event, state);
|
|
121
|
+
expect(result).toHaveLength(1);
|
|
122
|
+
expect(result[0]).toMatchObject({
|
|
123
|
+
type: 'system',
|
|
124
|
+
subtype: 'input_required',
|
|
125
|
+
message: 'Please provide the API key',
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
it('maps input-required without message to default message', () => {
|
|
129
|
+
const state = freshState();
|
|
130
|
+
const event = {
|
|
131
|
+
type: 'TaskStatusUpdate',
|
|
132
|
+
id: 'task-123',
|
|
133
|
+
status: { state: 'input-required' },
|
|
134
|
+
};
|
|
135
|
+
const result = mapA2aTaskEvent(event, state);
|
|
136
|
+
expect(result[0]).toMatchObject({
|
|
137
|
+
type: 'system',
|
|
138
|
+
subtype: 'input_required',
|
|
139
|
+
message: 'Agent requires additional input',
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// -----------------------------------------------------------------------
|
|
143
|
+
// TaskStatusUpdate — completed
|
|
144
|
+
// -----------------------------------------------------------------------
|
|
145
|
+
it('maps completed status to success result', () => {
|
|
146
|
+
const state = freshState();
|
|
147
|
+
state.turnCount = 3;
|
|
148
|
+
state.totalInputTokens = 500;
|
|
149
|
+
state.totalOutputTokens = 200;
|
|
150
|
+
const event = {
|
|
151
|
+
type: 'TaskStatusUpdate',
|
|
152
|
+
id: 'task-123',
|
|
153
|
+
status: {
|
|
154
|
+
state: 'completed',
|
|
155
|
+
message: {
|
|
156
|
+
role: 'agent',
|
|
157
|
+
parts: [{ type: 'text', text: 'All done!' }],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
const result = mapA2aTaskEvent(event, state);
|
|
162
|
+
expect(result).toHaveLength(1);
|
|
163
|
+
expect(result[0]).toMatchObject({
|
|
164
|
+
type: 'result',
|
|
165
|
+
success: true,
|
|
166
|
+
message: 'All done!',
|
|
167
|
+
cost: {
|
|
168
|
+
inputTokens: 500,
|
|
169
|
+
outputTokens: 200,
|
|
170
|
+
numTurns: 3,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
it('maps completed status without message', () => {
|
|
175
|
+
const state = freshState();
|
|
176
|
+
const event = {
|
|
177
|
+
type: 'TaskStatusUpdate',
|
|
178
|
+
id: 'task-123',
|
|
179
|
+
status: { state: 'completed' },
|
|
180
|
+
};
|
|
181
|
+
const result = mapA2aTaskEvent(event, state);
|
|
182
|
+
expect(result[0]).toMatchObject({
|
|
183
|
+
type: 'result',
|
|
184
|
+
success: true,
|
|
185
|
+
});
|
|
186
|
+
expect(result[0].message).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
it('omits zero-value cost fields', () => {
|
|
189
|
+
const state = freshState();
|
|
190
|
+
const event = {
|
|
191
|
+
type: 'TaskStatusUpdate',
|
|
192
|
+
id: 'task-123',
|
|
193
|
+
status: { state: 'completed' },
|
|
194
|
+
};
|
|
195
|
+
const result = mapA2aTaskEvent(event, state);
|
|
196
|
+
expect(result[0].cost).toEqual({
|
|
197
|
+
inputTokens: undefined,
|
|
198
|
+
outputTokens: undefined,
|
|
199
|
+
numTurns: undefined,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
// -----------------------------------------------------------------------
|
|
203
|
+
// TaskStatusUpdate — failed
|
|
204
|
+
// -----------------------------------------------------------------------
|
|
205
|
+
it('maps failed status to failure result', () => {
|
|
206
|
+
const state = freshState();
|
|
207
|
+
const event = {
|
|
208
|
+
type: 'TaskStatusUpdate',
|
|
209
|
+
id: 'task-123',
|
|
210
|
+
status: {
|
|
211
|
+
state: 'failed',
|
|
212
|
+
message: {
|
|
213
|
+
role: 'agent',
|
|
214
|
+
parts: [{ type: 'text', text: 'Model rate limit exceeded' }],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
const result = mapA2aTaskEvent(event, state);
|
|
219
|
+
expect(result).toHaveLength(1);
|
|
220
|
+
expect(result[0]).toMatchObject({
|
|
221
|
+
type: 'result',
|
|
222
|
+
success: false,
|
|
223
|
+
errors: ['Model rate limit exceeded'],
|
|
224
|
+
errorSubtype: 'task_failed',
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
it('maps failed status without message to default error', () => {
|
|
228
|
+
const state = freshState();
|
|
229
|
+
const event = {
|
|
230
|
+
type: 'TaskStatusUpdate',
|
|
231
|
+
id: 'task-123',
|
|
232
|
+
status: { state: 'failed' },
|
|
233
|
+
};
|
|
234
|
+
const result = mapA2aTaskEvent(event, state);
|
|
235
|
+
expect(result[0]).toMatchObject({
|
|
236
|
+
type: 'result',
|
|
237
|
+
success: false,
|
|
238
|
+
errors: ['Task failed'],
|
|
239
|
+
errorSubtype: 'task_failed',
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
// -----------------------------------------------------------------------
|
|
243
|
+
// TaskStatusUpdate — canceled
|
|
244
|
+
// -----------------------------------------------------------------------
|
|
245
|
+
it('maps canceled status to failure result with canceled subtype', () => {
|
|
246
|
+
const state = freshState();
|
|
247
|
+
const event = {
|
|
248
|
+
type: 'TaskStatusUpdate',
|
|
249
|
+
id: 'task-123',
|
|
250
|
+
status: {
|
|
251
|
+
state: 'canceled',
|
|
252
|
+
message: {
|
|
253
|
+
role: 'agent',
|
|
254
|
+
parts: [{ type: 'text', text: 'User requested cancellation' }],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
const result = mapA2aTaskEvent(event, state);
|
|
259
|
+
expect(result).toHaveLength(1);
|
|
260
|
+
expect(result[0]).toMatchObject({
|
|
261
|
+
type: 'result',
|
|
262
|
+
success: false,
|
|
263
|
+
errors: ['User requested cancellation'],
|
|
264
|
+
errorSubtype: 'canceled',
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
it('maps canceled status without message to default', () => {
|
|
268
|
+
const state = freshState();
|
|
269
|
+
const event = {
|
|
270
|
+
type: 'TaskStatusUpdate',
|
|
271
|
+
id: 'task-123',
|
|
272
|
+
status: { state: 'canceled' },
|
|
273
|
+
};
|
|
274
|
+
const result = mapA2aTaskEvent(event, state);
|
|
275
|
+
expect(result[0]).toMatchObject({
|
|
276
|
+
type: 'result',
|
|
277
|
+
success: false,
|
|
278
|
+
errors: ['Task canceled'],
|
|
279
|
+
errorSubtype: 'canceled',
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
// -----------------------------------------------------------------------
|
|
283
|
+
// TaskStatusUpdate — unknown state
|
|
284
|
+
// -----------------------------------------------------------------------
|
|
285
|
+
it('handles unknown status state gracefully', () => {
|
|
286
|
+
const state = freshState();
|
|
287
|
+
const event = {
|
|
288
|
+
type: 'TaskStatusUpdate',
|
|
289
|
+
id: 'task-123',
|
|
290
|
+
status: { state: 'paused' },
|
|
291
|
+
};
|
|
292
|
+
const result = mapA2aTaskEvent(event, state);
|
|
293
|
+
expect(result[0]).toMatchObject({
|
|
294
|
+
type: 'system',
|
|
295
|
+
subtype: 'unknown',
|
|
296
|
+
message: 'Unknown A2A task status: paused',
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
// -----------------------------------------------------------------------
|
|
300
|
+
// TaskArtifactUpdate
|
|
301
|
+
// -----------------------------------------------------------------------
|
|
302
|
+
it('maps artifact update with text parts to assistant_text', () => {
|
|
303
|
+
const state = freshState();
|
|
304
|
+
const event = {
|
|
305
|
+
type: 'TaskArtifactUpdate',
|
|
306
|
+
id: 'task-123',
|
|
307
|
+
artifact: {
|
|
308
|
+
name: 'analysis-report',
|
|
309
|
+
description: 'Code analysis report',
|
|
310
|
+
parts: [{ type: 'text', text: 'Found 3 issues in the codebase.' }],
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
const result = mapA2aTaskEvent(event, state);
|
|
314
|
+
expect(result).toHaveLength(1);
|
|
315
|
+
expect(result[0]).toMatchObject({
|
|
316
|
+
type: 'assistant_text',
|
|
317
|
+
text: 'Found 3 issues in the codebase.',
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
it('maps artifact update with data parts to JSON text', () => {
|
|
321
|
+
const state = freshState();
|
|
322
|
+
const event = {
|
|
323
|
+
type: 'TaskArtifactUpdate',
|
|
324
|
+
id: 'task-123',
|
|
325
|
+
artifact: {
|
|
326
|
+
parts: [{ type: 'data', data: { issues: 3, severity: 'high' } }],
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
const result = mapA2aTaskEvent(event, state);
|
|
330
|
+
expect(result[0]).toMatchObject({
|
|
331
|
+
type: 'assistant_text',
|
|
332
|
+
text: '{"issues":3,"severity":"high"}',
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
it('maps artifact update with file parts', () => {
|
|
336
|
+
const state = freshState();
|
|
337
|
+
const event = {
|
|
338
|
+
type: 'TaskArtifactUpdate',
|
|
339
|
+
id: 'task-123',
|
|
340
|
+
artifact: {
|
|
341
|
+
parts: [{ type: 'file', file: { name: 'report.pdf', mimeType: 'application/pdf' } }],
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
const result = mapA2aTaskEvent(event, state);
|
|
345
|
+
expect(result[0]).toMatchObject({
|
|
346
|
+
type: 'assistant_text',
|
|
347
|
+
text: '[File: report.pdf]',
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
it('maps artifact update with empty parts to artifact name', () => {
|
|
351
|
+
const state = freshState();
|
|
352
|
+
const event = {
|
|
353
|
+
type: 'TaskArtifactUpdate',
|
|
354
|
+
id: 'task-123',
|
|
355
|
+
artifact: {
|
|
356
|
+
name: 'my-artifact',
|
|
357
|
+
parts: [],
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
const result = mapA2aTaskEvent(event, state);
|
|
361
|
+
expect(result[0]).toMatchObject({
|
|
362
|
+
type: 'assistant_text',
|
|
363
|
+
text: '[Artifact: my-artifact]',
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
it('maps artifact update with unnamed empty parts', () => {
|
|
367
|
+
const state = freshState();
|
|
368
|
+
const event = {
|
|
369
|
+
type: 'TaskArtifactUpdate',
|
|
370
|
+
id: 'task-123',
|
|
371
|
+
artifact: {
|
|
372
|
+
parts: [],
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
const result = mapA2aTaskEvent(event, state);
|
|
376
|
+
expect(result[0]).toMatchObject({
|
|
377
|
+
type: 'assistant_text',
|
|
378
|
+
text: '[Artifact: unnamed]',
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
// -----------------------------------------------------------------------
|
|
382
|
+
// State tracking
|
|
383
|
+
// -----------------------------------------------------------------------
|
|
384
|
+
it('tracks taskId from first event', () => {
|
|
385
|
+
const state = freshState();
|
|
386
|
+
const event = {
|
|
387
|
+
type: 'TaskStatusUpdate',
|
|
388
|
+
id: 'task-abc',
|
|
389
|
+
status: { state: 'submitted' },
|
|
390
|
+
};
|
|
391
|
+
mapA2aTaskEvent(event, state);
|
|
392
|
+
expect(state.taskId).toBe('task-abc');
|
|
393
|
+
});
|
|
394
|
+
it('tracks sessionId from first event', () => {
|
|
395
|
+
const state = freshState();
|
|
396
|
+
const event = {
|
|
397
|
+
type: 'TaskStatusUpdate',
|
|
398
|
+
id: 'task-abc',
|
|
399
|
+
sessionId: 'sess-xyz',
|
|
400
|
+
status: { state: 'submitted' },
|
|
401
|
+
};
|
|
402
|
+
mapA2aTaskEvent(event, state);
|
|
403
|
+
expect(state.sessionId).toBe('sess-xyz');
|
|
404
|
+
});
|
|
405
|
+
it('does not overwrite taskId once set', () => {
|
|
406
|
+
const state = freshState();
|
|
407
|
+
state.taskId = 'existing-task';
|
|
408
|
+
const event = {
|
|
409
|
+
type: 'TaskArtifactUpdate',
|
|
410
|
+
id: 'new-task',
|
|
411
|
+
artifact: {
|
|
412
|
+
parts: [{ type: 'text', text: 'hello' }],
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
mapA2aTaskEvent(event, state);
|
|
416
|
+
expect(state.taskId).toBe('existing-task');
|
|
417
|
+
});
|
|
418
|
+
it('does not overwrite sessionId once set', () => {
|
|
419
|
+
const state = freshState();
|
|
420
|
+
state.sessionId = 'existing-session';
|
|
421
|
+
const event = {
|
|
422
|
+
type: 'TaskArtifactUpdate',
|
|
423
|
+
id: 'task-123',
|
|
424
|
+
sessionId: 'new-session',
|
|
425
|
+
artifact: {
|
|
426
|
+
parts: [{ type: 'text', text: 'hello' }],
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
mapA2aTaskEvent(event, state);
|
|
430
|
+
expect(state.sessionId).toBe('existing-session');
|
|
431
|
+
});
|
|
432
|
+
// -----------------------------------------------------------------------
|
|
433
|
+
// Unknown event type
|
|
434
|
+
// -----------------------------------------------------------------------
|
|
435
|
+
it('handles unknown event type gracefully', () => {
|
|
436
|
+
const state = freshState();
|
|
437
|
+
const event = { type: 'TaskSomethingElse', id: 'task-1' };
|
|
438
|
+
const result = mapA2aTaskEvent(event, state);
|
|
439
|
+
expect(result[0]).toMatchObject({
|
|
440
|
+
type: 'system',
|
|
441
|
+
subtype: 'unknown',
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
// -----------------------------------------------------------------------
|
|
445
|
+
// Multi-part messages
|
|
446
|
+
// -----------------------------------------------------------------------
|
|
447
|
+
it('concatenates multiple text parts', () => {
|
|
448
|
+
const state = freshState();
|
|
449
|
+
const event = {
|
|
450
|
+
type: 'TaskStatusUpdate',
|
|
451
|
+
id: 'task-123',
|
|
452
|
+
status: {
|
|
453
|
+
state: 'working',
|
|
454
|
+
message: {
|
|
455
|
+
role: 'agent',
|
|
456
|
+
parts: [
|
|
457
|
+
{ type: 'text', text: 'Part 1' },
|
|
458
|
+
{ type: 'text', text: 'Part 2' },
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
const result = mapA2aTaskEvent(event, state);
|
|
464
|
+
expect(result[0]).toMatchObject({
|
|
465
|
+
type: 'assistant_text',
|
|
466
|
+
text: 'Part 1\nPart 2',
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
it('mixes text and data parts in message', () => {
|
|
470
|
+
const state = freshState();
|
|
471
|
+
const event = {
|
|
472
|
+
type: 'TaskStatusUpdate',
|
|
473
|
+
id: 'task-123',
|
|
474
|
+
status: {
|
|
475
|
+
state: 'working',
|
|
476
|
+
message: {
|
|
477
|
+
role: 'agent',
|
|
478
|
+
parts: [
|
|
479
|
+
{ type: 'text', text: 'Results:' },
|
|
480
|
+
{ type: 'data', data: { count: 42 } },
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
const result = mapA2aTaskEvent(event, state);
|
|
486
|
+
expect(result[0]).toMatchObject({
|
|
487
|
+
type: 'assistant_text',
|
|
488
|
+
text: 'Results:\n{"count":42}',
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
// -----------------------------------------------------------------------
|
|
492
|
+
// raw event preservation
|
|
493
|
+
// -----------------------------------------------------------------------
|
|
494
|
+
it('preserves the raw event on all mapped events', () => {
|
|
495
|
+
const state = freshState();
|
|
496
|
+
const event = {
|
|
497
|
+
type: 'TaskStatusUpdate',
|
|
498
|
+
id: 'task-123',
|
|
499
|
+
status: { state: 'submitted' },
|
|
500
|
+
};
|
|
501
|
+
const result = mapA2aTaskEvent(event, state);
|
|
502
|
+
expect(result[0].raw).toBe(event);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
// ---------------------------------------------------------------------------
|
|
506
|
+
// fetchAgentCard
|
|
507
|
+
// ---------------------------------------------------------------------------
|
|
508
|
+
describe('fetchAgentCard', () => {
|
|
509
|
+
const originalFetch = globalThis.fetch;
|
|
510
|
+
beforeEach(() => {
|
|
511
|
+
vi.restoreAllMocks();
|
|
512
|
+
});
|
|
513
|
+
afterEach(() => {
|
|
514
|
+
globalThis.fetch = originalFetch;
|
|
515
|
+
});
|
|
516
|
+
it('fetches and parses a valid agent card', async () => {
|
|
517
|
+
const card = {
|
|
518
|
+
name: 'Test Agent',
|
|
519
|
+
url: 'https://agent.example.com',
|
|
520
|
+
description: 'A test A2A agent',
|
|
521
|
+
version: '1.0.0',
|
|
522
|
+
skills: [
|
|
523
|
+
{ id: 'code-review', name: 'Code Review', description: 'Reviews code' },
|
|
524
|
+
],
|
|
525
|
+
capabilities: {
|
|
526
|
+
streaming: true,
|
|
527
|
+
multiTurn: true,
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
531
|
+
ok: true,
|
|
532
|
+
json: () => Promise.resolve(card),
|
|
533
|
+
});
|
|
534
|
+
const result = await fetchAgentCard('https://agent.example.com');
|
|
535
|
+
expect(result).toEqual(card);
|
|
536
|
+
expect(globalThis.fetch).toHaveBeenCalledWith('https://agent.example.com/.well-known/agent-card.json', expect.objectContaining({
|
|
537
|
+
method: 'GET',
|
|
538
|
+
headers: { 'Accept': 'application/json' },
|
|
539
|
+
}));
|
|
540
|
+
});
|
|
541
|
+
it('strips trailing slashes from base URL', async () => {
|
|
542
|
+
const card = {
|
|
543
|
+
name: 'Test Agent',
|
|
544
|
+
url: 'https://agent.example.com',
|
|
545
|
+
};
|
|
546
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
547
|
+
ok: true,
|
|
548
|
+
json: () => Promise.resolve(card),
|
|
549
|
+
});
|
|
550
|
+
await fetchAgentCard('https://agent.example.com/');
|
|
551
|
+
expect(globalThis.fetch).toHaveBeenCalledWith('https://agent.example.com/.well-known/agent-card.json', expect.anything());
|
|
552
|
+
});
|
|
553
|
+
it('throws on HTTP error', async () => {
|
|
554
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
555
|
+
ok: false,
|
|
556
|
+
status: 404,
|
|
557
|
+
statusText: 'Not Found',
|
|
558
|
+
});
|
|
559
|
+
await expect(fetchAgentCard('https://agent.example.com'))
|
|
560
|
+
.rejects.toThrow('Failed to fetch A2A agent card');
|
|
561
|
+
});
|
|
562
|
+
it('throws on invalid agent card (missing name)', async () => {
|
|
563
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
564
|
+
ok: true,
|
|
565
|
+
json: () => Promise.resolve({ url: 'https://agent.example.com' }),
|
|
566
|
+
});
|
|
567
|
+
await expect(fetchAgentCard('https://agent.example.com'))
|
|
568
|
+
.rejects.toThrow('missing required fields');
|
|
569
|
+
});
|
|
570
|
+
it('throws on invalid agent card (missing url)', async () => {
|
|
571
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
572
|
+
ok: true,
|
|
573
|
+
json: () => Promise.resolve({ name: 'Test' }),
|
|
574
|
+
});
|
|
575
|
+
await expect(fetchAgentCard('https://agent.example.com'))
|
|
576
|
+
.rejects.toThrow('missing required fields');
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
// ---------------------------------------------------------------------------
|
|
580
|
+
// A2aProvider class and factory
|
|
581
|
+
// ---------------------------------------------------------------------------
|
|
582
|
+
describe('A2aProvider', () => {
|
|
583
|
+
it('exports A2aProvider class', async () => {
|
|
584
|
+
const { A2aProvider } = await import('./a2a-provider.js');
|
|
585
|
+
const provider = new A2aProvider();
|
|
586
|
+
expect(provider.name).toBe('a2a');
|
|
587
|
+
});
|
|
588
|
+
it('exports createA2aProvider factory', async () => {
|
|
589
|
+
const { createA2aProvider } = await import('./a2a-provider.js');
|
|
590
|
+
const provider = createA2aProvider();
|
|
591
|
+
expect(provider.name).toBe('a2a');
|
|
592
|
+
});
|
|
593
|
+
it('spawn returns a handle with error when A2A_AGENT_URL is not set', async () => {
|
|
594
|
+
const { A2aProvider } = await import('./a2a-provider.js');
|
|
595
|
+
const provider = new A2aProvider();
|
|
596
|
+
// Save and clear env
|
|
597
|
+
const savedUrl = process.env.A2A_AGENT_URL;
|
|
598
|
+
delete process.env.A2A_AGENT_URL;
|
|
599
|
+
// Clear any A2A_AGENT_URL_* vars
|
|
600
|
+
const savedUrlVars = {};
|
|
601
|
+
for (const key of Object.keys(process.env)) {
|
|
602
|
+
if (key.startsWith('A2A_AGENT_URL_')) {
|
|
603
|
+
savedUrlVars[key] = process.env[key];
|
|
604
|
+
delete process.env[key];
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
const handle = provider.spawn({
|
|
609
|
+
prompt: 'test',
|
|
610
|
+
cwd: '/tmp',
|
|
611
|
+
env: {},
|
|
612
|
+
abortController: new AbortController(),
|
|
613
|
+
autonomous: true,
|
|
614
|
+
sandboxEnabled: false,
|
|
615
|
+
});
|
|
616
|
+
expect(handle.sessionId).toBeNull();
|
|
617
|
+
// Consume the stream to check error events
|
|
618
|
+
const events = [];
|
|
619
|
+
for await (const event of handle.stream) {
|
|
620
|
+
events.push(event);
|
|
621
|
+
}
|
|
622
|
+
expect(events).toHaveLength(2);
|
|
623
|
+
expect(events[0]).toMatchObject({
|
|
624
|
+
type: 'error',
|
|
625
|
+
message: expect.stringContaining('A2A_AGENT_URL'),
|
|
626
|
+
});
|
|
627
|
+
expect(events[1]).toMatchObject({
|
|
628
|
+
type: 'result',
|
|
629
|
+
success: false,
|
|
630
|
+
errorSubtype: 'configuration_error',
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
finally {
|
|
634
|
+
// Restore env
|
|
635
|
+
if (savedUrl !== undefined) {
|
|
636
|
+
process.env.A2A_AGENT_URL = savedUrl;
|
|
637
|
+
}
|
|
638
|
+
for (const [key, value] of Object.entries(savedUrlVars)) {
|
|
639
|
+
if (value !== undefined) {
|
|
640
|
+
process.env[key] = value;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
it('resume sets sessionId on the handle', async () => {
|
|
646
|
+
const { A2aProvider } = await import('./a2a-provider.js');
|
|
647
|
+
const provider = new A2aProvider();
|
|
648
|
+
// Save and clear env
|
|
649
|
+
const savedUrl = process.env.A2A_AGENT_URL;
|
|
650
|
+
delete process.env.A2A_AGENT_URL;
|
|
651
|
+
const savedUrlVars = {};
|
|
652
|
+
for (const key of Object.keys(process.env)) {
|
|
653
|
+
if (key.startsWith('A2A_AGENT_URL_')) {
|
|
654
|
+
savedUrlVars[key] = process.env[key];
|
|
655
|
+
delete process.env[key];
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
const handle = provider.spawn({
|
|
660
|
+
prompt: 'test',
|
|
661
|
+
cwd: '/tmp',
|
|
662
|
+
env: { A2A_AGENT_URL: 'https://agent.example.com' },
|
|
663
|
+
abortController: new AbortController(),
|
|
664
|
+
autonomous: true,
|
|
665
|
+
sandboxEnabled: false,
|
|
666
|
+
});
|
|
667
|
+
// When the URL is set, sessionId starts null (until init event)
|
|
668
|
+
expect(handle.sessionId).toBeNull();
|
|
669
|
+
}
|
|
670
|
+
finally {
|
|
671
|
+
if (savedUrl !== undefined) {
|
|
672
|
+
process.env.A2A_AGENT_URL = savedUrl;
|
|
673
|
+
}
|
|
674
|
+
for (const [key, value] of Object.entries(savedUrlVars)) {
|
|
675
|
+
if (value !== undefined) {
|
|
676
|
+
process.env[key] = value;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
});
|