@oh-my-pi/pi-coding-agent 16.1.13 → 16.1.15
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 +48 -0
- package/dist/cli.js +5615 -2833
- package/dist/types/advisor/runtime.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +64 -14
- package/dist/types/eval/__tests__/julia-prelude.test.d.ts +1 -0
- package/dist/types/eval/backend-helpers.d.ts +23 -0
- package/dist/types/eval/executor-base.d.ts +118 -0
- package/dist/types/eval/index.d.ts +2 -0
- package/dist/types/eval/jl/executor.d.ts +44 -0
- package/dist/types/eval/jl/index.d.ts +11 -0
- package/dist/types/eval/jl/kernel.d.ts +28 -0
- package/dist/types/eval/jl/prelude.d.ts +1 -0
- package/dist/types/eval/jl/runtime.d.ts +22 -0
- package/dist/types/eval/kernel-base.d.ts +105 -0
- package/dist/types/eval/py/kernel.d.ts +3 -61
- package/dist/types/eval/rb/executor.d.ts +77 -0
- package/dist/types/eval/rb/index.d.ts +11 -0
- package/dist/types/eval/rb/kernel.d.ts +31 -0
- package/dist/types/eval/rb/prelude.d.ts +1 -0
- package/dist/types/eval/rb/runtime.d.ts +23 -0
- package/dist/types/eval/runtime-env.d.ts +24 -0
- package/dist/types/eval/types.d.ts +3 -3
- package/dist/types/export/share.d.ts +8 -1
- package/dist/types/extensibility/extensions/runner.d.ts +2 -15
- package/dist/types/extensibility/hooks/loader.d.ts +1 -25
- package/dist/types/extensibility/session-handler-types.d.ts +23 -0
- package/dist/types/jsonrpc/message-framing.d.ts +35 -0
- package/dist/types/mcp/transports/stdio.d.ts +12 -1
- package/dist/types/mnemopi/embed-client.d.ts +7 -24
- package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +11 -0
- package/dist/types/modes/components/selector-helpers.d.ts +53 -0
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +4 -3
- package/dist/types/modes/components/status-line/types.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +8 -1
- package/dist/types/modes/types.d.ts +0 -2
- package/dist/types/modes/utils/copy-targets.d.ts +1 -1
- package/dist/types/modes/utils/interactive-context-helpers.d.ts +14 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +54 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +3 -3
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/stt/asr-client.d.ts +3 -29
- package/dist/types/subprocess/worker-client.d.ts +149 -0
- package/dist/types/subprocess/worker-runtime.d.ts +107 -0
- package/dist/types/tiny/title-client.d.ts +14 -34
- package/dist/types/tools/eval-backends.d.ts +6 -3
- package/dist/types/tools/eval-render.d.ts +6 -5
- package/dist/types/tools/eval.d.ts +13 -15
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tts/tts-client.d.ts +3 -28
- package/dist/types/tui/code-cell.d.ts +7 -0
- package/dist/types/utils/file-display-mode.d.ts +1 -1
- package/dist/types/utils/shell-snapshot.d.ts +10 -0
- package/dist/types/web/parallel.d.ts +6 -0
- package/dist/types/web/search/providers/perplexity.d.ts +17 -3
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +114 -0
- package/src/advisor/runtime.ts +129 -1
- package/src/config/model-registry.ts +12 -4
- package/src/config/settings-schema.ts +74 -18
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +13 -107
- package/src/eval/__tests__/julia-prelude.test.ts +77 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/executor-base.ts +425 -0
- package/src/eval/index.ts +2 -0
- package/src/eval/jl/executor.ts +540 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +235 -0
- package/src/eval/jl/prelude.jl +930 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +634 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/index.ts +3 -14
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/executor.ts +43 -252
- package/src/eval/py/index.ts +9 -20
- package/src/eval/py/kernel.ts +29 -544
- package/src/eval/rb/executor.ts +504 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +230 -0
- package/src/eval/rb/prelude.rb +721 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +474 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/types.ts +3 -3
- package/src/exec/bash-executor.ts +44 -0
- package/src/export/share.ts +51 -28
- package/src/extensibility/extensions/runner.ts +4 -11
- package/src/extensibility/hooks/loader.ts +3 -21
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/lsp/client.ts +13 -109
- package/src/mcp/transports/stdio.ts +20 -4
- package/src/mnemopi/embed-client.ts +43 -198
- package/src/modes/components/agent-dashboard.ts +17 -40
- package/src/modes/components/chat-transcript-builder.ts +18 -102
- package/src/modes/components/custom-editor.test.ts +22 -0
- package/src/modes/components/custom-editor.ts +29 -1
- package/src/modes/components/extensions/extension-dashboard.ts +4 -13
- package/src/modes/components/extensions/extension-list.ts +16 -44
- package/src/modes/components/footer.ts +4 -3
- package/src/modes/components/history-search.ts +4 -16
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/settings-selector.ts +7 -5
- package/src/modes/components/status-line/component.ts +5 -1
- package/src/modes/components/status-line/context-thresholds.ts +11 -3
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +1 -0
- package/src/modes/components/tree-selector.ts +13 -18
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -9
- package/src/modes/controllers/input-controller.ts +32 -54
- package/src/modes/interactive-mode.ts +5 -7
- package/src/modes/theme/theme.ts +35 -0
- package/src/modes/types.ts +0 -2
- package/src/modes/utils/copy-targets.ts +3 -2
- package/src/modes/utils/interactive-context-helpers.ts +27 -0
- package/src/modes/utils/transcript-render-helpers.ts +157 -0
- package/src/modes/utils/ui-helpers.ts +21 -126
- package/src/prompts/system/system-prompt.md +5 -5
- package/src/prompts/tools/bash.md +2 -3
- package/src/prompts/tools/eval.md +6 -4
- package/src/prompts/tools/find.md +0 -4
- package/src/prompts/tools/read.md +1 -2
- package/src/prompts/tools/replace.md +1 -1
- package/src/sdk.ts +13 -7
- package/src/secrets/obfuscator.ts +3 -9
- package/src/session/agent-session.ts +42 -9
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/stt/asr-client.ts +35 -215
- package/src/stt/asr-worker.ts +29 -181
- package/src/subprocess/worker-client.ts +297 -0
- package/src/subprocess/worker-runtime.ts +277 -0
- package/src/task/executor.ts +4 -4
- package/src/tiny/title-client.ts +53 -219
- package/src/tiny/worker.ts +29 -180
- package/src/tools/eval-backends.ts +10 -3
- package/src/tools/eval-render.ts +17 -8
- package/src/tools/eval.ts +187 -22
- package/src/tools/index.ts +51 -28
- package/src/tts/tts-client.ts +38 -206
- package/src/tts/tts-worker.ts +23 -97
- package/src/tui/code-cell.ts +12 -1
- package/src/utils/file-display-mode.ts +2 -3
- package/src/utils/shell-snapshot.ts +63 -1
- package/src/web/parallel.ts +43 -42
- package/src/web/search/providers/parallel.ts +10 -99
- package/src/web/search/providers/perplexity.ts +18 -6
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type FetchImpl } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { AgentStorage } from "../session/agent-storage";
|
|
3
|
+
export declare const PARALLEL_SEARCH_URL = "https://api.parallel.ai/v1beta/search";
|
|
4
|
+
export declare const PARALLEL_BETA_HEADER = "search-extract-2025-10-10";
|
|
3
5
|
export interface ParallelUsageItem {
|
|
4
6
|
name?: string;
|
|
5
7
|
count?: number;
|
|
@@ -55,6 +57,10 @@ export declare class ParallelApiError extends Error {
|
|
|
55
57
|
readonly statusCode?: number;
|
|
56
58
|
constructor(message: string, statusCode?: number);
|
|
57
59
|
}
|
|
60
|
+
export declare function parseParallelErrorResponse(statusCode: number, responseText: string): ParallelApiError;
|
|
61
|
+
export declare function parseParallelSearchPayload(payload: unknown, options?: {
|
|
62
|
+
parseMetadata?: boolean;
|
|
63
|
+
}): ParallelSearchResult;
|
|
58
64
|
export declare function findParallelApiKey(storage: AgentStorage | null | undefined): string | null;
|
|
59
65
|
export declare function getParallelExtractContent(document: ParallelExtractDocument): string;
|
|
60
66
|
export declare function searchWithParallel(objective: string, queries: string[], options: ParallelSearchOptions, storage: AgentStorage | null | undefined): Promise<ParallelSearchResult>;
|
|
@@ -33,11 +33,25 @@ export declare function searchPerplexity(params: PerplexitySearchParams): Promis
|
|
|
33
33
|
export declare class PerplexityProvider extends SearchProvider {
|
|
34
34
|
readonly id = "perplexity";
|
|
35
35
|
readonly label = "Perplexity";
|
|
36
|
+
/**
|
|
37
|
+
* Auto-chain admission. Requires a direct Perplexity credential
|
|
38
|
+
* (`PERPLEXITY_COOKIES`, OAuth session, or `PERPLEXITY_API_KEY`).
|
|
39
|
+
*
|
|
40
|
+
* OpenRouter auth is intentionally NOT accepted here: silently using
|
|
41
|
+
* OpenRouter's `perplexity/sonar-pro` whenever any OpenRouter key is
|
|
42
|
+
* configured surprises users (and bills them) for a path they never
|
|
43
|
+
* asked for. The auto chain skips Perplexity in that case and falls
|
|
44
|
+
* through to the next configured provider. Users who DO want the
|
|
45
|
+
* OpenRouter-backed Perplexity path can still opt in by setting
|
|
46
|
+
* `webSearch: perplexity` explicitly — see {@link isExplicitlyAvailable}.
|
|
47
|
+
*/
|
|
36
48
|
isAvailable(authStorage: AuthStorage): boolean;
|
|
37
49
|
/**
|
|
38
|
-
* Perplexity accepts anonymous browser-style ask requests,
|
|
39
|
-
*
|
|
40
|
-
*
|
|
50
|
+
* Perplexity accepts anonymous browser-style ask requests, and the
|
|
51
|
+
* OpenRouter-backed `perplexity/sonar-pro` path is opt-in through
|
|
52
|
+
* explicit selection. Keep auto-chain admission credential-gated so a
|
|
53
|
+
* configured provider keeps priority over the anonymous/OpenRouter
|
|
54
|
+
* fallbacks.
|
|
41
55
|
*/
|
|
42
56
|
isExplicitlyAvailable(_authStorage: AuthStorage): boolean;
|
|
43
57
|
search(params: SearchParams): Promise<SearchResponse>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "16.1.
|
|
4
|
+
"version": "16.1.15",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
"@agentclientprotocol/sdk": "0.25.0",
|
|
49
49
|
"@babel/parser": "^7.29.7",
|
|
50
50
|
"@mozilla/readability": "^0.6.0",
|
|
51
|
-
"@oh-my-pi/hashline": "16.1.
|
|
52
|
-
"@oh-my-pi/omp-stats": "16.1.
|
|
53
|
-
"@oh-my-pi/pi-agent-core": "16.1.
|
|
54
|
-
"@oh-my-pi/pi-ai": "16.1.
|
|
55
|
-
"@oh-my-pi/pi-catalog": "16.1.
|
|
56
|
-
"@oh-my-pi/pi-mnemopi": "16.1.
|
|
57
|
-
"@oh-my-pi/pi-natives": "16.1.
|
|
58
|
-
"@oh-my-pi/pi-tui": "16.1.
|
|
59
|
-
"@oh-my-pi/pi-utils": "16.1.
|
|
60
|
-
"@oh-my-pi/pi-wire": "16.1.
|
|
61
|
-
"@oh-my-pi/snapcompact": "16.1.
|
|
51
|
+
"@oh-my-pi/hashline": "16.1.15",
|
|
52
|
+
"@oh-my-pi/omp-stats": "16.1.15",
|
|
53
|
+
"@oh-my-pi/pi-agent-core": "16.1.15",
|
|
54
|
+
"@oh-my-pi/pi-ai": "16.1.15",
|
|
55
|
+
"@oh-my-pi/pi-catalog": "16.1.15",
|
|
56
|
+
"@oh-my-pi/pi-mnemopi": "16.1.15",
|
|
57
|
+
"@oh-my-pi/pi-natives": "16.1.15",
|
|
58
|
+
"@oh-my-pi/pi-tui": "16.1.15",
|
|
59
|
+
"@oh-my-pi/pi-utils": "16.1.15",
|
|
60
|
+
"@oh-my-pi/pi-wire": "16.1.15",
|
|
61
|
+
"@oh-my-pi/snapcompact": "16.1.15",
|
|
62
62
|
"@opentelemetry/api": "^1.9.1",
|
|
63
63
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
64
64
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
@@ -3,6 +3,7 @@ import type { AgentMessage, AgentTelemetryConfig } from "@oh-my-pi/pi-agent-core
|
|
|
3
3
|
import { type } from "arktype";
|
|
4
4
|
import { createAdvisorMessageCard } from "../../modes/components/advisor-message";
|
|
5
5
|
import { getThemeByName } from "../../modes/theme/theme";
|
|
6
|
+
import { SecretObfuscator } from "../../secrets/obfuscator";
|
|
6
7
|
import { formatSessionHistoryMarkdown } from "../../session/session-history-format";
|
|
7
8
|
import { YieldQueue } from "../../session/yield-queue";
|
|
8
9
|
import {
|
|
@@ -447,6 +448,119 @@ describe("advisor", () => {
|
|
|
447
448
|
expect(promptInputs[0]).not.toContain("note");
|
|
448
449
|
});
|
|
449
450
|
|
|
451
|
+
it("obfuscates session updates before prompting the advisor", async () => {
|
|
452
|
+
const secret = "ADVISOR_SECRET_TOKEN_123";
|
|
453
|
+
const obfuscator = new SecretObfuscator([{ type: "plain", content: secret }]);
|
|
454
|
+
const placeholder = obfuscator.obfuscate(secret);
|
|
455
|
+
const promptInputs: string[] = [];
|
|
456
|
+
const agent = makeAgent(promptInputs);
|
|
457
|
+
const messages: AgentMessage[] = [{ role: "user", content: `token ${secret}`, timestamp: 1 } as AgentMessage];
|
|
458
|
+
const host: AdvisorRuntimeHost = {
|
|
459
|
+
snapshotMessages: () => messages,
|
|
460
|
+
enqueueAdvice: () => {},
|
|
461
|
+
obfuscator,
|
|
462
|
+
};
|
|
463
|
+
const runtime = new AdvisorRuntime(agent, host);
|
|
464
|
+
|
|
465
|
+
runtime.onTurnEnd();
|
|
466
|
+
await Promise.resolve();
|
|
467
|
+
|
|
468
|
+
expect(promptInputs).toHaveLength(1);
|
|
469
|
+
expect(promptInputs[0]).toContain(placeholder);
|
|
470
|
+
expect(promptInputs[0]).not.toContain(secret);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it("redacts expanded primary context before XML escaping", async () => {
|
|
474
|
+
const secret = "ADVISOR&SECRET<TOKEN>123";
|
|
475
|
+
const obfuscator = new SecretObfuscator([{ type: "plain", content: secret }]);
|
|
476
|
+
const placeholder = obfuscator.obfuscate(secret);
|
|
477
|
+
const promptInputs: string[] = [];
|
|
478
|
+
const agent = makeAgent(promptInputs);
|
|
479
|
+
const messages: AgentMessage[] = [
|
|
480
|
+
{
|
|
481
|
+
role: "custom",
|
|
482
|
+
customType: "plan-mode-context",
|
|
483
|
+
content: `Plan mode carries ${secret}`,
|
|
484
|
+
display: false,
|
|
485
|
+
timestamp: 1,
|
|
486
|
+
} as AgentMessage,
|
|
487
|
+
];
|
|
488
|
+
const host: AdvisorRuntimeHost = {
|
|
489
|
+
snapshotMessages: () => messages,
|
|
490
|
+
enqueueAdvice: () => {},
|
|
491
|
+
obfuscator,
|
|
492
|
+
};
|
|
493
|
+
const runtime = new AdvisorRuntime(agent, host);
|
|
494
|
+
|
|
495
|
+
runtime.onTurnEnd();
|
|
496
|
+
await Promise.resolve();
|
|
497
|
+
|
|
498
|
+
expect(promptInputs).toHaveLength(1);
|
|
499
|
+
expect(promptInputs[0]).toContain(placeholder);
|
|
500
|
+
expect(promptInputs[0]).not.toContain(secret);
|
|
501
|
+
expect(promptInputs[0]).not.toContain("ADVISOR&SECRET<TOKEN>123");
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("redacts file-mention paths before formatting", async () => {
|
|
505
|
+
const secret = "MENTION_SECRET_TOKEN_123";
|
|
506
|
+
const obfuscator = new SecretObfuscator([{ type: "plain", content: secret }]);
|
|
507
|
+
const placeholder = obfuscator.obfuscate(secret);
|
|
508
|
+
const promptInputs: string[] = [];
|
|
509
|
+
const agent = makeAgent(promptInputs);
|
|
510
|
+
const messages: AgentMessage[] = [
|
|
511
|
+
{
|
|
512
|
+
role: "fileMention",
|
|
513
|
+
files: [{ path: `notes/${secret}.txt`, content: "ignored" }],
|
|
514
|
+
timestamp: 1,
|
|
515
|
+
} as unknown as AgentMessage,
|
|
516
|
+
];
|
|
517
|
+
const host: AdvisorRuntimeHost = {
|
|
518
|
+
snapshotMessages: () => messages,
|
|
519
|
+
enqueueAdvice: () => {},
|
|
520
|
+
obfuscator,
|
|
521
|
+
};
|
|
522
|
+
const runtime = new AdvisorRuntime(agent, host);
|
|
523
|
+
|
|
524
|
+
runtime.onTurnEnd();
|
|
525
|
+
await Promise.resolve();
|
|
526
|
+
|
|
527
|
+
expect(promptInputs).toHaveLength(1);
|
|
528
|
+
expect(promptInputs[0]).toContain(placeholder);
|
|
529
|
+
expect(promptInputs[0]).not.toContain(secret);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("redacts nested async-result job labels before formatting", async () => {
|
|
533
|
+
const secret = "JOB_LABEL_SECRET_TOKEN_123";
|
|
534
|
+
const obfuscator = new SecretObfuscator([{ type: "plain", content: secret }]);
|
|
535
|
+
const placeholder = obfuscator.obfuscate(secret);
|
|
536
|
+
const promptInputs: string[] = [];
|
|
537
|
+
const agent = makeAgent(promptInputs);
|
|
538
|
+
const messages: AgentMessage[] = [
|
|
539
|
+
{
|
|
540
|
+
role: "custom",
|
|
541
|
+
customType: "async-result",
|
|
542
|
+
content: "",
|
|
543
|
+
details: { jobs: [{ label: `bash: echo ${secret}`, jobId: "j1" }] },
|
|
544
|
+
display: true,
|
|
545
|
+
attribution: "agent",
|
|
546
|
+
timestamp: 1,
|
|
547
|
+
} as unknown as AgentMessage,
|
|
548
|
+
];
|
|
549
|
+
const host: AdvisorRuntimeHost = {
|
|
550
|
+
snapshotMessages: () => messages,
|
|
551
|
+
enqueueAdvice: () => {},
|
|
552
|
+
obfuscator,
|
|
553
|
+
};
|
|
554
|
+
const runtime = new AdvisorRuntime(agent, host);
|
|
555
|
+
|
|
556
|
+
runtime.onTurnEnd();
|
|
557
|
+
await Promise.resolve();
|
|
558
|
+
|
|
559
|
+
expect(promptInputs).toHaveLength(1);
|
|
560
|
+
expect(promptInputs[0]).toContain(placeholder);
|
|
561
|
+
expect(promptInputs[0]).not.toContain(secret);
|
|
562
|
+
});
|
|
563
|
+
|
|
450
564
|
it("expands plan-mode context once, then collapses an unchanged re-injection", async () => {
|
|
451
565
|
const promptInputs: string[] = [];
|
|
452
566
|
const agent = makeAgent(promptInputs);
|
package/src/advisor/runtime.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { estimateTokens } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
|
+
import type { AssistantMessage, ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
3
4
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import { obfuscateToolArguments, type SecretObfuscator } from "../secrets/obfuscator";
|
|
4
6
|
import { formatSessionHistoryMarkdown, PRIMARY_CONTEXT_CUSTOM_TYPES } from "../session/session-history-format";
|
|
5
7
|
|
|
6
8
|
/** Minimal slice of `Agent` the runtime drives — satisfied by pi-agent-core `Agent`. */
|
|
@@ -16,6 +18,8 @@ export interface AdvisorRuntimeHost {
|
|
|
16
18
|
snapshotMessages(): AgentMessage[];
|
|
17
19
|
/** Surface one advice note to the primary (enqueues into the session YieldQueue). */
|
|
18
20
|
enqueueAdvice(note: string, severity?: "nit" | "concern" | "blocker"): void;
|
|
21
|
+
/** Redact primary transcript bytes before they reach the advisor model. */
|
|
22
|
+
obfuscator?: SecretObfuscator;
|
|
19
23
|
/**
|
|
20
24
|
* Pre-prompt context maintenance for the advisor's own append-only context.
|
|
21
25
|
* Promotes the advisor model to a larger sibling when its context nears the
|
|
@@ -174,7 +178,9 @@ export class AdvisorRuntime {
|
|
|
174
178
|
.map(m => this.#dedupContextMessage(m));
|
|
175
179
|
this.#lastCount = all.length;
|
|
176
180
|
if (delta.length === 0) return null;
|
|
177
|
-
const
|
|
181
|
+
const obfuscator = this.host.obfuscator;
|
|
182
|
+
const formattedDelta = obfuscator?.hasSecrets() ? obfuscateAdvisorDelta(obfuscator, delta) : delta;
|
|
183
|
+
const md = formatSessionHistoryMarkdown(formattedDelta, {
|
|
178
184
|
includeThinking: true,
|
|
179
185
|
includeToolIntent: true,
|
|
180
186
|
watchedRoles: true,
|
|
@@ -304,3 +310,125 @@ export class AdvisorRuntime {
|
|
|
304
310
|
}
|
|
305
311
|
}
|
|
306
312
|
}
|
|
313
|
+
|
|
314
|
+
type TextualContent = string | readonly (TextContent | ImageContent)[];
|
|
315
|
+
|
|
316
|
+
function obfuscateTextualContent(obfuscator: SecretObfuscator, content: TextualContent): TextualContent {
|
|
317
|
+
if (typeof content === "string") return obfuscator.obfuscate(content);
|
|
318
|
+
let changed = false;
|
|
319
|
+
const result = content.map((block): TextContent | ImageContent => {
|
|
320
|
+
if (block.type !== "text") return block;
|
|
321
|
+
const text = obfuscator.obfuscate(block.text);
|
|
322
|
+
if (text === block.text) return block;
|
|
323
|
+
changed = true;
|
|
324
|
+
return { ...block, text };
|
|
325
|
+
});
|
|
326
|
+
return changed ? result : content;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function obfuscateAssistantMessage(obfuscator: SecretObfuscator, message: AssistantMessage): AssistantMessage {
|
|
330
|
+
let changed = false;
|
|
331
|
+
const content = message.content.map((block): AssistantMessage["content"][number] => {
|
|
332
|
+
if (block.type === "text") {
|
|
333
|
+
const text = obfuscator.obfuscate(block.text);
|
|
334
|
+
if (text === block.text) return block;
|
|
335
|
+
changed = true;
|
|
336
|
+
return { ...block, text };
|
|
337
|
+
}
|
|
338
|
+
if (block.type === "toolCall") {
|
|
339
|
+
const args = obfuscateToolArguments(obfuscator, block.arguments);
|
|
340
|
+
if (args === block.arguments) return block;
|
|
341
|
+
changed = true;
|
|
342
|
+
return { ...block, arguments: args };
|
|
343
|
+
}
|
|
344
|
+
return block;
|
|
345
|
+
});
|
|
346
|
+
return changed ? { ...message, content } : message;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function obfuscateDetails(
|
|
350
|
+
obfuscator: SecretObfuscator,
|
|
351
|
+
details: Record<string, unknown> | undefined,
|
|
352
|
+
): Record<string, unknown> | undefined {
|
|
353
|
+
if (!details) return details;
|
|
354
|
+
// Walk strings at every depth: `customOneLiner` renders nested fields
|
|
355
|
+
// (e.g. `async-result` reads `details.jobs[].label`/`jobId`), so a shallow
|
|
356
|
+
// pass leaks any secret a background job's label happens to contain.
|
|
357
|
+
return obfuscateToolArguments(obfuscator, details);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function obfuscateAdvisorMessage(obfuscator: SecretObfuscator, message: AgentMessage): AgentMessage {
|
|
361
|
+
switch (message.role) {
|
|
362
|
+
case "user":
|
|
363
|
+
case "developer":
|
|
364
|
+
case "toolResult": {
|
|
365
|
+
const content = obfuscateTextualContent(obfuscator, message.content as TextualContent);
|
|
366
|
+
return content === message.content ? message : ({ ...(message as object), content } as AgentMessage);
|
|
367
|
+
}
|
|
368
|
+
case "assistant":
|
|
369
|
+
return obfuscateAssistantMessage(obfuscator, message as AssistantMessage) as AgentMessage;
|
|
370
|
+
case "custom":
|
|
371
|
+
case "hookMessage": {
|
|
372
|
+
const msg = message as AgentMessage & {
|
|
373
|
+
content: TextualContent;
|
|
374
|
+
details?: Record<string, unknown>;
|
|
375
|
+
};
|
|
376
|
+
const content = obfuscateTextualContent(obfuscator, msg.content);
|
|
377
|
+
const details = obfuscateDetails(obfuscator, msg.details);
|
|
378
|
+
if (content === msg.content && details === msg.details) return message;
|
|
379
|
+
return { ...(message as object), content, details } as AgentMessage;
|
|
380
|
+
}
|
|
381
|
+
case "bashExecution": {
|
|
382
|
+
const msg = message as AgentMessage & { command: string; output: string };
|
|
383
|
+
const command = obfuscator.obfuscate(msg.command);
|
|
384
|
+
const output = obfuscator.obfuscate(msg.output);
|
|
385
|
+
return command === msg.command && output === msg.output
|
|
386
|
+
? message
|
|
387
|
+
: ({ ...(message as object), command, output } as AgentMessage);
|
|
388
|
+
}
|
|
389
|
+
case "pythonExecution": {
|
|
390
|
+
const msg = message as AgentMessage & { code: string; output: string };
|
|
391
|
+
const code = obfuscator.obfuscate(msg.code);
|
|
392
|
+
const output = obfuscator.obfuscate(msg.output);
|
|
393
|
+
return code === msg.code && output === msg.output
|
|
394
|
+
? message
|
|
395
|
+
: ({ ...(message as object), code, output } as AgentMessage);
|
|
396
|
+
}
|
|
397
|
+
case "branchSummary": {
|
|
398
|
+
const msg = message as AgentMessage & { summary: string };
|
|
399
|
+
const summary = obfuscator.obfuscate(msg.summary);
|
|
400
|
+
return summary === msg.summary ? message : ({ ...(message as object), summary } as AgentMessage);
|
|
401
|
+
}
|
|
402
|
+
case "compactionSummary": {
|
|
403
|
+
const msg = message as AgentMessage & { summary: string };
|
|
404
|
+
const summary = obfuscator.obfuscate(msg.summary);
|
|
405
|
+
return summary === msg.summary ? message : ({ ...(message as object), summary } as AgentMessage);
|
|
406
|
+
}
|
|
407
|
+
case "fileMention": {
|
|
408
|
+
const msg = message as AgentMessage & {
|
|
409
|
+
files: Array<{ path: string; content: string; image?: unknown }>;
|
|
410
|
+
};
|
|
411
|
+
let changed = false;
|
|
412
|
+
const files = msg.files.map(file => {
|
|
413
|
+
const path = obfuscator.obfuscate(file.path);
|
|
414
|
+
const content = obfuscator.obfuscate(file.content);
|
|
415
|
+
if (path === file.path && content === file.content) return file;
|
|
416
|
+
changed = true;
|
|
417
|
+
return { ...file, path, content };
|
|
418
|
+
});
|
|
419
|
+
return changed ? ({ ...(message as object), files } as AgentMessage) : message;
|
|
420
|
+
}
|
|
421
|
+
default:
|
|
422
|
+
return message;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function obfuscateAdvisorDelta(obfuscator: SecretObfuscator, messages: AgentMessage[]): AgentMessage[] {
|
|
427
|
+
let changed = false;
|
|
428
|
+
const result = messages.map(message => {
|
|
429
|
+
const next = obfuscateAdvisorMessage(obfuscator, message);
|
|
430
|
+
if (next !== message) changed = true;
|
|
431
|
+
return next;
|
|
432
|
+
});
|
|
433
|
+
return changed ? result : messages;
|
|
434
|
+
}
|
|
@@ -992,6 +992,7 @@ export class ModelRegistry {
|
|
|
992
992
|
});
|
|
993
993
|
continue;
|
|
994
994
|
}
|
|
995
|
+
const configStale = this.#isDiscoveryCacheOlderThanModelsConfig(cache.updatedAt);
|
|
995
996
|
const models = this.#applyProviderModelOverrides(
|
|
996
997
|
providerConfig.provider,
|
|
997
998
|
this.#normalizeDiscoverableModels(
|
|
@@ -1007,7 +1008,7 @@ export class ModelRegistry {
|
|
|
1007
1008
|
provider: providerConfig.provider,
|
|
1008
1009
|
status: "cached",
|
|
1009
1010
|
optional: providerConfig.optional ?? false,
|
|
1010
|
-
stale: !cache.fresh || !cache.authoritative,
|
|
1011
|
+
stale: !cache.fresh || !cache.authoritative || configStale,
|
|
1011
1012
|
fetchedAt: cache.updatedAt,
|
|
1012
1013
|
models: models.map(model => model.id),
|
|
1013
1014
|
});
|
|
@@ -1259,12 +1260,19 @@ export class ModelRegistry {
|
|
|
1259
1260
|
return providerConfig.provider;
|
|
1260
1261
|
}
|
|
1261
1262
|
|
|
1263
|
+
#isDiscoveryCacheOlderThanModelsConfig(cacheUpdatedAt: number): boolean {
|
|
1264
|
+
const configMtime = this.#modelsConfigFile.getMtimeMs();
|
|
1265
|
+
return configMtime !== null && cacheUpdatedAt < Math.floor(configMtime);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1262
1268
|
async #discoverProviderModels(
|
|
1263
1269
|
providerConfig: DiscoveryProviderConfig,
|
|
1264
1270
|
strategy: ModelRefreshStrategy,
|
|
1265
1271
|
): Promise<Model<Api>[]> {
|
|
1266
1272
|
const cacheProviderId = this.#configuredDiscoveryCacheProviderId(providerConfig);
|
|
1267
1273
|
const cached = readModelCache<Api>(cacheProviderId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
|
|
1274
|
+
const cacheOlderThanConfig = cached !== null && this.#isDiscoveryCacheOlderThanModelsConfig(cached.updatedAt);
|
|
1275
|
+
const effectiveStrategy = strategy === "online-if-uncached" && cacheOlderThanConfig ? "online" : strategy;
|
|
1268
1276
|
const requiresAuth = !this.#keylessProviders.has(providerConfig.provider);
|
|
1269
1277
|
if (requiresAuth) {
|
|
1270
1278
|
const apiKey = await this.#peekApiKeyForProvider(providerConfig.provider);
|
|
@@ -1311,12 +1319,12 @@ export class ModelRegistry {
|
|
|
1311
1319
|
cacheTtlMs: 24 * 60 * 60 * 1000,
|
|
1312
1320
|
fetchDynamicModels,
|
|
1313
1321
|
});
|
|
1314
|
-
const result = await manager.refresh(
|
|
1322
|
+
const result = await manager.refresh(effectiveStrategy);
|
|
1315
1323
|
const status = discoveryError
|
|
1316
1324
|
? result.models.length > 0
|
|
1317
1325
|
? "cached"
|
|
1318
1326
|
: "unavailable"
|
|
1319
|
-
:
|
|
1327
|
+
: effectiveStrategy === "offline"
|
|
1320
1328
|
? cached
|
|
1321
1329
|
? "cached"
|
|
1322
1330
|
: "idle"
|
|
@@ -1327,7 +1335,7 @@ export class ModelRegistry {
|
|
|
1327
1335
|
provider: providerId,
|
|
1328
1336
|
status,
|
|
1329
1337
|
optional: providerConfig.optional ?? false,
|
|
1330
|
-
stale: result.stale || status === "cached",
|
|
1338
|
+
stale: result.stale || status === "cached" || (cacheOlderThanConfig && status !== "ok"),
|
|
1331
1339
|
fetchedAt: discoveryError ? cached?.updatedAt : Date.now(),
|
|
1332
1340
|
models: result.models.map(model => model.id),
|
|
1333
1341
|
error: discoveryError,
|
|
@@ -122,7 +122,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
|
|
|
122
122
|
context: ["General", "Compaction", "Rules (TTSR)", "Experimental"],
|
|
123
123
|
memory: ["General", "Auto-Learn", "Mnemopi", "Hindsight"],
|
|
124
124
|
files: ["Editing", "Reading", "Read Summaries", "LSP"],
|
|
125
|
-
shell: ["Bash", "Eval &
|
|
125
|
+
shell: ["Bash", "Eval & Runtimes"],
|
|
126
126
|
tools: [
|
|
127
127
|
"Available Tools",
|
|
128
128
|
"Todos",
|
|
@@ -1592,6 +1592,30 @@ export const SETTINGS_SCHEMA = {
|
|
|
1592
1592
|
},
|
|
1593
1593
|
},
|
|
1594
1594
|
|
|
1595
|
+
"share.store": {
|
|
1596
|
+
type: "enum",
|
|
1597
|
+
values: ["blob", "gist"] as const,
|
|
1598
|
+
default: "blob",
|
|
1599
|
+
ui: {
|
|
1600
|
+
tab: "interaction",
|
|
1601
|
+
group: "Collab",
|
|
1602
|
+
label: "Share Store",
|
|
1603
|
+
description: "Where /share uploads the encrypted session blob",
|
|
1604
|
+
options: [
|
|
1605
|
+
{
|
|
1606
|
+
value: "blob",
|
|
1607
|
+
label: "Encrypted Blob",
|
|
1608
|
+
description: "Upload to the share server (no GitHub account needed; avoids gist API rate limits)",
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
value: "gist",
|
|
1612
|
+
label: "GitHub Gist",
|
|
1613
|
+
description: "Push to a secret gist (needs authenticated gh), falling back to the share server",
|
|
1614
|
+
},
|
|
1615
|
+
],
|
|
1616
|
+
},
|
|
1617
|
+
},
|
|
1618
|
+
|
|
1595
1619
|
"share.redactSecrets": {
|
|
1596
1620
|
type: "boolean",
|
|
1597
1621
|
default: true,
|
|
@@ -2703,18 +2727,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
2703
2727
|
},
|
|
2704
2728
|
},
|
|
2705
2729
|
|
|
2706
|
-
readHashLines: {
|
|
2707
|
-
type: "boolean",
|
|
2708
|
-
default: true,
|
|
2709
|
-
ui: {
|
|
2710
|
-
tab: "files",
|
|
2711
|
-
group: "Reading",
|
|
2712
|
-
label: "Hash Lines",
|
|
2713
|
-
description:
|
|
2714
|
-
"Include snapshot-tag headers and line numbers in read output for hashline edit mode ([PATH#TAG] plus LINE:content)",
|
|
2715
|
-
},
|
|
2716
|
-
},
|
|
2717
|
-
|
|
2718
2730
|
"read.defaultLimit": {
|
|
2719
2731
|
type: "number",
|
|
2720
2732
|
default: 300,
|
|
@@ -2981,7 +2993,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2981
2993
|
default: true,
|
|
2982
2994
|
ui: {
|
|
2983
2995
|
tab: "shell",
|
|
2984
|
-
group: "Eval &
|
|
2996
|
+
group: "Eval & Runtimes",
|
|
2985
2997
|
label: "Python Eval Backend",
|
|
2986
2998
|
description: "Allow the eval tool to dispatch Python cells to the IPython kernel",
|
|
2987
2999
|
},
|
|
@@ -2992,20 +3004,42 @@ export const SETTINGS_SCHEMA = {
|
|
|
2992
3004
|
default: true,
|
|
2993
3005
|
ui: {
|
|
2994
3006
|
tab: "shell",
|
|
2995
|
-
group: "Eval &
|
|
3007
|
+
group: "Eval & Runtimes",
|
|
2996
3008
|
label: "JavaScript Eval Backend",
|
|
2997
3009
|
description: "Allow the eval tool to dispatch JavaScript cells to the in-process runtime",
|
|
2998
3010
|
},
|
|
2999
3011
|
},
|
|
3000
3012
|
|
|
3001
|
-
|
|
3013
|
+
"eval.rb": {
|
|
3014
|
+
type: "boolean",
|
|
3015
|
+
default: false,
|
|
3016
|
+
ui: {
|
|
3017
|
+
tab: "shell",
|
|
3018
|
+
group: "Eval & Runtimes",
|
|
3019
|
+
label: "Ruby Eval Backend",
|
|
3020
|
+
description: "Allow the eval tool to dispatch Ruby cells to the persistent Ruby kernel",
|
|
3021
|
+
},
|
|
3022
|
+
},
|
|
3023
|
+
|
|
3024
|
+
"eval.jl": {
|
|
3025
|
+
type: "boolean",
|
|
3026
|
+
default: false,
|
|
3027
|
+
ui: {
|
|
3028
|
+
tab: "shell",
|
|
3029
|
+
group: "Eval & Runtimes",
|
|
3030
|
+
label: "Julia Eval Backend",
|
|
3031
|
+
description: "Allow the eval tool to dispatch Julia cells to the persistent Julia kernel",
|
|
3032
|
+
},
|
|
3033
|
+
},
|
|
3034
|
+
|
|
3035
|
+
// Runtime knobs (consumed by eval backends and the /python slash command)
|
|
3002
3036
|
"python.kernelMode": {
|
|
3003
3037
|
type: "enum",
|
|
3004
3038
|
values: ["session", "per-call"] as const,
|
|
3005
3039
|
default: "session",
|
|
3006
3040
|
ui: {
|
|
3007
3041
|
tab: "shell",
|
|
3008
|
-
group: "Eval &
|
|
3042
|
+
group: "Eval & Runtimes",
|
|
3009
3043
|
label: "Python Kernel Mode",
|
|
3010
3044
|
description: "Keep the IPython kernel alive across eval calls or start fresh each time",
|
|
3011
3045
|
},
|
|
@@ -3015,12 +3049,34 @@ export const SETTINGS_SCHEMA = {
|
|
|
3015
3049
|
default: "",
|
|
3016
3050
|
ui: {
|
|
3017
3051
|
tab: "shell",
|
|
3018
|
-
group: "Eval &
|
|
3052
|
+
group: "Eval & Runtimes",
|
|
3019
3053
|
label: "Python Interpreter",
|
|
3020
3054
|
description:
|
|
3021
3055
|
"Optional path to an exact Python executable. When set, automatic Python runtime discovery is skipped.",
|
|
3022
3056
|
},
|
|
3023
3057
|
},
|
|
3058
|
+
"ruby.interpreter": {
|
|
3059
|
+
type: "string",
|
|
3060
|
+
default: "",
|
|
3061
|
+
ui: {
|
|
3062
|
+
tab: "shell",
|
|
3063
|
+
group: "Eval & Runtimes",
|
|
3064
|
+
label: "Ruby Interpreter",
|
|
3065
|
+
description:
|
|
3066
|
+
"Optional path to an exact Ruby executable. When set, automatic Ruby runtime discovery is skipped.",
|
|
3067
|
+
},
|
|
3068
|
+
},
|
|
3069
|
+
"julia.interpreter": {
|
|
3070
|
+
type: "string",
|
|
3071
|
+
default: "",
|
|
3072
|
+
ui: {
|
|
3073
|
+
tab: "shell",
|
|
3074
|
+
group: "Eval & Runtimes",
|
|
3075
|
+
label: "Julia Interpreter",
|
|
3076
|
+
description:
|
|
3077
|
+
"Optional path to an exact Julia executable. When set, automatic Julia runtime discovery is skipped.",
|
|
3078
|
+
},
|
|
3079
|
+
},
|
|
3024
3080
|
|
|
3025
3081
|
// ────────────────────────────────────────────────────────────────────────
|
|
3026
3082
|
// Tools
|
package/src/config/settings.ts
CHANGED
|
@@ -949,6 +949,11 @@ export class Settings {
|
|
|
949
949
|
delete raw["power.preventDisplaySleep"];
|
|
950
950
|
}
|
|
951
951
|
|
|
952
|
+
// readHashLines: removed. Hashline anchors are now driven solely by
|
|
953
|
+
// edit.mode === "hashline"; the separate read toggle only ever produced
|
|
954
|
+
// the incoherent "hashline edits without addressable anchors" state.
|
|
955
|
+
delete raw.readHashLines;
|
|
956
|
+
|
|
952
957
|
return raw;
|
|
953
958
|
}
|
|
954
959
|
|