Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ Workflow 在清理隔离 checkout 前原子保存有界 Handoff Manifest:track

设计细节见 [Workflow invocation graph](https://github.com/openpi-dev/openpi/blob/main/docs/design/WORKFLOW_INVOCATION_GRAPH.md)。

### Owner-bound resource references

当 Direct Subagent 的有界 manager final、Workflow 的 result/transcript/agent result,或 Background Terminal 的完整 spill 已真实落盘时,结果 details 可附带 versioned resource reference。引用明确记录 producer owner、generation、revision、media type、byte length、相对于哪个 owner value 的完整性以及 owner-specific lifetime;它只是恢复 metadata,不是读取授权,也不会延长制品生命周期。

解析仍由对应 extension 在自己的 root、generation 与 Pi Trust/tool boundary 内完成。owner mismatch、stale generation、owner lost、unauthorized、目录穿越、symlink substitution、missing 与 revision drift 是可区分的 fail-closed 结果。OpenPI 不增加全局 URI/router、统一 artifact store 或新的常驻 read/search tool;Pi 原生 `read` 仍是实际读取机制。

---

## 连续工作,而不是堆 Context
Expand Down
49 changes: 43 additions & 6 deletions extensions/background-terminals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
registerWebCapability,
} from "../shared/web-observer-registry.ts";
import type { TerminalSnapshot } from "./src/domain.ts";
import { createOwnerFileResourceRef } from "../shared/resource-reference.ts";
import {
MAX_RUNNING,
TerminalManager,
Expand Down Expand Up @@ -89,6 +90,35 @@ import {
const WIDGET_KEY = "background-terminals";
const IDLE_RESULT_BATCH_MS = 200;

export function terminalResourceRefs(snap: TerminalSnapshot) {
if (snap.status === "running") return [];
return (["stdout", "stderr"] as const).flatMap((stream) => {
const view = snap[stream];
if (!view.spillPath) return [];
try {
return [
createOwnerFileResourceRef({
owner: {
kind: "background",
id: snap.id,
generation: String(snap.createdAt),
},
resourceId: stream,
root: path.dirname(view.spillPath),
file: view.spillPath,
mediaType: "text/plain; charset=utf-8",
completeness: "complete-owner-value",
sourceCoverage: "process-stream",
lifetime: "session-temporary",
expectedByteLength: view.totalBytes,
}),
];
} catch {
return [];
}
});
}

interface WatchToolDetails {
id: string;
pattern: string;
Expand Down Expand Up @@ -214,6 +244,7 @@ export default function (pi: ExtensionAPI) {
status: snaps[0]!.status,
exitCode: snaps[0]!.exitCode,
signal: snaps[0]!.signal,
resources: terminalResourceRefs(snaps[0]!),
}
: {
count: snaps.length,
Expand All @@ -223,6 +254,7 @@ export default function (pi: ExtensionAPI) {
status: snap.status,
exitCode: snap.exitCode,
signal: snap.signal,
resources: terminalResourceRefs(snap),
})),
},
},
Expand Down Expand Up @@ -463,6 +495,7 @@ export default function (pi: ExtensionAPI) {
exitCode: snap.exitCode,
signal: snap.signal,
timeoutAt: snap.timeoutAt,
resources: terminalResourceRefs(snap),
},
};
},
Expand Down Expand Up @@ -542,12 +575,16 @@ export default function (pi: ExtensionAPI) {
return {
content: [{ type: "text", text: buildKillReport(report) }],
details: {
results: report.map((entry) => ({
id: entry.id,
title: entry.title,
status: entry.status,
killed: entry.killed,
})),
results: report.map((entry) => {
const snap = manager.view.get(entry.id);
return {
id: entry.id,
title: entry.title,
status: entry.status,
killed: entry.killed,
resources: snap ? terminalResourceRefs(snap) : [],
};
}),
},
};
},
Expand Down
Loading
Loading