opencarly 1.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 +78 -0
- package/bin/install.js +304 -0
- package/commands/carly-manager.md +69 -0
- package/dist/config/discovery.d.ts +22 -0
- package/dist/config/discovery.d.ts.map +1 -0
- package/dist/config/discovery.js +43 -0
- package/dist/config/discovery.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +7 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manifest.d.ts +39 -0
- package/dist/config/manifest.d.ts.map +1 -0
- package/dist/config/manifest.js +139 -0
- package/dist/config/manifest.js.map +1 -0
- package/dist/config/schema.d.ts +663 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +208 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/engine/brackets.d.ts +26 -0
- package/dist/engine/brackets.d.ts.map +1 -0
- package/dist/engine/brackets.js +49 -0
- package/dist/engine/brackets.js.map +1 -0
- package/dist/engine/index.d.ts +8 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +8 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/loader.d.ts +82 -0
- package/dist/engine/loader.d.ts.map +1 -0
- package/dist/engine/loader.js +147 -0
- package/dist/engine/loader.js.map +1 -0
- package/dist/engine/matcher.d.ts +43 -0
- package/dist/engine/matcher.d.ts.map +1 -0
- package/dist/engine/matcher.js +174 -0
- package/dist/engine/matcher.js.map +1 -0
- package/dist/engine/trimmer.d.ts +91 -0
- package/dist/engine/trimmer.d.ts.map +1 -0
- package/dist/engine/trimmer.js +236 -0
- package/dist/engine/trimmer.js.map +1 -0
- package/dist/formatter/formatter.d.ts +23 -0
- package/dist/formatter/formatter.d.ts.map +1 -0
- package/dist/formatter/formatter.js +129 -0
- package/dist/formatter/formatter.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +484 -0
- package/dist/index.js.map +1 -0
- package/dist/session/session.d.ts +60 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +394 -0
- package/dist/session/session.js.map +1 -0
- package/package.json +59 -0
- package/templates/.opencarly/commands.json +96 -0
- package/templates/.opencarly/context.json +44 -0
- package/templates/.opencarly/domains/development.md +13 -0
- package/templates/.opencarly/domains/global.md +13 -0
- package/templates/.opencarly/domains/security.md +14 -0
- package/templates/.opencarly/domains/testing.md +12 -0
- package/templates/.opencarly/manifest.json +43 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCarly Domain Matcher
|
|
3
|
+
*
|
|
4
|
+
* Scans user prompts for domain recall keywords and star-commands.
|
|
5
|
+
* Handles global and per-domain exclusions.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Star-command detection
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Detect star-commands in the prompt.
|
|
12
|
+
* e.g. "*brief *dev explain this" -> ["brief", "dev"]
|
|
13
|
+
*/
|
|
14
|
+
export function detectStarCommands(prompt) {
|
|
15
|
+
const commands = [];
|
|
16
|
+
// Use matchAll to avoid global RegExp state mutation race conditions
|
|
17
|
+
const matches = prompt.matchAll(/\*([a-zA-Z]\w*)/g);
|
|
18
|
+
for (const match of matches) {
|
|
19
|
+
commands.push(match[1].toLowerCase());
|
|
20
|
+
}
|
|
21
|
+
return [...new Set(commands)]; // deduplicate
|
|
22
|
+
}
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Path and Glob Matching
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
const globRegexCache = new Map();
|
|
27
|
+
function globToRegExp(glob) {
|
|
28
|
+
if (globRegexCache.has(glob))
|
|
29
|
+
return globRegexCache.get(glob);
|
|
30
|
+
let escaped = "";
|
|
31
|
+
for (let i = 0; i < glob.length; i++) {
|
|
32
|
+
const c = glob[i];
|
|
33
|
+
if (/[.+^${}()|[\]\\]/.test(c)) {
|
|
34
|
+
escaped += "\\" + c;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
escaped += c;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const regexStr = "^" + escaped.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\?/g, ".") + "$";
|
|
41
|
+
const regex = new RegExp(regexStr);
|
|
42
|
+
globRegexCache.set(glob, regex);
|
|
43
|
+
return regex;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if a file path matches any of the given glob patterns.
|
|
47
|
+
*/
|
|
48
|
+
export function isPathMatch(filePath, patterns) {
|
|
49
|
+
for (const pattern of patterns) {
|
|
50
|
+
if (globToRegExp(pattern).test(filePath)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Extracts possible file paths from a user prompt.
|
|
58
|
+
* Looks for words containing `/` or a file extension (like `.ts`).
|
|
59
|
+
*/
|
|
60
|
+
function extractPathsFromPrompt(prompt) {
|
|
61
|
+
const words = prompt.split(/\s+/);
|
|
62
|
+
const paths = [];
|
|
63
|
+
for (const word of words) {
|
|
64
|
+
// Strip trailing punctuation
|
|
65
|
+
const cleanWord = word.replace(/[.,;:!?)$'"]+$/, "").replace(/^['"(]+/, "");
|
|
66
|
+
if (cleanWord.includes("/") || /\.[a-z0-9]{1,4}$/i.test(cleanWord)) {
|
|
67
|
+
paths.push(cleanWord);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return [...new Set(paths)];
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Keyword matching
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
/**
|
|
76
|
+
* Check if any keywords from the list appear in the prompt (case-insensitive substring match).
|
|
77
|
+
* Returns the list of matching keywords.
|
|
78
|
+
*/
|
|
79
|
+
function findMatchingKeywords(prompt, keywords) {
|
|
80
|
+
const promptLower = prompt.toLowerCase();
|
|
81
|
+
const matches = [];
|
|
82
|
+
for (const keyword of keywords) {
|
|
83
|
+
const keywordLower = keyword.toLowerCase().trim();
|
|
84
|
+
if (keywordLower === "")
|
|
85
|
+
continue;
|
|
86
|
+
// Escape regex special chars and do boundary match
|
|
87
|
+
const escaped = keywordLower.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
88
|
+
if (new RegExp(`(^|\\W)${escaped}($|\\W)`, "i").test(promptLower)) {
|
|
89
|
+
matches.push(keyword);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return matches;
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Main matching function
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
/**
|
|
98
|
+
* Match domains against a user prompt.
|
|
99
|
+
*
|
|
100
|
+
* Algorithm:
|
|
101
|
+
* 1. Check globalExclude - if any match, skip all domain matching
|
|
102
|
+
* 2. Collect always-on active domains
|
|
103
|
+
* 3. For each active, non-alwaysOn domain:
|
|
104
|
+
* a. Check per-domain exclude keywords
|
|
105
|
+
* b. Check recall keywords
|
|
106
|
+
* 4. Detect star-commands
|
|
107
|
+
*/
|
|
108
|
+
export function matchDomains(prompt, manifest, activeFiles = []) {
|
|
109
|
+
const result = {
|
|
110
|
+
matched: {},
|
|
111
|
+
matchedPaths: {},
|
|
112
|
+
excluded: {},
|
|
113
|
+
globalExcluded: [],
|
|
114
|
+
starCommands: [],
|
|
115
|
+
alwaysOn: [],
|
|
116
|
+
};
|
|
117
|
+
// 1. Check global exclusions
|
|
118
|
+
if (manifest.globalExclude.length > 0) {
|
|
119
|
+
const globalMatches = findMatchingKeywords(prompt, manifest.globalExclude);
|
|
120
|
+
if (globalMatches.length > 0) {
|
|
121
|
+
result.globalExcluded = globalMatches;
|
|
122
|
+
// Still detect star-commands even when globally excluded
|
|
123
|
+
result.starCommands = detectStarCommands(prompt);
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Extract possible paths from user prompt and combine with activeFiles
|
|
128
|
+
const promptPaths = extractPathsFromPrompt(prompt);
|
|
129
|
+
const allActiveFiles = [...new Set([...activeFiles, ...promptPaths])];
|
|
130
|
+
// 2-3. Process each domain
|
|
131
|
+
for (const [name, domain] of Object.entries(manifest.domains)) {
|
|
132
|
+
// Skip inactive domains
|
|
133
|
+
if (domain.state === "inactive")
|
|
134
|
+
continue;
|
|
135
|
+
// Collect always-on domains
|
|
136
|
+
if (domain.alwaysOn) {
|
|
137
|
+
result.alwaysOn.push(name);
|
|
138
|
+
continue; // always-on domains don't need keyword matching
|
|
139
|
+
}
|
|
140
|
+
// Check per-domain exclusions
|
|
141
|
+
if (domain.exclude.length > 0) {
|
|
142
|
+
const excludeMatches = findMatchingKeywords(prompt, domain.exclude);
|
|
143
|
+
if (excludeMatches.length > 0) {
|
|
144
|
+
result.excluded[name] = excludeMatches;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Check file paths first
|
|
149
|
+
if (domain.paths && domain.paths.length > 0) {
|
|
150
|
+
const pathMatches = [];
|
|
151
|
+
for (const file of allActiveFiles) {
|
|
152
|
+
if (isPathMatch(file, domain.paths)) {
|
|
153
|
+
pathMatches.push(file);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (pathMatches.length > 0) {
|
|
157
|
+
result.matchedPaths[name] = pathMatches;
|
|
158
|
+
// Skip keyword recall check if path triggered it
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Check recall keywords
|
|
163
|
+
if (domain.recall.length > 0) {
|
|
164
|
+
const recallMatches = findMatchingKeywords(prompt, domain.recall);
|
|
165
|
+
if (recallMatches.length > 0) {
|
|
166
|
+
result.matched[name] = recallMatches;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// 4. Detect star-commands
|
|
171
|
+
result.starCommands = detectStarCommands(prompt);
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../src/engine/matcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCarly Smart Tool Output Trimmer
|
|
3
|
+
*
|
|
4
|
+
* Multi-factor scoring system that determines which tool outputs in
|
|
5
|
+
* conversation history are safe to trim. Replaces stale outputs with
|
|
6
|
+
* compact summaries to reduce token usage without degrading AI quality.
|
|
7
|
+
*
|
|
8
|
+
* Factors considered:
|
|
9
|
+
* - Age (turns since message)
|
|
10
|
+
* - Superseded file reads (same file read again more recently)
|
|
11
|
+
* - Post-read file edits (file was modified after being read)
|
|
12
|
+
* - Output size (larger = better trim candidate)
|
|
13
|
+
* - Tool type (ephemeral tools like bash/glob are safer to trim)
|
|
14
|
+
* - Tiny output protection (don't bother trimming small outputs)
|
|
15
|
+
*/
|
|
16
|
+
import { type TrimmingConfig } from "../config/schema";
|
|
17
|
+
/**
|
|
18
|
+
* Minimal types mirroring the OpenCode SDK Part/Message shapes.
|
|
19
|
+
* We use structural typing so we don't need to import the SDK directly.
|
|
20
|
+
*/
|
|
21
|
+
interface ToolStateCompleted {
|
|
22
|
+
status: "completed";
|
|
23
|
+
input: Record<string, unknown>;
|
|
24
|
+
output: string;
|
|
25
|
+
title: string;
|
|
26
|
+
metadata: Record<string, unknown>;
|
|
27
|
+
time: {
|
|
28
|
+
start: number;
|
|
29
|
+
end: number;
|
|
30
|
+
compacted?: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
interface ToolStateOther {
|
|
34
|
+
status: "pending" | "running" | "error";
|
|
35
|
+
input: Record<string, unknown>;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
type ToolState = ToolStateCompleted | ToolStateOther;
|
|
39
|
+
interface ToolPart {
|
|
40
|
+
type: "tool";
|
|
41
|
+
tool: string;
|
|
42
|
+
callID: string;
|
|
43
|
+
state: ToolState;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
interface TextPart {
|
|
47
|
+
type: "text";
|
|
48
|
+
text: string;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
}
|
|
51
|
+
type AnyPart = ToolPart | TextPart | {
|
|
52
|
+
type: string;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
};
|
|
55
|
+
interface TransformMessage {
|
|
56
|
+
info: {
|
|
57
|
+
role: "user" | "assistant";
|
|
58
|
+
time: {
|
|
59
|
+
created: number;
|
|
60
|
+
};
|
|
61
|
+
[key: string]: unknown;
|
|
62
|
+
};
|
|
63
|
+
parts: AnyPart[];
|
|
64
|
+
}
|
|
65
|
+
export interface TrimStats {
|
|
66
|
+
/** Number of tool outputs trimmed */
|
|
67
|
+
partsTrimmed: number;
|
|
68
|
+
/** Estimated tokens saved from tool output trimming (excludes carly blocks) */
|
|
69
|
+
tokensSaved: number;
|
|
70
|
+
/** Number of carly-rules blocks stripped */
|
|
71
|
+
carlyBlocksStripped: number;
|
|
72
|
+
/** Estimated tokens saved from carly-rules block removal */
|
|
73
|
+
carlyTokensSaved: number;
|
|
74
|
+
/** Files read or edited in the last 5 turns */
|
|
75
|
+
activeFiles: string[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Trim stale tool outputs and carly-rules blocks from message history.
|
|
79
|
+
*
|
|
80
|
+
* Algorithm:
|
|
81
|
+
* 1. Build TrimContext (catalog all file operations for cross-referencing)
|
|
82
|
+
* 2. For each message (except the last preserveLastN):
|
|
83
|
+
* - Strip <carly-rules> from text parts
|
|
84
|
+
* - For each completed tool part without a compacted timestamp:
|
|
85
|
+
* - Calculate relevance score
|
|
86
|
+
* - If score < threshold: replace output with compact summary
|
|
87
|
+
* 3. Return stats for logging
|
|
88
|
+
*/
|
|
89
|
+
export declare function trimMessageHistory(messages: TransformMessage[], config: TrimmingConfig): TrimStats;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=trimmer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trimmer.d.ts","sourceRoot":"","sources":["../../src/engine/trimmer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAMxE;;;GAGG;AAEH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,KAAK,SAAS,GAAG,kBAAkB,GAAG,cAAc,CAAC;AAErD,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,KAAK,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE9E,UAAU,gBAAgB;IACxB,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAC3B,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAyMD,MAAM,WAAW,SAAS;IACxB,qCAAqC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,MAAM,EAAE,cAAc,GACrB,SAAS,CAoEX"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCarly Smart Tool Output Trimmer
|
|
3
|
+
*
|
|
4
|
+
* Multi-factor scoring system that determines which tool outputs in
|
|
5
|
+
* conversation history are safe to trim. Replaces stale outputs with
|
|
6
|
+
* compact summaries to reduce token usage without degrading AI quality.
|
|
7
|
+
*
|
|
8
|
+
* Factors considered:
|
|
9
|
+
* - Age (turns since message)
|
|
10
|
+
* - Superseded file reads (same file read again more recently)
|
|
11
|
+
* - Post-read file edits (file was modified after being read)
|
|
12
|
+
* - Output size (larger = better trim candidate)
|
|
13
|
+
* - Tool type (ephemeral tools like bash/glob are safer to trim)
|
|
14
|
+
* - Tiny output protection (don't bother trimming small outputs)
|
|
15
|
+
*/
|
|
16
|
+
import { TRIM_THRESHOLDS } from "../config/schema";
|
|
17
|
+
class TrimContext {
|
|
18
|
+
fileOps = new Map();
|
|
19
|
+
constructor(messages) {
|
|
20
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
21
|
+
for (const part of messages[mi].parts) {
|
|
22
|
+
if (part.type !== "tool")
|
|
23
|
+
continue;
|
|
24
|
+
const toolPart = part;
|
|
25
|
+
if (toolPart.state.status !== "completed" && toolPart.state.status !== "error")
|
|
26
|
+
continue;
|
|
27
|
+
const filePath = toolPart.state.input.filePath;
|
|
28
|
+
if (!filePath)
|
|
29
|
+
continue;
|
|
30
|
+
if (toolPart.tool === "read" || toolPart.tool === "edit" || toolPart.tool === "write") {
|
|
31
|
+
const ops = this.fileOps.get(filePath);
|
|
32
|
+
const entry = { messageIndex: mi, op: toolPart.tool };
|
|
33
|
+
if (ops) {
|
|
34
|
+
ops.push(entry);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.fileOps.set(filePath, [entry]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Check if the same file was read in a later message */
|
|
44
|
+
hasNewerRead(filePath, messageIndex) {
|
|
45
|
+
const ops = this.fileOps.get(filePath);
|
|
46
|
+
if (!ops)
|
|
47
|
+
return false;
|
|
48
|
+
return ops.some((op) => op.op === "read" && op.messageIndex > messageIndex);
|
|
49
|
+
}
|
|
50
|
+
/** Check if the file was edited or written after this message */
|
|
51
|
+
wasEditedAfter(filePath, messageIndex) {
|
|
52
|
+
const ops = this.fileOps.get(filePath);
|
|
53
|
+
if (!ops)
|
|
54
|
+
return false;
|
|
55
|
+
return ops.some((op) => (op.op === "edit" || op.op === "write") &&
|
|
56
|
+
op.messageIndex > messageIndex);
|
|
57
|
+
}
|
|
58
|
+
/** Get unique file paths operated on in the last N messages */
|
|
59
|
+
getRecentFiles(totalMessages, lastN) {
|
|
60
|
+
const recentPaths = new Set();
|
|
61
|
+
const startIdx = Math.max(0, totalMessages - lastN);
|
|
62
|
+
for (const [filePath, ops] of this.fileOps.entries()) {
|
|
63
|
+
if (ops.some(op => op.messageIndex >= startIdx)) {
|
|
64
|
+
recentPaths.add(filePath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return Array.from(recentPaths);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Scoring
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/** Ephemeral tool types that are cheap to re-run */
|
|
74
|
+
const EPHEMERAL_TOOLS = new Set(["bash", "glob", "grep"]);
|
|
75
|
+
/** Minimum token estimate to bother trimming */
|
|
76
|
+
const MIN_TRIM_TOKENS = 100;
|
|
77
|
+
/**
|
|
78
|
+
* Rough token estimate from string length.
|
|
79
|
+
* ~4 chars per token is a reasonable approximation for code/text.
|
|
80
|
+
*/
|
|
81
|
+
function estimateTokens(text) {
|
|
82
|
+
return Math.ceil(text.length / 4);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Score a tool part for trimming relevance.
|
|
86
|
+
*
|
|
87
|
+
* Returns 0-100 where lower = more trimmable.
|
|
88
|
+
* Parts that should never be trimmed return 100+.
|
|
89
|
+
*/
|
|
90
|
+
function scoreToolPart(toolPart, messageIndex, totalMessages, context) {
|
|
91
|
+
const state = toolPart.state;
|
|
92
|
+
// Only trim completed tool outputs
|
|
93
|
+
if (state.status !== "completed")
|
|
94
|
+
return 200;
|
|
95
|
+
const completed = state;
|
|
96
|
+
// Already trimmed by us or by OpenCode's own compaction
|
|
97
|
+
if (completed.time.compacted)
|
|
98
|
+
return 200;
|
|
99
|
+
// Don't bother trimming tiny outputs
|
|
100
|
+
const tokenEstimate = estimateTokens(completed.output);
|
|
101
|
+
if (tokenEstimate < MIN_TRIM_TOKENS)
|
|
102
|
+
return 200;
|
|
103
|
+
let score = 100;
|
|
104
|
+
const turnsAgo = totalMessages - 1 - messageIndex;
|
|
105
|
+
// --- Factor 1: Age decay (6 points per turn) ---
|
|
106
|
+
score -= turnsAgo * 6;
|
|
107
|
+
// --- Factor 2: Superseded file reads ---
|
|
108
|
+
if (toolPart.tool === "read") {
|
|
109
|
+
const filePath = toolPart.state.input.filePath;
|
|
110
|
+
if (filePath && context.hasNewerRead(filePath, messageIndex)) {
|
|
111
|
+
score -= 60;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// --- Factor 3: File was edited after this read ---
|
|
115
|
+
if (toolPart.tool === "read") {
|
|
116
|
+
const filePath = toolPart.state.input.filePath;
|
|
117
|
+
if (filePath && context.wasEditedAfter(filePath, messageIndex)) {
|
|
118
|
+
score -= 50;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// --- Factor 4: Output size (larger = better trim candidate) ---
|
|
122
|
+
if (tokenEstimate > 2000) {
|
|
123
|
+
score -= 15;
|
|
124
|
+
}
|
|
125
|
+
else if (tokenEstimate > 500) {
|
|
126
|
+
score -= 8;
|
|
127
|
+
}
|
|
128
|
+
// --- Factor 5: Ephemeral tool types ---
|
|
129
|
+
if (EPHEMERAL_TOOLS.has(toolPart.tool)) {
|
|
130
|
+
score -= 10;
|
|
131
|
+
}
|
|
132
|
+
return Math.max(0, score);
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Trim summary generation
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
/**
|
|
138
|
+
* Build a compact summary to replace trimmed tool output.
|
|
139
|
+
*/
|
|
140
|
+
function buildTrimSummary(toolPart, tokensSaved) {
|
|
141
|
+
const completed = toolPart.state;
|
|
142
|
+
const title = completed.title || toolPart.tool;
|
|
143
|
+
if (toolPart.tool === "read") {
|
|
144
|
+
const filePath = toolPart.state.input.filePath || "unknown file";
|
|
145
|
+
const lineCount = completed.output.split("\n").length;
|
|
146
|
+
return (`[Trimmed by OpenCarly] Read ${filePath} (${lineCount} lines, ~${tokensSaved} tokens saved)\n` +
|
|
147
|
+
`Re-read this file if its contents are needed.`);
|
|
148
|
+
}
|
|
149
|
+
if (toolPart.tool === "bash") {
|
|
150
|
+
const command = toolPart.state.input.command || "unknown command";
|
|
151
|
+
// Show first 80 chars of command
|
|
152
|
+
const shortCmd = command.length > 80 ? command.slice(0, 77) + "..." : command;
|
|
153
|
+
return (`[Trimmed by OpenCarly] Ran: ${shortCmd} (~${tokensSaved} tokens saved)\n` +
|
|
154
|
+
`Re-run this command if output is needed.`);
|
|
155
|
+
}
|
|
156
|
+
if (toolPart.tool === "glob" || toolPart.tool === "grep") {
|
|
157
|
+
const pattern = toolPart.state.input.pattern || "";
|
|
158
|
+
return (`[Trimmed by OpenCarly] ${toolPart.tool}: ${pattern} (~${tokensSaved} tokens saved)\n` +
|
|
159
|
+
`Re-run this search if results are needed.`);
|
|
160
|
+
}
|
|
161
|
+
// Generic fallback
|
|
162
|
+
return (`[Trimmed by OpenCarly] ${title} (~${tokensSaved} tokens saved)\n` +
|
|
163
|
+
`Tool output trimmed from history.`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Trim stale tool outputs and carly-rules blocks from message history.
|
|
167
|
+
*
|
|
168
|
+
* Algorithm:
|
|
169
|
+
* 1. Build TrimContext (catalog all file operations for cross-referencing)
|
|
170
|
+
* 2. For each message (except the last preserveLastN):
|
|
171
|
+
* - Strip <carly-rules> from text parts
|
|
172
|
+
* - For each completed tool part without a compacted timestamp:
|
|
173
|
+
* - Calculate relevance score
|
|
174
|
+
* - If score < threshold: replace output with compact summary
|
|
175
|
+
* 3. Return stats for logging
|
|
176
|
+
*/
|
|
177
|
+
export function trimMessageHistory(messages, config) {
|
|
178
|
+
// Step 1: Build context for cross-referencing file operations
|
|
179
|
+
const context = new TrimContext(messages);
|
|
180
|
+
const totalMessages = messages.length;
|
|
181
|
+
const stats = {
|
|
182
|
+
partsTrimmed: 0,
|
|
183
|
+
tokensSaved: 0,
|
|
184
|
+
carlyBlocksStripped: 0,
|
|
185
|
+
carlyTokensSaved: 0,
|
|
186
|
+
activeFiles: context.getRecentFiles(totalMessages, 5),
|
|
187
|
+
};
|
|
188
|
+
if (!config.enabled)
|
|
189
|
+
return stats;
|
|
190
|
+
const threshold = TRIM_THRESHOLDS[config.mode] ?? 40;
|
|
191
|
+
const protectedStart = Math.max(0, totalMessages - config.preserveLastN);
|
|
192
|
+
// Step 2: Process each message
|
|
193
|
+
for (let mi = 0; mi < totalMessages; mi++) {
|
|
194
|
+
const message = messages[mi];
|
|
195
|
+
for (let pi = 0; pi < message.parts.length; pi++) {
|
|
196
|
+
const part = message.parts[pi];
|
|
197
|
+
// --- Strip <carly-rules> from text parts (all messages) ---
|
|
198
|
+
if (part.type === "text") {
|
|
199
|
+
const textPart = part;
|
|
200
|
+
if (typeof textPart.text === "string" && textPart.text.includes("<carly-rules>")) {
|
|
201
|
+
const tokensBefore = estimateTokens(textPart.text);
|
|
202
|
+
const textBefore = textPart.text;
|
|
203
|
+
textPart.text = textPart.text
|
|
204
|
+
.replace(/<carly-rules>[\s\S]*?<\/carly-rules>/g, "")
|
|
205
|
+
.trim();
|
|
206
|
+
if (textPart.text !== textBefore) {
|
|
207
|
+
const tokensAfter = estimateTokens(textPart.text);
|
|
208
|
+
stats.carlyBlocksStripped++;
|
|
209
|
+
stats.carlyTokensSaved += Math.max(0, tokensBefore - tokensAfter);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
// --- Tool output trimming (skip protected messages) ---
|
|
215
|
+
if (mi >= protectedStart)
|
|
216
|
+
continue;
|
|
217
|
+
if (part.type !== "tool")
|
|
218
|
+
continue;
|
|
219
|
+
const toolPart = part;
|
|
220
|
+
const score = scoreToolPart(toolPart, mi, totalMessages, context);
|
|
221
|
+
if (score < threshold) {
|
|
222
|
+
const completed = toolPart.state;
|
|
223
|
+
const tokensBefore = estimateTokens(completed.output);
|
|
224
|
+
const summary = buildTrimSummary(toolPart, tokensBefore);
|
|
225
|
+
const tokensAfter = estimateTokens(summary);
|
|
226
|
+
// Replace output with summary
|
|
227
|
+
completed.output = summary;
|
|
228
|
+
completed.time.compacted = Date.now();
|
|
229
|
+
stats.partsTrimmed++;
|
|
230
|
+
stats.tokensSaved += Math.max(0, tokensBefore - tokensAfter);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return stats;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=trimmer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trimmer.js","sourceRoot":"","sources":["../../src/engine/trimmer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCarly Rule Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats loaded rules into a text block for injection into the system prompt.
|
|
5
|
+
* Output is wrapped in <carly-rules> XML tags.
|
|
6
|
+
*/
|
|
7
|
+
import type { LoadedRules } from "../engine/loader";
|
|
8
|
+
/**
|
|
9
|
+
* Format loaded rules into an injectable text block.
|
|
10
|
+
*
|
|
11
|
+
* Output sections (in order):
|
|
12
|
+
* 1. Context bracket status
|
|
13
|
+
* 2. Bracket-specific rules
|
|
14
|
+
* 3. Active star-commands (prominent)
|
|
15
|
+
* 4. DEVMODE instruction
|
|
16
|
+
* 5. Loaded domains summary
|
|
17
|
+
* 6. Always-on domain rules
|
|
18
|
+
* 7. Keyword-matched domain rules
|
|
19
|
+
* 8. Exclusion notices
|
|
20
|
+
* 9. Available (not loaded) domains
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatRules(loaded: LoadedRules): string;
|
|
23
|
+
//# sourceMappingURL=formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/formatter/formatter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAkBpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CA8HvD"}
|