@ngockhoale/ukit 3.1.9 → 3.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/package.json +1 -1
  3. package/src/cli/commands/memory.js +11 -87
  4. package/src/cli/commands/selfImprove.js +55 -0
  5. package/src/cli/commands/telemetry.js +45 -3
  6. package/src/cli/index.js +7 -0
  7. package/src/core/agentRuntime/adapters.js +177 -0
  8. package/src/core/agentRuntime/contract.js +77 -0
  9. package/src/core/agentRuntime/diagnostics.js +343 -42
  10. package/src/core/agentRuntime/eventStore.js +139 -0
  11. package/src/core/agentRuntime/planCompiler.js +45 -6
  12. package/src/core/agentRuntime/planLibrary.js +43 -7
  13. package/src/core/agentRuntime/plans/bugfix-loop.json +1 -0
  14. package/src/core/agentRuntime/plans/flag-promotion.json +138 -0
  15. package/src/core/agentRuntime/plans/handoff-review-batch.json +1 -0
  16. package/src/core/agentRuntime/plans/release-check.json +2 -1
  17. package/src/core/agentRuntime/promotion.js +147 -9
  18. package/src/core/agentRuntime/runtimeSupport.js +18 -0
  19. package/src/core/agentRuntime/supervisor.js +44 -2
  20. package/src/core/agentRuntime/telemetry.js +123 -0
  21. package/src/core/agentRuntime/vmEngine.js +51 -10
  22. package/src/core/memory/episodes.js +168 -0
  23. package/src/core/metadata.js +21 -0
  24. package/src/core/runInstallPipeline.js +6 -1
  25. package/src/core/runtimeConfig.js +71 -40
  26. package/src/decision/client.js +4 -5
  27. package/src/decision/runtimeDecide.js +183 -5
  28. package/src/learning/selfImprove.js +205 -0
  29. package/src/learning/tunedOverlay.js +112 -0
  30. package/template_project/.claude/hooks/session-episode.sh +35 -15
  31. package/template_project/.claude/ukit/index/route-task.mjs +13 -0
  32. package/template_project/.claude/ukit/index/unic-decision.mjs +1 -2
  33. package/template_project/.claude/ukit/runtime/self-improve-trigger.mjs +98 -0
  34. package/template_project/ukit/storage/config.json +68 -17
@@ -0,0 +1,98 @@
1
+ // self-improve-trigger.mjs — fire-and-forget launcher for `ukit self-improve`.
2
+ //
3
+ // Called from every engine's natural lifecycle point:
4
+ // * Claude Code — SessionEnd (session-episode.sh)
5
+ // * omp — session_stop (same script via ukit-bridge.js)
6
+ // * Codex — route-task.mjs, the helperCommand Codex runs per task
7
+ // (Codex has no hook lifecycle)
8
+ //
9
+ // Contract:
10
+ // * NEVER blocks the caller: the CLI runs detached (own process group,
11
+ // stdio ignored, unref'd); this module returns in a few ms.
12
+ // * NEVER throws; every failure is a silent no-op — self-improvement is
13
+ // background work and must not add noise to a user's session.
14
+ // * Cheap gates first: config opt-out (learning.selfImprove.enabled:false),
15
+ // the pass's own rate-limit stamp, and the CLI locator. The locator
16
+ // (.ukit/storage/cli.json, written by `ukit install`) holds absolute
17
+ // node + bin paths, so GUI-launched hosts without nvm on PATH still work;
18
+ // without it there is no trusted CLI to launch and the trigger no-ops.
19
+ //
20
+ // CLI: node self-improve-trigger.mjs [projectRoot]
21
+
22
+ import fs from 'node:fs';
23
+ import path from 'node:path';
24
+ import { spawn } from 'node:child_process';
25
+ import { fileURLToPath } from 'node:url';
26
+
27
+ export const MIN_INTERVAL_MS = 10 * 60 * 1000;
28
+
29
+ function readJson(filePath) {
30
+ try {
31
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ // The locator is a project file: never trust it blindly. `bin` must be the
38
+ // `bin/ukit` of a directory whose package.json names @ngockhoale/ukit, and
39
+ // `node` must be a node executable — a committed/forged cli.json cannot turn
40
+ // the trigger into an arbitrary-binary launcher.
41
+ export function resolveCliLocator(projectRoot) {
42
+ const locator = readJson(path.join(projectRoot, '.ukit', 'storage', 'cli.json'));
43
+ if (!locator || typeof locator.node !== 'string' || typeof locator.bin !== 'string') return null;
44
+ if (!/^node(\.exe)?$/i.test(path.basename(locator.node))) return null;
45
+ if (path.basename(locator.bin) !== 'ukit' || path.basename(path.dirname(locator.bin)) !== 'bin') return null;
46
+ const pkg = readJson(path.join(path.dirname(locator.bin), '..', 'package.json'));
47
+ if (pkg?.name !== '@ngockhoale/ukit') return null;
48
+ if (!fs.existsSync(locator.node) || !fs.existsSync(locator.bin)) return null;
49
+ return { node: locator.node, bin: locator.bin };
50
+ }
51
+
52
+ export function selfImproveDue(projectRoot, now = Date.now()) {
53
+ const config = readJson(path.join(projectRoot, '.ukit', 'storage', 'config.json'));
54
+ if (config?.learning?.selfImprove?.enabled === false) return false;
55
+ const stamp = readJson(path.join(projectRoot, '.ukit', 'storage', 'learning', 'self-improve.json'));
56
+ const lastRunAt = Date.parse(stamp?.lastRunAt ?? '');
57
+ return !(Number.isFinite(lastRunAt) && now - lastRunAt < MIN_INTERVAL_MS);
58
+ }
59
+
60
+ // Returns true when a pass was launched.
61
+ export function triggerSelfImprove({ projectRoot, now = Date.now(), spawnImpl = spawn } = {}) {
62
+ try {
63
+ if (typeof projectRoot !== 'string' || projectRoot.length === 0) return false;
64
+ const root = path.resolve(projectRoot);
65
+ if (!selfImproveDue(root, now)) return false;
66
+ const cli = resolveCliLocator(root);
67
+ if (!cli) return false;
68
+ const env = { ...process.env };
69
+ // Hook self-deadlines must not leak into a background pass.
70
+ delete env.UKIT_HOOK_DEADLINE_MS;
71
+ const child = spawnImpl(cli.node, [cli.bin, 'self-improve', '--if-due', '--quiet'], {
72
+ cwd: root,
73
+ env,
74
+ detached: true,
75
+ stdio: 'ignore',
76
+ windowsHide: true,
77
+ });
78
+ child.on?.('error', () => {});
79
+ child.unref?.();
80
+ return true;
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+
86
+ function isMainModule() {
87
+ try {
88
+ const self = fs.realpathSync(fileURLToPath(import.meta.url));
89
+ const invoked = process.argv[1] ? fs.realpathSync(process.argv[1]) : '';
90
+ return self === invoked;
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+
96
+ if (isMainModule()) {
97
+ triggerSelfImprove({ projectRoot: process.argv[2] || process.env.CLAUDE_PROJECT_DIR || process.cwd() });
98
+ }
@@ -85,16 +85,16 @@
85
85
  },
86
86
  "routing": {
87
87
  "routeSchema": {
88
- "stage": "off"
88
+ "stage": "default"
89
89
  },
90
90
  "rigor": {
91
- "stage": "shadow"
91
+ "stage": "default"
92
92
  },
93
93
  "fastPath": {
94
- "stage": "shadow"
94
+ "stage": "default"
95
95
  },
96
96
  "escalation": {
97
- "stage": "shadow"
97
+ "stage": "default"
98
98
  }
99
99
  },
100
100
  "decisionPlane": {
@@ -316,7 +316,20 @@
316
316
  },
317
317
  "recall": {
318
318
  "maxRecords": 8
319
- }
319
+ },
320
+ "eligibility": {
321
+ "stage": "default"
322
+ },
323
+ "writer": {
324
+ "stage": "default"
325
+ },
326
+ "index": {
327
+ "stage": "default"
328
+ },
329
+ "decision": {
330
+ "stage": "default"
331
+ },
332
+ "killSwitch": false
320
333
  },
321
334
  "memory": {
322
335
  "enabled": true,
@@ -346,31 +359,69 @@
346
359
  },
347
360
  "tuning": {
348
361
  "enabled": true,
349
- "applyMode": "manual"
362
+ "applyMode": "auto"
363
+ },
364
+ "selfImprove": {
365
+ "enabled": true
350
366
  },
351
367
  "candidates": {
352
- "stage": "shadow",
368
+ "stage": "default",
353
369
  "minOccurrences": 3
354
370
  },
355
371
  "overlays": {
356
- "stage": "off"
372
+ "stage": "default"
357
373
  }
358
374
  },
359
375
  "continuity": {
360
376
  "resumableRun": {
361
- "stage": "off"
377
+ "stage": "default"
362
378
  }
363
379
  },
364
380
  "experiments": {
365
381
  "deliberation": {
366
- "enabled": false
382
+ "enabled": true
367
383
  },
368
384
  "dynamicWorkflow": {
369
- "enabled": false,
385
+ "enabled": true,
370
386
  "maxAttemptsPerFailure": 2,
371
387
  "noProgressCap": 3
372
388
  }
373
389
  },
390
+ "decisionRuntime": {
391
+ "contract": {
392
+ "stage": "shadow"
393
+ },
394
+ "supervisor": {
395
+ "stage": "shadow"
396
+ },
397
+ "scheduler": {
398
+ "stage": "shadow"
399
+ },
400
+ "vm": {
401
+ "stage": "shadow"
402
+ },
403
+ "adapters": {
404
+ "stage": "shadow"
405
+ },
406
+ "context": {
407
+ "stage": "shadow"
408
+ },
409
+ "completion": {
410
+ "stage": "shadow"
411
+ },
412
+ "quality": {
413
+ "stage": "shadow"
414
+ },
415
+ "promotion": {
416
+ "stage": "shadow"
417
+ },
418
+ "resourcePolicy": {
419
+ "stage": "shadow"
420
+ },
421
+ "diagnostics": {
422
+ "stage": "default"
423
+ }
424
+ },
374
425
  "safePatch": {
375
426
  "enabled": true,
376
427
  "strictSharedRisk": true,
@@ -600,15 +651,15 @@
600
651
  },
601
652
  "tu_ghi_episode_ket_thuc_session": {
602
653
  "field": "learning.episodes.autoWrite",
603
- "mac_dinh": false,
604
- "y_nghia": "N\u1ebfu true, hook SessionEnd t\u1ef1 ghi episode v\u00e0o memory v2 khi k\u1ebft th\u00fac session (c\u1ea7n `ukit` c\u00f3 tr\u00ean PATH). M\u1eb7c \u0111\u1ecbnh t\u1eaft \u0111\u1ec3 tr\u00e1nh ghi memory khi user ch\u01b0a mu\u1ed1n.",
605
- "khi_nao_bat": "Ch\u1ec9 b\u1eadt khi anh mu\u1ed1n UKit t\u1ef1 l\u01b0u episode m\u1ed7i session; d\u1eef li\u1ec7u v\u1eabn n\u1eb1m local trong .ukit/storage."
654
+ "mac_dinh": true,
655
+ "y_nghia": "B\u1eadt m\u1eb7c \u0111\u1ecbnh: UKit t\u1ef1 ghi episode v\u00e0o memory v2 cho m\u1ed7i session \u0111\u00e3 k\u1ebft th\u00fac (hook SessionEnd/session_stop + chu k\u1ef3 self-improve qu\u00e9t exec-ledger, ch\u1ea1y \u0111\u01b0\u1ee3c c\u1ea3 tr\u00ean Codex). D\u1eef li\u1ec7u n\u1eb1m local trong .ukit/storage.",
656
+ "khi_nao_bat": "Ch\u1ec9 t\u1eaft (false) khi kh\u00f4ng mu\u1ed1n UKit l\u01b0u episode."
606
657
  },
607
658
  "tat_goi_y_tuning": {
608
659
  "field": "learning.tuning.applyMode",
609
- "mac_dinh": "manual",
610
- "y_nghia": "manual = UKit ch\u1ec9 t\u00ednh v\u00e0 l\u01b0u g\u1ee3i \u00fd tuning v\u00e0o .ukit/storage/learning/suggestions.json, KH\u00d4NG bao gi\u1edd t\u1ef1 s\u1eeda weights/threshold. off = t\u1eaft lu\u00f4n vi\u1ec7c t\u00ednh g\u1ee3i \u00fd.",
611
- "khuyen_nghi": "Gi\u1eef manual. Kh\u00f4ng c\u00f3 ch\u1ebf \u0111\u1ed9 auto-apply \u2014 m\u1ecdi thay \u0111\u1ed5i weights/threshold \u0111\u1ec1u do ng\u01b0\u1eddi d\u00f9ng t\u1ef1 s\u1eeda."
660
+ "mac_dinh": "auto",
661
+ "y_nghia": "auto (m\u1eb7c \u0111\u1ecbnh) = chu k\u1ef3 self-improve t\u1ef1 \u00e1p d\u1ee5ng thay \u0111\u1ed5i tuning t\u1eebng b\u01b0\u1edbc nh\u1ecf, c\u00f3 gi\u1edbi h\u1ea1n (retriever weights, escalation.debugLoopThreshold) v\u00e0o .ukit/storage/learning/tuned.json. manual = ch\u1ec9 ghi g\u1ee3i \u00fd v\u00e0o suggestions.json. off = kh\u00f4ng t\u00ednh.",
662
+ "khuyen_nghi": "Gi\u1eef auto. Rollback: xo\u00e1 .ukit/storage/learning/tuned.json ho\u1eb7c \u0111\u1eb7t manual."
612
663
  }
613
664
  },
614
665
  "version": "Phi\u00ean b\u1ea3n config runtime \u0111i k\u00e8m package UKit.",