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.
- package/dist/dsh/clarification-context.d.ts.map +1 -1
- package/dist/dsh/clarification-context.js +24 -7
- package/dist/dsh/clarification-context.js.map +1 -1
- package/dist/dsh/coding-workflow/host.d.ts +1 -1
- package/dist/dsh/coding-workflow/host.d.ts.map +1 -1
- package/dist/dsh/coding-workflow/host.js +226 -83
- package/dist/dsh/coding-workflow/host.js.map +1 -1
- package/dist/dsh/core/host.d.ts.map +1 -1
- package/dist/dsh/core/host.js +1026 -315
- package/dist/dsh/core/host.js.map +1 -1
- package/dist/dsh/core/tasks.d.ts.map +1 -1
- package/dist/dsh/core/tasks.js +9 -2
- package/dist/dsh/core/tasks.js.map +1 -1
- package/dist/dsh/decisions/intent-assessment.d.ts +1 -0
- package/dist/dsh/decisions/intent-assessment.d.ts.map +1 -1
- package/dist/dsh/decisions/intent-assessment.js +100 -18
- package/dist/dsh/decisions/intent-assessment.js.map +1 -1
- package/dist/dsh/decisions/request-intent.d.ts.map +1 -1
- package/dist/dsh/decisions/request-intent.js +405 -138
- package/dist/dsh/decisions/request-intent.js.map +1 -1
- package/dist/dsh/host-adapter/admission.d.ts +2 -1
- package/dist/dsh/host-adapter/admission.d.ts.map +1 -1
- package/dist/dsh/host-adapter/admission.js +877 -312
- package/dist/dsh/host-adapter/admission.js.map +1 -1
- package/dist/dsh/host-adapter.d.ts.map +1 -1
- package/dist/dsh/host-adapter.js +1 -0
- package/dist/dsh/host-adapter.js.map +1 -1
- package/dist/dsh/on-demand-intake.d.ts +1 -0
- package/dist/dsh/on-demand-intake.d.ts.map +1 -1
- package/dist/dsh/on-demand-intake.js +1120 -316
- package/dist/dsh/on-demand-intake.js.map +1 -1
- package/dist/dsh/request-proposals.d.ts +8 -0
- package/dist/dsh/request-proposals.d.ts.map +1 -0
- package/dist/dsh/request-proposals.js +67 -0
- package/dist/dsh/request-proposals.js.map +1 -0
- package/dist/dsh/skill-prompts.json +1 -1
- package/dist/locale/en.json +1 -1
- package/dist/locale/ja.json +1 -1
- package/dist/locale/ko.json +1 -1
- package/dist/locale/zh-CN.json +1 -1
- package/package.json +2 -2
- 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 {
|
|
37
|
-
import {
|
|
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
|
|
56
|
+
...(event.profileHints === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: { profileHints: event.profileHints }),
|
|
57
59
|
}).profileHints.taskType;
|
|
58
|
-
return incomingType !== null &&
|
|
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' &&
|
|
62
|
-
|
|
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 &&
|
|
68
|
-
|
|
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 = {
|
|
76
|
-
|
|
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, {
|
|
96
|
-
|
|
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 &&
|
|
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 &&
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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({
|
|
108
|
-
|
|
109
|
-
|
|
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 ||
|
|
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, {
|
|
171
|
+
const project = await resolveProjectWorkspaceReadOnly(db, cwd, {
|
|
172
|
+
allowDirectory: true,
|
|
173
|
+
});
|
|
125
174
|
if (!project)
|
|
126
175
|
return false;
|
|
127
|
-
const activeRuns = db
|
|
128
|
-
|
|
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 !==
|
|
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 ||
|
|
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' &&
|
|
172
|
-
|
|
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, {
|
|
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'
|
|
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({
|
|
195
|
-
|
|
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 ||
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
227
|
-
|
|
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' &&
|
|
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 &&
|
|
247
|
-
|
|
248
|
-
|
|
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 ||
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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 &&
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
|
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 &&
|
|
280
|
-
|
|
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, {
|
|
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
|
|
410
|
+
...(event.deferTaskTypeInference
|
|
411
|
+
? { deferTaskTypeInference: true }
|
|
412
|
+
: {}),
|
|
302
413
|
evidence: event.evidence ?? null,
|
|
303
414
|
requestIntent: event.requestIntent ?? null,
|
|
304
|
-
requestIntentContextDigest: event.requestIntentContext
|
|
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({
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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 &&
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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 = {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
332
|
-
|
|
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 ?? []) &&
|
|
337
|
-
|
|
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
|
|
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) &&
|
|
347
|
-
|
|
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
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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 &&
|
|
360
|
-
|
|
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 {
|
|
363
|
-
|
|
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
|
|
367
|
-
|
|
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 ??
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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 {
|
|
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' &&
|
|
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 = {
|
|
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 &&
|
|
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 &&
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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 &&
|
|
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
|
|
445
|
-
|
|
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 = {
|
|
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 = {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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' ||
|
|
718
|
+
...(event.requestIntent?.relation === 'unknown' ||
|
|
719
|
+
event.requestIntent?.action === 'unknown'
|
|
720
|
+
? { unresolvedBinding }
|
|
721
|
+
: {}),
|
|
464
722
|
result: continued,
|
|
465
|
-
...(event.profileHints === undefined
|
|
466
|
-
|
|
467
|
-
|
|
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 {
|
|
475
|
-
|
|
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
|
|
483
|
-
|
|
484
|
-
|
|
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
|
|
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
|
|
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({
|
|
536
|
-
|
|
537
|
-
|
|
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) ||
|
|
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 = {
|
|
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' ||
|
|
588
|
-
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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' &&
|
|
893
|
+
return (typeof id === 'string' &&
|
|
894
|
+
!deliveredIds.has(id) &&
|
|
895
|
+
!currentIds.has(id));
|
|
595
896
|
});
|
|
596
897
|
if (missing.length)
|
|
597
|
-
nativeDecision = {
|
|
898
|
+
nativeDecision = {
|
|
899
|
+
...nativeDecision,
|
|
900
|
+
messages: [...missing, ...nativeDecision.messages],
|
|
901
|
+
};
|
|
598
902
|
}
|
|
599
|
-
const humanMessages = [
|
|
600
|
-
|
|
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 &&
|
|
605
|
-
|
|
606
|
-
|
|
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, {
|
|
613
|
-
|
|
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 {
|
|
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 &&
|
|
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
|
|
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 = {
|
|
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, {
|
|
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 ||
|
|
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) ||
|
|
667
|
-
|
|
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 &&
|
|
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
|
|
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
|
|
684
|
-
|
|
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 &&
|
|
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
|
|
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
|
-
`)
|
|
701
|
-
|
|
1059
|
+
`)
|
|
1060
|
+
.get(continuationId);
|
|
1061
|
+
return outbox === undefined
|
|
1062
|
+
? !isLoopRecoveryMessage(message)
|
|
1063
|
+
: outbox.messageForm !== 'loop-recovery';
|
|
702
1064
|
});
|
|
703
|
-
const automaticClaimId = automaticMessage === undefined
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
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 ??
|
|
1078
|
+
throw new KiokukoError('CONFLICT', decision.warning ??
|
|
1079
|
+
'The active run cannot be resumed by this DSH session');
|
|
710
1080
|
}
|
|
711
|
-
const capabilityEntries = [
|
|
1081
|
+
const capabilityEntries = [
|
|
1082
|
+
...event.capabilities.skills,
|
|
1083
|
+
...event.capabilities.tools,
|
|
1084
|
+
];
|
|
712
1085
|
const { state: intakeState, link: intakeLink } = await continueTaskIntake(database, {
|
|
713
|
-
...task,
|
|
714
|
-
|
|
715
|
-
|
|
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, {
|
|
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 &&
|
|
729
|
-
|
|
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, {
|
|
736
|
-
|
|
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' &&
|
|
739
|
-
|
|
740
|
-
|
|
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 ??
|
|
744
|
-
|
|
745
|
-
|
|
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: {
|
|
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: {
|
|
1184
|
+
memoryPolicy: {
|
|
1185
|
+
memoryReasoningRequired: false,
|
|
1186
|
+
contextWithheld: false,
|
|
1187
|
+
withheldReason: null,
|
|
1188
|
+
deliveryEmpty: true,
|
|
1189
|
+
},
|
|
768
1190
|
warnings: capabilityResolution.warnings,
|
|
769
|
-
nextAction: admitted
|
|
770
|
-
? '
|
|
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
|
|
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, {
|
|
789
|
-
|
|
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 &&
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
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
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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 &&
|
|
822
|
-
|
|
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
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
ideal:
|
|
832
|
-
|
|
833
|
-
outOfScope: [...
|
|
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
|
-
|
|
1286
|
+
: workflow
|
|
1287
|
+
? {
|
|
836
1288
|
runId,
|
|
837
|
-
|
|
838
|
-
|
|
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:
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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) &&
|
|
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({
|
|
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) &&
|
|
911
|
-
|
|
912
|
-
|
|
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
|
|
923
|
-
|
|
924
|
-
|
|
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 ??
|
|
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 &&
|
|
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({
|
|
933
|
-
|
|
934
|
-
|
|
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 ??
|
|
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' &&
|
|
961
|
-
|
|
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 =
|
|
965
|
-
|
|
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({
|
|
971
|
-
|
|
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,
|
|
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 &&
|
|
981
|
-
|
|
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
|
-
|
|
986
|
-
const establishedConversation = profile?.taskType === 'chat' &&
|
|
987
|
-
|
|
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 &&
|
|
993
|
-
|
|
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 },
|
|
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
|
|
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 {
|
|
1592
|
+
return {
|
|
1593
|
+
context: event.requestIntentContext,
|
|
1594
|
+
decision: event.requestIntent,
|
|
1595
|
+
};
|
|
1034
1596
|
},
|
|
1035
1597
|
refreshMemoryOwner,
|
|
1036
|
-
clear: () => {
|
|
1598
|
+
clear: () => {
|
|
1599
|
+
continuedTurns.clear();
|
|
1600
|
+
resumedTurns.clear();
|
|
1601
|
+
},
|
|
1037
1602
|
};
|
|
1038
1603
|
}
|
|
1039
1604
|
//# sourceMappingURL=admission.js.map
|