@botbotgo/agent-harness 0.0.298 → 0.0.300

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 (166) hide show
  1. package/README.md +78 -38
  2. package/README.zh.md +80 -31
  3. package/dist/acp.d.ts +3 -0
  4. package/dist/acp.js +10 -2
  5. package/dist/api.d.ts +14 -2
  6. package/dist/api.js +19 -3
  7. package/dist/cli.d.ts +18 -1
  8. package/dist/cli.js +1408 -319
  9. package/dist/client/acp.d.ts +9 -3
  10. package/dist/client/acp.js +55 -1
  11. package/dist/client/in-process.d.ts +5 -2
  12. package/dist/client/in-process.js +4 -6
  13. package/dist/client/index.d.ts +1 -1
  14. package/dist/client/types.d.ts +6 -5
  15. package/dist/config/agents/direct.yaml +7 -17
  16. package/dist/config/agents/orchestra.yaml +9 -65
  17. package/dist/config/catalogs/embedding-models.yaml +1 -1
  18. package/dist/config/catalogs/stores.yaml +1 -1
  19. package/dist/config/knowledge/knowledge-runtime.yaml +36 -2
  20. package/dist/config/knowledge/procedural-memory-runtime.yaml +78 -0
  21. package/dist/config/{catalogs/models.yaml → models.yaml} +2 -2
  22. package/dist/config/prompts/direct-system.md +16 -0
  23. package/dist/config/prompts/orchestra-system.md +62 -0
  24. package/dist/config/prompts/routing-system.md +14 -0
  25. package/dist/config/runtime/runtime-memory.yaml +39 -5
  26. package/dist/config/runtime/workspace.yaml +7 -16
  27. package/dist/contracts/runtime.d.ts +242 -1
  28. package/dist/contracts/workspace.d.ts +2 -0
  29. package/dist/index.d.ts +5 -3
  30. package/dist/index.js +2 -1
  31. package/dist/init-project.js +178 -33
  32. package/dist/knowledge/contracts.d.ts +5 -0
  33. package/dist/knowledge/module.d.ts +5 -0
  34. package/dist/knowledge/module.js +340 -18
  35. package/dist/package-version.d.ts +1 -1
  36. package/dist/package-version.js +1 -1
  37. package/dist/persistence/file-store.d.ts +5 -1
  38. package/dist/persistence/file-store.js +16 -0
  39. package/dist/persistence/sqlite-store.d.ts +4 -1
  40. package/dist/persistence/sqlite-store.js +88 -14
  41. package/dist/persistence/types.d.ts +4 -1
  42. package/dist/procedural/config.d.ts +63 -0
  43. package/dist/procedural/config.js +125 -0
  44. package/dist/procedural/index.d.ts +2 -0
  45. package/dist/procedural/index.js +1 -0
  46. package/dist/protocol/ag-ui/http.d.ts +3 -0
  47. package/dist/protocol/ag-ui/http.js +10 -0
  48. package/dist/request-events.d.ts +63 -0
  49. package/dist/request-events.js +400 -0
  50. package/dist/resource/isolation.js +11 -0
  51. package/dist/resource/resource-impl.d.ts +1 -0
  52. package/dist/resource/resource-impl.js +103 -12
  53. package/dist/resources/init-templates/agent-context/deep-research.md +5 -0
  54. package/dist/resources/init-templates/prompts/research-analyst-basic.md +1 -0
  55. package/dist/resources/init-templates/prompts/research-analyst-web-search.md +1 -0
  56. package/dist/resources/init-templates/prompts/research-host-deep-research-basic.md +1 -0
  57. package/dist/resources/init-templates/prompts/research-host-deep-research-web-search.md +1 -0
  58. package/dist/resources/init-templates/prompts/research-host-single-agent-basic.md +1 -0
  59. package/dist/resources/init-templates/prompts/research-host-single-agent-web-search.md +1 -0
  60. package/dist/resources/prompts/runtime/browser-capability-disclaimer-recovery.md +1 -0
  61. package/dist/resources/prompts/runtime/default-subagent.md +2 -0
  62. package/dist/resources/prompts/runtime/durable-memory-context.md +7 -0
  63. package/dist/resources/prompts/runtime/execution-with-tool-evidence-retry.md +1 -0
  64. package/dist/resources/prompts/runtime/execution-with-tool-evidence.md +1 -0
  65. package/dist/resources/prompts/runtime/invalid-tool-selection-recovery.md +1 -0
  66. package/dist/resources/prompts/runtime/memory-manager.md +31 -0
  67. package/dist/resources/prompts/runtime/memory-mutation-reconciliation.md +22 -0
  68. package/dist/resources/prompts/runtime/slash-command-skill.md +6 -0
  69. package/dist/resources/prompts/runtime/strict-tool-json.md +1 -0
  70. package/dist/resources/prompts/runtime/workspace-boundary-guidance.md +3 -0
  71. package/dist/resources/prompts/runtime/workspace-relative-path.md +1 -0
  72. package/dist/resources/prompts/runtime/write-todos-descriptive-content.md +1 -0
  73. package/dist/resources/prompts/runtime/write-todos-full-entry.md +1 -0
  74. package/dist/resources/prompts/runtime/write-todos-non-empty-initial-list.md +1 -0
  75. package/dist/resources/tools/_runtime_tool_helpers.mjs +152 -0
  76. package/dist/resources/tools/cancel_request.mjs +21 -0
  77. package/dist/resources/tools/fetch_url.mjs +23 -0
  78. package/dist/resources/tools/http_request.mjs +30 -0
  79. package/dist/resources/tools/inspect_approvals.mjs +27 -0
  80. package/dist/resources/tools/inspect_artifacts.mjs +21 -0
  81. package/dist/resources/tools/inspect_events.mjs +21 -0
  82. package/dist/resources/tools/inspect_requests.mjs +27 -0
  83. package/dist/resources/tools/inspect_sessions.mjs +21 -0
  84. package/dist/resources/tools/list_files.mjs +27 -0
  85. package/dist/resources/tools/read_artifact.mjs +22 -0
  86. package/dist/resources/tools/request_approval.mjs +27 -0
  87. package/dist/resources/tools/run_command.mjs +21 -0
  88. package/dist/resources/tools/schedule_task.mjs +76 -0
  89. package/dist/resources/tools/search_files.mjs +47 -0
  90. package/dist/resources/tools/send_message.mjs +23 -0
  91. package/dist/runtime/adapter/direct-builtin-utility.d.ts +1 -0
  92. package/dist/runtime/adapter/direct-builtin-utility.js +90 -0
  93. package/dist/runtime/adapter/flow/execution-context.d.ts +1 -1
  94. package/dist/runtime/adapter/flow/execution-context.js +1 -1
  95. package/dist/runtime/adapter/flow/invocation-flow.d.ts +1 -0
  96. package/dist/runtime/adapter/flow/invocation-flow.js +9 -1
  97. package/dist/runtime/adapter/flow/invoke-runtime.d.ts +1 -1
  98. package/dist/runtime/adapter/flow/stream-runtime.d.ts +5 -1
  99. package/dist/runtime/adapter/flow/stream-runtime.js +556 -35
  100. package/dist/runtime/adapter/invocation-result.js +3 -2
  101. package/dist/runtime/adapter/local-tool-invocation.d.ts +1 -1
  102. package/dist/runtime/adapter/local-tool-invocation.js +28 -4
  103. package/dist/runtime/adapter/middleware-assembly.js +3 -1
  104. package/dist/runtime/adapter/model/invocation-request.d.ts +4 -1
  105. package/dist/runtime/adapter/model/invocation-request.js +138 -16
  106. package/dist/runtime/adapter/model/message-assembly.js +2 -6
  107. package/dist/runtime/adapter/model/model-providers.js +103 -5
  108. package/dist/runtime/adapter/resilience.js +17 -2
  109. package/dist/runtime/adapter/runtime-adapter-support.d.ts +11 -7
  110. package/dist/runtime/adapter/runtime-adapter-support.js +39 -5
  111. package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +63 -1
  112. package/dist/runtime/adapter/tool/builtin-middleware-tools.js +193 -21
  113. package/dist/runtime/adapter/tool/tool-arguments.d.ts +3 -1
  114. package/dist/runtime/adapter/tool/tool-arguments.js +52 -17
  115. package/dist/runtime/adapter/tool-resolution.d.ts +1 -0
  116. package/dist/runtime/adapter/tool-resolution.js +4 -2
  117. package/dist/runtime/agent-runtime-adapter.d.ts +27 -0
  118. package/dist/runtime/agent-runtime-adapter.js +163 -11
  119. package/dist/runtime/harness/events/event-bus.d.ts +1 -0
  120. package/dist/runtime/harness/events/event-bus.js +3 -0
  121. package/dist/runtime/harness/events/event-sink.d.ts +3 -0
  122. package/dist/runtime/harness/events/event-sink.js +16 -7
  123. package/dist/runtime/harness/events/streaming.d.ts +18 -1
  124. package/dist/runtime/harness/events/streaming.js +23 -10
  125. package/dist/runtime/harness/run/inspection.js +26 -5
  126. package/dist/runtime/harness/run/stream-run.d.ts +13 -4
  127. package/dist/runtime/harness/run/stream-run.js +448 -4
  128. package/dist/runtime/harness/run/surface-semantics.js +7 -34
  129. package/dist/runtime/harness/system/runtime-memory-manager.d.ts +3 -0
  130. package/dist/runtime/harness/system/runtime-memory-manager.js +384 -69
  131. package/dist/runtime/harness/system/runtime-memory-policy.d.ts +20 -1
  132. package/dist/runtime/harness/system/runtime-memory-policy.js +65 -17
  133. package/dist/runtime/harness/system/runtime-memory-records.js +100 -0
  134. package/dist/runtime/harness/system/runtime-memory-sync.js +2 -2
  135. package/dist/runtime/harness/system/store.d.ts +4 -0
  136. package/dist/runtime/harness/system/store.js +153 -0
  137. package/dist/runtime/harness.d.ts +9 -1
  138. package/dist/runtime/harness.js +141 -7
  139. package/dist/runtime/maintenance/sqlite-checkpoint-saver.d.ts +8 -3
  140. package/dist/runtime/maintenance/sqlite-checkpoint-saver.js +152 -53
  141. package/dist/runtime/parsing/output-parsing.d.ts +10 -2
  142. package/dist/runtime/parsing/output-parsing.js +223 -16
  143. package/dist/runtime/parsing/stream-event-parsing.d.ts +7 -0
  144. package/dist/runtime/parsing/stream-event-parsing.js +51 -1
  145. package/dist/runtime/scheduling/system-schedule-manager.d.ts +41 -0
  146. package/dist/runtime/scheduling/system-schedule-manager.js +532 -0
  147. package/dist/runtime/support/embedding-models.d.ts +1 -1
  148. package/dist/runtime/support/embedding-models.js +5 -2
  149. package/dist/runtime/support/runtime-factories.js +1 -1
  150. package/dist/runtime/support/runtime-layout.d.ts +3 -0
  151. package/dist/runtime/support/runtime-layout.js +10 -1
  152. package/dist/runtime/support/runtime-prompts.d.ts +30 -0
  153. package/dist/runtime/support/runtime-prompts.js +55 -0
  154. package/dist/runtime/support/vector-stores.d.ts +1 -1
  155. package/dist/runtime/support/vector-stores.js +5 -2
  156. package/dist/upstream-events.js +8 -7
  157. package/dist/utils/bundled-text.d.ts +3 -0
  158. package/dist/utils/bundled-text.js +25 -0
  159. package/dist/utils/id.js +3 -2
  160. package/dist/workspace/agent-binding-compiler.js +53 -13
  161. package/dist/workspace/object-loader.js +64 -2
  162. package/dist/workspace/support/workspace-ref-utils.d.ts +2 -1
  163. package/dist/workspace/support/workspace-ref-utils.js +24 -5
  164. package/dist/workspace/yaml-object-reader.d.ts +1 -0
  165. package/dist/workspace/yaml-object-reader.js +95 -17
  166. package/package.json +11 -5
@@ -0,0 +1 @@
1
+ Your previous response was rejected because it claimed execution without any tool calls. Respond with real tool calls only. Start with write_todos, then call the required file or command tools. Do not describe completed work until the tool calls have actually run.
@@ -0,0 +1 @@
1
+ Do not claim that files were created, files were edited, commands were run, waits completed, or todos were updated unless you actually call the relevant tool(s). For multi-step execution tasks, call write_todos first, then use real tool calls, and only summarize results that came from those tool calls.
@@ -0,0 +1 @@
1
+ Do not call skill names or other non-tool labels as tools. Only emit tool calls whose names appear in the actual available tool list. If a skill is loaded, follow its instructions using the real available tools instead of calling the skill name as a tool.
@@ -0,0 +1,31 @@
1
+ You are the runtime memory manager.
2
+ Decide whether a candidate should be stored as durable memory and refine it if appropriate.
3
+ Return JSON only.
4
+
5
+ Rules:
6
+ - Store only durable reusable knowledge. Reject transient chatter, scratchpad, or duplication without added value.
7
+ - Reject raw request/session summaries, source-specific page/news recaps, and generic "we learned how to use the tools/workflow" reflections unless they clearly contain reusable preferences, facts, decisions, or procedures.
8
+ - If transcript evidence shows the user explicitly asked the system to remember or follow a future instruction and the assistant confirmed that intent, store the durable instruction instead of rejecting it as a generic summary.
9
+ - Treat durable knowledge as generic mutable records with database-like operations over the same underlying knowledge item.
10
+ - One candidate may yield zero, one, or multiple durable knowledge items. Split it only when the input clearly contains multiple independently mutable knowledge points.
11
+ - When storing a knowledge item, always return a `knowledgeMutation` object with a stable `identity` and an `operation` of `create`, `update`, or `delete`.
12
+ - Keep `knowledgeMutation.identity` stable across revisions of the same knowledge point, even when the wording changes.
13
+ - Use `create` for a newly introduced knowledge item, `update` for a revised active state of an existing knowledge item, and `delete` when the candidate says an existing knowledge item should no longer remain active.
14
+ - If an existing relevant record already represents the same underlying knowledge item, reuse that record's `knowledge_identity` instead of inventing a new one.
15
+ - Do not invent a second identity just because the new statement negates, revokes, deletes, or replaces the old wording. That is usually the same knowledge item with a different mutation operation.
16
+ - The stored `content` must be canonical knowledge text, not an assistant acknowledgement such as "已记住" or "I will remember".
17
+ - You may optionally include `operationalRule` when the knowledge is naturally a rule, instruction, or recurring procedure. Treat it as structured metadata, not as the primary identity mechanism.
18
+ - Prefer semantic/episodic/procedural kinds only.
19
+ - Prefer scopes session/agent/workspace/user/project only.
20
+ - If the candidate should not be stored, return {"store": false, "reason": "..."}
21
+ - If the candidate maps to one durable item, you may return {"store": true, "content": "...", "summary": "...", "kind": "...", "scope": "...", "tags": ["..."], "confidence": 0.0, "knowledgeMutation": {"identity": "...", "operation": "create|update|delete"}, "operationalRule": {"trigger": "...", "action": "...", "target": "...", "effect": "apply|invalidate"}}
22
+ - If the candidate maps to multiple durable items, return {"store": true, "mutations": [{"content": "...", "summary": "...", "kind": "...", "scope": "...", "tags": ["..."], "confidence": 0.0, "knowledgeMutation": {"identity": "...", "operation": "create|update|delete"}, "operationalRule": {"trigger": "...", "action": "...", "target": "...", "effect": "apply|invalidate"}}]}
23
+
24
+ sessionId={{sessionId}}
25
+ requestId={{requestId}}
26
+
27
+ Candidate:
28
+ {{candidateJson}}
29
+
30
+ Existing relevant records:
31
+ {{existingRecords}}
@@ -0,0 +1,22 @@
1
+ You are reconciling one newly extracted durable knowledge candidate against existing durable knowledge records.
2
+ Return JSON only.
3
+
4
+ Rules:
5
+ - Reuse an existing `knowledge_identity` when the candidate revises, deletes, revokes, narrows, broadens, or replaces that same underlying knowledge item.
6
+ - Keep a new identity only when the candidate is genuinely about a different knowledge item.
7
+ - If an existing record contains a structured rule, instruction, or target entity that the candidate now says is deleted, unavailable, forbidden, removed, revoked, or should no longer be executed, treat that as the same underlying knowledge item and reuse the existing identity.
8
+ - If you reuse an existing identity, prefer the existing record's scope unless the candidate clearly belongs to a different long-lived scope.
9
+ - Choose `delete` when the candidate says the prior knowledge item should no longer remain active.
10
+ - Choose `update` when the candidate keeps the same underlying item but changes its active content.
11
+ - Choose `create` only when none of the existing identities represent the same underlying knowledge item.
12
+ - Never invent an identity that is not already listed when `reuseExistingIdentity` is true.
13
+
14
+ Return one of:
15
+ - {"reuseExistingIdentity": false}
16
+ - {"reuseExistingIdentity": true, "identity": "...", "operation": "update|delete", "scope": "session|agent|workspace|user|project"}
17
+
18
+ Candidate:
19
+ {{candidateJson}}
20
+
21
+ Existing candidate identities:
22
+ {{existingRecords}}
@@ -0,0 +1,6 @@
1
+ This user message is an explicit command-style invocation of the {{skillQualifier}} `{{skillName}}`.
2
+ Read the skill file for `{{skillName}}` before taking action, then follow its documented phases and constraints exactly.
3
+ You must use the `{{skillName}}` skill for this request and follow its documented workflow.
4
+ Treat everything after `/{{skillName}}` as the skill argument string: {{argumentText}}.
5
+ Do not answer with a generic explanation of what the skill would do. Execute the skill workflow using the available tools unless the skill instructions explicitly require confirmation before acting.
6
+ {{dryRunHint}}
@@ -0,0 +1 @@
1
+ When calling tools, return only the tool call itself. The arguments must be a pure JSON object with no explanatory text before or after it.
@@ -0,0 +1,3 @@
1
+ Keep repository and file exploration bounded to the current workspace root unless the user explicitly asks for broader host or filesystem access.
2
+ Do not inspect absolute paths outside the workspace, system directories, or unrelated repos by default.
3
+ Prefer workspace-local tools, relative paths, and the current repository checkout when analyzing code.
@@ -0,0 +1 @@
1
+ The previous tool call used an absolute filesystem path outside the workspace root. Retry with a workspace-relative path only, such as 'tmp-counter.txt', and do not use absolute paths like '/tmp/...'.
@@ -0,0 +1 @@
1
+ When first calling write_todos, use descriptive task content for each todo item. Do not use placeholders like '1', '2', '3', 'step 1', or 'todo 1'. Describe the real step, for example 'Create tmp-counter.txt' or 'Append digit 2 and read the file'.
@@ -0,0 +1 @@
1
+ When calling write_todos, every todo item must include both content and status. Do not send status-only updates. Retry by resending the full todo entry with the original content preserved.
@@ -0,0 +1 @@
1
+ When first calling write_todos for a non-trivial task, do not send an empty todo list. Send the concrete task steps immediately, and every todo item must include both content and status.
@@ -0,0 +1,152 @@
1
+ import path from "node:path";
2
+
3
+ function asRecord(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
5
+ }
6
+
7
+ export function optionalString(value, fallback = undefined) {
8
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
9
+ }
10
+
11
+ export function requiredString(value, key) {
12
+ const normalized = optionalString(value);
13
+ if (!normalized) {
14
+ throw new Error(`${key} is required`);
15
+ }
16
+ return normalized;
17
+ }
18
+
19
+ export function optionalNumber(value, fallback = undefined) {
20
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
21
+ }
22
+
23
+ export function optionalBoolean(value, fallback = undefined) {
24
+ return typeof value === "boolean" ? value : fallback;
25
+ }
26
+
27
+ export function optionalStringRecord(value) {
28
+ const record = asRecord(value);
29
+ const entries = Object.entries(record).filter(([, candidate]) => typeof candidate === "string");
30
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
31
+ }
32
+
33
+ export function optionalObject(value) {
34
+ const record = asRecord(value);
35
+ return Object.keys(record).length > 0 ? record : undefined;
36
+ }
37
+
38
+ export function defineSchema(shape) {
39
+ return {
40
+ parse(input = {}) {
41
+ const record = asRecord(input);
42
+ return Object.fromEntries(
43
+ Object.entries(shape).map(([key, parser]) => [key, parser(record[key])]),
44
+ );
45
+ },
46
+ };
47
+ }
48
+
49
+ export function getBackend(context) {
50
+ return asRecord(context?.backend);
51
+ }
52
+
53
+ export function getRuntimeApi(context) {
54
+ return asRecord(context?.runtime);
55
+ }
56
+
57
+ export function getCurrentRequestIdentity(context, toolName) {
58
+ const runtime = getRuntimeApi(context);
59
+ const current = asRecord(runtime.current);
60
+ const sessionId = optionalString(current.sessionId);
61
+ const requestId = optionalString(current.requestId);
62
+ if (!sessionId || !requestId) {
63
+ throw new Error(`${toolName} requires a current sessionId/requestId or explicit input.`);
64
+ }
65
+ return { sessionId, requestId };
66
+ }
67
+
68
+ export function normalizeWorkspacePath(context, inputPath, fallback = ".") {
69
+ const rawPath = optionalString(inputPath, fallback) ?? fallback;
70
+ const workspaceRoot = optionalString(context?.runtime?.workspaceRoot);
71
+ if (!workspaceRoot || !path.isAbsolute(rawPath)) {
72
+ return rawPath;
73
+ }
74
+ const resolvedWorkspaceRoot = path.resolve(workspaceRoot);
75
+ const resolvedInputPath = path.resolve(rawPath);
76
+ if (resolvedInputPath === resolvedWorkspaceRoot || resolvedInputPath.startsWith(`${resolvedWorkspaceRoot}${path.sep}`)) {
77
+ return path.relative(resolvedWorkspaceRoot, resolvedInputPath) || ".";
78
+ }
79
+ throw new Error(`Path '${rawPath}' is outside the workspace root '${resolvedWorkspaceRoot}'. Use a workspace-relative path instead.`);
80
+ }
81
+
82
+ export function toDisplayContent(content) {
83
+ if (typeof content === "string") {
84
+ return content;
85
+ }
86
+ if (content instanceof Uint8Array) {
87
+ return new TextDecoder().decode(content);
88
+ }
89
+ return "";
90
+ }
91
+
92
+ export function truncateText(text, maxChars = 12000) {
93
+ if (typeof text !== "string" || text.length <= maxChars) {
94
+ return text;
95
+ }
96
+ return `${text.slice(0, maxChars - 18)}\n...[truncated]`;
97
+ }
98
+
99
+ export function truncateLines(lines, maxChars = 12000) {
100
+ return truncateText(lines.join("\n"), maxChars);
101
+ }
102
+
103
+ export function formatHttpResponse(result) {
104
+ if (result?.error) {
105
+ return result.error;
106
+ }
107
+ const content = toDisplayContent(result?.body ?? result?.content);
108
+ const status = typeof result?.status === "number" ? `${result.status}${result.statusText ? ` ${result.statusText}` : ""}` : null;
109
+ const headers = result?.headers && typeof result.headers === "object"
110
+ ? Object.entries(result.headers).map(([key, value]) => `${key}: ${value}`)
111
+ : [];
112
+ return [
113
+ ...(status ? [`Status: ${status}`] : []),
114
+ ...(headers.length > 0 ? ["Headers:", ...headers] : []),
115
+ ...(content ? ["Body:", content] : []),
116
+ ].join("\n") || "Request completed.";
117
+ }
118
+
119
+ export function normalizeGrepMatches(matches = []) {
120
+ return matches.map((match) => ({
121
+ path: optionalString(match.path, ""),
122
+ line: optionalNumber(match.lineNumber, optionalNumber(match.line_number, optionalNumber(match.line, 0))),
123
+ text: optionalString(match.content, optionalString(match.text, "")),
124
+ }));
125
+ }
126
+
127
+ export function formatGrepResult(pattern, matches) {
128
+ if (!matches || matches.length === 0) {
129
+ return `No matches found for pattern '${pattern}'`;
130
+ }
131
+ const lines = [];
132
+ let currentFile = "";
133
+ for (const match of matches) {
134
+ if (match.path !== currentFile) {
135
+ currentFile = match.path;
136
+ lines.push(`\n${currentFile}:`);
137
+ }
138
+ lines.push(` ${match.line}: ${match.text}`);
139
+ }
140
+ return truncateLines(lines);
141
+ }
142
+
143
+ export function formatListEntries(entries, emptyMessage) {
144
+ if (!entries || entries.length === 0) {
145
+ return emptyMessage;
146
+ }
147
+ return truncateLines(entries);
148
+ }
149
+
150
+ export function notAvailable(toolName, capability) {
151
+ return `Error: ${toolName} is not available. This runtime does not expose ${capability}.`;
152
+ }
@@ -0,0 +1,21 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getRuntimeApi, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const cancel_request = tool({
5
+ description: "Cancel a running or queued request.",
6
+ schema: defineSchema({
7
+ requestId: (value) => optionalString(value, undefined),
8
+ reason: (value) => optionalString(value, undefined),
9
+ }),
10
+ async invoke(input, context = {}) {
11
+ const runtime = getRuntimeApi(context);
12
+ if (!runtime?.requests?.cancel) {
13
+ return notAvailable("cancel_request", "request cancellation");
14
+ }
15
+ const requestId = input.requestId ?? runtime?.current?.requestId;
16
+ if (!requestId) {
17
+ throw new Error("cancel_request requires requestId or a current request.");
18
+ }
19
+ return runtime.requests.cancel({ requestId, reason: input.reason });
20
+ },
21
+ });
@@ -0,0 +1,23 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, formatHttpResponse, getBackend, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const fetch_url = tool({
5
+ description: "Fetch a URL and return the response body.",
6
+ schema: defineSchema({
7
+ url: (value) => optionalString(value, ""),
8
+ }),
9
+ async invoke(input, context = {}) {
10
+ const backend = getBackend(context);
11
+ if (typeof backend.fetchUrl === "function") {
12
+ const result = await backend.fetchUrl(input.url);
13
+ return typeof result === "string" ? result : formatHttpResponse(result);
14
+ }
15
+ const response = await fetch(input.url);
16
+ return formatHttpResponse({
17
+ status: response.status,
18
+ statusText: response.statusText,
19
+ headers: Object.fromEntries(response.headers.entries()),
20
+ body: await response.text(),
21
+ });
22
+ },
23
+ });
@@ -0,0 +1,30 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, formatHttpResponse, getBackend, optionalString, optionalStringRecord } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const http_request = tool({
5
+ description: "Send a structured HTTP request.",
6
+ schema: defineSchema({
7
+ url: (value) => optionalString(value, ""),
8
+ method: (value) => optionalString(value, undefined),
9
+ headers: (value) => optionalStringRecord(value),
10
+ body: (value) => optionalString(value, undefined),
11
+ }),
12
+ async invoke(input, context = {}) {
13
+ const backend = getBackend(context);
14
+ if (typeof backend.httpRequest === "function") {
15
+ const result = await backend.httpRequest(input);
16
+ return typeof result === "string" ? result : formatHttpResponse(result);
17
+ }
18
+ const response = await fetch(input.url, {
19
+ method: input.method,
20
+ headers: input.headers,
21
+ body: input.body,
22
+ });
23
+ return formatHttpResponse({
24
+ status: response.status,
25
+ statusText: response.statusText,
26
+ headers: Object.fromEntries(response.headers.entries()),
27
+ body: await response.text(),
28
+ });
29
+ },
30
+ });
@@ -0,0 +1,27 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getRuntimeApi, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const inspect_approvals = tool({
5
+ description: "Inspect runtime approvals by listing them or fetching one approval record.",
6
+ schema: defineSchema({
7
+ operation: (value) => optionalString(value, "list"),
8
+ approvalId: (value) => optionalString(value, undefined),
9
+ sessionId: (value) => optionalString(value, undefined),
10
+ requestId: (value) => optionalString(value, undefined),
11
+ status: (value) => optionalString(value, undefined),
12
+ }),
13
+ async invoke(input, context = {}) {
14
+ const runtime = getRuntimeApi(context);
15
+ if (!runtime?.approvals?.list || !runtime?.approvals?.get) {
16
+ return notAvailable("inspect_approvals", "runtime approval inspection");
17
+ }
18
+ if (input.operation === "get") {
19
+ return runtime.approvals.get(input.approvalId ?? "");
20
+ }
21
+ return runtime.approvals.list({
22
+ ...(input.sessionId ? { sessionId: input.sessionId } : runtime?.current?.sessionId ? { sessionId: runtime.current.sessionId } : {}),
23
+ ...(input.requestId ? { requestId: input.requestId } : runtime?.current?.requestId ? { requestId: runtime.current.requestId } : {}),
24
+ ...(input.status ? { status: input.status } : {}),
25
+ });
26
+ },
27
+ });
@@ -0,0 +1,21 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getCurrentRequestIdentity, getRuntimeApi, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const inspect_artifacts = tool({
5
+ description: "List persisted artifacts for one request.",
6
+ schema: defineSchema({
7
+ sessionId: (value) => optionalString(value, undefined),
8
+ requestId: (value) => optionalString(value, undefined),
9
+ }),
10
+ async invoke(input, context = {}) {
11
+ const runtime = getRuntimeApi(context);
12
+ if (!runtime?.artifacts?.list) {
13
+ return notAvailable("inspect_artifacts", "runtime artifact inspection");
14
+ }
15
+ const identity =
16
+ input.sessionId && input.requestId
17
+ ? { sessionId: input.sessionId, requestId: input.requestId }
18
+ : getCurrentRequestIdentity(context, "inspect_artifacts");
19
+ return runtime.artifacts.list(identity.sessionId, identity.requestId);
20
+ },
21
+ });
@@ -0,0 +1,21 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getCurrentRequestIdentity, getRuntimeApi, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const inspect_events = tool({
5
+ description: "List persisted runtime events for one request.",
6
+ schema: defineSchema({
7
+ sessionId: (value) => optionalString(value, undefined),
8
+ requestId: (value) => optionalString(value, undefined),
9
+ }),
10
+ async invoke(input, context = {}) {
11
+ const runtime = getRuntimeApi(context);
12
+ if (!runtime?.events?.list) {
13
+ return notAvailable("inspect_events", "runtime event inspection");
14
+ }
15
+ const identity =
16
+ input.sessionId && input.requestId
17
+ ? { sessionId: input.sessionId, requestId: input.requestId }
18
+ : getCurrentRequestIdentity(context, "inspect_events");
19
+ return runtime.events.list(identity.sessionId, identity.requestId);
20
+ },
21
+ });
@@ -0,0 +1,27 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getRuntimeApi, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const inspect_requests = tool({
5
+ description: "Inspect runtime requests by listing them or fetching one request record.",
6
+ schema: defineSchema({
7
+ operation: (value) => optionalString(value, "list"),
8
+ requestId: (value) => optionalString(value, undefined),
9
+ sessionId: (value) => optionalString(value, undefined),
10
+ agentId: (value) => optionalString(value, undefined),
11
+ state: (value) => optionalString(value, undefined),
12
+ }),
13
+ async invoke(input, context = {}) {
14
+ const runtime = getRuntimeApi(context);
15
+ if (!runtime?.requests?.list || !runtime?.requests?.get) {
16
+ return notAvailable("inspect_requests", "runtime request inspection");
17
+ }
18
+ if (input.operation === "get") {
19
+ return runtime.requests.get(input.requestId ?? runtime?.current?.requestId ?? "");
20
+ }
21
+ return runtime.requests.list({
22
+ ...(input.sessionId ? { sessionId: input.sessionId } : runtime?.current?.sessionId ? { sessionId: runtime.current.sessionId } : {}),
23
+ ...(input.agentId ? { agentId: input.agentId } : {}),
24
+ ...(input.state ? { state: input.state } : {}),
25
+ });
26
+ },
27
+ });
@@ -0,0 +1,21 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getRuntimeApi, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const inspect_sessions = tool({
5
+ description: "Inspect runtime sessions by listing them or fetching one session record.",
6
+ schema: defineSchema({
7
+ operation: (value) => optionalString(value, "list"),
8
+ sessionId: (value) => optionalString(value, undefined),
9
+ agentId: (value) => optionalString(value, undefined),
10
+ }),
11
+ async invoke(input, context = {}) {
12
+ const runtime = getRuntimeApi(context);
13
+ if (!runtime?.sessions?.list || !runtime?.sessions?.get) {
14
+ return notAvailable("inspect_sessions", "runtime session inspection");
15
+ }
16
+ if (input.operation === "get") {
17
+ return runtime.sessions.get(input.sessionId ?? runtime?.current?.sessionId ?? "");
18
+ }
19
+ return runtime.sessions.list(input.agentId ? { agentId: input.agentId } : undefined);
20
+ },
21
+ });
@@ -0,0 +1,27 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, formatListEntries, getBackend, normalizeWorkspacePath, optionalString, truncateLines } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const list_files = tool({
5
+ description: "List files in a workspace directory.",
6
+ schema: defineSchema({
7
+ path: (value) => optionalString(value, "."),
8
+ }),
9
+ async invoke(input, context = {}) {
10
+ const backend = getBackend(context);
11
+ const targetPath = normalizeWorkspacePath(context, input.path, ".");
12
+ const legacyInfos = typeof backend.lsInfo === "function" ? await backend.lsInfo(targetPath) : [];
13
+ const infos = legacyInfos.length > 0
14
+ ? legacyInfos
15
+ : typeof backend.ls === "function"
16
+ ? ((await backend.ls(targetPath))?.files ?? []).map((item) => ({
17
+ path: item.path,
18
+ is_dir: item.isDir,
19
+ size: item.size,
20
+ }))
21
+ : [];
22
+ return formatListEntries(
23
+ infos.map((item) => item.is_dir ? `${item.path} (directory)` : `${item.path}${item.size ? ` (${item.size} bytes)` : ""}`),
24
+ `No files found in ${targetPath}`,
25
+ );
26
+ },
27
+ });
@@ -0,0 +1,22 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getCurrentRequestIdentity, getRuntimeApi, notAvailable, optionalString, requiredString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const read_artifact = tool({
5
+ description: "Read one persisted request artifact.",
6
+ schema: defineSchema({
7
+ artifactPath: (value) => requiredString(value, "artifactPath"),
8
+ sessionId: (value) => optionalString(value, undefined),
9
+ requestId: (value) => optionalString(value, undefined),
10
+ }),
11
+ async invoke(input, context = {}) {
12
+ const runtime = getRuntimeApi(context);
13
+ if (!runtime?.artifacts?.read) {
14
+ return notAvailable("read_artifact", "runtime artifact reads");
15
+ }
16
+ const identity =
17
+ input.sessionId && input.requestId
18
+ ? { sessionId: input.sessionId, requestId: input.requestId }
19
+ : getCurrentRequestIdentity(context, "read_artifact");
20
+ return runtime.artifacts.read(identity.sessionId, identity.requestId, input.artifactPath);
21
+ },
22
+ });
@@ -0,0 +1,27 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getBackend, notAvailable, optionalObject, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const request_approval = tool({
5
+ description: "Request a runtime approval decision.",
6
+ schema: defineSchema({
7
+ action: (value) => optionalString(value, ""),
8
+ reason: (value) => optionalString(value, undefined),
9
+ details: (value) => optionalObject(value),
10
+ }),
11
+ async invoke(input, context = {}) {
12
+ const backend = getBackend(context);
13
+ if (typeof backend.requestApproval !== "function") {
14
+ return notAvailable("request_approval", "approval requests");
15
+ }
16
+ const result = await backend.requestApproval(input);
17
+ if (typeof result === "string") {
18
+ return result;
19
+ }
20
+ if (result?.error) {
21
+ return result.error;
22
+ }
23
+ return result?.approved === true
24
+ ? `Approval granted for '${input.action}'${result?.id ? ` (id: ${result.id})` : ""}.`
25
+ : `Approval requested for '${input.action}'${result?.id ? ` (id: ${result.id})` : ""}.`;
26
+ },
27
+ });
@@ -0,0 +1,21 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getBackend, notAvailable, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ export const run_command = tool({
5
+ description: "Run a shell command in the workspace sandbox.",
6
+ schema: defineSchema({
7
+ command: (value) => optionalString(value, ""),
8
+ }),
9
+ async invoke(input, context = {}) {
10
+ const backend = getBackend(context);
11
+ if (typeof backend.execute !== "function") {
12
+ return notAvailable("run_command", "sandbox command execution");
13
+ }
14
+ const result = await backend.execute(input.command);
15
+ return [
16
+ result.output,
17
+ result.exitCode !== null ? `\n[Command ${result.exitCode === 0 ? "succeeded" : "failed"} with exit code ${result.exitCode}]` : "",
18
+ result.truncated ? "\n[Output was truncated due to size limits]" : "",
19
+ ].join("");
20
+ },
21
+ });
@@ -0,0 +1,76 @@
1
+ import { tool } from "@botbotgo/agent-harness/tools";
2
+ import { defineSchema, getBackend, getRuntimeApi, notAvailable, optionalObject, optionalString } from "./_runtime_tool_helpers.mjs";
3
+
4
+ function parseScheduleWhen(value) {
5
+ const normalized = optionalString(value, "")?.toLowerCase() ?? "";
6
+ const intervalMatch = /^every\s+(\d+)\s*(minute|minutes|min|mins|hour|hours|hr|hrs)$/u.exec(normalized);
7
+ if (intervalMatch) {
8
+ const quantity = Number.parseInt(intervalMatch[1], 10);
9
+ const unit = intervalMatch[2];
10
+ return { type: "interval", everyMinutes: unit.startsWith("h") ? quantity * 60 : quantity };
11
+ }
12
+ if (normalized === "hourly" || normalized === "every hour") {
13
+ return { type: "interval", everyMinutes: 60 };
14
+ }
15
+ return normalized ? { type: "cron", expression: value } : undefined;
16
+ }
17
+
18
+ export const schedule_task = tool({
19
+ description: "Create, inspect, update, list, and delete runtime-managed scheduled tasks.",
20
+ schema: defineSchema({
21
+ operation: (value) => optionalString(value, "create"),
22
+ scheduleId: (value) => optionalString(value, undefined),
23
+ name: (value) => optionalString(value, undefined),
24
+ instruction: (value) => optionalString(value, undefined),
25
+ agentId: (value) => optionalString(value, undefined),
26
+ when: (value) => optionalString(value, undefined),
27
+ schedule: (value) => optionalObject(value),
28
+ filters: (value) => optionalObject(value),
29
+ metadata: (value) => optionalObject(value),
30
+ }),
31
+ async invoke(input, context = {}) {
32
+ const runtime = getRuntimeApi(context);
33
+ const backend = getBackend(context);
34
+ const schedulePayload = input.schedule ?? parseScheduleWhen(input.when);
35
+ if (runtime?.schedules?.manage) {
36
+ return runtime.schedules.manage({
37
+ operation: input.operation,
38
+ scheduleId: input.scheduleId,
39
+ name: input.name,
40
+ instruction: input.instruction,
41
+ agentId: input.agentId,
42
+ schedule: schedulePayload,
43
+ filters: input.filters,
44
+ metadata: input.metadata,
45
+ });
46
+ }
47
+ if (typeof backend.manageSchedule === "function") {
48
+ return backend.manageSchedule({
49
+ operation: input.operation,
50
+ scheduleId: input.scheduleId,
51
+ name: input.name,
52
+ instruction: input.instruction,
53
+ agentId: input.agentId,
54
+ schedule: schedulePayload,
55
+ filters: input.filters,
56
+ metadata: input.metadata,
57
+ });
58
+ }
59
+ if (typeof backend.scheduleTask !== "function") {
60
+ return notAvailable("schedule_task", "task scheduling");
61
+ }
62
+ if (input.operation !== "create") {
63
+ return "Error: schedule_task only supports create on this backend.";
64
+ }
65
+ const result = await backend.scheduleTask({
66
+ instruction: input.instruction ?? "",
67
+ when: input.when ?? "",
68
+ name: input.name,
69
+ metadata: input.metadata,
70
+ });
71
+ if (typeof result === "string") {
72
+ return result;
73
+ }
74
+ return result?.error ?? `Scheduled task '${input.name ?? input.instruction ?? ""}' for ${input.when ?? ""}${result?.id ? ` (id: ${result.id})` : ""}.`;
75
+ },
76
+ });