@myagentroam/node 0.9.95 → 0.9.97
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/codex-app-server.js +4 -15
- package/dist/connector.js +0 -18
- package/dist/runner/claude/managed-run-controller.js +1 -1
- package/dist/runner/claude-code-runner.js +14 -48
- package/dist/runner/codex/managed-run-controller.js +1 -1
- package/dist/runner/codex-runner.js +6 -13
- package/dist/runner/mar-agent-runner.js +3 -10
- package/dist/runner/opencode-runner.js +7 -19
- package/dist/service/conversation-history-service.js +1 -1
- package/dist/service/external-session-resume-service.js +5 -22
- package/dist/service/node-control-message-service.js +1 -1
- package/dist/service/node-upgrade-coordinator.js +0 -1
- package/dist/service/session-catalog-service.js +2 -27
- package/dist/service/session-command-service.js +3 -3
- package/dist/service/session-message-service.js +3 -15
- package/dist/service/session-presentation-service.js +0 -1
- package/dist/service/workspace-code-navigation-service.js +70 -9
- package/dist/service/workspace-content-search-service.js +54 -24
- package/dist/service/workspace-file-service.js +1 -1
- package/dist/service/workspace-workbench-service.js +5 -0
- package/package.json +3 -3
package/dist/codex-app-server.js
CHANGED
|
@@ -9,22 +9,11 @@ import { createInterface } from 'node:readline';
|
|
|
9
9
|
import { minimalRunnerEnvironment, platformCliInvocation, runRunnerProbe } from './operational.js';
|
|
10
10
|
const REQUEST_TIMEOUT_MS = 15_000;
|
|
11
11
|
/**
|
|
12
|
-
* Maps the facts that the Node can
|
|
13
|
-
*
|
|
14
|
-
* is safe to write to its active turn.
|
|
12
|
+
* Maps the facts that the Node can establish to the unified session controls.
|
|
13
|
+
* Native-session activation is validated atomically with the first message.
|
|
15
14
|
*/
|
|
16
15
|
export function codexSessionControl(input) {
|
|
17
|
-
|
|
18
|
-
return { level: 'UNAVAILABLE', capabilities: [] };
|
|
19
|
-
if (input.sharedControl) {
|
|
20
|
-
return {
|
|
21
|
-
level: 'SHARED_CONTROL',
|
|
22
|
-
capabilities: ['history.read', 'events.live', 'turn.start', 'turn.steer', 'turn.interrupt']
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
if (input.nativeThreadId !== null && input.nativeControl === 'EXTERNAL') {
|
|
26
|
-
return { level: 'OBSERVE_ONLY', capabilities: ['history.read'] };
|
|
27
|
-
}
|
|
16
|
+
void [input.available, input.nativeThreadId, input.nativeControl];
|
|
28
17
|
if (input.activeTurn) {
|
|
29
18
|
return {
|
|
30
19
|
level: 'MANAGED',
|
|
@@ -32,7 +21,7 @@ export function codexSessionControl(input) {
|
|
|
32
21
|
};
|
|
33
22
|
}
|
|
34
23
|
return {
|
|
35
|
-
level: '
|
|
24
|
+
level: 'MANAGED',
|
|
36
25
|
capabilities: ['history.read', 'turn.start']
|
|
37
26
|
};
|
|
38
27
|
}
|
package/dist/connector.js
CHANGED
|
@@ -635,7 +635,6 @@ export class NodeConnector {
|
|
|
635
635
|
runners: this.runners,
|
|
636
636
|
capabilities: () => this.capabilities,
|
|
637
637
|
hasManagedActiveRun: (session) => hasManagedActiveRun(this.runtime, session),
|
|
638
|
-
externalActivity: (sessionId) => this.sessionState.externalActivity.get(sessionId),
|
|
639
638
|
projectionForNative: (session) => this.nativeSessionProjectionService.projectionForNative(session)
|
|
640
639
|
});
|
|
641
640
|
this.nativeSessionProjectionService = new NativeSessionProjectionService({
|
|
@@ -851,28 +850,12 @@ export class NodeConnector {
|
|
|
851
850
|
configuration: (session) => this.nativeSessionProjectionService.resumedConfiguration(session),
|
|
852
851
|
workspacePath: (workspaceId) => this.database?.getWorkspace(workspaceId)?.path,
|
|
853
852
|
rememberNative: (nativeSessionId, sessionId) => this.managedRunnerSessions.remember(nativeSessionId, sessionId),
|
|
854
|
-
stage: (session) => {
|
|
855
|
-
const configuration = this.nativeSessionProjectionService.resumedConfiguration(session);
|
|
856
|
-
return this.runtime.createAgentSession({
|
|
857
|
-
workspaceId: session.workspaceId,
|
|
858
|
-
runner: session.runner,
|
|
859
|
-
externalSessionId: session.externalSessionId ?? session.id,
|
|
860
|
-
nativeControl: 'EXTERNAL',
|
|
861
|
-
cwd: session.cwd,
|
|
862
|
-
model: configuration.model,
|
|
863
|
-
effort: configuration.effort,
|
|
864
|
-
access: configuration.access,
|
|
865
|
-
...(session.runnerTitle === null ? {} : { title: session.runnerTitle }),
|
|
866
|
-
...(session.customTitle === null ? {} : { customTitle: session.customTitle })
|
|
867
|
-
});
|
|
868
|
-
},
|
|
869
853
|
resolve: (sessionId) => this.nativeSessionProjectionService.resolve(sessionId)
|
|
870
854
|
});
|
|
871
855
|
this.sessionCommandService = new SessionCommandService({
|
|
872
856
|
runtime: this.runtime,
|
|
873
857
|
runners: this.runners,
|
|
874
858
|
projection: this.nativeSessionProjectionService,
|
|
875
|
-
resume: this.externalSessionResumeService,
|
|
876
859
|
profiles: () => this.runnerService.profiles(),
|
|
877
860
|
present: (session) => this.sessionPresentationService.present(session),
|
|
878
861
|
resumeQueueAfterCommandRun: (sessionId) => this.workspaceQueueWorkbenchService.resumeAfterCommandRun(sessionId),
|
|
@@ -1018,7 +1001,6 @@ export class NodeConnector {
|
|
|
1018
1001
|
runtime: this.runtime,
|
|
1019
1002
|
runners: this.runners,
|
|
1020
1003
|
projection: this.nativeSessionProjectionService,
|
|
1021
|
-
resume: this.externalSessionResumeService,
|
|
1022
1004
|
cachedNative: () => [...this.sessionState.directNative.values()],
|
|
1023
1005
|
cachedWorkspace: (workspaceId) => this.workspaceCoordinator.cachedSessions(workspaceId),
|
|
1024
1006
|
discoverWorkspace: (workspaceId) => this.nativeSessionProjectionService.discoverWorkspace(workspaceId),
|
|
@@ -115,7 +115,7 @@ export class ClaudeManagedRunController {
|
|
|
115
115
|
return;
|
|
116
116
|
if (typeof payload.externalSessionId === 'string' &&
|
|
117
117
|
!this.runner.execution.managedNativeIds.has(payload.externalSessionId)) {
|
|
118
|
-
this.host.emit(runId, 'run.rejected', { code: '
|
|
118
|
+
this.host.emit(runId, 'run.rejected', { code: 'SESSION_ACTIVATION_REQUIRED' }, 'FAILED');
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
121
|
this.host.active.add(runId);
|
|
@@ -147,62 +147,28 @@ export class ClaudeCodeRunner extends AbstractRunner {
|
|
|
147
147
|
: this.client.forkSession(session.externalSessionId, session.cwd, boundary, title);
|
|
148
148
|
}
|
|
149
149
|
presentSession(session, capabilities, context) {
|
|
150
|
-
|
|
151
|
-
const runtimeCapabilities = [
|
|
152
|
-
'history.read',
|
|
153
|
-
'events.live',
|
|
154
|
-
'turn.start',
|
|
155
|
-
'turn.queue',
|
|
156
|
-
'turn.interrupt',
|
|
157
|
-
'run.cancel',
|
|
158
|
-
'approval.respond',
|
|
159
|
-
'session.rename',
|
|
160
|
-
'session.remove',
|
|
161
|
-
'session.pin',
|
|
162
|
-
...(session.externalSessionId === null ? [] : ['session.rewind'])
|
|
163
|
-
];
|
|
164
|
-
return {
|
|
165
|
-
activity: context.managedActive ? 'MANAGED_ACTIVE' : 'IDLE',
|
|
166
|
-
control: {
|
|
167
|
-
level: 'MANAGED',
|
|
168
|
-
capabilities: ['history.read', 'events.live', 'turn.start', 'turn.interrupt']
|
|
169
|
-
},
|
|
170
|
-
commandStates: context.commandStates,
|
|
171
|
-
runtime: { level: 'MANAGED', capabilities: runtimeCapabilities, reasonCode: null }
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (capabilities.claudeCode.available && session.externalSessionId !== null) {
|
|
175
|
-
const runtimeCapabilities = [
|
|
176
|
-
'history.read',
|
|
177
|
-
'turn.start',
|
|
178
|
-
'session.rename',
|
|
179
|
-
'session.remove',
|
|
180
|
-
'session.pin',
|
|
181
|
-
'session.rewind'
|
|
182
|
-
];
|
|
183
|
-
return {
|
|
184
|
-
activity: 'IDLE',
|
|
185
|
-
control: { level: 'RESUMABLE', capabilities: ['history.read', 'turn.start'] },
|
|
186
|
-
commandStates: context.commandStates,
|
|
187
|
-
runtime: { level: 'RESUMABLE', capabilities: runtimeCapabilities, reasonCode: null }
|
|
188
|
-
};
|
|
189
|
-
}
|
|
150
|
+
void capabilities;
|
|
190
151
|
const runtimeCapabilities = [
|
|
191
152
|
'history.read',
|
|
192
153
|
'events.live',
|
|
154
|
+
'turn.start',
|
|
155
|
+
'turn.queue',
|
|
156
|
+
'turn.interrupt',
|
|
157
|
+
'run.cancel',
|
|
158
|
+
'approval.respond',
|
|
193
159
|
'session.rename',
|
|
194
160
|
'session.remove',
|
|
195
|
-
'session.pin'
|
|
161
|
+
'session.pin',
|
|
162
|
+
...(session.externalSessionId === null ? [] : ['session.rewind'])
|
|
196
163
|
];
|
|
197
164
|
return {
|
|
198
|
-
activity: '
|
|
199
|
-
control: {
|
|
165
|
+
activity: context.managedActive ? 'MANAGED_ACTIVE' : 'IDLE',
|
|
166
|
+
control: {
|
|
167
|
+
level: 'MANAGED',
|
|
168
|
+
capabilities: ['history.read', 'events.live', 'turn.start', 'turn.interrupt']
|
|
169
|
+
},
|
|
200
170
|
commandStates: context.commandStates,
|
|
201
|
-
runtime: {
|
|
202
|
-
level: 'OBSERVE_ONLY',
|
|
203
|
-
capabilities: runtimeCapabilities,
|
|
204
|
-
reasonCode: 'EXTERNAL_SESSION_OBSERVE_ONLY'
|
|
205
|
-
}
|
|
171
|
+
runtime: { level: 'MANAGED', capabilities: runtimeCapabilities, reasonCode: null }
|
|
206
172
|
};
|
|
207
173
|
}
|
|
208
174
|
prepareMessageInput(input, collaborationMode) {
|
|
@@ -357,7 +357,7 @@ export class CodexManagedRunController {
|
|
|
357
357
|
}
|
|
358
358
|
if (typeof payload.externalSessionId === 'string' &&
|
|
359
359
|
(session === undefined || session.nativeControl !== 'MAR_MANAGED')) {
|
|
360
|
-
this.host.emit(runId, 'run.rejected', { code: '
|
|
360
|
+
this.host.emit(runId, 'run.rejected', { code: 'SESSION_ACTIVATION_REQUIRED' }, 'FAILED');
|
|
361
361
|
return;
|
|
362
362
|
}
|
|
363
363
|
try {
|
|
@@ -309,10 +309,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
309
309
|
return { activity: 'IDLE', session: context.create(external) };
|
|
310
310
|
}
|
|
311
311
|
catch {
|
|
312
|
-
return {
|
|
313
|
-
activity: 'UNAVAILABLE',
|
|
314
|
-
failureCode: 'RUNNER_RESUME_FAILED'
|
|
315
|
-
};
|
|
312
|
+
return { failureCode: 'RUNNER_RESUME_FAILED' };
|
|
316
313
|
}
|
|
317
314
|
}
|
|
318
315
|
async discoverSessions(context, workspace) {
|
|
@@ -391,13 +388,7 @@ export class CodexRunner extends AbstractRunner {
|
|
|
391
388
|
nativeControl: session.nativeControl,
|
|
392
389
|
activeTurn
|
|
393
390
|
});
|
|
394
|
-
const activity =
|
|
395
|
-
? 'UNAVAILABLE'
|
|
396
|
-
: context.managedActive
|
|
397
|
-
? 'MANAGED_ACTIVE'
|
|
398
|
-
: session.nativeControl === 'EXTERNAL'
|
|
399
|
-
? (context.externalActivity ?? 'IDLE')
|
|
400
|
-
: 'IDLE';
|
|
391
|
+
const activity = context.managedActive ? 'MANAGED_ACTIVE' : 'IDLE';
|
|
401
392
|
return {
|
|
402
393
|
control,
|
|
403
394
|
activity,
|
|
@@ -406,13 +397,15 @@ export class CodexRunner extends AbstractRunner {
|
|
|
406
397
|
level: control.level,
|
|
407
398
|
capabilities: [
|
|
408
399
|
...control.capabilities,
|
|
409
|
-
|
|
400
|
+
'turn.queue',
|
|
401
|
+
'run.cancel',
|
|
402
|
+
'approval.respond',
|
|
410
403
|
'session.rename',
|
|
411
404
|
'session.remove',
|
|
412
405
|
'session.pin',
|
|
413
406
|
...(session.externalSessionId === null ? [] : ['session.rewind'])
|
|
414
407
|
],
|
|
415
|
-
reasonCode:
|
|
408
|
+
reasonCode: null
|
|
416
409
|
}
|
|
417
410
|
};
|
|
418
411
|
}
|
|
@@ -170,14 +170,7 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
presentSession(session, capabilities, context) {
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
activity: 'UNAVAILABLE',
|
|
176
|
-
control: { level: 'UNAVAILABLE', capabilities: [] },
|
|
177
|
-
runtime: { level: 'UNAVAILABLE', capabilities: [], reasonCode: 'RUNNER_UNAVAILABLE' },
|
|
178
|
-
commandStates: context.commandStates
|
|
179
|
-
};
|
|
180
|
-
const managed = session.nativeControl === 'MAR_MANAGED';
|
|
173
|
+
void capabilities;
|
|
181
174
|
const activity = context.managedActive ? 'MANAGED_ACTIVE' : 'IDLE';
|
|
182
175
|
const runtimeCapabilities = [
|
|
183
176
|
'history.read',
|
|
@@ -195,11 +188,11 @@ export class MarAgentRunner extends AbstractRunner {
|
|
|
195
188
|
return {
|
|
196
189
|
activity,
|
|
197
190
|
control: {
|
|
198
|
-
level:
|
|
191
|
+
level: 'MANAGED',
|
|
199
192
|
capabilities: ['history.read', 'events.live', 'turn.start', 'turn.interrupt']
|
|
200
193
|
},
|
|
201
194
|
runtime: {
|
|
202
|
-
level:
|
|
195
|
+
level: 'MANAGED',
|
|
203
196
|
capabilities: runtimeCapabilities,
|
|
204
197
|
reasonCode: null
|
|
205
198
|
},
|
|
@@ -206,7 +206,7 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
206
206
|
const projection = context.projectionFor(external);
|
|
207
207
|
const session = projection === undefined ? context.create(external) : context.promote(projection.id);
|
|
208
208
|
return session === undefined
|
|
209
|
-
? {
|
|
209
|
+
? { failureCode: 'RUNNER_RESUME_FAILED' }
|
|
210
210
|
: { activity: 'IDLE', session: context.ensureConfiguration(session) };
|
|
211
211
|
}
|
|
212
212
|
async readOfficialConversation(session, input, managedTurn) {
|
|
@@ -227,18 +227,8 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
227
227
|
return this.managedTurns.get(nativeTurnId);
|
|
228
228
|
}
|
|
229
229
|
presentSession(session, capabilities, context) {
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
const activity = context.managedActive
|
|
233
|
-
? 'MANAGED_ACTIVE'
|
|
234
|
-
: (context.externalActivity ?? (available ? 'IDLE' : 'UNAVAILABLE'));
|
|
235
|
-
if (!available)
|
|
236
|
-
return {
|
|
237
|
-
activity: 'UNAVAILABLE',
|
|
238
|
-
control: { level: 'UNAVAILABLE', capabilities: [] },
|
|
239
|
-
runtime: { level: 'UNAVAILABLE', capabilities: [], reasonCode: 'RUNNER_UNAVAILABLE' },
|
|
240
|
-
commandStates: context.commandStates
|
|
241
|
-
};
|
|
230
|
+
void capabilities;
|
|
231
|
+
const activity = context.managedActive ? 'MANAGED_ACTIVE' : 'IDLE';
|
|
242
232
|
const runtimeCapabilities = [
|
|
243
233
|
'history.read',
|
|
244
234
|
'events.live',
|
|
@@ -256,15 +246,13 @@ export class OpenCodeRunner extends AbstractRunner {
|
|
|
256
246
|
return {
|
|
257
247
|
activity,
|
|
258
248
|
control: {
|
|
259
|
-
level:
|
|
260
|
-
capabilities:
|
|
261
|
-
? ['history.read', 'events.live', 'turn.start', 'turn.interrupt']
|
|
262
|
-
: ['history.read', 'events.live']
|
|
249
|
+
level: 'MANAGED',
|
|
250
|
+
capabilities: ['history.read', 'events.live', 'turn.start', 'turn.interrupt']
|
|
263
251
|
},
|
|
264
252
|
runtime: {
|
|
265
|
-
level:
|
|
253
|
+
level: 'MANAGED',
|
|
266
254
|
capabilities: runtimeCapabilities,
|
|
267
|
-
reasonCode:
|
|
255
|
+
reasonCode: null
|
|
268
256
|
},
|
|
269
257
|
commandStates: context.commandStates
|
|
270
258
|
};
|
|
@@ -317,7 +317,7 @@ export class ConversationHistoryService {
|
|
|
317
317
|
async refreshExternalActivity(session) {
|
|
318
318
|
const runner = this.options.runners.require(session.runner);
|
|
319
319
|
if (!runner.available(this.options.capabilities())) {
|
|
320
|
-
this.options.setExternalActivity(session.id, '
|
|
320
|
+
this.options.setExternalActivity(session.id, 'IDLE');
|
|
321
321
|
return;
|
|
322
322
|
}
|
|
323
323
|
const activity = await runner.readExternalActivity(session);
|
|
@@ -31,6 +31,7 @@ export class ExternalSessionResumeService {
|
|
|
31
31
|
return this.options.ensureConfiguration(prior);
|
|
32
32
|
const runner = this.options.runners.product(external.runner);
|
|
33
33
|
if (runner === undefined) {
|
|
34
|
+
this.options.state.externalActivity.set(external.id, 'IDLE');
|
|
34
35
|
this.setFailure(external.id, 'RUNNER_UNAVAILABLE');
|
|
35
36
|
return undefined;
|
|
36
37
|
}
|
|
@@ -78,6 +79,8 @@ export class ExternalSessionResumeService {
|
|
|
78
79
|
}
|
|
79
80
|
if (result.activity !== undefined)
|
|
80
81
|
this.options.state.externalActivity.set(external.id, result.activity);
|
|
82
|
+
else if (result.failureCode === 'RUNNER_UNAVAILABLE')
|
|
83
|
+
this.options.state.externalActivity.set(external.id, 'IDLE');
|
|
81
84
|
if (result.session !== undefined) {
|
|
82
85
|
this.options.state.resumedByDirectId.set(external.id, result.session.id);
|
|
83
86
|
return result.session;
|
|
@@ -102,32 +105,12 @@ export class ExternalSessionResumeService {
|
|
|
102
105
|
const resumed = await this.resume(current);
|
|
103
106
|
if (resumed !== undefined)
|
|
104
107
|
return resumed;
|
|
105
|
-
if (this.options.state.externalActivity.get(current.id) === '
|
|
108
|
+
if (this.options.state.externalActivity.get(current.id) === 'BUSY')
|
|
106
109
|
throw new Error('EXTERNAL_SESSION_ACTIVE');
|
|
107
110
|
throw new Error(this.options.state.messageResumeFailures.get(current.id) ?? this.failure(current.id).code);
|
|
108
111
|
}
|
|
109
112
|
async acceptForMessage(sessionId) {
|
|
110
|
-
|
|
111
|
-
const current = this.options.runtime.getAgentSession(resumedId ?? sessionId) ??
|
|
112
|
-
(await this.options.resolve(sessionId));
|
|
113
|
-
if (current === undefined)
|
|
114
|
-
throw new Error('SESSION_NOT_FOUND');
|
|
115
|
-
if (current.nativeControl !== 'EXTERNAL')
|
|
116
|
-
return { session: current, ready: Promise.resolve(current), deferred: false };
|
|
117
|
-
const staged = this.options.projectionFor(current) ?? this.options.stage(current);
|
|
118
|
-
const ready = this.resume(current).then((session) => {
|
|
119
|
-
if (session !== undefined)
|
|
120
|
-
return session;
|
|
121
|
-
if (this.options.state.externalActivity.get(current.id) === 'EXTERNAL_ACTIVE')
|
|
122
|
-
throw new Error('EXTERNAL_SESSION_ACTIVE');
|
|
123
|
-
throw new Error(this.options.state.messageResumeFailures.get(current.id) ?? this.failure(current.id).code);
|
|
124
|
-
});
|
|
125
|
-
return { session: staged, ready, deferred: true };
|
|
126
|
-
}
|
|
127
|
-
delivery(session) {
|
|
128
|
-
return session.nativeControl === 'EXTERNAL' && session.externalSessionId !== null
|
|
129
|
-
? 'RESUMABLE'
|
|
130
|
-
: 'REJECTED';
|
|
113
|
+
return { session: await this.resolveForMessage(sessionId) };
|
|
131
114
|
}
|
|
132
115
|
clearFailure(sessionId) {
|
|
133
116
|
const timer = this.options.state.failureTimers.get(sessionId);
|
|
@@ -68,7 +68,7 @@ export class NodeControlMessageService {
|
|
|
68
68
|
this.cancelRun(envelope);
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
|
-
if (envelope.type === '
|
|
71
|
+
if (envelope.type === 'lease.reconcile') {
|
|
72
72
|
this.options.send('ack', { type: envelope.type }, envelope.id);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
@@ -62,7 +62,6 @@ export class NodeUpgradeCoordinator {
|
|
|
62
62
|
blocks(operation) {
|
|
63
63
|
return (this.pending() &&
|
|
64
64
|
(operation === 'session.start' ||
|
|
65
|
-
operation === 'session.resume' ||
|
|
66
65
|
operation === 'session.message' ||
|
|
67
66
|
operation === 'session.command.execute' ||
|
|
68
67
|
operation === 'runner.command.execute' ||
|
|
@@ -11,7 +11,6 @@ export class SessionCatalogService {
|
|
|
11
11
|
operations() {
|
|
12
12
|
return {
|
|
13
13
|
'session.list': (data) => this.list(record(data)),
|
|
14
|
-
'session.resume': (data) => this.resume(record(data)),
|
|
15
14
|
'session.remove': (data) => this.enqueueMutation(() => this.remove(record(data))),
|
|
16
15
|
'workspace.sessions.cleanup': (data) => this.enqueueMutation(() => this.cleanupWorkspaceSessions(record(data))),
|
|
17
16
|
'session.rewind': (data) => this.enqueueMutation(() => this.rewind(record(data))),
|
|
@@ -100,30 +99,6 @@ export class SessionCatalogService {
|
|
|
100
99
|
}));
|
|
101
100
|
}
|
|
102
101
|
}
|
|
103
|
-
async resume(input) {
|
|
104
|
-
if (typeof input.sessionId !== 'string')
|
|
105
|
-
throw new Error('SESSION_INVALID');
|
|
106
|
-
const value = this.options.runtime.getAgentSession(input.sessionId) ??
|
|
107
|
-
(await this.options.projection.resolve(input.sessionId));
|
|
108
|
-
if (value === undefined)
|
|
109
|
-
throw new Error('SESSION_NOT_FOUND');
|
|
110
|
-
if (value.nativeControl !== 'EXTERNAL')
|
|
111
|
-
return {
|
|
112
|
-
session: this.options.present(value),
|
|
113
|
-
delivery: this.options.resume.delivery(value)
|
|
114
|
-
};
|
|
115
|
-
const resumed = await this.options.resume.resume(value);
|
|
116
|
-
const failure = resumed === undefined ? this.options.resume.failure(value.id) : undefined;
|
|
117
|
-
return {
|
|
118
|
-
session: this.options.present(resumed ?? value),
|
|
119
|
-
delivery: resumed === undefined ? 'REJECTED' : 'RESUMABLE',
|
|
120
|
-
...(resumed === undefined
|
|
121
|
-
? {
|
|
122
|
-
reasonCode: failure?.code ?? 'SESSION_RESUME_REJECTED'
|
|
123
|
-
}
|
|
124
|
-
: {})
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
102
|
enqueueMutation(operation) {
|
|
128
103
|
const result = this.rewindQueue.then(operation);
|
|
129
104
|
this.rewindQueue = result.then(() => undefined, () => undefined);
|
|
@@ -263,7 +238,7 @@ export class SessionCatalogService {
|
|
|
263
238
|
this.options.runtime
|
|
264
239
|
.listRunsForSession(session.id)
|
|
265
240
|
.some((run) => !isTerminalRunStatus(run.status)) ||
|
|
266
|
-
this.options.externalActivity(session.id) === '
|
|
241
|
+
this.options.externalActivity(session.id) === 'BUSY') {
|
|
267
242
|
skipped += 1;
|
|
268
243
|
continue;
|
|
269
244
|
}
|
|
@@ -317,7 +292,7 @@ export class SessionCatalogService {
|
|
|
317
292
|
throw new Error('SESSION_ACTIVE');
|
|
318
293
|
if (session.nativeControl === 'EXTERNAL') {
|
|
319
294
|
await this.options.refreshActivity(session);
|
|
320
|
-
if (this.options.externalActivity(session.id) === '
|
|
295
|
+
if (this.options.externalActivity(session.id) === 'BUSY')
|
|
321
296
|
throw new Error('SESSION_ACTIVE');
|
|
322
297
|
}
|
|
323
298
|
if (requireCleanupSafety) {
|
|
@@ -39,9 +39,9 @@ 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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (resolved.nativeControl === 'EXTERNAL')
|
|
43
|
+
throw new Error('SESSION_ACTIVATION_REQUIRED');
|
|
44
|
+
const session = resolved;
|
|
45
45
|
const command = this.options
|
|
46
46
|
.profiles()
|
|
47
47
|
.find((profile) => profile.runner === session.runner)
|
|
@@ -88,7 +88,7 @@ export class SessionMessageService {
|
|
|
88
88
|
const publicContent = missionMessage?.activated === true ? 'Mission Start' : content;
|
|
89
89
|
const intent = missionMessage === undefined ? requestedIntent : 'REPLACE_CURRENT';
|
|
90
90
|
const operation = (async () => {
|
|
91
|
-
const acceptedSession = accepted.
|
|
91
|
+
const acceptedSession = accepted.session;
|
|
92
92
|
const session = this.options.runtime.getAgentSession(acceptedSession.id) ?? acceptedSession;
|
|
93
93
|
const runId = randomUUID();
|
|
94
94
|
const queueBeforeCreate = this.options.runtime.sessionQueue(session.id);
|
|
@@ -108,16 +108,12 @@ export class SessionMessageService {
|
|
|
108
108
|
const sessionForPresentation = input.__mission === true
|
|
109
109
|
? session
|
|
110
110
|
: this.options.runtime.setTemporaryTitleIfMissing(session.id, sessionTitleFromFirstMessage(content));
|
|
111
|
-
const deferPreparation = accepted.deferred && attachmentIds.length === 0 && inlineAttachments.length === 0;
|
|
112
111
|
this.options.emitSession(sessionForPresentation);
|
|
113
112
|
if (created.queueItem === undefined)
|
|
114
113
|
this.options.coordinator.publishCurrentTurn(created.run.id);
|
|
115
114
|
const prepare = async () => {
|
|
116
115
|
try {
|
|
117
|
-
const
|
|
118
|
-
const currentSession = this.options.runtime.getAgentSession(readySession.id) ?? readySession;
|
|
119
|
-
if (deferPreparation)
|
|
120
|
-
this.options.emitSession(currentSession);
|
|
116
|
+
const currentSession = this.options.runtime.getAgentSession(session.id) ?? session;
|
|
121
117
|
const uploaded = await this.options.uploads.resolve(session.workspaceId, attachmentIds);
|
|
122
118
|
const attachments = attachmentIds.length === 0
|
|
123
119
|
? inlineAttachments
|
|
@@ -190,15 +186,7 @@ export class SessionMessageService {
|
|
|
190
186
|
throw error;
|
|
191
187
|
}
|
|
192
188
|
};
|
|
193
|
-
|
|
194
|
-
void prepare().catch(() => undefined);
|
|
195
|
-
const delivery = deferPreparation
|
|
196
|
-
? created.queueItem === undefined
|
|
197
|
-
? 'STARTED'
|
|
198
|
-
: intent === 'REPLACE_CURRENT'
|
|
199
|
-
? 'RESTARTING'
|
|
200
|
-
: 'QUEUED'
|
|
201
|
-
: await prepare();
|
|
189
|
+
const delivery = await prepare();
|
|
202
190
|
return {
|
|
203
191
|
session: this.options.present(sessionForPresentation),
|
|
204
192
|
run: created.run,
|
|
@@ -14,7 +14,6 @@ export class SessionPresentationService {
|
|
|
14
14
|
.require(session.runner)
|
|
15
15
|
.presentSession(session, this.options.capabilities(), {
|
|
16
16
|
managedActive: this.options.hasManagedActiveRun(session),
|
|
17
|
-
externalActivity: this.options.externalActivity(session.id),
|
|
18
17
|
commandStates: this.options.commands.list(session.id)
|
|
19
18
|
});
|
|
20
19
|
return {
|
|
@@ -88,6 +88,7 @@ const buildWaiters = [];
|
|
|
88
88
|
export class WorkspaceCodeNavigationService {
|
|
89
89
|
options;
|
|
90
90
|
builds = new Map();
|
|
91
|
+
preparations = new Map();
|
|
91
92
|
candidateRefreshes = new Map();
|
|
92
93
|
locations = new Map();
|
|
93
94
|
failures = new Map();
|
|
@@ -96,6 +97,49 @@ export class WorkspaceCodeNavigationService {
|
|
|
96
97
|
constructor(options) {
|
|
97
98
|
this.options = options;
|
|
98
99
|
}
|
|
100
|
+
async prepare(workspace, input) {
|
|
101
|
+
if (typeof input.path !== 'string' || input.path.length === 0 || input.path.length > 4096)
|
|
102
|
+
throw new Error('SYMBOL_DEFINITION_INVALID');
|
|
103
|
+
if (!isNavigablePath(input.path))
|
|
104
|
+
return { state: 'READY' };
|
|
105
|
+
if (!(await this.available()))
|
|
106
|
+
return { state: 'UNAVAILABLE' };
|
|
107
|
+
const location = await this.indexLocation(workspace);
|
|
108
|
+
this.locations.set(workspace.id, location);
|
|
109
|
+
this.forgotten.delete(location.directory);
|
|
110
|
+
const manifest = await readManifest(location.manifest);
|
|
111
|
+
const tags = manifest === undefined
|
|
112
|
+
? undefined
|
|
113
|
+
: await tagsPath(location, manifest).catch(() => undefined);
|
|
114
|
+
const reusable = manifest !== undefined &&
|
|
115
|
+
tags !== undefined &&
|
|
116
|
+
(await tagsAvailable(tags)) &&
|
|
117
|
+
manifest.sourceFiles !== undefined;
|
|
118
|
+
const current = reusable &&
|
|
119
|
+
manifest.schemaVersion === INDEX_SCHEMA_VERSION &&
|
|
120
|
+
manifest.optionsVersion === INDEX_OPTIONS_VERSION &&
|
|
121
|
+
manifest.ctagsVersion === BUNDLED_CTAGS_VERSION;
|
|
122
|
+
const cachedCandidates = this.options.files.index(workspace).peekContentCandidates();
|
|
123
|
+
const forceRefreshGeneration = this.forceFreshCandidates.get(workspace.id);
|
|
124
|
+
if (reusable) {
|
|
125
|
+
if (!current || cachedCandidates === undefined || forceRefreshGeneration !== undefined)
|
|
126
|
+
this.refreshCandidatesInBackground(workspace, location, manifest, {
|
|
127
|
+
requireFresh: forceRefreshGeneration !== undefined,
|
|
128
|
+
mustBuild: !current || forceRefreshGeneration !== undefined,
|
|
129
|
+
forceRefreshGeneration
|
|
130
|
+
});
|
|
131
|
+
return { state: current ? 'READY' : 'STALE' };
|
|
132
|
+
}
|
|
133
|
+
const failure = this.failures.get(location.directory);
|
|
134
|
+
if (failure !== undefined &&
|
|
135
|
+
!this.builds.has(location.directory) &&
|
|
136
|
+
!this.preparations.has(location.directory)) {
|
|
137
|
+
this.failures.delete(location.directory);
|
|
138
|
+
return { state: 'FAILED', failureCode: failure };
|
|
139
|
+
}
|
|
140
|
+
this.startInitialBuild(workspace, location);
|
|
141
|
+
return { state: 'BUILDING' };
|
|
142
|
+
}
|
|
99
143
|
async definition(workspace, input) {
|
|
100
144
|
const path = input.path;
|
|
101
145
|
const symbol = input.symbol;
|
|
@@ -276,15 +320,32 @@ export class WorkspaceCodeNavigationService {
|
|
|
276
320
|
};
|
|
277
321
|
}
|
|
278
322
|
startBuild(input, location, previous) {
|
|
279
|
-
void this.ensureBuild(input, location, previous).catch((error) =>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
323
|
+
void this.ensureBuild(input, location, previous).catch((error) => this.recordBuildFailure(location, error, input));
|
|
324
|
+
}
|
|
325
|
+
startInitialBuild(workspace, location) {
|
|
326
|
+
if (this.builds.has(location.directory) || this.preparations.has(location.directory))
|
|
327
|
+
return;
|
|
328
|
+
const preparation = this.indexInput(workspace, true)
|
|
329
|
+
.then((input) => {
|
|
330
|
+
if (this.forgotten.has(location.directory))
|
|
331
|
+
return;
|
|
332
|
+
return this.ensureBuild(input, location);
|
|
333
|
+
})
|
|
334
|
+
.catch((error) => this.recordBuildFailure(location, error))
|
|
335
|
+
.finally(() => {
|
|
336
|
+
if (this.preparations.get(location.directory) === preparation)
|
|
337
|
+
this.preparations.delete(location.directory);
|
|
338
|
+
});
|
|
339
|
+
this.preparations.set(location.directory, preparation);
|
|
340
|
+
}
|
|
341
|
+
recordBuildFailure(location, error, input) {
|
|
342
|
+
const failure = buildFailure(error);
|
|
343
|
+
this.failures.set(location.directory, failure);
|
|
344
|
+
nodeLog('workspace.code_navigation.index.failed', {
|
|
345
|
+
nodeId: this.options.nodeId(),
|
|
346
|
+
failureCode: failure,
|
|
347
|
+
sourceFiles: input?.entries.length ?? 0,
|
|
348
|
+
candidateScanMs: input?.candidateScanMs ?? 0
|
|
288
349
|
});
|
|
289
350
|
}
|
|
290
351
|
refreshCandidatesInBackground(workspace, location, manifest, options = {
|
|
@@ -53,29 +53,38 @@ export class WorkspaceContentSearchService {
|
|
|
53
53
|
search(workspace, input) {
|
|
54
54
|
if (typeof input.query !== 'string' || input.query.length < 1 || input.query.length > 256)
|
|
55
55
|
throw new Error('FILE_CONTENT_SEARCH_INVALID');
|
|
56
|
-
|
|
56
|
+
if (input.regex !== undefined && typeof input.regex !== 'boolean')
|
|
57
|
+
throw new Error('FILE_CONTENT_SEARCH_INVALID');
|
|
58
|
+
const search = { query: input.query, regex: input.regex === true };
|
|
59
|
+
const queryKey = search.regex ? search.query : search.query.toLocaleLowerCase();
|
|
60
|
+
const key = `${workspace.id}\0${search.regex}\0${queryKey}`;
|
|
57
61
|
const existing = this.pending.get(key);
|
|
58
62
|
if (existing !== undefined)
|
|
59
63
|
return existing;
|
|
60
|
-
const task = this.perform(workspace,
|
|
64
|
+
const task = this.perform(workspace, search).finally(() => this.pending.delete(key));
|
|
61
65
|
this.pending.set(key, task);
|
|
62
66
|
return task;
|
|
63
67
|
}
|
|
64
|
-
async perform(workspace,
|
|
68
|
+
async perform(workspace, search) {
|
|
65
69
|
const deadline = Date.now() + TIMEOUT_MS;
|
|
66
70
|
const release = await acquireSearch(deadline);
|
|
67
71
|
try {
|
|
72
|
+
if (search.regex && this.ripgrep === null)
|
|
73
|
+
throw new Error('RIPGREP_RESOURCE_UNAVAILABLE');
|
|
68
74
|
const prepared = await this.candidates(workspace, deadline);
|
|
69
|
-
if (prepared.files.length === 0)
|
|
75
|
+
if (prepared.files.length === 0) {
|
|
76
|
+
if (search.regex)
|
|
77
|
+
await this.validateNativeRegex(workspace.path, search, deadline);
|
|
70
78
|
return {
|
|
71
79
|
matches: [],
|
|
72
80
|
engine: 'NODE',
|
|
73
81
|
truncated: prepared.truncated || Date.now() >= deadline,
|
|
74
82
|
skippedFiles: prepared.skippedFiles
|
|
75
83
|
};
|
|
84
|
+
}
|
|
76
85
|
if (this.ripgrep !== null) {
|
|
77
86
|
try {
|
|
78
|
-
const matches = await this.nativeSearch('RIPGREP', workspace.path, prepared.files,
|
|
87
|
+
const matches = await this.nativeSearch('RIPGREP', workspace.path, prepared.files, search, deadline);
|
|
79
88
|
return {
|
|
80
89
|
matches: matches.slice(0, RESULT_LIMIT),
|
|
81
90
|
engine: 'RIPGREP',
|
|
@@ -83,11 +92,13 @@ export class WorkspaceContentSearchService {
|
|
|
83
92
|
skippedFiles: prepared.skippedFiles
|
|
84
93
|
};
|
|
85
94
|
}
|
|
86
|
-
catch {
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (search.regex)
|
|
97
|
+
throw error;
|
|
87
98
|
// Missing, incompatible or failed bundled resources safely fall through.
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
|
-
const fallback = await nodeSearch(prepared.files, query, deadline);
|
|
101
|
+
const fallback = await nodeSearch(prepared.files, search.query, deadline);
|
|
91
102
|
return {
|
|
92
103
|
matches: fallback.matches.slice(0, RESULT_LIMIT),
|
|
93
104
|
engine: 'NODE',
|
|
@@ -99,6 +110,14 @@ export class WorkspaceContentSearchService {
|
|
|
99
110
|
release();
|
|
100
111
|
}
|
|
101
112
|
}
|
|
113
|
+
async validateNativeRegex(root, search, deadline) {
|
|
114
|
+
const command = commandFor('RIPGREP', [], search, this.ripgrep ?? undefined);
|
|
115
|
+
const result = await runCommand(command.file, [...command.args, '-'], root, deadline);
|
|
116
|
+
if (result.code === 2)
|
|
117
|
+
throw new Error('FILE_CONTENT_SEARCH_INVALID');
|
|
118
|
+
if (!command.validCodes.includes(result.code))
|
|
119
|
+
throw new Error('CONTENT_SEARCH_COMMAND_FAILED');
|
|
120
|
+
}
|
|
102
121
|
async candidates(workspace, deadline) {
|
|
103
122
|
const root = await realpath(workspace.path);
|
|
104
123
|
let candidates;
|
|
@@ -139,21 +158,24 @@ export class WorkspaceContentSearchService {
|
|
|
139
158
|
}
|
|
140
159
|
return { files, skippedFiles, truncated };
|
|
141
160
|
}
|
|
142
|
-
async nativeSearch(engine, root, files,
|
|
161
|
+
async nativeSearch(engine, root, files, search, deadline) {
|
|
143
162
|
const matches = [];
|
|
144
163
|
const argumentBatchBytes = this.platform === 'win32' ? WINDOWS_ARGUMENT_BATCH_BYTES : POSIX_ARGUMENT_BATCH_BYTES;
|
|
145
164
|
for (const batch of batches(files, argumentBatchBytes)) {
|
|
146
165
|
if (Date.now() >= deadline || matches.length > RESULT_LIMIT)
|
|
147
166
|
break;
|
|
148
|
-
const command = commandFor(engine, batch,
|
|
167
|
+
const command = commandFor(engine, batch, search, this.ripgrep ?? undefined);
|
|
149
168
|
const result = await runCommand(command.file, command.args, root, deadline, engine === 'RIPGREP');
|
|
150
|
-
if (!command.validCodes.includes(result.code))
|
|
169
|
+
if (!command.validCodes.includes(result.code)) {
|
|
170
|
+
if (search.regex && result.code === 2)
|
|
171
|
+
throw new Error('FILE_CONTENT_SEARCH_INVALID');
|
|
151
172
|
throw new Error('CONTENT_SEARCH_COMMAND_FAILED');
|
|
173
|
+
}
|
|
152
174
|
const paths = new Map(batch.flatMap((file) => [
|
|
153
175
|
[normalizeResultPath(file.path, this.platform), file.path],
|
|
154
176
|
[normalizeResultPath(file.absolutePath, this.platform), file.path]
|
|
155
177
|
]));
|
|
156
|
-
matches.push(...parseOutput(engine, result.stdout,
|
|
178
|
+
matches.push(...parseOutput(engine, result.stdout, search).map((match) => ({
|
|
157
179
|
...match,
|
|
158
180
|
path: paths.get(normalizeResultPath(match.path, this.platform)) ??
|
|
159
181
|
(() => {
|
|
@@ -213,7 +235,7 @@ function batches(files, maximumBytes) {
|
|
|
213
235
|
result.push(current);
|
|
214
236
|
return result;
|
|
215
237
|
}
|
|
216
|
-
function commandFor(engine, files,
|
|
238
|
+
function commandFor(engine, files, search, ripgrep) {
|
|
217
239
|
const paths = files.map((file) => file.path);
|
|
218
240
|
if (engine === 'RIPGREP')
|
|
219
241
|
return {
|
|
@@ -224,12 +246,12 @@ function commandFor(engine, files, query, ripgrep) {
|
|
|
224
246
|
args: [
|
|
225
247
|
'--json',
|
|
226
248
|
'--no-config',
|
|
227
|
-
'--fixed-strings',
|
|
228
249
|
'--ignore-case',
|
|
229
250
|
'--max-count',
|
|
230
251
|
String(PER_FILE_LIMIT),
|
|
252
|
+
...(search.regex ? [] : ['--fixed-strings']),
|
|
231
253
|
'--',
|
|
232
|
-
query,
|
|
254
|
+
search.query,
|
|
233
255
|
...paths
|
|
234
256
|
],
|
|
235
257
|
validCodes: [0, 1]
|
|
@@ -301,14 +323,14 @@ async function runCommand(file, args, cwd, deadline, stopAfterResultLimit = fals
|
|
|
301
323
|
});
|
|
302
324
|
});
|
|
303
325
|
}
|
|
304
|
-
function parseOutput(engine, output,
|
|
326
|
+
function parseOutput(engine, output, search) {
|
|
305
327
|
if (engine === 'RIPGREP')
|
|
306
|
-
return parseRipgrep(output,
|
|
328
|
+
return parseRipgrep(output, search);
|
|
307
329
|
if (engine === 'GREP')
|
|
308
|
-
return parseGrep(output,
|
|
309
|
-
return parsePowerShell(output,
|
|
330
|
+
return parseGrep(output, search);
|
|
331
|
+
return parsePowerShell(output, search);
|
|
310
332
|
}
|
|
311
|
-
function parseRipgrep(output,
|
|
333
|
+
function parseRipgrep(output, search) {
|
|
312
334
|
return output
|
|
313
335
|
.toString('utf8')
|
|
314
336
|
.split('\n')
|
|
@@ -320,7 +342,9 @@ function parseRipgrep(output, query) {
|
|
|
320
342
|
return [];
|
|
321
343
|
const data = value.data;
|
|
322
344
|
const preview = String(data?.lines?.text).replace(/[\r\n]+$/u, '');
|
|
323
|
-
const column =
|
|
345
|
+
const column = search.regex
|
|
346
|
+
? regexRipgrepIndex(preview, data?.submatches)
|
|
347
|
+
: lowerIndex(preview, search.query);
|
|
324
348
|
if (typeof data?.path?.text !== 'string' || !Number.isInteger(data.line_number) || column < 0)
|
|
325
349
|
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
326
350
|
return [
|
|
@@ -333,7 +357,7 @@ function parseRipgrep(output, query) {
|
|
|
333
357
|
];
|
|
334
358
|
});
|
|
335
359
|
}
|
|
336
|
-
function parseGrep(output,
|
|
360
|
+
function parseGrep(output, search) {
|
|
337
361
|
const matches = [];
|
|
338
362
|
let offset = 0;
|
|
339
363
|
while (offset < output.length) {
|
|
@@ -345,7 +369,7 @@ function parseGrep(output, query) {
|
|
|
345
369
|
const record = output.subarray(separator + 1, end).toString('utf8');
|
|
346
370
|
const colon = record.indexOf(':');
|
|
347
371
|
const preview = record.slice(colon + 1).replace(/\r$/u, '');
|
|
348
|
-
const column = lowerIndex(preview, query);
|
|
372
|
+
const column = lowerIndex(preview, search.query);
|
|
349
373
|
if (colon < 1 || column < 0)
|
|
350
374
|
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
351
375
|
matches.push({
|
|
@@ -358,7 +382,7 @@ function parseGrep(output, query) {
|
|
|
358
382
|
}
|
|
359
383
|
return matches;
|
|
360
384
|
}
|
|
361
|
-
function parsePowerShell(output,
|
|
385
|
+
function parsePowerShell(output, search) {
|
|
362
386
|
return output
|
|
363
387
|
.toString('utf8')
|
|
364
388
|
.split(/\r?\n/u)
|
|
@@ -370,7 +394,7 @@ function parsePowerShell(output, query) {
|
|
|
370
394
|
!Number.isInteger(match.line) ||
|
|
371
395
|
typeof match.preview !== 'string')
|
|
372
396
|
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
373
|
-
const column = lowerIndex(match.preview, query);
|
|
397
|
+
const column = lowerIndex(match.preview, search.query);
|
|
374
398
|
if (column < 0)
|
|
375
399
|
throw new Error('CONTENT_SEARCH_OUTPUT_INVALID');
|
|
376
400
|
return [
|
|
@@ -445,6 +469,12 @@ async function nodeSearch(files, query, deadline) {
|
|
|
445
469
|
function lowerIndex(source, query) {
|
|
446
470
|
return source.toLocaleLowerCase().indexOf(query.toLocaleLowerCase());
|
|
447
471
|
}
|
|
472
|
+
function regexRipgrepIndex(preview, submatches) {
|
|
473
|
+
const start = submatches?.[0]?.start;
|
|
474
|
+
if (typeof start !== 'number' || !Number.isInteger(start) || start < 0)
|
|
475
|
+
return -1;
|
|
476
|
+
return Buffer.from(preview, 'utf8').subarray(0, start).toString('utf8').length;
|
|
477
|
+
}
|
|
448
478
|
function limitPerFile(matches) {
|
|
449
479
|
const counts = new Map();
|
|
450
480
|
return matches.filter((match) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { tmpdir } from 'node:os';
|
|
2
2
|
import { isAbsolute, win32 } from 'node:path';
|
|
3
3
|
import { listWorkspaceFiles, mutateWorkspaceFile, normalizeFilePreviewPath, readAllowedFileContentRange, readWorkspaceFileContentRange, readAllowedTextFile, readWorkspaceTextFile, writeWorkspaceTextFile, WorkspaceFileIndex } from '../workspace.js';
|
|
4
|
-
const INDEX_CACHE_LIMIT =
|
|
4
|
+
const INDEX_CACHE_LIMIT = 32;
|
|
5
5
|
export class WorkspaceFileService {
|
|
6
6
|
state;
|
|
7
7
|
allowedRoots;
|
|
@@ -37,6 +37,7 @@ export class WorkspaceWorkbenchService {
|
|
|
37
37
|
'workspace.files.search': (data) => this.searchFiles(record(data)),
|
|
38
38
|
'workspace.files.mutate': (data) => this.mutateFile(record(data)),
|
|
39
39
|
'workspace.content.search': (data) => this.searchContent(record(data)),
|
|
40
|
+
'workspace.symbol.prepare': (data) => this.prepareSymbolIndex(record(data)),
|
|
40
41
|
'workspace.symbol.definition': (data) => this.symbolDefinition(record(data)),
|
|
41
42
|
'workspace.files.upload.preflight': (data) => this.preflight(record(data)),
|
|
42
43
|
'workspace.files.upload.create': (data) => this.createUpload(record(data)),
|
|
@@ -116,6 +117,10 @@ export class WorkspaceWorkbenchService {
|
|
|
116
117
|
const workspace = this.options.requireWorkspace(input.workspaceId);
|
|
117
118
|
return { definition: await this.codeNavigation.definition(workspace, input) };
|
|
118
119
|
}
|
|
120
|
+
async prepareSymbolIndex(input) {
|
|
121
|
+
const workspace = this.options.requireWorkspace(input.workspaceId);
|
|
122
|
+
return { preparation: await this.codeNavigation.prepare(workspace, input) };
|
|
123
|
+
}
|
|
119
124
|
async preflight(input) {
|
|
120
125
|
return {
|
|
121
126
|
uploads: await this.options.uploads.preflight(this.options.requireWorkspace(input.workspaceId), input)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.97",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"license": "Apache-2.0 AND GPL-2.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -30,8 +30,8 @@
|
|
|
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/protocol": "0.9.
|
|
33
|
+
"@myagentroam/agent": "0.9.97",
|
|
34
|
+
"@myagentroam/protocol": "0.9.97"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/ws": "^8.18.1"
|