gsd-pi 2.18.0 → 2.20.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/README.md +5 -1
- package/dist/cli.js +3 -3
- package/dist/onboarding.d.ts +3 -1
- package/dist/onboarding.js +77 -3
- package/dist/remote-questions-config.d.ts +1 -1
- package/dist/resources/extensions/google-search/index.ts +164 -47
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/dist/resources/extensions/gsd/auto.ts +690 -39
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +654 -36
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/context-budget.ts +243 -0
- package/dist/resources/extensions/gsd/context-store.ts +195 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/dist/resources/extensions/gsd/db-writer.ts +341 -0
- package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/dist/resources/extensions/gsd/doctor.ts +283 -2
- package/dist/resources/extensions/gsd/export.ts +81 -2
- package/dist/resources/extensions/gsd/files.ts +39 -9
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
- package/dist/resources/extensions/gsd/history.ts +0 -1
- package/dist/resources/extensions/gsd/index.ts +277 -1
- package/dist/resources/extensions/gsd/md-importer.ts +526 -0
- package/dist/resources/extensions/gsd/metrics.ts +84 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/notifications.ts +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/dist/resources/extensions/gsd/preferences.ts +198 -150
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/quick.ts +156 -0
- package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/dist/resources/extensions/gsd/skill-health.ts +417 -0
- package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/types.ts +29 -0
- package/dist/resources/extensions/gsd/undo.ts +0 -1
- package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/dist/resources/extensions/remote-questions/config.ts +4 -2
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/dist/resources/extensions/remote-questions/format.ts +166 -14
- package/dist/resources/extensions/remote-questions/manager.ts +14 -4
- package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/dist/resources/extensions/remote-questions/types.ts +2 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/dist/resources/extensions/voice/index.ts +4 -3
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
- package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
- package/src/resources/extensions/google-search/index.ts +164 -47
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/src/resources/extensions/gsd/auto.ts +690 -39
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +654 -36
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/context-budget.ts +243 -0
- package/src/resources/extensions/gsd/context-store.ts +195 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/src/resources/extensions/gsd/db-writer.ts +341 -0
- package/src/resources/extensions/gsd/debug-logger.ts +178 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/src/resources/extensions/gsd/doctor.ts +283 -2
- package/src/resources/extensions/gsd/export.ts +81 -2
- package/src/resources/extensions/gsd/files.ts +39 -9
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gsd-db.ts +752 -0
- package/src/resources/extensions/gsd/guided-flow.ts +26 -1
- package/src/resources/extensions/gsd/history.ts +0 -1
- package/src/resources/extensions/gsd/index.ts +277 -1
- package/src/resources/extensions/gsd/md-importer.ts +526 -0
- package/src/resources/extensions/gsd/metrics.ts +84 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/notifications.ts +0 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/src/resources/extensions/gsd/preferences.ts +198 -150
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/quick.ts +156 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/src/resources/extensions/gsd/skill-health.ts +417 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/types.ts +29 -0
- package/src/resources/extensions/gsd/undo.ts +0 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/src/resources/extensions/gsd/worktree-command.ts +18 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/src/resources/extensions/remote-questions/config.ts +4 -2
- package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/src/resources/extensions/remote-questions/format.ts +166 -14
- package/src/resources/extensions/remote-questions/manager.ts +14 -4
- package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/src/resources/extensions/remote-questions/types.ts +2 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/src/resources/extensions/voice/index.ts +4 -3
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import {
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join, dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { parseSlackReply, parseDiscordResponse, formatForDiscord, formatForSlack, parseSlackReactionResponse, formatForTelegram, parseTelegramResponse } from "../../remote-questions/format.ts";
|
|
4
7
|
import { resolveRemoteConfig, isValidChannelId } from "../../remote-questions/config.ts";
|
|
5
8
|
import { sanitizeError } from "../../remote-questions/manager.ts";
|
|
6
9
|
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
7
13
|
test("parseSlackReply handles single-number single-question answers", () => {
|
|
8
14
|
const result = parseSlackReply("2", [{
|
|
9
15
|
id: "choice",
|
|
@@ -88,6 +94,21 @@ test("parseDiscordResponse rejects multi-question reaction parsing", () => {
|
|
|
88
94
|
assert.match(String(result.answers.second.user_note), /single-question prompts/i);
|
|
89
95
|
});
|
|
90
96
|
|
|
97
|
+
test("parseSlackReactionResponse handles single-question reactions", () => {
|
|
98
|
+
const result = parseSlackReactionResponse(["two"], [{
|
|
99
|
+
id: "choice",
|
|
100
|
+
header: "Choice",
|
|
101
|
+
question: "Pick one",
|
|
102
|
+
allowMultiple: false,
|
|
103
|
+
options: [
|
|
104
|
+
{ label: "Alpha", description: "A" },
|
|
105
|
+
{ label: "Beta", description: "B" },
|
|
106
|
+
],
|
|
107
|
+
}]);
|
|
108
|
+
|
|
109
|
+
assert.deepEqual(result, { answers: { choice: { answers: ["Beta"] } } });
|
|
110
|
+
});
|
|
111
|
+
|
|
91
112
|
test("parseSlackReply truncates user_note longer than 500 chars", () => {
|
|
92
113
|
const longText = "x".repeat(600);
|
|
93
114
|
const result = parseSlackReply(longText, [{
|
|
@@ -153,3 +174,469 @@ test("sanitizeError preserves short safe messages", () => {
|
|
|
153
174
|
assert.equal(sanitizeError("Connection refused"), "Connection refused");
|
|
154
175
|
});
|
|
155
176
|
|
|
177
|
+
|
|
178
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
179
|
+
// Discord Parity Tests
|
|
180
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
181
|
+
|
|
182
|
+
test("formatForDiscord includes context source in footer when present", () => {
|
|
183
|
+
const prompt = {
|
|
184
|
+
id: "test-1",
|
|
185
|
+
channel: "discord" as const,
|
|
186
|
+
createdAt: Date.now(),
|
|
187
|
+
timeoutAt: Date.now() + 60000,
|
|
188
|
+
pollIntervalMs: 5000,
|
|
189
|
+
context: { source: "auto-mode-dispatch" },
|
|
190
|
+
questions: [{
|
|
191
|
+
id: "q1",
|
|
192
|
+
header: "Confirm",
|
|
193
|
+
question: "Proceed?",
|
|
194
|
+
options: [
|
|
195
|
+
{ label: "Yes", description: "Continue" },
|
|
196
|
+
{ label: "No", description: "Stop" },
|
|
197
|
+
],
|
|
198
|
+
allowMultiple: false,
|
|
199
|
+
}],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const { embeds } = formatForDiscord(prompt);
|
|
203
|
+
assert.equal(embeds.length, 1);
|
|
204
|
+
assert.ok(embeds[0].footer?.text.includes("auto-mode-dispatch"), "footer should include context source");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("formatForSlack includes context source when present", () => {
|
|
208
|
+
const blocks = formatForSlack({
|
|
209
|
+
id: "slack-1",
|
|
210
|
+
channel: "slack",
|
|
211
|
+
createdAt: Date.now(),
|
|
212
|
+
timeoutAt: Date.now() + 60000,
|
|
213
|
+
pollIntervalMs: 5000,
|
|
214
|
+
context: { source: "ask_user_questions" },
|
|
215
|
+
questions: [{
|
|
216
|
+
id: "q1",
|
|
217
|
+
header: "Confirm",
|
|
218
|
+
question: "Proceed?",
|
|
219
|
+
options: [
|
|
220
|
+
{ label: "Yes", description: "Continue" },
|
|
221
|
+
{ label: "No", description: "Stop" },
|
|
222
|
+
],
|
|
223
|
+
allowMultiple: false,
|
|
224
|
+
}],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const sourceBlock = blocks.find((block) => block.type === "context" && block.elements?.some((el) => el.text.includes("Source:")));
|
|
228
|
+
assert.ok(sourceBlock, "Slack blocks should include a context source block");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("formatForSlack multi-question prompts explain semicolon and newline reply format", () => {
|
|
232
|
+
const blocks = formatForSlack({
|
|
233
|
+
id: "slack-2",
|
|
234
|
+
channel: "slack",
|
|
235
|
+
createdAt: Date.now(),
|
|
236
|
+
timeoutAt: Date.now() + 60000,
|
|
237
|
+
pollIntervalMs: 5000,
|
|
238
|
+
questions: [
|
|
239
|
+
{
|
|
240
|
+
id: "q1",
|
|
241
|
+
header: "First",
|
|
242
|
+
question: "Pick one",
|
|
243
|
+
options: [
|
|
244
|
+
{ label: "Alpha", description: "A" },
|
|
245
|
+
{ label: "Beta", description: "B" },
|
|
246
|
+
],
|
|
247
|
+
allowMultiple: false,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: "q2",
|
|
251
|
+
header: "Second",
|
|
252
|
+
question: "Explain",
|
|
253
|
+
options: [
|
|
254
|
+
{ label: "Gamma", description: "G" },
|
|
255
|
+
{ label: "Delta", description: "D" },
|
|
256
|
+
],
|
|
257
|
+
allowMultiple: false,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const instructionBlock = blocks.find((block) => block.type === "context" && block.elements?.some((el) => el.text.includes("one line per question")));
|
|
263
|
+
assert.ok(instructionBlock, "Slack multi-question prompts should explain one-line or semicolon reply format");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("formatForDiscord omits source from footer when context is absent", () => {
|
|
267
|
+
const prompt = {
|
|
268
|
+
id: "test-2",
|
|
269
|
+
channel: "discord" as const,
|
|
270
|
+
createdAt: Date.now(),
|
|
271
|
+
timeoutAt: Date.now() + 60000,
|
|
272
|
+
pollIntervalMs: 5000,
|
|
273
|
+
questions: [{
|
|
274
|
+
id: "q1",
|
|
275
|
+
header: "Choice",
|
|
276
|
+
question: "Pick one",
|
|
277
|
+
options: [
|
|
278
|
+
{ label: "A", description: "Alpha" },
|
|
279
|
+
{ label: "B", description: "Beta" },
|
|
280
|
+
],
|
|
281
|
+
allowMultiple: false,
|
|
282
|
+
}],
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const { embeds } = formatForDiscord(prompt);
|
|
286
|
+
assert.ok(!embeds[0].footer?.text.includes("Source:"), "footer should not include Source when context absent");
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("formatForDiscord multi-question footer includes question position", () => {
|
|
290
|
+
const prompt = {
|
|
291
|
+
id: "test-3",
|
|
292
|
+
channel: "discord" as const,
|
|
293
|
+
createdAt: Date.now(),
|
|
294
|
+
timeoutAt: Date.now() + 60000,
|
|
295
|
+
pollIntervalMs: 5000,
|
|
296
|
+
questions: [
|
|
297
|
+
{
|
|
298
|
+
id: "q1",
|
|
299
|
+
header: "First",
|
|
300
|
+
question: "Pick",
|
|
301
|
+
options: [{ label: "A", description: "a" }],
|
|
302
|
+
allowMultiple: false,
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: "q2",
|
|
306
|
+
header: "Second",
|
|
307
|
+
question: "Pick",
|
|
308
|
+
options: [{ label: "B", description: "b" }],
|
|
309
|
+
allowMultiple: false,
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const { embeds } = formatForDiscord(prompt);
|
|
315
|
+
assert.equal(embeds.length, 2);
|
|
316
|
+
assert.ok(embeds[0].footer?.text.includes("1/2"), "first embed footer should show 1/2");
|
|
317
|
+
assert.ok(embeds[1].footer?.text.includes("2/2"), "second embed footer should show 2/2");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("formatForDiscord single-question generates reaction emojis", () => {
|
|
321
|
+
const prompt = {
|
|
322
|
+
id: "test-4",
|
|
323
|
+
channel: "discord" as const,
|
|
324
|
+
createdAt: Date.now(),
|
|
325
|
+
timeoutAt: Date.now() + 60000,
|
|
326
|
+
pollIntervalMs: 5000,
|
|
327
|
+
questions: [{
|
|
328
|
+
id: "q1",
|
|
329
|
+
header: "Pick",
|
|
330
|
+
question: "Choose",
|
|
331
|
+
options: [
|
|
332
|
+
{ label: "A", description: "a" },
|
|
333
|
+
{ label: "B", description: "b" },
|
|
334
|
+
{ label: "C", description: "c" },
|
|
335
|
+
],
|
|
336
|
+
allowMultiple: false,
|
|
337
|
+
}],
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const { reactionEmojis } = formatForDiscord(prompt);
|
|
341
|
+
assert.equal(reactionEmojis.length, 3, "should generate 3 reaction emojis for 3 options");
|
|
342
|
+
assert.equal(reactionEmojis[0], "1️⃣");
|
|
343
|
+
assert.equal(reactionEmojis[1], "2️⃣");
|
|
344
|
+
assert.equal(reactionEmojis[2], "3️⃣");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("formatForDiscord multi-question generates no reaction emojis", () => {
|
|
348
|
+
const prompt = {
|
|
349
|
+
id: "test-5",
|
|
350
|
+
channel: "discord" as const,
|
|
351
|
+
createdAt: Date.now(),
|
|
352
|
+
timeoutAt: Date.now() + 60000,
|
|
353
|
+
pollIntervalMs: 5000,
|
|
354
|
+
questions: [
|
|
355
|
+
{
|
|
356
|
+
id: "q1",
|
|
357
|
+
header: "First",
|
|
358
|
+
question: "Pick",
|
|
359
|
+
options: [{ label: "A", description: "a" }],
|
|
360
|
+
allowMultiple: false,
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "q2",
|
|
364
|
+
header: "Second",
|
|
365
|
+
question: "Pick",
|
|
366
|
+
options: [{ label: "B", description: "b" }],
|
|
367
|
+
allowMultiple: false,
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const { reactionEmojis } = formatForDiscord(prompt);
|
|
373
|
+
assert.equal(reactionEmojis.length, 0, "multi-question should not generate reaction emojis");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("parseDiscordResponse handles multi-question text reply via semicolons", () => {
|
|
377
|
+
const result = parseDiscordResponse([], "1;2", [
|
|
378
|
+
{
|
|
379
|
+
id: "first",
|
|
380
|
+
header: "First",
|
|
381
|
+
question: "Pick one",
|
|
382
|
+
allowMultiple: false,
|
|
383
|
+
options: [
|
|
384
|
+
{ label: "Alpha", description: "A" },
|
|
385
|
+
{ label: "Beta", description: "B" },
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
id: "second",
|
|
390
|
+
header: "Second",
|
|
391
|
+
question: "Pick one",
|
|
392
|
+
allowMultiple: false,
|
|
393
|
+
options: [
|
|
394
|
+
{ label: "Gamma", description: "G" },
|
|
395
|
+
{ label: "Delta", description: "D" },
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
]);
|
|
399
|
+
|
|
400
|
+
assert.deepEqual(result.answers.first.answers, ["Alpha"]);
|
|
401
|
+
assert.deepEqual(result.answers.second.answers, ["Delta"]);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test("parseDiscordResponse handles multiple reactions for allowMultiple question", () => {
|
|
405
|
+
const result = parseDiscordResponse(
|
|
406
|
+
[{ emoji: "1️⃣", count: 1 }, { emoji: "3️⃣", count: 1 }],
|
|
407
|
+
null,
|
|
408
|
+
[{
|
|
409
|
+
id: "choice",
|
|
410
|
+
header: "Choice",
|
|
411
|
+
question: "Pick any",
|
|
412
|
+
allowMultiple: true,
|
|
413
|
+
options: [
|
|
414
|
+
{ label: "Alpha", description: "A" },
|
|
415
|
+
{ label: "Beta", description: "B" },
|
|
416
|
+
{ label: "Gamma", description: "G" },
|
|
417
|
+
],
|
|
418
|
+
}],
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
assert.deepEqual(result.answers.choice.answers, ["Alpha", "Gamma"]);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("DiscordAdapter source-level: acknowledgeAnswer method exists", () => {
|
|
425
|
+
const adapterSrc = readFileSync(
|
|
426
|
+
join(__dirname, "..", "..", "remote-questions", "discord-adapter.ts"),
|
|
427
|
+
"utf-8",
|
|
428
|
+
);
|
|
429
|
+
assert.ok(adapterSrc.includes("async acknowledgeAnswer"), "should have acknowledgeAnswer method");
|
|
430
|
+
assert.ok(adapterSrc.includes("✅"), "should use checkmark emoji for acknowledgement");
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test("SlackAdapter source-level: supports reaction polling and acknowledgement", () => {
|
|
434
|
+
const adapterSrc = readFileSync(
|
|
435
|
+
join(__dirname, "..", "..", "remote-questions", "slack-adapter.ts"),
|
|
436
|
+
"utf-8",
|
|
437
|
+
);
|
|
438
|
+
assert.ok(adapterSrc.includes("reactions.get"), "should poll Slack reactions");
|
|
439
|
+
assert.ok(adapterSrc.includes("reactions.add"), "should add Slack reactions");
|
|
440
|
+
assert.ok(adapterSrc.includes("async acknowledgeAnswer"), "should acknowledge Slack answers");
|
|
441
|
+
assert.ok(adapterSrc.includes("white_check_mark"), "should use a checkmark acknowledgement reaction");
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test("Slack setup source-level: offers channel picker with manual fallback", () => {
|
|
445
|
+
const commandSrc = readFileSync(
|
|
446
|
+
join(__dirname, "..", "..", "remote-questions", "remote-command.ts"),
|
|
447
|
+
"utf-8",
|
|
448
|
+
);
|
|
449
|
+
assert.ok(commandSrc.includes("users.conversations"), "Slack setup should query Slack channels");
|
|
450
|
+
assert.ok(commandSrc.includes("Select a Slack channel"), "Slack setup should present a channel picker");
|
|
451
|
+
assert.ok(commandSrc.includes("Enter channel ID manually"), "Slack setup should preserve manual fallback");
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
test("DiscordAdapter source-level: resolves guild ID for message URLs", () => {
|
|
455
|
+
const adapterSrc = readFileSync(
|
|
456
|
+
join(__dirname, "..", "..", "remote-questions", "discord-adapter.ts"),
|
|
457
|
+
"utf-8",
|
|
458
|
+
);
|
|
459
|
+
assert.ok(adapterSrc.includes("guildId"), "should track guild ID");
|
|
460
|
+
assert.ok(adapterSrc.includes("guild_id"), "should read guild_id from channel info");
|
|
461
|
+
assert.ok(
|
|
462
|
+
adapterSrc.includes("discord.com/channels/"),
|
|
463
|
+
"should construct message URL with guild/channel/message format",
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
468
|
+
// Telegram Tests
|
|
469
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
470
|
+
|
|
471
|
+
test("formatForTelegram single-question produces inline keyboard", () => {
|
|
472
|
+
const prompt = {
|
|
473
|
+
id: "tg-1",
|
|
474
|
+
channel: "telegram" as const,
|
|
475
|
+
createdAt: Date.now(),
|
|
476
|
+
timeoutAt: Date.now() + 60000,
|
|
477
|
+
pollIntervalMs: 5000,
|
|
478
|
+
questions: [{
|
|
479
|
+
id: "q1",
|
|
480
|
+
header: "Confirm",
|
|
481
|
+
question: "Proceed?",
|
|
482
|
+
options: [
|
|
483
|
+
{ label: "Yes", description: "Continue" },
|
|
484
|
+
{ label: "No", description: "Stop" },
|
|
485
|
+
],
|
|
486
|
+
allowMultiple: false,
|
|
487
|
+
}],
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const msg = formatForTelegram(prompt);
|
|
491
|
+
assert.equal(msg.parse_mode, "HTML");
|
|
492
|
+
assert.ok(msg.text.includes("<b>GSD needs your input</b>"));
|
|
493
|
+
assert.ok(msg.text.includes("<b>Confirm</b>"));
|
|
494
|
+
assert.ok(msg.reply_markup, "single-question should have inline keyboard");
|
|
495
|
+
assert.equal(msg.reply_markup!.inline_keyboard.length, 2, "should have 2 button rows");
|
|
496
|
+
assert.equal(msg.reply_markup!.inline_keyboard[0][0].callback_data, "tg-1:0");
|
|
497
|
+
assert.equal(msg.reply_markup!.inline_keyboard[1][0].callback_data, "tg-1:1");
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test("formatForTelegram multi-question omits inline keyboard", () => {
|
|
501
|
+
const prompt = {
|
|
502
|
+
id: "tg-2",
|
|
503
|
+
channel: "telegram" as const,
|
|
504
|
+
createdAt: Date.now(),
|
|
505
|
+
timeoutAt: Date.now() + 60000,
|
|
506
|
+
pollIntervalMs: 5000,
|
|
507
|
+
questions: [
|
|
508
|
+
{
|
|
509
|
+
id: "q1",
|
|
510
|
+
header: "First",
|
|
511
|
+
question: "Pick",
|
|
512
|
+
options: [{ label: "A", description: "a" }],
|
|
513
|
+
allowMultiple: false,
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
id: "q2",
|
|
517
|
+
header: "Second",
|
|
518
|
+
question: "Pick",
|
|
519
|
+
options: [{ label: "B", description: "b" }],
|
|
520
|
+
allowMultiple: false,
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const msg = formatForTelegram(prompt);
|
|
526
|
+
assert.equal(msg.reply_markup, undefined, "multi-question should not have inline keyboard");
|
|
527
|
+
assert.ok(msg.text.includes("1/2"), "should show question position");
|
|
528
|
+
assert.ok(msg.text.includes("2/2"), "should show question position");
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("formatForTelegram escapes HTML in user content", () => {
|
|
532
|
+
const prompt = {
|
|
533
|
+
id: "tg-3",
|
|
534
|
+
channel: "telegram" as const,
|
|
535
|
+
createdAt: Date.now(),
|
|
536
|
+
timeoutAt: Date.now() + 60000,
|
|
537
|
+
pollIntervalMs: 5000,
|
|
538
|
+
questions: [{
|
|
539
|
+
id: "q1",
|
|
540
|
+
header: "Test <script>",
|
|
541
|
+
question: "Is 5 > 3 & 2 < 4?",
|
|
542
|
+
options: [{ label: "<b>Yes</b>", description: "it's true" }],
|
|
543
|
+
allowMultiple: false,
|
|
544
|
+
}],
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const msg = formatForTelegram(prompt);
|
|
548
|
+
assert.ok(msg.text.includes("<script>"), "should escape < > in header");
|
|
549
|
+
assert.ok(msg.text.includes("5 > 3 & 2 < 4"), "should escape in question");
|
|
550
|
+
assert.ok(msg.text.includes("<b>Yes</b>"), "should escape in option label");
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("parseTelegramResponse handles callback_data button press", () => {
|
|
554
|
+
const questions = [{
|
|
555
|
+
id: "choice",
|
|
556
|
+
header: "Pick",
|
|
557
|
+
question: "Choose",
|
|
558
|
+
allowMultiple: false,
|
|
559
|
+
options: [
|
|
560
|
+
{ label: "Alpha", description: "A" },
|
|
561
|
+
{ label: "Beta", description: "B" },
|
|
562
|
+
],
|
|
563
|
+
}];
|
|
564
|
+
|
|
565
|
+
const result = parseTelegramResponse("prompt-123:1", null, questions, "prompt-123");
|
|
566
|
+
assert.deepEqual(result, { answers: { choice: { answers: ["Beta"] } } });
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test("parseTelegramResponse handles text reply delegation", () => {
|
|
570
|
+
const questions = [{
|
|
571
|
+
id: "choice",
|
|
572
|
+
header: "Pick",
|
|
573
|
+
question: "Choose",
|
|
574
|
+
allowMultiple: false,
|
|
575
|
+
options: [
|
|
576
|
+
{ label: "Alpha", description: "A" },
|
|
577
|
+
{ label: "Beta", description: "B" },
|
|
578
|
+
],
|
|
579
|
+
}];
|
|
580
|
+
|
|
581
|
+
const result = parseTelegramResponse(null, "1", questions, "prompt-123");
|
|
582
|
+
assert.deepEqual(result, { answers: { choice: { answers: ["Alpha"] } } });
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
test("parseTelegramResponse handles multi-question semicolons", () => {
|
|
586
|
+
const questions = [
|
|
587
|
+
{
|
|
588
|
+
id: "first",
|
|
589
|
+
header: "First",
|
|
590
|
+
question: "Pick",
|
|
591
|
+
allowMultiple: false,
|
|
592
|
+
options: [
|
|
593
|
+
{ label: "Alpha", description: "A" },
|
|
594
|
+
{ label: "Beta", description: "B" },
|
|
595
|
+
],
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
id: "second",
|
|
599
|
+
header: "Second",
|
|
600
|
+
question: "Pick",
|
|
601
|
+
allowMultiple: false,
|
|
602
|
+
options: [
|
|
603
|
+
{ label: "Gamma", description: "G" },
|
|
604
|
+
{ label: "Delta", description: "D" },
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
];
|
|
608
|
+
|
|
609
|
+
const result = parseTelegramResponse(null, "2;1", questions, "prompt-123");
|
|
610
|
+
assert.deepEqual(result.answers.first.answers, ["Beta"]);
|
|
611
|
+
assert.deepEqual(result.answers.second.answers, ["Gamma"]);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
test("isValidChannelId validates Telegram chat IDs", () => {
|
|
615
|
+
// Valid positive ID
|
|
616
|
+
assert.equal(isValidChannelId("telegram", "12345"), true);
|
|
617
|
+
// Valid negative group ID
|
|
618
|
+
assert.equal(isValidChannelId("telegram", "-1001234567890"), true);
|
|
619
|
+
// Too short
|
|
620
|
+
assert.equal(isValidChannelId("telegram", "1234"), false);
|
|
621
|
+
// Non-numeric
|
|
622
|
+
assert.equal(isValidChannelId("telegram", "abc12345"), false);
|
|
623
|
+
// URL injection
|
|
624
|
+
assert.equal(isValidChannelId("telegram", "https://evil.com"), false);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
test("sanitizeError strips Telegram bot token patterns", () => {
|
|
628
|
+
const fakeToken = "1234567890:ABCdefGHIjklMNOpqrSTUvwxyz12345678";
|
|
629
|
+
const result = sanitizeError(`Token: ${fakeToken}`);
|
|
630
|
+
assert.ok(!result.includes("1234567890:ABC"), "should strip Telegram bot token");
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
test("DiscordAdapter source-level: sendPrompt sets threadUrl in ref", () => {
|
|
634
|
+
const adapterSrc = readFileSync(
|
|
635
|
+
join(__dirname, "..", "..", "remote-questions", "discord-adapter.ts"),
|
|
636
|
+
"utf-8",
|
|
637
|
+
);
|
|
638
|
+
assert.ok(
|
|
639
|
+
adapterSrc.includes("threadUrl: messageUrl"),
|
|
640
|
+
"sendPrompt should set threadUrl to the constructed message URL",
|
|
641
|
+
);
|
|
642
|
+
});
|
|
@@ -1,35 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
// Only rewrites relative imports from our own source files — not from node_modules.
|
|
3
|
-
//
|
|
4
|
-
// Handles two patterns:
|
|
5
|
-
// 1. .js → .ts (pi bundler convention: source files use .js specifiers)
|
|
6
|
-
// 2. extensionless → .ts (some source files omit extensions in relative imports)
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const isFromNodeModules = parentURL.includes('/node_modules/');
|
|
11
|
-
const isFromPackages = parentURL.includes('/packages/');
|
|
12
|
-
|
|
13
|
-
if (!isFromNodeModules && !isFromPackages && !specifier.startsWith('node:')) {
|
|
14
|
-
// Rewrite .js → .ts
|
|
15
|
-
if (specifier.endsWith('.js')) {
|
|
16
|
-
const tsSpecifier = specifier.replace(/\.js$/, '.ts');
|
|
17
|
-
try {
|
|
18
|
-
return nextResolve(tsSpecifier, context);
|
|
19
|
-
} catch {
|
|
20
|
-
// fall through to default resolution
|
|
21
|
-
}
|
|
22
|
-
}
|
|
3
|
+
const ROOT = new URL("../../../../../", import.meta.url);
|
|
4
|
+
const PACKAGES_ROOT = fileURLToPath(new URL("packages/", ROOT));
|
|
23
5
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
6
|
+
export function resolve(specifier, context, nextResolve) {
|
|
7
|
+
let tsSpecifier = specifier;
|
|
8
|
+
if (specifier.includes('@gsd/')) {
|
|
9
|
+
tsSpecifier = specifier.replace('@gsd/', PACKAGES_ROOT).replace('/dist/', '/src/');
|
|
10
|
+
if (tsSpecifier.includes('/packages/pi-ai') && !tsSpecifier.endsWith('.ts')) {
|
|
11
|
+
tsSpecifier = tsSpecifier.replace(/\/packages\/pi-ai$/, '/packages/pi-ai/src/index.ts');
|
|
12
|
+
} else if (!tsSpecifier.includes('/src/') && !tsSpecifier.endsWith('.ts')) {
|
|
13
|
+
// Fallback for other gsd packages like pi-coding-agent, pi-tui, pi-agent-core
|
|
14
|
+
tsSpecifier = tsSpecifier.replace(/\/packages\/([^\/]+)$/, '/packages/$1/src/index.ts');
|
|
15
|
+
} else if (!tsSpecifier.endsWith('.ts') && !tsSpecifier.endsWith('.js') && !tsSpecifier.endsWith('.mjs')) {
|
|
16
|
+
tsSpecifier += '/index.ts';
|
|
31
17
|
}
|
|
18
|
+
} else if (specifier.endsWith('.js')) {
|
|
19
|
+
tsSpecifier = specifier.replace(/\.js$/, '.ts');
|
|
32
20
|
}
|
|
33
21
|
|
|
34
|
-
return nextResolve(
|
|
22
|
+
return nextResolve(tsSpecifier, context);
|
|
35
23
|
}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
// Custom ESM resolver: rewrites .js imports to .ts for node --test with TypeScript sources.
|
|
2
|
-
// Usage: node --import ./agent/extensions/gsd/tests/resolve-ts.mjs --test ...
|
|
3
|
-
//
|
|
4
|
-
// This is needed because pi extension source files use .js import specifiers
|
|
5
|
-
// (the pi runtime bundler convention), but only .ts files exist on disk.
|
|
6
|
-
// Node's built-in TypeScript support strips types but doesn't rewrite specifiers.
|
|
7
|
-
|
|
8
1
|
import { register } from 'node:module';
|
|
9
2
|
import { pathToFileURL } from 'node:url';
|
|
10
3
|
|
|
11
|
-
|
|
4
|
+
// Register hook to redirect imports to the dist directory
|
|
5
|
+
register(new URL('./dist-redirect.mjs', import.meta.url), pathToFileURL('./'));
|