engineering-memory 1.11.17 → 1.11.19
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/dispatcher/sections.mjs +7 -3
- package/package.json +1 -1
- package/runtime/build.json +1 -1
- package/runtime/dist/src/config.js +1 -0
- package/runtime/dist/src/mcp/decision-tools.js +119 -0
- package/runtime/dist/src/mcp/delivery-tools.js +343 -0
- package/runtime/dist/src/mcp/questionnaire-tools.js +14 -4
- package/runtime/dist/src/mcp/release-tools.js +27 -0
- package/runtime/dist/src/mcp/status-meaning-tools.js +374 -0
- package/runtime/dist/src/mcp/tool-annotations.js +15 -0
- package/runtime/dist/src/mcp/tool-definitions.js +164 -10
- package/runtime/dist/src/mcp/workflow-tools.js +401 -0
- package/runtime/dist/src/mcp/worktree-tools.js +22 -1
- package/runtime/dist/src/runtime/api-client.js +35 -4
- package/runtime/dist/src/runtime/bridge-service.js +541 -7
- package/runtime/dist/src/runtime/create-bridge-service.js +2 -0
- package/runtime/dist/src/runtime/decision-mode-store.js +88 -0
- package/runtime/dist/src/runtime/questionnaire-store.js +27 -3
- package/runtime/dist/src/runtime/release-notes.js +284 -0
- package/runtime/dist/src/runtime/release-report.js +59 -0
- package/runtime/dist/src/runtime/worktree-editor.js +6 -3
- package/runtime/dist/src/runtime/worktree-pool.js +42 -9
- package/runtime/dist/src/runtime/worktree-preparation.js +73 -3
- package/skill/SKILL.md +7 -3
- package/skill/references/lifecycle.md +48 -5
- package/skill/references/memory-updates.md +6 -2
- package/skill/references/questionnaires.md +24 -3
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ignoredRuntimeFiles, localRelativePath } from './worktree-preparation.js';
|
|
2
|
+
import { decisionModeSchema } from './decision-mode-store.js';
|
|
3
|
+
import { delegatedReasonSchema } from './questionnaire-store.js';
|
|
1
4
|
import { unreadableWorktree, } from './worktree-pool.js';
|
|
2
5
|
import { validWorktreePolicy } from './worktree-policy.js';
|
|
3
6
|
import { resolveTaskBase } from './branch-preferences.js';
|
|
@@ -57,6 +60,202 @@ export class BridgeService {
|
|
|
57
60
|
await this.dependencies.languages.remember(principalHash, told);
|
|
58
61
|
return told ?? (await this.dependencies.languages.read(principalHash));
|
|
59
62
|
}
|
|
63
|
+
async decisionModeStatus(input) {
|
|
64
|
+
const scope = await this.questionnaireScope(input.repoRoot);
|
|
65
|
+
return this.dependencies.questionnaires.decisions.read(scope, this.dependencies.client.namespace, input.externalTaskId);
|
|
66
|
+
}
|
|
67
|
+
async decisionModeQuestion(input) {
|
|
68
|
+
const scope = await this.questionnaireScope(input.repoRoot);
|
|
69
|
+
if (!scope.projectId || !(await this.dependencies.credentials.get('access-token')))
|
|
70
|
+
throw refuse('Select a signed-in project before choosing a task decision mode.', 'session.entry');
|
|
71
|
+
const namespace = this.dependencies.client.namespace;
|
|
72
|
+
const id = 'decision-mode-' +
|
|
73
|
+
sha256(stableStringify({
|
|
74
|
+
scope,
|
|
75
|
+
namespace,
|
|
76
|
+
task: input.externalTaskId,
|
|
77
|
+
version: input.expectedVersion,
|
|
78
|
+
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
79
|
+
}));
|
|
80
|
+
const current = await this.decisionModeStatus(input);
|
|
81
|
+
if (current.version !== input.expectedVersion &&
|
|
82
|
+
!(current.version === input.expectedVersion + 1 && current.questionnaireId === id))
|
|
83
|
+
throw refuse('The mode changed. Read decision.mode_status before opening another mode question.', 'decision.mode_status');
|
|
84
|
+
const language = await this.language(input.language);
|
|
85
|
+
const existing = await this.dependencies.questionnaires.get(scope, id);
|
|
86
|
+
if (existing) {
|
|
87
|
+
if (existing.owner?.tool !== 'decision.mode' ||
|
|
88
|
+
existing.owner.externalTaskId !== input.externalTaskId)
|
|
89
|
+
throw refuse('This mode question has a different owner. Start a new decisionAttempt.', 'decision.mode');
|
|
90
|
+
if (existing.status === 'withdrawn')
|
|
91
|
+
throw refuse('The mode question was withdrawn. When the user resumes this choice, increment decisionAttempt without changing expectedVersion.', 'decision.mode');
|
|
92
|
+
return {
|
|
93
|
+
questionnaireId: id,
|
|
94
|
+
message: existing.message,
|
|
95
|
+
context: existing.context,
|
|
96
|
+
example: existing.example,
|
|
97
|
+
language: existing.language,
|
|
98
|
+
options: existing.options,
|
|
99
|
+
binding: existing.binding,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const tr = language?.startsWith('tr');
|
|
103
|
+
const definition = {
|
|
104
|
+
questionnaireId: id,
|
|
105
|
+
message: tr
|
|
106
|
+
? input.expectedVersion
|
|
107
|
+
? 'Hangi moda geçelim?'
|
|
108
|
+
: 'Bu taskta nasıl ilerleyelim?'
|
|
109
|
+
: input.expectedVersion
|
|
110
|
+
? 'Which mode should we switch to?'
|
|
111
|
+
: 'How should we work on this task?',
|
|
112
|
+
context: tr
|
|
113
|
+
? 'Seçim bu task için geçerli; istediğinde değiştirebilirsin.'
|
|
114
|
+
: 'This choice applies to this task; you can change it anytime.',
|
|
115
|
+
example: tr
|
|
116
|
+
? 'Örneğin canlıya alma kararını ajana bırakabilir veya kendin onaylayabilirsin.'
|
|
117
|
+
: 'For example, you can delegate a release decision or approve it yourself.',
|
|
118
|
+
language: tr ? 'tr' : 'en',
|
|
119
|
+
options: tr
|
|
120
|
+
? [
|
|
121
|
+
{
|
|
122
|
+
id: 'approve_for_me',
|
|
123
|
+
label: 'Benim için onayla',
|
|
124
|
+
description: 'Sıradan kararları ben veririm; çok kritik olanları sana sorarım.',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'autonomous',
|
|
128
|
+
label: 'Otonom',
|
|
129
|
+
description: 'Seçenekleri senin hedeflerine göre değerlendirip kararları gerekçesiyle veririm.',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'ask',
|
|
133
|
+
label: 'Onay iste',
|
|
134
|
+
description: 'Karar gereken her durumda sana sorarım.',
|
|
135
|
+
},
|
|
136
|
+
]
|
|
137
|
+
: [
|
|
138
|
+
{
|
|
139
|
+
id: 'approve_for_me',
|
|
140
|
+
label: 'Approve for me',
|
|
141
|
+
description: 'I decide routine matters and ask you about critical ones.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: 'autonomous',
|
|
145
|
+
label: 'Autonomous',
|
|
146
|
+
description: 'I weigh the choices against your goals and record my reasoning.',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: 'ask',
|
|
150
|
+
label: 'Ask for approval',
|
|
151
|
+
description: 'I ask you whenever a decision is needed.',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
binding: {
|
|
155
|
+
namespace,
|
|
156
|
+
externalTaskId: input.externalTaskId,
|
|
157
|
+
expectedVersion: input.expectedVersion,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
return definition;
|
|
161
|
+
}
|
|
162
|
+
async decisionModeSelect(input) {
|
|
163
|
+
const record = await this.questionnaireResume(input);
|
|
164
|
+
const namespace = this.dependencies.client.namespace;
|
|
165
|
+
if (record.owner?.tool !== 'decision.mode' ||
|
|
166
|
+
record.owner.externalTaskId !== input.externalTaskId ||
|
|
167
|
+
record.binding?.namespace !== namespace ||
|
|
168
|
+
record.binding?.externalTaskId !== input.externalTaskId ||
|
|
169
|
+
record.binding?.expectedVersion !== input.expectedVersion ||
|
|
170
|
+
record.status !== 'answered' ||
|
|
171
|
+
!record.answerAvailable ||
|
|
172
|
+
record.answerSource?.kind === 'delegated_agent')
|
|
173
|
+
throw refuse('A mode can only be selected through its native task mode question.', 'decision.mode');
|
|
174
|
+
const mode = decisionModeSchema.parse(record.answer?.choice);
|
|
175
|
+
return this.dependencies.questionnaires.decisions.select(record.scope, namespace, input.externalTaskId, input.expectedVersion, mode, record.questionnaireId);
|
|
176
|
+
}
|
|
177
|
+
async questionnaireDecisionPolicy(record, answer) {
|
|
178
|
+
const externalTaskId = record.owner?.externalTaskId;
|
|
179
|
+
const state = externalTaskId
|
|
180
|
+
? await this.dependencies.questionnaires.decisions.read(record.scope, this.dependencies.client.namespace, externalTaskId)
|
|
181
|
+
: null;
|
|
182
|
+
let impact = record.owner?.tool === 'questionnaire.ask' ? (record.impact ?? 'critical') : 'critical';
|
|
183
|
+
if (record.owner?.tool === 'task.branch' && record.binding) {
|
|
184
|
+
if (!answer)
|
|
185
|
+
impact = 'routine';
|
|
186
|
+
else {
|
|
187
|
+
const selected = taskStartChoice({
|
|
188
|
+
...record,
|
|
189
|
+
status: 'answered',
|
|
190
|
+
answerAvailable: true,
|
|
191
|
+
...(record.questions
|
|
192
|
+
? { answers: answer }
|
|
193
|
+
: { answer: answer }),
|
|
194
|
+
});
|
|
195
|
+
if (selected?.status === 'deferred' ||
|
|
196
|
+
(selected?.status === 'start' && !selected.inPlace && !selected.keepCurrent))
|
|
197
|
+
impact = 'routine';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (record.questionnaireId.startsWith('rule-deviation-') ||
|
|
201
|
+
record.questionnaireId.startsWith('validation-waiver-'))
|
|
202
|
+
impact = 'critical';
|
|
203
|
+
const userOnly = record.owner?.tool === 'decision.mode' || !record.scope.projectId || !externalTaskId;
|
|
204
|
+
const delegated = !userOnly &&
|
|
205
|
+
!!state?.configured &&
|
|
206
|
+
(state.mode === 'autonomous' || (state.mode === 'approve_for_me' && impact === 'routine'));
|
|
207
|
+
return { delegated, impact, state, userOnly };
|
|
208
|
+
}
|
|
209
|
+
async assertDecisionCurrent(record) {
|
|
210
|
+
const source = record.answerSource;
|
|
211
|
+
if (source?.kind !== 'delegated_agent')
|
|
212
|
+
return;
|
|
213
|
+
const policy = await this.questionnaireDecisionPolicy(record, record.questions ? record.answers : record.answer);
|
|
214
|
+
if (!policy.delegated ||
|
|
215
|
+
policy.state?.version !== source.modeVersion ||
|
|
216
|
+
policy.state.mode !== source.mode ||
|
|
217
|
+
record.owner?.externalTaskId !== source.externalTaskId ||
|
|
218
|
+
source.namespace !== this.dependencies.client.namespace)
|
|
219
|
+
throw refuse('The mode changed after this delegated answer. Re-evaluate using a new questionnaireId, or increment the owning operation decisionAttempt. Do not execute this answer.', 'questionnaire.resume');
|
|
220
|
+
}
|
|
221
|
+
async questionnaireDecide(input) {
|
|
222
|
+
const scope = await this.questionnaireScope(input.repoRoot);
|
|
223
|
+
const record = await this.dependencies.questionnaires.get(scope, input.questionnaireId);
|
|
224
|
+
if (!record ||
|
|
225
|
+
record.requestKey !== input.requestKey ||
|
|
226
|
+
record.contentHash !== input.contentHash)
|
|
227
|
+
throw refuse('Read the exact pending questionnaire before deciding.', 'questionnaire.resume');
|
|
228
|
+
const externalTaskId = record.owner?.externalTaskId;
|
|
229
|
+
if (!externalTaskId)
|
|
230
|
+
throw refuse('This question has no task delegation scope.', 'questionnaire.resume');
|
|
231
|
+
return this.dependencies.questionnaires.decisions.locked(scope, this.dependencies.client.namespace, externalTaskId, async () => {
|
|
232
|
+
const answer = input.answer ?? input.answers;
|
|
233
|
+
if (!answer || Boolean(input.answer) === Boolean(input.answers))
|
|
234
|
+
throw refuse('Exactly one of answer or answers is required. Resume the original question.', 'questionnaire.resume');
|
|
235
|
+
const policy = await this.questionnaireDecisionPolicy(record, answer);
|
|
236
|
+
if (!policy.delegated || !policy.state || policy.state.version !== input.modeVersion)
|
|
237
|
+
throw refuse('The current mode requires a native answer for this decision, or its version changed. Resume the question.', 'questionnaire.resume');
|
|
238
|
+
await this.assertDecisionCurrent(record);
|
|
239
|
+
const reasoning = delegatedReasonSchema.parse({
|
|
240
|
+
reason: input.reason,
|
|
241
|
+
alternativesConsidered: input.alternativesConsidered,
|
|
242
|
+
userInterestReview: input.userInterestReview,
|
|
243
|
+
});
|
|
244
|
+
const resolved = await this.dependencies.questionnaires.accept(scope, record.questionnaireId, record.requestKey, answer, {
|
|
245
|
+
kind: 'delegated_agent',
|
|
246
|
+
externalTaskId,
|
|
247
|
+
mode: policy.state.mode,
|
|
248
|
+
modeVersion: policy.state.version,
|
|
249
|
+
namespace: this.dependencies.client.namespace,
|
|
250
|
+
impact: policy.impact,
|
|
251
|
+
...reasoning,
|
|
252
|
+
});
|
|
253
|
+
return {
|
|
254
|
+
...resolved,
|
|
255
|
+
retry: this.questionnaireRetry(resolved.record, input.repoRoot ?? process.cwd()),
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
}
|
|
60
259
|
async questionnaireAsk(input, previousDefinitions = [], owner) {
|
|
61
260
|
const scope = await this.questionnaireScope(input.repoRoot, input.preparation);
|
|
62
261
|
const { repoRoot: _repoRoot, preparation: _preparation, presentation: _presentation, ...definition } = input;
|
|
@@ -64,6 +263,7 @@ export class BridgeService {
|
|
|
64
263
|
? null
|
|
65
264
|
: await this.checkoutTask(scope.repoFingerprint, await this.dependencies.repositories.git.findRoot(input.repoRoot ?? process.cwd()));
|
|
66
265
|
const record = await this.dependencies.questionnaires.ask(scope, definition, previousDefinitions, owner ?? (askingTask ? { tool: 'questionnaire.ask', externalTaskId: askingTask } : undefined));
|
|
266
|
+
await this.assertDecisionCurrent(record);
|
|
67
267
|
return { ...record, language: record.language ?? (await this.language()) };
|
|
68
268
|
}
|
|
69
269
|
async checkoutTask(repoFingerprint, repoRoot) {
|
|
@@ -80,6 +280,7 @@ export class BridgeService {
|
|
|
80
280
|
const record = await this.dependencies.questionnaires.get(scope, input.questionnaireId);
|
|
81
281
|
if (!record)
|
|
82
282
|
throw refuse('No questionnaire exists for this account and repository binding.', 'session.entry');
|
|
283
|
+
await this.assertDecisionCurrent(record);
|
|
83
284
|
return { ...record, language: record.language ?? (await this.language()) };
|
|
84
285
|
}
|
|
85
286
|
async questionnaireWithdraw(input) {
|
|
@@ -96,7 +297,7 @@ export class BridgeService {
|
|
|
96
297
|
async acceptWithScope(scope, questionnaireId, requestKey, answer, answerSource) {
|
|
97
298
|
return this.dependencies.questionnaires.accept(scope, questionnaireId, requestKey, answer, answerSource);
|
|
98
299
|
}
|
|
99
|
-
|
|
300
|
+
questionnaireRetry(record, repoRoot, attemptOffset = 0) {
|
|
100
301
|
if (!record.owner || record.owner.tool === 'questionnaire.ask')
|
|
101
302
|
return undefined;
|
|
102
303
|
const attempt = (record.owner.decisionAttempt ?? 0) + attemptOffset;
|
|
@@ -121,7 +322,7 @@ export class BridgeService {
|
|
|
121
322
|
const resolved = await this.acceptWithScope(scope, input.questionnaireId, input.requestKey, (input.answer ?? input.answers), { kind: 'host_native_relay', hostTool: input.hostTool });
|
|
122
323
|
return {
|
|
123
324
|
...resolved,
|
|
124
|
-
retry: this.
|
|
325
|
+
retry: this.questionnaireRetry(resolved.record, input.repoRoot ?? process.cwd()),
|
|
125
326
|
};
|
|
126
327
|
}
|
|
127
328
|
async questionnaireScope(repoRoot, preparation = false) {
|
|
@@ -227,7 +428,7 @@ export class BridgeService {
|
|
|
227
428
|
.map(([externalTaskId, record]) => asJsonValue({
|
|
228
429
|
externalTaskId,
|
|
229
430
|
deferredAt: record.answeredAt ?? record.createdAt,
|
|
230
|
-
reconsider: this.
|
|
431
|
+
reconsider: this.questionnaireRetry(record, repoRoot, 1),
|
|
231
432
|
nextAction: `Starting ${externalTaskId} is on hold. Mention it only if the user brings that task up, and call reconsider only when they ask to start it.`,
|
|
232
433
|
}));
|
|
233
434
|
return { mine, other, deferred };
|
|
@@ -415,6 +616,10 @@ export class BridgeService {
|
|
|
415
616
|
await this.seedResumeSnapshot(pointer, data);
|
|
416
617
|
return asJsonValue({
|
|
417
618
|
...data,
|
|
619
|
+
decisionMode: await this.decisionModeStatus({
|
|
620
|
+
repoRoot: repository.repoRoot,
|
|
621
|
+
externalTaskId: input.externalTaskId,
|
|
622
|
+
}),
|
|
418
623
|
checkpoint: checkpointView(checkpoint),
|
|
419
624
|
repository: publicRepository(repository),
|
|
420
625
|
localJournal: journalSummary(await this.dependencies.journal.load(projectId, input.externalTaskId)),
|
|
@@ -774,6 +979,10 @@ export class BridgeService {
|
|
|
774
979
|
return asJsonValue({
|
|
775
980
|
backend: resumeBackendView(backend, backendFresh || !localJournal.projection),
|
|
776
981
|
requirements: backend.requirements ?? null,
|
|
982
|
+
decisionMode: await this.decisionModeStatus({
|
|
983
|
+
repoRoot: repository.repoRoot,
|
|
984
|
+
externalTaskId: taskSlug,
|
|
985
|
+
}),
|
|
777
986
|
...(contextRefreshRequired
|
|
778
987
|
? {
|
|
779
988
|
staleReason: backend.staleReason ?? null,
|
|
@@ -1250,6 +1459,8 @@ export class BridgeService {
|
|
|
1250
1459
|
const question = input.questionnaireId
|
|
1251
1460
|
? await this.dependencies.questionnaires.get(scope, input.questionnaireId)
|
|
1252
1461
|
: null;
|
|
1462
|
+
if (question)
|
|
1463
|
+
await this.assertDecisionCurrent(question);
|
|
1253
1464
|
const digest = sha256(stableStringify(input.choice));
|
|
1254
1465
|
if (question?.answer?.choice !== 'approve' ||
|
|
1255
1466
|
(question.binding?.choiceDigest !== digest &&
|
|
@@ -1341,6 +1552,8 @@ export class BridgeService {
|
|
|
1341
1552
|
const digest = sha256(stableStringify(confirmedInput));
|
|
1342
1553
|
const scope = await this.questionnaireScope(input.repoRoot);
|
|
1343
1554
|
const question = await this.dependencies.questionnaires.get(scope, questionnaireId);
|
|
1555
|
+
if (question)
|
|
1556
|
+
await this.assertDecisionCurrent(question);
|
|
1344
1557
|
if (questionnaireId !== 'sync-start-' + digest ||
|
|
1345
1558
|
question?.answer?.choice !== 'approve' ||
|
|
1346
1559
|
input.ref !== snapshot.commit)
|
|
@@ -1706,6 +1919,8 @@ export class BridgeService {
|
|
|
1706
1919
|
return rule;
|
|
1707
1920
|
const questionnaireId = ruleDeviationQuestionnaireId(input.taskId, path, rule);
|
|
1708
1921
|
const question = await this.dependencies.questionnaires.get(scope, questionnaireId);
|
|
1922
|
+
if (question)
|
|
1923
|
+
await this.assertDecisionCurrent(question);
|
|
1709
1924
|
const choice = question?.status === 'answered' ? question.answer?.choice : undefined;
|
|
1710
1925
|
if (choice === 'change')
|
|
1711
1926
|
throw refuse(`The user chose to change ${path} so it follows ${rule.resourceKey}. Change the code, then record task.self_review again with that rule as fixed.`, 'task.self_review');
|
|
@@ -1717,7 +1932,12 @@ export class BridgeService {
|
|
|
1717
1932
|
deviationApproval: {
|
|
1718
1933
|
questionnaireId,
|
|
1719
1934
|
answeredAt: question.answeredAt,
|
|
1720
|
-
hostTool: question.answerSource?.
|
|
1935
|
+
hostTool: question.answerSource?.kind === 'host_native_relay'
|
|
1936
|
+
? question.answerSource.hostTool
|
|
1937
|
+
: null,
|
|
1938
|
+
...(delegatedEvidence(question)
|
|
1939
|
+
? { delegatedDecision: delegatedEvidence(question) }
|
|
1940
|
+
: {}),
|
|
1721
1941
|
},
|
|
1722
1942
|
};
|
|
1723
1943
|
})),
|
|
@@ -1934,6 +2154,11 @@ export class BridgeService {
|
|
|
1934
2154
|
if (recoveredVerification) {
|
|
1935
2155
|
return asJsonValue({
|
|
1936
2156
|
...recoveredVerification,
|
|
2157
|
+
...(recoveredVerification.verified === true
|
|
2158
|
+
? {
|
|
2159
|
+
nextAction: 'Call task.close now for this verified task. It opens the mode-governed Git delivery selector. Memory publication approval is separate; do not stop after publishing memory.',
|
|
2160
|
+
}
|
|
2161
|
+
: {}),
|
|
1937
2162
|
repository: publicRepository(repository),
|
|
1938
2163
|
});
|
|
1939
2164
|
}
|
|
@@ -2043,6 +2268,8 @@ export class BridgeService {
|
|
|
2043
2268
|
for (const waiver of waivers) {
|
|
2044
2269
|
const questionnaireId = validationWaiverId(input.taskId, waiver.validationId, paths, waiver.reason);
|
|
2045
2270
|
const question = await this.dependencies.questionnaires.get(scope, questionnaireId);
|
|
2271
|
+
if (question)
|
|
2272
|
+
await this.assertDecisionCurrent(question);
|
|
2046
2273
|
if (question?.answer?.choice !== 'waive') {
|
|
2047
2274
|
throw refuse(`The user has not agreed to skip the ${waiver.validationId} check for ${samplePaths(paths) || 'this change'} with this reason. Call task.verify with the waiver so the tool asks them, or provide the evidence.`, 'task.verify');
|
|
2048
2275
|
}
|
|
@@ -2051,7 +2278,14 @@ export class BridgeService {
|
|
|
2051
2278
|
questionnaireId,
|
|
2052
2279
|
reason: waiver.reason,
|
|
2053
2280
|
paths,
|
|
2054
|
-
answerSource: question.answerSource?.
|
|
2281
|
+
answerSource: question.answerSource?.kind === 'delegated_agent'
|
|
2282
|
+
? 'delegated_agent'
|
|
2283
|
+
: question.answerSource?.kind === 'host_native_relay'
|
|
2284
|
+
? question.answerSource.hostTool
|
|
2285
|
+
: 'mcp_form',
|
|
2286
|
+
...(delegatedEvidence(question)
|
|
2287
|
+
? { delegatedDecision: delegatedEvidence(question) }
|
|
2288
|
+
: {}),
|
|
2055
2289
|
});
|
|
2056
2290
|
}
|
|
2057
2291
|
}
|
|
@@ -2129,6 +2363,11 @@ export class BridgeService {
|
|
|
2129
2363
|
return asJsonValue({
|
|
2130
2364
|
...objectOrEmpty(response.data),
|
|
2131
2365
|
mode: taskMode,
|
|
2366
|
+
...(responseData?.verified === true
|
|
2367
|
+
? {
|
|
2368
|
+
nextAction: 'Call task.close now for this verified task. It opens the mode-governed Git delivery selector. Memory publication approval is separate; do not stop after publishing memory.',
|
|
2369
|
+
}
|
|
2370
|
+
: {}),
|
|
2132
2371
|
...(foreignChangedPaths.length > 0
|
|
2133
2372
|
? { changedPathsOutsideThisTask: foreignChangedPaths }
|
|
2134
2373
|
: {}),
|
|
@@ -2170,6 +2409,13 @@ export class BridgeService {
|
|
|
2170
2409
|
});
|
|
2171
2410
|
});
|
|
2172
2411
|
}
|
|
2412
|
+
async taskDeliveryBase(input) {
|
|
2413
|
+
return this.execute(async () => {
|
|
2414
|
+
const repository = await this.dependencies.repositories.resolve(input.repoRoot ?? process.cwd());
|
|
2415
|
+
await this.dependencies.repositories.git.validateBranch(repository.repoRoot, input.branch);
|
|
2416
|
+
return { branch: input.branch };
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2173
2419
|
async taskClose(input) {
|
|
2174
2420
|
return await this.execute(async () => {
|
|
2175
2421
|
return await this.taskExclusive(input.taskId, async () => {
|
|
@@ -2226,6 +2472,47 @@ export class BridgeService {
|
|
|
2226
2472
|
return asJsonValue({ allocation });
|
|
2227
2473
|
});
|
|
2228
2474
|
}
|
|
2475
|
+
async worktreePrepareFiles(input) {
|
|
2476
|
+
return this.execute(async () => {
|
|
2477
|
+
const repository = await this.dependencies.repositories.resolve(input.repoRoot);
|
|
2478
|
+
const pool = this.dependencies.worktreePool;
|
|
2479
|
+
if (!repository.projectId || !pool)
|
|
2480
|
+
throw refuse('Resume a bound managed worktree before preparing files.', 'session.resume');
|
|
2481
|
+
const allocation = await pool.assertOwnership(repository.projectId, repository.repoRoot, input.generation);
|
|
2482
|
+
if (!allocation.managed ||
|
|
2483
|
+
allocation.phase !== 'working' ||
|
|
2484
|
+
allocation.externalTaskId !== input.externalTaskId)
|
|
2485
|
+
throw refuse('Resume this task with its current worktree generation.', 'session.resume');
|
|
2486
|
+
const source = allocation.sourceRepoRoot;
|
|
2487
|
+
if (!source)
|
|
2488
|
+
throw refuse('Resume worktree preparation before inspecting runtime files.', 'session.resume');
|
|
2489
|
+
const inventory = await ignoredRuntimeFiles(source, allocation.sourceMainRoot ?? source, allocation.repoRoot);
|
|
2490
|
+
let prepared = allocation;
|
|
2491
|
+
if (input.paths !== undefined) {
|
|
2492
|
+
if (input.expectedInventory !== inventory.inventory || input.paths.length > 100)
|
|
2493
|
+
throw refuse('Inspect the current ignored-file inventory before selecting required files.', 'worktree.prepare_files');
|
|
2494
|
+
const paths = input.paths.map(localRelativePath);
|
|
2495
|
+
if (new Set(paths).size !== paths.length)
|
|
2496
|
+
throw refuse('Choose each required runtime path once.', 'worktree.prepare_files');
|
|
2497
|
+
prepared = await pool.prepare(allocation, { inventory: inventory.inventory, paths });
|
|
2498
|
+
this.poolAllocations.set(repository.repoRoot, prepared);
|
|
2499
|
+
}
|
|
2500
|
+
const offset = input.offset ?? 0, limit = input.limit ?? 100;
|
|
2501
|
+
return asJsonValue({
|
|
2502
|
+
repoRoot: prepared.repoRoot,
|
|
2503
|
+
generation: prepared.generation,
|
|
2504
|
+
inventory: inventory.inventory,
|
|
2505
|
+
candidates: inventory.paths.slice(offset, offset + limit),
|
|
2506
|
+
total: inventory.paths.length,
|
|
2507
|
+
offset,
|
|
2508
|
+
limit,
|
|
2509
|
+
nextOffset: offset + limit < inventory.paths.length ? offset + limit : null,
|
|
2510
|
+
selectedPaths: prepared.runtimeFiles?.paths ?? [],
|
|
2511
|
+
readiness: prepared.readiness,
|
|
2512
|
+
nextAction: 'Inspect every candidate page and tracked imports, build/configuration references and explicit user requirements. Select only necessary local runtime files with paths and expectedInventory; use an empty paths array when none are needed. A selection supplements recognized automatic files and acknowledges the reviewed inventory. Keep generated caches/artifacts excluded; paths inside them may be selected only on explicit runtime evidence. Contents remain local and must never be logged, uploaded or committed. Resolve missing/conflicting/unsafe files without overwriting them; a runtimeReview.required result is not a ready worktree.',
|
|
2513
|
+
});
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2229
2516
|
async projectGitPreferences(input) {
|
|
2230
2517
|
return this.execute(async () => {
|
|
2231
2518
|
const repository = await this.dependencies.repositories.resolveIdentity(input.repoRoot ?? process.cwd(), input.projectId);
|
|
@@ -2648,7 +2935,7 @@ export class BridgeService {
|
|
|
2648
2935
|
return asJsonValue({
|
|
2649
2936
|
...allocation,
|
|
2650
2937
|
kept: !allocation.managed,
|
|
2651
|
-
nextAction: 'Run every file, terminal, lifecycle and delivery operation from this repoRoot. Inspect readiness separately: a file problem does not cancel the task, and an editor request does not prove its window is visible. Resolve reported local file issues without overwriting existing files, then retry task.branch or session.resume. Use task.heartbeat during long work and pause before handoff.',
|
|
2938
|
+
nextAction: 'Run every file, terminal, lifecycle and delivery operation from this repoRoot. Inspect readiness separately: a file problem does not cancel the task, and an editor request does not prove its window is visible. If files.runtimeReview.required, call worktree.prepare_files in this repoRoot with externalTaskId and generation, inspect all candidate pages against tracked imports/build configuration and user requirements, and select the necessary relative paths before claiming runtime readiness. Resolve reported local file issues without overwriting existing files, then retry task.branch or session.resume. Use task.heartbeat during long work and pause before handoff.',
|
|
2652
2939
|
});
|
|
2653
2940
|
}
|
|
2654
2941
|
adoptOwned(repoRoot, entry) {
|
|
@@ -3090,6 +3377,218 @@ export class BridgeService {
|
|
|
3090
3377
|
return asJsonValue(response.data);
|
|
3091
3378
|
});
|
|
3092
3379
|
}
|
|
3380
|
+
async workItemSetupScope(input) {
|
|
3381
|
+
const scope = await this.questionnaireScope(input.repoRoot);
|
|
3382
|
+
const root = await this.dependencies.repositories.git.findRoot(input.repoRoot);
|
|
3383
|
+
const task = await this.checkoutTask(scope.repoFingerprint, root);
|
|
3384
|
+
const pointer = await this.dependencies.activeContexts.loadForSlug(scope.repoFingerprint, input.externalTaskId);
|
|
3385
|
+
if (scope.projectId !== input.projectId ||
|
|
3386
|
+
task !== input.externalTaskId ||
|
|
3387
|
+
!pointer ||
|
|
3388
|
+
pointer.closedAt ||
|
|
3389
|
+
pointer.mode === 'read_only')
|
|
3390
|
+
throw refuse('Resume the active write task in its bound project before configuring its workflow.', 'session.resume');
|
|
3391
|
+
return scope;
|
|
3392
|
+
}
|
|
3393
|
+
async workItemSetupContext(input) {
|
|
3394
|
+
return this.execute(async () => {
|
|
3395
|
+
await this.workItemSetupScope(input);
|
|
3396
|
+
return {
|
|
3397
|
+
projectId: input.projectId,
|
|
3398
|
+
externalTaskId: input.externalTaskId,
|
|
3399
|
+
namespace: this.dependencies.client.namespace,
|
|
3400
|
+
};
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
async workItemApplyWorkflowSetup(input) {
|
|
3404
|
+
return this.execute(async () => {
|
|
3405
|
+
const scope = await this.workItemSetupScope(input);
|
|
3406
|
+
return this.dependencies.questionnaires.decisions.locked(scope, this.dependencies.client.namespace, input.externalTaskId, async () => {
|
|
3407
|
+
const final = await this.questionnaireResume({
|
|
3408
|
+
repoRoot: input.repoRoot,
|
|
3409
|
+
questionnaireId: input.questionnaireId,
|
|
3410
|
+
});
|
|
3411
|
+
const proposal = {
|
|
3412
|
+
projectId: input.projectId,
|
|
3413
|
+
expectedSnapshot: input.expectedSnapshot,
|
|
3414
|
+
rules: input.rules,
|
|
3415
|
+
};
|
|
3416
|
+
if (final.owner?.tool !== 'work_item.setup_workflow' ||
|
|
3417
|
+
final.owner.externalTaskId !== input.externalTaskId ||
|
|
3418
|
+
final.status !== 'answered' ||
|
|
3419
|
+
!final.answerAvailable ||
|
|
3420
|
+
final.answer?.choice !== 'approve' ||
|
|
3421
|
+
final.binding?.namespace !== this.dependencies.client.namespace ||
|
|
3422
|
+
final.binding?.proposalDigest !== sha256(stableStringify(proposal)) ||
|
|
3423
|
+
stableStringify(final.binding?.selectionQuestionnaireIds) !==
|
|
3424
|
+
stableStringify(input.selectionQuestionnaireIds) ||
|
|
3425
|
+
input.selectionQuestionnaireIds.length !== 5 ||
|
|
3426
|
+
input.rules.length !== 5 ||
|
|
3427
|
+
new Set(input.rules.map((rule) => rule.event)).size !== 5 ||
|
|
3428
|
+
input.rules.some((rule) => rule.event === 'draft_pr_opened'))
|
|
3429
|
+
throw refuse('Review the exact five choices through work_item.setup_workflow before saving.', 'work_item.setup_workflow');
|
|
3430
|
+
for (let index = 0; index < input.rules.length; index++) {
|
|
3431
|
+
const rule = input.rules[index];
|
|
3432
|
+
const record = await this.questionnaireResume({
|
|
3433
|
+
repoRoot: input.repoRoot,
|
|
3434
|
+
questionnaireId: input.selectionQuestionnaireIds[index],
|
|
3435
|
+
});
|
|
3436
|
+
if (record.owner?.tool !== 'work_item.setup_workflow' ||
|
|
3437
|
+
record.owner.externalTaskId !== input.externalTaskId ||
|
|
3438
|
+
record.status !== 'answered' ||
|
|
3439
|
+
!record.answerAvailable ||
|
|
3440
|
+
record.answer?.choice !== rule.toStatusId ||
|
|
3441
|
+
record.binding?.setupId !== final.binding?.setupId ||
|
|
3442
|
+
record.binding?.projectId !== input.projectId ||
|
|
3443
|
+
record.binding?.expectedSnapshot !== input.expectedSnapshot ||
|
|
3444
|
+
record.binding?.event !== rule.event ||
|
|
3445
|
+
record.binding?.mode !== rule.mode)
|
|
3446
|
+
throw refuse('A role choice no longer matches the exact workflow review. Resume its original decision.', 'work_item.setup_workflow');
|
|
3447
|
+
}
|
|
3448
|
+
if (stableStringify(await this.workItemSetupScope(input)) !== stableStringify(scope))
|
|
3449
|
+
throw refuse('The task account or repository binding changed before saving.', 'session.resume');
|
|
3450
|
+
const response = await this.dependencies.client.request(endpoints.workItemList(input.projectId) + '/workflow', {
|
|
3451
|
+
method: 'PUT',
|
|
3452
|
+
body: cleanJson({ expectedSnapshot: input.expectedSnapshot, rules: input.rules }),
|
|
3453
|
+
});
|
|
3454
|
+
return asJsonValue(response.data);
|
|
3455
|
+
});
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
async workItemApplyStatusDescription(input) {
|
|
3459
|
+
return this.execute(async () => {
|
|
3460
|
+
const scope = await this.workItemSetupScope(input);
|
|
3461
|
+
return this.dependencies.questionnaires.decisions.locked(scope, this.dependencies.client.namespace, input.externalTaskId, async () => {
|
|
3462
|
+
if (input.reviewQuestionnaireIds.length < 1 ||
|
|
3463
|
+
input.reviewQuestionnaireIds.length > 10 ||
|
|
3464
|
+
new Set(input.reviewQuestionnaireIds).size !== input.reviewQuestionnaireIds.length)
|
|
3465
|
+
throw refuse('Review the complete status description before saving.', 'work_item.describe_status');
|
|
3466
|
+
const final = await this.questionnaireResume({
|
|
3467
|
+
repoRoot: input.repoRoot,
|
|
3468
|
+
questionnaireId: input.reviewQuestionnaireIds.at(-1),
|
|
3469
|
+
});
|
|
3470
|
+
const proposal = {
|
|
3471
|
+
projectId: input.projectId,
|
|
3472
|
+
statusId: input.statusId,
|
|
3473
|
+
expectedVersion: input.expectedVersion,
|
|
3474
|
+
field: input.field,
|
|
3475
|
+
value: input.value,
|
|
3476
|
+
};
|
|
3477
|
+
const proposalDigest = sha256(stableStringify(proposal));
|
|
3478
|
+
if (final.owner?.tool !== 'work_item.describe_status' ||
|
|
3479
|
+
final.owner.externalTaskId !== input.externalTaskId ||
|
|
3480
|
+
final.binding?.namespace !== this.dependencies.client.namespace ||
|
|
3481
|
+
final.binding?.proposalDigest !== proposalDigest ||
|
|
3482
|
+
final.binding?.inputQuestionnaireId !== input.inputQuestionnaireId ||
|
|
3483
|
+
stableStringify(final.binding?.reviewQuestionnaireIds) !==
|
|
3484
|
+
stableStringify(input.reviewQuestionnaireIds))
|
|
3485
|
+
throw refuse('This approval does not match the exact description change.', 'work_item.describe_status');
|
|
3486
|
+
for (let index = 0; index < input.reviewQuestionnaireIds.length; index++) {
|
|
3487
|
+
const record = await this.questionnaireResume({
|
|
3488
|
+
repoRoot: input.repoRoot,
|
|
3489
|
+
questionnaireId: input.reviewQuestionnaireIds[index],
|
|
3490
|
+
});
|
|
3491
|
+
if (record.owner?.tool !== 'work_item.describe_status' ||
|
|
3492
|
+
record.owner.externalTaskId !== input.externalTaskId ||
|
|
3493
|
+
record.status !== 'answered' ||
|
|
3494
|
+
!record.answerAvailable ||
|
|
3495
|
+
record.answer?.choice !==
|
|
3496
|
+
(index === input.reviewQuestionnaireIds.length - 1 ? 'save' : 'continue') ||
|
|
3497
|
+
record.binding?.setupId !== final.binding?.setupId ||
|
|
3498
|
+
record.binding?.namespace !== this.dependencies.client.namespace ||
|
|
3499
|
+
record.binding?.proposalDigest !== proposalDigest ||
|
|
3500
|
+
record.binding?.index !== index ||
|
|
3501
|
+
record.binding?.inputQuestionnaireId !== input.inputQuestionnaireId ||
|
|
3502
|
+
stableStringify(record.binding?.reviewQuestionnaireIds) !==
|
|
3503
|
+
stableStringify(input.reviewQuestionnaireIds))
|
|
3504
|
+
throw refuse('A review page is missing or no longer matches this description.', 'work_item.describe_status');
|
|
3505
|
+
}
|
|
3506
|
+
if (input.inputQuestionnaireId) {
|
|
3507
|
+
const draft = await this.questionnaireResume({
|
|
3508
|
+
repoRoot: input.repoRoot,
|
|
3509
|
+
questionnaireId: input.inputQuestionnaireId,
|
|
3510
|
+
});
|
|
3511
|
+
if (draft.owner?.tool !== 'work_item.describe_status' ||
|
|
3512
|
+
draft.owner.externalTaskId !== input.externalTaskId ||
|
|
3513
|
+
draft.status !== 'answered' ||
|
|
3514
|
+
!draft.answerAvailable ||
|
|
3515
|
+
draft.binding?.setupId !== final.binding?.setupId ||
|
|
3516
|
+
draft.binding?.namespace !== this.dependencies.client.namespace ||
|
|
3517
|
+
draft.binding?.projectId !== input.projectId ||
|
|
3518
|
+
draft.binding?.statusId !== input.statusId ||
|
|
3519
|
+
draft.binding?.expectedVersion !== input.expectedVersion ||
|
|
3520
|
+
draft.binding?.field !== input.field ||
|
|
3521
|
+
!((draft.answer?.choice === 'clear' && input.value === '') ||
|
|
3522
|
+
(draft.answer?.choice === 'write' && draft.answer.text === input.value)))
|
|
3523
|
+
throw refuse('The saved draft does not match this exact editable answer.', 'work_item.describe_status');
|
|
3524
|
+
}
|
|
3525
|
+
else if (final.owner.retryArguments?.draft !== input.value) {
|
|
3526
|
+
throw refuse('The supplied draft changed after review.', 'work_item.describe_status');
|
|
3527
|
+
}
|
|
3528
|
+
if (stableStringify(await this.workItemSetupScope(input)) !== stableStringify(scope))
|
|
3529
|
+
throw refuse('The task account or binding changed before saving.', 'session.resume');
|
|
3530
|
+
const response = await this.dependencies.client.request(endpoints.workItemStatuses(input.projectId) + '/' + encodeURIComponent(input.statusId), {
|
|
3531
|
+
method: 'PATCH',
|
|
3532
|
+
body: { expectedVersion: input.expectedVersion, [input.field]: input.value },
|
|
3533
|
+
});
|
|
3534
|
+
return asJsonValue(response.data);
|
|
3535
|
+
});
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
async workItemWorkflow(input) {
|
|
3539
|
+
return this.execute(async () => {
|
|
3540
|
+
const query = new URLSearchParams({
|
|
3541
|
+
offset: String(input.offset ?? 0),
|
|
3542
|
+
limit: String(input.limit ?? 50),
|
|
3543
|
+
});
|
|
3544
|
+
if (input.includeArchived !== undefined)
|
|
3545
|
+
query.set('includeArchived', String(input.includeArchived));
|
|
3546
|
+
if (input.snapshot !== undefined)
|
|
3547
|
+
query.set('snapshot', input.snapshot);
|
|
3548
|
+
const response = await this.dependencies.client.request(endpoints.workItemList(input.projectId) + '/workflow?' + query.toString());
|
|
3549
|
+
return asJsonValue(response.data);
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
async workItemConfigureWorkflow(input) {
|
|
3553
|
+
return this.execute(async () => {
|
|
3554
|
+
const { projectId, ...body } = input;
|
|
3555
|
+
const response = await this.dependencies.client.request(endpoints.workItemList(projectId) + '/workflow', { method: 'PUT', body: cleanJson(body) });
|
|
3556
|
+
return asJsonValue(response.data);
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
async workItemCreateStatus(input) {
|
|
3560
|
+
return this.execute(async () => {
|
|
3561
|
+
const { projectId, ...body } = input;
|
|
3562
|
+
const response = await this.dependencies.client.request(endpoints.workItemStatuses(projectId), { method: 'POST', body: cleanJson(body) });
|
|
3563
|
+
return asJsonValue(response.data);
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3566
|
+
async workItemSetStatusArchived(input) {
|
|
3567
|
+
return this.execute(async () => {
|
|
3568
|
+
const response = await this.dependencies.client.request(endpoints.workItemStatuses(input.projectId) +
|
|
3569
|
+
'/' +
|
|
3570
|
+
encodeURIComponent(input.statusId) +
|
|
3571
|
+
(input.archived ? '' : '/restore'), {
|
|
3572
|
+
method: input.archived ? 'DELETE' : 'POST',
|
|
3573
|
+
body: { expectedVersion: input.expectedVersion },
|
|
3574
|
+
});
|
|
3575
|
+
return asJsonValue(response.data);
|
|
3576
|
+
});
|
|
3577
|
+
}
|
|
3578
|
+
async workItemSetInitialStatus(input) {
|
|
3579
|
+
return this.execute(async () => {
|
|
3580
|
+
const { projectId, statusId, ...body } = input;
|
|
3581
|
+
const response = await this.dependencies.client.request(endpoints.workItemStatuses(projectId) + '/' + encodeURIComponent(statusId) + '/initial', { method: 'PUT', body: cleanJson(body) });
|
|
3582
|
+
return asJsonValue(response.data);
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
async workItemUpdateStatus(input) {
|
|
3586
|
+
return this.execute(async () => {
|
|
3587
|
+
const { projectId, statusId, ...body } = input;
|
|
3588
|
+
const response = await this.dependencies.client.request(endpoints.workItemStatuses(projectId) + '/' + encodeURIComponent(statusId), { method: 'PATCH', body: cleanJson(body) });
|
|
3589
|
+
return asJsonValue(response.data);
|
|
3590
|
+
});
|
|
3591
|
+
}
|
|
3093
3592
|
async workItemList(input) {
|
|
3094
3593
|
return await this.execute(async () => {
|
|
3095
3594
|
const query = new URLSearchParams();
|
|
@@ -3446,7 +3945,7 @@ export class BridgeService {
|
|
|
3446
3945
|
async sessionEntry(input = {}) {
|
|
3447
3946
|
return await this.execute(async () => {
|
|
3448
3947
|
const timer = startPhaseTimer('session.entry');
|
|
3449
|
-
await this.language(input.language);
|
|
3948
|
+
const language = await this.language(input.language);
|
|
3450
3949
|
const authentication = await this.dependencies.browserAuth.status();
|
|
3451
3950
|
timer.mark('auth_status');
|
|
3452
3951
|
let repository;
|
|
@@ -3514,6 +4013,9 @@ export class BridgeService {
|
|
|
3514
4013
|
.map(([role]) => role)
|
|
3515
4014
|
: [];
|
|
3516
4015
|
const taskSelectionRequired = liveTasks.length > 0 || workItems.length > 0;
|
|
4016
|
+
const whatsNew = authenticated && state === 'bound' && !moved.nextAction
|
|
4017
|
+
? await this.dependencies.releaseNotes?.prepare(this.dependencies.clientVersion, language)
|
|
4018
|
+
: null;
|
|
3517
4019
|
timer.finish();
|
|
3518
4020
|
return asJsonValue({
|
|
3519
4021
|
authenticated,
|
|
@@ -3523,6 +4025,7 @@ export class BridgeService {
|
|
|
3523
4025
|
liveTasks,
|
|
3524
4026
|
workItems,
|
|
3525
4027
|
taskSelectionRequired,
|
|
4028
|
+
...(whatsNew ? { whatsNew } : {}),
|
|
3526
4029
|
...(worktrees ? { worktrees: worktrees.ok ? worktrees.data : worktrees.error } : {}),
|
|
3527
4030
|
...(gitPreferences
|
|
3528
4031
|
? {
|
|
@@ -3554,6 +4057,19 @@ export class BridgeService {
|
|
|
3554
4057
|
});
|
|
3555
4058
|
});
|
|
3556
4059
|
}
|
|
4060
|
+
async releaseNotesShow(input) {
|
|
4061
|
+
return await this.execute(async () => {
|
|
4062
|
+
await this.questionnaireScope(input.repoRoot);
|
|
4063
|
+
const language = await this.language(input.language);
|
|
4064
|
+
const report = await this.dependencies.releaseNotes?.show(this.dependencies.clientVersion, language);
|
|
4065
|
+
return asJsonValue({ report: report ?? null });
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
async releaseNotesPresented(input) {
|
|
4069
|
+
return await this.execute(async () => ({
|
|
4070
|
+
recorded: (await this.dependencies.releaseNotes?.presented(input.reportId, input.claimId)) ?? false,
|
|
4071
|
+
}));
|
|
4072
|
+
}
|
|
3557
4073
|
async movedAway(repository, projectId) {
|
|
3558
4074
|
try {
|
|
3559
4075
|
const response = await this.dependencies.client.request(endpoints.projectResolve, {
|
|
@@ -4365,6 +4881,12 @@ export class BridgeService {
|
|
|
4365
4881
|
...closedTask,
|
|
4366
4882
|
repository: publicRepository(repository),
|
|
4367
4883
|
recoveredAfterResponseLoss,
|
|
4884
|
+
deliveryContext: {
|
|
4885
|
+
externalTaskId: closedPointer?.taskSlug ?? null,
|
|
4886
|
+
namespace: this.dependencies.client.namespace,
|
|
4887
|
+
diffHash: String(body.diffHash),
|
|
4888
|
+
mode: normalizeTaskMode(closedPointer?.mode),
|
|
4889
|
+
},
|
|
4368
4890
|
delivery: deliveryQuestion(touchedContract, await this.dependencies.repositories.git.pushDestination(repository.repoRoot), objectValue(closedTask.sourcePublication)?.applicable === true),
|
|
4369
4891
|
});
|
|
4370
4892
|
}
|
|
@@ -5420,4 +5942,16 @@ function publicMembership(value) {
|
|
|
5420
5942
|
}
|
|
5421
5943
|
return result;
|
|
5422
5944
|
}
|
|
5945
|
+
function delegatedEvidence(question) {
|
|
5946
|
+
const source = question.answerSource;
|
|
5947
|
+
if (source?.kind !== 'delegated_agent')
|
|
5948
|
+
return undefined;
|
|
5949
|
+
return {
|
|
5950
|
+
mode: source.mode,
|
|
5951
|
+
modeVersion: source.modeVersion,
|
|
5952
|
+
reason: source.reason,
|
|
5953
|
+
alternativesConsidered: source.alternativesConsidered,
|
|
5954
|
+
userInterestReview: source.userInterestReview,
|
|
5955
|
+
};
|
|
5956
|
+
}
|
|
5423
5957
|
//# sourceMappingURL=bridge-service.js.map
|