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:
ZacharyZhang-NY committed 2026-04-19 16:18:25 -04:00
1 parent b1d1fbc54f
commit d4c6056f5e
3 files changed
+218 -57

No files matched your search

+10 -1
View File
@@ -11,7 +11,7 @@ import type { CredentialResolver } from "@main/credentials";
import { getConnectorDefinition } from "@main/connectors";
import { projectInboxMessageToPi } from "./inbox-projection";
import { projectAgentEventToLogChunk, extractTextBody } from "./agent-event-projection";
import { reconcileWorkspaceArtifacts } from "./artifact-projector";
import { captureWorkspaceBaseline, reconcileWorkspaceArtifacts } from "./artifact-projector";
import {
handleBeforeToolCall,
handleAfterToolCall,
@@ -130,6 +130,7 @@ export class RuntimeBridge {
taskId: ctx.taskId,
workspaceId: ctx.workspaceId,
workspacePath: ctx.workspacePath,
workspaceBaseline: ctx.workspaceBaseline,
});
const inserted = outcomes.filter((o) => o.kind === "inserted").length;
if (inserted > 0) {
@@ -500,6 +501,13 @@ export class RuntimeBridge {
workspaceId: 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({
runId,
agentId: payload.agentId,
@@ -510,6 +518,7 @@ export class RuntimeBridge {
taskSessionId: payload.taskSessionId,
workspaceId: payload.workspaceId,
workspacePath: payload.workspacePath,
workspaceBaseline: baseline,
state: "preparing",
});
const definition = getConnectorDefinition(payload.connectorId);