@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { $env, ptree } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
3
|
-
import { buildResult, loadPage } from "./types";
|
|
3
|
+
import { buildResult, formatMediaDuration, loadPage } from "./types";
|
|
4
4
|
|
|
5
5
|
interface GitHubUrl {
|
|
6
|
-
type:
|
|
6
|
+
type:
|
|
7
|
+
| "blob"
|
|
8
|
+
| "tree"
|
|
9
|
+
| "repo"
|
|
10
|
+
| "issue"
|
|
11
|
+
| "issues"
|
|
12
|
+
| "pull"
|
|
13
|
+
| "pulls"
|
|
14
|
+
| "discussion"
|
|
15
|
+
| "discussions"
|
|
16
|
+
| "actions-run"
|
|
17
|
+
| "actions-job"
|
|
18
|
+
| "other";
|
|
7
19
|
owner: string;
|
|
8
20
|
repo: string;
|
|
9
21
|
ref?: string;
|
|
10
22
|
path?: string;
|
|
11
23
|
number?: number;
|
|
24
|
+
runId?: number;
|
|
25
|
+
jobId?: number;
|
|
12
26
|
}
|
|
13
27
|
|
|
14
28
|
interface GitHubIssueComment {
|
|
@@ -20,7 +34,7 @@ interface GitHubIssueComment {
|
|
|
20
34
|
/**
|
|
21
35
|
* Parse GitHub URL into components
|
|
22
36
|
*/
|
|
23
|
-
function parseGitHubUrl(url: string): GitHubUrl | null {
|
|
37
|
+
export function parseGitHubUrl(url: string): GitHubUrl | null {
|
|
24
38
|
try {
|
|
25
39
|
const parsed = new URL(url);
|
|
26
40
|
if (parsed.hostname !== "github.com") return null;
|
|
@@ -54,6 +68,20 @@ function parseGitHubUrl(url: string): GitHubUrl | null {
|
|
|
54
68
|
return { type: "pulls", owner, repo };
|
|
55
69
|
case "pulls":
|
|
56
70
|
return { type: "pulls", owner, repo };
|
|
71
|
+
case "actions": {
|
|
72
|
+
// /actions/runs/{runId} → run summary + jobs
|
|
73
|
+
// /actions/runs/{runId}/job/{jobId} → single job (web URL uses singular "job")
|
|
74
|
+
// /actions/runs/{runId}/jobs/{jobId} → single job (API-style plural)
|
|
75
|
+
if (subParts[0] === "runs" && /^\d+$/.test(subParts[1] ?? "")) {
|
|
76
|
+
const runId = parseInt(subParts[1], 10);
|
|
77
|
+
const seg = subParts[2];
|
|
78
|
+
if ((seg === "job" || seg === "jobs") && /^\d+$/.test(subParts[3] ?? "")) {
|
|
79
|
+
return { type: "actions-job", owner, repo, runId, jobId: parseInt(subParts[3], 10) };
|
|
80
|
+
}
|
|
81
|
+
return { type: "actions-run", owner, repo, runId };
|
|
82
|
+
}
|
|
83
|
+
return { type: "other", owner, repo };
|
|
84
|
+
}
|
|
57
85
|
case "discussions":
|
|
58
86
|
if (subParts.length > 0 && /^\d+$/.test(subParts[0])) {
|
|
59
87
|
return { type: "discussion", owner, repo, number: parseInt(subParts[0], 10) };
|
|
@@ -371,6 +399,212 @@ async function renderGitHubRepo(
|
|
|
371
399
|
return { content: md, ok: true };
|
|
372
400
|
}
|
|
373
401
|
|
|
402
|
+
interface GitHubActionsStep {
|
|
403
|
+
name: string;
|
|
404
|
+
status: string;
|
|
405
|
+
conclusion: string | null;
|
|
406
|
+
number: number;
|
|
407
|
+
started_at: string | null;
|
|
408
|
+
completed_at: string | null;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
interface GitHubActionsJob {
|
|
412
|
+
id: number;
|
|
413
|
+
run_id: number;
|
|
414
|
+
name: string;
|
|
415
|
+
status: string;
|
|
416
|
+
conclusion: string | null;
|
|
417
|
+
started_at: string | null;
|
|
418
|
+
completed_at: string | null;
|
|
419
|
+
html_url: string | null;
|
|
420
|
+
steps?: GitHubActionsStep[];
|
|
421
|
+
runner_name?: string | null;
|
|
422
|
+
labels?: string[];
|
|
423
|
+
workflow_name?: string | null;
|
|
424
|
+
head_branch?: string | null;
|
|
425
|
+
head_sha?: string;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
interface GitHubActionsRun {
|
|
429
|
+
id: number;
|
|
430
|
+
name?: string | null;
|
|
431
|
+
display_title?: string;
|
|
432
|
+
run_number: number;
|
|
433
|
+
run_attempt?: number;
|
|
434
|
+
event: string;
|
|
435
|
+
status: string;
|
|
436
|
+
conclusion: string | null;
|
|
437
|
+
head_branch?: string | null;
|
|
438
|
+
head_sha?: string;
|
|
439
|
+
html_url: string;
|
|
440
|
+
created_at: string;
|
|
441
|
+
updated_at: string;
|
|
442
|
+
run_started_at?: string;
|
|
443
|
+
actor?: { login: string };
|
|
444
|
+
triggering_actor?: { login: string };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/** Combine status + conclusion into a single label, e.g. `completed (failure)`. */
|
|
448
|
+
function statusLabel(status: string, conclusion: string | null | undefined): string {
|
|
449
|
+
return conclusion ? `${status} (${conclusion})` : status;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** Wall-clock duration between two ISO timestamps, formatted HH:MM:SS / MM:SS. Empty when unknown. */
|
|
453
|
+
function actionDuration(start?: string | null, end?: string | null): string {
|
|
454
|
+
if (!start || !end) return "";
|
|
455
|
+
const ms = Date.parse(end) - Date.parse(start);
|
|
456
|
+
if (!Number.isFinite(ms) || ms < 0) return "";
|
|
457
|
+
return formatMediaDuration(Math.round(ms / 1000));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** Escape `|` so step/job names can't break a markdown table row. */
|
|
461
|
+
function escapeCell(text: string): string {
|
|
462
|
+
return text.replaceAll("|", "\\|");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Strip the per-line ISO-8601 timestamp prefix GitHub prepends to every job log line.
|
|
467
|
+
* Cuts ~28 bytes/line of noise while preserving the message text. Also drops the leading
|
|
468
|
+
* UTF-8 BOM GitHub puts at the start of the log file (otherwise the first line's timestamp
|
|
469
|
+
* survives because `^` no longer sits before a digit).
|
|
470
|
+
*/
|
|
471
|
+
export function stripActionsLogTimestamps(logs: string): string {
|
|
472
|
+
return logs.replace(/^\uFEFF/, "").replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z /gm, "");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** Render a job's steps as a markdown table. Empty string when there are no steps. */
|
|
476
|
+
function renderActionsSteps(steps?: GitHubActionsStep[]): string {
|
|
477
|
+
if (!steps || steps.length === 0) return "";
|
|
478
|
+
let md = "| # | Step | Status | Conclusion | Duration |\n";
|
|
479
|
+
md += "|---|------|--------|------------|----------|\n";
|
|
480
|
+
for (const step of steps) {
|
|
481
|
+
const dur = actionDuration(step.started_at, step.completed_at) || "-";
|
|
482
|
+
md += `| ${step.number} | ${escapeCell(step.name)} | ${step.status} | ${step.conclusion ?? "-"} | ${dur} |\n`;
|
|
483
|
+
}
|
|
484
|
+
return `${md}\n`;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Run-level metadata lines shared by the run and job renderers. */
|
|
488
|
+
function renderActionsRunMeta(run: GitHubActionsRun): string {
|
|
489
|
+
let md = `**Workflow:** ${run.name ?? "(unknown)"}\n`;
|
|
490
|
+
md += `**Run:** #${run.run_number}`;
|
|
491
|
+
if (run.run_attempt && run.run_attempt > 1) md += ` (attempt ${run.run_attempt})`;
|
|
492
|
+
md += ` · ${statusLabel(run.status, run.conclusion)}\n`;
|
|
493
|
+
if (run.head_branch) {
|
|
494
|
+
md += `**Branch:** ${run.head_branch}${run.head_sha ? ` @ ${run.head_sha.slice(0, 7)}` : ""}\n`;
|
|
495
|
+
}
|
|
496
|
+
const actor = run.triggering_actor?.login ?? run.actor?.login;
|
|
497
|
+
md += `**Event:** ${run.event}${actor ? ` · by @${actor}` : ""}\n`;
|
|
498
|
+
const started = run.run_started_at ?? run.created_at;
|
|
499
|
+
const dur = actionDuration(started, run.updated_at);
|
|
500
|
+
md += `Started: ${started}${dur ? ` · Duration: ${dur}` : ""}\n`;
|
|
501
|
+
md += `URL: ${run.html_url}\n`;
|
|
502
|
+
return md;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/** Fetch a job's plain-text logs. Returns null when unavailable (no token / expired / private). */
|
|
506
|
+
async function fetchGitHubJobLogs(
|
|
507
|
+
owner: string,
|
|
508
|
+
repo: string,
|
|
509
|
+
jobId: number,
|
|
510
|
+
timeout: number,
|
|
511
|
+
signal?: AbortSignal,
|
|
512
|
+
): Promise<string | null> {
|
|
513
|
+
const headers: Record<string, string> = {
|
|
514
|
+
Accept: "application/vnd.github+json",
|
|
515
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
516
|
+
};
|
|
517
|
+
const token = $env.GITHUB_TOKEN || $env.GH_TOKEN;
|
|
518
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
519
|
+
|
|
520
|
+
// 302 → signed log URL on a different origin; fetch strips Authorization on the cross-origin hop.
|
|
521
|
+
const result = await loadPage(`https://api.github.com/repos/${owner}/${repo}/actions/jobs/${jobId}/logs`, {
|
|
522
|
+
timeout,
|
|
523
|
+
headers,
|
|
524
|
+
signal,
|
|
525
|
+
});
|
|
526
|
+
return result.ok && result.content ? result.content : null;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Render a workflow run: run metadata plus a per-job breakdown. Steps are listed for any job that
|
|
531
|
+
* did not succeed (the debugging-relevant ones); successful jobs collapse to a single line.
|
|
532
|
+
*/
|
|
533
|
+
async function renderGitHubActionsRun(
|
|
534
|
+
gh: GitHubUrl,
|
|
535
|
+
timeout: number,
|
|
536
|
+
signal?: AbortSignal,
|
|
537
|
+
): Promise<{ content: string; ok: boolean }> {
|
|
538
|
+
const runResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}/actions/runs/${gh.runId}`, timeout, signal);
|
|
539
|
+
if (!runResult.ok || !runResult.data) return { content: "", ok: false };
|
|
540
|
+
|
|
541
|
+
const run = runResult.data as GitHubActionsRun;
|
|
542
|
+
let md = `# ${run.display_title || run.name || `Run #${run.run_number}`}\n\n`;
|
|
543
|
+
md += renderActionsRunMeta(run);
|
|
544
|
+
md += `\n---\n\n`;
|
|
545
|
+
|
|
546
|
+
const jobsResult = await fetchGitHubApi(
|
|
547
|
+
`/repos/${gh.owner}/${gh.repo}/actions/runs/${gh.runId}/jobs?per_page=100`,
|
|
548
|
+
timeout,
|
|
549
|
+
signal,
|
|
550
|
+
);
|
|
551
|
+
if (jobsResult.ok && jobsResult.data) {
|
|
552
|
+
const jobs = (jobsResult.data as { jobs?: GitHubActionsJob[] }).jobs ?? [];
|
|
553
|
+
md += `## Jobs (${jobs.length})\n\n`;
|
|
554
|
+
for (const job of jobs) {
|
|
555
|
+
const dur = actionDuration(job.started_at, job.completed_at);
|
|
556
|
+
md += `### ${escapeCell(job.name)} — ${statusLabel(job.status, job.conclusion)}${dur ? ` (${dur})` : ""}\n\n`;
|
|
557
|
+
if (job.conclusion !== "success") {
|
|
558
|
+
md += renderActionsSteps(job.steps);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return { content: md, ok: true };
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Render a single workflow job: run context, step table, and the full job logs.
|
|
568
|
+
*/
|
|
569
|
+
async function renderGitHubActionsJob(
|
|
570
|
+
gh: GitHubUrl,
|
|
571
|
+
timeout: number,
|
|
572
|
+
signal?: AbortSignal,
|
|
573
|
+
): Promise<{ content: string; ok: boolean }> {
|
|
574
|
+
const jobResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}/actions/jobs/${gh.jobId}`, timeout, signal);
|
|
575
|
+
if (!jobResult.ok || !jobResult.data) return { content: "", ok: false };
|
|
576
|
+
|
|
577
|
+
const job = jobResult.data as GitHubActionsJob;
|
|
578
|
+
|
|
579
|
+
// Best-effort run context for nicer headers; the job render stands on its own without it.
|
|
580
|
+
const runResult = await fetchGitHubApi(`/repos/${gh.owner}/${gh.repo}/actions/runs/${job.run_id}`, timeout, signal);
|
|
581
|
+
const run = runResult.ok && runResult.data ? (runResult.data as GitHubActionsRun) : null;
|
|
582
|
+
|
|
583
|
+
let md = `# ${escapeCell(job.name)}\n\n`;
|
|
584
|
+
if (run) {
|
|
585
|
+
md += renderActionsRunMeta(run);
|
|
586
|
+
} else if (job.workflow_name) {
|
|
587
|
+
md += `**Workflow:** ${job.workflow_name}\n`;
|
|
588
|
+
if (job.head_branch) md += `**Branch:** ${job.head_branch}\n`;
|
|
589
|
+
}
|
|
590
|
+
const dur = actionDuration(job.started_at, job.completed_at);
|
|
591
|
+
md += `**Job:** ${escapeCell(job.name)} · ${statusLabel(job.status, job.conclusion)}${dur ? ` · ${dur}` : ""}\n`;
|
|
592
|
+
if (job.runner_name) md += `**Runner:** ${job.runner_name}\n`;
|
|
593
|
+
if (job.html_url) md += `URL: ${job.html_url}\n`;
|
|
594
|
+
md += `\n---\n\n`;
|
|
595
|
+
|
|
596
|
+
const steps = renderActionsSteps(job.steps);
|
|
597
|
+
if (steps) md += `## Steps\n\n${steps}`;
|
|
598
|
+
|
|
599
|
+
const logs = await fetchGitHubJobLogs(gh.owner, gh.repo, job.id, timeout, signal);
|
|
600
|
+
md += `## Logs\n\n`;
|
|
601
|
+
md += logs
|
|
602
|
+
? stripActionsLogTimestamps(logs)
|
|
603
|
+
: "*Logs unavailable — requires a GITHUB_TOKEN/GH_TOKEN with read access, or the run's logs have expired.*\n";
|
|
604
|
+
|
|
605
|
+
return { content: md, ok: true };
|
|
606
|
+
}
|
|
607
|
+
|
|
374
608
|
/**
|
|
375
609
|
* Handle GitHub URLs specially
|
|
376
610
|
*/
|
|
@@ -445,6 +679,24 @@ export const handleGitHub: SpecialHandler = async (
|
|
|
445
679
|
}
|
|
446
680
|
break;
|
|
447
681
|
}
|
|
682
|
+
|
|
683
|
+
case "actions-run": {
|
|
684
|
+
notes.push(`Fetched via GitHub API`);
|
|
685
|
+
const result = await renderGitHubActionsRun(gh, timeout, signal);
|
|
686
|
+
if (result.ok) {
|
|
687
|
+
return buildResult(result.content, { url, method: "github-actions-run", fetchedAt, notes });
|
|
688
|
+
}
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
case "actions-job": {
|
|
693
|
+
notes.push(`Fetched via GitHub API`);
|
|
694
|
+
const result = await renderGitHubActionsJob(gh, timeout, signal);
|
|
695
|
+
if (result.ok) {
|
|
696
|
+
return buildResult(result.content, { url, method: "github-actions-job", fetchedAt, notes });
|
|
697
|
+
}
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
448
700
|
}
|
|
449
701
|
|
|
450
702
|
// Fall back to null (let normal rendering handle it)
|
|
@@ -113,8 +113,9 @@ export const handleYouTube: SpecialHandler = async (
|
|
|
113
113
|
const notes: string[] = [];
|
|
114
114
|
const videoUrl = `https://www.youtube.com/watch?v=${yt.videoId}`;
|
|
115
115
|
|
|
116
|
-
// Prefer Parallel extract when
|
|
117
|
-
|
|
116
|
+
// Prefer Parallel extract when it sits in the reader chain and creds exist
|
|
117
|
+
const fetchPreference = settings.get("providers.fetch");
|
|
118
|
+
if ((fetchPreference === "auto" || fetchPreference === "parallel") && findParallelApiKey(storage)) {
|
|
118
119
|
try {
|
|
119
120
|
const parallelResult = await extractWithParallel(
|
|
120
121
|
[videoUrl],
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
import {
|
|
8
8
|
type AnthropicAuthConfig,
|
|
9
9
|
type AnthropicSystemBlock,
|
|
10
|
+
type ApiKey,
|
|
10
11
|
type AuthStorage,
|
|
11
12
|
buildAnthropicAuthConfig,
|
|
12
13
|
buildAnthropicSearchHeaders,
|
|
13
14
|
buildAnthropicSystemBlocks,
|
|
14
15
|
buildAnthropicUrl,
|
|
15
16
|
stripClaudeToolPrefix,
|
|
17
|
+
withAuth,
|
|
16
18
|
} from "@oh-my-pi/pi-ai";
|
|
17
19
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
18
20
|
import type {
|
|
@@ -247,18 +249,13 @@ export async function searchAnthropic(
|
|
|
247
249
|
): Promise<SearchResponse> {
|
|
248
250
|
const searchApiKey = $env.ANTHROPIC_SEARCH_API_KEY;
|
|
249
251
|
const searchBaseUrl = $env.ANTHROPIC_SEARCH_BASE_URL;
|
|
250
|
-
|
|
252
|
+
const keyOrResolver: ApiKey | undefined = searchApiKey
|
|
253
|
+
? searchApiKey
|
|
254
|
+
: "authStorage" in params
|
|
255
|
+
? params.authStorage.resolver("anthropic", { sessionId: params.sessionId })
|
|
256
|
+
: undefined;
|
|
251
257
|
|
|
252
|
-
if (
|
|
253
|
-
auth = buildAnthropicAuthConfig(searchApiKey, searchBaseUrl);
|
|
254
|
-
} else if ("authStorage" in params) {
|
|
255
|
-
const apiKey = await params.authStorage.getApiKey("anthropic", params.sessionId, {
|
|
256
|
-
signal: params.signal,
|
|
257
|
-
});
|
|
258
|
-
if (apiKey) auth = buildAnthropicAuthConfig(apiKey, searchBaseUrl);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (!auth) {
|
|
258
|
+
if (!keyOrResolver) {
|
|
262
259
|
throw new Error(
|
|
263
260
|
"No Anthropic credentials found. Set ANTHROPIC_SEARCH_API_KEY or ANTHROPIC_API_KEY, or configure Anthropic OAuth.",
|
|
264
261
|
);
|
|
@@ -267,14 +264,23 @@ export async function searchAnthropic(
|
|
|
267
264
|
const model = getModel();
|
|
268
265
|
const systemPrompt = "authStorage" in params ? params.systemPrompt : params.system_prompt;
|
|
269
266
|
const maxTokens = "authStorage" in params ? params.maxOutputTokens : params.max_tokens;
|
|
270
|
-
const response = await
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
267
|
+
const response = await withAuth(
|
|
268
|
+
keyOrResolver,
|
|
269
|
+
key =>
|
|
270
|
+
callSearch(
|
|
271
|
+
buildAnthropicAuthConfig(key, searchBaseUrl),
|
|
272
|
+
model,
|
|
273
|
+
params.query,
|
|
274
|
+
systemPrompt,
|
|
275
|
+
maxTokens,
|
|
276
|
+
params.temperature,
|
|
277
|
+
params.signal,
|
|
278
|
+
),
|
|
279
|
+
{
|
|
280
|
+
signal: params.signal,
|
|
281
|
+
missingKeyMessage:
|
|
282
|
+
"No Anthropic credentials found. Set ANTHROPIC_SEARCH_API_KEY or ANTHROPIC_API_KEY, or configure Anthropic OAuth.",
|
|
283
|
+
},
|
|
278
284
|
);
|
|
279
285
|
|
|
280
286
|
const result = parseResponse(response);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Requests per-result summaries via `contents.summary` and synthesizes
|
|
7
7
|
* them into a combined `answer` string on the SearchResponse.
|
|
8
8
|
*/
|
|
9
|
-
import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
9
|
+
import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
10
10
|
import { settings } from "../../../config/settings";
|
|
11
11
|
import { callExaTool, findApiKey, isSearchResponse } from "../../../exa/mcp-client";
|
|
12
12
|
|
|
@@ -228,11 +228,19 @@ async function callExaMcpSearch(params: ExaSearchParams): Promise<ExaSearchRespo
|
|
|
228
228
|
|
|
229
229
|
/** Execute Exa web search */
|
|
230
230
|
export async function searchExa(params: ExaSearchParams): Promise<SearchResponse> {
|
|
231
|
+
// AuthStorage-backed key takes precedence (existing behavior); probe it once
|
|
232
|
+
// so the env-key and keyless-MCP fallbacks below stay intact, then drive the
|
|
233
|
+
// authStorage path through the central force-refresh/rotate retry policy.
|
|
231
234
|
const storedKey = params.authStorage
|
|
232
235
|
? await params.authStorage.getApiKey("exa", params.sessionId, { signal: params.signal })
|
|
233
236
|
: undefined;
|
|
234
|
-
const
|
|
235
|
-
|
|
237
|
+
const keyOrResolver: ApiKey | undefined =
|
|
238
|
+
storedKey && params.authStorage
|
|
239
|
+
? params.authStorage.resolver("exa", { sessionId: params.sessionId })
|
|
240
|
+
: getEnvApiKey("exa");
|
|
241
|
+
const response = keyOrResolver
|
|
242
|
+
? await withAuth(keyOrResolver, key => callExaSearch(key, params), { signal: params.signal })
|
|
243
|
+
: await callExaMcpSearch(params);
|
|
236
244
|
|
|
237
245
|
// Convert to unified SearchResponse
|
|
238
246
|
const sources: SearchSource[] = [];
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Uses Moonshot Kimi Code search API to retrieve web results.
|
|
5
5
|
* Endpoint: POST https://api.kimi.com/coding/v1/search
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type ApiKey, type AuthStorage, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
9
9
|
|
|
10
10
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
@@ -54,20 +54,26 @@ function resolveBaseUrl(): string {
|
|
|
54
54
|
return asTrimmed($env.MOONSHOT_SEARCH_BASE_URL) ?? asTrimmed($env.KIMI_SEARCH_BASE_URL) ?? KIMI_SEARCH_URL;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the Kimi search credential. Highest precedence is the static env key;
|
|
59
|
+
* otherwise an AuthStorage-backed resolver for whichever stored provider id
|
|
60
|
+
* holds a key (`moonshot` first, then `kimi-code`), so a stale token triggers
|
|
61
|
+
* the central force-refresh / sibling-rotate retry. Returns `undefined` when
|
|
62
|
+
* neither is configured.
|
|
63
|
+
*/
|
|
64
|
+
async function resolveKey(
|
|
59
65
|
authStorage: AuthStorage,
|
|
60
66
|
sessionId: string | undefined,
|
|
61
67
|
signal: AbortSignal | undefined,
|
|
62
|
-
): Promise<
|
|
68
|
+
): Promise<ApiKey | undefined> {
|
|
63
69
|
const envKey = asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ?? asTrimmed($env.KIMI_SEARCH_API_KEY);
|
|
64
70
|
if (envKey) return envKey;
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
(
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
for (const provider of ["moonshot", "kimi-code"] as const) {
|
|
73
|
+
const stored = await authStorage.getApiKey(provider, sessionId, { signal });
|
|
74
|
+
if (stored) return authStorage.resolver(provider, { sessionId });
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
async function callKimiSearch(
|
|
@@ -108,20 +114,25 @@ async function callKimiSearch(
|
|
|
108
114
|
|
|
109
115
|
/** Execute Kimi web search. */
|
|
110
116
|
export async function searchKimi(params: KimiSearchParams): Promise<SearchResponse> {
|
|
111
|
-
const
|
|
112
|
-
if (!
|
|
117
|
+
const keyOrResolver = await resolveKey(params.authStorage, params.sessionId, params.signal);
|
|
118
|
+
if (!keyOrResolver) {
|
|
113
119
|
throw new Error(
|
|
114
120
|
"Kimi search credentials not found. Set MOONSHOT_SEARCH_API_KEY, KIMI_SEARCH_API_KEY, MOONSHOT_API_KEY, or login with 'omp /login moonshot'.",
|
|
115
121
|
);
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
const limit = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
119
|
-
const { response, requestId } = await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
const { response, requestId } = await withAuth(
|
|
126
|
+
keyOrResolver,
|
|
127
|
+
key =>
|
|
128
|
+
callKimiSearch(key, {
|
|
129
|
+
query: params.query,
|
|
130
|
+
limit,
|
|
131
|
+
includeContent: params.include_content ?? false,
|
|
132
|
+
signal: params.signal,
|
|
133
|
+
}),
|
|
134
|
+
{ signal: params.signal },
|
|
135
|
+
);
|
|
125
136
|
const sources: SearchSource[] = [];
|
|
126
137
|
|
|
127
138
|
for (const result of response.search_results ?? []) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
1
|
+
import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { SearchResponse } from "../../../web/search/types";
|
|
3
3
|
import { SearchProviderError } from "../../../web/search/types";
|
|
4
4
|
import { ParallelApiError, type ParallelSearchResult, type ParallelSearchSource } from "../../parallel";
|
|
@@ -123,30 +123,41 @@ async function searchWithAuthStorage(
|
|
|
123
123
|
);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
126
|
+
// Drive the (already-present) credential through the central force-refresh /
|
|
127
|
+
// sibling-rotate retry policy. The `ParallelApiError` thrown below carries a
|
|
128
|
+
// `statusCode`, which `withAuth`'s default classifier reads to detect a
|
|
129
|
+
// retryable 401 / usage-limit.
|
|
130
|
+
const keyOrResolver: ApiKey = authStorage.resolver("parallel", { sessionId });
|
|
131
|
+
return withAuth(
|
|
132
|
+
keyOrResolver,
|
|
133
|
+
async key => {
|
|
134
|
+
const response = await fetch(PARALLEL_SEARCH_URL, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: {
|
|
137
|
+
Accept: "application/json",
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
"x-api-key": key,
|
|
140
|
+
"parallel-beta": PARALLEL_BETA_HEADER,
|
|
141
|
+
},
|
|
142
|
+
body: JSON.stringify({
|
|
143
|
+
objective,
|
|
144
|
+
search_queries: queries,
|
|
145
|
+
mode: "fast",
|
|
146
|
+
excerpts: {
|
|
147
|
+
max_chars_per_result: 10_000,
|
|
148
|
+
},
|
|
149
|
+
}),
|
|
150
|
+
signal: withHardTimeout(params.signal),
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw parseParallelErrorResponse(response.status, await response.text());
|
|
154
|
+
}
|
|
147
155
|
|
|
148
|
-
|
|
149
|
-
|
|
156
|
+
const payload: unknown = await response.json();
|
|
157
|
+
return parseSearchPayload(payload);
|
|
158
|
+
},
|
|
159
|
+
{ signal: params.signal },
|
|
160
|
+
);
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
export async function searchParallel(
|