mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
refactor(x): single live homes for getErrorDetails and isPathInside
getErrorDetails was the last live import out of runtime/legacy/ that isn't part of the code-mode / mini-apps carve-out — four knowledge pipelines pulled it from legacy/utils.ts, blocking 'legacy deletes as a unit'. Their agents run via the headless runner now, so the RunFailedError unwrap branch was dead for them; the live version in application/lib/errors.ts is the plain Error->message idiom, and the legacy copy (with the RunFailedError coupling) is deleted as consumerless. isPathInside backs path-grant permission decisions, and byte-identical copies lived in filesystem/files.ts, assembly/permission-metadata.ts, and legacy/repo.ts — a place for a future divergence to become a permission bypass in exactly one caller. files.ts now exports the one live copy; permission-metadata imports it (it already imports files.ts, so no new edge). legacy/repo.ts deliberately keeps its frozen private copy: the quarantine must not import live modules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7fdfa32c7e
commit
1f0cdbd366
8 changed files with 17 additions and 21 deletions
7
apps/x/packages/core/src/application/lib/errors.ts
Normal file
7
apps/x/packages/core/src/application/lib/errors.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// One home for the error -> human-readable message idiom. The legacy runs
|
||||
// engine's getErrorDetails also unwrapped RunFailedError's multi-error list;
|
||||
// live callers run agents through the headless runner, whose HeadlessRunError
|
||||
// carries the failure detail in .message — so this is the whole contract.
|
||||
export function getErrorDetails(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
|
@ -68,7 +68,11 @@ const MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
|
|||
let knowledgeCommitTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let canonicalWorkspaceRoot: string | null = null;
|
||||
|
||||
function isPathInside(parent: string, child: string): boolean {
|
||||
// Exported as the one live containment check: permission decisions
|
||||
// (assembly/permission-metadata) key on it, so a divergent copy is a
|
||||
// permission-bypass risk, not a style issue. (legacy/repo.ts keeps its own
|
||||
// frozen copy — the quarantine must not import live modules.)
|
||||
export function isPathInside(parent: string, child: string): boolean {
|
||||
const relative = path.relative(parent, child);
|
||||
return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { google } from 'googleapis';
|
|||
import { WorkDir } from '../config/config.js';
|
||||
import { runWhenPossible } from '../runtime/assembly/headless-app.js';
|
||||
import { getKgModel } from '../models/defaults.js';
|
||||
import { getErrorDetails } from '../runtime/legacy/utils.js';
|
||||
import { getErrorDetails } from '../application/lib/errors.js';
|
||||
import { serviceLogger } from '../services/service_logger.js';
|
||||
import { loadUserConfig, updateUserEmail } from '../config/user_config.js';
|
||||
import { GoogleClientFactory } from './google-client-factory.js';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import path from 'path';
|
|||
import { WorkDir } from '../config/config.js';
|
||||
import { getKgModel } from '../models/defaults.js';
|
||||
import { runWhenPossible, toolInputPaths } from '../runtime/assembly/headless-app.js';
|
||||
import { getErrorDetails } from '../runtime/legacy/utils.js';
|
||||
import { getErrorDetails } from '../application/lib/errors.js';
|
||||
import { serviceLogger, type ServiceRunContext } from '../services/service_logger.js';
|
||||
import {
|
||||
loadState,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import path from 'path';
|
|||
import { WorkDir } from '../config/config.js';
|
||||
import { runWhenPossible, toolInputPaths } from '../runtime/assembly/headless-app.js';
|
||||
import { getKgModel } from '../models/defaults.js';
|
||||
import { getErrorDetails } from '../runtime/legacy/utils.js';
|
||||
import { getErrorDetails } from '../application/lib/errors.js';
|
||||
import { serviceLogger } from '../services/service_logger.js';
|
||||
import { limitEventItems } from './limit_event_items.js';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import path from 'path';
|
|||
import { WorkDir } from '../config/config.js';
|
||||
import { runWhenPossible, toolInputPaths } from '../runtime/assembly/headless-app.js';
|
||||
import { getKgModel } from '../models/defaults.js';
|
||||
import { getErrorDetails } from '../runtime/legacy/utils.js';
|
||||
import { getErrorDetails } from '../application/lib/errors.js';
|
||||
import { serviceLogger } from '../services/service_logger.js';
|
||||
import { limitEventItems } from './limit_event_items.js';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -11,15 +11,10 @@ import { z } from "zod";
|
|||
import { ToolPermissionMetadata } from "@x/shared/dist/runs.js";
|
||||
import { isBlocked, extractCommandNames } from "../../application/lib/command-executor.js";
|
||||
import { getFileAccessAllowList, type FileAccessGrant, type FileAccessOperation } from "../../config/security.js";
|
||||
import { resolveFilePathForPermission } from "../../filesystem/files.js";
|
||||
import { isPathInside, resolveFilePathForPermission } from "../../filesystem/files.js";
|
||||
|
||||
type ToolPermissionMetadataValue = z.infer<typeof ToolPermissionMetadata>;
|
||||
|
||||
function isPathInside(parent: string, child: string): boolean {
|
||||
const relative = path.relative(parent, child);
|
||||
return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative));
|
||||
}
|
||||
|
||||
function fileGrantCoversPath(grant: FileAccessGrant, operation: FileAccessOperation, resolvedPath: string): boolean {
|
||||
return grant.operation === operation && isPathInside(path.resolve(grant.pathPrefix), path.resolve(resolvedPath));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,16 +20,6 @@ export class RunFailedError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
export function getErrorDetails(error: unknown): string {
|
||||
if (error instanceof RunFailedError) {
|
||||
return error.errors.join("\n\n");
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
return String(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the assistant's final text response from a run's log.
|
||||
* @param runId
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue