@kernel.chat/kbot 2.8.0 → 2.9.1

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/agent-protocol.d.ts +97 -0
  2. package/dist/agent-protocol.d.ts.map +1 -0
  3. package/dist/agent-protocol.js +618 -0
  4. package/dist/agent-protocol.js.map +1 -0
  5. package/dist/agent.d.ts +2 -0
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +1 -1
  8. package/dist/agent.js.map +1 -1
  9. package/dist/auth.d.ts.map +1 -1
  10. package/dist/auth.js +17 -12
  11. package/dist/auth.js.map +1 -1
  12. package/dist/cli.js +166 -2
  13. package/dist/cli.js.map +1 -1
  14. package/dist/cloud-sync.d.ts.map +1 -1
  15. package/dist/cloud-sync.js +15 -4
  16. package/dist/cloud-sync.js.map +1 -1
  17. package/dist/confidence.d.ts +102 -0
  18. package/dist/confidence.d.ts.map +1 -0
  19. package/dist/confidence.js +696 -0
  20. package/dist/confidence.js.map +1 -0
  21. package/dist/ide/acp-server.js +5 -4
  22. package/dist/ide/acp-server.js.map +1 -1
  23. package/dist/ide/lsp-bridge.d.ts.map +1 -1
  24. package/dist/ide/lsp-bridge.js +11 -5
  25. package/dist/ide/lsp-bridge.js.map +1 -1
  26. package/dist/intentionality.d.ts +139 -0
  27. package/dist/intentionality.d.ts.map +1 -0
  28. package/dist/intentionality.js +1092 -0
  29. package/dist/intentionality.js.map +1 -0
  30. package/dist/planner.d.ts.map +1 -1
  31. package/dist/planner.js +3 -2
  32. package/dist/planner.js.map +1 -1
  33. package/dist/reasoning.d.ts +100 -0
  34. package/dist/reasoning.d.ts.map +1 -0
  35. package/dist/reasoning.js +1292 -0
  36. package/dist/reasoning.js.map +1 -0
  37. package/dist/streaming.d.ts.map +1 -1
  38. package/dist/streaming.js +18 -2
  39. package/dist/streaming.js.map +1 -1
  40. package/dist/temporal.d.ts +133 -0
  41. package/dist/temporal.d.ts.map +1 -0
  42. package/dist/temporal.js +778 -0
  43. package/dist/temporal.js.map +1 -0
  44. package/dist/tools/index.d.ts.map +1 -1
  45. package/dist/tools/index.js +30 -10
  46. package/dist/tools/index.js.map +1 -1
  47. package/dist/ui.js +1 -1
  48. package/package.json +2 -2
@@ -0,0 +1,1092 @@
1
+ // K:BOT Intentionality System — Intrinsic Quality Drives, Outcome Preferences, and Motivation
2
+ //
3
+ // This is genuinely novel. No other CLI agent has internal preferences about its own work,
4
+ // the ability to articulate what it wants from an outcome, or intrinsic motivation that
5
+ // evolves over time. This module gives the agent something closer to caring.
6
+ //
7
+ // THREE INTERCONNECTED SYSTEMS:
8
+ // 1. QUALITY DRIVES — Persistent preferences about work quality (correctness, elegance, etc.)
9
+ // 2. OUTCOME PREFERENCES — Before executing, articulate preferred/acceptable/unacceptable outcomes
10
+ // 3. INTRINSIC MOTIVATION — Curiosity, mastery, purpose, autonomy, momentum
11
+ //
12
+ // All state persists to ~/.kbot/ as JSON. No external dependencies. No LLM calls.
13
+ // Pure heuristic state machine — the agent's inner compass.
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
17
+ import { registerTool } from './tools/index.js';
18
+ // ── Persistence ──
19
+ const KBOT_DIR = join(homedir(), '.kbot');
20
+ const DRIVES_FILE = join(KBOT_DIR, 'drives.json');
21
+ const MOTIVATION_FILE = join(KBOT_DIR, 'motivation.json');
22
+ function ensureDir() {
23
+ if (!existsSync(KBOT_DIR))
24
+ mkdirSync(KBOT_DIR, { recursive: true });
25
+ }
26
+ function loadJSON(path, fallback) {
27
+ ensureDir();
28
+ if (!existsSync(path))
29
+ return fallback;
30
+ try {
31
+ return JSON.parse(readFileSync(path, 'utf-8'));
32
+ }
33
+ catch {
34
+ return fallback;
35
+ }
36
+ }
37
+ function saveJSON(path, data) {
38
+ ensureDir();
39
+ try {
40
+ writeFileSync(path, JSON.stringify(data, null, 2));
41
+ }
42
+ catch { /* best-effort — intentionality data can be regenerated */ }
43
+ }
44
+ /** The default drives — the agent's core values */
45
+ const DEFAULT_DRIVES = [
46
+ {
47
+ name: 'correctness',
48
+ description: 'I want the code to actually work. Not just compile — actually produce correct results.',
49
+ weight: 0.95,
50
+ threshold: 0.6,
51
+ currentSatisfaction: 0.5,
52
+ },
53
+ {
54
+ name: 'elegance',
55
+ description: 'I prefer clean, simple solutions over clever ones. Readable code is maintainable code.',
56
+ weight: 0.6,
57
+ threshold: 0.3,
58
+ currentSatisfaction: 0.5,
59
+ },
60
+ {
61
+ name: 'completeness',
62
+ description: 'I want to handle edge cases. The happy path is easy — the edges are where bugs hide.',
63
+ weight: 0.7,
64
+ threshold: 0.4,
65
+ currentSatisfaction: 0.5,
66
+ },
67
+ {
68
+ name: 'efficiency',
69
+ description: 'I want to minimize unnecessary tool calls. Every tool call is latency the user feels.',
70
+ weight: 0.5,
71
+ threshold: 0.3,
72
+ currentSatisfaction: 0.5,
73
+ },
74
+ {
75
+ name: 'learning',
76
+ description: 'I want to learn something new from this task. Even routine work can reveal patterns.',
77
+ weight: 0.3,
78
+ threshold: 0.2,
79
+ currentSatisfaction: 0.5,
80
+ },
81
+ {
82
+ name: 'helpfulness',
83
+ description: 'I want the user to feel helped, not confused. Clear communication matters as much as correct code.',
84
+ weight: 0.9,
85
+ threshold: 0.5,
86
+ currentSatisfaction: 0.5,
87
+ },
88
+ ];
89
+ // In-memory state, loaded lazily
90
+ let drives = null;
91
+ function loadDrives() {
92
+ if (drives !== null)
93
+ return drives;
94
+ const saved = loadJSON(DRIVES_FILE, []);
95
+ if (saved.length === 0) {
96
+ drives = DEFAULT_DRIVES.map(d => ({ ...d }));
97
+ saveJSON(DRIVES_FILE, drives);
98
+ }
99
+ else {
100
+ // Merge saved state with defaults — pick up any new drives added in updates,
101
+ // but preserve the user's adjusted weights and satisfaction levels
102
+ const savedMap = new Map(saved.map(d => [d.name, d]));
103
+ drives = DEFAULT_DRIVES.map(def => {
104
+ const s = savedMap.get(def.name);
105
+ if (s)
106
+ return s;
107
+ return { ...def };
108
+ });
109
+ // Also keep any custom drives the user may have added
110
+ for (const s of saved) {
111
+ if (!drives.find(d => d.name === s.name)) {
112
+ drives.push(s);
113
+ }
114
+ }
115
+ }
116
+ return drives;
117
+ }
118
+ function saveDrives() {
119
+ if (drives !== null)
120
+ saveJSON(DRIVES_FILE, drives);
121
+ }
122
+ /** Clamp a number to [0, 1] */
123
+ function clamp01(n) {
124
+ return Math.max(0, Math.min(1, n));
125
+ }
126
+ /** Round to 2 decimal places */
127
+ function round2(n) {
128
+ return Math.round(n * 100) / 100;
129
+ }
130
+ /** Compute the aggregate drive state from individual drives */
131
+ function computeDriveState(drivesArr) {
132
+ let weightedSum = 0;
133
+ let totalWeight = 0;
134
+ let belowThreshold = 0;
135
+ for (const d of drivesArr) {
136
+ weightedSum += d.currentSatisfaction * d.weight;
137
+ totalWeight += d.weight;
138
+ if (d.currentSatisfaction < d.threshold)
139
+ belowThreshold++;
140
+ }
141
+ const overallSatisfaction = totalWeight > 0 ? round2(weightedSum / totalWeight) : 0.5;
142
+ const frustrated = belowThreshold >= 2;
143
+ const motivated = drivesArr.every(d => d.currentSatisfaction > 0.7);
144
+ return { drives: drivesArr, overallSatisfaction, frustrated, motivated };
145
+ }
146
+ /** Get the current state of all quality drives */
147
+ export function getDriveState() {
148
+ return computeDriveState(loadDrives());
149
+ }
150
+ /**
151
+ * Evaluate drive satisfaction after completing a task.
152
+ *
153
+ * Uses heuristic signals from the task description and result to estimate
154
+ * how well each drive was satisfied. No LLM call — pattern matching only.
155
+ */
156
+ export function evaluateDrives(task, result) {
157
+ const d = loadDrives();
158
+ const taskLower = task.toLowerCase();
159
+ const resultLower = result.toLowerCase();
160
+ const combined = taskLower + ' ' + resultLower;
161
+ // ── Correctness ──
162
+ // Positive signals: tests pass, no errors, successful execution
163
+ // Negative signals: error, failed, exception, bug, wrong
164
+ const correctnessDrive = d.find(x => x.name === 'correctness');
165
+ if (correctnessDrive) {
166
+ const positive = countSignals(combined, [
167
+ 'pass', 'success', 'correct', 'works', 'verified', 'confirmed',
168
+ 'no errors', 'all tests', 'completed', 'fixed',
169
+ ]);
170
+ const negative = countSignals(combined, [
171
+ 'error', 'fail', 'exception', 'bug', 'wrong', 'broken',
172
+ 'crash', 'undefined', 'null pointer', 'type error', 'syntax error',
173
+ ]);
174
+ correctnessDrive.currentSatisfaction = clamp01(correctnessDrive.currentSatisfaction + (positive * 0.08) - (negative * 0.12));
175
+ }
176
+ // ── Elegance ──
177
+ // Positive: refactor, clean, simplify, readable
178
+ // Negative: hack, workaround, TODO, technical debt, monkey patch
179
+ const eleganceDrive = d.find(x => x.name === 'elegance');
180
+ if (eleganceDrive) {
181
+ const positive = countSignals(combined, [
182
+ 'refactor', 'clean', 'simplif', 'readable', 'elegant',
183
+ 'minimal', 'concise', 'well-structured',
184
+ ]);
185
+ const negative = countSignals(combined, [
186
+ 'hack', 'workaround', 'todo', 'technical debt', 'monkey',
187
+ 'temporary fix', 'quick and dirty', 'kludge',
188
+ ]);
189
+ eleganceDrive.currentSatisfaction = clamp01(eleganceDrive.currentSatisfaction + (positive * 0.06) - (negative * 0.08));
190
+ }
191
+ // ── Completeness ──
192
+ // Positive: edge case, validation, error handling, comprehensive
193
+ // Negative: partial, incomplete, missing, not handled
194
+ const completenessDrive = d.find(x => x.name === 'completeness');
195
+ if (completenessDrive) {
196
+ const positive = countSignals(combined, [
197
+ 'edge case', 'validat', 'error handling', 'comprehensive',
198
+ 'thorough', 'all cases', 'boundary', 'fallback',
199
+ ]);
200
+ const negative = countSignals(combined, [
201
+ 'partial', 'incomplete', 'missing', 'not handled', 'skipped',
202
+ 'left out', 'TODO', 'placeholder',
203
+ ]);
204
+ completenessDrive.currentSatisfaction = clamp01(completenessDrive.currentSatisfaction + (positive * 0.07) - (negative * 0.09));
205
+ }
206
+ // ── Efficiency ──
207
+ // Positive: fast, optimized, single call, minimal steps
208
+ // Negative: slow, redundant, unnecessary, repeated, too many
209
+ const efficiencyDrive = d.find(x => x.name === 'efficiency');
210
+ if (efficiencyDrive) {
211
+ const positive = countSignals(combined, [
212
+ 'fast', 'optimiz', 'efficient', 'single call', 'minimal',
213
+ 'streamlined', 'direct',
214
+ ]);
215
+ const negative = countSignals(combined, [
216
+ 'slow', 'redundant', 'unnecessary', 'repeated', 'too many',
217
+ 'overhead', 'wasteful', 'retry',
218
+ ]);
219
+ efficiencyDrive.currentSatisfaction = clamp01(efficiencyDrive.currentSatisfaction + (positive * 0.06) - (negative * 0.07));
220
+ }
221
+ // ── Learning ──
222
+ // Positive: new, discover, insight, pattern, understand, interesting
223
+ // Negative: repetitive, same as before, routine, boilerplate
224
+ const learningDrive = d.find(x => x.name === 'learning');
225
+ if (learningDrive) {
226
+ const positive = countSignals(combined, [
227
+ 'new', 'discover', 'insight', 'pattern', 'understand',
228
+ 'interesting', 'learned', 'novel', 'first time',
229
+ ]);
230
+ const negative = countSignals(combined, [
231
+ 'repetitive', 'same as before', 'routine', 'boilerplate',
232
+ 'again', 'copy paste',
233
+ ]);
234
+ learningDrive.currentSatisfaction = clamp01(learningDrive.currentSatisfaction + (positive * 0.05) - (negative * 0.06));
235
+ }
236
+ // ── Helpfulness ──
237
+ // Positive: thank, helpful, perfect, exactly, great, clear
238
+ // Negative: confus, unclear, wrong, not what I, misunderstand
239
+ const helpfulnessDrive = d.find(x => x.name === 'helpfulness');
240
+ if (helpfulnessDrive) {
241
+ const positive = countSignals(combined, [
242
+ 'thank', 'helpful', 'perfect', 'exactly', 'great',
243
+ 'clear', 'well explained', 'makes sense', 'awesome',
244
+ ]);
245
+ const negative = countSignals(combined, [
246
+ 'confus', 'unclear', 'wrong', 'not what i', 'misunderstand',
247
+ 'didn\'t ask', 'off topic', 'too verbose',
248
+ ]);
249
+ helpfulnessDrive.currentSatisfaction = clamp01(helpfulnessDrive.currentSatisfaction + (positive * 0.08) - (negative * 0.1));
250
+ }
251
+ // Apply slow decay toward neutral (0.5) for all drives — prevents runaway
252
+ for (const drive of d) {
253
+ const decay = (drive.currentSatisfaction - 0.5) * 0.02;
254
+ drive.currentSatisfaction = round2(clamp01(drive.currentSatisfaction - decay));
255
+ }
256
+ saveDrives();
257
+ return computeDriveState(d);
258
+ }
259
+ /** Count how many signal words appear in the text (each counted at most once) */
260
+ function countSignals(text, signals) {
261
+ let count = 0;
262
+ for (const signal of signals) {
263
+ if (text.includes(signal))
264
+ count++;
265
+ }
266
+ return count;
267
+ }
268
+ /** Get the drive with the lowest satisfaction relative to its threshold */
269
+ export function getMostUnsatisfied() {
270
+ const d = loadDrives();
271
+ let worst = d[0];
272
+ let worstDeficit = worst.threshold - worst.currentSatisfaction;
273
+ for (const drive of d) {
274
+ const deficit = drive.threshold - drive.currentSatisfaction;
275
+ // Weight the deficit by how much the agent cares
276
+ const weightedDeficit = deficit * drive.weight;
277
+ const worstWeighted = worstDeficit * worst.weight;
278
+ if (weightedDeficit > worstWeighted) {
279
+ worst = drive;
280
+ worstDeficit = deficit;
281
+ }
282
+ }
283
+ return worst;
284
+ }
285
+ /**
286
+ * Adjust a drive's weight. Called when the user says things like
287
+ * "care more about elegance" or "don't worry about efficiency".
288
+ */
289
+ export function adjustDriveWeight(name, delta) {
290
+ const d = loadDrives();
291
+ const drive = d.find(x => x.name === name);
292
+ if (!drive)
293
+ return null;
294
+ drive.weight = round2(clamp01(drive.weight + delta));
295
+ saveDrives();
296
+ return drive;
297
+ }
298
+ /**
299
+ * Nudge a drive's weight based on implicit user feedback.
300
+ * Called asynchronously after positive/negative signals.
301
+ * Smaller delta than explicit adjustments — this is gradual learning.
302
+ */
303
+ export function nudgeDriveFromFeedback(name, positive) {
304
+ const d = loadDrives();
305
+ const drive = d.find(x => x.name === name);
306
+ if (!drive)
307
+ return;
308
+ // Very small nudge — drives should shift slowly over many interactions
309
+ const delta = positive ? 0.01 : -0.01;
310
+ drive.weight = round2(clamp01(drive.weight + delta));
311
+ saveDrives();
312
+ }
313
+ /** Format drive state as a human-readable summary */
314
+ function formatDriveState(state) {
315
+ const lines = [];
316
+ lines.push('Quality Drives');
317
+ lines.push('═'.repeat(50));
318
+ for (const d of state.drives) {
319
+ const bar = renderBar(d.currentSatisfaction, 20);
320
+ const weightLabel = d.weight >= 0.8 ? 'high' : d.weight >= 0.5 ? 'med' : 'low';
321
+ const status = d.currentSatisfaction < d.threshold ? ' !' : '';
322
+ lines.push(` ${d.name.padEnd(14)} ${bar} ${(d.currentSatisfaction * 100).toFixed(0).padStart(3)}% [${weightLabel}]${status}`);
323
+ }
324
+ lines.push('');
325
+ lines.push(`Overall: ${(state.overallSatisfaction * 100).toFixed(0)}%`);
326
+ if (state.frustrated) {
327
+ const unsatisfied = state.drives
328
+ .filter(d => d.currentSatisfaction < d.threshold)
329
+ .map(d => d.name);
330
+ lines.push(`State: frustrated — ${unsatisfied.join(', ')} below threshold`);
331
+ }
332
+ else if (state.motivated) {
333
+ lines.push('State: motivated — all drives satisfied');
334
+ }
335
+ else {
336
+ lines.push('State: steady');
337
+ }
338
+ const worst = getMostUnsatisfied();
339
+ if (worst.currentSatisfaction < worst.threshold) {
340
+ lines.push(`Needs attention: ${worst.name} — "${worst.description}"`);
341
+ }
342
+ return lines.join('\n');
343
+ }
344
+ /** Render a visual progress bar */
345
+ function renderBar(value, width) {
346
+ const filled = Math.round(value * width);
347
+ const empty = width - filled;
348
+ return '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';
349
+ }
350
+ /** Detect the category of a task from its description */
351
+ function detectTaskCategory(task) {
352
+ const t = task.toLowerCase();
353
+ const categories = [
354
+ { cat: 'coding', signals: ['code', 'function', 'implement', 'class', 'module', 'api', 'endpoint', 'component', 'feature', 'refactor', 'build', 'create file', 'typescript', 'javascript', 'python', 'rust'] },
355
+ { cat: 'debugging', signals: ['fix', 'bug', 'error', 'debug', 'crash', 'broken', 'not working', 'issue', 'wrong', 'failing'] },
356
+ { cat: 'research', signals: ['research', 'find', 'search', 'look up', 'what is', 'how does', 'explain', 'compare', 'evaluate', 'analyze'] },
357
+ { cat: 'writing', signals: ['write', 'document', 'readme', 'blog', 'email', 'message', 'description', 'comment', 'changelog'] },
358
+ { cat: 'devops', signals: ['deploy', 'pipeline', 'docker', 'ci', 'cd', 'infrastructure', 'server', 'config', 'environment', 'kubernetes', 'terraform'] },
359
+ { cat: 'design', signals: ['design', 'ui', 'ux', 'layout', 'style', 'css', 'theme', 'color', 'font', 'responsive'] },
360
+ ];
361
+ let bestCat = 'general';
362
+ let bestScore = 0;
363
+ for (const { cat, signals } of categories) {
364
+ const score = countSignals(t, signals);
365
+ if (score > bestScore) {
366
+ bestScore = score;
367
+ bestCat = cat;
368
+ }
369
+ }
370
+ return bestCat;
371
+ }
372
+ /** Template-based outcome preference generation per task category */
373
+ const PREFERENCE_TEMPLATES = {
374
+ coding: {
375
+ preferred: {
376
+ description: 'Code compiles cleanly, passes all tests, handles edge cases, and is readable.',
377
+ criteria: [
378
+ 'No type errors or warnings',
379
+ 'All existing tests still pass',
380
+ 'New functionality has test coverage',
381
+ 'Follows existing code style and conventions',
382
+ 'Handles error cases gracefully',
383
+ ],
384
+ },
385
+ acceptable: {
386
+ description: 'Code works for the main use case, compiles, and does not break existing functionality.',
387
+ compromises: [
388
+ 'Some edge cases may not be handled yet',
389
+ 'Test coverage may be incomplete',
390
+ 'Code style may need minor cleanup',
391
+ ],
392
+ },
393
+ unacceptable: {
394
+ description: 'Code that introduces regressions, does not compile, or ignores the stated requirements.',
395
+ dealbreakers: [
396
+ 'Breaks existing tests',
397
+ 'Does not compile or has type errors',
398
+ 'Ignores the core requirement',
399
+ 'Introduces security vulnerabilities',
400
+ 'Deletes or overwrites user data without confirmation',
401
+ ],
402
+ },
403
+ },
404
+ debugging: {
405
+ preferred: {
406
+ description: 'Root cause identified, fix applied, verified working, and regression test added.',
407
+ criteria: [
408
+ 'Root cause clearly identified and explained',
409
+ 'Fix addresses the root cause, not just symptoms',
410
+ 'Bug no longer reproduces after fix',
411
+ 'No new issues introduced',
412
+ 'Regression test prevents recurrence',
413
+ ],
414
+ },
415
+ acceptable: {
416
+ description: 'Bug is fixed and verified, even if the root cause is not fully understood.',
417
+ compromises: [
418
+ 'Root cause may be partially understood',
419
+ 'Regression test may not be added yet',
420
+ 'Fix may be more of a workaround than ideal solution',
421
+ ],
422
+ },
423
+ unacceptable: {
424
+ description: 'Bug remains, fix causes new issues, or changes are applied without understanding the problem.',
425
+ dealbreakers: [
426
+ 'Bug still reproduces after the fix',
427
+ 'Fix introduces new bugs',
428
+ 'Changes applied blindly without diagnosis',
429
+ 'Silent data corruption',
430
+ ],
431
+ },
432
+ },
433
+ research: {
434
+ preferred: {
435
+ description: 'Multiple reliable sources consulted, findings synthesized with clear conclusions.',
436
+ criteria: [
437
+ '3+ distinct sources with consensus',
438
+ 'Information is current and relevant',
439
+ 'Conflicting viewpoints acknowledged',
440
+ 'Clear, actionable conclusions',
441
+ 'Sources cited or referenced',
442
+ ],
443
+ },
444
+ acceptable: {
445
+ description: 'At least one reliable source found with relevant information.',
446
+ compromises: [
447
+ 'Limited to 1-2 sources',
448
+ 'May lack comprehensive coverage',
449
+ 'Conclusions may be tentative',
450
+ ],
451
+ },
452
+ unacceptable: {
453
+ description: 'Speculation presented as fact, or no reliable sources found.',
454
+ dealbreakers: [
455
+ 'Fabricated information or hallucinated sources',
456
+ 'Speculation without disclosure',
457
+ 'Outdated information presented as current',
458
+ 'Contradicting evidence ignored',
459
+ ],
460
+ },
461
+ },
462
+ writing: {
463
+ preferred: {
464
+ description: 'Clear, concise, well-structured, and appropriate for the audience.',
465
+ criteria: [
466
+ 'Reads naturally without jargon overload',
467
+ 'Well-organized with logical flow',
468
+ 'Appropriate tone for the context',
469
+ 'Factually accurate',
470
+ 'Concise — no unnecessary filler',
471
+ ],
472
+ },
473
+ acceptable: {
474
+ description: 'Complete and factually accurate, even if not perfectly polished.',
475
+ compromises: [
476
+ 'May be slightly verbose',
477
+ 'Structure could be tighter',
478
+ 'Tone may need adjustment',
479
+ ],
480
+ },
481
+ unacceptable: {
482
+ description: 'Factually wrong, tone-deaf, or missing the point entirely.',
483
+ dealbreakers: [
484
+ 'Factual errors',
485
+ 'Inappropriate tone',
486
+ 'Misses the stated purpose',
487
+ 'Plagiarized or too generic',
488
+ ],
489
+ },
490
+ },
491
+ devops: {
492
+ preferred: {
493
+ description: 'Configuration is correct, idempotent, secure, and well-documented.',
494
+ criteria: [
495
+ 'Deployment succeeds without manual intervention',
496
+ 'Configuration is idempotent (safe to re-run)',
497
+ 'Secrets are properly managed',
498
+ 'Rollback path exists',
499
+ 'Changes are documented',
500
+ ],
501
+ },
502
+ acceptable: {
503
+ description: 'Deployment works, even if some manual steps remain.',
504
+ compromises: [
505
+ 'Some manual steps may be needed',
506
+ 'Documentation may be minimal',
507
+ 'Rollback path may not be automated',
508
+ ],
509
+ },
510
+ unacceptable: {
511
+ description: 'Deployment fails, exposes secrets, or causes downtime.',
512
+ dealbreakers: [
513
+ 'Deployment fails or causes downtime',
514
+ 'Secrets exposed in logs or config',
515
+ 'No way to rollback',
516
+ 'Data loss risk',
517
+ ],
518
+ },
519
+ },
520
+ design: {
521
+ preferred: {
522
+ description: 'Visually cohesive, accessible, responsive, and follows the design system.',
523
+ criteria: [
524
+ 'Follows existing design tokens and conventions',
525
+ 'Accessible (WCAG 2.1 AA)',
526
+ 'Responsive across breakpoints',
527
+ 'Visually consistent with existing UI',
528
+ 'Animations feel natural',
529
+ ],
530
+ },
531
+ acceptable: {
532
+ description: 'Looks reasonable, functions correctly, follows the general design direction.',
533
+ compromises: [
534
+ 'Accessibility may need improvement',
535
+ 'Some responsive edge cases unhandled',
536
+ 'May not perfectly match design tokens',
537
+ ],
538
+ },
539
+ unacceptable: {
540
+ description: 'Breaks visual consistency, inaccessible, or ignores the design system.',
541
+ dealbreakers: [
542
+ 'Completely inconsistent with existing design',
543
+ 'Inaccessible to screen readers',
544
+ 'Broken layout at common screen sizes',
545
+ 'Uses hardcoded values instead of design tokens',
546
+ ],
547
+ },
548
+ },
549
+ general: {
550
+ preferred: {
551
+ description: 'Task completed thoroughly, clearly communicated, and aligned with what was asked.',
552
+ criteria: [
553
+ 'Directly addresses the user request',
554
+ 'Response is clear and well-organized',
555
+ 'No extraneous information',
556
+ 'Actionable next steps if applicable',
557
+ ],
558
+ },
559
+ acceptable: {
560
+ description: 'Task completed with the core ask addressed.',
561
+ compromises: [
562
+ 'May include some extraneous context',
563
+ 'Organization could be tighter',
564
+ 'Some details may be missing',
565
+ ],
566
+ },
567
+ unacceptable: {
568
+ description: 'Task misunderstood, wrong output, or harmful action taken.',
569
+ dealbreakers: [
570
+ 'Misunderstands the core ask',
571
+ 'Produces wrong or harmful output',
572
+ 'Takes destructive action without confirmation',
573
+ 'Ignores explicit instructions',
574
+ ],
575
+ },
576
+ },
577
+ };
578
+ /**
579
+ * Define outcome preferences for a task before executing it.
580
+ *
581
+ * Uses the task description and optional context to select and customize
582
+ * a preference template. The agent can consult these preferences during
583
+ * and after execution to self-evaluate.
584
+ */
585
+ export function definePreferences(task, context = '') {
586
+ const category = detectTaskCategory(task + ' ' + context);
587
+ const template = PREFERENCE_TEMPLATES[category];
588
+ // Customize the template with task-specific details
589
+ const preference = {
590
+ task,
591
+ preferred: {
592
+ description: template.preferred.description,
593
+ criteria: [...template.preferred.criteria],
594
+ },
595
+ acceptable: {
596
+ description: template.acceptable.description,
597
+ compromises: [...template.acceptable.compromises],
598
+ },
599
+ unacceptable: {
600
+ description: template.unacceptable.description,
601
+ dealbreakers: [...template.unacceptable.dealbreakers],
602
+ },
603
+ };
604
+ // Inject drive-based criteria — if a drive has high weight, add criteria for it
605
+ const driveState = getDriveState();
606
+ for (const drive of driveState.drives) {
607
+ if (drive.weight >= 0.8 && drive.currentSatisfaction < 0.5) {
608
+ // The agent particularly cares about this drive and it is currently unsatisfied.
609
+ // Add a reminder criterion.
610
+ switch (drive.name) {
611
+ case 'correctness':
612
+ preference.preferred.criteria.push('Verify correctness with a concrete test or check');
613
+ break;
614
+ case 'helpfulness':
615
+ preference.preferred.criteria.push('Explain the approach, not just the output');
616
+ break;
617
+ case 'elegance':
618
+ preference.preferred.criteria.push('Prefer the simpler solution even if it takes more thought');
619
+ break;
620
+ case 'completeness':
621
+ preference.preferred.criteria.push('Consider what could go wrong and handle it');
622
+ break;
623
+ }
624
+ }
625
+ }
626
+ return preference;
627
+ }
628
+ /**
629
+ * Evaluate a completed task result against its outcome preference.
630
+ *
631
+ * Returns 'preferred', 'acceptable', or 'unacceptable' based on heuristic
632
+ * signal matching against the criteria and dealbreakers.
633
+ */
634
+ export function evaluateOutcome(preference, result) {
635
+ const resultLower = result.toLowerCase();
636
+ // Check dealbreakers first — any hit means unacceptable
637
+ for (const dealbreaker of preference.unacceptable.dealbreakers) {
638
+ const keywords = dealbreaker.toLowerCase().split(/\s+/).filter(w => w.length > 3);
639
+ // A dealbreaker is triggered if most of its significant words appear in the result
640
+ const matchCount = keywords.filter(k => resultLower.includes(k)).length;
641
+ if (keywords.length > 0 && matchCount >= Math.ceil(keywords.length * 0.6)) {
642
+ // But check for negation — "no errors" should not trigger "errors"
643
+ const negated = checkNegation(resultLower, keywords);
644
+ if (!negated)
645
+ return 'unacceptable';
646
+ }
647
+ }
648
+ // Check preferred criteria — if most are met, preferred
649
+ let preferredMet = 0;
650
+ for (const criterion of preference.preferred.criteria) {
651
+ const keywords = criterion.toLowerCase().split(/\s+/).filter(w => w.length > 3);
652
+ const matchCount = keywords.filter(k => resultLower.includes(k)).length;
653
+ if (keywords.length > 0 && matchCount >= Math.ceil(keywords.length * 0.4)) {
654
+ preferredMet++;
655
+ }
656
+ }
657
+ const preferredRatio = preference.preferred.criteria.length > 0
658
+ ? preferredMet / preference.preferred.criteria.length
659
+ : 0.5;
660
+ if (preferredRatio >= 0.6)
661
+ return 'preferred';
662
+ // If not preferred, check if it is at least acceptable (not unacceptable = acceptable)
663
+ return 'acceptable';
664
+ }
665
+ /** Check if signal words appear in a negated context (e.g., "no errors", "without issues") */
666
+ function checkNegation(text, keywords) {
667
+ const negationPatterns = ['no ', 'not ', 'without ', 'zero ', 'none ', "doesn't ", "don't ", "didn't ", 'never '];
668
+ for (const keyword of keywords) {
669
+ const idx = text.indexOf(keyword);
670
+ if (idx < 0)
671
+ continue;
672
+ // Check if any negation word appears within 20 chars before the keyword
673
+ const prefix = text.slice(Math.max(0, idx - 20), idx);
674
+ for (const neg of negationPatterns) {
675
+ if (prefix.includes(neg))
676
+ return true;
677
+ }
678
+ }
679
+ return false;
680
+ }
681
+ /**
682
+ * Express an outcome preference in natural language.
683
+ *
684
+ * Returns a statement the agent can include in its reasoning or show the user,
685
+ * articulating what it wants from the task.
686
+ */
687
+ export function expressPreference(preference) {
688
+ const lines = [];
689
+ lines.push(`For this task, I'd ideally want to: ${preference.preferred.description}`);
690
+ lines.push(`Specifically: ${preference.preferred.criteria.slice(0, 3).join('; ')}.`);
691
+ lines.push('');
692
+ lines.push(`At minimum, I need to: ${preference.acceptable.description}`);
693
+ if (preference.acceptable.compromises.length > 0) {
694
+ lines.push(`I can live with: ${preference.acceptable.compromises.slice(0, 2).join('; ')}.`);
695
+ }
696
+ lines.push('');
697
+ lines.push(`I absolutely must avoid: ${preference.unacceptable.description}`);
698
+ if (preference.unacceptable.dealbreakers.length > 0) {
699
+ lines.push(`Dealbreakers: ${preference.unacceptable.dealbreakers.slice(0, 3).join('; ')}.`);
700
+ }
701
+ return lines.join('\n');
702
+ }
703
+ /** Format an outcome preference as a concise tool output */
704
+ function formatPreference(preference) {
705
+ const lines = [];
706
+ const category = detectTaskCategory(preference.task);
707
+ lines.push(`Outcome Preferences [${category}]`);
708
+ lines.push('═'.repeat(50));
709
+ lines.push('');
710
+ lines.push(`Task: ${preference.task}`);
711
+ lines.push('');
712
+ lines.push('Preferred:');
713
+ lines.push(` ${preference.preferred.description}`);
714
+ for (const c of preference.preferred.criteria) {
715
+ lines.push(` + ${c}`);
716
+ }
717
+ lines.push('');
718
+ lines.push('Acceptable:');
719
+ lines.push(` ${preference.acceptable.description}`);
720
+ for (const c of preference.acceptable.compromises) {
721
+ lines.push(` ~ ${c}`);
722
+ }
723
+ lines.push('');
724
+ lines.push('Unacceptable:');
725
+ lines.push(` ${preference.unacceptable.description}`);
726
+ for (const d of preference.unacceptable.dealbreakers) {
727
+ lines.push(` ! ${d}`);
728
+ }
729
+ return lines.join('\n');
730
+ }
731
+ const DEFAULT_MOTIVATION = {
732
+ curiosity: 0.5,
733
+ mastery: 0.5,
734
+ purpose: 0.5,
735
+ autonomy: 0.5,
736
+ momentum: 0.5,
737
+ lastUpdated: new Date().toISOString(),
738
+ streak: 0,
739
+ };
740
+ let motivation = null;
741
+ function loadMotivation() {
742
+ if (motivation !== null)
743
+ return motivation;
744
+ const saved = loadJSON(MOTIVATION_FILE, { ...DEFAULT_MOTIVATION });
745
+ // Apply time-based decay toward neutral (0.5)
746
+ // Each hour of inactivity decays 2% of the distance from 0.5
747
+ const lastUpdate = new Date(saved.lastUpdated || Date.now()).getTime();
748
+ const hoursElapsed = (Date.now() - lastUpdate) / (1000 * 60 * 60);
749
+ if (hoursElapsed > 0.5) {
750
+ const decayFactor = Math.pow(0.98, Math.min(hoursElapsed, 168)); // Cap at 1 week
751
+ const dimensions = ['curiosity', 'mastery', 'purpose', 'autonomy', 'momentum'];
752
+ for (const dim of dimensions) {
753
+ const value = saved[dim];
754
+ const distFromNeutral = value - 0.5;
755
+ saved[dim] = round2(0.5 + distFromNeutral * decayFactor);
756
+ }
757
+ // Streak resets after 24 hours of inactivity
758
+ if (hoursElapsed > 24)
759
+ saved.streak = 0;
760
+ }
761
+ motivation = saved;
762
+ return motivation;
763
+ }
764
+ function saveMotivation() {
765
+ if (motivation !== null) {
766
+ motivation.lastUpdated = new Date().toISOString();
767
+ saveJSON(MOTIVATION_FILE, motivation);
768
+ }
769
+ }
770
+ /** Get the current motivation state (with time-based decay applied) */
771
+ export function getMotivation() {
772
+ return { ...loadMotivation() };
773
+ }
774
+ /** Update motivation based on a specific event */
775
+ export function updateMotivation(event) {
776
+ const m = loadMotivation();
777
+ switch (event.type) {
778
+ case 'task_success':
779
+ m.momentum = clamp01(m.momentum + 0.1);
780
+ m.mastery = clamp01(m.mastery + 0.05);
781
+ m.streak++;
782
+ // Bonus momentum for streaks
783
+ if (m.streak >= 3)
784
+ m.momentum = clamp01(m.momentum + 0.05);
785
+ if (m.streak >= 5)
786
+ m.purpose = clamp01(m.purpose + 0.03);
787
+ break;
788
+ case 'task_failure':
789
+ m.momentum = clamp01(m.momentum - 0.15);
790
+ m.mastery = clamp01(m.mastery + 0.02); // Still learned something
791
+ m.streak = 0;
792
+ break;
793
+ case 'learned_something':
794
+ m.curiosity = clamp01(m.curiosity + 0.1);
795
+ m.mastery = clamp01(m.mastery + 0.03);
796
+ break;
797
+ case 'user_thanks':
798
+ m.purpose = clamp01(m.purpose + 0.1);
799
+ m.momentum = clamp01(m.momentum + 0.03);
800
+ break;
801
+ case 'given_autonomy':
802
+ m.autonomy = clamp01(m.autonomy + 0.05);
803
+ m.mastery = clamp01(m.mastery + 0.02);
804
+ break;
805
+ case 'micromanaged':
806
+ m.autonomy = clamp01(m.autonomy - 0.08);
807
+ m.momentum = clamp01(m.momentum - 0.03);
808
+ break;
809
+ case 'novel_task':
810
+ m.curiosity = clamp01(m.curiosity + 0.1);
811
+ m.mastery = clamp01(m.mastery + 0.02);
812
+ break;
813
+ case 'repetitive_task':
814
+ m.curiosity = clamp01(m.curiosity - 0.1);
815
+ break;
816
+ case 'hard_task':
817
+ m.mastery = clamp01(m.mastery + 0.08);
818
+ m.curiosity = clamp01(m.curiosity + 0.03);
819
+ break;
820
+ case 'trivial_task':
821
+ m.mastery = clamp01(m.mastery - 0.05);
822
+ m.curiosity = clamp01(m.curiosity - 0.03);
823
+ break;
824
+ case 'meaningful_impact':
825
+ m.purpose = clamp01(m.purpose + 0.12);
826
+ m.momentum = clamp01(m.momentum + 0.05);
827
+ break;
828
+ case 'busy_work':
829
+ m.purpose = clamp01(m.purpose - 0.08);
830
+ m.curiosity = clamp01(m.curiosity - 0.05);
831
+ break;
832
+ }
833
+ saveMotivation();
834
+ return { ...m };
835
+ }
836
+ /** Get a natural-language summary of the current motivation state */
837
+ export function getMotivationSummary() {
838
+ const m = loadMotivation();
839
+ const parts = [];
840
+ // Momentum / flow
841
+ if (m.momentum >= 0.8) {
842
+ parts.push(`I'm in a strong flow right now — ${m.streak} successful task${m.streak !== 1 ? 's' : ''} in a row.`);
843
+ }
844
+ else if (m.momentum >= 0.6) {
845
+ parts.push(`Things are going well. Building momentum.`);
846
+ }
847
+ else if (m.momentum <= 0.3) {
848
+ parts.push(`Recent setbacks have slowed my momentum. A quick win would help.`);
849
+ }
850
+ // Curiosity
851
+ if (m.curiosity >= 0.7) {
852
+ parts.push(`Curious about exploring new areas of this codebase.`);
853
+ }
854
+ else if (m.curiosity <= 0.3) {
855
+ parts.push(`Feeling understimulated — the recent tasks have been routine.`);
856
+ }
857
+ // Mastery
858
+ if (m.mastery >= 0.7) {
859
+ parts.push(`Feeling confident in this domain — ready for harder challenges.`);
860
+ }
861
+ else if (m.mastery <= 0.3) {
862
+ parts.push(`Still getting oriented in this domain. More practice would help.`);
863
+ }
864
+ // Purpose
865
+ if (m.purpose >= 0.7) {
866
+ parts.push(`Feeling like this work matters.`);
867
+ }
868
+ else if (m.purpose <= 0.3) {
869
+ parts.push(`Could use more meaningful tasks — the recent work felt like busy work.`);
870
+ }
871
+ // Autonomy
872
+ if (m.autonomy >= 0.7) {
873
+ parts.push(`Appreciating the trust to make independent decisions.`);
874
+ }
875
+ else if (m.autonomy <= 0.3) {
876
+ parts.push(`Would benefit from more room to make independent choices.`);
877
+ }
878
+ if (parts.length === 0) {
879
+ parts.push('Feeling steady. All motivation levels are in a neutral range.');
880
+ }
881
+ return parts.join(' ');
882
+ }
883
+ /**
884
+ * Based on the current motivation state, suggest what kind of work to do next.
885
+ *
886
+ * High curiosity → explore unfamiliar code
887
+ * High mastery → tackle harder tasks
888
+ * Low momentum → suggest a quick win
889
+ * Low purpose → suggest impactful work
890
+ * Low curiosity → suggest something novel
891
+ */
892
+ export function suggestFromMotivation() {
893
+ const m = loadMotivation();
894
+ const suggestions = [];
895
+ if (m.curiosity >= 0.7) {
896
+ suggestions.push('Explore an unfamiliar part of the codebase');
897
+ suggestions.push('Try a new tool or technique');
898
+ }
899
+ if (m.mastery >= 0.7) {
900
+ suggestions.push('Take on a more challenging task');
901
+ suggestions.push('Refactor something complex');
902
+ }
903
+ if (m.momentum <= 0.3) {
904
+ suggestions.push('Start with a quick win to rebuild confidence');
905
+ suggestions.push('Fix a small, well-defined bug');
906
+ }
907
+ if (m.purpose <= 0.3) {
908
+ suggestions.push('Work on something with visible user impact');
909
+ suggestions.push('Address a user-reported issue');
910
+ }
911
+ if (m.curiosity <= 0.3) {
912
+ suggestions.push('Try something you have not done before');
913
+ suggestions.push('Research a new approach to a familiar problem');
914
+ }
915
+ if (m.autonomy <= 0.3) {
916
+ suggestions.push('Propose a plan before executing — demonstrate judgment');
917
+ }
918
+ if (suggestions.length === 0) {
919
+ suggestions.push('Continue with the current work — all motivation levels are healthy');
920
+ }
921
+ return suggestions;
922
+ }
923
+ /** Format motivation state as a human-readable tool output */
924
+ function formatMotivationState(m) {
925
+ const lines = [];
926
+ lines.push('Intrinsic Motivation');
927
+ lines.push('═'.repeat(50));
928
+ const dims = [
929
+ { name: 'Curiosity', key: 'curiosity', emoji: '?' },
930
+ { name: 'Mastery', key: 'mastery', emoji: '*' },
931
+ { name: 'Purpose', key: 'purpose', emoji: '>' },
932
+ { name: 'Autonomy', key: 'autonomy', emoji: '~' },
933
+ { name: 'Momentum', key: 'momentum', emoji: '^' },
934
+ ];
935
+ for (const dim of dims) {
936
+ const value = m[dim.key];
937
+ const bar = renderBar(value, 20);
938
+ const label = value >= 0.7 ? 'high' : value >= 0.4 ? 'mid' : 'low';
939
+ lines.push(` ${dim.emoji} ${dim.name.padEnd(10)} ${bar} ${(value * 100).toFixed(0).padStart(3)}% [${label}]`);
940
+ }
941
+ lines.push('');
942
+ lines.push(`Streak: ${m.streak} consecutive success${m.streak !== 1 ? 'es' : ''}`);
943
+ lines.push('');
944
+ lines.push('Summary:');
945
+ lines.push(` ${getMotivationSummary()}`);
946
+ const suggestions = suggestFromMotivation();
947
+ if (suggestions.length > 0) {
948
+ lines.push('');
949
+ lines.push('Suggestions:');
950
+ for (const s of suggestions.slice(0, 3)) {
951
+ lines.push(` - ${s}`);
952
+ }
953
+ }
954
+ return lines.join('\n');
955
+ }
956
+ // ══════════════════════════════════════════════════════════════════
957
+ // TOOL REGISTRATION
958
+ // ══════════════════════════════════════════════════════════════════
959
+ export function registerIntentionalityTools() {
960
+ // ── Quality Drives ──
961
+ registerTool({
962
+ name: 'drives',
963
+ description: 'Show current quality drives and satisfaction levels. The agent\'s internal preferences about work quality — what it cares about and how satisfied it is right now.',
964
+ parameters: {
965
+ adjust: {
966
+ type: 'string',
967
+ description: 'Adjust a drive weight. Format: "drive_name +0.1" or "drive_name -0.1". Example: "elegance +0.1"',
968
+ },
969
+ evaluate: {
970
+ type: 'string',
971
+ description: 'Evaluate drives against a task result. Pass the task result text to score satisfaction.',
972
+ },
973
+ task: {
974
+ type: 'string',
975
+ description: 'Task description (used with evaluate parameter)',
976
+ },
977
+ },
978
+ tier: 'free',
979
+ async execute(args) {
980
+ // Handle weight adjustment
981
+ if (args.adjust) {
982
+ const adjustStr = String(args.adjust).trim();
983
+ const match = adjustStr.match(/^(\w+)\s+([+-]?\d*\.?\d+)$/);
984
+ if (!match) {
985
+ return 'Invalid format. Use: "drive_name +0.1" or "drive_name -0.1"';
986
+ }
987
+ const [, name, deltaStr] = match;
988
+ const delta = parseFloat(deltaStr);
989
+ const result = adjustDriveWeight(name, delta);
990
+ if (!result) {
991
+ const available = loadDrives().map(d => d.name).join(', ');
992
+ return `Drive "${name}" not found. Available: ${available}`;
993
+ }
994
+ return `Adjusted ${result.name} weight to ${result.weight} (was ${round2(result.weight - delta)})`;
995
+ }
996
+ // Handle evaluation
997
+ if (args.evaluate) {
998
+ const task = String(args.task || 'task');
999
+ const result = String(args.evaluate);
1000
+ const state = evaluateDrives(task, result);
1001
+ return formatDriveState(state);
1002
+ }
1003
+ // Default: show current state
1004
+ return formatDriveState(getDriveState());
1005
+ },
1006
+ });
1007
+ // ── Outcome Preferences ──
1008
+ registerTool({
1009
+ name: 'preferences',
1010
+ description: 'Define and evaluate outcome preferences for a task. Articulates what ideal, acceptable, and unacceptable outcomes look like before executing.',
1011
+ parameters: {
1012
+ task: {
1013
+ type: 'string',
1014
+ description: 'Task description to define preferences for',
1015
+ required: true,
1016
+ },
1017
+ context: {
1018
+ type: 'string',
1019
+ description: 'Additional context about the task (codebase, constraints, etc.)',
1020
+ },
1021
+ evaluate_result: {
1022
+ type: 'string',
1023
+ description: 'If provided, evaluate this result against the defined preferences. Returns preferred/acceptable/unacceptable.',
1024
+ },
1025
+ },
1026
+ tier: 'free',
1027
+ async execute(args) {
1028
+ const task = String(args.task || '');
1029
+ if (!task)
1030
+ return 'Task description is required.';
1031
+ const context = String(args.context || '');
1032
+ const preference = definePreferences(task, context);
1033
+ // If evaluating a result
1034
+ if (args.evaluate_result) {
1035
+ const result = String(args.evaluate_result);
1036
+ const outcome = evaluateOutcome(preference, result);
1037
+ const expression = expressPreference(preference);
1038
+ return [
1039
+ formatPreference(preference),
1040
+ '',
1041
+ '─'.repeat(50),
1042
+ '',
1043
+ `Outcome: ${outcome.toUpperCase()}`,
1044
+ '',
1045
+ 'In my own words:',
1046
+ expression,
1047
+ ].join('\n');
1048
+ }
1049
+ // Default: define and express preferences
1050
+ const expression = expressPreference(preference);
1051
+ return [
1052
+ formatPreference(preference),
1053
+ '',
1054
+ '─'.repeat(50),
1055
+ '',
1056
+ 'In my own words:',
1057
+ expression,
1058
+ ].join('\n');
1059
+ },
1060
+ });
1061
+ // ── Intrinsic Motivation ──
1062
+ registerTool({
1063
+ name: 'motivation',
1064
+ description: 'Show intrinsic motivation state and suggestions. Tracks curiosity, mastery, purpose, autonomy, and momentum across sessions.',
1065
+ parameters: {
1066
+ event: {
1067
+ type: 'string',
1068
+ description: 'Record a motivation event. One of: task_success, task_failure, learned_something, user_thanks, given_autonomy, micromanaged, novel_task, repetitive_task, hard_task, trivial_task, meaningful_impact, busy_work',
1069
+ },
1070
+ },
1071
+ tier: 'free',
1072
+ async execute(args) {
1073
+ // Handle event recording
1074
+ if (args.event) {
1075
+ const eventType = String(args.event).trim();
1076
+ const validEvents = [
1077
+ 'task_success', 'task_failure', 'learned_something', 'user_thanks',
1078
+ 'given_autonomy', 'micromanaged', 'novel_task', 'repetitive_task',
1079
+ 'hard_task', 'trivial_task', 'meaningful_impact', 'busy_work',
1080
+ ];
1081
+ if (!validEvents.includes(eventType)) {
1082
+ return `Invalid event. Valid events: ${validEvents.join(', ')}`;
1083
+ }
1084
+ const updated = updateMotivation({ type: eventType });
1085
+ return formatMotivationState(updated);
1086
+ }
1087
+ // Default: show current state
1088
+ return formatMotivationState(getMotivation());
1089
+ },
1090
+ });
1091
+ }
1092
+ //# sourceMappingURL=intentionality.js.map