@oh-my-pi/pi-coding-agent 14.8.1 → 14.9.0
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 +38 -0
- package/package.json +16 -7
- package/src/config/model-resolver.ts +92 -35
- package/src/config/prompt-templates.ts +1 -1
- package/src/debug/index.ts +21 -0
- package/src/debug/raw-sse-buffer.ts +229 -0
- package/src/debug/raw-sse.ts +213 -0
- package/src/edit/index.ts +9 -10
- package/src/edit/streaming.ts +6 -5
- package/src/eval/js/context-manager.ts +91 -47
- package/src/extensibility/extensions/loader.ts +9 -3
- package/src/extensibility/plugins/legacy-pi-compat.ts +99 -20
- package/src/hashline/anchors.ts +113 -0
- package/src/hashline/apply.ts +732 -0
- package/src/hashline/bigrams.json +649 -0
- package/src/hashline/constants.ts +8 -0
- package/src/hashline/diff-preview.ts +43 -0
- package/src/hashline/diff.ts +56 -0
- package/src/hashline/execute.ts +268 -0
- package/src/{edit/modes/hashline.lark → hashline/grammar.lark} +1 -1
- package/src/{edit/line-hash.ts → hashline/hash.ts} +5 -651
- package/src/hashline/index.ts +14 -0
- package/src/hashline/input.ts +110 -0
- package/src/hashline/parser.ts +220 -0
- package/src/hashline/prefixes.ts +101 -0
- package/src/hashline/recovery.ts +72 -0
- package/src/hashline/stream.ts +123 -0
- package/src/hashline/types.ts +69 -0
- package/src/hashline/utils.ts +3 -0
- package/src/index.ts +1 -1
- package/src/lsp/index.ts +1 -1
- package/src/lsp/render.ts +4 -0
- package/src/memories/index.ts +13 -4
- package/src/modes/components/assistant-message.ts +55 -9
- package/src/modes/components/welcome.ts +114 -38
- package/src/modes/controllers/event-controller.ts +3 -1
- package/src/modes/controllers/input-controller.ts +8 -1
- package/src/modes/interactive-mode.ts +9 -9
- package/src/modes/rpc/rpc-client.ts +53 -2
- package/src/modes/rpc/rpc-mode.ts +67 -1
- package/src/modes/rpc/rpc-types.ts +17 -2
- package/src/modes/utils/ui-helpers.ts +3 -1
- package/src/prompts/agents/reviewer.md +14 -0
- package/src/prompts/tools/hashline.md +57 -10
- package/src/sdk.ts +4 -3
- package/src/session/agent-session.ts +195 -30
- package/src/session/compaction/branch-summarization.ts +4 -2
- package/src/session/compaction/compaction.ts +22 -3
- package/src/task/executor.ts +21 -2
- package/src/task/index.ts +4 -1
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/match-line-format.ts +1 -1
- package/src/tools/read.ts +1 -1
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/title-generator.ts +11 -0
- package/src/edit/modes/hashline.ts +0 -2039
|
@@ -965,6 +965,7 @@ export interface SummaryOptions {
|
|
|
965
965
|
remoteEndpoint?: string;
|
|
966
966
|
remoteInstructions?: string;
|
|
967
967
|
initiatorOverride?: MessageAttribution;
|
|
968
|
+
metadata?: Record<string, unknown>;
|
|
968
969
|
}
|
|
969
970
|
|
|
970
971
|
export async function generateSummary(
|
|
@@ -1020,7 +1021,14 @@ export async function generateSummary(
|
|
|
1020
1021
|
const response = await completeSimple(
|
|
1021
1022
|
model,
|
|
1022
1023
|
{ systemPrompt: [SUMMARIZATION_SYSTEM_PROMPT], messages: summarizationMessages },
|
|
1023
|
-
{
|
|
1024
|
+
{
|
|
1025
|
+
maxTokens,
|
|
1026
|
+
signal,
|
|
1027
|
+
apiKey,
|
|
1028
|
+
reasoning: Effort.High,
|
|
1029
|
+
initiatorOverride: options?.initiatorOverride,
|
|
1030
|
+
metadata: options?.metadata,
|
|
1031
|
+
},
|
|
1024
1032
|
);
|
|
1025
1033
|
|
|
1026
1034
|
if (response.stopReason === "error") {
|
|
@@ -1069,7 +1077,14 @@ async function generateShortSummary(
|
|
|
1069
1077
|
systemPrompt: [SUMMARIZATION_SYSTEM_PROMPT],
|
|
1070
1078
|
messages: [{ role: "user", content: [{ type: "text", text: promptText }], timestamp: Date.now() }],
|
|
1071
1079
|
},
|
|
1072
|
-
{
|
|
1080
|
+
{
|
|
1081
|
+
maxTokens,
|
|
1082
|
+
signal,
|
|
1083
|
+
apiKey,
|
|
1084
|
+
reasoning: Effort.High,
|
|
1085
|
+
initiatorOverride: options?.initiatorOverride,
|
|
1086
|
+
metadata: options?.metadata,
|
|
1087
|
+
},
|
|
1073
1088
|
);
|
|
1074
1089
|
|
|
1075
1090
|
if (response.stopReason === "error") {
|
|
@@ -1249,6 +1264,7 @@ export async function compact(
|
|
|
1249
1264
|
remoteEndpoint: settings.remoteEnabled === false ? undefined : settings.remoteEndpoint,
|
|
1250
1265
|
remoteInstructions: options?.remoteInstructions,
|
|
1251
1266
|
initiatorOverride: options?.initiatorOverride,
|
|
1267
|
+
metadata: options?.metadata,
|
|
1252
1268
|
};
|
|
1253
1269
|
|
|
1254
1270
|
let preserveData = withOpenAiRemoteCompactionPreserveData(previousPreserveData, undefined);
|
|
@@ -1304,6 +1320,7 @@ export async function compact(
|
|
|
1304
1320
|
apiKey,
|
|
1305
1321
|
signal,
|
|
1306
1322
|
summaryOptions.initiatorOverride,
|
|
1323
|
+
summaryOptions.metadata,
|
|
1307
1324
|
),
|
|
1308
1325
|
]);
|
|
1309
1326
|
// Merge into single summary
|
|
@@ -1339,6 +1356,7 @@ export async function compact(
|
|
|
1339
1356
|
extraContext: options?.extraContext,
|
|
1340
1357
|
remoteEndpoint: summaryOptions.remoteEndpoint,
|
|
1341
1358
|
initiatorOverride: summaryOptions.initiatorOverride,
|
|
1359
|
+
metadata: summaryOptions.metadata,
|
|
1342
1360
|
},
|
|
1343
1361
|
);
|
|
1344
1362
|
|
|
@@ -1370,6 +1388,7 @@ async function generateTurnPrefixSummary(
|
|
|
1370
1388
|
apiKey: string,
|
|
1371
1389
|
signal?: AbortSignal,
|
|
1372
1390
|
initiatorOverride?: MessageAttribution,
|
|
1391
|
+
metadata?: Record<string, unknown>,
|
|
1373
1392
|
): Promise<string> {
|
|
1374
1393
|
const maxTokens = Math.floor(0.5 * reserveTokens); // Smaller budget for turn prefix
|
|
1375
1394
|
|
|
@@ -1387,7 +1406,7 @@ async function generateTurnPrefixSummary(
|
|
|
1387
1406
|
const response = await completeSimple(
|
|
1388
1407
|
model,
|
|
1389
1408
|
{ systemPrompt: [SUMMARIZATION_SYSTEM_PROMPT], messages: summarizationMessages },
|
|
1390
|
-
{ maxTokens, signal, apiKey, reasoning: Effort.High, initiatorOverride },
|
|
1409
|
+
{ maxTokens, signal, apiKey, reasoning: Effort.High, initiatorOverride, metadata },
|
|
1391
1410
|
);
|
|
1392
1411
|
|
|
1393
1412
|
if (response.stopReason === "error") {
|
package/src/task/executor.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { logger, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
|
10
10
|
import type { TSchema } from "@sinclair/typebox";
|
|
11
11
|
import Ajv, { type ValidateFunction } from "ajv";
|
|
12
12
|
import { ModelRegistry } from "../config/model-registry";
|
|
13
|
-
import {
|
|
13
|
+
import { resolveModelOverrideWithAuthFallback } from "../config/model-resolver";
|
|
14
14
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
15
15
|
import { Settings } from "../config/settings";
|
|
16
16
|
import { SETTINGS_SCHEMA, type SettingPath } from "../config/settings-schema";
|
|
@@ -144,6 +144,11 @@ export interface ExecutorOptions {
|
|
|
144
144
|
index: number;
|
|
145
145
|
id: string;
|
|
146
146
|
modelOverride?: string | string[];
|
|
147
|
+
/**
|
|
148
|
+
* Active model selector of the parent session, used as an auth-aware fallback
|
|
149
|
+
* if the resolved subagent model has no working credentials. See #985.
|
|
150
|
+
*/
|
|
151
|
+
parentActiveModelPattern?: string;
|
|
147
152
|
thinkingLevel?: ThinkingLevel;
|
|
148
153
|
outputSchema?: unknown;
|
|
149
154
|
/** Parent task recursion depth (0 = top-level, 1 = first child, etc.) */
|
|
@@ -944,7 +949,21 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
944
949
|
model,
|
|
945
950
|
thinkingLevel: resolvedThinkingLevel,
|
|
946
951
|
explicitThinkingLevel,
|
|
947
|
-
|
|
952
|
+
authFallbackUsed,
|
|
953
|
+
} = await resolveModelOverrideWithAuthFallback(
|
|
954
|
+
modelPatterns,
|
|
955
|
+
options.parentActiveModelPattern,
|
|
956
|
+
modelRegistry,
|
|
957
|
+
settings,
|
|
958
|
+
);
|
|
959
|
+
if (authFallbackUsed && model) {
|
|
960
|
+
logger.warn("Subagent model has no working credentials; falling back to parent session model", {
|
|
961
|
+
requested: modelPatterns,
|
|
962
|
+
parentModel: options.parentActiveModelPattern,
|
|
963
|
+
resolvedProvider: model.provider,
|
|
964
|
+
resolvedModel: model.id,
|
|
965
|
+
});
|
|
966
|
+
}
|
|
948
967
|
const effectiveThinkingLevel = explicitThinkingLevel
|
|
949
968
|
? resolvedThinkingLevel
|
|
950
969
|
: (thinkingLevel ?? resolvedThinkingLevel);
|
package/src/task/index.ts
CHANGED
|
@@ -583,11 +583,12 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
583
583
|
// Apply per-agent model override from settings (highest priority)
|
|
584
584
|
const agentModelOverrides = this.session.settings.get("task.agentModelOverrides");
|
|
585
585
|
const settingsModelOverride = agentModelOverrides[agentName];
|
|
586
|
+
const parentActiveModelPattern = this.session.getActiveModelString?.();
|
|
586
587
|
const modelOverride = resolveAgentModelPatterns({
|
|
587
588
|
settingsOverride: settingsModelOverride,
|
|
588
589
|
agentModel: effectiveAgent.model,
|
|
589
590
|
settings: this.session.settings,
|
|
590
|
-
activeModelPattern:
|
|
591
|
+
activeModelPattern: parentActiveModelPattern,
|
|
591
592
|
fallbackModelPattern: this.session.getModelString?.(),
|
|
592
593
|
});
|
|
593
594
|
const thinkingLevelOverride = effectiveAgent.thinkingLevel;
|
|
@@ -843,6 +844,7 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
843
844
|
id: task.id,
|
|
844
845
|
taskDepth,
|
|
845
846
|
modelOverride,
|
|
847
|
+
parentActiveModelPattern,
|
|
846
848
|
thinkingLevel: thinkingLevelOverride,
|
|
847
849
|
outputSchema: effectiveOutputSchema,
|
|
848
850
|
sessionFile,
|
|
@@ -900,6 +902,7 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
900
902
|
id: task.id,
|
|
901
903
|
taskDepth,
|
|
902
904
|
modelOverride,
|
|
905
|
+
parentActiveModelPattern,
|
|
903
906
|
thinkingLevel: thinkingLevelOverride,
|
|
904
907
|
outputSchema: effectiveOutputSchema,
|
|
905
908
|
sessionFile,
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
5
5
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
6
6
|
import { $envpos, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
7
7
|
import { type Static, Type } from "@sinclair/typebox";
|
|
8
|
-
import { computeLineHash, HL_BODY_SEP } from "../edit/line-hash";
|
|
9
8
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
9
|
+
import { computeLineHash, HL_BODY_SEP } from "../hashline/hash";
|
|
10
10
|
import type { Theme } from "../modes/theme/theme";
|
|
11
11
|
import astEditDescription from "../prompts/tools/ast-edit.md" with { type: "text" };
|
|
12
12
|
import { Ellipsis, Hasher, type RenderCache, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
package/src/tools/read.ts
CHANGED
|
@@ -9,9 +9,9 @@ import { Text } from "@oh-my-pi/pi-tui";
|
|
|
9
9
|
import { getRemoteDir, prompt, readImageMetadata, untilAborted } from "@oh-my-pi/pi-utils";
|
|
10
10
|
import { type Static, Type } from "@sinclair/typebox";
|
|
11
11
|
import { getFileReadCache } from "../edit/file-read-cache";
|
|
12
|
-
import { formatHashLine, formatHashLines, formatLineHash, HL_BODY_SEP } from "../edit/line-hash";
|
|
13
12
|
import { isNotebookPath, readEditableNotebookText } from "../edit/notebook";
|
|
14
13
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
14
|
+
import { formatHashLine, formatHashLines, formatLineHash, HL_BODY_SEP } from "../hashline/hash";
|
|
15
15
|
import { parseInternalUrl } from "../internal-urls/parse";
|
|
16
16
|
import type { InternalUrl } from "../internal-urls/types";
|
|
17
17
|
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
|
@@ -11,7 +11,7 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
|
11
11
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
12
12
|
import { glob } from "@oh-my-pi/pi-natives";
|
|
13
13
|
import { formatAge, formatBytes, readImageMetadata } from "@oh-my-pi/pi-utils";
|
|
14
|
-
import { formatHashLines } from "../
|
|
14
|
+
import { formatHashLines } from "../hashline/hash";
|
|
15
15
|
import type { FileMentionMessage } from "../session/messages";
|
|
16
16
|
import {
|
|
17
17
|
DEFAULT_MAX_BYTES,
|
|
@@ -36,6 +36,11 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
|
|
|
36
36
|
* @param registry Model registry
|
|
37
37
|
* @param settings Settings used to resolve the smol role
|
|
38
38
|
* @param sessionId Optional session id for sticky API key selection
|
|
39
|
+
* @param currentModel Current model (used to derive title model)
|
|
40
|
+
* @param metadataResolver Optional resolver evaluated after credential selection
|
|
41
|
+
* to produce request metadata (e.g. user_id for session attribution). Using a
|
|
42
|
+
* resolver instead of a pre-evaluated value ensures the metadata's account_uuid
|
|
43
|
+
* reflects the credential actually selected for this request.
|
|
39
44
|
*/
|
|
40
45
|
export async function generateSessionTitle(
|
|
41
46
|
firstMessage: string,
|
|
@@ -43,6 +48,7 @@ export async function generateSessionTitle(
|
|
|
43
48
|
settings: Settings,
|
|
44
49
|
sessionId?: string,
|
|
45
50
|
currentModel?: Model<Api>,
|
|
51
|
+
metadataResolver?: (provider: string) => Record<string, unknown> | undefined,
|
|
46
52
|
): Promise<string | null> {
|
|
47
53
|
const model = getTitleModel(registry, settings, currentModel);
|
|
48
54
|
if (!model) {
|
|
@@ -65,6 +71,10 @@ ${truncatedMessage}
|
|
|
65
71
|
});
|
|
66
72
|
return null;
|
|
67
73
|
}
|
|
74
|
+
// Resolve metadata after getApiKey so the session-sticky credential for this
|
|
75
|
+
// request is already recorded; metadataResolver can then return the correct
|
|
76
|
+
// account_uuid rather than the snapshot-at-call-site value.
|
|
77
|
+
const metadata = metadataResolver?.(model.provider);
|
|
68
78
|
|
|
69
79
|
// Title generation is a 3-6 word task; force reasoning off so reasoning models
|
|
70
80
|
// don't burn the entire output budget on internal thinking and return an empty
|
|
@@ -88,6 +98,7 @@ ${truncatedMessage}
|
|
|
88
98
|
apiKey,
|
|
89
99
|
maxTokens: 30,
|
|
90
100
|
disableReasoning: true,
|
|
101
|
+
metadata,
|
|
91
102
|
},
|
|
92
103
|
);
|
|
93
104
|
|