@fpr1m3/opencode-pai-plugin 1.2.0 → 1.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/index.js +27 -1
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +47 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -97,6 +97,9 @@ export const PAIPlugin = async ({ worktree }) => {
|
|
|
97
97
|
const messageTextCache = new Map();
|
|
98
98
|
// Track which messages we've already processed for archival (deduplication)
|
|
99
99
|
const processedMessageIds = new Set();
|
|
100
|
+
// Track pending Task tool calls to capture subagent_type
|
|
101
|
+
// Key: callID, Value: subagent_type
|
|
102
|
+
const pendingTaskCalls = new Map();
|
|
100
103
|
// Auto-initialize PAI infrastructure if needed
|
|
101
104
|
ensurePAIStructure();
|
|
102
105
|
// Load CORE skill content from $PAI_DIR/skill/core/SKILL.md
|
|
@@ -173,9 +176,14 @@ export const PAIPlugin = async ({ worktree }) => {
|
|
|
173
176
|
const file = props?.input?.file_path?.split('/').pop() || 'file';
|
|
174
177
|
process.stderr.write(`\x1b]0;Editing ${file}...\x07`);
|
|
175
178
|
}
|
|
176
|
-
else if (props?.tool === 'Task') {
|
|
179
|
+
else if (props?.tool === 'Task' || props?.tool === 'task') {
|
|
177
180
|
const type = props?.input?.subagent_type || 'agent';
|
|
178
181
|
process.stderr.write(`\x1b]0;Agent: ${type}...\x07`);
|
|
182
|
+
// Cache the subagent_type for when tool.execute.after fires
|
|
183
|
+
const callId = props?.id || props?.callId || props?.call_id;
|
|
184
|
+
if (callId && props?.input?.subagent_type) {
|
|
185
|
+
pendingTaskCalls.set(callId, props.input.subagent_type);
|
|
186
|
+
}
|
|
179
187
|
}
|
|
180
188
|
}
|
|
181
189
|
// Handle assistant message completion (Tab Titles & Artifact Archival)
|
|
@@ -232,12 +240,30 @@ export const PAIPlugin = async ({ worktree }) => {
|
|
|
232
240
|
// (In practice, messages are cleaned up after processing)
|
|
233
241
|
}
|
|
234
242
|
},
|
|
243
|
+
"tool.execute.before": async (input, output) => {
|
|
244
|
+
// Cache subagent_type from Task tool args for later use in tool.execute.after
|
|
245
|
+
if ((input.tool === 'Task' || input.tool === 'task') && input.callID) {
|
|
246
|
+
const args = output.args;
|
|
247
|
+
if (args?.subagent_type) {
|
|
248
|
+
pendingTaskCalls.set(input.callID, args.subagent_type);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
235
252
|
"tool.execute.after": async (input, output) => {
|
|
236
253
|
const sessionId = input.sessionID;
|
|
237
254
|
if (sessionId) {
|
|
238
255
|
if (!loggers.has(sessionId)) {
|
|
239
256
|
loggers.set(sessionId, new Logger(sessionId, worktree));
|
|
240
257
|
}
|
|
258
|
+
// For Task tools, inject the cached subagent_type into metadata
|
|
259
|
+
if ((input.tool === 'Task' || input.tool === 'task') && input.callID) {
|
|
260
|
+
const cachedAgentType = pendingTaskCalls.get(input.callID);
|
|
261
|
+
if (cachedAgentType) {
|
|
262
|
+
output.metadata = output.metadata || {};
|
|
263
|
+
output.metadata.subagent_type = cachedAgentType;
|
|
264
|
+
pendingTaskCalls.delete(input.callID);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
241
267
|
loggers.get(sessionId).logToolExecution(input, output);
|
|
242
268
|
}
|
|
243
269
|
},
|
package/dist/lib/logger.d.ts
CHANGED
|
@@ -28,6 +28,15 @@ export declare class Logger {
|
|
|
28
28
|
generateSessionSummary(): Promise<string | null>;
|
|
29
29
|
private parseStructuredResponse;
|
|
30
30
|
private isLearningCapture;
|
|
31
|
+
/**
|
|
32
|
+
* Normalize agent role from subagent_type patterns to base role.
|
|
33
|
+
* Handles patterns like:
|
|
34
|
+
* - "subagents/researcher-claude" → "researcher"
|
|
35
|
+
* - "subagents/sparc-architect" → "architect"
|
|
36
|
+
* - "subagents/sparc-dev" → "engineer"
|
|
37
|
+
* - "researcher" → "researcher" (passthrough)
|
|
38
|
+
*/
|
|
39
|
+
private normalizeAgentRole;
|
|
31
40
|
private determineArtifactType;
|
|
32
41
|
private createArtifact;
|
|
33
42
|
logError(context: string, error: any): void;
|
package/dist/lib/logger.js
CHANGED
|
@@ -126,7 +126,7 @@ export class Logger {
|
|
|
126
126
|
const sessionId = this.sessionId;
|
|
127
127
|
this.toolsUsed.add(toolName);
|
|
128
128
|
const metadata = output.metadata || {};
|
|
129
|
-
if (toolName === 'Task' && metadata?.subagent_type) {
|
|
129
|
+
if ((toolName === 'Task' || toolName === 'task') && metadata?.subagent_type) {
|
|
130
130
|
this.setAgentForSession(sessionId, metadata.subagent_type);
|
|
131
131
|
}
|
|
132
132
|
else if (toolName === 'subagent_stop' || toolName === 'stop') {
|
|
@@ -241,13 +241,56 @@ This session summary was automatically generated by the PAI OpenCode Plugin.
|
|
|
241
241
|
count++;
|
|
242
242
|
return count >= 2;
|
|
243
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Normalize agent role from subagent_type patterns to base role.
|
|
246
|
+
* Handles patterns like:
|
|
247
|
+
* - "subagents/researcher-claude" → "researcher"
|
|
248
|
+
* - "subagents/sparc-architect" → "architect"
|
|
249
|
+
* - "subagents/sparc-dev" → "engineer"
|
|
250
|
+
* - "researcher" → "researcher" (passthrough)
|
|
251
|
+
*/
|
|
252
|
+
normalizeAgentRole(agentRole) {
|
|
253
|
+
if (!agentRole)
|
|
254
|
+
return 'pai';
|
|
255
|
+
// Remove "subagents/" prefix if present (case-insensitive)
|
|
256
|
+
let role = agentRole.replace(/^subagents\//i, '').toLowerCase();
|
|
257
|
+
// Role keywords to look for anywhere in the string
|
|
258
|
+
// Order matters: more specific patterns first
|
|
259
|
+
const roleKeywords = [
|
|
260
|
+
// Researcher patterns (prefix or contains)
|
|
261
|
+
[/researcher/i, 'researcher'],
|
|
262
|
+
[/research/i, 'researcher'],
|
|
263
|
+
// Architect patterns
|
|
264
|
+
[/architect/i, 'architect'],
|
|
265
|
+
// Engineer patterns (includes "dev" for sparc-dev)
|
|
266
|
+
[/engineer/i, 'engineer'],
|
|
267
|
+
[/\bdev\b/i, 'engineer'], // "sparc-dev" → engineer
|
|
268
|
+
// Designer patterns
|
|
269
|
+
[/designer/i, 'designer'],
|
|
270
|
+
// Security patterns
|
|
271
|
+
[/pentester/i, 'pentester'],
|
|
272
|
+
// These map to researcher
|
|
273
|
+
[/analyst/i, 'researcher'],
|
|
274
|
+
[/explorer/i, 'researcher'],
|
|
275
|
+
[/^explore$/i, 'researcher'],
|
|
276
|
+
[/^intern$/i, 'researcher'],
|
|
277
|
+
];
|
|
278
|
+
for (const [pattern, normalized] of roleKeywords) {
|
|
279
|
+
if (pattern.test(role)) {
|
|
280
|
+
return normalized;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Return lowercase role if no pattern matched
|
|
284
|
+
return role;
|
|
285
|
+
}
|
|
244
286
|
determineArtifactType(agentRole, isLearning, sections) {
|
|
245
287
|
const summary = (sections['SUMMARY'] || '').toLowerCase();
|
|
246
|
-
|
|
288
|
+
const normalizedRole = this.normalizeAgentRole(agentRole);
|
|
289
|
+
if (normalizedRole === 'architect')
|
|
247
290
|
return 'DECISION';
|
|
248
|
-
if (
|
|
291
|
+
if (normalizedRole === 'researcher' || normalizedRole === 'pentester')
|
|
249
292
|
return 'RESEARCH';
|
|
250
|
-
if (
|
|
293
|
+
if (normalizedRole === 'engineer' || normalizedRole === 'designer') {
|
|
251
294
|
if (summary.includes('fix') || summary.includes('bug') || summary.includes('issue'))
|
|
252
295
|
return 'BUG';
|
|
253
296
|
if (summary.includes('refactor') || summary.includes('improve') || summary.includes('cleanup'))
|