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,44 +1,44 @@
1
+ import { nativeRequestProposals } from '../request-proposals.js';
1
2
  import { isNativeSubagent } from '../native-subagent.js';
2
- import { recordDiagnosticAdmission, observeDiagnostic } from '../decisions/diagnostics.js';
3
+ import { recordDiagnosticAdmission, observeDiagnostic, } from '../decisions/diagnostics.js';
3
4
  import { policyState } from '../host-adapter/turn-state.js';
4
5
  import { dshTurnRequestId } from '../intake-profile-resolver.js';
5
- import { classifyTaskForIntake, selectInstalledSkills } from '../decisions/workflows.js';
6
- import { classifyRequestIntent, createRequestIntentContext, rebindRequestIntentDecision, requestIntentMessageRefs, requestIntentReferenceIds, requestIntentTaskType, requestIntentIsUnresolved } from '../decisions/request-intent.js';
7
- import { ANSWER_REVIEW_FORM, hasHumanInput } from '../answer-review/contracts.js';
6
+ import { classifyTaskForIntake, selectInstalledSkills, } from '../decisions/workflows.js';
7
+ import { classifyRequestIntent, createRequestIntentContext, rebindRequestIntentDecision, requestIntentMessageRefs, requestIntentReferenceIds, requestIntentTaskType, requestIntentIsUnresolved, } from '../decisions/request-intent.js';
8
+ import { ANSWER_REVIEW_FORM, hasHumanInput, } from '../answer-review/contracts.js';
8
9
  import { saveSessionNotice } from '../plugin-records.js';
9
- import { explicitExecutionMode, initializeExecutionSelection, readExecutionSelection, writeExecutionSelection } from '../execution-selection.js';
10
+ import { explicitExecutionMode, initializeExecutionSelection, readExecutionSelection, writeExecutionSelection, } from '../execution-selection.js';
10
11
  import { getHeadWorkerForSession } from '../models/session-selection.js';
11
- import { selectExecution, ExecutionSelectionPending } from '../model-selection-ui.js';
12
+ import { selectExecution, ExecutionSelectionPending, } from '../model-selection-ui.js';
12
13
  import { ennoStateForPreparedTask } from '../../enno-oduno/service.js';
13
14
  import { realpathSync } from 'node:fs';
14
15
  import { withImmediateTransaction } from '../../db/transaction.js';
15
- import { DshIntakeGate } from '../intake-gate.js';
16
+ import { DshIntakeGate, } from '../intake-gate.js';
16
17
  import { resolveGroundedIntakeProfile } from '../intake-profile-resolver.js';
17
18
  import { deriveAkinatorReasoning } from '../../akinator/reasoning.js';
18
- import { resolveCapabilities, hasBlockingRequiredCapability } from '../../akinator/capabilities.js';
19
+ import { resolveCapabilities, hasBlockingRequiredCapability, } from '../../akinator/capabilities.js';
19
20
  import { readAkinatorSession } from '../../akinator/store.js';
20
- import { dshTurnBoundarySeq } from '../session-memory-finalizer.js';
21
- import { createDshIntakeAnswerer } from '../user-interaction.js';
21
+ import { dshTurnBoundarySeq, } from '../session-memory-finalizer.js';
22
+ import { createDshIntakeAnswerer, } from '../user-interaction.js';
22
23
  import { canonicalContentHash } from '../../serialization/validate.js';
23
24
  import { KiokukoError } from '../../errors.js';
24
- import { answerEnno, stateForSnapshot, inapplicableEnnoState } from '../../enno-oduno/service.js';
25
- import { claimExecutionLeaseInTransaction, readEnnoSnapshot, terminalizeLedgerRunInTransaction } from '../../enno-oduno/store.js';
25
+ import { answerEnno, stateForSnapshot, inapplicableEnnoState, } from '../../enno-oduno/service.js';
26
+ import { claimExecutionLeaseInTransaction, readEnnoSnapshot, terminalizeLedgerRunInTransaction, } from '../../enno-oduno/store.js';
26
27
  import { decideDshContinuation } from '../continuation.js';
27
28
  import { resolveProjectWorkspaceReadOnly } from '../../memory/workspaces.js';
28
- import { ENNO_APPLICABLE_TASK_TYPES } from '../../enno-oduno/types.js';
29
- import { supersedeBoundaryJobsAtOrBeforeRevisionInTransaction, supersedeOutboxAtOrBeforeRevisionInTransaction } from '../turn-process.js';
29
+ import { ENNO_APPLICABLE_TASK_TYPES, } from '../../enno-oduno/types.js';
30
+ import { supersedeBoundaryJobsAtOrBeforeRevisionInTransaction, supersedeOutboxAtOrBeforeRevisionInTransaction, } from '../turn-process.js';
30
31
  import { unexecutedRunInput } from '../input-claim.js';
31
32
  import { resetLoopGuardForUserInTransaction } from '../loop-guard.js';
32
33
  import { readCodingWorkflow } from '../coding-workflow/store.js';
33
34
  import { markCodingScopeChange } from '../coding-workflow/service.js';
34
35
  import { readExecutionFrame } from '../execution-frame.js';
35
- import { createMemoryOwner, memoryOwnerKind } from '../memory-owner.js';
36
- import { findSecret } from '../../memory/secrets.js';
37
- import { executionClarification, isContinuationReference } from '../intake-clarification.js';
38
- import { authorizeTaskLease, bindTaskRepository, captureTaskLease, clearTaskUnresolved, findSessionTask, readTaskSnapshot, recordTaskUnresolved, taskCanExecute } from '../task-state.js';
36
+ import { createMemoryOwner, memoryOwnerKind, } from '../memory-owner.js';
37
+ import { executionClarification, isContinuationReference, } from '../intake-clarification.js';
38
+ import { authorizeTaskLease, bindTaskRepository, captureTaskLease, clearTaskUnresolved, findSessionTask, readTaskSnapshot, recordTaskUnresolved, taskCanExecute, } from '../task-state.js';
39
39
  import { continueTaskIntake } from '../existing-task-intake.js';
40
40
  import { LedgerStore } from '../../ledger/store.js';
41
- import { claimExecutionOwner, readExecutionOwner } from '../orchestration/execution-owner.js';
41
+ import { claimExecutionOwner, readExecutionOwner, } from '../orchestration/execution-owner.js';
42
42
  import { initializeCodingWorkflow } from '../coding-workflow/store.js';
43
43
  import { codingWorkRequested } from '../coding-workflow/intent.js';
44
44
  import { initializeTaskCompletion } from '../task-completion.js';
@@ -53,27 +53,39 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
53
53
  task: event.task,
54
54
  cwd: event.cwd,
55
55
  deferTaskTypeInference: event.deferTaskTypeInference === true,
56
- ...(event.profileHints === undefined ? {} : { profileHints: event.profileHints }),
56
+ ...(event.profileHints === undefined
57
+ ? {}
58
+ : { profileHints: event.profileHints }),
57
59
  }).profileHints.taskType;
58
- return incomingType !== null && !ENNO_APPLICABLE_TASK_TYPES.includes(incomingType);
60
+ return (incomingType !== null &&
61
+ !ENNO_APPLICABLE_TASK_TYPES.includes(incomingType));
59
62
  }
60
63
  function eventTurn(event) {
61
- const data = typeof event.data === 'object' && event.data !== null && !Array.isArray(event.data)
62
- ? event.data : undefined;
64
+ const data = typeof event.data === 'object' &&
65
+ event.data !== null &&
66
+ !Array.isArray(event.data)
67
+ ? event.data
68
+ : undefined;
63
69
  return Number.isSafeInteger(data?.turn) ? data.turn : undefined;
64
70
  }
65
71
  function continuesCodingScope(event) {
66
72
  const intent = event.requestIntent;
67
- return !!intent && (intent.relation === 'same_scope_continuation' || intent.relation === 'scope_change')
68
- && intent.action !== 'answer' && intent.action !== 'unknown';
73
+ return (!!intent &&
74
+ (intent.relation === 'same_scope_continuation' ||
75
+ intent.relation === 'scope_change') &&
76
+ intent.action !== 'answer' &&
77
+ intent.action !== 'unknown');
69
78
  }
70
79
  let prepareGeneration = 0;
71
80
  const continuedTurns = new Map();
72
81
  const resumedTurns = new Map();
73
82
  const bindAndRecord = async (event, result, generation) => {
74
- const authority = await runtime.withDatabase(database => {
75
- const identity = { taskId: result.prepared.run.runId, sessionId: event.sessionId,
76
- repositoryRoot: result.prepared.project.repositoryRoot };
83
+ const authority = await runtime.withDatabase((database) => {
84
+ const identity = {
85
+ taskId: result.prepared.run.runId,
86
+ sessionId: event.sessionId,
87
+ repositoryRoot: result.prepared.project.repositoryRoot,
88
+ };
77
89
  bindTaskRepository(database, identity);
78
90
  if (event.signal.aborted)
79
91
  return undefined;
@@ -90,26 +102,61 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
90
102
  }
91
103
  });
92
104
  if (authority)
93
- await runtime.withDatabase(database => authorizeTaskLease(database, authority));
105
+ await runtime.withDatabase((database) => authorizeTaskLease(database, authority));
94
106
  turnState.record(event, result, generation, authority);
95
- await observeDiagnostic(runtime, event.sessionId, db => recordDiagnosticAdmission(db, { sessionId: event.sessionId, turn: event.turn, runId: result.prepared.run.runId,
96
- task: event.task, ...(event.requestIntentContext ? { context: event.requestIntentContext } : {}), ...(event.requestIntent ? { decision: event.requestIntent } : {}) }));
107
+ await observeDiagnostic(runtime, event.sessionId, (db) => recordDiagnosticAdmission(db, {
108
+ sessionId: event.sessionId,
109
+ turn: event.turn,
110
+ runId: result.prepared.run.runId,
111
+ task: event.task,
112
+ ...(event.requestIntentContext
113
+ ? { context: event.requestIntentContext }
114
+ : {}),
115
+ ...(event.requestIntent ? { decision: event.requestIntent } : {}),
116
+ }));
97
117
  const reviewItem = currentSession(event.sessionId);
98
118
  const reviewAgent = event.nativeAgent;
99
- if (reviewItem && reviewAgent?.session && typeof reviewAgent.session.snapshotEvents === 'function') {
119
+ if (reviewItem &&
120
+ reviewAgent?.session &&
121
+ typeof reviewAgent.session.snapshotEvents === 'function') {
100
122
  const eligible = () => {
101
123
  const item = currentSession(event.sessionId), selection = getSelection(reviewItem.runId)?.value;
102
- return item?.runId === reviewItem.runId && !item.closed && !item.failed && item.prepared.run.status === 'active'
103
- && !item.prepared.ennoOduno.applicable && !selection?.discussion && (!selection || selection.mode === 'normal' && selection.status === 'ready')
104
- && !delegation.isChild(reviewAgent) && !deepPlanning.executor.isChild(reviewAgent) && !isNativeSubagent(reviewAgent)
105
- && !executionSupport.paused(event.sessionId);
124
+ return (item?.runId === reviewItem.runId &&
125
+ !item.closed &&
126
+ !item.failed &&
127
+ item.prepared.run.status === 'active' &&
128
+ !item.prepared.ennoOduno.applicable &&
129
+ !selection?.discussion &&
130
+ (!selection ||
131
+ (selection.mode === 'normal' && selection.status === 'ready')) &&
132
+ !delegation.isChild(reviewAgent) &&
133
+ !deepPlanning.executor.isChild(reviewAgent) &&
134
+ !isNativeSubagent(reviewAgent) &&
135
+ !executionSupport.paused(event.sessionId));
106
136
  };
107
- answerReview.bind({ runId: reviewItem.runId, workspace: reviewItem.workspace, requestId: `run:${reviewItem.runId}`, task: reviewItem.task,
108
- catalogDigest: reviewItem.catalog.digest, turn: event.turn, agent: reviewAgent, eligible,
109
- current: () => { const item = currentSession(event.sessionId); return item?.runId === reviewItem.runId && item.nativeAgent === reviewAgent && item.nativeSession === reviewAgent.session && agents?.get(reviewAgent.id) === reviewAgent && sessions?.get(event.sessionId) === reviewAgent.session; },
137
+ answerReview.bind({
138
+ runId: reviewItem.runId,
139
+ workspace: reviewItem.workspace,
140
+ requestId: `run:${reviewItem.runId}`,
141
+ task: reviewItem.task,
142
+ catalogDigest: reviewItem.catalog.digest,
143
+ turn: event.turn,
144
+ agent: reviewAgent,
145
+ eligible,
146
+ current: () => {
147
+ const item = currentSession(event.sessionId);
148
+ return (item?.runId === reviewItem.runId &&
149
+ item.nativeAgent === reviewAgent &&
150
+ item.nativeSession === reviewAgent.session &&
151
+ agents?.get(reviewAgent.id) === reviewAgent &&
152
+ sessions?.get(event.sessionId) === reviewAgent.session);
153
+ },
110
154
  settled: async () => {
111
155
  const item = currentSession(event.sessionId);
112
- if (!item || item.runId !== reviewItem.runId || item.closed || reviewAgent.status === 'running')
156
+ if (!item ||
157
+ item.runId !== reviewItem.runId ||
158
+ item.closed ||
159
+ reviewAgent.status === 'running')
113
160
  return;
114
161
  const intent = await resolveIdleClose(item.agentId, item.sessionId, item.nativeSession, item.nativeAgent);
115
162
  if (intent)
@@ -121,14 +168,19 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
121
168
  };
122
169
  async function hasPendingExecutionDiscussion(sessionId, cwd) {
123
170
  return runtime.withDatabase(async (db) => {
124
- const project = await resolveProjectWorkspaceReadOnly(db, cwd, { allowDirectory: true });
171
+ const project = await resolveProjectWorkspaceReadOnly(db, cwd, {
172
+ allowDirectory: true,
173
+ });
125
174
  if (!project)
126
175
  return false;
127
- const activeRuns = db.prepare(`SELECT run_id FROM ledger_runs
128
- WHERE workspace=? AND dsh_session_id=? AND status='active'`).all(project.workspace, sessionId);
176
+ const activeRuns = db
177
+ .prepare(`SELECT run_id FROM ledger_runs
178
+ WHERE workspace=? AND dsh_session_id=? AND status='active'`)
179
+ .all(project.workspace, sessionId);
129
180
  if (activeRuns.length !== 1)
130
181
  return false;
131
- return readExecutionSelection(db, activeRuns[0].run_id)?.value.discussion !== undefined;
182
+ return (readExecutionSelection(db, activeRuns[0].run_id)?.value.discussion !==
183
+ undefined);
132
184
  });
133
185
  }
134
186
  const refreshContinuedWorkLease = async (item, expected) => {
@@ -147,7 +199,8 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
147
199
  const workUnitId = state.nextAction === 'execute_work_unit'
148
200
  ? state.directive?.workUnit?.id
149
201
  : undefined;
150
- if (state.contractRevision !== expected.contractRevision || workUnitId !== expectedWorkUnitId) {
202
+ if (state.contractRevision !== expected.contractRevision ||
203
+ workUnitId !== expectedWorkUnitId) {
151
204
  throw new KiokukoError('CONFLICT', 'Enno WorkUnit changed before DSH turn recovery');
152
205
  }
153
206
  if (snapshot.dshSessionId !== item.sessionId) {
@@ -165,70 +218,98 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
165
218
  supersededChoices = new Map();
166
219
  async choose(event, result) {
167
220
  const runId = result.prepared.run.runId;
168
- let stored = await runtime.withDatabase(db => readExecutionSelection(db, runId));
221
+ let stored = await runtime.withDatabase((db) => readExecutionSelection(db, runId));
169
222
  const key = `${event.sessionId}\u0000${event.turn}`;
170
223
  const inherited = this.supersededChoices.get(key);
171
- if (stored?.value.mode === 'pending' && inherited?.value.status === 'ready' && ['normal', 'enno'].includes(inherited.value.mode)
172
- && (!explicitExecutionMode(event.task) || explicitExecutionMode(event.task) === inherited.value.mode)) {
224
+ if (stored?.value.mode === 'pending' &&
225
+ inherited?.value.status === 'ready' &&
226
+ ['normal', 'enno'].includes(inherited.value.mode) &&
227
+ (!explicitExecutionMode(event.task) ||
228
+ explicitExecutionMode(event.task) === inherited.value.mode)) {
173
229
  const revision = stored.revision;
174
- stored = await runtime.withDatabase(db => writeExecutionSelection(db, runId, revision, inherited.value));
230
+ stored = await runtime.withDatabase((db) => writeExecutionSelection(db, runId, revision, inherited.value));
175
231
  this.supersededChoices.delete(key);
176
232
  }
177
233
  if (result.admitted && result.prepared.selectedSkills === undefined) {
178
- const task = await runtime.withDatabase(db => readAkinatorSession(db, { workspace: result.prepared.project.workspace, sessionId: result.prepared.intake.sessionId }).task);
234
+ const task = await runtime.withDatabase((db) => readAkinatorSession(db, {
235
+ workspace: result.prepared.project.workspace,
236
+ sessionId: result.prepared.intake.sessionId,
237
+ }).task);
179
238
  result.prepared.selectedSkills = await selectInstalledSkills(decisions, `run:${runId}`, task, [...event.capabilities.skills, ...event.capabilities.tools], result.prepared.capabilities, event.signal);
180
239
  }
181
240
  // Legacy accepted plans keep their original execution contract. A draft
182
241
  // awaiting the new review gate must first acquire an explicit check binding.
183
242
  if (!stored && result.prepared.ennoOduno.status === 'zenki_planning') {
184
- stored = await runtime.withDatabase(db => {
243
+ stored = await runtime.withDatabase((db) => {
185
244
  initializeExecutionSelection(db, runId);
186
245
  const current = readExecutionSelection(db, runId);
187
- return current.value.mode === 'pending' ? writeExecutionSelection(db, runId, current.revision, { mode: 'enno', status: 'selecting' }) : current;
246
+ return current.value.mode === 'pending'
247
+ ? writeExecutionSelection(db, runId, current.revision, {
248
+ mode: 'enno',
249
+ status: 'selecting',
250
+ })
251
+ : current;
188
252
  });
189
253
  }
190
254
  if (!stored)
191
255
  return result;
192
256
  const discussion = stored.value.discussion;
193
257
  if (discussion && event.turn > discussion.turn) {
194
- const incomingType = resolveGroundedIntakeProfile({ task: event.task, cwd: event.cwd, deferTaskTypeInference: event.deferTaskTypeInference === true,
195
- ...(event.profileHints === undefined ? {} : { profileHints: event.profileHints }),
258
+ const incomingType = resolveGroundedIntakeProfile({
259
+ task: event.task,
260
+ cwd: event.cwd,
261
+ deferTaskTypeInference: event.deferTaskTypeInference === true,
262
+ ...(event.profileHints === undefined
263
+ ? {}
264
+ : { profileHints: event.profileHints }),
196
265
  }).profileHints.taskType;
197
266
  const requestedMode = explicitExecutionMode(event.task);
198
- if (requestedMode !== undefined || incomingType !== null && incomingType !== 'chat') {
267
+ if (requestedMode !== undefined ||
268
+ (incomingType !== null && incomingType !== 'chat')) {
199
269
  const { discussion: _answered, ...value } = stored.value;
200
270
  const revision = stored.revision;
201
- stored = await runtime.withDatabase(db => writeExecutionSelection(db, runId, revision, requestedMode === undefined ? value : { ...value, mode: 'pending' }));
271
+ stored = await runtime.withDatabase((db) => writeExecutionSelection(db, runId, revision, requestedMode === undefined
272
+ ? value
273
+ : { ...value, mode: 'pending' }));
202
274
  }
203
275
  }
204
- if (!ENNO_APPLICABLE_TASK_TYPES.includes(result.prepared.intake.profile.taskType)) {
276
+ if (!ENNO_APPLICABLE_TASK_TYPES.includes(result.prepared.intake.profile
277
+ .taskType)) {
205
278
  const revision = stored.revision;
206
279
  if (stored.value.mode === 'pending')
207
- await runtime.withDatabase(db => db.prepare('DELETE FROM dsh_execution_selections WHERE run_id = ? AND revision = ?').run(runId, revision));
280
+ await runtime.withDatabase((db) => db
281
+ .prepare('DELETE FROM dsh_execution_selections WHERE run_id = ? AND revision = ?')
282
+ .run(runId, revision));
208
283
  return result;
209
284
  }
210
285
  setSelection(runId, stored);
211
286
  const savedHeadWorker = await getHeadWorkerForSession(native.get('llm', false), event.sessionId);
212
287
  const selected = await selectExecution({
213
- task: event.task, turn: event.turn, signal: event.signal, stored, routes: modelRoutes ?? [],
288
+ task: event.task,
289
+ turn: event.turn,
290
+ signal: event.signal,
291
+ stored,
292
+ routes: modelRoutes ?? [],
214
293
  ...(savedHeadWorker ? { savedHeadWorker } : {}),
215
294
  ...(event.nativeAgent ? { agent: event.nativeAgent } : {}),
216
295
  ...(userQuestions ? { questions: userQuestions } : {}),
217
296
  ...(modelCatalog ? { llm: modelCatalog } : {}),
218
297
  ...(modelCompatibility ? { compatibility: modelCompatibility } : {}),
219
298
  save: async (revision, value) => {
220
- const saved = await runtime.withDatabase(db => writeExecutionSelection(db, runId, revision, value));
299
+ const saved = await runtime.withDatabase((db) => writeExecutionSelection(db, runId, revision, value));
221
300
  setSelection(runId, saved);
222
301
  return saved;
223
302
  },
224
303
  });
225
304
  setSelection(runId, selected);
226
- const ennoOduno = selected.value.discussion ? result.prepared.ennoOduno
227
- : await runtime.withDatabase(db => ennoStateForPreparedTask(db, result.prepared, event.sessionId));
305
+ const ennoOduno = selected.value.discussion
306
+ ? result.prepared.ennoOduno
307
+ : await runtime.withDatabase((db) => ennoStateForPreparedTask(db, result.prepared, event.sessionId));
228
308
  return { ...result, prepared: { ...result.prepared, ennoOduno } };
229
309
  }
230
310
  async prepare(event) {
231
- if (event.requestIntent?.action === 'change' && event.requestIntent.changeScope === 'unknown') {
311
+ if (event.requestIntent?.action === 'change' &&
312
+ event.requestIntent.changeScope === 'unknown') {
232
313
  throw new KiokukoError('CONFLICT', 'request_intent_change_scope_unresolved');
233
314
  }
234
315
  // Capture completion ordering before any asynchronous database or intake
@@ -243,41 +324,67 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
243
324
  // task and opened a new run, so its source revision must not be treated as
244
325
  // the new run's execution authority. Human input still takes the source
245
326
  // revision check below; never refresh a stale interpretation implicitly.
246
- const admittedStep = !!canonical && continuesCodingScope(event) && !hasHumanInput(event.nativeMessages ?? []) && prior?.turn === event.turn
247
- && !prior.closed && !prior.failed && prior.task === event.task && !!prior.authority
248
- && prior.requestIntent === event.requestIntent && prior.requestIntentContext === event.requestIntentContext;
327
+ const admittedStep = !!canonical &&
328
+ continuesCodingScope(event) &&
329
+ !hasHumanInput(event.nativeMessages ?? []) &&
330
+ prior?.turn === event.turn &&
331
+ !prior.closed &&
332
+ !prior.failed &&
333
+ prior.task === event.task &&
334
+ !!prior.authority &&
335
+ prior.requestIntent === event.requestIntent &&
336
+ prior.requestIntentContext === event.requestIntentContext;
249
337
  if (admittedStep)
250
- await runtime.withDatabase(database => authorizeTaskLease(database, prior.authority));
338
+ await runtime.withDatabase((database) => authorizeTaskLease(database, prior.authority));
251
339
  if (canonical && continuesCodingScope(event) && !admittedStep) {
252
- const current = await runtime.withDatabase(database => readTaskSnapshot(database, canonical));
253
- if (current.revision !== canonical.revision || current.generation !== canonical.generation)
340
+ const current = await runtime.withDatabase((database) => readTaskSnapshot(database, canonical));
341
+ if (current.revision !== canonical.revision ||
342
+ current.generation !== canonical.generation)
254
343
  throw new KiokukoError('CONFLICT', 'Task continuation revision changed');
255
- const canonicalContinuation = event.requestIntent?.relation === 'same_scope_continuation'
256
- && !!event.requestIntentContext && !!rebindRequestIntentDecision(event.requestIntent, event.requestIntentContext)
257
- && event.requestIntent.evidence.taskId === current.taskId && taskCanExecute(current)
258
- && prior?.runId === current.taskId && !!prior.authority
259
- && prior.authority.revision === current.revision && prior.authority.generation === current.generation;
344
+ const canonicalContinuation = event.requestIntent?.relation === 'same_scope_continuation' &&
345
+ !!event.requestIntentContext &&
346
+ !!rebindRequestIntentDecision(event.requestIntent, event.requestIntentContext) &&
347
+ event.requestIntent.evidence.taskId === current.taskId &&
348
+ taskCanExecute(current) &&
349
+ prior?.runId === current.taskId &&
350
+ !!prior.authority &&
351
+ prior.authority.revision === current.revision &&
352
+ prior.authority.generation === current.generation;
260
353
  // An explicit resume invalidates the old in-memory preparation and memory
261
354
  // delivery, even when the native session and logical run ID are unchanged.
262
355
  // Reuse the existing resume path before advisory completion retires a
263
356
  // normal task; it also initializes any newly required coding workflow.
264
- const resumeCanonicalContinuation = canonicalContinuation && prior && !prior.closed && !prior.failed
265
- && prior.turn < event.turn && !prior.prepared.ennoOduno.applicable
266
- && !await runtime.withDatabase(db => readCodingWorkflow(db, current.taskId));
267
- if (resumeCanonicalContinuation || !taskCanExecute(current) || prior?.runId === current.taskId && prior.authority
268
- && (prior.authority.revision !== current.revision || prior.authority.generation !== current.generation)) {
357
+ const resumeCanonicalContinuation = canonicalContinuation &&
358
+ prior &&
359
+ !prior.closed &&
360
+ !prior.failed &&
361
+ prior.turn < event.turn &&
362
+ !prior.prepared.ennoOduno.applicable &&
363
+ !(await runtime.withDatabase((db) => readCodingWorkflow(db, current.taskId)));
364
+ if (resumeCanonicalContinuation ||
365
+ !taskCanExecute(current) ||
366
+ (prior?.runId === current.taskId &&
367
+ prior.authority &&
368
+ (prior.authority.revision !== current.revision ||
369
+ prior.authority.generation !== current.generation))) {
269
370
  const refreshed = await resumeExistingRun?.(event);
270
371
  if (!refreshed)
271
372
  throw new KiokukoError('CONFLICT', 'Current task could not be resumed');
272
373
  continuedTurns.delete(`${event.sessionId}\u0000${event.turn}`);
273
374
  resumedTurns.delete(`${event.sessionId}\u0000${event.turn}`);
274
- const selected = refreshed.admitted ? await this.choose(event, refreshed) : refreshed;
375
+ const selected = refreshed.admitted
376
+ ? await this.choose(event, refreshed)
377
+ : refreshed;
275
378
  await bindAndRecord(event, selected, generation);
276
379
  return selected;
277
380
  }
278
381
  }
279
- if (prior && prior.turn === event.turn && !prior.closed && prior.task !== event.task && hasHumanInput(event.nativeMessages ?? [])
280
- && event.requestIntent?.relation === 'new') {
382
+ if (prior &&
383
+ prior.turn === event.turn &&
384
+ !prior.closed &&
385
+ prior.task !== event.task &&
386
+ hasHumanInput(event.nativeMessages ?? []) &&
387
+ event.requestIntent?.relation === 'new') {
281
388
  const choice = getSelection(prior.runId);
282
389
  if (choice?.value.status === 'ready')
283
390
  this.supersededChoices.set(`${event.sessionId}\u0000${event.turn}`, choice);
@@ -286,7 +393,9 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
286
393
  continuedTurns.delete(`${event.sessionId}\u0000${event.turn}`);
287
394
  resumedTurns.delete(`${event.sessionId}\u0000${event.turn}`);
288
395
  }
289
- const reviewProject = await runtime.withDatabase((database) => resolveProjectWorkspaceReadOnly(database, event.cwd, { allowDirectory: true }));
396
+ const reviewProject = await runtime.withDatabase((database) => resolveProjectWorkspaceReadOnly(database, event.cwd, {
397
+ allowDirectory: true,
398
+ }));
290
399
  if (reviewProject)
291
400
  await autoReview.acceptInput(reviewProject.workspace, event.sessionId, event.task);
292
401
  const cacheKey = `${event.sessionId}\u0000${event.turn}`;
@@ -298,80 +407,177 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
298
407
  task: event.task,
299
408
  cwd: event.cwd,
300
409
  profileHints: event.profileHints ?? null,
301
- ...(event.deferTaskTypeInference ? { deferTaskTypeInference: true } : {}),
410
+ ...(event.deferTaskTypeInference
411
+ ? { deferTaskTypeInference: true }
412
+ : {}),
302
413
  evidence: event.evidence ?? null,
303
414
  requestIntent: event.requestIntent ?? null,
304
- requestIntentContextDigest: event.requestIntentContext ? canonicalContentHash(event.requestIntentContext) : null,
415
+ requestIntentContextDigest: event.requestIntentContext
416
+ ? canonicalContentHash(event.requestIntentContext)
417
+ : null,
305
418
  memoryOwner: event.memoryOwner ?? null,
306
419
  skillDiscoveryMode: event.skillDiscoveryMode ?? null,
307
420
  catalogDigest: event.capabilities.digest,
308
421
  });
309
- const unresolvedBinding = canonicalContentHash({ sessionId: event.sessionId, agentId: event.agent.id,
310
- turn: event.turn, sourceStartSeq: event.sourceStartSeq ?? null, task: event.task, cwd: event.cwd,
311
- evidence: event.evidence ?? null, context: event.requestIntentContext ?? null, catalogDigest: event.capabilities.digest,
312
- skillDiscoveryMode: event.skillDiscoveryMode ?? null });
422
+ const unresolvedBinding = canonicalContentHash({
423
+ sessionId: event.sessionId,
424
+ agentId: event.agent.id,
425
+ turn: event.turn,
426
+ sourceStartSeq: event.sourceStartSeq ?? null,
427
+ task: event.task,
428
+ cwd: event.cwd,
429
+ evidence: event.evidence ?? null,
430
+ context: event.requestIntentContext ?? null,
431
+ catalogDigest: event.capabilities.digest,
432
+ skillDiscoveryMode: event.skillDiscoveryMode ?? null,
433
+ });
313
434
  const cached = continuedTurns.get(cacheKey) ?? resumedTurns.get(cacheKey);
314
435
  if (cached !== undefined) {
315
- if (cached.fingerprint !== fingerprint && 'unresolvedBinding' in cached && cached.unresolvedBinding === unresolvedBinding
316
- && cached.nativeAgent === event.nativeAgent && cached.nativeSession === event.nativeSession
317
- && event.requestIntentContext && event.requestIntent
318
- && continuesCodingScope(event) && rebindRequestIntentDecision(event.requestIntent, event.requestIntentContext)) {
436
+ if (cached.fingerprint !== fingerprint &&
437
+ 'unresolvedBinding' in cached &&
438
+ cached.unresolvedBinding === unresolvedBinding &&
439
+ cached.nativeAgent === event.nativeAgent &&
440
+ cached.nativeSession === event.nativeSession &&
441
+ event.requestIntentContext &&
442
+ event.requestIntent &&
443
+ continuesCodingScope(event) &&
444
+ rebindRequestIntentDecision(event.requestIntent, event.requestIntentContext)) {
319
445
  // A host-owned confirmation may resume before optional interpretation.
320
446
  // Resolve that exact unresolved input once; changing resolved input still conflicts.
321
447
  const kind = memoryOwnerKind(event.requestIntent, event.profileHints?.taskType);
322
- const prepared = { ...cached.result.prepared,
323
- memoryOwner: createMemoryOwner(kind, { agentId: event.agent.id, sessionId: event.sessionId, turn: event.turn,
324
- requestDigest: canonicalContentHash(event.task), runId: cached.result.prepared.run.runId }) };
325
- const resolved = await this.choose(event, { ...cached.result, prepared });
326
- continuedTurns.set(cacheKey, { fingerprint, result: resolved, ...(event.nativeAgent ? { nativeAgent: event.nativeAgent } : {}), ...(event.nativeSession ? { nativeSession: event.nativeSession } : {}) });
448
+ const prepared = {
449
+ ...cached.result.prepared,
450
+ memoryOwner: createMemoryOwner(kind, {
451
+ agentId: event.agent.id,
452
+ sessionId: event.sessionId,
453
+ turn: event.turn,
454
+ requestDigest: canonicalContentHash(event.task),
455
+ runId: cached.result.prepared.run.runId,
456
+ }),
457
+ };
458
+ const resolved = await this.choose(event, {
459
+ ...cached.result,
460
+ prepared,
461
+ });
462
+ continuedTurns.set(cacheKey, {
463
+ fingerprint,
464
+ result: resolved,
465
+ ...(event.nativeAgent ? { nativeAgent: event.nativeAgent } : {}),
466
+ ...(event.nativeSession
467
+ ? { nativeSession: event.nativeSession }
468
+ : {}),
469
+ });
327
470
  await bindAndRecord(event, resolved, generation);
328
471
  return resolved;
329
472
  }
330
- if (cached.fingerprint !== fingerprint
331
- || cached.nativeAgent !== event.nativeAgent
332
- || cached.nativeSession !== event.nativeSession) {
473
+ if (cached.fingerprint !== fingerprint ||
474
+ cached.nativeAgent !== event.nativeAgent ||
475
+ cached.nativeSession !== event.nativeSession) {
333
476
  throw new KiokukoError('CONFLICT', 'dsh continued turn was reused with different bound input');
334
477
  }
335
478
  this.assertCatalog(cached.result.catalog, event.capabilities);
336
- if (!hasHumanInput(event.nativeMessages ?? []) && event.nativeMessages?.some(message => objectRecord(objectRecord(message)?.source)?.form === ANSWER_REVIEW_FORM)) {
337
- if (!event.nativeAgent || !await answerReview.accept(event.nativeAgent, event.nativeMessages, event.turn, event.capabilities.digest))
479
+ if (!hasHumanInput(event.nativeMessages ?? []) &&
480
+ event.nativeMessages?.some((message) => objectRecord(objectRecord(message)?.source)?.form ===
481
+ ANSWER_REVIEW_FORM)) {
482
+ if (!event.nativeAgent ||
483
+ !(await answerReview.accept(event.nativeAgent, event.nativeMessages, event.turn, event.capabilities.digest)))
338
484
  throw new Error('Stale answer review continuation');
339
485
  }
340
- return event.signal.aborted ? { ...cached.result, admitted: false } : this.choose(event, cached.result);
486
+ return event.signal.aborted
487
+ ? { ...cached.result, admitted: false }
488
+ : this.choose(event, cached.result);
341
489
  }
342
490
  const previous = currentForAgentEvent(event.agent.id, event.sessionId, undefined, event.nativeSession, event.nativeAgent);
343
491
  const reviewMessages = event.nativeMessages ?? [];
344
492
  if (hasHumanInput(reviewMessages))
345
493
  answerReview.humanInput(event.sessionId, event.turn);
346
- if (!hasHumanInput(reviewMessages) && reviewMessages.some(message => objectRecord(objectRecord(message)?.source)?.form === ANSWER_REVIEW_FORM)) {
347
- if (!previous || !event.nativeAgent || !await answerReview.accept(event.nativeAgent, reviewMessages, event.turn, event.capabilities.digest))
494
+ if (!hasHumanInput(reviewMessages) &&
495
+ reviewMessages.some((message) => objectRecord(objectRecord(message)?.source)?.form ===
496
+ ANSWER_REVIEW_FORM)) {
497
+ if (!previous ||
498
+ !event.nativeAgent ||
499
+ !(await answerReview.accept(event.nativeAgent, reviewMessages, event.turn, event.capabilities.digest)))
348
500
  throw new Error('Unbound answer review continuation');
349
501
  // Only the coordinator's exact reserved message may carry this owner
350
502
  // into another native turn; the request and run remain unchanged.
351
503
  const owner = previous.prepared.memoryOwner;
352
- const prepared = owner ? { ...previous.prepared,
353
- memoryOwner: createMemoryOwner(owner.kind, { ...owner, turn: event.turn }) } : previous.prepared;
354
- const continued = { admitted: true, prepared, catalog: previous.catalog };
355
- continuedTurns.set(cacheKey, { fingerprint, result: continued, nativeAgent: event.nativeAgent, ...(event.nativeSession === undefined ? {} : { nativeSession: event.nativeSession }) });
504
+ const prepared = owner
505
+ ? {
506
+ ...previous.prepared,
507
+ memoryOwner: createMemoryOwner(owner.kind, {
508
+ ...owner,
509
+ turn: event.turn,
510
+ }),
511
+ }
512
+ : previous.prepared;
513
+ const continued = {
514
+ admitted: true,
515
+ prepared,
516
+ catalog: previous.catalog,
517
+ };
518
+ continuedTurns.set(cacheKey, {
519
+ fingerprint,
520
+ result: continued,
521
+ nativeAgent: event.nativeAgent,
522
+ ...(event.nativeSession === undefined
523
+ ? {}
524
+ : { nativeSession: event.nativeSession }),
525
+ });
356
526
  await bindAndRecord(event, continued, generation);
357
527
  return continued;
358
528
  }
359
- if (previous !== undefined && previous.turn === event.turn && !previous.closed) {
360
- if (previous.task !== event.task && hasHumanInput(event.nativeMessages ?? [])) {
529
+ if (previous !== undefined &&
530
+ previous.turn === event.turn &&
531
+ !previous.closed) {
532
+ if (previous.task !== event.task &&
533
+ hasHumanInput(event.nativeMessages ?? [])) {
361
534
  if (!continuesCodingScope(event))
362
- return { admitted: false, prepared: previous.prepared, catalog: event.capabilities };
363
- const workflow = await runtime.withDatabase(db => readCodingWorkflow(db, previous.runId));
535
+ return {
536
+ admitted: false,
537
+ prepared: previous.prepared,
538
+ catalog: event.capabilities,
539
+ };
540
+ const workflow = await runtime.withDatabase((db) => readCodingWorkflow(db, previous.runId));
364
541
  const scopeChanging = !!workflow && event.requestIntent?.relation === 'scope_change';
365
542
  if (scopeChanging && event.requestIntent) {
366
- const evidenceRefs = event.requestIntentContext ? requestIntentReferenceIds(event.requestIntentContext) : [];
367
- await runtime.withDatabase(db => markCodingScopeChange(db, { runId: previous.runId, sessionId: previous.sessionId, repositoryRoot: previous.repositoryRoot }, `request-intent:${event.sessionId}:${event.turn}:${event.requestIntent.evidence.requestDigest}`, event.requestIntent.evidence.requestDigest, evidenceRefs));
543
+ const evidenceRefs = event.requestIntentContext
544
+ ? requestIntentReferenceIds(event.requestIntentContext)
545
+ : [];
546
+ await runtime.withDatabase((db) => markCodingScopeChange(db, {
547
+ runId: previous.runId,
548
+ sessionId: previous.sessionId,
549
+ repositoryRoot: previous.repositoryRoot,
550
+ }, `request-intent:${event.sessionId}:${event.turn}:${event.requestIntent.evidence.requestDigest}`, event.requestIntent.evidence.requestDigest, evidenceRefs));
368
551
  }
369
- const ownerKind = event.memoryOwner ?? memoryOwnerKind(event.requestIntent, event.profileHints?.taskType ?? previous.prepared.intake.profile.taskType);
370
- const prepared = { ...previous.prepared,
371
- ...(scopeChanging ? { context: null, memoryPolicy: { memoryReasoningRequired: false, contextWithheld: false, withheldReason: null, deliveryEmpty: true } } : {}),
372
- memoryOwner: createMemoryOwner(ownerKind, { agentId: event.agent.id, sessionId: event.sessionId, turn: event.turn,
373
- requestDigest: canonicalContentHash(event.task), runId: previous.runId }) };
374
- const selected = await this.choose(event, { admitted: !event.signal.aborted, prepared, catalog: event.capabilities });
552
+ const ownerKind = event.memoryOwner ??
553
+ memoryOwnerKind(event.requestIntent, event.profileHints?.taskType ??
554
+ previous.prepared.intake.profile.taskType);
555
+ const prepared = {
556
+ ...previous.prepared,
557
+ ...(scopeChanging
558
+ ? {
559
+ context: null,
560
+ memoryPolicy: {
561
+ memoryReasoningRequired: false,
562
+ contextWithheld: false,
563
+ withheldReason: null,
564
+ deliveryEmpty: true,
565
+ },
566
+ }
567
+ : {}),
568
+ memoryOwner: createMemoryOwner(ownerKind, {
569
+ agentId: event.agent.id,
570
+ sessionId: event.sessionId,
571
+ turn: event.turn,
572
+ requestDigest: canonicalContentHash(event.task),
573
+ runId: previous.runId,
574
+ }),
575
+ };
576
+ const selected = await this.choose(event, {
577
+ admitted: !event.signal.aborted,
578
+ prepared,
579
+ catalog: event.capabilities,
580
+ });
375
581
  if (selected.admitted)
376
582
  await bindAndRecord(event, selected, generation);
377
583
  return selected;
@@ -389,13 +595,18 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
389
595
  }
390
596
  if (previous !== undefined && previous.turn < event.turn) {
391
597
  if (event.signal.aborted)
392
- return { admitted: false, prepared: previous.prepared, catalog: event.capabilities };
598
+ return {
599
+ admitted: false,
600
+ prepared: previous.prepared,
601
+ catalog: event.capabilities,
602
+ };
393
603
  let previousState = await readStateForRun(previous);
394
604
  // DSH's dedicated plan-review card returns the composer to the user
395
605
  // when they choose "Chat about it". The next human message is the
396
606
  // requested plan revision; settle it host-side before Zenki resumes.
397
607
  // This keeps enno_answer host-only and avoids reopening Akinator.
398
- if (previousState.nextAction === 'ask_user_confirmation' && previousState.contractRevision !== null) {
608
+ if (previousState.nextAction === 'ask_user_confirmation' &&
609
+ previousState.contractRevision !== null) {
399
610
  const expectedRevision = previousState.contractRevision;
400
611
  const requestedChanges = event.task.trim();
401
612
  if (requestedChanges.length === 0)
@@ -415,73 +626,140 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
415
626
  action: 'revise',
416
627
  requestedChanges,
417
628
  }));
418
- previous.prepared = { ...previous.prepared, ennoOduno: revised.ennoOduno };
629
+ previous.prepared = {
630
+ ...previous.prepared,
631
+ ennoOduno: revised.ennoOduno,
632
+ };
419
633
  const next = policyState(revised.ennoOduno, previous, previous.sessionId, revised.executionLease);
420
634
  turnState.applyPolicy(previous.runId, next);
421
635
  previousState = revised.ennoOduno;
422
636
  }
423
637
  const previousWasChat = previous.prepared.intake.profile.taskType === 'chat';
424
- const superseded = previous.prepared.ennoOduno.applicable && supersedesUnstartedEnno(event, previousState);
638
+ const superseded = previous.prepared.ennoOduno.applicable &&
639
+ supersedesUnstartedEnno(event, previousState);
425
640
  const selection = getSelection(previous.runId)?.value;
426
- const coding = await runtime.withDatabase(db => readCodingWorkflow(db, previous.runId));
641
+ const coding = await runtime.withDatabase((db) => readCodingWorkflow(db, previous.runId));
427
642
  // Child results and other generated turns continue the admitted Goal.
428
643
  // Only a new human request can replace it without an explicit continuation.
429
- const resumeCoding = coding && !['completed', 'cancelled', 'superseded'].includes(coding.state)
430
- && (!hasHumanInput(event.nativeMessages ?? []) || continuesCodingScope(event));
431
- const continuePrevious = resumeCoding || selection && (selection.status !== 'ready' || selection.mode === 'normal' && previous.failed)
432
- ? true : previousWasChat
433
- ? event.profileHints?.taskType === 'chat'
434
- : !superseded
435
- && previousState.status !== 'completed'
436
- && previousState.status !== 'blocked'
437
- && previousState.status !== 'cancelled'
438
- && (previousState.nextAction !== 'complete' || executionSupport.paused(previous.sessionId));
644
+ const resumeCoding = coding &&
645
+ !['completed', 'cancelled', 'superseded'].includes(coding.state) &&
646
+ (!hasHumanInput(event.nativeMessages ?? []) ||
647
+ continuesCodingScope(event));
648
+ const continuePrevious = resumeCoding ||
649
+ (selection &&
650
+ (selection.status !== 'ready' ||
651
+ (selection.mode === 'normal' && previous.failed)))
652
+ ? true
653
+ : previousWasChat
654
+ ? event.profileHints?.taskType === 'chat'
655
+ : !superseded &&
656
+ previousState.status !== 'completed' &&
657
+ previousState.status !== 'blocked' &&
658
+ previousState.status !== 'cancelled' &&
659
+ (previousState.nextAction !== 'complete' ||
660
+ executionSupport.paused(previous.sessionId));
439
661
  if (continuePrevious) {
440
662
  this.assertCatalog(previous.catalog, event.capabilities);
441
663
  const intent = event.requestIntent;
442
- const scopeChanging = !!coding && intent?.relation === 'scope_change' && intent.action !== 'answer' && intent.action !== 'unknown';
664
+ const scopeChanging = !!coding &&
665
+ intent?.relation === 'scope_change' &&
666
+ intent.action !== 'answer' &&
667
+ intent.action !== 'unknown';
443
668
  if (scopeChanging && intent) {
444
- const evidenceRefs = event.requestIntentContext ? requestIntentReferenceIds(event.requestIntentContext) : [];
445
- await runtime.withDatabase(db => markCodingScopeChange(db, { runId: previous.runId, sessionId: previous.sessionId, repositoryRoot: previous.repositoryRoot }, `request-intent:${event.sessionId}:${event.turn}:${intent.evidence.requestDigest}`, intent.evidence.requestDigest, evidenceRefs));
669
+ const evidenceRefs = event.requestIntentContext
670
+ ? requestIntentReferenceIds(event.requestIntentContext)
671
+ : [];
672
+ await runtime.withDatabase((db) => markCodingScopeChange(db, {
673
+ runId: previous.runId,
674
+ sessionId: previous.sessionId,
675
+ repositoryRoot: previous.repositoryRoot,
676
+ }, `request-intent:${event.sessionId}:${event.turn}:${intent.evidence.requestDigest}`, intent.evidence.requestDigest, evidenceRefs));
446
677
  }
447
678
  const refreshedWork = await refreshContinuedWorkLease(previous, previousState);
448
679
  if (refreshedWork !== undefined) {
449
- previous.prepared = { ...previous.prepared, ennoOduno: refreshedWork.state };
680
+ previous.prepared = {
681
+ ...previous.prepared,
682
+ ennoOduno: refreshedWork.state,
683
+ };
450
684
  const next = policyState(refreshedWork.state, previous, previous.sessionId, refreshedWork.lease);
451
685
  turnState.applyPolicy(previous.runId, next);
452
686
  }
453
- const continuedMemory = { context: null,
454
- memoryPolicy: { memoryReasoningRequired: false, contextWithheld: false, withheldReason: null, deliveryEmpty: true } };
455
- const ownerKind = event.memoryOwner ?? memoryOwnerKind(intent, event.profileHints?.taskType ?? previous.prepared.intake.profile.taskType);
456
- const continuedPrepared = { ...previous.prepared, ...(continuedMemory ?? {}),
457
- memoryOwner: createMemoryOwner(ownerKind, { agentId: event.agent.id, sessionId: event.sessionId, turn: event.turn,
458
- requestDigest: canonicalContentHash(event.task), runId: previous.runId }) };
459
- const continued = await this.choose(event, { admitted: !event.signal.aborted, prepared: continuedPrepared, catalog: event.capabilities });
687
+ const continuedMemory = {
688
+ context: null,
689
+ memoryPolicy: {
690
+ memoryReasoningRequired: false,
691
+ contextWithheld: false,
692
+ withheldReason: null,
693
+ deliveryEmpty: true,
694
+ },
695
+ };
696
+ const ownerKind = event.memoryOwner ??
697
+ memoryOwnerKind(intent, event.profileHints?.taskType ??
698
+ previous.prepared.intake.profile.taskType);
699
+ const continuedPrepared = {
700
+ ...previous.prepared,
701
+ ...(continuedMemory ?? {}),
702
+ memoryOwner: createMemoryOwner(ownerKind, {
703
+ agentId: event.agent.id,
704
+ sessionId: event.sessionId,
705
+ turn: event.turn,
706
+ requestDigest: canonicalContentHash(event.task),
707
+ runId: previous.runId,
708
+ }),
709
+ };
710
+ const continued = await this.choose(event, {
711
+ admitted: !event.signal.aborted,
712
+ prepared: continuedPrepared,
713
+ catalog: event.capabilities,
714
+ });
460
715
  if (continued.admitted) {
461
716
  continuedTurns.set(cacheKey, {
462
717
  fingerprint,
463
- ...(event.requestIntent?.relation === 'unknown' || event.requestIntent?.action === 'unknown' ? { unresolvedBinding } : {}),
718
+ ...(event.requestIntent?.relation === 'unknown' ||
719
+ event.requestIntent?.action === 'unknown'
720
+ ? { unresolvedBinding }
721
+ : {}),
464
722
  result: continued,
465
- ...(event.profileHints === undefined ? {} : { profileHints: event.profileHints }),
466
- ...(event.nativeAgent === undefined ? {} : { nativeAgent: event.nativeAgent }),
467
- ...(event.nativeSession === undefined ? {} : { nativeSession: event.nativeSession }),
723
+ ...(event.profileHints === undefined
724
+ ? {}
725
+ : { profileHints: event.profileHints }),
726
+ ...(event.nativeAgent === undefined
727
+ ? {}
728
+ : { nativeAgent: event.nativeAgent }),
729
+ ...(event.nativeSession === undefined
730
+ ? {}
731
+ : { nativeSession: event.nativeSession }),
468
732
  });
469
733
  await bindAndRecord(event, continued, generation);
470
734
  }
471
735
  return continued;
472
736
  }
473
737
  if (event.signal.aborted)
474
- return { admitted: false, prepared: previous.prepared, catalog: event.capabilities };
475
- await retireSupersededRun(previous, superseded || previousState.status === 'cancelled' ? 'cancelled' : previousState.status === 'blocked' ? 'failed' : 'completed');
738
+ return {
739
+ admitted: false,
740
+ prepared: previous.prepared,
741
+ catalog: event.capabilities,
742
+ };
743
+ await retireSupersededRun(previous, superseded || previousState.status === 'cancelled'
744
+ ? 'cancelled'
745
+ : previousState.status === 'blocked'
746
+ ? 'failed'
747
+ : 'completed');
476
748
  }
477
749
  const resumed = await resumeExistingRun?.(event);
478
750
  if (resumed !== undefined) {
479
751
  resumedTurns.set(cacheKey, {
480
752
  fingerprint,
481
753
  result: resumed,
482
- ...(event.profileHints === undefined ? {} : { profileHints: event.profileHints }),
483
- ...(event.nativeAgent === undefined ? {} : { nativeAgent: event.nativeAgent }),
484
- ...(event.nativeSession === undefined ? {} : { nativeSession: event.nativeSession }),
754
+ ...(event.profileHints === undefined
755
+ ? {}
756
+ : { profileHints: event.profileHints }),
757
+ ...(event.nativeAgent === undefined
758
+ ? {}
759
+ : { nativeAgent: event.nativeAgent }),
760
+ ...(event.nativeSession === undefined
761
+ ? {}
762
+ : { nativeSession: event.nativeSession }),
485
763
  });
486
764
  await bindAndRecord(event, resumed, generation);
487
765
  const selected = await this.choose(event, resumed);
@@ -502,7 +780,9 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
502
780
  const previous = currentSession(event.sessionId);
503
781
  if (previous)
504
782
  ennoMemory.invalidate(previous.runId);
505
- const state = previous === undefined ? undefined : turnState.policyState(previous.runId);
783
+ const state = previous === undefined
784
+ ? undefined
785
+ : turnState.policyState(previous.runId);
506
786
  if (previous !== undefined) {
507
787
  try {
508
788
  await runtime.withDatabase((database) => withImmediateTransaction(database, () => {
@@ -529,12 +809,23 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
529
809
  const item = currentSession(event.sessionId);
530
810
  if (!item)
531
811
  return;
532
- const task = human ? textFromMessages(messages.filter(isHumanMessage), event.task) : undefined;
812
+ const task = human
813
+ ? textFromMessages(messages.filter(isHumanMessage), event.task)
814
+ : undefined;
533
815
  if (task !== undefined)
534
816
  item.memoryInput = task;
535
- await executionSupport.refresh({ ...executionBinding(item), ...(task === undefined ? {} : {
536
- task, humanInput: canonicalContentHash({ turn: event.turn, messages: messages.filter(isHumanMessage) }),
537
- }) }, human);
817
+ await executionSupport.refresh({
818
+ ...executionBinding(item),
819
+ ...(task === undefined
820
+ ? {}
821
+ : {
822
+ task,
823
+ humanInput: canonicalContentHash({
824
+ turn: event.turn,
825
+ messages: messages.filter(isHumanMessage),
826
+ }),
827
+ }),
828
+ }, human);
538
829
  }
539
830
  /** The public preparation tool uses the same human boundary as native pre-step. */
540
831
  async prepareRequestedWork(event) {
@@ -560,7 +851,8 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
560
851
  return { kind: 'reject' };
561
852
  const nativeMessages = Object.freeze([...(event.nativeMessages ?? [])]);
562
853
  await captureInitialInput(event.sessionId, event.turn, nativeMessages);
563
- const humanPresent = nativeMessages.some(isHumanMessage) || downstream.messages.some(isHumanMessage);
854
+ const humanPresent = nativeMessages.some(isHumanMessage) ||
855
+ downstream.messages.some(isHumanMessage);
564
856
  const seenContinuations = new Set();
565
857
  let nativeDecision = {
566
858
  ...downstream,
@@ -576,7 +868,11 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
576
868
  };
577
869
  if (humanPresent) {
578
870
  await this.acceptHumanInput(event);
579
- nativeDecision = { ...nativeDecision, messages: nativeDecision.messages.filter(message => objectRecord(objectRecord(message)?.source)?.form !== ANSWER_REVIEW_FORM) };
871
+ nativeDecision = {
872
+ ...nativeDecision,
873
+ messages: nativeDecision.messages.filter((message) => objectRecord(objectRecord(message)?.source)?.form !==
874
+ ANSWER_REVIEW_FORM),
875
+ };
580
876
  }
581
877
  try {
582
878
  const result = await this.prepare(event);
@@ -584,33 +880,59 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
584
880
  return nativeDecision;
585
881
  const item = currentSession(event.sessionId);
586
882
  if (item !== undefined) {
587
- if (getSelection(item.runId)?.value.status === 'ready' || getSelection(item.runId)?.value.discussion) {
588
- const original = await runtime.withDatabase(db => unexecutedRunInput(db, item.runId, item.sessionId));
589
- const deliveredIds = new Set(sessionEventSource(event.nativeSession).snapshotEvents()
590
- .filter(e => e.type === 'user/message').map(e => objectRecord(e.data)?.id));
591
- const currentIds = new Set(nativeDecision.messages.map(m => objectRecord(m)?.id));
592
- const missing = original.filter(m => {
883
+ if (getSelection(item.runId)?.value.status === 'ready' ||
884
+ getSelection(item.runId)?.value.discussion) {
885
+ const original = await runtime.withDatabase((db) => unexecutedRunInput(db, item.runId, item.sessionId));
886
+ const deliveredIds = new Set(sessionEventSource(event.nativeSession)
887
+ .snapshotEvents()
888
+ .filter((e) => e.type === 'user/message')
889
+ .map((e) => objectRecord(e.data)?.id));
890
+ const currentIds = new Set(nativeDecision.messages.map((m) => objectRecord(m)?.id));
891
+ const missing = original.filter((m) => {
593
892
  const id = objectRecord(m)?.id;
594
- return typeof id === 'string' && !deliveredIds.has(id) && !currentIds.has(id);
893
+ return (typeof id === 'string' &&
894
+ !deliveredIds.has(id) &&
895
+ !currentIds.has(id));
595
896
  });
596
897
  if (missing.length)
597
- nativeDecision = { ...nativeDecision, messages: [...missing, ...nativeDecision.messages] };
898
+ nativeDecision = {
899
+ ...nativeDecision,
900
+ messages: [...missing, ...nativeDecision.messages],
901
+ };
598
902
  }
599
- const humanMessages = [...new Map([...nativeMessages, ...nativeDecision.messages].filter(isHumanMessage)
600
- .map(message => [objectRecord(message)?.id ?? canonicalContentHash(message), message])).values()];
903
+ const humanMessages = [
904
+ ...new Map([...nativeMessages, ...nativeDecision.messages]
905
+ .filter(isHumanMessage)
906
+ .map((message) => [
907
+ objectRecord(message)?.id ?? canonicalContentHash(message),
908
+ message,
909
+ ])).values(),
910
+ ];
601
911
  await this.refreshExecutionInput(event, humanMessages, humanPresent);
602
912
  // Only an empty, automatic step can be deliberately paused. Human,
603
913
  // attachment and other pending inputs must never be consumed here.
604
- if (!humanPresent && event.nativeMessages?.length === 0 && nativeDecision.messages.every(message => {
605
- const source = objectRecord(objectRecord(message)?.source);
606
- return (source?.kind === 'runtime-context' || source?.kind === 'plugin' && source.plugin === '@deepseek-ai/dsh-system-prompt') && source.form === 'snapshot';
607
- })) {
914
+ if (!humanPresent &&
915
+ event.nativeMessages?.length === 0 &&
916
+ nativeDecision.messages.every((message) => {
917
+ const source = objectRecord(objectRecord(message)?.source);
918
+ return ((source?.kind === 'runtime-context' ||
919
+ (source?.kind === 'plugin' &&
920
+ source.plugin === '@deepseek-ai/dsh-system-prompt')) &&
921
+ source.form === 'snapshot');
922
+ })) {
608
923
  const paused = await executionSupport.pauseAtBoundary(event.sessionId, async (id, text) => {
609
924
  const session = event.nativeSession;
610
925
  if (!session?.snapshotEvents || !sessions?.flush)
611
926
  throw new Error('Pause notice delivery unavailable');
612
- await runtime.withDatabase(db => saveSessionNotice(db, { id, runId: item.runId, sessionId: item.sessionId,
613
- rootPath: item.cwd, kind: 'status', text, anchorSeq: session.snapshotEvents().at(-1)?.seq ?? 0 }));
927
+ await runtime.withDatabase((db) => saveSessionNotice(db, {
928
+ id,
929
+ runId: item.runId,
930
+ sessionId: item.sessionId,
931
+ rootPath: item.cwd,
932
+ kind: 'status',
933
+ text,
934
+ anchorSeq: session.snapshotEvents().at(-1)?.seq ?? 0,
935
+ }));
614
936
  await sessions.flush(session);
615
937
  event.signal.throwIfAborted();
616
938
  // A human can arrive while the native log flush is in flight.
@@ -626,12 +948,20 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
626
948
  if (item !== undefined && !getSelection(item.runId)?.value.discussion)
627
949
  await refreshEnnoMemory(item, event.signal);
628
950
  const messages = await contextMessages(event, nativeDecision.messages);
629
- return { ...nativeDecision, messages: [...executionSupport.projectMessages(event.sessionId, nativeDecision.messages), ...messages] };
951
+ return {
952
+ ...nativeDecision,
953
+ messages: [
954
+ ...executionSupport.projectMessages(event.sessionId, nativeDecision.messages),
955
+ ...messages,
956
+ ],
957
+ };
630
958
  }
631
959
  catch (error) {
632
960
  if (error instanceof ExecutionSelectionPending)
633
961
  return { kind: 'reject' };
634
- if (!humanPresent && nativeMessages.some(message => objectRecord(objectRecord(message)?.source)?.form === ANSWER_REVIEW_FORM))
962
+ if (!humanPresent &&
963
+ nativeMessages.some((message) => objectRecord(objectRecord(message)?.source)?.form ===
964
+ ANSWER_REVIEW_FORM))
635
965
  return { kind: 'reject' };
636
966
  // The native message array is authoritative. Kiokuko degradation must
637
967
  // not turn a claimed user prompt into a rejected/empty DSH step.
@@ -645,26 +975,44 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
645
975
  resumedTurns.delete(`${sessionId}\u0000${turn}`);
646
976
  }
647
977
  }
648
- const gate = new CapturingGate(runtime, userQuestions === undefined ? undefined : createDshIntakeAnswerer(userQuestions), (context) => capabilityCatalog(skills, tools, context), true, akinatorMemoryConfig, decisions, completionMode, memoryRetrievalConfig);
978
+ const gate = new CapturingGate(runtime, userQuestions === undefined
979
+ ? undefined
980
+ : createDshIntakeAnswerer(userQuestions), (context) => capabilityCatalog(skills, tools, context), true, akinatorMemoryConfig, decisions, completionMode, memoryRetrievalConfig);
649
981
  const refreshMemoryOwner = async (item, signal) => {
650
982
  signal.throwIfAborted();
651
- item.prepared = { ...item.prepared, context: null, memoryPolicy: { memoryReasoningRequired: false, contextWithheld: false, withheldReason: null, deliveryEmpty: true } };
983
+ item.prepared = {
984
+ ...item.prepared,
985
+ context: null,
986
+ memoryPolicy: {
987
+ memoryReasoningRequired: false,
988
+ contextWithheld: false,
989
+ withheldReason: null,
990
+ deliveryEmpty: true,
991
+ },
992
+ };
652
993
  };
653
994
  resumeExistingRun = async (event) => runtime.withDatabase(async (database) => {
654
995
  const project = await resolveProjectWorkspaceReadOnly(database, event.cwd, { allowDirectory: true });
655
996
  if (project === undefined)
656
997
  return undefined;
657
- const task = findSessionTask(database, { sessionId: event.sessionId, repositoryRoot: project.repositoryRoot });
998
+ const task = findSessionTask(database, {
999
+ sessionId: event.sessionId,
1000
+ repositoryRoot: project.repositoryRoot,
1001
+ });
658
1002
  if (!task)
659
1003
  return undefined;
660
1004
  const context = event.requestIntentContext;
661
1005
  if (hasHumanInput(event.nativeMessages ?? [])) {
662
- if (!event.requestIntent || !context || !rebindRequestIntentDecision(event.requestIntent, context))
1006
+ if (!event.requestIntent ||
1007
+ !context ||
1008
+ !rebindRequestIntentDecision(event.requestIntent, context))
663
1009
  throw new KiokukoError('CONFLICT', 'A source-bound interpretation is required for the current task');
664
1010
  if (event.requestIntent.relation === 'new')
665
1011
  return undefined;
666
- if (!continuesCodingScope(event) || context.activeTask?.taskId !== task.taskId
667
- || context.activeTask.revision !== task.revision || context.activeTask.generation !== task.generation)
1012
+ if (!continuesCodingScope(event) ||
1013
+ context.activeTask?.taskId !== task.taskId ||
1014
+ context.activeTask.revision !== task.revision ||
1015
+ context.activeTask.generation !== task.generation)
668
1016
  throw new KiokukoError('CONFLICT', 'The current task continuation is unresolved or stale');
669
1017
  }
670
1018
  const runId = task.taskId;
@@ -676,16 +1024,26 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
676
1024
  // Enno selection can recover Enno ownership instead of ordinary execution.
677
1025
  const executionMode = selection?.mode === 'enno' ? 'enno' : 'normal';
678
1026
  const owner = readExecutionOwner(database, event.sessionId);
679
- if (owner && (owner.run_id !== runId || owner.workspace !== project.workspace || owner.mode !== executionMode))
1027
+ if (owner &&
1028
+ (owner.run_id !== runId ||
1029
+ owner.workspace !== project.workspace ||
1030
+ owner.mode !== executionMode))
680
1031
  throw new KiokukoError('CONFLICT', 'Task execution owner changed');
681
- const orchestration = database.prepare('SELECT orchestration_session_id AS id FROM enno_contracts WHERE run_id=?')
1032
+ const orchestration = database
1033
+ .prepare('SELECT orchestration_session_id AS id FROM enno_contracts WHERE run_id=?')
682
1034
  .get(runId);
683
- const snapshot = !orchestration ? undefined : readEnnoSnapshot(database, {
684
- runId, workspace: project.workspace, orchestrationId: orchestration.id,
685
- });
1035
+ const snapshot = !orchestration
1036
+ ? undefined
1037
+ : readEnnoSnapshot(database, {
1038
+ runId,
1039
+ workspace: project.workspace,
1040
+ orchestrationId: orchestration.id,
1041
+ });
686
1042
  if (executionMode === 'enno' && !snapshot)
687
1043
  throw new KiokukoError('CONFLICT', 'Persisted Enno selection has no matching execution contract');
688
- if (snapshot && !event.signal.aborted && supersedesUnstartedEnno(event, stateForSnapshot(snapshot))) {
1044
+ if (snapshot &&
1045
+ !event.signal.aborted &&
1046
+ supersedesUnstartedEnno(event, stateForSnapshot(snapshot))) {
689
1047
  terminalizeLedgerRunInTransaction(database, runId, 'cancelled');
690
1048
  return undefined;
691
1049
  }
@@ -693,28 +1051,56 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
693
1051
  const continuationId = pluginContinuationId(message);
694
1052
  if (continuationId === undefined)
695
1053
  return false;
696
- const outbox = database.prepare(`
1054
+ const outbox = database
1055
+ .prepare(`
697
1056
  SELECT message_form AS messageForm
698
1057
  FROM dsh_continuation_outbox
699
1058
  WHERE continuation_id = ?
700
- `).get(continuationId);
701
- return outbox === undefined ? !isLoopRecoveryMessage(message) : outbox.messageForm !== 'loop-recovery';
1059
+ `)
1060
+ .get(continuationId);
1061
+ return outbox === undefined
1062
+ ? !isLoopRecoveryMessage(message)
1063
+ : outbox.messageForm !== 'loop-recovery';
702
1064
  });
703
- const automaticClaimId = automaticMessage === undefined ? undefined : pluginContinuationId(automaticMessage);
704
- const decision = snapshot ? decideDshContinuation(database, {
705
- dshSessionId: event.sessionId, cwd: event.cwd,
706
- ...(automaticClaimId === undefined ? {} : { claimId: automaticClaimId }),
707
- }, runId) : undefined;
1065
+ const automaticClaimId = automaticMessage === undefined
1066
+ ? undefined
1067
+ : pluginContinuationId(automaticMessage);
1068
+ const decision = snapshot
1069
+ ? decideDshContinuation(database, {
1070
+ dshSessionId: event.sessionId,
1071
+ cwd: event.cwd,
1072
+ ...(automaticClaimId === undefined
1073
+ ? {}
1074
+ : { claimId: automaticClaimId }),
1075
+ }, runId)
1076
+ : undefined;
708
1077
  if (decision && (!decision.continue || decision.runId !== runId)) {
709
- throw new KiokukoError('CONFLICT', decision.warning ?? 'The active run cannot be resumed by this DSH session');
1078
+ throw new KiokukoError('CONFLICT', decision.warning ??
1079
+ 'The active run cannot be resumed by this DSH session');
710
1080
  }
711
- const capabilityEntries = [...event.capabilities.skills, ...event.capabilities.tools];
1081
+ const capabilityEntries = [
1082
+ ...event.capabilities.skills,
1083
+ ...event.capabilities.tools,
1084
+ ];
712
1085
  const { state: intakeState, link: intakeLink } = await continueTaskIntake(database, {
713
- ...task, workspace: project.workspace, expectedRevision: task.revision, expectedGeneration: task.generation,
714
- capabilities: capabilityEntries, signal: event.signal,
715
- answerer: userQuestions ? createDshIntakeAnswerer(userQuestions) : undefined,
1086
+ ...task,
1087
+ workspace: project.workspace,
1088
+ expectedRevision: task.revision,
1089
+ expectedGeneration: task.generation,
1090
+ capabilities: capabilityEntries,
1091
+ signal: event.signal,
1092
+ answerer: userQuestions
1093
+ ? createDshIntakeAnswerer(userQuestions)
1094
+ : undefined,
716
1095
  agent: event.nativeAgent,
717
- validateCapabilities: async () => gate.assertCatalog(event.capabilities, await capabilityCatalog(skills, tools, { agent: event.agent, ...(event.nativeAgent ? { nativeAgent: event.nativeAgent } : {}), cwd: event.cwd, signal: event.signal })),
1096
+ validateCapabilities: async () => gate.assertCatalog(event.capabilities, await capabilityCatalog(skills, tools, {
1097
+ agent: event.agent,
1098
+ ...(event.nativeAgent
1099
+ ? { nativeAgent: event.nativeAgent }
1100
+ : {}),
1101
+ cwd: event.cwd,
1102
+ signal: event.signal,
1103
+ })),
718
1104
  });
719
1105
  const intake = intakeState.session;
720
1106
  const capabilityResolution = resolveCapabilities({
@@ -725,24 +1111,46 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
725
1111
  memoryUse: 'none',
726
1112
  });
727
1113
  const canonicalCwd = realpathSync(event.cwd);
728
- const admitted = !event.signal.aborted && intakeState.status !== 'needs_answer'
729
- && taskCanExecute(readTaskSnapshot(database, task)) && !hasBlockingRequiredCapability(capabilityResolution);
1114
+ const admitted = !event.signal.aborted &&
1115
+ intakeState.status !== 'needs_answer' &&
1116
+ taskCanExecute(readTaskSnapshot(database, task)) &&
1117
+ !hasBlockingRequiredCapability(capabilityResolution);
730
1118
  const runStatus = new LedgerStore(database).readRun(runId).status;
731
1119
  if (runStatus !== 'active' && runStatus !== 'intake')
732
1120
  throw new KiokukoError('CONFLICT', 'Task requires an explicit resume');
733
1121
  if (admitted) {
734
1122
  if (!owner)
735
- claimExecutionOwner(database, { sessionId: event.sessionId, workspace: project.workspace,
736
- mode: executionMode, startId: typeof run.metadata.requestId === 'string' ? run.metadata.requestId : `run:${runId}`, runId });
1123
+ claimExecutionOwner(database, {
1124
+ sessionId: event.sessionId,
1125
+ workspace: project.workspace,
1126
+ mode: executionMode,
1127
+ startId: typeof run.metadata.requestId === 'string'
1128
+ ? run.metadata.requestId
1129
+ : `run:${runId}`,
1130
+ runId,
1131
+ });
737
1132
  initializeTaskCompletion(database, runId, completionMode);
738
- if (executionMode === 'normal' && !readCodingWorkflow(database, runId) && codingWorkRequested(intake.task, event.requestIntent, intake.profile.taskType))
739
- initializeCodingWorkflow(database, { runId, sessionId: event.sessionId, repositoryRoot: project.repositoryRoot,
740
- requestId: typeof run.metadata.requestId === 'string' ? run.metadata.requestId : `run:${runId}`, task: intake.task });
1133
+ if (executionMode === 'normal' &&
1134
+ !readCodingWorkflow(database, runId) &&
1135
+ codingWorkRequested(intake.task, event.requestIntent, intake.profile.taskType))
1136
+ initializeCodingWorkflow(database, {
1137
+ runId,
1138
+ sessionId: event.sessionId,
1139
+ repositoryRoot: project.repositoryRoot,
1140
+ requestId: typeof run.metadata.requestId === 'string'
1141
+ ? run.metadata.requestId
1142
+ : `run:${runId}`,
1143
+ task: intake.task,
1144
+ });
741
1145
  }
742
1146
  const prepared = {
743
- memoryOwner: createMemoryOwner(event.memoryOwner ?? memoryOwnerKind(event.requestIntent, intake.profile.taskType), {
744
- agentId: event.agent.id, sessionId: event.sessionId, turn: event.turn,
745
- requestDigest: canonicalContentHash(event.task), runId,
1147
+ memoryOwner: createMemoryOwner(event.memoryOwner ??
1148
+ memoryOwnerKind(event.requestIntent, intake.profile.taskType), {
1149
+ agentId: event.agent.id,
1150
+ sessionId: event.sessionId,
1151
+ turn: event.turn,
1152
+ requestDigest: canonicalContentHash(event.task),
1153
+ runId,
746
1154
  }),
747
1155
  project,
748
1156
  executionContext: {
@@ -762,14 +1170,33 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
762
1170
  },
763
1171
  capabilities: capabilityResolution,
764
1172
  run: { runId, status: runStatus },
765
- skillDiscovery: { attempted: false, mode: 'off', requirements: [], queries: [], cacheHits: 0, candidates: 0, selected: [], failures: [] },
1173
+ skillDiscovery: {
1174
+ attempted: false,
1175
+ mode: 'off',
1176
+ requirements: [],
1177
+ queries: [],
1178
+ cacheHits: 0,
1179
+ candidates: 0,
1180
+ selected: [],
1181
+ failures: [],
1182
+ },
766
1183
  context: null,
767
- memoryPolicy: { memoryReasoningRequired: false, contextWithheld: false, withheldReason: null, deliveryEmpty: true },
1184
+ memoryPolicy: {
1185
+ memoryReasoningRequired: false,
1186
+ contextWithheld: false,
1187
+ withheldReason: null,
1188
+ deliveryEmpty: true,
1189
+ },
768
1190
  warnings: capabilityResolution.warnings,
769
- nextAction: admitted ? 'proceed' : hasBlockingRequiredCapability(capabilityResolution)
770
- ? 'required_capability_unavailable' : 'answer_from_evidence_or_ask_user',
1191
+ nextAction: admitted
1192
+ ? 'proceed'
1193
+ : hasBlockingRequiredCapability(capabilityResolution)
1194
+ ? 'required_capability_unavailable'
1195
+ : 'answer_from_evidence_or_ask_user',
771
1196
  securityNotice: 'This resumed DSH run uses only current repository evidence and the current host capability catalog; previously delivered ordinary memory is not replayed implicitly. Active ennoMemory may make a new bounded selection under current authority.',
772
- ennoOduno: snapshot ? stateForSnapshot(snapshot) : inapplicableEnnoState(),
1197
+ ennoOduno: snapshot
1198
+ ? stateForSnapshot(snapshot)
1199
+ : inapplicableEnnoState(),
773
1200
  };
774
1201
  if (decision?.executionLease)
775
1202
  turnState.stageLease(runId, decision.executionLease);
@@ -785,17 +1212,32 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
785
1212
  return undefined;
786
1213
  const messageRefs = requestIntentMessageRefs(human, task);
787
1214
  const activeTask = await runtime.withDatabase(async (database) => {
788
- const project = await resolveProjectWorkspaceReadOnly(database, cwd, { allowDirectory: true });
789
- return project ? findSessionTask(database, { sessionId, repositoryRoot: project.repositoryRoot }) : undefined;
1215
+ const project = await resolveProjectWorkspaceReadOnly(database, cwd, {
1216
+ allowDirectory: true,
1217
+ });
1218
+ return project
1219
+ ? findSessionTask(database, {
1220
+ sessionId,
1221
+ repositoryRoot: project.repositoryRoot,
1222
+ })
1223
+ : undefined;
790
1224
  });
791
1225
  let activeGoal;
792
1226
  let proposals = [];
793
- const sameOwner = previous && previous.sessionId === sessionId && previous.nativeAgent === input.agent && previous.nativeSession === nativeSession;
794
- const persistedRun = sameOwner ? undefined : await runtime.withDatabase(async (database) => {
795
- const project = await resolveProjectWorkspaceReadOnly(database, cwd, { allowDirectory: true });
796
- if (!project)
797
- return undefined;
798
- const candidates = database.prepare(`SELECT lr.run_id AS runId,
1227
+ const sameOwner = previous &&
1228
+ previous.sessionId === sessionId &&
1229
+ previous.nativeAgent === input.agent &&
1230
+ previous.nativeSession === nativeSession;
1231
+ const persistedRun = sameOwner
1232
+ ? undefined
1233
+ : await runtime.withDatabase(async (database) => {
1234
+ const project = await resolveProjectWorkspaceReadOnly(database, cwd, {
1235
+ allowDirectory: true,
1236
+ });
1237
+ if (!project)
1238
+ return undefined;
1239
+ const candidates = database
1240
+ .prepare(`SELECT lr.run_id AS runId,
799
1241
  ec.orchestration_session_id AS orchestrationId
800
1242
  FROM ledger_runs AS lr
801
1243
  LEFT JOIN dsh_coding_workflows AS cw ON cw.run_id = lr.run_id
@@ -807,69 +1249,112 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
807
1249
  AND json_extract(cw.snapshot_json, '$.state') NOT IN ('completed', 'cancelled', 'superseded'))
808
1250
  OR (ec.repository_root = ? AND ec.status NOT IN ('completed', 'cancelled', 'blocked')))
809
1251
  ORDER BY lr.created_at, lr.run_id LIMIT 2`)
810
- .all(project.workspace, sessionId, project.repositoryRoot, sessionId, project.repositoryRoot);
811
- if (candidates.length > 1)
812
- throw new KiokukoError('CONFLICT', 'Multiple active tasks match this session; refusing to guess');
813
- return candidates[0] === undefined ? undefined : { ...candidates[0], workspace: project.workspace };
814
- });
1252
+ .all(project.workspace, sessionId, project.repositoryRoot, sessionId, project.repositoryRoot);
1253
+ if (candidates.length > 1)
1254
+ throw new KiokukoError('CONFLICT', 'Multiple active tasks match this session; refusing to guess');
1255
+ return candidates[0] === undefined
1256
+ ? undefined
1257
+ : { ...candidates[0], workspace: project.workspace };
1258
+ });
815
1259
  const runId = sameOwner ? previous.runId : persistedRun?.runId;
816
1260
  if (runId) {
817
- const state = await runtime.withDatabase(database => {
1261
+ const state = await runtime.withDatabase((database) => {
818
1262
  const workflow = readCodingWorkflow(database, runId);
819
1263
  const definition = workflow?.definition;
820
1264
  const frame = readExecutionFrame(database, runId);
821
- const enno = !workflow && persistedRun?.runId === runId && persistedRun.orchestrationId !== null
822
- ? readEnnoSnapshot(database, { runId, workspace: persistedRun.workspace, orchestrationId: persistedRun.orchestrationId })
1265
+ const enno = !workflow &&
1266
+ persistedRun?.runId === runId &&
1267
+ persistedRun.orchestrationId !== null
1268
+ ? readEnnoSnapshot(database, {
1269
+ runId,
1270
+ workspace: persistedRun.workspace,
1271
+ orchestrationId: persistedRun.orchestrationId,
1272
+ })
823
1273
  : undefined;
824
- const active = definition ? {
825
- runId, workflowRevision: workflow.revision, goalId: definition.goal.goalId,
826
- goalRevision: definition.goal.revision, scopeRevision: workflow.scopeVersion, ideal: definition.goal.ideal,
827
- target: definition.goal.target, outOfScope: [...definition.goal.outOfScope], constraints: [...definition.goal.constraints],
828
- } : workflow ? { runId, workflowRevision: workflow.revision, scopeRevision: workflow.scopeVersion, outOfScope: [], constraints: [] }
829
- : enno ? {
830
- runId, workflowRevision: enno.revision, goalRevision: enno.revision, scopeRevision: enno.revision,
831
- ideal: enno.ideal?.objective ?? enno.handoff.objective,
832
- ...(enno.handoff.target === null ? {} : { target: enno.handoff.target }),
833
- outOfScope: [...enno.contract.exclusions], constraints: [...enno.handoff.constraints],
1274
+ const active = definition
1275
+ ? {
1276
+ runId,
1277
+ workflowRevision: workflow.revision,
1278
+ goalId: definition.goal.goalId,
1279
+ goalRevision: definition.goal.revision,
1280
+ scopeRevision: workflow.scopeVersion,
1281
+ ideal: definition.goal.ideal,
1282
+ target: definition.goal.target,
1283
+ outOfScope: [...definition.goal.outOfScope],
1284
+ constraints: [...definition.goal.constraints],
834
1285
  }
835
- : sameOwner && previous.prepared.ennoOduno.applicable ? {
1286
+ : workflow
1287
+ ? {
836
1288
  runId,
837
- ...(previous.prepared.ennoOduno.contractRevision === null ? {} : {
838
- workflowRevision: previous.prepared.ennoOduno.contractRevision,
839
- goalRevision: previous.prepared.ennoOduno.contractRevision,
840
- scopeRevision: previous.prepared.ennoOduno.contractRevision,
841
- }),
842
- ...(previous.prepared.ennoOduno.directive?.objective ? { ideal: previous.prepared.ennoOduno.directive.objective } : {}),
843
- ...(previous.prepared.intake.profile.target ? { target: previous.prepared.intake.profile.target } : {}),
1289
+ workflowRevision: workflow.revision,
1290
+ scopeRevision: workflow.scopeVersion,
844
1291
  outOfScope: [],
845
- constraints: previous.prepared.intake.profile.constraints ? [previous.prepared.intake.profile.constraints] : [],
846
- } : undefined;
847
- const candidates = (frame?.conditions ?? []).filter(condition => condition.approval === 'proposed').slice(0, 7)
848
- .map(condition => ({ id: `condition-${canonicalContentHash([runId, frame.revision, condition.field, condition.text, condition.planRevision])}`,
849
- revision: condition.planRevision ?? frame.revision, digest: canonicalContentHash(condition),
850
- text: `${condition.field}: ${condition.text}`, field: condition.field }));
1292
+ constraints: [],
1293
+ }
1294
+ : enno
1295
+ ? {
1296
+ runId,
1297
+ workflowRevision: enno.revision,
1298
+ goalRevision: enno.revision,
1299
+ scopeRevision: enno.revision,
1300
+ ideal: enno.ideal?.objective ?? enno.handoff.objective,
1301
+ ...(enno.handoff.target === null
1302
+ ? {}
1303
+ : { target: enno.handoff.target }),
1304
+ outOfScope: [...enno.contract.exclusions],
1305
+ constraints: [...enno.handoff.constraints],
1306
+ }
1307
+ : sameOwner && previous.prepared.ennoOduno.applicable
1308
+ ? {
1309
+ runId,
1310
+ ...(previous.prepared.ennoOduno.contractRevision === null
1311
+ ? {}
1312
+ : {
1313
+ workflowRevision: previous.prepared.ennoOduno.contractRevision,
1314
+ goalRevision: previous.prepared.ennoOduno.contractRevision,
1315
+ scopeRevision: previous.prepared.ennoOduno.contractRevision,
1316
+ }),
1317
+ ...(previous.prepared.ennoOduno.directive?.objective
1318
+ ? {
1319
+ ideal: previous.prepared.ennoOduno.directive.objective,
1320
+ }
1321
+ : {}),
1322
+ ...(previous.prepared.intake.profile.target
1323
+ ? { target: previous.prepared.intake.profile.target }
1324
+ : {}),
1325
+ outOfScope: [],
1326
+ constraints: previous.prepared.intake.profile.constraints
1327
+ ? [previous.prepared.intake.profile.constraints]
1328
+ : [],
1329
+ }
1330
+ : undefined;
1331
+ const candidates = (frame?.conditions ?? [])
1332
+ .filter((condition) => condition.approval === 'proposed')
1333
+ .slice(0, 7)
1334
+ .map((condition) => ({
1335
+ id: `condition-${canonicalContentHash([runId, frame.revision, condition.field, condition.text, condition.planRevision])}`,
1336
+ revision: condition.planRevision ?? frame.revision,
1337
+ digest: canonicalContentHash(condition),
1338
+ text: `${condition.field}: ${condition.text}`,
1339
+ field: condition.field,
1340
+ }));
851
1341
  return { active, candidates };
852
1342
  });
853
1343
  activeGoal = state.active;
854
1344
  proposals = state.candidates;
855
1345
  }
856
1346
  const eventSource = sessionEventSource(nativeSession);
857
- const precedingAssistant = [...eventSource.snapshotEvents()].reverse().find(event => event.type === 'assistant/message'
858
- && (eventTurn(event) ?? Number.MAX_SAFE_INTEGER) < input.turn);
859
- if (precedingAssistant && proposals.length < 8) {
860
- const data = objectRecord(precedingAssistant.data);
861
- const message = objectRecord(data?.message) ?? data;
862
- const content = Array.isArray(message?.content) ? message.content : [];
863
- const text = content.filter((block) => objectRecord(block)?.type === 'text')
864
- .map((block) => objectRecord(block)?.text).filter((value) => typeof value === 'string').join('\n').trim();
865
- const messageId = typeof message?.id === 'string' ? message.id : `seq:${precedingAssistant.seq}`;
866
- if (message?.role === 'assistant' && text && text.length <= 8192 && findSecret(text) === undefined) {
867
- proposals = [...proposals, { id: `assistant-${canonicalContentHash([sessionId, messageId])}`, revision: null,
868
- digest: canonicalContentHash(text), text }];
869
- }
870
- }
871
- return createRequestIntentContext({ task, messageRefs, ...(activeGoal ? { activeGoal } : {}),
872
- ...(activeTask ? { activeTask } : {}), proposals: proposals.slice(0, 8) });
1347
+ proposals = [
1348
+ ...proposals,
1349
+ ...nativeRequestProposals(eventSource.snapshotEvents(), input.turn, sessionId),
1350
+ ].slice(0, 8);
1351
+ return createRequestIntentContext({
1352
+ task,
1353
+ messageRefs,
1354
+ ...(activeGoal ? { activeGoal } : {}),
1355
+ ...(activeTask ? { activeTask } : {}),
1356
+ proposals: proposals.slice(0, 8),
1357
+ });
873
1358
  };
874
1359
  const mapPreStep = async (payload, advisoryType, suppliedIntent, interpretationOnly = false) => {
875
1360
  const nativeSession = payload.agent.session;
@@ -886,11 +1371,17 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
886
1371
  const cwd = boundSession?.header?.cwd;
887
1372
  if (typeof cwd !== 'string' || cwd.length === 0)
888
1373
  throw new Error('kiokuko-dsh native session cwd is unavailable');
889
- if (!currentSession(sessionId) && boundSession && !delegation.isChild(payload.agent))
1374
+ if (!currentSession(sessionId) &&
1375
+ boundSession &&
1376
+ !delegation.isChild(payload.agent))
890
1377
  await answerReview.recover(payload.agent, async (row) => {
891
1378
  await sessions?.flush?.(boundSession);
892
1379
  await sessionMirror.checkpointAfterNativeFlush(boundSession);
893
- await closeTurn({ runId: row.runId, status: row.status, ...(row.endSeq === undefined ? {} : { sourceEndSeq: row.endSeq }) });
1380
+ await closeTurn({
1381
+ runId: row.runId,
1382
+ status: row.status,
1383
+ ...(row.endSeq === undefined ? {} : { sourceEndSeq: row.endSeq }),
1384
+ });
894
1385
  });
895
1386
  const sourceStartSeq = dshTurnBoundarySeq(sessionEventSource(boundSession), payload.turn, 'start');
896
1387
  const bound = currentForAgentEvent(payload.agent.id, sessionId, payload.turn, boundSession, payload.agent);
@@ -907,9 +1398,18 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
907
1398
  // human task. The native conversation retains the new user message for the
908
1399
  // model, while any supplied step-local user text still replaces this
909
1400
  // fallback and is recorded as the continuation instruction.
910
- const reviewing = !hasHumanInput(payload.messages) && payload.messages.some(message => objectRecord(objectRecord(message)?.source)?.form === ANSWER_REVIEW_FORM);
911
- const intentTask = hasHumanInput(payload.messages) ? textFromMessages(payload.messages.filter(isHumanMessage), '') : '';
912
- const task = intentTask.trim() ? intentTask : bound?.task ?? (reviewing && previous ? previous.task : textFromMessages(payload.messages, previous?.task));
1401
+ const reviewing = !hasHumanInput(payload.messages) &&
1402
+ payload.messages.some((message) => objectRecord(objectRecord(message)?.source)?.form ===
1403
+ ANSWER_REVIEW_FORM);
1404
+ const intentTask = hasHumanInput(payload.messages)
1405
+ ? textFromMessages(payload.messages.filter(isHumanMessage), '')
1406
+ : '';
1407
+ const task = intentTask.trim()
1408
+ ? intentTask
1409
+ : (bound?.task ??
1410
+ (reviewing && previous
1411
+ ? previous.task
1412
+ : textFromMessages(payload.messages, previous?.task)));
913
1413
  let profile = (() => {
914
1414
  if (bound !== undefined)
915
1415
  return bound.profileHints;
@@ -919,19 +1419,35 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
919
1419
  return previous.profileHints;
920
1420
  if (previous === undefined)
921
1421
  return undefined;
922
- const inferred = resolveGroundedIntakeProfile({ task, cwd }).profileHints.taskType;
923
- const previousType = getSelection(previous.runId)?.value.discussion ? 'chat' : previous.prepared.intake.profile.taskType;
924
- return inferred === null || previousType === 'chat' && inferred === 'chat' ? { taskType: previousType } : undefined;
1422
+ const inferred = resolveGroundedIntakeProfile({ task, cwd }).profileHints
1423
+ .taskType;
1424
+ const previousType = getSelection(previous.runId)?.value.discussion
1425
+ ? 'chat'
1426
+ : previous.prepared.intake.profile.taskType;
1427
+ return inferred === null ||
1428
+ (previousType === 'chat' && inferred === 'chat')
1429
+ ? { taskType: previousType }
1430
+ : undefined;
925
1431
  })();
926
- let deferTaskTypeInference = bound?.deferTaskTypeInference ?? (reviewing ? previous?.deferTaskTypeInference : false) ?? false;
1432
+ let deferTaskTypeInference = bound?.deferTaskTypeInference ??
1433
+ (reviewing ? previous?.deferTaskTypeInference : false) ??
1434
+ false;
927
1435
  let requestIntentContext;
928
1436
  let requestIntent;
929
1437
  const activeRecord = bound ?? previous;
930
- if (!reviewing && hasHumanInput(payload.messages) && !delegation.isChild(payload.agent) && intentTask.trim()) {
1438
+ if (!reviewing &&
1439
+ hasHumanInput(payload.messages) &&
1440
+ !delegation.isChild(payload.agent) &&
1441
+ intentTask.trim()) {
931
1442
  requestIntentContext = await requestIntentContextFor(payload, sessionId, boundSession, activeRecord, intentTask);
932
- const requestId = dshTurnRequestId({ dshSessionId: sessionId, turn: payload.turn });
933
- const establishedConversation = profile?.taskType === 'chat' && !requestIntentContext?.activeGoal
934
- && (!requestIntentContext?.activeTask || requestIntentContext.activeTask.type === 'chat');
1443
+ const requestId = dshTurnRequestId({
1444
+ dshSessionId: sessionId,
1445
+ turn: payload.turn,
1446
+ });
1447
+ const establishedConversation = profile?.taskType === 'chat' &&
1448
+ !requestIntentContext?.activeGoal &&
1449
+ (!requestIntentContext?.activeTask ||
1450
+ requestIntentContext.activeTask.type === 'chat');
935
1451
  if (requestIntentContext && !establishedConversation) {
936
1452
  requestIntent = await classifyRequestIntent(decisions, requestId, requestIntentContext, payload.signal);
937
1453
  }
@@ -939,7 +1455,11 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
939
1455
  // This profile is inherited from a previous turn, not a current user choice.
940
1456
  // Let Laya assess new work, but retain the user's chat choice for an
941
1457
  // ambiguous continuation instead of asking the same classification again.
942
- const explicit = advisoryType ?? (configuration?.provider === 'laya-coreml' && profile?.taskType !== 'chat' ? undefined : profile?.taskType);
1458
+ const explicit = advisoryType ??
1459
+ (configuration?.provider === 'laya-coreml' &&
1460
+ profile?.taskType !== 'chat'
1461
+ ? undefined
1462
+ : profile?.taskType);
943
1463
  const classification = await classifyTaskForIntake(decisions, requestId, intentTask, explicit, payload.signal);
944
1464
  deferTaskTypeInference = classification.deferInference;
945
1465
  if (classification.taskType)
@@ -957,40 +1477,66 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
957
1477
  const rebound = rebindRequestIntentDecision(suppliedIntent, requestIntentContext);
958
1478
  if (!rebound)
959
1479
  throw new KiokukoError('CONFLICT', 'request_intent_evidence_stale');
960
- if (requestIntent?.source === 'decision-provider' && requestIntent.relation !== 'unknown' && requestIntent.action !== 'unknown'
961
- && (requestIntent.relation !== rebound.relation || requestIntent.action !== rebound.action)) {
1480
+ if (requestIntent?.source === 'decision-provider' &&
1481
+ requestIntent.relation !== 'unknown' &&
1482
+ requestIntent.action !== 'unknown' &&
1483
+ (requestIntent.relation !== rebound.relation ||
1484
+ requestIntent.action !== rebound.action)) {
962
1485
  throw new KiokukoError('CONFLICT', 'request_intent_provider_conflict');
963
1486
  }
964
- requestIntent = requestIntent?.source === 'decision-provider' && requestIntent.relation !== 'unknown' && requestIntent.action !== 'unknown'
965
- ? { ...requestIntent, evidence: rebound.evidence } : rebound;
1487
+ requestIntent =
1488
+ requestIntent?.source === 'decision-provider' &&
1489
+ requestIntent.relation !== 'unknown' &&
1490
+ requestIntent.action !== 'unknown'
1491
+ ? { ...requestIntent, evidence: rebound.evidence }
1492
+ : rebound;
966
1493
  }
967
1494
  if (requestIntentContext?.activeTask && hasHumanInput(payload.messages)) {
968
1495
  const identity = requestIntentContext.activeTask;
969
1496
  const contextDigest = canonicalContentHash(requestIntentContext);
970
- const requestId = dshTurnRequestId({ dshSessionId: sessionId, turn: payload.turn });
971
- await runtime.withDatabase(db => {
1497
+ const requestId = dshTurnRequestId({
1498
+ dshSessionId: sessionId,
1499
+ turn: payload.turn,
1500
+ });
1501
+ await runtime.withDatabase((db) => {
972
1502
  if (requestIntentIsUnresolved(requestIntent))
973
1503
  return recordTaskUnresolved(db, identity, {
974
1504
  operationId: `intent:${canonicalContentHash([requestId, contextDigest, requestIntent?.unresolvedReason ?? 'request_intent_unresolved'])}`,
975
- expectedRevision: identity.revision, requestId, contextDigest,
1505
+ expectedRevision: identity.revision,
1506
+ requestId,
1507
+ contextDigest,
976
1508
  reason: requestIntent?.unresolvedReason ?? 'request_intent_unresolved',
977
1509
  evidence: requestIntentReferenceIds(requestIntentContext),
978
1510
  });
979
1511
  const current = readTaskSnapshot(db, identity);
980
- return current.unresolved?.requestId === requestId && current.unresolved.contextDigest === contextDigest
981
- ? clearTaskUnresolved(db, identity, { requestId, contextDigest, expectedRevision: identity.revision }) : undefined;
1512
+ return current.unresolved?.requestId === requestId &&
1513
+ current.unresolved.contextDigest === contextDigest
1514
+ ? clearTaskUnresolved(db, identity, {
1515
+ requestId,
1516
+ contextDigest,
1517
+ expectedRevision: identity.revision,
1518
+ })
1519
+ : undefined;
982
1520
  });
983
1521
  }
984
- const pendingDiscussion = isContinuationReference(intentTask)
985
- && await hasPendingExecutionDiscussion(sessionId, cwd);
986
- const establishedConversation = profile?.taskType === 'chat' && !requestIntentContext?.activeGoal && !requestIntentContext?.activeTask;
987
- if (!interpretationOnly && intentTask.trim() && !pendingDiscussion && !establishedConversation) {
1522
+ const pendingDiscussion = isContinuationReference(intentTask) &&
1523
+ (await hasPendingExecutionDiscussion(sessionId, cwd));
1524
+ const establishedConversation = profile?.taskType === 'chat' &&
1525
+ !requestIntentContext?.activeGoal &&
1526
+ !requestIntentContext?.activeTask;
1527
+ if (!interpretationOnly &&
1528
+ intentTask.trim() &&
1529
+ !pendingDiscussion &&
1530
+ !establishedConversation) {
988
1531
  const clarification = executionClarification(intentTask, requestIntent, requestIntentContext);
989
1532
  if (clarification)
990
1533
  throw new KiokukoError('VALIDATION_ERROR', clarification);
991
1534
  }
992
- const legacyTaskType = advisoryType !== undefined && advisoryType !== 'chat'
993
- && !requestIntentContext?.activeGoal && !requestIntentContext?.activeTask && (requestIntentContext?.proposals.length ?? 0) === 0;
1535
+ const legacyTaskType = advisoryType !== undefined &&
1536
+ advisoryType !== 'chat' &&
1537
+ !requestIntentContext?.activeGoal &&
1538
+ !requestIntentContext?.activeTask &&
1539
+ (requestIntentContext?.proposals.length ?? 0) === 0;
994
1540
  if (requestIntent?.source === 'unresolved' && legacyTaskType)
995
1541
  requestIntent = undefined;
996
1542
  if (requestIntent?.source !== 'unresolved') {
@@ -1001,13 +1547,18 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
1001
1547
  // Coding Lisp choice belongs to the admitted Goal, after all criteria exist.
1002
1548
  // Task intake never starts Lisp or interrupts an explanation with a mode question.
1003
1549
  const catalog = await capabilityCatalog(skills, tools, {
1004
- agent: { id: payload.agent.id }, nativeAgent: payload.agent, cwd, signal: payload.signal,
1550
+ agent: { id: payload.agent.id },
1551
+ nativeAgent: payload.agent,
1552
+ cwd,
1553
+ signal: payload.signal,
1005
1554
  });
1006
1555
  return {
1007
1556
  agent: { id: payload.agent.id },
1008
1557
  nativeAgent: payload.agent,
1009
1558
  sessionId,
1010
- ...(boundSession === undefined ? {} : { nativeSession: boundSession }),
1559
+ ...(boundSession === undefined
1560
+ ? {}
1561
+ : { nativeSession: boundSession }),
1011
1562
  turn: payload.turn,
1012
1563
  sourceStartSeq,
1013
1564
  step: payload.step,
@@ -1026,14 +1577,28 @@ export function createAdmission({ native, skills, tools, userQuestions, sessions
1026
1577
  return {
1027
1578
  gate,
1028
1579
  mapPreStep,
1580
+ requestContext: async (payload, task) => {
1581
+ const session = payload.agent.session;
1582
+ if (!session)
1583
+ return undefined;
1584
+ const previous = currentForAgentEvent(payload.agent.id, session.id, payload.turn, session, payload.agent) ??
1585
+ currentForAgentEvent(payload.agent.id, session.id, undefined, session, payload.agent);
1586
+ return requestIntentContextFor(payload, session.id, session, previous, task);
1587
+ },
1029
1588
  interpret: async (payload) => {
1030
1589
  const event = await mapPreStep(payload, undefined, undefined, true);
1031
1590
  if (!event.requestIntentContext || !event.requestIntent)
1032
1591
  return undefined;
1033
- return { context: event.requestIntentContext, decision: event.requestIntent };
1592
+ return {
1593
+ context: event.requestIntentContext,
1594
+ decision: event.requestIntent,
1595
+ };
1034
1596
  },
1035
1597
  refreshMemoryOwner,
1036
- clear: () => { continuedTurns.clear(); resumedTurns.clear(); },
1598
+ clear: () => {
1599
+ continuedTurns.clear();
1600
+ resumedTurns.clear();
1601
+ },
1037
1602
  };
1038
1603
  }
1039
1604
  //# sourceMappingURL=admission.js.map