Diff non-git workspaces via an mtime baseline
`projectBashArtifacts` and `reconcileWorkspaceArtifacts` used to bail with `not_a_git_repo` whenever the workspace had no `.git`, so bash output in such workspaces never reached `work_products` and the DocumentCenter looked empty while files piled up on disk. `captureWorkspaceBaseline` now walks the workspace at `prepareRun` time, skipping the same categories the registrability filter rejects (`.git`, `node_modules`, `.next`, caches) and capping at `MAX_BASELINE_ENTRIES` so large trees stay bounded. The baseline rides on `ActiveRunContext.workspaceBaseline` and is passed into every artifact projection. `collectChangedPaths` is the single change-detector for both the per-bash projection and the end-of-run reconciliation. It picks `git status` when a `.git` is present and falls back to a `diffAgainstBaseline` walk otherwise. Paths already on the ignore list still never land in `work_products`; the identity stays `(company, workspace, provider, externalId)`; the upsert still refreshes `last_touched_by_run_id` so the deliverable signal follows the latest touching run.
This commit is contained in:
1 parent
b1d1fbc54f
commit
d4c6056f5e
3 files changed
+218
-57
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
import { isAbsolute, relative, resolve } from "node:path";
|
import { isAbsolute, relative, resolve } from "node:path";
|
||||||
import { execFileSync } from "node:child_process";
|
import { execFileSync } from "node:child_process";
|
||||||
import { existsSync, statSync } from "node:fs";
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
||||||
import type { AppDatabase } from "@main/database";
|
import type { AppDatabase } from "@main/database";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,21 +8,29 @@ import type { AppDatabase } from "@main/database";
|
|||||||
*
|
*
|
||||||
* Every successful destructive tool call (`write`, `edit`, `bash`) is
|
* Every successful destructive tool call (`write`, `edit`, `bash`) is
|
||||||
* projected into the `work_products` table so the desktop UI can
|
* projected into the `work_products` table so the desktop UI can
|
||||||
* surface what the agent actually produced without requiring the
|
* surface what the agent actually produced without requiring the agent
|
||||||
* agent to remember to POST /api/documents.
|
* to remember to POST /api/documents.
|
||||||
*
|
*
|
||||||
* Projection is idempotent: the natural identity of an artifact is
|
* Projection is idempotent: the natural identity of an artifact is
|
||||||
* `(companyId, provider="filesystem", externalId=<workspace-relative-path>)`.
|
* `(companyId, workspaceId, provider="filesystem", externalId=<workspace-relative-path>)`.
|
||||||
* The first tool call inserts; subsequent calls to the same path go
|
* The first tool call inserts; subsequent calls to the same path go
|
||||||
* through `updateWorkProduct` so repeated edits do not spam duplicates.
|
* through `updateWorkProduct`, which also refreshes
|
||||||
|
* `last_touched_by_run_id` so every run that modifies the file shows
|
||||||
|
* up in the deliverable signal and in the DocumentCenter preview.
|
||||||
*
|
*
|
||||||
* `write` / `edit` expose the touched path in their args — the
|
* `write` / `edit` expose the touched path in their args — the
|
||||||
* projection path is trivial. `bash` is opaque: the tool may run any
|
* projection path is trivial. `bash` is opaque: the tool may run any
|
||||||
* command, including commands that mutate the workspace
|
* command (git commit, npm build, shell redirection). Diffing uses two
|
||||||
* (git commit, npm build, file redirection). For bash we scan the
|
* sources of truth, chosen per workspace:
|
||||||
* workspace for filesystem changes with `git status --porcelain`;
|
*
|
||||||
* non-git workspaces fall through to an explicit skip since there
|
* - git workspace → `git status --porcelain` reports modified and
|
||||||
* is no authoritative baseline to diff against.
|
* untracked files relative to HEAD;
|
||||||
|
* - non-git workspace → an mtime baseline captured at run start
|
||||||
|
* reports every file whose mtime advanced during the run.
|
||||||
|
*
|
||||||
|
* Either way the projector only upserts paths that survive the
|
||||||
|
* registrability filter (`IGNORED_PATH_SEGMENTS` / extensions), so
|
||||||
|
* build output and caches never pollute the DocumentCenter.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Paths we never register — tool output that is never a deliverable. */
|
/** Paths we never register — tool output that is never a deliverable. */
|
||||||
@@ -71,8 +79,95 @@ export type ArtifactProjectorRunContext = {
|
|||||||
taskId: string | null;
|
taskId: string | null;
|
||||||
workspaceId: string | null;
|
workspaceId: string | null;
|
||||||
workspacePath: string | null;
|
workspacePath: string | null;
|
||||||
|
/**
|
||||||
|
* mtime snapshot captured at run start for workspaces that are NOT
|
||||||
|
* a git repo. Diff-against-baseline is how `projectBashArtifacts`
|
||||||
|
* and `reconcileWorkspaceArtifacts` find files the agent wrote
|
||||||
|
* without a `.git` to fall back on.
|
||||||
|
*/
|
||||||
|
workspaceBaseline: WorkspaceBaseline | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface WorkspaceBaseline {
|
||||||
|
/** Relative path → mtimeMs at capture. */
|
||||||
|
mtimeByRelPath: Map<string, number>;
|
||||||
|
/** Unix ms at which the baseline was taken. */
|
||||||
|
capturedAt: number;
|
||||||
|
/** True when the walker stopped early because the cap was hit; the baseline is still usable for the paths that were captured. */
|
||||||
|
truncated: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_BASELINE_ENTRIES = 20_000;
|
||||||
|
const BASELINE_SKIP_DIR_NAMES: ReadonlySet<string> = new Set([
|
||||||
|
".git",
|
||||||
|
"node_modules",
|
||||||
|
".next",
|
||||||
|
"dist",
|
||||||
|
"build",
|
||||||
|
"out",
|
||||||
|
".cache",
|
||||||
|
".pnpm-store",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk `workspacePath` and record the mtime of every non-directory
|
||||||
|
* entry. Directories matching `BASELINE_SKIP_DIR_NAMES` are pruned at
|
||||||
|
* walk time — the same categories the registrability filter rejects —
|
||||||
|
* so the baseline stays bounded on real project trees. Stops early at
|
||||||
|
* `MAX_BASELINE_ENTRIES` and flags `truncated`; the partial baseline
|
||||||
|
* still lets the diff detect every file it saw, while the operator is
|
||||||
|
* told via the skip reason that an oversize workspace fell off the
|
||||||
|
* tail.
|
||||||
|
*/
|
||||||
|
export function captureWorkspaceBaseline(workspacePath: string): WorkspaceBaseline | null {
|
||||||
|
if (!workspacePath) return null;
|
||||||
|
try {
|
||||||
|
if (!statSync(workspacePath).isDirectory()) return null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const mtimeByRelPath = new Map<string, number>();
|
||||||
|
const stack: string[] = [workspacePath];
|
||||||
|
let truncated = false;
|
||||||
|
while (stack.length > 0) {
|
||||||
|
if (mtimeByRelPath.size >= MAX_BASELINE_ENTRIES) {
|
||||||
|
truncated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const dir = stack.pop();
|
||||||
|
if (!dir) break;
|
||||||
|
let entries: { name: string; isDirectory: () => boolean; isSymbolicLink: () => boolean }[];
|
||||||
|
try {
|
||||||
|
entries = readdirSync(dir, { withFileTypes: true });
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
const abs = resolve(dir, entry.name);
|
||||||
|
if (entry.isSymbolicLink()) continue;
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
if (BASELINE_SKIP_DIR_NAMES.has(entry.name)) continue;
|
||||||
|
stack.push(abs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const stat = statSync(abs);
|
||||||
|
if (!stat.isFile()) continue;
|
||||||
|
const rel = relative(workspacePath, abs);
|
||||||
|
if (rel.length === 0) continue;
|
||||||
|
mtimeByRelPath.set(rel, stat.mtimeMs);
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mtimeByRelPath.size >= MAX_BASELINE_ENTRIES) {
|
||||||
|
truncated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { mtimeByRelPath, capturedAt: Date.now(), truncated };
|
||||||
|
}
|
||||||
|
|
||||||
type WriteArgs = { path: unknown; content?: unknown };
|
type WriteArgs = { path: unknown; content?: unknown };
|
||||||
type EditArgs = { path: unknown };
|
type EditArgs = { path: unknown };
|
||||||
|
|
||||||
@@ -144,41 +239,46 @@ export function projectToolExecutionToWorkProduct(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* End-of-run reconciliation sweep. The per-tool `afterToolCall` hook
|
* End-of-run reconciliation sweep. The per-tool `afterToolCall` hook
|
||||||
* registers artifacts eagerly, but individual projections can skip
|
* registers artifacts eagerly, but individual projections can still
|
||||||
* (bash against a non-git workspace, a path on the ignore list, a
|
* skip (path on the ignore list, transient `git status` failure, or
|
||||||
* transient `git status` failure). Without a final sweep, those files
|
* an mtime baseline that was truncated). The sweep is the backstop:
|
||||||
* land on disk but never in `work_products` — the DocumentCenter then
|
* every file the change-detector still knows about is upserted once
|
||||||
* looks empty even though the agent produced deliverables.
|
* more before the run's resources are released, so the DocumentCenter
|
||||||
|
* is never empty just because a single hook call lost a row.
|
||||||
*
|
*
|
||||||
* The sweep uses the same `git status` source of truth as
|
* Diffing uses the same dual-source strategy as `projectBashArtifacts`:
|
||||||
* `projectBashArtifacts`; non-git workspaces are deliberately a no-op
|
* `git status` when a `.git` is present, the captured mtime baseline
|
||||||
* because there is no authoritative diff against HEAD. It is
|
* otherwise. Idempotent through `findWorkProductByExternalId`, which
|
||||||
* idempotent via `findWorkProductByExternalId`.
|
* keys on `(company, workspace, provider, externalId)`.
|
||||||
*/
|
*/
|
||||||
export function reconcileWorkspaceArtifacts(
|
export function reconcileWorkspaceArtifacts(
|
||||||
db: AppDatabase,
|
db: AppDatabase,
|
||||||
ctx: ArtifactProjectorRunContext,
|
ctx: ArtifactProjectorRunContext,
|
||||||
): ProjectionOutcome[] {
|
): ProjectionOutcome[] {
|
||||||
if (!ctx.workspacePath) return [{ kind: "skipped", reason: "no_workspace" }];
|
const change = collectChangedPaths(ctx);
|
||||||
if (!isGitRepo(ctx.workspacePath)) return [{ kind: "skipped", reason: "not_a_git_repo" }];
|
if (change.source === "none") {
|
||||||
|
return [{ kind: "skipped", reason: change.skipReason ?? "no_changes" }];
|
||||||
let changed: string[];
|
|
||||||
try {
|
|
||||||
changed = listChangedRelPaths(ctx.workspacePath);
|
|
||||||
} catch {
|
|
||||||
return [{ kind: "skipped", reason: "git_status_failed" }];
|
|
||||||
}
|
}
|
||||||
if (changed.length === 0) return [{ kind: "skipped", reason: "no_changes" }];
|
if (change.paths.length === 0) return [{ kind: "skipped", reason: "no_changes" }];
|
||||||
|
return projectChangedPaths(db, ctx, change.paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
function projectChangedPaths(
|
||||||
|
db: AppDatabase,
|
||||||
|
ctx: ArtifactProjectorRunContext,
|
||||||
|
changed: string[],
|
||||||
|
): ProjectionOutcome[] {
|
||||||
|
const workspacePath = ctx.workspacePath;
|
||||||
|
if (!workspacePath) return [{ kind: "skipped", reason: "no_workspace" }];
|
||||||
const outcomes: ProjectionOutcome[] = [];
|
const outcomes: ProjectionOutcome[] = [];
|
||||||
const budget = changed.slice(0, MAX_BASH_PROJECTED_FILES);
|
const budget = changed.slice(0, MAX_BASH_PROJECTED_FILES);
|
||||||
for (const rel of budget) {
|
for (const rel of budget) {
|
||||||
const absPath = resolve(ctx.workspacePath, rel);
|
const absPath = resolve(workspacePath, rel);
|
||||||
if (!isRegistrablePath(absPath, ctx.workspacePath)) {
|
if (!isRegistrablePath(absPath, workspacePath)) {
|
||||||
outcomes.push({ kind: "skipped", reason: "path_ignored" });
|
outcomes.push({ kind: "skipped", reason: "path_ignored" });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
outcomes.push(upsertFilesystemArtifact(db, ctx, ctx.workspacePath, absPath));
|
outcomes.push(upsertFilesystemArtifact(db, ctx, workspacePath, absPath));
|
||||||
}
|
}
|
||||||
if (changed.length > MAX_BASH_PROJECTED_FILES) {
|
if (changed.length > MAX_BASH_PROJECTED_FILES) {
|
||||||
outcomes.push({ kind: "skipped", reason: "bash_scan_cap_exceeded" });
|
outcomes.push({ kind: "skipped", reason: "bash_scan_cap_exceeded" });
|
||||||
@@ -186,6 +286,51 @@ export function reconcileWorkspaceArtifacts(
|
|||||||
return outcomes;
|
return outcomes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare current workspace mtimes against the run-start baseline.
|
||||||
|
* Returns every file whose mtime advanced (modified) or was not in
|
||||||
|
* the baseline at all (created). Skipped-dir filtering mirrors the
|
||||||
|
* baseline walker so the diff and the capture stay in sync.
|
||||||
|
*/
|
||||||
|
function diffAgainstBaseline(workspacePath: string, baseline: WorkspaceBaseline): string[] {
|
||||||
|
const changed: string[] = [];
|
||||||
|
const stack: string[] = [workspacePath];
|
||||||
|
while (stack.length > 0) {
|
||||||
|
const dir = stack.pop();
|
||||||
|
if (!dir) break;
|
||||||
|
let entries: { name: string; isDirectory: () => boolean; isSymbolicLink: () => boolean }[];
|
||||||
|
try {
|
||||||
|
entries = readdirSync(dir, { withFileTypes: true });
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isSymbolicLink()) continue;
|
||||||
|
const abs = resolve(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
if (BASELINE_SKIP_DIR_NAMES.has(entry.name)) continue;
|
||||||
|
stack.push(abs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mtimeMs: number;
|
||||||
|
try {
|
||||||
|
const stat = statSync(abs);
|
||||||
|
if (!stat.isFile()) continue;
|
||||||
|
mtimeMs = stat.mtimeMs;
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const rel = relative(workspacePath, abs);
|
||||||
|
if (rel.length === 0) continue;
|
||||||
|
const previous = baseline.mtimeByRelPath.get(rel);
|
||||||
|
if (previous === undefined || mtimeMs > previous) {
|
||||||
|
changed.push(rel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
function projectSinglePathArtifact(
|
function projectSinglePathArtifact(
|
||||||
db: AppDatabase,
|
db: AppDatabase,
|
||||||
ctx: ArtifactProjectorRunContext,
|
ctx: ArtifactProjectorRunContext,
|
||||||
@@ -203,36 +348,41 @@ function projectSinglePathArtifact(
|
|||||||
return upsertFilesystemArtifact(db, ctx, ctx.workspacePath, absPath);
|
return upsertFilesystemArtifact(db, ctx, ctx.workspacePath, absPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChangeSource = "git" | "baseline" | "none";
|
||||||
|
|
||||||
|
interface ChangeSet {
|
||||||
|
source: ChangeSource;
|
||||||
|
/** Paths relative to the workspace root. */
|
||||||
|
paths: string[];
|
||||||
|
/** Reason the source chose to skip (only meaningful when source === "none"). */
|
||||||
|
skipReason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectChangedPaths(ctx: ArtifactProjectorRunContext): ChangeSet {
|
||||||
|
if (!ctx.workspacePath) return { source: "none", paths: [], skipReason: "no_workspace" };
|
||||||
|
if (isGitRepo(ctx.workspacePath)) {
|
||||||
|
try {
|
||||||
|
return { source: "git", paths: listChangedRelPaths(ctx.workspacePath) };
|
||||||
|
} catch {
|
||||||
|
return { source: "none", paths: [], skipReason: "git_status_failed" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ctx.workspaceBaseline) {
|
||||||
|
return { source: "baseline", paths: diffAgainstBaseline(ctx.workspacePath, ctx.workspaceBaseline) };
|
||||||
|
}
|
||||||
|
return { source: "none", paths: [], skipReason: "no_baseline" };
|
||||||
|
}
|
||||||
|
|
||||||
function projectBashArtifacts(
|
function projectBashArtifacts(
|
||||||
db: AppDatabase,
|
db: AppDatabase,
|
||||||
ctx: ArtifactProjectorRunContext,
|
ctx: ArtifactProjectorRunContext,
|
||||||
): ProjectionOutcome[] {
|
): ProjectionOutcome[] {
|
||||||
if (!ctx.workspacePath) return [{ kind: "skipped", reason: "no_workspace" }];
|
const change = collectChangedPaths(ctx);
|
||||||
if (!isGitRepo(ctx.workspacePath)) {
|
if (change.source === "none") {
|
||||||
return [{ kind: "skipped", reason: "not_a_git_repo" }];
|
return [{ kind: "skipped", reason: change.skipReason ?? "no_changes" }];
|
||||||
}
|
}
|
||||||
let changed: string[];
|
if (change.paths.length === 0) return [{ kind: "skipped", reason: "no_changes" }];
|
||||||
try {
|
return projectChangedPaths(db, ctx, change.paths);
|
||||||
changed = listChangedRelPaths(ctx.workspacePath);
|
|
||||||
} catch {
|
|
||||||
return [{ kind: "skipped", reason: "git_status_failed" }];
|
|
||||||
}
|
|
||||||
if (changed.length === 0) return [{ kind: "skipped", reason: "no_changes" }];
|
|
||||||
|
|
||||||
const outcomes: ProjectionOutcome[] = [];
|
|
||||||
const budget = changed.slice(0, MAX_BASH_PROJECTED_FILES);
|
|
||||||
for (const rel of budget) {
|
|
||||||
const absPath = resolve(ctx.workspacePath, rel);
|
|
||||||
if (!isRegistrablePath(absPath, ctx.workspacePath)) {
|
|
||||||
outcomes.push({ kind: "skipped", reason: "path_ignored" });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
outcomes.push(upsertFilesystemArtifact(db, ctx, ctx.workspacePath, absPath));
|
|
||||||
}
|
|
||||||
if (changed.length > MAX_BASH_PROJECTED_FILES) {
|
|
||||||
outcomes.push({ kind: "skipped", reason: "bash_scan_cap_exceeded" });
|
|
||||||
}
|
|
||||||
return outcomes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGitRepo(workspacePath: string): boolean {
|
function isGitRepo(workspacePath: string): boolean {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export interface ActiveRunContext {
|
|||||||
taskSessionId: string;
|
taskSessionId: string;
|
||||||
workspaceId: string | null;
|
workspaceId: string | null;
|
||||||
workspacePath: string | null;
|
workspacePath: string | null;
|
||||||
|
workspaceBaseline: import("./artifact-projector").WorkspaceBaseline | null;
|
||||||
state: ActiveRunLifecycleState;
|
state: ActiveRunLifecycleState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +180,7 @@ export function handleAfterToolCall(
|
|||||||
taskId: ctx.taskId,
|
taskId: ctx.taskId,
|
||||||
workspaceId: ctx.workspaceId,
|
workspaceId: ctx.workspaceId,
|
||||||
workspacePath: ctx.workspacePath,
|
workspacePath: ctx.workspacePath,
|
||||||
|
workspaceBaseline: ctx.workspaceBaseline,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const outcomes = projectToolExecutionToWorkProduct(
|
const outcomes = projectToolExecutionToWorkProduct(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type { CredentialResolver } from "@main/credentials";
|
|||||||
import { getConnectorDefinition } from "@main/connectors";
|
import { getConnectorDefinition } from "@main/connectors";
|
||||||
import { projectInboxMessageToPi } from "./inbox-projection";
|
import { projectInboxMessageToPi } from "./inbox-projection";
|
||||||
import { projectAgentEventToLogChunk, extractTextBody } from "./agent-event-projection";
|
import { projectAgentEventToLogChunk, extractTextBody } from "./agent-event-projection";
|
||||||
import { reconcileWorkspaceArtifacts } from "./artifact-projector";
|
import { captureWorkspaceBaseline, reconcileWorkspaceArtifacts } from "./artifact-projector";
|
||||||
import {
|
import {
|
||||||
handleBeforeToolCall,
|
handleBeforeToolCall,
|
||||||
handleAfterToolCall,
|
handleAfterToolCall,
|
||||||
@@ -130,6 +130,7 @@ export class RuntimeBridge {
|
|||||||
taskId: ctx.taskId,
|
taskId: ctx.taskId,
|
||||||
workspaceId: ctx.workspaceId,
|
workspaceId: ctx.workspaceId,
|
||||||
workspacePath: ctx.workspacePath,
|
workspacePath: ctx.workspacePath,
|
||||||
|
workspaceBaseline: ctx.workspaceBaseline,
|
||||||
});
|
});
|
||||||
const inserted = outcomes.filter((o) => o.kind === "inserted").length;
|
const inserted = outcomes.filter((o) => o.kind === "inserted").length;
|
||||||
if (inserted > 0) {
|
if (inserted > 0) {
|
||||||
@@ -500,6 +501,13 @@ export class RuntimeBridge {
|
|||||||
workspaceId: string | null;
|
workspaceId: string | null;
|
||||||
workspacePath: string | null;
|
workspacePath: string | null;
|
||||||
};
|
};
|
||||||
|
// Capture an mtime baseline for non-git workspaces so `bash` and
|
||||||
|
// the end-of-run reconciliation can still diff changed files
|
||||||
|
// against a known-start reference. Git workspaces leave the
|
||||||
|
// baseline as null — `git status` is the source of truth there.
|
||||||
|
const baseline = payload.workspacePath
|
||||||
|
? captureWorkspaceBaseline(payload.workspacePath)
|
||||||
|
: null;
|
||||||
this.registerActiveRun({
|
this.registerActiveRun({
|
||||||
runId,
|
runId,
|
||||||
agentId: payload.agentId,
|
agentId: payload.agentId,
|
||||||
@@ -510,6 +518,7 @@ export class RuntimeBridge {
|
|||||||
taskSessionId: payload.taskSessionId,
|
taskSessionId: payload.taskSessionId,
|
||||||
workspaceId: payload.workspaceId,
|
workspaceId: payload.workspaceId,
|
||||||
workspacePath: payload.workspacePath,
|
workspacePath: payload.workspacePath,
|
||||||
|
workspaceBaseline: baseline,
|
||||||
state: "preparing",
|
state: "preparing",
|
||||||
});
|
});
|
||||||
const definition = getConnectorDefinition(payload.connectorId);
|
const definition = getConnectorDefinition(payload.connectorId);
|
||||||
|
|||||||
Reference in new issue
Block a user