@myagentroam/node 0.9.96 → 0.9.98

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.
Files changed (35) hide show
  1. package/dist/codex-app-server.js +4 -15
  2. package/dist/connector.js +0 -18
  3. package/dist/ctags-binary.js +10 -0
  4. package/dist/runner/claude/managed-run-controller.js +1 -1
  5. package/dist/runner/claude-code-runner.js +14 -48
  6. package/dist/runner/codex/managed-run-controller.js +1 -1
  7. package/dist/runner/codex-runner.js +6 -13
  8. package/dist/runner/mar-agent-runner.js +3 -10
  9. package/dist/runner/opencode-runner.js +7 -19
  10. package/dist/service/code-navigation/c-family-path-specifier-resolver.js +40 -0
  11. package/dist/service/code-navigation/csharp-path-specifier-resolver.js +32 -0
  12. package/dist/service/code-navigation/ctags-index-storage.js +290 -0
  13. package/dist/service/code-navigation/ctags-query.js +140 -0
  14. package/dist/service/code-navigation/ecmascript-path-specifier-resolver.js +85 -0
  15. package/dist/service/code-navigation/file-reference-parser.js +28 -0
  16. package/dist/service/code-navigation/go-path-specifier-resolver.js +31 -0
  17. package/dist/service/code-navigation/jvm-path-specifier-resolver.js +28 -0
  18. package/dist/service/code-navigation/markdown-path-specifier-resolver.js +80 -0
  19. package/dist/service/code-navigation/navigation-resolution-definition.js +1 -0
  20. package/dist/service/code-navigation/navigation-resolution-registry.js +36 -0
  21. package/dist/service/code-navigation/navigation-target-validator.js +121 -0
  22. package/dist/service/code-navigation/path-specifier-resolver.js +6 -0
  23. package/dist/service/code-navigation/python-path-specifier-resolver.js +41 -0
  24. package/dist/service/code-navigation/rust-path-specifier-resolver.js +31 -0
  25. package/dist/service/code-navigation/stylesheet-path-specifier-resolver.js +55 -0
  26. package/dist/service/conversation-history-service.js +1 -1
  27. package/dist/service/external-session-resume-service.js +5 -22
  28. package/dist/service/node-control-message-service.js +1 -1
  29. package/dist/service/node-upgrade-coordinator.js +0 -1
  30. package/dist/service/session-catalog-service.js +2 -27
  31. package/dist/service/session-command-service.js +3 -3
  32. package/dist/service/session-message-service.js +3 -15
  33. package/dist/service/session-presentation-service.js +0 -1
  34. package/dist/service/workspace-code-navigation-service.js +80 -694
  35. package/package.json +4 -3
@@ -0,0 +1,36 @@
1
+ import { cFamilyNavigationResolutionDefinition, cNavigationResolutionDefinition, cppNavigationResolutionDefinition, objectiveCNavigationResolutionDefinition, objectiveCppNavigationResolutionDefinition } from './c-family-path-specifier-resolver.js';
2
+ import { csharpNavigationResolutionDefinition } from './csharp-path-specifier-resolver.js';
3
+ import { ecmascriptNavigationResolutionDefinition } from './ecmascript-path-specifier-resolver.js';
4
+ import { goNavigationResolutionDefinition } from './go-path-specifier-resolver.js';
5
+ import { jvmNavigationResolutionDefinition } from './jvm-path-specifier-resolver.js';
6
+ import { markdownNavigationResolutionDefinition } from './markdown-path-specifier-resolver.js';
7
+ import { pythonNavigationResolutionDefinition } from './python-path-specifier-resolver.js';
8
+ import { rustNavigationResolutionDefinition } from './rust-path-specifier-resolver.js';
9
+ import { cssNavigationResolutionDefinition, htmlNavigationResolutionDefinition, lessNavigationResolutionDefinition, sassNavigationResolutionDefinition, scssNavigationResolutionDefinition, vueNavigationResolutionDefinition } from './stylesheet-path-specifier-resolver.js';
10
+ const definitionsByLanguage = {
11
+ css: cssNavigationResolutionDefinition,
12
+ ecmascript: ecmascriptNavigationResolutionDefinition,
13
+ go: goNavigationResolutionDefinition,
14
+ html: htmlNavigationResolutionDefinition,
15
+ jvm: jvmNavigationResolutionDefinition,
16
+ csharp: csharpNavigationResolutionDefinition,
17
+ less: lessNavigationResolutionDefinition,
18
+ markdown: markdownNavigationResolutionDefinition,
19
+ python: pythonNavigationResolutionDefinition,
20
+ sass: sassNavigationResolutionDefinition,
21
+ scss: scssNavigationResolutionDefinition,
22
+ c: cNavigationResolutionDefinition,
23
+ cpp: cppNavigationResolutionDefinition,
24
+ 'objective-c': objectiveCNavigationResolutionDefinition,
25
+ 'objective-cpp': objectiveCppNavigationResolutionDefinition,
26
+ 'c-family': cFamilyNavigationResolutionDefinition,
27
+ rust: rustNavigationResolutionDefinition,
28
+ vue: vueNavigationResolutionDefinition
29
+ };
30
+ export function navigationResolutionDefinitions() {
31
+ return Object.values(definitionsByLanguage);
32
+ }
33
+ export function pathSpecifierResolver(languageId, specifier) {
34
+ const definition = definitionsByLanguage[languageId];
35
+ return definition?.resolver.accepts(specifier) === true ? definition.resolver : undefined;
36
+ }
@@ -0,0 +1,121 @@
1
+ import { lstat, open, readFile, realpath } from 'node:fs/promises';
2
+ import { relative, resolve, sep } from 'node:path';
3
+ const MAX_SOURCE_BYTES = 4 * 1024 * 1024;
4
+ const READ_BLOCK_BYTES = 4096;
5
+ const MAX_TARGET_PREVIEW_CHARACTERS = 512;
6
+ export function sourceFileFingerprint(size, modifiedAt) {
7
+ return `${size}:${modifiedAt ?? -1}`;
8
+ }
9
+ export async function validateSymbolTarget(root, target, symbol, expectedFingerprint) {
10
+ const actual = await validatedFile(root, target.path, expectedFingerprint);
11
+ if (actual === undefined)
12
+ return undefined;
13
+ const line = await lineAt(actual, target.line);
14
+ return line !== undefined && containsSymbol(line, symbol) ? targetPreview(line) : undefined;
15
+ }
16
+ export async function validatePathTarget(root, path, expectedFingerprint, anchor, anchorLine) {
17
+ const actual = await validatedFile(root, path, expectedFingerprint);
18
+ if (actual === undefined)
19
+ return undefined;
20
+ const line = anchor === undefined || anchorLine === undefined
21
+ ? 1
22
+ : anchorLine(await readFile(actual, 'utf8').catch(() => ''), anchor);
23
+ if (line === undefined)
24
+ return undefined;
25
+ const preview = await lineAt(actual, line);
26
+ return preview === undefined ? undefined : { line, preview };
27
+ }
28
+ async function validatedFile(root, path, expectedFingerprint) {
29
+ const requested = resolve(root, ...path.split('/'));
30
+ const requestedMetadata = await lstat(requested).catch(() => undefined);
31
+ if (requestedMetadata === undefined ||
32
+ !requestedMetadata.isFile() ||
33
+ requestedMetadata.isSymbolicLink() ||
34
+ requestedMetadata.size > MAX_SOURCE_BYTES)
35
+ return undefined;
36
+ const actual = await realpath(requested).catch(() => undefined);
37
+ if (actual === undefined ||
38
+ relative(root, actual)
39
+ .split(sep)
40
+ .some((part) => part === '..'))
41
+ return undefined;
42
+ const metadata = await lstat(actual).catch(() => undefined);
43
+ if (metadata === undefined ||
44
+ !metadata.isFile() ||
45
+ metadata.isSymbolicLink() ||
46
+ metadata.size > MAX_SOURCE_BYTES ||
47
+ (expectedFingerprint !== undefined &&
48
+ sourceFileFingerprint(metadata.size, metadata.mtimeMs) !== expectedFingerprint))
49
+ return undefined;
50
+ return actual;
51
+ }
52
+ export function targetPreview(line) {
53
+ if (line.length <= MAX_TARGET_PREVIEW_CHARACTERS)
54
+ return line;
55
+ const contentLimit = MAX_TARGET_PREVIEW_CHARACTERS - 1;
56
+ const end = line.charCodeAt(contentLimit - 1);
57
+ const safeEnd = end >= 0xd800 && end <= 0xdbff ? contentLimit - 1 : contentLimit;
58
+ return `${line.slice(0, safeEnd)}…`;
59
+ }
60
+ async function lineAt(path, targetLine) {
61
+ const handle = await open(path, 'r');
62
+ try {
63
+ let offset = 0;
64
+ let line = 1;
65
+ let pending = Buffer.alloc(0);
66
+ while (offset < MAX_SOURCE_BYTES) {
67
+ const chunk = Buffer.alloc(READ_BLOCK_BYTES);
68
+ const { bytesRead } = await handle.read(chunk, 0, chunk.length, offset);
69
+ if (bytesRead === 0)
70
+ return line === targetLine ? pending.toString('utf8') : undefined;
71
+ offset += bytesRead;
72
+ let content = pending.length === 0
73
+ ? chunk.subarray(0, bytesRead)
74
+ : Buffer.concat([pending, chunk.subarray(0, bytesRead)]);
75
+ let newline;
76
+ while ((newline = content.indexOf(0x0a)) >= 0) {
77
+ if (line === targetLine)
78
+ return content.subarray(0, newline).toString('utf8');
79
+ line += 1;
80
+ content = content.subarray(newline + 1);
81
+ }
82
+ pending = content;
83
+ }
84
+ return line === targetLine ? pending.toString('utf8') : undefined;
85
+ }
86
+ finally {
87
+ await handle.close();
88
+ }
89
+ }
90
+ export function containsSymbol(line, symbol) {
91
+ const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
92
+ const optionalScssPrefix = symbol.startsWith('$') ? '' : '\\$?';
93
+ return new RegExp(`(?:^|[^\\p{L}\\p{N}_$])${optionalScssPrefix}${escaped}(?![\\p{L}\\p{N}_$])`, 'u').test(line);
94
+ }
95
+ export function compareTargets(left, right, currentPath) {
96
+ const leftScore = targetScore(left, currentPath);
97
+ const rightScore = targetScore(right, currentPath);
98
+ return (rightScore - leftScore ||
99
+ left.path.localeCompare(right.path) ||
100
+ left.line - right.line ||
101
+ (left.kind ?? '').localeCompare(right.kind ?? ''));
102
+ }
103
+ function targetScore(target, currentPath) {
104
+ if (target.path === currentPath)
105
+ return 100;
106
+ const currentDirectory = currentPath.includes('/')
107
+ ? currentPath.slice(0, currentPath.lastIndexOf('/'))
108
+ : '';
109
+ const targetDirectory = target.path.includes('/')
110
+ ? target.path.slice(0, target.path.lastIndexOf('/'))
111
+ : '';
112
+ return commonPathSegments(currentDirectory, targetDirectory);
113
+ }
114
+ function commonPathSegments(left, right) {
115
+ const leftParts = left === '' ? [] : left.split('/');
116
+ const rightParts = right === '' ? [] : right.split('/');
117
+ let count = 0;
118
+ while (count < leftParts.length && leftParts[count] === rightParts[count])
119
+ count += 1;
120
+ return count;
121
+ }
@@ -0,0 +1,6 @@
1
+ export function matchesPathSuffix(path, suffix) {
2
+ return path === suffix || path.endsWith(`/${suffix}`);
3
+ }
4
+ export function shortestPathScore(path) {
5
+ return path.length;
6
+ }
@@ -0,0 +1,41 @@
1
+ import { matchesPathSuffix, shortestPathScore } from './path-specifier-resolver.js';
2
+ import { posix } from 'node:path';
3
+ function accepts(value) {
4
+ return (/^[\p{L}_$][\p{L}\p{N}_$]*(?:\.[\p{L}_$][\p{L}\p{N}_$]*)*$/u.test(value) ||
5
+ /^\.+(?:[\p{L}_$][\p{L}\p{N}_$]*(?:\.[\p{L}_$][\p{L}\p{N}_$]*)*)?$/u.test(value));
6
+ }
7
+ function matches(path, currentPath, specifier) {
8
+ return candidates(currentPath, specifier).some((candidate) => matchesPathSuffix(path, candidate));
9
+ }
10
+ function directCandidates(currentPath, specifier) {
11
+ return specifier.startsWith('.') ? candidates(currentPath, specifier) : [];
12
+ }
13
+ function candidates(currentPath, specifier) {
14
+ const packagePath = relativePackagePath(currentPath, specifier);
15
+ if (packagePath === undefined)
16
+ return [];
17
+ return packagePath === '.'
18
+ ? ['__init__.py']
19
+ : [`${packagePath}.py`, `${packagePath}.pyw`, `${packagePath}/__init__.py`];
20
+ }
21
+ function relativePackagePath(currentPath, specifier) {
22
+ if (!specifier.startsWith('.'))
23
+ return specifier.replaceAll('.', '/');
24
+ const levels = specifier.match(/^\.+/u)?.[0].length ?? 0;
25
+ const module = specifier.slice(levels).replaceAll('.', '/');
26
+ const base = posix.normalize(posix.join(posix.dirname(currentPath), ...Array.from({ length: levels - 1 }, () => '..')));
27
+ if (base === '..' || base.startsWith('../'))
28
+ return undefined;
29
+ return module.length === 0 ? base : posix.join(base, module);
30
+ }
31
+ export const pythonPathSpecifierResolver = {
32
+ id: 'python.path-specifier',
33
+ accepts,
34
+ directCandidates,
35
+ matches,
36
+ score: shortestPathScore
37
+ };
38
+ export const pythonNavigationResolutionDefinition = {
39
+ languageId: 'python',
40
+ resolver: pythonPathSpecifierResolver
41
+ };
@@ -0,0 +1,31 @@
1
+ import { posix } from 'node:path';
2
+ function accepts(value) {
3
+ return (/^[A-Za-z_][A-Za-z0-9_]*$/u.test(value) || /^(?:\.\.?\/)?[A-Za-z0-9_./-]+\.rs$/u.test(value));
4
+ }
5
+ function matches(path, currentPath, specifier) {
6
+ return candidates(currentPath, specifier).includes(path);
7
+ }
8
+ function score(path, currentPath, specifier) {
9
+ const index = candidates(currentPath, specifier).indexOf(path);
10
+ return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
11
+ }
12
+ function candidates(currentPath, specifier) {
13
+ const directory = posix.dirname(currentPath);
14
+ if (specifier.endsWith('.rs')) {
15
+ const resolved = posix.normalize(posix.join(directory, specifier));
16
+ return resolved.startsWith('../') || resolved === '..' ? [] : [resolved];
17
+ }
18
+ const base = directory === '.' ? specifier : `${directory}/${specifier}`;
19
+ return [`${base}.rs`, `${base}/mod.rs`];
20
+ }
21
+ export const rustPathSpecifierResolver = {
22
+ id: 'rust.path-specifier',
23
+ accepts,
24
+ directCandidates: candidates,
25
+ matches,
26
+ score
27
+ };
28
+ export const rustNavigationResolutionDefinition = {
29
+ languageId: 'rust',
30
+ resolver: rustPathSpecifierResolver
31
+ };
@@ -0,0 +1,55 @@
1
+ import { posix } from 'node:path';
2
+ function accepts(value) {
3
+ return /^(?:\.\.?\/)?[A-Za-z0-9_./$+@ -]+$/u.test(value);
4
+ }
5
+ function createResolver(languageId, extensions, partials = false) {
6
+ const candidates = (currentPath, specifier) => pathCandidates(currentPath, specifier, extensions, partials);
7
+ return {
8
+ id: `${languageId}.path-specifier`,
9
+ accepts,
10
+ directCandidates: candidates,
11
+ matches: (path, currentPath, specifier) => candidates(currentPath, specifier).includes(path),
12
+ score: (path, currentPath, specifier) => {
13
+ const index = candidates(currentPath, specifier).indexOf(path);
14
+ return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
15
+ }
16
+ };
17
+ }
18
+ function pathCandidates(currentPath, specifier, extensions, partials) {
19
+ const resolved = posix.normalize(posix.join(posix.dirname(currentPath), specifier));
20
+ if (resolved === '..' || resolved.startsWith('../'))
21
+ return [];
22
+ const path = resolved.replace(/^\.\//u, '');
23
+ if (path.slice(path.lastIndexOf('/') + 1).includes('.'))
24
+ return [path];
25
+ const base = extensions.flatMap((extension) => [
26
+ `${path}${extension}`,
27
+ `${path}/index${extension}`
28
+ ]);
29
+ if (!partials)
30
+ return base;
31
+ const directory = posix.dirname(path);
32
+ const name = path.slice(path.lastIndexOf('/') + 1);
33
+ return [...base, ...extensions.map((extension) => posix.join(directory, `_${name}${extension}`))];
34
+ }
35
+ function definition(languageId, extensions, partials = false) {
36
+ return { languageId, resolver: createResolver(languageId, extensions, partials) };
37
+ }
38
+ export const cssNavigationResolutionDefinition = definition('css', ['.css']);
39
+ export const scssNavigationResolutionDefinition = definition('scss', ['.scss', '.css'], true);
40
+ export const sassNavigationResolutionDefinition = definition('sass', ['.sass', '.scss', '.css'], true);
41
+ export const lessNavigationResolutionDefinition = definition('less', ['.less', '.css']);
42
+ export const htmlNavigationResolutionDefinition = definition('html', [
43
+ '.html',
44
+ '.htm',
45
+ '.css',
46
+ '.js'
47
+ ]);
48
+ export const vueNavigationResolutionDefinition = definition('vue', [
49
+ '.vue',
50
+ '.html',
51
+ '.css',
52
+ '.scss',
53
+ '.sass',
54
+ '.less'
55
+ ]);
@@ -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, 'UNAVAILABLE');
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) === 'EXTERNAL_ACTIVE')
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
- const resumedId = this.options.state.resumedByDirectId.get(sessionId);
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 === 'session.resume' || envelope.type === 'lease.reconcile') {
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) === 'EXTERNAL_ACTIVE') {
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) === 'EXTERNAL_ACTIVE')
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
- const session = resolved.nativeControl === 'EXTERNAL' ? await this.options.resume.resume(resolved) : resolved;
43
- if (session === undefined)
44
- throw new Error('RUNNER_COMMAND_UNAVAILABLE');
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.deferred ? accepted.session : await accepted.ready;
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 readySession = await accepted.ready;
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
- if (deferPreparation)
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 {