@zigai/pi-autoname-session 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/config.schema.json +163 -0
- package/package.json +95 -0
- package/src/index.ts +751 -0
- package/src/picker-request.ts +104 -0
- package/src/session-naming.ts +711 -0
- package/src/settings-input.ts +164 -0
- package/src/settings.prevalidated.ts +31 -0
- package/src/settings.ts +121 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionSettingsDefinitionInput } from "@zigai/pi-extension-settings";
|
|
3
|
+
import { Type, type StaticDecode } from "typebox";
|
|
4
|
+
|
|
5
|
+
const NAMING_TRIGGERS = ["messages", "turns", "tool_calls", "tokens", "minutes"] as const;
|
|
6
|
+
const INITIAL_NAMING_TIMINGS = ["prompt", "settled"] as const;
|
|
7
|
+
const CONVERSATION_SCOPES = ["minimized", "full"] as const;
|
|
8
|
+
|
|
9
|
+
const initialTriggerSchema = StringEnum(NAMING_TRIGGERS, {
|
|
10
|
+
description: "The session activity that starts a naming attempt.",
|
|
11
|
+
default: "messages",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const refreshTriggerSchema = StringEnum(NAMING_TRIGGERS, {
|
|
15
|
+
description: "The activity that starts a naming refresh.",
|
|
16
|
+
default: "turns",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const conversationScopeSchema = StringEnum(CONVERSATION_SCOPES, {
|
|
20
|
+
description:
|
|
21
|
+
"How much refresh context to send. Minimized sends only user and assistant text; full also includes tool arguments, results, and shell output. Initial naming always uses only the first user request.",
|
|
22
|
+
default: "minimized",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const settingsSchema = Type.Object(
|
|
26
|
+
{
|
|
27
|
+
enabled: Type.Boolean({
|
|
28
|
+
default: true,
|
|
29
|
+
description: "Enable the extension.",
|
|
30
|
+
}),
|
|
31
|
+
initialNaming: Type.Object(
|
|
32
|
+
{
|
|
33
|
+
enabled: Type.Boolean({
|
|
34
|
+
default: true,
|
|
35
|
+
description: "Automatically name an otherwise unnamed session once.",
|
|
36
|
+
}),
|
|
37
|
+
timing: StringEnum(INITIAL_NAMING_TIMINGS, {
|
|
38
|
+
default: "prompt",
|
|
39
|
+
description:
|
|
40
|
+
"When to first check the naming trigger: before the agent starts on a prompt, or after the agent has settled.",
|
|
41
|
+
}),
|
|
42
|
+
trigger: initialTriggerSchema,
|
|
43
|
+
threshold: Type.Integer({
|
|
44
|
+
default: 1,
|
|
45
|
+
minimum: 1,
|
|
46
|
+
description: "The initial activity threshold.",
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
default: { enabled: true, timing: "prompt", trigger: "messages", threshold: 1 },
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
refreshNaming: Type.Object(
|
|
55
|
+
{
|
|
56
|
+
enabled: Type.Boolean({
|
|
57
|
+
default: false,
|
|
58
|
+
description: "Periodically refresh the name as the session develops.",
|
|
59
|
+
}),
|
|
60
|
+
trigger: refreshTriggerSchema,
|
|
61
|
+
threshold: Type.Integer({
|
|
62
|
+
default: 10,
|
|
63
|
+
minimum: 1,
|
|
64
|
+
description: "The amount of activity between refreshes.",
|
|
65
|
+
}),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
additionalProperties: false,
|
|
69
|
+
default: { enabled: false, trigger: "turns", threshold: 10 },
|
|
70
|
+
},
|
|
71
|
+
),
|
|
72
|
+
model: Type.String({
|
|
73
|
+
default: "current",
|
|
74
|
+
minLength: 1,
|
|
75
|
+
pattern: "^(?:current|[^/\\s]+/\\S+)$",
|
|
76
|
+
description: "Picker model: provider/model-id, or current for the active model.",
|
|
77
|
+
}),
|
|
78
|
+
reasoningEffort: StringEnum(
|
|
79
|
+
["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const,
|
|
80
|
+
{
|
|
81
|
+
description: "Reasoning effort used by the session-name picker.",
|
|
82
|
+
default: "low",
|
|
83
|
+
},
|
|
84
|
+
),
|
|
85
|
+
timeoutMs: Type.Integer({
|
|
86
|
+
default: 30_000,
|
|
87
|
+
minimum: 1_000,
|
|
88
|
+
description:
|
|
89
|
+
"Maximum time in milliseconds for picker authentication and the model response before the naming attempt is treated as failed.",
|
|
90
|
+
}),
|
|
91
|
+
conversationScope: conversationScopeSchema,
|
|
92
|
+
prompt: Type.String({
|
|
93
|
+
default: [
|
|
94
|
+
"Generate a title that will help the user recognize this coding session weeks later.",
|
|
95
|
+
"",
|
|
96
|
+
"Before answering, silently identify:",
|
|
97
|
+
"- Subject: the system, feature, or problem the request is really about.",
|
|
98
|
+
"- Outcome: what the user ultimately wants to understand or change.",
|
|
99
|
+
"- Incidental instructions: details about tools, process, output, or how the agent should work.",
|
|
100
|
+
"",
|
|
101
|
+
"Title the durable subject and desired outcome. Discard incidental instructions.",
|
|
102
|
+
"Prioritize user requests over assistant discoveries. Preserve the original subject until the user clearly changes goals.",
|
|
103
|
+
"",
|
|
104
|
+
"Editorial rules:",
|
|
105
|
+
"- Use 3 to 8 words and a compact noun phrase or clear action phrase.",
|
|
106
|
+
"- Capture the umbrella goal when the request lists several symptoms or steps.",
|
|
107
|
+
"- Name the product change, not a plan, report, branch, commit, PR, test run, or monitoring step used to produce it.",
|
|
108
|
+
"- Exclude models, subagents, tools, and output formats unless they are themselves the topic.",
|
|
109
|
+
"- For reviews, name what is being reviewed and the relevant concern.",
|
|
110
|
+
"- For research, name the question domain rather than the research process.",
|
|
111
|
+
"- Do not claim the work is complete or merely copy and truncate the request.",
|
|
112
|
+
"- Avoid repository names already visible in the workspace metadata, quotes, labels, filler, and trailing punctuation.",
|
|
113
|
+
"",
|
|
114
|
+
"Workspace metadata:",
|
|
115
|
+
"<workspace>",
|
|
116
|
+
"{{repository_context}}",
|
|
117
|
+
"</workspace>",
|
|
118
|
+
"",
|
|
119
|
+
"Conversation:",
|
|
120
|
+
"<conversation>",
|
|
121
|
+
"{{conversation}}",
|
|
122
|
+
"</conversation>",
|
|
123
|
+
"",
|
|
124
|
+
"Current title: {{current_name}}",
|
|
125
|
+
"Naming phase: {{reason}}",
|
|
126
|
+
].join("\n"),
|
|
127
|
+
minLength: 1,
|
|
128
|
+
"x-control": "textarea",
|
|
129
|
+
description:
|
|
130
|
+
"Prompt used by the picker. Available placeholders are {{repository_context}}, {{conversation}}, {{current_name}}, {{cwd}}, and {{reason}}.",
|
|
131
|
+
}),
|
|
132
|
+
nameConstraints: Type.Object(
|
|
133
|
+
{
|
|
134
|
+
minLength: Type.Integer({
|
|
135
|
+
default: 6,
|
|
136
|
+
minimum: 1,
|
|
137
|
+
description: "Minimum number of characters in a name returned by the picker.",
|
|
138
|
+
}),
|
|
139
|
+
maxLength: Type.Integer({
|
|
140
|
+
default: 40,
|
|
141
|
+
minimum: 1,
|
|
142
|
+
description: "Maximum number of characters in a name returned by the picker.",
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
additionalProperties: false,
|
|
147
|
+
default: { minLength: 6, maxLength: 40 },
|
|
148
|
+
},
|
|
149
|
+
),
|
|
150
|
+
},
|
|
151
|
+
{ additionalProperties: false },
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
export type ExtensionSettingsDocument = StaticDecode<typeof settingsSchema>;
|
|
155
|
+
|
|
156
|
+
export const settingsInput = {
|
|
157
|
+
id: "pi-autoname-session",
|
|
158
|
+
title: "Pi Autoname Session",
|
|
159
|
+
description: "Settings for Pi Autoname Session.",
|
|
160
|
+
schemaId: "https://raw.githubusercontent.com/zigai/pi-autoname-session/HEAD/config.schema.json",
|
|
161
|
+
schema: settingsSchema,
|
|
162
|
+
} as const satisfies ExtensionSettingsDefinitionInput<typeof settingsSchema>;
|
|
163
|
+
|
|
164
|
+
export default settingsInput;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Generated by pi-extension-settings. Do not edit.
|
|
2
|
+
const artifact = {
|
|
3
|
+
"formatVersion": 1,
|
|
4
|
+
"fingerprint": "json-sha256-v1:12c1ba14d3b1f6ee8b9f742b5003063525bb4648d28ea7dfed54d36e42371922",
|
|
5
|
+
"semanticFingerprint": "typebox-value-hash-v1:c239a47b9932bbb9",
|
|
6
|
+
"defaultSettings": {
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"initialNaming": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"timing": "prompt",
|
|
11
|
+
"trigger": "messages",
|
|
12
|
+
"threshold": 1
|
|
13
|
+
},
|
|
14
|
+
"refreshNaming": {
|
|
15
|
+
"enabled": false,
|
|
16
|
+
"trigger": "turns",
|
|
17
|
+
"threshold": 10
|
|
18
|
+
},
|
|
19
|
+
"model": "current",
|
|
20
|
+
"reasoningEffort": "low",
|
|
21
|
+
"timeoutMs": 30000,
|
|
22
|
+
"conversationScope": "minimized",
|
|
23
|
+
"prompt": "Generate a title that will help the user recognize this coding session weeks later.\n\nBefore answering, silently identify:\n- Subject: the system, feature, or problem the request is really about.\n- Outcome: what the user ultimately wants to understand or change.\n- Incidental instructions: details about tools, process, output, or how the agent should work.\n\nTitle the durable subject and desired outcome. Discard incidental instructions.\nPrioritize user requests over assistant discoveries. Preserve the original subject until the user clearly changes goals.\n\nEditorial rules:\n- Use 3 to 8 words and a compact noun phrase or clear action phrase.\n- Capture the umbrella goal when the request lists several symptoms or steps.\n- Name the product change, not a plan, report, branch, commit, PR, test run, or monitoring step used to produce it.\n- Exclude models, subagents, tools, and output formats unless they are themselves the topic.\n- For reviews, name what is being reviewed and the relevant concern.\n- For research, name the question domain rather than the research process.\n- Do not claim the work is complete or merely copy and truncate the request.\n- Avoid repository names already visible in the workspace metadata, quotes, labels, filler, and trailing punctuation.\n\nWorkspace metadata:\n<workspace>\n{{repository_context}}\n</workspace>\n\nConversation:\n<conversation>\n{{conversation}}\n</conversation>\n\nCurrent title: {{current_name}}\nNaming phase: {{reason}}",
|
|
24
|
+
"nameConstraints": {
|
|
25
|
+
"minLength": 6,
|
|
26
|
+
"maxLength": 40
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export default artifact;
|
package/src/settings.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { loadPiExtensionSettings, type PiSettingsContext } from "@zigai/pi-extension-settings/pi";
|
|
2
|
+
import { definePrevalidatedExtensionSettings } from "@zigai/pi-extension-settings/runtime";
|
|
3
|
+
|
|
4
|
+
import prevalidatedSettings from "./settings.prevalidated.ts";
|
|
5
|
+
import settingsInput, { type ExtensionSettingsDocument, settingsSchema } from "./settings-input.ts";
|
|
6
|
+
|
|
7
|
+
/** A resolved picker-model selection, parsed from the model setting. */
|
|
8
|
+
export type PickerModelReference =
|
|
9
|
+
| { readonly type: "current" }
|
|
10
|
+
| { readonly type: "specific"; readonly provider: string; readonly id: string };
|
|
11
|
+
|
|
12
|
+
export type ExtensionSettings = Omit<ExtensionSettingsDocument, "model"> & {
|
|
13
|
+
readonly model: PickerModelReference;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type AutonameSessionSettingsLoadResult = {
|
|
17
|
+
readonly settings: ExtensionSettings | undefined;
|
|
18
|
+
|
|
19
|
+
readonly diagnostics: readonly {
|
|
20
|
+
readonly severity: "error" | "warning";
|
|
21
|
+
readonly message: string;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parse the picker model setting into a tagged reference.
|
|
27
|
+
*
|
|
28
|
+
* "current" selects the session's active model; any other value must be
|
|
29
|
+
* provider/model-id form. Returns undefined for malformed references so the
|
|
30
|
+
* caller can report the configured value as unavailable.
|
|
31
|
+
*/
|
|
32
|
+
export function parsePickerModelReference(model: string): PickerModelReference | undefined {
|
|
33
|
+
if (model.trim() !== model) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (model === "current") {
|
|
38
|
+
return { type: "current" };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const separatorIndex = model.indexOf("/");
|
|
42
|
+
if (separatorIndex <= 0 || separatorIndex === model.length - 1 || /\s/.test(model)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
type: "specific",
|
|
48
|
+
provider: model.slice(0, separatorIndex),
|
|
49
|
+
id: model.slice(separatorIndex + 1),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Render a parsed picker-model reference in its persisted setting form. */
|
|
54
|
+
export function formatPickerModelReference(reference: PickerModelReference): string {
|
|
55
|
+
return reference.type === "current" ? "current" : `${reference.provider}/${reference.id}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const extensionSettingsDefinition = definePrevalidatedExtensionSettings(
|
|
59
|
+
settingsInput,
|
|
60
|
+
prevalidatedSettings,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load resolved extension settings from Pi's global and trusted-project
|
|
65
|
+
* settings layers, refreshing missing or stale generated schemas.
|
|
66
|
+
*/
|
|
67
|
+
export function loadAutonameSessionSettings(
|
|
68
|
+
ctx: PiSettingsContext,
|
|
69
|
+
): AutonameSessionSettingsLoadResult {
|
|
70
|
+
const loaded = loadPiExtensionSettings(extensionSettingsDefinition, ctx, {
|
|
71
|
+
bundledSchema: {
|
|
72
|
+
kind: "url",
|
|
73
|
+
url: new URL("../config.schema.json", import.meta.url),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const configFailed = loaded.diagnostics.some(
|
|
78
|
+
({ scope, code }) => scope !== "schema" && code !== "config-scaffold-failed",
|
|
79
|
+
);
|
|
80
|
+
if (configFailed) {
|
|
81
|
+
return { settings: undefined, diagnostics: loaded.diagnostics };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (loaded.settings.nameConstraints.minLength > loaded.settings.nameConstraints.maxLength) {
|
|
85
|
+
return {
|
|
86
|
+
settings: undefined,
|
|
87
|
+
diagnostics: [
|
|
88
|
+
...loaded.diagnostics,
|
|
89
|
+
{
|
|
90
|
+
severity: "error" as const,
|
|
91
|
+
message:
|
|
92
|
+
"Session naming is disabled because nameConstraints.minLength is greater than maxLength.",
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const model = parsePickerModelReference(loaded.settings.model);
|
|
99
|
+
if (model === undefined) {
|
|
100
|
+
return {
|
|
101
|
+
settings: undefined,
|
|
102
|
+
diagnostics: [
|
|
103
|
+
...loaded.diagnostics,
|
|
104
|
+
{
|
|
105
|
+
severity: "error" as const,
|
|
106
|
+
message:
|
|
107
|
+
'Session naming is disabled because model must be "current" or provider/model-id.',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
settings: { ...loaded.settings, model },
|
|
115
|
+
diagnostics: loaded.diagnostics,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export { settingsSchema };
|
|
120
|
+
export type { ExtensionSettingsDocument };
|
|
121
|
+
export default extensionSettingsDefinition;
|