tau-all-agent 0.1.2 → 0.1.4
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 +23 -22
- package/extensions/answer.ts +4 -4
- package/extensions/branch-term.ts +3 -3
- package/extensions/btw.ts +4 -4
- package/extensions/ghostty.ts +1 -1
- package/extensions/git-diff-stats.ts +1 -1
- package/extensions/git-pr-status.ts +1 -1
- package/extensions/insights.ts +3 -3
- package/extensions/interlude.ts +1 -1
- package/extensions/loop.ts +3 -3
- package/extensions/memory.ts +8 -2
- package/extensions/notify.ts +1 -1
- package/extensions/openai-fast.ts +1 -1
- package/extensions/openai-verbosity.ts +1 -1
- package/extensions/review.ts +111 -38
- package/extensions/sandbox/index.ts +84 -8
- package/extensions/telegram/daemon.mjs +148 -7
- package/extensions/telegram/index.ts +1 -1
- package/extensions/tool-display-mode.ts +544 -0
- package/extensions/usage/anthropic.ts +1 -1
- package/extensions/usage/github-copilot.ts +1 -1
- package/extensions/usage/index.ts +3 -3
- package/extensions/usage/minimax.ts +1 -1
- package/extensions/usage/openai-codex.ts +1 -1
- package/extensions/usage/openrouter.ts +1 -1
- package/extensions/usage/providers.ts +0 -2
- package/extensions/usage/types.ts +1 -2
- package/extensions/usage/zai.ts +1 -1
- package/extensions/websearch/index.ts +3 -3
- package/extensions/websearch/providers/anthropic.pi.ts +1 -1
- package/extensions/websearch/providers/gemini.pi.ts +1 -1
- package/extensions/websearch/providers/openai-codex.pi.ts +1 -1
- package/extensions/websearch/providers/pi-model.shared.ts +2 -2
- package/extensions/worktree.ts +3 -3
- package/package.json +23 -8
- package/skills/oracle/scripts/oracle +0 -1
- package/extensions/usage/google-gemini-cli.ts +0 -232
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
* },
|
|
26
26
|
* "filesystem": {
|
|
27
27
|
* "denyRead": ["~/.ssh", "~/.aws"],
|
|
28
|
-
* "allowWrite": ["."
|
|
28
|
+
* "allowWrite": ["."],
|
|
29
29
|
* "denyWrite": [".env"],
|
|
30
|
+
* "allowTempDirs": true,
|
|
30
31
|
* "allowGitCommonDir": true
|
|
31
32
|
* }
|
|
32
33
|
* }
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
|
|
48
49
|
import { spawn, spawnSync } from "node:child_process";
|
|
49
50
|
import { existsSync, readFileSync } from "node:fs";
|
|
50
|
-
import { homedir } from "node:os";
|
|
51
|
+
import { homedir, tmpdir } from "node:os";
|
|
51
52
|
import { join, resolve } from "node:path";
|
|
52
53
|
import {
|
|
53
54
|
SandboxManager,
|
|
@@ -62,8 +63,8 @@ import {
|
|
|
62
63
|
globToRegex,
|
|
63
64
|
normalizePathForSandbox,
|
|
64
65
|
} from "@anthropic-ai/sandbox-runtime/dist/sandbox/sandbox-utils.js";
|
|
65
|
-
import type { ExtensionAPI, ExtensionContext } from "@
|
|
66
|
-
import { type BashOperations, createBashTool } from "@
|
|
66
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
67
|
+
import { type BashOperations, createBashTool } from "@earendil-works/pi-coding-agent";
|
|
67
68
|
|
|
68
69
|
// --- Constants ---
|
|
69
70
|
|
|
@@ -74,24 +75,69 @@ const DEFAULT_CONFIG: SandboxConfig = {
|
|
|
74
75
|
mode: DEFAULT_PROMPT_MODE,
|
|
75
76
|
network: {
|
|
76
77
|
allowedDomains: [
|
|
78
|
+
"localhost",
|
|
79
|
+
"127.0.0.1",
|
|
77
80
|
"npmjs.org",
|
|
78
81
|
"*.npmjs.org",
|
|
79
82
|
"registry.npmjs.org",
|
|
80
83
|
"registry.yarnpkg.com",
|
|
81
84
|
"pypi.org",
|
|
82
85
|
"*.pypi.org",
|
|
86
|
+
"pythonhosted.org",
|
|
87
|
+
"*.pythonhosted.org",
|
|
88
|
+
"crates.io",
|
|
89
|
+
"*.crates.io",
|
|
90
|
+
"proxy.golang.org",
|
|
91
|
+
"sum.golang.org",
|
|
92
|
+
"go.dev",
|
|
93
|
+
"golang.org",
|
|
94
|
+
"rubygems.org",
|
|
95
|
+
"*.rubygems.org",
|
|
83
96
|
"github.com",
|
|
84
97
|
"*.github.com",
|
|
98
|
+
"githubusercontent.com",
|
|
99
|
+
"*.githubusercontent.com",
|
|
85
100
|
"api.github.com",
|
|
86
101
|
"raw.githubusercontent.com",
|
|
102
|
+
"ghcr.io",
|
|
103
|
+
"docker.io",
|
|
104
|
+
"*.docker.io",
|
|
105
|
+
"production.cloudflare.docker.com",
|
|
106
|
+
"sentry.io",
|
|
107
|
+
"*.sentry.io",
|
|
108
|
+
"datadoghq.com",
|
|
109
|
+
"*.datadoghq.com",
|
|
110
|
+
"datadoghq.eu",
|
|
111
|
+
"*.datadoghq.eu",
|
|
112
|
+
"anthropic.com",
|
|
113
|
+
"*.anthropic.com",
|
|
114
|
+
"claude.ai",
|
|
115
|
+
"*.claude.ai",
|
|
116
|
+
"openai.com",
|
|
117
|
+
"*.openai.com",
|
|
118
|
+
"chatgpt.com",
|
|
119
|
+
"*.chatgpt.com",
|
|
120
|
+
"google.com",
|
|
121
|
+
"*.google.com",
|
|
122
|
+
"googleapis.com",
|
|
123
|
+
"*.googleapis.com",
|
|
124
|
+
"todoist.com",
|
|
125
|
+
"*.todoist.com",
|
|
126
|
+
"twist.com",
|
|
127
|
+
"*.twist.com",
|
|
128
|
+
"doist.com",
|
|
129
|
+
"*.doist.com",
|
|
87
130
|
],
|
|
88
131
|
deniedDomains: [],
|
|
132
|
+
allowLocalBinding: true,
|
|
89
133
|
},
|
|
90
134
|
filesystem: {
|
|
91
135
|
denyRead: ["~/.ssh", "~/.aws", "~/.gnupg"],
|
|
92
|
-
allowWrite: [".", "
|
|
136
|
+
allowWrite: [".", "~/.cache", "~/.npm"],
|
|
93
137
|
denyWrite: [".env", ".env.*", "*.pem", "*.key"],
|
|
94
|
-
|
|
138
|
+
allowTempDirs: true,
|
|
139
|
+
allowGitConfig: true,
|
|
140
|
+
allowGitCommonDir: true,
|
|
95
141
|
},
|
|
96
142
|
};
|
|
97
143
|
|
|
@@ -120,6 +166,7 @@ type SandboxConfig = Omit<SandboxRuntimeConfig, "filesystem"> & {
|
|
|
120
166
|
enabled?: boolean;
|
|
121
167
|
mode?: PromptMode;
|
|
122
168
|
filesystem: SandboxRuntimeConfig["filesystem"] & {
|
|
169
|
+
allowTempDirs?: boolean;
|
|
123
170
|
allowGitCommonDir?: boolean;
|
|
124
171
|
};
|
|
125
172
|
};
|
|
@@ -582,6 +629,10 @@ function finalizeConfig(config: SandboxConfig): SandboxConfig {
|
|
|
582
629
|
DEFAULT_CONFIG.filesystem.denyWrite,
|
|
583
630
|
"filesystem.denyWrite",
|
|
584
631
|
),
|
|
632
|
+
allowTempDirs:
|
|
633
|
+
typeof config.filesystem?.allowTempDirs === "boolean"
|
|
634
|
+
? config.filesystem.allowTempDirs
|
|
635
|
+
: DEFAULT_CONFIG.filesystem.allowTempDirs,
|
|
585
636
|
allowGitCommonDir:
|
|
586
637
|
typeof config.filesystem?.allowGitCommonDir === "boolean"
|
|
587
638
|
? config.filesystem.allowGitCommonDir
|
|
@@ -705,12 +756,32 @@ function deepMerge(base: SandboxConfig, overrides: Partial<SandboxConfig>): Sand
|
|
|
705
756
|
return result;
|
|
706
757
|
}
|
|
707
758
|
|
|
759
|
+
let cachedTemporaryWritePaths: string[] | undefined;
|
|
760
|
+
|
|
761
|
+
function getTemporaryWritePaths(): string[] {
|
|
762
|
+
if (cachedTemporaryWritePaths) return cachedTemporaryWritePaths;
|
|
763
|
+
|
|
764
|
+
// allowTempDirs always includes the conventional shared temp path, even when
|
|
765
|
+
// os.tmpdir() points at a platform-specific per-user directory.
|
|
766
|
+
const currentTmpDir = tmpdir();
|
|
767
|
+
const paths = ["/tmp", currentTmpDir, normalizePathForSandbox(currentTmpDir)];
|
|
768
|
+
if (process.platform === "darwin") paths.push("/private/tmp");
|
|
769
|
+
|
|
770
|
+
cachedTemporaryWritePaths = Array.from(
|
|
771
|
+
new Set(paths.map((path) => path.replace(/\/+$/, "") || "/")),
|
|
772
|
+
);
|
|
773
|
+
return cachedTemporaryWritePaths;
|
|
774
|
+
}
|
|
775
|
+
|
|
708
776
|
function toRuntimeConfig(config: SandboxConfig): SandboxRuntimeConfig {
|
|
709
|
-
const { allowGitCommonDir: _allowGitCommonDir, ...filesystem } = config.filesystem;
|
|
777
|
+
const { allowGitCommonDir: _allowGitCommonDir, allowTempDirs, ...filesystem } = config.filesystem;
|
|
778
|
+
const allowWrite = allowTempDirs
|
|
779
|
+
? Array.from(new Set([...filesystem.allowWrite, ...getTemporaryWritePaths()]))
|
|
780
|
+
: filesystem.allowWrite;
|
|
710
781
|
|
|
711
782
|
return {
|
|
712
783
|
network: config.network,
|
|
713
|
-
filesystem,
|
|
784
|
+
filesystem: { ...filesystem, allowWrite },
|
|
714
785
|
ignoreViolations: config.ignoreViolations,
|
|
715
786
|
enableWeakerNestedSandbox: config.enableWeakerNestedSandbox,
|
|
716
787
|
enableWeakerNetworkIsolation: config.enableWeakerNetworkIsolation,
|
|
@@ -2455,11 +2526,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
2455
2526
|
" Network:",
|
|
2456
2527
|
` Allowed: ${runtimeConfig.network.allowedDomains.join(", ") || "(none)"}`,
|
|
2457
2528
|
` Denied: ${runtimeConfig.network.deniedDomains.join(", ") || "(none)"}`,
|
|
2529
|
+
` allowLocalBinding: ${runtimeConfig.network.allowLocalBinding ? "true" : "false"}`,
|
|
2530
|
+
` allowAllUnixSockets: ${runtimeConfig.network.allowAllUnixSockets ? "true" : "false"}`,
|
|
2531
|
+
` allowUnixSockets: ${runtimeConfig.network.allowUnixSockets?.join(", ") || "(none)"}`,
|
|
2458
2532
|
"",
|
|
2459
2533
|
" Filesystem:",
|
|
2460
2534
|
` Deny Read: ${runtimeConfig.filesystem.denyRead.join(", ") || "(none)"}`,
|
|
2461
2535
|
` Allow Write: ${runtimeConfig.filesystem.allowWrite.join(", ") || "(none)"}`,
|
|
2462
2536
|
` Deny Write: ${runtimeConfig.filesystem.denyWrite.join(", ") || "(none)"}`,
|
|
2537
|
+
` allowTempDirs: ${sandboxConfig?.filesystem.allowTempDirs ? "true" : "false"}`,
|
|
2538
|
+
` allowGitConfig: ${runtimeConfig.filesystem.allowGitConfig ? "true" : "false"}`,
|
|
2463
2539
|
` allowGitCommonDir: ${sandboxConfig?.filesystem.allowGitCommonDir ? "true" : "false"}`,
|
|
2464
2540
|
"",
|
|
2465
2541
|
" Advanced:",
|
|
@@ -6,7 +6,7 @@ import net from "node:net";
|
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import { execFile, spawn } from "node:child_process";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
|
-
import
|
|
9
|
+
import { EventEmitter } from "node:events";
|
|
10
10
|
import { extractTextFromMessage } from "./message-text.mjs";
|
|
11
11
|
|
|
12
12
|
const AGENT_DIR = path.join(os.homedir(), ".pi", "agent");
|
|
@@ -26,7 +26,8 @@ const TELEGRAM_COMMANDS = [
|
|
|
26
26
|
{ command: "help", description: "Show available commands" },
|
|
27
27
|
];
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const TELEGRAM_POLL_TIMEOUT_SEC = 30;
|
|
30
|
+
const TELEGRAM_POLL_RETRY_MS = 1_000;
|
|
30
31
|
const TELEGRAM_HTTP_TIMEOUT_MS = 35_000;
|
|
31
32
|
const POLLING_STOP_TIMEOUT_MS = 4_000;
|
|
32
33
|
const ACTIVITY_NOTICE_COOLDOWN_MS = 60 * 60 * 1000;
|
|
@@ -44,6 +45,146 @@ const PI_ENTRYPOINT = process.env.PI_TELEGRAM_PI_ENTRYPOINT?.trim() || undefined
|
|
|
44
45
|
const RESOLVED_TMPDIR = await fsp.realpath(os.tmpdir()).catch(() => os.tmpdir());
|
|
45
46
|
const HEADLESS_SESSION_PATH_ERROR = "Path must start with / or ~ and refer to a directory.";
|
|
46
47
|
|
|
48
|
+
class TelegramApiError extends Error {
|
|
49
|
+
constructor(method, status, description) {
|
|
50
|
+
const suffix = description ? `: ${description}` : "";
|
|
51
|
+
super(`Telegram ${method} failed (${status})${suffix}`);
|
|
52
|
+
this.name = "TelegramApiError";
|
|
53
|
+
this.status = status;
|
|
54
|
+
this.description = description;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class TelegramPollingBot extends EventEmitter {
|
|
59
|
+
constructor(token, options = {}) {
|
|
60
|
+
super();
|
|
61
|
+
this.baseUrl = `https://api.telegram.org/bot${token}`;
|
|
62
|
+
this.pollTimeoutSeconds = options.polling?.params?.timeout ?? TELEGRAM_POLL_TIMEOUT_SEC;
|
|
63
|
+
this.httpTimeoutMs = options.request?.timeout ?? TELEGRAM_HTTP_TIMEOUT_MS;
|
|
64
|
+
this.offset = 0;
|
|
65
|
+
this.stopped = false;
|
|
66
|
+
this.pollController = new AbortController();
|
|
67
|
+
this.pollPromise = this.pollLoop();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async sendChatAction(chatId, action) {
|
|
71
|
+
return this.call("sendChatAction", { chat_id: chatId, action });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async sendMessage(chatId, text, options = {}) {
|
|
75
|
+
return this.call("sendMessage", { chat_id: chatId, text, ...options });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async setMyCommands(commands) {
|
|
79
|
+
return this.call("setMyCommands", { commands });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async answerCallbackQuery(callbackQueryId, options = {}) {
|
|
83
|
+
return this.call("answerCallbackQuery", { callback_query_id: callbackQueryId, ...options });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async stopPolling() {
|
|
87
|
+
if (this.stopped) return;
|
|
88
|
+
this.stopped = true;
|
|
89
|
+
this.pollController.abort();
|
|
90
|
+
await this.pollPromise.catch(() => {});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async pollLoop() {
|
|
94
|
+
while (!this.stopped) {
|
|
95
|
+
try {
|
|
96
|
+
const updates = await this.call(
|
|
97
|
+
"getUpdates",
|
|
98
|
+
{
|
|
99
|
+
offset: this.offset,
|
|
100
|
+
timeout: this.pollTimeoutSeconds,
|
|
101
|
+
allowed_updates: ["message", "callback_query"],
|
|
102
|
+
},
|
|
103
|
+
{ signal: this.pollController.signal },
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
for (const update of Array.isArray(updates) ? updates : []) {
|
|
107
|
+
if (Number.isInteger(update.update_id)) {
|
|
108
|
+
this.offset = Math.max(this.offset, update.update_id + 1);
|
|
109
|
+
}
|
|
110
|
+
if (update.message) this.emit("message", update.message);
|
|
111
|
+
if (update.callback_query) this.emit("callback_query", update.callback_query);
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (this.stopped) return;
|
|
115
|
+
if (this.isAbortError(error)) continue;
|
|
116
|
+
this.emit("polling_error", error);
|
|
117
|
+
await sleep(TELEGRAM_POLL_RETRY_MS);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async call(method, payload, options = {}) {
|
|
123
|
+
const response = await this.fetchJson(method, payload, options);
|
|
124
|
+
if (!response || response.ok !== true) {
|
|
125
|
+
throw new TelegramApiError(
|
|
126
|
+
method,
|
|
127
|
+
response?.error_code ?? 200,
|
|
128
|
+
response?.description || "Unexpected response",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return response.result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async fetchJson(method, payload, options = {}) {
|
|
135
|
+
const controller = new AbortController();
|
|
136
|
+
const timeoutId = setTimeout(() => controller.abort(), this.httpTimeoutMs);
|
|
137
|
+
const onAbort = () => controller.abort();
|
|
138
|
+
if (options.signal?.aborted) {
|
|
139
|
+
clearTimeout(timeoutId);
|
|
140
|
+
throw new DOMException("Aborted", "AbortError");
|
|
141
|
+
}
|
|
142
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch(`${this.baseUrl}/${method}`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: { "Content-Type": "application/json" },
|
|
148
|
+
body: JSON.stringify(payload),
|
|
149
|
+
signal: controller.signal,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
let body;
|
|
153
|
+
try {
|
|
154
|
+
body = await response.json();
|
|
155
|
+
} catch {
|
|
156
|
+
body = undefined;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
throw new TelegramApiError(method, response.status, body?.description);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return body;
|
|
164
|
+
} finally {
|
|
165
|
+
clearTimeout(timeoutId);
|
|
166
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
emit(eventName, ...args) {
|
|
171
|
+
let handled = false;
|
|
172
|
+
for (const listener of this.listeners(eventName)) {
|
|
173
|
+
handled = true;
|
|
174
|
+
try {
|
|
175
|
+
listener(...args);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(`[telegram] ${eventName} handler error`, error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return handled;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
isAbortError(error) {
|
|
184
|
+
return error instanceof Error && error.name === "AbortError";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
47
188
|
async function loadConfig() {
|
|
48
189
|
try {
|
|
49
190
|
const raw = await fsp.readFile(CONFIG_PATH, "utf8");
|
|
@@ -1857,10 +1998,10 @@ async function startServer() {
|
|
|
1857
1998
|
|
|
1858
1999
|
server = await startServer();
|
|
1859
2000
|
|
|
1860
|
-
bot = new
|
|
2001
|
+
bot = new TelegramPollingBot(botTokenInfo.token, {
|
|
1861
2002
|
polling: {
|
|
1862
2003
|
params: {
|
|
1863
|
-
timeout:
|
|
2004
|
+
timeout: TELEGRAM_POLL_TIMEOUT_SEC,
|
|
1864
2005
|
},
|
|
1865
2006
|
},
|
|
1866
2007
|
request: {
|
|
@@ -1869,9 +2010,9 @@ bot = new TelegramBot(botTokenInfo.token, {
|
|
|
1869
2010
|
});
|
|
1870
2011
|
|
|
1871
2012
|
void syncBotCommands();
|
|
1872
|
-
//
|
|
1873
|
-
//
|
|
1874
|
-
//
|
|
2013
|
+
// The polling client retries failed polls internally. Avoid stop/start recovery here:
|
|
2014
|
+
// cancelling an in-flight poll can spin up overlapping pollers and replay the same
|
|
2015
|
+
// Telegram update multiple times.
|
|
1875
2016
|
bot.on("polling_error", (error) => {
|
|
1876
2017
|
const message = errorMessage(error);
|
|
1877
2018
|
const kind = isLikelyNetworkPollingError(error) ? "polling_error (network)" : "polling_error";
|