@myagentroam/node 0.9.105 → 0.9.106
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 +1 -0
- package/dist/runner/codex/conversation-parser.js +9 -20
- package/dist/runner/mar-agent/history-projection.js +9 -4
- package/dist/runner/opencode/conversation-parser.js +8 -4
- package/dist/service/conversation-history-service.js +34 -8
- package/dist/service/conversation-segment-service.js +1 -1
- package/dist/service/native-session-watch-service.js +31 -25
- package/dist/service/run-event-service.js +6 -1
- package/dist/service/session-command-service.js +8 -4
- package/dist/util/runner-native-session-parsers.js +3 -17
- package/package.json +4 -4
package/dist/connector.js
CHANGED
|
@@ -856,6 +856,7 @@ export class NodeConnector {
|
|
|
856
856
|
runtime: this.runtime,
|
|
857
857
|
runners: this.runners,
|
|
858
858
|
projection: this.nativeSessionProjectionService,
|
|
859
|
+
resolveForCommand: (sessionId) => this.externalSessionResumeService.resolveForMessage(sessionId),
|
|
859
860
|
profiles: () => this.runnerService.profiles(),
|
|
860
861
|
present: (session) => this.sessionPresentationService.present(session),
|
|
861
862
|
resumeQueueAfterCommandRun: (sessionId) => this.workspaceQueueWorkbenchService.resumeAfterCommandRun(sessionId),
|
|
@@ -43,21 +43,17 @@ export function codexOfficialTurn(session, value, sequence, managedTurn) {
|
|
|
43
43
|
if (items.length === 0)
|
|
44
44
|
return undefined;
|
|
45
45
|
if (managedTurn !== undefined) {
|
|
46
|
+
const officialComplete = value.status === 'completed' ||
|
|
47
|
+
value.status === 'failed' ||
|
|
48
|
+
value.status === 'interrupted' ||
|
|
49
|
+
(value.status === undefined && codexTimestamp(value.completedAt) !== null);
|
|
46
50
|
const localUser = managedTurn.items.find((item) => item.kind === 'user_message');
|
|
47
51
|
const officialNativeItemIds = codexNativeItemIds(value.items);
|
|
48
52
|
const officialAssistantCount = items.filter((item) => item.kind === 'assistant_message').length;
|
|
49
53
|
const officialAssistantMessage = officialAssistantCount > 0;
|
|
50
54
|
const managedAssistants = managedTurn.items.filter((item) => item.kind === 'assistant_message' && conversationItemNativeId(item.id) === undefined);
|
|
51
55
|
const managedAssistantCandidates = officialAssistantCount === 0 ? [] : managedAssistants.slice(-officialAssistantCount);
|
|
52
|
-
const
|
|
53
|
-
if (item.kind !== 'assistant_message' || !isPlainRecord(item.payload))
|
|
54
|
-
return length;
|
|
55
|
-
return length + (typeof item.payload.text === 'string' ? item.payload.text.length : 0);
|
|
56
|
-
}, 0);
|
|
57
|
-
const preferLocalAssistant = officialAssistantMessage &&
|
|
58
|
-
(assistantLength(managedAssistantCandidates) > assistantLength(items) ||
|
|
59
|
-
((managedTurn.completedAt ?? 0) > (completedAt ?? 0) &&
|
|
60
|
-
assistantLength(managedAssistantCandidates) > 0));
|
|
56
|
+
const preferLocalAssistant = officialAssistantMessage && !officialComplete;
|
|
61
57
|
const replacedManagedAssistantIds = new Set(preferLocalAssistant
|
|
62
58
|
? []
|
|
63
59
|
: officialAssistantCount === 0
|
|
@@ -66,6 +62,8 @@ export function codexOfficialTurn(session, value, sequence, managedTurn) {
|
|
|
66
62
|
const localLiveItems = managedTurn.items.filter((item) => {
|
|
67
63
|
if (item.kind === 'user_message')
|
|
68
64
|
return false;
|
|
65
|
+
if (preferLocalAssistant && item.kind === 'assistant_message')
|
|
66
|
+
return true;
|
|
69
67
|
// `item/agentMessage/delta` uses a synthetic runtime ID. Once
|
|
70
68
|
// `thread/read` returns the final Agent message, it is the same reply
|
|
71
69
|
// without a shared native item ID and must replace that transient item.
|
|
@@ -112,15 +110,6 @@ export function codexOfficialTurn(session, value, sequence, managedTurn) {
|
|
|
112
110
|
replacement = officialAssistants.find((item) => !consumedOfficialIds.has(item.id));
|
|
113
111
|
}
|
|
114
112
|
if (replacement !== undefined) {
|
|
115
|
-
if (managedItem.kind === 'assistant_message' &&
|
|
116
|
-
replacement.kind === 'assistant_message' &&
|
|
117
|
-
(assistantLength([managedItem]) > assistantLength([replacement]) ||
|
|
118
|
-
((managedTurn.completedAt ?? 0) > (completedAt ?? 0) &&
|
|
119
|
-
assistantLength([managedItem]) > 0))) {
|
|
120
|
-
reconciledItems.push(managedItem);
|
|
121
|
-
consumedOfficialIds.add(replacement.id);
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
113
|
if (!consumedOfficialIds.has(replacement.id)) {
|
|
125
114
|
reconciledItems.push(replacement);
|
|
126
115
|
consumedOfficialIds.add(replacement.id);
|
|
@@ -134,13 +123,13 @@ export function codexOfficialTurn(session, value, sequence, managedTurn) {
|
|
|
134
123
|
reconciledItems.push(item);
|
|
135
124
|
return {
|
|
136
125
|
...managedTurn,
|
|
137
|
-
status: 'SUCCEEDED',
|
|
126
|
+
status: officialComplete ? 'SUCCEEDED' : managedTurn.status,
|
|
138
127
|
// Reconcile against the managed runtime order. Official items replace
|
|
139
128
|
// matching live projections in place; newly visible terminal items such
|
|
140
129
|
// as a plan are appended after the commentary and tools that preceded it.
|
|
141
130
|
items: reconciledItems,
|
|
142
131
|
startedAt,
|
|
143
|
-
completedAt
|
|
132
|
+
completedAt: officialComplete ? completedAt : null
|
|
144
133
|
};
|
|
145
134
|
}
|
|
146
135
|
return {
|
|
@@ -20,7 +20,7 @@ export function projectMarAgentHistory(session, entries, options = {}) {
|
|
|
20
20
|
rewind: { before: previous?.id ?? null, after: turnId },
|
|
21
21
|
items: [],
|
|
22
22
|
startedAt: at,
|
|
23
|
-
completedAt:
|
|
23
|
+
completedAt: null
|
|
24
24
|
};
|
|
25
25
|
turns.set(turnId, created);
|
|
26
26
|
return created;
|
|
@@ -32,13 +32,17 @@ export function projectMarAgentHistory(session, entries, options = {}) {
|
|
|
32
32
|
items.push(item);
|
|
33
33
|
else
|
|
34
34
|
items[index] = { ...items[index], ...item, startedAt: items[index].startedAt };
|
|
35
|
-
turns.set(turn.id, { ...turn, items
|
|
35
|
+
turns.set(turn.id, { ...turn, items });
|
|
36
36
|
};
|
|
37
37
|
for (const entry of entries) {
|
|
38
38
|
if (entry.kind === 'user') {
|
|
39
39
|
const at = Date.parse(entry.timestamp);
|
|
40
40
|
const turn = ensure(entry.turnId, at);
|
|
41
|
-
|
|
41
|
+
// SDK mailbox messages are persisted as additional turn.user records in the same Turn.
|
|
42
|
+
// The first input keeps its existing identity; later inputs use their storage sequence.
|
|
43
|
+
const id = turn.userItemId === null
|
|
44
|
+
? `${entry.turnId}:user`
|
|
45
|
+
: `${entry.turnId}:user:${entry.sequence}`;
|
|
42
46
|
const images = entry.images;
|
|
43
47
|
const registeredImages = images === undefined ? [] : (options.registerInputImages?.(entry) ?? []);
|
|
44
48
|
replace(turn, historyItem(session, turn, id, 'user_message', 'COMPLETED', {
|
|
@@ -56,7 +60,8 @@ export function projectMarAgentHistory(session, entries, options = {}) {
|
|
|
56
60
|
}
|
|
57
61
|
: {})
|
|
58
62
|
}, at));
|
|
59
|
-
|
|
63
|
+
if (turn.userItemId === null)
|
|
64
|
+
turns.set(entry.turnId, { ...turns.get(entry.turnId), userItemId: id });
|
|
60
65
|
continue;
|
|
61
66
|
}
|
|
62
67
|
const event = entry.event;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { boundedConversationItemId, compactRunnerText } from '../codex/conversation-parser.js';
|
|
2
|
-
import { runtimeAssistantProjectionMoreComplete } from '../../util/runner-native-session-parsers.js';
|
|
3
2
|
import { stripMissionStartPrompt } from '../../service/mission-runtime.js';
|
|
4
3
|
import { normalizeOpenCodeTool } from './tool-normalizer.js';
|
|
5
4
|
export function openCodeConversationPage(session, messages, input, managedTurn) {
|
|
@@ -73,9 +72,14 @@ function openCodeTurn(session, user, userParts, assistants, sequence, managed) {
|
|
|
73
72
|
const completedAt = Math.max(startedAt ?? 0, ...assistants.map(({ info }) => number(record(info.time)?.completed) ?? 0)) || startedAt;
|
|
74
73
|
const id = managed?.id ?? `${session.id}:official:${nativeTurnId}`;
|
|
75
74
|
const runId = managed?.runId ?? null;
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const officialAssistantIds = new Set(assistants.flatMap(({ parts }) => parts.flatMap((part) => {
|
|
76
|
+
const value = record(part);
|
|
77
|
+
return value?.type === 'text' && typeof value.id === 'string'
|
|
78
|
+
? [`${managed?.runId}:${boundedConversationItemId('opencode', value.id)}`]
|
|
79
|
+
: [];
|
|
80
|
+
})));
|
|
81
|
+
const pendingOfficialReply = managed?.items.some((item) => item.kind === 'assistant_message' && !officialAssistantIds.has(item.id));
|
|
82
|
+
const projectedItems = pendingOfficialReply ? managed.items : items;
|
|
79
83
|
return {
|
|
80
84
|
id,
|
|
81
85
|
sessionId: session.id,
|
|
@@ -13,6 +13,11 @@ export function convergeConversationWatchPage(previous, previousObserved, next,
|
|
|
13
13
|
const authority = isConversationHistorySource(previousAuthority)
|
|
14
14
|
? previousAuthority
|
|
15
15
|
: previous?.source;
|
|
16
|
+
if (next.deferredOlderHistory === true && previous !== undefined)
|
|
17
|
+
return {
|
|
18
|
+
page: composeConversationRuntimePage(previous, next),
|
|
19
|
+
authority: authority ?? next.source
|
|
20
|
+
};
|
|
16
21
|
if (previous === undefined ||
|
|
17
22
|
authority === undefined ||
|
|
18
23
|
sourceRank(next.source) >= sourceRank(authority))
|
|
@@ -91,13 +96,37 @@ export class ConversationHistoryService {
|
|
|
91
96
|
const snapshotSequence = this.options.snapshotSequence();
|
|
92
97
|
const readAt = Date.now();
|
|
93
98
|
const activeCursor = decodeActiveHistoryCursor(input.cursor, session.id);
|
|
94
|
-
if (activeCursor !== undefined ||
|
|
99
|
+
if (activeCursor !== undefined ||
|
|
100
|
+
this.options.hasManagedActiveRun(session) ||
|
|
101
|
+
this.hasManagedRuntimeHistory(session))
|
|
95
102
|
return this.readManagedActive(session, input, activeCursor, snapshotSequence, readAt);
|
|
96
103
|
return this.readHistory(session, input, snapshotSequence, readAt);
|
|
97
104
|
}
|
|
98
105
|
async readHistoryPage(session, input) {
|
|
106
|
+
if (this.hasManagedRuntimeHistory(session)) {
|
|
107
|
+
const runtimeTurns = this.options.runtime.listConversationTurns({
|
|
108
|
+
sessionId: session.id,
|
|
109
|
+
limit: 500
|
|
110
|
+
}).turns;
|
|
111
|
+
const olderCursor = this.options.hasManagedActiveRun(session)
|
|
112
|
+
? {
|
|
113
|
+
sessionId: session.id,
|
|
114
|
+
stage: 'OFFICIAL',
|
|
115
|
+
beforeAt: oldestTurnActivityAt(runtimeTurns),
|
|
116
|
+
cursor: null
|
|
117
|
+
}
|
|
118
|
+
: undefined;
|
|
119
|
+
return this.readManagedActive(session, input, olderCursor, this.options.snapshotSequence(), Date.now());
|
|
120
|
+
}
|
|
99
121
|
return this.readHistory(session, input, this.options.snapshotSequence(), Date.now());
|
|
100
122
|
}
|
|
123
|
+
hasManagedRuntimeHistory(session) {
|
|
124
|
+
return (session.runner === 'mar-agent' &&
|
|
125
|
+
session.nativeControl === 'MAR_MANAGED' &&
|
|
126
|
+
this.options.runtime.getAgentSession(session.id) !== undefined &&
|
|
127
|
+
this.options.runtime.listConversationTurns({ sessionId: session.id, limit: 1 }).turns.length >
|
|
128
|
+
0);
|
|
129
|
+
}
|
|
101
130
|
async readHistory(session, input, snapshotSequence, readAt) {
|
|
102
131
|
const selected = await this.readRunnerHistory(session, input);
|
|
103
132
|
if (selected.page?.source === 'native')
|
|
@@ -113,9 +142,9 @@ export class ConversationHistoryService {
|
|
|
113
142
|
? []
|
|
114
143
|
: this.options.runtime.listConversationTurns({ sessionId: session.id, limit: 500 }).turns;
|
|
115
144
|
const initialOfficialTurns = official.turns;
|
|
116
|
-
const lastRepresentedRuntimeIndex = runtimeTurns.findLastIndex((turn) => initialOfficialTurns.some((officialTurn) =>
|
|
145
|
+
const lastRepresentedRuntimeIndex = runtimeTurns.findLastIndex((turn) => initialOfficialTurns.some((officialTurn) => sameConversationTurn(officialTurn, turn)));
|
|
117
146
|
const officialHasTurns = initialOfficialTurns.length > 0;
|
|
118
|
-
const absentLocalTurns = runtimeTurns.filter((turn, index) => !initialOfficialTurns.some((officialTurn) =>
|
|
147
|
+
const absentLocalTurns = runtimeTurns.filter((turn, index) => !initialOfficialTurns.some((officialTurn) => sameConversationTurn(officialTurn, turn)) &&
|
|
119
148
|
(!officialHasTurns || index > lastRepresentedRuntimeIndex));
|
|
120
149
|
const missingLocalTurns = absentLocalTurns.slice(-limit);
|
|
121
150
|
if (input.cursor === undefined &&
|
|
@@ -130,7 +159,7 @@ export class ConversationHistoryService {
|
|
|
130
159
|
const runtimeTurnsByOfficialId = new Map();
|
|
131
160
|
const runtimeSupplementalItemsByOfficialId = new Map();
|
|
132
161
|
for (const officialTurn of officialTurns) {
|
|
133
|
-
const runtimeTurn = runtimeTurns.find((candidate) =>
|
|
162
|
+
const runtimeTurn = runtimeTurns.find((candidate) => sameConversationTurn(officialTurn, candidate));
|
|
134
163
|
if (runtimeTurn === undefined)
|
|
135
164
|
continue;
|
|
136
165
|
runtimeTurnsByOfficialId.set(officialTurn.id, runtimeTurn);
|
|
@@ -459,7 +488,7 @@ function latestTurnActivityAt(turn) {
|
|
|
459
488
|
return Math.max(turn.startedAt ?? 0, turn.completedAt ?? 0);
|
|
460
489
|
}
|
|
461
490
|
function oldestTurnActivityAt(turns) {
|
|
462
|
-
return turns.length === 0 ? 0 : Math.min(...turns.map(
|
|
491
|
+
return turns.length === 0 ? 0 : Math.min(...turns.map(earliestTurnActivityAt));
|
|
463
492
|
}
|
|
464
493
|
function mergeRuntimeUserImageReferences(officialTurn, runtimeTurn, attachments) {
|
|
465
494
|
const officialUser = officialTurn.items.find((item) => item.kind === 'user_message');
|
|
@@ -524,9 +553,6 @@ function mergeRuntimeUserImageReferences(officialTurn, runtimeTurn, attachments)
|
|
|
524
553
|
: item)
|
|
525
554
|
};
|
|
526
555
|
}
|
|
527
|
-
function sameHistoryConversationTurn(left, right) {
|
|
528
|
-
return sameConversationTurn(left, right) || (left.runId !== null && left.runId === right.runId);
|
|
529
|
-
}
|
|
530
556
|
function sameConversationTurn(left, right) {
|
|
531
557
|
if (left.id === right.id)
|
|
532
558
|
return true;
|
|
@@ -6,7 +6,7 @@ function conversationSegmentPrefix(turn) {
|
|
|
6
6
|
: undefined;
|
|
7
7
|
const clientMessageId = payload?.clientMessageId;
|
|
8
8
|
return typeof clientMessageId === 'string' && clientMessageId.length > 0
|
|
9
|
-
?
|
|
9
|
+
? `message:${clientMessageId}`
|
|
10
10
|
: turn.id;
|
|
11
11
|
}
|
|
12
12
|
export function conversationSegments(turn) {
|
|
@@ -115,6 +115,7 @@ export class NativeSessionWatchService {
|
|
|
115
115
|
mode: this.options.managedActive(session) ? 'RUNNING' : 'HISTORY',
|
|
116
116
|
expiresAt: Date.now() + TTL_MS,
|
|
117
117
|
refresh: undefined,
|
|
118
|
+
refreshMode: undefined,
|
|
118
119
|
sessionSignature: undefined,
|
|
119
120
|
signature: undefined,
|
|
120
121
|
page: undefined,
|
|
@@ -202,7 +203,7 @@ export class NativeSessionWatchService {
|
|
|
202
203
|
get size() {
|
|
203
204
|
return this.watches.size;
|
|
204
205
|
}
|
|
205
|
-
async refresh(sessionId, limit = 10,
|
|
206
|
+
async refresh(sessionId, limit = 10, mode = 'observation') {
|
|
206
207
|
const watch = this.watches.get(sessionId);
|
|
207
208
|
if (watch === undefined)
|
|
208
209
|
return undefined;
|
|
@@ -212,20 +213,19 @@ export class NativeSessionWatchService {
|
|
|
212
213
|
this.stop(sessionId);
|
|
213
214
|
return undefined;
|
|
214
215
|
}
|
|
215
|
-
const refresh = this.performRefresh(sessionId, watch, limit,
|
|
216
|
-
watch.refresh = refresh;
|
|
217
|
-
try {
|
|
218
|
-
return await refresh;
|
|
219
|
-
}
|
|
220
|
-
finally {
|
|
216
|
+
const refresh = this.performRefresh(sessionId, watch, limit, mode).finally(() => {
|
|
221
217
|
if (this.watches.get(sessionId) === watch) {
|
|
222
218
|
watch.refresh = undefined;
|
|
219
|
+
watch.refreshMode = undefined;
|
|
223
220
|
clearTimeout(watch.timer);
|
|
224
221
|
watch.timer = this.refreshTimer(sessionId);
|
|
225
222
|
}
|
|
226
|
-
}
|
|
223
|
+
});
|
|
224
|
+
watch.refresh = refresh;
|
|
225
|
+
watch.refreshMode = mode;
|
|
226
|
+
return refresh;
|
|
227
227
|
}
|
|
228
|
-
async performRefresh(sessionId, watch, limit,
|
|
228
|
+
async performRefresh(sessionId, watch, limit, mode) {
|
|
229
229
|
let stage = 'resolve';
|
|
230
230
|
let session;
|
|
231
231
|
const readHistoricalPage = watch.mode === 'HISTORY' && this.options.readHistoryPage !== undefined;
|
|
@@ -238,7 +238,7 @@ export class NativeSessionWatchService {
|
|
|
238
238
|
return undefined;
|
|
239
239
|
}
|
|
240
240
|
watch.session = session;
|
|
241
|
-
if (
|
|
241
|
+
if (mode === 'observation' && this.options.managedActive(session))
|
|
242
242
|
return watch.page;
|
|
243
243
|
stage = 'refresh_activity';
|
|
244
244
|
await this.options.refreshActivity(session);
|
|
@@ -249,14 +249,14 @@ export class NativeSessionWatchService {
|
|
|
249
249
|
const page = await (readHistoricalPage
|
|
250
250
|
? this.options.readHistoryPage(session, limit)
|
|
251
251
|
: this.options.readPage(session, limit));
|
|
252
|
-
if (
|
|
252
|
+
if (mode === 'observation' && revisionBeforeRead !== watch.eventRevision)
|
|
253
253
|
return watch.page;
|
|
254
254
|
stage = 'present_session';
|
|
255
255
|
const presented = this.options.present(session);
|
|
256
256
|
const sessionSignature = JSON.stringify(presented);
|
|
257
257
|
if (sessionSignature !== watch.sessionSignature) {
|
|
258
258
|
watch.sessionSignature = sessionSignature;
|
|
259
|
-
if (
|
|
259
|
+
if (mode === 'observation')
|
|
260
260
|
this.options.emitSession(presented);
|
|
261
261
|
}
|
|
262
262
|
watch.failures = 0;
|
|
@@ -288,11 +288,13 @@ export class NativeSessionWatchService {
|
|
|
288
288
|
watch.page = accepted;
|
|
289
289
|
}
|
|
290
290
|
stage = 'emit_page';
|
|
291
|
-
if (
|
|
291
|
+
if (completingConvergence && accepted.source === 'runtime')
|
|
292
|
+
this.releasePendingEvents(session.id);
|
|
293
|
+
else if (completingConvergence)
|
|
292
294
|
this.emitSnapshotToLeases(session, accepted, true);
|
|
293
|
-
else if (
|
|
295
|
+
else if (changed)
|
|
294
296
|
this.emitObservedPage(session, accepted);
|
|
295
|
-
return
|
|
297
|
+
return mode === 'initial' ? page : accepted;
|
|
296
298
|
}
|
|
297
299
|
catch (error) {
|
|
298
300
|
watch.failures += 1;
|
|
@@ -345,15 +347,19 @@ export class NativeSessionWatchService {
|
|
|
345
347
|
const activeAtStart = this.options.managedActive(session);
|
|
346
348
|
if (watch !== undefined)
|
|
347
349
|
watch.mode = activeAtStart ? 'RUNNING' : watch.mode;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
350
|
+
let page;
|
|
351
|
+
if (watch !== undefined) {
|
|
352
|
+
if (activeAtStart)
|
|
353
|
+
page = this.composeRuntimePage(watch.page, await this.readRuntimePage(watch, session));
|
|
354
|
+
else {
|
|
355
|
+
// An initial subscription must read after any older observation, not join its
|
|
356
|
+
// potentially smaller or already-stale page.
|
|
357
|
+
if (watch.refresh !== undefined &&
|
|
358
|
+
(watch.page !== undefined || watch.refreshMode === 'observation'))
|
|
359
|
+
await watch.refresh;
|
|
360
|
+
page = await this.refresh(session.id, INITIAL_TURN_LIMIT, 'initial');
|
|
361
|
+
}
|
|
362
|
+
}
|
|
357
363
|
if (page === undefined)
|
|
358
364
|
throw new Error('NATIVE_TRANSCRIPT_UNAVAILABLE');
|
|
359
365
|
if (lease.snapshotSent)
|
|
@@ -506,7 +512,7 @@ export class NativeSessionWatchService {
|
|
|
506
512
|
watch.mode !== 'CONVERGING' ||
|
|
507
513
|
watch.transitionGeneration !== generation)
|
|
508
514
|
return;
|
|
509
|
-
await this.refresh(session.id, INITIAL_TURN_LIMIT
|
|
515
|
+
await this.refresh(session.id, INITIAL_TURN_LIMIT);
|
|
510
516
|
}
|
|
511
517
|
emitLeaseEvent(lease, event) {
|
|
512
518
|
this.options.emitWatchEvent?.({
|
|
@@ -146,7 +146,12 @@ export class RunEventService {
|
|
|
146
146
|
}
|
|
147
147
|
if (!terminalExecution)
|
|
148
148
|
return;
|
|
149
|
-
|
|
149
|
+
// A completed reply has already been delivered through its own stream event. Only a
|
|
150
|
+
// continuing Run, an exceptional termination, or a pending item needs a final Turn update.
|
|
151
|
+
if (targetsCurrentTurn &&
|
|
152
|
+
createdTurn === undefined &&
|
|
153
|
+
(finalStatus !== 'SUCCEEDED' ||
|
|
154
|
+
targetTurn?.items.some((item) => item.status === 'PENDING' || item.status === 'IN_PROGRESS'))) {
|
|
150
155
|
const turn = this.options.runtime.conversationTurnForRun(runId);
|
|
151
156
|
if (turn !== undefined)
|
|
152
157
|
this.options.emitWorkbench('conversation', { turn });
|
|
@@ -39,15 +39,19 @@ export class SessionCommandService {
|
|
|
39
39
|
(await this.options.projection.resolveCurrent(input.sessionId));
|
|
40
40
|
if (resolved === undefined)
|
|
41
41
|
throw new Error('SESSION_NOT_FOUND');
|
|
42
|
-
if (resolved.nativeControl === 'EXTERNAL')
|
|
43
|
-
throw new Error('SESSION_ACTIVATION_REQUIRED');
|
|
44
|
-
const session = resolved;
|
|
45
42
|
const command = this.options
|
|
46
43
|
.profiles()
|
|
47
|
-
.find((profile) => profile.runner ===
|
|
44
|
+
.find((profile) => profile.runner === resolved.runner)
|
|
48
45
|
?.commands.find((candidate) => isPlainRecord(candidate) && candidate.id === input.commandId);
|
|
46
|
+
if (resolved.nativeControl === 'EXTERNAL' && command?.source !== 'MAR_RUNTIME')
|
|
47
|
+
throw new Error('SESSION_ACTIVATION_REQUIRED');
|
|
49
48
|
if (command?.available !== true)
|
|
50
49
|
throw new Error('RUNNER_COMMAND_UNAVAILABLE');
|
|
50
|
+
const session = resolved.nativeControl === 'EXTERNAL' && command.source === 'MAR_RUNTIME'
|
|
51
|
+
? await this.options.resolveForCommand(input.sessionId)
|
|
52
|
+
: resolved;
|
|
53
|
+
if (session.nativeControl === 'EXTERNAL')
|
|
54
|
+
throw new Error('SESSION_ACTIVATION_REQUIRED');
|
|
51
55
|
if ((command.interaction === 'IMMEDIATE_ACTION' ||
|
|
52
56
|
command.interaction === 'DESTRUCTIVE_ACTION') &&
|
|
53
57
|
this.options.runtime.hasActiveSessionLease(session.id))
|
|
@@ -159,7 +159,9 @@ export function nativeConversationPage(session, history, input, runtimeTurns = [
|
|
|
159
159
|
const runtimeErrors = (runtimeTurn?.items ?? [])
|
|
160
160
|
.filter((item) => item.kind === 'error')
|
|
161
161
|
.map((item) => ({ ...item, turnId, runId: runtimeTurn?.runId ?? null }));
|
|
162
|
-
|
|
162
|
+
// Native transcripts do not expose a terminal event for an individual response.
|
|
163
|
+
// Once linked to a managed Run, the Runtime owns that Turn until it leaves memory.
|
|
164
|
+
const items = runtimeTurn !== undefined
|
|
163
165
|
? runtimeTurn.items.map((item) => ({
|
|
164
166
|
...item,
|
|
165
167
|
turnId,
|
|
@@ -225,22 +227,6 @@ export function nativeConversationPage(session, history, input, runtimeTurns = [
|
|
|
225
227
|
: precedingWindow
|
|
226
228
|
};
|
|
227
229
|
}
|
|
228
|
-
export function runtimeAssistantProjectionMoreComplete(runtimeTurn, projectedItems) {
|
|
229
|
-
if (runtimeTurn === undefined)
|
|
230
|
-
return false;
|
|
231
|
-
const assistantStats = (items) => items.reduce((stats, item) => {
|
|
232
|
-
if (item.kind !== 'assistant_message' || !isPlainRecord(item.payload))
|
|
233
|
-
return stats;
|
|
234
|
-
return {
|
|
235
|
-
count: stats.count + 1,
|
|
236
|
-
characters: stats.characters +
|
|
237
|
-
(typeof item.payload.text === 'string' ? item.payload.text.length : 0)
|
|
238
|
-
};
|
|
239
|
-
}, { count: 0, characters: 0 });
|
|
240
|
-
const runtime = assistantStats(runtimeTurn.items);
|
|
241
|
-
const projected = assistantStats(projectedItems);
|
|
242
|
-
return runtime.count > projected.count || runtime.characters > projected.characters;
|
|
243
|
-
}
|
|
244
230
|
function runtimeTurnClientMessageId(turn) {
|
|
245
231
|
const user = turn.items.find((item) => item.kind === 'user_message');
|
|
246
232
|
return isPlainRecord(user?.payload) && typeof user.payload.clientMessageId === 'string'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.106",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"npm": "11.12.1",
|
|
31
31
|
"ws": "^8.21.3",
|
|
32
32
|
"zod": "4.4.3",
|
|
33
|
-
"@myagentroam/agent": "0.9.
|
|
34
|
-
"@myagentroam/code-navigation": "0.9.
|
|
35
|
-
"@myagentroam/protocol": "0.9.
|
|
33
|
+
"@myagentroam/agent": "0.9.106",
|
|
34
|
+
"@myagentroam/code-navigation": "0.9.106",
|
|
35
|
+
"@myagentroam/protocol": "0.9.106"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/ws": "^8.18.1"
|