@orkestrel/agent 0.0.2 → 0.0.4
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/src/core/index.cjs +14 -315
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +36 -319
- package/dist/src/core/index.d.ts +36 -319
- package/dist/src/core/index.js +16 -312
- package/dist/src/core/index.js.map +1 -1
- package/package.json +2 -2
package/dist/src/core/index.cjs
CHANGED
|
@@ -120,64 +120,6 @@ var EXTENSION_TO_LANGUAGE = Object.freeze({
|
|
|
120
120
|
svg: "xml",
|
|
121
121
|
txt: "text"
|
|
122
122
|
});
|
|
123
|
-
/**
|
|
124
|
-
* The name {@link import('./factories.js').createWorkspaceTool} advertises by default — the key a
|
|
125
|
-
* model calls and the {@link import('./types.js').ToolManagerInterface} registers under.
|
|
126
|
-
*/
|
|
127
|
-
var WORKSPACE_TOOL_NAME = "workspace";
|
|
128
|
-
/**
|
|
129
|
-
* A valid {@link WorkspaceOperation} object — the canonical example embedded VERBATIM in
|
|
130
|
-
* {@link WORKSPACE_TOOL_DESCRIPTION} and pinned by a parity test (it must satisfy the compiled
|
|
131
|
-
* contract's `is`), so the doc example can never drift from a real, contract-valid operation.
|
|
132
|
-
*
|
|
133
|
-
* @remarks
|
|
134
|
-
* A `'write'` op (the most common authoring action): create or overwrite `notes.txt` with
|
|
135
|
-
* `hello`. Frozen so it cannot be mutated in place.
|
|
136
|
-
*/
|
|
137
|
-
var WORKSPACE_TOOL_EXAMPLE = Object.freeze({
|
|
138
|
-
operation: "write",
|
|
139
|
-
path: "notes.txt",
|
|
140
|
-
content: "hello"
|
|
141
|
-
});
|
|
142
|
-
/**
|
|
143
|
-
* The DESCRIPTION {@link import('./factories.js').createWorkspaceTool} advertises — a multi-line
|
|
144
|
-
* guide that teaches a small model how to drive a workspace through the single `operation`-keyed
|
|
145
|
-
* tool.
|
|
146
|
-
*
|
|
147
|
-
* @remarks
|
|
148
|
-
* Mirrors the analogous `WORKFLOW_TOOL_DESCRIPTION` in `@orkestrel/workflow`'s teaching style:
|
|
149
|
-
* names the `operation` discriminant field, enumerates all 13 operations with their FLAT fields,
|
|
150
|
-
* gives a worked example for the common ones (read / write / search / replace / splice), and embeds
|
|
151
|
-
* {@link WORKSPACE_TOOL_EXAMPLE} verbatim (pinned by a parity test). The range edit is the FLAT
|
|
152
|
-
* `'splice'` op — four positive-integer caret components, NOT a nested range — the ergonomic lever
|
|
153
|
-
* a 2B model can fill. The edit / read ops target the ACTIVE workspace (one is auto-created on the
|
|
154
|
-
* first edit when none is active); the `workspaces` / `switch` ops let the model move between
|
|
155
|
-
* workspaces.
|
|
156
|
-
*/
|
|
157
|
-
var WORKSPACE_TOOL_DESCRIPTION = [
|
|
158
|
-
"Read and edit files in a workspace. Every call is ONE operation, chosen by the \"operation\" field.",
|
|
159
|
-
"All file operations act on the ACTIVE workspace; use \"workspaces\" then \"switch\" to move between workspaces.",
|
|
160
|
-
"",
|
|
161
|
-
"Operations (each takes the fields listed):",
|
|
162
|
-
"- read { \"operation\": \"read\", \"path\": \"<file>\" } — return the file's text.",
|
|
163
|
-
"- list { \"operation\": \"list\" } — list every file in the active workspace (path, state, size, lines, kind).",
|
|
164
|
-
"- has { \"operation\": \"has\", \"path\": \"<file>\" } — whether the file exists.",
|
|
165
|
-
"- search { \"operation\": \"search\", \"query\": \"<text>\", \"regex\"?: bool, \"exact\"?: bool, \"limit\"?: int } — find lines matching the query across all files.",
|
|
166
|
-
"- replace { \"operation\": \"replace\", \"query\": \"<text>\", \"replacement\": \"<text>\", \"regex\"?: bool, \"exact\"?: bool, \"limit\"?: int } — replace matches across all files.",
|
|
167
|
-
"- write { \"operation\": \"write\", \"path\": \"<file>\", \"content\": \"<text>\" } — create or overwrite the whole file.",
|
|
168
|
-
"- splice { \"operation\": \"splice\", \"path\": \"<file>\", \"content\": \"<text>\", \"fromLine\": int, \"fromColumn\": int, \"toLine\": int, \"toColumn\": int } — replace a 1-based range (from inclusive, to exclusive) with content.",
|
|
169
|
-
"- prepend { \"operation\": \"prepend\", \"path\": \"<file>\", \"content\": \"<text>\" } — add content to the start of the file.",
|
|
170
|
-
"- append { \"operation\": \"append\", \"path\": \"<file>\", \"content\": \"<text>\" } — add content to the end of the file.",
|
|
171
|
-
"- move { \"operation\": \"move\", \"from\": \"<file>\", \"to\": \"<file>\" } — rename / move a file.",
|
|
172
|
-
"- remove { \"operation\": \"remove\", \"path\": \"<file>\" } — delete a file.",
|
|
173
|
-
"- workspaces { \"operation\": \"workspaces\" } — list the workspaces you can switch between (each id, file count, active).",
|
|
174
|
-
"- switch { \"operation\": \"switch\", \"id\": \"<id>\" } — make the workspace with that id active (ids come from \"workspaces\").",
|
|
175
|
-
"",
|
|
176
|
-
"Notes: lines and columns are 1-based (column 1 is the first character). \"regex\" defaults to false (a literal substring), \"exact\" defaults to true (case-sensitive). \"search\"/\"replace\"/\"splice\" act only on text files. Editing with no active workspace auto-creates one.",
|
|
177
|
-
"",
|
|
178
|
-
"Example — write a file:",
|
|
179
|
-
JSON.stringify(WORKSPACE_TOOL_EXAMPLE)
|
|
180
|
-
].join("\n");
|
|
181
123
|
//#endregion
|
|
182
124
|
//#region src/core/errors.ts
|
|
183
125
|
/**
|
|
@@ -337,93 +279,6 @@ function isWorkspaceError(value) {
|
|
|
337
279
|
return value instanceof WorkspaceError;
|
|
338
280
|
}
|
|
339
281
|
//#endregion
|
|
340
|
-
//#region src/core/shapers.ts
|
|
341
|
-
/**
|
|
342
|
-
* The shape of a {@link import('./types.js').WorkspaceOperation} — a descriptive tagged union over
|
|
343
|
-
* the 13 workspace edit / read / navigation operations, discriminated by the `operation` literal
|
|
344
|
-
* (never a bare `kind`; AGENTS §4.8). Each variant leads with its `operation` discriminant then its
|
|
345
|
-
* FLAT fields, every field via `stringShape` / `optionalShape` / `integerShape({ min: 1 })` /
|
|
346
|
-
* `booleanShape`, each carrying a strong field-level `description`.
|
|
347
|
-
*
|
|
348
|
-
* @remarks
|
|
349
|
-
* The union compiles to an `anyOf` JSON Schema + a `unionOf` guard + a first-match parser
|
|
350
|
-
* automatically ({@link import('./factories.js').createWorkspaceTool} types the result to the
|
|
351
|
-
* hand-written `WorkspaceOperation`). `limit` and the four `'splice'` caret components are POSITIVE
|
|
352
|
-
* integers (`integerShape({ min: 1 })`); `regex` / `exact` are `optionalShape(booleanShape(...))`.
|
|
353
|
-
* The two REGISTRY arms — `workspaces` (list the workspaces the model can move between) and `switch`
|
|
354
|
-
* (re-point the active one by `id`) — let a model DISCOVER then CHOOSE which workspace the edit /
|
|
355
|
-
* read arms target. The descriptions are the small-model ergonomic lever — they ride into the
|
|
356
|
-
* advertised schema so a model authoring an operation gets per-field guidance.
|
|
357
|
-
*/
|
|
358
|
-
var workspaceToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.objectShape)({
|
|
359
|
-
operation: (0, _orkestrel_contract.literalShape)(["read"], { description: "Read a whole text file's text by path." }),
|
|
360
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path of the file to read." })
|
|
361
|
-
}), (0, _orkestrel_contract.objectShape)({ operation: (0, _orkestrel_contract.literalShape)(["list"], { description: "List every file in the workspace." }) }), (0, _orkestrel_contract.objectShape)({
|
|
362
|
-
operation: (0, _orkestrel_contract.literalShape)(["has"], { description: "Check whether a file exists at the path." }),
|
|
363
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path to check for." })
|
|
364
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
365
|
-
operation: (0, _orkestrel_contract.literalShape)(["search"], { description: "Search every text file for a query, returning each hit." }),
|
|
366
|
-
query: (0, _orkestrel_contract.stringShape)({ description: "The text (or regular-expression source) to search for." }),
|
|
367
|
-
regex: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.booleanShape)({ description: "Treat the query as a regular expression. Defaults to false (a literal substring)." })),
|
|
368
|
-
exact: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.booleanShape)({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
|
|
369
|
-
limit: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
|
|
370
|
-
min: 1,
|
|
371
|
-
description: "Stop after this many matches across all files. Omitted means unlimited."
|
|
372
|
-
}))
|
|
373
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
374
|
-
operation: (0, _orkestrel_contract.literalShape)(["replace"], { description: "Replace a query with a replacement across every text file." }),
|
|
375
|
-
query: (0, _orkestrel_contract.stringShape)({ description: "The text (or regular-expression source) to replace." }),
|
|
376
|
-
replacement: (0, _orkestrel_contract.stringShape)({ description: "The text to substitute for each match." }),
|
|
377
|
-
regex: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.booleanShape)({ description: "Treat the query as a regular expression. Defaults to false (a literal substring)." })),
|
|
378
|
-
exact: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.booleanShape)({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
|
|
379
|
-
limit: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
|
|
380
|
-
min: 1,
|
|
381
|
-
description: "Stop after this many replacements across all files. Omitted means unlimited."
|
|
382
|
-
}))
|
|
383
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
384
|
-
operation: (0, _orkestrel_contract.literalShape)(["write"], { description: "Create or overwrite a whole file with content." }),
|
|
385
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path of the file to write." }),
|
|
386
|
-
content: (0, _orkestrel_contract.stringShape)({ description: "The full new contents of the file." })
|
|
387
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
388
|
-
operation: (0, _orkestrel_contract.literalShape)(["splice"], { description: "Replace a 1-based range of an existing text file (from inclusive, to exclusive) with content." }),
|
|
389
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path of the text file to edit." }),
|
|
390
|
-
content: (0, _orkestrel_contract.stringShape)({ description: "The text to splice in place of the range." }),
|
|
391
|
-
fromLine: (0, _orkestrel_contract.integerShape)({
|
|
392
|
-
min: 1,
|
|
393
|
-
description: "The 1-based start line of the range (inclusive)."
|
|
394
|
-
}),
|
|
395
|
-
fromColumn: (0, _orkestrel_contract.integerShape)({
|
|
396
|
-
min: 1,
|
|
397
|
-
description: "The 1-based start column of the range (inclusive; column 1 is the first character)."
|
|
398
|
-
}),
|
|
399
|
-
toLine: (0, _orkestrel_contract.integerShape)({
|
|
400
|
-
min: 1,
|
|
401
|
-
description: "The 1-based end line of the range (exclusive)."
|
|
402
|
-
}),
|
|
403
|
-
toColumn: (0, _orkestrel_contract.integerShape)({
|
|
404
|
-
min: 1,
|
|
405
|
-
description: "The 1-based end column of the range (exclusive)."
|
|
406
|
-
})
|
|
407
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
408
|
-
operation: (0, _orkestrel_contract.literalShape)(["prepend"], { description: "Add content to the start of a file (creating it when absent)." }),
|
|
409
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path of the file to prepend to." }),
|
|
410
|
-
content: (0, _orkestrel_contract.stringShape)({ description: "The text to add at the start of the file." })
|
|
411
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
412
|
-
operation: (0, _orkestrel_contract.literalShape)(["append"], { description: "Add content to the end of a file (creating it when absent)." }),
|
|
413
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path of the file to append to." }),
|
|
414
|
-
content: (0, _orkestrel_contract.stringShape)({ description: "The text to add at the end of the file." })
|
|
415
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
416
|
-
operation: (0, _orkestrel_contract.literalShape)(["move"], { description: "Rename or move a file (overwriting an occupied target)." }),
|
|
417
|
-
from: (0, _orkestrel_contract.stringShape)({ description: "The current path of the file." }),
|
|
418
|
-
to: (0, _orkestrel_contract.stringShape)({ description: "The new path for the file." })
|
|
419
|
-
}), (0, _orkestrel_contract.objectShape)({
|
|
420
|
-
operation: (0, _orkestrel_contract.literalShape)(["remove"], { description: "Delete a file from the workspace." }),
|
|
421
|
-
path: (0, _orkestrel_contract.stringShape)({ description: "The path of the file to remove." })
|
|
422
|
-
}), (0, _orkestrel_contract.objectShape)({ operation: (0, _orkestrel_contract.literalShape)(["workspaces"], { description: "List the workspaces you can move between (each id, file count, and whether it is active), so you can pick an id to switch to." }) }), (0, _orkestrel_contract.objectShape)({
|
|
423
|
-
operation: (0, _orkestrel_contract.literalShape)(["switch"], { description: "Switch the active workspace to the one with this id (get ids from the \"workspaces\" operation). Edit and read operations then target it." }),
|
|
424
|
-
id: (0, _orkestrel_contract.stringShape)({ description: "The id of the workspace to make active (from the \"workspaces\" listing)." })
|
|
425
|
-
}));
|
|
426
|
-
//#endregion
|
|
427
282
|
//#region src/core/conversations/Conversation.ts
|
|
428
283
|
/**
|
|
429
284
|
* A conversation grouping messages ABOVE a flat message store — a live uncompacted tail it
|
|
@@ -1372,10 +1227,10 @@ function spliceRange(text, range, replacement) {
|
|
|
1372
1227
|
return text.slice(0, start) + replacement + text.slice(end);
|
|
1373
1228
|
}
|
|
1374
1229
|
/**
|
|
1375
|
-
* Assemble a 1-based nested {@link Range} from
|
|
1376
|
-
*
|
|
1377
|
-
*
|
|
1378
|
-
*
|
|
1230
|
+
* Assemble a 1-based nested {@link Range} from four FLAT caret integers — the bridge a
|
|
1231
|
+
* workspace-editing tool (see `@orkestrel/tool`) uses from its small-model FLAT surface back to the
|
|
1232
|
+
* nested {@link Range} the {@link import('./workspaces/Workspace.js').Workspace} edit surface
|
|
1233
|
+
* speaks. Published as a public helper for that consumer.
|
|
1379
1234
|
*
|
|
1380
1235
|
* @remarks
|
|
1381
1236
|
* Pairs `(fromLine, fromColumn)` into `start` and `(toLine, toColumn)` into `end` verbatim — a pure
|
|
@@ -1604,7 +1459,9 @@ var InstructionManager = class {
|
|
|
1604
1459
|
* (last write wins). `count` is the map size, `tool(name)` looks one up, `tools()`
|
|
1605
1460
|
* lists them in insertion order, and `definitions()` maps each to a plain
|
|
1606
1461
|
* {@link ToolDefinition} (`name` / `description?` / `parameters?`, the `execute`
|
|
1607
|
-
* handler stripped) — exactly what a provider advertises to the model
|
|
1462
|
+
* handler stripped) — exactly what a provider advertises to the model, with
|
|
1463
|
+
* `tool.summary ?? tool.description` advertised as `description` (a lean `summary`
|
|
1464
|
+
* stands in for the full text when set).
|
|
1608
1465
|
* - **Per-call error isolation (the load-bearing part).** `execute` resolves a
|
|
1609
1466
|
* {@link ToolCall}'s tool by name and ALWAYS resolves a {@link ToolResult}: an
|
|
1610
1467
|
* unknown name → `{ id, name, error: 'tool not found: <name>' }`; a successful run →
|
|
@@ -1685,7 +1542,8 @@ var ToolManager = class {
|
|
|
1685
1542
|
}
|
|
1686
1543
|
#definition(tool) {
|
|
1687
1544
|
const definition = { name: tool.name };
|
|
1688
|
-
|
|
1545
|
+
const description = tool.summary ?? tool.description;
|
|
1546
|
+
if (description !== void 0) definition.description = description;
|
|
1689
1547
|
if (tool.parameters !== void 0) definition.parameters = tool.parameters;
|
|
1690
1548
|
return definition;
|
|
1691
1549
|
}
|
|
@@ -2709,11 +2567,13 @@ var AgentRegistry = class {
|
|
|
2709
2567
|
#tools;
|
|
2710
2568
|
#authorities;
|
|
2711
2569
|
#schedulers;
|
|
2570
|
+
#store;
|
|
2712
2571
|
constructor(options) {
|
|
2713
2572
|
this.#providers = new Map(Object.entries(options.providers));
|
|
2714
2573
|
this.#tools = new Map(Object.entries(options.tools ?? {}));
|
|
2715
2574
|
this.#authorities = new Map(Object.entries(options.authorities ?? {}));
|
|
2716
2575
|
this.#schedulers = new Map(Object.entries(options.schedulers ?? {}));
|
|
2576
|
+
this.#store = options.store;
|
|
2717
2577
|
}
|
|
2718
2578
|
provider(name) {
|
|
2719
2579
|
return this.#resolve(this.#providers, "provider", name);
|
|
@@ -2741,6 +2601,7 @@ var AgentRegistry = class {
|
|
|
2741
2601
|
budget: this.#budget(input.budget),
|
|
2742
2602
|
authority: input.authority === void 0 ? void 0 : this.authority(input.authority),
|
|
2743
2603
|
scheduler: input.scheduler === void 0 ? void 0 : this.scheduler(input.scheduler),
|
|
2604
|
+
conversations: this.#store === void 0 ? void 0 : new ConversationManager({ store: this.#store }),
|
|
2744
2605
|
signal
|
|
2745
2606
|
};
|
|
2746
2607
|
}
|
|
@@ -3225,11 +3086,13 @@ var ThinkSplitter = class {
|
|
|
3225
3086
|
var Tool = class {
|
|
3226
3087
|
name;
|
|
3227
3088
|
description;
|
|
3089
|
+
summary;
|
|
3228
3090
|
parameters;
|
|
3229
3091
|
#execute;
|
|
3230
3092
|
constructor(options) {
|
|
3231
3093
|
this.name = options.name;
|
|
3232
3094
|
this.description = options.description;
|
|
3095
|
+
this.summary = options.summary;
|
|
3233
3096
|
this.parameters = options.parameters;
|
|
3234
3097
|
this.#execute = options.execute;
|
|
3235
3098
|
}
|
|
@@ -4150,165 +4013,6 @@ function createDatabaseWorkspaceStore(driver = (0, _orkestrel_database.createMem
|
|
|
4150
4013
|
function createWorkspaceManager(options) {
|
|
4151
4014
|
return new WorkspaceManager(options);
|
|
4152
4015
|
}
|
|
4153
|
-
/**
|
|
4154
|
-
* Wrap a {@link WorkspaceManagerInterface} as an LLM-callable {@link ToolInterface} — it ADVERTISES
|
|
4155
|
-
* the `operation`-discriminated 13-op union ({@link import('./shapers.js').workspaceToolShape}) as
|
|
4156
|
-
* its `parameters`, and its handler PARSES the model-supplied args against that contract and
|
|
4157
|
-
* DISPATCHES the matched operation against the manager's ACTIVE workspace (the registry ops drive
|
|
4158
|
-
* the manager itself), returning the plain result (throwing a typed {@link WorkspaceError} on
|
|
4159
|
-
* failure).
|
|
4160
|
-
*
|
|
4161
|
-
* @remarks
|
|
4162
|
-
* MANAGER-DRIVEN: every edit / read op (read / list / has / search / replace / write / splice /
|
|
4163
|
-
* prepend / append / move / remove) targets `manager.active`, so the model edits whichever workspace
|
|
4164
|
-
* is active and a host can re-point it ({@link WorkspaceManagerInterface.switch}) between turns —
|
|
4165
|
-
* wire the agent's registry in via `createWorkspaceTool(agent.context.workspaces)`. Two REGISTRY ops
|
|
4166
|
-
* make the model self-sufficient: `workspaces` LISTS the registered workspaces (each
|
|
4167
|
-
* `{ id, files, active }`) so it can discover an id, and `switch` re-points the active workspace by
|
|
4168
|
-
* id (lenient — an unknown id is a no-op reporting `switched: false`, never a throw).
|
|
4169
|
-
*
|
|
4170
|
-
* NO-ACTIVE RULE (the ergonomic seam): a WRITING op (write / splice / prepend / append / move /
|
|
4171
|
-
* remove / replace) run when `manager.active` is `undefined` AUTO-CREATES + activates a default
|
|
4172
|
-
* workspace (`manager.add()`) so the model can just start writing; a pure-READ op (read / list / has
|
|
4173
|
-
* / search) against no active workspace returns the EMPTY result (`undefined` / `[]` / `false`),
|
|
4174
|
-
* never creating one and never throwing.
|
|
4175
|
-
*
|
|
4176
|
-
* A plain {@link ToolManagerInterface}-compatible tool (so `createMCPServer` / `createMCPRoutes`
|
|
4177
|
-
* expose it over MCP for free — nothing MCP is wired here), built as a factory + dispatch closure
|
|
4178
|
-
* exactly like the analogous `createWorkflowTool` in `@orkestrel/workflow` (NOT a class). The
|
|
4179
|
-
* contract is compiled ONCE and its JSON Schema is narrowed to the open
|
|
4180
|
-
* `Readonly<Record<string, unknown>>` a tool advertises via the shared
|
|
4181
|
-
* {@link schemaToParameters} contracts helper, never an assertion
|
|
4182
|
-
* (§14) — a compiled contract schema is always a record, so it passes; the `undefined` fallback only
|
|
4183
|
-
* satisfies the type's optionality.
|
|
4184
|
-
*
|
|
4185
|
-
* The handler conforms to the universal tool-handler contract (AGENTS §14): it `contract.parse`s the
|
|
4186
|
-
* args, THROWS a `TOOL` {@link WorkspaceError} when no operation arm matched (a malformed / unknown
|
|
4187
|
-
* operation), else `switch`es on `op.operation` and RETURNS the plain result — letting a
|
|
4188
|
-
* `WorkspaceError` raised by the live workspace (`MODALITY` / `PATTERN` / `RANGE`) PROPAGATE
|
|
4189
|
-
* unCAUGHT. The {@link import('./tools/ToolManager.js').ToolManager} performs the ONE
|
|
4190
|
-
* canonical wrap (`{ id, name, value }` on a return; `{ id, name, error }` on a throw, ISOLATED so
|
|
4191
|
-
* nothing escapes the run), so the outcome appears EXACTLY ONCE over BOTH the agent loop and MCP (a
|
|
4192
|
-
* throw → MCP `isError: true`). There is no `{ ok, output, duration }` envelope. The range edit is
|
|
4193
|
-
* the FLAT `'splice'` op: its four flat caret integers are reassembled into a {@link import('./types.js').Range}
|
|
4194
|
-
* by {@link rangeOf} and fed to the workspace's ranged `write`.
|
|
4195
|
-
*
|
|
4196
|
-
* @param manager - The workspace registry the tool reads + edits (its ACTIVE workspace; closed over by the handler)
|
|
4197
|
-
* @param options - Optional advertised `name` / `description` overrides (see {@link WorkspaceToolOptions})
|
|
4198
|
-
* @returns A {@link ToolInterface} (named {@link import('./constants.js').WORKSPACE_TOOL_NAME} by
|
|
4199
|
-
* default) whose `parameters` advertise the `operation`-discriminated union schema
|
|
4200
|
-
*
|
|
4201
|
-
* @example
|
|
4202
|
-
* ```ts
|
|
4203
|
-
* import { createWorkspaceManager, createWorkspaceTool, createToolManager } from '@src/core'
|
|
4204
|
-
*
|
|
4205
|
-
* const manager = createWorkspaceManager()
|
|
4206
|
-
* const tool = createWorkspaceTool(manager) // edits the manager's ACTIVE workspace
|
|
4207
|
-
* const tools = createToolManager()
|
|
4208
|
-
* tools.add(tool) // a model can now read + edit the active workspace by calling `workspace`
|
|
4209
|
-
* ```
|
|
4210
|
-
*/
|
|
4211
|
-
function createWorkspaceTool(manager, options) {
|
|
4212
|
-
const contract = (0, _orkestrel_contract.createContract)(workspaceToolShape);
|
|
4213
|
-
const parameters = (0, _orkestrel_contract.schemaToParameters)(contract.schema);
|
|
4214
|
-
return createTool({
|
|
4215
|
-
name: options?.name ?? "workspace",
|
|
4216
|
-
description: options?.description ?? WORKSPACE_TOOL_DESCRIPTION,
|
|
4217
|
-
parameters,
|
|
4218
|
-
execute: (args) => {
|
|
4219
|
-
const op = contract.parse(args);
|
|
4220
|
-
if (op === void 0) throw new WorkspaceError("TOOL", `unknown or malformed operation`, { args });
|
|
4221
|
-
if (op.operation === "workspaces") {
|
|
4222
|
-
const activeId = manager.active?.id;
|
|
4223
|
-
return manager.workspaces().map((workspace) => ({
|
|
4224
|
-
id: workspace.id,
|
|
4225
|
-
files: workspace.count,
|
|
4226
|
-
active: workspace.id === activeId
|
|
4227
|
-
}));
|
|
4228
|
-
}
|
|
4229
|
-
if (op.operation === "switch") {
|
|
4230
|
-
const switched = manager.switch(op.id);
|
|
4231
|
-
return switched === void 0 ? {
|
|
4232
|
-
id: op.id,
|
|
4233
|
-
switched: false
|
|
4234
|
-
} : {
|
|
4235
|
-
id: switched.id,
|
|
4236
|
-
switched: true,
|
|
4237
|
-
files: switched.count
|
|
4238
|
-
};
|
|
4239
|
-
}
|
|
4240
|
-
const active = manager.active;
|
|
4241
|
-
switch (op.operation) {
|
|
4242
|
-
case "read": return active?.read(op.path);
|
|
4243
|
-
case "list": return (active?.files() ?? []).map((file) => ({
|
|
4244
|
-
path: file.path,
|
|
4245
|
-
state: file.state,
|
|
4246
|
-
size: file.size,
|
|
4247
|
-
lines: file.lines,
|
|
4248
|
-
kind: isText(file.content) ? "text" : "binary"
|
|
4249
|
-
}));
|
|
4250
|
-
case "has": return active?.has(op.path) ?? false;
|
|
4251
|
-
case "search": return active?.search(op.query, {
|
|
4252
|
-
regex: op.regex,
|
|
4253
|
-
exact: op.exact,
|
|
4254
|
-
limit: op.limit
|
|
4255
|
-
}) ?? [];
|
|
4256
|
-
case "replace": return (active ?? manager.add()).replace(op.query, op.replacement, {
|
|
4257
|
-
regex: op.regex,
|
|
4258
|
-
exact: op.exact,
|
|
4259
|
-
limit: op.limit
|
|
4260
|
-
});
|
|
4261
|
-
case "write": {
|
|
4262
|
-
const workspace = active ?? manager.add();
|
|
4263
|
-
workspace.write(op.path, op.content);
|
|
4264
|
-
return {
|
|
4265
|
-
path: op.path,
|
|
4266
|
-
state: workspace.file(op.path)?.state
|
|
4267
|
-
};
|
|
4268
|
-
}
|
|
4269
|
-
case "splice": {
|
|
4270
|
-
const workspace = active ?? manager.add();
|
|
4271
|
-
workspace.write(op.path, op.content, rangeOf(op.fromLine, op.fromColumn, op.toLine, op.toColumn));
|
|
4272
|
-
return {
|
|
4273
|
-
path: op.path,
|
|
4274
|
-
state: workspace.file(op.path)?.state
|
|
4275
|
-
};
|
|
4276
|
-
}
|
|
4277
|
-
case "prepend": {
|
|
4278
|
-
const workspace = active ?? manager.add();
|
|
4279
|
-
workspace.prepend(op.path, op.content);
|
|
4280
|
-
return {
|
|
4281
|
-
path: op.path,
|
|
4282
|
-
state: workspace.file(op.path)?.state
|
|
4283
|
-
};
|
|
4284
|
-
}
|
|
4285
|
-
case "append": {
|
|
4286
|
-
const workspace = active ?? manager.add();
|
|
4287
|
-
workspace.append(op.path, op.content);
|
|
4288
|
-
return {
|
|
4289
|
-
path: op.path,
|
|
4290
|
-
state: workspace.file(op.path)?.state
|
|
4291
|
-
};
|
|
4292
|
-
}
|
|
4293
|
-
case "move": {
|
|
4294
|
-
const workspace = active ?? manager.add();
|
|
4295
|
-
return {
|
|
4296
|
-
from: op.from,
|
|
4297
|
-
to: op.to,
|
|
4298
|
-
moved: workspace.move(op.from, op.to)
|
|
4299
|
-
};
|
|
4300
|
-
}
|
|
4301
|
-
case "remove": {
|
|
4302
|
-
const workspace = active ?? manager.add();
|
|
4303
|
-
return {
|
|
4304
|
-
path: op.path,
|
|
4305
|
-
removed: workspace.remove(op.path)
|
|
4306
|
-
};
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
}
|
|
4310
|
-
});
|
|
4311
|
-
}
|
|
4312
4016
|
//#endregion
|
|
4313
4017
|
exports.Agent = Agent;
|
|
4314
4018
|
exports.AgentContext = AgentContext;
|
|
@@ -4339,9 +4043,6 @@ exports.ThinkSplitter = ThinkSplitter;
|
|
|
4339
4043
|
exports.Tool = Tool;
|
|
4340
4044
|
exports.ToolManager = ToolManager;
|
|
4341
4045
|
exports.WORKSPACE_SECTION_HEADER = WORKSPACE_SECTION_HEADER;
|
|
4342
|
-
exports.WORKSPACE_TOOL_DESCRIPTION = WORKSPACE_TOOL_DESCRIPTION;
|
|
4343
|
-
exports.WORKSPACE_TOOL_EXAMPLE = WORKSPACE_TOOL_EXAMPLE;
|
|
4344
|
-
exports.WORKSPACE_TOOL_NAME = WORKSPACE_TOOL_NAME;
|
|
4345
4046
|
exports.Workspace = Workspace;
|
|
4346
4047
|
exports.WorkspaceError = WorkspaceError;
|
|
4347
4048
|
exports.WorkspaceManager = WorkspaceManager;
|
|
@@ -4374,7 +4075,6 @@ exports.createTool = createTool;
|
|
|
4374
4075
|
exports.createToolManager = createToolManager;
|
|
4375
4076
|
exports.createWorkspace = createWorkspace;
|
|
4376
4077
|
exports.createWorkspaceManager = createWorkspaceManager;
|
|
4377
|
-
exports.createWorkspaceTool = createWorkspaceTool;
|
|
4378
4078
|
exports.decodedSize = decodedSize;
|
|
4379
4079
|
exports.escapeRegExp = escapeRegExp;
|
|
4380
4080
|
exports.estimateMessages = estimateMessages;
|
|
@@ -4401,6 +4101,5 @@ exports.rangeOf = rangeOf;
|
|
|
4401
4101
|
exports.settleAgentJob = settleAgentJob;
|
|
4402
4102
|
exports.sliceRange = sliceRange;
|
|
4403
4103
|
exports.spliceRange = spliceRange;
|
|
4404
|
-
exports.workspaceToolShape = workspaceToolShape;
|
|
4405
4104
|
|
|
4406
4105
|
//# sourceMappingURL=index.cjs.map
|