@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.
- package/CHANGELOG.md +29 -0
- package/manifests/documentation.yaml +100 -0
- package/package.json +1 -1
- package/src/core/executionContracts.js +32 -0
- package/src/core/permissionPolicy.js +1 -1
- package/src/decision/client.js +15 -0
- package/src/decision/protocol.js +12 -14
- package/src/index/buildIndex.js +244 -4
- package/src/index/impactContext.js +91 -11
- package/src/index/languageTools.js +126 -0
- package/src/index/paths.js +5 -1
- package/src/index/playbookRegistry.js +108 -9
- package/src/index/queryIndex.js +415 -1
- package/src/index/routeCatalog.js +1 -1
- package/src/index/routeResolver.js +2 -2
- package/src/index/taskRouting.js +28 -4
- package/template_project/.claude/commands/ukit/handoff-create.md +5 -0
- package/template_project/.claude/commands/ukit/handoff-fullstack.md +43 -4
- package/template_project/.claude/hooks/block-dangerous.mjs +7 -1
- package/template_project/.claude/hooks/handoff-model-guard.sh +235 -21
- package/template_project/.claude/hooks/record-execution.mjs +21 -0
- package/template_project/.claude/hooks/skill-router.sh +187 -16
- package/template_project/.claude/settings.json +43 -1
- package/template_project/.claude/ukit/index/cleanup-task-worktree.mjs +1 -1
- package/template_project/.claude/ukit/index/fix-loop-escalation.mjs +10 -4
- package/template_project/.claude/ukit/index/impact-context.mjs +70 -15
- package/template_project/.claude/ukit/index/land-worktree.mjs +2 -2
- package/template_project/.claude/ukit/index/lib/index-core.mjs +883 -14
- package/template_project/.claude/ukit/index/playbook-registry.mjs +108 -9
- package/template_project/.claude/ukit/index/query-index.mjs +100 -7
- package/template_project/.claude/ukit/index/review-verdict.mjs +14 -4
- package/template_project/.claude/ukit/index/route-catalog.mjs +1 -1
- package/template_project/.claude/ukit/index/route-resolver.mjs +34 -2
- package/template_project/.claude/ukit/index/route-task.mjs +243 -38
- package/template_project/.claude/ukit/index/sidecar-decision.mjs +3 -59
- package/template_project/.claude/ukit/index/unic-decision.mjs +27 -14
- package/template_project/.claude/ukit/runtime/cron-operation-adapter.mjs +43 -0
- package/template_project/.claude/ukit/runtime/execution-ledger.mjs +612 -56
- package/template_project/.claude/ukit/runtime/handoff-intent.mjs +122 -6
- package/template_project/.claude/ukit/runtime/loop-state.mjs +1 -1
- package/template_project/.claude/ukit/runtime/operation-evidence.mjs +87 -0
- package/template_project/.claude/ukit/runtime/resumable-run.mjs +417 -0
- package/template_project/.claude/ukit/runtime/stop-coordinator.mjs +635 -28
- package/template_project/.omp/hooks/pre/ukit-bridge.js +40 -5
- package/template_project/AGENTS.md +1 -1
- package/template_project/CLAUDE.md +1 -1
- package/template_project/instructions/core.md +1 -1
- package/template_user/playbooks/feature-implementation.md +9 -0
- 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
|
-
|
|
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
|