opencodekit 0.9.2 → 0.11.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/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +116 -47
- package/dist/template/.opencode/agent/build.md +16 -48
- package/dist/template/.opencode/agent/explore.md +13 -34
- package/dist/template/.opencode/agent/planner.md +41 -11
- package/dist/template/.opencode/agent/review.md +2 -23
- package/dist/template/.opencode/agent/rush.md +24 -65
- package/dist/template/.opencode/agent/scout.md +5 -21
- package/dist/template/.opencode/agent/vision.md +0 -14
- package/dist/template/.opencode/command/accessibility-check.md +293 -30
- package/dist/template/.opencode/command/analyze-mockup.md +406 -20
- package/dist/template/.opencode/command/analyze-project.md +439 -30
- package/dist/template/.opencode/command/brainstorm.md +288 -5
- package/dist/template/.opencode/command/commit.md +226 -17
- package/dist/template/.opencode/command/create.md +138 -35
- package/dist/template/.opencode/command/design-audit.md +477 -29
- package/dist/template/.opencode/command/design.md +609 -6
- package/dist/template/.opencode/command/edit-image.md +223 -20
- package/dist/template/.opencode/command/finish.md +162 -71
- package/dist/template/.opencode/command/fix-ci.md +296 -24
- package/dist/template/.opencode/command/fix-types.md +345 -13
- package/dist/template/.opencode/command/fix-ui.md +293 -13
- package/dist/template/.opencode/command/fix.md +256 -9
- package/dist/template/.opencode/command/generate-diagram.md +327 -26
- package/dist/template/.opencode/command/generate-icon.md +266 -22
- package/dist/template/.opencode/command/generate-image.md +232 -12
- package/dist/template/.opencode/command/generate-pattern.md +234 -20
- package/dist/template/.opencode/command/generate-storyboard.md +231 -21
- package/dist/template/.opencode/command/handoff.md +202 -30
- package/dist/template/.opencode/command/implement.md +162 -50
- package/dist/template/.opencode/command/import-plan.md +247 -51
- package/dist/template/.opencode/command/init.md +154 -35
- package/dist/template/.opencode/command/integration-test.md +405 -24
- package/dist/template/.opencode/command/issue.md +171 -21
- package/dist/template/.opencode/command/new-feature.md +382 -54
- package/dist/template/.opencode/command/plan.md +144 -118
- package/dist/template/.opencode/command/pr.md +229 -28
- package/dist/template/.opencode/command/quick-build.md +234 -5
- package/dist/template/.opencode/command/research-and-implement.md +436 -12
- package/dist/template/.opencode/command/research-ui.md +444 -34
- package/dist/template/.opencode/command/research.md +173 -45
- package/dist/template/.opencode/command/restore-image.md +416 -22
- package/dist/template/.opencode/command/resume.md +439 -63
- package/dist/template/.opencode/command/revert-feature.md +341 -64
- package/dist/template/.opencode/command/review-codebase.md +193 -4
- package/dist/template/.opencode/command/skill-create.md +506 -14
- package/dist/template/.opencode/command/skill-optimize.md +487 -16
- package/dist/template/.opencode/command/status.md +320 -60
- package/dist/template/.opencode/command/summarize.md +374 -33
- package/dist/template/.opencode/command/triage.md +355 -0
- package/dist/template/.opencode/command/ui-review.md +292 -25
- package/dist/template/.opencode/plugin/README.md +110 -98
- package/dist/template/.opencode/plugin/compactor.ts +95 -171
- package/dist/template/.opencode/plugin/enforcer.ts +177 -127
- package/dist/template/.opencode/plugin/injector.ts +150 -0
- package/dist/template/.opencode/plugin/lib/notify.ts +86 -0
- package/dist/template/.opencode/plugin/notification.ts +57 -123
- package/dist/template/.opencode/plugin/truncator.ts +60 -166
- package/dist/template/.opencode/skill/mqdh/SKILL.md +161 -0
- package/dist/template/.opencode/skill/playwriter/SKILL.md +148 -0
- package/dist/template/.opencode/skill/v0/SKILL.md +154 -0
- package/package.json +1 -1
|
@@ -2,27 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript plugins for extending OpenCode functionality following official best practices.
|
|
4
4
|
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
plugin/
|
|
9
|
+
├── lib/
|
|
10
|
+
│ └── notify.ts # Shared notification utilities
|
|
11
|
+
├── injector.ts # AGENTS.md hierarchy walker
|
|
12
|
+
├── compactor.ts # Context usage warnings
|
|
13
|
+
├── enforcer.ts # TODO completion enforcement
|
|
14
|
+
├── notification.ts # Session completion alerts
|
|
15
|
+
├── sessions.ts # Session management tools
|
|
16
|
+
├── truncator.ts # Output size monitoring
|
|
17
|
+
└── README.md
|
|
18
|
+
```
|
|
19
|
+
|
|
5
20
|
## Installed Plugins
|
|
6
21
|
|
|
7
|
-
###
|
|
22
|
+
### injector.ts
|
|
8
23
|
|
|
9
|
-
**
|
|
24
|
+
**AGENTS.md hierarchy walker** - solves OpenCode's limitation where findUp only finds the first AGENTS.md match.
|
|
10
25
|
|
|
11
|
-
|
|
26
|
+
- Hooks into `tool.execute.after` for `read` tool
|
|
27
|
+
- Walks up from file directory to project root
|
|
28
|
+
- Collects ALL AGENTS.md files in the path
|
|
29
|
+
- Injects in order: root → specific (T-shaped context loading)
|
|
30
|
+
- Caches per session to avoid duplicate injections
|
|
12
31
|
|
|
13
|
-
**
|
|
32
|
+
**Example:** When reading `src/components/Button.tsx`:
|
|
14
33
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
```
|
|
35
|
+
Injects:
|
|
36
|
+
1. /project/AGENTS.md (root context)
|
|
37
|
+
2. /project/src/AGENTS.md (src context)
|
|
38
|
+
3. /project/src/components/AGENTS.md (component context)
|
|
39
|
+
```
|
|
19
40
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
### sessions.ts
|
|
42
|
+
|
|
43
|
+
**Session management and context transfer** - enables short, focused sessions.
|
|
44
|
+
|
|
45
|
+
**Tools:**
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
- `list_sessions(project?, since?, limit?)` - Discover available sessions
|
|
48
|
+
- `read_session(session_reference, project?, focus?)` - Load context from previous sessions
|
|
49
|
+
- `summarize_session(session_id)` - Generate AI summary of a session
|
|
26
50
|
|
|
27
51
|
**Workflow pattern:**
|
|
28
52
|
|
|
@@ -30,133 +54,121 @@ Based on [AmpCode's 200k tokens article](https://ampcode.com/200k-tokens-is-plen
|
|
|
30
54
|
Session 1: Implementation (80k) → close
|
|
31
55
|
Session 2: read_session("last") → Refactor (60k) → close
|
|
32
56
|
Session 3: read_session("previous") → Tests (90k) → close
|
|
33
|
-
Session 4: read_session refs → Review (100k)
|
|
34
57
|
```
|
|
35
58
|
|
|
36
|
-
|
|
59
|
+
### compactor.ts
|
|
37
60
|
|
|
38
|
-
**
|
|
61
|
+
**Context usage warnings** - notifies at token thresholds before hitting limits.
|
|
39
62
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
- After handoff
|
|
63
|
+
- Warns at 70% (info), 85% (warn), 95% (critical)
|
|
64
|
+
- Sends native notifications at 85%+
|
|
65
|
+
- Tracks per-session to avoid duplicate warnings
|
|
44
66
|
|
|
45
|
-
###
|
|
67
|
+
### enforcer.ts
|
|
46
68
|
|
|
47
|
-
|
|
69
|
+
**TODO completion enforcement** - forces continuation when session idles with incomplete work.
|
|
48
70
|
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
71
|
+
- Tracks TODOs per session via `todo.updated` events
|
|
72
|
+
- On `session.idle`, checks for incomplete high-priority or in-progress TODOs
|
|
73
|
+
- **ENFORCES** continuation by calling `client.session.promptAsync()` (not just notification)
|
|
74
|
+
- Injects prompt: "Continue working on incomplete TODOs: [list]"
|
|
75
|
+
- 5-minute cooldown to prevent spam
|
|
76
|
+
- Falls back to OS notification if prompt injection fails
|
|
53
77
|
|
|
54
|
-
**
|
|
78
|
+
**Behavior:**
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
- High-priority or in-progress TODOs → Inject continuation prompt
|
|
81
|
+
- Low-priority pending TODOs → OS notification only (no forced continuation)
|
|
57
82
|
|
|
58
|
-
|
|
83
|
+
### notification.ts
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
- `find_skills` - List all available skills (project, personal, superpowers)
|
|
62
|
-
- Skills loaded on-demand, not auto-injected
|
|
63
|
-
- Supports skill resolution priority: project > personal > superpowers
|
|
85
|
+
**Session completion alerts** - sends native notifications when AI finishes.
|
|
64
86
|
|
|
65
|
-
|
|
87
|
+
- Extracts session summary from messages
|
|
88
|
+
- Cross-platform (macOS, Linux, WSL, Windows)
|
|
89
|
+
- Uses shared `lib/notify.ts` utilities
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
### truncator.ts
|
|
68
92
|
|
|
69
|
-
|
|
93
|
+
**Output size monitoring** - logs warnings for large outputs under context pressure.
|
|
70
94
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
95
|
+
- Monitors `tool.execute.after` events
|
|
96
|
+
- Warns when outputs exceed thresholds based on context usage
|
|
97
|
+
- Note: Actual truncation requires OpenCode core changes; this is observation-only
|
|
74
98
|
|
|
75
|
-
|
|
99
|
+
## Shared Library
|
|
76
100
|
|
|
77
|
-
|
|
78
|
-
export const MyPlugin: Plugin = async ({ project, client, $, directory }) => {
|
|
79
|
-
return {
|
|
80
|
-
event: async ({ event }) => {
|
|
81
|
-
/* ... */
|
|
82
|
-
},
|
|
83
|
-
tool: {
|
|
84
|
-
mytool: tool({
|
|
85
|
-
/* ... */
|
|
86
|
-
}),
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
```
|
|
101
|
+
### lib/notify.ts
|
|
91
102
|
|
|
92
|
-
|
|
103
|
+
Shared utilities used by multiple plugins:
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
- Type safety with `@opencode-ai/plugin` types
|
|
105
|
+
```typescript
|
|
106
|
+
import { notify, THRESHOLDS, getContextPercentage } from "./lib/notify";
|
|
97
107
|
|
|
98
|
-
|
|
108
|
+
// Send cross-platform notification using $ shell API
|
|
109
|
+
await notify($, "Title", "Message");
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
// Context thresholds
|
|
112
|
+
THRESHOLDS.MODERATE; // 70%
|
|
113
|
+
THRESHOLDS.URGENT; // 85%
|
|
114
|
+
THRESHOLDS.CRITICAL; // 95%
|
|
104
115
|
```
|
|
105
116
|
|
|
106
|
-
##
|
|
117
|
+
## Best Practices Applied
|
|
107
118
|
|
|
108
|
-
|
|
119
|
+
### Use `$` Shell API (not `exec`)
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
```typescript
|
|
122
|
+
// ✅ Correct - uses Bun shell from plugin context
|
|
123
|
+
export const MyPlugin: Plugin = async ({ $ }) => {
|
|
124
|
+
await $`osascript -e 'display notification "Done!"'`;
|
|
125
|
+
};
|
|
114
126
|
|
|
115
|
-
|
|
127
|
+
// ❌ Wrong - manual exec with escaping
|
|
128
|
+
import { exec } from "child_process";
|
|
129
|
+
exec(`osascript -e '...'`, () => {});
|
|
130
|
+
```
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
### Share Common Code
|
|
118
133
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- `tool` - Add custom tools to OpenCode
|
|
134
|
+
```typescript
|
|
135
|
+
// ✅ Correct - import from shared lib
|
|
136
|
+
import { notify } from "./lib/notify";
|
|
123
137
|
|
|
124
|
-
|
|
138
|
+
// ❌ Wrong - copy-paste same code in every plugin
|
|
139
|
+
function notify() {
|
|
140
|
+
/* duplicated */
|
|
141
|
+
}
|
|
142
|
+
```
|
|
125
143
|
|
|
126
|
-
###
|
|
144
|
+
### Proper Plugin Structure
|
|
127
145
|
|
|
128
146
|
```typescript
|
|
129
|
-
|
|
147
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
148
|
+
|
|
149
|
+
export const MyPlugin: Plugin = async ({ client, $ }) => {
|
|
130
150
|
return {
|
|
131
151
|
event: async ({ event }) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
152
|
+
/* ... */
|
|
153
|
+
},
|
|
154
|
+
"tool.execute.after": async (input, output) => {
|
|
155
|
+
/* ... */
|
|
135
156
|
},
|
|
136
157
|
};
|
|
137
158
|
};
|
|
138
159
|
```
|
|
139
160
|
|
|
140
|
-
|
|
161
|
+
## Available Hooks
|
|
141
162
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
args: { skill_name: tool.schema.string() },
|
|
149
|
-
async execute(args, ctx) {
|
|
150
|
-
// Load and return skill content
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
},
|
|
154
|
-
};
|
|
155
|
-
};
|
|
156
|
-
```
|
|
163
|
+
| Hook | Purpose |
|
|
164
|
+
| --------------------- | --------------------------------------------------------------- |
|
|
165
|
+
| `event` | Listen to OpenCode events (session.idle, session.updated, etc.) |
|
|
166
|
+
| `tool.execute.before` | Hook before tool execution |
|
|
167
|
+
| `tool.execute.after` | Hook after tool execution (observation only) |
|
|
168
|
+
| `tool` | Add custom tools |
|
|
157
169
|
|
|
158
170
|
## Resources
|
|
159
171
|
|
|
160
172
|
- [OpenCode Plugin Documentation](https://opencode.ai/docs/plugins/)
|
|
161
|
-
- [OpenCode
|
|
173
|
+
- [OpenCode Custom Tools](https://opencode.ai/docs/custom-tools/)
|
|
162
174
|
- [Community Examples](https://github.com/sst/opencode/discussions)
|
|
@@ -1,183 +1,107 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenCode Compactor Plugin
|
|
3
|
-
* Warns at token thresholds before hitting limits
|
|
4
|
-
*
|
|
5
|
-
* Inspired by oh-my-opencode's context-window-monitor and anthropic-auto-compact
|
|
3
|
+
* Warns at token thresholds before hitting limits
|
|
6
4
|
*/
|
|
7
5
|
|
|
8
6
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const release = readFileSync("/proc/version", "utf8").toLowerCase();
|
|
16
|
-
return release.includes("microsoft") || release.includes("wsl");
|
|
17
|
-
} catch {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
7
|
+
import {
|
|
8
|
+
THRESHOLDS,
|
|
9
|
+
type TokenStats,
|
|
10
|
+
getContextPercentage,
|
|
11
|
+
notify,
|
|
12
|
+
} from "./lib/notify";
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
return str.replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
|
|
24
|
-
}
|
|
14
|
+
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const escapedTitle = escapeShell(safeTitle);
|
|
31
|
-
const escapedMessage = escapeShell(safeMessage);
|
|
32
|
-
|
|
33
|
-
let command: string;
|
|
34
|
-
|
|
35
|
-
if (platform === "darwin") {
|
|
36
|
-
command = `osascript -e 'display notification "${escapedMessage}" with title "${escapedTitle}"'`;
|
|
37
|
-
} else if (platform === "linux") {
|
|
38
|
-
command = `notify-send "${escapedTitle}" "${escapedMessage}"`;
|
|
39
|
-
if (isWSL()) {
|
|
40
|
-
command = `(${command}) 2>&1 || echo "WSL notification failed"`;
|
|
41
|
-
}
|
|
42
|
-
} else if (platform === "win32") {
|
|
43
|
-
command = `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${escapedMessage}', '${escapedTitle}')"`;
|
|
44
|
-
} else {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
exec(command, () => {});
|
|
16
|
+
interface WarningLevel {
|
|
17
|
+
level: LogLevel;
|
|
18
|
+
emoji: string;
|
|
19
|
+
action: string;
|
|
49
20
|
}
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
22
|
+
function getWarningLevel(percentage: number): WarningLevel | null {
|
|
23
|
+
if (percentage >= THRESHOLDS.CRITICAL) {
|
|
24
|
+
return {
|
|
25
|
+
level: "error",
|
|
26
|
+
emoji: "🚨",
|
|
27
|
+
action: "CRITICAL: Prune immediately or start new session.",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (percentage >= THRESHOLDS.URGENT) {
|
|
31
|
+
return {
|
|
32
|
+
level: "warn",
|
|
33
|
+
emoji: "⚠️",
|
|
34
|
+
action: "Consider pruning completed tool outputs.",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (percentage >= THRESHOLDS.MODERATE) {
|
|
38
|
+
return {
|
|
39
|
+
level: "info",
|
|
40
|
+
emoji: "📈",
|
|
41
|
+
action: "Context at 70%. Consider consolidating.",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
55
45
|
}
|
|
56
46
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
| undefined;
|
|
118
|
-
const sessionId = (info?.id || props?.sessionID) as string | undefined;
|
|
119
|
-
|
|
120
|
-
if (!sessionId || !tokenStats?.used || !tokenStats?.limit) return;
|
|
121
|
-
|
|
122
|
-
const pct =
|
|
123
|
-
tokenStats.percentage ||
|
|
124
|
-
Math.round((tokenStats.used / tokenStats.limit) * 100);
|
|
125
|
-
const lastWarned = warnedSessions.get(sessionId) || 0;
|
|
126
|
-
|
|
127
|
-
const warning = getWarningLevel(pct);
|
|
128
|
-
if (!warning) return;
|
|
129
|
-
|
|
130
|
-
let currentThreshold = 0;
|
|
131
|
-
if (pct >= THRESHOLDS.CRITICAL) currentThreshold = THRESHOLDS.CRITICAL;
|
|
132
|
-
else if (pct >= THRESHOLDS.URGENT) currentThreshold = THRESHOLDS.URGENT;
|
|
133
|
-
else if (pct >= THRESHOLDS.MODERATE)
|
|
134
|
-
currentThreshold = THRESHOLDS.MODERATE;
|
|
135
|
-
|
|
136
|
-
if (lastWarned >= currentThreshold) return;
|
|
137
|
-
|
|
138
|
-
warnedSessions.set(sessionId, currentThreshold);
|
|
139
|
-
|
|
140
|
-
const message = `${warning.emoji} Context: ${pct}% (${tokenStats.used.toLocaleString()}/${tokenStats.limit.toLocaleString()} tokens). ${warning.action}`;
|
|
141
|
-
|
|
142
|
-
client.app
|
|
143
|
-
.log({
|
|
144
|
-
body: {
|
|
145
|
-
service: "compactor-plugin",
|
|
146
|
-
level: warning.level,
|
|
147
|
-
message,
|
|
148
|
-
},
|
|
149
|
-
})
|
|
150
|
-
.catch(() => {});
|
|
151
|
-
|
|
152
|
-
if (pct >= THRESHOLDS.URGENT) {
|
|
153
|
-
notify(`Context ${pct}%`, warning.action);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (event.type === "session.compacted") {
|
|
158
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
159
|
-
|
|
160
|
-
if (sessionId) {
|
|
161
|
-
client.app
|
|
162
|
-
.log({
|
|
163
|
-
body: {
|
|
164
|
-
service: "compactor-plugin",
|
|
165
|
-
level: "info",
|
|
166
|
-
message: "♻️ Session compacted - context freed",
|
|
167
|
-
},
|
|
168
|
-
})
|
|
169
|
-
.catch(() => {});
|
|
170
|
-
|
|
171
|
-
warnedSessions.set(sessionId, 0);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (event.type === "session.deleted") {
|
|
176
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
177
|
-
if (sessionId) {
|
|
178
|
-
warnedSessions.delete(sessionId);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
};
|
|
47
|
+
export const CompactorPlugin: Plugin = async ({ client, $ }) => {
|
|
48
|
+
const warnedSessions = new Map<string, number>();
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
event: async ({ event }) => {
|
|
52
|
+
const props = event.properties as Record<string, unknown>;
|
|
53
|
+
|
|
54
|
+
if (event.type === "session.updated") {
|
|
55
|
+
const info = props?.info as Record<string, unknown> | undefined;
|
|
56
|
+
const tokenStats = (info?.tokens || props?.tokens) as
|
|
57
|
+
| TokenStats
|
|
58
|
+
| undefined;
|
|
59
|
+
const sessionId = (info?.id || props?.sessionID) as string | undefined;
|
|
60
|
+
|
|
61
|
+
if (!sessionId || !tokenStats?.used || !tokenStats?.limit) return;
|
|
62
|
+
|
|
63
|
+
const pct = getContextPercentage(tokenStats);
|
|
64
|
+
const lastWarned = warnedSessions.get(sessionId) || 0;
|
|
65
|
+
|
|
66
|
+
const warning = getWarningLevel(pct);
|
|
67
|
+
if (!warning) return;
|
|
68
|
+
|
|
69
|
+
let currentThreshold = 0;
|
|
70
|
+
if (pct >= THRESHOLDS.CRITICAL) currentThreshold = THRESHOLDS.CRITICAL;
|
|
71
|
+
else if (pct >= THRESHOLDS.URGENT) currentThreshold = THRESHOLDS.URGENT;
|
|
72
|
+
else if (pct >= THRESHOLDS.MODERATE)
|
|
73
|
+
currentThreshold = THRESHOLDS.MODERATE;
|
|
74
|
+
|
|
75
|
+
if (lastWarned >= currentThreshold) return;
|
|
76
|
+
|
|
77
|
+
warnedSessions.set(sessionId, currentThreshold);
|
|
78
|
+
|
|
79
|
+
const message = `${warning.emoji} Context: ${pct}% (${tokenStats.used.toLocaleString()}/${tokenStats.limit.toLocaleString()} tokens). ${warning.action}`;
|
|
80
|
+
|
|
81
|
+
client.app
|
|
82
|
+
.log({
|
|
83
|
+
body: { service: "compactor", level: warning.level, message },
|
|
84
|
+
})
|
|
85
|
+
.catch(() => {});
|
|
86
|
+
|
|
87
|
+
if (pct >= THRESHOLDS.URGENT) {
|
|
88
|
+
await notify($, `Context ${pct}%`, warning.action);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (event.type === "session.compacted") {
|
|
93
|
+
const sessionId = props?.sessionID as string | undefined;
|
|
94
|
+
if (sessionId) {
|
|
95
|
+
warnedSessions.set(sessionId, 0);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (event.type === "session.deleted") {
|
|
100
|
+
const sessionId = props?.sessionID as string | undefined;
|
|
101
|
+
if (sessionId) {
|
|
102
|
+
warnedSessions.delete(sessionId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
183
107
|
};
|