@calltelemetry/openclaw-linear 0.9.16 → 0.9.18

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.
@@ -1,231 +0,0 @@
1
- /**
2
- * retro.ts — Post-dispatch retrospective spawner.
3
- *
4
- * After a dispatch completes (pass, fail, or stuck), spawns a sub-agent
5
- * that analyzes the full interaction and writes a structured retrospective
6
- * to the shared coding directory. Past retros are discoverable via QMD
7
- * memory_search, enabling pattern detection across dispatches.
8
- *
9
- * Output format: YAML frontmatter + markdown sections with priority tags
10
- * and actionable recommendations.
11
- */
12
- import { existsSync, mkdirSync } from "node:fs";
13
- import { join } from "node:path";
14
- import { homedir } from "node:os";
15
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
16
- import { runAgent } from "../agent/agent.js";
17
- import { readWorkerOutputs, readAuditVerdicts, readLog } from "./artifacts.js";
18
- import type { ActiveDispatch, CompletedDispatch } from "./dispatch-state.js";
19
- import type { AuditVerdict } from "./pipeline.js";
20
- import type { HookContext } from "./pipeline.js";
21
-
22
- // ---------------------------------------------------------------------------
23
- // Shared coding directory resolution
24
- // ---------------------------------------------------------------------------
25
-
26
- /**
27
- * Resolve the shared coding directory for retrospective files.
28
- *
29
- * Resolution order:
30
- * 1. pluginConfig.retroDir (explicit override, supports ~/ expansion)
31
- * 2. api.runtime.config.loadConfig().stateDir + "/shared/coding"
32
- * 3. Fallback: ~/.openclaw/shared/coding
33
- */
34
- export function resolveSharedCodingDir(
35
- api: OpenClawPluginApi,
36
- pluginConfig?: Record<string, unknown>,
37
- ): string {
38
- // 1. Explicit plugin config override (supports ~/expansion)
39
- const custom = pluginConfig?.retroDir as string | undefined;
40
- if (custom) {
41
- return custom.startsWith("~/") ? custom.replace("~", homedir()) : custom;
42
- }
43
-
44
- // 2. Derive from gateway config stateDir
45
- try {
46
- const config = (api as any).runtime.config.loadConfig() as Record<string, any>;
47
- const rawStateDir = config?.stateDir as string | undefined;
48
- const stateDir = rawStateDir
49
- ? (rawStateDir.startsWith("~/")
50
- ? rawStateDir.replace("~", homedir())
51
- : rawStateDir)
52
- : join(homedir(), ".openclaw");
53
- return join(stateDir, "shared", "coding");
54
- } catch {
55
- // 3. Safe fallback
56
- return join(homedir(), ".openclaw", "shared", "coding");
57
- }
58
- }
59
-
60
- // ---------------------------------------------------------------------------
61
- // Agent ID resolution (mirrors webhook.ts pattern)
62
- // ---------------------------------------------------------------------------
63
-
64
- function resolveAgentId(api: OpenClawPluginApi): string {
65
- const pluginConfig = (api as any).pluginConfig as Record<string, unknown> | undefined;
66
- const fromConfig = pluginConfig?.defaultAgentId;
67
- if (typeof fromConfig === "string" && fromConfig) return fromConfig;
68
- return "default";
69
- }
70
-
71
- // ---------------------------------------------------------------------------
72
- // Retrospective artifacts interface
73
- // ---------------------------------------------------------------------------
74
-
75
- export interface RetroArtifacts {
76
- verdict?: AuditVerdict;
77
- summary?: string;
78
- prUrl?: string;
79
- workerOutputs: string[];
80
- auditVerdicts: string[];
81
- logEntries: string[];
82
- }
83
-
84
- // ---------------------------------------------------------------------------
85
- // spawnRetrospective
86
- // ---------------------------------------------------------------------------
87
-
88
- /**
89
- * Spawn a sub-agent to create a structured retrospective after dispatch
90
- * completion. The agent analyzes worker outputs, audit verdicts, and the
91
- * interaction log, then writes a retro file with YAML frontmatter.
92
- *
93
- * Designed to be called fire-and-forget (non-blocking):
94
- * void spawnRetrospective(hookCtx, dispatch, artifacts).catch(...)
95
- *
96
- * The retro agent has access to memory_search to find past retros and
97
- * detect recurring patterns.
98
- */
99
- export async function spawnRetrospective(
100
- hookCtx: HookContext,
101
- dispatch: ActiveDispatch | CompletedDispatch,
102
- artifacts: RetroArtifacts,
103
- ): Promise<void> {
104
- const { api, pluginConfig } = hookCtx;
105
- const TAG = `[retro:${dispatch.issueIdentifier}]`;
106
-
107
- const codingDir = resolveSharedCodingDir(api, pluginConfig);
108
-
109
- // Ensure the shared coding directory exists
110
- if (!existsSync(codingDir)) {
111
- mkdirSync(codingDir, { recursive: true });
112
- }
113
-
114
- // Compute duration from dispatchedAt if available (ActiveDispatch has it)
115
- let durationMs: number | undefined;
116
- if ("dispatchedAt" in dispatch && dispatch.dispatchedAt) {
117
- durationMs = Date.now() - new Date(dispatch.dispatchedAt).getTime();
118
- }
119
-
120
- // Resolve fields that may differ between ActiveDispatch and CompletedDispatch
121
- const issueTitle = ("issueTitle" in dispatch ? dispatch.issueTitle : undefined) ?? dispatch.issueIdentifier;
122
- const model = "model" in dispatch ? dispatch.model : "unknown";
123
- const tier = dispatch.tier;
124
- const status = dispatch.status;
125
- const attempt = "attempt" in dispatch
126
- ? (dispatch.attempt ?? 0)
127
- : ("totalAttempts" in dispatch ? (dispatch.totalAttempts ?? 0) : 0);
128
- const worktreePath = "worktreePath" in dispatch ? dispatch.worktreePath : undefined;
129
-
130
- const dateStr = new Date().toISOString().slice(0, 10);
131
- const retroFilename = `retro-${dateStr}-${dispatch.issueIdentifier}.md`;
132
- const retroPath = join(codingDir, retroFilename);
133
-
134
- // Build the retro prompt
135
- const retroPrompt = [
136
- `You are a coding retrospective analyst. A dispatch just completed.`,
137
- `Analyze the interaction and create a structured retrospective.`,
138
- ``,
139
- `## Dispatch Details`,
140
- `- Issue: ${dispatch.issueIdentifier} — ${issueTitle}`,
141
- `- Backend: ${model} | Tier: ${tier}`,
142
- `- Attempts: ${attempt + 1} | Status: ${status}`,
143
- worktreePath ? `- Worktree: ${worktreePath}` : "",
144
- durationMs != null ? `- Duration: ${Math.round(durationMs / 1000)}s` : "",
145
- artifacts.prUrl ? `- PR: ${artifacts.prUrl}` : "",
146
- ``,
147
- `## Worker Outputs (${artifacts.workerOutputs.length} attempts)`,
148
- artifacts.workerOutputs
149
- .map((o, i) => `### Attempt ${i}\n${o.slice(0, 3000)}`)
150
- .join("\n\n"),
151
- ``,
152
- `## Audit Verdicts`,
153
- artifacts.auditVerdicts
154
- .map((v, i) => `### Attempt ${i}\n${v}`)
155
- .join("\n\n"),
156
- ``,
157
- `## Interaction Log`,
158
- artifacts.logEntries.length > 0
159
- ? artifacts.logEntries.slice(-20).map((e) => `- ${e}`).join("\n")
160
- : "(no log entries)",
161
- ``,
162
- `## Instructions`,
163
- ``,
164
- `**First**, use \`memory_search\` to find past retros related to this issue's`,
165
- `domain, backend, or error patterns. Look for recurring friction points.`,
166
- ``,
167
- `**Then**, write a retrospective file to: ${retroPath}`,
168
- ``,
169
- `Use this exact format — YAML frontmatter followed by markdown sections:`,
170
- ``,
171
- "```yaml",
172
- `---`,
173
- `type: retro`,
174
- `issue: ${dispatch.issueIdentifier}`,
175
- `title: "${issueTitle}"`,
176
- `backend: ${model}`,
177
- `tier: ${tier}`,
178
- `status: ${status}`,
179
- `attempts: ${attempt + 1}`,
180
- `date: ${dateStr}`,
181
- durationMs != null ? `duration_ms: ${durationMs}` : "",
182
- `---`,
183
- "```",
184
- ``,
185
- `Sections (all required):`,
186
- ``,
187
- `## Summary`,
188
- `Brief description of what was done and outcome.`,
189
- ``,
190
- `## What Went Well`,
191
- `- [P1/P2/P3] Items that worked. Tag each with priority.`,
192
- ``,
193
- `## Friction Points`,
194
- `- [P1/P2/P3:CATEGORY] Issues encountered.`,
195
- ` Categories: PROCESS, ENV, TOOLING, PROMPT, CONFIG`,
196
- ``,
197
- `## Environment Issues`,
198
- `- [P1/P2/P3:ENV] Environment-specific problems.`,
199
- ``,
200
- `## Recommendations`,
201
- `- [RECOMMEND:target] Specific changes to prevent future friction.`,
202
- ` Targets: AGENTS.md, CLAUDE.md, config, prompt, codex-config, etc.`,
203
- ``,
204
- `## Actionable Items`,
205
- `- [ ] Checkbox items for follow-up.`,
206
- ``,
207
- `Focus on patterns that would help FUTURE dispatches succeed faster:`,
208
- `- What context was missing that caused extra attempts?`,
209
- `- What environment setup tripped up the agent?`,
210
- `- What prompt improvements would have helped?`,
211
- `- What bootstrap file changes would prevent this friction?`,
212
- `- Compare against past retros — are we seeing recurring patterns?`,
213
- ].filter(Boolean).join("\n");
214
-
215
- api.logger.info(`${TAG} spawning retrospective agent → ${retroPath}`);
216
-
217
- const agentId = resolveAgentId(api);
218
-
219
- try {
220
- await runAgent({
221
- api,
222
- agentId,
223
- sessionId: `retro-${dispatch.issueIdentifier}-${Date.now()}`,
224
- message: retroPrompt,
225
- timeoutMs: 120_000, // 2min max for retro
226
- });
227
- api.logger.info(`${TAG} retrospective complete → ${retroFilename}`);
228
- } catch (err) {
229
- api.logger.warn(`${TAG} retrospective agent failed: ${err}`);
230
- }
231
- }