@oh-my-pi/pi-coding-agent 15.12.0 → 15.12.2
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/CHANGELOG.md +18 -1
- package/dist/cli.js +51 -65
- package/dist/types/collab/protocol.d.ts +8 -2
- package/dist/types/config/settings-schema.d.ts +13 -2
- package/dist/types/tools/tool-result.d.ts +2 -0
- package/package.json +12 -12
- package/src/collab/host.ts +13 -1
- package/src/collab/protocol.ts +25 -16
- package/src/config/settings-schema.ts +15 -5
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/lsp/index.ts +11 -0
- package/src/session/agent-session.ts +45 -12
- package/src/tools/ast-grep.ts +3 -1
- package/src/tools/find.ts +3 -1
- package/src/tools/gh.ts +20 -6
- package/src/tools/irc.ts +4 -0
- package/src/tools/job.ts +12 -4
- package/src/tools/memory-recall.ts +2 -0
- package/src/tools/search.ts +3 -1
- package/src/tools/tool-result.ts +8 -0
|
@@ -92,11 +92,17 @@ export declare function rewriteEnvelopePeer(data: Uint8Array, peerId: number): v
|
|
|
92
92
|
export declare function generateRoomId(): string;
|
|
93
93
|
/**
|
|
94
94
|
* Render the shareable link. Compact forms: the default relay collapses to
|
|
95
|
-
* `<roomId
|
|
95
|
+
* `<roomId>.<key>`, other wss relays drop the scheme (`host[:port]/r/…`);
|
|
96
96
|
* only localhost ws:// links keep their full URL so parsing cannot
|
|
97
97
|
* mis-infer wss.
|
|
98
98
|
*
|
|
99
|
-
*
|
|
99
|
+
* The room secret is dot-joined (`<roomId>.<key>`) rather than `#`-joined:
|
|
100
|
+
* RFC 3986 forbids a raw `#` inside a fragment, so strict URL stacks (macOS
|
|
101
|
+
* Foundation behind terminal click-to-open) percent-encode a second `#` to
|
|
102
|
+
* `%23` and break the link. Parsers still accept the legacy `#` form and the
|
|
103
|
+
* mangled `%23` form.
|
|
104
|
+
*
|
|
105
|
+
* Full links append the write token to the key
|
|
100
106
|
* (`base64url(key ∥ writeToken)`); read-only (view) links carry the bare
|
|
101
107
|
* 32-byte key, which is also the pre-token link format.
|
|
102
108
|
*/
|
|
@@ -1184,7 +1184,7 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
1184
1184
|
readonly doubleEscapeAction: {
|
|
1185
1185
|
readonly type: "enum";
|
|
1186
1186
|
readonly values: readonly ["branch", "tree", "none"];
|
|
1187
|
-
readonly default: "
|
|
1187
|
+
readonly default: "branch";
|
|
1188
1188
|
readonly ui: {
|
|
1189
1189
|
readonly tab: "interaction";
|
|
1190
1190
|
readonly group: "Input";
|
|
@@ -1401,7 +1401,7 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
1401
1401
|
readonly tab: "interaction";
|
|
1402
1402
|
readonly group: "Collab";
|
|
1403
1403
|
readonly label: "Relay URL";
|
|
1404
|
-
readonly description: "Relay used by /collab (wss://host[:port]
|
|
1404
|
+
readonly description: "Relay used by /collab (wss://host[:port])";
|
|
1405
1405
|
};
|
|
1406
1406
|
};
|
|
1407
1407
|
readonly "collab.displayName": {
|
|
@@ -1777,6 +1777,16 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
1777
1777
|
readonly description: "Prune older read results when the same file is read again (cache-aware, runs every turn)";
|
|
1778
1778
|
};
|
|
1779
1779
|
};
|
|
1780
|
+
readonly "compaction.dropUseless": {
|
|
1781
|
+
readonly type: "boolean";
|
|
1782
|
+
readonly default: true;
|
|
1783
|
+
readonly ui: {
|
|
1784
|
+
readonly tab: "context";
|
|
1785
|
+
readonly group: "Compaction";
|
|
1786
|
+
readonly label: "Elide Uneventful Results";
|
|
1787
|
+
readonly description: "Prune tool results flagged contextually useless (no matches, timed-out waits) once consumed (cache-aware)";
|
|
1788
|
+
};
|
|
1789
|
+
};
|
|
1780
1790
|
readonly "snapcompact.systemPrompt": {
|
|
1781
1791
|
readonly type: "enum";
|
|
1782
1792
|
readonly values: readonly ["none", "agents-md", "all"];
|
|
@@ -4509,6 +4519,7 @@ export interface CompactionSettings {
|
|
|
4509
4519
|
idleThresholdTokens: number;
|
|
4510
4520
|
idleTimeoutSeconds: number;
|
|
4511
4521
|
supersedeReads: boolean;
|
|
4522
|
+
dropUseless: boolean;
|
|
4512
4523
|
}
|
|
4513
4524
|
export interface ContextPromotionSettings {
|
|
4514
4525
|
enabled: boolean;
|
|
@@ -26,6 +26,8 @@ export declare class ToolResultBuilder<TDetails extends DetailsWithMeta> {
|
|
|
26
26
|
diagnostics(summary: string, messages: string[]): this;
|
|
27
27
|
/** Flag the result as a non-throwing failure (agent-loop surfaces it as a tool error). */
|
|
28
28
|
error(value?: boolean): this;
|
|
29
|
+
/** Marks the result contextually useless — compaction may elide it once consumed. */
|
|
30
|
+
useless(value?: boolean): this;
|
|
29
31
|
done(): AgentToolResult<TDetails>;
|
|
30
32
|
}
|
|
31
33
|
export declare function toolResult<TDetails extends DetailsWithMeta>(details?: TDetails): ToolResultBuilder<TDetails>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "15.12.
|
|
4
|
+
"version": "15.12.2",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
"@agentclientprotocol/sdk": "0.22.1",
|
|
48
48
|
"@babel/parser": "^7.29.7",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/hashline": "15.12.
|
|
51
|
-
"@oh-my-pi/omp-stats": "15.12.
|
|
52
|
-
"@oh-my-pi/pi-agent-core": "15.12.
|
|
53
|
-
"@oh-my-pi/pi-ai": "15.12.
|
|
54
|
-
"@oh-my-pi/pi-catalog": "15.12.
|
|
55
|
-
"@oh-my-pi/pi-mnemopi": "15.12.
|
|
56
|
-
"@oh-my-pi/pi-natives": "15.12.
|
|
57
|
-
"@oh-my-pi/pi-tui": "15.12.
|
|
58
|
-
"@oh-my-pi/pi-utils": "15.12.
|
|
59
|
-
"@oh-my-pi/pi-wire": "15.12.
|
|
60
|
-
"@oh-my-pi/snapcompact": "15.12.
|
|
50
|
+
"@oh-my-pi/hashline": "15.12.2",
|
|
51
|
+
"@oh-my-pi/omp-stats": "15.12.2",
|
|
52
|
+
"@oh-my-pi/pi-agent-core": "15.12.2",
|
|
53
|
+
"@oh-my-pi/pi-ai": "15.12.2",
|
|
54
|
+
"@oh-my-pi/pi-catalog": "15.12.2",
|
|
55
|
+
"@oh-my-pi/pi-mnemopi": "15.12.2",
|
|
56
|
+
"@oh-my-pi/pi-natives": "15.12.2",
|
|
57
|
+
"@oh-my-pi/pi-tui": "15.12.2",
|
|
58
|
+
"@oh-my-pi/pi-utils": "15.12.2",
|
|
59
|
+
"@oh-my-pi/pi-wire": "15.12.2",
|
|
60
|
+
"@oh-my-pi/snapcompact": "15.12.2",
|
|
61
61
|
"@opentelemetry/api": "^1.9.1",
|
|
62
62
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
63
63
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
package/src/collab/host.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
COLLAB_PROTO,
|
|
30
30
|
type CollabFrame,
|
|
31
31
|
type CollabParticipant,
|
|
32
|
+
type CollabPromptDetails,
|
|
32
33
|
type CollabSessionState,
|
|
33
34
|
formatCollabLink,
|
|
34
35
|
formatCollabWebLink,
|
|
@@ -364,13 +365,24 @@ export class CollabHost {
|
|
|
364
365
|
const name = peer.name;
|
|
365
366
|
const content: string | (TextContent | ImageContent)[] =
|
|
366
367
|
images && images.length > 0 ? [{ type: "text", text }, ...images] : text;
|
|
368
|
+
const details: CollabPromptDetails & { __pendingDisplayTag?: string } = { from: name };
|
|
369
|
+
if (this.#ctx.session.isStreaming) {
|
|
370
|
+
// Mid-turn guest prompts are steered: register the pending-display twin
|
|
371
|
+
// so queuedMessageCount reflects the queued steer (host pending bar +
|
|
372
|
+
// guests' "queued ×N" badge). The tag dequeues the entry when the agent
|
|
373
|
+
// consumes the message (mirrors the skill-prompt path).
|
|
374
|
+
details.__pendingDisplayTag = this.#ctx.session.enqueueCustomMessageDisplay(text, "steer");
|
|
375
|
+
this.#ctx.updatePendingMessagesDisplay();
|
|
376
|
+
this.#ctx.ui.requestRender();
|
|
377
|
+
this.#scheduleStateBroadcast();
|
|
378
|
+
}
|
|
367
379
|
this.#ctx.session
|
|
368
380
|
.promptCustomMessage(
|
|
369
381
|
{
|
|
370
382
|
customType: COLLAB_PROMPT_MESSAGE_TYPE,
|
|
371
383
|
content,
|
|
372
384
|
display: true,
|
|
373
|
-
details
|
|
385
|
+
details,
|
|
374
386
|
attribution: "user",
|
|
375
387
|
},
|
|
376
388
|
{ streamingBehavior: "steer" },
|
package/src/collab/protocol.ts
CHANGED
|
@@ -108,11 +108,11 @@ export function rewriteEnvelopePeer(data: Uint8Array, peerId: number): void {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
111
|
-
// Link format: wss://<host[:port]>/r/<roomId
|
|
111
|
+
// Link format: wss://<host[:port]>/r/<roomId>.<base64url-32-byte-key>
|
|
112
112
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
113
113
|
|
|
114
|
-
const ROOM_PATH_RE = /^\/r\/([A-Za-z0-9_-]{10,64})
|
|
115
|
-
const BARE_LINK_RE = /^([A-Za-z0-9_-]{10,64})
|
|
114
|
+
const ROOM_PATH_RE = /^\/r\/([A-Za-z0-9_-]{10,64})(?:\.([A-Za-z0-9_-]+))?$/;
|
|
115
|
+
const BARE_LINK_RE = /^([A-Za-z0-9_-]{10,64})[#.]([A-Za-z0-9_-]+)$/;
|
|
116
116
|
const B64URL_RE = /^[A-Za-z0-9_-]+$/;
|
|
117
117
|
const LOCAL_HOSTNAMES: Record<string, true> = { localhost: true, "127.0.0.1": true, "::1": true, "[::1]": true };
|
|
118
118
|
|
|
@@ -152,11 +152,17 @@ function normalizeRelayOrigin(relayUrl: string): { origin: string } | { error: s
|
|
|
152
152
|
|
|
153
153
|
/**
|
|
154
154
|
* Render the shareable link. Compact forms: the default relay collapses to
|
|
155
|
-
* `<roomId
|
|
155
|
+
* `<roomId>.<key>`, other wss relays drop the scheme (`host[:port]/r/…`);
|
|
156
156
|
* only localhost ws:// links keep their full URL so parsing cannot
|
|
157
157
|
* mis-infer wss.
|
|
158
158
|
*
|
|
159
|
-
*
|
|
159
|
+
* The room secret is dot-joined (`<roomId>.<key>`) rather than `#`-joined:
|
|
160
|
+
* RFC 3986 forbids a raw `#` inside a fragment, so strict URL stacks (macOS
|
|
161
|
+
* Foundation behind terminal click-to-open) percent-encode a second `#` to
|
|
162
|
+
* `%23` and break the link. Parsers still accept the legacy `#` form and the
|
|
163
|
+
* mangled `%23` form.
|
|
164
|
+
*
|
|
165
|
+
* Full links append the write token to the key
|
|
160
166
|
* (`base64url(key ∥ writeToken)`); read-only (view) links carry the bare
|
|
161
167
|
* 32-byte key, which is also the pre-token link format.
|
|
162
168
|
*/
|
|
@@ -165,11 +171,11 @@ export function formatCollabLink(relayUrl: string, roomId: string, key: Uint8Arr
|
|
|
165
171
|
if ("error" in normalized) throw new Error(normalized.error);
|
|
166
172
|
const secret = writeToken ? Buffer.concat([key, writeToken]) : Buffer.from(key);
|
|
167
173
|
const keyText = secret.toString("base64url");
|
|
168
|
-
if (normalized.origin === DEFAULT_RELAY_URL) return `${roomId}
|
|
174
|
+
if (normalized.origin === DEFAULT_RELAY_URL) return `${roomId}.${keyText}`;
|
|
169
175
|
const compact = normalized.origin.startsWith("wss://")
|
|
170
176
|
? normalized.origin.slice("wss://".length)
|
|
171
177
|
: normalized.origin;
|
|
172
|
-
return `${compact}/r/${roomId}
|
|
178
|
+
return `${compact}/r/${roomId}.${keyText}`;
|
|
173
179
|
}
|
|
174
180
|
|
|
175
181
|
/**
|
|
@@ -193,10 +199,12 @@ export function formatCollabWebLink(
|
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
export function parseCollabLink(link: string): ParsedCollabLink | { error: string } {
|
|
196
|
-
|
|
197
|
-
//
|
|
202
|
+
// Lenient input: terminals that open OSC 8 links through strict URL stacks
|
|
203
|
+
// (macOS Foundation) percent-encode the legacy second `#` to `%23`.
|
|
204
|
+
let text = link.trim().replace(/%23/gi, "#");
|
|
205
|
+
// Bare `<roomId>.<key>` (legacy `<roomId>#<key>`) → default relay.
|
|
198
206
|
const bare = BARE_LINK_RE.exec(text);
|
|
199
|
-
if (bare) text = `${DEFAULT_RELAY_URL}/r/${bare[1]}
|
|
207
|
+
if (bare) text = `${DEFAULT_RELAY_URL}/r/${bare[1]}.${bare[2]}`;
|
|
200
208
|
// Scheme-less `host[:port]/r/…` → wss.
|
|
201
209
|
else if (!text.includes("://")) text = `wss://${text}`;
|
|
202
210
|
let url: URL;
|
|
@@ -210,17 +218,18 @@ export function parseCollabLink(link: string): ParsedCollabLink | { error: strin
|
|
|
210
218
|
const match = ROOM_PATH_RE.exec(url.pathname);
|
|
211
219
|
if (!match) {
|
|
212
220
|
// Web deep link: `http(s)://<relay>/#<collab-link>` — the fragment holds
|
|
213
|
-
// the whole link
|
|
214
|
-
// the
|
|
215
|
-
// the key (no `#`), which bounds the recursion.
|
|
221
|
+
// the whole link, so recurse on it. The recursion terminates because
|
|
222
|
+
// the inner text is a strict suffix of the input.
|
|
216
223
|
const inner = url.hash.startsWith("#") ? url.hash.slice(1) : url.hash;
|
|
217
|
-
if (inner
|
|
224
|
+
if (inner) return parseCollabLink(inner);
|
|
218
225
|
return { error: "Collab link must contain a /r/<roomId> path" };
|
|
219
226
|
}
|
|
220
227
|
const roomId = match[1]!;
|
|
221
|
-
|
|
228
|
+
// Key rides dot-joined in the path (`/r/<roomId>.<key>`); legacy links
|
|
229
|
+
// carry it in the fragment (`/r/<roomId>#<key>`).
|
|
230
|
+
const fragment = match[2] ?? (url.hash.startsWith("#") ? url.hash.slice(1) : url.hash);
|
|
222
231
|
if (!fragment) {
|
|
223
|
-
return { error: "Collab link is missing the
|
|
232
|
+
return { error: "Collab link is missing the <key> part" };
|
|
224
233
|
}
|
|
225
234
|
const secret = B64URL_RE.test(fragment) ? new Uint8Array(Buffer.from(fragment, "base64url")) : null;
|
|
226
235
|
if (!secret || (secret.byteLength !== ROOM_KEY_BYTES && secret.byteLength !== ROOM_KEY_BYTES + WRITE_TOKEN_BYTES)) {
|
|
@@ -1132,7 +1132,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1132
1132
|
doubleEscapeAction: {
|
|
1133
1133
|
type: "enum",
|
|
1134
1134
|
values: ["branch", "tree", "none"] as const,
|
|
1135
|
-
default: "
|
|
1135
|
+
default: "branch",
|
|
1136
1136
|
ui: {
|
|
1137
1137
|
tab: "interaction",
|
|
1138
1138
|
group: "Input",
|
|
@@ -1339,7 +1339,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1339
1339
|
tab: "interaction",
|
|
1340
1340
|
group: "Collab",
|
|
1341
1341
|
label: "Relay URL",
|
|
1342
|
-
description: "Relay used by /collab (wss://host[:port]
|
|
1342
|
+
description: "Relay used by /collab (wss://host[:port])",
|
|
1343
1343
|
},
|
|
1344
1344
|
},
|
|
1345
1345
|
|
|
@@ -1621,6 +1621,18 @@ export const SETTINGS_SCHEMA = {
|
|
|
1621
1621
|
},
|
|
1622
1622
|
},
|
|
1623
1623
|
|
|
1624
|
+
"compaction.dropUseless": {
|
|
1625
|
+
type: "boolean",
|
|
1626
|
+
default: true,
|
|
1627
|
+
ui: {
|
|
1628
|
+
tab: "context",
|
|
1629
|
+
group: "Compaction",
|
|
1630
|
+
label: "Elide Uneventful Results",
|
|
1631
|
+
description:
|
|
1632
|
+
"Prune tool results flagged contextually useless (no matches, timed-out waits) once consumed (cache-aware)",
|
|
1633
|
+
},
|
|
1634
|
+
},
|
|
1635
|
+
|
|
1624
1636
|
// Experimental: snapcompact inline imaging (transient, per-request; never persisted)
|
|
1625
1637
|
"snapcompact.systemPrompt": {
|
|
1626
1638
|
type: "enum",
|
|
@@ -3936,9 +3948,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
3936
3948
|
|
|
3937
3949
|
"dev.autoqaPush.endpoint": {
|
|
3938
3950
|
type: "string",
|
|
3939
|
-
// Bundled QA collector — runs `/work/pi-www/autoqa` behind qa.omp.sh.
|
|
3940
|
-
// Override via `PI_AUTO_QA_PUSH_URL` or `dev.autoqaPush.endpoint`
|
|
3941
|
-
// in `config.yml` to point at a self-hosted instance.
|
|
3942
3951
|
default: "https://qa.omp.sh/v1/grievances" as const,
|
|
3943
3952
|
ui: {
|
|
3944
3953
|
tab: "tools",
|
|
@@ -4079,6 +4088,7 @@ export interface CompactionSettings {
|
|
|
4079
4088
|
idleThresholdTokens: number;
|
|
4080
4089
|
idleTimeoutSeconds: number;
|
|
4081
4090
|
supersedeReads: boolean;
|
|
4091
|
+
dropUseless: boolean;
|
|
4082
4092
|
}
|
|
4083
4093
|
|
|
4084
4094
|
export interface ContextPromotionSettings {
|