@sammorrowdrums/mcpi-agent-core 0.85.0-bootstrap.0

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 (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +513 -0
  3. package/dist/agent-loop.d.ts +24 -0
  4. package/dist/agent-loop.d.ts.map +1 -0
  5. package/dist/agent-loop.js +553 -0
  6. package/dist/agent-loop.js.map +1 -0
  7. package/dist/agent.d.ts +122 -0
  8. package/dist/agent.d.ts.map +1 -0
  9. package/dist/agent.js +422 -0
  10. package/dist/agent.js.map +1 -0
  11. package/dist/harness/agent-harness.d.ts +461 -0
  12. package/dist/harness/agent-harness.d.ts.map +1 -0
  13. package/dist/harness/agent-harness.js +252 -0
  14. package/dist/harness/agent-harness.js.map +1 -0
  15. package/dist/harness/compaction/branch-summarization.d.ts +62 -0
  16. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  17. package/dist/harness/compaction/branch-summarization.js +169 -0
  18. package/dist/harness/compaction/branch-summarization.js.map +1 -0
  19. package/dist/harness/compaction/compaction.d.ts +104 -0
  20. package/dist/harness/compaction/compaction.d.ts.map +1 -0
  21. package/dist/harness/compaction/compaction.js +570 -0
  22. package/dist/harness/compaction/compaction.js.map +1 -0
  23. package/dist/harness/compaction/utils.d.ts +25 -0
  24. package/dist/harness/compaction/utils.d.ts.map +1 -0
  25. package/dist/harness/compaction/utils.js +120 -0
  26. package/dist/harness/compaction/utils.js.map +1 -0
  27. package/dist/harness/env/nodejs.d.ts +46 -0
  28. package/dist/harness/env/nodejs.d.ts.map +1 -0
  29. package/dist/harness/env/nodejs.js +639 -0
  30. package/dist/harness/env/nodejs.js.map +1 -0
  31. package/dist/harness/events.d.ts +43 -0
  32. package/dist/harness/events.d.ts.map +1 -0
  33. package/dist/harness/events.js +65 -0
  34. package/dist/harness/events.js.map +1 -0
  35. package/dist/harness/messages.d.ts +51 -0
  36. package/dist/harness/messages.d.ts.map +1 -0
  37. package/dist/harness/messages.js +102 -0
  38. package/dist/harness/messages.js.map +1 -0
  39. package/dist/harness/prompt-templates.d.ts +48 -0
  40. package/dist/harness/prompt-templates.d.ts.map +1 -0
  41. package/dist/harness/prompt-templates.js +225 -0
  42. package/dist/harness/prompt-templates.js.map +1 -0
  43. package/dist/harness/reducer.d.ts +100 -0
  44. package/dist/harness/reducer.d.ts.map +1 -0
  45. package/dist/harness/reducer.js +415 -0
  46. package/dist/harness/reducer.js.map +1 -0
  47. package/dist/harness/result.d.ts +40 -0
  48. package/dist/harness/result.d.ts.map +1 -0
  49. package/dist/harness/result.js +41 -0
  50. package/dist/harness/result.js.map +1 -0
  51. package/dist/harness/session/context.d.ts +22 -0
  52. package/dist/harness/session/context.d.ts.map +1 -0
  53. package/dist/harness/session/context.js +67 -0
  54. package/dist/harness/session/context.js.map +1 -0
  55. package/dist/harness/session/index.d.ts +7 -0
  56. package/dist/harness/session/index.d.ts.map +1 -0
  57. package/dist/harness/session/index.js +6 -0
  58. package/dist/harness/session/index.js.map +1 -0
  59. package/dist/harness/session/jsonl/codec.d.ts +10 -0
  60. package/dist/harness/session/jsonl/codec.d.ts.map +1 -0
  61. package/dist/harness/session/jsonl/codec.js +229 -0
  62. package/dist/harness/session/jsonl/codec.js.map +1 -0
  63. package/dist/harness/session/jsonl/errors.d.ts +9 -0
  64. package/dist/harness/session/jsonl/errors.d.ts.map +1 -0
  65. package/dist/harness/session/jsonl/errors.js +19 -0
  66. package/dist/harness/session/jsonl/errors.js.map +1 -0
  67. package/dist/harness/session/jsonl/repo.d.ts +27 -0
  68. package/dist/harness/session/jsonl/repo.d.ts.map +1 -0
  69. package/dist/harness/session/jsonl/repo.js +175 -0
  70. package/dist/harness/session/jsonl/repo.js.map +1 -0
  71. package/dist/harness/session/jsonl/storage.d.ts +43 -0
  72. package/dist/harness/session/jsonl/storage.d.ts.map +1 -0
  73. package/dist/harness/session/jsonl/storage.js +217 -0
  74. package/dist/harness/session/jsonl/storage.js.map +1 -0
  75. package/dist/harness/session/jsonl/types.d.ts +38 -0
  76. package/dist/harness/session/jsonl/types.d.ts.map +1 -0
  77. package/dist/harness/session/jsonl/types.js +2 -0
  78. package/dist/harness/session/jsonl/types.js.map +1 -0
  79. package/dist/harness/session/jsonl.d.ts +3 -0
  80. package/dist/harness/session/jsonl.d.ts.map +1 -0
  81. package/dist/harness/session/jsonl.js +2 -0
  82. package/dist/harness/session/jsonl.js.map +1 -0
  83. package/dist/harness/session/memory.d.ts +44 -0
  84. package/dist/harness/session/memory.d.ts.map +1 -0
  85. package/dist/harness/session/memory.js +141 -0
  86. package/dist/harness/session/memory.js.map +1 -0
  87. package/dist/harness/session/session.d.ts +50 -0
  88. package/dist/harness/session/session.d.ts.map +1 -0
  89. package/dist/harness/session/session.js +227 -0
  90. package/dist/harness/session/session.js.map +1 -0
  91. package/dist/harness/session/state.d.ts +65 -0
  92. package/dist/harness/session/state.d.ts.map +1 -0
  93. package/dist/harness/session/state.js +319 -0
  94. package/dist/harness/session/state.js.map +1 -0
  95. package/dist/harness/session/testing/conformance.d.ts +4 -0
  96. package/dist/harness/session/testing/conformance.d.ts.map +1 -0
  97. package/dist/harness/session/testing/conformance.js +762 -0
  98. package/dist/harness/session/testing/conformance.js.map +1 -0
  99. package/dist/harness/session/testing/index.d.ts +3 -0
  100. package/dist/harness/session/testing/index.d.ts.map +1 -0
  101. package/dist/harness/session/testing/index.js +2 -0
  102. package/dist/harness/session/testing/index.js.map +1 -0
  103. package/dist/harness/session/testing/types.d.ts +14 -0
  104. package/dist/harness/session/testing/types.d.ts.map +1 -0
  105. package/dist/harness/session/testing/types.js +2 -0
  106. package/dist/harness/session/testing/types.js.map +1 -0
  107. package/dist/harness/session/types.d.ts +346 -0
  108. package/dist/harness/session/types.d.ts.map +1 -0
  109. package/dist/harness/session/types.js +10 -0
  110. package/dist/harness/session/types.js.map +1 -0
  111. package/dist/harness/skills.d.ts +44 -0
  112. package/dist/harness/skills.d.ts.map +1 -0
  113. package/dist/harness/skills.js +313 -0
  114. package/dist/harness/skills.js.map +1 -0
  115. package/dist/harness/system-prompt.d.ts +3 -0
  116. package/dist/harness/system-prompt.d.ts.map +1 -0
  117. package/dist/harness/system-prompt.js +30 -0
  118. package/dist/harness/system-prompt.js.map +1 -0
  119. package/dist/harness/telemetry.d.ts +1221 -0
  120. package/dist/harness/telemetry.d.ts.map +1 -0
  121. package/dist/harness/telemetry.js +508 -0
  122. package/dist/harness/telemetry.js.map +1 -0
  123. package/dist/harness/tools/bash.d.ts +27 -0
  124. package/dist/harness/tools/bash.d.ts.map +1 -0
  125. package/dist/harness/tools/bash.js +132 -0
  126. package/dist/harness/tools/bash.js.map +1 -0
  127. package/dist/harness/tools/edit-diff.d.ts +89 -0
  128. package/dist/harness/tools/edit-diff.d.ts.map +1 -0
  129. package/dist/harness/tools/edit-diff.js +386 -0
  130. package/dist/harness/tools/edit-diff.js.map +1 -0
  131. package/dist/harness/tools/edit.d.ts +19 -0
  132. package/dist/harness/tools/edit.d.ts.map +1 -0
  133. package/dist/harness/tools/edit.js +95 -0
  134. package/dist/harness/tools/edit.js.map +1 -0
  135. package/dist/harness/tools/file-mutation-queue.d.ts +4 -0
  136. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -0
  137. package/dist/harness/tools/file-mutation-queue.js +46 -0
  138. package/dist/harness/tools/file-mutation-queue.js.map +1 -0
  139. package/dist/harness/tools/image.d.ts +3 -0
  140. package/dist/harness/tools/image.d.ts.map +1 -0
  141. package/dist/harness/tools/image.js +106 -0
  142. package/dist/harness/tools/image.js.map +1 -0
  143. package/dist/harness/tools/index.d.ts +6 -0
  144. package/dist/harness/tools/index.d.ts.map +1 -0
  145. package/dist/harness/tools/index.js +5 -0
  146. package/dist/harness/tools/index.js.map +1 -0
  147. package/dist/harness/tools/path-utils.d.ts +4 -0
  148. package/dist/harness/tools/path-utils.d.ts.map +1 -0
  149. package/dist/harness/tools/path-utils.js +26 -0
  150. package/dist/harness/tools/path-utils.js.map +1 -0
  151. package/dist/harness/tools/read.d.ts +34 -0
  152. package/dist/harness/tools/read.d.ts.map +1 -0
  153. package/dist/harness/tools/read.js +110 -0
  154. package/dist/harness/tools/read.js.map +1 -0
  155. package/dist/harness/tools/tool-context.d.ts +6 -0
  156. package/dist/harness/tools/tool-context.d.ts.map +1 -0
  157. package/dist/harness/tools/tool-context.js +2 -0
  158. package/dist/harness/tools/tool-context.js.map +1 -0
  159. package/dist/harness/tools/write.d.ts +11 -0
  160. package/dist/harness/tools/write.d.ts.map +1 -0
  161. package/dist/harness/tools/write.js +31 -0
  162. package/dist/harness/tools/write.js.map +1 -0
  163. package/dist/harness/types.d.ts +228 -0
  164. package/dist/harness/types.d.ts.map +1 -0
  165. package/dist/harness/types.js +75 -0
  166. package/dist/harness/types.js.map +1 -0
  167. package/dist/harness/utils/shell-output.d.ts +22 -0
  168. package/dist/harness/utils/shell-output.d.ts.map +1 -0
  169. package/dist/harness/utils/shell-output.js +181 -0
  170. package/dist/harness/utils/shell-output.js.map +1 -0
  171. package/dist/harness/utils/truncate.d.ts +70 -0
  172. package/dist/harness/utils/truncate.d.ts.map +1 -0
  173. package/dist/harness/utils/truncate.js +296 -0
  174. package/dist/harness/utils/truncate.js.map +1 -0
  175. package/dist/index.d.ts +25 -0
  176. package/dist/index.d.ts.map +1 -0
  177. package/dist/index.js +29 -0
  178. package/dist/index.js.map +1 -0
  179. package/dist/node.d.ts +3 -0
  180. package/dist/node.d.ts.map +1 -0
  181. package/dist/node.js +3 -0
  182. package/dist/node.js.map +1 -0
  183. package/dist/proxy.d.ts +70 -0
  184. package/dist/proxy.d.ts.map +1 -0
  185. package/dist/proxy.js +280 -0
  186. package/dist/proxy.js.map +1 -0
  187. package/dist/search/index.d.ts +21 -0
  188. package/dist/search/index.d.ts.map +1 -0
  189. package/dist/search/index.js +2 -0
  190. package/dist/search/index.js.map +1 -0
  191. package/dist/search/scanning.d.ts +29 -0
  192. package/dist/search/scanning.d.ts.map +1 -0
  193. package/dist/search/scanning.js +103 -0
  194. package/dist/search/scanning.js.map +1 -0
  195. package/dist/stream-fn.d.ts +10 -0
  196. package/dist/stream-fn.d.ts.map +1 -0
  197. package/dist/stream-fn.js +17 -0
  198. package/dist/stream-fn.js.map +1 -0
  199. package/dist/types.d.ts +413 -0
  200. package/dist/types.d.ts.map +1 -0
  201. package/dist/types.js +2 -0
  202. package/dist/types.js.map +1 -0
  203. package/package.json +77 -0
@@ -0,0 +1,228 @@
1
+ import type { SimpleStreamOptions, Transport } from "@sammorrowdrums/mcpi-ai";
2
+ import type { Static, TSchema } from "typebox";
3
+ import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "../types.ts";
4
+ /** Result of a fallible operation. Expected failures are returned as `ok: false` instead of thrown. */
5
+ export type Result<TValue, TError> = {
6
+ ok: true;
7
+ value: TValue;
8
+ } | {
9
+ ok: false;
10
+ error: TError;
11
+ };
12
+ /** Create a successful {@link Result}. */
13
+ export declare function ok<TValue, TError>(value: TValue): Result<TValue, TError>;
14
+ /** Create a failed {@link Result}. */
15
+ export declare function err<TValue, TError>(error: TError): Result<TValue, TError>;
16
+ /** Return the success value or throw the failure error. Intended for tests and explicit adapter boundaries. */
17
+ export declare function getOrThrow<TValue, TError>(result: Result<TValue, TError>): TValue;
18
+ /** Return the success value or `undefined`. Only object values are allowed to avoid truthiness bugs with primitives. */
19
+ export declare function getOrUndefined<TValue extends object, TError>(result: Result<TValue, TError>): TValue | undefined;
20
+ /** Normalize unknown thrown values into Error instances before using them as typed error causes. */
21
+ export declare function toError(error: unknown): Error;
22
+ /**
23
+ * Skill loaded from a `SKILL.md` file or provided by an application.
24
+ *
25
+ * `name`, `description`, and `filePath` are inserted into the system prompt in an XML-formatted block as suggested by agentskills.io.
26
+ * Use {@link formatSkillsForSystemPrompt} to generate the spec-compatible system prompt block.
27
+ */
28
+ export interface Skill {
29
+ /** Stable skill name used for lookup and model-visible listings. */
30
+ name: string;
31
+ /** Short model-visible description of when to use the skill. */
32
+ description: string;
33
+ /** Full skill instructions. */
34
+ content: string;
35
+ /** Absolute path to the skill file. Used for model-visible location and resolving relative references. */
36
+ filePath: string;
37
+ /** Exclude this skill from model-visible skill lists while still allowing explicit application invocation. */
38
+ disableModelInvocation?: boolean;
39
+ }
40
+ /** Prompt template that can be formatted into a prompt for explicit invocation. */
41
+ export interface PromptTemplate {
42
+ /** Stable template name used for lookup or application command routing. */
43
+ name: string;
44
+ /** Optional description for command lists or autocomplete. */
45
+ description?: string;
46
+ /** Template content. Argument placeholders are formatted by `formatPromptTemplateInvocation`. */
47
+ content: string;
48
+ }
49
+ /** Resources made available to explicit invocation methods and system-prompt callbacks. */
50
+ export interface AgentHarnessResources<TSkill extends Skill = Skill, TPromptTemplate extends PromptTemplate = PromptTemplate> {
51
+ /** Prompt templates available for explicit invocation. */
52
+ promptTemplates?: TPromptTemplate[];
53
+ /** Skills available to the model and explicit skill invocation. */
54
+ skills?: TSkill[];
55
+ }
56
+ /** Tool definition executed by an {@link AgentHarness} with an application-defined context. */
57
+ export type AgentHarnessTool<TContext extends object | undefined, TParameters extends TSchema = TSchema, TDetails = unknown> = Omit<AgentTool<TParameters, TDetails>, "execute"> & {
58
+ /** Execute the tool call with the context resolved for the current turn snapshot. */
59
+ execute(toolCallId: string, params: Static<TParameters>, signal: AbortSignal | undefined, onUpdate: AgentToolUpdateCallback<TDetails> | undefined, context: TContext): Promise<AgentToolResult<TDetails>>;
60
+ };
61
+ /** Static tool context or zero-argument provider resolved for each turn snapshot. */
62
+ export type AgentHarnessToolContextSource<TContext extends object | undefined> = TContext | (() => TContext | Promise<TContext>);
63
+ /** Curated provider request options owned by the harness and snapshotted per turn. */
64
+ export interface AgentHarnessStreamOptions {
65
+ /** Preferred transport forwarded to the stream function. */
66
+ transport?: Transport;
67
+ /** Provider request timeout in milliseconds. */
68
+ timeoutMs?: number;
69
+ /** Maximum provider retry attempts. */
70
+ maxRetries?: number;
71
+ /** Optional cap for provider-requested retry delays. */
72
+ maxRetryDelayMs?: number;
73
+ /** Additional request headers merged with auth and lifecycle headers. */
74
+ headers?: Record<string, string>;
75
+ /** Provider metadata forwarded with requests. */
76
+ metadata?: SimpleStreamOptions["metadata"];
77
+ /** Provider cache retention hint. */
78
+ cacheRetention?: SimpleStreamOptions["cacheRetention"];
79
+ }
80
+ /** Per-request stream option patch returned by provider hooks. */
81
+ export interface AgentHarnessStreamOptionsPatch extends Omit<Partial<AgentHarnessStreamOptions>, "headers" | "metadata"> {
82
+ /** Header patch. `undefined` values delete keys; explicit `headers: undefined` clears all headers. */
83
+ headers?: Record<string, string | undefined>;
84
+ /** Metadata patch. `undefined` values delete keys; explicit `metadata: undefined` clears all metadata. */
85
+ metadata?: Record<string, unknown | undefined>;
86
+ }
87
+ /** Kind of filesystem object as addressed by a {@link FileSystem}. Symlinks are not followed automatically. */
88
+ export type FileKind = "file" | "directory" | "symlink";
89
+ /** Stable, backend-independent file error codes returned by {@link FileSystem} file operations. */
90
+ export type FileErrorCode = "aborted" | "not_found" | "permission_denied" | "not_directory" | "is_directory" | "invalid" | "not_supported" | "unknown";
91
+ /** Error returned by {@link FileSystem} file operations. */
92
+ export declare class FileError extends Error {
93
+ /** Backend-independent error code. */
94
+ code: FileErrorCode;
95
+ /** Absolute addressed path associated with the failure, when available. */
96
+ path?: string;
97
+ constructor(code: FileErrorCode, message: string, path?: string, cause?: Error);
98
+ }
99
+ /** Stable, backend-independent execution error codes returned by {@link ExecutionEnv.exec}. */
100
+ export type ExecutionErrorCode = "aborted" | "timeout" | "shell_unavailable" | "spawn_error" | "callback_error" | "unknown";
101
+ /** Error returned by {@link ExecutionEnv.exec}. */
102
+ export declare class ExecutionError extends Error {
103
+ /** Backend-independent error code. */
104
+ code: ExecutionErrorCode;
105
+ constructor(code: ExecutionErrorCode, message: string, cause?: Error);
106
+ }
107
+ /** Stable compaction error codes returned by compaction helpers. */
108
+ export type CompactionErrorCode = "aborted" | "summarization_failed";
109
+ /** Error returned by compaction helpers. */
110
+ export declare class CompactionError extends Error {
111
+ /** Backend-independent error code. */
112
+ code: CompactionErrorCode;
113
+ constructor(code: CompactionErrorCode, message: string, cause?: Error);
114
+ }
115
+ /** Stable branch-summary error codes returned by branch summarization helpers. */
116
+ export type BranchSummaryErrorCode = "aborted" | "summarization_failed";
117
+ /** Error returned by branch summarization helpers. */
118
+ export declare class BranchSummaryError extends Error {
119
+ /** Backend-independent error code. */
120
+ code: BranchSummaryErrorCode;
121
+ constructor(code: BranchSummaryErrorCode, message: string, cause?: Error);
122
+ }
123
+ /** Metadata for one filesystem object in a {@link FileSystem}. */
124
+ export interface FileInfo {
125
+ /** Basename of {@link path}. */
126
+ name: string;
127
+ /** Absolute, syntactically normalized addressed path in the execution environment. Symlinks are not followed. */
128
+ path: string;
129
+ /** Object kind. Symlink targets are not followed; use {@link FileSystem.canonicalPath} explicitly. */
130
+ kind: FileKind;
131
+ /** Size in bytes for the addressed filesystem object. */
132
+ size: number;
133
+ /** Modification time as milliseconds since Unix epoch. */
134
+ mtimeMs: number;
135
+ }
136
+ /**
137
+ * Filesystem capability used by the harness.
138
+ *
139
+ * Paths passed to methods may be absolute or relative to {@link cwd}. Paths returned by file operations are addressed paths
140
+ * in the filesystem namespace, but are not canonicalized through symlinks unless returned by {@link canonicalPath}.
141
+ *
142
+ * Operation methods must never throw or reject. All filesystem failures, including unexpected backend failures, must be
143
+ * encoded in the returned {@link Result}. Implementations must preserve this invariant.
144
+ */
145
+ export interface FileSystem {
146
+ /** Current working directory for relative paths. */
147
+ cwd: string;
148
+ /** Return an absolute addressed path without requiring it to exist and without resolving symlinks. */
149
+ absolutePath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
150
+ /** Join path segments in the filesystem namespace without requiring the result to exist. */
151
+ joinPath(parts: string[], abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
152
+ /** Read a UTF-8 text file. */
153
+ readTextFile(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
154
+ /** Read UTF-8 text lines. Implementations should stop once `maxLines` lines have been read. */
155
+ readTextLines(path: string, options?: {
156
+ maxLines?: number;
157
+ abortSignal?: AbortSignal;
158
+ }): Promise<Result<string[], FileError>>;
159
+ /** Read a binary file. */
160
+ readBinaryFile(path: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;
161
+ /** Create or overwrite a file, creating parent directories when supported. */
162
+ writeFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
163
+ /** Create or append to a file, creating parent directories when supported. */
164
+ appendFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
165
+ /** Atomically rename a file, replacing the destination when it exists. Does not copy across filesystems. */
166
+ renameFile(sourcePath: string, destinationPath: string, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
167
+ /** Return metadata for the addressed path without following symlinks. */
168
+ fileInfo(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;
169
+ /** List direct children of a directory without following symlinks. */
170
+ listDir(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;
171
+ /** Return the canonical path for an existing path, resolving symlinks where supported. */
172
+ canonicalPath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
173
+ /** Return false for missing paths. Other errors, such as permission failures, return a {@link FileError}. */
174
+ exists(path: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
175
+ /** Create a directory. Defaults: `recursive: true`, no abort signal. */
176
+ createDir(path: string, options?: {
177
+ recursive?: boolean;
178
+ abortSignal?: AbortSignal;
179
+ }): Promise<Result<void, FileError>>;
180
+ /** Remove a file or directory. Defaults: `recursive: false`, `force: false`, no abort signal. */
181
+ remove(path: string, options?: {
182
+ recursive?: boolean;
183
+ force?: boolean;
184
+ abortSignal?: AbortSignal;
185
+ }): Promise<Result<void, FileError>>;
186
+ /** Create a temporary directory and return its absolute path. Defaults: `prefix: "tmp-"`, no abort signal. */
187
+ createTempDir(prefix?: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
188
+ /** Create a temporary file and return its absolute path. Defaults: `prefix: ""`, `suffix: ""`, no abort signal. */
189
+ createTempFile(options?: {
190
+ prefix?: string;
191
+ suffix?: string;
192
+ abortSignal?: AbortSignal;
193
+ }): Promise<Result<string, FileError>>;
194
+ /** Release filesystem resources. Must be best-effort and must not throw or reject. */
195
+ cleanup(): Promise<void>;
196
+ }
197
+ /** Options for {@link Shell.exec}. */
198
+ export interface ShellExecOptions {
199
+ /** Working directory for the command. Relative paths are resolved against {@link ExecutionEnv.cwd}. Defaults to {@link ExecutionEnv.cwd}. */
200
+ cwd?: string;
201
+ /** Environment variables for the command. Values override inherited defaults when `inheritEnv` is true. */
202
+ env?: Record<string, string>;
203
+ /** Whether to inherit the execution environment's default variables. Defaults to true. */
204
+ inheritEnv?: boolean;
205
+ /** Timeout in seconds. Implementations should return a timeout error when the command exceeds this duration. Defaults to no timeout. */
206
+ timeout?: number;
207
+ /** Abort signal used to terminate the command. Defaults to no abort signal. */
208
+ abortSignal?: AbortSignal;
209
+ /** Called with stdout chunks as they are produced. */
210
+ onStdout?: (chunk: string) => void;
211
+ /** Called with stderr chunks as they are produced. */
212
+ onStderr?: (chunk: string) => void;
213
+ }
214
+ /** Shell execution capability used by the harness. */
215
+ export interface Shell {
216
+ /** Execute a shell command in {@link FileSystem.cwd} unless `options.cwd` is provided. */
217
+ exec(command: string, options?: ShellExecOptions): Promise<Result<{
218
+ stdout: string;
219
+ stderr: string;
220
+ exitCode: number;
221
+ }, ExecutionError>>;
222
+ /** Release shell resources. Must be best-effort and must not throw or reject. */
223
+ cleanup(): Promise<void>;
224
+ }
225
+ /** Filesystem and process execution environment used by the harness. */
226
+ export interface ExecutionEnv extends FileSystem, Shell {
227
+ }
228
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/harness/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SimpleStreamOptions, Transport } from \"@sammorrowdrums/mcpi-ai\";\nimport type { Static, TSchema } from \"typebox\";\nimport type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from \"../types.ts\";\n\n/** Result of a fallible operation. Expected failures are returned as `ok: false` instead of thrown. */\nexport type Result<TValue, TError> = { ok: true; value: TValue } | { ok: false; error: TError };\n\n/** Create a successful {@link Result}. */\nexport function ok<TValue, TError>(value: TValue): Result<TValue, TError> {\n\treturn { ok: true, value };\n}\n\n/** Create a failed {@link Result}. */\nexport function err<TValue, TError>(error: TError): Result<TValue, TError> {\n\treturn { ok: false, error };\n}\n\n/** Return the success value or throw the failure error. Intended for tests and explicit adapter boundaries. */\nexport function getOrThrow<TValue, TError>(result: Result<TValue, TError>): TValue {\n\tif (!result.ok) throw result.error;\n\treturn result.value;\n}\n\n/** Return the success value or `undefined`. Only object values are allowed to avoid truthiness bugs with primitives. */\nexport function getOrUndefined<TValue extends object, TError>(result: Result<TValue, TError>): TValue | undefined {\n\treturn result.ok ? result.value : undefined;\n}\n\n/** Normalize unknown thrown values into Error instances before using them as typed error causes. */\nexport function toError(error: unknown): Error {\n\tif (error instanceof Error) return error;\n\tif (typeof error === \"string\") return new Error(error);\n\ttry {\n\t\treturn new Error(JSON.stringify(error));\n\t} catch {\n\t\treturn new Error(String(error));\n\t}\n}\n\n/**\n * Skill loaded from a `SKILL.md` file or provided by an application.\n *\n * `name`, `description`, and `filePath` are inserted into the system prompt in an XML-formatted block as suggested by agentskills.io.\n * Use {@link formatSkillsForSystemPrompt} to generate the spec-compatible system prompt block.\n */\nexport interface Skill {\n\t/** Stable skill name used for lookup and model-visible listings. */\n\tname: string;\n\t/** Short model-visible description of when to use the skill. */\n\tdescription: string;\n\t/** Full skill instructions. */\n\tcontent: string;\n\t/** Absolute path to the skill file. Used for model-visible location and resolving relative references. */\n\tfilePath: string;\n\t/** Exclude this skill from model-visible skill lists while still allowing explicit application invocation. */\n\tdisableModelInvocation?: boolean;\n}\n\n/** Prompt template that can be formatted into a prompt for explicit invocation. */\nexport interface PromptTemplate {\n\t/** Stable template name used for lookup or application command routing. */\n\tname: string;\n\t/** Optional description for command lists or autocomplete. */\n\tdescription?: string;\n\t/** Template content. Argument placeholders are formatted by `formatPromptTemplateInvocation`. */\n\tcontent: string;\n}\n\n/** Resources made available to explicit invocation methods and system-prompt callbacks. */\nexport interface AgentHarnessResources<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n> {\n\t/** Prompt templates available for explicit invocation. */\n\tpromptTemplates?: TPromptTemplate[];\n\t/** Skills available to the model and explicit skill invocation. */\n\tskills?: TSkill[];\n}\n\n/** Tool definition executed by an {@link AgentHarness} with an application-defined context. */\nexport type AgentHarnessTool<\n\tTContext extends object | undefined,\n\tTParameters extends TSchema = TSchema,\n\tTDetails = unknown,\n> = Omit<AgentTool<TParameters, TDetails>, \"execute\"> & {\n\t/** Execute the tool call with the context resolved for the current turn snapshot. */\n\texecute(\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal: AbortSignal | undefined,\n\t\tonUpdate: AgentToolUpdateCallback<TDetails> | undefined,\n\t\tcontext: TContext,\n\t): Promise<AgentToolResult<TDetails>>;\n};\n\n/** Static tool context or zero-argument provider resolved for each turn snapshot. */\nexport type AgentHarnessToolContextSource<TContext extends object | undefined> =\n\t| TContext\n\t| (() => TContext | Promise<TContext>);\n\n/** Curated provider request options owned by the harness and snapshotted per turn. */\nexport interface AgentHarnessStreamOptions {\n\t/** Preferred transport forwarded to the stream function. */\n\ttransport?: Transport;\n\t/** Provider request timeout in milliseconds. */\n\ttimeoutMs?: number;\n\t/** Maximum provider retry attempts. */\n\tmaxRetries?: number;\n\t/** Optional cap for provider-requested retry delays. */\n\tmaxRetryDelayMs?: number;\n\t/** Additional request headers merged with auth and lifecycle headers. */\n\theaders?: Record<string, string>;\n\t/** Provider metadata forwarded with requests. */\n\tmetadata?: SimpleStreamOptions[\"metadata\"];\n\t/** Provider cache retention hint. */\n\tcacheRetention?: SimpleStreamOptions[\"cacheRetention\"];\n}\n\n/** Per-request stream option patch returned by provider hooks. */\nexport interface AgentHarnessStreamOptionsPatch\n\textends Omit<Partial<AgentHarnessStreamOptions>, \"headers\" | \"metadata\"> {\n\t/** Header patch. `undefined` values delete keys; explicit `headers: undefined` clears all headers. */\n\theaders?: Record<string, string | undefined>;\n\t/** Metadata patch. `undefined` values delete keys; explicit `metadata: undefined` clears all metadata. */\n\tmetadata?: Record<string, unknown | undefined>;\n}\n\n/** Kind of filesystem object as addressed by a {@link FileSystem}. Symlinks are not followed automatically. */\nexport type FileKind = \"file\" | \"directory\" | \"symlink\";\n\n/** Stable, backend-independent file error codes returned by {@link FileSystem} file operations. */\nexport type FileErrorCode =\n\t| \"aborted\"\n\t| \"not_found\"\n\t| \"permission_denied\"\n\t| \"not_directory\"\n\t| \"is_directory\"\n\t| \"invalid\"\n\t| \"not_supported\"\n\t| \"unknown\";\n\n/** Error returned by {@link FileSystem} file operations. */\nexport class FileError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: FileErrorCode;\n\t/** Absolute addressed path associated with the failure, when available. */\n\tpublic path?: string;\n\n\tconstructor(code: FileErrorCode, message: string, path?: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"FileError\";\n\t\tthis.code = code;\n\t\tthis.path = path;\n\t}\n}\n\n/** Stable, backend-independent execution error codes returned by {@link ExecutionEnv.exec}. */\nexport type ExecutionErrorCode =\n\t| \"aborted\"\n\t| \"timeout\"\n\t| \"shell_unavailable\"\n\t| \"spawn_error\"\n\t| \"callback_error\"\n\t| \"unknown\";\n\n/** Error returned by {@link ExecutionEnv.exec}. */\nexport class ExecutionError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: ExecutionErrorCode;\n\n\tconstructor(code: ExecutionErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"ExecutionError\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Stable compaction error codes returned by compaction helpers. */\nexport type CompactionErrorCode = \"aborted\" | \"summarization_failed\";\n\n/** Error returned by compaction helpers. */\nexport class CompactionError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: CompactionErrorCode;\n\n\tconstructor(code: CompactionErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"CompactionError\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Stable branch-summary error codes returned by branch summarization helpers. */\nexport type BranchSummaryErrorCode = \"aborted\" | \"summarization_failed\";\n\n/** Error returned by branch summarization helpers. */\nexport class BranchSummaryError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: BranchSummaryErrorCode;\n\n\tconstructor(code: BranchSummaryErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"BranchSummaryError\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Metadata for one filesystem object in a {@link FileSystem}. */\nexport interface FileInfo {\n\t/** Basename of {@link path}. */\n\tname: string;\n\t/** Absolute, syntactically normalized addressed path in the execution environment. Symlinks are not followed. */\n\tpath: string;\n\t/** Object kind. Symlink targets are not followed; use {@link FileSystem.canonicalPath} explicitly. */\n\tkind: FileKind;\n\t/** Size in bytes for the addressed filesystem object. */\n\tsize: number;\n\t/** Modification time as milliseconds since Unix epoch. */\n\tmtimeMs: number;\n}\n\n/**\n * Filesystem capability used by the harness.\n *\n * Paths passed to methods may be absolute or relative to {@link cwd}. Paths returned by file operations are addressed paths\n * in the filesystem namespace, but are not canonicalized through symlinks unless returned by {@link canonicalPath}.\n *\n * Operation methods must never throw or reject. All filesystem failures, including unexpected backend failures, must be\n * encoded in the returned {@link Result}. Implementations must preserve this invariant.\n */\nexport interface FileSystem {\n\t/** Current working directory for relative paths. */\n\tcwd: string;\n\n\t/** Return an absolute addressed path without requiring it to exist and without resolving symlinks. */\n\tabsolutePath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Join path segments in the filesystem namespace without requiring the result to exist. */\n\tjoinPath(parts: string[], abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Read a UTF-8 text file. */\n\treadTextFile(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Read UTF-8 text lines. Implementations should stop once `maxLines` lines have been read. */\n\treadTextLines(\n\t\tpath: string,\n\t\toptions?: { maxLines?: number; abortSignal?: AbortSignal },\n\t): Promise<Result<string[], FileError>>;\n\t/** Read a binary file. */\n\treadBinaryFile(path: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;\n\t/** Create or overwrite a file, creating parent directories when supported. */\n\twriteFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;\n\t/** Create or append to a file, creating parent directories when supported. */\n\tappendFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;\n\t/** Atomically rename a file, replacing the destination when it exists. Does not copy across filesystems. */\n\trenameFile(sourcePath: string, destinationPath: string, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;\n\t/** Return metadata for the addressed path without following symlinks. */\n\tfileInfo(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;\n\t/** List direct children of a directory without following symlinks. */\n\tlistDir(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;\n\t/** Return the canonical path for an existing path, resolving symlinks where supported. */\n\tcanonicalPath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Return false for missing paths. Other errors, such as permission failures, return a {@link FileError}. */\n\texists(path: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;\n\t/** Create a directory. Defaults: `recursive: true`, no abort signal. */\n\tcreateDir(\n\t\tpath: string,\n\t\toptions?: { recursive?: boolean; abortSignal?: AbortSignal },\n\t): Promise<Result<void, FileError>>;\n\t/** Remove a file or directory. Defaults: `recursive: false`, `force: false`, no abort signal. */\n\tremove(\n\t\tpath: string,\n\t\toptions?: { recursive?: boolean; force?: boolean; abortSignal?: AbortSignal },\n\t): Promise<Result<void, FileError>>;\n\t/** Create a temporary directory and return its absolute path. Defaults: `prefix: \"tmp-\"`, no abort signal. */\n\tcreateTempDir(prefix?: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Create a temporary file and return its absolute path. Defaults: `prefix: \"\"`, `suffix: \"\"`, no abort signal. */\n\tcreateTempFile(options?: {\n\t\tprefix?: string;\n\t\tsuffix?: string;\n\t\tabortSignal?: AbortSignal;\n\t}): Promise<Result<string, FileError>>;\n\n\t/** Release filesystem resources. Must be best-effort and must not throw or reject. */\n\tcleanup(): Promise<void>;\n}\n\n/** Options for {@link Shell.exec}. */\nexport interface ShellExecOptions {\n\t/** Working directory for the command. Relative paths are resolved against {@link ExecutionEnv.cwd}. Defaults to {@link ExecutionEnv.cwd}. */\n\tcwd?: string;\n\t/** Environment variables for the command. Values override inherited defaults when `inheritEnv` is true. */\n\tenv?: Record<string, string>;\n\t/** Whether to inherit the execution environment's default variables. Defaults to true. */\n\tinheritEnv?: boolean;\n\t/** Timeout in seconds. Implementations should return a timeout error when the command exceeds this duration. Defaults to no timeout. */\n\ttimeout?: number;\n\t/** Abort signal used to terminate the command. Defaults to no abort signal. */\n\tabortSignal?: AbortSignal;\n\t/** Called with stdout chunks as they are produced. */\n\tonStdout?: (chunk: string) => void;\n\t/** Called with stderr chunks as they are produced. */\n\tonStderr?: (chunk: string) => void;\n}\n\n/** Shell execution capability used by the harness. */\nexport interface Shell {\n\t/** Execute a shell command in {@link FileSystem.cwd} unless `options.cwd` is provided. */\n\texec(\n\t\tcommand: string,\n\t\toptions?: ShellExecOptions,\n\t): Promise<Result<{ stdout: string; stderr: string; exitCode: number }, ExecutionError>>;\n\t/** Release shell resources. Must be best-effort and must not throw or reject. */\n\tcleanup(): Promise<void>;\n}\n\n/** Filesystem and process execution environment used by the harness. */\nexport interface ExecutionEnv extends FileSystem, Shell {}\n"]}
@@ -0,0 +1,75 @@
1
+ /** Create a successful {@link Result}. */
2
+ export function ok(value) {
3
+ return { ok: true, value };
4
+ }
5
+ /** Create a failed {@link Result}. */
6
+ export function err(error) {
7
+ return { ok: false, error };
8
+ }
9
+ /** Return the success value or throw the failure error. Intended for tests and explicit adapter boundaries. */
10
+ export function getOrThrow(result) {
11
+ if (!result.ok)
12
+ throw result.error;
13
+ return result.value;
14
+ }
15
+ /** Return the success value or `undefined`. Only object values are allowed to avoid truthiness bugs with primitives. */
16
+ export function getOrUndefined(result) {
17
+ return result.ok ? result.value : undefined;
18
+ }
19
+ /** Normalize unknown thrown values into Error instances before using them as typed error causes. */
20
+ export function toError(error) {
21
+ if (error instanceof Error)
22
+ return error;
23
+ if (typeof error === "string")
24
+ return new Error(error);
25
+ try {
26
+ return new Error(JSON.stringify(error));
27
+ }
28
+ catch {
29
+ return new Error(String(error));
30
+ }
31
+ }
32
+ /** Error returned by {@link FileSystem} file operations. */
33
+ export class FileError extends Error {
34
+ /** Backend-independent error code. */
35
+ code;
36
+ /** Absolute addressed path associated with the failure, when available. */
37
+ path;
38
+ constructor(code, message, path, cause) {
39
+ super(message, cause === undefined ? undefined : { cause });
40
+ this.name = "FileError";
41
+ this.code = code;
42
+ this.path = path;
43
+ }
44
+ }
45
+ /** Error returned by {@link ExecutionEnv.exec}. */
46
+ export class ExecutionError extends Error {
47
+ /** Backend-independent error code. */
48
+ code;
49
+ constructor(code, message, cause) {
50
+ super(message, cause === undefined ? undefined : { cause });
51
+ this.name = "ExecutionError";
52
+ this.code = code;
53
+ }
54
+ }
55
+ /** Error returned by compaction helpers. */
56
+ export class CompactionError extends Error {
57
+ /** Backend-independent error code. */
58
+ code;
59
+ constructor(code, message, cause) {
60
+ super(message, cause === undefined ? undefined : { cause });
61
+ this.name = "CompactionError";
62
+ this.code = code;
63
+ }
64
+ }
65
+ /** Error returned by branch summarization helpers. */
66
+ export class BranchSummaryError extends Error {
67
+ /** Backend-independent error code. */
68
+ code;
69
+ constructor(code, message, cause) {
70
+ super(message, cause === undefined ? undefined : { cause });
71
+ this.name = "BranchSummaryError";
72
+ this.code = code;
73
+ }
74
+ }
75
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/harness/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SimpleStreamOptions, Transport } from \"@sammorrowdrums/mcpi-ai\";\nimport type { Static, TSchema } from \"typebox\";\nimport type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from \"../types.ts\";\n\n/** Result of a fallible operation. Expected failures are returned as `ok: false` instead of thrown. */\nexport type Result<TValue, TError> = { ok: true; value: TValue } | { ok: false; error: TError };\n\n/** Create a successful {@link Result}. */\nexport function ok<TValue, TError>(value: TValue): Result<TValue, TError> {\n\treturn { ok: true, value };\n}\n\n/** Create a failed {@link Result}. */\nexport function err<TValue, TError>(error: TError): Result<TValue, TError> {\n\treturn { ok: false, error };\n}\n\n/** Return the success value or throw the failure error. Intended for tests and explicit adapter boundaries. */\nexport function getOrThrow<TValue, TError>(result: Result<TValue, TError>): TValue {\n\tif (!result.ok) throw result.error;\n\treturn result.value;\n}\n\n/** Return the success value or `undefined`. Only object values are allowed to avoid truthiness bugs with primitives. */\nexport function getOrUndefined<TValue extends object, TError>(result: Result<TValue, TError>): TValue | undefined {\n\treturn result.ok ? result.value : undefined;\n}\n\n/** Normalize unknown thrown values into Error instances before using them as typed error causes. */\nexport function toError(error: unknown): Error {\n\tif (error instanceof Error) return error;\n\tif (typeof error === \"string\") return new Error(error);\n\ttry {\n\t\treturn new Error(JSON.stringify(error));\n\t} catch {\n\t\treturn new Error(String(error));\n\t}\n}\n\n/**\n * Skill loaded from a `SKILL.md` file or provided by an application.\n *\n * `name`, `description`, and `filePath` are inserted into the system prompt in an XML-formatted block as suggested by agentskills.io.\n * Use {@link formatSkillsForSystemPrompt} to generate the spec-compatible system prompt block.\n */\nexport interface Skill {\n\t/** Stable skill name used for lookup and model-visible listings. */\n\tname: string;\n\t/** Short model-visible description of when to use the skill. */\n\tdescription: string;\n\t/** Full skill instructions. */\n\tcontent: string;\n\t/** Absolute path to the skill file. Used for model-visible location and resolving relative references. */\n\tfilePath: string;\n\t/** Exclude this skill from model-visible skill lists while still allowing explicit application invocation. */\n\tdisableModelInvocation?: boolean;\n}\n\n/** Prompt template that can be formatted into a prompt for explicit invocation. */\nexport interface PromptTemplate {\n\t/** Stable template name used for lookup or application command routing. */\n\tname: string;\n\t/** Optional description for command lists or autocomplete. */\n\tdescription?: string;\n\t/** Template content. Argument placeholders are formatted by `formatPromptTemplateInvocation`. */\n\tcontent: string;\n}\n\n/** Resources made available to explicit invocation methods and system-prompt callbacks. */\nexport interface AgentHarnessResources<\n\tTSkill extends Skill = Skill,\n\tTPromptTemplate extends PromptTemplate = PromptTemplate,\n> {\n\t/** Prompt templates available for explicit invocation. */\n\tpromptTemplates?: TPromptTemplate[];\n\t/** Skills available to the model and explicit skill invocation. */\n\tskills?: TSkill[];\n}\n\n/** Tool definition executed by an {@link AgentHarness} with an application-defined context. */\nexport type AgentHarnessTool<\n\tTContext extends object | undefined,\n\tTParameters extends TSchema = TSchema,\n\tTDetails = unknown,\n> = Omit<AgentTool<TParameters, TDetails>, \"execute\"> & {\n\t/** Execute the tool call with the context resolved for the current turn snapshot. */\n\texecute(\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal: AbortSignal | undefined,\n\t\tonUpdate: AgentToolUpdateCallback<TDetails> | undefined,\n\t\tcontext: TContext,\n\t): Promise<AgentToolResult<TDetails>>;\n};\n\n/** Static tool context or zero-argument provider resolved for each turn snapshot. */\nexport type AgentHarnessToolContextSource<TContext extends object | undefined> =\n\t| TContext\n\t| (() => TContext | Promise<TContext>);\n\n/** Curated provider request options owned by the harness and snapshotted per turn. */\nexport interface AgentHarnessStreamOptions {\n\t/** Preferred transport forwarded to the stream function. */\n\ttransport?: Transport;\n\t/** Provider request timeout in milliseconds. */\n\ttimeoutMs?: number;\n\t/** Maximum provider retry attempts. */\n\tmaxRetries?: number;\n\t/** Optional cap for provider-requested retry delays. */\n\tmaxRetryDelayMs?: number;\n\t/** Additional request headers merged with auth and lifecycle headers. */\n\theaders?: Record<string, string>;\n\t/** Provider metadata forwarded with requests. */\n\tmetadata?: SimpleStreamOptions[\"metadata\"];\n\t/** Provider cache retention hint. */\n\tcacheRetention?: SimpleStreamOptions[\"cacheRetention\"];\n}\n\n/** Per-request stream option patch returned by provider hooks. */\nexport interface AgentHarnessStreamOptionsPatch\n\textends Omit<Partial<AgentHarnessStreamOptions>, \"headers\" | \"metadata\"> {\n\t/** Header patch. `undefined` values delete keys; explicit `headers: undefined` clears all headers. */\n\theaders?: Record<string, string | undefined>;\n\t/** Metadata patch. `undefined` values delete keys; explicit `metadata: undefined` clears all metadata. */\n\tmetadata?: Record<string, unknown | undefined>;\n}\n\n/** Kind of filesystem object as addressed by a {@link FileSystem}. Symlinks are not followed automatically. */\nexport type FileKind = \"file\" | \"directory\" | \"symlink\";\n\n/** Stable, backend-independent file error codes returned by {@link FileSystem} file operations. */\nexport type FileErrorCode =\n\t| \"aborted\"\n\t| \"not_found\"\n\t| \"permission_denied\"\n\t| \"not_directory\"\n\t| \"is_directory\"\n\t| \"invalid\"\n\t| \"not_supported\"\n\t| \"unknown\";\n\n/** Error returned by {@link FileSystem} file operations. */\nexport class FileError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: FileErrorCode;\n\t/** Absolute addressed path associated with the failure, when available. */\n\tpublic path?: string;\n\n\tconstructor(code: FileErrorCode, message: string, path?: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"FileError\";\n\t\tthis.code = code;\n\t\tthis.path = path;\n\t}\n}\n\n/** Stable, backend-independent execution error codes returned by {@link ExecutionEnv.exec}. */\nexport type ExecutionErrorCode =\n\t| \"aborted\"\n\t| \"timeout\"\n\t| \"shell_unavailable\"\n\t| \"spawn_error\"\n\t| \"callback_error\"\n\t| \"unknown\";\n\n/** Error returned by {@link ExecutionEnv.exec}. */\nexport class ExecutionError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: ExecutionErrorCode;\n\n\tconstructor(code: ExecutionErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"ExecutionError\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Stable compaction error codes returned by compaction helpers. */\nexport type CompactionErrorCode = \"aborted\" | \"summarization_failed\";\n\n/** Error returned by compaction helpers. */\nexport class CompactionError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: CompactionErrorCode;\n\n\tconstructor(code: CompactionErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"CompactionError\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Stable branch-summary error codes returned by branch summarization helpers. */\nexport type BranchSummaryErrorCode = \"aborted\" | \"summarization_failed\";\n\n/** Error returned by branch summarization helpers. */\nexport class BranchSummaryError extends Error {\n\t/** Backend-independent error code. */\n\tpublic code: BranchSummaryErrorCode;\n\n\tconstructor(code: BranchSummaryErrorCode, message: string, cause?: Error) {\n\t\tsuper(message, cause === undefined ? undefined : { cause });\n\t\tthis.name = \"BranchSummaryError\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Metadata for one filesystem object in a {@link FileSystem}. */\nexport interface FileInfo {\n\t/** Basename of {@link path}. */\n\tname: string;\n\t/** Absolute, syntactically normalized addressed path in the execution environment. Symlinks are not followed. */\n\tpath: string;\n\t/** Object kind. Symlink targets are not followed; use {@link FileSystem.canonicalPath} explicitly. */\n\tkind: FileKind;\n\t/** Size in bytes for the addressed filesystem object. */\n\tsize: number;\n\t/** Modification time as milliseconds since Unix epoch. */\n\tmtimeMs: number;\n}\n\n/**\n * Filesystem capability used by the harness.\n *\n * Paths passed to methods may be absolute or relative to {@link cwd}. Paths returned by file operations are addressed paths\n * in the filesystem namespace, but are not canonicalized through symlinks unless returned by {@link canonicalPath}.\n *\n * Operation methods must never throw or reject. All filesystem failures, including unexpected backend failures, must be\n * encoded in the returned {@link Result}. Implementations must preserve this invariant.\n */\nexport interface FileSystem {\n\t/** Current working directory for relative paths. */\n\tcwd: string;\n\n\t/** Return an absolute addressed path without requiring it to exist and without resolving symlinks. */\n\tabsolutePath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Join path segments in the filesystem namespace without requiring the result to exist. */\n\tjoinPath(parts: string[], abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Read a UTF-8 text file. */\n\treadTextFile(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Read UTF-8 text lines. Implementations should stop once `maxLines` lines have been read. */\n\treadTextLines(\n\t\tpath: string,\n\t\toptions?: { maxLines?: number; abortSignal?: AbortSignal },\n\t): Promise<Result<string[], FileError>>;\n\t/** Read a binary file. */\n\treadBinaryFile(path: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;\n\t/** Create or overwrite a file, creating parent directories when supported. */\n\twriteFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;\n\t/** Create or append to a file, creating parent directories when supported. */\n\tappendFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;\n\t/** Atomically rename a file, replacing the destination when it exists. Does not copy across filesystems. */\n\trenameFile(sourcePath: string, destinationPath: string, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;\n\t/** Return metadata for the addressed path without following symlinks. */\n\tfileInfo(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;\n\t/** List direct children of a directory without following symlinks. */\n\tlistDir(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;\n\t/** Return the canonical path for an existing path, resolving symlinks where supported. */\n\tcanonicalPath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Return false for missing paths. Other errors, such as permission failures, return a {@link FileError}. */\n\texists(path: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;\n\t/** Create a directory. Defaults: `recursive: true`, no abort signal. */\n\tcreateDir(\n\t\tpath: string,\n\t\toptions?: { recursive?: boolean; abortSignal?: AbortSignal },\n\t): Promise<Result<void, FileError>>;\n\t/** Remove a file or directory. Defaults: `recursive: false`, `force: false`, no abort signal. */\n\tremove(\n\t\tpath: string,\n\t\toptions?: { recursive?: boolean; force?: boolean; abortSignal?: AbortSignal },\n\t): Promise<Result<void, FileError>>;\n\t/** Create a temporary directory and return its absolute path. Defaults: `prefix: \"tmp-\"`, no abort signal. */\n\tcreateTempDir(prefix?: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;\n\t/** Create a temporary file and return its absolute path. Defaults: `prefix: \"\"`, `suffix: \"\"`, no abort signal. */\n\tcreateTempFile(options?: {\n\t\tprefix?: string;\n\t\tsuffix?: string;\n\t\tabortSignal?: AbortSignal;\n\t}): Promise<Result<string, FileError>>;\n\n\t/** Release filesystem resources. Must be best-effort and must not throw or reject. */\n\tcleanup(): Promise<void>;\n}\n\n/** Options for {@link Shell.exec}. */\nexport interface ShellExecOptions {\n\t/** Working directory for the command. Relative paths are resolved against {@link ExecutionEnv.cwd}. Defaults to {@link ExecutionEnv.cwd}. */\n\tcwd?: string;\n\t/** Environment variables for the command. Values override inherited defaults when `inheritEnv` is true. */\n\tenv?: Record<string, string>;\n\t/** Whether to inherit the execution environment's default variables. Defaults to true. */\n\tinheritEnv?: boolean;\n\t/** Timeout in seconds. Implementations should return a timeout error when the command exceeds this duration. Defaults to no timeout. */\n\ttimeout?: number;\n\t/** Abort signal used to terminate the command. Defaults to no abort signal. */\n\tabortSignal?: AbortSignal;\n\t/** Called with stdout chunks as they are produced. */\n\tonStdout?: (chunk: string) => void;\n\t/** Called with stderr chunks as they are produced. */\n\tonStderr?: (chunk: string) => void;\n}\n\n/** Shell execution capability used by the harness. */\nexport interface Shell {\n\t/** Execute a shell command in {@link FileSystem.cwd} unless `options.cwd` is provided. */\n\texec(\n\t\tcommand: string,\n\t\toptions?: ShellExecOptions,\n\t): Promise<Result<{ stdout: string; stderr: string; exitCode: number }, ExecutionError>>;\n\t/** Release shell resources. Must be best-effort and must not throw or reject. */\n\tcleanup(): Promise<void>;\n}\n\n/** Filesystem and process execution environment used by the harness. */\nexport interface ExecutionEnv extends FileSystem, Shell {}\n"]}
@@ -0,0 +1,22 @@
1
+ import { type ExecutionEnv, ExecutionError, type Result, type ShellExecOptions } from "../types.ts";
2
+ import { type TruncationResult } from "./truncate.ts";
3
+ export interface ShellCaptureProgress {
4
+ output: string;
5
+ truncation: TruncationResult;
6
+ fullOutputPath?: string;
7
+ lastLineBytes: number;
8
+ }
9
+ export interface ShellCaptureOptions extends Omit<ShellExecOptions, "onStdout" | "onStderr"> {
10
+ onChunk?: (chunk: string, getProgress: () => ShellCaptureProgress) => void;
11
+ /** Return shell execution failures with captured output instead of as a failed Result. */
12
+ returnExecutionErrors?: boolean;
13
+ }
14
+ export interface ShellCaptureResult extends ShellCaptureProgress {
15
+ exitCode: number | undefined;
16
+ cancelled: boolean;
17
+ truncated: boolean;
18
+ executionError?: ExecutionError;
19
+ }
20
+ export declare function sanitizeBinaryOutput(str: string): string;
21
+ export declare function executeShellWithCapture(env: ExecutionEnv, command: string, options?: ShellCaptureOptions): Promise<Result<ShellCaptureResult, ExecutionError>>;
22
+ //# sourceMappingURL=shell-output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-output.d.ts","sourceRoot":"","sources":["../../../src/harness/utils/shell-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,cAAc,EAAW,KAAK,MAAM,EAAE,KAAK,gBAAgB,EAAW,MAAM,aAAa,CAAC;AACtH,OAAO,EAAwC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAE1G,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,gBAAgB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,UAAU,CAAC;IAC3F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,KAAK,IAAI,CAAC;IAC3E,0FAA0F;IAC1F,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,kBAAmB,SAAQ,oBAAoB;IAC/D,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC;AAQD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWxD;AAUD,wBAAsB,uBAAuB,CAC5C,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC,CA4IrD","sourcesContent":["import { type ExecutionEnv, ExecutionError, err, ok, type Result, type ShellExecOptions, toError } from \"../types.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type TruncationResult, truncateTail } from \"./truncate.ts\";\n\nexport interface ShellCaptureProgress {\n\toutput: string;\n\ttruncation: TruncationResult;\n\tfullOutputPath?: string;\n\tlastLineBytes: number;\n}\n\nexport interface ShellCaptureOptions extends Omit<ShellExecOptions, \"onStdout\" | \"onStderr\"> {\n\tonChunk?: (chunk: string, getProgress: () => ShellCaptureProgress) => void;\n\t/** Return shell execution failures with captured output instead of as a failed Result. */\n\treturnExecutionErrors?: boolean;\n}\n\nexport interface ShellCaptureResult extends ShellCaptureProgress {\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\texecutionError?: ExecutionError;\n}\n\nfunction toExecutionError(error: unknown): ExecutionError {\n\tif (error instanceof ExecutionError) return error;\n\tconst cause = toError(error);\n\treturn new ExecutionError(\"unknown\", cause.message, cause);\n}\n\nexport function sanitizeBinaryOutput(str: string): string {\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\tconst code = char.codePointAt(0);\n\t\t\tif (code === undefined) return false;\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\t\t\tif (code <= 0x1f) return false;\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n\nfunction trimToLastUtf8Bytes(text: string, maxBytes: number, encoder: { encode(input?: string): Uint8Array }): string {\n\tconst bytes = encoder.encode(text);\n\tif (bytes.byteLength <= maxBytes) return text;\n\tlet start = bytes.byteLength - maxBytes;\n\twhile (start < bytes.byteLength && ((bytes[start] ?? 0) & 0xc0) === 0x80) start++;\n\treturn new TextDecoder().decode(bytes.subarray(start));\n}\n\nexport async function executeShellWithCapture(\n\tenv: ExecutionEnv,\n\tcommand: string,\n\toptions?: ShellCaptureOptions,\n): Promise<Result<ShellCaptureResult, ExecutionError>> {\n\tlet tailOutput = \"\";\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\tconst encoder = new TextEncoder();\n\n\tlet totalBytes = 0;\n\tlet completedLines = 0;\n\tlet hasOpenLine = false;\n\tlet currentLineBytes = 0;\n\tlet fullOutputPath: string | undefined;\n\tlet fullOutputRequested = false;\n\tlet acceptingOutput = true;\n\tlet writeChain: Promise<Result<void, ExecutionError>> = Promise.resolve(ok(undefined));\n\tlet captureError: ExecutionError | undefined;\n\n\tconst appendFullOutput = (text: string): void => {\n\t\tif (!fullOutputRequested || captureError) return;\n\t\twriteChain = writeChain.then(async (previous) => {\n\t\t\tif (!previous.ok) return previous;\n\t\t\tif (!fullOutputPath) return err(new ExecutionError(\"unknown\", \"Full output path was not created\"));\n\t\t\tconst appendResult = await env.appendFile(fullOutputPath, text);\n\t\t\treturn appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));\n\t\t});\n\t};\n\n\tconst ensureFullOutputFile = (initialContent: string): void => {\n\t\tif (fullOutputRequested || captureError) return;\n\t\tfullOutputRequested = true;\n\t\twriteChain = writeChain.then(async (previous) => {\n\t\t\tif (!previous.ok) return previous;\n\t\t\tconst tempFile = await env.createTempFile({ prefix: \"bash-\", suffix: \".log\" });\n\t\t\tif (!tempFile.ok) return err(toExecutionError(tempFile.error));\n\t\t\tfullOutputPath = tempFile.value;\n\t\t\tconst appendResult = await env.appendFile(tempFile.value, initialContent);\n\t\t\treturn appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));\n\t\t});\n\t};\n\n\tconst createProgress = (): ShellCaptureProgress => {\n\t\tconst tailTruncation = truncateTail(tailOutput);\n\t\tconst totalLines = completedLines + (hasOpenLine ? 1 : 0);\n\t\tconst truncated = totalLines > DEFAULT_MAX_LINES || totalBytes > DEFAULT_MAX_BYTES;\n\t\tconst truncation: TruncationResult = {\n\t\t\t...tailTruncation,\n\t\t\ttruncated,\n\t\t\ttruncatedBy: truncated\n\t\t\t\t? (tailTruncation.truncatedBy ?? (totalBytes > DEFAULT_MAX_BYTES ? \"bytes\" : \"lines\"))\n\t\t\t\t: null,\n\t\t\ttotalLines,\n\t\t\ttotalBytes,\n\t\t};\n\t\treturn {\n\t\t\toutput: truncated ? truncation.content : tailOutput,\n\t\t\ttruncation,\n\t\t\tfullOutputPath,\n\t\t\tlastLineBytes: currentLineBytes,\n\t\t};\n\t};\n\n\tconst onChunk = (chunk: string): void => {\n\t\tif (!acceptingOutput) return;\n\t\ttry {\n\t\t\tconst text = sanitizeBinaryOutput(chunk).replace(/\\r/g, \"\");\n\t\t\tconst textBytes = encoder.encode(text).byteLength;\n\t\t\ttotalBytes += textBytes;\n\t\t\tconst newlineCount = text.split(\"\\n\").length - 1;\n\t\t\tcompletedLines += newlineCount;\n\t\t\tconst lastNewline = text.lastIndexOf(\"\\n\");\n\t\t\tif (lastNewline >= 0) {\n\t\t\t\tconst trailingText = text.slice(lastNewline + 1);\n\t\t\t\tcurrentLineBytes = encoder.encode(trailingText).byteLength;\n\t\t\t\thasOpenLine = trailingText.length > 0;\n\t\t\t} else if (text.length > 0) {\n\t\t\t\tcurrentLineBytes += textBytes;\n\t\t\t\thasOpenLine = true;\n\t\t\t}\n\n\t\t\ttailOutput += text;\n\t\t\tconst totalLines = completedLines + (hasOpenLine ? 1 : 0);\n\t\t\tif ((totalBytes > DEFAULT_MAX_BYTES || totalLines > DEFAULT_MAX_LINES) && !fullOutputRequested) {\n\t\t\t\tensureFullOutputFile(tailOutput);\n\t\t\t} else if (fullOutputRequested) {\n\t\t\t\tappendFullOutput(text);\n\t\t\t}\n\t\t\ttailOutput = trimToLastUtf8Bytes(tailOutput, maxOutputBytes, encoder);\n\t\t\toptions?.onChunk?.(text, createProgress);\n\t\t} catch (error) {\n\t\t\tcaptureError = toExecutionError(error);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await env.exec(command, {\n\t\t\tcwd: options?.cwd,\n\t\t\tenv: options?.env,\n\t\t\tinheritEnv: options?.inheritEnv,\n\t\t\ttimeout: options?.timeout,\n\t\t\tabortSignal: options?.abortSignal,\n\t\t\tonStdout: onChunk,\n\t\t\tonStderr: onChunk,\n\t\t});\n\t\tacceptingOutput = false;\n\t\tlet progress = createProgress();\n\t\tif (progress.truncation.truncated && !fullOutputRequested) ensureFullOutputFile(tailOutput);\n\t\tconst writeResult = await writeChain;\n\t\tif (!writeResult.ok) return err(writeResult.error);\n\t\tif (captureError) return err(captureError);\n\t\tprogress = createProgress();\n\n\t\tif (!result.ok) {\n\t\t\tif (result.error.code === \"aborted\" || options?.abortSignal?.aborted) {\n\t\t\t\treturn ok({\n\t\t\t\t\t...progress,\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: progress.truncation.truncated,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (options?.returnExecutionErrors) {\n\t\t\t\treturn ok({\n\t\t\t\t\t...progress,\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: false,\n\t\t\t\t\ttruncated: progress.truncation.truncated,\n\t\t\t\t\texecutionError: result.error,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn err(result.error);\n\t\t}\n\t\tconst cancelled = options?.abortSignal?.aborted ?? false;\n\t\treturn ok({\n\t\t\t...progress,\n\t\t\texitCode: cancelled ? undefined : result.value.exitCode,\n\t\t\tcancelled,\n\t\t\ttruncated: progress.truncation.truncated,\n\t\t});\n\t} catch (error) {\n\t\tacceptingOutput = false;\n\t\treturn err(toExecutionError(error));\n\t}\n}\n"]}
@@ -0,0 +1,181 @@
1
+ import { ExecutionError, err, ok, toError } from "../types.js";
2
+ import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "./truncate.js";
3
+ function toExecutionError(error) {
4
+ if (error instanceof ExecutionError)
5
+ return error;
6
+ const cause = toError(error);
7
+ return new ExecutionError("unknown", cause.message, cause);
8
+ }
9
+ export function sanitizeBinaryOutput(str) {
10
+ return Array.from(str)
11
+ .filter((char) => {
12
+ const code = char.codePointAt(0);
13
+ if (code === undefined)
14
+ return false;
15
+ if (code === 0x09 || code === 0x0a || code === 0x0d)
16
+ return true;
17
+ if (code <= 0x1f)
18
+ return false;
19
+ if (code >= 0xfff9 && code <= 0xfffb)
20
+ return false;
21
+ return true;
22
+ })
23
+ .join("");
24
+ }
25
+ function trimToLastUtf8Bytes(text, maxBytes, encoder) {
26
+ const bytes = encoder.encode(text);
27
+ if (bytes.byteLength <= maxBytes)
28
+ return text;
29
+ let start = bytes.byteLength - maxBytes;
30
+ while (start < bytes.byteLength && ((bytes[start] ?? 0) & 0xc0) === 0x80)
31
+ start++;
32
+ return new TextDecoder().decode(bytes.subarray(start));
33
+ }
34
+ export async function executeShellWithCapture(env, command, options) {
35
+ let tailOutput = "";
36
+ const maxOutputBytes = DEFAULT_MAX_BYTES * 2;
37
+ const encoder = new TextEncoder();
38
+ let totalBytes = 0;
39
+ let completedLines = 0;
40
+ let hasOpenLine = false;
41
+ let currentLineBytes = 0;
42
+ let fullOutputPath;
43
+ let fullOutputRequested = false;
44
+ let acceptingOutput = true;
45
+ let writeChain = Promise.resolve(ok(undefined));
46
+ let captureError;
47
+ const appendFullOutput = (text) => {
48
+ if (!fullOutputRequested || captureError)
49
+ return;
50
+ writeChain = writeChain.then(async (previous) => {
51
+ if (!previous.ok)
52
+ return previous;
53
+ if (!fullOutputPath)
54
+ return err(new ExecutionError("unknown", "Full output path was not created"));
55
+ const appendResult = await env.appendFile(fullOutputPath, text);
56
+ return appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));
57
+ });
58
+ };
59
+ const ensureFullOutputFile = (initialContent) => {
60
+ if (fullOutputRequested || captureError)
61
+ return;
62
+ fullOutputRequested = true;
63
+ writeChain = writeChain.then(async (previous) => {
64
+ if (!previous.ok)
65
+ return previous;
66
+ const tempFile = await env.createTempFile({ prefix: "bash-", suffix: ".log" });
67
+ if (!tempFile.ok)
68
+ return err(toExecutionError(tempFile.error));
69
+ fullOutputPath = tempFile.value;
70
+ const appendResult = await env.appendFile(tempFile.value, initialContent);
71
+ return appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));
72
+ });
73
+ };
74
+ const createProgress = () => {
75
+ const tailTruncation = truncateTail(tailOutput);
76
+ const totalLines = completedLines + (hasOpenLine ? 1 : 0);
77
+ const truncated = totalLines > DEFAULT_MAX_LINES || totalBytes > DEFAULT_MAX_BYTES;
78
+ const truncation = {
79
+ ...tailTruncation,
80
+ truncated,
81
+ truncatedBy: truncated
82
+ ? (tailTruncation.truncatedBy ?? (totalBytes > DEFAULT_MAX_BYTES ? "bytes" : "lines"))
83
+ : null,
84
+ totalLines,
85
+ totalBytes,
86
+ };
87
+ return {
88
+ output: truncated ? truncation.content : tailOutput,
89
+ truncation,
90
+ fullOutputPath,
91
+ lastLineBytes: currentLineBytes,
92
+ };
93
+ };
94
+ const onChunk = (chunk) => {
95
+ if (!acceptingOutput)
96
+ return;
97
+ try {
98
+ const text = sanitizeBinaryOutput(chunk).replace(/\r/g, "");
99
+ const textBytes = encoder.encode(text).byteLength;
100
+ totalBytes += textBytes;
101
+ const newlineCount = text.split("\n").length - 1;
102
+ completedLines += newlineCount;
103
+ const lastNewline = text.lastIndexOf("\n");
104
+ if (lastNewline >= 0) {
105
+ const trailingText = text.slice(lastNewline + 1);
106
+ currentLineBytes = encoder.encode(trailingText).byteLength;
107
+ hasOpenLine = trailingText.length > 0;
108
+ }
109
+ else if (text.length > 0) {
110
+ currentLineBytes += textBytes;
111
+ hasOpenLine = true;
112
+ }
113
+ tailOutput += text;
114
+ const totalLines = completedLines + (hasOpenLine ? 1 : 0);
115
+ if ((totalBytes > DEFAULT_MAX_BYTES || totalLines > DEFAULT_MAX_LINES) && !fullOutputRequested) {
116
+ ensureFullOutputFile(tailOutput);
117
+ }
118
+ else if (fullOutputRequested) {
119
+ appendFullOutput(text);
120
+ }
121
+ tailOutput = trimToLastUtf8Bytes(tailOutput, maxOutputBytes, encoder);
122
+ options?.onChunk?.(text, createProgress);
123
+ }
124
+ catch (error) {
125
+ captureError = toExecutionError(error);
126
+ }
127
+ };
128
+ try {
129
+ const result = await env.exec(command, {
130
+ cwd: options?.cwd,
131
+ env: options?.env,
132
+ inheritEnv: options?.inheritEnv,
133
+ timeout: options?.timeout,
134
+ abortSignal: options?.abortSignal,
135
+ onStdout: onChunk,
136
+ onStderr: onChunk,
137
+ });
138
+ acceptingOutput = false;
139
+ let progress = createProgress();
140
+ if (progress.truncation.truncated && !fullOutputRequested)
141
+ ensureFullOutputFile(tailOutput);
142
+ const writeResult = await writeChain;
143
+ if (!writeResult.ok)
144
+ return err(writeResult.error);
145
+ if (captureError)
146
+ return err(captureError);
147
+ progress = createProgress();
148
+ if (!result.ok) {
149
+ if (result.error.code === "aborted" || options?.abortSignal?.aborted) {
150
+ return ok({
151
+ ...progress,
152
+ exitCode: undefined,
153
+ cancelled: true,
154
+ truncated: progress.truncation.truncated,
155
+ });
156
+ }
157
+ if (options?.returnExecutionErrors) {
158
+ return ok({
159
+ ...progress,
160
+ exitCode: undefined,
161
+ cancelled: false,
162
+ truncated: progress.truncation.truncated,
163
+ executionError: result.error,
164
+ });
165
+ }
166
+ return err(result.error);
167
+ }
168
+ const cancelled = options?.abortSignal?.aborted ?? false;
169
+ return ok({
170
+ ...progress,
171
+ exitCode: cancelled ? undefined : result.value.exitCode,
172
+ cancelled,
173
+ truncated: progress.truncation.truncated,
174
+ });
175
+ }
176
+ catch (error) {
177
+ acceptingOutput = false;
178
+ return err(toExecutionError(error));
179
+ }
180
+ }
181
+ //# sourceMappingURL=shell-output.js.map