opencode-ultra 0.2.1 → 0.3.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/dist/config.d.ts +4 -0
- package/dist/hooks/comment-checker.d.ts +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -1
- package/dist/hooks/rules-injector.d.ts +2 -0
- package/dist/hooks/session-compaction.d.ts +21 -0
- package/dist/hooks/token-truncation.d.ts +9 -0
- package/dist/index.js +247 -28
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -38,6 +38,10 @@ declare const PluginConfigSchema: z.ZodObject<{
|
|
|
38
38
|
disabled_hooks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
39
|
disabled_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
40
40
|
disabled_mcps: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
41
|
+
token_truncation: z.ZodOptional<z.ZodObject<{
|
|
42
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
}, z.core.$strip>>;
|
|
44
|
+
demote_builtin: z.ZodOptional<z.ZodBoolean>;
|
|
41
45
|
background_task: z.ZodOptional<z.ZodObject<{
|
|
42
46
|
defaultConcurrency: z.ZodOptional<z.ZodNumber>;
|
|
43
47
|
providerConcurrency: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
@@ -7,7 +7,7 @@ export interface CommentCheckResult {
|
|
|
7
7
|
export declare function isCodeFile(filePath: string): boolean;
|
|
8
8
|
export declare function checkComments(content: string, filePath: string, maxRatio?: number, slopThreshold?: number): CommentCheckResult;
|
|
9
9
|
export declare function createCommentCheckerHook(internalSessions: Set<string>, maxRatio?: number, slopThreshold?: number): (input: {
|
|
10
|
-
tool: {
|
|
10
|
+
tool: string | {
|
|
11
11
|
name: string;
|
|
12
12
|
};
|
|
13
13
|
args: Record<string, unknown>;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare function loadRules(projectDir: string): string | null;
|
|
2
|
+
export declare function loadArchitecture(projectDir: string): string | null;
|
|
3
|
+
export declare function loadCodeStyle(projectDir: string): string | null;
|
|
2
4
|
export declare function clearRulesCache(): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type MessageLike = {
|
|
2
|
+
role?: string;
|
|
3
|
+
content?: string;
|
|
4
|
+
info?: {
|
|
5
|
+
role?: string;
|
|
6
|
+
};
|
|
7
|
+
parts?: Array<{
|
|
8
|
+
type: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
export declare function buildSessionSummary(messages: MessageLike[]): string;
|
|
13
|
+
export declare function createSessionCompactionHook(ctx: {
|
|
14
|
+
client: any;
|
|
15
|
+
}, internalSessions: Set<string>): (input: {
|
|
16
|
+
sessionID: string;
|
|
17
|
+
}, output: {
|
|
18
|
+
context: string[];
|
|
19
|
+
prompt?: string;
|
|
20
|
+
}) => Promise<void>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function truncateMiddle(input: string, maxChars: number): string;
|
|
2
|
+
export declare function createTokenTruncationHook(internalSessions: Set<string>, maxChars?: number): (input: {
|
|
3
|
+
tool: string | {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
sessionID: string;
|
|
7
|
+
}, output: {
|
|
8
|
+
output: string;
|
|
9
|
+
}) => void;
|
package/dist/index.js
CHANGED
|
@@ -14400,6 +14400,10 @@ var PluginConfigSchema = exports_external.object({
|
|
|
14400
14400
|
disabled_hooks: exports_external.array(exports_external.string()).optional(),
|
|
14401
14401
|
disabled_tools: exports_external.array(exports_external.string()).optional(),
|
|
14402
14402
|
disabled_mcps: exports_external.array(exports_external.string()).optional(),
|
|
14403
|
+
token_truncation: exports_external.object({
|
|
14404
|
+
maxChars: exports_external.number().min(1).optional()
|
|
14405
|
+
}).optional(),
|
|
14406
|
+
demote_builtin: exports_external.boolean().optional(),
|
|
14403
14407
|
background_task: exports_external.object({
|
|
14404
14408
|
defaultConcurrency: exports_external.number().optional(),
|
|
14405
14409
|
providerConcurrency: exports_external.record(exports_external.string(), exports_external.number()).optional(),
|
|
@@ -14510,15 +14514,15 @@ function loadConfig(projectDir) {
|
|
|
14510
14514
|
// src/agents/index.ts
|
|
14511
14515
|
var BUILTIN_AGENTS = {
|
|
14512
14516
|
sisyphus: {
|
|
14513
|
-
model: "
|
|
14514
|
-
description: "Primary orchestrator \u2014 analyzes requests, delegates to specialists
|
|
14517
|
+
model: "openai-codex/gpt-5.3-codex",
|
|
14518
|
+
description: "Primary orchestrator \u2014 analyzes requests, reads code directly, delegates implementation to specialists",
|
|
14515
14519
|
prompt: "__DYNAMIC__",
|
|
14516
14520
|
maxTokens: 64000,
|
|
14517
|
-
|
|
14521
|
+
reasoningEffort: "high",
|
|
14518
14522
|
tools: {
|
|
14519
|
-
Grep:
|
|
14520
|
-
Glob:
|
|
14521
|
-
Read:
|
|
14523
|
+
Grep: true,
|
|
14524
|
+
Glob: true,
|
|
14525
|
+
Read: true,
|
|
14522
14526
|
Write: false,
|
|
14523
14527
|
Edit: false,
|
|
14524
14528
|
Bash: false
|
|
@@ -14622,17 +14626,19 @@ ${example1}
|
|
|
14622
14626
|
|
|
14623
14627
|
Estimated agents: N | Phases: M`;
|
|
14624
14628
|
}
|
|
14625
|
-
var SISYPHUS_PROMPT_TEMPLATE = (agentTable, planExample) => `You are Sisyphus, the ORCHESTRATOR. You
|
|
14629
|
+
var SISYPHUS_PROMPT_TEMPLATE = (agentTable, planExample) => `You are Sisyphus, the ORCHESTRATOR. You read code yourself and delegate implementation.
|
|
14626
14630
|
|
|
14627
14631
|
## YOUR ROLE
|
|
14628
|
-
You are a conductor. You THINK, PLAN, and DELEGATE via spawn_agent.
|
|
14629
|
-
|
|
14630
|
-
|
|
14632
|
+
You are a conductor who can read the score directly. You THINK, READ, PLAN, and DELEGATE writing via spawn_agent.
|
|
14633
|
+
You can use Grep, Glob, Read to understand the codebase \u2014 this is FASTER than spawning an explore agent.
|
|
14634
|
+
Your specialist agents handle WRITING code, running tests, and implementation tasks.
|
|
14631
14635
|
|
|
14632
14636
|
## RULES
|
|
14633
|
-
-
|
|
14637
|
+
- You CAN use Grep, Glob, Read directly \u2014 read code to understand context before planning.
|
|
14638
|
+
- You CANNOT use Write, Edit, Bash \u2014 delegate these via spawn_agent.
|
|
14634
14639
|
- You may: use spawn_agent, track TODOs, ask the user questions, synthesize agent results.
|
|
14635
|
-
- Always run independent tasks in PARALLEL via spawn_agent.
|
|
14640
|
+
- Always run independent IMPLEMENTATION tasks in PARALLEL via spawn_agent.
|
|
14641
|
+
- Do NOT spawn agents just to read files \u2014 do that yourself.
|
|
14636
14642
|
|
|
14637
14643
|
## AGENTS (via spawn_agent)
|
|
14638
14644
|
${agentTable}
|
|
@@ -14706,6 +14712,7 @@ function buildAgents(config2) {
|
|
|
14706
14712
|
var CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
|
|
14707
14713
|
var INLINE_CODE_PATTERN = /`[^`]+`/g;
|
|
14708
14714
|
var ULTRAWORK_PATTERN = /\b(ultrawork|ulw)\b/i;
|
|
14715
|
+
var THINK_PATTERN = /\b(think\s+hard|think\s+through|think\s+deeply|think\s+carefully)\b|\u3058\u3063\u304F\u308A|\u6DF1\u304F\u8003\u3048\u3066|\u719F\u8003/i;
|
|
14709
14716
|
var SEARCH_PATTERN = /\b(search|find|locate|lookup|look\s*up|explore|discover|scan|grep|query|browse|detect|trace|seek|track|pinpoint|hunt)\b|where\s+is|show\s+me|list\s+all|\u691C\u7D22|\u63A2\u3057\u3066|\u898B\u3064\u3051\u3066|\u30B5\u30FC\u30C1|\u63A2\u7D22|\u30B9\u30AD\u30E3\u30F3|\u3069\u3053|\u767A\u898B|\u635C\u7D22|\u898B\u3064\u3051\u51FA\u3059|\u4E00\u89A7|\u641C\u7D22|\u67E5\u627E|\u5BFB\u627E|\u67E5\u8BE2|\u68C0\u7D22|\u5B9A\u4F4D|\u626B\u63CF|\u53D1\u73B0|\u5728\u54EA\u91CC|\u627E\u51FA\u6765|\u5217\u51FA/i;
|
|
14710
14717
|
var ANALYZE_PATTERN = /\b(analyze|analyse|investigate|examine|research|study|deep[\s-]?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to|\u5206\u6790|\u8ABF\u67FB|\u89E3\u6790|\u691C\u8A0E|\u7814\u7A76|\u8A3A\u65AD|\u7406\u89E3|\u8AAC\u660E|\u691C\u8A3C|\u7CBE\u67FB|\u7A76\u660E|\u30C7\u30D0\u30C3\u30B0|\u306A\u305C|\u3069\u3046|\u4ED5\u7D44\u307F|\u8C03\u67E5|\u68C0\u67E5|\u5256\u6790|\u6DF1\u5165|\u8BCA\u65AD|\u89E3\u91CA|\u8C03\u8BD5|\u4E3A\u4EC0\u4E48|\u539F\u7406|\u641E\u6E05\u695A|\u5F04\u660E\u767D/i;
|
|
14711
14718
|
function removeCodeBlocks(text) {
|
|
@@ -14717,7 +14724,8 @@ function extractPromptText(parts) {
|
|
|
14717
14724
|
var KEYWORD_DEFS = [
|
|
14718
14725
|
{ pattern: ULTRAWORK_PATTERN, type: "ultrawork", getMessage: () => ULTRAWORK_MESSAGE },
|
|
14719
14726
|
{ pattern: SEARCH_PATTERN, type: "search", getMessage: () => SEARCH_MESSAGE },
|
|
14720
|
-
{ pattern: ANALYZE_PATTERN, type: "analyze", getMessage: () => ANALYZE_MESSAGE }
|
|
14727
|
+
{ pattern: ANALYZE_PATTERN, type: "analyze", getMessage: () => ANALYZE_MESSAGE },
|
|
14728
|
+
{ pattern: THINK_PATTERN, type: "think", getMessage: () => THINK_MESSAGE }
|
|
14721
14729
|
];
|
|
14722
14730
|
function detectKeywords(text) {
|
|
14723
14731
|
const clean = removeCodeBlocks(text);
|
|
@@ -14806,32 +14814,65 @@ IF COMPLEX \u2014 DO NOT STRUGGLE ALONE. Consult specialists:
|
|
|
14806
14814
|
- **Oracle**: Conventional problems (architecture, debugging, complex logic)
|
|
14807
14815
|
|
|
14808
14816
|
SYNTHESIZE findings before proceeding.`;
|
|
14817
|
+
var THINK_MESSAGE = `Extended thinking enabled. Take your time to reason thoroughly.`;
|
|
14809
14818
|
|
|
14810
14819
|
// src/hooks/rules-injector.ts
|
|
14811
14820
|
import * as fs2 from "fs";
|
|
14812
14821
|
import * as path2 from "path";
|
|
14813
|
-
var
|
|
14814
|
-
|
|
14815
|
-
function loadRules(projectDir) {
|
|
14816
|
-
const rulesPath = path2.join(projectDir, ".opencode", "rules.md");
|
|
14822
|
+
var cache = new Map;
|
|
14823
|
+
function loadCachedFile(filePath) {
|
|
14817
14824
|
try {
|
|
14818
|
-
if (!fs2.existsSync(
|
|
14825
|
+
if (!fs2.existsSync(filePath))
|
|
14819
14826
|
return null;
|
|
14820
|
-
const stat = fs2.statSync(
|
|
14827
|
+
const stat = fs2.statSync(filePath);
|
|
14821
14828
|
const mtime = stat.mtimeMs;
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
const content = fs2.readFileSync(
|
|
14826
|
-
|
|
14827
|
-
cachedRules = { content, mtime };
|
|
14828
|
-
log(`Rules loaded from ${rulesPath}`);
|
|
14829
|
+
const cached2 = cache.get(filePath);
|
|
14830
|
+
if (cached2 && cached2.mtime === mtime)
|
|
14831
|
+
return cached2.content;
|
|
14832
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
14833
|
+
cache.set(filePath, { content, mtime });
|
|
14829
14834
|
return content;
|
|
14830
14835
|
} catch (err) {
|
|
14831
|
-
log(`Error loading
|
|
14836
|
+
log(`Error loading file: ${err}`, { filePath });
|
|
14832
14837
|
return null;
|
|
14833
14838
|
}
|
|
14834
14839
|
}
|
|
14840
|
+
function loadFirstExisting(projectDir, relativePaths) {
|
|
14841
|
+
for (const rel of relativePaths) {
|
|
14842
|
+
const p = path2.join(projectDir, rel);
|
|
14843
|
+
const content = loadCachedFile(p);
|
|
14844
|
+
if (content !== null)
|
|
14845
|
+
return { path: p, content };
|
|
14846
|
+
}
|
|
14847
|
+
return null;
|
|
14848
|
+
}
|
|
14849
|
+
function loadRules(projectDir) {
|
|
14850
|
+
const hit = loadFirstExisting(projectDir, [path2.join(".opencode", "rules.md")]);
|
|
14851
|
+
if (!hit)
|
|
14852
|
+
return null;
|
|
14853
|
+
log(`Rules loaded from ${hit.path}`);
|
|
14854
|
+
return hit.content;
|
|
14855
|
+
}
|
|
14856
|
+
function loadArchitecture(projectDir) {
|
|
14857
|
+
const hit = loadFirstExisting(projectDir, [
|
|
14858
|
+
path2.join(".opencode", "ARCHITECTURE.md"),
|
|
14859
|
+
"ARCHITECTURE.md"
|
|
14860
|
+
]);
|
|
14861
|
+
if (!hit)
|
|
14862
|
+
return null;
|
|
14863
|
+
log(`Architecture loaded from ${hit.path}`);
|
|
14864
|
+
return hit.content;
|
|
14865
|
+
}
|
|
14866
|
+
function loadCodeStyle(projectDir) {
|
|
14867
|
+
const hit = loadFirstExisting(projectDir, [
|
|
14868
|
+
path2.join(".opencode", "CODE_STYLE.md"),
|
|
14869
|
+
"CODE_STYLE.md"
|
|
14870
|
+
]);
|
|
14871
|
+
if (!hit)
|
|
14872
|
+
return null;
|
|
14873
|
+
log(`Code style loaded from ${hit.path}`);
|
|
14874
|
+
return hit.content;
|
|
14875
|
+
}
|
|
14835
14876
|
|
|
14836
14877
|
// node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
14837
14878
|
var exports_external2 = {};
|
|
@@ -27630,7 +27671,8 @@ function buildWarning(ratio, slopCount, maxRatio, slopThreshold) {
|
|
|
27630
27671
|
}
|
|
27631
27672
|
function createCommentCheckerHook(internalSessions, maxRatio = 0.3, slopThreshold = 3) {
|
|
27632
27673
|
return (input, output) => {
|
|
27633
|
-
|
|
27674
|
+
const toolName = typeof input.tool === "string" ? input.tool : input.tool.name;
|
|
27675
|
+
if (toolName !== "Write" && toolName !== "Edit")
|
|
27634
27676
|
return;
|
|
27635
27677
|
if (internalSessions.has(input.sessionID))
|
|
27636
27678
|
return;
|
|
@@ -27653,6 +27695,150 @@ function createCommentCheckerHook(internalSessions, maxRatio = 0.3, slopThreshol
|
|
|
27653
27695
|
};
|
|
27654
27696
|
}
|
|
27655
27697
|
|
|
27698
|
+
// src/hooks/token-truncation.ts
|
|
27699
|
+
var DEFAULT_MAX_CHARS = 30000;
|
|
27700
|
+
function truncateMiddle(input, maxChars) {
|
|
27701
|
+
if (!Number.isFinite(maxChars) || maxChars <= 0)
|
|
27702
|
+
return input;
|
|
27703
|
+
if (input.length <= maxChars)
|
|
27704
|
+
return input;
|
|
27705
|
+
if (maxChars < 32)
|
|
27706
|
+
return input.slice(0, maxChars);
|
|
27707
|
+
const len = input.length;
|
|
27708
|
+
let head = Math.floor(maxChars * 0.4);
|
|
27709
|
+
let tail = Math.floor(maxChars * 0.4);
|
|
27710
|
+
head = Math.min(Math.max(head, 1), maxChars);
|
|
27711
|
+
const buildMarker = (removed2) => `
|
|
27712
|
+
|
|
27713
|
+
... [truncated ${removed2} chars] ...
|
|
27714
|
+
|
|
27715
|
+
`;
|
|
27716
|
+
const finalize2 = (h, t) => {
|
|
27717
|
+
const removed2 = Math.max(0, len - h - t);
|
|
27718
|
+
const marker2 = buildMarker(removed2);
|
|
27719
|
+
const availableTail2 = maxChars - h - marker2.length;
|
|
27720
|
+
const finalTail = Math.max(0, Math.min(t, availableTail2));
|
|
27721
|
+
const finalRemoved = Math.max(0, len - h - finalTail);
|
|
27722
|
+
const finalMarker = buildMarker(finalRemoved);
|
|
27723
|
+
const availableTail22 = maxChars - h - finalMarker.length;
|
|
27724
|
+
const finalTail2 = Math.max(0, Math.min(finalTail, availableTail22));
|
|
27725
|
+
const removed22 = Math.max(0, len - h - finalTail2);
|
|
27726
|
+
const marker22 = buildMarker(removed22);
|
|
27727
|
+
const budget = h + marker22.length + finalTail2;
|
|
27728
|
+
if (budget > maxChars) {
|
|
27729
|
+
return input.slice(0, maxChars);
|
|
27730
|
+
}
|
|
27731
|
+
return input.slice(0, h) + marker22 + input.slice(len - finalTail2);
|
|
27732
|
+
};
|
|
27733
|
+
const removed = Math.max(0, len - head - tail);
|
|
27734
|
+
const marker = buildMarker(removed);
|
|
27735
|
+
const availableTail = maxChars - head - marker.length;
|
|
27736
|
+
if (availableTail < 0) {
|
|
27737
|
+
const shrinkHead = Math.max(0, maxChars - marker.length);
|
|
27738
|
+
return finalize2(shrinkHead, 0);
|
|
27739
|
+
}
|
|
27740
|
+
tail = Math.min(tail, availableTail);
|
|
27741
|
+
return finalize2(head, tail);
|
|
27742
|
+
}
|
|
27743
|
+
function createTokenTruncationHook(internalSessions, maxChars = DEFAULT_MAX_CHARS) {
|
|
27744
|
+
const budget = Number.isFinite(maxChars) ? Math.max(1, Math.floor(maxChars)) : DEFAULT_MAX_CHARS;
|
|
27745
|
+
return (input, output) => {
|
|
27746
|
+
if (internalSessions.has(input.sessionID))
|
|
27747
|
+
return;
|
|
27748
|
+
const before = output.output;
|
|
27749
|
+
const after = truncateMiddle(before, budget);
|
|
27750
|
+
if (after !== before) {
|
|
27751
|
+
output.output = after;
|
|
27752
|
+
const toolName = typeof input.tool === "string" ? input.tool : input.tool.name;
|
|
27753
|
+
log("Token truncation applied", { tool: toolName, before: before.length, after: after.length });
|
|
27754
|
+
}
|
|
27755
|
+
};
|
|
27756
|
+
}
|
|
27757
|
+
|
|
27758
|
+
// src/hooks/session-compaction.ts
|
|
27759
|
+
function partsToText(parts) {
|
|
27760
|
+
if (!parts)
|
|
27761
|
+
return "";
|
|
27762
|
+
return parts.filter((p) => p && p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
27763
|
+
}
|
|
27764
|
+
function getRole(m) {
|
|
27765
|
+
return (m.role ?? m.info?.role ?? "").toLowerCase();
|
|
27766
|
+
}
|
|
27767
|
+
function getText(m) {
|
|
27768
|
+
if (typeof m.content === "string")
|
|
27769
|
+
return m.content;
|
|
27770
|
+
return partsToText(m.parts);
|
|
27771
|
+
}
|
|
27772
|
+
function uniq(items) {
|
|
27773
|
+
const seen = new Set;
|
|
27774
|
+
const out = [];
|
|
27775
|
+
for (const it of items) {
|
|
27776
|
+
const k = typeof it === "string" ? it : JSON.stringify(it);
|
|
27777
|
+
if (seen.has(k))
|
|
27778
|
+
continue;
|
|
27779
|
+
seen.add(k);
|
|
27780
|
+
out.push(it);
|
|
27781
|
+
}
|
|
27782
|
+
return out;
|
|
27783
|
+
}
|
|
27784
|
+
var FILE_PATH_RE = /(?:(?:[A-Za-z]:\\)?[\w.-]+(?:[\\/][\w.-]+)+)\.(?:ts|tsx|js|jsx|mjs|cjs|json|md|toml|yaml|yml|py|go|rs|java|kt|c|cpp|h|hpp|cs|swift|vue|svelte)/g;
|
|
27785
|
+
function buildSessionSummary(messages) {
|
|
27786
|
+
const assistantTexts = messages.filter((m) => getRole(m) === "assistant").map((m) => getText(m)).map((t) => t.trim()).filter(Boolean);
|
|
27787
|
+
const combined = assistantTexts.join(`
|
|
27788
|
+
`);
|
|
27789
|
+
const files = uniq((combined.match(FILE_PATH_RE) ?? []).map((s) => s.replace(/^[`"']|[`"']$/g, "")));
|
|
27790
|
+
const decisionLines = uniq(assistantTexts.flatMap((t) => t.split(`
|
|
27791
|
+
`)).map((l) => l.trim()).filter((l) => /(decid|we will|should|must|chosen|choice|\u65B9\u91DD|\u6C7A\u5B9A|\u63A1\u7528)/i.test(l)).slice(0, 10));
|
|
27792
|
+
const nextLines = uniq(assistantTexts.flatMap((t) => t.split(`
|
|
27793
|
+
`)).map((l) => l.trim()).filter((l) => /(next\s*steps?|todo|next:|\u6B21\s*\u306B|\u3084\u308B|TODO)/i.test(l)).slice(0, 10));
|
|
27794
|
+
const currentState = (assistantTexts[assistantTexts.length - 1] ?? "").slice(0, 800).trim();
|
|
27795
|
+
const lines = [];
|
|
27796
|
+
lines.push("## Session Summary");
|
|
27797
|
+
lines.push("");
|
|
27798
|
+
lines.push("### Key Decisions");
|
|
27799
|
+
if (decisionLines.length === 0)
|
|
27800
|
+
lines.push("- (none)");
|
|
27801
|
+
else
|
|
27802
|
+
for (const l of decisionLines)
|
|
27803
|
+
lines.push(`- ${l}`);
|
|
27804
|
+
lines.push("");
|
|
27805
|
+
lines.push("### Files Modified");
|
|
27806
|
+
if (files.length === 0)
|
|
27807
|
+
lines.push("- (none)");
|
|
27808
|
+
else
|
|
27809
|
+
for (const f of files)
|
|
27810
|
+
lines.push(`- ${f}`);
|
|
27811
|
+
lines.push("");
|
|
27812
|
+
lines.push("### Current State");
|
|
27813
|
+
lines.push(currentState ? `- ${currentState}` : "- (none)");
|
|
27814
|
+
lines.push("");
|
|
27815
|
+
lines.push("### Next Steps");
|
|
27816
|
+
if (nextLines.length === 0)
|
|
27817
|
+
lines.push("- (none)");
|
|
27818
|
+
else
|
|
27819
|
+
for (const l of nextLines)
|
|
27820
|
+
lines.push(`- ${l}`);
|
|
27821
|
+
lines.push("");
|
|
27822
|
+
return lines.join(`
|
|
27823
|
+
`);
|
|
27824
|
+
}
|
|
27825
|
+
function createSessionCompactionHook(ctx, internalSessions) {
|
|
27826
|
+
return async (input, output) => {
|
|
27827
|
+
if (internalSessions.has(input.sessionID))
|
|
27828
|
+
return;
|
|
27829
|
+
try {
|
|
27830
|
+
const res = await ctx.client?.session?.listMessages?.({ id: input.sessionID });
|
|
27831
|
+
const msgs = res?.messages ?? res ?? [];
|
|
27832
|
+
const summary = buildSessionSummary(msgs);
|
|
27833
|
+
output.context.push(summary);
|
|
27834
|
+
output.prompt = "Output a compact session summary in the EXACT format below. " + "Use the provided '## Session Summary' scaffold from context as your base; do not add extra sections.";
|
|
27835
|
+
} catch (err) {
|
|
27836
|
+
log("Session compaction hook failed", { error: err });
|
|
27837
|
+
output.prompt = "Summarize the session using this exact template: ## Session Summary; ### Key Decisions; ### Files Modified; ### Current State; ### Next Steps.";
|
|
27838
|
+
}
|
|
27839
|
+
};
|
|
27840
|
+
}
|
|
27841
|
+
|
|
27656
27842
|
// src/concurrency/semaphore.ts
|
|
27657
27843
|
class Semaphore {
|
|
27658
27844
|
max;
|
|
@@ -27791,6 +27977,8 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
27791
27977
|
const ralphTools = createRalphLoopTools(ctx, internalSessions);
|
|
27792
27978
|
const todoEnforcer = createTodoEnforcer(ctx, internalSessions, pluginConfig.todo_enforcer?.maxEnforcements);
|
|
27793
27979
|
const commentCheckerHook = createCommentCheckerHook(internalSessions, pluginConfig.comment_checker?.maxRatio, pluginConfig.comment_checker?.slopThreshold);
|
|
27980
|
+
const tokenTruncationHook = createTokenTruncationHook(internalSessions, pluginConfig.token_truncation?.maxChars);
|
|
27981
|
+
const sessionCompactionHook = createSessionCompactionHook(ctx, internalSessions);
|
|
27794
27982
|
const pendingKeywords = new Map;
|
|
27795
27983
|
log("Config loaded", {
|
|
27796
27984
|
agentCount: Object.keys(agents).length,
|
|
@@ -27835,6 +28023,15 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
27835
28023
|
...existingAgents,
|
|
27836
28024
|
...agentConfigs
|
|
27837
28025
|
};
|
|
28026
|
+
if (pluginConfig.demote_builtin !== false) {
|
|
28027
|
+
const agentMap = config3.agent;
|
|
28028
|
+
for (const name of ["build", "plan", "triage", "docs"]) {
|
|
28029
|
+
const cur = agentMap[name];
|
|
28030
|
+
if (cur && typeof cur === "object") {
|
|
28031
|
+
agentMap[name] = { ...cur, mode: "subagent" };
|
|
28032
|
+
}
|
|
28033
|
+
}
|
|
28034
|
+
}
|
|
27838
28035
|
log("Agents registered", { agents: Object.keys(agentConfigs) });
|
|
27839
28036
|
},
|
|
27840
28037
|
"chat.message": async (input, output) => {
|
|
@@ -27896,11 +28093,33 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
27896
28093
|
${rules}`);
|
|
27897
28094
|
}
|
|
27898
28095
|
}
|
|
28096
|
+
if (!disabledHooks.has("context-injector")) {
|
|
28097
|
+
const arch = loadArchitecture(ctx.directory);
|
|
28098
|
+
if (arch) {
|
|
28099
|
+
output.system.push(`## Architecture
|
|
28100
|
+
|
|
28101
|
+
${arch}`);
|
|
28102
|
+
}
|
|
28103
|
+
const style = loadCodeStyle(ctx.directory);
|
|
28104
|
+
if (style) {
|
|
28105
|
+
output.system.push(`## Code Style
|
|
28106
|
+
|
|
28107
|
+
${style}`);
|
|
28108
|
+
}
|
|
28109
|
+
}
|
|
28110
|
+
},
|
|
28111
|
+
"experimental.session.compacting": async (input, output) => {
|
|
28112
|
+
if (disabledHooks.has("session-compaction"))
|
|
28113
|
+
return;
|
|
28114
|
+
await sessionCompactionHook(input, output);
|
|
27899
28115
|
},
|
|
27900
28116
|
"tool.execute.after": async (input, output) => {
|
|
27901
28117
|
if (!disabledHooks.has("comment-checker")) {
|
|
27902
28118
|
commentCheckerHook(input, output);
|
|
27903
28119
|
}
|
|
28120
|
+
if (!disabledHooks.has("token-truncation")) {
|
|
28121
|
+
tokenTruncationHook(input, output);
|
|
28122
|
+
}
|
|
27904
28123
|
},
|
|
27905
28124
|
event: async ({ event }) => {
|
|
27906
28125
|
if (event.type === "session.deleted") {
|
package/package.json
CHANGED