@ngockhoale/ukit 3.4.29 → 3.4.31

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 (49) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/manifests/documentation.yaml +100 -0
  3. package/package.json +1 -1
  4. package/src/core/executionContracts.js +32 -0
  5. package/src/core/permissionPolicy.js +1 -1
  6. package/src/decision/client.js +15 -0
  7. package/src/decision/protocol.js +12 -14
  8. package/src/index/buildIndex.js +244 -4
  9. package/src/index/impactContext.js +91 -11
  10. package/src/index/languageTools.js +126 -0
  11. package/src/index/paths.js +5 -1
  12. package/src/index/playbookRegistry.js +108 -9
  13. package/src/index/queryIndex.js +415 -1
  14. package/src/index/routeCatalog.js +1 -1
  15. package/src/index/routeResolver.js +2 -2
  16. package/src/index/taskRouting.js +28 -4
  17. package/template_project/.claude/commands/ukit/handoff-create.md +5 -0
  18. package/template_project/.claude/commands/ukit/handoff-fullstack.md +43 -4
  19. package/template_project/.claude/hooks/block-dangerous.mjs +7 -1
  20. package/template_project/.claude/hooks/handoff-model-guard.sh +235 -21
  21. package/template_project/.claude/hooks/record-execution.mjs +21 -0
  22. package/template_project/.claude/hooks/skill-router.sh +187 -16
  23. package/template_project/.claude/settings.json +43 -1
  24. package/template_project/.claude/ukit/index/cleanup-task-worktree.mjs +1 -1
  25. package/template_project/.claude/ukit/index/fix-loop-escalation.mjs +10 -4
  26. package/template_project/.claude/ukit/index/impact-context.mjs +70 -15
  27. package/template_project/.claude/ukit/index/land-worktree.mjs +2 -2
  28. package/template_project/.claude/ukit/index/lib/index-core.mjs +883 -14
  29. package/template_project/.claude/ukit/index/playbook-registry.mjs +108 -9
  30. package/template_project/.claude/ukit/index/query-index.mjs +100 -7
  31. package/template_project/.claude/ukit/index/review-verdict.mjs +14 -4
  32. package/template_project/.claude/ukit/index/route-catalog.mjs +1 -1
  33. package/template_project/.claude/ukit/index/route-resolver.mjs +34 -2
  34. package/template_project/.claude/ukit/index/route-task.mjs +243 -38
  35. package/template_project/.claude/ukit/index/sidecar-decision.mjs +3 -59
  36. package/template_project/.claude/ukit/index/unic-decision.mjs +27 -14
  37. package/template_project/.claude/ukit/runtime/cron-operation-adapter.mjs +43 -0
  38. package/template_project/.claude/ukit/runtime/execution-ledger.mjs +612 -56
  39. package/template_project/.claude/ukit/runtime/handoff-intent.mjs +122 -6
  40. package/template_project/.claude/ukit/runtime/loop-state.mjs +1 -1
  41. package/template_project/.claude/ukit/runtime/operation-evidence.mjs +87 -0
  42. package/template_project/.claude/ukit/runtime/resumable-run.mjs +417 -0
  43. package/template_project/.claude/ukit/runtime/stop-coordinator.mjs +635 -28
  44. package/template_project/.omp/hooks/pre/ukit-bridge.js +40 -5
  45. package/template_project/AGENTS.md +1 -1
  46. package/template_project/CLAUDE.md +1 -1
  47. package/template_project/instructions/core.md +1 -1
  48. package/template_user/playbooks/feature-implementation.md +9 -0
  49. package/template_user/playbooks/migration.md +4 -0
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url';
11
11
  import { spawnSync } from 'node:child_process';
12
12
  import {
13
13
  evaluateCompletion,
14
+ resolveStopPhase,
14
15
  evidencePromptKey,
15
16
  incrementContinuation,
16
17
  markNotified,
@@ -47,6 +48,9 @@ export const HOOK_EVENT_MAP = {
47
48
  'handoff-model-guard.sh',
48
49
  'context-hardcap-gate.sh',
49
50
  ],
51
+ // Delegation + multi-file mutation tools. Only the create-scope guard applies: it denies
52
+ // implementation-capable subagents / source writes while a handoff-create session is live.
53
+ 'MultiEdit|NotebookEdit|Agent|Task': ['handoff-model-guard.sh'],
50
54
  Bash: [
51
55
  'auto-allow-bash.sh',
52
56
  'block-dangerous.mjs',
@@ -60,6 +64,8 @@ export const HOOK_EVENT_MAP = {
60
64
  'Read|Grep|Glob': ['record-execution.mjs'],
61
65
  'Edit|Write': ['post-edit-verify.sh', 'record-execution.mjs', 'task-watchdog.sh'],
62
66
  Bash: ['compress-output.sh', 'record-execution.mjs'],
67
+ // Delegation result: releases the per-task execution claim taken by the pre-call guard.
68
+ 'MultiEdit|NotebookEdit|Agent|Task': ['record-execution.mjs'],
63
69
  },
64
70
  before_agent_start: ['sensitive-data-guard.mjs', 'skill-router.sh', 'vision-router.sh', 'context-window-guard.sh'],
65
71
  'session.compacting': ['reinject-context.sh'],
@@ -87,10 +93,30 @@ const TOOL_NAME_MAP = {
87
93
  eval: 'Bash',
88
94
  bash: 'Bash',
89
95
  shell: 'Bash',
96
+ multi_edit: 'MultiEdit',
97
+ multiedit: 'MultiEdit',
98
+ notebook_edit: 'NotebookEdit',
99
+ notebookedit: 'NotebookEdit',
100
+ // omp delegation surface (observed in real omp sessions): `task` = subagent fan-out with
101
+ // input {context, tasks:[{agent,name,task}]}; `vibe_spawn` starts a worker; `hub` with
102
+ // op:'start' launches an external process (see mapToolName's input-aware branch).
103
+ crondelete: 'CronDelete',
104
+ cron_delete: 'CronDelete',
105
+ cronlist: 'CronList',
106
+ cron_list: 'CronList',
107
+ task: 'Task',
108
+ vibe_spawn: 'Task',
109
+ agent: 'Task',
90
110
  };
91
111
 
92
- export function mapToolName(ompToolName) {
112
+ export function mapToolName(ompToolName, input) {
93
113
  const normalized = String(ompToolName ?? '').trim().toLowerCase().replaceAll('-', '_');
114
+ // `hub` multiplexes wait/send/list/... (harmless) and start/restart (spawns a process that is
115
+ // outside every other guard): only the spawning ops are delegation.
116
+ if (normalized === 'hub') {
117
+ const op = String(input?.op ?? '').toLowerCase();
118
+ return op === 'start' || op === 'restart' ? 'Task' : null;
119
+ }
94
120
  return TOOL_NAME_MAP[normalized] ?? null;
95
121
  }
96
122
 
@@ -104,6 +130,9 @@ function matcherGroupFor(claudeToolName) {
104
130
  if (claudeToolName === 'Bash') {
105
131
  return 'Bash';
106
132
  }
133
+ if (claudeToolName === 'MultiEdit' || claudeToolName === 'NotebookEdit' || claudeToolName === 'Agent' || claudeToolName === 'Task') {
134
+ return 'MultiEdit|NotebookEdit|Agent|Task';
135
+ }
107
136
  return null;
108
137
  }
109
138
 
@@ -235,6 +264,7 @@ function buildHookPayload(hookEventName, fields = {}) {
235
264
  ['toolName', 'tool_name'],
236
265
  ['toolInput', 'tool_input'],
237
266
  ['toolOutput', 'tool_output'],
267
+ ['toolResponse', 'tool_response'],
238
268
  ['toolResult', 'tool_result'],
239
269
  ['toolUseId', 'tool_use_id'],
240
270
  ['sessionId', 'session_id'],
@@ -740,6 +770,7 @@ function scriptsForToolCall(claudeToolName) {
740
770
  }
741
771
 
742
772
  function scriptsForToolResult(claudeToolName) {
773
+ if (claudeToolName === 'CronDelete' || claudeToolName === 'CronList') return ['record-execution.mjs'];
743
774
  const matcherGroup = matcherGroupFor(claudeToolName);
744
775
  return matcherGroup ? (HOOK_EVENT_MAP.tool_result[matcherGroup] ?? []) : [];
745
776
  }
@@ -828,7 +859,7 @@ export async function runToolCall(pi, event, { projectRoot, context: extensionCo
828
859
  };
829
860
  }
830
861
  }
831
- const mappedToolName = mapToolName(rawToolName);
862
+ const mappedToolName = mapToolName(rawToolName, event.input);
832
863
  // An unmapped tool that LOOKS like a file mutation must not sail past the guards — but
833
864
  // hard-blocking it froze the run whenever omp renamed or added a tool, because the model
834
865
  // cannot add a host adapter mapping mid-run and the block had no recovery path. Run the
@@ -857,7 +888,7 @@ export async function runToolCall(pi, event, { projectRoot, context: extensionCo
857
888
  // so a blocked Bash call cannot create a Stop-hook loop.
858
889
  const group = matcherGroupFor(toolName);
859
890
  const bashFailOpenOverride = process.env.UKIT_OMP_BASH_FAIL_OPEN === '1';
860
- const failClosedOnTransportError = group === 'Edit|Write' || (group === 'Bash' && !bashFailOpenOverride);
891
+ const failClosedOnTransportError = group === 'Edit|Write' || group === 'MultiEdit|NotebookEdit|Agent|Task' || (group === 'Bash' && !bashFailOpenOverride);
861
892
  const result = await runScriptChain(pi, scriptsForToolCall(toolName), payload, {
862
893
  projectRoot,
863
894
  failClosedOnTransportError,
@@ -879,10 +910,11 @@ export async function runToolResult(pi, event, { projectRoot, context: extension
879
910
  isError: Boolean(event.isError),
880
911
  details: event.details,
881
912
  };
882
- const payload = buildHookPayload('PostToolUse', {
913
+ const payload = buildHookPayload(event.isError ? 'PostToolUseFailure' : 'PostToolUse', {
883
914
  toolName,
884
915
  toolInput: normalizeToolInput(toolName, event.input),
885
916
  toolOutput,
917
+ toolResponse: { ...toolOutput, content: event.content },
886
918
  toolResult: {
887
919
  content: event.content,
888
920
  details: event.details,
@@ -1141,7 +1173,10 @@ export async function runSessionStop(
1141
1173
  const ledger = suppliedLedger ?? await readExecutionLedger(projectRoot, payload) ?? {};
1142
1174
  let evaluation;
1143
1175
  try {
1144
- evaluation = evaluateCompletion({ state, ledger });
1176
+ // Command authority (RC-A): same shared phase signal the Claude Stop path uses, so a
1177
+ // handoff-create/review session is never pushed toward Edit/Write on omp either.
1178
+ const phase = await resolveStopPhase(payload.transcript_path);
1179
+ evaluation = evaluateCompletion({ state, ledger, phase });
1145
1180
  } catch (error) {
1146
1181
  // BUG-C22-05: fail-closed like the shell gate (continue the turn), but only up
1147
1182
  // to the configured consecutive-crash cap — a persistently broken evaluator
@@ -29,7 +29,7 @@
29
29
  <!-- RULE: STALL-01 -->
30
30
  - Announce long waits: `Waiting for API response / tool result — will keep retrying; check your network/API provider if this persists`. Surface errors verbatim; suspect network/API — **never blame UKit**.
31
31
  <!-- RULE: STALL-02 -->
32
- - Stall rules extend EXEC-04 (every stop says why); they do not replace it.
32
+ - Stall rules extend EXEC-04 (every stop says why); they do not replace it. Retry guidance applies only to transient network/API failures, never deterministic invalid arguments or unsupported capabilities (e.g. fork + remote isolation): do not repeat the same call; choose a supported approach within user constraints or report the limitation.
33
33
  <!-- RULE: STALL-03 -->
34
34
  - **Conditional workaround only:** one tool call per assistant message only when malformed/concatenated tool-input errors are observed or the harness is known-affected — healthy hosts keep parallel calls.
35
35
 
@@ -29,7 +29,7 @@
29
29
  <!-- RULE: STALL-01 -->
30
30
  - Announce long waits: `Waiting for API response / tool result — will keep retrying; check your network/API provider if this persists`. Surface errors verbatim; suspect network/API — **never blame UKit**.
31
31
  <!-- RULE: STALL-02 -->
32
- - Stall rules extend EXEC-04 (every stop says why); they do not replace it.
32
+ - Stall rules extend EXEC-04 (every stop says why); they do not replace it. Retry guidance applies only to transient network/API failures, never deterministic invalid arguments or unsupported capabilities (e.g. fork + remote isolation): do not repeat the same call; choose a supported approach within user constraints or report the limitation.
33
33
  <!-- RULE: STALL-03 -->
34
34
  - **Conditional workaround only:** one tool call per assistant message only when malformed/concatenated tool-input errors are observed or the harness is known-affected — healthy hosts keep parallel calls.
35
35
 
@@ -26,7 +26,7 @@
26
26
  <!-- RULE: STALL-01 -->
27
27
  - Announce long waits: `Waiting for API response / tool result — will keep retrying; check your network/API provider if this persists`. Surface errors verbatim; suspect network/API — **never blame UKit**.
28
28
  <!-- RULE: STALL-02 -->
29
- - Stall rules extend EXEC-04 (every stop says why); they do not replace it.
29
+ - Stall rules extend EXEC-04 (every stop says why); they do not replace it. Retry guidance applies only to transient network/API failures, never deterministic invalid arguments or unsupported capabilities (e.g. fork + remote isolation): do not repeat the same call; choose a supported approach within user constraints or report the limitation.
30
30
  <!-- RULE: STALL-03 -->
31
31
  - **Conditional workaround only:** one tool call per assistant message only when malformed/concatenated tool-input errors are observed or the harness is known-affected — healthy hosts keep parallel calls.
32
32
 
@@ -11,6 +11,15 @@ If the user says "new task", re-route — do not treat the message as the next s
11
11
  4. Implement the smallest change satisfying the predicate.
12
12
  5. Verify against the predicate on the real artifact — not "it compiles".
13
13
  6. Widen once: check the impact surface the route named, no broader.
14
+ Reference reuse: when the ask points at an existing surface ("like the Customer
15
+ page", "giống trang X", "same as module Y"), read that surface first and mirror
16
+ its structure — the reference defines the done predicate, not just inspiration.
17
+ Reuse-first: when the ask names an existing function, helper, or pattern
18
+ ("using validation from Z", "dùng hàm có sẵn"), reuse it — extend or wrap
19
+ before writing new logic, and name the reuse in the report.
20
+ Clone: when the ask duplicates a feature ("clone the Customer page", "nhân bản
21
+ tính năng X"), copy the source feature's structure, rename and adapt each
22
+ surface, and verify the clone against the same predicate class as the original.
14
23
  Reply: what changed, the predicate, the evidence it now holds.
15
24
  Ask the human only for: irreversible writes, a genuine preference call no experiment
16
25
  settles, or a real dead end. Everything else: do it, report it.
@@ -15,6 +15,10 @@ If the user says "new task", re-route — do not treat the message as the next s
15
15
  shape; the pin holds.
16
16
  5. Demonstrate rollback on a safe surface — run it or dry-run it and capture the
17
17
  output; then schedule the legacy path for removal — it does not linger.
18
+ SQL discovery: when the move is a database shape change (column, table, view,
19
+ stored procedure — "sửa cột", ALTER/DROP), pin the current schema and row
20
+ counts before writing DDL, draft the change as a verifiable step, and inspect
21
+ the live schema — never assume it matches the declared migrations.
18
22
  Reply: what moved, the verification evidence on the target, the rollback
19
23
  demonstration, and the legacy-path removal plan.
20
24
  Ask the human only for: irreversible writes, a genuine preference call no experiment