kiokuko-dsh 0.2.28 → 0.2.30

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 (42) hide show
  1. package/dist/dsh/clarification-context.d.ts.map +1 -1
  2. package/dist/dsh/clarification-context.js +24 -7
  3. package/dist/dsh/clarification-context.js.map +1 -1
  4. package/dist/dsh/coding-workflow/host.d.ts +1 -1
  5. package/dist/dsh/coding-workflow/host.d.ts.map +1 -1
  6. package/dist/dsh/coding-workflow/host.js +226 -83
  7. package/dist/dsh/coding-workflow/host.js.map +1 -1
  8. package/dist/dsh/core/host.d.ts.map +1 -1
  9. package/dist/dsh/core/host.js +1026 -315
  10. package/dist/dsh/core/host.js.map +1 -1
  11. package/dist/dsh/core/tasks.d.ts.map +1 -1
  12. package/dist/dsh/core/tasks.js +9 -2
  13. package/dist/dsh/core/tasks.js.map +1 -1
  14. package/dist/dsh/decisions/intent-assessment.d.ts +1 -0
  15. package/dist/dsh/decisions/intent-assessment.d.ts.map +1 -1
  16. package/dist/dsh/decisions/intent-assessment.js +100 -18
  17. package/dist/dsh/decisions/intent-assessment.js.map +1 -1
  18. package/dist/dsh/decisions/request-intent.d.ts.map +1 -1
  19. package/dist/dsh/decisions/request-intent.js +405 -138
  20. package/dist/dsh/decisions/request-intent.js.map +1 -1
  21. package/dist/dsh/host-adapter/admission.d.ts +2 -1
  22. package/dist/dsh/host-adapter/admission.d.ts.map +1 -1
  23. package/dist/dsh/host-adapter/admission.js +877 -312
  24. package/dist/dsh/host-adapter/admission.js.map +1 -1
  25. package/dist/dsh/host-adapter.d.ts.map +1 -1
  26. package/dist/dsh/host-adapter.js +1 -0
  27. package/dist/dsh/host-adapter.js.map +1 -1
  28. package/dist/dsh/on-demand-intake.d.ts +1 -0
  29. package/dist/dsh/on-demand-intake.d.ts.map +1 -1
  30. package/dist/dsh/on-demand-intake.js +1120 -316
  31. package/dist/dsh/on-demand-intake.js.map +1 -1
  32. package/dist/dsh/request-proposals.d.ts +8 -0
  33. package/dist/dsh/request-proposals.d.ts.map +1 -0
  34. package/dist/dsh/request-proposals.js +67 -0
  35. package/dist/dsh/request-proposals.js.map +1 -0
  36. package/dist/dsh/skill-prompts.json +1 -1
  37. package/dist/locale/en.json +1 -1
  38. package/dist/locale/ja.json +1 -1
  39. package/dist/locale/ko.json +1 -1
  40. package/dist/locale/zh-CN.json +1 -1
  41. package/package.json +2 -2
  42. package/skills/coding-ideal-routine-skill/SKILL.md +1 -1
@@ -1,24 +1,57 @@
1
1
  import { z } from 'zod';
2
2
  import { canonicalContentHash } from '../../serialization/validate.js';
3
3
  import { DecisionError } from './contracts.js';
4
- import { TaskSnapshotSchema, TASK_OPERATIONS, validateTaskOperation } from '../task-state.js';
5
- import { correlateRequestIntent, requestIntentDiagnosticId } from './diagnostic-correlation.js';
4
+ import { TaskSnapshotSchema, TASK_OPERATIONS, validateTaskOperation, } from '../task-state.js';
5
+ import { correlateRequestIntent, requestIntentDiagnosticId, } from './diagnostic-correlation.js';
6
6
  export const REQUEST_INTENT_POLICY_VERSION = 'request-intent-task-state-v3';
7
7
  export const MAX_REQUEST_INTENT_BYTES = 32 * 1024;
8
- export const RequestRelationship = z.enum(['new', 'same_scope_continuation', 'scope_change', 'unknown']);
9
- export const RequestAction = z.enum(['answer', 'research', 'verify', 'change', 'operate', 'unknown']);
10
- export const RequestChangeScope = z.enum(['implementation', 'document', 'unknown']);
11
- const safeId = z.string().min(1).max(256).refine(value => !/[\p{Cc}\p{Cf}]/u.test(value));
8
+ export const RequestRelationship = z.enum([
9
+ 'new',
10
+ 'same_scope_continuation',
11
+ 'scope_change',
12
+ 'unknown',
13
+ ]);
14
+ export const RequestAction = z.enum([
15
+ 'answer',
16
+ 'research',
17
+ 'verify',
18
+ 'change',
19
+ 'operate',
20
+ 'unknown',
21
+ ]);
22
+ export const RequestChangeScope = z.enum([
23
+ 'implementation',
24
+ 'document',
25
+ 'unknown',
26
+ ]);
27
+ const safeId = z
28
+ .string()
29
+ .min(1)
30
+ .max(256)
31
+ .refine((value) => !/[\p{Cc}\p{Cf}]/u.test(value));
12
32
  const boundedText = z.string().min(1).max(8192);
13
- const messageReference = z.object({ id: safeId, digest: z.string().regex(/^[a-f0-9]{64}$/u) }).strict();
14
- const proposalReference = z.object({
33
+ const messageReference = z
34
+ .object({ id: safeId, digest: z.string().regex(/^[a-f0-9]{64}$/u) })
35
+ .strict();
36
+ const proposalReference = z
37
+ .object({
15
38
  id: safeId,
16
39
  revision: z.number().int().min(0).nullable(),
17
40
  digest: z.string().regex(/^[a-f0-9]{64}$/u),
18
41
  text: boundedText,
19
- field: z.enum(['readPaths', 'writePaths', 'excludedPaths', 'constraints', 'completion']).optional(),
20
- }).strict();
21
- const activeGoal = z.object({
42
+ field: z
43
+ .enum([
44
+ 'readPaths',
45
+ 'writePaths',
46
+ 'excludedPaths',
47
+ 'constraints',
48
+ 'completion',
49
+ ])
50
+ .optional(),
51
+ })
52
+ .strict();
53
+ const activeGoal = z
54
+ .object({
22
55
  runId: safeId,
23
56
  workflowRevision: z.number().int().min(1).optional(),
24
57
  goalId: safeId.optional(),
@@ -28,8 +61,10 @@ const activeGoal = z.object({
28
61
  target: boundedText.optional(),
29
62
  outOfScope: z.array(boundedText).max(32).default([]),
30
63
  constraints: z.array(boundedText).max(32).default([]),
31
- }).strict();
32
- export const RequestIntentContextSchema = z.object({
64
+ })
65
+ .strict();
66
+ export const RequestIntentContextSchema = z
67
+ .object({
33
68
  /** Exact current user request. No transcript or other session is accepted. */
34
69
  task: z.string().min(1).max(16_384),
35
70
  messageRefs: z.array(messageReference).min(1).max(16),
@@ -37,27 +72,48 @@ export const RequestIntentContextSchema = z.object({
37
72
  /** SQLite task identity and revision, independent of an implementation-only Goal. */
38
73
  activeTask: TaskSnapshotSchema.optional(),
39
74
  proposals: z.array(proposalReference).max(8).default([]),
40
- }).strict().superRefine((value, context) => {
41
- if (new Set(value.messageRefs.map(item => item.id)).size !== value.messageRefs.length) {
42
- context.addIssue({ code: 'custom', path: ['messageRefs'], message: 'Message references must be unique' });
75
+ })
76
+ .strict()
77
+ .superRefine((value, context) => {
78
+ if (new Set(value.messageRefs.map((item) => item.id)).size !==
79
+ value.messageRefs.length) {
80
+ context.addIssue({
81
+ code: 'custom',
82
+ path: ['messageRefs'],
83
+ message: 'Message references must be unique',
84
+ });
43
85
  }
44
- if (new Set(value.proposals.map(item => item.id)).size !== value.proposals.length) {
45
- context.addIssue({ code: 'custom', path: ['proposals'], message: 'Proposal references must be unique' });
86
+ if (new Set(value.proposals.map((item) => item.id)).size !==
87
+ value.proposals.length) {
88
+ context.addIssue({
89
+ code: 'custom',
90
+ path: ['proposals'],
91
+ message: 'Proposal references must be unique',
92
+ });
46
93
  }
47
- if (Buffer.byteLength(JSON.stringify(value), 'utf8') > MAX_REQUEST_INTENT_BYTES) {
48
- context.addIssue({ code: 'custom', message: 'Request intent context exceeds its byte limit' });
94
+ if (Buffer.byteLength(JSON.stringify(value), 'utf8') >
95
+ MAX_REQUEST_INTENT_BYTES) {
96
+ context.addIssue({
97
+ code: 'custom',
98
+ message: 'Request intent context exceeds its byte limit',
99
+ });
49
100
  }
50
101
  });
51
102
  export { requestIntentMessageRefs } from './message-refs.js';
52
103
  export function createRequestIntentContext(input) {
53
104
  // Request-scoped diagnostics are not new task authority and must not invalidate their own retry context.
54
105
  const { unresolved: _unresolved, ...activeTask } = input.activeTask ?? {};
55
- const parsed = RequestIntentContextSchema.safeParse({ task: input.task, messageRefs: input.messageRefs,
106
+ const parsed = RequestIntentContextSchema.safeParse({
107
+ task: input.task,
108
+ messageRefs: input.messageRefs,
56
109
  ...(input.activeGoal === undefined ? {} : { activeGoal: input.activeGoal }),
57
- ...(input.activeTask === undefined ? {} : { activeTask }), proposals: input.proposals ?? [] });
110
+ ...(input.activeTask === undefined ? {} : { activeTask }),
111
+ proposals: input.proposals ?? [],
112
+ });
58
113
  return parsed.success ? parsed.data : undefined;
59
114
  }
60
- export const RequestIntentSubmissionSchema = z.object({
115
+ export const RequestIntentSubmissionSchema = z
116
+ .object({
61
117
  relation: RequestRelationship,
62
118
  action: RequestAction,
63
119
  /** Missing legacy values normalize to unknown and can never authorize a change. */
@@ -66,29 +122,74 @@ export const RequestIntentSubmissionSchema = z.object({
66
122
  taskOperation: z.enum(TASK_OPERATIONS).optional(),
67
123
  /** Exact opaque references supplied in the current host context. */
68
124
  evidenceRefs: z.array(safeId).min(1).max(32),
69
- }).strict().superRefine((value, context) => {
125
+ })
126
+ .strict()
127
+ .superRefine((value, context) => {
70
128
  if (new Set(value.evidenceRefs).size !== value.evidenceRefs.length) {
71
- context.addIssue({ code: 'custom', path: ['evidenceRefs'], message: 'Evidence references must be unique' });
129
+ context.addIssue({
130
+ code: 'custom',
131
+ path: ['evidenceRefs'],
132
+ message: 'Evidence references must be unique',
133
+ });
72
134
  }
73
135
  });
74
136
  const relationshipChoices = [
75
- { id: 'new', description: 'A new task, not a continuation or revision of the active Goal or listed proposals.' },
76
- { id: 'same_scope_continuation', description: 'Continue, inspect, verify, or finish the current source-bound conversation or active task without changing its target, exclusions, or requirements. A formal Goal is optional.' },
77
- { id: 'scope_change', description: 'Change the active Goal, target, exclusions, requirements, or adopt proposals that expand or alter its scope.' },
78
- { id: 'unknown', description: 'The current message does not establish its relationship to the active Goal or proposals.' },
137
+ {
138
+ id: 'new',
139
+ description: 'A new task, not a continuation or revision of the active Goal or listed proposals.',
140
+ },
141
+ {
142
+ id: 'same_scope_continuation',
143
+ description: 'Continue, inspect, verify, or finish the current source-bound conversation or active task without changing its target, exclusions, or requirements. A formal Goal is optional.',
144
+ },
145
+ {
146
+ id: 'scope_change',
147
+ description: 'Change the active Goal, target, exclusions, requirements, or adopt proposals that expand or alter its scope.',
148
+ },
149
+ {
150
+ id: 'unknown',
151
+ description: 'The current message does not establish its relationship to the active Goal or proposals.',
152
+ },
79
153
  ];
80
154
  const actionChoices = [
81
- { id: 'answer', description: 'Answer, explain, or discuss; no tool-backed work is requested.' },
82
- { id: 'research', description: 'Inspect sources or gather information without changing project state.' },
83
- { id: 'verify', description: 'Inspect behavior, run or review checks, or establish whether something works.' },
84
- { id: 'change', description: 'Modify code, files, configuration, or another project artifact.' },
85
- { id: 'operate', description: 'Perform deployment, restart, installation, or other system/service operation.' },
86
- { id: 'unknown', description: 'The requested action or target is ambiguous, or not supported by the current evidence. Multiple compatible requested actions alone are not ambiguity.' },
155
+ {
156
+ id: 'answer',
157
+ description: 'Answer, explain, or discuss; no tool-backed work is requested.',
158
+ },
159
+ {
160
+ id: 'research',
161
+ description: 'Inspect sources or gather information without changing project state.',
162
+ },
163
+ {
164
+ id: 'verify',
165
+ description: 'Inspect behavior, run or review checks, or establish whether something works.',
166
+ },
167
+ {
168
+ id: 'change',
169
+ description: 'Modify code, files, configuration, or another project artifact.',
170
+ },
171
+ {
172
+ id: 'operate',
173
+ description: 'Perform deployment, restart, installation, or other system/service operation.',
174
+ },
175
+ {
176
+ id: 'unknown',
177
+ description: 'The requested action or target is ambiguous, or not supported by the current evidence. Multiple compatible requested actions alone are not ambiguity.',
178
+ },
87
179
  ];
88
180
  const changeScopeChoices = [
89
- { id: 'implementation', description: 'The requested artifact change affects code, tests, runtime behavior, or configuration behavior. If documentation is changed together with implementation, choose implementation.' },
90
- { id: 'document', description: 'The requested change is prose-only documentation and does not change code, tests, or behavior configuration.' },
91
- { id: 'unknown', description: 'The requested artifact scope cannot be determined from the current request and supplied Goal/proposal references.' },
181
+ {
182
+ id: 'implementation',
183
+ description: 'The requested artifact change affects code, tests, runtime behavior, or configuration behavior. If documentation is changed together with implementation, choose implementation.',
184
+ },
185
+ {
186
+ id: 'document',
187
+ description: 'The requested change is prose-only documentation and does not change code, tests, or behavior configuration.',
188
+ },
189
+ {
190
+ id: 'unknown',
191
+ description: 'The requested artifact scope cannot be determined from the current request and supplied Goal/proposal references.',
192
+ },
92
193
  ];
93
194
  /** Finite choices preserve both the action and the proposals it refers to. */
94
195
  export function buildRequestIntentBatch(raw) {
@@ -106,23 +207,71 @@ export function buildRequestIntentBatch(raw) {
106
207
  boundary: 'Use only this exact current request and the supplied active Goal/proposal candidates. Never use another chat, infer a free-text target, or grant execution permission.',
107
208
  },
108
209
  questions: [
109
- { id: 'request-relation', instructions: 'Classify the relationship of the current user message to the supplied active Goal and proposals. Respect negation, quotation, actor, and omitted references. An exact continuation phrase is not required. If the intended reference or scope is not established, choose unknown.', choices: [...relationshipChoices], abstainId: 'unknown' },
110
- { id: 'request-action', instructions: 'Classify the action the user requests now, not a quoted example or an action they forbid. “See whether it works” is verify/research, not a file edit. When explanation or inspection accompanies an explicit edit request, choose change and preserve every requested action and prohibition. Do not turn a verification-only request into an edit. Classification grants no permission. If the action or target is unclear, choose unknown.', choices: [...actionChoices], abstainId: 'unknown' },
111
- { id: 'request-change-scope', instructions: 'For action=change, classify the exact current requested artifact scope using only the current message and supplied Goal/proposal references. Code, tests, runtime behavior, or behavior configuration is implementation; a request that mixes implementation with documentation is implementation. Prose-only document edits are document. Do not infer scope from an advisory task label, wording style, or whether the message happens to say “write”. For other actions choose unknown.', choices: [...changeScopeChoices], abstainId: 'unknown' },
112
- ...(parsed.data.activeTask ? [{ id: 'request-task-operation', instructions: 'Propose only a structured state operation clearly requested by the current human message for the exact SQLite task. Do not mutate state, fabricate completion or stop evidence, or infer approval from a keyword. Choose none when no state transition is requested; choose unknown when ambiguous. Terminal resume requires an explicit user operation.',
113
- choices: [{ id: 'none', description: 'No task state operation is requested.' },
114
- ...parsed.data.activeTask.allowedOps.map(operation => ({ id: operation, description: `Propose ${operation} for this exact task revision; host validation and evidence still apply.` })),
115
- { id: 'unknown', description: 'The intended operation or target is uncertain.' }], abstainId: 'unknown' }] : []),
116
- ...parsed.data.proposals.map((proposal, index) => ({ id: `request-proposal-${index}`,
210
+ {
211
+ id: 'request-relation',
212
+ instructions: 'Classify the relationship of the current user message to the supplied active Goal and proposals. Respect negation, quotation, actor, and omitted references. An exact continuation phrase is not required. If the intended reference or scope is not established, choose unknown.',
213
+ choices: [...relationshipChoices],
214
+ abstainId: 'unknown',
215
+ },
216
+ {
217
+ id: 'request-action',
218
+ instructions: 'Classify the action the user requests now, not a quoted example or an action they forbid. “See whether it works” is verify/research, not a file edit. When explanation or inspection accompanies an explicit edit request, choose change and preserve every requested action and prohibition. Do not turn a verification-only request into an edit. Classification grants no permission. If the action or target is unclear, choose unknown.',
219
+ choices: [...actionChoices],
220
+ abstainId: 'unknown',
221
+ },
222
+ {
223
+ id: 'request-change-scope',
224
+ instructions: 'For action=change, classify the exact current requested artifact scope using only the current message and supplied Goal/proposal references. Code, tests, runtime behavior, or behavior configuration is implementation; a request that mixes implementation with documentation is implementation. Prose-only document edits are document. Do not infer scope from an advisory task label, wording style, or whether the message happens to say “write”. For other actions choose unknown.',
225
+ choices: [...changeScopeChoices],
226
+ abstainId: 'unknown',
227
+ },
228
+ ...(parsed.data.activeTask
229
+ ? [
230
+ {
231
+ id: 'request-task-operation',
232
+ instructions: 'Propose only a structured state operation clearly requested by the current human message for the exact SQLite task. Do not mutate state, fabricate completion or stop evidence, or infer approval from a keyword. Choose none when no state transition is requested; choose unknown when ambiguous. Terminal resume requires an explicit user operation.',
233
+ choices: [
234
+ {
235
+ id: 'none',
236
+ description: 'No task state operation is requested.',
237
+ },
238
+ ...parsed.data.activeTask.allowedOps.map((operation) => ({
239
+ id: operation,
240
+ description: `Propose ${operation} for this exact task revision; host validation and evidence still apply.`,
241
+ })),
242
+ {
243
+ id: 'unknown',
244
+ description: 'The intended operation or target is uncertain.',
245
+ },
246
+ ],
247
+ abstainId: 'unknown',
248
+ },
249
+ ]
250
+ : []),
251
+ ...parsed.data.proposals.map((proposal, index) => ({
252
+ id: `request-proposal-${index}`,
117
253
  instructions: `Does the current user instruction adopt or refer to proposal ${JSON.stringify(proposal.id)}? Interpret omitted Japanese subjects and references such as 全部, それ, 続けて against the supplied context. Quoted or forbidden actions are not approvals.`,
118
- choices: [{ id: 'selected', description: 'The current request refers to this proposal.' },
119
- { id: 'not_selected', description: 'This proposal is not referenced by the current request.' },
120
- { id: 'unknown', description: 'The reference cannot be resolved from the supplied context.' }], abstainId: 'unknown' })),
254
+ choices: [
255
+ {
256
+ id: 'selected',
257
+ description: 'The current request refers to this proposal.',
258
+ },
259
+ {
260
+ id: 'not_selected',
261
+ description: 'This proposal is not referenced by the current request.',
262
+ },
263
+ {
264
+ id: 'unknown',
265
+ description: 'The reference cannot be resolved from the supplied context.',
266
+ },
267
+ ],
268
+ abstainId: 'unknown',
269
+ })),
121
270
  ],
122
271
  };
123
272
  }
124
273
  export function requestIntentReferenceIds(context) {
125
- const refs = context.messageRefs.map(item => `message:${canonicalContentHash(item)}`);
274
+ const refs = context.messageRefs.map((item) => `message:${canonicalContentHash(item)}`);
126
275
  if (context.activeGoal) {
127
276
  refs.push(`run:${canonicalContentHash(context.activeGoal.runId)}`);
128
277
  if (context.activeGoal.workflowRevision !== undefined)
@@ -134,37 +283,61 @@ export function requestIntentReferenceIds(context) {
134
283
  }
135
284
  if (context.activeTask)
136
285
  refs.push(`task:${canonicalContentHash([context.activeTask.taskId, context.activeTask.revision, context.activeTask.generation])}`);
137
- refs.push(...context.proposals.map(item => `proposal:${canonicalContentHash([item.id, item.revision, item.digest])}`));
286
+ refs.push(...context.proposals.map((item) => `proposal:${canonicalContentHash([item.id, item.revision, item.digest])}`));
138
287
  return refs;
139
288
  }
140
289
  /** Current identity is mandatory; proposal references are optional evidence of selection. */
141
290
  export function requestIntentRequiredReferenceIds(context) {
142
- return requestIntentReferenceIds(context).filter(reference => !reference.startsWith('proposal:'));
291
+ return requestIntentReferenceIds(context).filter((reference) => !reference.startsWith('proposal:'));
143
292
  }
144
293
  function evidenceFor(context, selectedProposalIds = new Set()) {
145
294
  const active = context.activeGoal;
146
295
  return {
147
296
  requestDigest: canonicalContentHash(context.task),
148
- messageRefs: context.messageRefs.map(item => ({ ...item })),
149
- ...(active ? {
150
- runId: active.runId,
151
- ...(active.workflowRevision === undefined ? {} : { workflowRevision: active.workflowRevision }),
152
- ...(active.goalRevision === undefined ? {} : { goalRevision: active.goalRevision }),
153
- ...(active.scopeRevision === undefined ? {} : { scopeRevision: active.scopeRevision }),
154
- } : {}),
155
- ...(context.activeTask ? { taskId: context.activeTask.taskId, taskRevision: context.activeTask.revision,
156
- taskGeneration: context.activeTask.generation } : {}),
157
- proposalRefs: context.proposals.filter(item => selectedProposalIds.has(item.id))
297
+ messageRefs: context.messageRefs.map((item) => ({ ...item })),
298
+ ...(active
299
+ ? {
300
+ runId: active.runId,
301
+ ...(active.workflowRevision === undefined
302
+ ? {}
303
+ : { workflowRevision: active.workflowRevision }),
304
+ ...(active.goalRevision === undefined
305
+ ? {}
306
+ : { goalRevision: active.goalRevision }),
307
+ ...(active.scopeRevision === undefined
308
+ ? {}
309
+ : { scopeRevision: active.scopeRevision }),
310
+ }
311
+ : {}),
312
+ ...(context.activeTask
313
+ ? {
314
+ taskId: context.activeTask.taskId,
315
+ taskRevision: context.activeTask.revision,
316
+ taskGeneration: context.activeTask.generation,
317
+ }
318
+ : {}),
319
+ proposalRefs: context.proposals
320
+ .filter((item) => selectedProposalIds.has(item.id))
158
321
  .map(({ id, revision, digest }) => ({ id, revision, digest })),
159
322
  };
160
323
  }
161
324
  export function unresolvedRequestIntent(context, reason, providerSent = false) {
162
- return { relation: 'unknown', action: 'unknown', changeScope: 'unknown', source: 'unresolved', policyVersion: REQUEST_INTENT_POLICY_VERSION,
163
- evidence: evidenceFor(context), providerSent, unresolvedReason: reason };
325
+ return {
326
+ relation: 'unknown',
327
+ action: 'unknown',
328
+ changeScope: 'unknown',
329
+ source: 'unresolved',
330
+ policyVersion: REQUEST_INTENT_POLICY_VERSION,
331
+ evidence: evidenceFor(context),
332
+ providerSent,
333
+ unresolvedReason: reason,
334
+ };
164
335
  }
165
336
  export function requestIntentIsUnresolved(value) {
166
- return value === undefined || value.relation === 'unknown' || value.action === 'unknown'
167
- || value.action === 'change' && value.changeScope === 'unknown';
337
+ return (value === undefined ||
338
+ value.relation === 'unknown' ||
339
+ value.action === 'unknown' ||
340
+ (value.action === 'change' && value.changeScope === 'unknown'));
168
341
  }
169
342
  /** Validate exact current-session evidence before checking whether its Goal supports the selected relation. */
170
343
  export function validateMainModelRequestIntent(raw, context) {
@@ -174,32 +347,55 @@ export function validateMainModelRequestIntent(raw, context) {
174
347
  if (parsed.data.relation === 'unknown' || parsed.data.action === 'unknown')
175
348
  return { ok: false, reason: 'unresolved_intent' };
176
349
  const allowed = new Set(requestIntentReferenceIds(context));
177
- if (parsed.data.evidenceRefs.some(reference => !allowed.has(reference)))
350
+ if (parsed.data.evidenceRefs.some((reference) => !allowed.has(reference)))
178
351
  return { ok: false, reason: 'unknown_reference' };
179
352
  const requiredRefs = requestIntentRequiredReferenceIds(context);
180
- if (!requiredRefs.every(reference => parsed.data.evidenceRefs.includes(reference)))
353
+ if (!requiredRefs.every((reference) => parsed.data.evidenceRefs.includes(reference)))
181
354
  return { ok: false, reason: 'missing_reference' };
182
- if (parsed.data.relation === 'scope_change' && !context.activeGoal && !context.activeTask)
355
+ if (parsed.data.relation === 'scope_change' &&
356
+ !context.activeGoal &&
357
+ !context.activeTask &&
358
+ !context.proposals.some((item) => parsed.data.evidenceRefs.includes(`proposal:${canonicalContentHash([item.id, item.revision, item.digest])}`)))
183
359
  return { ok: false, reason: 'no_active_goal' };
184
360
  if (parsed.data.taskOperation) {
185
361
  if (!context.activeTask)
186
362
  return { ok: false, reason: 'invalid_task_operation' };
187
363
  try {
188
- validateTaskOperation(context.activeTask, { operation: parsed.data.taskOperation,
189
- operationId: `intent:${canonicalContentHash(context)}`, expectedRevision: context.activeTask.revision,
190
- expectedGeneration: context.activeTask.generation, reason: 'Advisory interpretation of the current human request', evidence: [], explicit: false });
364
+ validateTaskOperation(context.activeTask, {
365
+ operation: parsed.data.taskOperation,
366
+ operationId: `intent:${canonicalContentHash(context)}`,
367
+ expectedRevision: context.activeTask.revision,
368
+ expectedGeneration: context.activeTask.generation,
369
+ reason: 'Advisory interpretation of the current human request',
370
+ evidence: [],
371
+ explicit: false,
372
+ });
191
373
  }
192
374
  catch {
193
375
  return { ok: false, reason: 'invalid_task_operation' };
194
376
  }
195
377
  }
196
- const selectedProposalIds = new Set(context.proposals.filter(item => parsed.data.evidenceRefs
197
- .includes(`proposal:${canonicalContentHash([item.id, item.revision, item.digest])}`)).map(item => item.id));
198
- const changeScope = parsed.data.action === 'change' ? parsed.data.changeScope ?? 'unknown' : 'unknown';
199
- return { ok: true, decision: { relation: parsed.data.relation, action: parsed.data.action, changeScope,
378
+ const selectedProposalIds = new Set(context.proposals
379
+ .filter((item) => parsed.data.evidenceRefs.includes(`proposal:${canonicalContentHash([item.id, item.revision, item.digest])}`))
380
+ .map((item) => item.id));
381
+ const changeScope = parsed.data.action === 'change'
382
+ ? (parsed.data.changeScope ?? 'unknown')
383
+ : 'unknown';
384
+ return {
385
+ ok: true,
386
+ decision: {
387
+ relation: parsed.data.relation,
388
+ action: parsed.data.action,
389
+ changeScope,
200
390
  source: 'main-model',
201
- policyVersion: REQUEST_INTENT_POLICY_VERSION, evidence: evidenceFor(context, selectedProposalIds), providerSent: false,
202
- ...(parsed.data.taskOperation ? { taskOperation: parsed.data.taskOperation } : {}) } };
391
+ policyVersion: REQUEST_INTENT_POLICY_VERSION,
392
+ evidence: evidenceFor(context, selectedProposalIds),
393
+ providerSent: false,
394
+ ...(parsed.data.taskOperation
395
+ ? { taskOperation: parsed.data.taskOperation }
396
+ : {}),
397
+ },
398
+ };
203
399
  }
204
400
  /** Validate a main-model interpretation against exact refs from this request and its active session only. */
205
401
  export function bindMainModelRequestIntent(raw, context) {
@@ -211,40 +407,57 @@ export function rebindRequestIntentDecision(raw, context) {
211
407
  if (!raw || typeof raw !== 'object' || Array.isArray(raw))
212
408
  return undefined;
213
409
  const value = raw;
214
- if (!RequestRelationship.safeParse(value.relation).success || !RequestAction.safeParse(value.action).success
215
- || !RequestChangeScope.safeParse(value.changeScope).success
216
- || !['decision-provider', 'main-model', 'unresolved'].includes(value.source)
217
- || value.policyVersion !== REQUEST_INTENT_POLICY_VERSION || !value.evidence
218
- || !Array.isArray(value.evidence.messageRefs) || !Array.isArray(value.evidence.proposalRefs)
219
- || value.evidence.requestDigest !== canonicalContentHash(context.task))
410
+ if (!RequestRelationship.safeParse(value.relation).success ||
411
+ !RequestAction.safeParse(value.action).success ||
412
+ !RequestChangeScope.safeParse(value.changeScope).success ||
413
+ !['decision-provider', 'main-model', 'unresolved'].includes(value.source) ||
414
+ value.policyVersion !== REQUEST_INTENT_POLICY_VERSION ||
415
+ !value.evidence ||
416
+ !Array.isArray(value.evidence.messageRefs) ||
417
+ !Array.isArray(value.evidence.proposalRefs) ||
418
+ value.evidence.requestDigest !== canonicalContentHash(context.task))
220
419
  return undefined;
221
420
  const refs = requestIntentReferenceIds(context);
222
- const messagesMatch = value.evidence.messageRefs.length === context.messageRefs.length
223
- && context.messageRefs.every((item, index) => value.evidence.messageRefs[index]?.id === item.id
224
- && value.evidence.messageRefs[index]?.digest === item.digest);
421
+ const messagesMatch = value.evidence.messageRefs.length === context.messageRefs.length &&
422
+ context.messageRefs.every((item, index) => value.evidence.messageRefs[index]?.id === item.id &&
423
+ value.evidence.messageRefs[index]?.digest === item.digest);
225
424
  const active = context.activeGoal;
226
425
  const task = context.activeTask;
227
- if (value.evidence.taskId !== task?.taskId || value.evidence.taskRevision !== task?.revision
228
- || value.evidence.taskGeneration !== task?.generation)
426
+ if (value.evidence.taskId !== task?.taskId ||
427
+ value.evidence.taskRevision !== task?.revision ||
428
+ value.evidence.taskGeneration !== task?.generation)
229
429
  return undefined;
230
- if (!messagesMatch || (active ? value.evidence.runId !== active.runId
231
- || value.evidence.workflowRevision !== active.workflowRevision || value.evidence.goalRevision !== active.goalRevision
232
- || value.evidence.scopeRevision !== active.scopeRevision
233
- : value.evidence.runId !== undefined))
430
+ if (!messagesMatch ||
431
+ (active
432
+ ? value.evidence.runId !== active.runId ||
433
+ value.evidence.workflowRevision !== active.workflowRevision ||
434
+ value.evidence.goalRevision !== active.goalRevision ||
435
+ value.evidence.scopeRevision !== active.scopeRevision
436
+ : value.evidence.runId !== undefined))
234
437
  return undefined;
235
438
  const selected = new Set();
236
439
  for (const proposal of value.evidence.proposalRefs) {
237
440
  if (!proposal || typeof proposal !== 'object')
238
441
  return undefined;
239
- const current = context.proposals.find(item => item.id === proposal.id && item.revision === proposal.revision && item.digest === proposal.digest);
240
- if (!current || !refs.includes(`proposal:${canonicalContentHash([proposal.id, proposal.revision, proposal.digest])}`))
442
+ const current = context.proposals.find((item) => item.id === proposal.id &&
443
+ item.revision === proposal.revision &&
444
+ item.digest === proposal.digest);
445
+ if (!current ||
446
+ !refs.includes(`proposal:${canonicalContentHash([proposal.id, proposal.revision, proposal.digest])}`))
241
447
  return undefined;
242
448
  selected.add(proposal.id);
243
449
  }
244
450
  if (value.relation !== 'unknown' && value.action !== 'unknown') {
245
- const validated = validateMainModelRequestIntent({ relation: value.relation, action: value.action, changeScope: value.changeScope,
246
- ...(value.taskOperation ? { taskOperation: value.taskOperation } : {}), evidenceRefs: refs.filter(reference => !reference.startsWith('proposal:')
247
- || context.proposals.some(proposal => selected.has(proposal.id) && reference === `proposal:${canonicalContentHash([proposal.id, proposal.revision, proposal.digest])}`)) }, context);
451
+ const validated = validateMainModelRequestIntent({
452
+ relation: value.relation,
453
+ action: value.action,
454
+ changeScope: value.changeScope,
455
+ ...(value.taskOperation ? { taskOperation: value.taskOperation } : {}),
456
+ evidenceRefs: refs.filter((reference) => !reference.startsWith('proposal:') ||
457
+ context.proposals.some((proposal) => selected.has(proposal.id) &&
458
+ reference ===
459
+ `proposal:${canonicalContentHash([proposal.id, proposal.revision, proposal.digest])}`)),
460
+ }, context);
248
461
  if (!validated.ok)
249
462
  return undefined;
250
463
  }
@@ -257,19 +470,31 @@ export function rebindRequestIntentDecision(raw, context) {
257
470
  /** A selected action can advise intake type; it never supplies a target or execution permission. */
258
471
  export function requestIntentTaskType(decision, fallback) {
259
472
  switch (decision?.action) {
260
- case 'answer': return 'chat';
261
- case 'research': return 'research';
262
- case 'verify': return fallback === 'debug' ? 'debug' : 'review';
263
- case 'change': return decision?.changeScope === 'implementation'
264
- ? fallback === 'debug' ? 'debug' : 'build'
265
- : decision?.changeScope === 'document' ? 'writing' : fallback;
266
- case 'operate': return 'devops';
267
- default: return fallback;
473
+ case 'answer':
474
+ return 'chat';
475
+ case 'research':
476
+ return fallback === 'analysis' ? 'analysis' : 'research';
477
+ case 'verify':
478
+ return fallback === 'debug' ? 'debug' : 'review';
479
+ case 'change':
480
+ return decision?.changeScope === 'implementation'
481
+ ? fallback === 'debug'
482
+ ? 'debug'
483
+ : 'build'
484
+ : decision?.changeScope === 'document'
485
+ ? 'writing'
486
+ : fallback;
487
+ case 'operate':
488
+ return 'devops';
489
+ default:
490
+ return fallback;
268
491
  }
269
492
  }
270
493
  export async function classifyRequestIntent(service, requestId, rawContext, signal) {
271
494
  let diagnosticId;
272
- const result = await classifyRequestIntentAttempt(service, requestId, rawContext, signal, id => { diagnosticId = id; });
495
+ const result = await classifyRequestIntentAttempt(service, requestId, rawContext, signal, (id) => {
496
+ diagnosticId = id;
497
+ });
273
498
  if (result && diagnosticId)
274
499
  correlateRequestIntent(result, diagnosticId);
275
500
  return result;
@@ -290,61 +515,103 @@ async function classifyRequestIntentAttempt(service, requestId, rawContext, sign
290
515
  if (!batch)
291
516
  return unresolvedRequestIntent(context, 'context_not_usable');
292
517
  const outcome = await service.evaluate(requestId, batch, signal, REQUEST_INTENT_POLICY_VERSION, undefined, {
293
- onProviderDispatch: () => { providerSent = true; },
294
- onEvaluation: observation => { if (observation.decisionId)
295
- observeId(observation.decisionId); },
518
+ onProviderDispatch: () => {
519
+ providerSent = true;
520
+ },
521
+ onEvaluation: (observation) => {
522
+ if (observation.decisionId)
523
+ observeId(observation.decisionId);
524
+ },
296
525
  });
297
526
  if (outcome.status !== 'completed')
298
527
  return unresolvedRequestIntent(context, outcome.reason, providerSent);
299
- const relation = outcome.result.answers.find(answer => answer.id === 'request-relation');
300
- const action = outcome.result.answers.find(answer => answer.id === 'request-action');
301
- const changeScopeAnswer = outcome.result.answers.find(answer => answer.id === 'request-change-scope');
302
- const relationValue = relation?.status === 'selected' && RequestRelationship.safeParse(relation.choiceId).success
303
- ? relation.choiceId : 'unknown';
304
- const actionValue = action?.status === 'selected' && RequestAction.safeParse(action.choiceId).success
305
- ? action.choiceId : 'unknown';
306
- const changeScopeValue = actionValue === 'change' && changeScopeAnswer?.status === 'selected'
307
- && RequestChangeScope.safeParse(changeScopeAnswer.choiceId).success
308
- ? changeScopeAnswer.choiceId : 'unknown';
528
+ const relation = outcome.result.answers.find((answer) => answer.id === 'request-relation');
529
+ const action = outcome.result.answers.find((answer) => answer.id === 'request-action');
530
+ const changeScopeAnswer = outcome.result.answers.find((answer) => answer.id === 'request-change-scope');
531
+ const relationValue = relation?.status === 'selected' &&
532
+ RequestRelationship.safeParse(relation.choiceId).success
533
+ ? relation.choiceId
534
+ : 'unknown';
535
+ const actionValue = action?.status === 'selected' &&
536
+ RequestAction.safeParse(action.choiceId).success
537
+ ? action.choiceId
538
+ : 'unknown';
539
+ const changeScopeValue = actionValue === 'change' &&
540
+ changeScopeAnswer?.status === 'selected' &&
541
+ RequestChangeScope.safeParse(changeScopeAnswer.choiceId).success
542
+ ? changeScopeAnswer.choiceId
543
+ : 'unknown';
309
544
  const selected = new Set();
310
545
  let uncertainProposal = false;
311
546
  for (const [index, proposal] of context.proposals.entries()) {
312
- const answer = outcome.result.answers.find(answer => answer.id === `request-proposal-${index}`);
547
+ const answer = outcome.result.answers.find((answer) => answer.id === `request-proposal-${index}`);
313
548
  if (answer?.status === 'selected' && answer.choiceId === 'selected')
314
549
  selected.add(proposal.id);
315
- else if (answer?.status !== 'selected' || answer.choiceId !== 'not_selected')
550
+ else if (answer?.status !== 'selected' ||
551
+ answer.choiceId !== 'not_selected')
316
552
  uncertainProposal = true;
317
553
  }
318
- const unresolved = relationValue === 'unknown' || actionValue === 'unknown' || actionValue === 'change' && changeScopeValue === 'unknown' || uncertainProposal;
554
+ const unresolved = relationValue === 'unknown' ||
555
+ actionValue === 'unknown' ||
556
+ (actionValue === 'change' && changeScopeValue === 'unknown') ||
557
+ uncertainProposal;
319
558
  if (uncertainProposal)
320
559
  return unresolvedRequestIntent(context, 'proposal_reference_uncertain', providerSent);
321
- const taskOperationAnswer = outcome.result.answers.find(answer => answer.id === 'request-task-operation');
322
- const taskOperation = taskOperationAnswer?.status === 'selected' && TASK_OPERATIONS.includes(taskOperationAnswer.choiceId)
323
- ? taskOperationAnswer.choiceId : undefined;
560
+ const taskOperationAnswer = outcome.result.answers.find((answer) => answer.id === 'request-task-operation');
561
+ const taskOperation = taskOperationAnswer?.status === 'selected' &&
562
+ TASK_OPERATIONS.includes(taskOperationAnswer.choiceId)
563
+ ? taskOperationAnswer.choiceId
564
+ : undefined;
324
565
  // A task operation is a separate advisory candidate, not a prerequisite for
325
566
  // ordinary work on an already authorized task. Abstention must not erase
326
567
  // independently resolved action/scope or fabricate a lifecycle transition.
327
568
  // Keep the diagnostic and fallback source so the main model can resolve it;
328
569
  // actual task state, lease, scope and native permission checks still apply.
329
- const taskOperationUnresolved = !!context.activeTask && (!taskOperationAnswer
330
- || taskOperationAnswer.status !== 'selected' || taskOperationAnswer.choiceId === 'unknown');
570
+ const taskOperationUnresolved = !!context.activeTask &&
571
+ (!taskOperationAnswer ||
572
+ taskOperationAnswer.status !== 'selected' ||
573
+ taskOperationAnswer.choiceId === 'unknown');
331
574
  if (!unresolved) {
332
- const refs = requestIntentReferenceIds(context).filter(reference => !reference.startsWith('proposal:')
333
- || context.proposals.some(proposal => selected.has(proposal.id) && reference === `proposal:${canonicalContentHash([proposal.id, proposal.revision, proposal.digest])}`));
334
- const bound = validateMainModelRequestIntent({ relation: relationValue, action: actionValue, changeScope: changeScopeValue,
335
- ...(taskOperation ? { taskOperation } : {}), evidenceRefs: refs }, context);
575
+ const refs = requestIntentReferenceIds(context).filter((reference) => !reference.startsWith('proposal:') ||
576
+ context.proposals.some((proposal) => selected.has(proposal.id) &&
577
+ reference ===
578
+ `proposal:${canonicalContentHash([proposal.id, proposal.revision, proposal.digest])}`));
579
+ const bound = validateMainModelRequestIntent({
580
+ relation: relationValue,
581
+ action: actionValue,
582
+ changeScope: changeScopeValue,
583
+ ...(taskOperation ? { taskOperation } : {}),
584
+ evidenceRefs: refs,
585
+ }, context);
336
586
  if (!bound.ok)
337
587
  return unresolvedRequestIntent(context, bound.reason, providerSent);
338
- return { ...bound.decision, source: taskOperationUnresolved ? 'unresolved' : 'decision-provider', providerSent,
339
- ...(taskOperationUnresolved ? { unresolvedReason: 'task_operation_unresolved' } : {}) };
588
+ return {
589
+ ...bound.decision,
590
+ source: taskOperationUnresolved ? 'unresolved' : 'decision-provider',
591
+ providerSent,
592
+ ...(taskOperationUnresolved
593
+ ? { unresolvedReason: 'task_operation_unresolved' }
594
+ : {}),
595
+ };
340
596
  }
341
- return { relation: relationValue, action: actionValue, changeScope: changeScopeValue, source: unresolved ? 'unresolved' : 'decision-provider',
342
- policyVersion: REQUEST_INTENT_POLICY_VERSION, evidence: evidenceFor(context, selected), providerSent,
343
- ...(unresolved ? { unresolvedReason: 'provider_abstained_or_uncertain' } : {}) };
597
+ return {
598
+ relation: relationValue,
599
+ action: actionValue,
600
+ changeScope: changeScopeValue,
601
+ source: unresolved ? 'unresolved' : 'decision-provider',
602
+ policyVersion: REQUEST_INTENT_POLICY_VERSION,
603
+ evidence: evidenceFor(context, selected),
604
+ providerSent,
605
+ ...(unresolved
606
+ ? { unresolvedReason: 'provider_abstained_or_uncertain' }
607
+ : {}),
608
+ };
344
609
  }
345
610
  catch (error) {
346
611
  if (signal.aborted)
347
- throw error instanceof DecisionError ? error : new DecisionError('CANCELLED');
612
+ throw error instanceof DecisionError
613
+ ? error
614
+ : new DecisionError('CANCELLED');
348
615
  return unresolvedRequestIntent(context, error instanceof DecisionError ? error.code : 'decision_provider_failed', providerSent);
349
616
  }
350
617
  }