@oh-my-pi/pi-coding-agent 15.1.2 → 15.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/CHANGELOG.md +60 -0
- package/dist/types/async/job-manager.d.ts +3 -2
- package/dist/types/cli/auth-broker-cli.d.ts +25 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +18 -0
- package/dist/types/cli/grievances-cli.d.ts +12 -0
- package/dist/types/commands/auth-broker.d.ts +54 -0
- package/dist/types/commands/auth-gateway.d.ts +32 -0
- package/dist/types/commands/grievances.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -1
- package/dist/types/commit/agentic/tools/schemas.d.ts +9 -1
- package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -1
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +46 -0
- package/dist/types/discovery/agents.d.ts +12 -1
- package/dist/types/edit/renderer.d.ts +3 -0
- package/dist/types/eval/index.d.ts +0 -2
- package/dist/types/goals/tools/goal-tool.d.ts +10 -2
- package/dist/types/index.d.ts +0 -1
- package/dist/types/internal-urls/index.d.ts +1 -1
- package/dist/types/internal-urls/{pi-protocol.d.ts → omp-protocol.d.ts} +3 -3
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/main.d.ts +11 -2
- package/dist/types/modes/acp/acp-agent.d.ts +2 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +13 -1
- package/dist/types/modes/acp/acp-mode.d.ts +3 -1
- package/dist/types/modes/emoji-autocomplete.d.ts +16 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -0
- package/dist/types/plan-mode/approved-plan.d.ts +10 -4
- package/dist/types/sdk.d.ts +10 -3
- package/dist/types/session/agent-session.d.ts +7 -3
- package/dist/types/session/auth-broker-config.d.ts +13 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/client-bridge.d.ts +3 -0
- package/dist/types/tools/eval.d.ts +41 -7
- package/dist/types/tools/irc.d.ts +8 -2
- package/dist/types/tools/report-tool-issue.d.ts +118 -1
- package/dist/types/tools/resolve.d.ts +8 -2
- package/examples/custom-tools/README.md +3 -12
- package/examples/extensions/README.md +2 -15
- package/examples/extensions/api-demo.ts +1 -7
- package/package.json +7 -7
- package/src/async/job-manager.ts +111 -13
- package/src/autoresearch/tools/init-experiment.ts +11 -33
- package/src/autoresearch/tools/log-experiment.ts +10 -24
- package/src/autoresearch/tools/run-experiment.ts +1 -1
- package/src/autoresearch/tools/update-notes.ts +2 -9
- package/src/cli/auth-broker-cli.ts +746 -0
- package/src/cli/auth-gateway-cli.ts +342 -0
- package/src/cli/grievances-cli.ts +109 -16
- package/src/cli/update-cli.ts +1 -5
- package/src/cli.ts +4 -2
- package/src/commands/auth-broker.ts +96 -0
- package/src/commands/auth-gateway.ts +61 -0
- package/src/commands/grievances.ts +13 -8
- package/src/commands/launch.ts +1 -1
- package/src/commit/agentic/agent.ts +2 -0
- package/src/commit/agentic/tools/analyze-file.ts +2 -2
- package/src/commit/agentic/tools/git-file-diff.ts +2 -2
- package/src/commit/agentic/tools/git-hunk.ts +3 -3
- package/src/commit/agentic/tools/git-overview.ts +2 -2
- package/src/commit/agentic/tools/propose-changelog.ts +1 -3
- package/src/commit/agentic/tools/recent-commits.ts +1 -1
- package/src/commit/agentic/tools/schemas.ts +1 -9
- package/src/config/model-equivalence.ts +279 -174
- package/src/config/model-registry.ts +37 -6
- package/src/config/model-resolver.ts +13 -8
- package/src/config/models-config-schema.ts +8 -0
- package/src/config/settings-schema.ts +52 -0
- package/src/cursor.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/profiler.ts +4 -0
- package/src/debug/raw-sse-buffer.ts +100 -59
- package/src/debug/raw-sse.ts +1 -1
- package/src/discovery/agents.ts +15 -4
- package/src/edit/modes/apply-patch.ts +1 -5
- package/src/edit/modes/patch.ts +5 -5
- package/src/edit/modes/replace.ts +5 -5
- package/src/edit/renderer.ts +2 -1
- package/src/edit/streaming.ts +1 -1
- package/src/eval/index.ts +0 -2
- package/src/eval/js/shared/runtime.ts +107 -2
- package/src/eval/py/kernel.ts +1 -1
- package/src/exa/researcher.ts +4 -4
- package/src/exa/search.ts +10 -22
- package/src/exa/websets.ts +33 -33
- package/src/extensibility/typebox.ts +44 -17
- package/src/goals/tools/goal-tool.ts +3 -3
- package/src/index.ts +0 -3
- package/src/internal-urls/docs-index.generated.ts +21 -18
- package/src/internal-urls/index.ts +1 -1
- package/src/internal-urls/{pi-protocol.ts → omp-protocol.ts} +10 -10
- package/src/internal-urls/router.ts +3 -3
- package/src/internal-urls/types.ts +1 -1
- package/src/lsp/types.ts +8 -11
- package/src/main.ts +216 -146
- package/src/mcp/tool-bridge.ts +3 -3
- package/src/modes/acp/acp-agent.ts +203 -57
- package/src/modes/acp/acp-client-bridge.ts +2 -1
- package/src/modes/acp/acp-event-mapper.ts +208 -32
- package/src/modes/acp/acp-mode.ts +11 -3
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/diff.ts +1 -2
- package/src/modes/components/eval-execution.ts +1 -1
- package/src/modes/components/oauth-selector.ts +38 -2
- package/src/modes/components/tool-execution.ts +1 -2
- package/src/modes/components/tree-selector.ts +26 -7
- package/src/modes/controllers/command-controller.ts +95 -34
- package/src/modes/controllers/input-controller.ts +4 -3
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/interactive-mode.ts +92 -19
- package/src/modes/print-mode.ts +3 -3
- package/src/modes/prompt-action-autocomplete.ts +14 -0
- package/src/plan-mode/approved-plan.ts +30 -9
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/ttsr-tool-reminder.md +5 -0
- package/src/prompts/tools/ask.md +4 -3
- package/src/prompts/tools/eval.md +25 -26
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/resolve.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/web-search.md +1 -1
- package/src/sdk.ts +81 -8
- package/src/session/agent-session.ts +362 -131
- package/src/session/agent-storage.ts +7 -2
- package/src/session/auth-broker-config.ts +102 -0
- package/src/session/auth-storage.ts +7 -1
- package/src/session/client-bridge.ts +3 -0
- package/src/session/streaming-output.ts +1 -1
- package/src/task/types.ts +10 -35
- package/src/tools/bash-interactive.ts +4 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser.ts +12 -20
- package/src/tools/eval.ts +77 -100
- package/src/tools/gh.ts +21 -45
- package/src/tools/hindsight-recall.ts +1 -1
- package/src/tools/hindsight-reflect.ts +2 -2
- package/src/tools/hindsight-retain.ts +3 -7
- package/src/tools/index.ts +8 -1
- package/src/tools/inspect-image.ts +4 -1
- package/src/tools/irc.ts +4 -12
- package/src/tools/job.ts +3 -11
- package/src/tools/report-tool-issue.ts +462 -17
- package/src/tools/resolve.ts +2 -7
- package/src/tools/todo-write.ts +8 -15
- package/src/utils/title-generator.ts +3 -0
- package/src/web/search/index.ts +6 -6
- package/dist/types/eval/parse.d.ts +0 -28
- package/dist/types/eval/sniff.d.ts +0 -11
- package/src/eval/eval.lark +0 -36
- package/src/eval/parse.ts +0 -407
- package/src/eval/sniff.ts +0 -28
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
SessionNotification,
|
|
3
3
|
SessionUpdate,
|
|
4
|
+
ToolCall,
|
|
4
5
|
ToolCallContent,
|
|
5
6
|
ToolCallLocation,
|
|
6
7
|
ToolKind,
|
|
@@ -17,6 +18,7 @@ interface MessageProgress {
|
|
|
17
18
|
interface AcpEventMapperOptions {
|
|
18
19
|
getMessageId?: (message: unknown) => string | undefined;
|
|
19
20
|
getMessageProgress?: (message: unknown) => MessageProgress | undefined;
|
|
21
|
+
getToolArgs?: (toolCallId: string) => unknown;
|
|
20
22
|
/**
|
|
21
23
|
* Session cwd. Tool call locations sent to ACP clients must be absolute
|
|
22
24
|
* (the editor host needs them to open or focus files). When provided,
|
|
@@ -30,6 +32,10 @@ interface ContentArrayContainer {
|
|
|
30
32
|
content?: unknown;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
interface DetailsContainer {
|
|
36
|
+
details?: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
interface TypedValue {
|
|
34
40
|
type?: unknown;
|
|
35
41
|
}
|
|
@@ -38,6 +44,10 @@ interface TextLikeContent extends TypedValue {
|
|
|
38
44
|
text?: unknown;
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
interface TerminalIdContainer {
|
|
48
|
+
terminalId?: unknown;
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
interface BinaryLikeContent extends TypedValue {
|
|
42
52
|
data?: unknown;
|
|
43
53
|
mimeType?: unknown;
|
|
@@ -118,6 +128,8 @@ export function mapToolKind(toolName: string): ToolKind {
|
|
|
118
128
|
case "move":
|
|
119
129
|
return "move";
|
|
120
130
|
case "bash":
|
|
131
|
+
case "shell":
|
|
132
|
+
case "exec":
|
|
121
133
|
case "eval":
|
|
122
134
|
return "execute";
|
|
123
135
|
case "search":
|
|
@@ -144,24 +156,20 @@ export function mapAgentSessionEventToAcpSessionUpdates(
|
|
|
144
156
|
case "message_end":
|
|
145
157
|
return mapAssistantMessageEnd(event, sessionId, options);
|
|
146
158
|
case "tool_execution_start": {
|
|
147
|
-
const update
|
|
148
|
-
sessionUpdate: "tool_call",
|
|
159
|
+
const update = buildToolCallStartUpdate({
|
|
149
160
|
toolCallId: event.toolCallId,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
155
|
-
const locations = extractToolLocations(event.args, options.cwd);
|
|
156
|
-
if (locations.length > 0) {
|
|
157
|
-
update.locations = locations;
|
|
158
|
-
}
|
|
161
|
+
toolName: event.toolName,
|
|
162
|
+
args: event.args,
|
|
163
|
+
intent: event.intent,
|
|
164
|
+
cwd: options.cwd,
|
|
165
|
+
});
|
|
159
166
|
return [toSessionNotification(sessionId, update)];
|
|
160
167
|
}
|
|
161
168
|
case "tool_execution_update": {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
const content = mergeToolUpdateContent(
|
|
170
|
+
buildToolStartContent(event.toolName, event.args),
|
|
171
|
+
extractToolCallContent(event.partialResult),
|
|
172
|
+
);
|
|
165
173
|
const update: SessionUpdate = {
|
|
166
174
|
sessionUpdate: "tool_call_update",
|
|
167
175
|
toolCallId: event.toolCallId,
|
|
@@ -178,10 +186,11 @@ export function mapAgentSessionEventToAcpSessionUpdates(
|
|
|
178
186
|
return [toSessionNotification(sessionId, update)];
|
|
179
187
|
}
|
|
180
188
|
case "tool_execution_end": {
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
189
|
+
const resultContent = [...extractDiffToolCallContent(event.result), ...extractToolCallContent(event.result)];
|
|
190
|
+
const content = mergeToolUpdateContent(
|
|
191
|
+
buildToolStartContent(event.toolName, getToolExecutionEndArgs(event, options)),
|
|
192
|
+
resultContent,
|
|
193
|
+
);
|
|
185
194
|
const update: SessionUpdate = {
|
|
186
195
|
sessionUpdate: "tool_call_update",
|
|
187
196
|
toolCallId: event.toolCallId,
|
|
@@ -195,7 +204,12 @@ export function mapAgentSessionEventToAcpSessionUpdates(
|
|
|
195
204
|
if (locations.length > 0) {
|
|
196
205
|
update.locations = locations;
|
|
197
206
|
}
|
|
198
|
-
|
|
207
|
+
const notifications = [toSessionNotification(sessionId, update)];
|
|
208
|
+
const planUpdate = mapTodoWriteResultToPlanUpdate(event);
|
|
209
|
+
if (planUpdate) {
|
|
210
|
+
notifications.push(toSessionNotification(sessionId, planUpdate));
|
|
211
|
+
}
|
|
212
|
+
return notifications;
|
|
199
213
|
}
|
|
200
214
|
case "todo_reminder": {
|
|
201
215
|
const entries = event.todos.map(todo => ({
|
|
@@ -312,6 +326,144 @@ function mapTodoStatus(status: TodoStatus): "pending" | "in_progress" | "complet
|
|
|
312
326
|
return todoStatusMap[status];
|
|
313
327
|
}
|
|
314
328
|
|
|
329
|
+
function mapTodoWriteResultToPlanUpdate(
|
|
330
|
+
event: Extract<AgentSessionEvent, { type: "tool_execution_end" }>,
|
|
331
|
+
): SessionUpdate | undefined {
|
|
332
|
+
if (event.toolName !== "todo_write" || event.isError) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
const phases = extractTodoWritePhases(event.result);
|
|
336
|
+
if (!Array.isArray(phases)) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
sessionUpdate: "plan",
|
|
341
|
+
entries: extractTodoEntries(phases).map(todo => ({
|
|
342
|
+
content: todo.content,
|
|
343
|
+
priority: "medium" as const,
|
|
344
|
+
status: mapTodoStatus(todo.status),
|
|
345
|
+
})),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function extractTodoWritePhases(result: unknown): unknown {
|
|
350
|
+
if (typeof result !== "object" || result === null || !("details" in result)) {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
const details = (result as { details?: unknown }).details;
|
|
354
|
+
if (typeof details !== "object" || details === null || !("phases" in details)) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
return (details as { phases?: unknown }).phases;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function extractTodoEntries(phases: unknown[]): Array<{ content: string; status: TodoStatus }> {
|
|
361
|
+
const entries: Array<{ content: string; status: TodoStatus }> = [];
|
|
362
|
+
for (const phase of phases) {
|
|
363
|
+
if (typeof phase !== "object" || phase === null || !("tasks" in phase)) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const tasks = (phase as { tasks?: unknown }).tasks;
|
|
367
|
+
if (!Array.isArray(tasks)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
for (const task of tasks) {
|
|
371
|
+
if (typeof task !== "object" || task === null || !("content" in task)) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const content = (task as { content?: unknown }).content;
|
|
375
|
+
if (typeof content !== "string" || content.length === 0) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const status = (task as { status?: TodoStatus }).status;
|
|
379
|
+
entries.push({ content, status: isTodoStatus(status) ? status : "pending" });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return entries;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function isTodoStatus(status: unknown): status is TodoStatus {
|
|
386
|
+
return status === "pending" || status === "in_progress" || status === "completed" || status === "abandoned";
|
|
387
|
+
}
|
|
388
|
+
export function buildToolCallStartUpdate(input: {
|
|
389
|
+
toolCallId: string;
|
|
390
|
+
toolName: string;
|
|
391
|
+
args: unknown;
|
|
392
|
+
intent?: string;
|
|
393
|
+
cwd?: string;
|
|
394
|
+
status?: "pending" | "completed";
|
|
395
|
+
}): SessionUpdate {
|
|
396
|
+
const update: ToolCall & { sessionUpdate: "tool_call" } = {
|
|
397
|
+
sessionUpdate: "tool_call",
|
|
398
|
+
toolCallId: input.toolCallId,
|
|
399
|
+
title: buildToolTitle(input.toolName, input.args, input.intent),
|
|
400
|
+
kind: mapToolKind(input.toolName),
|
|
401
|
+
status: input.status ?? "pending",
|
|
402
|
+
rawInput: input.args,
|
|
403
|
+
};
|
|
404
|
+
const content = buildToolStartContent(input.toolName, input.args);
|
|
405
|
+
if (content.length > 0) {
|
|
406
|
+
update.content = content;
|
|
407
|
+
}
|
|
408
|
+
const locations = extractToolLocations(input.args, input.cwd);
|
|
409
|
+
if (locations.length > 0) {
|
|
410
|
+
update.locations = locations;
|
|
411
|
+
}
|
|
412
|
+
return update;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function normalizeReplayToolArguments(value: unknown): { args: unknown } {
|
|
416
|
+
if (typeof value !== "string") {
|
|
417
|
+
return { args: value ?? {} };
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const parsed: unknown = JSON.parse(value);
|
|
421
|
+
return { args: parsed };
|
|
422
|
+
} catch {
|
|
423
|
+
return { args: value };
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function getToolExecutionEndArgs(
|
|
428
|
+
event: Extract<AgentSessionEvent, { type: "tool_execution_end" }>,
|
|
429
|
+
options: AcpEventMapperOptions,
|
|
430
|
+
): unknown {
|
|
431
|
+
if ("args" in event) {
|
|
432
|
+
return (event as { args?: unknown }).args;
|
|
433
|
+
}
|
|
434
|
+
return options.getToolArgs?.(event.toolCallId);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function buildToolStartContent(toolName: string, args: unknown): ToolCallContent[] {
|
|
438
|
+
if (!isCommandToolName(toolName)) {
|
|
439
|
+
return [];
|
|
440
|
+
}
|
|
441
|
+
const command = extractStringProperty<CommandContainer>(args, "command");
|
|
442
|
+
return command ? [textToolCallContent(`$ ${command}`)] : [];
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function mergeToolUpdateContent(startContent: ToolCallContent[], resultContent: ToolCallContent[]): ToolCallContent[] {
|
|
446
|
+
if (startContent.length === 0) {
|
|
447
|
+
return resultContent;
|
|
448
|
+
}
|
|
449
|
+
const merged = [...startContent];
|
|
450
|
+
for (const item of resultContent) {
|
|
451
|
+
if (
|
|
452
|
+
item.type === "content" &&
|
|
453
|
+
item.content.type === "text" &&
|
|
454
|
+
hasEquivalentTextContent(merged, item.content.text)
|
|
455
|
+
) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
merged.push(item);
|
|
459
|
+
}
|
|
460
|
+
return merged;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function isCommandToolName(toolName: string): boolean {
|
|
464
|
+
return toolName === "bash" || toolName === "shell" || toolName === "exec";
|
|
465
|
+
}
|
|
466
|
+
|
|
315
467
|
function buildToolTitle(toolName: string, args: unknown, intent: string | undefined): string {
|
|
316
468
|
const trimmedIntent = intent?.trim();
|
|
317
469
|
if (trimmedIntent) {
|
|
@@ -418,26 +570,33 @@ function buildDiffContent(entry: unknown): ToolCallContent | undefined {
|
|
|
418
570
|
};
|
|
419
571
|
}
|
|
420
572
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
573
|
+
function extractTerminalId(value: unknown): string | undefined {
|
|
574
|
+
const direct = extractStringProperty<TerminalIdContainer>(value, "terminalId");
|
|
575
|
+
if (direct) return direct;
|
|
576
|
+
if (typeof value !== "object" || value === null) return undefined;
|
|
577
|
+
const details = (value as DetailsContainer).details;
|
|
578
|
+
return extractStringProperty<TerminalIdContainer>(details, "terminalId");
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function terminalToolCallContent(terminalId: string): ToolCallContent {
|
|
582
|
+
return { type: "terminal", terminalId };
|
|
429
583
|
}
|
|
430
584
|
|
|
431
585
|
function extractToolCallContent(value: unknown): ToolCallContent[] {
|
|
432
586
|
const richContent = extractStructuredToolCallContent(value);
|
|
587
|
+
const terminalId = extractTerminalId(value);
|
|
588
|
+
const content =
|
|
589
|
+
terminalId && !hasTerminalContent(richContent, terminalId)
|
|
590
|
+
? [...richContent, terminalToolCallContent(terminalId)]
|
|
591
|
+
: richContent;
|
|
433
592
|
const fallbackText = extractReadableText(value);
|
|
434
593
|
if (!fallbackText) {
|
|
435
|
-
return
|
|
594
|
+
return content;
|
|
436
595
|
}
|
|
437
|
-
if (hasEquivalentTextContent(
|
|
438
|
-
return
|
|
596
|
+
if (hasEquivalentTextContent(content, fallbackText)) {
|
|
597
|
+
return content;
|
|
439
598
|
}
|
|
440
|
-
return [...
|
|
599
|
+
return [...content, textToolCallContent(fallbackText)];
|
|
441
600
|
}
|
|
442
601
|
|
|
443
602
|
function extractStructuredToolCallContent(value: unknown): ToolCallContent[] {
|
|
@@ -596,6 +755,10 @@ function hasEquivalentTextContent(content: ToolCallContent[], text: string): boo
|
|
|
596
755
|
return content.some(item => item.type === "content" && item.content.type === "text" && item.content.text === text);
|
|
597
756
|
}
|
|
598
757
|
|
|
758
|
+
function hasTerminalContent(content: ToolCallContent[], terminalId: string): boolean {
|
|
759
|
+
return content.some(item => item.type === "terminal" && item.terminalId === terminalId);
|
|
760
|
+
}
|
|
761
|
+
|
|
599
762
|
function extractReadableText(value: unknown): string | undefined {
|
|
600
763
|
if (typeof value === "string") {
|
|
601
764
|
return normalizeText(value);
|
|
@@ -625,11 +788,24 @@ function extractReadableText(value: unknown): string | undefined {
|
|
|
625
788
|
return normalizeText(text);
|
|
626
789
|
}
|
|
627
790
|
}
|
|
628
|
-
|
|
791
|
+
if (isTerminalOnlyDetails(value)) {
|
|
792
|
+
return undefined;
|
|
793
|
+
}
|
|
629
794
|
const serialized = safeJsonStringify(value);
|
|
630
795
|
return normalizeText(serialized);
|
|
631
796
|
}
|
|
632
797
|
|
|
798
|
+
function isTerminalOnlyDetails(value: unknown): boolean {
|
|
799
|
+
if (typeof value !== "object" || value === null) {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
if (extractTerminalId(value) === undefined) {
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
805
|
+
const content = (value as ContentArrayContainer).content;
|
|
806
|
+
return content === undefined || (Array.isArray(content) && content.length === 0);
|
|
807
|
+
}
|
|
808
|
+
|
|
633
809
|
function extractAssistantMessageText(value: unknown): string {
|
|
634
810
|
if (typeof value !== "object" || value === null || !("content" in value)) {
|
|
635
811
|
return "";
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import * as stream from "node:stream";
|
|
2
|
-
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { AgentSideConnection, ndJsonStream, type Stream } from "@agentclientprotocol/sdk";
|
|
3
3
|
import type { AgentSession } from "../../session/agent-session";
|
|
4
4
|
import { AcpAgent } from "./acp-agent";
|
|
5
5
|
|
|
6
6
|
export type AcpSessionFactory = (cwd: string) => Promise<AgentSession>;
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export function createAcpConnection(
|
|
9
|
+
transport: Stream,
|
|
10
|
+
createSession: AcpSessionFactory,
|
|
11
|
+
initialSession?: AgentSession,
|
|
12
|
+
): AgentSideConnection {
|
|
13
|
+
return new AgentSideConnection(conn => new AcpAgent(conn, createSession, initialSession), transport);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function runAcpMode(createSession: AcpSessionFactory, initialSession?: AgentSession): Promise<never> {
|
|
9
17
|
const input = stream.Writable.toWeb(process.stdout);
|
|
10
18
|
const output = stream.Readable.toWeb(process.stdin);
|
|
11
19
|
const transport = ndJsonStream(input, output);
|
|
12
|
-
const connection =
|
|
20
|
+
const connection = createAcpConnection(transport, createSession, initialSession);
|
|
13
21
|
await connection.closed;
|
|
14
22
|
process.exit(0);
|
|
15
23
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Component for displaying bash command execution with streaming output.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { sanitizeText } from "@oh-my-pi/pi-natives";
|
|
6
5
|
import {
|
|
7
6
|
Container,
|
|
8
7
|
Ellipsis,
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
truncateToWidth,
|
|
15
14
|
visibleWidth,
|
|
16
15
|
} from "@oh-my-pi/pi-tui";
|
|
16
|
+
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
17
17
|
import { theme } from "../../modes/theme/theme";
|
|
18
18
|
import type { TruncationMeta } from "../../tools/output-meta";
|
|
19
19
|
import { getSixelLineMask, isSixelPassthroughEnabled, sanitizeWithOptionalSixelPassthrough } from "../../utils/sixel";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { sanitizeText } from "@oh-my-pi/pi-
|
|
2
|
-
import { getIndentation } from "@oh-my-pi/pi-utils";
|
|
1
|
+
import { getIndentation, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
3
2
|
import * as Diff from "diff";
|
|
4
3
|
import { getLanguageFromPath, highlightCode, theme } from "../../modes/theme/theme";
|
|
5
4
|
import { type CodeFrameMarker, formatCodeFrameLine, replaceTabs } from "../../tools/render-utils";
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Shares the same kernel session as the agent's eval tool.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { sanitizeText } from "@oh-my-pi/pi-natives";
|
|
7
6
|
import { Container, type Loader, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
7
|
+
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import { highlightCode, theme } from "../../modes/theme/theme";
|
|
9
9
|
import type { TruncationMeta } from "../../tools/output-meta";
|
|
10
10
|
import {
|
|
@@ -5,6 +5,8 @@ import { theme } from "../../modes/theme/theme";
|
|
|
5
5
|
import { matchesSelectCancel } from "../../modes/utils/keybinding-matchers";
|
|
6
6
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
7
7
|
import { DynamicBorder } from "./dynamic-border";
|
|
8
|
+
|
|
9
|
+
const OAUTH_SELECTOR_MAX_VISIBLE = 10;
|
|
8
10
|
/**
|
|
9
11
|
* Component that renders an OAuth provider selector.
|
|
10
12
|
*/
|
|
@@ -144,7 +146,16 @@ export class OAuthSelectorComponent extends Container {
|
|
|
144
146
|
}
|
|
145
147
|
#updateList(): void {
|
|
146
148
|
this.#listContainer.clear();
|
|
147
|
-
|
|
149
|
+
|
|
150
|
+
const total = this.#allProviders.length;
|
|
151
|
+
const maxVisible = OAUTH_SELECTOR_MAX_VISIBLE;
|
|
152
|
+
const startIndex =
|
|
153
|
+
total <= maxVisible
|
|
154
|
+
? 0
|
|
155
|
+
: Math.max(0, Math.min(this.#selectedIndex - Math.floor(maxVisible / 2), total - maxVisible));
|
|
156
|
+
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
157
|
+
|
|
158
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
148
159
|
const provider = this.#allProviders[i];
|
|
149
160
|
if (!provider) continue;
|
|
150
161
|
const isSelected = i === this.#selectedIndex;
|
|
@@ -163,8 +174,14 @@ export class OAuthSelectorComponent extends Container {
|
|
|
163
174
|
this.#listContainer.addChild(new TruncatedText(line, 0, 0));
|
|
164
175
|
}
|
|
165
176
|
|
|
177
|
+
// Scroll indicator when list is windowed
|
|
178
|
+
if (startIndex > 0 || endIndex < total) {
|
|
179
|
+
const scrollInfo = theme.fg("muted", ` (${this.#selectedIndex + 1}/${total})`);
|
|
180
|
+
this.#listContainer.addChild(new TruncatedText(scrollInfo, 0, 0));
|
|
181
|
+
}
|
|
182
|
+
|
|
166
183
|
// Show "no providers" if empty
|
|
167
|
-
if (
|
|
184
|
+
if (total === 0) {
|
|
168
185
|
const message =
|
|
169
186
|
this.#mode === "login" ? "No OAuth providers available" : "No OAuth providers logged in. Use /login first.";
|
|
170
187
|
this.#listContainer.addChild(new TruncatedText(theme.fg("muted", ` ${message}`), 0, 0));
|
|
@@ -191,6 +208,25 @@ export class OAuthSelectorComponent extends Container {
|
|
|
191
208
|
this.#statusMessage = undefined;
|
|
192
209
|
this.#updateList();
|
|
193
210
|
}
|
|
211
|
+
// Page up - jump up by one visible page
|
|
212
|
+
else if (matchesKey(keyData, "pageUp")) {
|
|
213
|
+
if (this.#allProviders.length > 0) {
|
|
214
|
+
this.#selectedIndex = Math.max(0, this.#selectedIndex - OAUTH_SELECTOR_MAX_VISIBLE);
|
|
215
|
+
}
|
|
216
|
+
this.#statusMessage = undefined;
|
|
217
|
+
this.#updateList();
|
|
218
|
+
}
|
|
219
|
+
// Page down - jump down by one visible page
|
|
220
|
+
else if (matchesKey(keyData, "pageDown")) {
|
|
221
|
+
if (this.#allProviders.length > 0) {
|
|
222
|
+
this.#selectedIndex = Math.min(
|
|
223
|
+
this.#allProviders.length - 1,
|
|
224
|
+
this.#selectedIndex + OAUTH_SELECTOR_MAX_VISIBLE,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
this.#statusMessage = undefined;
|
|
228
|
+
this.#updateList();
|
|
229
|
+
}
|
|
194
230
|
// Enter
|
|
195
231
|
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
196
232
|
const selectedProvider = this.#allProviders[this.#selectedIndex];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import { sanitizeText } from "@oh-my-pi/pi-natives";
|
|
3
2
|
import {
|
|
4
3
|
Box,
|
|
5
4
|
type Component,
|
|
@@ -13,7 +12,7 @@ import {
|
|
|
13
12
|
Text,
|
|
14
13
|
type TUI,
|
|
15
14
|
} from "@oh-my-pi/pi-tui";
|
|
16
|
-
import { getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
15
|
+
import { getProjectDir, logger, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
17
16
|
import { EDIT_MODE_STRATEGIES, type EditMode, type PerFileDiffPreview } from "../../edit";
|
|
18
17
|
import type { Theme } from "../../modes/theme/theme";
|
|
19
18
|
import { theme } from "../../modes/theme/theme";
|
|
@@ -453,6 +453,16 @@ class TreeList implements Component {
|
|
|
453
453
|
);
|
|
454
454
|
const endIndex = Math.min(startIndex + this.maxVisibleLines, this.#filteredNodes.length);
|
|
455
455
|
|
|
456
|
+
// Cap the per-row gutter prefix so a content budget is always preserved.
|
|
457
|
+
// Each indent level renders as 3 cells; deep branching would otherwise eat the
|
|
458
|
+
// entire viewport (issue #1144). Reserve at least MIN_CONTENT_COLS for entry
|
|
459
|
+
// text — or half the viewport, whichever is larger — and compress older gutter
|
|
460
|
+
// levels off-screen behind a leading ellipsis when the row would exceed budget.
|
|
461
|
+
const MIN_CONTENT_COLS = 24;
|
|
462
|
+
const OVERHEAD_COLS = 4; // cursor (2) + a touch of breathing room
|
|
463
|
+
const contentReserve = Math.max(MIN_CONTENT_COLS, Math.floor(width / 2));
|
|
464
|
+
const maxIndentLevels = Math.max(1, Math.floor((width - contentReserve - OVERHEAD_COLS) / 3));
|
|
465
|
+
|
|
456
466
|
for (let i = startIndex; i < endIndex; i++) {
|
|
457
467
|
const flatNode = this.#filteredNodes[i];
|
|
458
468
|
const entry = flatNode.node.entry;
|
|
@@ -464,29 +474,34 @@ class TreeList implements Component {
|
|
|
464
474
|
// If multiple roots, shift display (roots at 0, not 1)
|
|
465
475
|
const displayIndent = this.#multipleRoots ? Math.max(0, flatNode.indent - 1) : flatNode.indent;
|
|
466
476
|
|
|
467
|
-
// Build prefix with gutters at their correct positions
|
|
468
|
-
//
|
|
477
|
+
// Build prefix with gutters at their correct positions, clamped to
|
|
478
|
+
// `maxIndentLevels` cells so the content always fits. When clamped, the
|
|
479
|
+
// leftmost cells represent the deepest visible ancestors and a `…` marker
|
|
480
|
+
// indicates older branch context has been compressed.
|
|
469
481
|
const hasConnector = flatNode.showConnector && !flatNode.isVirtualRootChild;
|
|
470
482
|
const connectorSymbol = hasConnector ? (flatNode.isLast ? theme.tree.last : theme.tree.branch) : "";
|
|
471
483
|
const connectorChars = hasConnector ? Array.from(connectorSymbol) : [];
|
|
472
|
-
const
|
|
484
|
+
const renderedIndent = Math.min(displayIndent, maxIndentLevels);
|
|
485
|
+
const scrollOffset = displayIndent - renderedIndent;
|
|
486
|
+
const connectorPositionDisplay = hasConnector ? renderedIndent - 1 : -1;
|
|
473
487
|
|
|
474
488
|
// Build prefix char by char, placing gutters and connector at their positions
|
|
475
|
-
const totalChars =
|
|
489
|
+
const totalChars = renderedIndent * 3;
|
|
476
490
|
const prefixChars: string[] = [];
|
|
477
491
|
for (let i = 0; i < totalChars; i++) {
|
|
478
492
|
const level = Math.floor(i / 3);
|
|
493
|
+
const originalLevel = level + scrollOffset;
|
|
479
494
|
const posInLevel = i % 3;
|
|
480
495
|
|
|
481
|
-
// Check if there's a gutter at this level
|
|
482
|
-
const gutter = flatNode.gutters.find(g => g.position ===
|
|
496
|
+
// Check if there's a gutter at this level (translated to original tree depth)
|
|
497
|
+
const gutter = flatNode.gutters.find(g => g.position === originalLevel);
|
|
483
498
|
if (gutter) {
|
|
484
499
|
if (posInLevel === 0) {
|
|
485
500
|
prefixChars.push(gutter.show ? theme.tree.vertical : " ");
|
|
486
501
|
} else {
|
|
487
502
|
prefixChars.push(" ");
|
|
488
503
|
}
|
|
489
|
-
} else if (hasConnector && level ===
|
|
504
|
+
} else if (hasConnector && level === connectorPositionDisplay) {
|
|
490
505
|
// Connector at this level
|
|
491
506
|
if (posInLevel === 0) {
|
|
492
507
|
prefixChars.push(connectorChars[0] ?? " ");
|
|
@@ -499,6 +514,10 @@ class TreeList implements Component {
|
|
|
499
514
|
prefixChars.push(" ");
|
|
500
515
|
}
|
|
501
516
|
}
|
|
517
|
+
// Mark the leftmost cell when ancestors were compressed off-screen.
|
|
518
|
+
if (scrollOffset > 0 && prefixChars.length > 0) {
|
|
519
|
+
prefixChars[0] = "…";
|
|
520
|
+
}
|
|
502
521
|
const prefix = prefixChars.join("");
|
|
503
522
|
|
|
504
523
|
// Active path marker - shown right before the entry text
|