aws-runtime-bridge 1.9.130 → 1.9.134
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/dist/adapter/AcodeSdkAdapter.d.ts +15 -0
- package/dist/adapter/AcodeSdkAdapter.js +5 -5
- package/dist/adapter/types.d.ts +9 -0
- package/dist/browser/browser-frame-encode.d.ts +16 -0
- package/dist/browser/browser-frame-encode.js +2 -0
- package/dist/browser/browser-pool.js +1 -1
- package/dist/browser/built-in-browser-tools.js +1 -1
- package/dist/browser/screencast-differ.d.ts +35 -0
- package/dist/browser/screencast-differ.js +2 -0
- package/dist/browser/screencast-manager.d.ts +32 -0
- package/dist/browser/screencast-manager.js +1 -1
- package/dist/browser/types.d.ts +48 -1
- package/dist/browser/types.js +1 -1
- package/dist/browser/ws-server.d.ts +1 -1
- package/dist/browser/ws-server.js +1 -1
- package/dist/routes/background-tasks.js +7 -1
- package/dist/routes/instance-tool-status.d.ts +4 -0
- package/dist/routes/instance-tool-status.js +2 -0
- package/dist/routes/instance.d.ts +0 -2
- package/dist/routes/instance.js +1 -1
- package/dist/routes/system-metrics.js +1 -1
- package/dist/routes/terminal.js +19 -19
- package/dist/services/acode-bg-manager.d.ts +36 -0
- package/dist/services/acode-bg-manager.js +2 -0
- package/dist/services/browser-session-manager.js +1 -1
- package/dist/services/instance-features.d.ts +5 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/process-detail.d.ts +77 -0
- package/dist/services/process-detail.js +32 -0
- package/dist/services/process-top.d.ts +53 -0
- package/dist/services/process-top.js +50 -0
- package/dist/services/session-output.d.ts +10 -0
- package/dist/services/session-output.js +3 -3
- package/package/acode/dist/await-complete-tool.js +4 -4
- package/package/acode/dist/background-command-manager.d.ts +39 -0
- package/package/acode/dist/background-command-manager.js +5 -5
- package/package/acode/dist/background-command-tools.js +1 -1
- package/package/acode/dist/built-in-file-tools.d.ts +25 -1
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/acode/dist/builtins/shellCommandResult.d.ts +3 -0
- package/package/acode/dist/builtins/shellCommandResult.js +1 -1
- package/package/acode/dist/builtins/shellCommandRunner.d.ts +2 -0
- package/package/acode/dist/builtins/shellCommandRunner.js +1 -1
- package/package/acode/dist/image-utils.d.ts +52 -0
- package/package/acode/dist/image-utils.js +2 -0
- package/package/acode/dist/index.d.ts +1 -0
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/runtime.d.ts +15 -1
- package/package/acode/dist/runtime.js +26 -26
- package/package/acode/dist/sub-agent-tools.js +30 -26
- package/package/acode/dist/sub-agent-types.d.ts +1 -1
- package/package/acode/dist/tool-allocation.d.ts +4 -4
- package/package/acode/dist/tool-allocation.js +1 -1
- package/package/acode/dist/types.d.ts +3 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +4 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +8 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +28 -25
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +6 -6
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +6 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.js +2 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts +4 -0
- package/package/aws-client-agent-mcp/package.json +1 -1
- package/package.json +3 -3
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +0 -1066
- package/package/aws-client-agent-mcp/dist/config.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/config.test.js +0 -259
- package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts +0 -2
- package/package/aws-client-agent-mcp/dist/context-manager.test.js +0 -312
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js +0 -808
- package/package/aws-client-agent-mcp/dist/launch-config-tools.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/launch-config-tools.test.js +0 -110
- package/package/aws-client-agent-mcp/dist/logger.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/logger.test.js +0 -27
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +0 -2798
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +0 -152
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +0 -664
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js +0 -142
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.js +0 -81
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +0 -201
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js +0 -157
- package/package/aws-client-agent-mcp/dist/watchdog.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/watchdog.test.js +0 -124
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +0 -725
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { MessageBuffer } from './message-buffer.js';
|
|
4
|
-
describe('MessageBuffer poll', () => {
|
|
5
|
-
it('keeps waiting until a new direct message arrives', async () => {
|
|
6
|
-
const buffer = new MessageBuffer();
|
|
7
|
-
let settled = false;
|
|
8
|
-
const pendingPoll = buffer.poll().then((messages) => {
|
|
9
|
-
settled = true;
|
|
10
|
-
return messages;
|
|
11
|
-
});
|
|
12
|
-
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
13
|
-
assert.equal(settled, false, 'poll should remain pending when no message arrives');
|
|
14
|
-
const directMessage = {
|
|
15
|
-
msgId: 'dm-1',
|
|
16
|
-
senderId: 'agent-1',
|
|
17
|
-
senderName: 'Agent One',
|
|
18
|
-
content: 'hello',
|
|
19
|
-
requireReply: false,
|
|
20
|
-
timestamp: new Date().toISOString()
|
|
21
|
-
};
|
|
22
|
-
buffer.push(directMessage);
|
|
23
|
-
const messages = await pendingPoll;
|
|
24
|
-
assert.equal(messages.directMessages.length, 1);
|
|
25
|
-
assert.deepEqual(messages.directMessages[0], directMessage);
|
|
26
|
-
assert.deepEqual(messages.groupMessages, []);
|
|
27
|
-
});
|
|
28
|
-
it('returns buffered group messages immediately without blocking', async () => {
|
|
29
|
-
const buffer = new MessageBuffer();
|
|
30
|
-
const groupMessage = {
|
|
31
|
-
msgId: 1,
|
|
32
|
-
senderId: 'human-1',
|
|
33
|
-
senderName: 'Human One',
|
|
34
|
-
content: 'announcement',
|
|
35
|
-
timestamp: new Date().toISOString()
|
|
36
|
-
};
|
|
37
|
-
buffer.push(groupMessage);
|
|
38
|
-
const messages = await buffer.poll();
|
|
39
|
-
assert.deepEqual(messages.directMessages, []);
|
|
40
|
-
assert.equal(messages.groupMessages.length, 1);
|
|
41
|
-
assert.deepEqual(messages.groupMessages[0], groupMessage);
|
|
42
|
-
});
|
|
43
|
-
it('takeForceDirectMessages only takes force=true direct messages and keeps others', () => {
|
|
44
|
-
const buffer = new MessageBuffer();
|
|
45
|
-
const forcedMessage = {
|
|
46
|
-
msgId: 'dm-force-1',
|
|
47
|
-
senderId: 'human-1',
|
|
48
|
-
senderName: 'Human One',
|
|
49
|
-
content: '紧急:先停下处理这个',
|
|
50
|
-
requireReply: false,
|
|
51
|
-
force: true,
|
|
52
|
-
timestamp: new Date().toISOString()
|
|
53
|
-
};
|
|
54
|
-
const normalMessage = {
|
|
55
|
-
msgId: 'dm-normal-1',
|
|
56
|
-
senderId: 'agent-2',
|
|
57
|
-
senderName: 'Agent Two',
|
|
58
|
-
content: '普通消息',
|
|
59
|
-
requireReply: false,
|
|
60
|
-
timestamp: new Date().toISOString()
|
|
61
|
-
};
|
|
62
|
-
const groupMessage = {
|
|
63
|
-
msgId: 2,
|
|
64
|
-
senderId: 'human-1',
|
|
65
|
-
senderName: 'Human One',
|
|
66
|
-
content: '群消息',
|
|
67
|
-
timestamp: new Date().toISOString()
|
|
68
|
-
};
|
|
69
|
-
buffer.push(normalMessage);
|
|
70
|
-
buffer.push(groupMessage);
|
|
71
|
-
buffer.push(forcedMessage);
|
|
72
|
-
const taken = buffer.takeForceDirectMessages();
|
|
73
|
-
assert.equal(taken.directMessages.length, 1);
|
|
74
|
-
assert.deepEqual(taken.directMessages[0], forcedMessage);
|
|
75
|
-
assert.deepEqual(taken.groupMessages, []);
|
|
76
|
-
const rest = buffer.get();
|
|
77
|
-
assert.equal(rest.directMessages.length, 1);
|
|
78
|
-
assert.deepEqual(rest.directMessages[0], normalMessage);
|
|
79
|
-
assert.equal(rest.groupMessages.length, 1);
|
|
80
|
-
assert.deepEqual(rest.groupMessages[0], groupMessage);
|
|
81
|
-
});
|
|
82
|
-
it('classifies meeting-channel messages with numeric msgId as direct messages', () => {
|
|
83
|
-
const buffer = new MessageBuffer();
|
|
84
|
-
const meetingNotice = {
|
|
85
|
-
msgId: 7,
|
|
86
|
-
channel: 'meeting',
|
|
87
|
-
meetingId: 'm-1',
|
|
88
|
-
topic: '评审会',
|
|
89
|
-
senderId: 'system',
|
|
90
|
-
senderName: '系统',
|
|
91
|
-
content: '系统已自动为你加入会议',
|
|
92
|
-
isSystem: true,
|
|
93
|
-
timestamp: new Date().toISOString()
|
|
94
|
-
};
|
|
95
|
-
buffer.push(meetingNotice);
|
|
96
|
-
const messages = buffer.get();
|
|
97
|
-
assert.equal(messages.directMessages.length, 1, 'meeting notice must land in directMessages bucket');
|
|
98
|
-
assert.deepEqual(messages.directMessages[0], meetingNotice);
|
|
99
|
-
assert.deepEqual(messages.groupMessages, []);
|
|
100
|
-
});
|
|
101
|
-
it('does not drop meeting notices whose numeric msgId collides with consumed group msgIds', () => {
|
|
102
|
-
const buffer = new MessageBuffer();
|
|
103
|
-
const groupMessage = {
|
|
104
|
-
msgId: 42,
|
|
105
|
-
roomId: 'room-1',
|
|
106
|
-
senderId: 'human-1',
|
|
107
|
-
senderName: 'Human One',
|
|
108
|
-
content: '群消息',
|
|
109
|
-
timestamp: new Date().toISOString()
|
|
110
|
-
};
|
|
111
|
-
buffer.push(groupMessage);
|
|
112
|
-
const first = buffer.get();
|
|
113
|
-
assert.equal(first.groupMessages.length, 1);
|
|
114
|
-
// 同数字 id 的会议通知:跨表 id 碰撞不得被去重误丢
|
|
115
|
-
const meetingNotice = {
|
|
116
|
-
msgId: 42,
|
|
117
|
-
channel: 'meeting',
|
|
118
|
-
meetingId: 'm-1',
|
|
119
|
-
senderId: 'system',
|
|
120
|
-
content: '自动加入提示',
|
|
121
|
-
timestamp: new Date().toISOString()
|
|
122
|
-
};
|
|
123
|
-
buffer.push(meetingNotice);
|
|
124
|
-
const second = buffer.get();
|
|
125
|
-
assert.equal(second.directMessages.length, 1, 'meeting notice with colliding numeric id must be delivered');
|
|
126
|
-
assert.deepEqual(second.directMessages[0], meetingNotice);
|
|
127
|
-
});
|
|
128
|
-
it('deduplicates repeated meeting notices by namespaced key', () => {
|
|
129
|
-
const buffer = new MessageBuffer();
|
|
130
|
-
const meetingNotice = {
|
|
131
|
-
msgId: 9,
|
|
132
|
-
channel: 'meeting_invite',
|
|
133
|
-
senderId: 'agent-a',
|
|
134
|
-
content: '会议邀请',
|
|
135
|
-
timestamp: new Date().toISOString()
|
|
136
|
-
};
|
|
137
|
-
buffer.push(meetingNotice);
|
|
138
|
-
buffer.push({ ...meetingNotice });
|
|
139
|
-
const messages = buffer.get();
|
|
140
|
-
assert.equal(messages.directMessages.length, 1, 'duplicate meeting_invite must be deduplicated');
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { EventEmitter } from 'node:events';
|
|
3
|
-
import { describe, it } from 'node:test';
|
|
4
|
-
import { createParentExitWatcher } from './parent-lifecycle.js';
|
|
5
|
-
class FakeStdin extends EventEmitter {
|
|
6
|
-
isTTY;
|
|
7
|
-
}
|
|
8
|
-
describe('createParentExitWatcher', () => {
|
|
9
|
-
it('exits with code 0 when stdin receives EOF (end)', () => {
|
|
10
|
-
const stdin = new FakeStdin();
|
|
11
|
-
let exitCode = null;
|
|
12
|
-
createParentExitWatcher(stdin, (code) => {
|
|
13
|
-
exitCode = code;
|
|
14
|
-
});
|
|
15
|
-
stdin.emit('end');
|
|
16
|
-
assert.equal(exitCode, 0);
|
|
17
|
-
});
|
|
18
|
-
it('exits on close', () => {
|
|
19
|
-
const stdin = new FakeStdin();
|
|
20
|
-
let exitCode = null;
|
|
21
|
-
createParentExitWatcher(stdin, (code) => {
|
|
22
|
-
exitCode = code;
|
|
23
|
-
});
|
|
24
|
-
stdin.emit('close');
|
|
25
|
-
assert.equal(exitCode, 0);
|
|
26
|
-
});
|
|
27
|
-
it('exits on stream error', () => {
|
|
28
|
-
const stdin = new FakeStdin();
|
|
29
|
-
let exitCode = null;
|
|
30
|
-
createParentExitWatcher(stdin, (code) => {
|
|
31
|
-
exitCode = code;
|
|
32
|
-
});
|
|
33
|
-
stdin.emit('error', new Error('pipe broken'));
|
|
34
|
-
assert.equal(exitCode, 0);
|
|
35
|
-
});
|
|
36
|
-
it('exits only once when end and close both fire', () => {
|
|
37
|
-
const stdin = new FakeStdin();
|
|
38
|
-
let exitCount = 0;
|
|
39
|
-
createParentExitWatcher(stdin, () => {
|
|
40
|
-
exitCount += 1;
|
|
41
|
-
});
|
|
42
|
-
stdin.emit('end');
|
|
43
|
-
stdin.emit('close');
|
|
44
|
-
assert.equal(exitCount, 1);
|
|
45
|
-
});
|
|
46
|
-
it('does not watch a TTY stdin (interactive/degraded mode)', () => {
|
|
47
|
-
const stdin = new FakeStdin();
|
|
48
|
-
stdin.isTTY = true;
|
|
49
|
-
// EventEmitter 无 error 监听器时 emit('error') 会抛出,这里静默掉
|
|
50
|
-
stdin.on('error', () => { });
|
|
51
|
-
let exitCount = 0;
|
|
52
|
-
createParentExitWatcher(stdin, () => {
|
|
53
|
-
exitCount += 1;
|
|
54
|
-
});
|
|
55
|
-
stdin.emit('end');
|
|
56
|
-
stdin.emit('close');
|
|
57
|
-
stdin.emit('error', new Error('x'));
|
|
58
|
-
assert.equal(exitCount, 0);
|
|
59
|
-
});
|
|
60
|
-
it('does not throw when stdin is absent', () => {
|
|
61
|
-
let exitCount = 0;
|
|
62
|
-
const watcher = createParentExitWatcher(null, () => {
|
|
63
|
-
exitCount += 1;
|
|
64
|
-
});
|
|
65
|
-
assert.equal(exitCount, 0);
|
|
66
|
-
watcher.dispose();
|
|
67
|
-
});
|
|
68
|
-
it('dispose removes all listeners', () => {
|
|
69
|
-
const stdin = new FakeStdin();
|
|
70
|
-
stdin.on('error', () => { });
|
|
71
|
-
let exitCount = 0;
|
|
72
|
-
const watcher = createParentExitWatcher(stdin, () => {
|
|
73
|
-
exitCount += 1;
|
|
74
|
-
});
|
|
75
|
-
watcher.dispose();
|
|
76
|
-
stdin.emit('end');
|
|
77
|
-
stdin.emit('close');
|
|
78
|
-
stdin.emit('error', new Error('x'));
|
|
79
|
-
assert.equal(exitCount, 0);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
3
|
-
import { claimRuntimeLaunchBinding, claimRuntimeLaunchBindingDetailed, } from './runtime-launch-binding.js';
|
|
4
|
-
describe('claimRuntimeLaunchBinding', () => {
|
|
5
|
-
const originalEnv = { ...process.env };
|
|
6
|
-
const originalFetch = global.fetch;
|
|
7
|
-
const agentConfig = {
|
|
8
|
-
roleName: '',
|
|
9
|
-
agentId: 'agent-a',
|
|
10
|
-
projectId: 'default',
|
|
11
|
-
workspacePath: 'C:/repo/demo',
|
|
12
|
-
prompt: '',
|
|
13
|
-
describe: '',
|
|
14
|
-
};
|
|
15
|
-
const serverConfig = {
|
|
16
|
-
serverUrl: 'ws://server:7380/ws/agent',
|
|
17
|
-
heartbeatInterval: 3000,
|
|
18
|
-
reconnectInterval: 200,
|
|
19
|
-
maxReconnectAttempts: -1,
|
|
20
|
-
runtimeBridgeBaseUrl: 'http://127.0.0.1:18081',
|
|
21
|
-
};
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
process.env = { ...originalEnv };
|
|
24
|
-
});
|
|
25
|
-
afterEach(() => {
|
|
26
|
-
process.env = { ...originalEnv };
|
|
27
|
-
global.fetch = originalFetch;
|
|
28
|
-
});
|
|
29
|
-
it('skips claim when launch binding flag is disabled', async () => {
|
|
30
|
-
process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'false';
|
|
31
|
-
const result = await claimRuntimeLaunchBinding(agentConfig, serverConfig);
|
|
32
|
-
assert.equal(result, null);
|
|
33
|
-
});
|
|
34
|
-
it('claims launch binding even when runtime token is already configured', async () => {
|
|
35
|
-
delete process.env.AWS_MCP_CLAIM_LAUNCH_BINDING;
|
|
36
|
-
let called = false;
|
|
37
|
-
global.fetch = async () => {
|
|
38
|
-
called = true;
|
|
39
|
-
return {
|
|
40
|
-
ok: true,
|
|
41
|
-
status: 200,
|
|
42
|
-
statusText: 'OK',
|
|
43
|
-
json: async () => ({
|
|
44
|
-
ok: true,
|
|
45
|
-
agentId: 'agent-a',
|
|
46
|
-
runtimeAccessToken: 'binding-token',
|
|
47
|
-
}),
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
const result = await claimRuntimeLaunchBinding(agentConfig, {
|
|
51
|
-
...serverConfig,
|
|
52
|
-
runtimeAccessToken: 'direct-configured-token',
|
|
53
|
-
});
|
|
54
|
-
assert.equal(result?.runtimeAccessToken, 'binding-token');
|
|
55
|
-
assert.equal(called, true);
|
|
56
|
-
});
|
|
57
|
-
it('claims queued agent identity from local runtime bridge', async () => {
|
|
58
|
-
delete process.env.AWS_MCP_CLAIM_LAUNCH_BINDING;
|
|
59
|
-
let capturedUrl = '';
|
|
60
|
-
let capturedBody;
|
|
61
|
-
global.fetch = async (url, options) => {
|
|
62
|
-
capturedUrl = url.toString();
|
|
63
|
-
capturedBody = JSON.parse(String(options?.body));
|
|
64
|
-
return {
|
|
65
|
-
ok: true,
|
|
66
|
-
status: 200,
|
|
67
|
-
statusText: 'OK',
|
|
68
|
-
json: async () => ({
|
|
69
|
-
ok: true,
|
|
70
|
-
agentId: 'agent-a',
|
|
71
|
-
runtimeAccessToken: 'token-a',
|
|
72
|
-
userId: 'user-a',
|
|
73
|
-
}),
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
const result = await claimRuntimeLaunchBinding(agentConfig, serverConfig);
|
|
77
|
-
assert.equal(capturedUrl, 'http://127.0.0.1:18081/runtime/binding/claim-agent');
|
|
78
|
-
assert.deepEqual(capturedBody, {
|
|
79
|
-
workspacePath: 'C:/repo/demo',
|
|
80
|
-
serverUrl: 'ws://server:7380/ws/agent',
|
|
81
|
-
agentId: 'agent-a',
|
|
82
|
-
});
|
|
83
|
-
assert.equal(result?.agentId, 'agent-a');
|
|
84
|
-
assert.equal(result?.runtimeAccessToken, 'token-a');
|
|
85
|
-
});
|
|
86
|
-
it('claims by workspace path and server URL when agent id is missing', async () => {
|
|
87
|
-
process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'true';
|
|
88
|
-
let capturedBody;
|
|
89
|
-
global.fetch = async (_url, options) => {
|
|
90
|
-
capturedBody = JSON.parse(String(options?.body));
|
|
91
|
-
return {
|
|
92
|
-
ok: true,
|
|
93
|
-
status: 200,
|
|
94
|
-
statusText: 'OK',
|
|
95
|
-
json: async () => ({
|
|
96
|
-
ok: true,
|
|
97
|
-
agentId: 'agent-from-queue',
|
|
98
|
-
runtimeAccessToken: 'token-a',
|
|
99
|
-
}),
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
const result = await claimRuntimeLaunchBinding({ ...agentConfig, agentId: undefined }, serverConfig);
|
|
103
|
-
assert.deepEqual(capturedBody, {
|
|
104
|
-
workspacePath: 'C:/repo/demo',
|
|
105
|
-
serverUrl: 'ws://server:7380/ws/agent',
|
|
106
|
-
});
|
|
107
|
-
assert.equal(result?.agentId, 'agent-from-queue');
|
|
108
|
-
});
|
|
109
|
-
it('retries a transient missing launch binding before falling back', async () => {
|
|
110
|
-
process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'true';
|
|
111
|
-
process.env.AWS_MCP_LAUNCH_BINDING_CLAIM_ATTEMPTS = '2';
|
|
112
|
-
process.env.AWS_MCP_LAUNCH_BINDING_CLAIM_RETRY_DELAY_MS = '0';
|
|
113
|
-
let calls = 0;
|
|
114
|
-
global.fetch = async () => {
|
|
115
|
-
calls += 1;
|
|
116
|
-
if (calls === 1) {
|
|
117
|
-
return {
|
|
118
|
-
ok: false,
|
|
119
|
-
status: 404,
|
|
120
|
-
statusText: 'Not Found',
|
|
121
|
-
json: async () => ({
|
|
122
|
-
ok: false,
|
|
123
|
-
error: 'launch binding not found for workspacePath and serverUrl',
|
|
124
|
-
}),
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
return {
|
|
128
|
-
ok: true,
|
|
129
|
-
status: 200,
|
|
130
|
-
statusText: 'OK',
|
|
131
|
-
json: async () => ({
|
|
132
|
-
ok: true,
|
|
133
|
-
agentId: 'agent-a',
|
|
134
|
-
runtimeAccessToken: 'token-after-retry',
|
|
135
|
-
}),
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
const result = await claimRuntimeLaunchBinding(agentConfig, serverConfig);
|
|
139
|
-
assert.equal(calls, 2);
|
|
140
|
-
assert.equal(result?.runtimeAccessToken, 'token-after-retry');
|
|
141
|
-
});
|
|
142
|
-
it('ignores binding id and still claims by workspace path plus server URL', async () => {
|
|
143
|
-
process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'true';
|
|
144
|
-
process.env.AWS_MCP_LAUNCH_BINDING_ID = 'binding-a';
|
|
145
|
-
let capturedBody;
|
|
146
|
-
global.fetch = async (_url, options) => {
|
|
147
|
-
capturedBody = JSON.parse(String(options?.body));
|
|
148
|
-
return {
|
|
149
|
-
ok: true,
|
|
150
|
-
status: 200,
|
|
151
|
-
statusText: 'OK',
|
|
152
|
-
json: async () => ({ ok: true, agentId: 'agent-a' }),
|
|
153
|
-
};
|
|
154
|
-
};
|
|
155
|
-
const result = await claimRuntimeLaunchBinding(agentConfig, serverConfig);
|
|
156
|
-
assert.deepEqual(capturedBody, {
|
|
157
|
-
workspacePath: 'C:/repo/demo',
|
|
158
|
-
serverUrl: 'ws://server:7380/ws/agent',
|
|
159
|
-
agentId: 'agent-a',
|
|
160
|
-
});
|
|
161
|
-
assert.equal(result?.agentId, 'agent-a');
|
|
162
|
-
});
|
|
163
|
-
describe('claimRuntimeLaunchBindingDetailed', () => {
|
|
164
|
-
beforeEach(() => {
|
|
165
|
-
process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'true';
|
|
166
|
-
process.env.AWS_MCP_LAUNCH_BINDING_CLAIM_ATTEMPTS = '2';
|
|
167
|
-
process.env.AWS_MCP_LAUNCH_BINDING_CLAIM_RETRY_DELAY_MS = '0';
|
|
168
|
-
});
|
|
169
|
-
it('reports unreachable when bridge connection keeps failing', async () => {
|
|
170
|
-
let calls = 0;
|
|
171
|
-
global.fetch = async () => {
|
|
172
|
-
calls += 1;
|
|
173
|
-
throw new Error('fetch failed');
|
|
174
|
-
};
|
|
175
|
-
const outcome = await claimRuntimeLaunchBindingDetailed(agentConfig, serverConfig);
|
|
176
|
-
assert.equal(calls, 2);
|
|
177
|
-
assert.equal(outcome.status, 'unreachable');
|
|
178
|
-
assert.equal(outcome.status === 'unreachable' && outcome.message.includes('fetch failed'), true);
|
|
179
|
-
});
|
|
180
|
-
it('reports missing when bridge responds 404 on every attempt', async () => {
|
|
181
|
-
let calls = 0;
|
|
182
|
-
global.fetch = async () => {
|
|
183
|
-
calls += 1;
|
|
184
|
-
return {
|
|
185
|
-
ok: false,
|
|
186
|
-
status: 404,
|
|
187
|
-
statusText: 'Not Found',
|
|
188
|
-
json: async () => ({ ok: false, error: 'launch binding not found' }),
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
|
-
const outcome = await claimRuntimeLaunchBindingDetailed(agentConfig, serverConfig);
|
|
192
|
-
assert.equal(calls, 2);
|
|
193
|
-
assert.equal(outcome.status, 'missing');
|
|
194
|
-
});
|
|
195
|
-
it('reports skipped when launch binding claim is disabled', async () => {
|
|
196
|
-
process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'false';
|
|
197
|
-
const outcome = await claimRuntimeLaunchBindingDetailed(agentConfig, serverConfig);
|
|
198
|
-
assert.equal(outcome.status, 'skipped');
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import { StatusReporter } from "./status-reporter.js";
|
|
4
|
-
describe("StatusReporter tool action mapping", () => {
|
|
5
|
-
it("reports poll_message as a normal aws-mcp tool call", () => {
|
|
6
|
-
const updates = [];
|
|
7
|
-
const reporter = new StatusReporter({
|
|
8
|
-
agentId: "agent-self",
|
|
9
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
10
|
-
});
|
|
11
|
-
reporter.reportToolUse("poll_message", {});
|
|
12
|
-
assert.equal(updates.length, 1);
|
|
13
|
-
assert.equal(updates[0]?.status, "tool_using");
|
|
14
|
-
assert.equal(updates[0]?.actionType, "mcp");
|
|
15
|
-
assert.equal(updates[0]?.actionLabel, "Calling MCP");
|
|
16
|
-
assert.equal(updates[0]?.actionDetail, "aws-mcp/poll_message");
|
|
17
|
-
});
|
|
18
|
-
it("redacts transient mini-term paste paths from file action details", () => {
|
|
19
|
-
const updates = [];
|
|
20
|
-
const reporter = new StatusReporter({
|
|
21
|
-
agentId: "agent-self",
|
|
22
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
23
|
-
});
|
|
24
|
-
reporter.reportToolUse("read", {
|
|
25
|
-
path: "C:\\Users\\ZHUANG~1\\AppData\\Local\\Temp\\mini-term-clipboard\\paste-1780484619651.txt",
|
|
26
|
-
});
|
|
27
|
-
assert.equal(updates[0]?.actionDetail, "[temp clipboard paste file]");
|
|
28
|
-
});
|
|
29
|
-
it("redacts transient mini-term paste paths from command action details", () => {
|
|
30
|
-
const updates = [];
|
|
31
|
-
const reporter = new StatusReporter({
|
|
32
|
-
agentId: "agent-self",
|
|
33
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
34
|
-
});
|
|
35
|
-
reporter.reportToolUse("bash", {
|
|
36
|
-
command: "type \"C:\\Users\\ZHUANG~1\\AppData\\Local\\Temp\\mini-term-clipboard\\paste-1780484619651.txt\"",
|
|
37
|
-
});
|
|
38
|
-
assert.equal(updates[0]?.actionDetail, "type \"[temp clipboard paste file]\"");
|
|
39
|
-
});
|
|
40
|
-
it("keeps normal project paths unchanged", () => {
|
|
41
|
-
const updates = [];
|
|
42
|
-
const reporter = new StatusReporter({
|
|
43
|
-
agentId: "agent-self",
|
|
44
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
45
|
-
});
|
|
46
|
-
reporter.reportToolUse("read", {
|
|
47
|
-
path: "D:\\code\\AgentsWorkStudio\\README.md",
|
|
48
|
-
});
|
|
49
|
-
assert.equal(updates[0]?.actionDetail, "D:\\code\\AgentsWorkStudio\\README.md");
|
|
50
|
-
});
|
|
51
|
-
it("redacts transient mini-term paste paths from tool results", () => {
|
|
52
|
-
const updates = [];
|
|
53
|
-
const reporter = new StatusReporter({
|
|
54
|
-
agentId: "agent-self",
|
|
55
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
56
|
-
});
|
|
57
|
-
reporter.reportToolUse("read", { path: "D:\\code\\AgentsWorkStudio\\README.md" });
|
|
58
|
-
reporter.reportToolResult({
|
|
59
|
-
error: "failed: C:\\Users\\ZHUANG~1\\AppData\\Local\\Temp\\mini-term-clipboard\\paste-1780484619651.txt",
|
|
60
|
-
});
|
|
61
|
-
assert.match(String(updates.at(-1)?.actionResult), /\[temp clipboard paste file\]/);
|
|
62
|
-
assert.doesNotMatch(String(updates.at(-1)?.actionResult), /mini-term-clipboard/);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe("StatusReporter pure status transitions must not leak previous action", () => {
|
|
66
|
-
// 回归:一次 poll_message 在时间线上出现 3 条事件,根因是 reportThinking 等
|
|
67
|
-
// 纯状态变更方法在 clearAction() 之前调用 updateStatus,后者从 currentAction
|
|
68
|
-
// 残留值读取 actionType/Label/Detail,导致前端多创建一条 bare 工具事件。
|
|
69
|
-
it("reportThinking after tool use does not carry the tool action", () => {
|
|
70
|
-
const updates = [];
|
|
71
|
-
const reporter = new StatusReporter({
|
|
72
|
-
agentId: "agent-self",
|
|
73
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
74
|
-
});
|
|
75
|
-
reporter.reportToolUse("poll_message", {});
|
|
76
|
-
reporter.reportThinking();
|
|
77
|
-
assert.equal(updates.length, 2);
|
|
78
|
-
const thinking = updates[1];
|
|
79
|
-
assert.equal(thinking?.status, "thinking");
|
|
80
|
-
assert.equal(thinking?.actionType, undefined);
|
|
81
|
-
assert.equal(thinking?.actionLabel, undefined);
|
|
82
|
-
assert.equal(thinking?.actionDetail, undefined);
|
|
83
|
-
});
|
|
84
|
-
it("reportWaitingInput after tool use does not carry the tool action", () => {
|
|
85
|
-
const updates = [];
|
|
86
|
-
const reporter = new StatusReporter({
|
|
87
|
-
agentId: "agent-self",
|
|
88
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
89
|
-
});
|
|
90
|
-
reporter.reportToolUse("poll_message", {});
|
|
91
|
-
reporter.reportWaitingInput();
|
|
92
|
-
const waiting = updates[1];
|
|
93
|
-
assert.equal(waiting?.status, "waiting_input");
|
|
94
|
-
assert.equal(waiting?.actionType, undefined);
|
|
95
|
-
assert.equal(waiting?.actionLabel, undefined);
|
|
96
|
-
assert.equal(waiting?.actionDetail, undefined);
|
|
97
|
-
});
|
|
98
|
-
it("reportStopped after tool use does not carry the tool action", () => {
|
|
99
|
-
const updates = [];
|
|
100
|
-
const reporter = new StatusReporter({
|
|
101
|
-
agentId: "agent-self",
|
|
102
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
103
|
-
});
|
|
104
|
-
reporter.reportToolUse("poll_message", {});
|
|
105
|
-
reporter.reportStopped();
|
|
106
|
-
const stopped = updates[1];
|
|
107
|
-
assert.equal(stopped?.status, "stopped");
|
|
108
|
-
assert.equal(stopped?.actionType, undefined);
|
|
109
|
-
assert.equal(stopped?.actionLabel, undefined);
|
|
110
|
-
assert.equal(stopped?.actionDetail, undefined);
|
|
111
|
-
});
|
|
112
|
-
it("reportToolResult still carries the tool action after reportToolUse", () => {
|
|
113
|
-
const updates = [];
|
|
114
|
-
const reporter = new StatusReporter({
|
|
115
|
-
agentId: "agent-self",
|
|
116
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
117
|
-
});
|
|
118
|
-
reporter.reportToolUse("poll_message", {});
|
|
119
|
-
reporter.reportToolResult({ items: [] });
|
|
120
|
-
const result = updates[1];
|
|
121
|
-
assert.equal(result?.status, "tool_using");
|
|
122
|
-
assert.equal(result?.actionType, "mcp");
|
|
123
|
-
assert.equal(result?.actionLabel, "Calling MCP");
|
|
124
|
-
assert.equal(result?.actionDetail, "aws-mcp/poll_message");
|
|
125
|
-
assert.equal(typeof result?.actionResult, "string");
|
|
126
|
-
});
|
|
127
|
-
it("reportToolError still carries the tool action after reportToolUse", () => {
|
|
128
|
-
const updates = [];
|
|
129
|
-
const reporter = new StatusReporter({
|
|
130
|
-
agentId: "agent-self",
|
|
131
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
132
|
-
});
|
|
133
|
-
reporter.reportToolUse("poll_message", {});
|
|
134
|
-
reporter.reportToolError(new Error("boom"));
|
|
135
|
-
const err = updates[1];
|
|
136
|
-
assert.equal(err?.status, "error");
|
|
137
|
-
assert.equal(err?.actionType, "mcp");
|
|
138
|
-
assert.equal(err?.actionLabel, "Calling MCP");
|
|
139
|
-
assert.equal(err?.actionDetail, "aws-mcp/poll_message");
|
|
140
|
-
assert.match(String(err?.actionResult), /boom/);
|
|
141
|
-
});
|
|
142
|
-
it("poll_message lifecycle emits exactly two tool events (use + result)", () => {
|
|
143
|
-
const updates = [];
|
|
144
|
-
const reporter = new StatusReporter({
|
|
145
|
-
agentId: "agent-self",
|
|
146
|
-
sendStatusUpdate: (update) => updates.push(update),
|
|
147
|
-
});
|
|
148
|
-
reporter.reportToolUse("poll_message", {});
|
|
149
|
-
reporter.reportToolResult({ items: [] });
|
|
150
|
-
reporter.reportThinking();
|
|
151
|
-
// 仅 reportToolUse 和 reportToolResult 应携带 mcp action;
|
|
152
|
-
// reportThinking 是纯状态变更,不应再产生工具事件。
|
|
153
|
-
const toolEvents = updates.filter((u) => u.actionType === "mcp");
|
|
154
|
-
assert.equal(toolEvents.length, 2);
|
|
155
|
-
assert.equal(updates.length, 3);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|