supipowers 1.5.3 → 2.0.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 +14 -8
- package/bin/install.mjs +20 -5
- package/bin/install.ts +95 -0
- package/package.json +8 -4
- package/skills/context-mode/SKILL.md +17 -10
- package/skills/harness/SKILL.md +94 -0
- package/skills/ui-design/SKILL.md +63 -0
- package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
- package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
- package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
- package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
- package/skills/ultraplan-discover/SKILL.md +96 -0
- package/skills/ultraplan-intake/SKILL.md +89 -0
- package/skills/ultraplan-research/SKILL.md +129 -0
- package/skills/ultraplan-review/SKILL.md +86 -0
- package/skills/ultraplan-review-scope/SKILL.md +111 -0
- package/skills/ultraplan-review-structure/SKILL.md +120 -0
- package/skills/ultraplan-review-tdd/SKILL.md +142 -0
- package/skills/ultraplan-scout/SKILL.md +110 -0
- package/skills/ultraplan-synthesize/SKILL.md +124 -0
- package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
- package/src/ai/schema-text.ts +129 -0
- package/src/ai/structured-output.ts +274 -0
- package/src/ai/template.ts +27 -0
- package/src/bootstrap.ts +63 -28
- package/src/commands/agents.ts +131 -42
- package/src/commands/ai-review.ts +251 -30
- package/src/commands/clear.ts +434 -0
- package/src/commands/commit.ts +1 -0
- package/src/commands/config.ts +242 -44
- package/src/commands/context.ts +55 -28
- package/src/commands/doctor.ts +234 -6
- package/src/commands/fix-pr.ts +306 -131
- package/src/commands/generate.ts +111 -21
- package/src/commands/memory.ts +192 -0
- package/src/commands/model-picker.ts +28 -21
- package/src/commands/model.ts +18 -8
- package/src/commands/optimize-context.ts +408 -29
- package/src/commands/plan.ts +2 -0
- package/src/commands/qa.ts +312 -137
- package/src/commands/release.ts +259 -76
- package/src/commands/review.ts +293 -59
- package/src/commands/status.ts +200 -13
- package/src/commands/supi.ts +3 -35
- package/src/commands/ui-design.ts +394 -0
- package/src/commands/ultraplan.ts +1518 -0
- package/src/commands/update.ts +86 -0
- package/src/config/defaults.ts +62 -0
- package/src/config/loader.ts +448 -60
- package/src/config/schema.ts +108 -2
- package/src/context/optimizer.ts +25 -33
- package/src/context/rule-renderer.ts +223 -0
- package/src/context/savings.ts +258 -0
- package/src/context/startup-check.ts +380 -0
- package/src/context/startup-optimizer.ts +355 -0
- package/src/context/tokenignore.ts +146 -0
- package/src/context-mode/cache-handle.ts +49 -0
- package/src/context-mode/cache-preview.ts +71 -0
- package/src/context-mode/cache-store.ts +738 -0
- package/src/context-mode/compressor.ts +131 -26
- package/src/context-mode/dedup.ts +108 -0
- package/src/context-mode/detector.ts +35 -4
- package/src/context-mode/event-extractor.ts +14 -12
- package/src/context-mode/event-store.ts +91 -36
- package/src/context-mode/hooks.ts +798 -56
- package/src/context-mode/knowledge/store.ts +255 -11
- package/src/context-mode/memory-store.ts +325 -0
- package/src/context-mode/metrics-recorder.ts +158 -0
- package/src/context-mode/metrics-store.ts +765 -0
- package/src/context-mode/model.ts +24 -0
- package/src/context-mode/processor-keys.ts +29 -0
- package/src/context-mode/processors/build.ts +66 -0
- package/src/context-mode/processors/docker.ts +57 -0
- package/src/context-mode/processors/git.ts +111 -0
- package/src/context-mode/processors/json.ts +112 -0
- package/src/context-mode/processors/k8s.ts +67 -0
- package/src/context-mode/processors/lint.ts +67 -0
- package/src/context-mode/processors/log.ts +86 -0
- package/src/context-mode/processors/registry.ts +116 -0
- package/src/context-mode/processors/test-runner.ts +102 -0
- package/src/context-mode/processors/types.ts +20 -0
- package/src/context-mode/repomap.ts +400 -0
- package/src/context-mode/routing.ts +97 -24
- package/src/context-mode/sandbox/runners.ts +5 -1
- package/src/context-mode/snapshot-builder.ts +106 -11
- package/src/context-mode/source-hash.ts +173 -0
- package/src/context-mode/tool-name.ts +11 -0
- package/src/context-mode/tools.ts +654 -22
- package/src/context-mode/web/fetcher.ts +31 -12
- package/src/debug/logger.ts +2 -1
- package/src/deps/registry.ts +1 -1
- package/src/discipline/failure-summarizer.ts +170 -0
- package/src/discipline/failure-taxonomy.ts +131 -0
- package/src/discipline/workflow-invariants.ts +125 -0
- package/src/discovery/index.ts +31 -0
- package/src/discovery/lsp.ts +87 -0
- package/src/discovery/rank.ts +144 -0
- package/src/discovery/sources.ts +89 -0
- package/src/discovery/workflow.ts +87 -0
- package/src/docs/contracts.ts +39 -0
- package/src/docs/drift.ts +117 -87
- package/src/fix-pr/assessment.ts +200 -0
- package/src/fix-pr/contracts.ts +47 -0
- package/src/fix-pr/fetch-comments.ts +80 -0
- package/src/fix-pr/prompt-builder.ts +58 -40
- package/src/fix-pr/scripts/exec.ts +34 -0
- package/src/fix-pr/scripts/trigger-review.ts +106 -0
- package/src/fix-pr/scripts/wait-and-check.ts +108 -0
- package/src/fix-pr/types.ts +4 -0
- package/src/git/branch-finish.ts +5 -0
- package/src/git/commit-contract.ts +83 -0
- package/src/git/commit.ts +121 -184
- package/src/git/status.ts +62 -8
- package/src/harness/anti_slop/architecture-parser.ts +210 -0
- package/src/harness/anti_slop/backend-factory.ts +30 -0
- package/src/harness/anti_slop/backend.ts +140 -0
- package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
- package/src/harness/anti_slop/fallow-adapter.ts +305 -0
- package/src/harness/anti_slop/installer.ts +227 -0
- package/src/harness/anti_slop/queue.ts +216 -0
- package/src/harness/anti_slop/recommend.ts +84 -0
- package/src/harness/anti_slop/score.ts +180 -0
- package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
- package/src/harness/artifacts/agents-md.ts +88 -0
- package/src/harness/artifacts/checks-wiring.ts +57 -0
- package/src/harness/artifacts/docs-tree.ts +79 -0
- package/src/harness/artifacts/lint-configs.ts +136 -0
- package/src/harness/artifacts/review-agents.ts +67 -0
- package/src/harness/bare-entry.ts +108 -0
- package/src/harness/command.ts +1010 -0
- package/src/harness/default-agents/design.md +23 -0
- package/src/harness/default-agents/discover.md +18 -0
- package/src/harness/default-agents/implement.md +24 -0
- package/src/harness/default-agents/plan.md +19 -0
- package/src/harness/default-agents/research.md +21 -0
- package/src/harness/default-agents/validate.md +22 -0
- package/src/harness/gc/reporter.ts +28 -0
- package/src/harness/gc/runner.ts +136 -0
- package/src/harness/hooks/layer-context-inject.ts +155 -0
- package/src/harness/hooks/post-session-sweep.ts +130 -0
- package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
- package/src/harness/hooks/register.ts +118 -0
- package/src/harness/model.ts +117 -0
- package/src/harness/pipeline.ts +348 -0
- package/src/harness/project-paths.ts +235 -0
- package/src/harness/stage-runner.ts +107 -0
- package/src/harness/stages/design.ts +386 -0
- package/src/harness/stages/discover.ts +454 -0
- package/src/harness/stages/implement.ts +162 -0
- package/src/harness/stages/plan.ts +335 -0
- package/src/harness/stages/research.ts +263 -0
- package/src/harness/stages/validate.ts +684 -0
- package/src/harness/storage.ts +467 -0
- package/src/harness/tools.ts +426 -0
- package/src/lsp/bridge.ts +56 -95
- package/src/lsp/capabilities.ts +108 -0
- package/src/lsp/contracts.ts +35 -0
- package/src/lsp/detector.ts +8 -12
- package/src/markdown-frontmatter.ts +68 -0
- package/src/mempalace/bridge.ts +129 -0
- package/src/mempalace/config.ts +75 -0
- package/src/mempalace/format.ts +163 -0
- package/src/mempalace/hooks.ts +370 -0
- package/src/mempalace/installer-helper.ts +194 -0
- package/src/mempalace/python/mempalace_bridge.py +440 -0
- package/src/mempalace/runtime.ts +565 -0
- package/src/mempalace/schema.ts +264 -0
- package/src/mempalace/session-summary.ts +198 -0
- package/src/mempalace/tool.ts +186 -0
- package/src/mempalace/uv.ts +256 -0
- package/src/migrate/runner.ts +354 -0
- package/src/planning/approval-flow.ts +206 -9
- package/src/planning/plan-writer-prompt.ts +4 -3
- package/src/planning/planning-ask-tool.ts +39 -0
- package/src/planning/render-markdown.ts +74 -0
- package/src/planning/spec.ts +42 -0
- package/src/planning/system-prompt.ts +11 -8
- package/src/planning/validate.ts +84 -0
- package/src/platform/omp.ts +15 -2
- package/src/platform/system-prompt.ts +37 -0
- package/src/platform/test-utils.ts +3 -0
- package/src/platform/types.ts +6 -1
- package/src/qa/config.ts +12 -6
- package/src/qa/detect-app-type.ts +13 -6
- package/src/qa/matrix.ts +12 -6
- package/src/qa/prompt-builder.ts +28 -30
- package/src/qa/scripts/dev-server-utils.ts +72 -0
- package/src/qa/scripts/run-e2e-tests.ts +226 -0
- package/src/qa/scripts/start-dev-server.ts +138 -0
- package/src/qa/scripts/stop-dev-server.ts +77 -0
- package/src/qa/session.ts +13 -7
- package/src/quality/ai-setup.ts +27 -25
- package/src/quality/contracts.ts +34 -0
- package/src/quality/gates/ai-review.ts +20 -58
- package/src/quality/gates/command.ts +249 -46
- package/src/quality/review-gates.ts +18 -2
- package/src/quality/runner.ts +63 -22
- package/src/quality/schemas.ts +37 -2
- package/src/quality/setup.ts +96 -16
- package/src/release/changelog.ts +1 -1
- package/src/release/channels/custom.ts +13 -3
- package/src/release/channels/types.ts +5 -0
- package/src/release/contracts.ts +90 -0
- package/src/release/executor.ts +122 -45
- package/src/release/prompt.ts +18 -2
- package/src/release/targets.ts +86 -0
- package/src/release/version.ts +96 -71
- package/src/review/agent-loader.ts +221 -109
- package/src/review/fixer.ts +10 -6
- package/src/review/multi-agent-runner.ts +114 -13
- package/src/review/output.ts +12 -139
- package/src/review/runner.ts +12 -6
- package/src/review/scope.ts +144 -24
- package/src/review/types.ts +1 -20
- package/src/review/validator.ts +12 -6
- package/src/storage/fix-pr-sessions.ts +21 -14
- package/src/storage/plans.ts +14 -5
- package/src/storage/qa-sessions.ts +25 -19
- package/src/storage/reliability-metrics.ts +180 -0
- package/src/storage/reports.ts +8 -7
- package/src/storage/review-sessions.ts +55 -20
- package/src/tool-catalog/active-tool-controller.ts +164 -0
- package/src/tool-catalog/active-tool-planner.ts +212 -0
- package/src/tool-catalog/tool-groups.ts +102 -0
- package/src/types.ts +1399 -5
- package/src/ui-design/backend-adapter.ts +78 -0
- package/src/ui-design/backends/local-html.ts +82 -0
- package/src/ui-design/backends/pencil-mcp.ts +111 -0
- package/src/ui-design/components-scanner.ts +124 -0
- package/src/ui-design/config.ts +55 -0
- package/src/ui-design/pen-scanner.ts +95 -0
- package/src/ui-design/pen-selector.ts +72 -0
- package/src/ui-design/prompt-builder.ts +73 -0
- package/src/ui-design/scanner.ts +136 -0
- package/src/ui-design/session.ts +974 -0
- package/src/ui-design/system-prompt.ts +312 -0
- package/src/ui-design/tokens-scanner.ts +181 -0
- package/src/ui-design/types.ts +96 -0
- package/src/ultraplan/agent-catalog.ts +522 -0
- package/src/ultraplan/authoring/agent-catalog.ts +310 -0
- package/src/ultraplan/authoring/authoring-tools.ts +552 -0
- package/src/ultraplan/authoring/command-handlers.ts +339 -0
- package/src/ultraplan/authoring/markdown.ts +510 -0
- package/src/ultraplan/authoring/model.ts +162 -0
- package/src/ultraplan/authoring/pipeline.ts +319 -0
- package/src/ultraplan/authoring/stage-runner.ts +141 -0
- package/src/ultraplan/authoring/stages/approve.ts +249 -0
- package/src/ultraplan/authoring/stages/discover.ts +289 -0
- package/src/ultraplan/authoring/stages/intake.ts +203 -0
- package/src/ultraplan/authoring/stages/research.ts +399 -0
- package/src/ultraplan/authoring/stages/review.ts +333 -0
- package/src/ultraplan/authoring/stages/scout.ts +188 -0
- package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
- package/src/ultraplan/authoring/storage.ts +594 -0
- package/src/ultraplan/authoring/synth-gate.ts +165 -0
- package/src/ultraplan/authoring-draft.ts +653 -0
- package/src/ultraplan/authoring-persist.ts +180 -0
- package/src/ultraplan/authoring-tool.ts +608 -0
- package/src/ultraplan/authoring-wizard.ts +587 -0
- package/src/ultraplan/batch/merge.ts +98 -0
- package/src/ultraplan/batch/planner.ts +150 -0
- package/src/ultraplan/batch/presenter.ts +97 -0
- package/src/ultraplan/batch/storage.ts +420 -0
- package/src/ultraplan/batch/supervisor.ts +317 -0
- package/src/ultraplan/batch/worker.ts +26 -0
- package/src/ultraplan/batch/worktree.ts +110 -0
- package/src/ultraplan/contracts.ts +1593 -0
- package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
- package/src/ultraplan/default-agents/authoring/intake.md +12 -0
- package/src/ultraplan/default-agents/authoring/planner.md +12 -0
- package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
- package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/scout.md +12 -0
- package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
- package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-executor.md +10 -0
- package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-tester.md +10 -0
- package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-executor.md +10 -0
- package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-tester.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
- package/src/ultraplan/execution/contract.ts +71 -0
- package/src/ultraplan/execution/policy.ts +217 -0
- package/src/ultraplan/execution/runtime-tools.ts +107 -0
- package/src/ultraplan/execution/session-runner.ts +281 -0
- package/src/ultraplan/next-router.ts +85 -0
- package/src/ultraplan/presenter.ts +359 -0
- package/src/ultraplan/project-paths.ts +342 -0
- package/src/ultraplan/runtime/active-execution.ts +72 -0
- package/src/ultraplan/runtime/apply-mutation.ts +416 -0
- package/src/ultraplan/runtime/blockers.ts +243 -0
- package/src/ultraplan/runtime/hook-bridge.ts +486 -0
- package/src/ultraplan/runtime/launch-context.ts +207 -0
- package/src/ultraplan/runtime/migration.ts +524 -0
- package/src/ultraplan/runtime/normalize.ts +281 -0
- package/src/ultraplan/runtime/proof.ts +260 -0
- package/src/ultraplan/runtime/reducer.ts +416 -0
- package/src/ultraplan/runtime/repair.ts +251 -0
- package/src/ultraplan/runtime/tracker-storage.ts +368 -0
- package/src/ultraplan/session-selection.ts +291 -0
- package/src/ultraplan/storage.ts +374 -0
- package/src/utils/editor.ts +38 -0
- package/src/utils/executable.ts +80 -0
- package/src/utils/paths.ts +1 -20
- package/src/utils/shell.ts +31 -0
- package/src/visual/companion.ts +2 -1
- package/src/visual/scripts/frame-template.html +60 -0
- package/src/visual/scripts/index.js +59 -13
- package/src/visual/scripts/package.json +3 -0
- package/src/visual/start-server.ts +2 -1
- package/src/workspace/git-scope.ts +64 -0
- package/src/workspace/locks.ts +23 -0
- package/src/workspace/package-manager.ts +117 -0
- package/src/workspace/path-mapping.ts +75 -0
- package/src/workspace/project-slug.ts +92 -0
- package/src/workspace/repo-root.ts +137 -0
- package/src/workspace/selector.ts +115 -0
- package/src/workspace/state-paths.ts +118 -0
- package/src/workspace/targets.ts +313 -0
- package/src/fix-pr/scripts/diff-comments.sh +0 -33
- package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
- package/src/fix-pr/scripts/trigger-review.sh +0 -36
- package/src/fix-pr/scripts/wait-and-check.sh +0 -37
- package/src/qa/scripts/detect-app-type.sh +0 -68
- package/src/qa/scripts/discover-routes.sh +0 -143
- package/src/qa/scripts/run-e2e-tests.sh +0 -131
- package/src/qa/scripts/start-dev-server.sh +0 -46
- package/src/qa/scripts/stop-dev-server.sh +0 -36
- package/src/review/prompts/fix-output-schema.md +0 -18
- package/src/review/prompts/review-output-schema.md +0 -38
- package/src/review/template.ts +0 -15
- /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// src/context-mode/compressor.ts
|
|
2
|
+
import { canonicalToolName } from "./tool-name.js";
|
|
3
|
+
import type { ContextModeProcessorsConfig } from "../types.js";
|
|
4
|
+
import type { ProcessorKey } from "./metrics-store.js";
|
|
5
|
+
import { processorKeyForTool } from "./processor-keys.js";
|
|
6
|
+
import { lookupProcessor } from "./processors/registry.js";
|
|
2
7
|
|
|
3
8
|
interface ToolResultEventLike {
|
|
4
9
|
toolName: string;
|
|
@@ -12,13 +17,53 @@ interface ToolResultEventResult {
|
|
|
12
17
|
content?: Array<{ type: string; text: string }>;
|
|
13
18
|
}
|
|
14
19
|
|
|
20
|
+
export interface RunEmissionPipelineOptions {
|
|
21
|
+
processors?: ContextModeProcessorsConfig;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PipelineResult {
|
|
25
|
+
result: ToolResultEventResult | undefined;
|
|
26
|
+
processorKey: ProcessorKey;
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
const BASH_HEAD_LINES = 5;
|
|
16
30
|
const BASH_TAIL_LINES = 10;
|
|
17
31
|
const READ_HEAD_LINES = 80;
|
|
18
32
|
const READ_TAIL_LINES = 30;
|
|
19
|
-
const
|
|
33
|
+
const SEARCH_MAX_MATCHES = 10;
|
|
20
34
|
const FIND_MAX_PATHS = 20;
|
|
21
35
|
|
|
36
|
+
|
|
37
|
+
const READ_PATH_SELECTOR_RE = /:(?:raw|\d+(?:[-+]\d+)?|L\d+(?:-L?\d+|\+L?\d+)?)$/i;
|
|
38
|
+
|
|
39
|
+
function getReadPath(input: Record<string, unknown>): string | null {
|
|
40
|
+
const path = input.path ?? input.file_path;
|
|
41
|
+
return typeof path === "string" && path.length > 0 ? path : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hasEmbeddedReadSelector(input: Record<string, unknown>): boolean {
|
|
45
|
+
const path = getReadPath(input);
|
|
46
|
+
return path != null && READ_PATH_SELECTOR_RE.test(path);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatReadSelectorHint(input: Record<string, unknown>, start: number, end: number): string {
|
|
50
|
+
const path = getReadPath(input);
|
|
51
|
+
const selector = path?.startsWith("http://") || path?.startsWith("https://")
|
|
52
|
+
? `L${start}-L${end}`
|
|
53
|
+
: `${start}-${end}`;
|
|
54
|
+
return path
|
|
55
|
+
? `read(path="${path}:${selector}")`
|
|
56
|
+
: `read(path="<path>:${selector}")`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* OMP's `shellMinimizer` already shrinks bash output and appends this footer
|
|
60
|
+
* pointing at the full bytes. When we see it, supipowers must NOT re-compress —
|
|
61
|
+
* doing so would either drop the pointer or double-truncate already-trimmed text.
|
|
62
|
+
* The artifact id is recoverable via `read artifact://<id>`.
|
|
63
|
+
*/
|
|
64
|
+
export const OMP_MINIMIZER_FOOTER_RE = /(?:^|\n)\[raw output: artifact:\/\/[a-zA-Z0-9_-]+\]\s*$/;
|
|
65
|
+
|
|
66
|
+
|
|
22
67
|
/** Measure total byte length of text content entries */
|
|
23
68
|
function measureTextBytes(content: Array<{ type: string; text?: string }>): number {
|
|
24
69
|
let total = 0;
|
|
@@ -43,6 +88,22 @@ function getCombinedText(content: Array<{ type: string; text?: string }>): strin
|
|
|
43
88
|
.join("\n");
|
|
44
89
|
}
|
|
45
90
|
|
|
91
|
+
function exitCodeFromDetails(details: unknown): number | null {
|
|
92
|
+
if (details && typeof details === "object" && "exitCode" in details) {
|
|
93
|
+
const exitCode = (details as { exitCode?: unknown }).exitCode;
|
|
94
|
+
return typeof exitCode === "number" ? exitCode : null;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function eolOf(text: string): "\n" | "\r\n" {
|
|
100
|
+
return text.includes("\r\n") ? "\r\n" : "\n";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function wrapText(text: string): ToolResultEventResult {
|
|
104
|
+
return { content: [{ type: "text", text }] };
|
|
105
|
+
}
|
|
106
|
+
|
|
46
107
|
/** Compress bash tool output */
|
|
47
108
|
function compressBash(text: string, details: unknown): string | undefined {
|
|
48
109
|
const exitCode =
|
|
@@ -50,6 +111,9 @@ function compressBash(text: string, details: unknown): string | undefined {
|
|
|
50
111
|
? (details as { exitCode: number }).exitCode
|
|
51
112
|
: 0;
|
|
52
113
|
|
|
114
|
+
// Already minimized by OMP — pass through to preserve the artifact pointer.
|
|
115
|
+
if (OMP_MINIMIZER_FOOTER_RE.test(text)) return undefined;
|
|
116
|
+
|
|
53
117
|
// Non-zero exit: keep full output for debugging
|
|
54
118
|
if (exitCode !== 0) return undefined;
|
|
55
119
|
|
|
@@ -71,8 +135,11 @@ function compressBash(text: string, details: unknown): string | undefined {
|
|
|
71
135
|
|
|
72
136
|
/** Compress read tool output — head+tail with hashline preservation */
|
|
73
137
|
function compressRead(text: string, input: Record<string, unknown>): string | undefined {
|
|
74
|
-
//
|
|
75
|
-
if (
|
|
138
|
+
// Already minimized by OMP (e.g. when reading a `bash-original` artifact back). Pass through.
|
|
139
|
+
if (OMP_MINIMIZER_FOOTER_RE.test(text)) return undefined;
|
|
140
|
+
|
|
141
|
+
// Scoped reads (offset/limit/path selector) are already targeted — pass through
|
|
142
|
+
if (input.offset != null || input.limit != null || hasEmbeddedReadSelector(input)) return undefined;
|
|
76
143
|
|
|
77
144
|
const lines = text.split("\n");
|
|
78
145
|
const totalLines = lines.length;
|
|
@@ -84,26 +151,27 @@ function compressRead(text: string, input: Record<string, unknown>): string | un
|
|
|
84
151
|
const omittedStart = READ_HEAD_LINES + 1;
|
|
85
152
|
const omittedEnd = totalLines - READ_TAIL_LINES;
|
|
86
153
|
|
|
154
|
+
const selectorHint = formatReadSelectorHint(input, omittedStart, omittedEnd);
|
|
87
155
|
return [
|
|
88
156
|
...head,
|
|
89
|
-
`[...${omittedEnd - omittedStart + 1} lines omitted. Use
|
|
157
|
+
`[...${omittedEnd - omittedStart + 1} lines omitted. Use ${selectorHint} to view...]`,
|
|
90
158
|
...tail,
|
|
91
159
|
].join("\n");
|
|
92
160
|
}
|
|
93
161
|
|
|
94
|
-
/** Compress
|
|
95
|
-
function
|
|
162
|
+
/** Compress search tool output */
|
|
163
|
+
function compressSearch(text: string): string | undefined {
|
|
96
164
|
const lines = text.split("\n").filter((l) => l.length > 0);
|
|
97
165
|
const totalMatches = lines.length;
|
|
98
166
|
|
|
99
|
-
if (totalMatches <=
|
|
167
|
+
if (totalMatches <= SEARCH_MAX_MATCHES) return undefined;
|
|
100
168
|
|
|
101
|
-
const kept = lines.slice(0,
|
|
169
|
+
const kept = lines.slice(0, SEARCH_MAX_MATCHES);
|
|
102
170
|
return [
|
|
103
|
-
`${totalMatches} matches total, showing first ${
|
|
171
|
+
`${totalMatches} matches total, showing first ${SEARCH_MAX_MATCHES}:`,
|
|
104
172
|
"",
|
|
105
173
|
...kept,
|
|
106
|
-
`[...compressed: ${totalMatches -
|
|
174
|
+
`[...compressed: ${totalMatches - SEARCH_MAX_MATCHES} more matches omitted...]`,
|
|
107
175
|
].join("\n");
|
|
108
176
|
}
|
|
109
177
|
|
|
@@ -123,45 +191,82 @@ function compressFind(text: string): string | undefined {
|
|
|
123
191
|
].join("\n");
|
|
124
192
|
}
|
|
125
193
|
|
|
126
|
-
/**
|
|
127
|
-
export function
|
|
194
|
+
/** Run the deterministic emission pipeline and report the processor decision. */
|
|
195
|
+
export function runEmissionPipeline(
|
|
128
196
|
event: ToolResultEventLike,
|
|
129
197
|
threshold: number,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
198
|
+
options: RunEmissionPipelineOptions = {},
|
|
199
|
+
): PipelineResult {
|
|
200
|
+
const canonicalTool = canonicalToolName(event.toolName);
|
|
201
|
+
const nativeProcessorKey = processorKeyForTool(canonicalTool);
|
|
202
|
+
const passthroughProcessorKey: ProcessorKey = nativeProcessorKey === null ? null : "passthrough";
|
|
203
|
+
|
|
204
|
+
// General rules: pass through errors, non-text content, and small outputs.
|
|
205
|
+
if (event.isError) return { result: undefined, processorKey: passthroughProcessorKey };
|
|
206
|
+
if (hasNonTextContent(event.content)) return { result: undefined, processorKey: passthroughProcessorKey };
|
|
207
|
+
|
|
208
|
+
const byteSize = measureTextBytes(event.content);
|
|
209
|
+
if (byteSize <= threshold) {
|
|
210
|
+
return {
|
|
211
|
+
result: undefined,
|
|
212
|
+
processorKey: passthroughProcessorKey,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
135
215
|
|
|
136
216
|
const text = getCombinedText(event.content);
|
|
137
|
-
let compressed: string | undefined;
|
|
138
217
|
|
|
139
|
-
|
|
218
|
+
if (canonicalTool === "bash" && OMP_MINIMIZER_FOOTER_RE.test(text)) {
|
|
219
|
+
return { result: undefined, processorKey: "omp-minimizer" };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (canonicalTool === "bash") {
|
|
223
|
+
const match = lookupProcessor(canonicalTool, event.input, text, options);
|
|
224
|
+
if (match) {
|
|
225
|
+
const output = match.processor(text, {
|
|
226
|
+
exitCode: exitCodeFromDetails(event.details),
|
|
227
|
+
eol: eolOf(text),
|
|
228
|
+
});
|
|
229
|
+
if (!output.passthrough) {
|
|
230
|
+
return { result: wrapText(output.text), processorKey: output.processorKey };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let compressed: string | undefined;
|
|
236
|
+
switch (canonicalTool) {
|
|
140
237
|
case "bash":
|
|
141
238
|
compressed = compressBash(text, event.details);
|
|
142
239
|
break;
|
|
143
240
|
case "read":
|
|
144
241
|
compressed = compressRead(text, event.input);
|
|
145
242
|
break;
|
|
146
|
-
case "
|
|
147
|
-
compressed =
|
|
243
|
+
case "search":
|
|
244
|
+
compressed = compressSearch(text);
|
|
148
245
|
break;
|
|
149
246
|
case "find":
|
|
150
247
|
compressed = compressFind(text);
|
|
151
248
|
break;
|
|
152
249
|
default:
|
|
153
|
-
return undefined;
|
|
250
|
+
return { result: undefined, processorKey: null };
|
|
154
251
|
}
|
|
155
252
|
|
|
156
|
-
if (!compressed) return undefined;
|
|
157
|
-
return {
|
|
253
|
+
if (!compressed) return { result: undefined, processorKey: "passthrough" };
|
|
254
|
+
return { result: wrapText(compressed), processorKey: nativeProcessorKey };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Compress a tool result if it exceeds the threshold */
|
|
258
|
+
export function compressToolResult(
|
|
259
|
+
event: ToolResultEventLike,
|
|
260
|
+
threshold: number,
|
|
261
|
+
): ToolResultEventResult | undefined {
|
|
262
|
+
return runEmissionPipeline(event, threshold).result;
|
|
158
263
|
}
|
|
159
264
|
|
|
160
265
|
/** Summarization prompt templates by tool type */
|
|
161
266
|
const SUMMARIZE_PROMPTS: Record<string, string> = {
|
|
162
267
|
bash: "Summarize this command output. Preserve: exit code, key findings, error messages, file paths mentioned. Be concise (under 200 words).",
|
|
163
268
|
read: "Summarize this file content. Preserve: file structure, key exports/functions, notable patterns. Be concise (under 200 words).",
|
|
164
|
-
|
|
269
|
+
search: "Summarize these search results. Preserve: match count, most relevant matches, file distribution. Be concise (under 200 words).",
|
|
165
270
|
find: "Summarize these file paths. Preserve: directory structure, file count, key patterns. Be concise (under 200 words).",
|
|
166
271
|
};
|
|
167
272
|
|
|
@@ -187,7 +292,7 @@ export async function compressToolResultWithLLM(
|
|
|
187
292
|
|
|
188
293
|
// Above LLM threshold: try LLM summarization
|
|
189
294
|
try {
|
|
190
|
-
const prompt = SUMMARIZE_PROMPTS[event.toolName] ?? "Summarize this output concisely (under 200 words).";
|
|
295
|
+
const prompt = SUMMARIZE_PROMPTS[canonicalToolName(event.toolName)] ?? "Summarize this output concisely (under 200 words).";
|
|
191
296
|
const summary = await summarize(`${prompt}\n\n${text}`, event.toolName);
|
|
192
297
|
|
|
193
298
|
// Validate: non-empty and reasonably sized
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { ProcessorKey } from "./metrics-store.js";
|
|
3
|
+
|
|
4
|
+
export interface DedupRecord {
|
|
5
|
+
contentHash: string;
|
|
6
|
+
turnId: number;
|
|
7
|
+
bytes: number;
|
|
8
|
+
tsMonotonic: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface DedupState {
|
|
12
|
+
records: Map<string, DedupRecord>;
|
|
13
|
+
turnCounter: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ToolResultEventResult {
|
|
17
|
+
content?: Array<{ type: string; text: string }>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DedupSubstitutionInput {
|
|
21
|
+
result: ToolResultEventResult | undefined;
|
|
22
|
+
processorKey: ProcessorKey;
|
|
23
|
+
sourceHash: string | null;
|
|
24
|
+
dedupState: DedupState;
|
|
25
|
+
processedBytes: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DedupSubstitutionResult {
|
|
29
|
+
result: ToolResultEventResult | undefined;
|
|
30
|
+
processorKey: ProcessorKey;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const TTL_TURNS = 10;
|
|
34
|
+
|
|
35
|
+
export function createDedupState(): DedupState {
|
|
36
|
+
return { records: new Map(), turnCounter: 0 };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function combinedTextOf(
|
|
40
|
+
content: Array<{ type: string; text?: string }> | undefined,
|
|
41
|
+
): string {
|
|
42
|
+
if (!content) return "";
|
|
43
|
+
const textEntries: string[] = [];
|
|
44
|
+
for (const entry of content) {
|
|
45
|
+
if (entry.type === "text" && entry.text) {
|
|
46
|
+
textEntries.push(entry.text);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return textEntries.join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function contentHashFor(result: ToolResultEventResult): string {
|
|
53
|
+
return createHash("sha256").update(combinedTextOf(result.content)).digest("hex");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function maybeSubstitute(input: DedupSubstitutionInput): DedupSubstitutionResult {
|
|
57
|
+
const { result, processorKey, sourceHash, dedupState, processedBytes } = input;
|
|
58
|
+
if (sourceHash === null || result === undefined) {
|
|
59
|
+
return { result, processorKey };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const key = sourceHash;
|
|
63
|
+
const contentHash = contentHashFor(result);
|
|
64
|
+
const existing = dedupState.records.get(key);
|
|
65
|
+
if (!existing || existing.contentHash !== contentHash) {
|
|
66
|
+
const supersedes = existing && existing.contentHash !== contentHash ? existing : null;
|
|
67
|
+
dedupState.turnCounter += 1;
|
|
68
|
+
dedupState.records.set(key, {
|
|
69
|
+
contentHash,
|
|
70
|
+
turnId: dedupState.turnCounter,
|
|
71
|
+
bytes: processedBytes,
|
|
72
|
+
tsMonotonic: dedupState.turnCounter,
|
|
73
|
+
});
|
|
74
|
+
if (supersedes) {
|
|
75
|
+
const annotated = withSupersedesAnnotation(result, supersedes);
|
|
76
|
+
return { result: annotated, processorKey };
|
|
77
|
+
}
|
|
78
|
+
return { result, processorKey };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (dedupState.turnCounter - existing.tsMonotonic >= TTL_TURNS) {
|
|
82
|
+
dedupState.turnCounter += 1;
|
|
83
|
+
dedupState.records.set(key, {
|
|
84
|
+
contentHash,
|
|
85
|
+
turnId: dedupState.turnCounter,
|
|
86
|
+
bytes: processedBytes,
|
|
87
|
+
tsMonotonic: dedupState.turnCounter,
|
|
88
|
+
});
|
|
89
|
+
return { result, processorKey };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const placeholderText = `[…dedup: same as turn ${existing.turnId} (${existing.bytes} B); processor=${processorKey}]`;
|
|
93
|
+
return {
|
|
94
|
+
result: { content: [{ type: "text", text: placeholderText }] },
|
|
95
|
+
processorKey: "dedup",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function withSupersedesAnnotation(
|
|
100
|
+
result: ToolResultEventResult,
|
|
101
|
+
prior: DedupRecord,
|
|
102
|
+
): ToolResultEventResult {
|
|
103
|
+
const banner = `[\u2026 supersedes turn ${prior.turnId} (was ${prior.bytes} B); old bytes remain in transcript history]`;
|
|
104
|
+
const text = combinedTextOf(result.content);
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text", text: text.length > 0 ? `${banner}\n${text}` : banner }],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -10,15 +10,41 @@ export interface ContextModeStatus {
|
|
|
10
10
|
ctxIndex: boolean;
|
|
11
11
|
ctxSearch: boolean;
|
|
12
12
|
ctxFetchAndIndex: boolean;
|
|
13
|
+
ctxOpenCached: boolean;
|
|
14
|
+
ctxStats: boolean;
|
|
15
|
+
ctxPurge: boolean;
|
|
16
|
+
ctxRepomap: boolean;
|
|
17
|
+
ctxSymbol: boolean;
|
|
13
18
|
};
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
22
|
+
* Return active context-mode tool status. When activeTools is supplied, it is
|
|
23
|
+
* treated as the current model-visible tool set. Without it, keep the legacy
|
|
24
|
+
* registered-tool fallback for compatibility with older call sites.
|
|
20
25
|
*/
|
|
21
|
-
export function detectContextMode(
|
|
26
|
+
export function detectContextMode(activeTools?: string[]): ContextModeStatus {
|
|
27
|
+
if (activeTools) {
|
|
28
|
+
const active = new Set(activeTools);
|
|
29
|
+
const tools = {
|
|
30
|
+
ctxExecute: active.has("ctx_execute"),
|
|
31
|
+
ctxBatchExecute: active.has("ctx_batch_execute"),
|
|
32
|
+
ctxExecuteFile: active.has("ctx_execute_file"),
|
|
33
|
+
ctxIndex: active.has("ctx_index"),
|
|
34
|
+
ctxSearch: active.has("ctx_search"),
|
|
35
|
+
ctxFetchAndIndex: active.has("ctx_fetch_and_index"),
|
|
36
|
+
ctxOpenCached: active.has("ctx_open_cached"),
|
|
37
|
+
ctxStats: active.has("ctx_stats"),
|
|
38
|
+
ctxPurge: active.has("ctx_purge"),
|
|
39
|
+
ctxRepomap: active.has("ctx_repomap"),
|
|
40
|
+
ctxSymbol: active.has("ctx_symbol"),
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
available: Object.values(tools).some(Boolean),
|
|
44
|
+
tools,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
22
48
|
return {
|
|
23
49
|
available: true,
|
|
24
50
|
tools: {
|
|
@@ -28,6 +54,11 @@ export function detectContextMode(_activeTools?: string[]): ContextModeStatus {
|
|
|
28
54
|
ctxIndex: true,
|
|
29
55
|
ctxSearch: true,
|
|
30
56
|
ctxFetchAndIndex: true,
|
|
57
|
+
ctxOpenCached: true,
|
|
58
|
+
ctxStats: true,
|
|
59
|
+
ctxPurge: true,
|
|
60
|
+
ctxRepomap: true,
|
|
61
|
+
ctxSymbol: true,
|
|
31
62
|
},
|
|
32
63
|
};
|
|
33
64
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/context-mode/event-extractor.ts
|
|
2
2
|
import { PRIORITY } from "./event-store.js";
|
|
3
3
|
import type { EventCategory, EventPriority, TrackedEvent } from "./event-store.js";
|
|
4
|
+
import { canonicalToolName } from "./tool-name.js";
|
|
4
5
|
|
|
5
6
|
type Event = Omit<TrackedEvent, "id" | "dataHash">;
|
|
6
7
|
|
|
@@ -53,6 +54,7 @@ export function extractEvents(
|
|
|
53
54
|
details: unknown;
|
|
54
55
|
},
|
|
55
56
|
sessionId: string,
|
|
57
|
+
sourceHash?: string | null,
|
|
56
58
|
): Event[] {
|
|
57
59
|
const events: Event[] = [];
|
|
58
60
|
const text = getTextContent(event.content);
|
|
@@ -65,7 +67,7 @@ export function extractEvents(
|
|
|
65
67
|
}, PRIORITY.critical, "tool_result"));
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
switch (event.toolName) {
|
|
70
|
+
switch (canonicalToolName(event.toolName)) {
|
|
69
71
|
case "bash":
|
|
70
72
|
extractBash(events, event, sessionId, text);
|
|
71
73
|
break;
|
|
@@ -77,21 +79,20 @@ export function extractEvents(
|
|
|
77
79
|
if (readPath.includes("/skills/")) {
|
|
78
80
|
events.push(makeEvent(sessionId, "skill", { path: readPath }, PRIORITY.medium, "tool_result"));
|
|
79
81
|
}
|
|
80
|
-
extractFile(events, event, sessionId, "read");
|
|
82
|
+
extractFile(events, event, sessionId, "read", PRIORITY.medium, sourceHash);
|
|
81
83
|
break;
|
|
82
84
|
}
|
|
83
85
|
case "edit":
|
|
84
|
-
extractFile(events, event, sessionId, "edit", PRIORITY.high);
|
|
86
|
+
extractFile(events, event, sessionId, "edit", PRIORITY.high, sourceHash);
|
|
85
87
|
break;
|
|
86
88
|
case "write":
|
|
87
|
-
extractFile(events, event, sessionId, "write", PRIORITY.high);
|
|
88
|
-
break;
|
|
89
|
-
case "grep":
|
|
90
|
-
extractFile(events, event, sessionId, "search");
|
|
91
|
-
break;
|
|
92
|
-
case "find":
|
|
93
|
-
extractFile(events, event, sessionId, "find");
|
|
89
|
+
extractFile(events, event, sessionId, "write", PRIORITY.high, sourceHash);
|
|
94
90
|
break;
|
|
91
|
+
// `search` and `find` are intentionally not extracted: their `path`
|
|
92
|
+
// input is usually a directory or a glob, not a real file the caller
|
|
93
|
+
// touched. The downstream snapshot builder only consumes
|
|
94
|
+
// `op === "edit"|"write"|"read"` file events, so emitting here would
|
|
95
|
+
// (a) be silently dropped and (b) waste the 200-event read window.
|
|
95
96
|
case "todo_write":
|
|
96
97
|
events.push(makeEvent(sessionId, "task", {
|
|
97
98
|
input: event.input,
|
|
@@ -102,7 +103,7 @@ export function extractEvents(
|
|
|
102
103
|
events.push(makeEvent(sessionId, "mcp", {
|
|
103
104
|
tool: event.toolName,
|
|
104
105
|
}, PRIORITY.low, "tool_result"));
|
|
105
|
-
} else if (event.toolName === "task" || event.toolName === "sub_agent") {
|
|
106
|
+
} else if (canonicalToolName(event.toolName) === "task" || event.toolName === "sub_agent") {
|
|
106
107
|
events.push(makeEvent(sessionId, "subagent", {
|
|
107
108
|
toolName: event.toolName,
|
|
108
109
|
input: event.input,
|
|
@@ -171,9 +172,10 @@ function extractFile(
|
|
|
171
172
|
sessionId: string,
|
|
172
173
|
op: string,
|
|
173
174
|
priority: EventPriority = PRIORITY.medium,
|
|
175
|
+
sourceHash?: string | null,
|
|
174
176
|
): void {
|
|
175
177
|
const path = typeof event.input.path === "string" ? event.input.path : "unknown";
|
|
176
|
-
events.push(makeEvent(sessionId, "file", { op, path }, priority, "tool_result"));
|
|
178
|
+
events.push(makeEvent(sessionId, "file", { op, path, ...(sourceHash ? { sourceHash } : {}) }, priority, "tool_result"));
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
/** Extract events from a user prompt (called from before_agent_start handler) */
|