@myagentroam/node 0.9.0 → 0.9.2
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.d.ts +2 -0
- package/dist/connector.js +38 -12
- package/dist/native-session-history.js +72 -14
- package/dist/runner/abstract-runner.d.ts +1 -1
- package/dist/runner/abstract-runner.js +2 -2
- package/dist/runner/claude/managed-run-controller.js +17 -7
- package/dist/runner/claude-code-runner.js +3 -4
- package/dist/runner/codex/conversation-parser.d.ts +2 -2
- package/dist/runner/codex/conversation-parser.js +30 -12
- package/dist/runner/codex/managed-run-controller.js +7 -6
- package/dist/runner/codex-runner.d.ts +1 -1
- package/dist/runner/codex-runner.js +10 -8
- package/dist/runner/opencode/conversation-parser.d.ts +11 -7
- package/dist/runner/opencode/conversation-parser.js +15 -15
- package/dist/runner/opencode/managed-run-controller.d.ts +2 -1
- package/dist/runner/opencode/managed-run-controller.js +26 -12
- package/dist/runner-command-engine.js +1 -1
- package/dist/runner-profiles.js +25 -17
- package/dist/runner-usage.js +5 -5
- package/dist/service/conversation-history-service.js +55 -24
- package/dist/service/conversation-segment-service.d.ts +20 -0
- package/dist/service/conversation-segment-service.js +155 -0
- package/dist/service/mcp-installation-verifier.js +1 -1
- package/dist/service/native-session-watch-service.d.ts +1 -0
- package/dist/service/native-session-watch-service.js +21 -4
- package/dist/service/node-request-service.js +2 -1
- package/dist/service/run-attachment-service.d.ts +3 -2
- package/dist/service/run-attachment-service.js +21 -11
- package/dist/service/run-event-service.d.ts +1 -0
- package/dist/service/run-event-service.js +8 -2
- package/dist/service/session-catalog-service.js +4 -4
- package/dist/service/session-presentation-service.d.ts +1 -0
- package/dist/service/session-presentation-service.js +8 -1
- package/dist/service/session-query-service.d.ts +4 -0
- package/dist/service/session-query-service.js +6 -1
- package/dist/service/skill-directory-service.js +3 -26
- package/dist/service/skill-install-service.js +4 -74
- package/dist/service/skill-node-operation-service.js +2 -1
- package/dist/service/workspace-git-exclude-service.d.ts +4 -0
- package/dist/service/workspace-git-exclude-service.js +119 -0
- package/dist/service/workspace-queue-workbench-service.js +3 -2
- package/dist/service/workspace-service.js +2 -0
- package/dist/supervisor.js +2 -1
- package/dist/terminal.js +1 -1
- package/dist/util/node-operation-parsers.js +2 -2
- package/dist/util/runner-native-session-parsers.d.ts +2 -3
- package/dist/util/runner-native-session-parsers.js +5 -14
- package/dist/util/safe-error.d.ts +2 -0
- package/dist/util/safe-error.js +5 -0
- package/package.json +2 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
const MAR_TEMP_DIRECTORY = '/.mar/';
|
|
7
|
+
const BLOCK_BEGIN = '# BEGIN MyAgentRoam';
|
|
8
|
+
const BLOCK_END = '# END MyAgentRoam';
|
|
9
|
+
const LEGACY_BLOCK_BEGIN = '# BEGIN MyAgentRoam Skills';
|
|
10
|
+
const LEGACY_BLOCK_END = '# END MyAgentRoam Skills';
|
|
11
|
+
export async function ensureWorkspaceGitExclude(workspace) {
|
|
12
|
+
await mutateGitExclude(workspace, (entries) => entries.add(MAR_TEMP_DIRECTORY));
|
|
13
|
+
}
|
|
14
|
+
export async function updateWorkspaceSkillGitExclude(workspace, name, add) {
|
|
15
|
+
await mutateGitExclude(workspace, (entries) => {
|
|
16
|
+
entries.add(MAR_TEMP_DIRECTORY);
|
|
17
|
+
for (const root of ['.agents', '.claude']) {
|
|
18
|
+
const line = `/${root}/skills/${name}/`;
|
|
19
|
+
if (add)
|
|
20
|
+
entries.add(line);
|
|
21
|
+
else
|
|
22
|
+
entries.delete(line);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export async function rewriteWorkspaceGitExclude(workspace, names) {
|
|
27
|
+
await mutateGitExclude(workspace, (entries) => {
|
|
28
|
+
entries.clear();
|
|
29
|
+
entries.add(MAR_TEMP_DIRECTORY);
|
|
30
|
+
for (const name of names)
|
|
31
|
+
for (const root of ['.agents', '.claude'])
|
|
32
|
+
entries.add(`/${root}/skills/${name}/`);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export async function hasBrokenWorkspaceSkillGitExclude(workspace, names) {
|
|
36
|
+
if (!(await exists(path.join(workspace, '.git'))))
|
|
37
|
+
return false;
|
|
38
|
+
try {
|
|
39
|
+
const exclude = await resolveGitExclude(workspace);
|
|
40
|
+
const content = await readFile(exclude, 'utf8');
|
|
41
|
+
return names
|
|
42
|
+
.flatMap((name) => [`/.agents/skills/${name}/`, `/.claude/skills/${name}/`])
|
|
43
|
+
.some((entry) => !content.includes(entry));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function mutateGitExclude(workspace, mutate) {
|
|
50
|
+
if (!(await exists(path.join(workspace, '.git'))))
|
|
51
|
+
return;
|
|
52
|
+
const exclude = await resolveGitExclude(workspace);
|
|
53
|
+
await mkdir(path.dirname(exclude), { recursive: true });
|
|
54
|
+
let content = '';
|
|
55
|
+
try {
|
|
56
|
+
const excludeStat = await lstat(exclude);
|
|
57
|
+
if (!excludeStat.isFile() || excludeStat.isSymbolicLink())
|
|
58
|
+
throw new Error('WORKSPACE_SKILL_GIT_EXCLUDE_FAILED');
|
|
59
|
+
content = await readFile(exclude, 'utf8');
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (!isMissing(error))
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
const pattern = managedBlockPattern();
|
|
66
|
+
const existing = [...content.matchAll(pattern)].map((match) => match[0]).join('\n');
|
|
67
|
+
const entries = new Set(existing.split(/\r?\n/u).filter((line) => line.startsWith('/.')));
|
|
68
|
+
mutate(entries);
|
|
69
|
+
const block = `${BLOCK_BEGIN}\n${[...entries].sort().join('\n')}\n${BLOCK_END}\n`;
|
|
70
|
+
const next = content.replace(pattern, '').replace(/\s*$/u, '\n') + block;
|
|
71
|
+
const staging = path.join(path.dirname(exclude), `.mar-git-exclude-${randomUUID()}.tmp`);
|
|
72
|
+
try {
|
|
73
|
+
await writeFile(staging, next, { encoding: 'utf8', flag: 'wx' });
|
|
74
|
+
await rename(staging, exclude);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
await rm(staging, { force: true });
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function resolveGitExclude(workspace) {
|
|
82
|
+
try {
|
|
83
|
+
const result = await promisify(execFile)('git', ['-C', workspace, 'rev-parse', '--git-path', 'info/exclude', '--git-common-dir'], { timeout: 5_000, windowsHide: true });
|
|
84
|
+
const [excludeOutput, commonOutput] = result.stdout.trim().split(/\r?\n/u);
|
|
85
|
+
if (!excludeOutput || !commonOutput)
|
|
86
|
+
throw new Error();
|
|
87
|
+
const exclude = path.resolve(workspace, excludeOutput);
|
|
88
|
+
const common = path.resolve(workspace, commonOutput);
|
|
89
|
+
const relative = path.relative(common, exclude);
|
|
90
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
91
|
+
throw new Error();
|
|
92
|
+
return exclude;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw new Error('WORKSPACE_SKILL_GIT_EXCLUDE_FAILED');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function exists(value) {
|
|
99
|
+
try {
|
|
100
|
+
await lstat(value);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (isMissing(error))
|
|
105
|
+
return false;
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function isMissing(error) {
|
|
110
|
+
return (error instanceof Error && 'code' in error && error.code === 'ENOENT');
|
|
111
|
+
}
|
|
112
|
+
function escapeRegExp(value) {
|
|
113
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
114
|
+
}
|
|
115
|
+
function managedBlockPattern() {
|
|
116
|
+
const current = `${escapeRegExp(BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp(BLOCK_END)}(?:\\r?\\n)?`;
|
|
117
|
+
const legacy = `${escapeRegExp(LEGACY_BLOCK_BEGIN)}\\r?\\n[\\s\\S]*?${escapeRegExp(LEGACY_BLOCK_END)}(?:\\r?\\n)?`;
|
|
118
|
+
return new RegExp(`(?:${current}|${legacy})`, 'gu');
|
|
119
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isPlainRecord } from '../runner/codex/conversation-parser.js';
|
|
2
2
|
import { isTerminalRunStatus } from '../util/node-operation-parsers.js';
|
|
3
3
|
import { withNonImageAttachmentPrompt } from '../runner/codex/conversation-parser.js';
|
|
4
|
+
import { safeErrorCode } from '../util/safe-error.js';
|
|
4
5
|
export class WorkspaceQueueWorkbenchService {
|
|
5
6
|
options;
|
|
6
7
|
starts = new Map();
|
|
@@ -183,7 +184,7 @@ export class WorkspaceQueueWorkbenchService {
|
|
|
183
184
|
});
|
|
184
185
|
}
|
|
185
186
|
catch (error) {
|
|
186
|
-
this.options.emitRun(run.id, 'run.rejected', { code: error
|
|
187
|
+
this.options.emitRun(run.id, 'run.rejected', { code: safeErrorCode(error, 'QUEUE_START_FAILED') }, 'FAILED');
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
present(workspaceId, sessionId) {
|
|
@@ -203,7 +204,7 @@ export class WorkspaceQueueWorkbenchService {
|
|
|
203
204
|
{
|
|
204
205
|
run,
|
|
205
206
|
sessionId: run.sessionId,
|
|
206
|
-
sessionTitle: queuedSession?.customTitle ?? queuedSession?.runnerTitle ?? '
|
|
207
|
+
sessionTitle: queuedSession?.customTitle ?? queuedSession?.runnerTitle ?? 'Untitled session',
|
|
207
208
|
content: this.options.runtime.queuedMessageText(run.id)
|
|
208
209
|
}
|
|
209
210
|
];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { rmdir } from 'node:fs/promises';
|
|
2
2
|
import { createWorkspaceDirectory, inspectWorkspace, listWorkspaceDirectories } from '../workspace.js';
|
|
3
|
+
import { ensureWorkspaceGitExclude } from './workspace-git-exclude-service.js';
|
|
3
4
|
export class WorkspaceService {
|
|
4
5
|
database;
|
|
5
6
|
config;
|
|
@@ -31,6 +32,7 @@ export class WorkspaceService {
|
|
|
31
32
|
? (createdPath = await createWorkspaceDirectory(input.path, input.createDirectoryName, this.config().allowedRoots))
|
|
32
33
|
: input.path;
|
|
33
34
|
const inspected = await inspectWorkspace(path, this.config().allowedRoots);
|
|
35
|
+
await ensureWorkspaceGitExclude(inspected.path);
|
|
34
36
|
return {
|
|
35
37
|
workspace: this.database().saveWorkspace({
|
|
36
38
|
path: inspected.path,
|
package/dist/supervisor.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
4
4
|
import { loadNodeConfig, nodeDatabasePath } from './config.js';
|
|
5
5
|
import { nodeLog, platformCliInvocation } from './operational.js';
|
|
6
6
|
import { nodeUpgradeRequestPath } from './service/node-upgrade-coordinator.js';
|
|
7
|
+
import { safeErrorCode } from './util/safe-error.js';
|
|
7
8
|
const UPGRADE_EXIT_CODE = 75;
|
|
8
9
|
const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
|
|
9
10
|
export async function superviseNode(configPath, bootstrapEntrypoint) {
|
|
@@ -73,7 +74,7 @@ export async function superviseNode(configPath, bootstrapEntrypoint) {
|
|
|
73
74
|
catch (error) {
|
|
74
75
|
await writeResult(dataDirectory, {
|
|
75
76
|
state: 'FAILED',
|
|
76
|
-
errorCode: error
|
|
77
|
+
errorCode: safeErrorCode(error, 'NODE_UPGRADE_INSTALL_FAILED'),
|
|
77
78
|
updatedAt: Date.now()
|
|
78
79
|
});
|
|
79
80
|
await rm(requestPath, { force: true });
|
package/dist/terminal.js
CHANGED
|
@@ -2,7 +2,7 @@ import { nodeCapabilitiesSchema } from '@myagentroam/protocol';
|
|
|
2
2
|
const MAX_COMPOSER_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
3
3
|
const MAX_COMPOSER_ATTACHMENTS_BYTES = 10 * 1024 * 1024;
|
|
4
4
|
const USER_AUTHORIZATION_CLOCK_SKEW_MS = 5_000;
|
|
5
|
-
const NON_IMAGE_ATTACHMENT_PROMPT_PREFIX = '\n\n
|
|
5
|
+
const NON_IMAGE_ATTACHMENT_PROMPT_PREFIX = '\n\nNon-image attachments for this turn were uploaded to the Workspace. Read these files as needed:\n';
|
|
6
6
|
const COMPOSER_IMAGE_MIME_TYPES = new Set([
|
|
7
7
|
'image/png',
|
|
8
8
|
'image/jpeg',
|
|
@@ -100,7 +100,7 @@ export function compactRunnerValue(value, limit) {
|
|
|
100
100
|
text = JSON.stringify(value);
|
|
101
101
|
}
|
|
102
102
|
catch {
|
|
103
|
-
return { text: '
|
|
103
|
+
return { text: '(Unable to display)', truncated: false };
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
return { text: compactRunnerText(text, limit), truncated: text.length > limit };
|
|
@@ -25,7 +25,6 @@ export declare function extractCodexThreads(result: unknown): readonly {
|
|
|
25
25
|
readonly title: string | undefined;
|
|
26
26
|
}[];
|
|
27
27
|
export declare function codexThreadActivity(result: unknown): SessionActivityState;
|
|
28
|
-
export declare function externalResumeFailureDetail(error: unknown): string;
|
|
29
28
|
export declare function codexActiveTurnId(result: unknown): string | undefined;
|
|
30
29
|
export declare function codexThreadCwd(value: unknown): string;
|
|
31
30
|
export declare function isWithinWorkspace(cwd: string, workspacePath: string): boolean;
|
|
@@ -46,8 +45,8 @@ export declare function nativeConversationPage(session: NodeAgentSession, histor
|
|
|
46
45
|
readonly nextCursor: string | null;
|
|
47
46
|
};
|
|
48
47
|
/**
|
|
49
|
-
* Claude JSONL
|
|
50
|
-
*
|
|
48
|
+
* Claude JSONL does not retain browser request IDs. While the Node process is alive, match current
|
|
49
|
+
* runtime user messages by content and creation time so native history replaces the optimistic Turn.
|
|
51
50
|
*/
|
|
52
51
|
export declare function nativeUserClientMessageId(entry: Extract<NativeSessionHistory['items'][number], {
|
|
53
52
|
readonly kind: 'message';
|
|
@@ -69,15 +69,6 @@ export function codexThreadActivity(result) {
|
|
|
69
69
|
return 'IDLE';
|
|
70
70
|
return 'UNAVAILABLE';
|
|
71
71
|
}
|
|
72
|
-
export function externalResumeFailureDetail(error) {
|
|
73
|
-
const message = error instanceof Error ? error.message : 'SESSION_RESUME_REJECTED';
|
|
74
|
-
return [...message]
|
|
75
|
-
.map((character) => (character <= '\u001f' || character === '\u007f' ? ' ' : character))
|
|
76
|
-
.join('')
|
|
77
|
-
.replace(/\s+/g, ' ')
|
|
78
|
-
.trim()
|
|
79
|
-
.slice(0, 240);
|
|
80
|
-
}
|
|
81
72
|
export function codexActiveTurnId(result) {
|
|
82
73
|
if (!isPlainRecord(result) ||
|
|
83
74
|
!isPlainRecord(result.thread) ||
|
|
@@ -228,8 +219,8 @@ function nativeTranscriptConversationItem(session, entry, index, turnId, runtime
|
|
|
228
219
|
const clientMessageId = entry.kind === 'message' && entry.role === 'USER'
|
|
229
220
|
? nativeUserClientMessageId(entry, time, runtimeTurns)
|
|
230
221
|
: null;
|
|
231
|
-
// Claude
|
|
232
|
-
//
|
|
222
|
+
// Claude tagged Plan Mode strips the injected planning instruction label from user content
|
|
223
|
+
// and normalizes a complete <proposed_plan> Agent response into a plan card.
|
|
233
224
|
const entryText = entry.kind === 'message'
|
|
234
225
|
? entry.role === 'USER'
|
|
235
226
|
? stripClaudePlanTag(entry.text)
|
|
@@ -304,8 +295,8 @@ function nativeTranscriptConversationItem(session, entry, index, turnId, runtime
|
|
|
304
295
|
return item;
|
|
305
296
|
}
|
|
306
297
|
/**
|
|
307
|
-
* Claude JSONL
|
|
308
|
-
*
|
|
298
|
+
* Claude JSONL does not retain browser request IDs. While the Node process is alive, match current
|
|
299
|
+
* runtime user messages by content and creation time so native history replaces the optimistic Turn.
|
|
309
300
|
*/
|
|
310
301
|
export function nativeUserClientMessageId(entry, createdAt, runtimeTurns) {
|
|
311
302
|
for (const turn of runtimeTurns) {
|
|
@@ -314,7 +305,7 @@ export function nativeUserClientMessageId(entry, createdAt, runtimeTurns) {
|
|
|
314
305
|
continue;
|
|
315
306
|
const payload = user.payload;
|
|
316
307
|
const clientMessageId = typeof payload.clientMessageId === 'string' ? payload.clientMessageId : null;
|
|
317
|
-
//
|
|
308
|
+
// Tagged Plan Mode instruction labels exist only in native transcripts; runtime bubbles retain the original content.
|
|
318
309
|
if (clientMessageId === null || payload.text !== stripClaudePlanTag(entry.text))
|
|
319
310
|
continue;
|
|
320
311
|
const runtimeCreatedAt = user.startedAt ?? turn.startedAt;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const STABLE_ERROR_CODE = /^[A-Z][A-Z0-9_]{0,127}$/u;
|
|
2
|
+
/** Returns only a stable public error code, never a raw runtime exception message. */
|
|
3
|
+
export function safeErrorCode(error, fallback) {
|
|
4
|
+
return error instanceof Error && STABLE_ERROR_CODE.test(error.message) ? error.message : fallback;
|
|
5
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "MyAgentRoam Node runtime CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"node-pty": "1.1.0",
|
|
25
25
|
"ws": "^8.21.3",
|
|
26
26
|
"zod": "4.4.3",
|
|
27
|
-
"@myagentroam/protocol": "^0.9.
|
|
27
|
+
"@myagentroam/protocol": "^0.9.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/ws": "^8.18.1"
|