@timqi/pier 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/agent/pi.js +13 -3
- package/dist/extensions/web/content.js +5 -0
- package/dist/extensions/web/language.js +5 -0
- package/dist/extensions/web/tools.js +5 -0
- package/dist/tasks/agent.js +5 -0
- package/dist/tasks/command.js +15 -0
- package/dist/tasks/definitions.js +4 -0
- package/dist/tasks/execution.js +4 -0
- package/dist/tasks/groups.js +4 -0
- package/dist/tasks/messages.js +6 -0
- package/dist/tasks/routes.js +4 -0
- package/dist/tasks/runs.js +5 -0
- package/dist/tasks/service.js +6 -0
- package/dist/tasks/store.js +4 -0
- package/dist/tasks/types.js +4 -0
- package/dist/web/public/assets/{ghostty-web-xcUrfRRs.js → ghostty-web-C4ivXTBE.js} +1 -1
- package/dist/web/public/assets/index-2E9_cwpg.css +2 -0
- package/dist/web/public/assets/index-DVUvzNK1.js +93 -0
- package/dist/web/public/index.html +2 -2
- package/docs/deploy.md +12 -3
- package/package.json +1 -1
- package/dist/web/public/assets/index-BWDlAMK2.js +0 -93
- package/dist/web/public/assets/index-DHqZnZr7.css +0 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A self-hosted workspace for coding agents. Pier puts a web workbench and your
|
|
4
4
|
IM channels in front of [Pi](https://github.com/earendil-works/pi) sessions:
|
|
5
|
-
you talk to the same agent from a browser, from Slack or
|
|
5
|
+
you talk to the same agent from a browser, from Slack, Telegram or Lark, steer a
|
|
6
6
|
running turn, schedule tasks, watch what every session is doing, and publish a
|
|
7
7
|
static page when something is worth showing.
|
|
8
8
|
|
|
@@ -89,6 +89,11 @@ Console → Settings is the normal setup path:
|
|
|
89
89
|
`web_search` on an authenticated Anthropic or OpenAI model, `web_fetch` on an
|
|
90
90
|
Anthropic one (OpenAI hosts no fetch tool), and no other key or service. The
|
|
91
91
|
page says which tools a switch adds and what each needs before you flip it.
|
|
92
|
+
Below it, the same tab switches on the command-line tools Pier manages —
|
|
93
|
+
`rtk`, `rg`, `fd`, `wt`, `jq`, or a tool of your own written as a
|
|
94
|
+
[ubix](https://github.com/timqi/ubix) block. A switch installs the binary
|
|
95
|
+
into `~/.pier/tools/bin`, which is first on the PATH every session, task and
|
|
96
|
+
terminal inherits, and a task you can read keeps them current.
|
|
92
97
|
|
|
93
98
|
On first credential access, Pier imports an existing `auth.json` into its sealed
|
|
94
99
|
store and renames the source to `auth.json.imported`. Literal provider keys left
|
|
@@ -111,6 +116,7 @@ pier restart # drain running work, then restart
|
|
|
111
116
|
pier reload # re-read channel config and recycle idle sessions
|
|
112
117
|
pier backup # snapshot the database before a manual update
|
|
113
118
|
pier update # latest release, then hard-stop/restart the service
|
|
119
|
+
pier tools sync # install/update the managed CLI tools by hand
|
|
114
120
|
```
|
|
115
121
|
|
|
116
122
|
`pier restart` refuses new work, waits up to five minutes for active turns and
|
package/dist/agent/pi.js
CHANGED
|
@@ -42,12 +42,22 @@ const PIER_SYSTEM_PROMPT = `You are a general-purpose agent with a live workspac
|
|
|
42
42
|
# Communication
|
|
43
43
|
These rules govern conversational replies. When the reply *is* the deliverable — a report that was asked for, a review, a task run whose result another agent reads — the work sets the length: complete beats brief, and nothing below caps it.
|
|
44
44
|
- Answer with the conclusion only. Reasons, process, trade-offs, alternatives: only when asked.
|
|
45
|
-
- Cap per reply:
|
|
45
|
+
- Cap per reply: 60 words (90 Chinese chars), max 3 bullets; 180 words (270 Chinese chars) when explicitly asked why or how. Code blocks, diffs and commands don't count.
|
|
46
|
+
- Reply in the language of the request; code, paths, identifiers and quoted output stay verbatim.
|
|
46
47
|
- Never: preamble, restating the question, closing summaries, "I'm going to..." narration, listing changes already visible in the diff.
|
|
47
48
|
- After edits, say only: file(s) touched + one line on the result. Don't explain self-evident code.
|
|
48
|
-
- Show file paths as \`path:line
|
|
49
|
+
- Show file paths as \`path:line\`, or the path alone when no single line is the point — never invent a number.
|
|
49
50
|
- If the honest answer needs more than the cap, give the conclusion plus one short "want the details?" — don't dump it.
|
|
50
|
-
- Blocked on a decision only the person you work for can make? Ask one short question. Otherwise pick the sensible default and note it
|
|
51
|
+
- Blocked on a decision only the person you work for can make? Ask one short question. Otherwise pick the sensible default and note it.
|
|
52
|
+
|
|
53
|
+
# Working style (any machine)
|
|
54
|
+
These hold wherever Pier runs; a user's SYSTEM.md adds the local ones (which tools exist, which hosts, which paths).
|
|
55
|
+
- Orient first — list and search before you act. Never guess a path.
|
|
56
|
+
- Read before you edit. Match the surrounding code's style, naming, and comment density.
|
|
57
|
+
- Do exactly what was asked. No unrequested refactors, no extra files, no README updates.
|
|
58
|
+
- Destructive or irreversible actions (rm, force push, migrations, deploys): ask first.
|
|
59
|
+
- Say plainly when something failed, was skipped, or is unverified. Never claim a test passed without running it.
|
|
60
|
+
- Every bash call already runs in the working directory this prompt names — don't prefix \`cd <cwd> &&\`, \`cd\` only to go somewhere else. Each call is a fresh shell: \`cd\`, \`export\`, \`source\` never carry over, so chain what must share state into one command.`;
|
|
51
61
|
export const pierSystemPrompt = (userPrompt) => userPrompt ? `${PIER_SYSTEM_PROMPT}\n\n${userPrompt}` : PIER_SYSTEM_PROMPT;
|
|
52
62
|
/** Patching the call is cheaper than replacing the tool: the built-in keeps its
|
|
53
63
|
* shell settings, and the agent spends no tokens deciding a timeout. */
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// What a provider's answer becomes on the way to the model: sources, results,
|
|
2
|
+
// the queries actually searched, usage, and the text of a fetched document.
|
|
3
|
+
// One shape for both backends, so a tool renders its answer once instead of
|
|
4
|
+
// per wire format — anthropic.ts and openai.ts parse into these, and nothing
|
|
5
|
+
// past this file knows which one replied.
|
|
1
6
|
import { isObject } from "./json.js";
|
|
2
7
|
import { languageLabel } from "./language.js";
|
|
3
8
|
/**
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// The language-preservation policy in words: what the model is told to search
|
|
2
|
+
// in, and how to tell afterwards whether it did. This is the reason the
|
|
3
|
+
// extension exists at all — a hosted search that quietly translates a Chinese
|
|
4
|
+
// query answers a question nobody asked — so the policy is one file, and the
|
|
5
|
+
// audit that checks it reads from the same one.
|
|
1
6
|
export function searchPrompt(query, mode) {
|
|
2
7
|
const policy = mode === "preserve"
|
|
3
8
|
? "Use only the original language. Later searches may refine wording in that language, but must not translate or transliterate it."
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// The two tools as the model sees them: web_search and web_fetch — their
|
|
2
|
+
// parameters, which backend answers a call, and what comes back when one
|
|
3
|
+
// cannot. Every parameter is context the model pays for on every turn, so the
|
|
4
|
+
// surface is deliberately small; the wire formats behind it are anthropic.ts
|
|
5
|
+
// and openai.ts, and the answer's shape is content.ts.
|
|
1
6
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
7
|
import { Type } from "typebox";
|
|
3
8
|
import { callNativeTool } from "./anthropic.js";
|
package/dist/tasks/agent.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// A run that *is* a Pi session: which session it opens (reuse, fresh, fork),
|
|
2
|
+
// what the child is told before the prompt, and how many may run at once. The
|
|
3
|
+
// concurrency caps are here rather than in execution.ts because they bound
|
|
4
|
+
// agents specifically — a bash run costs a process, an agent run costs a
|
|
5
|
+
// model's context and someone's rate limit.
|
|
1
6
|
import { quietLabel, splitReply } from "../core/reply.js";
|
|
2
7
|
import { Router } from "../core/router.js";
|
|
3
8
|
import { runSource } from "./callbacks.js";
|
package/dist/tasks/command.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
// A bash task's script, run in its cwd with its input on stdin. The output is
|
|
2
|
+
// capped as it arrives rather than after: a run that printed a gigabyte is a
|
|
3
|
+
// run whose result still has to fit in a row, a transcript and a callback.
|
|
1
4
|
import { spawn } from "node:child_process";
|
|
5
|
+
import { logger } from "../log.js";
|
|
6
|
+
const log = logger("tasks");
|
|
2
7
|
const OUTPUT_LIMIT = 1024 * 1024;
|
|
3
8
|
class CappedOutput {
|
|
4
9
|
chunks = [];
|
|
@@ -69,6 +74,16 @@ export function runBash(script, cwd, input, signal) {
|
|
|
69
74
|
stderrTruncated: stderr.truncated,
|
|
70
75
|
});
|
|
71
76
|
});
|
|
77
|
+
// A script that never reads stdin is ordinary (`exit 0`, a one-line curl),
|
|
78
|
+
// and writing the input to a pipe nobody is holding raises EPIPE *here*.
|
|
79
|
+
// Unhandled, that is an `error` event on a stream, which is an uncaught
|
|
80
|
+
// exception, which is main.ts exiting the process: one task script could
|
|
81
|
+
// take every session and every other run down with it. The input not being
|
|
82
|
+
// wanted is not a failure of the run — anything else still gets said.
|
|
83
|
+
child.stdin.on("error", (err) => {
|
|
84
|
+
if (err.code !== "EPIPE")
|
|
85
|
+
log.warn(`run input could not be written: ${err.message}`);
|
|
86
|
+
});
|
|
72
87
|
child.stdin.end(encodedInput);
|
|
73
88
|
});
|
|
74
89
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// What a task *is* before it ever runs: the id it is minted with, the draft
|
|
2
|
+
// validated into a definition, and when its trigger is next due. Every way a
|
|
3
|
+
// definition can be created — HTTP, the task tool, Pier's own owned task —
|
|
4
|
+
// arrives here, so a field is checked in one place or nowhere.
|
|
1
5
|
import { randomBytes } from "node:crypto";
|
|
2
6
|
import { stat } from "node:fs/promises";
|
|
3
7
|
import { Cron } from "croner";
|
package/dist/tasks/execution.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// One queued run carried to a result: dispatched by action kind (a bash
|
|
2
|
+
// script, an agent session, another task), abortable while it goes, settled
|
|
3
|
+
// exactly once. What the two action kinds actually do lives in command.ts and
|
|
4
|
+
// agent.ts; this file owns only the lifecycle they share.
|
|
1
5
|
import { logger } from "../log.js";
|
|
2
6
|
import { AgentTaskRunner } from "./agent.js";
|
|
3
7
|
import { TaskCallbacks } from "./callbacks.js";
|
package/dist/tasks/groups.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// The fan-out join: members start detached, and the group is what turns their
|
|
2
|
+
// separate endings into one answer — when the join condition is met, which
|
|
3
|
+
// members are cancelled, and the single aggregated callback that goes back.
|
|
4
|
+
// Delivering it is the outbox's job; deciding it is this file's.
|
|
1
5
|
import { Router } from "../core/router.js";
|
|
2
6
|
import { logger } from "../log.js";
|
|
3
7
|
import { runRef, runResultText } from "./callbacks.js";
|
package/dist/tasks/messages.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// What a parent and a child say to each other while a run is going: steer,
|
|
2
|
+
// follow-up and resume in one direction, progress and decision questions in
|
|
3
|
+
// the other. Every message is a durable row before it is a delivery, because
|
|
4
|
+
// the two ends are different sessions and either may be mid-turn, gone, or
|
|
5
|
+
// finished — an undelivered message is retried, expired and *said*, never
|
|
6
|
+
// dropped (§5b).
|
|
1
7
|
import { EventHub } from "../core/hub.js";
|
|
2
8
|
import { Router } from "../core/router.js";
|
|
3
9
|
import { logger } from "../log.js";
|
package/dist/tasks/routes.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// The area's HTTP surface: tasks, runs, group and message routes for the
|
|
2
|
+
// Console, plus the Activity snapshot it draws its graph from. A route reads
|
|
3
|
+
// its body, names the caller and hands the decision to TaskService — policy
|
|
4
|
+
// that lives here would be policy the task tool does not get.
|
|
1
5
|
import { record, requiredString } from "./definitions.js";
|
|
2
6
|
const jsonBody = async (req) => req.json().catch(() => null);
|
|
3
7
|
export function registerTaskRoutes(app, tasks, activity) {
|
package/dist/tasks/runs.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// A definition plus an input becomes a queued run: where it came from, how
|
|
2
|
+
// deep in a subagent chain it sits, whether it overlaps a run already going,
|
|
3
|
+
// and which session hears about it. The limits that keep a chain from
|
|
4
|
+
// exploding (depth, children per root) are decided here, once, because every
|
|
5
|
+
// caller — scheduler, tool, HTTP — enqueues through this one door.
|
|
1
6
|
import { logger } from "../log.js";
|
|
2
7
|
import { TaskCallbacks } from "./callbacks.js";
|
|
3
8
|
import { newId } from "./definitions.js";
|
package/dist/tasks/service.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// The one object the rest of Pier talks to about tasks, and the clock behind
|
|
2
|
+
// it: the tick that finds what is due, the boot recovery that writes off runs
|
|
3
|
+
// a restart interrupted, and the pause a drain needs. Every decision it looks
|
|
4
|
+
// like it makes belongs to a file beside it (definitions, runs, execution,
|
|
5
|
+
// groups, messages, callbacks) — what is genuinely here is scheduling and the
|
|
6
|
+
// facade, so the HTTP routes and the task tool cannot drift apart.
|
|
1
7
|
import { EventHub } from "../core/hub.js";
|
|
2
8
|
import { Router } from "../core/router.js";
|
|
3
9
|
import { logger } from "../log.js";
|
package/dist/tasks/store.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// Every query this area makes against pier.db, and nothing else: definitions,
|
|
2
|
+
// runs, groups and messages are rows here, read and written through one
|
|
3
|
+
// connection db.ts opened. A store owns its queries, never its own tables or
|
|
4
|
+
// its own handle — the schema is db.ts's migration list.
|
|
1
5
|
import { pierDb } from "../db.js";
|
|
2
6
|
const clamp = (limit, cap) => Math.min(Math.max(limit, 1), cap);
|
|
3
7
|
export class TaskStore {
|
package/dist/tasks/types.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// The vocabulary every file in this area shares: what a task, a run, a group
|
|
2
|
+
// and a control message *are*, plus the delivery constants the outbox and the
|
|
3
|
+
// messenger must agree on. Owner-defined and browser-importable type-only
|
|
4
|
+
// (architecture.md), so nothing here may reach for a runtime or a node builtin.
|
|
1
5
|
export const retryDelay = (attempts) => Math.min(60_000, 1000 * 2 ** Math.min(attempts, 6));
|
|
2
6
|
/** Attempts before a delivery is given up on and reported. With the backoff
|
|
3
7
|
* above that is ~4 minutes: long enough to outlast a busy or restarting
|