@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
82 lines • 3.58 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { PolicyDenied, describeRefusal } from "../../policy/index.js";
import { errorVirtualPath, fsStrerror } from "../../errors/fs.js";
export function decode(value) {
if (value === null || value === undefined)
return '';
return new TextDecoder('utf-8', { ignoreBOM: true }).decode(value);
}
/**
* The one line a text surface appends for a refusal, newline included, or
* the empty string when there is no record. The refusal's stderr is the
* bare `Permission denied` or the path's own error, which never says why.
* Mirrors Python's `refusal_line`.
*/
export function refusalLine(refusal) {
if (refusal === null)
return '';
return `${describeRefusal(refusal)}\n`;
}
/**
* Append the refusal's reason as one more line after the shell's own
* output, for a surface that hands the agent text.
*/
export function withRefusal(text, refusal) {
const line = refusalLine(refusal);
if (line === '' || text === '')
return text || line;
return text.endsWith('\n') ? `${text}${line}` : `${text}\n${line}`;
}
/**
* A tool's failure as the agent reads it: a filesystem error as
* `<path>: <phrase>` (a policy's refusal reads as `Permission denied`),
* or the phrase alone when no path was stamped and the message adds
* nothing, anything else in its own words, then the refusal's line when a
* policy refused the op. Mirrors Python's `error_text`.
*/
export function errorText(error) {
const strerror = fsStrerror(error);
const message = error instanceof Error ? error.message : String(error);
const stamped = strerror !== null && typeof error.virtualPath === 'string';
const words = strerror === null
? message
: stamped
? `${errorVirtualPath(error)}: ${strerror}`
: message === '' || message === strerror
? strerror
: `${message}: ${strerror}`;
return withRefusal(`Error: ${words}`, error instanceof PolicyDenied ? error.refusal : null);
}
export function ioToStr(io) {
const stdout = io.stdoutText;
const stderr = io.stderrText;
let text = stdout;
if (stderr)
text = stdout ? `${stdout}\n${stderr}` : stderr;
return withRefusal(text, io.refusal);
}
/**
* The edit tools' one substitution: `content` with `oldString` replaced
* once, or everywhere under `replaceAll`, beside how many times it occurs.
* The replacement is literal (no `$&` or `$1` expansion), as Python's
* `str.replace` is; a count other than one without `replaceAll` is the
* caller's refusal to word. Mirrors Python's `replace_text`.
*/
export function replaceText(content, oldString, newString, replaceAll) {
const parts = content.split(oldString);
const text = replaceAll ? parts.join(newString) : content.replace(oldString, () => newString);
return [text, parts.length - 1];
}
//# sourceMappingURL=io_text.js.map