@myagentroam/node 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connector.js +19 -24
- package/dist/service/node-run-event-bridge.js +0 -113
- package/dist/service/scheduled-session-coordinator.js +10 -12
- package/dist/service/workbench-event-projection.js +112 -0
- package/dist/service/workbench-event-service.js +24 -7
- package/dist/terminal.js +13 -12
- package/package.json +4 -4
package/dist/connector.js
CHANGED
|
@@ -87,7 +87,8 @@ import { NodeControlChannel } from './service/node-control-channel.js';
|
|
|
87
87
|
import { NodeControlMessageService } from './service/node-control-message-service.js';
|
|
88
88
|
import { NodeRequestService } from './service/node-request-service.js';
|
|
89
89
|
import { WorkbenchManifestService } from './service/workbench-manifest-service.js';
|
|
90
|
-
import { NodeRunEventBridge
|
|
90
|
+
import { NodeRunEventBridge } from './service/node-run-event-bridge.js';
|
|
91
|
+
import { workbenchPayloadSessionId } from './service/workbench-event-projection.js';
|
|
91
92
|
import { NodeWorkspaceCoordinator } from './service/node-workspace-coordinator.js';
|
|
92
93
|
import { FakeManagedRunService } from './service/fake-managed-run-service.js';
|
|
93
94
|
import { RunnerChannelMessageService } from './service/runner-channel-message-service.js';
|
|
@@ -203,9 +204,18 @@ export class NodeConnector {
|
|
|
203
204
|
configPath;
|
|
204
205
|
reconnect;
|
|
205
206
|
claudeApprovalTimeoutMs;
|
|
206
|
-
workbenchEventService = new WorkbenchEventService(
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
workbenchEventService = new WorkbenchEventService({
|
|
208
|
+
send: (envelope) => this.sendEnvelope(envelope),
|
|
209
|
+
accepts: (category, payload) => {
|
|
210
|
+
const sessionId = workbenchPayloadSessionId((id) => this.runtime.getRun(id), category, payload);
|
|
211
|
+
return sessionId === undefined || !this.sessionIdentityService.bootstrapIds.has(sessionId);
|
|
212
|
+
},
|
|
213
|
+
projection: {
|
|
214
|
+
getRun: (id) => this.runtime.getRun(id),
|
|
215
|
+
conversationTurnForRun: (id) => this.runtime.conversationTurnForRun(id),
|
|
216
|
+
progress: (sessionId) => marctlSessionProgress(this.runtime, sessionId)
|
|
217
|
+
},
|
|
218
|
+
emitRealtime: (sessionId, event) => this.nativeSessionWatchService.emitRealtime(sessionId, event)
|
|
209
219
|
});
|
|
210
220
|
fakeRunner;
|
|
211
221
|
fakeScript;
|
|
@@ -1146,8 +1156,10 @@ export class NodeConnector {
|
|
|
1146
1156
|
nodeLog('node.connection.replaced', {
|
|
1147
1157
|
message: 'This Node connection was replaced by a newer instance with the same identity. Check for duplicate processes.'
|
|
1148
1158
|
});
|
|
1149
|
-
|
|
1150
|
-
|
|
1159
|
+
// Credential revocation ends terminals, but the control channel can still reconnect.
|
|
1160
|
+
// Only final Node disposal shuts down the manager and its idle collector.
|
|
1161
|
+
if (code === 4003)
|
|
1162
|
+
void this.terminalManager.closeAll();
|
|
1151
1163
|
}
|
|
1152
1164
|
});
|
|
1153
1165
|
this.controlMessages = new NodeControlMessageService({
|
|
@@ -1718,24 +1730,7 @@ export class NodeConnector {
|
|
|
1718
1730
|
}
|
|
1719
1731
|
this.emitWorkbenchEvent('session', payload);
|
|
1720
1732
|
};
|
|
1721
|
-
emitWorkbenchEvent = (category, payload) =>
|
|
1722
|
-
payload = this.runEventBridge.projectWorkbenchPayload(category, payload);
|
|
1723
|
-
if (payload === undefined)
|
|
1724
|
-
return;
|
|
1725
|
-
const event = this.workbenchEventService.next(category, payload);
|
|
1726
|
-
if (event === undefined)
|
|
1727
|
-
return;
|
|
1728
|
-
const notification = category === 'conversation' &&
|
|
1729
|
-
isPlainRecord(payload) &&
|
|
1730
|
-
isPlainRecord(payload.event) &&
|
|
1731
|
-
payload.event.eventType === 'notification';
|
|
1732
|
-
const sessionId = workbenchPayloadSessionId(this.runtime, category, payload);
|
|
1733
|
-
if (!notification &&
|
|
1734
|
-
sessionId !== undefined &&
|
|
1735
|
-
this.nativeSessionWatchService.emitRealtime(sessionId, event))
|
|
1736
|
-
return;
|
|
1737
|
-
this.workbenchEventService.publish(event);
|
|
1738
|
-
};
|
|
1733
|
+
emitWorkbenchEvent = (category, payload) => this.workbenchEventService.dispatch(category, payload);
|
|
1739
1734
|
discoverWorkspaceSessions = (workspaceId, onRunnerDiscovered) => this.nativeSessionProjectionService.discoverWorkspace(workspaceId, onRunnerDiscovered);
|
|
1740
1735
|
invalidateWorkspaceWatchTopics = (workspaceId) => this.workspaceCoordinator.invalidate(workspaceId);
|
|
1741
1736
|
reconcilePersistedRuns = () => undefined;
|
|
@@ -1,122 +1,9 @@
|
|
|
1
|
-
import { conversationSegmentIdentityForItem, conversationSegments } from './conversation-segment-service.js';
|
|
2
|
-
import { runConversationItemId } from '../runtime-state.js';
|
|
3
|
-
import { marctlSessionProgress } from './session-recent-activity.js';
|
|
4
|
-
import { isNodeAgentRun, isNodeConversationTurn, isPlainRecord } from '../runner/codex/conversation-parser.js';
|
|
5
|
-
export function workbenchPayloadSessionId(runtime, category, payload) {
|
|
6
|
-
if (!isPlainRecord(payload))
|
|
7
|
-
return undefined;
|
|
8
|
-
if (category === 'session') {
|
|
9
|
-
if (isPlainRecord(payload.session)) {
|
|
10
|
-
const id = payload.session.id;
|
|
11
|
-
if (typeof id === 'string')
|
|
12
|
-
return id;
|
|
13
|
-
}
|
|
14
|
-
return typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
|
|
15
|
-
}
|
|
16
|
-
if (category === 'run' && isNodeAgentRun(payload.run))
|
|
17
|
-
return payload.run.sessionId;
|
|
18
|
-
if (category === 'conversation') {
|
|
19
|
-
if (isNodeConversationTurn(payload.turn))
|
|
20
|
-
return payload.turn.sessionId;
|
|
21
|
-
if (typeof payload.sessionId === 'string')
|
|
22
|
-
return payload.sessionId;
|
|
23
|
-
if (isPlainRecord(payload.event)) {
|
|
24
|
-
const runId = payload.event.runId;
|
|
25
|
-
return typeof runId === 'string' ? runtime.getRun(runId)?.sessionId : undefined;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (category === 'queue') {
|
|
29
|
-
const id = payload.sessionId;
|
|
30
|
-
return typeof id === 'string' ? id : undefined;
|
|
31
|
-
}
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
1
|
/** Bridges process-local Run state to normalized control and Workbench events. */
|
|
35
2
|
export class NodeRunEventBridge {
|
|
36
3
|
options;
|
|
37
4
|
constructor(options) {
|
|
38
5
|
this.options = options;
|
|
39
6
|
}
|
|
40
|
-
/** The sole public projection boundary for normalized Run callbacks. */
|
|
41
|
-
projectWorkbenchPayload(category, payload) {
|
|
42
|
-
if (category === 'conversation' &&
|
|
43
|
-
isPlainRecord(payload) &&
|
|
44
|
-
isPlainRecord(payload.event) &&
|
|
45
|
-
payload.event.eventType !== 'notification') {
|
|
46
|
-
const runId = typeof payload.event.runId === 'string' ? payload.event.runId : undefined;
|
|
47
|
-
const sequence = typeof payload.event.sequence === 'number' ? payload.event.sequence : undefined;
|
|
48
|
-
const turn = runId === undefined ? undefined : this.options.runtime.conversationTurnForRun(runId);
|
|
49
|
-
if (turn === undefined)
|
|
50
|
-
return;
|
|
51
|
-
const rawPayload = isPlainRecord(payload.event.payload) ? payload.event.payload : undefined;
|
|
52
|
-
const text = payload.event.eventType === 'text.delta' && typeof rawPayload?.text === 'string'
|
|
53
|
-
? rawPayload.text
|
|
54
|
-
: undefined;
|
|
55
|
-
const nativeItemId = typeof rawPayload?.itemId === 'string'
|
|
56
|
-
? rawPayload.itemId
|
|
57
|
-
: typeof rawPayload?.approvalId === 'string'
|
|
58
|
-
? `approval:${rawPayload.approvalId}`
|
|
59
|
-
: undefined;
|
|
60
|
-
const item = text !== undefined && sequence !== undefined
|
|
61
|
-
? turn.items.find((candidate) => candidate.kind === 'assistant_message' && candidate.revision === sequence)
|
|
62
|
-
: nativeItemId === undefined
|
|
63
|
-
? undefined
|
|
64
|
-
: turn.items.find((candidate) => candidate.id ===
|
|
65
|
-
runConversationItemId(runId, nativeItemId, typeof rawPayload?.kind === 'string' ? rawPayload.kind : undefined));
|
|
66
|
-
if (text !== undefined && item?.kind === 'assistant_message') {
|
|
67
|
-
const segment = conversationSegmentIdentityForItem(turn, item.id, item.kind);
|
|
68
|
-
payload = {
|
|
69
|
-
sessionId: turn.sessionId,
|
|
70
|
-
delta: {
|
|
71
|
-
turnId: turn.id,
|
|
72
|
-
segmentId: segment.id,
|
|
73
|
-
segmentIndex: segment.index,
|
|
74
|
-
runId: turn.runId,
|
|
75
|
-
itemId: item.id,
|
|
76
|
-
text,
|
|
77
|
-
createdAt: typeof payload.event.createdAt === 'number' ? payload.event.createdAt : Date.now()
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
else if (item !== undefined) {
|
|
82
|
-
const segment = conversationSegmentIdentityForItem(turn, item.id, item.kind);
|
|
83
|
-
payload = {
|
|
84
|
-
sessionId: turn.sessionId,
|
|
85
|
-
runId: turn.runId,
|
|
86
|
-
...(['assistant_message', 'tool_call', 'command_execution', 'file_change'].includes(item.kind) &&
|
|
87
|
-
(rawPayload?.append === undefined || item.status !== 'IN_PROGRESS')
|
|
88
|
-
? { progress: marctlSessionProgress(this.options.runtime, turn.sessionId) }
|
|
89
|
-
: {}),
|
|
90
|
-
item: {
|
|
91
|
-
turnId: turn.id,
|
|
92
|
-
itemId: item.id,
|
|
93
|
-
kind: item.kind,
|
|
94
|
-
status: item.status,
|
|
95
|
-
payload: item.payload,
|
|
96
|
-
startedAt: item.startedAt,
|
|
97
|
-
completedAt: item.completedAt,
|
|
98
|
-
segmentId: segment.id,
|
|
99
|
-
segmentIndex: segment.index
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
if (payload.event.eventType !== 'run.started' && payload.event.eventType !== 'run.running')
|
|
105
|
-
return;
|
|
106
|
-
payload = { turn };
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (category === 'conversation' &&
|
|
110
|
-
isPlainRecord(payload) &&
|
|
111
|
-
isNodeConversationTurn(payload.turn))
|
|
112
|
-
return { ...payload, segments: conversationSegments(payload.turn) };
|
|
113
|
-
if (category === 'run' && isPlainRecord(payload) && isNodeAgentRun(payload.run))
|
|
114
|
-
return {
|
|
115
|
-
...payload,
|
|
116
|
-
progress: marctlSessionProgress(this.options.runtime, payload.run.sessionId)
|
|
117
|
-
};
|
|
118
|
-
return payload;
|
|
119
|
-
}
|
|
120
7
|
emitConversationItem(runId, item) {
|
|
121
8
|
this.emitRun(runId, 'conversation.item', item);
|
|
122
9
|
}
|
|
@@ -16,24 +16,22 @@ export class ScheduledSessionCoordinator {
|
|
|
16
16
|
const store = this.options.store();
|
|
17
17
|
store.finish(run.clientMessageId, status === 'SUCCEEDED' ? 'SUCCEEDED' : 'FAILED', status === 'SUCCEEDED' ? undefined : `SCHEDULE_RUN_${status}`, run.id);
|
|
18
18
|
const task = store.getById(run.clientMessageId.split(':', 1)[0]);
|
|
19
|
-
if (task !== undefined)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
26
|
-
else if (status !== 'SUCCEEDED')
|
|
27
|
-
this.notify(task, run.clientMessageId, `SCHEDULE_RUN_${status}`, 'FAILED');
|
|
28
|
-
}
|
|
19
|
+
if (task !== undefined)
|
|
20
|
+
this.publishTaskNotification(task, run.clientMessageId, {
|
|
21
|
+
kind: 'SCHEDULE_TERMINAL',
|
|
22
|
+
outcome: status,
|
|
23
|
+
runId: run.id
|
|
24
|
+
});
|
|
29
25
|
}
|
|
30
26
|
notify = (task, occurrenceId, code, status) => this.publishTaskNotification(task, occurrenceId, {
|
|
31
27
|
kind: status === 'FAILED' ? 'SCHEDULE_FAILED' : 'SCHEDULE_SKIPPED',
|
|
32
28
|
code
|
|
33
29
|
});
|
|
34
30
|
publishTaskNotification = (task, occurrenceId, event) => {
|
|
31
|
+
const currentTask = this.options.store().getById(task.id) ?? task;
|
|
32
|
+
if (!currentTask.notifyOnTerminal)
|
|
33
|
+
return;
|
|
35
34
|
const occurredAt = Date.now();
|
|
36
|
-
const recipient = this.options.store().getById(task.id)?.ownerUserId ?? task.ownerUserId;
|
|
37
35
|
this.options.emitNotification({
|
|
38
36
|
event: {
|
|
39
37
|
runId: event.kind === 'SCHEDULE_TERMINAL' ? event.runId : occurrenceId,
|
|
@@ -41,7 +39,7 @@ export class ScheduledSessionCoordinator {
|
|
|
41
39
|
eventType: 'notification',
|
|
42
40
|
payload: {
|
|
43
41
|
targetType: 'WORKSPACE_SESSION',
|
|
44
|
-
userId:
|
|
42
|
+
userId: currentTask.ownerUserId,
|
|
45
43
|
workspaceId: task.workspaceId,
|
|
46
44
|
sessionId: task.sessionId,
|
|
47
45
|
sessionTitle: this.options.sessionTitle(task.sessionId),
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { conversationSegmentIdentityForItem, conversationSegments } from './conversation-segment-service.js';
|
|
2
|
+
import { runConversationItemId } from '../runtime-state.js';
|
|
3
|
+
import { isNodeAgentRun, isNodeConversationTurn, isPlainRecord } from '../runner/codex/conversation-parser.js';
|
|
4
|
+
export function workbenchPayloadSessionId(getRun, category, payload) {
|
|
5
|
+
if (!isPlainRecord(payload))
|
|
6
|
+
return undefined;
|
|
7
|
+
if (category === 'session') {
|
|
8
|
+
if (isPlainRecord(payload.session)) {
|
|
9
|
+
const id = payload.session.id;
|
|
10
|
+
if (typeof id === 'string')
|
|
11
|
+
return id;
|
|
12
|
+
}
|
|
13
|
+
return typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
|
|
14
|
+
}
|
|
15
|
+
if (category === 'run' && isNodeAgentRun(payload.run))
|
|
16
|
+
return payload.run.sessionId;
|
|
17
|
+
if (category === 'conversation') {
|
|
18
|
+
if (isNodeConversationTurn(payload.turn))
|
|
19
|
+
return payload.turn.sessionId;
|
|
20
|
+
if (typeof payload.sessionId === 'string')
|
|
21
|
+
return payload.sessionId;
|
|
22
|
+
if (isPlainRecord(payload.event)) {
|
|
23
|
+
const runId = payload.event.runId;
|
|
24
|
+
return typeof runId === 'string' ? getRun(runId)?.sessionId : undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (category === 'queue') {
|
|
28
|
+
const id = payload.sessionId;
|
|
29
|
+
return typeof id === 'string' ? id : undefined;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Projects normalized runtime facts without publishing or mutating them. */
|
|
34
|
+
export function projectWorkbenchPayload(queries, category, payload) {
|
|
35
|
+
if (category === 'conversation' &&
|
|
36
|
+
isPlainRecord(payload) &&
|
|
37
|
+
isPlainRecord(payload.event) &&
|
|
38
|
+
payload.event.eventType !== 'notification') {
|
|
39
|
+
const runId = typeof payload.event.runId === 'string' ? payload.event.runId : undefined;
|
|
40
|
+
const sequence = typeof payload.event.sequence === 'number' ? payload.event.sequence : undefined;
|
|
41
|
+
const turn = runId === undefined ? undefined : queries.conversationTurnForRun(runId);
|
|
42
|
+
if (turn === undefined)
|
|
43
|
+
return;
|
|
44
|
+
const rawPayload = isPlainRecord(payload.event.payload) ? payload.event.payload : undefined;
|
|
45
|
+
const text = payload.event.eventType === 'text.delta' && typeof rawPayload?.text === 'string'
|
|
46
|
+
? rawPayload.text
|
|
47
|
+
: undefined;
|
|
48
|
+
const nativeItemId = typeof rawPayload?.itemId === 'string'
|
|
49
|
+
? rawPayload.itemId
|
|
50
|
+
: typeof rawPayload?.approvalId === 'string'
|
|
51
|
+
? `approval:${rawPayload.approvalId}`
|
|
52
|
+
: undefined;
|
|
53
|
+
const item = text !== undefined && sequence !== undefined
|
|
54
|
+
? turn.items.find((candidate) => candidate.kind === 'assistant_message' && candidate.revision === sequence)
|
|
55
|
+
: nativeItemId === undefined
|
|
56
|
+
? undefined
|
|
57
|
+
: turn.items.find((candidate) => candidate.id ===
|
|
58
|
+
runConversationItemId(runId, nativeItemId, typeof rawPayload?.kind === 'string' ? rawPayload.kind : undefined));
|
|
59
|
+
if (text !== undefined && item?.kind === 'assistant_message') {
|
|
60
|
+
const segment = conversationSegmentIdentityForItem(turn, item.id, item.kind);
|
|
61
|
+
payload = {
|
|
62
|
+
sessionId: turn.sessionId,
|
|
63
|
+
delta: {
|
|
64
|
+
turnId: turn.id,
|
|
65
|
+
segmentId: segment.id,
|
|
66
|
+
segmentIndex: segment.index,
|
|
67
|
+
runId: turn.runId,
|
|
68
|
+
itemId: item.id,
|
|
69
|
+
text,
|
|
70
|
+
createdAt: typeof payload.event.createdAt === 'number'
|
|
71
|
+
? payload.event.createdAt
|
|
72
|
+
: (queries.now ?? Date.now)()
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
else if (item !== undefined) {
|
|
77
|
+
const segment = conversationSegmentIdentityForItem(turn, item.id, item.kind);
|
|
78
|
+
payload = {
|
|
79
|
+
sessionId: turn.sessionId,
|
|
80
|
+
runId: turn.runId,
|
|
81
|
+
...(['assistant_message', 'tool_call', 'command_execution', 'file_change'].includes(item.kind) &&
|
|
82
|
+
(rawPayload?.append === undefined || item.status !== 'IN_PROGRESS')
|
|
83
|
+
? { progress: queries.progress(turn.sessionId) }
|
|
84
|
+
: {}),
|
|
85
|
+
item: {
|
|
86
|
+
turnId: turn.id,
|
|
87
|
+
itemId: item.id,
|
|
88
|
+
kind: item.kind,
|
|
89
|
+
status: item.status,
|
|
90
|
+
payload: item.payload,
|
|
91
|
+
startedAt: item.startedAt,
|
|
92
|
+
completedAt: item.completedAt,
|
|
93
|
+
segmentId: segment.id,
|
|
94
|
+
segmentIndex: segment.index
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
if (payload.event.eventType !== 'run.started' && payload.event.eventType !== 'run.running')
|
|
100
|
+
return;
|
|
101
|
+
payload = { turn };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (category === 'conversation' && isPlainRecord(payload) && isNodeConversationTurn(payload.turn))
|
|
105
|
+
return { ...payload, segments: conversationSegments(payload.turn) };
|
|
106
|
+
if (category === 'run' && isPlainRecord(payload) && isNodeAgentRun(payload.run))
|
|
107
|
+
return {
|
|
108
|
+
...payload,
|
|
109
|
+
progress: queries.progress(payload.run.sessionId)
|
|
110
|
+
};
|
|
111
|
+
return payload;
|
|
112
|
+
}
|
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
import { createEnvelope } from '@myagentroam/protocol';
|
|
2
|
+
import { isPlainRecord } from '../runner/codex/conversation-parser.js';
|
|
3
|
+
import { projectWorkbenchPayload, workbenchPayloadSessionId } from './workbench-event-projection.js';
|
|
2
4
|
export class WorkbenchEventService {
|
|
3
|
-
|
|
4
|
-
accepts;
|
|
5
|
+
options;
|
|
5
6
|
sequence = 0;
|
|
6
|
-
constructor(
|
|
7
|
-
this.
|
|
8
|
-
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
dispatch(category, payload) {
|
|
11
|
+
payload = projectWorkbenchPayload(this.options.projection, category, payload);
|
|
12
|
+
if (payload === undefined)
|
|
13
|
+
return;
|
|
14
|
+
const event = this.next(category, payload);
|
|
15
|
+
if (event === undefined)
|
|
16
|
+
return;
|
|
17
|
+
const notification = category === 'conversation' &&
|
|
18
|
+
isPlainRecord(payload) &&
|
|
19
|
+
isPlainRecord(payload.event) &&
|
|
20
|
+
payload.event.eventType === 'notification';
|
|
21
|
+
const sessionId = workbenchPayloadSessionId(this.options.projection.getRun, category, payload);
|
|
22
|
+
// A Watch owns pending buffering and observation drops as well as ready delivery.
|
|
23
|
+
if (!notification && sessionId !== undefined && this.options.emitRealtime(sessionId, event))
|
|
24
|
+
return;
|
|
25
|
+
this.publish(event);
|
|
9
26
|
}
|
|
10
27
|
next(category, payload) {
|
|
11
|
-
if (!this.accepts(category, payload))
|
|
28
|
+
if (!this.options.accepts(category, payload))
|
|
12
29
|
return undefined;
|
|
13
30
|
return { sequence: ++this.sequence, category, payload };
|
|
14
31
|
}
|
|
15
32
|
publish(event) {
|
|
16
|
-
this.send(createEnvelope('workbench.event', event));
|
|
33
|
+
this.options.send(createEnvelope('workbench.event', event));
|
|
17
34
|
}
|
|
18
35
|
snapshotSequence() {
|
|
19
36
|
return this.sequence;
|
package/dist/terminal.js
CHANGED
|
@@ -318,17 +318,14 @@ export class TerminalManager {
|
|
|
318
318
|
const summary = this.notifySummary(terminal);
|
|
319
319
|
try {
|
|
320
320
|
if (terminal.pty !== undefined) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
catch {
|
|
327
|
-
await this.terminate(terminal.pty, this.platform);
|
|
328
|
-
}
|
|
321
|
+
// Native PTY hangup reaches POSIX foreground jobs in a separate process group
|
|
322
|
+
// and releases the native console on Windows; shell-group TERM cannot do this.
|
|
323
|
+
try {
|
|
324
|
+
terminal.pty.kill();
|
|
329
325
|
}
|
|
330
|
-
|
|
326
|
+
catch {
|
|
331
327
|
await this.terminate(terminal.pty, this.platform);
|
|
328
|
+
}
|
|
332
329
|
}
|
|
333
330
|
if (terminal.pipe !== undefined)
|
|
334
331
|
await this.terminate(terminal.pipe, this.platform);
|
|
@@ -338,13 +335,17 @@ export class TerminalManager {
|
|
|
338
335
|
}
|
|
339
336
|
return summary;
|
|
340
337
|
}
|
|
338
|
+
/** Ends current terminals without disposing the manager's idle collection. */
|
|
339
|
+
async closeAll() {
|
|
340
|
+
await Promise.all([...this.terminals.values()]
|
|
341
|
+
.filter((terminal) => terminal.state === 'RUNNING')
|
|
342
|
+
.map((terminal) => this.close(terminal.id)));
|
|
343
|
+
}
|
|
341
344
|
async shutdown() {
|
|
342
345
|
if (this.idleTimer !== undefined)
|
|
343
346
|
clearInterval(this.idleTimer);
|
|
344
347
|
this.idleTimer = undefined;
|
|
345
|
-
await
|
|
346
|
-
.filter((terminal) => terminal.state === 'RUNNING')
|
|
347
|
-
.map((terminal) => this.close(terminal.id)));
|
|
348
|
+
await this.closeAll();
|
|
348
349
|
}
|
|
349
350
|
onFrame(listener) {
|
|
350
351
|
this.frameListeners.add(listener);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"playwright": "1.62.1",
|
|
38
38
|
"ws": "^8.21.3",
|
|
39
39
|
"zod": "4.4.3",
|
|
40
|
-
"@myagentroam/agent": "0.11.
|
|
41
|
-
"@myagentroam/marctl": "0.11.0",
|
|
40
|
+
"@myagentroam/agent": "0.11.1",
|
|
42
41
|
"@myagentroam/code-navigation": "0.11.0",
|
|
43
|
-
"@myagentroam/
|
|
42
|
+
"@myagentroam/marctl": "0.11.0",
|
|
43
|
+
"@myagentroam/protocol": "0.11.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/ws": "^8.18.1"
|