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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ acceptance: {

Workflow 在清理隔离 checkout 前原子保存有界 Handoff Manifest:tracked binary patch、stat、branch/HEAD、untracked/ignored 清单与 cleanup receipt。状态不明就保留现场,不自动 merge、apply 或强删。

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 请改为 canonical repository 链接。这里指向个人 fork tt-a1i/openpi,发布到 npm/GitHub 后文档可用性会依赖该 fork;本 PR 对应 issue #69 的目标正是让发布包中的链接稳定可解析。应指向 openpi-dev/openpi/blob/main/docs/design/WORKFLOW_INVOCATION_GRAPH.md


---

Expand Down Expand Up @@ -647,7 +647,7 @@ bun run test

npm 仍用于发布包的 `pack` / clean-install 验证,因为用户通过 npm Registry 安装 OpenPI。

测试覆盖进程树终止与竞态、Subagent 生命周期与工具边界、Workflow Sandbox / Ledger / Graph / Replay / Acceptance、Worktree 数据保全、Session 状态恢复、配置迁移和 TUI 渲染。设计记录见 [`docs/design/`](docs/design/),问题请提交到 [GitHub Issues](https://github.com/openpi-dev/openpi/issues)。
测试覆盖进程树终止与竞态、Subagent 生命周期与工具边界、Workflow Sandbox / Ledger / Graph / Replay / Acceptance、Worktree 数据保全、Session 状态恢复、配置迁移和 TUI 渲染。设计记录见 [docs/design/](https://github.com/openpi-dev/openpi/tree/main/docs/design/),问题请提交到 [GitHub Issues](https://github.com/openpi-dev/openpi/issues)。

---

Expand Down
2 changes: 1 addition & 1 deletion SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ Session Tasks remain advisory multi-item work intent and do not determine Goal c
- `/sessions` searches and previews project sessions before switching.
- `/tasks` inspects branch-scoped advisory work items.
- `/goal ...` controls the persistent autonomous session objective.
- `/context-pivot <next phase>` deliberately compacts a long current session into a next-phase brief. It requires at least 30,000 context tokens and is rejected below that; use the separate `/handoff` skill when work should move to a genuinely new session.
- `/context-pivot <next phase>` deliberately compacts a long current session into a next-phase brief. It requires at least 30,000 context tokens and is rejected below that; use `/sessions` to browse or switch sessions, or install the optional `pi-intercom` package for communication between top-level sessions.
- `/cron every <5m> <prompt>`, `/cron in <30s> <prompt>`, `/cron list`, and `/cron remove <id>` schedule a prompt for this session. Jobs are in-memory and session-scoped (cleared on shutdown), fire only while the session is idle, and use a duration grammar (`30s`/`5m`/`2h`, minimum 30s) rather than crontab fields, because the scheduler polls about every 30 seconds. Jobs due in the same poll are delivered as one triggered turn while retaining each job's id and recurrence metadata; if that atomic delivery fails, every due job remains pending for retry.
- `/plan [objective]` explores read-only before changing anything and automatically loads the `search` capability for the current Session. While armed it blocks `edit`, `write`, mutating Bash, raw Git diff-rendering commands, `subagent_send`, `workflow`, and `bg_start`; read/grep/find/ls/fd/rg, `git_log`/`git_diff`/`git_show`, and verified non-diff Git/GitHub Bash commands stay available. It permits `subagent_spawn`, but the harness narrows every newly spawned planning child to investigation-only tools; agent types can narrow that list further, never widen it. The model must submit the complete plan through parent-only `plan_ready`; the write gate stays closed until `/plan` prepares an editable implementation prompt for the current or a fresh Session. `/plan off` cancels.
4 changes: 2 additions & 2 deletions extensions/context-pivot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function validateBrief(brief: string, ctx: ExtensionContext) {
}
if (tokens < MIN_CONTEXT_PIVOT_TOKENS) {
throw new Error(
`Context is only ${Math.round(tokens).toLocaleString()} tokens; use context_pivot once context reaches at least ${MIN_CONTEXT_PIVOT_TOKENS.toLocaleString()} tokens, or /handoff for a genuinely new session.`,
`Context is only ${Math.round(tokens).toLocaleString()} tokens; use context_pivot once context reaches at least ${MIN_CONTEXT_PIVOT_TOKENS.toLocaleString()} tokens, or /sessions to browse or switch to a genuinely new session.`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 这里把 /sessions 描述成“新建 session”的入口,但它实际只搜索、预览和切换已有 Session。当前 main 已明确区分这两个动作;合并后这段提示会让用户把“切换已有会话”误当成“创建干净会话”。请保留 /sessions 的已有会话语义,并把新建动作写成在 Pi 中启动新的 Session;同类表述在下方另一处也要同步。

);
}
}
Expand Down Expand Up @@ -127,7 +127,7 @@ export default function contextPivot(pi: ExtensionAPI) {
name: "context_pivot",
label: "Context Pivot",
description:
"Deliberately replace a long, noisy active context with a concise brief for the next phase while staying in the same Pi session. Use once context is at least 30k tokens and the work is moving between phases such as research → implementation or implementation → review; below 30k it is rejected. Use /handoff instead for a genuinely new session.",
"Deliberately replace a long, noisy active context with a concise brief for the next phase while staying in the same Pi session. Use once context is at least 30k tokens and the work is moving between phases such as research → implementation or implementation → review; below 30k it is rejected. Use /sessions to browse or switch to another session when a genuinely new session is needed.",
promptSnippet:
"Compress a long current session into a clean brief before changing phase",
promptGuidelines: [
Expand Down
3 changes: 2 additions & 1 deletion extensions/subagents/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
* settle. `/subagents` opens a picker + full interactive takeover view.
*
* Agent types (`src/agent-types.ts`) are optional named presets that fix a
* child's system prompt, model, and tool allowlist; see `docs/agent-types.md`.
* child's system prompt, model, and tool allowlist; see
* `skills/subagents/REFERENCE.md`.
*
* Architecture: Effect v4 generators throughout (backend -> manager ->
* runtime); this file is the async boundary where tool handlers run effects
Expand Down
2 changes: 1 addition & 1 deletion extensions/user-input-fold/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Segment =
| { kind: "code"; open: string; content: string[]; close: string };

const FENCE_OPEN = /^ {0,3}`{3,}/;
const FENCE_CLOSE = /^ {0,3}`{3,}[ \t]*$/;
const FENCE_CLOSE = /^ {0,3}`{3,}[ \t]*\r?$/;

function countLines(markdown: string) {
const parts = markdown.split("\n");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Agent types
# Agent types reference

An agent type is a reusable child-agent definition shared by
`subagent_spawn` and Workflow `agent()`: a named preset that can set a child's
Expand Down Expand Up @@ -172,18 +172,18 @@ and are not treated as missing, so the child denylist stays authoritative.

## Implementation

- `src/agent-types.ts` — built-ins, parsing, discovery, diagnostics, and model
- `extensions/subagents/src/agent-types.ts` — built-ins, parsing, discovery, diagnostics, and model
precedence helpers.
- `../../shared/subagent-roles.ts` — one typed source for built-in role names.
- `index.ts` — safe initial discovery, session-scoped roster refresh and
- `extensions/shared/subagent-roles.ts` — one typed source for built-in role names.
- `extensions/subagents/index.ts` — safe initial discovery, session-scoped roster refresh and
re-registration, the `agent_type` parameter, and merging a type/config
assignment into the spawn task.
- `src/backends/pi.ts` — applies `appendSystemPrompt` and the tool allowlist to
- `extensions/subagents/src/backends/pi.ts` — applies `appendSystemPrompt` and the tool allowlist to
a direct subagent session.
- `../../workflows/index.ts` and `../../workflows/runner.ts` — resolve the same
- `extensions/workflows/index.ts` and `extensions/workflows/runner.ts` — resolve the same
type for each Workflow call and enforce its prompt, model, effort, and tools.
- `../../shared/child-session.ts` — `childToolPolicy(tools?)`, where the allowlist
- `extensions/shared/child-session.ts` — `childToolPolicy(tools?)`, where the allowlist
and denylist compose.

Covered by `../../../tests/extensions/subagents/agent-types.test.ts`, including the trust gate and the
Covered by `tests/extensions/subagents/agent-types.test.ts`, including the trust gate and the
narrowing-only property.
2 changes: 1 addition & 1 deletion skills/subagents/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The tool definitions are canonical for parameters, limits, model syntax, isolati
- Delegate substantial independent work, not a lookup or edit the parent can do directly.
- Give the child a standalone prompt with paths, constraints, relevant context, and the expected report; it cannot see the parent conversation or ask the user.
- Inherit the parent model by default. When choosing the child's reasoning effort, honor an explicit user requirement first; otherwise use the selected role's relative guidance and the task's difficulty, choosing from levels supported by the resolved child model.
- Prefer a matching agent type when one exists; its tool restriction is enforced. Model precedence is explicit spawn override, selected type-file model, configured built-in role model, then parent model. Reasoning precedence is explicit spawn override, selected type default, then parent effort. Types live in `~/.pi/agent/agents/*.md` and, for trusted projects, `.pi/agents/*.md`; see `extensions/subagents/docs/agent-types.md`.
- Prefer a matching agent type when one exists; its tool restriction is enforced. Model precedence is explicit spawn override, selected type-file model, configured built-in role model, then parent model. Reasoning precedence is explicit spawn override, selected type default, then parent effort. Types live in `~/.pi/agent/agents/*.md` and, for trusted projects, `.pi/agents/*.md`; see [Agent types](REFERENCE.md).
- Isolate concurrent writers in worktrees according to the `subagent_spawn` schema so they cannot overwrite one checkout or git index. While Plan Mode is active, use only read-only exploration types (or no type); worktree isolation and types narrowed by Plan Mode are rejected.
- After spawning, continue useful parent work. In an interactive session, if none remains, tell the user the child is still running and end the turn; automatic result delivery will re-invoke the parent when it settles. Do not block merely because the next step depends on the result or because there is nothing else to do. Use `subagent_wait` only when the user explicitly asks to keep the current response open for the result, or when non-interactive automation must return it in the same invocation.

Expand Down
15 changes: 15 additions & 0 deletions tests/extensions/user-input-fold/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,21 @@ test("CRLF messages fold without losing their line endings", () => {
);
});

test("LF and CRLF fenced messages fold with the same semantics", () => {
const lf = [
"intro",
"```js",
...Array.from({ length: 30 }, (_, i) => `console.log(${i});`),
"```",
"outro",
].join("\n");
const crlf = lf.replaceAll("\n", "\r\n");
const normalizeNewlines = (text: string) => text.replaceAll("\r\n", "\n");

assert.equal(normalizeNewlines(foldUserMessage(crlf)), foldUserMessage(lf));
assert.ok(foldUserMessage(crlf).includes("```\r\n"));
});

test("trailing newlines neither fold short messages nor pad the count", () => {
assert.equal(foldUserMessage("hello\n"), "hello\n");
assert.equal(foldUserMessage("hello\n\n"), "hello\n\n");
Expand Down
Loading