@opencode_weave/weave 0.4.1 → 0.4.2
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/hooks/index.d.ts +2 -0
- package/dist/hooks/session-token-state.d.ts +42 -0
- package/dist/index.js +68 -13
- package/package.json +1 -1
package/dist/hooks/index.d.ts
CHANGED
|
@@ -10,3 +10,5 @@ export { detectKeywords, buildKeywordInjection, processMessageForKeywords, DEFAU
|
|
|
10
10
|
export type { KeywordAction } from "./keyword-detector";
|
|
11
11
|
export { buildVerificationReminder } from "./verification-reminder";
|
|
12
12
|
export type { VerificationInput, VerificationResult } from "./verification-reminder";
|
|
13
|
+
export { setContextLimit, updateUsage, getState, clearSession as clearTokenSession, clear as clearAllTokenState, } from "./session-token-state";
|
|
14
|
+
export type { SessionTokenEntry } from "./session-token-state";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session token state tracker.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the gap between:
|
|
5
|
+
* - `chat.params` hook → provides model context limit (maxTokens)
|
|
6
|
+
* - `message.updated` event → provides latest input token usage (usedTokens)
|
|
7
|
+
*
|
|
8
|
+
* Uses an in-memory Map keyed by sessionId. State resets on plugin restart,
|
|
9
|
+
* which is acceptable — data rebuilds on the next `chat.params` + `message.updated` pair.
|
|
10
|
+
*/
|
|
11
|
+
export interface SessionTokenEntry {
|
|
12
|
+
maxTokens: number;
|
|
13
|
+
usedTokens: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Store the model's context limit for a session.
|
|
17
|
+
* Called from the `chat.params` hook when a session starts.
|
|
18
|
+
* Does NOT overwrite existing `usedTokens`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function setContextLimit(sessionId: string, maxTokens: number): void;
|
|
21
|
+
/**
|
|
22
|
+
* Update the latest input token usage for a session.
|
|
23
|
+
* Called from `message.updated` events with AssistantMessage tokens.
|
|
24
|
+
* Stores the latest value — NOT cumulative across messages.
|
|
25
|
+
* Does NOT overwrite existing `maxTokens`.
|
|
26
|
+
* Only updates when inputTokens > 0 (guards against partial streaming updates).
|
|
27
|
+
*/
|
|
28
|
+
export declare function updateUsage(sessionId: string, inputTokens: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Get the current token state for a session.
|
|
31
|
+
* Returns undefined if the session is unknown.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getState(sessionId: string): SessionTokenEntry | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Remove a session from the tracker.
|
|
36
|
+
* Called on `session.deleted` events.
|
|
37
|
+
*/
|
|
38
|
+
export declare function clearSession(sessionId: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all session state. Used in tests only.
|
|
41
|
+
*/
|
|
42
|
+
export declare function clear(): void;
|
package/dist/index.js
CHANGED
|
@@ -563,7 +563,9 @@ For complex tasks that benefit from structured planning before execution:
|
|
|
563
563
|
1. PLAN: Delegate to Pattern to produce a plan saved to \`.weave/plans/{name}.md\`
|
|
564
564
|
- Pattern researches the codebase, produces a structured plan with \`- [ ]\` checkboxes
|
|
565
565
|
- Pattern ONLY writes .md files in .weave/ — it never writes code
|
|
566
|
-
2. REVIEW
|
|
566
|
+
2. REVIEW: Delegate to Weft to validate the plan before execution
|
|
567
|
+
- TRIGGER: Plan touches 3+ files OR has 5+ tasks — Weft review is mandatory
|
|
568
|
+
- SKIP ONLY IF: User explicitly says "skip review"
|
|
567
569
|
- Weft reads the plan, verifies file references, checks executability
|
|
568
570
|
- If Weft rejects, send issues back to Pattern for revision
|
|
569
571
|
3. EXECUTE: Tell the user to run \`/start-work\` to begin execution
|
|
@@ -2074,12 +2076,12 @@ Only mark complete when ALL checks pass.`
|
|
|
2074
2076
|
|
|
2075
2077
|
// src/hooks/create-hooks.ts
|
|
2076
2078
|
function createHooks(args) {
|
|
2077
|
-
const { isHookEnabled, directory } = args;
|
|
2079
|
+
const { pluginConfig, isHookEnabled, directory } = args;
|
|
2078
2080
|
const writeGuardState = createWriteGuardState();
|
|
2079
2081
|
const writeGuard = createWriteGuard(writeGuardState);
|
|
2080
2082
|
const contextWindowThresholds = {
|
|
2081
|
-
warningPct: 0.8,
|
|
2082
|
-
criticalPct: 0.95
|
|
2083
|
+
warningPct: pluginConfig.experimental?.context_window_warning_threshold ?? 0.8,
|
|
2084
|
+
criticalPct: pluginConfig.experimental?.context_window_critical_threshold ?? 0.95
|
|
2083
2085
|
};
|
|
2084
2086
|
return {
|
|
2085
2087
|
checkContextWindow: isHookEnabled("context-window-monitor") ? (state) => checkContextWindow(state, contextWindowThresholds) : null,
|
|
@@ -2094,7 +2096,30 @@ function createHooks(args) {
|
|
|
2094
2096
|
verificationReminder: isHookEnabled("verification-reminder") ? buildVerificationReminder : null
|
|
2095
2097
|
};
|
|
2096
2098
|
}
|
|
2097
|
-
|
|
2099
|
+
// src/hooks/session-token-state.ts
|
|
2100
|
+
var sessionMap = new Map;
|
|
2101
|
+
function setContextLimit(sessionId, maxTokens) {
|
|
2102
|
+
const existing = sessionMap.get(sessionId);
|
|
2103
|
+
sessionMap.set(sessionId, {
|
|
2104
|
+
usedTokens: existing?.usedTokens ?? 0,
|
|
2105
|
+
maxTokens
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
function updateUsage(sessionId, inputTokens) {
|
|
2109
|
+
if (inputTokens <= 0)
|
|
2110
|
+
return;
|
|
2111
|
+
const existing = sessionMap.get(sessionId);
|
|
2112
|
+
sessionMap.set(sessionId, {
|
|
2113
|
+
maxTokens: existing?.maxTokens ?? 0,
|
|
2114
|
+
usedTokens: inputTokens
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
function getState(sessionId) {
|
|
2118
|
+
return sessionMap.get(sessionId);
|
|
2119
|
+
}
|
|
2120
|
+
function clearSession2(sessionId) {
|
|
2121
|
+
sessionMap.delete(sessionId);
|
|
2122
|
+
}
|
|
2098
2123
|
// src/plugin/plugin-interface.ts
|
|
2099
2124
|
function createPluginInterface(args) {
|
|
2100
2125
|
const { pluginConfig, hooks, tools, configHandler, agents, client } = args;
|
|
@@ -2114,13 +2139,6 @@ function createPluginInterface(args) {
|
|
|
2114
2139
|
},
|
|
2115
2140
|
"chat.message": async (input, _output) => {
|
|
2116
2141
|
const { sessionID } = input;
|
|
2117
|
-
if (hooks.checkContextWindow) {
|
|
2118
|
-
hooks.checkContextWindow({
|
|
2119
|
-
usedTokens: 0,
|
|
2120
|
-
maxTokens: 0,
|
|
2121
|
-
sessionId: sessionID
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2124
2142
|
if (hooks.firstMessageVariant) {
|
|
2125
2143
|
if (hooks.firstMessageVariant.shouldApplyVariant(sessionID)) {
|
|
2126
2144
|
hooks.firstMessageVariant.markApplied(sessionID);
|
|
@@ -2159,7 +2177,15 @@ ${result.contextInjection}`;
|
|
|
2159
2177
|
}
|
|
2160
2178
|
}
|
|
2161
2179
|
},
|
|
2162
|
-
"chat.params": async (_input, _output) => {
|
|
2180
|
+
"chat.params": async (_input, _output) => {
|
|
2181
|
+
const input = _input;
|
|
2182
|
+
const sessionId = input.sessionID ?? "";
|
|
2183
|
+
const maxTokens = input.model?.limit?.context ?? 0;
|
|
2184
|
+
if (sessionId && maxTokens > 0) {
|
|
2185
|
+
setContextLimit(sessionId, maxTokens);
|
|
2186
|
+
log("[context-window] Captured context limit", { sessionId, maxTokens });
|
|
2187
|
+
}
|
|
2188
|
+
},
|
|
2163
2189
|
"chat.headers": async (_input, _output) => {},
|
|
2164
2190
|
event: async (input) => {
|
|
2165
2191
|
const { event } = input;
|
|
@@ -2173,6 +2199,35 @@ ${result.contextInjection}`;
|
|
|
2173
2199
|
hooks.firstMessageVariant.clearSession(evt.properties.info.id);
|
|
2174
2200
|
}
|
|
2175
2201
|
}
|
|
2202
|
+
if (event.type === "session.deleted") {
|
|
2203
|
+
const evt = event;
|
|
2204
|
+
clearSession2(evt.properties.info.id);
|
|
2205
|
+
}
|
|
2206
|
+
if (event.type === "message.updated" && hooks.checkContextWindow) {
|
|
2207
|
+
const evt = event;
|
|
2208
|
+
const info = evt.properties?.info;
|
|
2209
|
+
if (info?.role === "assistant" && info.sessionID) {
|
|
2210
|
+
const inputTokens = info.tokens?.input ?? 0;
|
|
2211
|
+
if (inputTokens > 0) {
|
|
2212
|
+
updateUsage(info.sessionID, inputTokens);
|
|
2213
|
+
const tokenState = getState(info.sessionID);
|
|
2214
|
+
if (tokenState && tokenState.maxTokens > 0) {
|
|
2215
|
+
const result = hooks.checkContextWindow({
|
|
2216
|
+
usedTokens: tokenState.usedTokens,
|
|
2217
|
+
maxTokens: tokenState.maxTokens,
|
|
2218
|
+
sessionId: info.sessionID
|
|
2219
|
+
});
|
|
2220
|
+
if (result.action !== "none") {
|
|
2221
|
+
log("[context-window] Threshold crossed", {
|
|
2222
|
+
sessionId: info.sessionID,
|
|
2223
|
+
action: result.action,
|
|
2224
|
+
usagePct: result.usagePct
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2176
2231
|
if (hooks.workContinuation && event.type === "session.idle") {
|
|
2177
2232
|
const evt = event;
|
|
2178
2233
|
const sessionId = evt.properties?.sessionID ?? "";
|