@myagentroam/agent 0.9.62 → 0.9.64
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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/dist/cli/codex-auth-file.js +268 -3
- package/dist/cli/main.js +427 -2
- package/dist/error.js +66 -2
- package/dist/host/contracts.js +20 -2
- package/dist/host/local-agent-host.js +146 -2
- package/dist/host/local-process-executor.js +39 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -2
- package/dist/model/anthropic-messages.js +474 -2
- package/dist/model/anthropic-thinking.js +33 -2
- package/dist/model/catalog-file.js +88 -2
- package/dist/model/configuration.js +137 -2
- package/dist/model/contracts.d.ts +2 -0
- package/dist/model/contracts.js +1 -2
- package/dist/model/http.js +529 -2
- package/dist/model/openai-responses.d.ts +2 -0
- package/dist/model/openai-responses.js +1602 -2
- package/dist/model/runtime-credential.js +35 -2
- package/dist/model/sse.js +199 -2
- package/dist/prompts/compact.js +25 -2
- package/dist/prompts/core.js +37 -2
- package/dist/prompts/execution-budget.js +10 -2
- package/dist/prompts/index.d.ts +3 -2
- package/dist/prompts/index.js +47 -2
- package/dist/prompts/modes.js +25 -2
- package/dist/prompts/output.js +1 -2
- package/dist/prompts/resources.d.ts +1 -1
- package/dist/prompts/resources.js +31 -2
- package/dist/prompts/subagent.d.ts +6 -0
- package/dist/prompts/subagent.js +29 -2
- package/dist/prompts/title.js +9 -2
- package/dist/prompts/workflow.js +32 -2
- package/dist/runtime/async-queue.js +62 -2
- package/dist/runtime/builtin-skills.js +19 -2
- package/dist/runtime/byted-ark-imagegen-skill.js +18 -2
- package/dist/runtime/codex-imagegen-skill.js +35 -2
- package/dist/runtime/compact.js +225 -2
- package/dist/runtime/context-gc-runtime.d.ts +35 -0
- package/dist/runtime/context-gc-runtime.js +163 -0
- package/dist/runtime/context-gc-tokens.d.ts +24 -0
- package/dist/runtime/context-gc-tokens.js +83 -0
- package/dist/runtime/context-gc.d.ts +66 -0
- package/dist/runtime/context-gc.js +909 -0
- package/dist/runtime/context-projection.d.ts +23 -0
- package/dist/runtime/context-projection.js +203 -0
- package/dist/runtime/environment-context.js +41 -2
- package/dist/runtime/execution-policy.js +13 -2
- package/dist/runtime/instructions.js +298 -2
- package/dist/runtime/normalize.js +40 -2
- package/dist/runtime/plan-output.js +95 -2
- package/dist/runtime/rollout-budget.d.ts +2 -0
- package/dist/runtime/rollout-budget.js +92 -2
- package/dist/runtime/token-budget.d.ts +2 -0
- package/dist/runtime/token-budget.js +44 -2
- package/dist/sdk/agent.js +1804 -2
- package/dist/sdk/types.d.ts +15 -5
- package/dist/sdk/types.js +1 -2
- package/dist/session/catalog.d.ts +5 -4
- package/dist/session/catalog.js +133 -2
- package/dist/session/context-gc-checkpoint.d.ts +148 -0
- package/dist/session/context-gc-checkpoint.js +665 -0
- package/dist/session/context-gc-replacements.d.ts +52 -0
- package/dist/session/context-gc-replacements.js +226 -0
- package/dist/session/context-gc-subagent.d.ts +43 -0
- package/dist/session/context-gc-subagent.js +267 -0
- package/dist/session/jsonl-store.d.ts +42 -4
- package/dist/session/jsonl-store.js +1657 -2
- package/dist/subagent/scheduler.d.ts +35 -2
- package/dist/subagent/scheduler.js +249 -2
- package/dist/subagent/session-controller.d.ts +94 -0
- package/dist/subagent/session-controller.js +675 -0
- package/dist/tools/agent-cancel.js +28 -2
- package/dist/tools/{task.d.ts → agent-message.d.ts} +2 -2
- package/dist/tools/agent-message.js +51 -0
- package/dist/tools/agent-start.js +84 -2
- package/dist/tools/agent-wait.js +53 -2
- package/dist/tools/apply-patch.js +478 -2
- package/dist/tools/code-mode.js +370 -2
- package/dist/tools/codex-image-gen.js +312 -2
- package/dist/tools/exec.js +455 -2
- package/dist/tools/execution-limits.d.ts +1 -0
- package/dist/tools/execution-limits.js +44 -2
- package/dist/tools/local-registry.js +233 -2
- package/dist/tools/local-workspace-tools.js +28 -2
- package/dist/tools/question.js +124 -2
- package/dist/tools/read-context.d.ts +16 -0
- package/dist/tools/read-context.js +85 -0
- package/dist/tools/read.d.ts +13 -0
- package/dist/tools/read.js +294 -2
- package/dist/tools/registry.js +76 -2
- package/dist/tools/runtime-context.d.ts +9 -2
- package/dist/tools/runtime-context.js +1 -2
- package/dist/tools/runtime.js +21 -2
- package/dist/tools/scheduler.js +41 -2
- package/dist/tools/shared/path-resolver.js +133 -2
- package/dist/tools/skill.js +37 -2
- package/dist/tools/todo-read.js +10 -2
- package/dist/tools/todo-write.js +65 -2
- package/dist/tools/todo.js +67 -2
- package/dist/tools/view-image.js +81 -2
- package/dist/tools/web-fetch.js +11 -2
- package/dist/tools/web-search.js +14 -2
- package/package.json +9 -7
- package/README.md +0 -3
- package/dist/runtime/prompt.d.ts +0 -9
- package/dist/runtime/prompt.js +0 -2
- package/dist/tools/shared/secret-redaction.d.ts +0 -4
- package/dist/tools/shared/secret-redaction.js +0 -2
- package/dist/tools/task-cancel.d.ts +0 -7
- package/dist/tools/task-cancel.js +0 -2
- package/dist/tools/task-output.d.ts +0 -7
- package/dist/tools/task-output.js +0 -2
- package/dist/tools/task-tools.d.ts +0 -36
- package/dist/tools/task-tools.js +0 -2
- package/dist/tools/task.js +0 -2
- package/dist/tools/todo-tools.d.ts +0 -25
- package/dist/tools/todo-tools.js +0 -2
|
@@ -1,2 +1,133 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { lstat, realpath } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, posix, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { MarAgentError } from '../../error.js';
|
|
4
|
+
export class WorkspacePathResolver {
|
|
5
|
+
root;
|
|
6
|
+
#restrictPaths;
|
|
7
|
+
#roots;
|
|
8
|
+
#logicalWorkspacePath;
|
|
9
|
+
constructor(root, options = {}) {
|
|
10
|
+
this.root = resolve(root);
|
|
11
|
+
this.#restrictPaths = options.restrictPaths === true;
|
|
12
|
+
this.#roots = [
|
|
13
|
+
{ physicalPath: this.root, writable: options.rootWritable !== false },
|
|
14
|
+
...(options.mounts ?? []).map((mount) => ({
|
|
15
|
+
logicalPath: logicalRoot(mount.logicalPath),
|
|
16
|
+
physicalPath: resolve(mount.physicalPath),
|
|
17
|
+
writable: mount.writable
|
|
18
|
+
}))
|
|
19
|
+
];
|
|
20
|
+
const logicalPaths = this.#roots
|
|
21
|
+
.flatMap((candidate) => (candidate.logicalPath === undefined ? [] : [candidate.logicalPath]))
|
|
22
|
+
.sort();
|
|
23
|
+
if (new Set(logicalPaths).size !== logicalPaths.length)
|
|
24
|
+
throw denied('Logical mount paths must be unique.');
|
|
25
|
+
const logicalMounts = this.#roots.filter((candidate) => candidate.logicalPath !== undefined);
|
|
26
|
+
for (const [index, mount] of logicalMounts.entries()) {
|
|
27
|
+
if (logicalMounts
|
|
28
|
+
.slice(index + 1)
|
|
29
|
+
.some((candidate) => candidate.physicalPath === mount.physicalPath && candidate.writable !== mount.writable))
|
|
30
|
+
throw denied('Logical aliases for one physical path must use the same access mode.');
|
|
31
|
+
}
|
|
32
|
+
this.#logicalWorkspacePath = logicalMounts.find((candidate) => candidate.physicalPath === this.root)?.logicalPath;
|
|
33
|
+
}
|
|
34
|
+
async resolve(input, allowMissing = false, access = 'read') {
|
|
35
|
+
return (await this.resolveWithDisplay(input, allowMissing, access)).physicalPath;
|
|
36
|
+
}
|
|
37
|
+
async resolveWithDisplay(input, allowMissing = false, access = 'read') {
|
|
38
|
+
const selected = this.#select(input);
|
|
39
|
+
if (access === 'write' && !selected.root.writable)
|
|
40
|
+
throw denied('The requested path is read-only.');
|
|
41
|
+
const target = selected.target;
|
|
42
|
+
if (this.#restrictPaths && !withinOrEqual(selected.root.physicalPath, target))
|
|
43
|
+
throw denied('The requested path is outside the allowed roots.');
|
|
44
|
+
try {
|
|
45
|
+
const resolved = await realpath(target);
|
|
46
|
+
await lstat(resolved);
|
|
47
|
+
if (this.#restrictPaths &&
|
|
48
|
+
!withinOrEqual(await realpath(selected.root.physicalPath), resolved))
|
|
49
|
+
throw denied('The requested path escapes the allowed root.');
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof MarAgentError)
|
|
53
|
+
throw error;
|
|
54
|
+
if (!allowMissing)
|
|
55
|
+
throw error;
|
|
56
|
+
const parent = await nearestExistingParent(dirname(target));
|
|
57
|
+
if (this.#restrictPaths &&
|
|
58
|
+
!withinOrEqual(await realpath(selected.root.physicalPath), await realpath(parent)))
|
|
59
|
+
throw denied('The requested path escapes the allowed root.');
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
physicalPath: target,
|
|
63
|
+
displayPath: this.#display(selected)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
#select(input) {
|
|
67
|
+
if (!isAbsolute(input)) {
|
|
68
|
+
if (this.#logicalWorkspacePath !== undefined)
|
|
69
|
+
return this.#selectAbsolute(posix.join(this.#logicalWorkspacePath, input.replaceAll('\\', '/')));
|
|
70
|
+
const root = this.#roots[0];
|
|
71
|
+
return { root, target: resolve(root.physicalPath, input) };
|
|
72
|
+
}
|
|
73
|
+
return this.#selectAbsolute(input);
|
|
74
|
+
}
|
|
75
|
+
#display(selected) {
|
|
76
|
+
if (selected.root.logicalPath === undefined)
|
|
77
|
+
return selected.target;
|
|
78
|
+
const suffix = relative(selected.root.physicalPath, selected.target).split(sep).filter(Boolean);
|
|
79
|
+
return suffix.length === 0
|
|
80
|
+
? selected.root.logicalPath
|
|
81
|
+
: posix.join(selected.root.logicalPath, ...suffix);
|
|
82
|
+
}
|
|
83
|
+
#selectAbsolute(input) {
|
|
84
|
+
const normalized = posix.normalize(input.replaceAll('\\', '/'));
|
|
85
|
+
const mount = this.#roots
|
|
86
|
+
.filter((candidate) => candidate.logicalPath !== undefined)
|
|
87
|
+
.sort((left, right) => right.logicalPath.length - left.logicalPath.length)
|
|
88
|
+
.find((candidate) => normalized === candidate.logicalPath || normalized.startsWith(`${candidate.logicalPath}/`));
|
|
89
|
+
if (mount !== undefined) {
|
|
90
|
+
const suffix = normalized.slice(mount.logicalPath.length).replace(/^\/+/u, '');
|
|
91
|
+
return {
|
|
92
|
+
root: mount,
|
|
93
|
+
target: resolve(mount.physicalPath, ...suffix.split('/').filter(Boolean))
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (this.#restrictPaths)
|
|
97
|
+
throw denied('The requested path is outside the allowed roots.');
|
|
98
|
+
return {
|
|
99
|
+
root: { physicalPath: dirname(resolve(input)), writable: true },
|
|
100
|
+
target: resolve(input)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function logicalRoot(value) {
|
|
105
|
+
const normalized = posix.normalize(value.replaceAll('\\', '/'));
|
|
106
|
+
if (!normalized.startsWith('/') || normalized === '/' || normalized.includes('\0'))
|
|
107
|
+
throw denied('Logical mount path is invalid.');
|
|
108
|
+
return normalized.replace(/\/+$/u, '');
|
|
109
|
+
}
|
|
110
|
+
function withinOrEqual(parent, candidate) {
|
|
111
|
+
const relation = relative(parent, candidate);
|
|
112
|
+
return relation === '' || (relation !== '..' && !relation.startsWith(`..${sep}`));
|
|
113
|
+
}
|
|
114
|
+
async function nearestExistingParent(path) {
|
|
115
|
+
let candidate = path;
|
|
116
|
+
for (;;) {
|
|
117
|
+
try {
|
|
118
|
+
await lstat(candidate);
|
|
119
|
+
return candidate;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error.code !== 'ENOENT')
|
|
123
|
+
throw error;
|
|
124
|
+
const parent = dirname(candidate);
|
|
125
|
+
if (parent === candidate)
|
|
126
|
+
throw error;
|
|
127
|
+
candidate = parent;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function denied(message) {
|
|
132
|
+
return new MarAgentError('MAR_AGENT_FILE_ACCESS_DENIED', message);
|
|
133
|
+
}
|
package/dist/tools/skill.js
CHANGED
|
@@ -1,2 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { MarAgentError } from '../error.js';
|
|
2
|
+
import { agentSkillSource, loadAgentSkill } from '../runtime/instructions.js';
|
|
3
|
+
export const skillToolDefinition = {
|
|
4
|
+
name: 'skill',
|
|
5
|
+
parallelSafety: 'safe',
|
|
6
|
+
description: 'Load a specialized Skill when the task matches one listed in the system prompt. Returns its instructions and source; file-based Skills also include a base directory and resource-path sample. SDK built-ins have no filesystem base. The name must exactly match the advertised catalog.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
name: { type: 'string', minLength: 1, maxLength: 128 }
|
|
11
|
+
},
|
|
12
|
+
required: ['name'],
|
|
13
|
+
additionalProperties: false
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export async function executeSkill(arguments_, context) {
|
|
17
|
+
context.signal.throwIfAborted();
|
|
18
|
+
if (arguments_ === null || typeof arguments_ !== 'object' || Array.isArray(arguments_))
|
|
19
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Skill input must be an object.');
|
|
20
|
+
const input = arguments_;
|
|
21
|
+
if (typeof input.name !== 'string' ||
|
|
22
|
+
input.name.length === 0 ||
|
|
23
|
+
input.name.length > 128 ||
|
|
24
|
+
Object.keys(input).some((key) => key !== 'name'))
|
|
25
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Skill input is invalid.');
|
|
26
|
+
const skill = context.skills.get(input.name);
|
|
27
|
+
if (!skill)
|
|
28
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Skill is not available.');
|
|
29
|
+
const content = await loadAgentSkill(skill);
|
|
30
|
+
if (!content)
|
|
31
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Skill could not be loaded.');
|
|
32
|
+
context.signal.throwIfAborted();
|
|
33
|
+
return {
|
|
34
|
+
content,
|
|
35
|
+
data: { name: skill.name, description: skill.description, source: agentSkillSource(skill) }
|
|
36
|
+
};
|
|
37
|
+
}
|
package/dist/tools/todo-read.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export const todoReadToolDefinition = {
|
|
2
|
+
name: 'todo_read',
|
|
3
|
+
parallelSafety: 'safe',
|
|
4
|
+
description: 'Read this session’s current Todo revision and items. Use before updating when the revision may have changed. Do not read an empty or new Todo merely to decide whether straightforward bounded work needs one.',
|
|
5
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false }
|
|
6
|
+
};
|
|
7
|
+
export function executeTodoRead(context) {
|
|
8
|
+
const plan = context.todo.read();
|
|
9
|
+
return Promise.resolve({ content: JSON.stringify(plan), data: plan });
|
|
10
|
+
}
|
package/dist/tools/todo-write.js
CHANGED
|
@@ -1,2 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { MarAgentError } from '../error.js';
|
|
2
|
+
import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
|
|
3
|
+
export const todoWriteToolDefinition = {
|
|
4
|
+
name: 'todo_write',
|
|
5
|
+
parallelSafety: 'serial',
|
|
6
|
+
description: 'Replace this session’s entire Todo list using optimistic expectedRevision. Preserve stable IDs and statuses only when extending the same objective. For a materially different objective, include only the new objective’s items. Do not retain completed items from an earlier objective; when all current items are completed, default a substantive new request to replacement unless the user explicitly asks to continue, extend, verify, or follow up. Use only after Todo has been chosen for coordination-heavy work; straightforward bounded work does not need it. Keep at most one in_progress item and update statuses after meaningful stage changes. When another substantive tool call is already ready and does not depend on the Todo result, include both in the same model response instead of spending a response only for Todo bookkeeping.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
expectedRevision: { type: 'integer' },
|
|
11
|
+
items: {
|
|
12
|
+
type: 'array',
|
|
13
|
+
maxItems: TOOL_EXECUTION_LIMITS.todoMaxItems,
|
|
14
|
+
items: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
id: { type: 'string' },
|
|
18
|
+
step: { type: 'string', maxLength: TOOL_EXECUTION_LIMITS.todoMaxTextCharacters },
|
|
19
|
+
status: { enum: ['pending', 'in_progress', 'completed'] }
|
|
20
|
+
},
|
|
21
|
+
required: ['id', 'step', 'status'],
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
required: ['expectedRevision', 'items'],
|
|
27
|
+
additionalProperties: false
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export async function executeTodoWrite(arguments_, context) {
|
|
31
|
+
const value = parseTodoWriteInput(arguments_);
|
|
32
|
+
const todo = context.todo.write(value);
|
|
33
|
+
await context.emit({ type: 'todo.updated', revision: todo.revision, items: todo.items });
|
|
34
|
+
return { content: JSON.stringify(todo), data: todo };
|
|
35
|
+
}
|
|
36
|
+
function parseTodoWriteInput(value) {
|
|
37
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
38
|
+
return invalid();
|
|
39
|
+
const input = value;
|
|
40
|
+
if (Object.keys(input).some((key) => !['expectedRevision', 'items'].includes(key)) ||
|
|
41
|
+
!Number.isInteger(input.expectedRevision) ||
|
|
42
|
+
input.expectedRevision < 0 ||
|
|
43
|
+
!Array.isArray(input.items) ||
|
|
44
|
+
input.items.length > TOOL_EXECUTION_LIMITS.todoMaxItems)
|
|
45
|
+
return invalid();
|
|
46
|
+
const items = input.items.map((item) => {
|
|
47
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
48
|
+
return invalid();
|
|
49
|
+
const record = item;
|
|
50
|
+
if (Object.keys(record).some((key) => !['id', 'step', 'status'].includes(key)) ||
|
|
51
|
+
typeof record.id !== 'string' ||
|
|
52
|
+
record.id.trim().length === 0 ||
|
|
53
|
+
record.id.length > 128 ||
|
|
54
|
+
typeof record.step !== 'string' ||
|
|
55
|
+
record.step.trim().length === 0 ||
|
|
56
|
+
record.step.length > TOOL_EXECUTION_LIMITS.todoMaxTextCharacters ||
|
|
57
|
+
!['pending', 'in_progress', 'completed'].includes(String(record.status)))
|
|
58
|
+
return invalid();
|
|
59
|
+
return { id: record.id, step: record.step, status: record.status };
|
|
60
|
+
});
|
|
61
|
+
return { expectedRevision: input.expectedRevision, items };
|
|
62
|
+
}
|
|
63
|
+
function invalid() {
|
|
64
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Todo input is invalid.');
|
|
65
|
+
}
|
package/dist/tools/todo.js
CHANGED
|
@@ -1,2 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { MarAgentError } from '../error.js';
|
|
2
|
+
export class TodoStore {
|
|
3
|
+
#revision = 0;
|
|
4
|
+
#items = [];
|
|
5
|
+
static restore(snapshot) {
|
|
6
|
+
if (!Number.isSafeInteger(snapshot.revision) || snapshot.revision < 0)
|
|
7
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Todo revision is invalid.');
|
|
8
|
+
const todo = new TodoStore();
|
|
9
|
+
todo.#revision = snapshot.revision;
|
|
10
|
+
todo.#items = normalizeTodoItems(snapshot.items);
|
|
11
|
+
return todo;
|
|
12
|
+
}
|
|
13
|
+
read() {
|
|
14
|
+
return { revision: this.#revision, items: this.#items.map((item) => ({ ...item })) };
|
|
15
|
+
}
|
|
16
|
+
write(input) {
|
|
17
|
+
if (input.expectedRevision !== this.#revision)
|
|
18
|
+
throw new MarAgentError('MAR_AGENT_TODO_CONFLICT', 'Todo revision is stale.', {
|
|
19
|
+
details: { revision: this.#revision }
|
|
20
|
+
});
|
|
21
|
+
this.#items = normalizeTodoItems(input.items);
|
|
22
|
+
this.#revision++;
|
|
23
|
+
return this.read();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function todoItemsFromSnapshot(snapshot) {
|
|
27
|
+
if (snapshot === null || typeof snapshot !== 'object' || Array.isArray(snapshot))
|
|
28
|
+
return undefined;
|
|
29
|
+
const items = snapshot.items;
|
|
30
|
+
if (!Array.isArray(items))
|
|
31
|
+
return undefined;
|
|
32
|
+
try {
|
|
33
|
+
return normalizeTodoItems(items);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof MarAgentError && error.code === 'MAR_AGENT_TOOL_INPUT_INVALID')
|
|
37
|
+
return undefined;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function restoreTodoStore(snapshots) {
|
|
42
|
+
const todo = new TodoStore();
|
|
43
|
+
for (const snapshot of snapshots) {
|
|
44
|
+
const items = todoItemsFromSnapshot(snapshot);
|
|
45
|
+
if (!items)
|
|
46
|
+
continue;
|
|
47
|
+
todo.write({
|
|
48
|
+
expectedRevision: todo.read().revision,
|
|
49
|
+
items
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return todo;
|
|
53
|
+
}
|
|
54
|
+
function normalizeTodoItems(items) {
|
|
55
|
+
if (items.filter((item) => item?.status === 'in_progress').length > 1 ||
|
|
56
|
+
new Set(items.map((item) => item?.id)).size !== items.length ||
|
|
57
|
+
items.some((item) => item === null ||
|
|
58
|
+
typeof item !== 'object' ||
|
|
59
|
+
Array.isArray(item) ||
|
|
60
|
+
typeof item.id !== 'string' ||
|
|
61
|
+
!item.id ||
|
|
62
|
+
typeof item.step !== 'string' ||
|
|
63
|
+
!item.step.trim() ||
|
|
64
|
+
!['pending', 'in_progress', 'completed'].includes(item.status)))
|
|
65
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Todo items are invalid.');
|
|
66
|
+
return items.map((item) => ({ ...item, step: item.step.trim() }));
|
|
67
|
+
}
|
package/dist/tools/view-image.js
CHANGED
|
@@ -1,2 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { open } from 'node:fs/promises';
|
|
3
|
+
import { MarAgentError } from '../error.js';
|
|
4
|
+
import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
|
|
5
|
+
export const viewImageToolDefinition = {
|
|
6
|
+
name: 'view_image',
|
|
7
|
+
parallelSafety: 'safe',
|
|
8
|
+
description: `Read one known local PNG, JPEG, or WebP image and provide its actual pixels to the vision-capable model. Relative paths use the workspace; absolute paths are allowed. The file must be a regular file no larger than ${TOOL_EXECUTION_LIMITS.viewImageMaxBytes} bytes. MIME is detected from file content, not its extension. Use this instead of installing OCR or converting the image through exec.`,
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
path: { type: 'string', description: 'Relative workspace path or absolute image path.' }
|
|
13
|
+
},
|
|
14
|
+
required: ['path'],
|
|
15
|
+
additionalProperties: false
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export class ViewImageTool {
|
|
19
|
+
paths;
|
|
20
|
+
constructor(paths) {
|
|
21
|
+
this.paths = paths;
|
|
22
|
+
}
|
|
23
|
+
async execute(arguments_, signal) {
|
|
24
|
+
const input = parseViewImageInput(arguments_);
|
|
25
|
+
signal?.throwIfAborted();
|
|
26
|
+
const path = await this.paths.resolve(input.path);
|
|
27
|
+
const handle = await open(path, 'r');
|
|
28
|
+
try {
|
|
29
|
+
const stat = await handle.stat();
|
|
30
|
+
if (!stat.isFile())
|
|
31
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Path is not a regular file.');
|
|
32
|
+
if (stat.size > TOOL_EXECUTION_LIMITS.viewImageMaxBytes)
|
|
33
|
+
throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', `Image exceeds the ${TOOL_EXECUTION_LIMITS.viewImageMaxBytes}-byte limit.`);
|
|
34
|
+
signal?.throwIfAborted();
|
|
35
|
+
const data = await handle.readFile();
|
|
36
|
+
signal?.throwIfAborted();
|
|
37
|
+
if (data.byteLength > TOOL_EXECUTION_LIMITS.viewImageMaxBytes)
|
|
38
|
+
throw new MarAgentError('MAR_AGENT_TOOL_OUTPUT_LIMIT', `Image exceeds the ${TOOL_EXECUTION_LIMITS.viewImageMaxBytes}-byte limit.`);
|
|
39
|
+
const mimeType = detectImageMime(data);
|
|
40
|
+
const metadata = {
|
|
41
|
+
path: input.path,
|
|
42
|
+
mimeType,
|
|
43
|
+
bytes: data.byteLength,
|
|
44
|
+
sha256: createHash('sha256').update(data).digest('hex')
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
content: `Viewed image ${input.path} (${mimeType}, ${data.byteLength} bytes, sha256 ${metadata.sha256}).`,
|
|
48
|
+
data: metadata,
|
|
49
|
+
images: [{ mimeType, dataBase64: data.toString('base64') }]
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
await handle.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function parseViewImageInput(value) {
|
|
58
|
+
if (!value || typeof value !== 'object')
|
|
59
|
+
return invalidInput();
|
|
60
|
+
const input = value;
|
|
61
|
+
if (Object.keys(input).some((key) => key !== 'path') ||
|
|
62
|
+
typeof input.path !== 'string' ||
|
|
63
|
+
input.path.length === 0)
|
|
64
|
+
return invalidInput();
|
|
65
|
+
return { path: input.path };
|
|
66
|
+
}
|
|
67
|
+
export function detectImageMime(data) {
|
|
68
|
+
if (data.length >= 8 &&
|
|
69
|
+
data.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
|
|
70
|
+
return 'image/png';
|
|
71
|
+
if (data.length >= 3 && data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff)
|
|
72
|
+
return 'image/jpeg';
|
|
73
|
+
if (data.length >= 12 &&
|
|
74
|
+
data.subarray(0, 4).toString('ascii') === 'RIFF' &&
|
|
75
|
+
data.subarray(8, 12).toString('ascii') === 'WEBP')
|
|
76
|
+
return 'image/webp';
|
|
77
|
+
throw new MarAgentError('MAR_AGENT_IMAGE_FORMAT_UNSUPPORTED', 'Only PNG, JPEG, and WebP images are supported.');
|
|
78
|
+
}
|
|
79
|
+
function invalidInput() {
|
|
80
|
+
throw new MarAgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'View image input is invalid.');
|
|
81
|
+
}
|
package/dist/tools/web-fetch.js
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export const webFetchToolDefinition = {
|
|
2
|
+
name: 'web_fetch',
|
|
3
|
+
parallelSafety: 'safe',
|
|
4
|
+
description: 'Fetch a specific HTTP or HTTPS URL, including explicitly permitted intranet URLs.',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: { url: { type: 'string' } },
|
|
8
|
+
required: ['url'],
|
|
9
|
+
additionalProperties: false
|
|
10
|
+
}
|
|
11
|
+
};
|
package/dist/tools/web-search.js
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export const webSearchToolDefinition = {
|
|
2
|
+
name: 'web_search',
|
|
3
|
+
parallelSafety: 'safe',
|
|
4
|
+
description: 'Search the web for current information. Use web_fetch to open a specific result.',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
query: { type: 'string', description: 'Search query.' },
|
|
9
|
+
maxResults: { type: 'integer', minimum: 1, maximum: 10, default: 8 }
|
|
10
|
+
},
|
|
11
|
+
required: ['query'],
|
|
12
|
+
additionalProperties: false
|
|
13
|
+
}
|
|
14
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.64",
|
|
4
4
|
"description": "Embeddable MAR coding agent SDK and CLI.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"files": [
|
|
7
8
|
"dist/**/*.js",
|
|
8
9
|
"dist/**/*.d.ts",
|
|
9
|
-
"
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"NOTICE"
|
|
10
12
|
],
|
|
11
13
|
"exports": {
|
|
12
14
|
".": {
|
|
@@ -18,26 +20,26 @@
|
|
|
18
20
|
"maragent": "./dist/cli/main.js"
|
|
19
21
|
},
|
|
20
22
|
"engines": {
|
|
21
|
-
"node": ">=22.13
|
|
23
|
+
"node": ">=22.13"
|
|
22
24
|
},
|
|
23
25
|
"publishConfig": {
|
|
24
26
|
"access": "public"
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
|
-
"tiktoken": "1.0.22",
|
|
28
29
|
"undici": "7.29.0",
|
|
29
30
|
"ws": "8.21.3",
|
|
30
31
|
"zod": "4.4.3"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
|
-
"@types/ws": "8.18.1"
|
|
34
|
+
"@types/ws": "8.18.1",
|
|
35
|
+
"vitest": "^4.0.14"
|
|
34
36
|
},
|
|
35
37
|
"scripts": {
|
|
36
|
-
"build": "tsc -p tsconfig.json",
|
|
38
|
+
"build": "node ../scripts/clean-build-output.mjs dist && tsc -p tsconfig.json",
|
|
37
39
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
40
|
"test:unit": "vitest run --config vitest.config.ts test/unit",
|
|
39
41
|
"test:performance": "vitest run --config vitest.performance.config.ts",
|
|
40
|
-
"test:memory": "node --expose-gc
|
|
42
|
+
"test:memory": "node --expose-gc ./node_modules/vitest/vitest.mjs run --config vitest.memory.config.ts",
|
|
41
43
|
"test:e2e": "vitest run --config vitest.config.ts test/e2e --no-file-parallelism",
|
|
42
44
|
"test": "pnpm test:unit",
|
|
43
45
|
"lint": "prettier --check ."
|
package/README.md
DELETED
package/dist/runtime/prompt.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ExecutionMode } from '../sdk/types.js';
|
|
2
|
-
export declare const MAR_AGENT_PROMPT_VERSION = "1.2";
|
|
3
|
-
export declare function buildSystemPrompt(input: {
|
|
4
|
-
mode: ExecutionMode;
|
|
5
|
-
platform: string;
|
|
6
|
-
workspace: string;
|
|
7
|
-
tools: readonly string[];
|
|
8
|
-
extension?: string | undefined;
|
|
9
|
-
}): string;
|