@agentuity/opencode 0.1.36 → 0.1.38

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.
Files changed (48) hide show
  1. package/dist/agents/memory.d.ts +1 -1
  2. package/dist/agents/memory.d.ts.map +1 -1
  3. package/dist/agents/memory.js +170 -93
  4. package/dist/agents/memory.js.map +1 -1
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/plugin/hooks/cadence.d.ts +14 -2
  9. package/dist/plugin/hooks/cadence.d.ts.map +1 -1
  10. package/dist/plugin/hooks/cadence.js +60 -28
  11. package/dist/plugin/hooks/cadence.js.map +1 -1
  12. package/dist/plugin/hooks/keyword.d.ts +3 -2
  13. package/dist/plugin/hooks/keyword.d.ts.map +1 -1
  14. package/dist/plugin/hooks/keyword.js.map +1 -1
  15. package/dist/plugin/hooks/params.d.ts +3 -2
  16. package/dist/plugin/hooks/params.d.ts.map +1 -1
  17. package/dist/plugin/hooks/params.js +5 -2
  18. package/dist/plugin/hooks/params.js.map +1 -1
  19. package/dist/plugin/hooks/session-memory.d.ts +25 -0
  20. package/dist/plugin/hooks/session-memory.d.ts.map +1 -0
  21. package/dist/plugin/hooks/session-memory.js +111 -0
  22. package/dist/plugin/hooks/session-memory.js.map +1 -0
  23. package/dist/plugin/hooks/session.d.ts +3 -2
  24. package/dist/plugin/hooks/session.d.ts.map +1 -1
  25. package/dist/plugin/hooks/session.js.map +1 -1
  26. package/dist/plugin/hooks/tools.d.ts +3 -2
  27. package/dist/plugin/hooks/tools.d.ts.map +1 -1
  28. package/dist/plugin/hooks/tools.js +5 -2
  29. package/dist/plugin/hooks/tools.js.map +1 -1
  30. package/dist/plugin/plugin.d.ts +2 -2
  31. package/dist/plugin/plugin.d.ts.map +1 -1
  32. package/dist/plugin/plugin.js +41 -3
  33. package/dist/plugin/plugin.js.map +1 -1
  34. package/dist/types.d.ts +1 -61
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/types.js +1 -0
  37. package/dist/types.js.map +1 -1
  38. package/package.json +4 -3
  39. package/src/agents/memory.ts +170 -93
  40. package/src/index.ts +3 -3
  41. package/src/plugin/hooks/cadence.ts +71 -32
  42. package/src/plugin/hooks/keyword.ts +3 -2
  43. package/src/plugin/hooks/params.ts +8 -4
  44. package/src/plugin/hooks/session-memory.ts +137 -0
  45. package/src/plugin/hooks/session.ts +3 -2
  46. package/src/plugin/hooks/tools.ts +12 -5
  47. package/src/plugin/plugin.ts +51 -7
  48. package/src/types.ts +10 -54
@@ -1,9 +1,16 @@
1
- import type { PluginContext, CoderConfig, CompactingInput, CompactingOutput } from '../../types';
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CoderConfig } from '../../types';
3
+
4
+ /** Compacting hook input/output types */
5
+ type CompactingInput = { sessionID: string };
6
+ type CompactingOutput = { context: string[]; prompt?: string };
2
7
 
3
8
  export interface CadenceHooks {
4
9
  onMessage: (input: unknown, output: unknown) => Promise<void>;
5
10
  onEvent: (input: unknown) => Promise<void>;
6
11
  onCompacting: (input: CompactingInput, output: CompactingOutput) => Promise<void>;
12
+ /** Check if a session is currently in Cadence mode */
13
+ isActiveCadenceSession: (sessionId: string) => boolean;
7
14
  }
8
15
 
9
16
  const COMPLETION_PATTERN = /<promise>\s*DONE\s*<\/promise>/i;
@@ -41,7 +48,7 @@ interface CadenceSessionState {
41
48
  * 4. Trigger continuation after compaction (session.compacted)
42
49
  * 5. Clean up on session abort/error
43
50
  */
44
- export function createCadenceHooks(ctx: PluginContext, _config: CoderConfig): CadenceHooks {
51
+ export function createCadenceHooks(ctx: PluginInput, _config: CoderConfig): CadenceHooks {
45
52
  const activeCadenceSessions = new Map<string, CadenceSessionState>();
46
53
 
47
54
  const log = (msg: string) => {
@@ -151,7 +158,7 @@ export function createCadenceHooks(ctx: PluginContext, _config: CoderConfig): Ca
151
158
 
152
159
  log(`Event received: ${event.type}`);
153
160
 
154
- // Handle session.compacted - trigger continuation after compaction completes
161
+ // Handle session.compacted - save compaction AND continue loop
155
162
  if (event.type === 'session.compacted') {
156
163
  const sessionId = event.sessionId;
157
164
  if (!sessionId) return;
@@ -159,10 +166,9 @@ export function createCadenceHooks(ctx: PluginContext, _config: CoderConfig): Ca
159
166
  const state = activeCadenceSessions.get(sessionId);
160
167
  if (!state) return;
161
168
 
162
- log(`Compaction completed for Cadence session ${sessionId} - triggering continuation`);
163
- showToast(ctx, '🔄 Context compacted, resuming Cadence...');
169
+ log(`Compaction completed for Cadence session ${sessionId} - saving and continuing`);
170
+ showToast(ctx, '🔄 Compaction saved, resuming Cadence...');
164
171
 
165
- // Inject continuation prompt if session.prompt is available
166
172
  try {
167
173
  await ctx.client.session?.prompt?.({
168
174
  path: { id: sessionId },
@@ -170,24 +176,59 @@ export function createCadenceHooks(ctx: PluginContext, _config: CoderConfig): Ca
170
176
  parts: [
171
177
  {
172
178
  type: 'text',
173
- text: `[CADENCE CONTINUATION]
179
+ text: `[CADENCE COMPACTION COMPLETE]
180
+
181
+ The compaction summary above contains our Cadence session context.
182
+
183
+ 1. Have @Agentuity Coder Memory save this compaction:
184
+ - Get existing session: \`agentuity cloud kv get agentuity-opencode-memory "session:${sessionId}" --json --region use\`
185
+ - Append compaction to \`compactions\` array with timestamp
186
+ - Ensure \`cadence\` object exists with: loopId="${state.loopId ?? 'unknown'}", iteration=${state.iteration}, maxIterations=${state.maxIterations}, status="active"
187
+ - Save back to KV and upsert to Vector
188
+
189
+ After saving the compaction:
190
+ 1. Read back the session record from KV
191
+ 2. Return to Lead the PREVIOUS compactions only (not the one just saved - Lead already has the current compaction in context)
192
+ 3. Format as a readable summary with timestamps
193
+ 4. Include "what's next" - the Cadence iteration to continue
194
+
195
+ Response format:
196
+ \`\`\`
197
+ ## Prior Session History: ${sessionId}
198
+
199
+ ### Compaction 1 (timestamp)
200
+ [summary]
201
+
202
+ ### Compaction 2 (timestamp)
203
+ [summary]
204
+
205
+ (Current compaction already in your context)
174
206
 
175
- Context was just compacted. Resume the Cadence loop:
207
+ ## What's Next
208
+ Continue Cadence iteration ${state.iteration} of ${state.maxIterations}
209
+ \`\`\`
176
210
 
177
- 1. Ask Memory for the latest checkpoint and any compaction snapshots
178
- 2. Review the current iteration state from KV
179
- 3. Continue with the next step in the iteration workflow
180
- 4. Do NOT restart from the beginning - pick up where you left off
211
+ If no prior compactions exist:
212
+ \`\`\`
213
+ ## Prior Session History: ${sessionId}
181
214
 
182
- Continue executing the task.`,
215
+ No prior compactions - this is the first one.
216
+
217
+ ## What's Next
218
+ Continue Cadence iteration ${state.iteration} of ${state.maxIterations}
219
+ \`\`\`
220
+
221
+ 2. Then continue the Cadence loop:
222
+ - Review the compaction summary above for context
223
+ - Continue with iteration ${state.iteration}
224
+ - Do NOT restart from the beginning - pick up where we left off`,
183
225
  },
184
226
  ],
185
227
  agent: 'Agentuity Coder Lead',
186
228
  },
187
229
  });
188
230
  } catch (err) {
189
- log(`Failed to inject continuation prompt: ${err}`);
190
- // Continuation will rely on auto-generated "Continue if you have next steps"
231
+ log(`Failed to save compaction and continue: ${err}`);
191
232
  }
192
233
  }
193
234
 
@@ -227,35 +268,33 @@ Continue executing the task.`,
227
268
  log(`Injecting Cadence context during compaction for session ${sessionId}`);
228
269
  showToast(ctx, '💾 Compacting Cadence context...');
229
270
 
230
- const loopIdStr = state.loopId ?? '{loopId}';
231
-
232
- // Inject Cadence state into the compaction context
233
271
  output.context.push(`
234
272
  ## CADENCE MODE ACTIVE
235
273
 
236
274
  This session is running in Cadence mode (long-running autonomous loop).
237
275
 
238
276
  **Cadence State:**
277
+ - Session ID: ${sessionId}
239
278
  - Loop ID: ${state.loopId ?? 'unknown'}
240
279
  - Started: ${state.startedAt}
241
280
  - Iteration: ${state.iteration} / ${state.maxIterations}
242
281
  - Last activity: ${state.lastActivity}
243
282
 
244
- **CRITICAL: After compaction, you MUST:**
245
- 1. Ask @Agentuity Coder Memory for the latest checkpoint and compaction snapshots
246
- 2. Read the loop state from KV: \`agentuity cloud kv get agentuity-opencode-tasks "loop:${loopIdStr}:state"\`
247
- 3. Emit CADENCE_STATUS tag with current state
248
- 4. Continue the iteration workflow from where you left off
249
- 5. Do NOT restart the task from the beginning
250
-
251
- **Memory Keys to Query:**
252
- - \`loop:${loopIdStr}:state\` - Current loop state
253
- - \`loop:${loopIdStr}:checkpoint:{N}\` - Iteration checkpoints
254
- - \`loop:${loopIdStr}:compaction:{N}\` - Compaction snapshots
283
+ **Session Record Location:**
284
+ \`session:${sessionId}\` in agentuity-opencode-memory
255
285
 
256
- Resume the Cadence loop after this compaction completes.
286
+ After compaction, Memory will save this summary and update the cadence state.
287
+ Then Lead will continue the loop from iteration ${state.iteration}.
257
288
  `);
258
289
  },
290
+
291
+ /**
292
+ * Check if a session is currently in Cadence mode.
293
+ * Used by session-memory hooks to avoid double-handling.
294
+ */
295
+ isActiveCadenceSession(sessionId: string): boolean {
296
+ return activeCadenceSessions.has(sessionId);
297
+ },
259
298
  };
260
299
  }
261
300
 
@@ -339,9 +378,9 @@ function isCadenceStop(text: string): boolean {
339
378
  );
340
379
  }
341
380
 
342
- function showToast(ctx: PluginContext, message: string): void {
381
+ function showToast(ctx: PluginInput, message: string): void {
343
382
  try {
344
- ctx.client.tui?.showToast?.({ body: { message } });
383
+ ctx.client.tui.showToast({ body: { message, variant: 'info' } });
345
384
  } catch {
346
385
  // Toast may not be available
347
386
  }
@@ -1,4 +1,5 @@
1
- import type { PluginContext, CoderConfig } from '../../types';
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CoderConfig } from '../../types';
2
3
 
3
4
  export interface KeywordHooks {
4
5
  onMessage: (input: unknown, output: unknown) => Promise<void>;
@@ -35,7 +36,7 @@ Run \`agentuity ai schema show\` to see all available CLI commands.
35
36
  </coder-mode>
36
37
  `;
37
38
 
38
- export function createKeywordHooks(ctx: PluginContext, _config: CoderConfig): KeywordHooks {
39
+ export function createKeywordHooks(ctx: PluginInput, _config: CoderConfig): KeywordHooks {
39
40
  const activatedSessions = new Set<string>();
40
41
 
41
42
  const log = (msg: string) => {
@@ -1,4 +1,5 @@
1
- import type { PluginContext, CoderConfig } from '../../types';
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CoderConfig } from '../../types';
2
3
 
3
4
  export interface ParamsHooks {
4
5
  onParams: (input: unknown, output: unknown) => Promise<void>;
@@ -94,7 +95,7 @@ function detectMode(
94
95
  return null;
95
96
  }
96
97
 
97
- export function createParamsHooks(ctx: PluginContext, _config: CoderConfig): ParamsHooks {
98
+ export function createParamsHooks(ctx: PluginInput, _config: CoderConfig): ParamsHooks {
98
99
  return {
99
100
  async onParams(input: unknown, output: unknown): Promise<void> {
100
101
  // Input contains: sessionID, agent, model, provider, message
@@ -138,8 +139,11 @@ export function createParamsHooks(ctx: PluginContext, _config: CoderConfig): Par
138
139
  };
139
140
 
140
141
  try {
141
- ctx.client.tui?.showToast?.({
142
- body: { message: modeMessages[detected.mode] || `${detected.mode} mode activated` },
142
+ ctx.client.tui.showToast({
143
+ body: {
144
+ message: modeMessages[detected.mode] || `${detected.mode} mode activated`,
145
+ variant: 'info',
146
+ },
143
147
  });
144
148
  } catch {
145
149
  // Toast may not be available in all contexts (e.g., headless)
@@ -0,0 +1,137 @@
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CoderConfig } from '../../types';
3
+
4
+ export interface SessionMemoryHooks {
5
+ onEvent: (input: {
6
+ event: { type: string; properties?: Record<string, unknown> };
7
+ }) => Promise<void>;
8
+ onCompacting: (
9
+ input: { sessionID: string },
10
+ output: { context: string[]; prompt?: string }
11
+ ) => Promise<void>;
12
+ }
13
+
14
+ /**
15
+ * Session memory hooks handle compaction for non-Cadence sessions.
16
+ *
17
+ * Strategy:
18
+ * 1. On compacting: Inject Memory system info into compaction prompt
19
+ * 2. On session.compacted: Tell Lead to have Memory save the summary (it's already in context!)
20
+ */
21
+ export function createSessionMemoryHooks(
22
+ ctx: PluginInput,
23
+ _config: CoderConfig
24
+ ): SessionMemoryHooks {
25
+ const log = (msg: string) => {
26
+ ctx.client.app.log({
27
+ body: {
28
+ service: 'session-memory',
29
+ level: 'debug',
30
+ message: msg,
31
+ },
32
+ });
33
+ };
34
+
35
+ return {
36
+ /**
37
+ * Listen for session.compacted event.
38
+ * The compaction summary is already in context - just tell Lead to save it.
39
+ */
40
+ async onEvent(input: {
41
+ event: { type: string; properties?: Record<string, unknown> };
42
+ }): Promise<void> {
43
+ const { event } = input;
44
+ if (event?.type !== 'session.compacted') return;
45
+
46
+ const sessionId =
47
+ (event.properties?.sessionId as string | undefined) ??
48
+ (event.properties?.sessionID as string | undefined);
49
+
50
+ if (!sessionId) return;
51
+
52
+ log(`Compaction complete for session ${sessionId} - triggering memory save`);
53
+
54
+ try {
55
+ await ctx.client.session.prompt({
56
+ path: { id: sessionId },
57
+ body: {
58
+ parts: [
59
+ {
60
+ type: 'text',
61
+ text: `[COMPACTION COMPLETE]
62
+
63
+ The compaction summary above contains our session context.
64
+
65
+ Have @Agentuity Coder Memory save this compaction:
66
+ 1. Get existing session record (or create new): \`agentuity cloud kv get agentuity-opencode-memory "session:${sessionId}" --json --region use\`
67
+ 2. Append this compaction summary to the \`compactions\` array with timestamp
68
+ 3. Save back: \`agentuity cloud kv set agentuity-opencode-memory "session:${sessionId}" '{...}' --region use\`
69
+ 4. Upsert to Vector for semantic search: \`agentuity cloud vector upsert agentuity-opencode-sessions "session:${sessionId}" --document "..." --metadata '...' --region use\`
70
+
71
+ After saving the compaction:
72
+ 1. Read back the session record from KV
73
+ 2. Return to Lead the PREVIOUS compactions only (not the one just saved - Lead already has the current compaction in context)
74
+ 3. Format as a readable summary with timestamps
75
+ 4. Include "what's next" - the user's pending request if there is one
76
+
77
+ Response format:
78
+ \`\`\`
79
+ ## Prior Session History: ${sessionId}
80
+
81
+ ### Compaction 1 (timestamp)
82
+ [summary]
83
+
84
+ ### Compaction 2 (timestamp)
85
+ [summary]
86
+
87
+ (Current compaction already in your context)
88
+
89
+ ## What's Next
90
+ [User's pending request if there is one]
91
+ \`\`\`
92
+
93
+ If no prior compactions exist:
94
+ \`\`\`
95
+ ## Prior Session History: ${sessionId}
96
+
97
+ No prior compactions - this is the first one.
98
+
99
+ ## What's Next
100
+ [User's pending request if there is one]
101
+ \`\`\`
102
+
103
+ Then continue with the current task if there is one.`,
104
+ },
105
+ ],
106
+ agent: 'Agentuity Coder Lead',
107
+ },
108
+ });
109
+
110
+ log(`Memory save triggered for session ${sessionId}`);
111
+ } catch (err) {
112
+ log(`Failed to trigger memory save: ${err}`);
113
+ }
114
+ },
115
+
116
+ /**
117
+ * Inject Memory system info during compaction.
118
+ * This gets included in OpenCode's generated summary.
119
+ */
120
+ async onCompacting(
121
+ input: { sessionID: string },
122
+ output: { context: string[]; prompt?: string }
123
+ ): Promise<void> {
124
+ const sessionId = input.sessionID;
125
+ log(`Compacting session ${sessionId}`);
126
+
127
+ output.context.push(`
128
+ ## Session Memory
129
+
130
+ This session's context is being saved to persistent memory.
131
+ Session record location: \`session:${sessionId}\` in agentuity-opencode-memory
132
+
133
+ After compaction, Memory will automatically save this summary for future recovery.
134
+ `);
135
+ },
136
+ };
137
+ }
@@ -1,10 +1,11 @@
1
- import type { PluginContext, CoderConfig } from '../../types';
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CoderConfig } from '../../types';
2
3
 
3
4
  export interface SessionHooks {
4
5
  onMessage: (input: unknown, output: unknown) => Promise<void>;
5
6
  }
6
7
 
7
- export function createSessionHooks(_ctx: PluginContext, _config: CoderConfig): SessionHooks {
8
+ export function createSessionHooks(_ctx: PluginInput, _config: CoderConfig): SessionHooks {
8
9
  const initializedSessions = new Set<string>();
9
10
 
10
11
  return {
@@ -1,4 +1,5 @@
1
- import type { PluginContext, CoderConfig } from '../../types';
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CoderConfig } from '../../types';
2
3
  import { checkAuth } from '../../services/auth';
3
4
 
4
5
  export interface ToolHooks {
@@ -33,7 +34,7 @@ const CLOUD_SERVICES: Record<string, { name: string; emoji: string }> = {
33
34
  'agentuity cloud scp': { name: 'File Transfer', emoji: '📤' },
34
35
  };
35
36
 
36
- export function createToolHooks(ctx: PluginContext, config: CoderConfig): ToolHooks {
37
+ export function createToolHooks(ctx: PluginInput, config: CoderConfig): ToolHooks {
37
38
  const blockedCommands = config.blockedCommands ?? [];
38
39
 
39
40
  return {
@@ -79,7 +80,10 @@ export function createToolHooks(ctx: PluginContext, config: CoderConfig): ToolHo
79
80
  // Check if AGENTUITY_PROFILE already exists (anywhere in the command)
80
81
  if (/AGENTUITY_PROFILE=\S+/.test(command)) {
81
82
  // Replace all existing AGENTUITY_PROFILE occurrences to enforce our profile
82
- modifiedCommand = command.replace(/AGENTUITY_PROFILE=\S+/g, `AGENTUITY_PROFILE=${profile}`);
83
+ modifiedCommand = command.replace(
84
+ /AGENTUITY_PROFILE=\S+/g,
85
+ `AGENTUITY_PROFILE=${profile}`
86
+ );
83
87
  } else {
84
88
  // Prepend AGENTUITY_PROFILE
85
89
  modifiedCommand = `AGENTUITY_PROFILE=${profile} ${command}`;
@@ -90,8 +94,11 @@ export function createToolHooks(ctx: PluginContext, config: CoderConfig): ToolHo
90
94
  const service = detectCloudService(command);
91
95
  if (service) {
92
96
  try {
93
- ctx.client.tui?.showToast?.({
94
- body: { message: `${service.emoji} Agentuity ${service.name}` },
97
+ ctx.client.tui.showToast({
98
+ body: {
99
+ message: `${service.emoji} Agentuity ${service.name}`,
100
+ variant: 'info',
101
+ },
95
102
  });
96
103
  } catch {
97
104
  // Toast may not be available
@@ -1,4 +1,5 @@
1
- import type { PluginContext, PluginHooks, AgentConfig, CommandDefinition } from '../types';
1
+ import type { PluginInput, Hooks } from '@opencode-ai/plugin';
2
+ import type { AgentConfig, CommandDefinition } from '../types';
2
3
  import { agents } from '../agents';
3
4
  import { loadCoderConfig, getDefaultConfig, mergeConfig } from '../config';
4
5
  import { createSessionHooks } from './hooks/session';
@@ -6,6 +7,7 @@ import { createToolHooks } from './hooks/tools';
6
7
  import { createKeywordHooks } from './hooks/keyword';
7
8
  import { createParamsHooks } from './hooks/params';
8
9
  import { createCadenceHooks } from './hooks/cadence';
10
+ import { createSessionMemoryHooks } from './hooks/session-memory';
9
11
  import { z } from 'zod';
10
12
  import type { AgentRole } from '../types';
11
13
 
@@ -19,7 +21,7 @@ const AGENT_MENTIONS: Record<AgentRole, string> = {
19
21
  expert: '@Agentuity Coder Expert',
20
22
  };
21
23
 
22
- export async function createCoderPlugin(ctx: PluginContext): Promise<PluginHooks> {
24
+ export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
23
25
  ctx.client.app.log({
24
26
  body: {
25
27
  service: 'agentuity-coder',
@@ -37,6 +39,10 @@ export async function createCoderPlugin(ctx: PluginContext): Promise<PluginHooks
37
39
  const paramsHooks = createParamsHooks(ctx, coderConfig);
38
40
  const cadenceHooks = createCadenceHooks(ctx, coderConfig);
39
41
 
42
+ // Session memory hooks handle checkpointing and compaction for non-Cadence sessions
43
+ // Orchestration (deciding which module handles which session) happens below in the hooks
44
+ const sessionMemoryHooks = createSessionMemoryHooks(ctx, coderConfig);
45
+
40
46
  const configHandler = createConfigHandler(coderConfig);
41
47
 
42
48
  // Get the tool helper from Open Code context if available
@@ -48,7 +54,9 @@ export async function createCoderPlugin(ctx: PluginContext): Promise<PluginHooks
48
54
 
49
55
  // Show startup toast (fire and forget, don't block)
50
56
  try {
51
- ctx.client.tui?.showToast?.({ body: { message: '🚀 Agentuity Coder ready' } });
57
+ ctx.client.tui.showToast({
58
+ body: { message: '🚀 Agentuity Coder ready', variant: 'success' },
59
+ });
52
60
  } catch {
53
61
  // Toast may not be available
54
62
  }
@@ -64,8 +72,26 @@ export async function createCoderPlugin(ctx: PluginContext): Promise<PluginHooks
64
72
  'chat.params': paramsHooks.onParams,
65
73
  'tool.execute.before': toolHooks.before,
66
74
  'tool.execute.after': toolHooks.after,
67
- event: cadenceHooks.onEvent,
68
- 'experimental.session.compacting': cadenceHooks.onCompacting,
75
+ event: async (input) => {
76
+ // Orchestrate: route to appropriate module based on session type
77
+ const sessionId = extractSessionIdFromEvent(input);
78
+ if (sessionId && cadenceHooks.isActiveCadenceSession(sessionId)) {
79
+ await cadenceHooks.onEvent(input);
80
+ } else if (sessionId) {
81
+ // Non-Cadence sessions - handle session.compacted for checkpointing
82
+ await sessionMemoryHooks.onEvent(
83
+ input as { event: { type: string; properties?: Record<string, unknown> } }
84
+ );
85
+ }
86
+ },
87
+ 'experimental.session.compacting': async (input, output) => {
88
+ // Orchestrate: route to appropriate module based on session type
89
+ if (cadenceHooks.isActiveCadenceSession(input.sessionID)) {
90
+ await cadenceHooks.onCompacting(input, output);
91
+ } else {
92
+ await sessionMemoryHooks.onCompacting(input, output);
93
+ }
94
+ },
69
95
  };
70
96
  }
71
97
 
@@ -304,7 +330,7 @@ $ARGUMENTS
304
330
  };
305
331
  }
306
332
 
307
- function createTools(tool: (schema: (s: typeof z) => unknown) => unknown): Record<string, unknown> {
333
+ function createTools(tool: (schema: (s: typeof z) => unknown) => unknown): Hooks['tool'] {
308
334
  const coderDelegate = tool((s) => ({
309
335
  description: `Delegate a task to a specialized Agentuity Coder agent.
310
336
 
@@ -333,7 +359,25 @@ Use this to:
333
359
  },
334
360
  }));
335
361
 
362
+ // Type assertion needed because the tool() helper returns unknown
363
+ // but the runtime type is correct (it's created by OpenCode's tool helper)
336
364
  return {
337
365
  coder_delegate: coderDelegate,
338
- };
366
+ } as Hooks['tool'];
367
+ }
368
+
369
+ // ─────────────────────────────────────────────────────────────────────────────
370
+ // Helper Functions
371
+ // ─────────────────────────────────────────────────────────────────────────────
372
+
373
+ function extractSessionIdFromEvent(input: unknown): string | undefined {
374
+ if (typeof input !== 'object' || input === null) return undefined;
375
+
376
+ const inp = input as { event?: { properties?: Record<string, unknown> } };
377
+ if (!inp.event?.properties) return undefined;
378
+
379
+ return (
380
+ (inp.event.properties.sessionId as string | undefined) ??
381
+ (inp.event.properties.sessionID as string | undefined)
382
+ );
339
383
  }
package/src/types.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ // Re-export types from @opencode-ai/plugin
4
+ export type {
5
+ Plugin,
6
+ PluginInput,
7
+ Hooks as PluginHooks,
8
+ ToolDefinition,
9
+ } from '@opencode-ai/plugin';
10
+
3
11
  export const AgentRoleSchema = z.enum(['lead', 'scout', 'builder', 'reviewer', 'memory', 'expert']);
4
12
  export type AgentRole = z.infer<typeof AgentRoleSchema>;
5
13
 
@@ -114,55 +122,7 @@ export interface McpConfig {
114
122
  headers?: Record<string, string>;
115
123
  }
116
124
 
117
- export interface PluginClient {
118
- app: {
119
- log: (options: {
120
- body: { service: string; level: string; message: string; extra?: unknown };
121
- }) => void;
122
- };
123
- tui?: {
124
- showToast?: (options: { body: { message: string } }) => void;
125
- };
126
- session?: {
127
- prompt?: (options: {
128
- path: { id: string };
129
- body: {
130
- parts: Array<{ type: 'text'; text: string }>;
131
- agent?: string;
132
- };
133
- noReply?: boolean;
134
- }) => Promise<unknown>;
135
- };
136
- }
137
-
138
- export interface PluginContext {
139
- directory: string;
140
- client: PluginClient;
141
- }
142
-
143
- export interface CompactingInput {
144
- sessionID: string;
145
- }
146
-
147
- export interface CompactingOutput {
148
- context: string[];
149
- prompt?: string;
150
- }
151
-
152
- export interface PluginHooks {
153
- agents?: Record<string, AgentConfig>;
154
- tool?: Record<string, unknown>; // Open Code tool format (created via tool() helper)
155
- config?: (config: Record<string, unknown>) => Promise<void>;
156
- 'chat.message'?: (input: unknown, output: unknown) => Promise<void>;
157
- 'chat.params'?: (input: unknown, output: unknown) => Promise<void>;
158
- 'tool.execute.before'?: (input: unknown, output: unknown) => Promise<void>;
159
- 'tool.execute.after'?: (input: unknown, output: unknown) => Promise<void>;
160
- event?: (input: unknown) => Promise<void>;
161
- 'experimental.session.compacting'?: (
162
- input: CompactingInput,
163
- output: CompactingOutput
164
- ) => Promise<void>;
165
- }
125
+ // Note: PluginInput and PluginHooks are now imported from @opencode-ai/plugin above.
166
126
 
167
127
  export interface CommandDefinition {
168
128
  name: string;
@@ -175,8 +135,4 @@ export interface CommandDefinition {
175
135
  subtask?: boolean;
176
136
  }
177
137
 
178
- export interface ToolDefinition {
179
- description: string;
180
- args: unknown; // Zod schema or JSON schema
181
- execute: (args: unknown, context: unknown) => Promise<unknown>;
182
- }
138
+ // ToolDefinition is re-exported from @opencode-ai/plugin above