@vitos-pizza/vitos-pizza 0.1.0 → 0.2.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/AGENTS.md +158 -158
- package/CHANGELOG.md +42 -24
- package/LICENSE +21 -21
- package/README.md +237 -234
- package/assets/logo.svg +28 -28
- package/node_modules/@vitos-pizza/agent-mode/extensions/index.ts +138 -117
- package/node_modules/@vitos-pizza/agent-mode/package.json +3 -3
- package/node_modules/@vitos-pizza/agent-mode/src/apply-mode.ts +60 -60
- package/node_modules/@vitos-pizza/agent-mode/src/mode-events.ts +102 -102
- package/node_modules/@vitos-pizza/agent-mode/src/modes.ts +43 -43
- package/node_modules/@vitos-pizza/agent-mode/src/plan-instructions.ts +85 -44
- package/node_modules/@vitos-pizza/agent-mode/tests/apply-mode.test.ts +36 -36
- package/node_modules/@vitos-pizza/agent-mode/tests/mode-events.test.ts +22 -22
- package/node_modules/@vitos-pizza/agent-mode/tests/modes.test.ts +38 -38
- package/node_modules/@vitos-pizza/agent-mode/tests/plan-instructions.test.ts +68 -45
- package/node_modules/@vitos-pizza/agent-mode/tsconfig.json +13 -13
- package/node_modules/@vitos-pizza/agent-mode/vitest.config.ts +8 -8
- package/node_modules/@vitos-pizza/keybindings/extensions/index.ts +104 -104
- package/node_modules/@vitos-pizza/keybindings/package.json +1 -1
- package/node_modules/@vitos-pizza/keybindings/presets/shortcuts.json +3 -3
- package/node_modules/@vitos-pizza/keybindings/src/bind.ts +57 -57
- package/node_modules/@vitos-pizza/keybindings/src/config.ts +84 -84
- package/node_modules/@vitos-pizza/keybindings/src/registry.ts +43 -43
- package/node_modules/@vitos-pizza/keybindings/src/types.ts +54 -54
- package/node_modules/@vitos-pizza/keybindings/tests/config.test.ts +83 -83
- package/node_modules/@vitos-pizza/keybindings/tests/registry.test.ts +142 -142
- package/node_modules/@vitos-pizza/keybindings/tsconfig.json +13 -13
- package/node_modules/@vitos-pizza/keybindings/vitest.config.ts +8 -8
- package/node_modules/@vitos-pizza/permission-system/extensions/index.ts +162 -162
- package/node_modules/@vitos-pizza/permission-system/package.json +2 -2
- package/node_modules/@vitos-pizza/permission-system/src/bash/matcher.ts +54 -51
- package/node_modules/@vitos-pizza/permission-system/src/bash/parser.ts +40 -40
- package/node_modules/@vitos-pizza/permission-system/src/config-paths.ts +23 -23
- package/node_modules/@vitos-pizza/permission-system/src/detect-preset-mode.ts +61 -61
- package/node_modules/@vitos-pizza/permission-system/src/expand-home.ts +12 -12
- package/node_modules/@vitos-pizza/permission-system/src/forwarding/forwarder.ts +224 -224
- package/node_modules/@vitos-pizza/permission-system/src/forwarding/subagent-registry.ts +61 -61
- package/node_modules/@vitos-pizza/permission-system/src/gates/path-utils.ts +66 -66
- package/node_modules/@vitos-pizza/permission-system/src/gates/pipeline.ts +151 -155
- package/node_modules/@vitos-pizza/permission-system/src/handlers/before-agent-start.ts +86 -65
- package/node_modules/@vitos-pizza/permission-system/src/handlers/skill-input.ts +1 -1
- package/node_modules/@vitos-pizza/permission-system/src/handlers/tool-call-gate.ts +204 -199
- package/node_modules/@vitos-pizza/permission-system/src/mode-api.ts +44 -44
- package/node_modules/@vitos-pizza/permission-system/src/permission-evaluator.ts +233 -216
- package/node_modules/@vitos-pizza/permission-system/src/permission-session.ts +62 -62
- package/node_modules/@vitos-pizza/permission-system/src/policy-loader.ts +211 -211
- package/node_modules/@vitos-pizza/permission-system/src/sanitizers/skill-prompt.ts +99 -99
- package/node_modules/@vitos-pizza/permission-system/src/sanitizers/system-prompt.ts +221 -221
- package/node_modules/@vitos-pizza/permission-system/src/service.ts +66 -66
- package/node_modules/@vitos-pizza/permission-system/src/session-approvals.ts +4 -1
- package/node_modules/@vitos-pizza/permission-system/src/types.ts +80 -80
- package/node_modules/@vitos-pizza/permission-system/src/ui/approval-summary.ts +43 -43
- package/node_modules/@vitos-pizza/permission-system/src/ui/config-modal.ts +36 -36
- package/node_modules/@vitos-pizza/permission-system/src/ui/permission-dialog.ts +52 -52
- package/node_modules/@vitos-pizza/permission-system/src/wildcard-matcher.ts +81 -81
- package/node_modules/@vitos-pizza/permission-system/src/yaml-frontmatter.ts +84 -84
- package/node_modules/@vitos-pizza/permission-system/src/yolo/runtime-api.ts +49 -49
- package/node_modules/@vitos-pizza/permission-system/tests/bash-parser.test.ts +32 -32
- package/node_modules/@vitos-pizza/permission-system/tests/before-agent-start.test.ts +61 -0
- package/node_modules/@vitos-pizza/permission-system/tests/detect-preset-mode.test.ts +68 -68
- package/node_modules/@vitos-pizza/permission-system/tests/forwarder.test.ts +55 -55
- package/node_modules/@vitos-pizza/permission-system/tests/gates.test.ts +170 -61
- package/node_modules/@vitos-pizza/permission-system/tests/path-utils.test.ts +68 -68
- package/node_modules/@vitos-pizza/permission-system/tests/permission-evaluator.test.ts +82 -55
- package/node_modules/@vitos-pizza/permission-system/tests/policy-loader.test.ts +25 -25
- package/node_modules/@vitos-pizza/permission-system/tests/system-prompt-sanitizer.test.ts +22 -22
- package/node_modules/@vitos-pizza/permission-system/tests/wildcard-matcher.test.ts +27 -27
- package/node_modules/@vitos-pizza/permission-system/tsconfig.json +13 -13
- package/node_modules/@vitos-pizza/permission-system/vitest.config.ts +8 -8
- package/node_modules/@vitos-pizza/question/package.json +1 -1
- package/node_modules/@vitos-pizza/question/src/question-ui.ts +5 -1
- package/node_modules/@vitos-pizza/question/src/register-tool.ts +1 -4
- package/node_modules/@vitos-pizza/question/tests/question-ui-width-cache.test.ts +45 -0
- package/node_modules/@vitos-pizza/question/tests/register-tool-render.test.ts +48 -45
- package/node_modules/@vitos-pizza/session-title/extensions/index.ts +137 -137
- package/node_modules/@vitos-pizza/session-title/package.json +3 -3
- package/node_modules/@vitos-pizza/session-title/src/build-title-prompt.ts +16 -16
- package/node_modules/@vitos-pizza/session-title/src/extract-title-context.ts +55 -55
- package/node_modules/@vitos-pizza/session-title/src/fast-rules.ts +48 -48
- package/node_modules/@vitos-pizza/session-title/src/load-settings.ts +70 -70
- package/node_modules/@vitos-pizza/session-title/src/normalize-title.ts +20 -20
- package/node_modules/@vitos-pizza/session-title/src/parse-title-response.ts +6 -6
- package/node_modules/@vitos-pizza/session-title/src/resolve-title-model.ts +33 -33
- package/node_modules/@vitos-pizza/session-title/src/schedule-title-job.ts +53 -53
- package/node_modules/@vitos-pizza/session-title/src/should-schedule.ts +30 -30
- package/node_modules/@vitos-pizza/session-title/src/title-agent.ts +60 -60
- package/node_modules/@vitos-pizza/session-title/src/truncate-input.ts +10 -10
- package/node_modules/@vitos-pizza/session-title/src/types.ts +29 -29
- package/node_modules/@vitos-pizza/session-title/tests/build-title-prompt.test.ts +34 -34
- package/node_modules/@vitos-pizza/session-title/tests/extension.test.ts +198 -198
- package/node_modules/@vitos-pizza/session-title/tests/extract-title-context.test.ts +48 -48
- package/node_modules/@vitos-pizza/session-title/tests/fast-rules.test.ts +43 -43
- package/node_modules/@vitos-pizza/session-title/tests/parse-title-response.test.ts +34 -34
- package/node_modules/@vitos-pizza/session-title/tests/should-schedule.test.ts +30 -30
- package/node_modules/@vitos-pizza/session-title/tests/title-agent.test.ts +154 -154
- package/node_modules/@vitos-pizza/session-title/tests/truncate-input.test.ts +15 -15
- package/node_modules/@vitos-pizza/session-title/tsconfig.json +13 -13
- package/node_modules/@vitos-pizza/session-title/vitest.config.ts +8 -8
- package/node_modules/@vitos-pizza/subagents/agents/planner.md +46 -43
- package/node_modules/@vitos-pizza/subagents/agents/scout.md +40 -40
- package/node_modules/@vitos-pizza/subagents/agents/title.md +13 -13
- package/node_modules/@vitos-pizza/subagents/agents/worker.md +36 -36
- package/node_modules/@vitos-pizza/subagents/extensions/index.ts +164 -164
- package/node_modules/@vitos-pizza/subagents/package.json +1 -1
- package/node_modules/@vitos-pizza/subagents/skills/subagents/SKILL.md +86 -80
- package/node_modules/@vitos-pizza/subagents/src/agents.ts +166 -166
- package/node_modules/@vitos-pizza/subagents/src/async/job-tracker.ts +71 -71
- package/node_modules/@vitos-pizza/subagents/src/async/spawn-async.ts +220 -220
- package/node_modules/@vitos-pizza/subagents/src/async/storage.ts +57 -57
- package/node_modules/@vitos-pizza/subagents/src/env.ts +4 -4
- package/node_modules/@vitos-pizza/subagents/src/execute-subagent.ts +285 -285
- package/node_modules/@vitos-pizza/subagents/src/progress.ts +187 -187
- package/node_modules/@vitos-pizza/subagents/src/render-animation.ts +40 -40
- package/node_modules/@vitos-pizza/subagents/src/render.ts +428 -428
- package/node_modules/@vitos-pizza/subagents/src/rpc/channels.ts +21 -21
- package/node_modules/@vitos-pizza/subagents/src/rpc/client.ts +144 -144
- package/node_modules/@vitos-pizza/subagents/src/rpc/server.ts +11 -11
- package/node_modules/@vitos-pizza/subagents/src/run-agent.ts +313 -313
- package/node_modules/@vitos-pizza/subagents/src/runtime.ts +102 -102
- package/node_modules/@vitos-pizza/subagents/src/schema.ts +62 -62
- package/node_modules/@vitos-pizza/subagents/src/spawn.ts +104 -104
- package/node_modules/@vitos-pizza/subagents/src/types.ts +92 -92
- package/node_modules/@vitos-pizza/subagents/src/utils.ts +100 -100
- package/node_modules/@vitos-pizza/subagents/src/wait.ts +159 -159
- package/node_modules/@vitos-pizza/subagents/tests/agents.test.ts +19 -19
- package/node_modules/@vitos-pizza/subagents/tests/execute-subagent.test.ts +96 -96
- package/node_modules/@vitos-pizza/subagents/tests/extension.test.ts +42 -42
- package/node_modules/@vitos-pizza/subagents/tests/progress.test.ts +206 -206
- package/node_modules/@vitos-pizza/subagents/tests/rpc.test.ts +74 -74
- package/node_modules/@vitos-pizza/subagents/tsconfig.json +13 -13
- package/node_modules/@vitos-pizza/subagents/vitest.config.ts +8 -8
- package/node_modules/@vitos-pizza/ui-enhancements/package.json +1 -1
- package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
- package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/border-status.ts +14 -1
- package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
- package/node_modules/@vitos-pizza/ui-enhancements/tests/agent-mode-badge.test.ts +35 -35
- package/node_modules/@vitos-pizza/ui-enhancements/tests/resize-recovery.test.ts +135 -0
- package/node_modules/@vitos-pizza/websearch/package.json +1 -1
- package/node_modules/node-addon-api/tools/conversion.js +0 -0
- package/node_modules/node-gyp-build/bin.js +0 -0
- package/node_modules/node-gyp-build/build-test.js +0 -0
- package/node_modules/node-gyp-build/optional.js +0 -0
- package/package.json +11 -9
- package/packages/agent-mode/extensions/index.ts +138 -117
- package/packages/agent-mode/package.json +3 -3
- package/packages/agent-mode/src/apply-mode.ts +60 -60
- package/packages/agent-mode/src/mode-events.ts +102 -102
- package/packages/agent-mode/src/modes.ts +43 -43
- package/packages/agent-mode/src/plan-instructions.ts +85 -44
- package/packages/agent-mode/tsconfig.json +13 -13
- package/packages/agent-mode/vitest.config.ts +8 -8
- package/packages/keybindings/extensions/index.ts +104 -104
- package/packages/keybindings/package.json +1 -1
- package/packages/keybindings/presets/shortcuts.json +3 -3
- package/packages/keybindings/src/bind.ts +57 -57
- package/packages/keybindings/src/config.ts +84 -84
- package/packages/keybindings/src/registry.ts +43 -43
- package/packages/keybindings/src/types.ts +54 -54
- package/packages/keybindings/tsconfig.json +13 -13
- package/packages/keybindings/vitest.config.ts +8 -8
- package/packages/permission-system/extensions/index.ts +162 -162
- package/packages/permission-system/node_modules/web-tree-sitter/debug/tree-sitter.wasm +0 -0
- package/packages/permission-system/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
- package/packages/permission-system/package.json +2 -2
- package/packages/permission-system/src/bash/matcher.ts +54 -51
- package/packages/permission-system/src/bash/parser.ts +40 -40
- package/packages/permission-system/src/config-paths.ts +23 -23
- package/packages/permission-system/src/detect-preset-mode.ts +61 -61
- package/packages/permission-system/src/expand-home.ts +12 -12
- package/packages/permission-system/src/forwarding/forwarder.ts +224 -224
- package/packages/permission-system/src/forwarding/subagent-registry.ts +61 -61
- package/packages/permission-system/src/gates/path-utils.ts +66 -66
- package/packages/permission-system/src/gates/pipeline.ts +151 -155
- package/packages/permission-system/src/handlers/before-agent-start.ts +86 -65
- package/packages/permission-system/src/handlers/skill-input.ts +1 -1
- package/packages/permission-system/src/handlers/tool-call-gate.ts +204 -199
- package/packages/permission-system/src/mode-api.ts +44 -44
- package/packages/permission-system/src/permission-evaluator.ts +233 -216
- package/packages/permission-system/src/permission-session.ts +62 -62
- package/packages/permission-system/src/policy-loader.ts +211 -211
- package/packages/permission-system/src/sanitizers/skill-prompt.ts +99 -99
- package/packages/permission-system/src/sanitizers/system-prompt.ts +221 -221
- package/packages/permission-system/src/service.ts +66 -66
- package/packages/permission-system/src/session-approvals.ts +4 -1
- package/packages/permission-system/src/types.ts +80 -80
- package/packages/permission-system/src/ui/approval-summary.ts +43 -43
- package/packages/permission-system/src/ui/config-modal.ts +36 -36
- package/packages/permission-system/src/ui/permission-dialog.ts +52 -52
- package/packages/permission-system/src/wildcard-matcher.ts +81 -81
- package/packages/permission-system/src/yaml-frontmatter.ts +84 -84
- package/packages/permission-system/src/yolo/runtime-api.ts +49 -49
- package/packages/permission-system/tsconfig.json +13 -13
- package/packages/permission-system/vitest.config.ts +8 -8
- package/packages/question/package.json +1 -1
- package/packages/question/src/question-ui.ts +5 -1
- package/packages/question/src/register-tool.ts +1 -4
- package/packages/session-title/extensions/index.ts +137 -137
- package/packages/session-title/package.json +3 -3
- package/packages/session-title/src/build-title-prompt.ts +16 -16
- package/packages/session-title/src/extract-title-context.ts +55 -55
- package/packages/session-title/src/fast-rules.ts +48 -48
- package/packages/session-title/src/load-settings.ts +70 -70
- package/packages/session-title/src/normalize-title.ts +20 -20
- package/packages/session-title/src/parse-title-response.ts +6 -6
- package/packages/session-title/src/resolve-title-model.ts +33 -33
- package/packages/session-title/src/schedule-title-job.ts +53 -53
- package/packages/session-title/src/should-schedule.ts +30 -30
- package/packages/session-title/src/title-agent.ts +60 -60
- package/packages/session-title/src/truncate-input.ts +10 -10
- package/packages/session-title/src/types.ts +29 -29
- package/packages/session-title/tsconfig.json +13 -13
- package/packages/session-title/vitest.config.ts +8 -8
- package/packages/subagents/agents/planner.md +46 -43
- package/packages/subagents/agents/scout.md +40 -40
- package/packages/subagents/agents/title.md +13 -13
- package/packages/subagents/agents/worker.md +36 -36
- package/packages/subagents/extensions/index.ts +164 -164
- package/packages/subagents/package.json +1 -1
- package/packages/subagents/skills/subagents/SKILL.md +86 -80
- package/packages/subagents/src/agents.ts +166 -166
- package/packages/subagents/src/async/job-tracker.ts +71 -71
- package/packages/subagents/src/async/spawn-async.ts +220 -220
- package/packages/subagents/src/async/storage.ts +57 -57
- package/packages/subagents/src/env.ts +4 -4
- package/packages/subagents/src/execute-subagent.ts +285 -285
- package/packages/subagents/src/progress.ts +187 -187
- package/packages/subagents/src/render-animation.ts +40 -40
- package/packages/subagents/src/render.ts +428 -428
- package/packages/subagents/src/rpc/channels.ts +21 -21
- package/packages/subagents/src/rpc/client.ts +144 -144
- package/packages/subagents/src/rpc/server.ts +11 -11
- package/packages/subagents/src/run-agent.ts +313 -313
- package/packages/subagents/src/runtime.ts +102 -102
- package/packages/subagents/src/schema.ts +62 -62
- package/packages/subagents/src/spawn.ts +104 -104
- package/packages/subagents/src/types.ts +92 -92
- package/packages/subagents/src/utils.ts +100 -100
- package/packages/subagents/src/wait.ts +159 -159
- package/packages/subagents/tsconfig.json +13 -13
- package/packages/subagents/vitest.config.ts +8 -8
- package/packages/todoist/extensions/index.ts +163 -0
- package/packages/todoist/package.json +34 -0
- package/packages/todoist/src/events.ts +7 -0
- package/packages/todoist/src/silent-render.ts +8 -0
- package/packages/todoist/src/state.ts +147 -0
- package/packages/todoist/src/tools.ts +175 -0
- package/packages/todoist/src/types.ts +34 -0
- package/packages/todoist/src/widget.ts +109 -0
- package/packages/todoist/tsconfig.json +13 -0
- package/packages/todoist/vitest.config.ts +8 -0
- package/packages/ui-enhancements/package.json +1 -1
- package/packages/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
- package/packages/ui-enhancements/src/chrome/border-status.ts +14 -1
- package/packages/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
- package/packages/websearch/package.json +1 -1
- package/scripts/sync-pi-manifest.mjs +1 -1
- package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.cjs +0 -2988
- package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm +0 -0
- package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { TitleNamingContext } from "./extract-title-context.ts";
|
|
2
|
-
|
|
3
|
-
export function buildTitleSystemPrompt(maxLength: number): string {
|
|
4
|
-
return `分析用户意图,结合助手最后一次回复,用${maxLength}字以内简短总结本次会话主题。只输出标题,不要引号或解释。若无明确任务意图则回复 SKIP。`;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function buildTitleUserPrompt(context: TitleNamingContext): string {
|
|
8
|
-
return `用户:${context.userMessage}\n\n助手最后一次回复:${context.assistantReply}`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function buildTitleTask(
|
|
12
|
-
context: TitleNamingContext,
|
|
13
|
-
maxTitleLength: number,
|
|
14
|
-
): string {
|
|
15
|
-
return `最大标题长度:${maxTitleLength} 字。\n\n${buildTitleUserPrompt(context)}`;
|
|
16
|
-
}
|
|
1
|
+
import type { TitleNamingContext } from "./extract-title-context.ts";
|
|
2
|
+
|
|
3
|
+
export function buildTitleSystemPrompt(maxLength: number): string {
|
|
4
|
+
return `分析用户意图,结合助手最后一次回复,用${maxLength}字以内简短总结本次会话主题。只输出标题,不要引号或解释。若无明确任务意图则回复 SKIP。`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function buildTitleUserPrompt(context: TitleNamingContext): string {
|
|
8
|
+
return `用户:${context.userMessage}\n\n助手最后一次回复:${context.assistantReply}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildTitleTask(
|
|
12
|
+
context: TitleNamingContext,
|
|
13
|
+
maxTitleLength: number,
|
|
14
|
+
): string {
|
|
15
|
+
return `最大标题长度:${maxTitleLength} 字。\n\n${buildTitleUserPrompt(context)}`;
|
|
16
|
+
}
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
-
|
|
3
|
-
export interface TitleNamingContext {
|
|
4
|
-
userMessage: string;
|
|
5
|
-
assistantReply: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function extractTextFromMessage(message: AgentMessage): string {
|
|
9
|
-
if (message.role === "user") {
|
|
10
|
-
if (typeof message.content === "string") {
|
|
11
|
-
return message.content;
|
|
12
|
-
}
|
|
13
|
-
return message.content
|
|
14
|
-
.filter(
|
|
15
|
-
(part): part is { type: "text"; text: string } =>
|
|
16
|
-
part.type === "text" && typeof part.text === "string",
|
|
17
|
-
)
|
|
18
|
-
.map((part) => part.text)
|
|
19
|
-
.join("\n");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (message.role === "assistant") {
|
|
23
|
-
return message.content
|
|
24
|
-
.filter(
|
|
25
|
-
(part): part is { type: "text"; text: string } =>
|
|
26
|
-
part.type === "text" && typeof part.text === "string",
|
|
27
|
-
)
|
|
28
|
-
.map((part) => part.text)
|
|
29
|
-
.join("\n");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function extractTitleContext(
|
|
36
|
-
messages: AgentMessage[],
|
|
37
|
-
): TitleNamingContext | null {
|
|
38
|
-
let userMessage = "";
|
|
39
|
-
let assistantReply = "";
|
|
40
|
-
|
|
41
|
-
for (const message of messages) {
|
|
42
|
-
if (message.role === "user") {
|
|
43
|
-
userMessage = extractTextFromMessage(message);
|
|
44
|
-
}
|
|
45
|
-
if (message.role === "assistant") {
|
|
46
|
-
assistantReply = extractTextFromMessage(message);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const user = userMessage.trim();
|
|
51
|
-
const assistant = assistantReply.trim();
|
|
52
|
-
if (!user || !assistant) return null;
|
|
53
|
-
|
|
54
|
-
return { userMessage: user, assistantReply: assistant };
|
|
55
|
-
}
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
|
|
3
|
+
export interface TitleNamingContext {
|
|
4
|
+
userMessage: string;
|
|
5
|
+
assistantReply: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function extractTextFromMessage(message: AgentMessage): string {
|
|
9
|
+
if (message.role === "user") {
|
|
10
|
+
if (typeof message.content === "string") {
|
|
11
|
+
return message.content;
|
|
12
|
+
}
|
|
13
|
+
return message.content
|
|
14
|
+
.filter(
|
|
15
|
+
(part): part is { type: "text"; text: string } =>
|
|
16
|
+
part.type === "text" && typeof part.text === "string",
|
|
17
|
+
)
|
|
18
|
+
.map((part) => part.text)
|
|
19
|
+
.join("\n");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (message.role === "assistant") {
|
|
23
|
+
return message.content
|
|
24
|
+
.filter(
|
|
25
|
+
(part): part is { type: "text"; text: string } =>
|
|
26
|
+
part.type === "text" && typeof part.text === "string",
|
|
27
|
+
)
|
|
28
|
+
.map((part) => part.text)
|
|
29
|
+
.join("\n");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function extractTitleContext(
|
|
36
|
+
messages: AgentMessage[],
|
|
37
|
+
): TitleNamingContext | null {
|
|
38
|
+
let userMessage = "";
|
|
39
|
+
let assistantReply = "";
|
|
40
|
+
|
|
41
|
+
for (const message of messages) {
|
|
42
|
+
if (message.role === "user") {
|
|
43
|
+
userMessage = extractTextFromMessage(message);
|
|
44
|
+
}
|
|
45
|
+
if (message.role === "assistant") {
|
|
46
|
+
assistantReply = extractTextFromMessage(message);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const user = userMessage.trim();
|
|
51
|
+
const assistant = assistantReply.trim();
|
|
52
|
+
if (!user || !assistant) return null;
|
|
53
|
+
|
|
54
|
+
return { userMessage: user, assistantReply: assistant };
|
|
55
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
const PUNCTUATION_ONLY = /^[\p{P}\p{S}\s]+$/u;
|
|
2
|
-
|
|
3
|
-
export function codePointLength(text: string): number {
|
|
4
|
-
return [...text].length;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function isTooShort(text: string, minChars: number): boolean {
|
|
8
|
-
const trimmed = text.trim();
|
|
9
|
-
if (!trimmed) return true;
|
|
10
|
-
return codePointLength(trimmed) < minChars;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function isPurePunctuation(text: string): boolean {
|
|
14
|
-
const trimmed = text.trim();
|
|
15
|
-
if (!trimmed) return false;
|
|
16
|
-
return PUNCTUATION_ONLY.test(trimmed);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isEmojiOnly(text: string): boolean {
|
|
20
|
-
const trimmed = text.trim();
|
|
21
|
-
if (!trimmed) return false;
|
|
22
|
-
const withoutJoiners = trimmed.replace(/[\uFE0F\u200D]/g, "");
|
|
23
|
-
return /^[\p{Extended_Pictographic}\p{Emoji_Presentation}\s]+$/u.test(
|
|
24
|
-
withoutJoiners,
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function isRepeatedSingleChar(text: string, minRepeat = 3): boolean {
|
|
29
|
-
const trimmed = text.trim();
|
|
30
|
-
if (codePointLength(trimmed) < minRepeat) return false;
|
|
31
|
-
const chars = [...trimmed];
|
|
32
|
-
return chars.every((char) => char === chars[0]);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function matchesFastReject(
|
|
36
|
-
text: string,
|
|
37
|
-
options: { minCharsForLlm: number; fastRules: boolean },
|
|
38
|
-
): boolean {
|
|
39
|
-
if (!options.fastRules) return false;
|
|
40
|
-
const trimmed = text.trim();
|
|
41
|
-
if (!trimmed) return false;
|
|
42
|
-
return (
|
|
43
|
-
isTooShort(trimmed, options.minCharsForLlm) ||
|
|
44
|
-
isPurePunctuation(trimmed) ||
|
|
45
|
-
isEmojiOnly(trimmed) ||
|
|
46
|
-
isRepeatedSingleChar(trimmed)
|
|
47
|
-
);
|
|
48
|
-
}
|
|
1
|
+
const PUNCTUATION_ONLY = /^[\p{P}\p{S}\s]+$/u;
|
|
2
|
+
|
|
3
|
+
export function codePointLength(text: string): number {
|
|
4
|
+
return [...text].length;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isTooShort(text: string, minChars: number): boolean {
|
|
8
|
+
const trimmed = text.trim();
|
|
9
|
+
if (!trimmed) return true;
|
|
10
|
+
return codePointLength(trimmed) < minChars;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isPurePunctuation(text: string): boolean {
|
|
14
|
+
const trimmed = text.trim();
|
|
15
|
+
if (!trimmed) return false;
|
|
16
|
+
return PUNCTUATION_ONLY.test(trimmed);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isEmojiOnly(text: string): boolean {
|
|
20
|
+
const trimmed = text.trim();
|
|
21
|
+
if (!trimmed) return false;
|
|
22
|
+
const withoutJoiners = trimmed.replace(/[\uFE0F\u200D]/g, "");
|
|
23
|
+
return /^[\p{Extended_Pictographic}\p{Emoji_Presentation}\s]+$/u.test(
|
|
24
|
+
withoutJoiners,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isRepeatedSingleChar(text: string, minRepeat = 3): boolean {
|
|
29
|
+
const trimmed = text.trim();
|
|
30
|
+
if (codePointLength(trimmed) < minRepeat) return false;
|
|
31
|
+
const chars = [...trimmed];
|
|
32
|
+
return chars.every((char) => char === chars[0]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function matchesFastReject(
|
|
36
|
+
text: string,
|
|
37
|
+
options: { minCharsForLlm: number; fastRules: boolean },
|
|
38
|
+
): boolean {
|
|
39
|
+
if (!options.fastRules) return false;
|
|
40
|
+
const trimmed = text.trim();
|
|
41
|
+
if (!trimmed) return false;
|
|
42
|
+
return (
|
|
43
|
+
isTooShort(trimmed, options.minCharsForLlm) ||
|
|
44
|
+
isPurePunctuation(trimmed) ||
|
|
45
|
+
isEmojiOnly(trimmed) ||
|
|
46
|
+
isRepeatedSingleChar(trimmed)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
type AutoTitleSettings,
|
|
6
|
-
DEFAULT_AUTO_TITLE_SETTINGS,
|
|
7
|
-
} from "./types.ts";
|
|
8
|
-
|
|
9
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
10
|
-
return typeof value === "object" && value !== null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function readJson(path: string): unknown {
|
|
14
|
-
try {
|
|
15
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
16
|
-
} catch {
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function extractAutoTitle(
|
|
22
|
-
raw: unknown,
|
|
23
|
-
): Partial<AutoTitleSettings> | undefined {
|
|
24
|
-
if (!isRecord(raw)) return undefined;
|
|
25
|
-
const session = raw.session;
|
|
26
|
-
if (!isRecord(session)) return undefined;
|
|
27
|
-
const autoTitle = session.autoTitle;
|
|
28
|
-
if (!isRecord(autoTitle)) return undefined;
|
|
29
|
-
return autoTitle as Partial<AutoTitleSettings>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function mergeSettings(
|
|
33
|
-
base: AutoTitleSettings,
|
|
34
|
-
partial?: Partial<AutoTitleSettings>,
|
|
35
|
-
): AutoTitleSettings {
|
|
36
|
-
if (!partial) return base;
|
|
37
|
-
return {
|
|
38
|
-
enabled: partial.enabled ?? base.enabled,
|
|
39
|
-
model: partial.model ?? base.model,
|
|
40
|
-
minCharsForLlm: partial.minCharsForLlm ?? base.minCharsForLlm,
|
|
41
|
-
fastRules: partial.fastRules ?? base.fastRules,
|
|
42
|
-
maxInputChars: partial.maxInputChars ?? base.maxInputChars,
|
|
43
|
-
maxOutputTokens: partial.maxOutputTokens ?? base.maxOutputTokens,
|
|
44
|
-
timeoutMs: partial.timeoutMs ?? base.timeoutMs,
|
|
45
|
-
maxTitleLength: partial.maxTitleLength ?? base.maxTitleLength,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function loadAutoTitleSettings(
|
|
50
|
-
cwd: string,
|
|
51
|
-
overrides?: Partial<AutoTitleSettings>,
|
|
52
|
-
): AutoTitleSettings {
|
|
53
|
-
let merged = { ...DEFAULT_AUTO_TITLE_SETTINGS };
|
|
54
|
-
|
|
55
|
-
const globalPath = join(homedir(), ".pi", "agent", "settings.json");
|
|
56
|
-
if (existsSync(globalPath)) {
|
|
57
|
-
merged = mergeSettings(merged, extractAutoTitle(readJson(globalPath)));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const projectPath = join(cwd, ".pi", "settings.json");
|
|
61
|
-
if (existsSync(projectPath)) {
|
|
62
|
-
merged = mergeSettings(merged, extractAutoTitle(readJson(projectPath)));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (overrides) {
|
|
66
|
-
merged = mergeSettings(merged, overrides);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return merged;
|
|
70
|
-
}
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
type AutoTitleSettings,
|
|
6
|
+
DEFAULT_AUTO_TITLE_SETTINGS,
|
|
7
|
+
} from "./types.ts";
|
|
8
|
+
|
|
9
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
10
|
+
return typeof value === "object" && value !== null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function readJson(path: string): unknown {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
16
|
+
} catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function extractAutoTitle(
|
|
22
|
+
raw: unknown,
|
|
23
|
+
): Partial<AutoTitleSettings> | undefined {
|
|
24
|
+
if (!isRecord(raw)) return undefined;
|
|
25
|
+
const session = raw.session;
|
|
26
|
+
if (!isRecord(session)) return undefined;
|
|
27
|
+
const autoTitle = session.autoTitle;
|
|
28
|
+
if (!isRecord(autoTitle)) return undefined;
|
|
29
|
+
return autoTitle as Partial<AutoTitleSettings>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mergeSettings(
|
|
33
|
+
base: AutoTitleSettings,
|
|
34
|
+
partial?: Partial<AutoTitleSettings>,
|
|
35
|
+
): AutoTitleSettings {
|
|
36
|
+
if (!partial) return base;
|
|
37
|
+
return {
|
|
38
|
+
enabled: partial.enabled ?? base.enabled,
|
|
39
|
+
model: partial.model ?? base.model,
|
|
40
|
+
minCharsForLlm: partial.minCharsForLlm ?? base.minCharsForLlm,
|
|
41
|
+
fastRules: partial.fastRules ?? base.fastRules,
|
|
42
|
+
maxInputChars: partial.maxInputChars ?? base.maxInputChars,
|
|
43
|
+
maxOutputTokens: partial.maxOutputTokens ?? base.maxOutputTokens,
|
|
44
|
+
timeoutMs: partial.timeoutMs ?? base.timeoutMs,
|
|
45
|
+
maxTitleLength: partial.maxTitleLength ?? base.maxTitleLength,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function loadAutoTitleSettings(
|
|
50
|
+
cwd: string,
|
|
51
|
+
overrides?: Partial<AutoTitleSettings>,
|
|
52
|
+
): AutoTitleSettings {
|
|
53
|
+
let merged = { ...DEFAULT_AUTO_TITLE_SETTINGS };
|
|
54
|
+
|
|
55
|
+
const globalPath = join(homedir(), ".pi", "agent", "settings.json");
|
|
56
|
+
if (existsSync(globalPath)) {
|
|
57
|
+
merged = mergeSettings(merged, extractAutoTitle(readJson(globalPath)));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const projectPath = join(cwd, ".pi", "settings.json");
|
|
61
|
+
if (existsSync(projectPath)) {
|
|
62
|
+
merged = mergeSettings(merged, extractAutoTitle(readJson(projectPath)));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (overrides) {
|
|
66
|
+
merged = mergeSettings(merged, overrides);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return merged;
|
|
70
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { truncateInput } from "./truncate-input.ts";
|
|
2
|
-
|
|
3
|
-
export function normalizeTitle(title: string, maxLength = 48): string {
|
|
4
|
-
const cleaned = title
|
|
5
|
-
.trim()
|
|
6
|
-
.replace(/^["']+|["']+$/g, "")
|
|
7
|
-
.trim()
|
|
8
|
-
.replace(/\s+/g, " ");
|
|
9
|
-
if (!cleaned) return "";
|
|
10
|
-
if ([...cleaned].length <= maxLength) return cleaned;
|
|
11
|
-
return [...cleaned].slice(0, maxLength).join("").trim();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function fallbackTitleFromPrompt(
|
|
15
|
-
prompt: string,
|
|
16
|
-
maxLength = 48,
|
|
17
|
-
): string {
|
|
18
|
-
const snippet = truncateInput(prompt, maxLength);
|
|
19
|
-
return normalizeTitle(snippet, maxLength);
|
|
20
|
-
}
|
|
1
|
+
import { truncateInput } from "./truncate-input.ts";
|
|
2
|
+
|
|
3
|
+
export function normalizeTitle(title: string, maxLength = 48): string {
|
|
4
|
+
const cleaned = title
|
|
5
|
+
.trim()
|
|
6
|
+
.replace(/^["']+|["']+$/g, "")
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/\s+/g, " ");
|
|
9
|
+
if (!cleaned) return "";
|
|
10
|
+
if ([...cleaned].length <= maxLength) return cleaned;
|
|
11
|
+
return [...cleaned].slice(0, maxLength).join("").trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function fallbackTitleFromPrompt(
|
|
15
|
+
prompt: string,
|
|
16
|
+
maxLength = 48,
|
|
17
|
+
): string {
|
|
18
|
+
const snippet = truncateInput(prompt, maxLength);
|
|
19
|
+
return normalizeTitle(snippet, maxLength);
|
|
20
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function parseTitleResponse(raw: string): string | null {
|
|
2
|
-
const trimmed = raw.trim();
|
|
3
|
-
if (!trimmed) return null;
|
|
4
|
-
if (trimmed.toUpperCase() === "SKIP") return null;
|
|
5
|
-
return trimmed;
|
|
6
|
-
}
|
|
1
|
+
export function parseTitleResponse(raw: string): string | null {
|
|
2
|
+
const trimmed = raw.trim();
|
|
3
|
+
if (!trimmed) return null;
|
|
4
|
+
if (trimmed.toUpperCase() === "SKIP") return null;
|
|
5
|
+
return trimmed;
|
|
6
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
-
|
|
3
|
-
export interface TitleModelContext {
|
|
4
|
-
model?: Model<Api> | undefined;
|
|
5
|
-
modelRegistry?: {
|
|
6
|
-
find(provider: string, modelId: string): Model<Api> | undefined;
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function resolveTitleModel(
|
|
11
|
-
ctx: TitleModelContext,
|
|
12
|
-
configuredModel?: string,
|
|
13
|
-
): Model<Api> | undefined {
|
|
14
|
-
if (configuredModel && ctx.modelRegistry) {
|
|
15
|
-
const [provider, modelId] = configuredModel.split("/");
|
|
16
|
-
if (provider && modelId) {
|
|
17
|
-
const found = ctx.modelRegistry.find(provider, modelId);
|
|
18
|
-
if (found) return found;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return ctx.model;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function resolveTitleModelRef(
|
|
25
|
-
ctx: TitleModelContext,
|
|
26
|
-
configuredModel?: string,
|
|
27
|
-
): string | undefined {
|
|
28
|
-
if (configuredModel) return configuredModel;
|
|
29
|
-
if (ctx.model?.provider && ctx.model?.id) {
|
|
30
|
-
return `${ctx.model.provider}/${ctx.model.id}`;
|
|
31
|
-
}
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
|
|
3
|
+
export interface TitleModelContext {
|
|
4
|
+
model?: Model<Api> | undefined;
|
|
5
|
+
modelRegistry?: {
|
|
6
|
+
find(provider: string, modelId: string): Model<Api> | undefined;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function resolveTitleModel(
|
|
11
|
+
ctx: TitleModelContext,
|
|
12
|
+
configuredModel?: string,
|
|
13
|
+
): Model<Api> | undefined {
|
|
14
|
+
if (configuredModel && ctx.modelRegistry) {
|
|
15
|
+
const [provider, modelId] = configuredModel.split("/");
|
|
16
|
+
if (provider && modelId) {
|
|
17
|
+
const found = ctx.modelRegistry.find(provider, modelId);
|
|
18
|
+
if (found) return found;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return ctx.model;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveTitleModelRef(
|
|
25
|
+
ctx: TitleModelContext,
|
|
26
|
+
configuredModel?: string,
|
|
27
|
+
): string | undefined {
|
|
28
|
+
if (configuredModel) return configuredModel;
|
|
29
|
+
if (ctx.model?.provider && ctx.model?.id) {
|
|
30
|
+
return `${ctx.model.provider}/${ctx.model.id}`;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import type { TitleNamingContext } from "./extract-title-context.ts";
|
|
2
|
-
import { fallbackTitleFromPrompt, normalizeTitle } from "./normalize-title.ts";
|
|
3
|
-
import type { TitleAgent } from "./title-agent.ts";
|
|
4
|
-
|
|
5
|
-
export interface SessionNameApi {
|
|
6
|
-
getSessionName: () => string | undefined;
|
|
7
|
-
setSessionName: (name: string) => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface TitleJob {
|
|
11
|
-
agent: TitleAgent;
|
|
12
|
-
context: TitleNamingContext;
|
|
13
|
-
signal: AbortSignal;
|
|
14
|
-
sessionName: SessionNameApi;
|
|
15
|
-
maxTitleLength: number;
|
|
16
|
-
onSettled?: () => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface TitleJobState {
|
|
20
|
-
inFlight: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function scheduleTitleJob(job: TitleJob, state: TitleJobState): void {
|
|
24
|
-
if (state.inFlight) return;
|
|
25
|
-
state.inFlight = true;
|
|
26
|
-
|
|
27
|
-
void job.agent
|
|
28
|
-
.run(job.context, job.signal)
|
|
29
|
-
.then((title) => {
|
|
30
|
-
if (job.signal.aborted) return;
|
|
31
|
-
if (job.sessionName.getSessionName()) return;
|
|
32
|
-
if (title) {
|
|
33
|
-
job.sessionName.setSessionName(title);
|
|
34
|
-
job.onSettled?.();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
.catch(() => {
|
|
39
|
-
if (job.signal.aborted) return;
|
|
40
|
-
if (job.sessionName.getSessionName()) return;
|
|
41
|
-
const fallback = normalizeTitle(
|
|
42
|
-
fallbackTitleFromPrompt(job.context.userMessage, job.maxTitleLength),
|
|
43
|
-
job.maxTitleLength,
|
|
44
|
-
);
|
|
45
|
-
if (fallback) {
|
|
46
|
-
job.sessionName.setSessionName(fallback);
|
|
47
|
-
job.onSettled?.();
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
.finally(() => {
|
|
51
|
-
state.inFlight = false;
|
|
52
|
-
});
|
|
53
|
-
}
|
|
1
|
+
import type { TitleNamingContext } from "./extract-title-context.ts";
|
|
2
|
+
import { fallbackTitleFromPrompt, normalizeTitle } from "./normalize-title.ts";
|
|
3
|
+
import type { TitleAgent } from "./title-agent.ts";
|
|
4
|
+
|
|
5
|
+
export interface SessionNameApi {
|
|
6
|
+
getSessionName: () => string | undefined;
|
|
7
|
+
setSessionName: (name: string) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TitleJob {
|
|
11
|
+
agent: TitleAgent;
|
|
12
|
+
context: TitleNamingContext;
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
sessionName: SessionNameApi;
|
|
15
|
+
maxTitleLength: number;
|
|
16
|
+
onSettled?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TitleJobState {
|
|
20
|
+
inFlight: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function scheduleTitleJob(job: TitleJob, state: TitleJobState): void {
|
|
24
|
+
if (state.inFlight) return;
|
|
25
|
+
state.inFlight = true;
|
|
26
|
+
|
|
27
|
+
void job.agent
|
|
28
|
+
.run(job.context, job.signal)
|
|
29
|
+
.then((title) => {
|
|
30
|
+
if (job.signal.aborted) return;
|
|
31
|
+
if (job.sessionName.getSessionName()) return;
|
|
32
|
+
if (title) {
|
|
33
|
+
job.sessionName.setSessionName(title);
|
|
34
|
+
job.onSettled?.();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.catch(() => {
|
|
39
|
+
if (job.signal.aborted) return;
|
|
40
|
+
if (job.sessionName.getSessionName()) return;
|
|
41
|
+
const fallback = normalizeTitle(
|
|
42
|
+
fallbackTitleFromPrompt(job.context.userMessage, job.maxTitleLength),
|
|
43
|
+
job.maxTitleLength,
|
|
44
|
+
);
|
|
45
|
+
if (fallback) {
|
|
46
|
+
job.sessionName.setSessionName(fallback);
|
|
47
|
+
job.onSettled?.();
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
.finally(() => {
|
|
51
|
+
state.inFlight = false;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { matchesFastReject } from "./fast-rules.ts";
|
|
2
|
-
import type { ScheduleContext, ScheduleDecision } from "./types.ts";
|
|
3
|
-
|
|
4
|
-
const COMMAND_PREFIXES = ["/", "!", "$"];
|
|
5
|
-
|
|
6
|
-
export function isBlank(text: string): boolean {
|
|
7
|
-
return text.trim().length === 0;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function isCommandInput(text: string): boolean {
|
|
11
|
-
const trimmed = text.trim();
|
|
12
|
-
return COMMAND_PREFIXES.some((prefix) => trimmed.startsWith(prefix));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function decideSchedule(
|
|
16
|
-
input: string,
|
|
17
|
-
ctx: ScheduleContext,
|
|
18
|
-
): ScheduleDecision {
|
|
19
|
-
if (ctx.hasSessionName || ctx.inFlight) return "skip";
|
|
20
|
-
if (isBlank(input) || isCommandInput(input)) return "skip";
|
|
21
|
-
if (
|
|
22
|
-
matchesFastReject(input, {
|
|
23
|
-
minCharsForLlm: ctx.minCharsForLlm,
|
|
24
|
-
fastRules: ctx.fastRules,
|
|
25
|
-
})
|
|
26
|
-
) {
|
|
27
|
-
return "fastReject";
|
|
28
|
-
}
|
|
29
|
-
return "schedule";
|
|
30
|
-
}
|
|
1
|
+
import { matchesFastReject } from "./fast-rules.ts";
|
|
2
|
+
import type { ScheduleContext, ScheduleDecision } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
const COMMAND_PREFIXES = ["/", "!", "$"];
|
|
5
|
+
|
|
6
|
+
export function isBlank(text: string): boolean {
|
|
7
|
+
return text.trim().length === 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function isCommandInput(text: string): boolean {
|
|
11
|
+
const trimmed = text.trim();
|
|
12
|
+
return COMMAND_PREFIXES.some((prefix) => trimmed.startsWith(prefix));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function decideSchedule(
|
|
16
|
+
input: string,
|
|
17
|
+
ctx: ScheduleContext,
|
|
18
|
+
): ScheduleDecision {
|
|
19
|
+
if (ctx.hasSessionName || ctx.inFlight) return "skip";
|
|
20
|
+
if (isBlank(input) || isCommandInput(input)) return "skip";
|
|
21
|
+
if (
|
|
22
|
+
matchesFastReject(input, {
|
|
23
|
+
minCharsForLlm: ctx.minCharsForLlm,
|
|
24
|
+
fastRules: ctx.fastRules,
|
|
25
|
+
})
|
|
26
|
+
) {
|
|
27
|
+
return "fastReject";
|
|
28
|
+
}
|
|
29
|
+
return "schedule";
|
|
30
|
+
}
|