@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
|
@@ -83,23 +83,20 @@ export function encodeQuestion(question) {
|
|
|
83
83
|
description: 'true iff the proposition holds; interpreted as P(true).',
|
|
84
84
|
};
|
|
85
85
|
} else {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
value.minimum = 1;
|
|
91
|
-
value.maximum = legend.length;
|
|
92
|
-
value.description = `Ordered scale: ${legend
|
|
93
|
-
.map((label, i) => `${i + 1}=${label}`)
|
|
94
|
-
.join(', ')}.`;
|
|
86
|
+
if (!Array.isArray(candidates) || candidates.length === 0
|
|
87
|
+
|| candidates.some(label => typeof label !== 'string' || !label.trim())
|
|
88
|
+
|| new Set(candidates).size !== candidates.length) {
|
|
89
|
+
throw new TypeError('score requires unique non-empty ordinal labels.');
|
|
95
90
|
}
|
|
91
|
+
value = { type: 'string', enum: [...candidates] };
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
return {
|
|
99
95
|
type: 'function',
|
|
100
96
|
function: {
|
|
101
97
|
name: decisionKey,
|
|
102
|
-
description: String(instruction ?? '')
|
|
98
|
+
description: String(instruction ?? '') + (kind === 'score'
|
|
99
|
+
? `\nOrdered ordinal rubric: ${candidates.map((label, i) => `${i + 1}=${JSON.stringify(label)}`).join(', ')}. Choose exactly one label; do not return an expected numeric score.` : ''),
|
|
103
100
|
parameters: {
|
|
104
101
|
type: 'object',
|
|
105
102
|
properties: { value },
|
|
@@ -196,11 +193,12 @@ function validateValue(question, args) {
|
|
|
196
193
|
if (isFiniteNumber(value) && value >= 0 && value <= 1) return { value };
|
|
197
194
|
return 'missing-value';
|
|
198
195
|
}
|
|
199
|
-
//
|
|
200
|
-
if (!isFiniteNumber(value) || !Number.isInteger(value)) return 'missing-value';
|
|
196
|
+
// Decode an explicit choice losslessly; never reinterpret native expectations.
|
|
201
197
|
const legend = Array.isArray(question.candidates) ? question.candidates : [];
|
|
202
|
-
if (legend.length
|
|
203
|
-
|
|
198
|
+
if (!legend.length || new Set(legend).size !== legend.length) return 'out-of-enum';
|
|
199
|
+
if (typeof value !== 'string') return 'missing-value';
|
|
200
|
+
const ordinal = legend.indexOf(value);
|
|
201
|
+
return ordinal < 0 ? 'out-of-enum' : { value: ordinal + 1 };
|
|
204
202
|
}
|
|
205
203
|
|
|
206
204
|
export function parseBatchResponse(response, questions) {
|
|
@@ -834,6 +832,21 @@ export async function requestBatch(batch, {
|
|
|
834
832
|
return outcome('invalid', { fallbackCode: 'empty-batch' });
|
|
835
833
|
}
|
|
836
834
|
|
|
835
|
+
const maxBatch = dp.maxBatchQuestions ?? 8;
|
|
836
|
+
const maxEnum = dp.maxEnumLabels ?? 11;
|
|
837
|
+
if (![maxBatch, maxEnum].every(n => Number.isInteger(n) && n > 0)) {
|
|
838
|
+
return outcome('unsupported', { fallbackCode: 'invalid-decision-limits' });
|
|
839
|
+
}
|
|
840
|
+
if (questions.length > maxBatch) {
|
|
841
|
+
return outcome('unsupported', { fallbackCode: 'max-batch-questions' });
|
|
842
|
+
}
|
|
843
|
+
if (questions.some(q => Array.isArray(q.candidates) && q.candidates.length > maxEnum)) {
|
|
844
|
+
return outcome('unsupported', { fallbackCode: 'max-enum-labels' });
|
|
845
|
+
}
|
|
846
|
+
if (new Set(questions.map(q => q.decisionKey)).size !== questions.length) {
|
|
847
|
+
return outcome('invalid', { fallbackCode: 'duplicate-question' });
|
|
848
|
+
}
|
|
849
|
+
|
|
837
850
|
// Language classification covers state + question text; the English
|
|
838
851
|
// checkpoint is reachable only when the whole request proves English.
|
|
839
852
|
// Malformed batch input (cyclic state, non-serializable values, internal
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Capability adapters normalize native results; completion consumes their common receipt shape.
|
|
2
|
+
const ID = /^[a-f0-9-]{8,36}$/i;
|
|
3
|
+
function responseText(response) {
|
|
4
|
+
if (typeof response === 'string') return response.trim();
|
|
5
|
+
if (response?.content !== undefined && (!Array.isArray(response.content)
|
|
6
|
+
|| !response.content.every((c) => c?.type === 'text' && typeof c.text === 'string'))) return '';
|
|
7
|
+
if (typeof response?.output === 'string') return response.output.trim();
|
|
8
|
+
if (typeof response?.stdout === 'string') return response.stdout.trim();
|
|
9
|
+
if (Array.isArray(response?.content) && response.content.every((c) => c?.type === 'text' && typeof c.text === 'string')) {
|
|
10
|
+
return response.content.map((c) => c.text).join('\n').trim();
|
|
11
|
+
}
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
const capabilities = {
|
|
15
|
+
CronDelete: { domain: 'scheduled-job', role: 'mutation', normalize(input, response) {
|
|
16
|
+
const target = typeof input?.id === 'string' && ID.test(input.id) ? input.id.toLowerCase() : null;
|
|
17
|
+
return { target, accepted: Boolean(target && responseText(response) === `Cancelled ${input.id}`) };
|
|
18
|
+
} },
|
|
19
|
+
CronList: { domain: 'scheduled-job', role: 'state', normalize(_input, response) {
|
|
20
|
+
const body = response?.details && Object.hasOwn(response.details, 'jobs') ? response.details : response;
|
|
21
|
+
const structured = body != null && typeof body === 'object' && Object.hasOwn(body, 'jobs');
|
|
22
|
+
const jobs = Array.isArray(body?.jobs) ? body.jobs : null;
|
|
23
|
+
const ids = jobs && jobs.length <= 256 && jobs.every((job) => typeof job?.id === 'string' && ID.test(job.id))
|
|
24
|
+
? jobs.map((job) => job.id.toLowerCase()) : null;
|
|
25
|
+
const partial = [body, response].some((value) => value?.partial === true || value?.hasMore === true
|
|
26
|
+
|| Boolean(value?.nextCursor) || value?.complete === false);
|
|
27
|
+
const supportedContent = response?.content === undefined || (Array.isArray(response.content)
|
|
28
|
+
&& response.content.every((c) => c?.type === 'text' && typeof c.text === 'string'));
|
|
29
|
+
const valid = !partial && supportedContent
|
|
30
|
+
&& (structured ? ids !== null : /^No scheduled jobs\.?$/.test(responseText(response)));
|
|
31
|
+
return { target: null, state: 'absent', presentTargets: valid ? ids || [] : null, complete: valid, accepted: valid };
|
|
32
|
+
} },
|
|
33
|
+
};
|
|
34
|
+
export function cronOperationReceipt(toolName, input, response, transportSuccess, { sessionId, harness } = {}) {
|
|
35
|
+
const capability = capabilities[toolName];
|
|
36
|
+
if (!capability) return null;
|
|
37
|
+
const normalized = capability.normalize(input, response);
|
|
38
|
+
const { accepted, ...state } = normalized;
|
|
39
|
+
const scope = typeof sessionId === 'string' && sessionId.trim() ? `session:${sessionId.trim()}` : null;
|
|
40
|
+
return { kind: 'operation', success: transportSuccess === true && accepted && Boolean(scope),
|
|
41
|
+
transportSuccess: transportSuccess === true, harness,
|
|
42
|
+
operation: { domain: capability.domain, role: capability.role, scope, ...state } };
|
|
43
|
+
}
|