@oh-my-pi/pi-coding-agent 17.2.9 → 17.2.11
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 +83 -0
- package/dist/{CHANGELOG-8yy8h3bc.md → CHANGELOG-d9xenpn9.md} +83 -0
- package/dist/cli.js +10208 -12596
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +13 -0
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +34 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
- package/dist/types/extensibility/custom-tools/types.d.ts +3 -3
- package/dist/types/extensibility/extensions/loader.d.ts +9 -2
- package/dist/types/extensibility/extensions/types.d.ts +45 -8
- package/dist/types/extensibility/hooks/types.d.ts +5 -5
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/main.d.ts +1 -1
- package/dist/types/mcp/transports/header-policy.d.ts +45 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
- package/dist/types/modes/acp/acp-mode.d.ts +1 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
- package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
- package/dist/types/modes/components/agent-hub.d.ts +14 -5
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/model-browser.d.ts +9 -1
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +8 -1
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +47 -0
- package/dist/types/registry/persisted-agents.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +23 -1
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-entries.d.ts +10 -0
- package/dist/types/session/session-loader.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +9 -1
- package/dist/types/session/session-tools.d.ts +18 -1
- package/dist/types/session/turn-recovery.d.ts +7 -2
- package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
- package/dist/types/slash-commands/available-commands.d.ts +1 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +4 -3
- package/dist/types/task/read-only-policy.d.ts +3 -0
- package/dist/types/task/structured-subagent.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/launch.d.ts +2 -1
- package/dist/types/tools/browser/tab-worker.d.ts +2 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +1 -5
- package/dist/types/tools/run-scope.d.ts +27 -1
- package/dist/types/tools/shell-tokenize.d.ts +16 -0
- package/dist/types/tools/terminal-output.d.ts +1 -1
- package/dist/types/utils/turndown.d.ts +1 -1
- package/dist/types/vibe/runtime.d.ts +2 -3
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
- package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/api-demo.ts +5 -6
- package/examples/extensions/chalk-logger.ts +1 -1
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +15 -31
- package/scripts/bundle-dist.ts +1 -11
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +54 -0
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/agents-cli.ts +1 -1
- package/src/cli/args.ts +24 -2
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/command-help.ts +4 -0
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/flag-tables.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/help-extra.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +1 -1
- package/src/cli/tiny-models-cli.ts +1 -1
- package/src/cli/ttsr-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/say.ts +1 -1
- package/src/commands/share.ts +71 -0
- package/src/commands/token.ts +1 -1
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/cli.ts +1 -1
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-registry.ts +21 -1
- package/src/config/model-resolver.ts +54 -7
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +46 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/agents.ts +4 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/eval/py/runner.py +38 -1
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/extensibility/custom-commands/loader.ts +4 -4
- package/src/extensibility/custom-commands/types.ts +5 -5
- package/src/extensibility/custom-tools/loader.ts +4 -4
- package/src/extensibility/custom-tools/types.ts +3 -3
- package/src/extensibility/extensions/loader.ts +85 -20
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +51 -8
- package/src/extensibility/hooks/loader.ts +4 -5
- package/src/extensibility/hooks/types.ts +5 -5
- package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/index.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +5 -1
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/launch/terminal-output.ts +1 -1
- package/src/main.ts +88 -23
- package/src/markit/converters/docx.ts +1 -1
- package/src/markit/converters/epub.ts +1 -1
- package/src/markit/converters/pptx.ts +1 -1
- package/src/markit/converters/xlsx.ts +1 -1
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +20 -9
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/acp/acp-mode.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +674 -207
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/extensions/inspector-panel.ts +11 -6
- package/src/modes/components/footer.ts +1 -3
- package/src/modes/components/model-browser.ts +11 -3
- package/src/modes/components/model-hub.ts +4 -1
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +10 -7
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +4 -1
- package/src/modes/controllers/event-controller.ts +44 -20
- package/src/modes/controllers/extension-ui-controller.ts +2 -2
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/mcp-command-controller.ts +81 -20
- package/src/modes/controllers/selector-controller.ts +41 -49
- package/src/modes/controllers/streaming-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/computer.md +1 -1
- package/src/registry/agent-lifecycle.ts +14 -7
- package/src/registry/agent-registry.ts +65 -2
- package/src/registry/persisted-agents.ts +341 -16
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +52 -1
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session.ts +54 -16
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-context.ts +3 -5
- package/src/session/session-entries.ts +10 -0
- package/src/session/session-handoff.ts +5 -1
- package/src/session/session-listing.ts +1 -1
- package/src/session/session-loader.ts +74 -8
- package/src/session/session-manager.ts +22 -2
- package/src/session/session-paths.ts +57 -9
- package/src/session/session-tools.ts +65 -4
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +96 -74
- package/src/slash-commands/acp-builtins.ts +1 -1
- package/src/slash-commands/available-commands.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +12 -8
- package/src/task/executor.ts +70 -20
- package/src/task/index.ts +30 -34
- package/src/task/isolation-runner.ts +24 -11
- package/src/task/persisted-revive.ts +12 -5
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/structured-subagent.ts +14 -4
- package/src/task/types.ts +4 -0
- package/src/tools/auto-generated-guard.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +16 -17
- package/src/tools/browser/cmux/cmux-tab.ts +66 -18
- package/src/tools/browser/launch.ts +41 -12
- package/src/tools/browser/readable.ts +9 -9
- package/src/tools/browser/tab-supervisor.ts +12 -3
- package/src/tools/browser/tab-worker-entry.ts +1 -1
- package/src/tools/browser/tab-worker.ts +101 -12
- package/src/tools/debug.ts +1 -1
- package/src/tools/fetch.ts +1 -1
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/path-utils.ts +9 -2
- package/src/tools/read.ts +21 -9
- package/src/tools/run-scope.ts +290 -4
- package/src/tools/shell-tokenize.ts +98 -0
- package/src/tools/terminal-output.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/utils/clipboard.ts +10 -2
- package/src/utils/external-editor.ts +4 -2
- package/src/utils/jj.ts +1 -1
- package/src/utils/turndown.ts +1 -2
- package/src/vibe/runtime.ts +5 -5
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +2 -1
- package/src/web/scrapers/twitter.ts +2 -2
- package/src/web/scrapers/types.ts +1 -1
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/search/providers/browser-headers.ts +12 -39
- package/src/web/search/providers/codex.ts +3 -26
- package/src/web/search/providers/ecosia.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/google.ts +1 -1
- package/src/web/search/providers/mojeek.ts +1 -1
- package/src/web/search/providers/startpage.ts +1 -1
- package/src/markit/converters/mammoth.d.ts +0 -24
|
@@ -1,26 +1,281 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { ADVISOR_TRANSCRIPT_FILENAME, isAdvisorTranscriptName } from "../advisor/transcript-recorder";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveExplicitModelRole } from "../config/model-resolver";
|
|
5
|
+
import { EPHEMERAL_MODEL_CHANGE_ROLE } from "../session/session-entries";
|
|
6
|
+
import { visitEntriesFromFileStream } from "../session/session-loader";
|
|
7
|
+
import { loadBundledAgents } from "../task/agents";
|
|
8
|
+
import { isReadOnlyAgent } from "../task/read-only-policy";
|
|
5
9
|
import { persistedVibeChildIds } from "../vibe/runtime";
|
|
6
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
type AgentHistorySummary,
|
|
12
|
+
type AgentMetricsSummary,
|
|
13
|
+
type AgentRegistry,
|
|
14
|
+
getAgentTombstonePath,
|
|
15
|
+
MAIN_AGENT_ID,
|
|
16
|
+
} from "./agent-registry";
|
|
17
|
+
|
|
18
|
+
/** Maximum prefix entries inspected for task metadata. */
|
|
19
|
+
const MAX_METADATA_LINES = 64;
|
|
20
|
+
|
|
21
|
+
interface PersistedAgentMetadata {
|
|
22
|
+
activity?: string;
|
|
23
|
+
createdAt?: number;
|
|
24
|
+
lastActivity?: number;
|
|
25
|
+
history?: AgentHistorySummary;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface PersistedTranscript {
|
|
29
|
+
id: string;
|
|
30
|
+
sessionFile: string;
|
|
31
|
+
createdAt?: number;
|
|
32
|
+
lastActivity?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function recordOf(value: unknown): Record<string, unknown> | undefined {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
37
|
+
? (value as Record<string, unknown>)
|
|
38
|
+
: undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function timestampOf(value: unknown): number | undefined {
|
|
42
|
+
if (typeof value !== "string") return undefined;
|
|
43
|
+
const timestamp = Date.parse(value);
|
|
44
|
+
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function summarizePersistedTask(task: string): string | undefined {
|
|
48
|
+
const withoutPreamble = task.replace(/^Complete the assignment below,\s*thoroughly:\s*/i, "");
|
|
49
|
+
const lines = withoutPreamble.split(/\r?\n/);
|
|
50
|
+
const targetIndex = lines.findIndex(line => line.trim().toLowerCase() === "# target");
|
|
51
|
+
const targetLines: string[] = [];
|
|
52
|
+
if (targetIndex >= 0) {
|
|
53
|
+
for (const line of lines.slice(targetIndex + 1)) {
|
|
54
|
+
if (line.trimStart().startsWith("# ")) break;
|
|
55
|
+
targetLines.push(line);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const summary = (targetLines.length > 0 ? targetLines : lines).join(" ").replace(/\s+/g, " ").trim();
|
|
59
|
+
return summary ? summary.slice(0, 1_000) : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function finiteNumber(value: unknown): number {
|
|
63
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function inferBundledAgent(systemPrompt: string): { agent?: string; modelRole?: string; readOnly?: boolean } {
|
|
67
|
+
const matches = loadBundledAgents().filter(agent => {
|
|
68
|
+
const rolePrompt = agent.systemPrompt.trim();
|
|
69
|
+
return rolePrompt.length > 0 && systemPrompt.includes(rolePrompt);
|
|
70
|
+
});
|
|
71
|
+
// `task` and `sonic` intentionally share a prompt body. Ambiguous historical
|
|
72
|
+
// prompts stay unlabelled rather than inventing provenance.
|
|
73
|
+
if (matches.length !== 1) return {};
|
|
74
|
+
const [agent] = matches;
|
|
75
|
+
return {
|
|
76
|
+
agent: agent.name,
|
|
77
|
+
modelRole: resolveExplicitModelRole(agent.model),
|
|
78
|
+
readOnly: isReadOnlyAgent(agent),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function usageTokens(usage: Record<string, unknown>): number {
|
|
83
|
+
const computed = finiteNumber(usage.input) + finiteNumber(usage.output) + finiteNumber(usage.cacheWrite);
|
|
84
|
+
return computed > 0 ? computed : finiteNumber(usage.totalTokens);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface AssistantMetrics {
|
|
88
|
+
tokens: number;
|
|
89
|
+
tools: number;
|
|
90
|
+
cost: number;
|
|
91
|
+
contextTokens?: number;
|
|
92
|
+
resolvedModel?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function assistantMetrics(message: Record<string, unknown>): AssistantMetrics {
|
|
96
|
+
const usage = recordOf(message.usage) ?? {};
|
|
97
|
+
const cost = recordOf(usage.cost);
|
|
98
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
99
|
+
const provider = typeof message.provider === "string" ? message.provider : undefined;
|
|
100
|
+
const model = typeof message.model === "string" ? message.model : undefined;
|
|
101
|
+
return {
|
|
102
|
+
tokens: usageTokens(usage),
|
|
103
|
+
tools: content.filter(part => recordOf(part)?.type === "toolCall").length,
|
|
104
|
+
cost: finiteNumber(cost?.total),
|
|
105
|
+
contextTokens: finiteNumber(usage.totalTokens) || undefined,
|
|
106
|
+
resolvedModel: provider && model ? `${provider}/${model}` : undefined,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function readPersistedAgentHistory(
|
|
111
|
+
transcript: PersistedTranscript,
|
|
112
|
+
shouldContinue: () => boolean,
|
|
113
|
+
): Promise<AgentHistorySummary> {
|
|
114
|
+
const parents = new Map<string, string | undefined>();
|
|
115
|
+
const assistantById = new Map<string, AssistantMetrics>();
|
|
116
|
+
const modelChangeById = new Map<string, { model: string; role?: string; resolvedModelIsFallback: boolean }>();
|
|
117
|
+
let leafId: string | undefined;
|
|
118
|
+
let leafTimestamp: number | undefined;
|
|
119
|
+
try {
|
|
120
|
+
await visitEntriesFromFileStream(
|
|
121
|
+
transcript.sessionFile,
|
|
122
|
+
entry => {
|
|
123
|
+
const record = recordOf(entry);
|
|
124
|
+
if (!record) return;
|
|
125
|
+
const id = typeof record.id === "string" ? record.id : undefined;
|
|
126
|
+
if (!id) return;
|
|
127
|
+
const parentId = typeof record.parentId === "string" ? record.parentId : undefined;
|
|
128
|
+
parents.set(id, parentId);
|
|
129
|
+
leafId = id;
|
|
130
|
+
const parsedTimestamp = timestampOf(record.timestamp);
|
|
131
|
+
if (parsedTimestamp !== undefined) leafTimestamp = parsedTimestamp;
|
|
132
|
+
if (record.type === "model_change" && typeof record.model === "string") {
|
|
133
|
+
modelChangeById.set(id, {
|
|
134
|
+
model: record.model,
|
|
135
|
+
role: typeof record.role === "string" ? record.role : undefined,
|
|
136
|
+
resolvedModelIsFallback: record.resolvedModelIsFallback === true,
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (record.type !== "message") return;
|
|
141
|
+
const message = recordOf(record.message);
|
|
142
|
+
if (message?.role === "assistant") assistantById.set(id, assistantMetrics(message));
|
|
143
|
+
},
|
|
144
|
+
{ shouldContinue },
|
|
145
|
+
);
|
|
146
|
+
} catch {
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const metrics: AgentMetricsSummary = {
|
|
151
|
+
tokens: 0,
|
|
152
|
+
requests: 0,
|
|
153
|
+
tools: 0,
|
|
154
|
+
cost: 0,
|
|
155
|
+
durationMs: Math.max(
|
|
156
|
+
0,
|
|
157
|
+
(leafTimestamp ?? transcript.lastActivity ?? transcript.createdAt ?? 0) -
|
|
158
|
+
(transcript.createdAt ?? leafTimestamp ?? 0),
|
|
159
|
+
),
|
|
160
|
+
durationKind: "span",
|
|
161
|
+
};
|
|
162
|
+
let resolvedModel: string | undefined;
|
|
163
|
+
let resolvedModelIsFallback: boolean | undefined;
|
|
164
|
+
let modelRole: string | undefined;
|
|
165
|
+
let contextTokens: number | undefined;
|
|
166
|
+
let modelChangeFound = false;
|
|
167
|
+
const visited = new Set<string>();
|
|
168
|
+
for (let id = leafId; id && !visited.has(id); id = parents.get(id)) {
|
|
169
|
+
visited.add(id);
|
|
170
|
+
const modelChange = modelChangeById.get(id);
|
|
171
|
+
if (modelChange && !modelChangeFound) {
|
|
172
|
+
modelChangeFound = true;
|
|
173
|
+
resolvedModel = modelChange.model;
|
|
174
|
+
resolvedModelIsFallback = modelChange.resolvedModelIsFallback;
|
|
175
|
+
if (modelChange.role && modelChange.role !== EPHEMERAL_MODEL_CHANGE_ROLE) {
|
|
176
|
+
modelRole = modelChange.role;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const assistant = assistantById.get(id);
|
|
180
|
+
if (!assistant) continue;
|
|
181
|
+
if (!modelChangeFound && resolvedModel === undefined && assistant.resolvedModel) {
|
|
182
|
+
resolvedModel = assistant.resolvedModel;
|
|
183
|
+
}
|
|
184
|
+
metrics.requests++;
|
|
185
|
+
metrics.tokens += assistant.tokens;
|
|
186
|
+
metrics.tools += assistant.tools;
|
|
187
|
+
metrics.cost += assistant.cost;
|
|
188
|
+
contextTokens ??= assistant.contextTokens;
|
|
189
|
+
}
|
|
190
|
+
if (contextTokens !== undefined) metrics.contextTokens = contextTokens;
|
|
191
|
+
return {
|
|
192
|
+
...(metrics.requests > 0 ? { metrics } : {}),
|
|
193
|
+
...(resolvedModel ? { resolvedModel, resolvedModelIsFallback } : {}),
|
|
194
|
+
...(modelRole ? { modelRole } : {}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
7
197
|
|
|
8
198
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
199
|
+
* Read only the small session prefix needed by the Hub. A subagent's first
|
|
200
|
+
* `session_init` is written before its conversation, so this never walks a
|
|
201
|
+
* multi-megabyte historical transcript just to populate one roster row.
|
|
12
202
|
*/
|
|
13
|
-
async function
|
|
14
|
-
|
|
203
|
+
async function readPersistedAgentMetadata(sessionFile: string): Promise<PersistedAgentMetadata> {
|
|
204
|
+
const stat = fs.promises.stat(sessionFile).catch(() => undefined);
|
|
205
|
+
const artifactBase = sessionFile.slice(0, -".jsonl".length);
|
|
206
|
+
const outputPath = `${artifactBase}.md`;
|
|
207
|
+
const patchPath = `${artifactBase}.patch`;
|
|
208
|
+
const artifactFiles = Promise.all([Bun.file(outputPath).exists(), Bun.file(patchPath).exists()]);
|
|
209
|
+
let createdAt: number | undefined;
|
|
210
|
+
let activity: string | undefined;
|
|
211
|
+
let history: AgentHistorySummary = {};
|
|
15
212
|
try {
|
|
16
|
-
|
|
213
|
+
await visitEntriesFromFileStream(
|
|
214
|
+
sessionFile,
|
|
215
|
+
entry => {
|
|
216
|
+
const record = recordOf(entry);
|
|
217
|
+
if (!record) return;
|
|
218
|
+
if (record.type === "session") {
|
|
219
|
+
createdAt ??= timestampOf(record.timestamp);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (record.type === "model_change") {
|
|
223
|
+
if (typeof record.model === "string") history.resolvedModel = record.model;
|
|
224
|
+
if (typeof record.role === "string" && record.role !== EPHEMERAL_MODEL_CHANGE_ROLE) {
|
|
225
|
+
history.modelRole = record.role;
|
|
226
|
+
}
|
|
227
|
+
if (typeof record.resolvedModelIsFallback === "boolean") {
|
|
228
|
+
history.resolvedModelIsFallback = record.resolvedModelIsFallback;
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (record.type !== "session_init") return;
|
|
233
|
+
createdAt ??= timestampOf(record.timestamp);
|
|
234
|
+
if (typeof record.task === "string") activity = summarizePersistedTask(record.task);
|
|
235
|
+
const inferred = typeof record.systemPrompt === "string" ? inferBundledAgent(record.systemPrompt) : {};
|
|
236
|
+
history = {
|
|
237
|
+
...history,
|
|
238
|
+
...inferred,
|
|
239
|
+
agent: typeof record.agent === "string" ? record.agent : inferred.agent,
|
|
240
|
+
modelRole:
|
|
241
|
+
typeof record.modelRole === "string" ? record.modelRole : (history.modelRole ?? inferred.modelRole),
|
|
242
|
+
resolvedModel: typeof record.resolvedModel === "string" ? record.resolvedModel : history.resolvedModel,
|
|
243
|
+
readOnly: typeof record.readOnly === "boolean" ? record.readOnly : inferred.readOnly,
|
|
244
|
+
};
|
|
245
|
+
return false;
|
|
246
|
+
},
|
|
247
|
+
{ maxRecords: MAX_METADATA_LINES },
|
|
248
|
+
);
|
|
17
249
|
} catch {
|
|
18
|
-
|
|
250
|
+
// A readable transcript is still useful even when its optional metadata
|
|
251
|
+
// prefix is malformed.
|
|
19
252
|
}
|
|
253
|
+
const [file, [hasOutput, hasPatch]] = await Promise.all([stat, artifactFiles]);
|
|
254
|
+
return {
|
|
255
|
+
activity,
|
|
256
|
+
createdAt: createdAt ?? file?.birthtimeMs,
|
|
257
|
+
lastActivity: file?.mtimeMs,
|
|
258
|
+
history: {
|
|
259
|
+
...history,
|
|
260
|
+
...(hasOutput ? { outputPath } : {}),
|
|
261
|
+
...(hasPatch ? { patchPath } : {}),
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function readPersistedVibeChildIds(sessionFile: string, shouldContinue: () => boolean): Promise<Set<string>> {
|
|
267
|
+
const ids = new Set<string>();
|
|
20
268
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
269
|
+
await visitEntriesFromFileStream(
|
|
270
|
+
sessionFile,
|
|
271
|
+
entry => {
|
|
272
|
+
for (const id of persistedVibeChildIds([entry])) ids.add(id);
|
|
273
|
+
},
|
|
274
|
+
{ shouldContinue },
|
|
275
|
+
);
|
|
276
|
+
return ids;
|
|
277
|
+
} catch {
|
|
278
|
+
return new Set();
|
|
24
279
|
}
|
|
25
280
|
}
|
|
26
281
|
|
|
@@ -28,11 +283,30 @@ async function readPersistedVibeChildIds(sessionFile: string): Promise<Set<strin
|
|
|
28
283
|
export async function registerPersistedSubagents(
|
|
29
284
|
registry: AgentRegistry,
|
|
30
285
|
sessionFile: string | null | undefined,
|
|
286
|
+
options: { shouldContinue?: () => boolean } = {},
|
|
31
287
|
): Promise<void> {
|
|
32
288
|
if (!sessionFile?.endsWith(".jsonl")) return;
|
|
33
|
-
const
|
|
289
|
+
const shouldContinue = options.shouldContinue ?? (() => true);
|
|
290
|
+
if (!shouldContinue()) return;
|
|
291
|
+
const vibeOwnedIds = await readPersistedVibeChildIds(sessionFile, shouldContinue);
|
|
292
|
+
if (!shouldContinue()) return;
|
|
34
293
|
const root = sessionFile.slice(0, -6);
|
|
35
|
-
|
|
294
|
+
const transcripts: PersistedTranscript[] = [];
|
|
295
|
+
await registerPersistedSubagentsFromDir(registry, root, undefined, vibeOwnedIds, transcripts, shouldContinue);
|
|
296
|
+
if (!shouldContinue()) return;
|
|
297
|
+
let nextTranscript = 0;
|
|
298
|
+
const workers = Array.from({ length: Math.min(4, transcripts.length) }, async () => {
|
|
299
|
+
for (;;) {
|
|
300
|
+
if (!shouldContinue()) return;
|
|
301
|
+
const index = nextTranscript++;
|
|
302
|
+
const transcript = transcripts[index];
|
|
303
|
+
if (!transcript) return;
|
|
304
|
+
const history = await readPersistedAgentHistory(transcript, shouldContinue);
|
|
305
|
+
if (!shouldContinue()) return;
|
|
306
|
+
registry.setHistory(transcript.id, history, transcript.sessionFile);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
await Promise.all(workers);
|
|
36
310
|
}
|
|
37
311
|
|
|
38
312
|
async function registerPersistedSubagentsFromDir(
|
|
@@ -40,14 +314,25 @@ async function registerPersistedSubagentsFromDir(
|
|
|
40
314
|
dir: string,
|
|
41
315
|
parentId: string | undefined,
|
|
42
316
|
vibeOwnedIds: ReadonlySet<string>,
|
|
317
|
+
transcripts: PersistedTranscript[],
|
|
318
|
+
shouldContinue: () => boolean,
|
|
43
319
|
): Promise<void> {
|
|
320
|
+
if (!shouldContinue()) return;
|
|
44
321
|
let entries: fs.Dirent[];
|
|
45
322
|
try {
|
|
46
323
|
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
47
324
|
} catch {
|
|
48
325
|
return;
|
|
49
326
|
}
|
|
327
|
+
if (!shouldContinue()) return;
|
|
328
|
+
let entriesSinceYield = 0;
|
|
50
329
|
for (const entry of entries) {
|
|
330
|
+
if (!shouldContinue()) return;
|
|
331
|
+
if (++entriesSinceYield >= 16) {
|
|
332
|
+
entriesSinceYield = 0;
|
|
333
|
+
await Bun.sleep(0);
|
|
334
|
+
}
|
|
335
|
+
if (!shouldContinue()) return;
|
|
51
336
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl") || entry.name.includes(".bak")) continue;
|
|
52
337
|
const sessionFile = path.join(dir, entry.name);
|
|
53
338
|
// The advisor transcript is observability-only: register it as a non-peer
|
|
@@ -66,6 +351,8 @@ async function registerPersistedSubagentsFromDir(
|
|
|
66
351
|
// user task literally named `<owner>/advisor`): leave it, skip the advisor.
|
|
67
352
|
if (existing && existing.kind !== "advisor") continue;
|
|
68
353
|
if (existing?.sessionFile !== sessionFile) {
|
|
354
|
+
const metadata = await readPersistedAgentMetadata(sessionFile);
|
|
355
|
+
if (!shouldContinue()) return;
|
|
69
356
|
// The id is reused across `/new`; refresh it to the current session's file.
|
|
70
357
|
if (existing) registry.unregister(advisorId);
|
|
71
358
|
registry.register({
|
|
@@ -75,14 +362,34 @@ async function registerPersistedSubagentsFromDir(
|
|
|
75
362
|
parentId: owner,
|
|
76
363
|
session: null,
|
|
77
364
|
sessionFile,
|
|
365
|
+
activity: metadata.activity,
|
|
366
|
+
createdAt: metadata.createdAt,
|
|
367
|
+
lastActivity: metadata.lastActivity,
|
|
368
|
+
history: { ...metadata.history, readOnly: true },
|
|
78
369
|
status: "parked",
|
|
79
370
|
});
|
|
371
|
+
transcripts.push({
|
|
372
|
+
id: advisorId,
|
|
373
|
+
sessionFile,
|
|
374
|
+
createdAt: metadata.createdAt,
|
|
375
|
+
lastActivity: metadata.lastActivity,
|
|
376
|
+
});
|
|
80
377
|
}
|
|
81
378
|
continue;
|
|
82
379
|
}
|
|
83
380
|
const id = entry.name.slice(0, -6);
|
|
84
381
|
if (vibeOwnedIds.has(id) && registry.get(id)?.sessionFile !== sessionFile) continue;
|
|
382
|
+
let tombstoned = false;
|
|
383
|
+
try {
|
|
384
|
+
await fs.promises.access(getAgentTombstonePath(sessionFile));
|
|
385
|
+
tombstoned = true;
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") continue;
|
|
388
|
+
}
|
|
389
|
+
if (!shouldContinue()) return;
|
|
85
390
|
if (!registry.get(id)) {
|
|
391
|
+
const metadata = await readPersistedAgentMetadata(sessionFile);
|
|
392
|
+
if (!shouldContinue()) return;
|
|
86
393
|
registry.register({
|
|
87
394
|
id,
|
|
88
395
|
displayName: id,
|
|
@@ -90,9 +397,27 @@ async function registerPersistedSubagentsFromDir(
|
|
|
90
397
|
parentId: parentId ?? MAIN_AGENT_ID,
|
|
91
398
|
session: null,
|
|
92
399
|
sessionFile,
|
|
93
|
-
|
|
400
|
+
activity: metadata.activity,
|
|
401
|
+
createdAt: metadata.createdAt,
|
|
402
|
+
lastActivity: metadata.lastActivity,
|
|
403
|
+
history: metadata.history,
|
|
404
|
+
status: tombstoned ? "aborted" : "parked",
|
|
405
|
+
});
|
|
406
|
+
const ref = registry.get(id);
|
|
407
|
+
transcripts.push({
|
|
408
|
+
id,
|
|
409
|
+
sessionFile,
|
|
410
|
+
createdAt: ref?.createdAt,
|
|
411
|
+
lastActivity: ref?.lastActivity,
|
|
94
412
|
});
|
|
95
413
|
}
|
|
96
|
-
await registerPersistedSubagentsFromDir(
|
|
414
|
+
await registerPersistedSubagentsFromDir(
|
|
415
|
+
registry,
|
|
416
|
+
path.join(dir, id),
|
|
417
|
+
id,
|
|
418
|
+
vibeOwnedIds,
|
|
419
|
+
transcripts,
|
|
420
|
+
shouldContinue,
|
|
421
|
+
);
|
|
97
422
|
}
|
|
98
423
|
}
|
package/src/sdk.ts
CHANGED
|
@@ -123,18 +123,12 @@ import lateDiagnosticTemplate from "./prompts/tools/lsp-late-diagnostic.md" with
|
|
|
123
123
|
import { AgentLifecycleManager } from "./registry/agent-lifecycle";
|
|
124
124
|
import { type AgentRef, AgentRegistry, MAIN_AGENT_ID } from "./registry/agent-registry";
|
|
125
125
|
import {
|
|
126
|
-
|
|
127
|
-
collectEnvSecrets,
|
|
126
|
+
buildSecretObfuscator,
|
|
128
127
|
deobfuscateSessionContext,
|
|
129
128
|
deobfuscateToolArguments,
|
|
130
|
-
getExistingSecretPlaceholderKey,
|
|
131
|
-
getSecretPlaceholderKey,
|
|
132
|
-
getSecretPlaceholderKeySync,
|
|
133
|
-
loadSecrets,
|
|
134
129
|
obfuscateMessages,
|
|
135
130
|
obfuscateProviderContext,
|
|
136
|
-
SecretObfuscator,
|
|
137
|
-
secretEntriesNeedPlaceholderKey,
|
|
131
|
+
type SecretObfuscator,
|
|
138
132
|
} from "./secrets";
|
|
139
133
|
import { AgentSession, type InitialRetryFallbackState, type PlanYolo, type Prewalk } from "./session/agent-session";
|
|
140
134
|
import { discoverAuthStorage as discoverAuthStorageFromConfig } from "./session/auth-broker-config";
|
|
@@ -1039,7 +1033,7 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
|
|
|
1039
1033
|
success: event.success,
|
|
1040
1034
|
attempt: event.attempt,
|
|
1041
1035
|
finalError: event.finalError,
|
|
1042
|
-
|
|
1036
|
+
retryErrors: event.retryErrors,
|
|
1043
1037
|
},
|
|
1044
1038
|
ctx,
|
|
1045
1039
|
),
|
|
@@ -1377,46 +1371,9 @@ async function createAgentSessionScoped(options: CreateAgentSessionOptions): Pro
|
|
|
1377
1371
|
|
|
1378
1372
|
// Load and create secret obfuscator early so resumed session state and prompt warnings
|
|
1379
1373
|
// reflect actual loaded secrets, not just the setting toggle.
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
const envEntries = collectEnvSecrets();
|
|
1384
|
-
// Built-in credential-pattern entries come last so user-configured entries
|
|
1385
|
-
// (plain literals, custom regexes) take precedence in the scan order.
|
|
1386
|
-
const allEntries = [...envEntries, ...fileEntries, ...builtinCredentialSecretEntries()];
|
|
1387
|
-
// Only CONFIGURED entries force startup key creation: a configured
|
|
1388
|
-
// obfuscate-mode secret — or a default (no custom `replacement`)
|
|
1389
|
-
// replace-mode regex whose key-derived idempotent fallback marker needs a
|
|
1390
|
-
// stable key across restarts (see `secretEntryNeedsPlaceholderKey`) —
|
|
1391
|
-
// mints placeholders as soon as the obfuscator is built. The built-in
|
|
1392
|
-
// credential-pattern entry matches dynamically, so it resolves the
|
|
1393
|
-
// persisted key lazily on first match instead of creating the key file
|
|
1394
|
-
// for every secrets.enabled session; a session whose content never
|
|
1395
|
-
// contains a credential-shaped token must not require the key, otherwise a
|
|
1396
|
-
// headless run on an unwritable default config root pays for a feature it
|
|
1397
|
-
// does not use.
|
|
1398
|
-
const needsPlaceholderKey = secretEntriesNeedPlaceholderKey([...envEntries, ...fileEntries]);
|
|
1399
|
-
const explicitAgentDir = options.agentDir;
|
|
1400
|
-
const placeholderKey = needsPlaceholderKey
|
|
1401
|
-
? await getSecretPlaceholderKey(explicitAgentDir)
|
|
1402
|
-
: await getExistingSecretPlaceholderKey(explicitAgentDir);
|
|
1403
|
-
if (allEntries.length > 0) {
|
|
1404
|
-
obfuscator = new SecretObfuscator(
|
|
1405
|
-
allEntries,
|
|
1406
|
-
placeholderKey ?? (() => getSecretPlaceholderKeySync(explicitAgentDir)),
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
if (obfuscator?.hasSecrets() !== true && placeholderKey !== undefined) {
|
|
1410
|
-
// No configured entry produced an active secret (e.g. only ignored short
|
|
1411
|
-
// plain entries, or no entries at all), but a persisted key exists. Build a
|
|
1412
|
-
// redaction-only obfuscator so a tool read of the key file does not ship the
|
|
1413
|
-
// reusable HMAC key to the provider.
|
|
1414
|
-
obfuscator = new SecretObfuscator(
|
|
1415
|
-
[{ type: "plain", mode: "replace", content: placeholderKey }],
|
|
1416
|
-
placeholderKey,
|
|
1417
|
-
);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1374
|
+
const obfuscator: SecretObfuscator | undefined = settings.get("secrets.enabled")
|
|
1375
|
+
? await buildSecretObfuscator(cwd, agentDir, options.agentDir)
|
|
1376
|
+
: undefined;
|
|
1420
1377
|
const secretsEnabled = obfuscator?.hasSecrets() === true;
|
|
1421
1378
|
|
|
1422
1379
|
// An abnormal process exit after a non-terminal message tail is durable
|
package/src/secrets/index.ts
CHANGED
|
@@ -4,7 +4,13 @@ import * as path from "node:path";
|
|
|
4
4
|
import { SENSITIVE_TOKEN_RE } from "@oh-my-pi/pi-ai/providers/transform-messages";
|
|
5
5
|
import { getSecretPlaceholderKeyPath, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { YAML } from "bun";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
regexHasUnresolvableShortMatchFallback,
|
|
9
|
+
type SecretEntry,
|
|
10
|
+
SecretObfuscator,
|
|
11
|
+
sanitizeSecretFriendlyName,
|
|
12
|
+
secretEntriesNeedPlaceholderKey,
|
|
13
|
+
} from "./obfuscator";
|
|
8
14
|
import { compileSecretRegex } from "./regex";
|
|
9
15
|
|
|
10
16
|
const PLACEHOLDER_KEY_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
@@ -223,6 +229,51 @@ export function builtinCredentialSecretEntries(): SecretEntry[] {
|
|
|
223
229
|
];
|
|
224
230
|
}
|
|
225
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Build the session secret obfuscator from every configured source: secrets.yml
|
|
234
|
+
* (project + global), secret-shaped environment variables, and the built-in
|
|
235
|
+
* credential patterns. Callers gate on `secrets.enabled`.
|
|
236
|
+
*
|
|
237
|
+
* Only CONFIGURED entries force startup key creation: a configured
|
|
238
|
+
* obfuscate-mode secret — or a default (no custom `replacement`) replace-mode
|
|
239
|
+
* regex whose key-derived idempotent fallback marker needs a stable key across
|
|
240
|
+
* restarts (see `secretEntryNeedsPlaceholderKey`) — mints placeholders as soon
|
|
241
|
+
* as the obfuscator is built. The built-in credential-pattern entry matches
|
|
242
|
+
* dynamically, so it resolves the persisted key lazily on first match instead
|
|
243
|
+
* of creating the key file for every secrets-enabled session.
|
|
244
|
+
*
|
|
245
|
+
* When no configured entry produced an active secret but a persisted key
|
|
246
|
+
* exists, returns a redaction-only obfuscator so a tool read of the key file
|
|
247
|
+
* does not ship the reusable HMAC key to the provider. Returns undefined when
|
|
248
|
+
* there is nothing to protect.
|
|
249
|
+
*
|
|
250
|
+
* `keyDir` is the explicit agent dir override for the placeholder-key file
|
|
251
|
+
* (default XDG/agent location when omitted).
|
|
252
|
+
*/
|
|
253
|
+
export async function buildSecretObfuscator(
|
|
254
|
+
cwd: string,
|
|
255
|
+
agentDir: string,
|
|
256
|
+
keyDir?: string,
|
|
257
|
+
): Promise<SecretObfuscator | undefined> {
|
|
258
|
+
const fileEntries = await logger.time("loadSecrets", loadSecrets, cwd, agentDir);
|
|
259
|
+
const envEntries = collectEnvSecrets();
|
|
260
|
+
// Built-in credential-pattern entries come last so user-configured entries
|
|
261
|
+
// (plain literals, custom regexes) take precedence in the scan order.
|
|
262
|
+
const allEntries = [...envEntries, ...fileEntries, ...builtinCredentialSecretEntries()];
|
|
263
|
+
const needsPlaceholderKey = secretEntriesNeedPlaceholderKey([...envEntries, ...fileEntries]);
|
|
264
|
+
const placeholderKey = needsPlaceholderKey
|
|
265
|
+
? await getSecretPlaceholderKey(keyDir)
|
|
266
|
+
: await getExistingSecretPlaceholderKey(keyDir);
|
|
267
|
+
let obfuscator: SecretObfuscator | undefined;
|
|
268
|
+
if (allEntries.length > 0) {
|
|
269
|
+
obfuscator = new SecretObfuscator(allEntries, placeholderKey ?? (() => getSecretPlaceholderKeySync(keyDir)));
|
|
270
|
+
}
|
|
271
|
+
if (obfuscator?.hasSecrets() !== true && placeholderKey !== undefined) {
|
|
272
|
+
obfuscator = new SecretObfuscator([{ type: "plain", mode: "replace", content: placeholderKey }], placeholderKey);
|
|
273
|
+
}
|
|
274
|
+
return obfuscator;
|
|
275
|
+
}
|
|
276
|
+
|
|
226
277
|
async function loadSecretsFile(filePath: string): Promise<SecretEntry[]> {
|
|
227
278
|
try {
|
|
228
279
|
const text = await Bun.file(filePath).text();
|
|
@@ -2,7 +2,7 @@ import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
3
|
import type { Effort } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import type { Rule } from "../capability/rule";
|
|
5
|
-
import type {
|
|
5
|
+
import type { RetryErrorUpdate } from "../extensibility/shared-events";
|
|
6
6
|
import type { Goal, GoalModeState } from "../goals/state";
|
|
7
7
|
import type { ConfiguredThinkingLevel } from "../thinking";
|
|
8
8
|
import type { TodoItem } from "../tools/todo";
|
|
@@ -43,7 +43,7 @@ export type AgentSessionEvent =
|
|
|
43
43
|
success: boolean;
|
|
44
44
|
attempt: number;
|
|
45
45
|
finalError?: string;
|
|
46
|
-
|
|
46
|
+
retryErrors?: RetryErrorUpdate[];
|
|
47
47
|
}
|
|
48
48
|
| { type: "retry_fallback_applied"; from: string; to: string; role: string }
|
|
49
49
|
| { type: "retry_fallback_succeeded"; model: string; role: string }
|